@bridge_gpt/mcp-server 0.2.49 → 0.2.51
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/README.md +25 -8
- package/build/base-ref.js +28 -3
- package/build/claude-review-workflow-drift-probe.js +130 -0
- package/build/claude-review-workflow-drift.js +173 -0
- package/build/claude-review-workflow.js +81 -16
- package/build/commands.generated.js +5 -5
- package/build/conduct-epic/bridge-client.js +115 -1
- package/build/conduct-epic/cli.js +351 -33
- package/build/conduct-epic/cut-protocol.js +51 -0
- package/build/conductor/done-gate.js +25 -3
- package/build/conductor/install-doctor.js +65 -5
- package/build/conductor/latest-check-selector.js +170 -0
- package/build/conductor/local-merge.js +8 -6
- package/build/conductor-bin.js +1 -1
- package/build/{brainstorm-files.js → council-files.js} +15 -15
- package/build/decision-page-schema.js +1 -1
- package/build/docs.generated.js +1 -1
- package/build/doctor.js +352 -4
- package/build/epic-integration-pr.js +280 -0
- package/build/executor/job-runner.js +7 -1
- package/build/executor/merge-job.js +46 -1
- package/build/executor/worktree.js +46 -1
- package/build/index.js +153 -65
- package/build/init.js +9 -2
- package/build/install-bridge.js +60 -2
- package/build/install-reexec.js +47 -9
- package/build/pipelines.generated.js +8 -2
- package/build/plan-epic-conductor-eligibility.js +183 -0
- package/build/plane/cli.js +12 -2
- package/build/plane/manifest.js +25 -1
- package/build/plane/member-roster.js +61 -7
- package/build/plane/preflight.js +24 -9
- package/build/plane/supervisor.js +77 -5
- package/build/plane/types.js +23 -3
- package/build/readme.generated.js +1 -1
- package/build/run-unit-tests-launcher.js +2 -1
- package/build/setup-epic.js +32 -0
- package/build/sfcc/reads-custom-object-def.js +10 -13
- package/build/sfcc/reads-site-preference.js +5 -5
- package/build/sfcc/reads-system-object.js +4 -4
- package/build/sfcc/writes-custom-object-def.js +7 -7
- package/build/sfcc/writes-site-preference.js +4 -3
- package/build/sfcc/writes-system-object.js +7 -6
- package/build/stale-worktree-doctor.js +120 -0
- package/build/start-tickets-prereqs.js +70 -0
- package/build/start-tickets.js +91 -3
- package/build/version.generated.js +3 -2
- package/package.json +6 -3
- package/pipelines/plan-epic.json +5 -0
- package/build/chain-orchestrator.js +0 -1457
- package/build/chain-utils.js +0 -68
- package/build/command-catalog.js +0 -376
- package/build/schedule-run.js +0 -1300
- package/build/schedule-store.js +0 -172
- package/build/scheduled-prompt.js +0 -115
- package/build/scheduler-backends/at-fallback.js +0 -139
- package/build/scheduler-backends/escaping.js +0 -143
- package/build/scheduler-backends/index.js +0 -72
- package/build/scheduler-backends/launchd.js +0 -225
- package/build/scheduler-backends/systemd-user.js +0 -250
- package/build/scheduler-backends/task-scheduler.js +0 -214
- package/build/scheduler-backends/types.js +0 -23
|
@@ -50,11 +50,10 @@ const READ_ANNOTATIONS = {
|
|
|
50
50
|
// ---------------------------------------------------------------------------
|
|
51
51
|
// Input schemas
|
|
52
52
|
// ---------------------------------------------------------------------------
|
|
53
|
-
const OBJECT_TYPE_DISCOVERY_HINT = 'Known custom object type id (e.g. starting with "c_").
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"count matches that row's attribute_definition_count.";
|
|
53
|
+
const OBJECT_TYPE_DISCOVERY_HINT = 'Known custom object type id (e.g. starting with "c_"). ' +
|
|
54
|
+
"Type ids aren't enumerable; find via system_object_list projection=full. " +
|
|
55
|
+
"Match by display_name + attribute_definition_count per custom type, then " +
|
|
56
|
+
"confirm this tool's returned attribute count matches that row's count.";
|
|
58
57
|
const customObjectAttributesGetInput = z.object({
|
|
59
58
|
object_type: z.string().describe(OBJECT_TYPE_DISCOVERY_HINT),
|
|
60
59
|
projection: projectionInputFor("attribute_collection"),
|
|
@@ -139,20 +138,18 @@ function buildCustomObjectAttributeSearchHandler(gateDeps, getDocsDir) {
|
|
|
139
138
|
export function registerSfccCustomObjectDefReadTools(registerTool, deps) {
|
|
140
139
|
const { gateDeps, getDocsDir } = deps;
|
|
141
140
|
registerTool("custom_object_definition_attributes_get", {
|
|
142
|
-
description: "Retrieve attribute
|
|
141
|
+
description: "Retrieve the FULL attribute-definition set for a KNOWN custom object type via " +
|
|
143
142
|
"GET /custom_object_definitions/{type}/attribute_definitions " +
|
|
144
|
-
"(default_value withheld).
|
|
145
|
-
"
|
|
146
|
-
"confirm here. Type creation is v2 metadata-import only.",
|
|
143
|
+
"(default_value withheld). Type creation is v2 metadata-import only. " +
|
|
144
|
+
"Oversized output saved locally.",
|
|
147
145
|
inputSchema: customObjectAttributesGetInput,
|
|
148
146
|
annotations: READ_ANNOTATIONS,
|
|
149
147
|
}, buildCustomObjectAttributesGetHandler(gateDeps, getDocsDir));
|
|
150
148
|
registerTool("custom_object_definition_attribute_search", {
|
|
151
|
-
description: "
|
|
149
|
+
description: "Filter the attribute-definition set within a KNOWN custom object type via " +
|
|
152
150
|
"POST /custom_object_definitions/{type}/attribute_definition_search " +
|
|
153
|
-
"(default_value withheld).
|
|
154
|
-
"
|
|
155
|
-
"confirm here. Type creation is v2 metadata-import only.",
|
|
151
|
+
"(default_value withheld). Type creation is v2 metadata-import only. " +
|
|
152
|
+
"Oversized output saved locally.",
|
|
156
153
|
inputSchema: customObjectAttributeSearchInput,
|
|
157
154
|
annotations: READ_ANNOTATIONS,
|
|
158
155
|
}, buildCustomObjectAttributeSearchHandler(gateDeps, getDocsDir));
|
|
@@ -226,15 +226,15 @@ export function registerSitePreferenceTools(registerTool, deps) {
|
|
|
226
226
|
description: "List custom site preference group ids from the developer sandbox via " +
|
|
227
227
|
"GET /system_object_definitions/SitePreferences/attribute_groups. Read-only; " +
|
|
228
228
|
"use this to discover group ids before site_preference_get or site_preference_search. " +
|
|
229
|
-
"Accepts optional `count` and `start` for paging. Oversized
|
|
229
|
+
"Accepts optional `count` and `start` for paging. Oversized output saved locally.",
|
|
230
230
|
inputSchema: sitePreferenceGroupListInput,
|
|
231
231
|
annotations: READ_ANNOTATIONS,
|
|
232
232
|
}, buildSitePreferenceGroupListHandler(gateDeps, getDocsDir));
|
|
233
233
|
registerTool("site_preference_get", {
|
|
234
|
-
description: "List preference
|
|
234
|
+
description: "List ALL preference ids in a group (match-all) from the developer sandbox via " +
|
|
235
235
|
"POST /site_preferences/preference_groups/{group}/sandbox/preference_search. " +
|
|
236
|
-
"Returns
|
|
237
|
-
"Read-only, sandbox-only. Optional start/count paging. Oversized
|
|
236
|
+
"Returns identifiers only, NOT values — 'unset' and 'empty string' are indistinguishable. " +
|
|
237
|
+
"Read-only, sandbox-only. Optional start/count paging. Oversized output saved locally.",
|
|
238
238
|
inputSchema: sitePreferenceGetInput,
|
|
239
239
|
annotations: READ_ANNOTATIONS,
|
|
240
240
|
}, buildSitePreferenceGetHandler(gateDeps, getDocsDir));
|
|
@@ -243,7 +243,7 @@ export function registerSitePreferenceTools(registerTool, deps) {
|
|
|
243
243
|
"POST /site_preferences/preference_groups/{group}/sandbox/preference_search. " +
|
|
244
244
|
"Returns ids only — this endpoint does not expose values. Read-only; v1 sandbox only. " +
|
|
245
245
|
"Pass a plain string for id-only text search or a structured OCAPI query. " +
|
|
246
|
-
"Oversized
|
|
246
|
+
"Oversized output saved locally.",
|
|
247
247
|
inputSchema: sitePreferenceSearchInput,
|
|
248
248
|
annotations: READ_ANNOTATIONS,
|
|
249
249
|
}, buildSitePreferenceSearchHandler(gateDeps, getDocsDir));
|
|
@@ -149,7 +149,7 @@ export function registerSystemObjectReadTools(registerTool, deps) {
|
|
|
149
149
|
"GET /system_object_definitions. Read-only. " +
|
|
150
150
|
"Accepts optional `count` and `start` for paging (OCAPI default pagination applies when omitted). " +
|
|
151
151
|
"Returns ids only by default; pass projection=full for all fields. " +
|
|
152
|
-
"Oversized
|
|
152
|
+
"Oversized output saved locally.",
|
|
153
153
|
inputSchema: systemObjectListInput,
|
|
154
154
|
annotations: READ_ANNOTATIONS,
|
|
155
155
|
}, buildSystemObjectListHandler(gateDeps, getDocsDir));
|
|
@@ -157,17 +157,17 @@ export function registerSystemObjectReadTools(registerTool, deps) {
|
|
|
157
157
|
description: "Retrieve a system object type definition from the developer sandbox. Read-only. " +
|
|
158
158
|
"GET /system_object_definitions/{type}. " +
|
|
159
159
|
"Prefer system_object_attribute_search for targeted attribute lookups. " +
|
|
160
|
-
"Oversized
|
|
160
|
+
"Oversized output saved locally.",
|
|
161
161
|
inputSchema: systemObjectGetInput,
|
|
162
162
|
annotations: READ_ANNOTATIONS,
|
|
163
163
|
}, buildSystemObjectGetHandler(gateDeps, getDocsDir));
|
|
164
164
|
registerTool("system_object_attribute_search", {
|
|
165
165
|
description: "Search attribute definitions for a system object type. Read-only. " +
|
|
166
166
|
"POST /system_object_definitions/{type}/attribute_definition_search. " +
|
|
167
|
-
"Use for targeted
|
|
167
|
+
"Use for targeted attribute lookups; returns each attribute's schema " +
|
|
168
168
|
"by default (default_value withheld). " +
|
|
169
169
|
"Pass a plain string for text search or a structured OCAPI query. " +
|
|
170
|
-
"Oversized
|
|
170
|
+
"Oversized output saved locally.",
|
|
171
171
|
inputSchema: systemObjectAttributeSearchInput,
|
|
172
172
|
annotations: READ_ANNOTATIONS,
|
|
173
173
|
}, buildSystemObjectAttributeSearchHandler(gateDeps, getDocsDir));
|
|
@@ -37,9 +37,8 @@ const INSTANCE_DESCRIBE = "OCAPI instance context. Omit or pass \"sandbox\"; any
|
|
|
37
37
|
const createCustomObjectAttributeDefinitionInput = z.object({
|
|
38
38
|
object_type: z
|
|
39
39
|
.string()
|
|
40
|
-
.describe('Known custom object type identifier (must already exist
|
|
41
|
-
"
|
|
42
|
-
"definitions on a known type."),
|
|
40
|
+
.describe('Known custom object type identifier (must already exist; OCAPI cannot create ' +
|
|
41
|
+
"types). Type ids aren't enumerable; find via system_object_list projection=full."),
|
|
43
42
|
attribute_id: z
|
|
44
43
|
.string()
|
|
45
44
|
.describe("URL attribute-definition id. If the body also carries `id`, it must match."),
|
|
@@ -49,7 +48,8 @@ const createCustomObjectAttributeDefinitionInput = z.object({
|
|
|
49
48
|
const updateCustomObjectAttributeDefinitionInput = z.object({
|
|
50
49
|
object_type: z
|
|
51
50
|
.string()
|
|
52
|
-
.describe("Known custom object type identifier (must already exist
|
|
51
|
+
.describe("Known custom object type identifier (must already exist; OCAPI cannot create " +
|
|
52
|
+
"types). Type ids aren't enumerable; find via system_object_list projection=full."),
|
|
53
53
|
attribute_id: z.string().describe("URL attribute-definition id to update."),
|
|
54
54
|
patch: objectAttributeDefinitionPatchBodySchema.describe("Partial ObjectAttributeDefinition body; must change at least one field."),
|
|
55
55
|
instance: z.string().optional().describe(INSTANCE_DESCRIBE),
|
|
@@ -134,15 +134,15 @@ export function registerSfccCustomObjectDefWriteTools(registerTool, deps) {
|
|
|
134
134
|
registerTool("custom_object_definition_attribute_create", {
|
|
135
135
|
description: "Create an attribute definition on a KNOWN custom object type via " +
|
|
136
136
|
"PUT /custom_object_definitions/{type}/attribute_definitions/{id}. Sandbox-only, " +
|
|
137
|
-
"destructive; the type must pre-exist (OCAPI cannot create types).
|
|
138
|
-
"grant JSON
|
|
137
|
+
"destructive; the type must pre-exist (OCAPI cannot create types). " +
|
|
138
|
+
"403 → paste-ready grant JSON.",
|
|
139
139
|
inputSchema: createCustomObjectAttributeDefinitionInput,
|
|
140
140
|
annotations: WRITE_ANNOTATIONS,
|
|
141
141
|
}, buildCreateCustomObjectAttributeDefinitionHandler(gateDeps));
|
|
142
142
|
registerTool("custom_object_definition_attribute_update", {
|
|
143
143
|
description: "Update an attribute definition on a KNOWN custom object type via an ETag-conditional " +
|
|
144
144
|
"PATCH /custom_object_definitions/{type}/attribute_definitions/{id}. Sandbox-only, " +
|
|
145
|
-
"destructive; surfaces 409/412 conflicts
|
|
145
|
+
"destructive; surfaces 409/412 conflicts. 403 → paste-ready grant JSON.",
|
|
146
146
|
inputSchema: updateCustomObjectAttributeDefinitionInput,
|
|
147
147
|
annotations: WRITE_ANNOTATIONS,
|
|
148
148
|
}, buildUpdateCustomObjectAttributeDefinitionHandler(gateDeps));
|
|
@@ -92,9 +92,10 @@ export const SITE_PREFERENCE_WRITE_TOOL_NAMES = ["site_preference_values_set"];
|
|
|
92
92
|
export function registerSitePreferenceWriteTools(registerTool, deps) {
|
|
93
93
|
const { gateDeps } = deps;
|
|
94
94
|
registerTool("site_preference_values_set", {
|
|
95
|
-
description: "Set
|
|
96
|
-
"
|
|
97
|
-
"
|
|
95
|
+
description: "Set site-preference VALUES via PATCH /site_preferences/preference_groups/{group}/sandbox. " +
|
|
96
|
+
"Preference must already be defined via custom_preference_definition_create. Sandbox-only, " +
|
|
97
|
+
"destructive; flat c_-prefixed id→value map (string/number/boolean/string[]). " +
|
|
98
|
+
"Bad group → 404 CustomPreferenceGroupNotFoundException. 403 → paste-ready grant JSON.",
|
|
98
99
|
inputSchema: sitePreferenceValuesSetInput,
|
|
99
100
|
annotations: WRITE_ANNOTATIONS,
|
|
100
101
|
}, buildSitePreferenceValuesSetHandler(gateDeps));
|
|
@@ -276,42 +276,43 @@ export function registerSystemObjectWriteTools(registerTool, deps) {
|
|
|
276
276
|
registerTool("system_object_attribute_definition_create", {
|
|
277
277
|
description: "Create a system-object attribute definition (sandbox-only, destructive). " +
|
|
278
278
|
"PUT .../attribute_definitions/{id}; body id must equal URL id and system must be false. " +
|
|
279
|
-
"Returns 201.
|
|
279
|
+
"Returns 201. 403 → paste-ready grant JSON.",
|
|
280
280
|
inputSchema: createAttributeDefinitionInput,
|
|
281
281
|
annotations: WRITE_ANNOTATIONS,
|
|
282
282
|
}, buildCreateAttributeDefinitionHandler(gateDeps));
|
|
283
283
|
registerTool("system_object_attribute_definition_update", {
|
|
284
284
|
description: "Update a system-object attribute definition (sandbox-only, destructive). " +
|
|
285
285
|
"PATCH .../attribute_definitions/{id} via the ETag GET-then-If-Match round trip. " +
|
|
286
|
-
"Returns 200; surfaces 409/412 on ETag conflicts.
|
|
286
|
+
"Returns 200; surfaces 409/412 on ETag conflicts. 403 → paste-ready grant JSON.",
|
|
287
287
|
inputSchema: updateAttributeDefinitionInput,
|
|
288
288
|
annotations: WRITE_ANNOTATIONS,
|
|
289
289
|
}, buildUpdateAttributeDefinitionHandler(gateDeps));
|
|
290
290
|
registerTool("system_object_attribute_group_create", {
|
|
291
291
|
description: "Create a system-object attribute group (sandbox-only, destructive). " +
|
|
292
292
|
"PUT .../attribute_groups/{id} with a minimal body (display_name + internal). " +
|
|
293
|
-
"
|
|
293
|
+
"403 → paste-ready grant JSON.",
|
|
294
294
|
inputSchema: createAttributeGroupInput,
|
|
295
295
|
annotations: WRITE_ANNOTATIONS,
|
|
296
296
|
}, buildCreateAttributeGroupHandler(gateDeps));
|
|
297
297
|
registerTool("system_object_attribute_group_update", {
|
|
298
298
|
description: "Update a system-object attribute group (sandbox-only, destructive). " +
|
|
299
299
|
"PATCH .../attribute_groups/{id} via the ETag round trip (display_name and/or internal). " +
|
|
300
|
-
"
|
|
300
|
+
"403 → paste-ready grant JSON.",
|
|
301
301
|
inputSchema: updateAttributeGroupInput,
|
|
302
302
|
annotations: WRITE_ANNOTATIONS,
|
|
303
303
|
}, buildUpdateAttributeGroupHandler(gateDeps));
|
|
304
304
|
registerTool("system_object_attribute_assign_to_group", {
|
|
305
305
|
description: "Assign an attribute definition to an attribute group (sandbox-only, destructive). " +
|
|
306
306
|
"PUT .../attribute_groups/{group}/attribute_definitions/{def} with an empty body. " +
|
|
307
|
-
"
|
|
307
|
+
"403 → paste-ready grant JSON.",
|
|
308
308
|
inputSchema: assignAttributeToGroupInput,
|
|
309
309
|
annotations: WRITE_ANNOTATIONS,
|
|
310
310
|
}, buildAssignAttributeToGroupHandler(gateDeps));
|
|
311
311
|
registerTool("custom_preference_definition_create", {
|
|
312
312
|
description: "Create a custom preference definition (sandbox-only, destructive). " +
|
|
313
313
|
"PUT .../{SitePreferences|OrganizationPreferences}/attribute_definitions/{id}. " +
|
|
314
|
-
"Booleans resolve to null unless default_value is set.
|
|
314
|
+
"Booleans resolve to null unless default_value is set. After defining the schema, " +
|
|
315
|
+
"set its value with site_preference_values_set. 403 → paste-ready grant JSON.",
|
|
315
316
|
inputSchema: createCustomPreferenceDefinitionInput,
|
|
316
317
|
annotations: WRITE_ANNOTATIONS,
|
|
317
318
|
}, buildCreateCustomPreferenceDefinitionHandler(gateDeps));
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read-only stale-branch diagnostic (BAPI-948).
|
|
3
|
+
*
|
|
4
|
+
* `start-tickets --guard-stale-branch` (and `--conductor`, which implies it) refuses
|
|
5
|
+
* to reuse a pre-existing `feature/<KEY>` branch whose tip is not an ancestor of the
|
|
6
|
+
* resolved base. That refusal is correct but it only tells you AFTER you tried to cut
|
|
7
|
+
* the worktree. This module answers the same question BEFORE the cut, and without
|
|
8
|
+
* touching a single ref.
|
|
9
|
+
*
|
|
10
|
+
* It owns no ancestry logic of its own. `branchExists` and
|
|
11
|
+
* `isExistingBranchSafeToReuse` from `./worktree-core.js` are the exact predicates the
|
|
12
|
+
* guard itself runs, so a `would-refuse` finding here and a `create-failed` row there
|
|
13
|
+
* can never disagree — and the operator sentence is the same sentence in both places.
|
|
14
|
+
* Deliberately NOT a second ahead-count / diff / merge-base implementation: a
|
|
15
|
+
* diagnostic that re-derives the condition is a diagnostic that can be wrong about it.
|
|
16
|
+
*
|
|
17
|
+
* Strictly read-only. The only commands it can reach are the two the predicates issue
|
|
18
|
+
* — `git show-ref --verify --quiet`, `git rev-parse --verify --quiet`, and
|
|
19
|
+
* `git merge-base --is-ancestor` — none of which mutate anything. There is no fetch,
|
|
20
|
+
* reset, switch, checkout, branch deletion, or worktree creation on any path here, and
|
|
21
|
+
* nothing in this module writes a file.
|
|
22
|
+
*/
|
|
23
|
+
import { branchExists, isExistingBranchSafeToReuse } from "./worktree-core.js";
|
|
24
|
+
/**
|
|
25
|
+
* The one sentence an `indeterminate` finding renders. Fixed text: the underlying
|
|
26
|
+
* throw is swallowed rather than formatted, so no probe failure can smuggle command
|
|
27
|
+
* output into a report.
|
|
28
|
+
*/
|
|
29
|
+
export const STALE_WORKTREE_INDETERMINATE_REASON = "the read-only branch check could not complete (git was unavailable or failed to run); " +
|
|
30
|
+
"re-run the diagnostic, or inspect the branch by hand with " +
|
|
31
|
+
"`git merge-base --is-ancestor <branch> <base>`.";
|
|
32
|
+
/**
|
|
33
|
+
* Diagnose each target independently, read-only.
|
|
34
|
+
*
|
|
35
|
+
* Independence is the point: one unreadable branch must not blank out its siblings'
|
|
36
|
+
* findings, which is why every target is wrapped on its own rather than the loop as a
|
|
37
|
+
* whole. Targets are evaluated in the order given so the rendered report is stable.
|
|
38
|
+
*/
|
|
39
|
+
export async function diagnoseStaleWorktreeBranches(deps, targets) {
|
|
40
|
+
const findings = [];
|
|
41
|
+
for (const target of targets) {
|
|
42
|
+
const { key, branch, baseStartPoint } = target;
|
|
43
|
+
try {
|
|
44
|
+
// No local branch → nothing pre-existing for the guard to act on. Return before
|
|
45
|
+
// the ancestry probe: running it would be a wasted command AND would produce a
|
|
46
|
+
// "not an ancestor" reason about a branch that is not there.
|
|
47
|
+
if (!(await branchExists(deps, branch))) {
|
|
48
|
+
findings.push({ key, branch, baseStartPoint, status: "branch-absent" });
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
// The SAME predicate `createWorktreeForTicket` runs, with the same branch and
|
|
52
|
+
// the same base start point, so the verdict and its remedy are identical to the
|
|
53
|
+
// refusal the guarded cut would emit.
|
|
54
|
+
const safety = await isExistingBranchSafeToReuse(deps, branch, baseStartPoint);
|
|
55
|
+
if (safety.safe) {
|
|
56
|
+
findings.push({ key, branch, baseStartPoint, status: "safe" });
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
findings.push({
|
|
60
|
+
key,
|
|
61
|
+
branch,
|
|
62
|
+
baseStartPoint,
|
|
63
|
+
status: "would-refuse",
|
|
64
|
+
reason: safety.reason,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
findings.push({
|
|
69
|
+
key,
|
|
70
|
+
branch,
|
|
71
|
+
baseStartPoint,
|
|
72
|
+
status: "indeterminate",
|
|
73
|
+
reason: STALE_WORKTREE_INDETERMINATE_REASON,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return findings;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Render the advisory section. Advisory in the strict doctor sense: the caller never
|
|
81
|
+
* derives an exit code from it.
|
|
82
|
+
*
|
|
83
|
+
* One row per target, each naming the ticket, the resolved branch, and the base it was
|
|
84
|
+
* measured against — an operator diagnosing an epic cut needs all three to act. A
|
|
85
|
+
* `WOULD REFUSE` row is followed by the predicate's remedy on its own indented line
|
|
86
|
+
* rather than being folded into the row, because that sentence is the thing the
|
|
87
|
+
* operator has to read.
|
|
88
|
+
*/
|
|
89
|
+
export function formatStaleWorktreeDiagnosticReport(findings) {
|
|
90
|
+
const lines = ["", "Stale ticket branches (advisory)"];
|
|
91
|
+
if (findings.length === 0) {
|
|
92
|
+
lines.push(" SKIP No --stale-branch targets supplied; nothing was checked.");
|
|
93
|
+
return lines.join("\n");
|
|
94
|
+
}
|
|
95
|
+
for (const finding of findings) {
|
|
96
|
+
const where = `${finding.key}: ${finding.branch} vs ${finding.baseStartPoint}`;
|
|
97
|
+
switch (finding.status) {
|
|
98
|
+
case "branch-absent":
|
|
99
|
+
lines.push(` OK ${where} — no local branch; a fresh one is cut from the base.`);
|
|
100
|
+
break;
|
|
101
|
+
case "safe":
|
|
102
|
+
lines.push(` OK ${where} — branch is an ancestor of the base; safe to reuse.`);
|
|
103
|
+
break;
|
|
104
|
+
case "would-refuse":
|
|
105
|
+
lines.push(` WARN ${where} — WOULD REFUSE.`);
|
|
106
|
+
if (finding.reason)
|
|
107
|
+
lines.push(` ${finding.reason}`);
|
|
108
|
+
break;
|
|
109
|
+
case "indeterminate":
|
|
110
|
+
lines.push(` WARN ${where} — indeterminate.`);
|
|
111
|
+
if (finding.reason)
|
|
112
|
+
lines.push(` ${finding.reason}`);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
lines.push(" Read-only: this section reads current local refs only. It never fetches,");
|
|
117
|
+
lines.push(" resets, switches, deletes, or repairs any git state, and never changes the");
|
|
118
|
+
lines.push(" exit code.");
|
|
119
|
+
return lines.join("\n");
|
|
120
|
+
}
|
|
@@ -19,6 +19,11 @@ import { resolveStartTicketsRepoName } from "./start-tickets-repo.js";
|
|
|
19
19
|
import { probeWorktreeMcpRegistration } from "./mcp-registration-doctor.js";
|
|
20
20
|
import { probeWorktreeCommandAssets } from "./command-assets-doctor.js";
|
|
21
21
|
import { MCP_SERVER_NAME, MCP_PACKAGE_NAME } from "./mcp-identity.js";
|
|
22
|
+
// BAPI-941: both are LEAF modules (the pure classifier and its git-backed
|
|
23
|
+
// acquisition), so importing them here cannot close a cycle with `base-ref.ts`,
|
|
24
|
+
// which imports `commandSucceeded` from this module.
|
|
25
|
+
import { CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION, CLAUDE_REVIEW_WORKFLOW_RELPATH, summarizeClaudeReviewWorkflowDrift, } from "./claude-review-workflow-drift.js";
|
|
26
|
+
import { probeClaudeReviewWorkflowDrift, resolveCurrentBranch, resolveRepositoryDefaultBranch, } from "./claude-review-workflow-drift-probe.js";
|
|
22
27
|
import { CLAUDE_MCP_SHADOWING_REMEDIATION_COMMAND, claudeMcpShadowingRemediationCommand, formatClaudeMcpShadowFinding, formatClaudeUserConfigDiagnostic, inspectClaudeUserConfigForMcpShadowing, resolveClaudeUserConfigPath, } from "./claude-user-config-doctor.js";
|
|
23
28
|
// ---------------------------------------------------------------------------
|
|
24
29
|
// Constants (moved here from start-tickets.ts so both consumers share them)
|
|
@@ -717,6 +722,67 @@ export function liveSourceGuardDescriptor() {
|
|
|
717
722
|
},
|
|
718
723
|
};
|
|
719
724
|
}
|
|
725
|
+
/** Secret-free remediation hint for the workflow-drift diagnostic. */
|
|
726
|
+
const CLAUDE_REVIEW_WORKFLOW_DRIFT_INSTALL_HINTS = {
|
|
727
|
+
darwin: CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION,
|
|
728
|
+
linux: CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION,
|
|
729
|
+
win32: CLAUDE_REVIEW_WORKFLOW_DRIFT_REMEDIATION,
|
|
730
|
+
};
|
|
731
|
+
/**
|
|
732
|
+
* BAPI-941 doctor-only, strictly read-only probe: does the current branch's copy
|
|
733
|
+
* of the reviewed workflow match the repository default branch's copy?
|
|
734
|
+
*
|
|
735
|
+
* Answers, without spawning a worker or creating a worktree, the question that
|
|
736
|
+
* otherwise only surfaces an hour into a conductor run: has this branch gone
|
|
737
|
+
* blind to automated code review? `anthropics/claude-code-action` refuses to run
|
|
738
|
+
* when the invoking workflow differs from the default branch's copy, and a
|
|
739
|
+
* long-lived `epic/**` branch inherits that staleness into every pull request
|
|
740
|
+
* based on it — while the pull requests themselves change no workflow file at
|
|
741
|
+
* all, so no path-based guard fires.
|
|
742
|
+
*
|
|
743
|
+
* **Read-only, and network-free.** It runs `git symbolic-ref`, `git rev-parse`,
|
|
744
|
+
* and two `git show` invocations against the local object database. It never
|
|
745
|
+
* fetches, checks out, installs, migrates, creates a worktree, or writes a
|
|
746
|
+
* credential.
|
|
747
|
+
*
|
|
748
|
+
* **Never blocking.** `doctor`'s exit code is
|
|
749
|
+
* `results.some((r) => !r.found) ? 1 : 0`, so this probe reports `found: true`
|
|
750
|
+
* for ALL THREE outcomes and carries the state in `detail`. Confirmed drift is an
|
|
751
|
+
* advisory an operator should act on, not a missing prerequisite that fails the
|
|
752
|
+
* command — and an unavailable comparison is uncertainty, which is even less of
|
|
753
|
+
* a fault. Registered in `getDoctorOnlyPrereqDescriptors`, never in
|
|
754
|
+
* `getPreflightPrereqDescriptors`.
|
|
755
|
+
*
|
|
756
|
+
* **Bounded output.** Every rendered string comes from the shared classifier's
|
|
757
|
+
* closed vocabulary plus refs and the workflow path. Subprocess stderr, GitHub
|
|
758
|
+
* exception text, and file content never reach the report.
|
|
759
|
+
*/
|
|
760
|
+
export function claudeReviewWorkflowDriftDescriptor() {
|
|
761
|
+
return {
|
|
762
|
+
id: "claude-review-workflow-drift",
|
|
763
|
+
label: "claude-review workflow lineage",
|
|
764
|
+
installHint: CLAUDE_REVIEW_WORKFLOW_DRIFT_INSTALL_HINTS,
|
|
765
|
+
probe: async (deps) => {
|
|
766
|
+
const probeDeps = { runCommand: deps.runCommand, cwd: deps.cwd };
|
|
767
|
+
const baseRef = await resolveCurrentBranch(probeDeps);
|
|
768
|
+
if (!baseRef) {
|
|
769
|
+
// A detached HEAD has no branch whose lineage to report. Inconclusive,
|
|
770
|
+
// not a fault.
|
|
771
|
+
return {
|
|
772
|
+
found: true,
|
|
773
|
+
detail: `comparison unavailable (base_ref_unresolved) — advisory check failed open, ` +
|
|
774
|
+
`${CLAUDE_REVIEW_WORKFLOW_RELPATH} lineage unknown`,
|
|
775
|
+
};
|
|
776
|
+
}
|
|
777
|
+
const defaultRef = await resolveRepositoryDefaultBranch(probeDeps);
|
|
778
|
+
const classification = await probeClaudeReviewWorkflowDrift(probeDeps, {
|
|
779
|
+
baseRef,
|
|
780
|
+
defaultRef,
|
|
781
|
+
});
|
|
782
|
+
return { found: true, detail: summarizeClaudeReviewWorkflowDrift(classification) };
|
|
783
|
+
},
|
|
784
|
+
};
|
|
785
|
+
}
|
|
720
786
|
/**
|
|
721
787
|
* The exact existing live-preflight requirement set per platform, plus the git
|
|
722
788
|
* work-tree custom check appended after the command checks. This is the ONLY
|
|
@@ -765,6 +831,10 @@ export function getDoctorOnlyPrereqDescriptors(_platform, _env, agent) {
|
|
|
765
831
|
reviewTicketsGitDescriptor(),
|
|
766
832
|
liveSourceGuardDescriptor(),
|
|
767
833
|
claudeMcpShadowingDescriptor(),
|
|
834
|
+
// BAPI-941: advisory workflow-lineage diagnostic. Doctor-only by design —
|
|
835
|
+
// adding it to `getPreflightPrereqDescriptors` would make a stale base
|
|
836
|
+
// branch block worktree creation, which is the opposite of fail-open.
|
|
837
|
+
claudeReviewWorkflowDriftDescriptor(),
|
|
768
838
|
];
|
|
769
839
|
}
|
|
770
840
|
/**
|
package/build/start-tickets.js
CHANGED
|
@@ -60,6 +60,9 @@ import path from "path";
|
|
|
60
60
|
import { VERSION } from "./version.generated.js";
|
|
61
61
|
import { resolveBapiCredentials, getPrimaryCredentialStorePath, } from "./credential-store.js";
|
|
62
62
|
import { MCP_PACKAGE_NAME } from "./mcp-identity.js";
|
|
63
|
+
// BAPI-941: the shared drift diagnostic formatter. The leaf drift module imports
|
|
64
|
+
// nothing from here, so this direction stays acyclic.
|
|
65
|
+
import { formatClaudeReviewWorkflowDriftDiagnostic } from "./claude-review-workflow-drift.js";
|
|
63
66
|
import { resolveStartTicketsRepoName as resolveSharedStartTicketsRepoName, resolveRequiredStartTicketsRepoName, } from "./start-tickets-repo.js";
|
|
64
67
|
import { provisionMcpRegistrationsForCreatedWorktrees, } from "./mcp-provisioning.js";
|
|
65
68
|
import { provisionCommandsForCreatedWorktrees, } from "./command-provisioning.js";
|
|
@@ -132,7 +135,8 @@ export function getStartTicketsUsage() {
|
|
|
132
135
|
" --base-branch BRANCH Cut new worktrees from BRANCH and refresh origin/BRANCH (default: main)",
|
|
133
136
|
" --no-refresh-main Skip refresh of the configured base branch (default main); historical name retained for backward compatibility",
|
|
134
137
|
" --max-parallel N Max worktrees to create concurrently (default: 3)",
|
|
135
|
-
" --conductor Enable the Conductor system: per-worker hook injection + BAPI_CONDUCTOR_* env, lifecycle ledger events, and a supervisor peer tab (default: off — a plain run just spawns cd <worktree> && <agent> '/implement-ticket <KEY>')",
|
|
138
|
+
" --conductor Enable the Conductor system: per-worker hook injection + BAPI_CONDUCTOR_* env, lifecycle ledger events, and a supervisor peer tab (default: off — a plain run just spawns cd <worktree> && <agent> '/implement-ticket <KEY>'). Implies --guard-stale-branch: a pre-existing ticket branch carrying commits beyond the resolved base is refused, not silently reused.",
|
|
139
|
+
" --guard-stale-branch Refuse to reuse a pre-existing feature/KEY branch whose tip is NOT an ancestor of the resolved base (it carries commits the base does not). The ticket is reported create-failed with the stale-worktree remedy instead of spawning a worker on leftover code. Implied by --conductor; pass it explicitly on the LLM-conductor pilot cut path. Off by default, so a plain interactive run keeps reusing a same-named branch.",
|
|
136
140
|
" -h, --help Show this help",
|
|
137
141
|
"",
|
|
138
142
|
"Environment:",
|
|
@@ -176,6 +180,11 @@ export function parseStartTicketsArgs(argv) {
|
|
|
176
180
|
let agentName = DEFAULT_AGENT_NAME;
|
|
177
181
|
let baseBranch = "main";
|
|
178
182
|
let conductorEnabled = false;
|
|
183
|
+
// BAPI-948: `--guard-stale-branch` (and `--conductor`, which implies it) opt the
|
|
184
|
+
// packaged CLI into the F7 stale-branch guard. Tracked separately from
|
|
185
|
+
// `conductorEnabled` so the pilot can take the safety check WITHOUT taking the
|
|
186
|
+
// v2 conductor context that `--conductor` also switches on.
|
|
187
|
+
let guardStaleBranch = false;
|
|
179
188
|
let workflow = "implement";
|
|
180
189
|
let reviewRoundsRaw;
|
|
181
190
|
// BAPI-642 `--tier` override. `undefined` = flag absent (property omitted from
|
|
@@ -358,6 +367,10 @@ export function parseStartTicketsArgs(argv) {
|
|
|
358
367
|
conductorEnabled = true;
|
|
359
368
|
continue;
|
|
360
369
|
}
|
|
370
|
+
if (arg === "--guard-stale-branch") {
|
|
371
|
+
guardStaleBranch = true;
|
|
372
|
+
continue;
|
|
373
|
+
}
|
|
361
374
|
if (arg === "--no-refresh-main") {
|
|
362
375
|
refreshMain = false;
|
|
363
376
|
continue;
|
|
@@ -454,6 +467,11 @@ export function parseStartTicketsArgs(argv) {
|
|
|
454
467
|
conductorEnabled,
|
|
455
468
|
workflow,
|
|
456
469
|
reviewRounds,
|
|
470
|
+
// BAPI-948: include `guardStaleWorktree` ONLY when the run asked for the F7
|
|
471
|
+
// guard — `--guard-stale-branch`, or `--conductor` which implies it. A plain
|
|
472
|
+
// interactive invocation leaves the property entirely absent (not `false`),
|
|
473
|
+
// so the reuse-friendly human option shape is byte-identical to before.
|
|
474
|
+
...(guardStaleBranch || conductorEnabled ? { guardStaleWorktree: true } : {}),
|
|
457
475
|
// BAPI-642: include the injected-tier property ONLY when `--tier` was
|
|
458
476
|
// supplied, so an omitted flag leaves the legacy option shape untouched
|
|
459
477
|
// (no own `injectedTier` key, not even `undefined`).
|
|
@@ -834,9 +852,23 @@ export async function refreshBaseBranch(deps, options) {
|
|
|
834
852
|
return { ok: true };
|
|
835
853
|
const baseBranch = options.baseBranch;
|
|
836
854
|
const originRef = `origin/${baseBranch}`;
|
|
837
|
-
|
|
855
|
+
// BAPI-941: refresh the DEFAULT branch alongside the base in ONE invocation so
|
|
856
|
+
// the caller's workflow-drift advisory reads an up-to-date `origin/<default>`
|
|
857
|
+
// without a second network round trip. Deduplicated when they are the same
|
|
858
|
+
// branch, and retried without the extra refspec on failure so this enrichment
|
|
859
|
+
// can never convert a working refresh into an error.
|
|
860
|
+
const defaultBranch = await resolveRepositoryDefaultBranch(deps);
|
|
861
|
+
const extraRefs = defaultBranch && defaultBranch !== baseBranch && validateBranchName(defaultBranch) === null
|
|
862
|
+
? [defaultBranch]
|
|
863
|
+
: [];
|
|
864
|
+
let fetch = await deps.runCommand("git", ["fetch", "origin", baseBranch, ...extraRefs], {
|
|
838
865
|
cwd: deps.cwd,
|
|
839
866
|
});
|
|
867
|
+
if (!commandSucceeded(fetch) && extraRefs.length > 0) {
|
|
868
|
+
fetch = await deps.runCommand("git", ["fetch", "origin", baseBranch], {
|
|
869
|
+
cwd: deps.cwd,
|
|
870
|
+
});
|
|
871
|
+
}
|
|
840
872
|
if (!commandSucceeded(fetch)) {
|
|
841
873
|
return {
|
|
842
874
|
ok: false,
|
|
@@ -934,12 +966,49 @@ export async function runWithConcurrency(items, limit, worker) {
|
|
|
934
966
|
// same function references.
|
|
935
967
|
import { resolveBranchForTicket, branchExists, buildWtSwitchArgs, pathApiForPlatform, extractWorktreePath, isExistingBranchSafeToReuse, createWorktreeForTicket, } from "./worktree-core.js";
|
|
936
968
|
export { resolveBranchForTicket, branchExists, buildWtSwitchArgs, pathApiForPlatform, extractWorktreePath, isExistingBranchSafeToReuse, createWorktreeForTicket, };
|
|
969
|
+
/**
|
|
970
|
+
* BAPI-941: emit the per-spawn `claude-review` workflow-drift advisory.
|
|
971
|
+
*
|
|
972
|
+
* Called once at the shared post-base-resolution, pre-worktree choke point. It
|
|
973
|
+
* returns `void` and swallows everything: this diagnostic exists to tell an
|
|
974
|
+
* operator that their epic branch has gone blind to code review, and a
|
|
975
|
+
* diagnostic that can abort the run it is describing would be strictly worse
|
|
976
|
+
* than no diagnostic at all.
|
|
977
|
+
*
|
|
978
|
+
* All three outcomes continue to worktree creation:
|
|
979
|
+
*
|
|
980
|
+
* - `drifted` — a WARNING naming the stale base and the remedy, and saying
|
|
981
|
+
* plainly that work continues.
|
|
982
|
+
* - `unverified` — an INFO line naming which comparison was unavailable
|
|
983
|
+
* (missing ref, unreadable workflow, unresolved default branch).
|
|
984
|
+
* - `aligned` — silence. A clean spawn prints nothing and performs no
|
|
985
|
+
* additional network work beyond the fetch that already happened.
|
|
986
|
+
*/
|
|
987
|
+
async function emitClaudeReviewWorkflowDriftAdvisory(deps, baseBranch, overrides) {
|
|
988
|
+
const warn = overrides.workflowDriftWarningLog ?? ((m) => console.error(m));
|
|
989
|
+
try {
|
|
990
|
+
const probe = overrides.probeWorkflowDrift ?? probeClaudeReviewWorkflowDrift;
|
|
991
|
+
const defaultRef = await resolveRepositoryDefaultBranch(deps);
|
|
992
|
+
const classification = await probe(deps, { baseRef: baseBranch, defaultRef });
|
|
993
|
+
if (classification.state === "aligned")
|
|
994
|
+
return;
|
|
995
|
+
warn(formatClaudeReviewWorkflowDriftDiagnostic(classification));
|
|
996
|
+
}
|
|
997
|
+
catch {
|
|
998
|
+
// A thrown probe is itself an unavailable comparison. Stay silent about the
|
|
999
|
+
// exception (it can echo absolute paths and provider text) and never let it
|
|
1000
|
+
// reach the caller.
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
937
1003
|
// BAPI-586: base-ref resolution now lives in the focused `base-ref.ts` module so
|
|
938
1004
|
// the executor can depend on it without pulling in this large CLI module. We
|
|
939
1005
|
// import the VALUES here (so internal uses keep working) and re-export the same
|
|
940
1006
|
// references so existing importers of `./start-tickets.js` (index.ts,
|
|
941
1007
|
// review-tickets.ts) and the pinned start-tickets tests are unaffected.
|
|
942
|
-
import { validateBranchName, fetchAndResolveBaseSha
|
|
1008
|
+
import { validateBranchName, fetchAndResolveBaseSha,
|
|
1009
|
+
// BAPI-941: both live in the same leaf module so the interactive refresh path
|
|
1010
|
+
// and the non-mutating conductor path share one drift implementation.
|
|
1011
|
+
resolveRepositoryDefaultBranch, probeClaudeReviewWorkflowDrift, } from "./base-ref.js";
|
|
943
1012
|
export { validateBranchName, fetchAndResolveBaseSha };
|
|
944
1013
|
/**
|
|
945
1014
|
* Create / switch worktrees for every ticket, throttled to `maxParallel`, using
|
|
@@ -2898,6 +2967,22 @@ export async function orchestrateStartTickets(deps, options, overrides = {}) {
|
|
|
2898
2967
|
if (!refresh.ok)
|
|
2899
2968
|
return { ok: false, error: refresh.error };
|
|
2900
2969
|
}
|
|
2970
|
+
// BAPI-941: the `claude-review` workflow-drift advisory, emitted ONCE per
|
|
2971
|
+
// worker spawn at the one choke point both dispatch paths pass through, after
|
|
2972
|
+
// the base is resolved and before any worktree exists.
|
|
2973
|
+
//
|
|
2974
|
+
// Per-spawn timing is the whole point. `epic/BAPI-902` was cut clean and was
|
|
2975
|
+
// blinded 24 hours later by an unrelated commit on `main`, so a check that ran
|
|
2976
|
+
// only at `init`/`setup-epic` time would have passed and still let every
|
|
2977
|
+
// subsequent PR go unreviewed. Both refs were just refreshed by the fetch
|
|
2978
|
+
// above, so the comparison is local and costs no extra round trip.
|
|
2979
|
+
//
|
|
2980
|
+
// ADVISORY ONLY, and fail-open in every degraded case (unreachable remote,
|
|
2981
|
+
// missing ref, unresolvable default branch): it prints to stderr and the run
|
|
2982
|
+
// continues to worktree creation. It is not a `RunPolicy` key and not a
|
|
2983
|
+
// worker-emitted control signal — R14 rules 2 and 3 are about the conductor's
|
|
2984
|
+
// decision surface, and a spawner's warning is neither.
|
|
2985
|
+
await emitClaudeReviewWorkflowDriftAdvisory(deps, options.baseBranch, overrides);
|
|
2901
2986
|
const createWorktreesFn = overrides.createWorktrees ?? createWorktrees;
|
|
2902
2987
|
const provisionCommandsFn = overrides.provisionCommands ??
|
|
2903
2988
|
((rows, d) => provisionCommandsForCreatedWorktrees(rows, buildCommandProvisioningDeps(d)));
|
|
@@ -3113,6 +3198,9 @@ export async function runStartTicketsCli(argv, overrides = {}) {
|
|
|
3113
3198
|
modelRoutingLog: log,
|
|
3114
3199
|
modelRoutingWarningLog: errorLog,
|
|
3115
3200
|
repoNameWarningLog: errorLog,
|
|
3201
|
+
// BAPI-941: the workflow-drift advisory goes to stderr like every other
|
|
3202
|
+
// spawner diagnostic, so it never contaminates the summary on stdout.
|
|
3203
|
+
workflowDriftWarningLog: errorLog,
|
|
3116
3204
|
// Conductor is OPT-IN (BAPI-394): the three seams are supplied ONLY when the
|
|
3117
3205
|
// user passes `--conductor`. Supplying `createConductorContext` activates the
|
|
3118
3206
|
// conductor stage inside orchestrate (env injection + supervisor tab); the
|
|
@@ -1,3 +1,4 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit manually. Regenerate with: npm run build
|
|
2
|
-
export const VERSION = "0.2.
|
|
3
|
-
export const BUILD_COMMIT = "
|
|
2
|
+
export const VERSION = "0.2.51";
|
|
3
|
+
export const BUILD_COMMIT = "3cdbee6cfd9b";
|
|
4
|
+
export const LAUNCHER_ARGS = ["-y", "--prefer-offline", "@bridge_gpt/mcp-server@0.2.51", "serve"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bridge_gpt/mcp-server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.51",
|
|
4
4
|
"description": "Bridge API MCP server — exposes Jira endpoints as MCP tools for Claude Code agents",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,9 +21,12 @@
|
|
|
21
21
|
"LICENSE"
|
|
22
22
|
],
|
|
23
23
|
"//test": "The `test` script delegates to the bounded launcher (scripts/run-unit-tests.js → src/run-unit-tests-launcher.ts, BAPI-683), which discovers build/*.test.js at runtime and spawns them in size-bounded batches so the argv never exceeds Windows' ~8191-char command-line limit. Two determinism rules from the pre-launcher script are preserved INSIDE the launcher: (1) multi-file batches run WITHOUT --test-force-exit, because that flag makes node:test exit before aggregating per-subprocess summaries and the tally becomes nondeterministic (failures still surface and still set exit 1, but the count cannot be trusted as a completeness signal); (2) build/secret-safety.test.js leaks a handle that holds the event loop ~60s after its tests finish (they run in ~147ms), so the launcher quarantines it into its own single-file --test-force-exit invocation (FORCE_EXIT_QUARANTINE in src/run-unit-tests-launcher.ts) where the tally stays exact. Do NOT add --test-force-exit to batched invocations, and do NOT remove the quarantine, until the underlying handle leak is fixed.",
|
|
24
|
+
"//version": "BAPI-930: fired by `npm version <level>` AFTER npm rewrites package.json's version field, so the bumped version and the repinned README land in the SAME commit. A bump performed by hand-editing package.json instead of running `npm version` bypasses this hook entirely and must repin manually via `node scripts/sync-readme-host-examples.js` — the `--check` wired into `build` below is the backstop that catches that case.",
|
|
24
25
|
"scripts": {
|
|
25
|
-
"build": "node scripts/bundle-version.js && node scripts/validate-packaged-command-references.js && node scripts/bundle-readme.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && node scripts/bundle-docs.js && tsc && node scripts/sync-agent-mirrors.js && node scripts/bundle-esbuild.js",
|
|
26
|
+
"build": "node scripts/bundle-version.js && node scripts/sync-readme-host-examples.js --check && node scripts/validate-packaged-command-references.js && node scripts/bundle-readme.js && node scripts/bundle-pipelines.js && node scripts/bundle-commands.js && node scripts/bundle-agents.js && node scripts/bundle-docs.js && tsc && node scripts/sync-agent-mirrors.js && node scripts/bundle-esbuild.js",
|
|
27
|
+
"version": "node scripts/sync-readme-host-examples.js --write && git add -- README.md package.json package-lock.json && git commit -m \"chore(mcp-server): release $npm_package_version\" && git tag \"mcp-server/v$npm_package_version\"",
|
|
26
28
|
"check:version-generated": "node scripts/bundle-version.js && node scripts/check-version-generated.js",
|
|
29
|
+
"assert:release-identity": "node scripts/assert-release-identity.js",
|
|
27
30
|
"postbuild": "node scripts/prepend-shebang.cjs",
|
|
28
31
|
"start": "node build/index.js",
|
|
29
32
|
"test": "npm run test:normal && npm run test:module-mocks",
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
"test:integration": "node scripts/run-integration-tests.js",
|
|
33
36
|
"test:smoke": "node --test build/integration/packaged-cli-smoke.test.js",
|
|
34
37
|
"canary:agent-capabilities": "npm run build && node scripts/agent-capabilities-canary.mjs",
|
|
35
|
-
"prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs"
|
|
38
|
+
"prepublishOnly": "node scripts/bundle-assets.js && npm run build && node scripts/verify-shebang.cjs && npm run assert:release-identity"
|
|
36
39
|
},
|
|
37
40
|
"dependencies": {
|
|
38
41
|
"@modelcontextprotocol/sdk": "^1.29.0",
|