pipebrain-mcp 0.35.0 → 0.37.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.
Files changed (2) hide show
  1. package/dist/main.js +262 -6
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -15101,6 +15101,12 @@ var issueRefSchema = external_exports.string().min(1, { message: "issue referenc
15101
15101
  var requirementRefSchema = external_exports.string().min(1, {
15102
15102
  message: "requirement reference must not be empty \u2014 use a display id (`DWB-R4`) or uuid"
15103
15103
  });
15104
+ var conventionRefSchema = external_exports.string().min(1, {
15105
+ message: "convention reference must not be empty \u2014 use a display id (`CONV-3`) or uuid"
15106
+ });
15107
+ var promptRefSchema = external_exports.string().min(1, {
15108
+ message: "prompt reference must not be empty \u2014 use a display id (`PROMPT-3`) or uuid"
15109
+ });
15104
15110
  var decisionRefSchema = external_exports.string().min(1, {
15105
15111
  message: "decision reference must not be empty \u2014 use a display id (`DWB-D7`) or uuid"
15106
15112
  });
@@ -15167,6 +15173,36 @@ function parseDeliverableDisplayId(value) {
15167
15173
  return { key: match[1], number: Number(match[2]), platform: match[3] };
15168
15174
  }
15169
15175
 
15176
+ // ../../libs/domain/dist/project-scope.js
15177
+ var CODE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
15178
+ "cjs",
15179
+ "css",
15180
+ "go",
15181
+ "java",
15182
+ "js",
15183
+ "jsx",
15184
+ "mjs",
15185
+ "py",
15186
+ "rb",
15187
+ "rs",
15188
+ "sh",
15189
+ "sql",
15190
+ "ts",
15191
+ "tsx"
15192
+ ]);
15193
+ var DOCUMENT_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
15194
+ "html",
15195
+ "json",
15196
+ "md",
15197
+ "toml",
15198
+ "yaml",
15199
+ "yml"
15200
+ ]);
15201
+ var SOURCE_FILE_EXTENSIONS = /* @__PURE__ */ new Set([
15202
+ ...CODE_FILE_EXTENSIONS,
15203
+ ...DOCUMENT_FILE_EXTENSIONS
15204
+ ]);
15205
+
15170
15206
  // ../../libs/domain/dist/entities.js
