@bridge_gpt/mcp-server 0.2.2 → 0.2.3

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 (113) hide show
  1. package/README.md +97 -15
  2. package/build/agent-config-credential-migration.js +272 -0
  3. package/build/agents.generated.js +1 -1
  4. package/build/chain-orchestrator.js +16 -1
  5. package/build/commands.generated.js +9 -7
  6. package/build/conductor/bridge-api-client.js +625 -0
  7. package/build/conductor/claude-hook.js +251 -0
  8. package/build/conductor/cli.js +1048 -0
  9. package/build/conductor/data-normalization.js +114 -0
  10. package/build/conductor/doctor.js +164 -0
  11. package/build/conductor/done-gate.js +325 -0
  12. package/build/conductor/epic-reconcile.js +139 -0
  13. package/build/conductor/epic-runtime.js +611 -0
  14. package/build/conductor/epic-state.js +125 -0
  15. package/build/conductor/errors.js +85 -0
  16. package/build/conductor/git-ci-types.js +129 -0
  17. package/build/conductor/git-hooks.js +218 -0
  18. package/build/conductor/git-inspection.js +185 -0
  19. package/build/conductor/git-producer.js +137 -0
  20. package/build/conductor/merge-ledger.js +198 -0
  21. package/build/conductor/paths.js +224 -0
  22. package/build/conductor/plan.js +77 -0
  23. package/build/conductor/pr-ci-producer.js +427 -0
  24. package/build/conductor/pr-discovery.js +135 -0
  25. package/build/conductor/producer-ledger.js +125 -0
  26. package/build/conductor/redaction.js +112 -0
  27. package/build/conductor/store.js +1156 -0
  28. package/build/conductor/supervisor-config.js +150 -0
  29. package/build/conductor/supervisor-escalation.js +244 -0
  30. package/build/conductor/supervisor-judgment-python.js +141 -0
  31. package/build/conductor/supervisor-judgment.js +215 -0
  32. package/build/conductor/supervisor-ledger.js +119 -0
  33. package/build/conductor/supervisor-merge.js +127 -0
  34. package/build/conductor/supervisor-message-relay.js +61 -0
  35. package/build/conductor/supervisor-notification.js +39 -0
  36. package/build/conductor/supervisor-runtime.js +351 -0
  37. package/build/conductor/supervisor-state.js +572 -0
  38. package/build/conductor/supervisor-types.js +16 -0
  39. package/build/conductor/taxonomy.js +58 -0
  40. package/build/conductor/tools.js +367 -0
  41. package/build/conductor/types.js +9 -0
  42. package/build/conductor-bin.js +21 -0
  43. package/build/conductor-claude-hook-bin.js +21 -0
  44. package/build/credential-store.js +175 -4
  45. package/build/credentials-cli.js +223 -0
  46. package/build/decision-page-schema.js +60 -0
  47. package/build/decision-page-template.js +262 -10
  48. package/build/doctor.js +5 -1
  49. package/build/index.js +468 -59
  50. package/build/pipeline-orchestrator.js +5 -1
  51. package/build/pipeline-utils.js +45 -5
  52. package/build/pipelines.generated.js +37 -9
  53. package/build/readme.generated.js +1 -1
  54. package/build/review-tickets.js +596 -0
  55. package/build/scheduled-prompt.js +16 -10
  56. package/build/start-tickets-conductor.js +496 -0
  57. package/build/start-tickets-prereqs.js +32 -23
  58. package/build/start-tickets-repo.js +49 -0
  59. package/build/start-tickets.js +682 -81
  60. package/build/version.generated.js +1 -1
  61. package/design-assets/favicon/android-chrome-192x192.png +0 -0
  62. package/design-assets/favicon/android-chrome-512x512.png +0 -0
  63. package/design-assets/favicon/apple-touch-icon.png +0 -0
  64. package/design-assets/favicon/favicon-16x16.png +0 -0
  65. package/design-assets/favicon/favicon-32x32.png +0 -0
  66. package/design-assets/favicon/favicon.ico +0 -0
  67. package/design-assets/favicon/site.webmanifest +1 -0
  68. package/design-assets/just-logo-rough-draft.png +0 -0
  69. package/package.json +17 -5
  70. package/pipelines/idea-to-ticket.json +5 -0
  71. package/pipelines/plan-epic.json +16 -1
  72. package/pipelines/review-ticket.json +2 -1
  73. package/public/css/main.min.css +2 -0
  74. package/public/css/main.min.css.map +1 -0
  75. package/public/fonts/OFL.txt +93 -0
  76. package/public/fonts/SourceSansPro-Black.ttf +0 -0
  77. package/public/fonts/SourceSansPro-BlackItalic.ttf +0 -0
  78. package/public/fonts/SourceSansPro-Bold.ttf +0 -0
  79. package/public/fonts/SourceSansPro-BoldItalic.ttf +0 -0
  80. package/public/fonts/SourceSansPro-ExtraLight.ttf +0 -0
  81. package/public/fonts/SourceSansPro-ExtraLightItalic.ttf +0 -0
  82. package/public/fonts/SourceSansPro-Italic.ttf +0 -0
  83. package/public/fonts/SourceSansPro-Light.ttf +0 -0
  84. package/public/fonts/SourceSansPro-LightItalic.ttf +0 -0
  85. package/public/fonts/SourceSansPro-Regular.ttf +0 -0
  86. package/public/fonts/SourceSansPro-SemiBold.ttf +0 -0
  87. package/public/fonts/SourceSansPro-SemiBoldItalic.ttf +0 -0
  88. package/public/img/bridge-logo-160x51.webp +0 -0
  89. package/public/img/bridge-logo-300x92.webp +0 -0
  90. package/public/img/favicon/android-chrome-192x192.png +0 -0
  91. package/public/img/favicon/android-chrome-512x512.png +0 -0
  92. package/public/img/favicon/apple-touch-icon.png +0 -0
  93. package/public/img/favicon/favicon-16x16.png +0 -0
  94. package/public/img/favicon/favicon-32x32.png +0 -0
  95. package/public/img/favicon/favicon.ico +0 -0
  96. package/public/img/favicon/site.webmanifest +1 -0
  97. package/public/img/installation/bitbucket/app-password-1.png +0 -0
  98. package/public/img/installation/bitbucket/app-password-2.png +0 -0
  99. package/public/img/installation/bitbucket/create-token-1.png +0 -0
  100. package/public/img/installation/bitbucket/create-token-2.png +0 -0
  101. package/public/img/installation/bitbucket/webhook-1.png +0 -0
  102. package/public/img/installation/github/github-review-webhook.png +0 -0
  103. package/public/img/installation/jira/credentials/api-key.png +0 -0
  104. package/public/img/installation/jira/webhook/create-rule.png +0 -0
  105. package/public/img/installation/jira/webhook/project-settings.png +0 -0
  106. package/public/img/installation/jira/webhook/rule-create-1.png +0 -0
  107. package/public/img/installation/jira/webhook/rule-create-2.png +0 -0
  108. package/public/img/installation/jira/webhook/rule-create-3.png +0 -0
  109. package/public/img/installation/pinecone/pinecone-api-key.png +0 -0
  110. package/public/img/installation/pinecone/pinecone-index.png +0 -0
  111. package/public/js/main.min.js +2 -0
  112. package/public/js/main.min.js.map +1 -0
  113. package/smoke-test/SMOKE-TEST.md +16 -8
