@bridge_gpt/mcp-server 0.2.58 → 0.2.59

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 (86) hide show
  1. package/README.md +105 -109
  2. package/build/agent-registry.js +15 -0
  3. package/build/agent-utils.js +16 -8
  4. package/build/claude-user-config-doctor.js +1 -35
  5. package/build/codex-bridge-profile.js +1 -1
  6. package/build/commands.generated.js +5 -6
  7. package/build/conductor/bridge-api-client.js +149 -0
  8. package/build/conductor/bring-up-facts.js +1 -1
  9. package/build/conductor/claude-hook.js +16 -16
  10. package/build/conductor/doctor.js +24 -19
  11. package/build/conductor/epic-implementer-cli.js +38 -5
  12. package/build/conductor/epic-reconcile.js +1 -1
  13. package/build/conductor/epic-runtime.js +10 -10
  14. package/build/conductor/errors.js +3 -3
  15. package/build/conductor/file-scope-guard.js +4 -4
  16. package/build/conductor/git-ci-types.js +1 -1
  17. package/build/conductor/git-inspection.js +2 -2
  18. package/build/conductor/install-doctor.js +6 -6
  19. package/build/conductor/merge-ledger.js +2 -2
  20. package/build/conductor/pr-ci-producer.js +6 -6
  21. package/build/conductor/pr-review-producer.js +2 -2
  22. package/build/conductor/producer-ledger.js +2 -0
  23. package/build/conductor/readiness-sections.js +7 -7
  24. package/build/conductor/readiness.js +2 -2
  25. package/build/conductor/recovery-cli.js +243 -12
  26. package/build/conductor/recovery-operations.js +56 -4
  27. package/build/conductor/repair-contract.js +38 -4
  28. package/build/conductor/spec-review-producer.js +6 -6
  29. package/build/conductor/store.js +10 -10
  30. package/build/conductor/supervisor-config.js +14 -14
  31. package/build/conductor/supervisor-ledger.js +2 -2
  32. package/build/conductor/supervisor-message-relay.js +2 -2
  33. package/build/conductor/worker-ledger-cli.js +19 -19
  34. package/build/conductor-bundle-artifacts.js +2 -2
  35. package/build/doctor.js +16 -294
  36. package/build/drive-epic.js +65 -71
  37. package/build/epic-implementer-bin.js +22 -17
  38. package/build/epic-implementer-claude-hook-bin.js +4 -2
  39. package/build/epic-implementer-env.js +155 -0
  40. package/build/epic-integration-pr.js +2 -2
  41. package/build/epic-shared/bridge-client.js +773 -0
  42. package/build/epic-shared/cut-protocol.js +392 -0
  43. package/build/executor/env.js +19 -8
  44. package/build/executor/heartbeat.js +1 -1
  45. package/build/executor/job-errors.js +3 -3
  46. package/build/executor/job-runner.js +1 -1
  47. package/build/executor/merge-job.js +35 -3
  48. package/build/executor/observation.js +1 -1
  49. package/build/executor/preflight.js +1 -1
  50. package/build/executor/prompt-spec.js +1 -1
  51. package/build/executor/reconcile-overlap-job.js +10 -130
  52. package/build/executor/reconcile-overlap-types.js +1 -1
  53. package/build/executor/runner.js +1 -1
  54. package/build/implement-epic/cli.js +1 -1
  55. package/build/index.js +181 -218
  56. package/build/init.js +20 -28
  57. package/build/install-bridge-conductor.js +7 -7
  58. package/build/install-bridge.js +199 -7
  59. package/build/mcp-host-entry-adapter.js +6 -0
  60. package/build/mcp-host-targets.js +2 -0
  61. package/build/mcp-invoke.js +5 -5
  62. package/build/mcp-profile.js +2 -2
  63. package/build/mcp-provisioning.js +2 -2
  64. package/build/mcp-server-invocation.js +3 -3
  65. package/build/merge-pull-request.js +15 -0
  66. package/build/model-routing-host.js +25 -0
  67. package/build/model-routing-preferences.js +99 -0
  68. package/build/pipelines.generated.js +9 -9
  69. package/build/plan-epic-implementer-eligibility.js +215 -0
  70. package/build/plan-execution-ledger.js +8 -0
  71. package/build/plane/member-roster.js +5 -5
  72. package/build/plane/supervisor.js +1 -1
  73. package/build/plane/types.js +5 -5
  74. package/build/readiness-check.js +1 -1
  75. package/build/readme.generated.js +1 -1
  76. package/build/review-tickets.js +4 -4
  77. package/build/setup-epic.js +8 -8
  78. package/build/start-tickets-conductor.js +32 -32
  79. package/build/start-tickets-prereqs.js +4 -9
  80. package/build/start-tickets.js +475 -91
  81. package/build/version.generated.js +3 -3
  82. package/docs/CONDUCTOR.md +69 -18
  83. package/docs/install/mcp-tool-integrations.md +2 -1
  84. package/package.json +2 -2
  85. package/pipelines/implement-ticket.json +1 -1
  86. package/pipelines/plan-epic.json +2 -2
