pipebrain-mcp 0.37.3 → 0.38.0
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/dist/main.js +181 -4
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -15211,6 +15211,24 @@ function parseDeliverableDisplayId(value) {
|
|
|
15211
15211
|
}
|
|
15212
15212
|
|
|
15213
15213
|
// ../../libs/domain/dist/project-scope.js
|
|
15214
|
+
var SOURCE_TREE_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
15215
|
+
"apps",
|
|
15216
|
+
"components",
|
|
15217
|
+
"db",
|
|
15218
|
+
"dist",
|
|
15219
|
+
"docs",
|
|
15220
|
+
"e2e",
|
|
15221
|
+
"lib",
|
|
15222
|
+
"libs",
|
|
15223
|
+
"migrations",
|
|
15224
|
+
"modules",
|
|
15225
|
+
"node_modules",
|
|
15226
|
+
"packages",
|
|
15227
|
+
"plugins",
|
|
15228
|
+
"routes",
|
|
15229
|
+
"scripts",
|
|
15230
|
+
"src"
|
|
15231
|
+
]);
|
|
15214
15232
|
var CODE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
15215
15233
|
"cjs",
|
|
15216
15234
|
"css",
|
|
@@ -15239,6 +15257,83 @@ var SOURCE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
15239
15257
|
...CODE_FILE_EXTENSIONS,
|
|
15240
15258
|
...DOCUMENT_FILE_EXTENSIONS
|
|
15241
15259
|
]);
|
|
15260
|
+
var PATH_RE = /(?:\.{0,2}[/\\])?[\w.-]+(?:[/\\][\w.-]*[\w-][\w.-]*)+/g;
|
|
15261
|
+
var SEPARATOR_RE = /[/\\]/;
|
|
15262
|
+
var ROOTED_RE = /^\.{0,2}[/\\]/;
|
|
15263
|
+
function trimTrailingPunctuation(value) {
|
|
15264
|
+
return value.replace(/[.,;:!?)\]]+$/, "");
|
|
15265
|
+
}
|
|
15266
|
+
function namesAFile(candidate, extensions) {
|
|
15267
|
+
const segments = candidate.split(SEPARATOR_RE);
|
|
15268
|
+
const last = segments[segments.length - 1];
|
|
15269
|
+
const dot = last.lastIndexOf(".");
|
|
15270
|
+
return dot > 0 && extensions.has(last.slice(dot + 1).toLowerCase());
|
|
15271
|
+
}
|
|
15272
|
+
function namesASourceFile(candidate) {
|
|
15273
|
+
return namesAFile(candidate, SOURCE_FILE_EXTENSIONS);
|
|
15274
|
+
}
|
|
15275
|
+
function isRepositoryPath(candidate) {
|
|
15276
|
+
if (ROOTED_RE.test(candidate))
|
|
15277
|
+
return true;
|
|
15278
|
+
if (candidate.split(SEPARATOR_RE).some((segment) => SOURCE_TREE_DIRECTORIES.has(segment.toLowerCase()))) {
|
|
15279
|
+
return true;
|
|
15280
|
+
}
|
|
15281
|
+
return namesASourceFile(candidate);
|
|
15282
|
+
}
|
|
15283
|
+
function findPath(source, accepts) {
|
|
15284
|
+
for (const raw of source.match(PATH_RE) ?? []) {
|
|
15285
|
+
const candidate = trimTrailingPunctuation(raw);
|
|
15286
|
+
if (accepts(candidate))
|
|
15287
|
+
return candidate;
|
|
15288
|
+
}
|
|
15289
|
+
return null;
|
|
15290
|
+
}
|
|
15291
|
+
function findRepositoryPath(source) {
|
|
15292
|
+
return findPath(source, isRepositoryPath);
|
|
15293
|
+
}
|
|
15294
|
+
|
|
15295
|
+
// ../../libs/domain/dist/code-shape.js
|
|
15296
|
+
var LINK_RE = /\w+:\/\/\S+/g;
|
|
15297
|
+
var BARE_URL_RE = /\b[\w-]+(?:\.[\w-]+)*\.[A-Za-z]{2,}\/\S*/g;
|
|
15298
|
+
var LINE_SUFFIX_RE = /(?:\.{0,2}[/\\])?[\w.-]+(?:[/\\][\w.-]+)*:\d+/g;
|
|
15299
|
+
var LINE_IN_WORDS_RE = /\b(?:at|on|see|near|around|from)\s+lines?\s+\d+\b/i;
|
|
15300
|
+
var BACKTICKED_RE = /`[^`]+`/;
|
|
15301
|
+
var CALL_RE = /\b[A-Za-z_$][\w$]*[A-Z][\w$]*\(\)/;
|
|
15302
|
+
function findLineSuffix(source) {
|
|
15303
|
+
for (const candidate of source.match(LINE_SUFFIX_RE) ?? []) {
|
|
15304
|
+
const token = candidate.slice(0, candidate.lastIndexOf(":"));
|
|
15305
|
+
if (isRepositoryPath(token))
|
|
15306
|
+
return candidate;
|
|
15307
|
+
}
|
|
15308
|
+
return null;
|
|
15309
|
+
}
|
|
15310
|
+
function findCodeShape(text) {
|
|
15311
|
+
const prose = text.replace(LINK_RE, " ").replace(BARE_URL_RE, " ");
|
|
15312
|
+
const path = findRepositoryPath(prose);
|
|
15313
|
+
if (path !== null)
|
|
15314
|
+
return { kind: "repository_path", match: path };
|
|
15315
|
+
const suffixed = findLineSuffix(prose);
|
|
15316
|
+
if (suffixed !== null)
|
|
15317
|
+
return { kind: "line_number", match: suffixed };
|
|
15318
|
+
const spelled = LINE_IN_WORDS_RE.exec(prose);
|
|
15319
|
+
if (spelled !== null)
|
|
15320
|
+
return { kind: "line_number", match: spelled[0] };
|
|
15321
|
+
const backticked = BACKTICKED_RE.exec(prose);
|
|
15322
|
+
if (backticked !== null)
|
|
15323
|
+
return { kind: "symbol_name", match: backticked[0] };
|
|
15324
|
+
const called = CALL_RE.exec(prose);
|
|
15325
|
+
if (called !== null)
|
|
15326
|
+
return { kind: "symbol_name", match: called[0] };
|
|
15327
|
+
return null;
|
|
15328
|
+
}
|
|
15329
|
+
var CODE_SHAPE_NAMES = {
|
|
15330
|
+
repository_path: "repository path",
|
|
15331
|
+
line_number: "line number",
|
|
15332
|
+
symbol_name: "symbol name"
|
|
15333
|
+
};
|
|
15334
|
+
function codeShapeMessage(field, hit) {
|
|
15335
|
+
return `${field} is read by someone without the code open \u2014 take out the ${CODE_SHAPE_NAMES[hit.kind]} \u201C${hit.match}\u201D and say it in plain words`;
|
|
15336
|
+
}
|
|
15242
15337
|
|
|
15243
15338
|
// ../../libs/domain/dist/entities.js
|
|
15244
15339
|
var organizationSchema = external_exports.object({
|
|
@@ -15360,6 +15455,22 @@ var issueSchema = external_exports.object({
|
|
|
15360
15455
|
milestone: external_exports.string().nullable(),
|
|
15361
15456
|
title: external_exports.string().min(1),
|
|
15362
15457
|
body: markdownSchema,
|
|
15458
|
+
// The header (PIPE-373): what this work is, why it matters, and what it touches —
|
|
15459
|
+
// three fields written by `set_issue_header`, never by the authoring tools, and
|
|
15460
|
+
// carried on every surface that renders a display id.
|
|
15461
|
+
//
|
|
15462
|
+
// OPTIONAL RATHER THAN NULLABLE-REQUIRED, because a header is absent far more often
|
|
15463
|
+
// than it is empty: the columns arrived on a table already full of rows, and the wire
|
|
15464
|
+
// mappers drop an undefined field rather than sending an explicit null.
|
|
15465
|
+
summary: external_exports.string().nullable().optional(),
|
|
15466
|
+
why: external_exports.string().nullable().optional(),
|
|
15467
|
+
affectedSurfaces: external_exports.string().nullable().optional(),
|
|
15468
|
+
// DERIVED ON READ, never stored: true when the header was written from a body that
|
|
15469
|
+
// has since changed, and true as well when there is no header at all — one comparison
|
|
15470
|
+
// covers both, so "absent" and "drifted" cannot answer differently. The provenance
|
|
15471
|
+
// hash it is derived from (`header_body_sha`) is internal and never reaches the wire.
|
|
15472
|
+
headerStale: external_exports.boolean().optional(),
|
|
15473
|
+
headerDerivedAt: external_exports.iso.datetime({ offset: true }).nullable().optional(),
|
|
15363
15474
|
parentId: uuidSchema.nullable(),
|
|
15364
15475
|
claimedBy: uuidSchema.nullable(),
|
|
15365
15476
|
source: sourceSchema,
|
|
@@ -15376,6 +15487,7 @@ var criterionFiledBugSchema = external_exports.object({
|
|
|
15376
15487
|
projectKey: projectKeySchema,
|
|
15377
15488
|
displayId: external_exports.string().min(1),
|
|
15378
15489
|
title: external_exports.string().min(1),
|
|
15490
|
+
summary: external_exports.string().nullable().optional(),
|
|
15379
15491
|
status: issueStatusSchema
|
|
15380
15492
|
});
|
|
15381
15493
|
var criterionSchema = external_exports.object({
|
|
@@ -15809,7 +15921,19 @@ var criterionInputSchema = external_exports.strictObject({
|
|
|
15809
15921
|
message: "`howTo` applies to manual criteria only \u2014 an auto criterion is checked by a machine, so instructions on one have no reader",
|
|
15810
15922
|
path: ["howTo"]
|
|
15811
15923
|
});
|
|
15924
|
+
var refusedIssueHeaderFields = {
|
|
15925
|
+
summary: external_exports.never({
|
|
15926
|
+
error: "summary cannot be set here \u2014 create or update the issue first, then write its header with set_issue_header"
|
|
15927
|
+
}).optional().describe("Not writable here. The header summary is written by `set_issue_header`."),
|
|
15928
|
+
why: external_exports.never({
|
|
15929
|
+
error: "why cannot be set here \u2014 create or update the issue first, then write its header with set_issue_header"
|
|
15930
|
+
}).optional().describe("Not writable here. The header reason is written by `set_issue_header`."),
|
|
15931
|
+
affectedSurfaces: external_exports.never({
|
|
15932
|
+
error: "affected surfaces cannot be set here \u2014 create or update the issue first, then write its header with set_issue_header"
|
|
15933
|
+
}).optional().describe("Not writable here. The affected surfaces are written by `set_issue_header`.")
|
|
15934
|
+
};
|
|
15812
15935
|
var createIssueInputSchema = external_exports.strictObject({
|
|
15936
|
+
...refusedIssueHeaderFields,
|
|
15813
15937
|
project: projectRefSchema,
|
|
15814
15938
|
type: issueTypeSchema,
|
|
15815
15939
|
title: external_exports.string().min(1, { message: "issue title must not be empty" }),
|
|
@@ -15835,6 +15959,9 @@ var createIssueInputSchema = external_exports.strictObject({
|
|
|
15835
15959
|
criterionId: uuidSchema.optional()
|
|
15836
15960
|
});
|
|
15837
15961
|
var promoteFeedbackInputSchema = external_exports.strictObject({
|
|
15962
|
+
// Declared here as well as on create/update: this is a standalone strict object that
|
|
15963
|
+
// does NOT spread create's shape, and it funnels into `createIssue` (PIPE-373 AC-1).
|
|
15964
|
+
...refusedIssueHeaderFields,
|
|
15838
15965
|
type: external_exports.enum(["epic", "task"]),
|
|
15839
15966
|
title: external_exports.string().min(1, { message: "issue title must not be empty" }),
|
|
15840
15967
|
status: issueStatusSchema.optional(),
|
|
@@ -15858,6 +15985,7 @@ var decisionLinkChangesSchema = external_exports.strictObject({
|
|
|
15858
15985
|
remove: external_exports.array(decisionRefSchema).optional()
|
|
15859
15986
|
});
|
|
15860
15987
|
var updateIssueInputSchema = external_exports.strictObject({
|
|
15988
|
+
...refusedIssueHeaderFields,
|
|
15861
15989
|
issue: issueRefSchema,
|
|
15862
15990
|
title: external_exports.string().min(1, { message: "issue title must not be empty" }).optional(),
|
|
15863
15991
|
body: markdownSchema.optional(),
|
|
@@ -15870,6 +15998,23 @@ var updateIssueInputSchema = external_exports.strictObject({
|
|
|
15870
15998
|
deliverableLinks: deliverableLinkChangesSchema.optional(),
|
|
15871
15999
|
decisionLinks: decisionLinkChangesSchema.optional()
|
|
15872
16000
|
});
|
|
16001
|
+
var ISSUE_SUMMARY_MAX = 350;
|
|
16002
|
+
function inPlainWords(schema, field) {
|
|
16003
|
+
return schema.refine((value) => findCodeShape(value) === null, {
|
|
16004
|
+
error: (iss) => {
|
|
16005
|
+
const hit = findCodeShape(String(iss.input));
|
|
16006
|
+
return hit === null ? `${field} must be written in plain words` : codeShapeMessage(field, hit);
|
|
16007
|
+
}
|
|
16008
|
+
});
|
|
16009
|
+
}
|
|
16010
|
+
var setIssueHeaderInputSchema = external_exports.strictObject({
|
|
16011
|
+
issue: issueRefSchema,
|
|
16012
|
+
summary: inPlainWords(external_exports.string().min(1, { message: "summary must not be empty" }).max(ISSUE_SUMMARY_MAX, {
|
|
16013
|
+
error: (iss) => `summary must be at most ${ISSUE_SUMMARY_MAX} characters \u2014 received ${String(iss.input).length}`
|
|
16014
|
+
}), "summary"),
|
|
16015
|
+
why: inPlainWords(external_exports.string().min(1, { message: "why must not be empty \u2014 say why this matters" }), "why"),
|
|
16016
|
+
affectedSurfaces: inPlainWords(external_exports.string().min(1, { message: "affected surfaces must not be empty" }), "affected surfaces")
|
|
16017
|
+
});
|
|
15873
16018
|
var setStatusInputSchema = external_exports.strictObject({
|
|
15874
16019
|
issue: issueRefSchema,
|
|
15875
16020
|
status: issueStatusSchema
|
|
@@ -16064,6 +16209,12 @@ var listIssuesFilterSchema = external_exports.strictObject({
|
|
|
16064
16209
|
// query string and lets anything else fail HERE, out loud, because a filter the
|
|
16065
16210
|
// server quietly dropped returns everything and reads like one that matched.
|
|
16066
16211
|
blocked: external_exports.boolean().optional(),
|
|
16212
|
+
// Header-less or drifted work (PIPE-373 AC-9): `true` is everything whose header is
|
|
16213
|
+
// missing or no longer describes the body it was written from, which is the list
|
|
16214
|
+
// somebody sits down to write headers from. Omitted means both. A real boolean for
|
|
16215
|
+
// the same reason `blocked` is one — a filter the server quietly dropped returns
|
|
16216
|
+
// everything and reads exactly like one that matched everything.
|
|
16217
|
+
needsHeader: external_exports.boolean().optional(),
|
|
16067
16218
|
// Pagination — server default 200, max 500 (§9); default applied server-side.
|
|
16068
16219
|
limit: external_exports.number().int().min(1, { message: "limit must be at least 1" }).max(500, { message: "limit must be at most 500" }).optional(),
|
|
16069
16220
|
offset: external_exports.number().int().nonnegative().optional()
|
|
@@ -16549,6 +16700,22 @@ var ApiClient = class _ApiClient {
|
|
|
16549
16700
|
addComment(ref, body) {
|
|
16550
16701
|
return this.request("POST", `/api/v1/issues/${enc(ref)}/comments`, { body: { body } });
|
|
16551
16702
|
}
|
|
16703
|
+
/**
|
|
16704
|
+
* Writes the structured header onto an issue that already exists (PIPE-373).
|
|
16705
|
+
*
|
|
16706
|
+
* WHOLE VALUE, all three fields required — a stale header is replaced outright,
|
|
16707
|
+
* so there is nothing to merge and nothing to pin. The server stamps a hash of
|
|
16708
|
+
* the body the three texts were derived from at the same time, which is what
|
|
16709
|
+
* later reads compare against to say whether the header still describes the
|
|
16710
|
+
* issue; a partial write would leave that hash vouching for words nobody
|
|
16711
|
+
* rewrote.
|
|
16712
|
+
*
|
|
16713
|
+
* `issue` is dropped from the body because the ref rides the path, the way it
|
|
16714
|
+
* does on every other whole-value write here.
|
|
16715
|
+
*/
|
|
16716
|
+
setIssueHeader(ref, input) {
|
|
16717
|
+
return this.request("PUT", `/api/v1/issues/${enc(ref)}/header`, { body: input });
|
|
16718
|
+
}
|
|
16552
16719
|
/** Records the issue as blocked with a required reason (PIPE-99). */
|
|
16553
16720
|
setBlocked(ref, reason) {
|
|
16554
16721
|
return this.request("PUT", `/api/v1/issues/${enc(ref)}/blocked`, {
|
|
@@ -17517,7 +17684,7 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17517
17684
|
server2.registerTool(
|
|
17518
17685
|
"list_issues",
|
|
17519
17686
|
{
|
|
17520
|
-
description: `List issue summaries (display id, title, type, status, terminal, blocked, priority, milestone, claimer, criteria progress, updated_at) filtered by project, environment, status[], type[], claimedBy (me/unclaimed/uuid), priority[], milestone, text (title+body), parent, and
|
|
17687
|
+
description: `List issue summaries (display id, title, type, status, terminal, blocked, priority, milestone, claimer, criteria progress, updated_at, and the header \u2014 \`summary\`, \`why\`, \`affectedSurfaces\`, \`headerStale\`, \`headerDerivedAt\` \u2014 so a list says what each issue is about without opening it) filtered by project, environment, status[], type[], claimedBy (me/unclaimed/uuid), priority[], milestone, text (title+body), parent, blocked, and needsHeader (\xA77). DEFAULT: with no status filter this returns BUILDABLE work only \u2014 ${OPEN_STATUSES.join(", ")}. Terminal work (done, killed) is excluded unless you ask for it by name: pass status explicitly, e.g. status: ["done","killed"] for finished and abandoned work, or list every value to see everything. An explicit status filter is used verbatim and the default is not merged into it. Every row also carries \`terminal\` (PIPE-R15 r4 rule 5). blocked: false returns only work with nothing blocking it, and every issue it returns can be moved to ready without refusal; blocked: true returns only blocked work. needsHeader: true returns exactly the issues whose header is missing or no longer describes the body it was derived from \u2014 the set \`set_issue_header\` still has to be called on \u2014 and needsHeader: false returns only those whose header still holds. Each row\u2019s \`headerStale\` is the same expression this filter selects on, so a row cannot disagree with the filter that returned it. The status default above is not lifted for it: name done and killed explicitly when the sweep is meant to reach finished work as well.`,
|
|
17521
17688
|
inputSchema: listIssuesFilterSchema.shape
|
|
17522
17689
|
},
|
|
17523
17690
|
// The default lives HERE, not in the API. PIPE-R15 r4 rule 5 makes the default
|
|
@@ -17531,7 +17698,7 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17531
17698
|
(args) => run(() => client2.listIssues(args.status === void 0 ? { ...args, status: [...OPEN_STATUSES] } : args), GUIDANCE_POINTER)
|
|
17532
17699
|
);
|
|
17533
17700
|
server2.registerTool("get_issue", {
|
|
17534
|
-
description: "Full issue detail by display id or uuid: body, criteria (states, modes, evidence), explicit requirement links (pinned revision + staleness), the project\u2019s cross-cutting requirements at their latest approved revision, this organization\u2019s approved conventions \u2014 standing guidance that BINDS work in every project here, delivered in full at the revision a person approved \u2014 children, and comments (\xA77). Carries `terminal`: true when the issue is finished (`done`) or deliberately abandoned (`killed`) and is therefore NOT buildable work \u2014 check it before acting on an issue you were handed by key. Reading criterion state: `state_evidence` is required only when a TOKEN sets one, so a `manual` criterion \u2014 settable only from a web session \u2014 carries none by design, and the attribution the server already stores (who set it, and when) IS its record. What this payload tells you about ANOTHER issue goes stale. Reference that issue by key and say what you need from it; never restate its status, its criteria, or whether those criteria passed \u2014 such a sentence is true when written and wrong later, and a terminal issue is never revisited to correct it, so the stale reading is what the next reader inherits.",
|
|
17701
|
+
description: "Full issue detail by display id or uuid: the header \u2014 `summary`, `why`, `affectedSurfaces`, with `headerDerivedAt` and `headerStale`, true when nobody has written a header or the body has moved since one was written, and `set_issue_header` is what writes it \u2014 body, criteria (states, modes, evidence), explicit requirement links (pinned revision + staleness), the project\u2019s cross-cutting requirements at their latest approved revision, this organization\u2019s approved conventions \u2014 standing guidance that BINDS work in every project here, delivered in full at the revision a person approved \u2014 children, and comments (\xA77). Carries `terminal`: true when the issue is finished (`done`) or deliberately abandoned (`killed`) and is therefore NOT buildable work \u2014 check it before acting on an issue you were handed by key. Reading criterion state: `state_evidence` is required only when a TOKEN sets one, so a `manual` criterion \u2014 settable only from a web session \u2014 carries none by design, and the attribution the server already stores (who set it, and when) IS its record. What this payload tells you about ANOTHER issue goes stale. Reference that issue by key and say what you need from it; never restate its status, its criteria, or whether those criteria passed \u2014 such a sentence is true when written and wrong later, and a terminal issue is never revisited to correct it, so the stale reading is what the next reader inherits.",
|
|
17535
17702
|
inputSchema: { issue: issueRefSchema }
|
|
17536
17703
|
}, (args) => run(() => client2.getIssue(args.issue), GUIDANCE_POINTER));
|
|
17537
17704
|
server2.registerTool("create_issue", {
|
|
@@ -17557,6 +17724,10 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17557
17724
|
description: "Update any subset of an issue\u2019s title, body, type, priority, milestone, environment, parent (self/cycle rejected), requirement_links (add/remove; adding a link to an unapproved requirement is rejected), deliverable_links (add/remove content-deliverable links, add pins at the current revision), or decision_links (add/remove decision records this work sits under \u2014 `KEY-D{n}`, from `list_decisions`). Changing type to `feedback` is rejected while the issue has criteria (\xA77), since feedback carries none; epic, task and bug interchange freely and the criteria ride along, keeping their AC-{n} numbers and their states. Clearing `milestone` (passing null) is rejected while the issue sits past `triage` \u2014 work already scheduled keeps a milestone (PIPE-R10 rule 9); swap it for a different one, or move the issue back to `triage` first. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CLink by key; never paste another record\u2019s text\u201D and \u201CA decision you need is filed as a question, never written into a body\u201D.",
|
|
17558
17725
|
inputSchema: updateIssueInputSchema.shape
|
|
17559
17726
|
}, ({ issue: issue2, ...fields }) => run(() => client2.updateIssue(issue2, fields)));
|
|
17727
|
+
server2.registerTool("set_issue_header", {
|
|
17728
|
+
description: "Write an issue\u2019s HEADER \u2014 the three lines an operator reads before deciding anything: `summary` (the change, in one sentence), `why` (the user, business, cost or operational reason rather than the technical one; when nothing is user-visible, the cost of leaving it alone) and `affectedSurfaces` (which parts of the product this touches, and who feels it). All three are REQUIRED and the write is whole-value: the header is replaced entire and never merged, so a partial write cannot leave the stored provenance vouching for lines it did not touch. Returns the full issue detail. WRITE ALL THREE IN PLAIN WORDS: no identifiers, no file paths, no type names, no jargon. Their reader is deciding, not building, and the depth belongs in the body underneath them. A `summary` shaped like code \u2014 a repository path, a line number, a symbol written as a call or in backticks \u2014 is refused (400) naming what was found. `summary` is capped at 350 characters; over it the write is refused naming both the cap and the length received. THE HEADER CAN BE WRITTEN NO OTHER WAY: `summary`, `why` or `affectedSurfaces` passed to `create_issue`, `update_issue` or `promote_feedback` is refused (400) naming the field, rather than silently dropped \u2014 create the issue first, then write its header here. Every read of the issue carries `headerStale`, true when the body has changed since these three lines were derived from it and true when no header was ever written; writing here clears it. Writing guidance for these three lines is stated above; the organization\u2019s own writing rules, which `get_work_context` returns, govern the body beneath them.",
|
|
17729
|
+
inputSchema: setIssueHeaderInputSchema.shape
|
|
17730
|
+
}, ({ issue: issue2, ...fields }) => run(() => client2.setIssueHeader(issue2, fields)));
|
|
17560
17731
|
server2.registerTool("set_status", {
|
|
17561
17732
|
description: "Set an issue\u2019s status; the \xA76 guards apply and a guard failure returns the specific reason, naming the rows it refused on (\xA77). The criteria gates read whatever criteria the issue carries, whatever its type: verify needs every auto criterion passed, done needs every criterion passed. ONE guard remains EPIC-ONLY, and the asymmetry is the rule rather than an oversight \u2014 an epic that has stated NO criteria is refused at both gates, because an empty list would clear them having committed to nothing, while a task or a bug carrying none moves through both untouched (most work is stated adequately by its body). `done` additionally requires every child of the issue being closed to be terminal \u2014 an epic, a task and a bug alike \u2014 read from each child\u2019s STATUS and never from its criteria. A BLOCKED issue \u2014 one carrying an explicit mark or waiting on a blocker that is not yet `done`/`killed` \u2014 cannot move to `ready` or `in_progress`, and the refusal names the reason and every unresolved blocker, so clear the mark (`clear_blocked`) or finish, kill or `unlink_blocker` each blocker first; every other target still moves while blocked. An issue with NO milestone cannot move to any status but `triage` and `killed` (PIPE-R10 rule 9) \u2014 set one with `update_issue` first; `killed` is exempt, so work nobody scheduled can still be abandoned.",
|
|
17562
17733
|
inputSchema: setStatusInputSchema.shape
|
|
@@ -17765,7 +17936,13 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
|
|
|
17765
17936
|
title: external_exports.string().min(1),
|
|
17766
17937
|
type: issueTypeSchema,
|
|
17767
17938
|
status: issueStatusSchema.optional(),
|
|
17768
|
-
body: markdownSchema.optional()
|
|
17939
|
+
body: markdownSchema.optional(),
|
|
17940
|
+
// This tool CREATES an issue, so it is a door the header refusal has to cover
|
|
17941
|
+
// (PIPE-373 AC-1). The fields must be DECLARED here rather than left off: the
|
|
17942
|
+
// SDK rebuilds this raw shape as a non-strict object, so an undeclared key is
|
|
17943
|
+
// STRIPPED rather than refused — the call would succeed, the issue would be
|
|
17944
|
+
// created, and the author's three lines would be dropped with nothing said.
|
|
17945
|
+
...refusedIssueHeaderFields
|
|
17769
17946
|
}
|
|
17770
17947
|
}, ({ deliverable, ...fields }) => run(() => client2.promoteDeliverable(deliverable, fields)));
|
|
17771
17948
|
server2.registerTool("list_assets", {
|
|
@@ -17924,7 +18101,7 @@ function loadMcpConfig() {
|
|
|
17924
18101
|
}
|
|
17925
18102
|
|
|
17926
18103
|
// src/version.ts
|
|
17927
|
-
var VERSION = "0.
|
|
18104
|
+
var VERSION = "0.38.0";
|
|
17928
18105
|
|
|
17929
18106
|
// src/main.ts
|
|
17930
18107
|
var config2 = loadMcpConfig();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pipebrain-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.38.0",
|
|
4
4
|
"description": "Pipebrain MCP server for Claude Code and Claude Desktop — issues, requirements, content, assets, and metadata-only secrets over the Pipebrain API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|