15171
15207
  var organizationSchema = external_exports.object({
15172
15208
  id: uuidSchema,
@@ -15376,6 +15412,65 @@ var requirementApprovalSchema = external_exports.object({
15376
15412
  approvedBy: uuidSchema,
15377
15413
  createdAt: external_exports.iso.datetime({ offset: true })
15378
15414
  });
15415
+ var conventionSchema = external_exports.object({
15416
+ id: uuidSchema,
15417
+ displayId: external_exports.string().min(1),
15418
+ orgId: uuidSchema,
15419
+ number: positiveIntSchema,
15420
+ approvedRevisionId: uuidSchema.nullable(),
15421
+ lifecycle: requirementLifecycleSchema,
15422
+ supersededById: uuidSchema.nullable(),
15423
+ supersededAt: external_exports.iso.datetime({ offset: true }).nullable(),
15424
+ createdBy: uuidSchema.nullable(),
15425
+ createdAt: external_exports.iso.datetime({ offset: true }),
15426
+ updatedAt: external_exports.iso.datetime({ offset: true }),
15427
+ deletedAt: external_exports.iso.datetime({ offset: true }).nullable()
15428
+ });
15429
+ var conventionRevisionSchema = external_exports.object({
15430
+ id: uuidSchema,
15431
+ orgId: uuidSchema,
15432
+ conventionId: uuidSchema,
15433
+ revisionNumber: positiveIntSchema,
15434
+ title: external_exports.string().min(1),
15435
+ body: markdownSchema,
15436
+ note: external_exports.string().nullable(),
15437
+ createdBy: uuidSchema.nullable(),
15438
+ viaTokenId: uuidSchema.nullable(),
15439
+ createdAt: external_exports.iso.datetime({ offset: true })
15440
+ });
15441
+ var conventionApprovalSchema = external_exports.object({
15442
+ id: uuidSchema,
15443
+ orgId: uuidSchema,
15444
+ conventionId: uuidSchema,
15445
+ revisionId: uuidSchema,
15446
+ approvedBy: uuidSchema,
15447
+ viaTokenId: uuidSchema.nullable(),
15448
+ supersededId: uuidSchema.nullable(),
15449
+ createdAt: external_exports.iso.datetime({ offset: true })
15450
+ });
15451
+ var promptSchema = external_exports.object({
15452
+ id: uuidSchema,
15453
+ displayId: external_exports.string().min(1),
15454
+ orgId: uuidSchema,
15455
+ number: positiveIntSchema,
15456
+ projectId: uuidSchema.nullable(),
15457
+ createdBy: uuidSchema.nullable(),
15458
+ createdAt: external_exports.iso.datetime({ offset: true }),
15459
+ updatedAt: external_exports.iso.datetime({ offset: true }),
15460
+ deletedAt: external_exports.iso.datetime({ offset: true }).nullable()
15461
+ });
15462
+ var promptRevisionSchema = external_exports.object({
15463
+ id: uuidSchema,
15464
+ orgId: uuidSchema,
15465
+ promptId: uuidSchema,
15466
+ revisionNumber: positiveIntSchema,
15467
+ title: external_exports.string().min(1),
15468
+ body: markdownSchema,
15469
+ note: external_exports.string().nullable(),
15470
+ createdBy: uuidSchema.nullable(),
15471
+ viaTokenId: uuidSchema.nullable(),
15472
+ createdAt: external_exports.iso.datetime({ offset: true })
15473
+ });
15379
15474
  var issueRequirementLinkSchema = external_exports.object({
15380
15475
  id: uuidSchema,
15381
15476
  orgId: uuidSchema,
@@ -15821,6 +15916,51 @@ var approveRequirementInputSchema = external_exports.strictObject({
15821
15916
  // → requirement_revisions.revision_number, an `integer` column (PIPE-149).
15822
15917
  revision: int4Schema.optional()
15823
15918
  });
15919
+ var createConventionInputSchema = external_exports.strictObject({
15920
+ title: external_exports.string().min(1, { message: "convention title must not be empty" }),
15921
+ body: markdownSchema,
15922
+ note: external_exports.string().optional(),
15923
+ outlivesAnyProject: external_exports.literal(true, {
15924
+ message: "set outlivesAnyProject: true to assert this statement outlives any single project \u2014 if it stops being true when one project ends it is a cross-cutting requirement on that project, not a convention"
15925
+ })
15926
+ });
15927
+ var updateConventionInputSchema = external_exports.strictObject({
15928
+ convention: conventionRefSchema,
15929
+ title: external_exports.string().min(1, { message: "convention title must not be empty" }).optional(),
15930
+ body: markdownSchema.optional(),
15931
+ note: external_exports.string().min(1, { message: "note must not be empty \u2014 omit it if there is nothing to record" }).optional()
15932
+ }).refine((v) => v.title !== void 0 || v.body !== void 0, {
15933
+ message: "empty edit: supply at least one of `title` or `body`"
15934
+ });
15935
+ var listConventionsFilterSchema = external_exports.strictObject({
15936
+ text: external_exports.string().optional()
15937
+ });
15938
+ var createPromptInputSchema = external_exports.strictObject({
15939
+ title: external_exports.string().min(1, { message: "prompt title must not be empty" }),
15940
+ body: markdownSchema.min(1, { message: "prompt body must not be empty" }),
15941
+ note: external_exports.string().min(1, { message: "note must not be empty \u2014 omit it if there is nothing to record" }).optional(),
15942
+ // The OPTIONAL project attachment. Omitted = a prompt that belongs to the organisation
15943
+ // and no project, which is the common case for a prompt about how to work.
15944
+ project: projectRefSchema.optional()
15945
+ });
15946
+ var updatePromptInputSchema = external_exports.strictObject({
15947
+ prompt: promptRefSchema,
15948
+ title: external_exports.string().min(1, { message: "prompt title must not be empty" }).optional(),
15949
+ body: markdownSchema.min(1, { message: "prompt body must not be empty" }).optional(),
15950
+ note: external_exports.string().min(1, { message: "note must not be empty \u2014 omit it if there is nothing to record" }).optional(),
15951
+ // `null` DETACHES the prompt from its project; omitted leaves the attachment alone.
15952
+ // Moving the attachment is a record-level change and writes NO revision — the
15953
+ // revision chain is the history of the text, and a project move changes no text.
15954
+ project: projectRefSchema.nullable().optional()
15955
+ }).refine((v) => v.title !== void 0 || v.body !== void 0 || v.project !== void 0, {
15956
+ message: "empty edit: supply at least one of `title`, `body`, or `project`"
15957
+ });
15958
+ var listPromptsFilterSchema = external_exports.strictObject({
15959
+ // Filters to prompts attached to this project. Omitted returns EVERY prompt in the
15960
+ // organisation, attached or not — the org-wide library is the default view.
15961
+ project: projectRefSchema.optional(),
15962
+ text: external_exports.string().optional()
15963
+ });
15824
15964
  var createDecisionInputSchema = external_exports.strictObject({
15825
15965
  project: projectRefSchema,
15826
15966
  title: external_exports.string().min(1, { message: "decision title must not be empty" }),
@@ -15899,9 +16039,10 @@ var createOrgInputSchema = external_exports.strictObject({
15899
16039
  });
15900
16040
  var updateOrgInputSchema = external_exports.strictObject({
15901
16041
  name: external_exports.string().min(1, { message: "organization name must not be empty" }).optional(),
15902
- slug: orgSlugSchema.optional()
15903
- }).refine((v) => v.name !== void 0 || v.slug !== void 0, {
15904
- message: "empty edit: supply at least one of `name` or `slug`"
16042
+ slug: orgSlugSchema.optional(),
16043
+ conventionWarningThreshold: int4Schema.nullable().optional()
16044
+ }).refine((v) => v.name !== void 0 || v.slug !== void 0 || v.conventionWarningThreshold !== void 0, {
16045
+ message: "empty edit: supply at least one of `name`, `slug`, or `conventionWarningThreshold`"
15905
16046
  });
15906
16047
  var createInvitationInputSchema = external_exports.strictObject({
15907
16048
  email: emailSchema,
@@ -16401,6 +16542,71 @@ var ApiClient = class _ApiClient {
16401
16542
  body: revision === void 0 ? {} : { revision }
16402
16543
  });
16403
16544
  }
16545
+ // --- organization conventions (PIPE-193) -----------------------------------
16546
+ //
16547
+ // FOUR methods, and the absence of a fifth is the design: there is deliberately NO
16548
+ // `approveConvention` here, and there must not be one. Approving is a signed-in
16549
+ // person's act in the browser (AC-1) — the API refuses the route to a bearer caller —
16550
+ // and this client is what the agent surface calls, so a method for it would only ever
16551
+ // be a door an agent walks into a 403 through. The web app posts that route directly.
16552
+ /**
16553
+ * The whole list, superseded conventions included, with the delivered count and the
16554
+ * threshold that warns about it. NOT `requestList`: the response is a named object
16555
+ * rather than an `{ items }` envelope, because the count and the threshold are answers
16556
+ * to the same question the list is and belong in the same read.
16557
+ */
16558
+ listConventions(filter = {}) {
16559
+ return this.request("GET", "/api/v1/conventions", { query: filter });
16560
+ }
16561
+ getConvention(ref) {
16562
+ return this.request("GET", `/api/v1/conventions/${enc(ref)}`);
16563
+ }
16564
+ createConvention(input) {
16565
+ return this.request("POST", "/api/v1/conventions", { body: input });
16566
+ }
16567
+ updateConvention(ref, fields) {
16568
+ return this.request("PATCH", `/api/v1/conventions/${enc(ref)}`, {
16569
+ body: fields
16570
+ });
16571
+ }
16572
+ // --- prompts (PIPE-196) ----------------------------------------------------
16573
+ //
16574
+ // FOUR methods, and the absence of a fifth is the design here too: there is deliberately
16575
+ // NO `deletePrompt`, and there must not be one. Nothing removes a prompt on any surface
16576
+ // — an edit APPENDS a revision, so the text a prompt used to carry stays readable, and a
16577
+ // prompt nobody runs any more is one whose newest revision says so. There is no route
16578
+ // for such a method to call.
16579
+ /**
16580
+ * The whole organization's library, filtered by `project` and/or `text`.
16581
+ *
16582
+ * `requestList`, like every other list on this client, and NOT {@link listConventions}'s
16583
+ * named object: the convention response is a bespoke envelope because it carries the
16584
+ * delivered count and the threshold that warns about it beside the list — every approved
16585
+ * convention rides along on every work-item read. A prompt is delivered to nobody
16586
+ * automatically, so there is no second number to carry and the plain §9 `{ items }`
16587
+ * envelope is the whole shape. Going through `requestList` is what turns a proxy's
16588
+ * malformed 2xx into a typed `invalid_response` here rather than an opaque TypeError at
16589
+ * whichever caller first maps the result.
16590
+ */
16591
+ listPrompts(filter = {}) {
16592
+ return this.requestList("GET", "/api/v1/prompts", { query: filter });
16593
+ }
16594
+ getPrompt(ref) {
16595
+ return this.request("GET", `/api/v1/prompts/${enc(ref)}`);
16596
+ }
16597
+ createPrompt(input) {
16598
+ return this.request("POST", "/api/v1/prompts", { body: input });
16599
+ }
16600
+ /**
16601
+ * `title`/`body` APPEND a revision; `note` records why. Supplying none of the three
16602
+ * editable fields is an empty edit, refused by the API (400) — a note alone amends
16603
+ * nothing.
16604
+ */
16605
+ updatePrompt(ref, fields) {
16606
+ return this.request("PATCH", `/api/v1/prompts/${enc(ref)}`, {
16607
+ body: fields
16608
+ });
16609
+ }
16404
16610
  // --- decision records (PIPE-84) --------------------------------------------
16405
16611
  //
16406
16612
  // Five routes, no DELETE: PIPE-73 made four record kinds deletable and this epic
@@ -17118,7 +17324,7 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
17118
17324
  // one description loaded into EVERY session by `anthropic/alwaysLoad`. The clauses
17119
17325
  // that went were the ones arguing for the rule rather than stating it; what a caller
17120
17326
  // needs to predict a refusal all stayed.
17121
- description: "Everything a session needs before it starts work, in ONE read \u2014 assembled by the API and returned verbatim, with the BINDING GUIDANCE appended after it: the product doctrine and this organization\u2019s own writing rules, in full. This is the one call that returns them; every other read only names it. CALL IT FIRST, before you write anything. Addressed by `issue` OR by `project`, never both and never neither: an issue names its own project, so the pair is refused rather than resolved for you (\xA77). Given an ISSUE: that issue\u2019s full detail \u2014 the same payload `get_issue` returns \u2014 PLUS the BODY TEXT of every requirement it links, at the revision the link is pinned at. That text is the round trip this removes: `get_issue` names which documents bind the work and at which revision, never what they SAY. Comments come CAPPED at the newest `commentLimit`, with `commentCount` \u2014 the issue\u2019s TRUE total \u2014 beside them; read the issue directly for the rest. Given a PROJECT: `available`, the work a session could START right now. It EXCLUDES finished and abandoned work (`done`, `killed`), work another person has claimed, and anything blocked \u2014 the status guard would refuse to start that. Your OWN claims stay, and so does unscheduled work carrying no milestone. `available` comes CAPPED at `availableLimit` rows, with `availableCount` \u2014 the project\u2019s TRUE total of startable work \u2014 beside it. The item form carries no `available`; the project form carries no item. BOTH forms also carry the project half: the release currently being worked (the active milestone) and the project\u2019s cross-cutting requirements WITH their text \u2014 they bind every epic automatically. READ-ONLY \u2014 this claims nothing, moves nothing and writes nothing.",
17327
+ description: "Everything a session needs before it starts work, in ONE read \u2014 assembled by the API and returned verbatim, with the BINDING GUIDANCE appended after it: the product doctrine and this organization\u2019s own writing rules, in full. This is the one call that returns them; every other read only names it. CALL IT FIRST, before you write anything. Addressed by `issue` OR by `project`, never both and never neither: an issue names its own project, so the pair is refused rather than resolved for you (\xA77). Given an ISSUE: that issue\u2019s full detail \u2014 the same payload `get_issue` returns \u2014 PLUS the BODY TEXT of every requirement it links, at the revision the link is pinned at. That text is the round trip this removes: `get_issue` names which documents bind the work and at which revision, never what they SAY. Comments come CAPPED at the newest `commentLimit`, with `commentCount` \u2014 the issue\u2019s TRUE total \u2014 beside them; read the issue directly for the rest. Given a PROJECT: `available`, the work a session could START right now. It EXCLUDES finished and abandoned work (`done`, `killed`), work another person has claimed, and anything blocked \u2014 the status guard would refuse to start that. Your OWN claims stay, and so does unscheduled work carrying no milestone. `available` comes CAPPED at `availableLimit` rows, with `availableCount` \u2014 the project\u2019s TRUE total of startable work \u2014 beside it. The item form carries no `available`; the project form carries no item. BOTH forms also carry the project half: the release currently being worked (the active milestone) and the project\u2019s cross-cutting requirements WITH their text \u2014 they bind every epic automatically \u2014 and this organization\u2019s approved conventions in full, standing guidance that binds work in EVERY project here, not just this one. READ-ONLY \u2014 this claims nothing, moves nothing and writes nothing.",
17122
17328
  // The WHOLE domain schema rather than its `.shape`: the exactly-one-of rule is a
17123
17329
  // refinement on the OBJECT, and a raw shape drops it — the SDK validates against
17124
17330
  // what it is handed, so the shape alone would quietly accept both arguments and
@@ -17234,7 +17440,7 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
17234
17440
  (args) => run(() => client2.listIssues(args.status === void 0 ? { ...args, status: [...OPEN_STATUSES] } : args), GUIDANCE_POINTER)
17235
17441
  );
17236
17442
  server2.registerTool("get_issue", {
17237
- 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, 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.",
17443
+ 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.",
17238
17444
  inputSchema: { issue: issueRefSchema }
17239
17445
  }, (args) => run(() => client2.getIssue(args.issue), GUIDANCE_POINTER));
17240
17446
  server2.registerTool("create_issue", {
@@ -17367,6 +17573,56 @@ function createMcpServer(client2, apiBaseUrl, orgInstructions2 = /* @__PURE__ */
17367
17573
  description: "Approve (pin) a requirement\u2019s latest revision, or a specified `revision`, recording it in approvals history and advancing the approved revision that downstream links and reads build against (\xA75, \xA77).",
17368
17574
  inputSchema: approveRequirementInputSchema.shape
17369
17575
  }, (args) => run(() => client2.approveRequirement(args.requirement, args.revision)));
17576
+ server2.registerTool("list_conventions", {
17577
+ description: "List this organization\u2019s conventions \u2014 standing guidance that binds work in EVERY project here, unlike a requirement, which binds one project. Returns display id (`CONV-{n}`), title, the standing text, lifecycle (draft/approved/amended), latest + approved revision numbers, and whether it has been superseded and by which convention. Optional `text` matches over title and body. Retired conventions are INCLUDED and marked, so you can see what the organization used to require rather than proposing it again. Only APPROVED, unretired conventions are actually delivered to agents \u2014 every `get_issue` and `get_work_context` read carries them.",
17578
+ inputSchema: listConventionsFilterSchema.shape
17579
+ }, (args) => run(() => client2.listConventions(args), GUIDANCE_POINTER));
17580
+ server2.registerTool("get_convention", {
17581
+ description: "Get one convention by display id (`CONV-{n}`) or uuid: the standing text at the approved revision, its full revision history with who wrote each and when, its approval history, and \u2014 when it has been retired \u2014 which convention replaced it. A convention that was never approved is readable here and is delivered to nobody.",
17582
+ inputSchema: { convention: conventionRefSchema }
17583
+ }, (args) => run(() => client2.getConvention(args.convention), GUIDANCE_POINTER));
17584
+ server2.registerTool("create_convention", {
17585
+ description: "Write a convention: organization-wide standing guidance, with no project attached. THE GOVERNING TEST \u2014 if it stops being true when this project ends it is a cross-cutting requirement; if it would still be true on the next project it is a convention. You must assert that test by passing `outlivesAnyProject: true`; the write is refused without it. A statement naming one specific project \u2014 its key, a display id built on it, a repository path, or a database object \u2014 is also refused (422 `convention_names_project_scope`), and the message says to file it as a cross-cutting requirement on that project instead. IT BINDS NOBODY UNTIL IT IS APPROVED, and approval is NOT available on this surface: it requires a signed-in person in the browser, so there is no tool for it here and asking for one is asking for a tool that deliberately does not exist. A new convention starts as a draft; write it, then ask a person to review and approve it. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CSearch before you create\u201D and \u201CLink by key; never paste another record\u2019s text\u201D.",
17586
+ inputSchema: createConventionInputSchema.shape
17587
+ }, (args) => run(() => client2.createConvention(args)));
17588
+ server2.registerTool("update_convention", {
17589
+ description: "Amend a convention: `title` and/or `body` APPEND a new revision \u2014 nothing is ever edited in place \u2014 and optional `note` records why. Supplying neither is an empty edit (400); a note alone amends nothing. The same project-scope refusal applies as on create, over the amended text. THE AMENDMENT BINDS NOBODY UNTIL IT IS ITSELF APPROVED: until then agents keep receiving the approved revision, so amending is not a way to change what is being delivered \u2014 a signed-in person approving the new revision is.",
17590
+ // Inline shape (updateConventionInputSchema is a refine, so it has no `.shape`);
17591
+ // the empty-edit rule is enforced by the API, not by this shape.
17592
+ inputSchema: {
17593
+ convention: conventionRefSchema,
17594
+ title: external_exports.string().min(1).optional(),
17595
+ body: markdownSchema.optional(),
17596
+ note: external_exports.string().min(1).optional()
17597
+ }
17598
+ }, ({ convention, ...fields }) => run(() => client2.updateConvention(convention, fields)));
17599
+ server2.registerTool("list_prompts", {
17600
+ description: "List this organization\u2019s prompt library \u2014 reusable prompt text, addressed `PROMPT-{n}`. A prompt belongs to the ORGANIZATION rather than to a project, unlike a requirement or a content draft, so with no filter this returns the whole library. Returns display id, title, the current text, the latest revision number, and the attached project when it has one. `project` narrows to the prompts attached to that project \u2014 that attachment is metadata saying what a prompt is ABOUT, never its scope, so a prompt attached elsewhere is still one you may read and run. `text` matches over TITLE AND BODY, because someone looking for a prompt remembers what it says rather than what somebody called it.",
17601
+ inputSchema: listPromptsFilterSchema.shape
17602
+ }, (args) => run(() => client2.listPrompts(args), GUIDANCE_POINTER));
17603
+ server2.registerTool("get_prompt", {
17604
+ description: "Get one prompt by display id (`PROMPT-{n}`) or uuid: its current text \u2014 the LATEST revision, which is simply the prompt, since nothing approves a prompt and nothing pins to an older wording \u2014 plus the full revision history with who wrote each save, through which token, when, and the note saying why. Nothing here was edited in place, so a wording that has been replaced is still readable rather than lost; read the history before rewriting a prompt that already works.",
17605
+ inputSchema: { prompt: promptRefSchema }
17606
+ }, (args) => run(() => client2.getPrompt(args.prompt), GUIDANCE_POINTER));
17607
+ server2.registerTool("create_prompt", {
17608
+ description: "Write a prompt: reusable prompt text kept ORGANIZATION-WIDE (`PROMPT-{n}`) and available in every project here \u2014 unlike a requirement or a content draft, which belong to the one project they are filed under. `body` is the prompt itself and must not be empty; `title` names it; optional `note` records why it was written. Optional `project` ATTACHES the prompt to a project, which says what it is about and nothing more \u2014 the prompt is still the organization\u2019s, and a prompt about no project in particular simply omits it. There is no approval step: it is usable the moment it is written. Nothing deletes a prompt either \u2014 later wording is added as a revision, so what is here now stays readable. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CSearch before you create\u201D.",
17609
+ inputSchema: createPromptInputSchema.shape
17610
+ }, (args) => run(() => client2.createPrompt(args)));
17611
+ server2.registerTool("update_prompt", {
17612
+ description: "Amend a prompt: `title` and/or `body` APPEND a new revision \u2014 nothing is ever edited in place \u2014 and optional `note` records why. Amending is therefore safe rather than destructive: the earlier text stays readable, the revision chain is the record of how this prompt got to where it is, and the new wording answers immediately because there is nothing to approve. `project` moves the attachment and `project: null` DETACHES it, leaving a prompt the whole organization still has; either way NO revision is written, because the revision chain is the history of the TEXT and moving the attachment changes no text. Supplying none of `title`, `body` or `project` is an empty edit (400) \u2014 a note alone amends nothing.",
17613
+ // Inline shape (updatePromptInputSchema is a refine, so it has no `.shape`),
17614
+ // mirroring that schema's fields — it is the wording this copy follows, `.min(1)`
17615
+ // bounds included, so a caller learns a blank body is refused from the advertised
17616
+ // schema rather than from a round trip. The ONE rule this shape cannot state is the
17617
+ // empty edit, which lives in the schema's `.refine`; the API enforces it.
17618
+ inputSchema: {
17619
+ prompt: promptRefSchema,
17620
+ title: external_exports.string().min(1).optional(),
17621
+ body: markdownSchema.min(1).optional(),
17622
+ note: external_exports.string().min(1).optional(),
17623
+ project: projectRefSchema.nullable().optional()
17624
+ }
17625
+ }, ({ prompt, ...fields }) => run(() => client2.updatePrompt(prompt, fields)));
17370
17626
  server2.registerTool("create_decision", {
17371
17627
  description: "Record ONE decision as a project-scoped decision record (`KEY-D{n}`): `title` names the decision and `body` says what was decided and why. A new record starts `proposed`; moving it to `accepted` IS the acceptance \u2014 there is no separate approval step. Returns the created record. Title and body can be corrected only while the record is `proposed`; from `accepted` onward it is frozen, and the way to change a decision is to write a NEW record and `supersede_decision` the old one \u2014 which leaves the original readable and says where the current decision went. Nothing on any surface deletes one. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CA decision record is written once\u201D and \u201CSearch before you create\u201D.",
17372
17628
  inputSchema: createDecisionInputSchema.shape
@@ -17577,7 +17833,7 @@ function loadMcpConfig() {
17577
17833
  }
17578
17834
 
17579
17835
  // src/version.ts
17580
- var VERSION = "0.35.0";
17836
+ var VERSION = "0.37.0";
17581
17837
 
17582
17838
  // src/main.ts
17583
17839
  var config2 = loadMcpConfig();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipebrain-mcp",
3
- "version": "0.35.0",
3
+ "version": "0.37.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",