github-router 0.3.249 → 0.3.255

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.
Files changed (95) hide show
  1. package/dist/attribution-settings-CegtE0TY.js +2347 -0
  2. package/dist/attribution-settings-CegtE0TY.js.map +1 -0
  3. package/dist/auth-CP4q2CjO.js +44 -0
  4. package/dist/auth-CP4q2CjO.js.map +1 -0
  5. package/dist/browser-ext/background.js +8 -1
  6. package/dist/browser-ext/manifest.json +1 -1
  7. package/dist/check-usage-6PB5IUFb.js +43 -0
  8. package/dist/check-usage-6PB5IUFb.js.map +1 -0
  9. package/dist/claude-BiZxoNWq.js +593 -0
  10. package/dist/claude-BiZxoNWq.js.map +1 -0
  11. package/dist/client-CQMfroGV.js +413 -0
  12. package/dist/client-CQMfroGV.js.map +1 -0
  13. package/dist/codex-ST21X3WF.js +85 -0
  14. package/dist/codex-ST21X3WF.js.map +1 -0
  15. package/dist/debug-CjmE9sgG.js +81 -0
  16. package/dist/debug-CjmE9sgG.js.map +1 -0
  17. package/dist/engine-DNCX0Hfd.js +2 -0
  18. package/dist/{lifecycle-DHZsVN8j.js → exec-DTO2oin0.js} +3 -130
  19. package/dist/exec-DTO2oin0.js.map +1 -0
  20. package/dist/file-log-reporter-IFQcL5IK.js +158 -0
  21. package/dist/file-log-reporter-IFQcL5IK.js.map +1 -0
  22. package/dist/gate-discovery-CepmbZnc.js +635 -0
  23. package/dist/gate-discovery-CepmbZnc.js.map +1 -0
  24. package/dist/get-copilot-usage-DD1N0Xlh.js +11 -0
  25. package/dist/get-copilot-usage-DD1N0Xlh.js.map +1 -0
  26. package/dist/hook-mcp-client-DBvm9608.js +136 -0
  27. package/dist/hook-mcp-client-DBvm9608.js.map +1 -0
  28. package/dist/internal-artifact-open-D2cQ2QCw.js +319 -0
  29. package/dist/internal-artifact-open-D2cQ2QCw.js.map +1 -0
  30. package/dist/internal-first-mate-guard-7v2CtMA6.js +2 -0
  31. package/dist/internal-first-mate-guard-La0tIjTU.js +126 -0
  32. package/dist/internal-first-mate-guard-La0tIjTU.js.map +1 -0
  33. package/dist/internal-plan-review-ipnT3bhj.js +114 -0
  34. package/dist/internal-plan-review-ipnT3bhj.js.map +1 -0
  35. package/dist/internal-prompt-submit-DOj75_hh.js +121 -0
  36. package/dist/internal-prompt-submit-DOj75_hh.js.map +1 -0
  37. package/dist/internal-session-bind-DwK2iGW0.js +133 -0
  38. package/dist/internal-session-bind-DwK2iGW0.js.map +1 -0
  39. package/dist/internal-stop-hook-CNPGJm63.js +212 -0
  40. package/dist/internal-stop-hook-CNPGJm63.js.map +1 -0
  41. package/dist/internal-stop-review-AUcFssrW.js +122 -0
  42. package/dist/internal-stop-review-AUcFssrW.js.map +1 -0
  43. package/dist/internal-worker-guard-Lu8VHj5k.js +75 -0
  44. package/dist/internal-worker-guard-Lu8VHj5k.js.map +1 -0
  45. package/dist/internal-workspace-header-BRMz0Yql.js +24 -0
  46. package/dist/internal-workspace-header-BRMz0Yql.js.map +1 -0
  47. package/dist/lifecycle-Byw5s1G8.js +134 -0
  48. package/dist/lifecycle-Byw5s1G8.js.map +1 -0
  49. package/dist/lifecycle-D80CaWie.js +2 -0
  50. package/dist/{lifecycle-CUf1UDrH.js → lifecycle-DD88ka3f.js} +3 -3
  51. package/dist/{lifecycle-CUf1UDrH.js.map → lifecycle-DD88ka3f.js.map} +1 -1
  52. package/dist/lifecycle-DzqCqDlM.js +2 -0
  53. package/dist/main.js +43 -12884
  54. package/dist/main.js.map +1 -1
  55. package/dist/mcp-workspace-header-CGJbNeHb.js +21 -0
  56. package/dist/mcp-workspace-header-CGJbNeHb.js.map +1 -0
  57. package/dist/models-B6UnGyRo.js +158 -0
  58. package/dist/models-B6UnGyRo.js.map +1 -0
  59. package/dist/orchestration-hRU4TdWr.js +39 -0
  60. package/dist/orchestration-hRU4TdWr.js.map +1 -0
  61. package/dist/{paths-BjTMI_xK.js → paths-C1cLZ7Ga.js} +52 -20
  62. package/dist/paths-C1cLZ7Ga.js.map +1 -0
  63. package/dist/paths-CwKwc2pi.js +2 -0
  64. package/dist/{peer-mcp-personas-DJzLpfDJ.js → peer-mcp-personas-BDet588n.js} +1006 -2488
  65. package/dist/peer-mcp-personas-BDet588n.js.map +1 -0
  66. package/dist/plan-review-hook-C8x0uKPf.js +159 -0
  67. package/dist/plan-review-hook-C8x0uKPf.js.map +1 -0
  68. package/dist/prompt-submit-hook-BrkkLAAB.js +153 -0
  69. package/dist/prompt-submit-hook-BrkkLAAB.js.map +1 -0
  70. package/dist/provision-DSkPa5hY.js +656 -0
  71. package/dist/provision-DSkPa5hY.js.map +1 -0
  72. package/dist/serve-sa_7c-yD.js +1511 -0
  73. package/dist/serve-sa_7c-yD.js.map +1 -0
  74. package/dist/server-setup-DcO9qvN3.js +4776 -0
  75. package/dist/server-setup-DcO9qvN3.js.map +1 -0
  76. package/dist/start-siGxuZhO.js +129 -0
  77. package/dist/start-siGxuZhO.js.map +1 -0
  78. package/dist/stop-gate-hook-DQc2KV7B.js +686 -0
  79. package/dist/stop-gate-hook-DQc2KV7B.js.map +1 -0
  80. package/dist/stop-gate-policy-CHwWbuF_.js +268 -0
  81. package/dist/stop-gate-policy-CHwWbuF_.js.map +1 -0
  82. package/dist/token-R2ocFC3M.js +1054 -0
  83. package/dist/token-R2ocFC3M.js.map +1 -0
  84. package/dist/version-_Q1WpsQp.js +32 -0
  85. package/dist/version-_Q1WpsQp.js.map +1 -0
  86. package/dist/worker-dispatch-Bj1uYyG9.js +222 -0
  87. package/dist/worker-dispatch-Bj1uYyG9.js.map +1 -0
  88. package/package.json +1 -1
  89. package/dist/engine-BuuHUb4t.js +0 -2
  90. package/dist/lifecycle-0rXrwjWk.js +0 -2
  91. package/dist/lifecycle-DHZsVN8j.js.map +0 -1
  92. package/dist/lifecycle-hzPwXc3I.js +0 -2
  93. package/dist/paths-BjTMI_xK.js.map +0 -1
  94. package/dist/paths-D3XgmTcQ.js +0 -2
  95. package/dist/peer-mcp-personas-DJzLpfDJ.js.map +0 -1