@@ -0,0 +1,251 @@
1
+ /**
2
+ * Claude Code → conductor lifecycle hook writer.
3
+ *
4
+ * This is a dependency-light command-hook entrypoint registered into a spawned
5
+ * Claude worker's `.claude/settings.local.json` by `start-tickets`. Claude Code
6
+ * invokes it for lifecycle events (SessionStart, Stop, SubagentStop,
7
+ * Notification, optionally PreToolUse), passing the native hook JSON on stdin.
8
+ * The writer:
9
+ *
10
+ * 1. reads + parses the native Claude hook payload from stdin,
11
+ * 2. maps the Claude lifecycle event to a canonical conductor semantic type,
12
+ * 3. builds a normalized {@link ConductorEventInput} — Claude-native fields are
13
+ * kept ONLY under `data.raw`, never promoted to the envelope or normalized
14
+ * data fields,
15
+ * 4. shells out to the local `conductor emit-event` CLI using list-based
16
+ * subprocess arguments (never `shell: true`) and writes the normalized
17
+ * `data` object to the child's stdin via `--data-json-stdin`, so raw
18
+ * payloads and possible secrets never appear in the process argument list.
19
+ *
20
+ * The hook is strictly non-blocking: unmapped events, missing conductor
21
+ * identity, parse failures, and emission failures all resolve to exit code `0`
22
+ * with at most a generic, secret-free warning so the Claude tool loop is never
23
+ * blocked or polluted with diagnostics.
24
+ */
25
+ import { spawnSync } from "node:child_process";
26
+ import { readFileSync } from "node:fs";
27
+ /** Generic, secret-free warning emitted when an emission attempt fails. */
28
+ export const CONDUCTOR_HOOK_EMIT_FAILED_WARNING = "Warning: conductor hook emit failed.";
29
+ /**
30
+ * Extract the Claude lifecycle event name from a hook payload. The verified
31
+ * Claude hook contract field (`hook_event_name`) takes priority; a small set of
32
+ * defensive alternatives is accepted only as internal parsing inputs in case the
33
+ * field name drifts. Returns `null` when no recognizable name is present.
34
+ */
35
+ export function resolveClaudeHookEventName(payload) {
36
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
37
+ return null;
38
+ }
39
+ const record = payload;
40
+ // Verified contract field first, then defensive-only alternatives.
41
+ const candidates = ["hook_event_name", "hookEventName", "event_name", "event"];
42
+ for (const key of candidates) {
43
+ const value = record[key];
44
+ if (typeof value === "string" && value.trim().length > 0) {
45
+ return value.trim();
46
+ }
47
+ }
48
+ return null;
49
+ }
50
+ /**
51
+ * Map a Claude lifecycle event name to a canonical conductor semantic type.
52
+ * Unknown events map to `null` (the caller then skips emission).
53
+ */
54
+ export function mapClaudeHookEventToSemanticType(eventName) {
55
+ switch (eventName) {
56
+ case "SessionStart":
57
+ return "run.started";
58
+ case "Stop":
59
+ return "run.stopped";
60
+ case "SubagentStop":
61
+ return "run.stopped";
62
+ case "Notification":
63
+ return "agent.notification";
64
+ case "PreToolUse":
65
+ return "tool.intent";
66
+ default:
67
+ return null;
68
+ }
69
+ }
70
+ /** Conductor identity env required before any worker event may be emitted. */
71
+ const REQUIRED_IDENTITY_ENV = [
72
+ "BAPI_CONDUCTOR_RUN_ID",
73
+ "BAPI_CONDUCTOR_WORKER_ID",
74
+ "BAPI_CONDUCTOR_TICKET_KEY",
75
+ "BAPI_CONDUCTOR_WORKTREE_PATH",
76
+ ];
77
+ function nonEmpty(value) {
78
+ return typeof value === "string" && value.trim().length > 0;
79
+ }
80
+ /**
81
+ * Build a normalized {@link ConductorEventInput} from a Claude hook payload and
82
+ * the process environment, or `null` when the event is unmapped or the
83
+ * conductor identity is incomplete.
84
+ *
85
+ * Identity gating: requires `BAPI_CONDUCTOR_ENABLED=1` and all of
86
+ * {@link REQUIRED_IDENTITY_ENV}. Any missing value returns `null` WITHOUT
87
+ * emitting — this prevents accidental emissions from unrelated worktrees that
88
+ * happen to have the hook on PATH.
89
+ *
90
+ * Secret/normalization boundary: the COMPLETE native Claude payload is placed
91
+ * only under `data.raw`. No Claude-native field (session id, transcript path,
92
+ * tool name, tool input, notification text) is promoted to the envelope or to
93
+ * any normalized `data` field.
94
+ */
95
+ export function buildClaudeHookConductorEvent(payload, env) {
96
+ const eventName = resolveClaudeHookEventName(payload);
97
+ const type = mapClaudeHookEventToSemanticType(eventName);
98
+ if (type === null) {
99
+ return null;
100
+ }
101
+ if (env.BAPI_CONDUCTOR_ENABLED !== "1") {
102
+ return null;
103
+ }
104
+ for (const key of REQUIRED_IDENTITY_ENV) {
105
+ if (!nonEmpty(env[key])) {
106
+ return null;
107
+ }
108
+ }
109
+ const ticketKey = env.BAPI_CONDUCTOR_TICKET_KEY.trim();
110
+ const worktreePath = env.BAPI_CONDUCTOR_WORKTREE_PATH.trim();
111
+ const details = {
112
+ ticket_key: ticketKey,
113
+ worktree_path: worktreePath,
114
+ };
115
+ if (nonEmpty(env.BAPI_CONDUCTOR_REPO_NAME)) {
116
+ details.repo = env.BAPI_CONDUCTOR_REPO_NAME.trim();
117
+ }
118
+ // The native payload lives ONLY under data.raw. Wrap non-object payloads so the
119
+ // raw contract is always an object, never echoing native fields elsewhere.
120
+ const raw = payload !== null && typeof payload === "object" && !Array.isArray(payload)
121
+ ? payload
122
+ : { payload };
123
+ return {
124
+ source: "claude-code",
125
+ type,
126
+ subject: ticketKey,
127
+ run_id: env.BAPI_CONDUCTOR_RUN_ID.trim(),
128
+ worker_id: env.BAPI_CONDUCTOR_WORKER_ID.trim(),
129
+ producer: "claude-code-hook",
130
+ observed_via: "claude-code-hook",
131
+ data: {
132
+ details,
133
+ raw,
134
+ },
135
+ };
136
+ }
137
+ /**
138
+ * Build the list-based `emit-event` arguments for a normalized event. The
139
+ * normalized `data` object is intentionally NOT serialized into the argument
140
+ * list — it is delivered to the child via stdin through `--data-json-stdin`, so
141
+ * raw payloads/secrets never reach the process table.
142
+ */
143
+ export function buildConductorEmitEventArgs(event) {
144
+ const args = ["--type", event.type, "--source", event.source];
145
+ if (nonEmpty(event.subject ?? undefined))
146
+ args.push("--subject", event.subject);
147
+ if (nonEmpty(event.run_id ?? undefined))
148
+ args.push("--run-id", event.run_id);
149
+ if (nonEmpty(event.worker_id ?? undefined))
150
+ args.push("--worker-id", event.worker_id);
151
+ if (nonEmpty(event.producer ?? undefined))
152
+ args.push("--producer", event.producer);
153
+ if (nonEmpty(event.observed_via ?? undefined)) {
154
+ args.push("--observed-via", event.observed_via);
155
+ }
156
+ args.push("--data-json-stdin", "--json");
157
+ return args;
158
+ }
159
+ /**
160
+ * Resolve how to invoke `conductor emit-event`. Prefers `BAPI_CONDUCTOR_CLI_FILE`
161
+ * (run via the current Node executable so no global install is required),
162
+ * otherwise falls back to `BAPI_CONDUCTOR_BIN` or the bare `conductor` binary on
163
+ * PATH. `emitArgs` are appended after the `emit-event` subcommand.
164
+ */
165
+ export function resolveConductorEmitCommand(env, emitArgs) {
166
+ const cliFile = env.BAPI_CONDUCTOR_CLI_FILE;
167
+ if (nonEmpty(cliFile)) {
168
+ return {
169
+ command: process.execPath,
170
+ args: [cliFile.trim(), "emit-event", ...emitArgs],
171
+ };
172
+ }
173
+ const bin = nonEmpty(env.BAPI_CONDUCTOR_BIN) ? env.BAPI_CONDUCTOR_BIN.trim() : "conductor";
174
+ return { command: bin, args: ["emit-event", ...emitArgs] };
175
+ }
176
+ const defaultConductorSpawn = (command, args, input) => {
177
+ const result = spawnSync(command, args, {
178
+ input,
179
+ encoding: "utf-8",
180
+ // Never use a shell: list args + stdin keep raw payloads/secrets out of any
181
+ // shell-interpreted command string.
182
+ shell: false,
183
+ });
184
+ return { status: result.status, error: result.error };
185
+ };
186
+ /**
187
+ * Emit a normalized conductor event by shelling to the local `conductor`
188
+ * CLI. The normalized `data` object is written to the child's stdin; envelope
189
+ * fields are passed as list arguments. Returns `true` on a clean exit, `false`
190
+ * on any spawn error or non-zero exit. Never throws.
191
+ */
192
+ export function emitClaudeHookEventWithConductorCli(event, deps = {}) {
193
+ const env = deps.env ?? process.env;
194
+ const spawn = deps.spawn ?? defaultConductorSpawn;
195
+ const emitArgs = buildConductorEmitEventArgs(event);
196
+ const { command, args } = resolveConductorEmitCommand(env, emitArgs);
197
+ // Only the normalized data object crosses the stdin boundary (mirrors the
198
+ // CLI's --data-json-stdin contract); raw payload material is inside it.
199
+ const stdinPayload = JSON.stringify(event.data ?? {});
200
+ try {
201
+ const result = spawn(command, args, stdinPayload);
202
+ if (result.error)
203
+ return false;
204
+ return result.status === 0;
205
+ }
206
+ catch {
207
+ return false;
208
+ }
209
+ }
210
+ /**
211
+ * Full hook runner: read the native Claude payload from stdin, map/normalize it,
212
+ * and emit via the conductor CLI. ALWAYS returns `0` (success) — unmapped
213
+ * events, missing identity, invalid JSON, and emission failures are swallowed so
214
+ * the Claude tool loop is never blocked. Any warning is generic and secret-free.
215
+ */
216
+ export function runClaudeConductorHookCli(deps = {}) {
217
+ const env = deps.env ?? process.env;
218
+ const readStdin = deps.readStdin ?? (() => readStdinSync());
219
+ const warn = deps.warn ?? ((m) => process.stderr.write(`${m}\n`));
220
+ let payload;
221
+ try {
222
+ const raw = readStdin();
223
+ payload = JSON.parse(raw);
224
+ }
225
+ catch {
226
+ // Invalid/empty hook JSON: warn generically (no raw fragment) and exit 0.
227
+ warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING);
228
+ return 0;
229
+ }
230
+ let event;
231
+ try {
232
+ event = buildClaudeHookConductorEvent(payload, env);
233
+ }
234
+ catch {
235
+ warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING);
236
+ return 0;
237
+ }
238
+ // Unmapped event or incomplete identity: nothing to emit, silently succeed.
239
+ if (event === null) {
240
+ return 0;
241
+ }
242
+ const ok = emitClaudeHookEventWithConductorCli(event, { env, spawn: deps.spawn });
243
+ if (!ok) {
244
+ warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING);
245
+ }
246
+ return 0;
247
+ }
248
+ /** Blocking read of the full stdin contents (fd 0). */
249
+ function readStdinSync() {
250
+ return readFileSync(0, "utf-8");
251
+ }