@stofone/dsh-agent-roster 0.9.1 → 0.10.0
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 +71 -1
- package/README.md +78 -4
- package/README.zh.md +44 -1
- package/bin/cli.mjs +228 -2
- package/client.js +49 -24
- 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,75 @@
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.10.0] - 2026-11-03
|
|
8
|
+
|
|
9
|
+
### Roles, rosters and compiled artifacts (v2)
|
|
10
|
+
|
|
11
|
+
- **A roster is no longer one document doing four jobs.** Personas move to `roles/<dir>/AGENT.md`
|
|
12
|
+
(frontmatter for parameters, markdown body for the persona, optional `parts/`), assemblies stay in
|
|
13
|
+
`rosters/<id>/roster.yaml` (members are `name@x.y.z` references, the captain gets its own section),
|
|
14
|
+
and everything consumers read is a self-contained `dist/<id>-<ver>.json`.
|
|
15
|
+
- **Assembly is part of the format**: `executionPrompt = join("\n\n", [body, ...parts, ...append])`,
|
|
16
|
+
implemented once in `lib/frontmatter.js#assemblePrompt`. The compiled artifact records character
|
|
17
|
+
count, block list and SHA-256 per prompt.
|
|
18
|
+
- **The discriminator decides the version** — `dsh-agent-roster/role@1`, `/roster@2`, `/compiled@1`.
|
|
19
|
+
v1 is frozen; two formats never share a directory.
|
|
20
|
+
- **Identity comes from the document**, so the "directory name must equal `id`" rule is replaced by a
|
|
21
|
+
stronger one: role names are unique within a library, and duplicate `name` is an error.
|
|
22
|
+
- **`orchestration` is compiled as a declaration.** Stages and gates are validated (a gate's `after`
|
|
23
|
+
must name real stages, `by` must name a real member), the artifact marks
|
|
24
|
+
`status: "declaration"`, and every non-executed field is listed in `declarations[]`.
|
|
25
|
+
- **New CLI**: `dsh-agent-roster compile --roster <dir> [--roles <dir>] [--write] [--json]`.
|
|
26
|
+
- **Schema alignment tests**: `test/v2-schema.test.js` pins field sets, enums, limits and regexes to
|
|
27
|
+
`lib/format-v2.js`, and `test/examples-orchestration.test.js` compiles `examples-orchestration/` and fails if `dist/` drifts.
|
|
28
|
+
- Design contract: [`docs/roster-v2.md`](./docs/roster-v2.md).
|
|
29
|
+
- **Gates must be runnable.** Every gate carries at least one `verify` entry (`command`, `expectExit`);
|
|
30
|
+
a gate without one is rejected as `gate-unverifiable`, because a gate whose only criterion is a
|
|
31
|
+
sentence is judged by the person signing it.
|
|
32
|
+
- **A construction plan, not an execution.** `compile` now also writes
|
|
33
|
+
`dist/<id>-<ver>.tasks.json` (`plan@1`): stages and gates flattened into cards with dependencies,
|
|
34
|
+
`executor: "captain"`, and `loop.onGateFailure` naming where a failed gate goes back to and how many
|
|
35
|
+
rounds it may take. The plugin never calls `agent_teams_create_task` — the plan is a drawing.
|
|
36
|
+
|
|
37
|
+
### Migrating what already existed
|
|
38
|
+
|
|
39
|
+
- **`migrate --dir <library> [--write]`** rewrites a v1 library in place: each member's
|
|
40
|
+
`systemPrompt` becomes the body of `roles/<roster>-<member>/AGENT.md`, the member turns into a
|
|
41
|
+
reference, the captain becomes a role file of its own, `capabilities.yaml` moves next to the
|
|
42
|
+
assembly, every roster is compiled into `dist/`, and the old flat directory is removed.
|
|
43
|
+
Without `--write` it only reports what it would do — it is the one command that deletes things.
|
|
44
|
+
- **Role names carry the roster id prefix.** v2 requires role names to be unique per library; a
|
|
45
|
+
library whose 21 rosters all call their captain `lead` cannot have 21 files named `lead`. When the
|
|
46
|
+
captain collides with a member it gets a `-captain` suffix instead of being merged — they are two
|
|
47
|
+
personas and merging them would silently drop one.
|
|
48
|
+
- **This repository's own archives were migrated**: `examples/` (2 rosters, 5 members, one captain)
|
|
49
|
+
and `orgs/agent-roster-collective/` (21 rosters, 63 members) now live as `roles/ + rosters/ + dist/`.
|
|
50
|
+
- `maintainers` accepts handles (`STofone`) rather than the lowercase id pattern — migration must not
|
|
51
|
+
rename a person to satisfy a validator.
|
|
52
|
+
- **Both layouts are readable**: the scanner, the document route and the writer accept
|
|
53
|
+
`<root>/<id>/roster.*` and `<root>/rosters/<id>/roster.*`, so a v1 library keeps working while it is
|
|
54
|
+
being migrated, and the panel shows v2 rosters through the same `rosterViewOf` shape.
|
|
55
|
+
|
|
56
|
+
### An execution record feeds the next compile
|
|
57
|
+
|
|
58
|
+
- **`run@1`** (`lib/run.js`): rounds → tasks → findings/commands, with unknown fields rejected.
|
|
59
|
+
- **`compile --run <file>`** merges the previous round's failures into the retry card's description
|
|
60
|
+
(and stamps `round` on the plan). A failure that only lives on a red card never reaches the next
|
|
61
|
+
compile — which is how a gate becomes decoration.
|
|
62
|
+
- The record is written by the captain session, not by the plugin: task ids belong to the team.
|
|
63
|
+
|
|
64
|
+
## [0.9.2] - 2026-11-03
|
|
65
|
+
|
|
66
|
+
### The member list is a table
|
|
67
|
+
|
|
68
|
+
- **Four columns now line up across rows.** Each member row used to compute its own grid, so the
|
|
69
|
+
name / description / tool policy / model columns drifted from row to row and the list read as
|
|
70
|
+
separate blocks. `.rstr-members` owns the column widths and every row uses `subgrid`.
|
|
71
|
+
- **One line per member at narrow widths**: name and role on the left, the policy pinned right,
|
|
72
|
+
description and model beneath — four lines instead of five, and nothing overlaps.
|
|
73
|
+
- Member notes (aliases, declared skills, inherited fields) span the full row instead of pushing
|
|
74
|
+
the columns around.
|
|
75
|
+
|
|
7
76
|
## [0.9.1] - 2026-11-03
|
|
8
77
|
|
|
9
78
|
### Expansion stops clipping itself
|
|
@@ -376,7 +445,8 @@
|
|
|
376
445
|
- 工具返回值含 `undefined` 键,运行时按 `value is not lossless JSON` 整次拒绝;缺失值统一写 `null`,
|
|
377
446
|
且所有返回值先过 `lib/json.js#lossless`。
|
|
378
447
|
|
|
379
|
-
[Unreleased]: https://github.com/dsh-agent-roster/dsh-agent-roster/compare/v0.9.
|
|
448
|
+
[Unreleased]: https://github.com/dsh-agent-roster/dsh-agent-roster/compare/v0.9.2...main
|
|
449
|
+
[0.9.2]: https://github.com/dsh-agent-roster/dsh-agent-roster/releases/tag/v0.9.2
|
|
380
450
|
[0.9.1]: https://github.com/dsh-agent-roster/dsh-agent-roster/releases/tag/v0.9.1
|
|
381
451
|
[0.9.0]: https://github.com/dsh-agent-roster/dsh-agent-roster/releases/tag/v0.9.0
|
|
382
452
|
[0.8.3]: https://github.com/dsh-agent-roster/dsh-agent-roster/releases/tag/v0.8.3
|
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 |
|
|
@@ -378,9 +451,10 @@ lib/ format, parse, validate, registry, bridge, verify, render, lib
|
|
|
378
451
|
client.js client plugin: sidebar entry, main panel (rosters/teams/health), settings section
|
|
379
452
|
locale/ panel + listing dictionaries (en/zh)
|
|
380
453
|
schema/ roster / capabilities / organization / registry v1 schemas (editor completion; kept aligned by tests)
|
|
381
|
-
bin/cli.mjs validate/list/
|
|
454
|
+
bin/cli.mjs validate/list/compile/migrate/registry for CI
|
|
382
455
|
scripts/ host-smoke.mjs, client-smoke.mjs, check-locale.mjs (dev only, not shipped)
|
|
383
|
-
examples/
|
|
456
|
+
examples/ a v2 library migrated from the v1 archives
|
|
457
|
+
examples-orchestration/ a v2 library with orchestration, gates and a construction plan
|
|
384
458
|
orgs/ a worked example library with its own organization.yaml (21 rosters)
|
|
385
459
|
test/ unit tests
|
|
386
460
|
```
|
|
@@ -389,7 +463,7 @@ test/ unit tests
|
|
|
389
463
|
|
|
390
464
|
Verified on this machine:
|
|
391
465
|
|
|
392
|
-
- `npm test` —
|
|
466
|
+
- `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
467
|
- `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
468
|
- CLI: `validate --dir examples` returns exit 0 over both example libraries.
|
|
395
469
|
- 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
|
@@ -217,6 +217,49 @@ 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 冻结,`roster 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
|
+
|
|
220
263
|
## 工具
|
|
221
264
|
|
|
222
265
|
| 工具 | 作用 |
|
|
@@ -374,7 +417,7 @@ test/ 单测
|
|
|
374
417
|
|
|
375
418
|
本机已验证:
|
|
376
419
|
|
|
377
|
-
- `npm test` ——
|
|
420
|
+
- `npm test` —— 286/286 通过(格式、解析、校验、扫描、路径、桥接、核对四桶、摘要与溯源、档案差异、能力要求与能力闸门、档案库组织、索引 schema、无损 JSON、写入计划与执行、六条路由、快照与提示、两本词典一致、工具契约、客户端加载器契约、包清单、配置视图、样式 token 约束、声明层字段与队长段、schema 与常量对齐、v2 角色/组装/编译三层及其 schema 对齐、v1→v2 迁移、执行回执并入施工图)。
|
|
378
421
|
- `npm run smoke` —— 用真实 DSH bundle 的 `defineTool` 注册六个工具,逐个调用:输出全部通过无损校验、`path` 覆盖生效、释放干净;同一条命令随后用桩模块表加载 `client.js`,断言三处槽位注册、样式只注入一次、effect 可回收。
|
|
379
422
|
- CLI:`validate --dir examples` 对两座示例库退出码 0。
|
|
380
423
|
- 用插件管理器真的装进了本机 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
|
+
}
|
package/client.js
CHANGED
|
@@ -468,6 +468,20 @@ window.__ModuleLoader__.load({
|
|
|
468
468
|
/* ------------------------------------------------------------------ */
|
|
469
469
|
|
|
470
470
|
function MemberBlock({ member }) {
|
|
471
|
+
// 四列由 .rstr-members 的 subgrid 统一给宽度:姓名 / 说明 / 策略 / 模型。
|
|
472
|
+
// 说明与策略即使为空也占位,否则后面的列会整体左移,表格就对不齐了。
|
|
473
|
+
const notes = [];
|
|
474
|
+
if (Array.isArray(member.aliases) && member.aliases.length > 0) {
|
|
475
|
+
notes.push(`${t("member.aliases")}: ${member.aliases.join(", ")}`);
|
|
476
|
+
}
|
|
477
|
+
if (Array.isArray(member.skills) && member.skills.length > 0) {
|
|
478
|
+
notes.push(`${t("member.skills")}: ${member.skills.join(", ")}(${t("member.skillsHint")})`);
|
|
479
|
+
}
|
|
480
|
+
if (Array.isArray(member.inherited) && member.inherited.length > 0) {
|
|
481
|
+
notes.push(`inherited: ${member.inherited.join(", ")}`);
|
|
482
|
+
}
|
|
483
|
+
if (member.notes) notes.push(member.notes);
|
|
484
|
+
|
|
471
485
|
return h(
|
|
472
486
|
"div",
|
|
473
487
|
{ className: "rstr-member" },
|
|
@@ -477,33 +491,24 @@ window.__ModuleLoader__.load({
|
|
|
477
491
|
h("strong", { className: "rstr-member-name" }, member.name),
|
|
478
492
|
member.role ? h(Chip, null, member.role) : null,
|
|
479
493
|
),
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
:
|
|
494
|
+
h("p", { className: "rstr-member-desc" }, member.description ?? ""),
|
|
495
|
+
h(
|
|
496
|
+
"span",
|
|
497
|
+
{ className: "rstr-member-policy" },
|
|
498
|
+
member.toolPolicy ? h(Chip, { tone: "policy" }, member.toolPolicy) : null,
|
|
499
|
+
),
|
|
484
500
|
h(
|
|
485
501
|
"div",
|
|
486
502
|
{ className: "rstr-member-meta" },
|
|
487
|
-
member.
|
|
488
|
-
member.
|
|
489
|
-
member.reasoningEffort ? h("span", null, `${t("member.effort")}: ${member.reasoningEffort}`) : null,
|
|
503
|
+
member.model ? h("span", null, member.model) : null,
|
|
504
|
+
member.reasoningEffort ? h("span", null, member.reasoningEffort) : null,
|
|
490
505
|
member.fallback ? h("span", null, `${t("member.fallback")}: ${member.fallback}`) : null,
|
|
491
506
|
member.toolScope?.allow?.length
|
|
492
507
|
? h("span", null, `${t("member.tools")}: ${member.toolScope.allow.join(", ")}`)
|
|
493
508
|
: null,
|
|
494
509
|
h("span", { className: "rstr-muted" }, t("team.promptChars", { count: member.systemPromptChars })),
|
|
495
510
|
),
|
|
496
|
-
|
|
497
|
-
? h(
|
|
498
|
-
"p",
|
|
499
|
-
{ className: "rstr-muted rstr-small" },
|
|
500
|
-
`${t("member.skills")}: ${member.skills.join(", ")}(${t("member.skillsHint")})`,
|
|
501
|
-
)
|
|
502
|
-
: null,
|
|
503
|
-
Array.isArray(member.inherited) && member.inherited.length > 0
|
|
504
|
-
? h("p", { className: "rstr-muted rstr-small" }, `inherited: ${member.inherited.join(", ")}`)
|
|
505
|
-
: null,
|
|
506
|
-
member.notes ? h("p", { className: "rstr-muted rstr-small" }, member.notes) : null,
|
|
511
|
+
notes.map((text, index) => h("p", { key: `note-${index}`, className: "rstr-member-note" }, text)),
|
|
507
512
|
);
|
|
508
513
|
}
|
|
509
514
|
|
|
@@ -2360,21 +2365,41 @@ window.__ModuleLoader__.load({
|
|
|
2360
2365
|
|
|
2361
2366
|
/* ---- 成员块 ----------------------------------------------------------- */
|
|
2362
2367
|
|
|
2363
|
-
|
|
2368
|
+
/* 成员是一张表:四列由容器统一给宽度(subgrid),否则每一行各算各的网格,
|
|
2369
|
+
姓名 / 说明 / 策略 / 模型四列对不齐,读起来就是散的。 */
|
|
2370
|
+
.rstr-members {
|
|
2371
|
+
display: grid;
|
|
2372
|
+
grid-template-columns: minmax(120px, 172px) minmax(0, 1fr) 104px 244px;
|
|
2373
|
+
column-gap: 14px;
|
|
2374
|
+
border-top: 1px dashed var(--dsw-alias-border-l1);
|
|
2375
|
+
}
|
|
2364
2376
|
.rstr-member {
|
|
2365
2377
|
display: grid;
|
|
2366
|
-
grid-
|
|
2367
|
-
|
|
2378
|
+
grid-column: 1 / -1;
|
|
2379
|
+
grid-template-columns: subgrid;
|
|
2380
|
+
column-gap: 14px;
|
|
2381
|
+
row-gap: 2px;
|
|
2368
2382
|
align-items: baseline;
|
|
2369
|
-
padding:
|
|
2383
|
+
padding: 10px 4px;
|
|
2370
2384
|
border-bottom: 1px solid var(--dsw-alias-border-l1);
|
|
2371
2385
|
min-width: 0;
|
|
2372
2386
|
}
|
|
2373
2387
|
.rstr-member:last-child { border-bottom: none; }
|
|
2374
2388
|
.rstr-member-head { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; min-width: 0; }
|
|
2375
|
-
.rstr-member-name { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary); }
|
|
2389
|
+
.rstr-member-name { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary); overflow: hidden; text-overflow: ellipsis; }
|
|
2376
2390
|
.rstr-member-desc { margin: 0; font-size: 12px; color: var(--dsw-alias-label-secondary); min-width: 0; }
|
|
2377
|
-
.rstr-member-
|
|
2391
|
+
.rstr-member-policy { justify-self: start; min-width: 0; }
|
|
2392
|
+
.rstr-member-meta { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; font-size: 11px; color: var(--dsw-alias-label-secondary); justify-self: end; font-variant-numeric: tabular-nums; }
|
|
2393
|
+
.rstr-member-note { grid-column: 1 / -1; margin: 0; font-size: 11px; color: var(--dsw-alias-label-secondary); }
|
|
2394
|
+
@media (max-width: 900px) {
|
|
2395
|
+
/* 窄面板:姓名一行、策略贴右;说明与模型各占一整行。 */
|
|
2396
|
+
.rstr-members { grid-template-columns: minmax(0, 1fr) auto; }
|
|
2397
|
+
.rstr-member-head { grid-column: 1; grid-row: 1; }
|
|
2398
|
+
.rstr-member-policy { grid-column: 2; grid-row: 1; justify-self: end; }
|
|
2399
|
+
.rstr-member-desc { grid-column: 1 / -1; grid-row: 2; margin-top: 2px; }
|
|
2400
|
+
.rstr-member-meta { grid-column: 1 / -1; grid-row: 3; justify-self: start; }
|
|
2401
|
+
.rstr-member-note { grid-row: auto; }
|
|
2402
|
+
}
|
|
2378
2403
|
|
|
2379
2404
|
/* ---- 队长块 ----------------------------------------------------------- */
|
|
2380
2405
|
|