@akagilnc/pi-workflow-roles 0.1.2033 → 0.1.2043

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.
@@ -15444,6 +15444,724 @@ var init_uuidv7 = __esm({
15444
15444
  }
15445
15445
  });
15446
15446
 
15447
+ // src/public-cli/option-definitions.ts
15448
+ function resolveTaishiMode(presentOptionIds) {
15449
+ const selected = /* @__PURE__ */ new Set();
15450
+ for (const def of optionsForOwner("taishi")) {
15451
+ if (def.selectsMode === void 0) continue;
15452
+ if (presentOptionIds.has(def.id)) selected.add(def.selectsMode);
15453
+ }
15454
+ if (selected.size === 0) return TAISHI_DEFAULT_MODE;
15455
+ if (selected.has("cohort")) return "cohort";
15456
+ if (selected.has("model-groups")) return "model-groups";
15457
+ if (selected.has("sweep")) return "sweep";
15458
+ if (selected.has("issue")) return "issue";
15459
+ return TAISHI_DEFAULT_MODE;
15460
+ }
15461
+ function evaluateTaishiModeOptionContract(mode, counts) {
15462
+ const definitions = optionsForOwner("taishi");
15463
+ const byId = new Map(definitions.map((def) => [def.id, def]));
15464
+ for (const def of definitions) {
15465
+ const count2 = counts.get(def.id) ?? 0;
15466
+ if (count2 === 0 || def.exclusiveWith === void 0) continue;
15467
+ for (const otherId of def.exclusiveWith) {
15468
+ if ((counts.get(otherId) ?? 0) === 0) continue;
15469
+ const other = byId.get(otherId);
15470
+ return {
15471
+ ok: false,
15472
+ message: `taishi accepts only one of ${def.canonical} / ${other?.canonical ?? otherId}`
15473
+ };
15474
+ }
15475
+ }
15476
+ for (const def of definitions) {
15477
+ const count2 = counts.get(def.id) ?? 0;
15478
+ if (count2 === 0) continue;
15479
+ if (def.modes !== void 0 && !def.modes.includes(mode)) {
15480
+ if (mode === "sweep") {
15481
+ return {
15482
+ ok: false,
15483
+ message: `taishi sweep --attach cannot combine with ${def.canonical}`
15484
+ };
15485
+ }
15486
+ return {
15487
+ ok: false,
15488
+ message: `taishi ${mode} does not accept ${def.canonical}`
15489
+ };
15490
+ }
15491
+ const max = def.maxCountByMode?.[mode];
15492
+ if (max !== void 0 && count2 > max) {
15493
+ return {
15494
+ ok: false,
15495
+ message: max === 1 ? `taishi ${mode} accepts at most one ${def.canonical}` : `taishi ${mode} accepts at most ${max} ${def.canonical}`
15496
+ };
15497
+ }
15498
+ }
15499
+ const missingRequired = [];
15500
+ for (const def of definitions) {
15501
+ if (def.requiredInModes === void 0) continue;
15502
+ if (!def.requiredInModes.includes(mode)) continue;
15503
+ if ((counts.get(def.id) ?? 0) === 0) missingRequired.push(def);
15504
+ }
15505
+ if (missingRequired.length > 0) {
15506
+ if (mode === "cohort") {
15507
+ return {
15508
+ ok: false,
15509
+ message: "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]"
15510
+ };
15511
+ }
15512
+ if (mode === "model-groups") {
15513
+ return {
15514
+ ok: false,
15515
+ message: "usage: ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]"
15516
+ };
15517
+ }
15518
+ return {
15519
+ ok: false,
15520
+ message: `usage: ak-role taishi ${mode} requires ${missingRequired.map((def) => def.canonical).join(" ")}`
15521
+ };
15522
+ }
15523
+ for (const rule of TAISHI_REQUIRE_ANY_OF) {
15524
+ if (rule.mode !== mode) continue;
15525
+ const hit = rule.optionIds.some((id) => (counts.get(id) ?? 0) > 0);
15526
+ if (hit) continue;
15527
+ if (mode === "issue") {
15528
+ return {
15529
+ ok: false,
15530
+ message: "usage: ak-role taishi ((--ticket <N> | --project-root <P>) | [sweep] --attach <sweep.json> | --cohort ... | --model-groups ...)"
15531
+ };
15532
+ }
15533
+ const flags = rule.optionIds.map((id) => byId.get(id)?.canonical ?? id).join(" | ");
15534
+ return {
15535
+ ok: false,
15536
+ message: `usage: ak-role taishi ${mode} requires one of ${flags}`
15537
+ };
15538
+ }
15539
+ return { ok: true };
15540
+ }
15541
+ function bindOwner(owner, semantics) {
15542
+ return { ...semantics, owner };
15543
+ }
15544
+ function optionsForOwner(owner) {
15545
+ return PUBLIC_OPTION_TABLE[owner];
15546
+ }
15547
+ function dashedSpellings(def) {
15548
+ if (def.form !== "option") return [];
15549
+ return [def.canonical, ...def.aliases];
15550
+ }
15551
+ function matchDashedOption(token, definitions) {
15552
+ if (!token.startsWith("-") || token === "-") return void 0;
15553
+ for (const def of definitions) {
15554
+ if (def.form !== "option") continue;
15555
+ for (const spelling of dashedSpellings(def)) {
15556
+ if (token === spelling) {
15557
+ return { def };
15558
+ }
15559
+ if (def.valueMetavar !== null && token.startsWith(`${spelling}=`)) {
15560
+ return { def, inlineValue: token.slice(spelling.length + 1) };
15561
+ }
15562
+ }
15563
+ }
15564
+ return void 0;
15565
+ }
15566
+ function takeDashedOption(tokens, definitions) {
15567
+ const token = tokens[0];
15568
+ if (token === void 0) return void 0;
15569
+ const matched = matchDashedOption(token, definitions);
15570
+ if (matched === void 0) return void 0;
15571
+ tokens.shift();
15572
+ if (matched.def.valueMetavar === null) {
15573
+ return { def: matched.def, value: void 0 };
15574
+ }
15575
+ if (matched.inlineValue !== void 0) {
15576
+ return { def: matched.def, value: matched.inlineValue };
15577
+ }
15578
+ return { def: matched.def, value: tokens.shift() };
15579
+ }
15580
+ function matchPositionalOption(token, definitions) {
15581
+ for (const def of definitions) {
15582
+ if (def.form !== "positional") continue;
15583
+ if (def.canonical === token || def.aliases.includes(token)) {
15584
+ return def;
15585
+ }
15586
+ }
15587
+ return void 0;
15588
+ }
15589
+ function createTypedOptionConsumer(definitions) {
15590
+ const counts = /* @__PURE__ */ new Map();
15591
+ const note = (def) => {
15592
+ const next = (counts.get(def.id) ?? 0) + 1;
15593
+ counts.set(def.id, next);
15594
+ if (next > 1 && !def.repeatable) {
15595
+ throw new CliUsageError(`${def.canonical} cannot be repeated`);
15596
+ }
15597
+ };
15598
+ return {
15599
+ takeDashed(tokens) {
15600
+ const taken = takeDashedOption(tokens, definitions);
15601
+ if (taken === void 0) return void 0;
15602
+ note(taken.def);
15603
+ return taken;
15604
+ },
15605
+ takePositional(token) {
15606
+ const def = matchPositionalOption(token, definitions);
15607
+ if (def === void 0) return void 0;
15608
+ note(def);
15609
+ return def;
15610
+ },
15611
+ consumeLeadingPhase(positional) {
15612
+ const phaseDef = definitions.find(
15613
+ (def) => def.id === "phase" && def.form === "positional"
15614
+ );
15615
+ const defaultPhase = phaseDef?.defaultValue === "plan" || phaseDef?.defaultValue === "apply" ? phaseDef.defaultValue : "apply";
15616
+ if (phaseDef === void 0 || positional.length === 0) {
15617
+ return defaultPhase;
15618
+ }
15619
+ const token = positional[0];
15620
+ if (!phaseDef.aliases.includes(token) && phaseDef.canonical !== token) {
15621
+ return defaultPhase;
15622
+ }
15623
+ positional.shift();
15624
+ note(phaseDef);
15625
+ if (token !== "plan" && token !== "apply") {
15626
+ throw new CliUsageError(`invalid phase token: ${token}`);
15627
+ }
15628
+ return token;
15629
+ },
15630
+ count(id) {
15631
+ return counts.get(id) ?? 0;
15632
+ },
15633
+ assertRequired() {
15634
+ for (const def of definitions) {
15635
+ if (!def.required) continue;
15636
+ if ((counts.get(def.id) ?? 0) > 0) continue;
15637
+ const suffix = def.valueMetavar === null ? "" : ` <${def.valueMetavar}>`;
15638
+ throw new CliUsageError(
15639
+ `${def.owner} requires ${def.canonical}${suffix}`
15640
+ );
15641
+ }
15642
+ }
15643
+ };
15644
+ }
15645
+ function projectOwnerOptions(owner) {
15646
+ return optionsForOwner(owner).map((def) => ({
15647
+ id: def.id,
15648
+ owner: def.owner,
15649
+ canonical: def.canonical,
15650
+ aliases: def.aliases,
15651
+ valueMetavar: def.valueMetavar,
15652
+ required: def.required,
15653
+ repeatable: def.repeatable,
15654
+ ...def.defaultValue === void 0 ? {} : { defaultValue: def.defaultValue },
15655
+ form: def.form,
15656
+ ...def.phases === void 0 ? {} : { phases: def.phases },
15657
+ ...def.modes === void 0 ? {} : { modes: def.modes },
15658
+ ...def.requiredInModes === void 0 ? {} : { requiredInModes: def.requiredInModes },
15659
+ ...def.exclusiveWith === void 0 ? {} : { exclusiveWith: def.exclusiveWith },
15660
+ ...def.maxCountByMode === void 0 ? {} : { maxCountByMode: def.maxCountByMode },
15661
+ ...def.selectsMode === void 0 ? {} : { selectsMode: def.selectsMode },
15662
+ description: def.description
15663
+ }));
15664
+ }
15665
+ function renderOwnerOptionHelpLines(owner, locale2 = "en") {
15666
+ const lines = [];
15667
+ for (const opt of projectOwnerOptions(owner)) {
15668
+ const aliasText = opt.aliases.length === 0 ? "-" : opt.aliases.join(",");
15669
+ const metavar = opt.valueMetavar ?? "-";
15670
+ const required = opt.required ? "required" : "optional";
15671
+ const repeatable = opt.repeatable ? "repeatable" : "single";
15672
+ const form = opt.form;
15673
+ const phases = opt.phases === void 0 ? "-" : opt.phases.join("|");
15674
+ const modes = opt.modes === void 0 ? "-" : opt.modes.join("|");
15675
+ const requiredInModes = opt.requiredInModes === void 0 ? "-" : opt.requiredInModes.join("|");
15676
+ const exclusiveWith = opt.exclusiveWith === void 0 ? "-" : opt.exclusiveWith.join("|");
15677
+ const maxCount = opt.maxCountByMode === void 0 ? "-" : Object.entries(opt.maxCountByMode).map(([mode, n]) => `${mode}:${n}`).join(",");
15678
+ const defaultValue = opt.defaultValue ?? "-";
15679
+ const desc = locale2 === "zh" ? opt.description.zh : opt.description.en;
15680
+ lines.push(
15681
+ [
15682
+ "option",
15683
+ opt.id,
15684
+ opt.canonical,
15685
+ `aliases=${aliasText}`,
15686
+ `metavar=${metavar}`,
15687
+ required,
15688
+ repeatable,
15689
+ `form=${form}`,
15690
+ `phases=${phases}`,
15691
+ `modes=${modes}`,
15692
+ `requiredInModes=${requiredInModes}`,
15693
+ `exclusiveWith=${exclusiveWith}`,
15694
+ `maxCountByMode=${maxCount}`,
15695
+ `default=${defaultValue}`,
15696
+ desc
15697
+ ].join(" ")
15698
+ );
15699
+ }
15700
+ return lines;
15701
+ }
15702
+ var TAISHI_REQUIRE_ANY_OF, TAISHI_DEFAULT_MODE, REJECTED_PUBLIC_SPELLINGS, GLOBAL_OPTIONS, SHARED_PROJECT_SEMANTICS, SHARED_ATTACH_SEMANTICS, JUDGE_OPTIONS, CODER_OPTIONS, FIXER_OPTIONS, REVIEWER_OPTIONS, COLLECTOR_OPTIONS, DOCTOR_OPTIONS, MERGER_OPTIONS, TAISHI_OPTIONS, PUBLIC_OPTION_TABLE;
15703
+ var init_option_definitions = __esm({
15704
+ "src/public-cli/option-definitions.ts"() {
15705
+ "use strict";
15706
+ init_cli_errors();
15707
+ TAISHI_REQUIRE_ANY_OF = [
15708
+ { mode: "issue", optionIds: ["ticket", "project-root"] }
15709
+ ];
15710
+ TAISHI_DEFAULT_MODE = "issue";
15711
+ REJECTED_PUBLIC_SPELLINGS = [
15712
+ {
15713
+ owner: "judge",
15714
+ spellings: ["--burden", "--ak-judge-burden", "--judge-burden"],
15715
+ reason: {
15716
+ en: "Judge infers its own burden; no public burden selector.",
15717
+ zh: "\u5927\u7406\u5BFA\u81EA\u884C\u63A8\u65AD\u4E3E\u8BC1\u8D23\u4EFB\uFF0C\u4E0D\u63A5\u53D7\u516C\u5F00 burden \u65D7\u6807\u3002"
15718
+ }
15719
+ },
15720
+ {
15721
+ owner: "merger",
15722
+ spellings: ["--ak-merger-input"],
15723
+ reason: {
15724
+ en: "Merger input packet is assembled internally; not a public flag.",
15725
+ zh: "\u6821\u4E66\u90CE input packet \u7531\u5185\u90E8\u88C5\u914D\uFF0C\u4E0D\u662F\u516C\u5F00\u65D7\u6807\u3002"
15726
+ }
15727
+ }
15728
+ ];
15729
+ GLOBAL_OPTIONS = [
15730
+ {
15731
+ id: "model",
15732
+ owner: "global",
15733
+ canonical: "--model",
15734
+ aliases: [],
15735
+ valueMetavar: "provider/model",
15736
+ required: false,
15737
+ repeatable: false,
15738
+ form: "option",
15739
+ description: {
15740
+ en: "Override the effective seat model for this invocation (before or after the command).",
15741
+ zh: "\u8986\u76D6\u672C\u8C03\u7528\u6709\u6548\u5E2D\u4F4D\u6A21\u578B\uFF08\u53EF\u7F6E\u4E8E\u5B50\u547D\u4EE4\u524D\u6216\u540E\uFF09\u3002"
15742
+ }
15743
+ },
15744
+ {
15745
+ id: "thinking",
15746
+ owner: "global",
15747
+ canonical: "--thinking",
15748
+ aliases: [],
15749
+ valueMetavar: "level",
15750
+ required: false,
15751
+ repeatable: false,
15752
+ form: "option",
15753
+ description: {
15754
+ en: "Override thinking level: off|minimal|low|medium|high|xhigh|max.",
15755
+ zh: "\u8986\u76D6 thinking \u6863\u4F4D\uFF1Aoff|minimal|low|medium|high|xhigh|max\u3002"
15756
+ }
15757
+ },
15758
+ {
15759
+ id: "help",
15760
+ owner: "global",
15761
+ canonical: "--help",
15762
+ aliases: ["-h"],
15763
+ valueMetavar: null,
15764
+ required: false,
15765
+ repeatable: false,
15766
+ form: "option",
15767
+ description: {
15768
+ en: "Show public CLI help and exit.",
15769
+ zh: "\u663E\u793A\u516C\u5F00 CLI \u5E2E\u52A9\u5E76\u9000\u51FA\u3002"
15770
+ }
15771
+ }
15772
+ ];
15773
+ SHARED_PROJECT_SEMANTICS = {
15774
+ id: "project",
15775
+ canonical: "--project",
15776
+ aliases: [],
15777
+ valueMetavar: "path",
15778
+ required: false,
15779
+ repeatable: false,
15780
+ form: "option",
15781
+ description: {
15782
+ en: "Project root for ledger identity (defaults to process cwd).",
15783
+ zh: "\u5377\u5B97\u8EAB\u4EFD\u7528\u7684\u9879\u76EE\u6839\uFF08\u9ED8\u8BA4\u8FDB\u7A0B cwd\uFF09\u3002"
15784
+ }
15785
+ };
15786
+ SHARED_ATTACH_SEMANTICS = {
15787
+ id: "attach",
15788
+ canonical: "--attach",
15789
+ aliases: [],
15790
+ valueMetavar: "path",
15791
+ required: false,
15792
+ repeatable: true,
15793
+ form: "option",
15794
+ description: {
15795
+ en: "Attach a regular file; frozen at admission (repeatable).",
15796
+ zh: "\u9644\u52A0\u666E\u901A\u6587\u4EF6\uFF1B\u53D7\u7406\u5373\u51BB\u7ED3\uFF08\u53EF\u91CD\u590D\uFF09\u3002"
15797
+ }
15798
+ };
15799
+ JUDGE_OPTIONS = [
15800
+ bindOwner("judge", SHARED_PROJECT_SEMANTICS),
15801
+ bindOwner("judge", SHARED_ATTACH_SEMANTICS)
15802
+ ];
15803
+ CODER_OPTIONS = [
15804
+ {
15805
+ id: "phase",
15806
+ owner: "coder",
15807
+ canonical: "plan|apply",
15808
+ aliases: ["plan", "apply"],
15809
+ valueMetavar: null,
15810
+ required: false,
15811
+ repeatable: false,
15812
+ defaultValue: "apply",
15813
+ form: "positional",
15814
+ phases: ["plan", "apply"],
15815
+ description: {
15816
+ en: "Optional phase token before the instruction; defaults to apply.",
15817
+ zh: "\u6307\u4EE4\u524D\u53EF\u9009 phase \u8BCD\u5143\uFF1B\u9ED8\u8BA4 apply\u3002"
15818
+ }
15819
+ },
15820
+ bindOwner("coder", SHARED_PROJECT_SEMANTICS),
15821
+ bindOwner("coder", SHARED_ATTACH_SEMANTICS)
15822
+ ];
15823
+ FIXER_OPTIONS = [
15824
+ {
15825
+ id: "phase",
15826
+ owner: "fixer",
15827
+ canonical: "plan|apply",
15828
+ aliases: ["plan", "apply"],
15829
+ valueMetavar: null,
15830
+ required: false,
15831
+ repeatable: false,
15832
+ defaultValue: "apply",
15833
+ form: "positional",
15834
+ phases: ["plan", "apply"],
15835
+ description: {
15836
+ en: "Optional phase token before the instruction; defaults to apply.",
15837
+ zh: "\u6307\u4EE4\u524D\u53EF\u9009 phase \u8BCD\u5143\uFF1B\u9ED8\u8BA4 apply\u3002"
15838
+ }
15839
+ },
15840
+ bindOwner("fixer", SHARED_PROJECT_SEMANTICS),
15841
+ bindOwner("fixer", SHARED_ATTACH_SEMANTICS),
15842
+ {
15843
+ id: "prerequisites",
15844
+ owner: "fixer",
15845
+ canonical: "--prerequisites",
15846
+ aliases: [],
15847
+ valueMetavar: "path",
15848
+ required: false,
15849
+ repeatable: false,
15850
+ form: "option",
15851
+ description: {
15852
+ en: "JSON array of {id, requirement} prerequisite objects.",
15853
+ zh: "{id, requirement} \u524D\u7F6E\u6761\u4EF6 JSON \u6570\u7EC4\u8DEF\u5F84\u3002"
15854
+ }
15855
+ }
15856
+ ];
15857
+ REVIEWER_OPTIONS = [
15858
+ bindOwner("reviewer", SHARED_PROJECT_SEMANTICS),
15859
+ // Reviewer deliberately has no --attach face (gathers its own evidence).
15860
+ {
15861
+ id: "base",
15862
+ owner: "reviewer",
15863
+ canonical: "--base",
15864
+ aliases: [],
15865
+ valueMetavar: "revision",
15866
+ required: true,
15867
+ repeatable: false,
15868
+ form: "option",
15869
+ description: {
15870
+ en: "Required fixed-point revision for the pinned review target.",
15871
+ zh: "\u5FC5\u586B\uFF1B\u9489\u4F4F\u5BA1\u67E5\u76EE\u6807\u7684 fixed-point revision\u3002"
15872
+ }
15873
+ },
15874
+ {
15875
+ id: "authority-ref",
15876
+ owner: "reviewer",
15877
+ canonical: "--authority-ref",
15878
+ aliases: [],
15879
+ valueMetavar: "ref",
15880
+ required: false,
15881
+ repeatable: true,
15882
+ form: "option",
15883
+ description: {
15884
+ en: "Durable authority reference/URL (repeatable; refs only, not inline prose).",
15885
+ zh: "\u6301\u4E45 authority \u5F15\u7528/URL\uFF08\u53EF\u91CD\u590D\uFF1B\u4EC5 ref\uFF0C\u975E\u5185\u8054\u6563\u6587\uFF09\u3002"
15886
+ }
15887
+ }
15888
+ ];
15889
+ COLLECTOR_OPTIONS = [
15890
+ bindOwner("collector", SHARED_PROJECT_SEMANTICS),
15891
+ bindOwner("collector", SHARED_ATTACH_SEMANTICS),
15892
+ {
15893
+ id: "pr",
15894
+ owner: "collector",
15895
+ canonical: "--pr",
15896
+ aliases: [],
15897
+ valueMetavar: "number",
15898
+ required: true,
15899
+ repeatable: false,
15900
+ form: "option",
15901
+ description: {
15902
+ en: "Required positive GitHub pull request number.",
15903
+ zh: "\u5FC5\u586B\uFF1B\u6B63\u6574\u6570 GitHub PR \u53F7\u3002"
15904
+ }
15905
+ },
15906
+ {
15907
+ id: "repo",
15908
+ owner: "collector",
15909
+ canonical: "--repo",
15910
+ aliases: [],
15911
+ valueMetavar: "owner/repo",
15912
+ required: false,
15913
+ repeatable: false,
15914
+ form: "option",
15915
+ description: {
15916
+ en: "GitHub owner/repo override (defaults from origin when github.com).",
15917
+ zh: "GitHub owner/repo \u8986\u76D6\uFF08\u9ED8\u8BA4\u53D6 github.com origin\uFF09\u3002"
15918
+ }
15919
+ },
15920
+ {
15921
+ id: "request-manifest",
15922
+ owner: "collector",
15923
+ canonical: "--request-manifest",
15924
+ aliases: [],
15925
+ valueMetavar: "path",
15926
+ required: false,
15927
+ repeatable: false,
15928
+ form: "option",
15929
+ description: {
15930
+ en: "Optional request manifest JSON path ({requests:[{id,body}]}).",
15931
+ zh: "\u53EF\u9009 request manifest JSON \u8DEF\u5F84\uFF08{requests:[{id,body}]}\uFF09\u3002"
15932
+ }
15933
+ }
15934
+ ];
15935
+ DOCTOR_OPTIONS = [
15936
+ bindOwner("doctor", SHARED_PROJECT_SEMANTICS),
15937
+ bindOwner("doctor", SHARED_ATTACH_SEMANTICS),
15938
+ {
15939
+ id: "issue",
15940
+ owner: "doctor",
15941
+ canonical: "--issue",
15942
+ aliases: [],
15943
+ valueMetavar: "number",
15944
+ required: true,
15945
+ repeatable: false,
15946
+ form: "option",
15947
+ description: {
15948
+ en: "Required positive issue number for the retained case.",
15949
+ zh: "\u5FC5\u586B\uFF1B\u7559\u5B58\u75C5\u4F8B\u7684\u6B63\u6574\u6570 issue \u53F7\u3002"
15950
+ }
15951
+ },
15952
+ {
15953
+ id: "runs",
15954
+ owner: "doctor",
15955
+ canonical: "--runs",
15956
+ aliases: [],
15957
+ valueMetavar: "path",
15958
+ required: false,
15959
+ repeatable: false,
15960
+ form: "option",
15961
+ description: {
15962
+ en: "Optional project-relative .ak-roles/books/<book>/issues/<n>/runs override matching --issue.",
15963
+ zh: "\u53EF\u9009\u9879\u76EE\u76F8\u5BF9 .ak-roles/books/<book>/issues/<n>/runs \u8986\u76D6\uFF0C\u4E14\u987B\u5339\u914D --issue\u3002"
15964
+ }
15965
+ }
15966
+ ];
15967
+ MERGER_OPTIONS = [
15968
+ // Merger project face differs: requires an in-progress ordinary merge root.
15969
+ {
15970
+ id: "project",
15971
+ owner: "merger",
15972
+ canonical: "--project",
15973
+ aliases: [],
15974
+ valueMetavar: "path",
15975
+ required: false,
15976
+ repeatable: false,
15977
+ form: "option",
15978
+ description: {
15979
+ en: "Project root with one ordinary in-progress merge (defaults to cwd).",
15980
+ zh: "\u5DF2\u6709\u8FDB\u884C\u4E2D ordinary merge \u7684\u9879\u76EE\u6839\uFF08\u9ED8\u8BA4 cwd\uFF09\u3002"
15981
+ }
15982
+ },
15983
+ bindOwner("merger", SHARED_ATTACH_SEMANTICS)
15984
+ ];
15985
+ TAISHI_OPTIONS = [
15986
+ {
15987
+ id: "sweep",
15988
+ owner: "taishi",
15989
+ canonical: "sweep",
15990
+ aliases: [],
15991
+ valueMetavar: null,
15992
+ required: false,
15993
+ repeatable: false,
15994
+ form: "positional",
15995
+ modes: ["sweep"],
15996
+ selectsMode: "sweep",
15997
+ description: {
15998
+ en: "Optional sweep mode token (at most once; no other positionals).",
15999
+ zh: "\u53EF\u9009 sweep \u6A21\u5F0F\u8BCD\u5143\uFF08\u81F3\u591A\u4E00\u6B21\uFF1B\u4E0D\u5F97\u5939\u5E26\u5176\u4ED6 positional\uFF09\u3002"
16000
+ }
16001
+ },
16002
+ {
16003
+ id: "project-root",
16004
+ owner: "taishi",
16005
+ canonical: "--project-root",
16006
+ aliases: [],
16007
+ valueMetavar: "path",
16008
+ required: false,
16009
+ repeatable: true,
16010
+ form: "option",
16011
+ modes: ["issue", "model-groups"],
16012
+ requiredInModes: ["model-groups"],
16013
+ maxCountByMode: { issue: 1 },
16014
+ description: {
16015
+ en: "Project-root scope key. Issue: at most one (with --ticket at least one of the two). Model-groups: one or more required.",
16016
+ zh: "projectRoot \u8303\u56F4\u952E\u3002issue\uFF1A\u81F3\u591A\u4E00\u4E2A\uFF08\u4E0E --ticket \u81F3\u5C11\u5C45\u5176\u4E00\uFF09\u3002model-groups\uFF1A\u4E00\u4E2A\u6216\u591A\u4E2A\u4E14\u5FC5\u586B\u3002"
16017
+ }
16018
+ },
16019
+ {
16020
+ id: "ticket",
16021
+ owner: "taishi",
16022
+ canonical: "--ticket",
16023
+ aliases: [],
16024
+ valueMetavar: "number",
16025
+ required: false,
16026
+ repeatable: false,
16027
+ form: "option",
16028
+ modes: ["issue"],
16029
+ description: {
16030
+ en: "Ticket/issue number for issue mode (with --project-root at least one of the two).",
16031
+ zh: "issue \u6A21\u5F0F\u7684\u7968\u53F7\uFF08\u4E0E --project-root \u81F3\u5C11\u5C45\u5176\u4E00\uFF09\u3002"
16032
+ }
16033
+ },
16034
+ {
16035
+ id: "attach",
16036
+ owner: "taishi",
16037
+ canonical: "--attach",
16038
+ aliases: [],
16039
+ valueMetavar: "path",
16040
+ required: false,
16041
+ repeatable: true,
16042
+ form: "option",
16043
+ modes: ["sweep"],
16044
+ selectsMode: "sweep",
16045
+ requiredInModes: ["sweep"],
16046
+ maxCountByMode: { sweep: 1 },
16047
+ description: {
16048
+ en: "Sweep-mode attachment path; required exactly once in sweep; payload is the attachment body.",
16049
+ zh: "sweep \u6A21\u5F0F\u9644\u4EF6\u8DEF\u5F84\uFF1Bsweep \u5FC5\u586B\u4E14\u6070\u4E00\u6B21\uFF1B\u8F7D\u8377\u4E3A\u9644\u4EF6\u6B63\u6587\u3002"
16050
+ }
16051
+ },
16052
+ {
16053
+ id: "cohort",
16054
+ owner: "taishi",
16055
+ canonical: "--cohort",
16056
+ aliases: [],
16057
+ valueMetavar: null,
16058
+ required: false,
16059
+ repeatable: false,
16060
+ form: "option",
16061
+ modes: ["cohort"],
16062
+ exclusiveWith: ["model-groups"],
16063
+ selectsMode: "cohort",
16064
+ description: {
16065
+ en: "Select cohort mode (mutually exclusive with --model-groups).",
16066
+ zh: "\u9009\u62E9 cohort \u6A21\u5F0F\uFF08\u4E0E --model-groups \u4E92\u65A5\uFF09\u3002"
16067
+ }
16068
+ },
16069
+ {
16070
+ id: "model-groups",
16071
+ owner: "taishi",
16072
+ canonical: "--model-groups",
16073
+ aliases: [],
16074
+ valueMetavar: null,
16075
+ required: false,
16076
+ repeatable: false,
16077
+ form: "option",
16078
+ modes: ["model-groups"],
16079
+ exclusiveWith: ["cohort"],
16080
+ selectsMode: "model-groups",
16081
+ description: {
16082
+ en: "Select model-groups mode (mutually exclusive with --cohort).",
16083
+ zh: "\u9009\u62E9 model-groups \u6A21\u5F0F\uFF08\u4E0E --cohort \u4E92\u65A5\uFF09\u3002"
16084
+ }
16085
+ },
16086
+ {
16087
+ id: "group-a-label",
16088
+ owner: "taishi",
16089
+ canonical: "--group-a-label",
16090
+ aliases: [],
16091
+ valueMetavar: "label",
16092
+ required: false,
16093
+ repeatable: false,
16094
+ form: "option",
16095
+ modes: ["cohort"],
16096
+ requiredInModes: ["cohort"],
16097
+ description: {
16098
+ en: "Cohort group A label (required in cohort mode).",
16099
+ zh: "cohort A \u7EC4\u6807\u7B7E\uFF08cohort \u6A21\u5F0F\u5FC5\u586B\uFF09\u3002"
16100
+ }
16101
+ },
16102
+ {
16103
+ id: "group-a-issues",
16104
+ owner: "taishi",
16105
+ canonical: "--group-a-issues",
16106
+ aliases: [],
16107
+ valueMetavar: "N[,N...]",
16108
+ required: false,
16109
+ repeatable: false,
16110
+ form: "option",
16111
+ modes: ["cohort"],
16112
+ requiredInModes: ["cohort"],
16113
+ description: {
16114
+ en: "Cohort group A comma-separated positive issue numbers (required in cohort mode).",
16115
+ zh: "cohort A \u7EC4\u9017\u53F7\u5206\u9694\u6B63\u6574\u6570 issue \u5217\u8868\uFF08cohort \u6A21\u5F0F\u5FC5\u586B\uFF09\u3002"
16116
+ }
16117
+ },
16118
+ {
16119
+ id: "group-b-label",
16120
+ owner: "taishi",
16121
+ canonical: "--group-b-label",
16122
+ aliases: [],
16123
+ valueMetavar: "label",
16124
+ required: false,
16125
+ repeatable: false,
16126
+ form: "option",
16127
+ modes: ["cohort"],
16128
+ requiredInModes: ["cohort"],
16129
+ description: {
16130
+ en: "Cohort group B label (required in cohort mode).",
16131
+ zh: "cohort B \u7EC4\u6807\u7B7E\uFF08cohort \u6A21\u5F0F\u5FC5\u586B\uFF09\u3002"
16132
+ }
16133
+ },
16134
+ {
16135
+ id: "group-b-issues",
16136
+ owner: "taishi",
16137
+ canonical: "--group-b-issues",
16138
+ aliases: [],
16139
+ valueMetavar: "N[,N...]",
16140
+ required: false,
16141
+ repeatable: false,
16142
+ form: "option",
16143
+ modes: ["cohort"],
16144
+ requiredInModes: ["cohort"],
16145
+ description: {
16146
+ en: "Cohort group B comma-separated positive issue numbers (required in cohort mode).",
16147
+ zh: "cohort B \u7EC4\u9017\u53F7\u5206\u9694\u6B63\u6574\u6570 issue \u5217\u8868\uFF08cohort \u6A21\u5F0F\u5FC5\u586B\uFF09\u3002"
16148
+ }
16149
+ }
16150
+ ];
16151
+ PUBLIC_OPTION_TABLE = {
16152
+ global: GLOBAL_OPTIONS,
16153
+ judge: JUDGE_OPTIONS,
16154
+ coder: CODER_OPTIONS,
16155
+ fixer: FIXER_OPTIONS,
16156
+ reviewer: REVIEWER_OPTIONS,
16157
+ collector: COLLECTOR_OPTIONS,
16158
+ doctor: DOCTOR_OPTIONS,
16159
+ merger: MERGER_OPTIONS,
16160
+ taishi: TAISHI_OPTIONS
16161
+ };
16162
+ }
16163
+ });
16164
+
15447
16165
  // src/public-cli/invocation.ts