@@ -1,3 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import path from"node:path";import{fileURLToPath}from"node:url";import{spawnSync}from"node:child_process";import{readFileSync}from"node:fs";var CONDUCTOR_HOOK_EMIT_FAILED_WARNING="Warning: conductor hook emit failed.";function resolveClaudeHookEventName(payload){if(payload===null||typeof payload!="object"||Array.isArray(payload))return null;let record=payload,candidates=["hook_event_name","hookEventName","event_name","event"];for(let key of candidates){let value=record[key];if(typeof value=="string"&&value.trim().length>0)return value.trim()}return null}function mapClaudeHookEventToSemanticType(eventName){switch(eventName){case"SessionStart":return"run.started";case"SessionEnd":return"run.stopped";case"Stop":return null;case"SubagentStop":return null;case"Notification":return"agent.notification";case"PreToolUse":return"tool.intent";default:return null}}var REQUIRED_IDENTITY_ENV=["BAPI_CONDUCTOR_RUN_ID","BAPI_CONDUCTOR_WORKER_ID","BAPI_CONDUCTOR_TICKET_KEY","BAPI_CONDUCTOR_WORKTREE_PATH"];function nonEmpty(value){return typeof value=="string"&&value.trim().length>0}function buildClaudeHookConductorEvent(payload,env){let eventName=resolveClaudeHookEventName(payload),type=mapClaudeHookEventToSemanticType(eventName);if(type===null||env.BAPI_CONDUCTOR_ENABLED!=="1")return null;for(let key of REQUIRED_IDENTITY_ENV)if(!nonEmpty(env[key]))return null;let ticketKey=env.BAPI_CONDUCTOR_TICKET_KEY.trim(),worktreePath=env.BAPI_CONDUCTOR_WORKTREE_PATH.trim(),details={ticket_key:ticketKey,worktree_path:worktreePath};nonEmpty(env.BAPI_CONDUCTOR_REPO_NAME)&&(details.repo=env.BAPI_CONDUCTOR_REPO_NAME.trim());let raw=payload!==null&&typeof payload=="object"&&!Array.isArray(payload)?payload:{payload};return{source:"claude-code",type,subject:ticketKey,run_id:env.BAPI_CONDUCTOR_RUN_ID.trim(),worker_id:env.BAPI_CONDUCTOR_WORKER_ID.trim(),producer:"claude-code-hook",observed_via:"claude-code-hook",data:{details,raw}}}function buildConductorEmitEventArgs(event){let args=["--type",event.type,"--source",event.source];return nonEmpty(event.subject??void 0)&&args.push("--subject",event.subject),nonEmpty(event.run_id??void 0)&&args.push("--run-id",event.run_id),nonEmpty(event.worker_id??void 0)&&args.push("--worker-id",event.worker_id),nonEmpty(event.producer??void 0)&&args.push("--producer",event.producer),nonEmpty(event.observed_via??void 0)&&args.push("--observed-via",event.observed_via),args.push("--data-json-stdin","--json"),args}function resolveConductorEmitCommand(env,emitArgs){let cliFile=env.BAPI_CONDUCTOR_CLI_FILE;return nonEmpty(cliFile)?{command:process.execPath,args:[cliFile.trim(),"emit-event",...emitArgs]}:{command:nonEmpty(env.BAPI_CONDUCTOR_BIN)?env.BAPI_CONDUCTOR_BIN.trim():"epic-implementer",args:["emit-event",...emitArgs]}}var defaultConductorSpawn=(command,args,input)=>{let result=spawnSync(command,args,{input,encoding:"utf-8",shell:!1});return{status:result.status,error:result.error}};function emitClaudeHookEventWithConductorCli(event,deps={}){let env=deps.env??process.env,spawn=deps.spawn??defaultConductorSpawn,emitArgs=buildConductorEmitEventArgs(event),{command,args}=resolveConductorEmitCommand(env,emitArgs),stdinPayload=JSON.stringify(event.data??{});try{let result=spawn(command,args,stdinPayload);return result.error?!1:result.status===0}catch{return!1}}function runClaudeConductorHookCli(deps={}){let env=deps.env??process.env,readStdin=deps.readStdin??(()=>readStdinSync()),warn=deps.warn??(m=>process.stderr.write(`${m}
3
- `)),payload;try{let raw=readStdin();payload=JSON.parse(raw)}catch{return warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING),0}let event;try{event=buildClaudeHookConductorEvent(payload,env)}catch{return warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING),0}return event===null||emitClaudeHookEventWithConductorCli(event,{env,spawn:deps.spawn})||warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING),0}function readStdinSync(){return readFileSync(0,"utf-8")}function runEpicImplementerClaudeHookBin(deps={}){let handler=deps.handler??runClaudeConductorHookCli,exit=deps.exit??process.exit.bind(process),exitCode=0;try{exitCode=handler()}catch{exitCode=0}exit(exitCode)}var invokedDirectly=process.argv[1]!==void 0&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);invokedDirectly&&runEpicImplementerClaudeHookBin();export{runEpicImplementerClaudeHookBin};
2
+ import path from"node:path";import{fileURLToPath}from"node:url";import{spawnSync}from"node:child_process";import{readFileSync}from"node:fs";var CONDUCTOR_HOOK_EMIT_FAILED_WARNING="Warning: conductor hook emit failed.";function resolveClaudeHookEventName(payload){if(payload===null||typeof payload!="object"||Array.isArray(payload))return null;let record=payload,candidates=["hook_event_name","hookEventName","event_name","event"];for(let key of candidates){let value=record[key];if(typeof value=="string"&&value.trim().length>0)return value.trim()}return null}function mapClaudeHookEventToSemanticType(eventName){switch(eventName){case"SessionStart":return"run.started";case"SessionEnd":return"run.stopped";case"Stop":return null;case"SubagentStop":return null;case"Notification":return"agent.notification";case"PreToolUse":return"tool.intent";default:return null}}var REQUIRED_IDENTITY_ENV=["BAPI_EPIC_IMPLEMENTER_RUN_ID","BAPI_EPIC_IMPLEMENTER_WORKER_ID","BAPI_EPIC_IMPLEMENTER_TICKET_KEY","BAPI_EPIC_IMPLEMENTER_WORKTREE_PATH"];function nonEmpty(value){return typeof value=="string"&&value.trim().length>0}function buildClaudeHookConductorEvent(payload,env){let eventName=resolveClaudeHookEventName(payload),type=mapClaudeHookEventToSemanticType(eventName);if(type===null||env.BAPI_EPIC_IMPLEMENTER_ENABLED!=="1")return null;for(let key of REQUIRED_IDENTITY_ENV)if(!nonEmpty(env[key]))return null;let ticketKey=env.BAPI_EPIC_IMPLEMENTER_TICKET_KEY.trim(),worktreePath=env.BAPI_EPIC_IMPLEMENTER_WORKTREE_PATH.trim(),details={ticket_key:ticketKey,worktree_path:worktreePath};nonEmpty(env.BAPI_EPIC_IMPLEMENTER_REPO_NAME)&&(details.repo=env.BAPI_EPIC_IMPLEMENTER_REPO_NAME.trim());let raw=payload!==null&&typeof payload=="object"&&!Array.isArray(payload)?payload:{payload};return{source:"claude-code",type,subject:ticketKey,run_id:env.BAPI_EPIC_IMPLEMENTER_RUN_ID.trim(),worker_id:env.BAPI_EPIC_IMPLEMENTER_WORKER_ID.trim(),producer:"claude-code-hook",observed_via:"claude-code-hook",data:{details,raw}}}function buildConductorEmitEventArgs(event){let args=["--type",event.type,"--source",event.source];return nonEmpty(event.subject??void 0)&&args.push("--subject",event.subject),nonEmpty(event.run_id??void 0)&&args.push("--run-id",event.run_id),nonEmpty(event.worker_id??void 0)&&args.push("--worker-id",event.worker_id),nonEmpty(event.producer??void 0)&&args.push("--producer",event.producer),nonEmpty(event.observed_via??void 0)&&args.push("--observed-via",event.observed_via),args.push("--data-json-stdin","--json"),args}function resolveConductorEmitCommand(env,emitArgs){let cliFile=env.BAPI_EPIC_IMPLEMENTER_CLI_FILE;return nonEmpty(cliFile)?{command:process.execPath,args:[cliFile.trim(),"emit-event",...emitArgs]}:{command:nonEmpty(env.BAPI_EPIC_IMPLEMENTER_BIN)?env.BAPI_EPIC_IMPLEMENTER_BIN.trim():"epic-implementer",args:["emit-event",...emitArgs]}}var defaultConductorSpawn=(command,args,input)=>{let result=spawnSync(command,args,{input,encoding:"utf-8",shell:!1});return{status:result.status,error:result.error}};function emitClaudeHookEventWithConductorCli(event,deps={}){let env=deps.env??process.env,spawn=deps.spawn??defaultConductorSpawn,emitArgs=buildConductorEmitEventArgs(event),{command,args}=resolveConductorEmitCommand(env,emitArgs),stdinPayload=JSON.stringify(event.data??{});try{let result=spawn(command,args,stdinPayload);return result.error?!1:result.status===0}catch{return!1}}function runClaudeConductorHookCli(deps={}){let env=deps.env??process.env,readStdin=deps.readStdin??(()=>readStdinSync()),warn=deps.warn??(m=>process.stderr.write(`${m}
3
+ `)),payload;try{let raw=readStdin();payload=JSON.parse(raw)}catch{return warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING),0}let event;try{event=buildClaudeHookConductorEvent(payload,env)}catch{return warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING),0}return event===null||emitClaudeHookEventWithConductorCli(event,{env,spawn:deps.spawn})||warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING),0}function readStdinSync(){return readFileSync(0,"utf-8")}var LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX="BAPI_CONDUCTOR_",EPIC_IMPLEMENTER_ENV_PREFIX="BAPI_EPIC_IMPLEMENTER_",LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV="CONDUCTOR_NODE_PATH",EPIC_IMPLEMENTER_NODE_PATH_ENV="EPIC_IMPLEMENTER_NODE_PATH",renamed=suffix=>[`${LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX}${suffix}`,{kind:"renamed",successor:`${EPIC_IMPLEMENTER_ENV_PREFIX}${suffix}`}],RENAMED_SUFFIXES=["ALLOW_LIVE_SOURCE_DISPATCH","BIN","BUSY_TIMEOUT_MS","CLI_FILE","DEAD_AFTER_MS","DECLARED_TOUCHED_FILES_JSON","DENY_PROBE_TIMEOUT_MS","DISPATCH_DRY_RUN","ENABLE_PRE_TOOL_USE","ENABLED","EPIC_KEY","EPIC_RUN_ID","ESCALATION_COOLDOWN_MS","GATE_NAME","GLOBAL_TIMEOUT_MS","HOOK_COMMAND","LIVE_SOURCE_PATH","LIVENESS_STALLED_AFTER_MS","MESSAGE_COOLDOWN_MS","PLAN_VERSION","QUIET_AFTER_MS","REPO_NAME","RETENTION_DAYS","RETENTION_MAX_ROWS","RUN_ID","STALL_ACTIVE_MS","STALL_BLOCKED_MS","STALL_CANDIDATE_DONE_MS","STALL_NOT_STARTED_MS","STALL_STALLED_MS","STALL_UNKNOWN_MS","STALL_VERIFYING_MS","SUPERVISOR_MODE","SUPERVISOR_POLL_LIMIT","TICKET_KEY","WAKE_INTERVAL_MS","WORKER_ID","WORKTREE_PATH"],LEGACY_EPIC_IMPLEMENTER_ENV_MAPPINGS=new Map([...RENAMED_SUFFIXES.map(renamed),[`${LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX}ALLOW_CLAUDE_MCP_SHADOWING`,{kind:"retired"}],[LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV,{kind:"renamed",successor:EPIC_IMPLEMENTER_NODE_PATH_ENV}]]);function isLegacyEpicImplementerEnvKey(key){return key.startsWith(LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX)||key===LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV}function findLegacyEpicImplementerEnv(env){return Object.keys(env).filter(isLegacyEpicImplementerEnvKey).sort((a,b)=>a<b?-1:a>b?1:0).map(key=>({key,mapping:LEGACY_EPIC_IMPLEMENTER_ENV_MAPPINGS.get(key)??null}))}var LEGACY_EPIC_IMPLEMENTER_ENV_ERROR_HEADER="Error: legacy Conductor environment variables are set. They were renamed to the Epic Implementer names and are no longer read. Update or remove each one, then retry:";function describeFinding(finding){let{key,mapping}=finding;return mapping===null?` ${key}: unknown legacy setting. If it is still a current setting, rename the ${LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX} prefix to ${EPIC_IMPLEMENTER_ENV_PREFIX}; otherwise remove it.`:mapping.kind==="retired"?` ${key}: retired with no replacement. Remove it.`:` ${key} -> ${mapping.successor}`}function formatLegacyEpicImplementerEnvError(findings){return[LEGACY_EPIC_IMPLEMENTER_ENV_ERROR_HEADER,...findings.map(describeFinding)].join(`
4
+ `)+`
5
+ `}function runLegacyEpicImplementerEnvPreflight(env,writeStderr){let findings=findLegacyEpicImplementerEnv(env);return findings.length===0?0:(writeStderr(formatLegacyEpicImplementerEnvError(findings)),1)}function runEpicImplementerClaudeHookBin(deps={}){let handler=deps.handler??runClaudeConductorHookCli,exit=deps.exit??process.exit.bind(process),writeStderr=deps.writeStderr??(chunk=>process.stderr.write(chunk));if(runLegacyEpicImplementerEnvPreflight(deps.env??process.env,writeStderr)!==0){exit(0);return}let exitCode=0;try{exitCode=handler()}catch{exitCode=0}exit(exitCode)}var invokedDirectly=process.argv[1]!==void 0&&path.resolve(process.argv[1])===fileURLToPath(import.meta.url);invokedDirectly&&runEpicImplementerClaudeHookBin();export{runEpicImplementerClaudeHookBin};
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Legacy Epic Implementer environment guard (BAPI-1190).
3
+ *
4
+ * Every TypeScript `BAPI_CONDUCTOR_*` environment variable was renamed to
5
+ * `BAPI_EPIC_IMPLEMENTER_*`, and the worker Node-path variable
6
+ * `CONDUCTOR_NODE_PATH` to `EPIC_IMPLEMENTER_NODE_PATH`. The rule is a pure
7
+ * token swap: replace `CONDUCTOR` with `EPIC_IMPLEMENTER` and keep the rest of
8
+ * the name. There are no compatibility aliases and no dual reads (epic rule
9
+ * R-3, clean break).
10
+ *
11
+ * A clean break must not be a silent one. An operator who still exports an old
12
+ * name would otherwise see the setting quietly ignored. So every guarded
13
+ * process entry point calls {@link runLegacyEpicImplementerEnvPreflight} before
14
+ * any side effect, and refuses to start when an old name is present.
15
+ *
16
+ * Contract:
17
+ * - Detection is prefix-based: ANY key starting with `BAPI_CONDUCTOR_` is
18
+ * rejected, whether or not the table below knows it. `CONDUCTOR_NODE_PATH`
19
+ * is rejected by exact name (it has no `BAPI_` prefix).
20
+ * - A key is rejected on presence alone, whatever its value (including empty).
21
+ * - All offending keys are reported in one message, sorted by code point.
22
+ * - Diagnostics carry key names only. An env value is never read into the
23
+ * findings, so it can never be printed — these variables can hold secrets.
24
+ * - The env is injectable, so tests never mutate the real process environment.
25
+ *
26
+ * This module is the ONLY non-test source file allowed to spell an old name.
27
+ * Other modules import the prefix and node-path constants from here.
28
+ *
29
+ * Out of scope: the Python dead-man variables (`CONDUCTOR_DEAD_MAN_ONLY`,
30
+ * `CONDUCTOR_DEADMAN_*`) keep their own exact-name contract (BAPI-1231), and
31
+ * non-env tokens such as `BRIDGE_CONDUCTOR_HOOK_START` or
32
+ * `CURRENT_CONDUCTOR_SCHEMA_VERSION` are never checked.
33
+ */
34
+ /** Legacy env-var prefix. Any key with this prefix is refused. */
35
+ export const LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX = "BAPI_CONDUCTOR_";
36
+ /** Supported env-var prefix that replaced {@link LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX}. */
37
+ export const EPIC_IMPLEMENTER_ENV_PREFIX = "BAPI_EPIC_IMPLEMENTER_";
38
+ /** Legacy worker Node-path variable, refused by exact name. */
39
+ export const LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV = "CONDUCTOR_NODE_PATH";
40
+ /** Supported worker Node-path variable that replaced {@link LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV}. */
41
+ export const EPIC_IMPLEMENTER_NODE_PATH_ENV = "EPIC_IMPLEMENTER_NODE_PATH";
42
+ const renamed = (suffix) => [
43
+ `${LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX}${suffix}`,
44
+ { kind: "renamed", successor: `${EPIC_IMPLEMENTER_ENV_PREFIX}${suffix}` },
45
+ ];
46
+ /**
47
+ * The 38 live renamed `BAPI_CONDUCTOR_*` suffixes, as inventoried from non-test
48
+ * `mcp_server/src/**\/*.ts` at HEAD `45d854081` (re-confirmed at `315572187`).
49
+ *
50
+ * Deliberately absent: `PUBLISHED_IDENTITY_FIXTURE` (removed from source), the
51
+ * retired `LLM_*` names (their literals are forbidden in production source by
52
+ * `supervisor-retired-judgment.static.test.ts`), and test-only sentinels. All of
53
+ * those still fall through to the generic prefix pointer.
54
+ */
55
+ const RENAMED_SUFFIXES = [
56
+ "ALLOW_LIVE_SOURCE_DISPATCH",
57
+ "BIN",
58
+ "BUSY_TIMEOUT_MS",
59
+ "CLI_FILE",
60
+ "DEAD_AFTER_MS",
61
+ "DECLARED_TOUCHED_FILES_JSON",
62
+ "DENY_PROBE_TIMEOUT_MS",
63
+ "DISPATCH_DRY_RUN",
64
+ "ENABLE_PRE_TOOL_USE",
65
+ "ENABLED",
66
+ "EPIC_KEY",
67
+ "EPIC_RUN_ID",
68
+ "ESCALATION_COOLDOWN_MS",
69
+ "GATE_NAME",
70
+ "GLOBAL_TIMEOUT_MS",
71
+ "HOOK_COMMAND",
72
+ "LIVE_SOURCE_PATH",
73
+ "LIVENESS_STALLED_AFTER_MS",
74
+ "MESSAGE_COOLDOWN_MS",
75
+ "PLAN_VERSION",
76
+ "QUIET_AFTER_MS",
77
+ "REPO_NAME",
78
+ "RETENTION_DAYS",
79
+ "RETENTION_MAX_ROWS",
80
+ "RUN_ID",
81
+ "STALL_ACTIVE_MS",
82
+ "STALL_BLOCKED_MS",
83
+ "STALL_CANDIDATE_DONE_MS",
84
+ "STALL_NOT_STARTED_MS",
85
+ "STALL_STALLED_MS",
86
+ "STALL_UNKNOWN_MS",
87
+ "STALL_VERIFYING_MS",
88
+ "SUPERVISOR_MODE",
89
+ "SUPERVISOR_POLL_LIMIT",
90
+ "TICKET_KEY",
91
+ "WAKE_INTERVAL_MS",
92
+ "WORKER_ID",
93
+ "WORKTREE_PATH",
94
+ ];
95
+ /**
96
+ * Old name -> resolution. Exactly 40 keys: the 38 renamed names above, the
97
+ * retired shadowing override (inert since BAPI-790; this supersedes that
98
+ * ticket's "no error" promise), and the exact-name node-path variable.
99
+ */
100
+ export const LEGACY_EPIC_IMPLEMENTER_ENV_MAPPINGS = new Map([
101
+ ...RENAMED_SUFFIXES.map(renamed),
102
+ [`${LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX}ALLOW_CLAUDE_MCP_SHADOWING`, { kind: "retired" }],
103
+ [
104
+ LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV,
105
+ { kind: "renamed", successor: EPIC_IMPLEMENTER_NODE_PATH_ENV },
106
+ ],
107
+ ]);
108
+ /** True when `key` is a legacy env name this guard refuses. */
109
+ export function isLegacyEpicImplementerEnvKey(key) {
110
+ return key.startsWith(LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX) || key === LEGACY_EPIC_IMPLEMENTER_NODE_PATH_ENV;
111
+ }
112
+ /**
113
+ * Pure finder: env in, sorted findings out. Reads keys only, so a key is found
114
+ * on presence even when its value is `undefined` or empty.
115
+ */
116
+ export function findLegacyEpicImplementerEnv(env) {
117
+ return Object.keys(env)
118
+ .filter(isLegacyEpicImplementerEnvKey)
119
+ .sort((a, b) => (a < b ? -1 : a > b ? 1 : 0))
120
+ .map((key) => ({ key, mapping: LEGACY_EPIC_IMPLEMENTER_ENV_MAPPINGS.get(key) ?? null }));
121
+ }
122
+ /** Fixed first line of the refusal. */
123
+ export const LEGACY_EPIC_IMPLEMENTER_ENV_ERROR_HEADER = "Error: legacy Conductor environment variables are set. They were renamed to the Epic Implementer names and are no longer read. Update or remove each one, then retry:";
124
+ function describeFinding(finding) {
125
+ const { key, mapping } = finding;
126
+ if (mapping === null) {
127
+ return (` ${key}: unknown legacy setting. If it is still a current setting, rename the ` +
128
+ `${LEGACY_EPIC_IMPLEMENTER_ENV_PREFIX} prefix to ${EPIC_IMPLEMENTER_ENV_PREFIX}; otherwise remove it.`);
129
+ }
130
+ if (mapping.kind === "retired") {
131
+ return ` ${key}: retired with no replacement. Remove it.`;
132
+ }
133
+ return ` ${key} -> ${mapping.successor}`;
134
+ }
135
+ /**
136
+ * Format the refusal: one fixed header, one line per finding (in the order
137
+ * given), and a trailing newline. Only key names and table metadata appear.
138
+ */
139
+ export function formatLegacyEpicImplementerEnvError(findings) {
140
+ return [LEGACY_EPIC_IMPLEMENTER_ENV_ERROR_HEADER, ...findings.map(describeFinding)].join("\n") + "\n";
141
+ }
142
+ /**
143
+ * Process-entry preflight. On a hit, writes the complete diagnostic to
144
+ * `writeStderr` exactly once and returns `1`; otherwise writes nothing and
145
+ * returns `0`. It never exits, throws, or touches the filesystem, network, or
146
+ * the global process environment — the caller owns the exit code (the Claude hook bin
147
+ * deliberately exits `0` on a hit).
148
+ */
149
+ export function runLegacyEpicImplementerEnvPreflight(env, writeStderr) {
150
+ const findings = findLegacyEpicImplementerEnv(env);
151
+ if (findings.length === 0)
152
+ return 0;
153
+ writeStderr(formatLegacyEpicImplementerEnvError(findings));
154
+ return 1;
155
+ }
@@ -7,7 +7,7 @@
7
7
  * branches" 422 from a credential or provider failure — both of which this
8
8
  * helper must classify without throwing.
9
9
  *
10
- * A draft PR still receives the `conductor-ci / gate` required check (BAPI-949's
10
+ * A draft PR still receives the `epic-implementer-ci / gate` required check (BAPI-949's
11
11
  * trigger has no draft exclusion), while `claude-review.yml` excludes drafts
12
12
  * until the PR is marked ready. So opening the integration PR as a draft earns
13
13
  * the epic branch its required CI immediately, without paying for a paid review
@@ -62,7 +62,7 @@ export function buildEpicIntegrationPullRequestBody(epicKey, command) {
62
62
  return [
63
63
  `\`${command}\` opened this pull request automatically.`,
64
64
  "",
65
- `It exists so the \`conductor-ci / gate\` required check runs for the ${epicKey} ` +
65
+ `It exists so the \`epic-implementer-ci / gate\` required check runs for the ${epicKey} ` +
66
66
  "epic integration branch (BAPI-949). Draft status postpones the paid Claude " +
67
67
  "review until a human marks this pull request ready for review.",
68
68
  "",