@akagilnc/pi-workflow-roles 0.1.3572 → 0.1.3621
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/acp-host/production-host.js +20239 -10556
- package/dist/analyst-gate-cycles-read.js +387 -0
- package/dist/archivist-record-entry.js +22 -1
- package/dist/audit-escalation.js +6 -0
- package/dist/auditor-soul.js +74 -0
- package/dist/collector-config.js +86 -0
- package/dist/collector-evidence.js +316 -0
- package/dist/collector-github.js +527 -0
- package/dist/collector-ledger.js +853 -0
- package/dist/collector-tool-schemas.js +33 -0
- package/dist/compliance-transport.js +104 -52
- package/dist/diarist-mechanical.js +411 -0
- package/dist/diarist-ticket-resolution.js +177 -0
- package/dist/diarist.js +311 -0
- package/dist/doctor-auditor.js +25 -0
- package/dist/doctor-contracts.js +2 -0
- package/dist/doctor-evidence.js +142 -0
- package/dist/gatekeeper-role.js +104 -81
- package/dist/host-transition-prior-native.js +78 -0
- package/dist/institutional-resolution.js +1 -95
- package/dist/judge-auditor.js +26 -0
- package/dist/ledger-session-read.js +227 -0
- package/dist/merger-git-state.js +78 -0
- package/dist/navigator-attendance.js +15 -7
- package/dist/navigator-public-session.js +170 -0
- package/dist/navigator-session-contracts.js +36 -40
- package/dist/notary-source-run.js +122 -0
- package/dist/package-contracts/auditor-output.js +66 -0
- package/dist/package-contracts/evidence-child-output.js +34 -0
- package/dist/package-contracts/judge-output.js +2 -0
- package/dist/package-contracts/terminating-tools.js +25 -2
- package/dist/package-resources/method-skill.js +254 -0
- package/dist/packaged-role-registry.js +36 -0
- package/dist/pi/durable-principal.js +61 -0
- package/dist/pi/in-process-session.js +25 -6
- package/dist/pi/known-failure.js +52 -0
- package/dist/pi/role-turn-host.js +430 -0
- package/dist/public-cli/auto-resume.js +414 -0
- package/dist/public-cli/cli-errors.js +8 -0
- package/dist/public-cli/cli-io.js +1 -0
- package/dist/public-cli/command-renderer.js +5 -0
- package/dist/public-cli/doctor-run.js +84 -0
- package/dist/public-cli/inspector-run.js +136 -0
- package/dist/public-cli/instruction-seat-run.js +256 -0
- package/dist/public-cli/invocation.js +2225 -0
- package/dist/public-cli/judge-run.js +118 -0
- package/dist/public-cli/load-production-acp-host.js +40 -0
- package/dist/public-cli/main.js +1140 -652
- package/dist/public-cli/notary-run.js +161 -0
- package/dist/public-cli/option-definitions.js +1192 -0
- package/dist/public-cli/post-admission.js +667 -0
- package/dist/public-cli/public-run-credentials.js +49 -0
- package/dist/public-cli/registry.js +4 -1
- package/dist/public-cli/reviewer-dispatch-rejection.js +77 -0
- package/dist/public-cli/run-lifecycle.js +1344 -0
- package/dist/public-cli/seat-ticket-binding.js +113 -0
- package/dist/public-cli/settlement.js +3441 -0
- package/dist/public-cli/terminal.js +135 -0
- package/dist/public-cli/turn-request.js +25 -0
- package/dist/public-role-summons.js +301 -0
- package/dist/receipt-delivery-policy.js +10 -0
- package/dist/reviewer-child-executor.js +80 -8
- package/dist/reviewer-execution-ledger.js +2 -1
- package/dist/run-terminal-artifacts.js +195 -0
- package/dist/run-ticket-number.js +40 -0
- package/dist/session-assistant-usage.js +107 -0
- package/dist/shape-unreadable-failure.js +34 -0
- package/dist/submission-errors.js +3 -0
- package/dist/submission-ledger.js +388 -0
- package/dist/ticket-provenance-contracts.js +115 -0
- package/dist/ticket-provenance.js +340 -0
- package/extensions/role-runtime.ts +7 -0
- package/package.json +1 -1
- package/scripts/build-package.mjs +2 -1
- package/souls/doctor-auditor.md +1 -0
- package/souls/evidence-child.md +1 -0
- package/src/acp-host/production-host.ts +3 -0
- package/src/acp-host/role-envelope.ts +2 -2
- package/src/acp-host/role-turn-host.ts +9 -1
- package/src/analyst-gate-cycles-read.ts +37 -2
- package/src/archivist-record-entry.ts +45 -1
- package/src/audit-escalation.ts +17 -0
- package/src/auditor-role.ts +22 -0
- package/src/auditor-soul.ts +42 -0
- package/src/compliance-transport.ts +177 -66
- package/src/doctor-auditor.ts +10 -14
- package/src/doctor-contracts.ts +1 -0
- package/src/doctor-role.ts +2 -2
- package/src/evidence-child-role.ts +22 -0
- package/src/gatekeeper-pass-envelope.ts +109 -0
- package/src/gatekeeper-role.ts +145 -104
- package/src/host-contracts.ts +9 -1
- package/src/institutional-resolution.ts +8 -163
- package/src/judge-auditor.ts +10 -15
- package/src/judge-role.ts +8 -0
- package/src/navigator-attendance.ts +25 -9
- package/src/navigator-public-session.ts +217 -0
- package/src/navigator-session-contracts.ts +61 -54
- package/src/notary-source-run.ts +3 -1
- package/src/package-contracts/auditor-output.ts +82 -0
- package/src/package-contracts/evidence-child-output.ts +51 -0
- package/src/package-contracts/judge-output.ts +1 -0
- package/src/package-contracts/reviewer-output.ts +2 -2
- package/src/package-contracts/terminating-tools.ts +31 -1
- package/src/packaged-role-registry.ts +43 -0
- package/src/pi/adapter.ts +2 -1
- package/src/pi/in-process-session.ts +38 -12
- package/src/pi/role-turn-host.ts +34 -0
- package/src/public-cli/cli.ts +14 -6
- package/src/public-cli/instruction-seat-run.ts +289 -50
- package/src/public-cli/invocation.ts +66 -25
- package/src/public-cli/option-definitions.ts +61 -0
- package/src/public-cli/post-admission.ts +7 -1
- package/src/public-cli/registry.ts +4 -1
- package/src/public-cli/run-lifecycle.ts +22 -3
- package/src/public-cli/settlement.ts +120 -5
- package/src/public-cli/terminal.ts +2 -0
- package/src/public-role-summons.ts +431 -0
- package/src/receipt-delivery-policy.ts +10 -0
- package/src/reviewer-agent.ts +1 -1
- package/src/reviewer-child-executor.ts +114 -12
- package/src/reviewer-execution-ledger.ts +3 -2
- package/src/role-runtime.ts +158 -17
- package/src/session-assistant-usage.ts +119 -0
- package/src/session-opening-materials.ts +1 -1
- package/src/shape-unreadable-failure.ts +49 -0
- package/src/submission-errors.ts +3 -0
- package/dist/evidence-child-executor.js +0 -829
- package/src/evidence-child-executor.ts +0 -1140
|
@@ -0,0 +1,1192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* #342 — sole typed public CLI option-definition source.
|
|
3
|
+
*
|
|
4
|
+
* PUBLIC_ROLE_ARGV rows reference these definitions. Production parsers and
|
|
5
|
+
* `help <command>` consume this table. Do not maintain a parallel spelling set
|
|
6
|
+
* in parsers, help, or docs.
|
|
7
|
+
*
|
|
8
|
+
* Dashed-option take, positional selector match, role-phase resolution,
|
|
9
|
+
* `repeatable` enforcement, and unconditional `required` checks share one
|
|
10
|
+
* consumer (`createTypedOptionConsumer`). Parsers must not restate phase
|
|
11
|
+
* tokens or add parallel repeatability / requiredness branches.
|
|
12
|
+
*/
|
|
13
|
+
import { CliUsageError } from "./cli-errors.js";
|
|
14
|
+
/**
|
|
15
|
+
* Cross-field at-least-one rules for analyst modes.
|
|
16
|
+
* #399: issue bare call is lawful (whole book from cwd) — no require-any-of on issue.
|
|
17
|
+
*/
|
|
18
|
+
export const ANALYST_REQUIRE_ANY_OF = [];
|
|
19
|
+
/** Residual analyst mode when no `selectsMode` option is present. */
|
|
20
|
+
export const ANALYST_DEFAULT_MODE = "issue";
|
|
21
|
+
/**
|
|
22
|
+
* Resolve analyst mode from collected option ids via `selectsMode` on the table.
|
|
23
|
+
* Deterministic preference when multiple selectors co-occur; exclusiveWith then rejects.
|
|
24
|
+
*/
|
|
25
|
+
export function resolveAnalystMode(presentOptionIds) {
|
|
26
|
+
const selected = new Set();
|
|
27
|
+
for (const def of optionsForOwner("analyst")) {
|
|
28
|
+
if (def.selectsMode === undefined)
|
|
29
|
+
continue;
|
|
30
|
+
if (presentOptionIds.has(def.id))
|
|
31
|
+
selected.add(def.selectsMode);
|
|
32
|
+
}
|
|
33
|
+
if (selected.size === 0)
|
|
34
|
+
return ANALYST_DEFAULT_MODE;
|
|
35
|
+
if (selected.has("cohort"))
|
|
36
|
+
return "cohort";
|
|
37
|
+
if (selected.has("sweep"))
|
|
38
|
+
return "sweep";
|
|
39
|
+
if (selected.has("issue"))
|
|
40
|
+
return "issue";
|
|
41
|
+
return ANALYST_DEFAULT_MODE;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Evaluate analyst cross-field / cross-mode structured contracts from the sole typed table.
|
|
45
|
+
* Covers: modes admission, requiredInModes, exclusiveWith, maxCountByMode, ANALYST_REQUIRE_ANY_OF.
|
|
46
|
+
*/
|
|
47
|
+
export function evaluateAnalystModeOptionContract(mode, counts) {
|
|
48
|
+
const definitions = optionsForOwner("analyst");
|
|
49
|
+
const byId = new Map(definitions.map((def) => [def.id, def]));
|
|
50
|
+
for (const def of definitions) {
|
|
51
|
+
const count = counts.get(def.id) ?? 0;
|
|
52
|
+
if (count === 0 || def.exclusiveWith === undefined)
|
|
53
|
+
continue;
|
|
54
|
+
for (const otherId of def.exclusiveWith) {
|
|
55
|
+
if ((counts.get(otherId) ?? 0) === 0)
|
|
56
|
+
continue;
|
|
57
|
+
const other = byId.get(otherId);
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
message: `analyst accepts only one of ${def.canonical} / ${other?.canonical ?? otherId}`,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
for (const def of definitions) {
|
|
65
|
+
const count = counts.get(def.id) ?? 0;
|
|
66
|
+
if (count === 0)
|
|
67
|
+
continue;
|
|
68
|
+
if (def.modes !== undefined && !def.modes.includes(mode)) {
|
|
69
|
+
// Sweep face historically names the attach carrier on mix-face rejects.
|
|
70
|
+
if (mode === "sweep") {
|
|
71
|
+
return {
|
|
72
|
+
ok: false,
|
|
73
|
+
message: `analyst sweep --attach cannot combine with ${def.canonical}`,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
message: `analyst ${mode} does not accept ${def.canonical}`,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
const max = def.maxCountByMode?.[mode];
|
|
82
|
+
if (max !== undefined && count > max) {
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
message: max === 1
|
|
86
|
+
? `analyst ${mode} accepts at most one ${def.canonical}`
|
|
87
|
+
: `analyst ${mode} accepts at most ${max} ${def.canonical}`,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const missingRequired = [];
|
|
92
|
+
for (const def of definitions) {
|
|
93
|
+
if (def.requiredInModes === undefined)
|
|
94
|
+
continue;
|
|
95
|
+
if (!def.requiredInModes.includes(mode))
|
|
96
|
+
continue;
|
|
97
|
+
if ((counts.get(def.id) ?? 0) === 0)
|
|
98
|
+
missingRequired.push(def);
|
|
99
|
+
}
|
|
100
|
+
if (missingRequired.length > 0) {
|
|
101
|
+
if (mode === "cohort") {
|
|
102
|
+
return {
|
|
103
|
+
ok: false,
|
|
104
|
+
message: "usage: ak-role analyst --cohort --group-a-label <L> --group-a-issues <N|book:N[,...]> --group-b-label <L> --group-b-issues <N|book:N[,...]>",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
return {
|
|
108
|
+
ok: false,
|
|
109
|
+
message: `usage: ak-role analyst ${mode} requires ${missingRequired
|
|
110
|
+
.map((def) => def.canonical)
|
|
111
|
+
.join(" ")}`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
for (const rule of ANALYST_REQUIRE_ANY_OF) {
|
|
115
|
+
if (rule.mode !== mode)
|
|
116
|
+
continue;
|
|
117
|
+
const hit = rule.optionIds.some((id) => (counts.get(id) ?? 0) > 0);
|
|
118
|
+
if (hit)
|
|
119
|
+
continue;
|
|
120
|
+
if (mode === "issue") {
|
|
121
|
+
// Bare-usage surface names issue faces and the sweep attach carrier.
|
|
122
|
+
return {
|
|
123
|
+
ok: false,
|
|
124
|
+
message: "usage: ak-role analyst ([--ticket <N>] | [sweep] --attach <sweep.json> | --cohort ...)",
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
const flags = rule.optionIds
|
|
128
|
+
.map((id) => byId.get(id)?.canonical ?? id)
|
|
129
|
+
.join(" | ");
|
|
130
|
+
return {
|
|
131
|
+
ok: false,
|
|
132
|
+
message: `usage: ak-role analyst ${mode} requires one of ${flags}`,
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return { ok: true };
|
|
136
|
+
}
|
|
137
|
+
export const REJECTED_PUBLIC_SPELLINGS = [
|
|
138
|
+
{
|
|
139
|
+
owner: "judge",
|
|
140
|
+
spellings: ["--burden", "--ak-judge-burden", "--judge-burden"],
|
|
141
|
+
reason: {
|
|
142
|
+
en: "Judge infers its own burden; no public burden selector.",
|
|
143
|
+
zh: "大理寺自行推断举证责任,不接受公开 burden 旗标。",
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
owner: "merger",
|
|
148
|
+
spellings: ["--ak-merger-input"],
|
|
149
|
+
reason: {
|
|
150
|
+
en: "Merger input packet is assembled internally; not a public flag.",
|
|
151
|
+
zh: "校书郎 input packet 由内部装配,不是公开旗标。",
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
owner: "analyst",
|
|
156
|
+
spellings: ["--project-root"],
|
|
157
|
+
reason: {
|
|
158
|
+
en: "Deleted (#399). analyst no longer accepts --project-root; use bare call for whole book or --ticket N (cwd git common-dir selects the book).",
|
|
159
|
+
zh: "已删除(#399)。analyst 不再接受 --project-root;裸调用=整簿,或 --ticket N(cwd git common-dir 定簿)。",
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
owner: "analyst",
|
|
164
|
+
spellings: ["--model-groups"],
|
|
165
|
+
reason: {
|
|
166
|
+
en: "Public CLI face disabled (#399). Input face is being redesigned for multi-issue comparison (see follow-up ticket). Library kernel retained.",
|
|
167
|
+
zh: "公开 CLI 面已停用(#399)。输入面按多 issue 重设计中(见后续票)。聚合内核保留。",
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
];
|
|
171
|
+
const GLOBAL_OPTIONS = [
|
|
172
|
+
{
|
|
173
|
+
id: "model",
|
|
174
|
+
owner: "global",
|
|
175
|
+
canonical: "--model",
|
|
176
|
+
aliases: [],
|
|
177
|
+
valueMetavar: "provider/model",
|
|
178
|
+
required: false,
|
|
179
|
+
repeatable: false,
|
|
180
|
+
form: "option",
|
|
181
|
+
description: {
|
|
182
|
+
en: "Override the effective seat model for this invocation (before or after the command).",
|
|
183
|
+
zh: "覆盖本调用有效席位模型(可置于子命令前或后)。",
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
id: "thinking",
|
|
188
|
+
owner: "global",
|
|
189
|
+
canonical: "--thinking",
|
|
190
|
+
aliases: [],
|
|
191
|
+
valueMetavar: "level",
|
|
192
|
+
required: false,
|
|
193
|
+
repeatable: false,
|
|
194
|
+
form: "option",
|
|
195
|
+
description: {
|
|
196
|
+
en: "Override thinking level (opaque pass-through to Pi; e.g. off|minimal|low|medium|high|xhigh|max).",
|
|
197
|
+
zh: "覆盖 thinking 档位(原样透传给 Pi;例如 off|minimal|low|medium|high|xhigh|max)。",
|
|
198
|
+
},
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
id: "engine",
|
|
202
|
+
owner: "global",
|
|
203
|
+
canonical: "--engine",
|
|
204
|
+
aliases: [],
|
|
205
|
+
valueMetavar: "name",
|
|
206
|
+
required: false,
|
|
207
|
+
repeatable: false,
|
|
208
|
+
form: "option",
|
|
209
|
+
description: {
|
|
210
|
+
en: "Labor engine for this invocation (owner pool-directive name; packaged notes attached when present; any role).",
|
|
211
|
+
zh: "本调用劳动引擎(池令名字;有包内调法笔记则附卷;全部角色可用)。",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
id: "host",
|
|
216
|
+
owner: "global",
|
|
217
|
+
canonical: "--host",
|
|
218
|
+
aliases: [],
|
|
219
|
+
valueMetavar: "name",
|
|
220
|
+
required: false,
|
|
221
|
+
repeatable: false,
|
|
222
|
+
form: "option",
|
|
223
|
+
description: {
|
|
224
|
+
en: "Select the named main-session host adapter for this invocation (overrides persistent seat host; after config set-host the role command face is unchanged).",
|
|
225
|
+
zh: "为本调用选择具名主会话宿主适配器(覆盖席位持久 host;config set-host 后角色命令面不变)。",
|
|
226
|
+
},
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: "help",
|
|
230
|
+
owner: "global",
|
|
231
|
+
canonical: "--help",
|
|
232
|
+
aliases: ["-h"],
|
|
233
|
+
valueMetavar: null,
|
|
234
|
+
required: false,
|
|
235
|
+
repeatable: false,
|
|
236
|
+
form: "option",
|
|
237
|
+
description: {
|
|
238
|
+
en: "Show public CLI help and exit.",
|
|
239
|
+
zh: "显示公开 CLI 帮助并退出。",
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
];
|
|
243
|
+
/**
|
|
244
|
+
* Immutable shared semantics for the common ledger `--project` face.
|
|
245
|
+
* Role rows bind owner only — do not copy these fields per role.
|
|
246
|
+
* Role-specific project faces (merger merge-root) stay explicit below and
|
|
247
|
+
* must not use this binding. analyst `--project-root` is deleted (#399).
|
|
248
|
+
*/
|
|
249
|
+
const SHARED_PROJECT_SEMANTICS = {
|
|
250
|
+
id: "project",
|
|
251
|
+
canonical: "--project",
|
|
252
|
+
aliases: [],
|
|
253
|
+
valueMetavar: "path",
|
|
254
|
+
required: false,
|
|
255
|
+
repeatable: false,
|
|
256
|
+
form: "option",
|
|
257
|
+
description: {
|
|
258
|
+
en: "Project root for ledger identity (defaults to process cwd).",
|
|
259
|
+
zh: "卷宗身份用的项目根(默认进程 cwd)。",
|
|
260
|
+
},
|
|
261
|
+
};
|
|
262
|
+
/**
|
|
263
|
+
* Immutable shared semantics for the common frozen-file `--attach` face.
|
|
264
|
+
* Role rows bind owner only. Reviewer has no attach face; analyst sweep attach
|
|
265
|
+
* keeps its own modes/selectsMode/description and must not use this binding.
|
|
266
|
+
*/
|
|
267
|
+
const SHARED_ATTACH_SEMANTICS = {
|
|
268
|
+
id: "attach",
|
|
269
|
+
canonical: "--attach",
|
|
270
|
+
aliases: [],
|
|
271
|
+
valueMetavar: "path",
|
|
272
|
+
required: false,
|
|
273
|
+
repeatable: true,
|
|
274
|
+
form: "option",
|
|
275
|
+
description: {
|
|
276
|
+
en: "Attach a regular file; frozen at admission (repeatable).",
|
|
277
|
+
zh: "附加普通文件;受理即冻结(可重复)。",
|
|
278
|
+
},
|
|
279
|
+
};
|
|
280
|
+
/** Minimal owner-binding: one immutable semantic row → one role table entry. */
|
|
281
|
+
function bindOwner(owner, semantics) {
|
|
282
|
+
return { ...semantics, owner };
|
|
283
|
+
}
|
|
284
|
+
const JUDGE_OPTIONS = [
|
|
285
|
+
bindOwner("judge", SHARED_PROJECT_SEMANTICS),
|
|
286
|
+
bindOwner("judge", SHARED_ATTACH_SEMANTICS),
|
|
287
|
+
];
|
|
288
|
+
const GLEANER_LEFT_OPTIONS = [
|
|
289
|
+
bindOwner("gleaner-left", SHARED_PROJECT_SEMANTICS),
|
|
290
|
+
// Unanchored seat: no --attach face (self-fetches the merge-candidate diff).
|
|
291
|
+
{
|
|
292
|
+
id: "base",
|
|
293
|
+
owner: "gleaner-left",
|
|
294
|
+
canonical: "--base",
|
|
295
|
+
aliases: [],
|
|
296
|
+
valueMetavar: "revision",
|
|
297
|
+
required: true,
|
|
298
|
+
repeatable: false,
|
|
299
|
+
form: "option",
|
|
300
|
+
description: {
|
|
301
|
+
en: "Required comparison-base revision for the unanchored merge-candidate diff.",
|
|
302
|
+
zh: "必填;无锚定合并候选 diff 的比较基线 revision。",
|
|
303
|
+
},
|
|
304
|
+
},
|
|
305
|
+
];
|
|
306
|
+
const INSPECTOR_OPTIONS = [
|
|
307
|
+
bindOwner("inspector", SHARED_PROJECT_SEMANTICS),
|
|
308
|
+
bindOwner("inspector", SHARED_ATTACH_SEMANTICS),
|
|
309
|
+
];
|
|
310
|
+
const GATEKEEPER_OPTIONS = [
|
|
311
|
+
bindOwner("gatekeeper", SHARED_PROJECT_SEMANTICS),
|
|
312
|
+
bindOwner("gatekeeper", SHARED_ATTACH_SEMANTICS),
|
|
313
|
+
];
|
|
314
|
+
const NAVIGATOR_OPTIONS = [
|
|
315
|
+
bindOwner("navigator", SHARED_PROJECT_SEMANTICS),
|
|
316
|
+
bindOwner("navigator", SHARED_ATTACH_SEMANTICS),
|
|
317
|
+
];
|
|
318
|
+
const AUDITOR_OPTIONS = [
|
|
319
|
+
bindOwner("auditor", SHARED_PROJECT_SEMANTICS),
|
|
320
|
+
bindOwner("auditor", SHARED_ATTACH_SEMANTICS),
|
|
321
|
+
{
|
|
322
|
+
id: "subject",
|
|
323
|
+
owner: "auditor",
|
|
324
|
+
canonical: "--subject",
|
|
325
|
+
aliases: [],
|
|
326
|
+
valueMetavar: "judge|doctor",
|
|
327
|
+
required: true,
|
|
328
|
+
repeatable: false,
|
|
329
|
+
form: "option",
|
|
330
|
+
description: {
|
|
331
|
+
en: "Required audited subject: judge or doctor (selects judge-auditor.md / doctor-auditor.md).",
|
|
332
|
+
zh: "必填受审对象:judge 或 doctor(装 judge-auditor.md / doctor-auditor.md)。",
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
id: "source-run",
|
|
337
|
+
owner: "auditor",
|
|
338
|
+
canonical: "--source-run",
|
|
339
|
+
aliases: [],
|
|
340
|
+
valueMetavar: "runId@role|path",
|
|
341
|
+
required: true,
|
|
342
|
+
repeatable: false,
|
|
343
|
+
form: "option",
|
|
344
|
+
description: {
|
|
345
|
+
en: "Required source run locator of the audited subject volume (same input for direct and nested summons).",
|
|
346
|
+
zh: "必填受审卷宗 locator(直调与传召同一输入面)。",
|
|
347
|
+
},
|
|
348
|
+
},
|
|
349
|
+
];
|
|
350
|
+
const EVIDENCE_CHILD_OPTIONS = [
|
|
351
|
+
bindOwner("evidence-child", SHARED_PROJECT_SEMANTICS),
|
|
352
|
+
bindOwner("evidence-child", SHARED_ATTACH_SEMANTICS),
|
|
353
|
+
];
|
|
354
|
+
const COUNTERSIGN_OPTIONS = [
|
|
355
|
+
bindOwner("countersign", SHARED_PROJECT_SEMANTICS),
|
|
356
|
+
bindOwner("countersign", SHARED_ATTACH_SEMANTICS),
|
|
357
|
+
];
|
|
358
|
+
const DIARIST_OPTIONS = [
|
|
359
|
+
bindOwner("diarist", SHARED_PROJECT_SEMANTICS),
|
|
360
|
+
bindOwner("diarist", SHARED_ATTACH_SEMANTICS),
|
|
361
|
+
];
|
|
362
|
+
const CODER_OPTIONS = [
|
|
363
|
+
{
|
|
364
|
+
id: "phase",
|
|
365
|
+
owner: "coder",
|
|
366
|
+
canonical: "plan|apply",
|
|
367
|
+
aliases: ["plan", "apply"],
|
|
368
|
+
valueMetavar: null,
|
|
369
|
+
required: false,
|
|
370
|
+
repeatable: false,
|
|
371
|
+
defaultValue: "apply",
|
|
372
|
+
form: "positional",
|
|
373
|
+
phases: ["plan", "apply"],
|
|
374
|
+
description: {
|
|
375
|
+
en: "Optional phase token before the instruction; defaults to apply.",
|
|
376
|
+
zh: "指令前可选 phase 词元;默认 apply。",
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
bindOwner("coder", SHARED_PROJECT_SEMANTICS),
|
|
380
|
+
bindOwner("coder", SHARED_ATTACH_SEMANTICS),
|
|
381
|
+
];
|
|
382
|
+
const FIXER_OPTIONS = [
|
|
383
|
+
{
|
|
384
|
+
id: "phase",
|
|
385
|
+
owner: "fixer",
|
|
386
|
+
canonical: "plan|apply",
|
|
387
|
+
aliases: ["plan", "apply"],
|
|
388
|
+
valueMetavar: null,
|
|
389
|
+
required: false,
|
|
390
|
+
repeatable: false,
|
|
391
|
+
defaultValue: "apply",
|
|
392
|
+
form: "positional",
|
|
393
|
+
phases: ["plan", "apply"],
|
|
394
|
+
description: {
|
|
395
|
+
en: "Optional phase token before the instruction; defaults to apply.",
|
|
396
|
+
zh: "指令前可选 phase 词元;默认 apply。",
|
|
397
|
+
},
|
|
398
|
+
},
|
|
399
|
+
bindOwner("fixer", SHARED_PROJECT_SEMANTICS),
|
|
400
|
+
bindOwner("fixer", SHARED_ATTACH_SEMANTICS),
|
|
401
|
+
{
|
|
402
|
+
id: "prerequisites",
|
|
403
|
+
owner: "fixer",
|
|
404
|
+
canonical: "--prerequisites",
|
|
405
|
+
aliases: [],
|
|
406
|
+
valueMetavar: "path",
|
|
407
|
+
required: false,
|
|
408
|
+
repeatable: false,
|
|
409
|
+
form: "option",
|
|
410
|
+
description: {
|
|
411
|
+
en: "JSON array of {id, requirement} prerequisite objects.",
|
|
412
|
+
zh: "{id, requirement} 前置条件 JSON 数组路径。",
|
|
413
|
+
},
|
|
414
|
+
},
|
|
415
|
+
];
|
|
416
|
+
const REVIEWER_OPTIONS = [
|
|
417
|
+
bindOwner("reviewer", SHARED_PROJECT_SEMANTICS),
|
|
418
|
+
// Reviewer deliberately has no --attach face (gathers its own evidence).
|
|
419
|
+
{
|
|
420
|
+
id: "base",
|
|
421
|
+
owner: "reviewer",
|
|
422
|
+
canonical: "--base",
|
|
423
|
+
aliases: [],
|
|
424
|
+
valueMetavar: "revision",
|
|
425
|
+
required: true,
|
|
426
|
+
repeatable: false,
|
|
427
|
+
form: "option",
|
|
428
|
+
description: {
|
|
429
|
+
en: "Required fixed-point revision for the pinned review target.",
|
|
430
|
+
zh: "必填;钉住审查目标的 fixed-point revision。",
|
|
431
|
+
},
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
id: "authority-ref",
|
|
435
|
+
owner: "reviewer",
|
|
436
|
+
canonical: "--authority-ref",
|
|
437
|
+
aliases: [],
|
|
438
|
+
valueMetavar: "ref",
|
|
439
|
+
required: false,
|
|
440
|
+
repeatable: true,
|
|
441
|
+
form: "option",
|
|
442
|
+
description: {
|
|
443
|
+
en: "Durable authority reference/URL (repeatable; refs only, not inline prose).",
|
|
444
|
+
zh: "持久 authority 引用/URL(可重复;仅 ref,非内联散文)。",
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
];
|
|
448
|
+
const COLLECTOR_OPTIONS = [
|
|
449
|
+
bindOwner("collector", SHARED_PROJECT_SEMANTICS),
|
|
450
|
+
bindOwner("collector", SHARED_ATTACH_SEMANTICS),
|
|
451
|
+
{
|
|
452
|
+
id: "pr",
|
|
453
|
+
owner: "collector",
|
|
454
|
+
canonical: "--pr",
|
|
455
|
+
aliases: [],
|
|
456
|
+
valueMetavar: "number",
|
|
457
|
+
required: true,
|
|
458
|
+
repeatable: false,
|
|
459
|
+
form: "option",
|
|
460
|
+
description: {
|
|
461
|
+
en: "Required positive GitHub pull request number.",
|
|
462
|
+
zh: "必填;正整数 GitHub PR 号。",
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
id: "repo",
|
|
467
|
+
owner: "collector",
|
|
468
|
+
canonical: "--repo",
|
|
469
|
+
aliases: [],
|
|
470
|
+
valueMetavar: "owner/repo",
|
|
471
|
+
required: false,
|
|
472
|
+
repeatable: false,
|
|
473
|
+
form: "option",
|
|
474
|
+
description: {
|
|
475
|
+
en: "GitHub owner/repo override (defaults from origin when github.com).",
|
|
476
|
+
zh: "GitHub owner/repo 覆盖(默认取 github.com origin)。",
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
id: "request-manifest",
|
|
481
|
+
owner: "collector",
|
|
482
|
+
canonical: "--request-manifest",
|
|
483
|
+
aliases: [],
|
|
484
|
+
valueMetavar: "path",
|
|
485
|
+
required: false,
|
|
486
|
+
repeatable: false,
|
|
487
|
+
form: "option",
|
|
488
|
+
description: {
|
|
489
|
+
en: "Optional request manifest JSON path ({requests:[{id,body}]}).",
|
|
490
|
+
zh: "可选 request manifest JSON 路径({requests:[{id,body}]})。",
|
|
491
|
+
},
|
|
492
|
+
},
|
|
493
|
+
];
|
|
494
|
+
const DOCTOR_OPTIONS = [
|
|
495
|
+
bindOwner("doctor", SHARED_PROJECT_SEMANTICS),
|
|
496
|
+
bindOwner("doctor", SHARED_ATTACH_SEMANTICS),
|
|
497
|
+
{
|
|
498
|
+
id: "issue",
|
|
499
|
+
owner: "doctor",
|
|
500
|
+
canonical: "--issue",
|
|
501
|
+
aliases: [],
|
|
502
|
+
valueMetavar: "number",
|
|
503
|
+
required: true,
|
|
504
|
+
repeatable: false,
|
|
505
|
+
form: "option",
|
|
506
|
+
description: {
|
|
507
|
+
en: "Required positive issue number for the retained case.",
|
|
508
|
+
zh: "必填;留存病例的正整数 issue 号。",
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
id: "runs",
|
|
513
|
+
owner: "doctor",
|
|
514
|
+
canonical: "--runs",
|
|
515
|
+
aliases: [],
|
|
516
|
+
valueMetavar: "path",
|
|
517
|
+
required: false,
|
|
518
|
+
repeatable: false,
|
|
519
|
+
form: "option",
|
|
520
|
+
description: {
|
|
521
|
+
en: "Optional project-relative .ak-roles/books/<book>/issues/<n>/runs override matching --issue.",
|
|
522
|
+
zh: "可选项目相对 .ak-roles/books/<book>/issues/<n>/runs 覆盖,且须匹配 --issue。",
|
|
523
|
+
},
|
|
524
|
+
},
|
|
525
|
+
];
|
|
526
|
+
const NOTARY_OPTIONS = [
|
|
527
|
+
bindOwner("notary", SHARED_PROJECT_SEMANTICS),
|
|
528
|
+
{
|
|
529
|
+
id: "source-run",
|
|
530
|
+
owner: "notary",
|
|
531
|
+
canonical: "--source-run",
|
|
532
|
+
aliases: [],
|
|
533
|
+
valueMetavar: "runId@role|path",
|
|
534
|
+
required: true,
|
|
535
|
+
repeatable: false,
|
|
536
|
+
form: "option",
|
|
537
|
+
description: {
|
|
538
|
+
en: "Required source run locator (runId@role under the book home, or path to that run directory). Zero prompt/attachment projection.",
|
|
539
|
+
zh: "必填源 run 定位符(簿内 runId@role,或该 run 目录路径)。零 prompt/附件投影。",
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
];
|
|
543
|
+
const MERGER_OPTIONS = [
|
|
544
|
+
// Merger project face differs: requires an in-progress ordinary merge root.
|
|
545
|
+
{
|
|
546
|
+
id: "project",
|
|
547
|
+
owner: "merger",
|
|
548
|
+
canonical: "--project",
|
|
549
|
+
aliases: [],
|
|
550
|
+
valueMetavar: "path",
|
|
551
|
+
required: false,
|
|
552
|
+
repeatable: false,
|
|
553
|
+
form: "option",
|
|
554
|
+
description: {
|
|
555
|
+
en: "Project root with one ordinary in-progress merge (defaults to cwd).",
|
|
556
|
+
zh: "已有进行中 ordinary merge 的项目根(默认 cwd)。",
|
|
557
|
+
},
|
|
558
|
+
},
|
|
559
|
+
bindOwner("merger", SHARED_ATTACH_SEMANTICS),
|
|
560
|
+
];
|
|
561
|
+
const ANALYST_OPTIONS = [
|
|
562
|
+
{
|
|
563
|
+
id: "sweep",
|
|
564
|
+
owner: "analyst",
|
|
565
|
+
canonical: "sweep",
|
|
566
|
+
aliases: [],
|
|
567
|
+
valueMetavar: null,
|
|
568
|
+
required: false,
|
|
569
|
+
repeatable: false,
|
|
570
|
+
form: "positional",
|
|
571
|
+
modes: ["sweep"],
|
|
572
|
+
selectsMode: "sweep",
|
|
573
|
+
description: {
|
|
574
|
+
en: "Optional sweep mode token (at most once; no other positionals).",
|
|
575
|
+
zh: "可选 sweep 模式词元(至多一次;不得夹带其他 positional)。",
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
{
|
|
579
|
+
id: "ticket",
|
|
580
|
+
owner: "analyst",
|
|
581
|
+
canonical: "--ticket",
|
|
582
|
+
aliases: [],
|
|
583
|
+
valueMetavar: "number",
|
|
584
|
+
required: false,
|
|
585
|
+
repeatable: false,
|
|
586
|
+
form: "option",
|
|
587
|
+
modes: ["issue"],
|
|
588
|
+
description: {
|
|
589
|
+
en: "Ticket/issue number; live filter by invocation.ticketNumber inside the cwd book (git common-dir). Bare call = whole book. No library-index bootstrap.",
|
|
590
|
+
zh: "票号;在 cwd 候簿(git common-dir)内按 invocation.ticketNumber 现取现算。裸调用=整簿。不依赖 library-index 自举。",
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
{
|
|
594
|
+
id: "attach",
|
|
595
|
+
owner: "analyst",
|
|
596
|
+
canonical: "--attach",
|
|
597
|
+
aliases: [],
|
|
598
|
+
valueMetavar: "path",
|
|
599
|
+
required: false,
|
|
600
|
+
repeatable: true,
|
|
601
|
+
form: "option",
|
|
602
|
+
modes: ["sweep"],
|
|
603
|
+
selectsMode: "sweep",
|
|
604
|
+
requiredInModes: ["sweep"],
|
|
605
|
+
maxCountByMode: { sweep: 1 },
|
|
606
|
+
description: {
|
|
607
|
+
en: "Sweep-mode attachment path; required exactly once in sweep; payload is the attachment body.",
|
|
608
|
+
zh: "sweep 模式附件路径;sweep 必填且恰一次;载荷为附件正文。",
|
|
609
|
+
},
|
|
610
|
+
},
|
|
611
|
+
{
|
|
612
|
+
id: "cohort",
|
|
613
|
+
owner: "analyst",
|
|
614
|
+
canonical: "--cohort",
|
|
615
|
+
aliases: [],
|
|
616
|
+
valueMetavar: null,
|
|
617
|
+
required: false,
|
|
618
|
+
repeatable: false,
|
|
619
|
+
form: "option",
|
|
620
|
+
modes: ["cohort"],
|
|
621
|
+
selectsMode: "cohort",
|
|
622
|
+
description: {
|
|
623
|
+
en: "Select cohort mode.",
|
|
624
|
+
zh: "选择 cohort 模式。",
|
|
625
|
+
},
|
|
626
|
+
},
|
|
627
|
+
{
|
|
628
|
+
id: "group-a-label",
|
|
629
|
+
owner: "analyst",
|
|
630
|
+
canonical: "--group-a-label",
|
|
631
|
+
aliases: [],
|
|
632
|
+
valueMetavar: "label",
|
|
633
|
+
required: false,
|
|
634
|
+
repeatable: false,
|
|
635
|
+
form: "option",
|
|
636
|
+
modes: ["cohort"],
|
|
637
|
+
requiredInModes: ["cohort"],
|
|
638
|
+
description: {
|
|
639
|
+
en: "Cohort group A label (required in cohort mode).",
|
|
640
|
+
zh: "cohort A 组标签(cohort 模式必填)。",
|
|
641
|
+
},
|
|
642
|
+
},
|
|
643
|
+
{
|
|
644
|
+
id: "group-a-issues",
|
|
645
|
+
owner: "analyst",
|
|
646
|
+
canonical: "--group-a-issues",
|
|
647
|
+
aliases: [],
|
|
648
|
+
valueMetavar: "N|book:N[,...]",
|
|
649
|
+
required: false,
|
|
650
|
+
repeatable: false,
|
|
651
|
+
form: "option",
|
|
652
|
+
modes: ["cohort"],
|
|
653
|
+
requiredInModes: ["cohort"],
|
|
654
|
+
description: {
|
|
655
|
+
en: "Cohort group A issues: bare N joins cwd book; book:N selects another book; escape a literal comma/backslash in a book key as \\, / \\\\ (required in cohort mode).",
|
|
656
|
+
zh: "cohort A 组 issue:裸 N 归属 cwd 簿;book:N 显式跨簿;簿键中的逗号/反斜杠用 \\, / \\\\ 转义(cohort 模式必填)。",
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
{
|
|
660
|
+
id: "group-b-label",
|
|
661
|
+
owner: "analyst",
|
|
662
|
+
canonical: "--group-b-label",
|
|
663
|
+
aliases: [],
|
|
664
|
+
valueMetavar: "label",
|
|
665
|
+
required: false,
|
|
666
|
+
repeatable: false,
|
|
667
|
+
form: "option",
|
|
668
|
+
modes: ["cohort"],
|
|
669
|
+
requiredInModes: ["cohort"],
|
|
670
|
+
description: {
|
|
671
|
+
en: "Cohort group B label (required in cohort mode).",
|
|
672
|
+
zh: "cohort B 组标签(cohort 模式必填)。",
|
|
673
|
+
},
|
|
674
|
+
},
|
|
675
|
+
{
|
|
676
|
+
id: "group-b-issues",
|
|
677
|
+
owner: "analyst",
|
|
678
|
+
canonical: "--group-b-issues",
|
|
679
|
+
aliases: [],
|
|
680
|
+
valueMetavar: "N|book:N[,...]",
|
|
681
|
+
required: false,
|
|
682
|
+
repeatable: false,
|
|
683
|
+
form: "option",
|
|
684
|
+
modes: ["cohort"],
|
|
685
|
+
requiredInModes: ["cohort"],
|
|
686
|
+
description: {
|
|
687
|
+
en: "Cohort group B issues: bare N joins cwd book; book:N selects another book; escape a literal comma/backslash in a book key as \\, / \\\\ (required in cohort mode).",
|
|
688
|
+
zh: "cohort B 组 issue:裸 N 归属 cwd 簿;book:N 显式跨簿;簿键中的逗号/反斜杠用 \\, / \\\\ 转义(cohort 模式必填)。",
|
|
689
|
+
},
|
|
690
|
+
},
|
|
691
|
+
];
|
|
692
|
+
/**
|
|
693
|
+
* Sole production option tables keyed by PUBLIC_ROLE_ARGV / global owner.
|
|
694
|
+
* Rows are readonly definition lists — parsers look up spellings here.
|
|
695
|
+
*/
|
|
696
|
+
export const PUBLIC_OPTION_TABLE = {
|
|
697
|
+
global: GLOBAL_OPTIONS,
|
|
698
|
+
judge: JUDGE_OPTIONS,
|
|
699
|
+
countersign: COUNTERSIGN_OPTIONS,
|
|
700
|
+
"gleaner-left": GLEANER_LEFT_OPTIONS,
|
|
701
|
+
coder: CODER_OPTIONS,
|
|
702
|
+
fixer: FIXER_OPTIONS,
|
|
703
|
+
reviewer: REVIEWER_OPTIONS,
|
|
704
|
+
collector: COLLECTOR_OPTIONS,
|
|
705
|
+
doctor: DOCTOR_OPTIONS,
|
|
706
|
+
merger: MERGER_OPTIONS,
|
|
707
|
+
notary: NOTARY_OPTIONS,
|
|
708
|
+
inspector: INSPECTOR_OPTIONS,
|
|
709
|
+
gatekeeper: GATEKEEPER_OPTIONS,
|
|
710
|
+
navigator: NAVIGATOR_OPTIONS,
|
|
711
|
+
auditor: AUDITOR_OPTIONS,
|
|
712
|
+
"evidence-child": EVIDENCE_CHILD_OPTIONS,
|
|
713
|
+
diarist: DIARIST_OPTIONS,
|
|
714
|
+
analyst: ANALYST_OPTIONS,
|
|
715
|
+
};
|
|
716
|
+
/** Role/deterministic owners that appear on PUBLIC_ROLE_ARGV. */
|
|
717
|
+
export const PUBLIC_ROLE_OPTION_OWNERS = [
|
|
718
|
+
"judge",
|
|
719
|
+
"countersign",
|
|
720
|
+
"gleaner-left",
|
|
721
|
+
"coder",
|
|
722
|
+
"fixer",
|
|
723
|
+
"reviewer",
|
|
724
|
+
"collector",
|
|
725
|
+
"doctor",
|
|
726
|
+
"merger",
|
|
727
|
+
"notary",
|
|
728
|
+
"inspector",
|
|
729
|
+
"gatekeeper",
|
|
730
|
+
"navigator",
|
|
731
|
+
"auditor",
|
|
732
|
+
"evidence-child",
|
|
733
|
+
"diarist",
|
|
734
|
+
"analyst",
|
|
735
|
+
];
|
|
736
|
+
export function optionsForOwner(owner) {
|
|
737
|
+
return PUBLIC_OPTION_TABLE[owner];
|
|
738
|
+
}
|
|
739
|
+
export function optionById(owner, id) {
|
|
740
|
+
const found = PUBLIC_OPTION_TABLE[owner].find((entry) => entry.id === id);
|
|
741
|
+
if (found === undefined) {
|
|
742
|
+
throw new Error(`public option not defined: ${owner}/${id}`);
|
|
743
|
+
}
|
|
744
|
+
return found;
|
|
745
|
+
}
|
|
746
|
+
/** Every dashed spelling (canonical + aliases) for option-form entries. */
|
|
747
|
+
export function dashedSpellings(def) {
|
|
748
|
+
if (def.form !== "option")
|
|
749
|
+
return [];
|
|
750
|
+
return [def.canonical, ...def.aliases];
|
|
751
|
+
}
|
|
752
|
+
/**
|
|
753
|
+
* Match `token` against dashed option definitions.
|
|
754
|
+
* Supports exact `--flag` / `-h` and inline `--flag=value`.
|
|
755
|
+
*/
|
|
756
|
+
export function matchDashedOption(token, definitions) {
|
|
757
|
+
if (!token.startsWith("-") || token === "-")
|
|
758
|
+
return undefined;
|
|
759
|
+
for (const def of definitions) {
|
|
760
|
+
if (def.form !== "option")
|
|
761
|
+
continue;
|
|
762
|
+
for (const spelling of dashedSpellings(def)) {
|
|
763
|
+
if (token === spelling) {
|
|
764
|
+
return { def };
|
|
765
|
+
}
|
|
766
|
+
if (def.valueMetavar !== null && token.startsWith(`${spelling}=`)) {
|
|
767
|
+
return { def, inlineValue: token.slice(spelling.length + 1) };
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
return undefined;
|
|
772
|
+
}
|
|
773
|
+
/**
|
|
774
|
+
* Consume one dashed option from the front of `tokens` (mutates).
|
|
775
|
+
* Returns undefined when tokens[0] is not a known definition spelling.
|
|
776
|
+
* Does not enforce `repeatable` — production parsers use `createTypedOptionConsumer`.
|
|
777
|
+
*/
|
|
778
|
+
export function takeDashedOption(tokens, definitions) {
|
|
779
|
+
const token = tokens[0];
|
|
780
|
+
if (token === undefined)
|
|
781
|
+
return undefined;
|
|
782
|
+
const matched = matchDashedOption(token, definitions);
|
|
783
|
+
if (matched === undefined)
|
|
784
|
+
return undefined;
|
|
785
|
+
tokens.shift();
|
|
786
|
+
if (matched.def.valueMetavar === null) {
|
|
787
|
+
return { def: matched.def, value: undefined };
|
|
788
|
+
}
|
|
789
|
+
if (matched.inlineValue !== undefined) {
|
|
790
|
+
return { def: matched.def, value: matched.inlineValue };
|
|
791
|
+
}
|
|
792
|
+
return { def: matched.def, value: tokens.shift() };
|
|
793
|
+
}
|
|
794
|
+
/**
|
|
795
|
+
* Match a bare token against form:"positional" definitions (canonical or alias).
|
|
796
|
+
* Does not record occurrence — use `createTypedOptionConsumer().takePositional`.
|
|
797
|
+
*/
|
|
798
|
+
export function matchPositionalOption(token, definitions) {
|
|
799
|
+
for (const def of definitions) {
|
|
800
|
+
if (def.form !== "positional")
|
|
801
|
+
continue;
|
|
802
|
+
if (def.canonical === token || def.aliases.includes(token)) {
|
|
803
|
+
return def;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
return undefined;
|
|
807
|
+
}
|
|
808
|
+
/**
|
|
809
|
+
* Build the sole production consumer for one owner definition list.
|
|
810
|
+
* Every public argv parser shares this path — no parallel phase/repeatable/required logic.
|
|
811
|
+
*/
|
|
812
|
+
export function createTypedOptionConsumer(definitions) {
|
|
813
|
+
const counts = new Map();
|
|
814
|
+
const note = (def) => {
|
|
815
|
+
const next = (counts.get(def.id) ?? 0) + 1;
|
|
816
|
+
counts.set(def.id, next);
|
|
817
|
+
if (next > 1 && !def.repeatable) {
|
|
818
|
+
throw new CliUsageError(`${def.canonical} cannot be repeated`);
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
return {
|
|
822
|
+
takeDashed(tokens) {
|
|
823
|
+
const taken = takeDashedOption(tokens, definitions);
|
|
824
|
+
if (taken === undefined)
|
|
825
|
+
return undefined;
|
|
826
|
+
note(taken.def);
|
|
827
|
+
return taken;
|
|
828
|
+
},
|
|
829
|
+
takePositional(token) {
|
|
830
|
+
const def = matchPositionalOption(token, definitions);
|
|
831
|
+
if (def === undefined)
|
|
832
|
+
return undefined;
|
|
833
|
+
note(def);
|
|
834
|
+
return def;
|
|
835
|
+
},
|
|
836
|
+
consumeLeadingPhase(positional) {
|
|
837
|
+
const phaseDef = definitions.find((def) => def.id === "phase" && def.form === "positional");
|
|
838
|
+
const defaultPhase = phaseDef?.defaultValue === "plan" || phaseDef?.defaultValue === "apply"
|
|
839
|
+
? phaseDef.defaultValue
|
|
840
|
+
: "apply";
|
|
841
|
+
if (phaseDef === undefined || positional.length === 0) {
|
|
842
|
+
return defaultPhase;
|
|
843
|
+
}
|
|
844
|
+
const token = positional[0];
|
|
845
|
+
// Aliases carry single-token spellings; canonical may be a joint label (plan|apply).
|
|
846
|
+
if (!phaseDef.aliases.includes(token) && phaseDef.canonical !== token) {
|
|
847
|
+
return defaultPhase;
|
|
848
|
+
}
|
|
849
|
+
positional.shift();
|
|
850
|
+
note(phaseDef);
|
|
851
|
+
if (token !== "plan" && token !== "apply") {
|
|
852
|
+
throw new CliUsageError(`invalid phase token: ${token}`);
|
|
853
|
+
}
|
|
854
|
+
return token;
|
|
855
|
+
},
|
|
856
|
+
count(id) {
|
|
857
|
+
return counts.get(id) ?? 0;
|
|
858
|
+
},
|
|
859
|
+
assertRequired() {
|
|
860
|
+
for (const def of definitions) {
|
|
861
|
+
if (!def.required)
|
|
862
|
+
continue;
|
|
863
|
+
if ((counts.get(def.id) ?? 0) > 0)
|
|
864
|
+
continue;
|
|
865
|
+
const suffix = def.valueMetavar === null ? "" : ` <${def.valueMetavar}>`;
|
|
866
|
+
throw new CliUsageError(`${def.owner} requires ${def.canonical}${suffix}`);
|
|
867
|
+
}
|
|
868
|
+
},
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
export function projectOwnerOptions(owner) {
|
|
872
|
+
return optionsForOwner(owner).map((def) => ({
|
|
873
|
+
id: def.id,
|
|
874
|
+
owner: def.owner,
|
|
875
|
+
canonical: def.canonical,
|
|
876
|
+
aliases: def.aliases,
|
|
877
|
+
valueMetavar: def.valueMetavar,
|
|
878
|
+
required: def.required,
|
|
879
|
+
repeatable: def.repeatable,
|
|
880
|
+
...(def.defaultValue === undefined ? {} : { defaultValue: def.defaultValue }),
|
|
881
|
+
form: def.form,
|
|
882
|
+
...(def.phases === undefined ? {} : { phases: def.phases }),
|
|
883
|
+
...(def.modes === undefined ? {} : { modes: def.modes }),
|
|
884
|
+
...(def.requiredInModes === undefined
|
|
885
|
+
? {}
|
|
886
|
+
: { requiredInModes: def.requiredInModes }),
|
|
887
|
+
...(def.exclusiveWith === undefined
|
|
888
|
+
? {}
|
|
889
|
+
: { exclusiveWith: def.exclusiveWith }),
|
|
890
|
+
...(def.maxCountByMode === undefined
|
|
891
|
+
? {}
|
|
892
|
+
: { maxCountByMode: def.maxCountByMode }),
|
|
893
|
+
...(def.selectsMode === undefined ? {} : { selectsMode: def.selectsMode }),
|
|
894
|
+
description: def.description,
|
|
895
|
+
}));
|
|
896
|
+
}
|
|
897
|
+
/** All rejected spelling tokens flattened (for leakage scans). */
|
|
898
|
+
export function allRejectedSpellingTokens() {
|
|
899
|
+
const out = [];
|
|
900
|
+
for (const entry of REJECTED_PUBLIC_SPELLINGS) {
|
|
901
|
+
out.push(...entry.spellings);
|
|
902
|
+
}
|
|
903
|
+
return out;
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Top-level public help short note for Navigator automatic attendance (#639:
|
|
907
|
+
* attendance is orthogonal to callability — the direct command face lives on
|
|
908
|
+
* the role list like every other role).
|
|
909
|
+
*/
|
|
910
|
+
export const PUBLIC_NAVIGATOR_HELP_NOTE = "Navigator also attends automatically on every run; that sidecar is unchanged by direct `ak-role navigator` calls.";
|
|
911
|
+
const TOP_LEVEL_HELP = {
|
|
912
|
+
command: "top",
|
|
913
|
+
summary: "public role CLI",
|
|
914
|
+
usage: [
|
|
915
|
+
"ak-role <command> [options]",
|
|
916
|
+
"ak-role help <command>",
|
|
917
|
+
],
|
|
918
|
+
examples: [
|
|
919
|
+
'ak-role judge --attach ./plan.md "Review this plan."',
|
|
920
|
+
'ak-role coder plan "Propose the first implementation plan."',
|
|
921
|
+
],
|
|
922
|
+
};
|
|
923
|
+
const ROLE_COMMAND_HELP = {
|
|
924
|
+
judge: {
|
|
925
|
+
command: "judge",
|
|
926
|
+
summary: "Adjudicate the supplied materials; infers its own burden.",
|
|
927
|
+
usage: ["ak-role judge [options] [instruction]"],
|
|
928
|
+
examples: [
|
|
929
|
+
'ak-role judge --attach ./plan.md "Review this plan."',
|
|
930
|
+
'ak-role judge --attach ./findings.md --attach ./adr.md "Adjudicate every finding."',
|
|
931
|
+
],
|
|
932
|
+
},
|
|
933
|
+
countersign: {
|
|
934
|
+
command: "countersign",
|
|
935
|
+
summary: "Ticket-court review before work starts; five questions, 署/封驳/上呈.",
|
|
936
|
+
usage: ["ak-role countersign [options] [instruction]"],
|
|
937
|
+
examples: [
|
|
938
|
+
'ak-role countersign --attach ./ticket.md "裁:本票 #582 是否足以开工。"',
|
|
939
|
+
'ak-role countersign --attach ./plan.md --attach ./adr.md "裁:方案五问。"',
|
|
940
|
+
],
|
|
941
|
+
},
|
|
942
|
+
"gleaner-left": {
|
|
943
|
+
command: "gleaner-left",
|
|
944
|
+
summary: "Unanchored pre-merge memorials; findings only, no bounce. Instruction may be empty; callers must not pass directional instruction.",
|
|
945
|
+
usage: ["ak-role gleaner-left --base <revision> [options] [instruction]"],
|
|
946
|
+
examples: [
|
|
947
|
+
"ak-role gleaner-left --base main",
|
|
948
|
+
"ak-role gleaner-left --base origin/main --project ./worktree",
|
|
949
|
+
],
|
|
950
|
+
},
|
|
951
|
+
coder: {
|
|
952
|
+
command: "coder",
|
|
953
|
+
summary: "First implementation; phase defaults to apply.",
|
|
954
|
+
usage: ["ak-role coder [plan|apply] [options] <instruction>"],
|
|
955
|
+
examples: [
|
|
956
|
+
'ak-role coder plan "Propose the first implementation plan."',
|
|
957
|
+
'ak-role coder apply --attach ./plan.md "Implement the approved slice."',
|
|
958
|
+
],
|
|
959
|
+
},
|
|
960
|
+
fixer: {
|
|
961
|
+
command: "fixer",
|
|
962
|
+
summary: "Repair the assigned findings; phase defaults to apply.",
|
|
963
|
+
usage: ["ak-role fixer [plan|apply] [options] <instruction>"],
|
|
964
|
+
examples: [
|
|
965
|
+
'ak-role fixer --attach ./findings.md --prerequisites ./prereqs.json "Repair the findings."',
|
|
966
|
+
'ak-role fixer plan --attach ./findings.md "Propose the repair plan."',
|
|
967
|
+
],
|
|
968
|
+
},
|
|
969
|
+
reviewer: {
|
|
970
|
+
command: "reviewer",
|
|
971
|
+
summary: "Fixed-target two-axis review (Standards + Spec).",
|
|
972
|
+
usage: ["ak-role reviewer --base <revision> [options] <instruction>"],
|
|
973
|
+
examples: [
|
|
974
|
+
'ak-role reviewer --base main "Review the branch against the governing issue and repository authority."',
|
|
975
|
+
],
|
|
976
|
+
},
|
|
977
|
+
collector: {
|
|
978
|
+
command: "collector",
|
|
979
|
+
summary: "Collect GitHub PR review evidence.",
|
|
980
|
+
usage: ["ak-role collector --pr <number> [options] [instruction]"],
|
|
981
|
+
examples: [
|
|
982
|
+
"ak-role collector --pr 42 --repo owner/repository",
|
|
983
|
+
"ak-role collector --pr 42 --request-manifest ./requests.json",
|
|
984
|
+
],
|
|
985
|
+
},
|
|
986
|
+
doctor: {
|
|
987
|
+
command: "doctor",
|
|
988
|
+
summary: "Diagnose one retained case.",
|
|
989
|
+
usage: ["ak-role doctor --issue <number> [options] [instruction]"],
|
|
990
|
+
examples: [
|
|
991
|
+
'ak-role doctor --issue 115 "Diagnose this retained case."',
|
|
992
|
+
],
|
|
993
|
+
},
|
|
994
|
+
merger: {
|
|
995
|
+
command: "merger",
|
|
996
|
+
summary: "Resolve one ordinary merge already in conflict.",
|
|
997
|
+
usage: ["ak-role merger [options] <instruction>"],
|
|
998
|
+
examples: [
|
|
999
|
+
'ak-role merger --project /path/to/worktree "Reconcile the active merge."',
|
|
1000
|
+
],
|
|
1001
|
+
},
|
|
1002
|
+
inspector: {
|
|
1003
|
+
command: "inspector",
|
|
1004
|
+
summary: "Direct Inspector (察院) complexity and test-quality check; pass or bounce.",
|
|
1005
|
+
usage: ["ak-role inspector [options] [instruction]"],
|
|
1006
|
+
examples: [
|
|
1007
|
+
'ak-role inspector --attach ./change.patch "Review this material."',
|
|
1008
|
+
],
|
|
1009
|
+
},
|
|
1010
|
+
gatekeeper: {
|
|
1011
|
+
command: "gatekeeper",
|
|
1012
|
+
summary: "Direct Gatekeeper (门下省) review: dispatch an officer or pass.",
|
|
1013
|
+
usage: ["ak-role gatekeeper [options] [instruction]"],
|
|
1014
|
+
examples: [
|
|
1015
|
+
'ak-role gatekeeper --attach ./submission.json "审:这批材料该谁审?"',
|
|
1016
|
+
],
|
|
1017
|
+
},
|
|
1018
|
+
navigator: {
|
|
1019
|
+
command: "navigator",
|
|
1020
|
+
summary: "Direct Navigator (游奕使) route advice: ordered next-role candidates.",
|
|
1021
|
+
usage: ["ak-role navigator [options] [instruction]"],
|
|
1022
|
+
examples: [
|
|
1023
|
+
'ak-role navigator "刚完成 coder apply 收敛,下一步?"',
|
|
1024
|
+
],
|
|
1025
|
+
},
|
|
1026
|
+
auditor: {
|
|
1027
|
+
command: "auditor",
|
|
1028
|
+
summary: "Direct Auditor (审刑院) compliance audit: pass, revise, or escalate.",
|
|
1029
|
+
usage: ["ak-role auditor --subject <judge|doctor> --source-run <runId@role|path> [options] [instruction]"],
|
|
1030
|
+
examples: [
|
|
1031
|
+
'ak-role auditor --subject judge --source-run 01abc…@judge --attach ./dossier "审:本 run 是否合规。"',
|
|
1032
|
+
'ak-role auditor --subject doctor --source-run 01abc…@doctor "审:太医候选是否合规。"',
|
|
1033
|
+
],
|
|
1034
|
+
},
|
|
1035
|
+
"evidence-child": {
|
|
1036
|
+
command: "evidence-child",
|
|
1037
|
+
summary: "Direct evidence-child fact gathering; submit one report.",
|
|
1038
|
+
usage: ["ak-role evidence-child [options] [instruction]"],
|
|
1039
|
+
examples: [
|
|
1040
|
+
'ak-role evidence-child "查:本工作树 diff 与票面是否一致。"',
|
|
1041
|
+
],
|
|
1042
|
+
},
|
|
1043
|
+
diarist: {
|
|
1044
|
+
command: "diarist",
|
|
1045
|
+
summary: "Direct Diarist (起居郎) pass: gather and organize this case's decision basis into its 起居录.",
|
|
1046
|
+
usage: ["ak-role diarist [options] [instruction]"],
|
|
1047
|
+
examples: [
|
|
1048
|
+
'ak-role diarist "整理 #708 的本案依据。"',
|
|
1049
|
+
'ak-role diarist --attach ./design.md "补录本轮设计修订。"',
|
|
1050
|
+
],
|
|
1051
|
+
},
|
|
1052
|
+
notary: {
|
|
1053
|
+
command: "notary",
|
|
1054
|
+
summary: "Direct Notary document check (quote fidelity + ticket alignment); zero prompt/attachment.",
|
|
1055
|
+
usage: ["ak-role notary --source-run <runId@role|path> [options]"],
|
|
1056
|
+
examples: [
|
|
1057
|
+
"ak-role notary --source-run 01a034f1-75bf-71a6-bcf5-d1299145b1a5@judge",
|
|
1058
|
+
"ak-role notary --source-run 01a034f1-75bf-71a6-bcf5-d1299145b1a5@countersign",
|
|
1059
|
+
],
|
|
1060
|
+
},
|
|
1061
|
+
analyst: {
|
|
1062
|
+
command: "analyst",
|
|
1063
|
+
summary: "Deterministic analysis seat (issue / sweep / cohort).",
|
|
1064
|
+
usage: [
|
|
1065
|
+
"ak-role analyst [--ticket <N>]",
|
|
1066
|
+
"ak-role analyst [sweep] --attach <path>",
|
|
1067
|
+
"ak-role analyst --cohort --group-a-label <L> --group-a-issues <N|book:N[,...]> --group-b-label <L> --group-b-issues <N|book:N[,...]>",
|
|
1068
|
+
],
|
|
1069
|
+
examples: [
|
|
1070
|
+
"ak-role analyst",
|
|
1071
|
+
"ak-role analyst --ticket 125",
|
|
1072
|
+
"ak-role analyst sweep --attach ./sweep.json",
|
|
1073
|
+
],
|
|
1074
|
+
},
|
|
1075
|
+
};
|
|
1076
|
+
const SUPPORT_COMMAND_HELP = {
|
|
1077
|
+
roles: {
|
|
1078
|
+
command: "roles",
|
|
1079
|
+
summary: "List effective seats and models.",
|
|
1080
|
+
usage: ["ak-role roles"],
|
|
1081
|
+
examples: ["ak-role roles"],
|
|
1082
|
+
},
|
|
1083
|
+
config: {
|
|
1084
|
+
command: "config",
|
|
1085
|
+
summary: "Persistent seat model, labor-engine, host, and auto-resume defaults.",
|
|
1086
|
+
usage: [
|
|
1087
|
+
"ak-role config set <seat> <provider/model[:thinking]> [<seat> <spec> ...]",
|
|
1088
|
+
"ak-role config unset <gatekeeper|inspector|notary>",
|
|
1089
|
+
"ak-role config set-engine <seat> <name>",
|
|
1090
|
+
"ak-role config unset-engine <seat>",
|
|
1091
|
+
"ak-role config set-host <seat> <name>",
|
|
1092
|
+
"ak-role config unset-host <seat>",
|
|
1093
|
+
"ak-role config set-auto-resume-limit <N>",
|
|
1094
|
+
],
|
|
1095
|
+
examples: [
|
|
1096
|
+
"ak-role config set judge openai-codex/gpt-5.6-sol:high",
|
|
1097
|
+
"ak-role config unset gatekeeper",
|
|
1098
|
+
"ak-role config set-engine judge opus",
|
|
1099
|
+
"ak-role config set-host judge grok-build",
|
|
1100
|
+
"ak-role config set-auto-resume-limit 3",
|
|
1101
|
+
],
|
|
1102
|
+
},
|
|
1103
|
+
help: {
|
|
1104
|
+
command: "help",
|
|
1105
|
+
summary: "Show public CLI help.",
|
|
1106
|
+
usage: ["ak-role help [command]", "ak-role --help"],
|
|
1107
|
+
examples: ["ak-role help coder", "ak-role help judge"],
|
|
1108
|
+
},
|
|
1109
|
+
resume: {
|
|
1110
|
+
command: "resume",
|
|
1111
|
+
summary: "Resume a role run under the live seat table (model/host/engine); session principal must still exist. [message] applies only to seats that accept caller instruction; Notary/符宝郎 must omit message and derives evidence from the existing source-run/dossier binding.",
|
|
1112
|
+
usage: ["ak-role resume <runId> [message]"],
|
|
1113
|
+
examples: [
|
|
1114
|
+
"ak-role resume 01abc…",
|
|
1115
|
+
"ak-role resume 01abc… \"owner ruling\"",
|
|
1116
|
+
"ak-role --host grok-build resume 01abc…",
|
|
1117
|
+
"ak-role --engine agy resume 01abc…",
|
|
1118
|
+
],
|
|
1119
|
+
},
|
|
1120
|
+
new: {
|
|
1121
|
+
command: "new",
|
|
1122
|
+
summary: "Explicit fresh summons: same role options as a direct call, but always mint a new run (skip same-ticket auto-resume). Caller chooses new vs resume; no overflow judgment.",
|
|
1123
|
+
usage: ["ak-role new <role> [options] …"],
|
|
1124
|
+
examples: [
|
|
1125
|
+
'ak-role new countersign --attach ./ticket.md "裁:本票 #708 是否足以开工。"',
|
|
1126
|
+
"ak-role new notary --source-run 01abc…@countersign",
|
|
1127
|
+
"ak-role --host grok-build new inspector --attach ./change.patch",
|
|
1128
|
+
],
|
|
1129
|
+
},
|
|
1130
|
+
};
|
|
1131
|
+
/**
|
|
1132
|
+
* Sole public help-copy owner keyed by command/topic id.
|
|
1133
|
+
* Role topics share identity with PUBLIC_ROLE_OPTION_OWNERS; options still
|
|
1134
|
+
* project from PUBLIC_OPTION_TABLE. Support topics cover non-option commands.
|
|
1135
|
+
*/
|
|
1136
|
+
export const PUBLIC_COMMAND_HELP = {
|
|
1137
|
+
top: TOP_LEVEL_HELP,
|
|
1138
|
+
...ROLE_COMMAND_HELP,
|
|
1139
|
+
...SUPPORT_COMMAND_HELP,
|
|
1140
|
+
};
|
|
1141
|
+
/** Structured projector — identity + presence; free text is presentation. */
|
|
1142
|
+
export function projectCommandHelp(topic) {
|
|
1143
|
+
if (!(topic in PUBLIC_COMMAND_HELP))
|
|
1144
|
+
return undefined;
|
|
1145
|
+
const facts = PUBLIC_COMMAND_HELP[topic];
|
|
1146
|
+
return {
|
|
1147
|
+
command: facts.command,
|
|
1148
|
+
summary: facts.summary,
|
|
1149
|
+
usage: [...facts.usage],
|
|
1150
|
+
examples: [...facts.examples],
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* Human OPTIONS lines from the sole option table.
|
|
1155
|
+
* Layout is presentation; structured identity stays on projectOwnerOptions.
|
|
1156
|
+
*/
|
|
1157
|
+
export function renderHumanOwnerOptionLines(owner, locale = "en") {
|
|
1158
|
+
const lines = [];
|
|
1159
|
+
for (const opt of projectOwnerOptions(owner)) {
|
|
1160
|
+
let spelling = opt.canonical;
|
|
1161
|
+
if (opt.valueMetavar !== null) {
|
|
1162
|
+
spelling = `${spelling} <${opt.valueMetavar}>`;
|
|
1163
|
+
}
|
|
1164
|
+
if (opt.aliases.length > 0) {
|
|
1165
|
+
// Prefer single-token aliases in the spelling hint (plan/apply, -h).
|
|
1166
|
+
// #412/397-F2: inside aliases.length > 0 the empty branch is dead — join directly.
|
|
1167
|
+
const aliasHint = opt.aliases.join(", ");
|
|
1168
|
+
if (opt.form === "positional") {
|
|
1169
|
+
spelling = opt.aliases.join("|");
|
|
1170
|
+
}
|
|
1171
|
+
else {
|
|
1172
|
+
spelling = `${spelling} (${aliasHint})`;
|
|
1173
|
+
}
|
|
1174
|
+
}
|
|
1175
|
+
const tags = [];
|
|
1176
|
+
if (opt.required)
|
|
1177
|
+
tags.push("required");
|
|
1178
|
+
if (opt.requiredInModes !== undefined) {
|
|
1179
|
+
tags.push(`required:${opt.requiredInModes.join("|")}`);
|
|
1180
|
+
}
|
|
1181
|
+
if (opt.repeatable)
|
|
1182
|
+
tags.push("repeatable");
|
|
1183
|
+
if (opt.defaultValue !== undefined)
|
|
1184
|
+
tags.push(`default=${opt.defaultValue}`);
|
|
1185
|
+
if (opt.form === "positional")
|
|
1186
|
+
tags.push("positional");
|
|
1187
|
+
const tagText = tags.length === 0 ? "" : ` [${tags.join(", ")}]`;
|
|
1188
|
+
const desc = locale === "zh" ? opt.description.zh : opt.description.en;
|
|
1189
|
+
lines.push(` ${spelling}${tagText} ${desc}`);
|
|
1190
|
+
}
|
|
1191
|
+
return lines;
|
|
1192
|
+
}
|