15448
16166
  import { execFileSync as execFileSync2 } from "node:child_process";
15449
16167
  import {
@@ -15554,6 +16272,18 @@ function requireOptionPath(flag, value) {
15554
16272
  }
15555
16273
  return value;
15556
16274
  }
16275
+ function isRejectedPublicSpelling(owner, token) {
16276
+ for (const entry of REJECTED_PUBLIC_SPELLINGS) {
16277
+ if (entry.owner !== owner) continue;
16278
+ for (const spelling of entry.spellings) {
16279
+ if (token === spelling || token.startsWith(`${spelling}=`)) return true;
16280
+ }
16281
+ }
16282
+ return false;
16283
+ }
16284
+ function roleOptions(owner) {
16285
+ return optionsForOwner(owner);
16286
+ }
15557
16287
  function requireAuthorityRef(value) {
15558
16288
  if (value === void 0 || value.trim() === "") {
15559
16289
  throw new CliUsageError("--authority-ref requires a nonempty durable reference");
@@ -15570,31 +16300,28 @@ function parseJudgeArgv(args) {
15570
16300
  let project;
15571
16301
  const positional = [];
15572
16302
  const tokens = [...args];
16303
+ const definitions = roleOptions("judge");
16304
+ const options = createTypedOptionConsumer(definitions);
15573
16305
  while (tokens.length > 0) {
15574
- const token = tokens.shift();
15575
- if (token === "--") {
16306
+ if (tokens[0] === "--") {
16307
+ tokens.shift();
15576
16308
  positional.push(...tokens);
15577
16309
  break;
15578
16310
  }
15579
- if (token === "--attach") {
15580
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
15581
- continue;
15582
- }
15583
- if (token.startsWith("--attach=")) {
15584
- attachmentPaths.push(
15585
- requireOptionPath("--attach", token.slice("--attach=".length))
15586
- );
15587
- continue;
15588
- }
15589
- if (token === "--project") {
15590
- project = requireOptionPath("--project", tokens.shift());
15591
- continue;
15592
- }
15593
- if (token.startsWith("--project=")) {
15594
- project = requireOptionPath("--project", token.slice("--project=".length));
15595
- continue;
16311
+ const taken = options.takeDashed(tokens);
16312
+ if (taken !== void 0) {
16313
+ if (taken.def.id === "attach") {
16314
+ attachmentPaths.push(requireOptionPath(taken.def.canonical, taken.value));
16315
+ continue;
16316
+ }
16317
+ if (taken.def.id === "project") {
16318
+ project = requireOptionPath(taken.def.canonical, taken.value);
16319
+ continue;
16320
+ }
16321
+ throw new CliUsageError(`unknown judge option: ${taken.def.canonical}`);
15596
16322
  }
15597
- if (token === "--burden" || token.startsWith("--burden=") || token === "--ak-judge-burden" || token.startsWith("--ak-judge-burden=") || token === "--judge-burden" || token.startsWith("--judge-burden=")) {
16323
+ const token = tokens.shift();
16324
+ if (isRejectedPublicSpelling("judge", token)) {
15598
16325
  throw new CliUsageError(
15599
16326
  "judge does not accept a public burden selector; Judge infers its own burden"
15600
16327
  );
@@ -15604,6 +16331,7 @@ function parseJudgeArgv(args) {
15604
16331
  }
15605
16332
  positional.push(token);
15606
16333
  }
16334
+ options.assertRequired();
15607
16335
  return {
15608
16336
  instruction: positional.join(" "),
15609
16337
  attachmentPaths,
@@ -15615,39 +16343,34 @@ function parseCoderArgv(args) {
15615
16343
  let project;
15616
16344
  const positional = [];
15617
16345
  const tokens = [...args];
16346
+ const definitions = roleOptions("coder");
16347
+ const options = createTypedOptionConsumer(definitions);
15618
16348
  while (tokens.length > 0) {
15619
- const token = tokens.shift();
15620
- if (token === "--") {
16349
+ if (tokens[0] === "--") {
16350
+ tokens.shift();
15621
16351
  positional.push(...tokens);
15622
16352
  break;
15623
16353
  }
15624
- if (token === "--attach") {
15625
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
15626
- continue;
15627
- }
15628
- if (token.startsWith("--attach=")) {
15629
- attachmentPaths.push(
15630
- requireOptionPath("--attach", token.slice("--attach=".length))
15631
- );
15632
- continue;
15633
- }
15634
- if (token === "--project") {
15635
- project = requireOptionPath("--project", tokens.shift());
15636
- continue;
15637
- }
15638
- if (token.startsWith("--project=")) {
15639
- project = requireOptionPath("--project", token.slice("--project=".length));
15640
- continue;
16354
+ const taken = options.takeDashed(tokens);
16355
+ if (taken !== void 0) {
16356
+ if (taken.def.id === "attach") {
16357
+ attachmentPaths.push(requireOptionPath(taken.def.canonical, taken.value));
16358
+ continue;
16359
+ }
16360
+ if (taken.def.id === "project") {
16361
+ project = requireOptionPath(taken.def.canonical, taken.value);
16362
+ continue;
16363
+ }
16364
+ throw new CliUsageError(`unknown coder option: ${taken.def.canonical}`);
15641
16365
  }
16366
+ const token = tokens.shift();
15642
16367
  if (token.startsWith("-") && token !== "-") {
15643
16368
  throw new CliUsageError(`unknown coder option: ${token}`);
15644
16369
  }
15645
16370
  positional.push(token);
15646
16371
  }
15647
- let phase = "apply";
15648
- if (positional[0] === "plan" || positional[0] === "apply") {
15649
- phase = positional.shift();
15650
- }
16372
+ const phase = options.consumeLeadingPhase(positional);
16373
+ options.assertRequired();
15651
16374
  return {
15652
16375
  phase,
15653
16376
  instruction: positional.join(" "),
@@ -15661,50 +16384,38 @@ function parseFixerArgv(args) {
15661
16384
  let prerequisitesPath;
15662
16385
  const positional = [];
15663
16386
  const tokens = [...args];
16387
+ const definitions = roleOptions("fixer");
16388
+ const options = createTypedOptionConsumer(definitions);
15664
16389
  while (tokens.length > 0) {
15665
- const token = tokens.shift();
15666
- if (token === "--") {
16390
+ if (tokens[0] === "--") {
16391
+ tokens.shift();
15667
16392
  positional.push(...tokens);
15668
16393
  break;
15669
16394
  }
15670
- if (token === "--attach") {
15671
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
15672
- continue;
15673
- }
15674
- if (token.startsWith("--attach=")) {
15675
- attachmentPaths.push(
15676
- requireOptionPath("--attach", token.slice("--attach=".length))
15677
- );
15678
- continue;
15679
- }
15680
- if (token === "--project") {
15681
- project = requireOptionPath("--project", tokens.shift());
15682
- continue;
15683
- }
15684
- if (token.startsWith("--project=")) {
15685
- project = requireOptionPath("--project", token.slice("--project=".length));
15686
- continue;
15687
- }
15688
- if (token === "--prerequisites") {
15689
- prerequisitesPath = requireOptionPath("--prerequisites", tokens.shift());
15690
- continue;
15691
- }
15692
- if (token.startsWith("--prerequisites=")) {
15693
- prerequisitesPath = requireOptionPath(
15694
- "--prerequisites",
15695
- token.slice("--prerequisites=".length)
15696
- );
15697
- continue;
16395
+ const taken = options.takeDashed(tokens);
16396
+ if (taken !== void 0) {
16397
+ if (taken.def.id === "attach") {
16398
+ attachmentPaths.push(requireOptionPath(taken.def.canonical, taken.value));
16399
+ continue;
16400
+ }
16401
+ if (taken.def.id === "project") {
16402
+ project = requireOptionPath(taken.def.canonical, taken.value);
16403
+ continue;
16404
+ }
16405
+ if (taken.def.id === "prerequisites") {
16406
+ prerequisitesPath = requireOptionPath(taken.def.canonical, taken.value);
16407
+ continue;
16408
+ }
16409
+ throw new CliUsageError(`unknown fixer option: ${taken.def.canonical}`);
15698
16410
  }
16411
+ const token = tokens.shift();
15699
16412
  if (token.startsWith("-") && token !== "-") {
15700
16413
  throw new CliUsageError(`unknown fixer option: ${token}`);
15701
16414
  }
15702
16415
  positional.push(token);
15703
16416
  }
15704
- let phase = "apply";
15705
- if (positional[0] === "plan" || positional[0] === "apply") {
15706
- phase = positional.shift();
15707
- }
16417
+ const phase = options.consumeLeadingPhase(positional);
16418
+ options.assertRequired();
15708
16419
  return {
15709
16420
  phase,
15710
16421
  instruction: positional.join(" "),
@@ -16052,57 +16763,53 @@ function parseCollectorArgv(args) {
16052
16763
  let requestManifestPath;
16053
16764
  const positional = [];
16054
16765
  const tokens = [...args];
16766
+ const definitions = roleOptions("collector");
16767
+ const options = createTypedOptionConsumer(definitions);
16055
16768
  while (tokens.length > 0) {
16056
- const token = tokens.shift();
16057
- if (token === "--") {
16769
+ if (tokens[0] === "--") {
16770
+ tokens.shift();
16058
16771
  positional.push(...tokens);
16059
16772
  break;
16060
16773
  }
16061
- if (token === "--attach") {
16062
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
16063
- continue;
16064
- }
16065
- if (token.startsWith("--attach=")) {
16066
- attachmentPaths.push(requireOptionPath("--attach", token.slice(9)));
16067
- continue;
16068
- }
16069
- if (token === "--project") {
16070
- project = requireOptionPath("--project", tokens.shift());
16071
- continue;
16072
- }
16073
- if (token.startsWith("--project=")) {
16074
- project = requireOptionPath("--project", token.slice(10));
16075
- continue;
16076
- }
16077
- if (token === "--pr") {
16078
- prNumber = parsePositivePrOption(tokens.shift());
16079
- continue;
16080
- }
16081
- if (token.startsWith("--pr=")) {
16082
- prNumber = parsePositivePrOption(token.slice(5));
16083
- continue;
16084
- }
16085
- if (token === "--repo") {
16086
- repo = parseRepoOption(tokens.shift());
16087
- continue;
16088
- }
16089
- if (token.startsWith("--repo=")) {
16090
- repo = parseRepoOption(token.slice(7));
16091
- continue;
16092
- }
16093
- if (token === "--request-manifest") {
16094
- requestManifestPath = requireOptionPath("--request-manifest", tokens.shift());
16095
- continue;
16774
+ const taken = options.takeDashed(tokens);
16775
+ if (taken !== void 0) {
16776
+ if (taken.def.id === "attach") {
16777
+ attachmentPaths.push(requireOptionPath(taken.def.canonical, taken.value));
16778
+ continue;
16779
+ }
16780
+ if (taken.def.id === "project") {
16781
+ project = requireOptionPath(taken.def.canonical, taken.value);
16782
+ continue;
16783
+ }
16784
+ if (taken.def.id === "pr") {
16785
+ prNumber = parsePositivePrOption(taken.value);
16786
+ continue;
16787
+ }
16788
+ if (taken.def.id === "repo") {
16789
+ repo = parseRepoOption(taken.value);
16790
+ continue;
16791
+ }
16792
+ if (taken.def.id === "request-manifest") {
16793
+ requestManifestPath = requireOptionPath(taken.def.canonical, taken.value);
16794
+ continue;
16795
+ }
16796
+ throw new CliUsageError(`unknown collector option: ${taken.def.canonical}`);
16096
16797
  }
16097
- if (token.startsWith("--request-manifest=")) {
16098
- requestManifestPath = requireOptionPath("--request-manifest", token.slice(19));
16099
- continue;
16798
+ const token = tokens.shift();
16799
+ if (token.startsWith("-") && token !== "-") {
16800
+ throw new CliUsageError(`unknown collector option: ${token}`);
16100
16801
  }
16101
- if (token.startsWith("-") && token !== "-") throw new CliUsageError(`unknown collector option: ${token}`);
16102
16802
  positional.push(token);
16103
16803
  }
16104
- if (prNumber === void 0) throw new CliUsageError("collector requires --pr <positive-integer>");
16105
- return { prNumber, instruction: positional.join(" "), attachmentPaths, ...project === void 0 ? {} : { project }, ...repo === void 0 ? {} : { repo }, ...requestManifestPath === void 0 ? {} : { requestManifestPath } };
16804
+ options.assertRequired();
16805
+ return {
16806
+ prNumber,
16807
+ instruction: positional.join(" "),
16808
+ attachmentPaths,
16809
+ ...project === void 0 ? {} : { project },
16810
+ ...repo === void 0 ? {} : { repo },
16811
+ ...requestManifestPath === void 0 ? {} : { requestManifestPath }
16812
+ };
16106
16813
  }
16107
16814
  function resolveGitHubRemoteRepository(projectRoot) {
16108
16815
  let remoteUrl;
@@ -16284,69 +16991,47 @@ function parseDoctorArgv(args) {
16284
16991
  let runs;
16285
16992
  const positional = [];
16286
16993
  const tokens = [...args];
16994
+ const definitions = roleOptions("doctor");
16995
+ const options = createTypedOptionConsumer(definitions);
16287
16996
  while (tokens.length > 0) {
16288
- const token = tokens.shift();
16289
- if (token === "--") {
16997
+ if (tokens[0] === "--") {
16998
+ tokens.shift();
16290
16999
  positional.push(...tokens);
16291
17000
  break;
16292
17001
  }
16293
- if (token === "--issue") {
16294
- const value = tokens.shift();
16295
- if (value === void 0 || value.trim() === "") {
16296
- throw new CliUsageError("doctor --issue requires a positive integer");
17002
+ const taken = options.takeDashed(tokens);
17003
+ if (taken !== void 0) {
17004
+ if (taken.def.id === "issue") {
17005
+ if (taken.value === void 0 || taken.value.trim() === "") {
17006
+ throw new CliUsageError("doctor --issue requires a positive integer");
17007
+ }
17008
+ issueRaw = taken.value;
17009
+ continue;
16297
17010
  }
16298
- issueRaw = value;
16299
- continue;
16300
- }
16301
- if (token.startsWith("--issue=")) {
16302
- issueRaw = token.slice("--issue=".length);
16303
- if (issueRaw.trim() === "") {
16304
- throw new CliUsageError("doctor --issue requires a positive integer");
17011
+ if (taken.def.id === "runs") {
17012
+ if (taken.value === void 0 || taken.value.trim() === "") {
17013
+ throw new CliUsageError("doctor --runs requires a path");
17014
+ }
17015
+ runs = taken.value;
17016
+ continue;
16305
17017
  }
16306
- continue;
16307
- }
16308
- if (token === "--runs") {
16309
- const value = tokens.shift();
16310
- if (value === void 0 || value.trim() === "") {
16311
- throw new CliUsageError("doctor --runs requires a path");
17018
+ if (taken.def.id === "attach") {
17019
+ attachmentPaths.push(requireOptionPath(taken.def.canonical, taken.value));
17020
+ continue;
16312
17021
  }
16313
- runs = value;
16314
- continue;
16315
- }
16316
- if (token.startsWith("--runs=")) {
16317
- const value = token.slice("--runs=".length);
16318
- if (value.trim() === "") {
16319
- throw new CliUsageError("doctor --runs requires a path");
17022
+ if (taken.def.id === "project") {
17023
+ project = requireOptionPath(taken.def.canonical, taken.value);
17024
+ continue;
16320
17025
  }
16321
- runs = value;
16322
- continue;
16323
- }
16324
- if (token === "--attach") {
16325
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
16326
- continue;
16327
- }
16328
- if (token.startsWith("--attach=")) {
16329
- attachmentPaths.push(
16330
- requireOptionPath("--attach", token.slice("--attach=".length))
16331
- );
16332
- continue;
16333
- }
16334
- if (token === "--project") {
16335
- project = requireOptionPath("--project", tokens.shift());
16336
- continue;
16337
- }
16338
- if (token.startsWith("--project=")) {
16339
- project = requireOptionPath("--project", token.slice("--project=".length));
16340
- continue;
17026
+ throw new CliUsageError(`unknown doctor option: ${taken.def.canonical}`);
16341
17027
  }
17028
+ const token = tokens.shift();
16342
17029
  if (token.startsWith("-") && token !== "-") {
16343
17030
  throw new CliUsageError(`unknown doctor option: ${token}`);
16344
17031
  }
16345
17032
  positional.push(token);
16346
17033
  }
16347
- if (issueRaw === void 0) {
16348
- throw new CliUsageError("doctor requires --issue <positive-integer>");
16349
- }
17034
+ options.assertRequired();
16350
17035
  const issueNumber = parseDoctorIssueNumber(issueRaw);
16351
17036
  if (runs !== void 0 && runs.trim() === "") {
16352
17037
  throw new CliUsageError("doctor --runs requires a path");
@@ -16532,44 +17217,37 @@ function parseReviewerArgv(args) {
16532
17217
  let baseRevision;
16533
17218
  const positional = [];
16534
17219
  const tokens = [...args];
17220
+ const definitions = roleOptions("reviewer");
17221
+ const options = createTypedOptionConsumer(definitions);
16535
17222
  while (tokens.length > 0) {
16536
- const token = tokens.shift();
16537
- if (token === "--") {
17223
+ if (tokens[0] === "--") {
17224
+ tokens.shift();
16538
17225
  positional.push(...tokens);
16539
17226
  break;
16540
17227
  }
16541
- if (token === "--project") {
16542
- project = requireOptionPath("--project", tokens.shift());
16543
- continue;
16544
- }
16545
- if (token.startsWith("--project=")) {
16546
- project = requireOptionPath("--project", token.slice("--project=".length));
16547
- continue;
16548
- }
16549
- if (token === "--base") {
16550
- baseRevision = requireOptionPath("--base", tokens.shift());
16551
- continue;
16552
- }
16553
- if (token.startsWith("--base=")) {
16554
- baseRevision = requireOptionPath("--base", token.slice("--base=".length));
16555
- continue;
16556
- }
16557
- if (token === "--authority-ref") {
16558
- authorityRefs.push(requireAuthorityRef(tokens.shift()));
16559
- continue;
16560
- }
16561
- if (token.startsWith("--authority-ref=")) {
16562
- authorityRefs.push(requireAuthorityRef(token.slice("--authority-ref=".length)));
16563
- continue;
17228
+ const taken = options.takeDashed(tokens);
17229
+ if (taken !== void 0) {
17230
+ if (taken.def.id === "project") {
17231
+ project = requireOptionPath(taken.def.canonical, taken.value);
17232
+ continue;
17233
+ }
17234
+ if (taken.def.id === "base") {
17235
+ baseRevision = requireOptionPath(taken.def.canonical, taken.value);
17236
+ continue;
17237
+ }
17238
+ if (taken.def.id === "authority-ref") {
17239
+ authorityRefs.push(requireAuthorityRef(taken.value));
17240
+ continue;
17241
+ }
17242
+ throw new CliUsageError(`unknown reviewer option: ${taken.def.canonical}`);
16564
17243
  }
17244
+ const token = tokens.shift();
16565
17245
  if (token.startsWith("-") && token !== "-") {
16566
17246
  throw new CliUsageError(`unknown reviewer option: ${token}`);
16567
17247
  }
16568
17248
  positional.push(token);
16569
17249
  }
16570
- if (baseRevision === void 0) {
16571
- throw new CliUsageError("reviewer requires --base <revision>; canonical code-review requires the caller to select a fixed point");
16572
- }
17250
+ options.assertRequired();
16573
17251
  return {
16574
17252
  instruction: positional.join(" "),
16575
17253
  attachmentPaths,
@@ -16658,31 +17336,28 @@ function parseMergerArgv(args) {
16658
17336
  let project;
16659
17337
  const positional = [];
16660
17338
  const tokens = [...args];
17339
+ const definitions = roleOptions("merger");
17340
+ const options = createTypedOptionConsumer(definitions);
16661
17341
  while (tokens.length > 0) {
16662
- const token = tokens.shift();
16663
- if (token === "--") {
17342
+ if (tokens[0] === "--") {
17343
+ tokens.shift();
16664
17344
  positional.push(...tokens);
16665
17345
  break;
16666
17346
  }
16667
- if (token === "--attach") {
16668
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
16669
- continue;
16670
- }
16671
- if (token.startsWith("--attach=")) {
16672
- attachmentPaths.push(
16673
- requireOptionPath("--attach", token.slice("--attach=".length))
16674
- );
16675
- continue;
16676
- }
16677
- if (token === "--project") {
16678
- project = requireOptionPath("--project", tokens.shift());
16679
- continue;
16680
- }
16681
- if (token.startsWith("--project=")) {
16682
- project = requireOptionPath("--project", token.slice("--project=".length));
16683
- continue;
17347
+ const taken = options.takeDashed(tokens);
17348
+ if (taken !== void 0) {
17349
+ if (taken.def.id === "attach") {
17350
+ attachmentPaths.push(requireOptionPath(taken.def.canonical, taken.value));
17351
+ continue;
17352
+ }
17353
+ if (taken.def.id === "project") {
17354
+ project = requireOptionPath(taken.def.canonical, taken.value);
17355
+ continue;
17356
+ }
17357
+ throw new CliUsageError(`unknown merger option: ${taken.def.canonical}`);
16684
17358
  }
16685
- if (token === "--ak-merger-input" || token.startsWith("--ak-merger-input=") || token === "--targetObjectId" || token.startsWith("--targetObjectId=") || token === "--sourceObjectId" || token.startsWith("--sourceObjectId=") || token === "--expectedConflictPaths" || token.startsWith("--expectedConflictPaths=") || token === "--resolutionScope" || token.startsWith("--resolutionScope=")) {
17359
+ const token = tokens.shift();
17360
+ if (isRejectedPublicSpelling("merger", token) || token === "--targetObjectId" || token.startsWith("--targetObjectId=") || token === "--sourceObjectId" || token.startsWith("--sourceObjectId=") || token === "--expectedConflictPaths" || token.startsWith("--expectedConflictPaths=") || token === "--resolutionScope" || token.startsWith("--resolutionScope=")) {
16686
17361
  throw new CliUsageError(
16687
17362
  "merger does not accept public packet fields; the adapter derives the active-merge envelope"
16688
17363
  );
@@ -16692,6 +17367,7 @@ function parseMergerArgv(args) {
16692
17367
  }
16693
17368
  positional.push(token);
16694
17369
  }
17370
+ options.assertRequired();
16695
17371
  return {
16696
17372
  instruction: positional.join(" "),
16697
17373
  attachmentPaths,
@@ -16880,159 +17556,96 @@ function requireOptionValue(flag, value, what) {
16880
17556
  return value;
16881
17557
  }
16882
17558
  function parseTaishiArgv(args) {
16883
- let query = "issue";
16884
- let ticketRaw;
16885
- const projectRoots = [];
16886
- let groupALabel;
16887
- let groupAIssuesRaw;
16888
- let groupBLabel;
16889
- let groupBIssuesRaw;
16890
- let sweepToken = false;
16891
- const attachmentPaths = [];
17559
+ const valueLists = /* @__PURE__ */ new Map();
16892
17560
  const tokens = [...args];
17561
+ const definitions = roleOptions("taishi");
17562
+ const options = createTypedOptionConsumer(definitions);
17563
+ const pushValue = (id, value) => {
17564
+ const existing = valueLists.get(id);
17565
+ if (existing === void 0) valueLists.set(id, [value]);
17566
+ else existing.push(value);
17567
+ };
16893
17568
  while (tokens.length > 0) {
16894
- const token = tokens.shift();
16895
- if (token === "--") {
17569
+ if (tokens[0] === "--") {
17570
+ tokens.shift();
16896
17571
  if (tokens.length > 0) {
16897
17572
  throw new CliUsageError(`unexpected taishi argument: ${tokens[0]}`);
16898
17573
  }
16899
17574
  break;
16900
17575
  }
16901
- if (token === "--cohort") {
16902
- if (query !== "issue") {
16903
- throw new CliUsageError("taishi accepts only one of --cohort / --model-groups");
17576
+ const taken = options.takeDashed(tokens);
17577
+ if (taken !== void 0) {
17578
+ if (taken.def.valueMetavar === null) {
17579
+ pushValue(taken.def.id, "");
17580
+ continue;
16904
17581
  }
16905
- query = "cohort";
16906
- continue;
16907
- }
16908
- if (token === "--model-groups") {
16909
- if (query !== "issue") {
16910
- throw new CliUsageError("taishi accepts only one of --cohort / --model-groups");
17582
+ if (taken.def.id === "ticket") {
17583
+ if (taken.value === void 0 || taken.value.trim() === "") {
17584
+ throw new CliUsageError("taishi --ticket requires a positive integer");
17585
+ }
17586
+ pushValue("ticket", taken.value);
17587
+ continue;
16911
17588
  }
16912
- query = "model-groups";
16913
- continue;
16914
- }
16915
- if (token === "--ticket") {
16916
- const value = tokens.shift();
16917
- if (value === void 0 || value.trim() === "") {
16918
- throw new CliUsageError("taishi --ticket requires a positive integer");
17589
+ if (taken.def.id === "project-root") {
17590
+ pushValue(
17591
+ "project-root",
17592
+ requireOptionPath(taken.def.canonical, taken.value)
17593
+ );
17594
+ continue;
16919
17595
  }
16920
- ticketRaw = value;
16921
- continue;
16922
- }
16923
- if (token.startsWith("--ticket=")) {
16924
- ticketRaw = token.slice("--ticket=".length);
16925
- if (ticketRaw.trim() === "") {
16926
- throw new CliUsageError("taishi --ticket requires a positive integer");
17596
+ if (taken.def.id === "attach") {
17597
+ pushValue(
17598
+ "attach",
17599
+ requireOptionPath(taken.def.canonical, taken.value)
17600
+ );
17601
+ continue;
16927
17602
  }
16928
- continue;
16929
- }
16930
- if (token === "--project-root") {
16931
- projectRoots.push(requireOptionPath("--project-root", tokens.shift()));
16932
- continue;
16933
- }
16934
- if (token.startsWith("--project-root=")) {
16935
- projectRoots.push(
16936
- requireOptionPath("--project-root", token.slice("--project-root=".length))
16937
- );
16938
- continue;
16939
- }
16940
- if (token === "--group-a-label") {
16941
- groupALabel = requireOptionValue("--group-a-label", tokens.shift(), "a label");
16942
- continue;
16943
- }
16944
- if (token.startsWith("--group-a-label=")) {
16945
- groupALabel = requireOptionValue(
16946
- "--group-a-label",
16947
- token.slice("--group-a-label=".length),
16948
- "a label"
16949
- );
16950
- continue;
16951
- }
16952
- if (token === "--group-a-issues") {
16953
- groupAIssuesRaw = requireOptionValue(
16954
- "--group-a-issues",
16955
- tokens.shift(),
16956
- "a comma-separated positive integer list"
16957
- );
16958
- continue;
16959
- }
16960
- if (token.startsWith("--group-a-issues=")) {
16961
- groupAIssuesRaw = requireOptionValue(
16962
- "--group-a-issues",
16963
- token.slice("--group-a-issues=".length),
16964
- "a comma-separated positive integer list"
16965
- );
16966
- continue;
16967
- }
16968
- if (token === "--group-b-label") {
16969
- groupBLabel = requireOptionValue("--group-b-label", tokens.shift(), "a label");
16970
- continue;
16971
- }
16972
- if (token.startsWith("--group-b-label=")) {
16973
- groupBLabel = requireOptionValue(
16974
- "--group-b-label",
16975
- token.slice("--group-b-label=".length),
16976
- "a label"
16977
- );
16978
- continue;
16979
- }
16980
- if (token === "--group-b-issues") {
16981
- groupBIssuesRaw = requireOptionValue(
16982
- "--group-b-issues",
16983
- tokens.shift(),
16984
- "a comma-separated positive integer list"
16985
- );
16986
- continue;
16987
- }
16988
- if (token.startsWith("--group-b-issues=")) {
16989
- groupBIssuesRaw = requireOptionValue(
16990
- "--group-b-issues",
16991
- token.slice("--group-b-issues=".length),
16992
- "a comma-separated positive integer list"
16993
- );
16994
- continue;
16995
- }
16996
- if (token === "--attach") {
16997
- attachmentPaths.push(requireOptionPath("--attach", tokens.shift()));
16998
- continue;
16999
- }
17000
- if (token.startsWith("--attach=")) {
17001
- attachmentPaths.push(
17002
- requireOptionPath("--attach", token.slice("--attach=".length))
17003
- );
17004
- continue;
17603
+ if (taken.def.id === "group-a-label" || taken.def.id === "group-b-label") {
17604
+ pushValue(
17605
+ taken.def.id,
17606
+ requireOptionValue(taken.def.canonical, taken.value, "a label")
17607
+ );
17608
+ continue;
17609
+ }
17610
+ if (taken.def.id === "group-a-issues" || taken.def.id === "group-b-issues") {
17611
+ pushValue(
17612
+ taken.def.id,
17613
+ requireOptionValue(
17614
+ taken.def.canonical,
17615
+ taken.value,
17616
+ "a comma-separated positive integer list"
17617
+ )
17618
+ );
17619
+ continue;
17620
+ }
17621
+ throw new CliUsageError(`unknown taishi option: ${taken.def.canonical}`);
17005
17622
  }
17623
+ const token = tokens.shift();
17006
17624
  if (token.startsWith("-") && token !== "-") {
17007
17625
  throw new CliUsageError(`unknown taishi option: ${token}`);
17008
17626
  }
17009
- if (token === "sweep") {
17010
- if (sweepToken) {
17011
- throw new CliUsageError("unexpected taishi argument: sweep");
17012
- }
17013
- sweepToken = true;
17627
+ const positional = options.takePositional(token);
17628
+ if (positional !== void 0) {
17629
+ pushValue(positional.id, "");
17014
17630
  continue;
17015
17631
  }
17016
17632
  throw new CliUsageError(`unexpected taishi argument: ${token}`);
17017
17633
  }
17018
- const hasSweepFace = sweepToken || attachmentPaths.length > 0;
17019
- const hasCohortFlags = groupALabel !== void 0 || groupAIssuesRaw !== void 0 || groupBLabel !== void 0 || groupBIssuesRaw !== void 0;
17020
- if (query === "cohort") {
17021
- if (groupALabel === void 0 || groupAIssuesRaw === void 0 || groupBLabel === void 0 || groupBIssuesRaw === void 0) {
17022
- throw new CliUsageError(
17023
- "usage: ak-role taishi --cohort --group-a-label <L> --group-a-issues <N[,N...]> --group-b-label <L> --group-b-issues <N[,N...]>"
17024
- );
17025
- }
17026
- if (ticketRaw !== void 0 || projectRoots.length > 0) {
17027
- throw new CliUsageError(
17028
- "taishi --cohort does not accept --ticket or --project-root"
17029
- );
17030
- }
17031
- if (hasSweepFace) {
17032
- throw new CliUsageError(
17033
- "taishi --cohort does not accept sweep --attach"
17034
- );
17035
- }
17634
+ const counts = /* @__PURE__ */ new Map();
17635
+ for (const [id, values] of valueLists) {
17636
+ counts.set(id, values.length);
17637
+ }
17638
+ options.assertRequired();
17639
+ const mode = resolveTaishiMode(new Set(counts.keys()));
17640
+ const verdict = evaluateTaishiModeOptionContract(mode, counts);
17641
+ if (!verdict.ok) {
17642
+ throw new CliUsageError(verdict.message);
17643
+ }
17644
+ if (mode === "cohort") {
17645
+ const groupALabel = valueLists.get("group-a-label")[0];
17646
+ const groupAIssuesRaw = valueLists.get("group-a-issues")[0];
17647
+ const groupBLabel = valueLists.get("group-b-label")[0];
17648
+ const groupBIssuesRaw = valueLists.get("group-b-issues")[0];
17036
17649
  return {
17037
17650
  query: "cohort",
17038
17651
  groups: [
@@ -17047,53 +17660,20 @@ function parseTaishiArgv(args) {
17047
17660
  ]
17048
17661
  };
17049
17662
  }
17050
- if (query === "model-groups") {
17051
- if (projectRoots.length === 0) {
17052
- throw new CliUsageError(
17053
- "usage: ak-role taishi --model-groups --project-root <P> [--project-root <P> ...]"
17054
- );
17055
- }
17056
- if (ticketRaw !== void 0) {
17057
- throw new CliUsageError("taishi --model-groups does not accept --ticket");
17058
- }
17059
- if (hasCohortFlags) {
17060
- throw new CliUsageError("taishi --model-groups does not accept cohort group flags");
17061
- }
17062
- if (hasSweepFace) {
17063
- throw new CliUsageError(
17064
- "taishi --model-groups does not accept sweep --attach"
17065
- );
17066
- }
17663
+ if (mode === "model-groups") {
17067
17664
  return {
17068
17665
  query: "model-groups",
17069
- projectRoots
17666
+ projectRoots: valueLists.get("project-root") ?? []
17070
17667
  };
17071
17668
  }
17072
- if (hasCohortFlags) {
17073
- throw new CliUsageError("taishi issue query does not accept cohort group flags");
17074
- }
17075
- if (hasSweepFace) {
17076
- if (ticketRaw !== void 0 || projectRoots.length > 0) {
17077
- throw new CliUsageError(
17078
- "taishi sweep --attach cannot combine with --ticket or --project-root"
17079
- );
17080
- }
17669
+ if (mode === "sweep") {
17081
17670
  return {
17082
17671
  query: "sweep",
17083
- attachmentPaths
17672
+ attachmentPaths: valueLists.get("attach") ?? []
17084
17673
  };
17085
17674
  }
17086
- if (projectRoots.length > 1) {
17087
- throw new CliUsageError(
17088
- "taishi issue query accepts at most one --project-root (use --model-groups for many)"
17089
- );
17090
- }
17091
- const projectRoot = projectRoots[0];
17092
- if (ticketRaw === void 0 && projectRoot === void 0) {
17093
- throw new CliUsageError(
17094
- "usage: ak-role taishi ((--ticket <N> | --project-root <P>) | [sweep] --attach <sweep.json> | --cohort ... | --model-groups ...)"
17095
- );
17096
- }
17675
+ const ticketRaw = valueLists.get("ticket")?.[0];
17676
+ const projectRoot = valueLists.get("project-root")?.[0];
17097
17677
  return {
17098
17678
  query: "issue",
17099
17679
  ...ticketRaw === void 0 ? {} : { ticket: parseTaishiTicketNumber(ticketRaw) },
@@ -17116,6 +17696,7 @@ var init_invocation = __esm({
17116
17696
  init_sha256();
17117
17697
  init_uuidv7();
17118
17698
  init_cli_errors();
17699
+ init_option_definitions();
17119
17700
  MergerEnvelopeDerivationError = class extends Error {
17120
17701
  code = "merger-envelope-derivation";
17121
17702
  /** Typed cause for #107 classifyPostAdmissionFailure (isTypedActivationError). */
@@ -26129,47 +26710,37 @@ var init_taishi_run = __esm({
26129
26710
  var cli_exports = {};
26130
26711
  __export(cli_exports, {
26131
26712
  CliUsageError: () => CliUsageError,
26713
+ PUBLIC_GLOBAL_OPTIONS: () => PUBLIC_GLOBAL_OPTIONS,
26132
26714
  PUBLIC_ROLE_ARGV: () => PUBLIC_ROLE_ARGV,
26133
26715
  buildExplicitInternalActivationArgs: () => buildExplicitInternalActivationArgs,
26134
26716
  helpDocument: () => helpDocument,
26717
+ helpDocumentForCommand: () => helpDocumentForCommand,
26135
26718
  resolveInternalRoleEntrypoint: () => resolveInternalRoleEntrypoint,
26136
26719
  runAkRole: () => runAkRole
26137
26720
  });
26138
26721
  import { realpath as realpath5 } from "node:fs/promises";
26139
26722
  import { homedir as homedir3 } from "node:os";
26140
26723
  import { join as join23 } from "node:path";
26141
- function takePublicGlobalFlag(argv, index) {
26142
- const token = argv[index];
26143
- if (token === void 0) return void 0;
26144
- if (token === "--help" || token === "-h") {
26145
- return { flag: "help", consume: 1 };
26146
- }
26147
- if (token === "--model") {
26148
- const value = argv[index + 1];
26149
- if (value === void 0) {
26150
- return { flag: "model", consume: 1, value: void 0 };
26151
- }
26152
- return { flag: "model", consume: 2, value };
26153
- }
26154
- if (token.startsWith("--model=")) {
26724
+ function takePublicGlobalFlag(argv, index, options) {
26725
+ const tokens = argv.slice(index);
26726
+ const taken = options.takeDashed(tokens);
26727
+ if (taken === void 0) return void 0;
26728
+ const consumed = argv.length - index - tokens.length;
26729
+ if (taken.def.id === "help") {
26730
+ return { flag: "help", consume: consumed };
26731
+ }
26732
+ if (taken.def.id === "model") {
26155
26733
  return {
26156
26734
  flag: "model",
26157
- consume: 1,
26158
- value: token.slice("--model=".length)
26735
+ consume: consumed,
26736
+ value: taken.value
26159
26737
  };
26160
26738
  }
26161
- if (token === "--thinking") {
26162
- const raw = argv[index + 1];
26163
- if (raw === void 0) {
26164
- return { flag: "thinking", consume: 1, raw: void 0 };
26165
- }
26166
- return { flag: "thinking", consume: 2, raw };
26167
- }
26168
- if (token.startsWith("--thinking=")) {
26739
+ if (taken.def.id === "thinking") {
26169
26740
  return {
26170
26741
  flag: "thinking",
26171
- consume: 1,
26172
- raw: token.slice("--thinking=".length)
26742
+ consume: consumed,
26743
+ raw: taken.value
26173
26744
  };
26174
26745
  }
26175
26746
  return void 0;
@@ -26202,13 +26773,14 @@ function parseArgv(argv) {
26202
26773
  let thinking;
26203
26774
  let help = false;
26204
26775
  const positional = [];
26776
+ const globalOptions = createTypedOptionConsumer(PUBLIC_GLOBAL_OPTIONS);
26205
26777
  while (args.length > 0) {
26206
26778
  if (args[0] === "--") {
26207
26779
  args.shift();
26208
26780
  positional.push(...args);
26209
26781
  break;
26210
26782
  }
26211
- const taken = takePublicGlobalFlag(args, 0);
26783
+ const taken = takePublicGlobalFlag(args, 0, globalOptions);
26212
26784
  if (taken !== void 0) {
26213
26785
  if (taken.flag === "help") {
26214
26786
  help = true;
@@ -26252,9 +26824,29 @@ function helpDocument() {
26252
26824
  return {
26253
26825
  executable: "ak-role",
26254
26826
  capabilities: listHelpCapabilities(),
26255
- internalEntrypoint: INTERNAL_ROLE_ENTRYPOINT_RELATIVE
26827
+ internalEntrypoint: INTERNAL_ROLE_ENTRYPOINT_RELATIVE,
26828
+ /** #342 structured global options from the sole option table. */
26829
+ globalOptions: projectOwnerOptions("global")
26256
26830
  };
26257
26831
  }
26832
+ function helpDocumentForCommand(command) {
26833
+ if (command === "global") {
26834
+ return {
26835
+ command: "global",
26836
+ kind: "global",
26837
+ options: projectOwnerOptions("global")
26838
+ };
26839
+ }
26840
+ if (command in PUBLIC_ROLE_ARGV) {
26841
+ const owner = command;
26842
+ return {
26843
+ command: owner,
26844
+ kind: owner === "taishi" ? "deterministic" : "role",
26845
+ options: projectOwnerOptions(owner)
26846
+ };
26847
+ }
26848
+ return void 0;
26849
+ }
26258
26850
  function renderHelp() {
26259
26851
  const doc = helpDocument();
26260
26852
  const lines = [
@@ -26280,15 +26872,41 @@ function renderHelp() {
26280
26872
  lines.push(` ${cap.name}`);
26281
26873
  }
26282
26874
  }
26875
+ lines.push("", "Global options:");
26876
+ lines.push(...renderOwnerOptionHelpLines("global"));
26283
26877
  lines.push(
26284
26878
  "",
26285
- "Global options: --model provider/model --thinking level",
26879
+ "Role options: ak-role help <command>",
26286
26880
  "Persistent config: ak-role config set <seat> <provider/model:thinking>",
26287
26881
  "Effective seats: ak-role roles"
26288
26882
  );
26289
26883
  return `${lines.join("\n")}
26290
26884
  `;
26291
26885
  }
26886
+ function renderCommandHelp(command) {
26887
+ const caps = listHelpCapabilities();
26888
+ const match = caps.find((cap) => cap.name === command);
26889
+ if (match === void 0) return void 0;
26890
+ const lines = [];
26891
+ if (match.kind === "support") {
26892
+ lines.push(`command ${match.name} kind support`);
26893
+ } else if (match.kind === "deterministic") {
26894
+ lines.push(`command ${match.name} kind deterministic`);
26895
+ } else {
26896
+ lines.push(
26897
+ `command ${match.name} kind role phases ${match.phases.map((p) => p === null ? "none" : p).join(",")} default ${match.defaultPhase ?? "none"}`
26898
+ );
26899
+ }
26900
+ if (command in PUBLIC_ROLE_ARGV) {
26901
+ lines.push(
26902
+ ...renderOwnerOptionHelpLines(
26903
+ command
26904
+ )
26905
+ );
26906
+ }
26907
+ return `${lines.join("\n")}
26908
+ `;
26909
+ }
26292
26910
  function renderRoles(seats) {
26293
26911
  const lines = ["seat kind source model"];
26294
26912
  for (const seat of seats) {
@@ -26370,23 +26988,11 @@ async function runAkRole(argv, env) {
26370
26988
  if (parsed.help || parsed.command === void 0 || parsed.command === "help") {
26371
26989
  if (parsed.command === "help" && parsed.args[0] !== void 0) {
26372
26990
  const topic = parsed.args[0];
26373
- const caps = listHelpCapabilities();
26374
- const match = caps.find((cap) => cap.name === topic);
26375
- if (match === void 0) {
26991
+ const rendered = renderCommandHelp(topic);
26992
+ if (rendered === void 0) {
26376
26993
  throw new CliUsageError(`unknown help topic: ${topic}`);
26377
26994
  }
26378
- if (match.kind === "support") {
26379
- io.stdout(`command ${match.name} kind support
26380
- `);
26381
- } else if (match.kind === "deterministic") {
26382
- io.stdout(`command ${match.name} kind deterministic
26383
- `);
26384
- } else {
26385
- io.stdout(
26386
- `command ${match.name} kind role phases ${match.phases.map((p) => p === null ? "none" : p).join(",")} default ${match.defaultPhase ?? "none"}
26387
- `
26388
- );
26389
- }
26995
+ io.stdout(rendered);
26390
26996
  return { exitCode: 0 };
26391
26997
  }
26392
26998
  io.stdout(renderHelp());
@@ -26807,7 +27413,7 @@ async function runAkRole(argv, env) {
26807
27413
  return { exitCode: 1 };
26808
27414
  }
26809
27415
  }
26810
- var PUBLIC_ROLE_ARGV, THINKING_LEVELS2;
27416
+ var PUBLIC_ROLE_ARGV, PUBLIC_GLOBAL_OPTIONS, THINKING_LEVELS2;
26811
27417
  var init_cli = __esm({
26812
27418
  "src/public-cli/cli.ts"() {
26813
27419
  "use strict";
@@ -26815,6 +27421,7 @@ var init_cli = __esm({
26815
27421
  init_cli_errors();
26816
27422
  init_explicit_internal();
26817
27423
  init_invocation();
27424
+ init_option_definitions();
26818
27425
  init_coder_run();
26819
27426
  init_collector_run();
26820
27427
  init_doctor_run();
@@ -26829,16 +27436,17 @@ var init_cli = __esm({
26829
27436
  init_explicit_internal();
26830
27437
  init_cli_errors();
26831
27438
  PUBLIC_ROLE_ARGV = {
26832
- judge: { parse: parseJudgeArgv },
26833
- coder: { parse: parseCoderArgv },
26834
- fixer: { parse: parseFixerArgv },
26835
- collector: { parse: parseCollectorArgv },
26836
- doctor: { parse: parseDoctorArgv },
26837
- merger: { parse: parseMergerArgv },
26838
- reviewer: { parse: parseReviewerArgv },
27439
+ judge: { parse: parseJudgeArgv, options: optionsForOwner("judge") },
27440
+ coder: { parse: parseCoderArgv, options: optionsForOwner("coder") },
27441
+ fixer: { parse: parseFixerArgv, options: optionsForOwner("fixer") },
27442
+ collector: { parse: parseCollectorArgv, options: optionsForOwner("collector") },
27443
+ doctor: { parse: parseDoctorArgv, options: optionsForOwner("doctor") },
27444
+ merger: { parse: parseMergerArgv, options: optionsForOwner("merger") },
27445
+ reviewer: { parse: parseReviewerArgv, options: optionsForOwner("reviewer") },
26839
27446
  /** Deterministic analysis seat (#336) — argv parse only; no LLM admission. */
26840
- taishi: { parse: parseTaishiArgv }
27447
+ taishi: { parse: parseTaishiArgv, options: optionsForOwner("taishi") }
26841
27448
  };
27449
+ PUBLIC_GLOBAL_OPTIONS = optionsForOwner("global");
26842
27450
  THINKING_LEVELS2 = /* @__PURE__ */ new Set([
26843
27451
  "off",
26844
27452
  "minimal",