@@ -0,0 +1,43 @@
1
+ import { i as ensurePaths } from "./paths-C1cLZ7Ga.js";
2
+ import { r as setupGitHubToken } from "./token-R2ocFC3M.js";
3
+ import { t as getCopilotUsage } from "./get-copilot-usage-DD1N0Xlh.js";
4
+ import { defineCommand } from "citty";
5
+ import consola from "consola";
6
+ //#region src/check-usage.ts
7
+ const checkUsage = defineCommand({
8
+ meta: {
9
+ name: "check-usage",
10
+ description: "Show current GitHub Copilot usage/quota information"
11
+ },
12
+ async run() {
13
+ await ensurePaths();
14
+ await setupGitHubToken();
15
+ try {
16
+ const usage = await getCopilotUsage();
17
+ const premium = usage.quota_snapshots.premium_interactions;
18
+ const premiumTotal = premium.entitlement;
19
+ const premiumUsed = premiumTotal - premium.remaining;
20
+ const premiumPercentUsed = premiumTotal > 0 ? premiumUsed / premiumTotal * 100 : 0;
21
+ const premiumPercentRemaining = premium.percent_remaining;
22
+ function summarizeQuota(name, snap) {
23
+ if (!snap) return `${name}: N/A`;
24
+ const total = snap.entitlement;
25
+ const used = total - snap.remaining;
26
+ const percentUsed = total > 0 ? used / total * 100 : 0;
27
+ const percentRemaining = snap.percent_remaining;
28
+ return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`;
29
+ }
30
+ const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`;
31
+ const chatLine = summarizeQuota("Chat", usage.quota_snapshots.chat);
32
+ const completionsLine = summarizeQuota("Completions", usage.quota_snapshots.completions);
33
+ consola.box(`Copilot Usage (plan: ${usage.copilot_plan})\nQuota resets: ${usage.quota_reset_date}\n\nQuotas:\n ${premiumLine}\n ${chatLine}\n ${completionsLine}`);
34
+ } catch (err) {
35
+ consola.error("Failed to fetch Copilot usage:", err);
36
+ process.exit(1);
37
+ }
38
+ }
39
+ });
40
+ //#endregion
41
+ export { checkUsage };
42
+
43
+ //# sourceMappingURL=check-usage-6PB5IUFb.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-usage-6PB5IUFb.js","names":[],"sources":["../src/check-usage.ts"],"sourcesContent":["import { defineCommand } from \"citty\"\nimport consola from \"consola\"\n\nimport { ensurePaths } from \"./lib/paths\"\nimport { setupGitHubToken } from \"./lib/token\"\nimport {\n getCopilotUsage,\n type QuotaDetail,\n} from \"./services/github/get-copilot-usage\"\n\nexport const checkUsage = defineCommand({\n meta: {\n name: \"check-usage\",\n description: \"Show current GitHub Copilot usage/quota information\",\n },\n async run() {\n await ensurePaths()\n await setupGitHubToken()\n try {\n const usage = await getCopilotUsage()\n const premium = usage.quota_snapshots.premium_interactions\n const premiumTotal = premium.entitlement\n const premiumUsed = premiumTotal - premium.remaining\n const premiumPercentUsed =\n premiumTotal > 0 ? (premiumUsed / premiumTotal) * 100 : 0\n const premiumPercentRemaining = premium.percent_remaining\n\n // Helper to summarize a quota snapshot\n function summarizeQuota(name: string, snap: QuotaDetail | undefined) {\n if (!snap) return `${name}: N/A`\n const total = snap.entitlement\n const used = total - snap.remaining\n const percentUsed = total > 0 ? (used / total) * 100 : 0\n const percentRemaining = snap.percent_remaining\n return `${name}: ${used}/${total} used (${percentUsed.toFixed(1)}% used, ${percentRemaining.toFixed(1)}% remaining)`\n }\n\n const premiumLine = `Premium: ${premiumUsed}/${premiumTotal} used (${premiumPercentUsed.toFixed(1)}% used, ${premiumPercentRemaining.toFixed(1)}% remaining)`\n const chatLine = summarizeQuota(\"Chat\", usage.quota_snapshots.chat)\n const completionsLine = summarizeQuota(\n \"Completions\",\n usage.quota_snapshots.completions,\n )\n\n consola.box(\n `Copilot Usage (plan: ${usage.copilot_plan})\\n`\n + `Quota resets: ${usage.quota_reset_date}\\n`\n + `\\nQuotas:\\n`\n + ` ${premiumLine}\\n`\n + ` ${chatLine}\\n`\n + ` ${completionsLine}`,\n )\n } catch (err) {\n consola.error(\"Failed to fetch Copilot usage:\", err)\n process.exit(1)\n }\n },\n})\n"],"mappings":";;;;;;AAUA,MAAa,aAAa,cAAc;CACtC,MAAM;EACJ,MAAM;EACN,aAAa;CACf;CACA,MAAM,MAAM;EACV,MAAM,YAAY;EAClB,MAAM,iBAAiB;EACvB,IAAI;GACF,MAAM,QAAQ,MAAM,gBAAgB;GACpC,MAAM,UAAU,MAAM,gBAAgB;GACtC,MAAM,eAAe,QAAQ;GAC7B,MAAM,cAAc,eAAe,QAAQ;GAC3C,MAAM,qBACJ,eAAe,IAAK,cAAc,eAAgB,MAAM;GAC1D,MAAM,0BAA0B,QAAQ;GAGxC,SAAS,eAAe,MAAc,MAA+B;IACnE,IAAI,CAAC,MAAM,OAAO,GAAG,KAAK;IAC1B,MAAM,QAAQ,KAAK;IACnB,MAAM,OAAO,QAAQ,KAAK;IAC1B,MAAM,cAAc,QAAQ,IAAK,OAAO,QAAS,MAAM;IACvD,MAAM,mBAAmB,KAAK;IAC9B,OAAO,GAAG,KAAK,IAAI,KAAK,GAAG,MAAM,SAAS,YAAY,QAAQ,CAAC,EAAE,UAAU,iBAAiB,QAAQ,CAAC,EAAE;GACzG;GAEA,MAAM,cAAc,YAAY,YAAY,GAAG,aAAa,SAAS,mBAAmB,QAAQ,CAAC,EAAE,UAAU,wBAAwB,QAAQ,CAAC,EAAE;GAChJ,MAAM,WAAW,eAAe,QAAQ,MAAM,gBAAgB,IAAI;GAClE,MAAM,kBAAkB,eACtB,eACA,MAAM,gBAAgB,WACxB;GAEA,QAAQ,IACN,wBAAwB,MAAM,aAAa,mBACtB,MAAM,iBAAiB,iBAEnC,YAAY,MACZ,SAAS,MACT,iBACX;EACF,SAAS,KAAK;GACZ,QAAQ,MAAM,kCAAkC,GAAG;GACnD,QAAQ,KAAK,CAAC;EAChB;CACF;AACF,CAAC"}
@@ -0,0 +1,593 @@
1
+ import { $ as scoutModel, C as buildToolbeltAwareness, Ct as shouldUseInsecureTls, Et as DEFAULT_CLAUDE_MODEL_FALLBACKS, G as brainstormModel, J as browserToolsEnabled, K as browseAgentEnabled, Nt as pickClaudeDefault, Q as reviewerModel, S as availableToolCommands, U as agentToolsEnabled, W as artifactToolsEnabled, X as geminiAvailable, Y as fleetToolsEnabled, Z as nativeSubagentModel, _t as provisionAndIndexColbert, a as buildPeerAwarenessSnippet, bt as warmTreeSitterPool, et as scribeModel, ht as provisionBrowserAssets, nt as workerToolsEnabled, o as buildPeerAwarenessSummary, q as browserCompoundToolsEnabled, tt as standInToolEnabled, w as toolbeltEnabled } from "./peer-mcp-personas-BDet588n.js";
2
+ import { o as removeOwnClaudeConfigMirror, r as ensureClaudeConfigMirror, t as PATHS, u as writeArtifactCredsToMirror } from "./paths-C1cLZ7Ga.js";
3
+ import { O as state, d as resolveModel } from "./token-R2ocFC3M.js";
4
+ import { r as parseBoolEnv } from "./exec-DTO2oin0.js";
5
+ import { a as sharedServerArgs, c as listModelsForEndpoint, d as updateClaude, i as setupAndServe, l as runSelfUpdate, o as startKeepAwake, r as parseSharedArgs, s as stopKeepAwake, t as getClaudeCodeEnvVars, u as checkClaudeVersion } from "./server-setup-DcO9qvN3.js";
6
+ import { i as launchChild, r as getCodexVersion, t as provisionToolbelt } from "./provision-DSkPa5hY.js";
7
+ import { i as captureLaunchBaseline, l as stopGateDisabled, n as buildSessionBindHookCommand, r as buildStopHookCommand, s as injectStopHookIntoSettingsFile, t as buildArtifactOpenHookCommand, u as stopGateId } from "./stop-gate-hook-DQc2KV7B.js";
8
+ import { t as liveExec } from "./orchestration-hRU4TdWr.js";
9
+ import { c as repoRoot, d as trustRepo, l as stopGateEnabledForRepo, s as repoFingerprint, t as fileBaselineStore } from "./stop-gate-policy-CHwWbuF_.js";
10
+ import { l as guardToolMatcher, n as activeDispatchModes, r as buildWorkerGuardHookCommand } from "./worker-dispatch-Bj1uYyG9.js";
11
+ import { _ as injectPeerMcpIntoMirror, b as workersKeyOf, c as prependArtifactPanelDirectiveToMirroredClaudeMd, i as OPERATING_DEFAULTS_DIGEST, l as prependOperatingDefaultsToMirroredClaudeMd, m as planModeAllowRules, n as INJECTED_SKILLS, o as appendPeerAwarenessToMirroredClaudeMd, p as injectAllowRules, r as writeInjectedSkill, s as appendToolbeltAwarenessToMirroredClaudeMd, t as injectAttributionSuppressionIntoSettingsFile, u as prependStyleDirectiveToMirroredClaudeMd, v as resolveCodexCliBackend, x as writePeerMcpRuntimeFiles, y as resolveGroupKeysFromMirror } from "./attribution-settings-CegtE0TY.js";
12
+ import { t as enableFileLogging } from "./file-log-reporter-IFQcL5IK.js";
13
+ import { r as hookMcpRuntimeFromEnv } from "./hook-mcp-client-DBvm9608.js";
14
+ import { i as planReviewEnabled, t as buildPlanReviewHookCommand } from "./plan-review-hook-C8x0uKPf.js";
15
+ import { a as checksForDescriptor, i as writeDiscoveredGate, n as readDiscoveredGate, o as descriptorHash, r as sourcesHash, s as parseGateDescriptor, t as discoverGateCommands } from "./gate-discovery-CepmbZnc.js";
16
+ import { t as buildPromptSubmitHookCommand } from "./prompt-submit-hook-BrkkLAAB.js";
17
+ import { i as assertShapingInstalled, n as buildFirstMateGuardHookCommand, t as FIRST_MATE_GUARD_MATCHER } from "./internal-first-mate-guard-La0tIjTU.js";
18
+ import { defineCommand } from "citty";
19
+ import consola from "consola";
20
+ import path from "node:path";
21
+ import { randomBytes } from "node:crypto";
22
+ import { tmpdir } from "node:os";
23
+ import process from "node:process";
24
+ //#region src/lib/injected-skills/artifact-review-skill.ts
25
+ const ARTIFACT_REVIEW_SKILL = {
26
+ name: "gh-artifact-review",
27
+ md: `---
28
+ name: gh-artifact-review
29
+ description: Reviews plans and artifacts in the ai-or-die panel. Defaults to authoring a self-contained HTML artifact (rich, annotatable, optionally interactive) and opening THAT for the human, then drains feedback with artifact_await, revises, and ends the loop. Use when running inside an ai-or-die tab and there is a plan, comparison, diagram, table, diff, or report the user should see before proceeding.
30
+ user-invocable: true
31
+ ---
32
+
33
+ # gh-artifact-review: human review in the ai-or-die panel
34
+
35
+ Use this when you finish a plan or produce something a user should review and you are inside an ai-or-die tab (the \`mcp__peers__artifact_*\` tools drive a live panel). The human can click any block or select text to attach a comment, and can click declarative action controls you emit — both come back to you as typed events.
36
+
37
+ ## Default: present HTML, not raw markdown
38
+
39
+ HTML is the canonical review artifact — it renders richly and is annotatable element-by-element. When the content is anything visual or structured (a comparison, table, diagram, diff, dashboard, or a plan you want to look polished), **author a self-contained \`.html\` file** (inline CSS, no external deps, readable typography) and open THAT.
40
+
41
+ - Plan-mode plans: the panel auto-opens them already rendered to HTML — you do not need to convert them by hand.
42
+ - Do not paste raw markdown into an \`.html\`; write real HTML (headings, lists, tables, \`<pre>\` for code). Opening a raw \`.md\` still renders, but a purpose-built HTML artifact reads better and annotates cleanly.
43
+
44
+ ## When to reach for an artifact
45
+
46
+ Default to one for anything easier to grasp visually than as terminal prose: plans, design proposals, comparisons / trade-offs, decisions that need the user's input, diagrams / architecture, tables, code diffs, reports. Skip it only for trivial one-line answers.
47
+
48
+ ## Playbooks (what a good artifact of each type contains)
49
+
50
+ - **plan**: goal, current state, the proposed approach (high-level decisions, not every line), then risks and open questions at the end.
51
+ - **comparison**: options as columns, trade-offs as rows, current-vs-target where relevant, and an explicit recommendation. Do not make the reader infer the winner.
52
+ - **table**: scannable rows, a sticky header, aligned numeric columns; group/section dense records rather than one flat wall.
53
+ - **diagram**: boxes + arrows. Mermaid when automatic layout matters; positioned SVG/CSS when each node needs prose, code, or controls.
54
+ - **code / diff**: \`<pre>\` with before/after or unified-diff styling; keep line context tight; call out the changed lines.
55
+ - **report / dashboard**: lead with the headline number / verdict, then supporting detail; keep one idea per section.
56
+
57
+ ## Design system
58
+
59
+ Artifacts stay portable (they must render identically opened standalone), so do not depend on a server-injected theme. Pick the look in priority order: (1) a look the user named; (2) the **subject project's** own design system, its Tailwind / theme config, CSS variables / tokens, component library, or existing styled pages, especially when the artifact previews that app's UI; (3) only when both come up empty, clean readable defaults (system font stack, generous spacing, a single accent).
60
+
61
+ ## Loop
62
+
63
+ 1. Open: \`mcp__peers__artifact_open\` with the absolute path of the \`.html\` (or the file). Pass \`mode:"interactive"\` when the HTML carries \`data-aod-*\` action controls (below). Relay the returned \`viewUrl\` and tell the user to review in the panel.
64
+ 2. Drain: \`mcp__peers__artifact_await\`. It long-holds for the human's next events and returns \`{events, status, cursor}\`. **Pass the returned \`cursor\` on your next call** so you only receive newer events; if \`events\` is empty, call again with that cursor.
65
+ 3. Act on each event by \`kind\`:
66
+ - \`comment\` — a free-text note anchored to the artifact (\`selector\`, quoted \`text\`, \`sourceLine\`); apply the requested change at that exact spot.
67
+ - \`action\` — the human clicked a control you emitted (\`action\` verb, \`elementId\`, optional \`value\`; a multi-select submit arrives as one action carrying the selected set). Do what the verb means (approve a step, choose an option, apply a toggle set).
68
+ 4. Reply: \`mcp__peers__artifact_reply\` with a concise summary of what you changed. Optionally \`mcp__peers__artifact_update({file})\` or \`({html})\` to replace the artifact content in place, or \`mcp__peers__artifact_refresh\` to reload it from disk.
69
+ 5. Repeat 2-4 until the user is satisfied; then \`mcp__peers__artifact_end\`. Use \`mcp__peers__artifact_dismiss\` to hide the panel while keeping the review alive (queued feedback preserved) if the user wants it out of the way without ending.
70
+
71
+ ### Push arrival (you do not have to be polling)
72
+
73
+ When you are idle at the prompt, panel feedback can arrive on its own as a new turn (the tab injects it). Structured actions that answer a pending decision are routed to you directly. Either way, the durable record is the \`artifact_await\` drain, so when in doubt call \`artifact_await\` (with your last cursor) to reconcile — it replays anything you missed.
74
+
75
+ ### \`artifact_poll\` (frozen legacy)
76
+
77
+ \`mcp__peers__artifact_poll\` still resolves for back-compat but returns the OLD payload (human comments only, no structured actions). Prefer \`artifact_await\`.
78
+
79
+ ## Interactive controls (data-aod-* authoring)
80
+
81
+ To let the human act on the artifact (not just comment), emit declarative controls; the panel wires them and delivers a typed \`action\` event. No JS in the artifact.
82
+
83
+ - **choose-one (fires immediately):** each option is a button that posts the moment it is clicked.
84
+ \`<button data-aod-action="choose" data-aod-group="decision-1" data-aod-id="opt-jwt" data-aod-value="jwt">JWT</button>\`
85
+ - **multi-select (toggle then submit):** checkboxes sharing a \`data-aod-group\`, plus one submit button with the same group. The submit delivers ONE action carrying \`{group, selected:[{elementId, value?}]}\`.
86
+ \`<input type="checkbox" data-aod-action="check" data-aod-group="opts" data-aod-id="a" data-aod-value="a">\` … \`<button data-aod-action="submit" data-aod-group="opts" data-aod-id="opts-submit">Apply</button>\`
87
+ - **plan steps:** \`<li class="aod-step" data-aod-id="plan-step-3" data-source-line="14">\` with per-step \`data-aod-action="approve"/"skip"\` buttons.
88
+ - **Required attrs:** \`data-aod-action\` (verb), \`data-aod-id\` (stable, echoed back as \`elementId\`; a control missing it is ignored), optional \`data-aod-value\`. Keep \`data-source-line\` for comment mapping.
89
+
90
+ ## Honest limits
91
+
92
+ - If a tool errors (e.g. \`NOT_IN_AIORDIE_TAB\`, \`UNREACHABLE\`, \`INVALID_REQUEST\`), report the code/message verbatim; do not claim the panel opened.
93
+ - The panel is a review surface, not an approver: outward/irreversible actions still need explicit user confirmation.
94
+ `
95
+ };
96
+ //#endregion
97
+ //#region src/claude.ts
98
+ function isFirstMateSkillName(name) {
99
+ return name === "gh-first-mate" || name === "gh-first-mate-scaffold" || name === "gh-first-mate-operate" || name === "gh-first-mate-conduct";
100
+ }
101
+ const claudeArgs = {
102
+ ...sharedServerArgs,
103
+ model: {
104
+ alias: "m",
105
+ type: "string",
106
+ description: "Override the default model for Claude Code. Accepts a full slug (e.g. claude-opus-4-7) or an Opus family shorthand (e.g. 4.7, 4.8, 4.6) which expands to the best variant for that family — adding the [1m] suffix when a 1M-context backend is in the catalog."
107
+ },
108
+ "codex-mcp": {
109
+ type: "boolean",
110
+ default: true,
111
+ description: "Wire peer-model MCP personas (codex-critic, codex-reviewer, gemini-critic) into the spawned Claude Code session"
112
+ },
113
+ "codex-cli": {
114
+ type: "boolean",
115
+ default: false,
116
+ description: "Add a `codex mcp-server` stdio backend so codex-implementer can mutate files. Requires codex CLI 0.129+; gracefully falls back to HTTP-only if absent."
117
+ },
118
+ "codex-mcp-only": {
119
+ type: "boolean",
120
+ default: false,
121
+ description: "Pass --strict-mcp-config to claude code so only github-router's MCP servers are loaded (hides user's existing MCP servers)"
122
+ },
123
+ stealth: {
124
+ type: "boolean",
125
+ default: false,
126
+ description: "Opt back into VS Code-only beta header filtering. Loses leverage features (task budgets, token-efficient tools, prompt caching, etc.) but minimizes the wire-fingerprint difference from VS Code Copilot Chat. By default the `claude` subcommand enables extended/leverage betas because the spawned Claude Code already identifies itself via UA and other headers — partial stealth doesn't buy much."
127
+ },
128
+ "trust-gate": {
129
+ type: "boolean",
130
+ default: false,
131
+ description: "Explicitly record consent for the structural Stop-gate in THIS repo (pinned to the repo's root-commit). The gate is ON BY DEFAULT when a harness is detected (consent-by-launching), so this is now mostly redundant; it stays for explicit/scripted use. Disable the gate entirely with GH_ROUTER_DISABLE_STOP_GATE=1."
132
+ },
133
+ "no-stop-gate": {
134
+ type: "boolean",
135
+ default: false,
136
+ description: "Disable the structural Stop-gate for THIS session (same effect as GH_ROUTER_DISABLE_STOP_GATE=1). Intended for driven/automated sessions where a blocking Stop hook would hang the turn-end while a fleet driver waits."
137
+ },
138
+ "auto-update": {
139
+ type: "boolean",
140
+ default: true,
141
+ description: "Check for and install the latest Claude Code on launch via `claude update` (throttled to once per hour via ~/.local/share/github-router/last-update-check). `claude update` respects the real install method (native installer or npm), so it never creates a conflicting second install; builds too old to support it fall back to `npm install -g @anthropic-ai/claude-code@latest`. Set to false (--no-auto-update) to check and warn only. Falls back gracefully if claude/npm/network unavailable."
142
+ },
143
+ "update-check": {
144
+ type: "boolean",
145
+ default: true,
146
+ description: "Check the npm registry for a newer Claude Code version on launch and warn if stale (non-blocking ~500ms cost). Set to false (--no-update-check) to skip the check entirely (useful for offline/CI). Independent from --auto-update: --no-update-check implies no auto-install (nothing to install since we never check)."
147
+ }
148
+ };
149
+ /**
150
+ * Build the argv to forward to the spawned `claude` child from citty's
151
+ * rawArgs (every token after the `claude` subcommand). citty is non-strict,
152
+ * so an unknown flag such as `--print`/`-p`… `--output-format` is absorbed
153
+ * into the parsed `args` object AND its value swallowed, instead of landing
154
+ * in `args._`; forwarding only `args._` therefore drops headless flags unless
155
+ * the user wrapped them in `--`. Here we walk rawArgs and forward every token
156
+ * that is NOT one of github-router's OWN declared flags (or that flag's
157
+ * consumed value). Everything after a literal `--` is forwarded verbatim,
158
+ * preserving the prior explicit-passthrough behavior.
159
+ *
160
+ * A child flag whose NAME collides with a github-router flag (`-p`/`--port`,
161
+ * `-v`/`--verbose`, `-m`/`--model`, `-a`/`--account-type`, `-r`/`--rate-limit`,
162
+ * `-g`/`--github-token`) is owned by github-router; forward it to the child
163
+ * explicitly after `--` (e.g. `github-router claude -- -p`). Every other Claude
164
+ * flag (`--print`, `--output-format`, `--resume`, `--continue`, …) flows
165
+ * through automatically.
166
+ */
167
+ function collectChildPassthroughArgs(rawArgs, argsDef) {
168
+ const known = /* @__PURE__ */ new Set();
169
+ const stringTyped = /* @__PURE__ */ new Set();
170
+ for (const [name, def] of Object.entries(argsDef)) {
171
+ const rawAlias = "alias" in def ? def.alias : void 0;
172
+ const aliases = rawAlias === void 0 ? [] : Array.isArray(rawAlias) ? rawAlias : [rawAlias];
173
+ for (const n of [name, ...aliases]) {
174
+ known.add(n);
175
+ if (def.type === "string") stringTyped.add(n);
176
+ }
177
+ }
178
+ const forwarded = [];
179
+ for (let i = 0; i < rawArgs.length; i++) {
180
+ const tok = rawArgs[i];
181
+ if (tok === "--") {
182
+ forwarded.push(...rawArgs.slice(i + 1));
183
+ break;
184
+ }
185
+ if (tok === "-" || !tok.startsWith("-")) {
186
+ forwarded.push(tok);
187
+ continue;
188
+ }
189
+ const doubleDash = tok.startsWith("--");
190
+ const afterDashes = tok.slice(doubleDash ? 2 : 1);
191
+ const eq = afterDashes.indexOf("=");
192
+ const rawName = eq >= 0 ? afterDashes.slice(0, eq) : afterDashes;
193
+ const hasInlineValue = eq >= 0;
194
+ const negated = doubleDash && rawName.startsWith("no-");
195
+ const baseName = negated ? rawName.slice(3) : rawName;
196
+ if (!(known.has(rawName) || negated && known.has(baseName))) {
197
+ forwarded.push(tok);
198
+ continue;
199
+ }
200
+ if (!negated && !hasInlineValue && stringTyped.has(rawName) && i + 1 < rawArgs.length && rawArgs[i + 1] !== "--" && !rawArgs[i + 1].startsWith("-")) i++;
201
+ }
202
+ return forwarded;
203
+ }
204
+ const claude = defineCommand({
205
+ meta: {
206
+ name: "claude",
207
+ description: "Start the proxy server and launch Claude Code"
208
+ },
209
+ args: claudeArgs,
210
+ async run({ args, rawArgs }) {
211
+ if (!process.stdout.isTTY) {
212
+ consola.error("The claude subcommand requires a TTY (interactive terminal).");
213
+ process.exit(1);
214
+ }
215
+ const parsed = parseSharedArgs(args);
216
+ if (args.stealth) {
217
+ parsed.extendedBetas = false;
218
+ consola.info("Stealth mode: VS Code-only beta filtering. Leverage features disabled.");
219
+ } else if (!args["extended-betas"]) parsed.extendedBetas = true;
220
+ if (args["update-check"] !== false) try {
221
+ const versionCheck = await checkClaudeVersion({ noCheck: false });
222
+ if (versionCheck.skipped && versionCheck.skipReason === "no-claude") consola.debug("claude --version probe failed; skipping auto-update.");
223
+ else if (versionCheck.skipped && versionCheck.skipReason === "no-npm") consola.debug("npm view @anthropic-ai/claude-code failed; skipping auto-update check (likely offline).");
224
+ else if (versionCheck.needsUpdate && versionCheck.installedVersion && versionCheck.latestVersion) if (args["auto-update"] !== false) try {
225
+ await updateClaude(versionCheck.latestVersion);
226
+ } catch (err) {
227
+ const msg = err instanceof Error ? err.message : String(err);
228
+ consola.warn(`Auto-update of Claude Code from ${versionCheck.installedVersion} to ${versionCheck.latestVersion} failed (${msg}); continuing with installed version. Run \`claude update\` (or \`npm install -g @anthropic-ai/claude-code@latest\`) manually to retry.`);
229
+ }
230
+ else consola.warn(`Claude Code v${versionCheck.installedVersion} is installed; v${versionCheck.latestVersion} is available. Run with --auto-update (the default) to install on launch, or \`claude update\` manually.`);
231
+ } catch (err) {
232
+ consola.debug("Claude version check failed:", err);
233
+ }
234
+ let server;
235
+ let serverUrl;
236
+ try {
237
+ const result = await setupAndServe({
238
+ ...parsed,
239
+ port: parsed.port,
240
+ silent: true
241
+ });
242
+ server = result.server;
243
+ serverUrl = result.serverUrl;
244
+ } catch (error) {
245
+ consola.error("Failed to start server:", error instanceof Error ? error.message : error);
246
+ process.exit(1);
247
+ }
248
+ runSelfUpdate({ selfUpdate: args["self-update"] !== false });
249
+ try {
250
+ await ensureClaudeConfigMirror();
251
+ } catch (err) {
252
+ consola.error(`Failed to provision CLAUDE_CONFIG_DIR mirror: ${err instanceof Error ? err.message : String(err)}. Spawned Claude Code would not be able to authenticate.`);
253
+ process.exit(1);
254
+ }
255
+ enableFileLogging();
256
+ const usingDefault = !args.model;
257
+ const opusFamilyShorthand = args.model?.match(/^(\d+\.\d+)$/)?.[1];
258
+ let chosenSlug = opusFamilyShorthand ? pickClaudeDefault(opusFamilyShorthand) : args.model ?? pickClaudeDefault();
259
+ let resolvedSlug = resolveModel(chosenSlug);
260
+ if (usingDefault && state.models) {
261
+ const inCache = (slug) => state.models?.data.some((m) => m.id === resolveModel(slug)) ?? false;
262
+ if (!inCache(chosenSlug)) {
263
+ for (const fallback of DEFAULT_CLAUDE_MODEL_FALLBACKS) if (inCache(fallback)) {
264
+ const fallbackSlug = pickClaudeDefault(fallback.replace(/^claude-opus-/, ""));
265
+ consola.info(`Default model "${chosenSlug}" not in your Copilot model list; falling back to "${fallbackSlug}".`);
266
+ chosenSlug = fallbackSlug;
267
+ resolvedSlug = resolveModel(fallbackSlug);
268
+ break;
269
+ }
270
+ }
271
+ }
272
+ if (resolvedSlug !== chosenSlug) consola.info(`Model "${chosenSlug}" resolved to "${resolvedSlug}"`);
273
+ if (!state.models?.data.find((m) => m.id === resolvedSlug)) {
274
+ const available = listModelsForEndpoint("/v1/messages");
275
+ consola.warn(`Model "${resolvedSlug}" not found. Available claude models: ${available.join(", ")}`);
276
+ }
277
+ const banner = chosenSlug === resolvedSlug ? chosenSlug : `${chosenSlug} → ${resolvedSlug}`;
278
+ process.stderr.write(`Server ready on ${serverUrl}, launching Claude Code (${banner})...\n`);
279
+ const envVars = getClaudeCodeEnvVars(serverUrl, chosenSlug);
280
+ const extraArgs = collectChildPassthroughArgs(rawArgs, claudeArgs);
281
+ if (toolbeltEnabled()) {
282
+ provisionToolbelt().catch((err) => consola.debug("Toolbelt provisioning failed:", err));
283
+ const toolbeltLine = buildToolbeltAwareness(availableToolCommands());
284
+ if (toolbeltLine) try {
285
+ await appendToolbeltAwarenessToMirroredClaudeMd(toolbeltLine);
286
+ } catch (err) {
287
+ consola.warn(`Toolbelt CLAUDE.md append failed: ${err instanceof Error ? err.message : String(err)}`);
288
+ }
289
+ }
290
+ provisionAndIndexColbert();
291
+ warmTreeSitterPool();
292
+ startKeepAwake();
293
+ if (browserToolsEnabled()) provisionBrowserAssets().catch((err) => consola.debug("Browser extension provisioning failed:", err));
294
+ const baseShutdown = async () => {
295
+ await stopKeepAwake();
296
+ await removeOwnClaudeConfigMirror();
297
+ };
298
+ let onShutdown = baseShutdown;
299
+ let peerAwarenessSnippet;
300
+ let peerAwarenessSummary;
301
+ if (args["codex-mcp"] !== false) try {
302
+ const requestedCli = args["codex-cli"] ?? false;
303
+ const backend = resolveCodexCliBackend({
304
+ requested: requestedCli,
305
+ codexInfo: requestedCli ? getCodexVersion() : null
306
+ });
307
+ const geminiModelsAvailable = geminiAvailable();
308
+ if (!geminiModelsAvailable) consola.info("gemini-3.1-pro-preview not found in your Copilot model catalog; gemini-critic persona will not be registered.");
309
+ const enabledGroups = [
310
+ "peers",
311
+ "search",
312
+ "orchestrate"
313
+ ];
314
+ if (workerToolsEnabled()) enabledGroups.push("workers");
315
+ if (standInToolEnabled()) enabledGroups.push("decide");
316
+ if (browserToolsEnabled()) enabledGroups.push("browser");
317
+ if (fleetToolsEnabled()) enabledGroups.push("fleet");
318
+ if (agentToolsEnabled()) enabledGroups.push("first-mate");
319
+ const { keys: groupKeys, skipped: skippedGroups } = await resolveGroupKeysFromMirror(enabledGroups);
320
+ const runtime = await writePeerMcpRuntimeFiles(serverUrl, {
321
+ codexCli: backend === "cli",
322
+ geminiAvailable: geminiModelsAvailable,
323
+ groupKeys,
324
+ workerToolsAvailable: workerToolsEnabled(),
325
+ browseAvailable: browseAgentEnabled(),
326
+ nativeSubagentModel: nativeSubagentModel(),
327
+ reviewerModel: reviewerModel(),
328
+ brainstormModel: brainstormModel(),
329
+ scoutModel: scoutModel(),
330
+ scribeModel: scribeModel()
331
+ });
332
+ state.peerMcpNonce = runtime.nonce;
333
+ envVars.GH_ROUTER_HOOK_MCP_URL = serverUrl;
334
+ envVars.GH_ROUTER_HOOK_NONCE = runtime.nonce;
335
+ onShutdown = async () => {
336
+ await runtime.cleanup();
337
+ await baseShutdown();
338
+ };
339
+ const injected = await injectPeerMcpIntoMirror(serverUrl, {
340
+ codexCli: backend === "cli",
341
+ geminiAvailable: geminiModelsAvailable,
342
+ groupKeys,
343
+ nonce: runtime.nonce
344
+ });
345
+ if (!injected.ok) {
346
+ extraArgs.push("--mcp-config", runtime.mcpConfigPath);
347
+ if (args["codex-mcp-only"] === true) extraArgs.push("--strict-mcp-config");
348
+ } else if (args["codex-mcp-only"] === true) consola.warn("--codex-mcp-only has no effect when peer MCP is wired via the mirrored .claude.json (the user's existing user-scope MCPs in the snapshot are still visible). Pass --no-codex-mcp to skip peer-MCP wiring entirely.");
349
+ const personaNames = runtime.personas.map((p) => p.agentName).join(", ");
350
+ const subagentVisibility = injected.ok ? `subagent-visible (mirrored mcpServers: [${injected.serversAdded.join(", ")}])` : `subagent-INVISIBLE (collision on user-side mcpServers: [${injected.conflictingServers.join(", ")}]; parent-only via --mcp-config)`;
351
+ const skippedNote = skippedGroups.length > 0 ? ` WARNING: groups [${skippedGroups.join(", ")}] skipped — both the bare and \`gh-router-<group>\` keys collide with your own mcpServers; those tools are unavailable this session (rename the user-side server to re-enable).` : "";
352
+ process.stderr.write(`Peer MCP wired (backend=${backend}, personas=[${personaNames}], subagent .md files=${runtime.agentMdPaths.length}, ${subagentVisibility}).${skippedNote}\n`);
353
+ const sessionCwd = process.cwd();
354
+ if (workerToolsEnabled()) {
355
+ const skillsToWrite = INJECTED_SKILLS.filter((s) => !isFirstMateSkillName(s.name) || agentToolsEnabled());
356
+ let skillsWritten = 0;
357
+ for (const s of skillsToWrite) if ((await writeInjectedSkill(s.name, s.md).catch(() => ({ written: false }))).written) skillsWritten++;
358
+ try {
359
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
360
+ const cmd = buildPromptSubmitHookCommand(process.execPath, process.argv[1]);
361
+ await injectStopHookIntoSettingsFile(settingsPath, cmd, "UserPromptSubmit", 45);
362
+ } catch (err) {
363
+ consola.warn(`Could not register the UserPromptSubmit hook: ${String(err)}`);
364
+ }
365
+ try {
366
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
367
+ const injectedServerKeys = injected.ok ? injected.serversAdded : [...Object.values(groupKeys).filter((k) => Boolean(k)), ...backend === "cli" ? ["codex-cli"] : []];
368
+ const res = await injectAllowRules(settingsPath, planModeAllowRules(injectedServerKeys));
369
+ if (res.added.length) consola.debug(`Auto-approved for plan mode: ${res.added.join(", ")}`);
370
+ } catch (err) {
371
+ consola.warn(`Could not auto-approve injected tools: ${String(err)}`);
372
+ }
373
+ if (!injected.ok) consola.warn("Workers non-blocking guard NOT registered: subagent MCP injection fell back to parent-only (--mcp-config), so worker-* dispatchers cannot reach the workers server. Raw (blocking) worker tools remain usable on the main thread this session.");
374
+ else if (process.env.GH_ROUTER_DISABLE_WORKER_GUARD === "1") consola.info("Workers non-blocking guard disabled via GH_ROUTER_DISABLE_WORKER_GUARD=1 — raw mcp__workers__* is callable on the main thread (blocking); the worker-* background agents remain the steered, non-blocking default.");
375
+ else try {
376
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
377
+ const workersKey = workersKeyOf(groupKeys);
378
+ const modes = activeDispatchModes({ browse: browseAgentEnabled() });
379
+ const cmd = buildWorkerGuardHookCommand(process.execPath, process.argv[1], workersKey, modes);
380
+ const matcher = guardToolMatcher(workersKey, modes);
381
+ await injectStopHookIntoSettingsFile(settingsPath, cmd, "PreToolUse", 10, matcher);
382
+ } catch (err) {
383
+ consola.warn(`Could not register the workers PreToolUse guard hook: ${String(err)}`);
384
+ }
385
+ if (skillsWritten > 0) {
386
+ const skillNames = skillsToWrite.map((s) => `/${s.name}`).join(", ");
387
+ process.stderr.write(`Injected skills (${skillsWritten}/${skillsToWrite.length}): ${skillNames}.\n`);
388
+ }
389
+ }
390
+ if (agentToolsEnabled()) {
391
+ let shapingInstalled = false;
392
+ try {
393
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
394
+ const guardCmd = buildFirstMateGuardHookCommand(process.execPath, process.argv[1]);
395
+ await injectStopHookIntoSettingsFile(settingsPath, guardCmd, "PreToolUse", void 0, FIRST_MATE_GUARD_MATCHER);
396
+ shapingInstalled = true;
397
+ } catch (err) {
398
+ consola.error(`Could not register the operator worker/orchestrate guard hook: ${String(err)}`);
399
+ }
400
+ assertShapingInstalled(true, shapingInstalled);
401
+ }
402
+ const aiordieSidecar = (process.env.AIORDIE_CLAUDE_BIND ?? "").trim();
403
+ if (aiordieSidecar.length > 0 && !aiordieSidecar.includes("\"")) try {
404
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
405
+ const command = buildSessionBindHookCommand(process.execPath, process.argv[1], aiordieSidecar);
406
+ await injectStopHookIntoSettingsFile(settingsPath, command, "SessionStart");
407
+ await injectStopHookIntoSettingsFile(settingsPath, command, "SessionEnd");
408
+ } catch (err) {
409
+ consola.warn(`Could not register the ai-or-die session-bind hook: ${String(err)}`);
410
+ }
411
+ if (artifactToolsEnabled()) {
412
+ await writeInjectedSkill(ARTIFACT_REVIEW_SKILL.name, ARTIFACT_REVIEW_SKILL.md).catch(() => ({ written: false }));
413
+ try {
414
+ await prependArtifactPanelDirectiveToMirroredClaudeMd(groupKeys.peers);
415
+ } catch (err) {
416
+ consola.warn(`Artifact-panel directive prepend failed: ${String(err)}`);
417
+ }
418
+ try {
419
+ await writeArtifactCredsToMirror(shouldUseInsecureTls(process.env.AIORDIE_BASE_URL ?? ""));
420
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
421
+ const cmd = buildArtifactOpenHookCommand(process.execPath, process.argv[1]);
422
+ await injectStopHookIntoSettingsFile(settingsPath, cmd, "PostToolUse", void 0, "ExitPlanMode");
423
+ } catch (err) {
424
+ consola.warn(`Could not register the artifact auto-open hook: ${String(err)}`);
425
+ }
426
+ }
427
+ if (hookMcpRuntimeFromEnv(envVars) && planReviewEnabled()) try {
428
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
429
+ const command = buildPlanReviewHookCommand(process.execPath, process.argv[1]);
430
+ await injectStopHookIntoSettingsFile(settingsPath, command, "PostToolUse", void 0, "ExitPlanMode");
431
+ } catch (err) {
432
+ consola.warn(`Could not register the advisory plan-review hook: ${String(err)}`);
433
+ }
434
+ if (args["trust-gate"] === true) try {
435
+ const root = await trustRepo(sessionCwd);
436
+ process.stderr.write(`Structural gate trusted for this repo (${root}); it will run on launch here from now on.\n`);
437
+ } catch (err) {
438
+ consola.warn(`Could not record gate trust: ${String(err)}`);
439
+ }
440
+ const gateDisabled = stopGateDisabled(args);
441
+ const forceEnabled = parseBoolEnv(process.env.GH_ROUTER_ENABLE_STOP_GATE) === true;
442
+ const includeTests = parseBoolEnv(process.env.GH_ROUTER_STOP_GATE_RUN_TESTS) === true;
443
+ const gateRoot = await repoRoot(sessionCwd).catch(() => sessionCwd);
444
+ let descriptor = null;
445
+ let discovering = false;
446
+ if (!gateDisabled) {
447
+ descriptor = await parseGateDescriptor(gateRoot, { includeTests }).catch(() => null);
448
+ if (!descriptor) {
449
+ const cached = await readDiscoveredGate(gateRoot).catch(() => null);
450
+ if (cached) descriptor = {
451
+ kind: "discovered",
452
+ checks: cached.checks,
453
+ ecosystem: cached.ecosystem,
454
+ workdir: gateRoot,
455
+ evidence: cached.evidence
456
+ };
457
+ else if (!forceEnabled && workerToolsEnabled() && parseBoolEnv(process.env.GH_ROUTER_ENABLE_GATE_DISCOVERY) === true) discovering = true;
458
+ }
459
+ }
460
+ let gateEnabled = gateDisabled ? false : await stopGateEnabledForRepo(sessionCwd).catch(() => false);
461
+ let autoTrusted = false;
462
+ if (!gateEnabled && !gateDisabled && (descriptor || discovering)) try {
463
+ await trustRepo(sessionCwd);
464
+ gateEnabled = true;
465
+ autoTrusted = true;
466
+ } catch (err) {
467
+ consola.warn(`Could not auto-trust this repo for the structural gate: ${String(err)}`);
468
+ }
469
+ if (gateEnabled && !descriptor && !discovering) descriptor = {
470
+ kind: "sealed",
471
+ gateId: stopGateId(),
472
+ workdir: gateRoot
473
+ };
474
+ if (gateEnabled && descriptor) {
475
+ const armed = descriptor;
476
+ try {
477
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
478
+ const command = buildStopHookCommand(process.execPath, process.argv[1]);
479
+ if (armed.kind === "sealed") envVars.GH_ROUTER_STOP_GATE_ID = armed.gateId;
480
+ else if (armed.kind === "parsed") envVars.GH_ROUTER_STOP_GATE_PARSED = "1";
481
+ else envVars.GH_ROUTER_STOP_GATE_DISCOVERED = "1";
482
+ if (includeTests) envVars.GH_ROUTER_STOP_GATE_RUN_TESTS = "1";
483
+ await injectStopHookIntoSettingsFile(settingsPath, command);
484
+ if (armed.kind !== "sealed") {
485
+ const checks = checksForDescriptor(armed);
486
+ const descriptorKey = descriptorHash(armed);
487
+ const token = randomBytes(8).toString("hex");
488
+ envVars.GH_ROUTER_STOP_GATE_BASELINE_TOKEN = token;
489
+ (async () => {
490
+ await captureLaunchBaseline({
491
+ checks,
492
+ workdir: armed.workdir,
493
+ exec: liveExec,
494
+ descriptorKey,
495
+ token,
496
+ baseline: fileBaselineStore(path.join(tmpdir(), "gh-router-stopgate-baseline"))
497
+ });
498
+ })().catch(() => {});
499
+ }
500
+ const tail = "A regression or a gate-weakening diff blocks stopping until fixed (per-prompt, max 2). Opt out with GH_ROUTER_DISABLE_STOP_GATE=1.\n";
501
+ if (armed.kind === "sealed") process.stderr.write((autoTrusted ? `Structural-gate Stop hook enabled by default for this repo (gate=${armed.gateId}; runs typecheck/test/lint at stop). ` : `Structural-gate Stop hook enabled (gate=${armed.gateId}). `) + tail);
502
+ else {
503
+ const ids = checksForDescriptor(armed).map((c) => c.id).join(", ");
504
+ const testNote = includeTests ? "Full test suite ON. " : "Full test suite off by default (enable with GH_ROUTER_STOP_GATE_RUN_TESTS=1). ";
505
+ process.stderr.write(`Structural-gate Stop hook armed for this ${armed.ecosystem} repo (checks: ${ids}; from ${armed.evidence.join(", ")}). ${testNote}${tail}`);
506
+ }
507
+ } catch (err) {
508
+ consola.warn(`Could not register the structural-gate Stop hook: ${String(err)}`);
509
+ }
510
+ } else if (gateEnabled && discovering) {
511
+ process.stderr.write("Structural Stop-gate: no checks parsed for this repo — discovering check commands in the background (arms on the next launch). Disable with GH_ROUTER_ENABLE_GATE_DISCOVERY=0.\n");
512
+ (async () => {
513
+ try {
514
+ const disc = await discoverGateCommands(gateRoot, {
515
+ includeTests,
516
+ signal: AbortSignal.timeout(12e4)
517
+ });
518
+ if (disc) {
519
+ const [fingerprint, sh] = await Promise.all([repoFingerprint(gateRoot).catch(() => ""), sourcesHash(gateRoot).catch(() => "")]);
520
+ await writeDiscoveredGate({
521
+ root: gateRoot,
522
+ fingerprint,
523
+ sourcesHash: sh,
524
+ discoveredAt: (/* @__PURE__ */ new Date()).toISOString(),
525
+ model: "gemini-3.5-flash",
526
+ ecosystem: disc.ecosystem,
527
+ checks: disc.checks,
528
+ confidence: disc.confidence,
529
+ evidence: disc.evidence
530
+ });
531
+ }
532
+ } catch {}
533
+ })().catch(() => {});
534
+ } else if (!gateDisabled && !descriptor && !discovering) process.stderr.write("Structural Stop-gate not enabled: no checks found in this repo (looked at package.json scripts / CI workflows / Makefile / Cargo.toml / go.mod / pyproject). Force a sealed gate with GH_ROUTER_ENABLE_STOP_GATE=1" + (workerToolsEnabled() ? ", or let a model discover them with GH_ROUTER_ENABLE_GATE_DISCOVERY=1" : "") + ".\n");
535
+ const peerSnippet = buildPeerAwarenessSnippet({
536
+ codexCli: backend === "cli",
537
+ geminiAvailable: geminiModelsAvailable,
538
+ workerToolsAvailable: workerToolsEnabled(),
539
+ standInAvailable: standInToolEnabled(),
540
+ browseAvailable: browserToolsEnabled(),
541
+ compoundBrowseAvailable: browserCompoundToolsEnabled(),
542
+ powerBrowseAvailable: state.powerBrowseEnabled,
543
+ fleetAvailable: fleetToolsEnabled(),
544
+ agentToolsAvailable: agentToolsEnabled(),
545
+ scoutAvailable: scoutModel() != null,
546
+ groupKeys
547
+ });
548
+ peerAwarenessSnippet = peerSnippet;
549
+ peerAwarenessSummary = buildPeerAwarenessSummary({
550
+ workerToolsAvailable: workerToolsEnabled(),
551
+ standInAvailable: standInToolEnabled(),
552
+ browseAvailable: browserToolsEnabled(),
553
+ fleetAvailable: fleetToolsEnabled(),
554
+ agentToolsAvailable: agentToolsEnabled(),
555
+ groupKeys
556
+ });
557
+ try {
558
+ await appendPeerAwarenessToMirroredClaudeMd(peerSnippet);
559
+ } catch (err) {
560
+ consola.warn(`Peer-awareness CLAUDE.md append failed (agent keeps tool descriptions via tools/list, loses the inventory overview): ${err instanceof Error ? err.message : String(err)}`);
561
+ }
562
+ try {
563
+ await prependStyleDirectiveToMirroredClaudeMd();
564
+ } catch (err) {
565
+ consola.warn(`Style-directive CLAUDE.md prepend failed: ${err instanceof Error ? err.message : String(err)}`);
566
+ }
567
+ } catch (err) {
568
+ consola.warn(`Peer MCP wiring failed (claude will launch without it): ${err instanceof Error ? err.message : String(err)}`);
569
+ }
570
+ extraArgs.push("--append-system-prompt", peerAwarenessSnippet && peerAwarenessSummary ? `${OPERATING_DEFAULTS_DIGEST}\n\n${peerAwarenessSummary}` : OPERATING_DEFAULTS_DIGEST);
571
+ try {
572
+ await prependOperatingDefaultsToMirroredClaudeMd();
573
+ } catch (err) {
574
+ consola.warn(`Operating-defaults CLAUDE.md prepend failed: ${err instanceof Error ? err.message : String(err)}`);
575
+ }
576
+ if (process.env.GH_ROUTER_DISABLE_NO_ATTRIBUTION !== "1") try {
577
+ const settingsPath = path.join(PATHS.CLAUDE_CONFIG_DIR, "settings.json");
578
+ await injectAttributionSuppressionIntoSettingsFile(settingsPath);
579
+ } catch (err) {
580
+ consola.warn(`No-attribution settings backstop skipped: ${err instanceof Error ? err.message : String(err)}`);
581
+ }
582
+ launchChild({
583
+ kind: "claude-code",
584
+ envVars,
585
+ extraArgs,
586
+ model: chosenSlug
587
+ }, server, { onShutdown });
588
+ }
589
+ });
590
+ //#endregion
591
+ export { claude };
592
+
593
+ //# sourceMappingURL=claude-BiZxoNWq.js.map