@bridge_gpt/mcp-server 0.2.26 → 0.2.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/commands.generated.js +1 -1
- package/build/conductor-bin.js +68 -9267
- package/build/conductor-claude-hook-bin.js +2 -172
- package/build/decision-page-template.js +2 -31
- package/build/index.js +4652 -37042
- package/build/init.js +80 -56
- package/build/install-bridge.js +36 -20
- package/build/version.generated.js +1 -1
- package/package.json +1 -3
- package/design-assets/favicon/android-chrome-192x192.png +0 -0
- package/design-assets/favicon/android-chrome-512x512.png +0 -0
- package/design-assets/favicon/apple-touch-icon.png +0 -0
- package/design-assets/favicon/favicon-16x16.png +0 -0
- package/design-assets/favicon/favicon.ico +0 -0
- package/design-assets/favicon/site.webmanifest +0 -1
- package/public/css/main.min.css +0 -4971
- package/public/css/main.min.css.map +0 -1
- package/public/fonts/OFL.txt +0 -93
- package/public/fonts/SourceSansPro-Black.ttf +0 -0
- package/public/fonts/SourceSansPro-BlackItalic.ttf +0 -0
- package/public/fonts/SourceSansPro-Bold.ttf +0 -0
- package/public/fonts/SourceSansPro-BoldItalic.ttf +0 -0
- package/public/fonts/SourceSansPro-ExtraLight.ttf +0 -0
- package/public/fonts/SourceSansPro-ExtraLightItalic.ttf +0 -0
- package/public/fonts/SourceSansPro-Italic.ttf +0 -0
- package/public/fonts/SourceSansPro-Light.ttf +0 -0
- package/public/fonts/SourceSansPro-LightItalic.ttf +0 -0
- package/public/fonts/SourceSansPro-Regular.ttf +0 -0
- package/public/fonts/SourceSansPro-SemiBold.ttf +0 -0
- package/public/fonts/SourceSansPro-SemiBoldItalic.ttf +0 -0
- package/public/img/bridge-logo-160x51.webp +0 -0
- package/public/img/bridge-logo-300x92.webp +0 -0
- package/public/img/favicon/android-chrome-192x192.png +0 -0
- package/public/img/favicon/android-chrome-512x512.png +0 -0
- package/public/img/favicon/apple-touch-icon.png +0 -0
- package/public/img/favicon/favicon-16x16.png +0 -0
- package/public/img/favicon/favicon-32x32.png +0 -0
- package/public/img/favicon/favicon.ico +0 -0
- package/public/img/favicon/site.webmanifest +0 -1
- package/public/img/installation/bitbucket/app-password-1.png +0 -0
- package/public/img/installation/bitbucket/app-password-2.png +0 -0
- package/public/img/installation/bitbucket/create-token-1.png +0 -0
- package/public/img/installation/bitbucket/create-token-2.png +0 -0
- package/public/img/installation/bitbucket/webhook-1.png +0 -0
- package/public/img/installation/github/github-review-webhook.png +0 -0
- package/public/img/installation/jira/credentials/api-key.png +0 -0
- package/public/img/installation/jira/webhook/create-rule.png +0 -0
- package/public/img/installation/jira/webhook/project-settings.png +0 -0
- package/public/img/installation/jira/webhook/rule-create-1.png +0 -0
- package/public/img/installation/jira/webhook/rule-create-2.png +0 -0
- package/public/img/installation/jira/webhook/rule-create-3.png +0 -0
- package/public/img/installation/pinecone/pinecone-api-key.png +0 -0
- package/public/img/installation/pinecone/pinecone-index.png +0 -0
- package/public/js/main.min.js +0 -10409
- package/public/js/main.min.js.map +0 -1
- package/smoke-test/SMOKE-TEST.md +0 -560
- package/smoke-test/smoke-test-mcp.md +0 -23
|
@@ -1,173 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
import { spawnSync } from "node:child_process";
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
|
-
var CONDUCTOR_HOOK_EMIT_FAILED_WARNING = "Warning: conductor hook emit failed.";
|
|
7
|
-
function resolveClaudeHookEventName(payload) {
|
|
8
|
-
if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
|
|
9
|
-
return null;
|
|
10
|
-
}
|
|
11
|
-
const record = payload;
|
|
12
|
-
const candidates = ["hook_event_name", "hookEventName", "event_name", "event"];
|
|
13
|
-
for (const key of candidates) {
|
|
14
|
-
const value = record[key];
|
|
15
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
16
|
-
return value.trim();
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
function mapClaudeHookEventToSemanticType(eventName) {
|
|
22
|
-
switch (eventName) {
|
|
23
|
-
case "SessionStart":
|
|
24
|
-
return "run.started";
|
|
25
|
-
case "SessionEnd":
|
|
26
|
-
return "run.stopped";
|
|
27
|
-
case "Stop":
|
|
28
|
-
return null;
|
|
29
|
-
case "SubagentStop":
|
|
30
|
-
return null;
|
|
31
|
-
case "Notification":
|
|
32
|
-
return "agent.notification";
|
|
33
|
-
case "PreToolUse":
|
|
34
|
-
return "tool.intent";
|
|
35
|
-
default:
|
|
36
|
-
return null;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
var REQUIRED_IDENTITY_ENV = [
|
|
40
|
-
"BAPI_CONDUCTOR_RUN_ID",
|
|
41
|
-
"BAPI_CONDUCTOR_WORKER_ID",
|
|
42
|
-
"BAPI_CONDUCTOR_TICKET_KEY",
|
|
43
|
-
"BAPI_CONDUCTOR_WORKTREE_PATH"
|
|
44
|
-
];
|
|
45
|
-
function nonEmpty(value) {
|
|
46
|
-
return typeof value === "string" && value.trim().length > 0;
|
|
47
|
-
}
|
|
48
|
-
function buildClaudeHookConductorEvent(payload, env) {
|
|
49
|
-
const eventName = resolveClaudeHookEventName(payload);
|
|
50
|
-
const type = mapClaudeHookEventToSemanticType(eventName);
|
|
51
|
-
if (type === null) {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
if (env.BAPI_CONDUCTOR_ENABLED !== "1") {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
for (const key of REQUIRED_IDENTITY_ENV) {
|
|
58
|
-
if (!nonEmpty(env[key])) {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
const ticketKey = env.BAPI_CONDUCTOR_TICKET_KEY.trim();
|
|
63
|
-
const worktreePath = env.BAPI_CONDUCTOR_WORKTREE_PATH.trim();
|
|
64
|
-
const details = {
|
|
65
|
-
ticket_key: ticketKey,
|
|
66
|
-
worktree_path: worktreePath
|
|
67
|
-
};
|
|
68
|
-
if (nonEmpty(env.BAPI_CONDUCTOR_REPO_NAME)) {
|
|
69
|
-
details.repo = env.BAPI_CONDUCTOR_REPO_NAME.trim();
|
|
70
|
-
}
|
|
71
|
-
const raw = payload !== null && typeof payload === "object" && !Array.isArray(payload) ? payload : { payload };
|
|
72
|
-
return {
|
|
73
|
-
source: "claude-code",
|
|
74
|
-
type,
|
|
75
|
-
subject: ticketKey,
|
|
76
|
-
run_id: env.BAPI_CONDUCTOR_RUN_ID.trim(),
|
|
77
|
-
worker_id: env.BAPI_CONDUCTOR_WORKER_ID.trim(),
|
|
78
|
-
producer: "claude-code-hook",
|
|
79
|
-
observed_via: "claude-code-hook",
|
|
80
|
-
data: {
|
|
81
|
-
details,
|
|
82
|
-
raw
|
|
83
|
-
}
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
function buildConductorEmitEventArgs(event) {
|
|
87
|
-
const args = ["--type", event.type, "--source", event.source];
|
|
88
|
-
if (nonEmpty(event.subject ?? void 0)) args.push("--subject", event.subject);
|
|
89
|
-
if (nonEmpty(event.run_id ?? void 0)) args.push("--run-id", event.run_id);
|
|
90
|
-
if (nonEmpty(event.worker_id ?? void 0)) args.push("--worker-id", event.worker_id);
|
|
91
|
-
if (nonEmpty(event.producer ?? void 0)) args.push("--producer", event.producer);
|
|
92
|
-
if (nonEmpty(event.observed_via ?? void 0)) {
|
|
93
|
-
args.push("--observed-via", event.observed_via);
|
|
94
|
-
}
|
|
95
|
-
args.push("--data-json-stdin", "--json");
|
|
96
|
-
return args;
|
|
97
|
-
}
|
|
98
|
-
function resolveConductorEmitCommand(env, emitArgs) {
|
|
99
|
-
const cliFile = env.BAPI_CONDUCTOR_CLI_FILE;
|
|
100
|
-
if (nonEmpty(cliFile)) {
|
|
101
|
-
return {
|
|
102
|
-
command: process.execPath,
|
|
103
|
-
args: [cliFile.trim(), "emit-event", ...emitArgs]
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
const bin = nonEmpty(env.BAPI_CONDUCTOR_BIN) ? env.BAPI_CONDUCTOR_BIN.trim() : "conductor";
|
|
107
|
-
return { command: bin, args: ["emit-event", ...emitArgs] };
|
|
108
|
-
}
|
|
109
|
-
var defaultConductorSpawn = (command, args, input) => {
|
|
110
|
-
const result = spawnSync(command, args, {
|
|
111
|
-
input,
|
|
112
|
-
encoding: "utf-8",
|
|
113
|
-
// Never use a shell: list args + stdin keep raw payloads/secrets out of any
|
|
114
|
-
// shell-interpreted command string.
|
|
115
|
-
shell: false
|
|
116
|
-
});
|
|
117
|
-
return { status: result.status, error: result.error };
|
|
118
|
-
};
|
|
119
|
-
function emitClaudeHookEventWithConductorCli(event, deps = {}) {
|
|
120
|
-
const env = deps.env ?? process.env;
|
|
121
|
-
const spawn = deps.spawn ?? defaultConductorSpawn;
|
|
122
|
-
const emitArgs = buildConductorEmitEventArgs(event);
|
|
123
|
-
const { command, args } = resolveConductorEmitCommand(env, emitArgs);
|
|
124
|
-
const stdinPayload = JSON.stringify(event.data ?? {});
|
|
125
|
-
try {
|
|
126
|
-
const result = spawn(command, args, stdinPayload);
|
|
127
|
-
if (result.error) return false;
|
|
128
|
-
return result.status === 0;
|
|
129
|
-
} catch {
|
|
130
|
-
return false;
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
function runClaudeConductorHookCli(deps = {}) {
|
|
134
|
-
const env = deps.env ?? process.env;
|
|
135
|
-
const readStdin = deps.readStdin ?? (() => readStdinSync());
|
|
136
|
-
const warn = deps.warn ?? ((m) => process.stderr.write(`${m}
|
|
137
|
-
`));
|
|
138
|
-
let payload;
|
|
139
|
-
try {
|
|
140
|
-
const raw = readStdin();
|
|
141
|
-
payload = JSON.parse(raw);
|
|
142
|
-
} catch {
|
|
143
|
-
warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING);
|
|
144
|
-
return 0;
|
|
145
|
-
}
|
|
146
|
-
let event;
|
|
147
|
-
try {
|
|
148
|
-
event = buildClaudeHookConductorEvent(payload, env);
|
|
149
|
-
} catch {
|
|
150
|
-
warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING);
|
|
151
|
-
return 0;
|
|
152
|
-
}
|
|
153
|
-
if (event === null) {
|
|
154
|
-
return 0;
|
|
155
|
-
}
|
|
156
|
-
const ok = emitClaudeHookEventWithConductorCli(event, { env, spawn: deps.spawn });
|
|
157
|
-
if (!ok) {
|
|
158
|
-
warn(CONDUCTOR_HOOK_EMIT_FAILED_WARNING);
|
|
159
|
-
}
|
|
160
|
-
return 0;
|
|
161
|
-
}
|
|
162
|
-
function readStdinSync() {
|
|
163
|
-
return readFileSync(0, "utf-8");
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
// src/conductor-claude-hook-bin.ts
|
|
167
|
-
var exitCode = 0;
|
|
168
|
-
try {
|
|
169
|
-
exitCode = runClaudeConductorHookCli();
|
|
170
|
-
} catch {
|
|
171
|
-
exitCode = 0;
|
|
172
|
-
}
|
|
173
|
-
process.exit(exitCode);
|
|
2
|
+
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():"conductor",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 exitCode=0;try{exitCode=runClaudeConductorHookCli()}catch{exitCode=0}process.exit(exitCode);
|
|
@@ -59,7 +59,7 @@ function hasDisplayText(value) {
|
|
|
59
59
|
// ---------------------------------------------------------------------------
|
|
60
60
|
// Template
|
|
61
61
|
// ---------------------------------------------------------------------------
|
|
62
|
-
const DEFAULT_ASSETS = { faviconBase64: "", logoBase64: ""
|
|
62
|
+
const DEFAULT_ASSETS = { faviconBase64: "", logoBase64: "" };
|
|
63
63
|
export function generateDecisionPageHtml(data, assets = DEFAULT_ASSETS) {
|
|
64
64
|
const { ticket_key, actionable_items, clear_improvements } = data;
|
|
65
65
|
const hasDecisions = actionable_items.length > 0;
|
|
@@ -73,7 +73,6 @@ export function generateDecisionPageHtml(data, assets = DEFAULT_ASSETS) {
|
|
|
73
73
|
const faviconLink = assets.faviconBase64
|
|
74
74
|
? `<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,${assets.faviconBase64}">`
|
|
75
75
|
: "";
|
|
76
|
-
const fontFaces = assets.fontsRelPath ? renderFontFaces(assets.fontsRelPath) : "";
|
|
77
76
|
// The has-decisions intro is an overridable label; the no-decisions copy is a
|
|
78
77
|
// fixed constant. For review_decisions pages the MCP handler short-circuits
|
|
79
78
|
// before rendering when there are no decisions, so that branch is reachable
|
|
@@ -93,7 +92,6 @@ export function generateDecisionPageHtml(data, assets = DEFAULT_ASSETS) {
|
|
|
93
92
|
<title>${escapeHtml(effectiveLabels.title)}: ${escapeHtml(ticket_key)}</title>
|
|
94
93
|
${faviconLink}
|
|
95
94
|
<style>
|
|
96
|
-
${fontFaces}
|
|
97
95
|
:root {
|
|
98
96
|
--primary-color: #E2624B;
|
|
99
97
|
--primary-dark: #C51616;
|
|
@@ -112,7 +110,7 @@ ${fontFaces}
|
|
|
112
110
|
}
|
|
113
111
|
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
114
112
|
body {
|
|
115
|
-
font-family:
|
|
113
|
+
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
|
116
114
|
background-color: var(--background-color);
|
|
117
115
|
color: var(--text-color);
|
|
118
116
|
line-height: 1.5;
|
|
@@ -541,33 +539,6 @@ ${needsForm ? renderScript(data, isPlanning) : ""}
|
|
|
541
539
|
</html>`;
|
|
542
540
|
}
|
|
543
541
|
// ---------------------------------------------------------------------------
|
|
544
|
-
// Font faces
|
|
545
|
-
// ---------------------------------------------------------------------------
|
|
546
|
-
// fontsRelPath comes from server-controlled env vars, so this is not an
|
|
547
|
-
// injection vector — but a deploy path containing a single quote or backslash
|
|
548
|
-
// would silently break CSS parsing and stop fonts from loading. Escape both
|
|
549
|
-
// so the url('...') stays well-formed regardless of deploy layout.
|
|
550
|
-
function escapeCssUrl(value) {
|
|
551
|
-
return value.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
552
|
-
}
|
|
553
|
-
function renderFontFaces(fontsRelPath) {
|
|
554
|
-
const weights = [
|
|
555
|
-
{ weight: "400", style: "normal", file: "SourceSansPro-Regular.ttf" },
|
|
556
|
-
{ weight: "400", style: "italic", file: "SourceSansPro-Italic.ttf" },
|
|
557
|
-
{ weight: "600", style: "normal", file: "SourceSansPro-SemiBold.ttf" },
|
|
558
|
-
{ weight: "600", style: "italic", file: "SourceSansPro-SemiBoldItalic.ttf" },
|
|
559
|
-
{ weight: "700", style: "normal", file: "SourceSansPro-Bold.ttf" },
|
|
560
|
-
{ weight: "700", style: "italic", file: "SourceSansPro-BoldItalic.ttf" },
|
|
561
|
-
];
|
|
562
|
-
const safePath = escapeCssUrl(fontsRelPath);
|
|
563
|
-
return weights.map(({ weight, style, file }) => ` @font-face {
|
|
564
|
-
font-family: 'Source Sans Pro';
|
|
565
|
-
src: url('${safePath}/${escapeCssUrl(file)}') format('truetype');
|
|
566
|
-
font-weight: ${weight};
|
|
567
|
-
font-style: ${style};
|
|
568
|
-
}`).join("\n");
|
|
569
|
-
}
|
|
570
|
-
// ---------------------------------------------------------------------------
|
|
571
542
|
// Section renderers
|
|
572
543
|
// ---------------------------------------------------------------------------
|
|
573
544
|
function renderHeader(assets) {
|