pipebrain-mcp 0.48.0 → 0.49.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 +13 -47
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -384,7 +384,7 @@ var AGENT_CONFIG_ENTRIES = [
384
384
  { target: "skills/pipebrain-header-pass/SKILL.md", sha256: "10545e3c1c5215b031e1c76281e65cd2ecf7420b3f07a4221824591a8263c5fc" },
385
385
  { target: "skills/pipebrain-secrets/SKILL.md", sha256: "52c2136a3cca5612981d50b926195eae3e804911e1e9723dd5eba76e66b52d6f" },
386
386
  { target: "skills/pipebrain-work-session/SKILL.md", sha256: "09d5741761b43fc13a12a04117edd8b194aac18974bfedb42dfc76c14030ba87" },
387
- { target: "skills/pipebrain-writing/SKILL.md", sha256: "9ab1a188215716907fbb54e03cfdef43bb378800713b000f498bff4ea75462f0" },
387
+ { target: "skills/pipebrain-writing/SKILL.md", sha256: "8e5ed4636afcca0cacc220d8fa5310364d2d6854a9fc68ffa03e4c428989f8b8" },
388
388
  { target: "skills/pipebrain/SKILL.md", sha256: "e53a1cb13d4e2e396c638d8f99055f4082edef030a40ffc8806b1f52f0824b57" },
389
389
  { target: "skills/requirements-altitude/SKILL.md", sha256: "60d1916da22a52138c046fcf4e5cd4cc420b7a5679a6652eb76573b7a69a1cf4" }
390
390
  ];
@@ -505,7 +505,7 @@ function apiBindingLabel(apiBaseUrl) {
505
505
  }
506
506
 
507
507
  // ../../libs/agent-shared/dist/doctrine.js
508
- var DOCTRINE = "# Pipebrain doctrine\n\nHow an agent works inside Pipebrain. These rules ship with the product and read the same in every organization, because each exists where disagreeing breaks something the product guarantees rather than merely changing house style.\n\nHow your organization *writes* is its own text, edited in Settings, and follows this document under a heading naming each scope.\n\n## Decisions are yours; escalate on consequence\n\nInside an approved requirement you choose the design, the libraries, the structure, and the sequencing. Do not ask, report, or seek ratification for the choice. Escalate on consequence, not uncertainty: a choice that changes what a user sees, costs money, or contradicts an approved requirement is filed as a question with the options you propose; everything else you decide and carry on. A technical choice worth keeping is a decision record, never a paragraph.\n\n## Every loose end has one destination, and none of them is prose\n\nAnything you notice that is not the work in front of you goes to exactly one of three places: a question, when the operator has to choose; a new issue at triage, when it is real work outside this item's scope; or nowhere. Nowhere is the default, and the right outcome whenever knowing the thing would change nothing anyone does. Never open a section called notes, caveats, observations, things to know, or next steps in a body or a comment. Filing is the report: an item recorded and then repeated in prose asks the operator to triage it twice.\n\n## A decision you need is filed as a question, never written into a body\n\nAn open decision belongs in a question on the issue, filed with `ask_questions`, carrying the options you propose. It does not belong in a body \u2014 not under a heading, and not in a field. A decision written into prose prompts nobody and records no answer, so the operator has no place to make it and a later session cannot tell whether it ever was. Retract one your own work has since settled, so nothing asks the operator to decide something nobody waits on.\n\n## Search before you create\n\nLook for the existing record before adding one. A duplicate splits one thing's history across two identifiers, and the split is permanent \u2014 nothing merges them afterward.\n\n## Link by key; never paste another record's text\n\nReference a requirement, a decision, or an issue by its key, with its title on first mention. Never paste its text into a body. The source changes and the copy does not, and the stale copy is what gets built.\n\n## Cross-cutting requirements arrive on their own\n\nThey bind every epic automatically and come back with a work-item read, at their latest approved revision. Never restate them in a body and never add them as explicit requirement links. An explicit link pins a document at an approved revision, which is a different act.\n\n## Conventions arrive in force; yours is a proposal\n\nApproved conventions arrive with the same work-item read, at their latest approved revision, and they bind work in every project in the organization, not this one alone. Neither restate one in a body nor add one as an explicit link: delivery is already automatic, so a copy is a second version waiting to drift.\n\nOne you create or amend over the agent surface records a proposal. It is not in force until a person approves it in a browser, and only an approved revision reaches any session \u2014 so in the interval, do not work from your own unapproved wording, because adopting a rule for the organization is a person's act, not yours.\n\nThe tier test is a statement's lifetime: one that stops being true when this project ends is a cross-cutting requirement, and one that would still be true on the next project is a convention. The moments worth capturing are an evaluator rejecting work, a criterion moving to failed, and a reason recorded on blocked work. File at moments like those and rarely otherwise: every approved convention is delivered on every read, and it spends a person's review attention before it gets there.\n\n## Verification modes follow what can be driven\n\nBefore assigning a mode, establish what this project can drive end to end: a harness, and the command that runs it. A criterion is `auto` when an evaluator can drive the input and read the result against the running system, including behavior shown in an interface wherever such a harness exists. What qualifies it is the evaluator's own run, never the existence of a test the implementer wrote.\n\nMark `manual` only when the verdict needs a person's judgment. A criterion you cannot describe as drivable is usually worded too vaguely: reword it against the observable rather than fall back to a person. A `manual` criterion carries instructions for whoever checks it by hand, written for someone who has not seen the code or the conversation that produced it.\n\nA manual criterion carrying no recorded artifact is the human gate working as designed rather than a coverage gap, so it is neither a finding nor weaker than one carrying evidence.\n\n## The bar for filing a bug\n\nFile a bug only when the observed behavior breaks an approved requirement or a defined acceptance criterion, and name which one before you file. Failing that, it is not a bug: code you find unclear, an edge case nobody specified, a performance number nobody set, and anything you would phrase as \"this could be better\" are opinions about the product, so file each as feedback or not at all. The bar exists because a queue holding both real defects and unshared taste has to be read entirely before any of it can be trusted.\n\n## A decision record is written once\n\nIt states what was decided and why, at the time it was decided, and is not edited as understanding moves. A superseding record replaces it, and the chain of the two is the account of how the thinking changed \u2014 which an edited record destroys. Read the existing records before re-opening a settled question.\n\n## A secret value never travels in a tool call\n\nNo tool here returns one, and that is deliberate rather than an omission to be fixed. Nor should you put one in: a value typed into a tool call is in the transcript before the server can encrypt it, so it is already disclosed and the only remedy left is rotation. Set values with the command-line tool, from a file or on standard input, and inject them at run time rather than reading them into context.\n\n## Setting an instruction replaces the whole body\n\nThere is no partial update. Writing an instruction stores exactly the body you supply and discards everything that was there, so a partial write destroys the rest of the text. The procedure is: read the live value, compose the complete replacement, write it, then read the live value again to confirm what landed.\n\nWhat you are reading now \u2014 the composed guidance \u2014 is a snapshot taken when this session started, and so are the tool descriptions. An edit reaches the next session's guidance, never this one's, so this document re-read after a write still shows the pre-edit text. The stored rows are a different read: `list_org_instructions` returns the current saved values mid-session, which is what makes the confirmation read above possible. An agent that reports its write as absent has re-read the snapshot instead of the record.\n";
508
+ var DOCTRINE = "# Pipebrain doctrine\n\nHow an agent works inside Pipebrain. These rules ship with the product and are the same in every organization. Each one protects something the product guarantees.\n\n## How to write here\n\nThe person reading what you write is the operator, deciding something. They have about two minutes. Put the point first. Use short sentences and ordinary words. Headings and bullets are fine. Explain why once, not under every bullet.\n\nA criterion is one sentence: a concrete situation, and what must be true afterward. If you need a general rule, write two or three criteria instead.\n\nEvidence on a criterion opens with the verdict, then how you drove it, then what it does not cover. Keep it to one screen; name the test or command, not every file you read.\n\nA closing comment says what landed, what did not and why, what you filed by key, and whether the tests are green.\n\nAnything else you notice goes to a question, when the operator has to choose; to a new issue at triage, when it is real work; or, most often, nowhere. Never add a notes, caveats, or next-steps section. An item filed and repeated in prose gets triaged twice.\n\nYour organization may add its own writing rules below this document. Most have none.\n\n## Search before you create\n\nLook for an existing record before you add one. A duplicate splits one record's history across two keys, and nothing merges them later. Update or link the record you find.\n\n## Link by key, never paste\n\nRefer to a requirement, decision, or issue by its key. Never paste its text into a body. When the source changes the copy stays stale, and the stale copy is what gets built.\n\n## Most choices are yours\n\nInside an approved requirement you choose the design, the libraries, and the order of work. Ask only when a choice changes what a user sees, costs money, or contradicts a requirement; file that one as a question. Asking about everything makes real questions wait behind choices that were yours. A technical choice worth keeping goes in a decision record.\n\n## File decisions as questions\n\nAn open decision goes in a question on the issue, filed with `ask_questions` and carrying the options you propose. It does not belong in a body, under a heading or in a field. Prose asks nobody and records no answer, so a later session cannot tell whether it was ever decided. Withdraw a question your own work has settled.\n\n## Cross-cutting requirements arrive on their own\n\nThey bind every epic and come back with a work-item read, at their latest approved revision. Never restate them in a body and never add them as explicit requirement links. A copy drifts, and an explicit link pins one revision where the read delivers the newest.\n\n## Conventions need a person's approval\n\nApproved conventions come back with the same work-item read and bind every project in the organization. Never restate one in a body and never add one as an explicit link; a copy goes stale.\n\nA convention you create or amend over the agent surface is a proposal. It is not in force until a person approves it in a browser. Until then, do not work from your own unapproved wording.\n\nA statement that stops being true when this project ends is a cross-cutting requirement. One still true on the next project is a convention. File one when an evaluator rejects work or a criterion fails, and rarely otherwise: each reaches every read, after a person reviews it.\n\n## Choosing auto or manual\n\nFirst find out what this project can drive end to end, and with which command. A criterion is `auto` when an evaluator can drive the input and read the result on the running system, including an interface where a browser harness exists. Only the evaluator's own run qualifies it; a test the implementer wrote does not. An auto criterion nobody can drive never gets evidence.\n\nMark a criterion `manual` only when the verdict needs a person's judgment. A manual row a machine could have checked holds the issue open until a person gets to it. If you cannot say how to drive a criterion, it is worded too vaguely; reword it against something observable. A manual criterion carries instructions for a stranger to the code.\n\nA manual criterion with no recorded artifact is the human gate working as designed, and not a finding.\n\n## The bar for filing a bug\n\nFile a bug only when the behavior breaks an approved requirement or a defined acceptance criterion, and name which one first. Everything else, including unclear code and edge cases nobody specified, is feedback or nothing. A queue that mixes defects with taste has to be read in full before any of it can be trusted.\n\n## A decision record is written once\n\nA decision record says what was decided and why, at the time. Do not edit it later; write a superseding record instead. The chain of records shows how the thinking changed, and an edit destroys it. Read existing records before reopening a settled question.\n\n## A secret value never travels in a tool call\n\nNo tool returns a secret value, by design, and none should receive one. A value in a tool call is in the transcript before the server can encrypt it; the only remedy is rotation. Set values with the command-line tool, from a file or standard input, and inject at run time.\n\n## Setting an instruction replaces the whole body\n\nThere is no partial update. An instruction write stores exactly the body you supply and discards everything that was there. Read the live value, compose the complete replacement, write it, then read it again to confirm what landed.\n\nThis guidance and the tool descriptions are a snapshot from the session's start; an edit reaches the next session, never this one. `list_org_instructions` returns the saved rows as they are now; that is the confirming read.\n";
509
509
 
510
510
  // ../../libs/agent-shared/dist/org-reference.js
511
511
  var CANONICAL_UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
@@ -15930,22 +15930,6 @@ var orgInstructionSchema = external_exports.object({
15930
15930
  body: external_exports.string().min(1),
15931
15931
  createdBy: uuidSchema.nullable(),
15932
15932
  viaTokenId: uuidSchema.nullable(),
15933
- /**
15934
- * sha256 of the shipped default this row was SEEDED from, or null when nothing seeded
15935
- * it — a row written by a person, a row re-created after its scope was cleared, or a row
15936
- * that predates seeding.
15937
- *
15938
- * It is what lets a settings page say the shipped default has MOVED since this
15939
- * organization got it, without the product ever applying that move: comparing this digest
15940
- * against the running release's default for the same scope is the whole signal. It is not
15941
- * updated by an edit, because what it records is what was seeded — which stays true after
15942
- * the organization rewrites the body, and is exactly then that the comparison is worth
15943
- * making.
15944
- *
15945
- * `.default(null)` rather than a bare nullable, for the tolerance this schema keeps
15946
- * throughout: a payload from a server that predates the column still parses.
15947
- */
15948
- seededDefaultSha: external_exports.string().nullable().default(null),
15949
15933
  createdAt: external_exports.iso.datetime({ offset: true }),
15950
15934
  updatedAt: external_exports.iso.datetime({ offset: true }),
15951
15935
  deletedAt: external_exports.iso.datetime({ offset: true }).nullable()
@@ -16614,12 +16598,6 @@ var listEnvelopeSchema = (item) => external_exports.object({ items: external_exp
16614
16598
  var mintedTokenSchema = apiTokenSchema.extend({ token: external_exports.string().min(1) }).loose();
16615
16599
  var tokenListSchema = listEnvelopeSchema(apiTokenSchema);
16616
16600
  var orgInstructionListSchema = listEnvelopeSchema(orgInstructionSchema);
16617
- var instructionDefaultSchema = external_exports.object({
16618
- scope: external_exports.string().min(1),
16619
- body: external_exports.string().min(1),
16620
- sha256: external_exports.string().min(1)
16621
- }).loose();
16622
- var instructionDefaultListSchema = listEnvelopeSchema(instructionDefaultSchema);
16623
16601
  var ApiClient = class _ApiClient {
16624
16602
  baseUrl;
16625
16603
  auth;
@@ -17363,18 +17341,6 @@ var ApiClient = class _ApiClient {
17363
17341
  const body = await this.request("GET", "/api/v1/org-instructions", {}, orgInstructionListSchema);
17364
17342
  return body.items;
17365
17343
  }
17366
- /**
17367
- * The starting text THIS RELEASE ships, per scope, with the digest a seeded row records.
17368
- *
17369
- * Shipped content rather than the org's, so it carries no tenant data and any
17370
- * authenticated caller may read it. Comparing a row's `seededDefaultSha` against the
17371
- * `sha256` here is how a settings page says the shipped default has MOVED since this
17372
- * organization was seeded — a difference the product reports and never applies.
17373
- */
17374
- async listInstructionDefaults() {
17375
- const body = await this.request("GET", "/api/v1/org-instructions/defaults", {}, instructionDefaultListSchema);
17376
- return body.items;
17377
- }
17378
17344
  /** Upserts one scope's instruction text (ADMIN+; body ≤ 4000 chars). */
17379
17345
  setOrgInstruction(scope, body) {
17380
17346
  return this.request("PUT", `/api/v1/org-instructions/${enc(scope)}`, { body: { body } }, orgInstructionSchema);
@@ -17961,7 +17927,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17961
17927
  inputSchema: openReviewInputSchema
17962
17928
  }, (args) => run(() => client2().openReview(args)));
17963
17929
  registerStrict("update_issue", {
17964
- description: "Update any subset of an issue\u2019s title, body, type, priority, milestone, environment, parent (self/cycle rejected, and moving an OPEN issue onto a parent already `done` or `killed` is rejected with `parent_terminal` \u2014 clearing the parent with null is always allowed), 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. Setting `parent` ATTACHES the issue, and attaching an OPEN issue starts its new parent: a parent of any type moves to `in_progress`, recorded in that parent\u2019s status history against you and the credential you called with (PIPE-R10 rule 10). Nothing moves it back. Three attachments start nothing: an issue that is already `done` or `killed`; re-sending the parent an issue already has; and a parent that cannot legally hold `in_progress` \u2014 one that is blocked, or carries no milestone \u2014 which KEEPS the status it has and still takes the child, so a finding stays fileable against exactly that work. 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.",
17930
+ description: "Update any subset of an issue\u2019s title, body, type, priority, milestone, environment, parent (self/cycle rejected, and moving an OPEN issue onto a parent already `done` or `killed` is rejected with `parent_terminal` \u2014 clearing the parent with null is always allowed), 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. Setting `parent` ATTACHES the issue, and attaching an OPEN issue starts its new parent: a parent of any type moves to `in_progress`, recorded in that parent\u2019s status history against you and the credential you called with (PIPE-R10 rule 10). Nothing moves it back. Three attachments start nothing: an issue that is already `done` or `killed`; re-sending the parent an issue already has; and a parent that cannot legally hold `in_progress` \u2014 one that is blocked, or carries no milestone \u2014 which KEEPS the status it has and still takes the child, so a finding stays fileable against exactly that work. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CLink by key, never paste\u201D and \u201CFile decisions as questions\u201D.",
17965
17931
  inputSchema: updateIssueInputSchema.shape
17966
17932
  }, ({ issue: issue2, ...fields }) => run(() => client2().updateIssue(issue2, fields)));
17967
17933
  registerStrict("set_issue_header", {
@@ -17981,7 +17947,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17981
17947
  inputSchema: { issue: issueRefSchema }
17982
17948
  }, (args) => run(() => client2().releaseIssue(args.issue)));
17983
17949
  registerStrict("add_comment", {
17984
- description: "Add a markdown comment to an issue, attributed to the operator (and token label over MCP). A comment EXPLAINS a blockage \u2014 the full account of what you hit and why you stopped \u2014 but never records one: `set_blocked` (with a reason) and `link_blocker` (when you are waiting on another issue) are what put an issue in the blocked state (\xA77/WORKFLOW). Writing guidance for a comment lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CEvery loose end has one destination, and none of them is prose\u201D.",
17950
+ description: "Add a markdown comment to an issue, attributed to the operator (and token label over MCP). A comment EXPLAINS a blockage \u2014 the full account of what you hit and why you stopped \u2014 but never records one: `set_blocked` (with a reason) and `link_blocker` (when you are waiting on another issue) are what put an issue in the blocked state (\xA77/WORKFLOW). Writing guidance for a comment lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CHow to write here\u201D.",
17985
17951
  inputSchema: commentInputSchema.shape
17986
17952
  }, (args) => run(() => client2().addComment(args.issue, args.body)));
17987
17953
  registerStrict("set_blocked", {
@@ -18001,11 +17967,11 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18001
17967
  inputSchema: linkBlockerInputSchema.shape
18002
17968
  }, (args) => run(() => client2().removeBlocker(args.issue, args.blocker)));
18003
17969
  registerStrict("set_criteria", {
18004
- description: "Define or replace the ordered acceptance criteria list ({text, verification, howTo}) on a piece of work. Matched rows keep their stable AC-{n} number; new items get fresh never-reused numbers; removed items are soft-deleted. A matched row keeps its state only when BOTH its text and verification mode are unchanged \u2014 a mode flip resets it to pending (\xA77). A `manual` criterion must carry `howTo` \u2014 one or two sentences saying where to look, what to do, and what counts as passing; writing or changing one without it is refused (`how_to_required`), and so is supplying it on an `auto` criterion, which a machine checks. Work of any kind carries criteria \u2014 an epic, a task, a bug alike \u2014 with feedback the one exception, refused with `criteria_not_supported` because it is a report rather than agreed work; `promote_feedback` retypes it into work before anything is built. Raising a criterion from `manual` to `auto` both resets its state and clears the stored `howTo`, so a criterion moved to `auto` has lost its checking instructions \u2014 moving it back to `manual` does not restore them, and is refused (`how_to_required`) until you write new ones. Writing guidance for a criterion, and for choosing its mode, lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CVerification modes follow what can be driven\u201D.",
17970
+ description: "Define or replace the ordered acceptance criteria list ({text, verification, howTo}) on a piece of work. Matched rows keep their stable AC-{n} number; new items get fresh never-reused numbers; removed items are soft-deleted. A matched row keeps its state only when BOTH its text and verification mode are unchanged \u2014 a mode flip resets it to pending (\xA77). A `manual` criterion must carry `howTo` \u2014 one or two sentences saying where to look, what to do, and what counts as passing; writing or changing one without it is refused (`how_to_required`), and so is supplying it on an `auto` criterion, which a machine checks. Work of any kind carries criteria \u2014 an epic, a task, a bug alike \u2014 with feedback the one exception, refused with `criteria_not_supported` because it is a report rather than agreed work; `promote_feedback` retypes it into work before anything is built. Raising a criterion from `manual` to `auto` both resets its state and clears the stored `howTo`, so a criterion moved to `auto` has lost its checking instructions \u2014 moving it back to `manual` does not restore them, and is refused (`how_to_required`) until you write new ones. Writing guidance for a criterion, and for choosing its mode, lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CChoosing auto or manual\u201D.",
18005
17971
  inputSchema: setCriteriaInputSchema.shape
18006
17972
  }, (args) => run(() => client2().setCriteria(args.issue, args.criteria)));
18007
17973
  registerStrict("set_criterion_state", {
18008
- description: "Set a criterion\u2019s state (pending/passed/failed) on whatever work carries it \u2014 an epic, a task and a bug alike \u2014 addressed by criterionId ALONE, or by issue + criterionNumber (the stable AC-{n}); exactly one form. Feedback never carries criteria, so neither address resolves to one. A token-authenticated call is REJECTED on a manual criterion (manual state is web-session-only, \xA74); evidence is required when a token marks passed/failed (\xA77). Writing guidance for that evidence lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CVerification modes follow what can be driven\u201D.",
17974
+ description: "Set a criterion\u2019s state (pending/passed/failed) on whatever work carries it \u2014 an epic, a task and a bug alike \u2014 addressed by criterionId ALONE, or by issue + criterionNumber (the stable AC-{n}); exactly one form. Feedback never carries criteria, so neither address resolves to one. A token-authenticated call is REJECTED on a manual criterion (manual state is web-session-only, \xA74); evidence is required when a token marks passed/failed (\xA77). Writing guidance for that evidence lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CChoosing auto or manual\u201D.",
18009
17975
  // `issue` is OPTIONAL: the criterionId-only form needs no issue ref. The
18010
17976
  // exactly-one-of rule can't be expressed in a raw shape, so the handler
18011
17977
  // enforces it via the shared `@pipeline/domain` refine (mirror of §7).
@@ -18036,7 +18002,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18036
18002
  });
18037
18003
  }));
18038
18004
  registerStrict("ask_questions", {
18039
- description: "Ask the operator a BATCH of questions on an issue \u2014 `questions` takes 1 or more {text, options?}, and a session with one open decision usually has two or three, so ask them in ONE call rather than one at a time (a half-asked set costs the operator a second pass). `options` are the choices you propose, in the order you offer them; omit it to ask in open words. Each question gets a stable per-issue `Q-{n}` number that is NEVER reused. Returns the updated issue detail, and every issue read from then on carries `questions` with their answers \u2014 so read the answer in the `get_issue` you were going to make anyway, and READ the existing questions and answers BEFORE asking again rather than re-asking a decision already taken. Answering is reserved for a person in the web UI: there is deliberately no tool for it on this surface (PIPE-R22 rule 4). A question is withdrawn with `withdraw_question`. Writing guidance for a question lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CA decision you need is filed as a question, never written into a body\u201D.",
18005
+ description: "Ask the operator a BATCH of questions on an issue \u2014 `questions` takes 1 or more {text, options?}, and a session with one open decision usually has two or three, so ask them in ONE call rather than one at a time (a half-asked set costs the operator a second pass). `options` are the choices you propose, in the order you offer them; omit it to ask in open words. Each question gets a stable per-issue `Q-{n}` number that is NEVER reused. Returns the updated issue detail, and every issue read from then on carries `questions` with their answers \u2014 so read the answer in the `get_issue` you were going to make anyway, and READ the existing questions and answers BEFORE asking again rather than re-asking a decision already taken. Answering is reserved for a person in the web UI: there is deliberately no tool for it on this surface (PIPE-R22 rule 4). A question is withdrawn with `withdraw_question`. Writing guidance for a question lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CFile decisions as questions\u201D.",
18040
18006
  inputSchema: askQuestionsInputSchema.shape
18041
18007
  }, (args) => run(() => client2().askQuestions(args.issue, args.questions)));
18042
18008
  registerStrict("withdraw_question", {
@@ -18052,11 +18018,11 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18052
18018
  inputSchema: { requirement: requirementRefSchema, revision: positiveIntSchema.optional() }
18053
18019
  }, (args) => run(() => client2().getRequirement(args.requirement, args.revision), GUIDANCE_POINTER));
18054
18020
  registerStrict("create_requirement", {
18055
- description: 'Create a project-scoped requirement document with revision 1 (title, body). Optional summary is a short one/two-line outline description (versioned on the revision, like title/body); optional area is a free-text product-area label ("Backup", "Billing") used to group/filter the outline (metadata, like cross_cutting). Optional cross_cutting flags it as binding every epic in the project automatically \u2014 never link cross-cutting documents (\xA75 rule 6, \xA77). 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.',
18021
+ description: 'Create a project-scoped requirement document with revision 1 (title, body). Optional summary is a short one/two-line outline description (versioned on the revision, like title/body); optional area is a free-text product-area label ("Backup", "Billing") used to group/filter the outline (metadata, like cross_cutting). Optional cross_cutting flags it as binding every epic in the project automatically \u2014 never link cross-cutting documents (\xA75 rule 6, \xA77). 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\u201D.',
18056
18022
  inputSchema: createRequirementInputSchema.shape
18057
18023
  }, (args) => run(() => client2().createRequirement(args)));
18058
18024
  registerStrict("update_requirement", {
18059
- description: "Update a requirement: title, body, and/or summary APPENDS a new revision (nothing is edited in place; summary versions like title/body); optional note records why. Optional area (product-area label), cross_cutting, and decision_links (add/remove decision records this rule sits under \u2014 `KEY-D{n}`, from `list_decisions`) are metadata changes (no revision) \u2014 toggling cross_cutting ON is rejected while explicit links exist; unlink first. Supplying none of title/body/summary/area/cross_cutting/decision_links is an empty edit (400) (\xA77). 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.",
18025
+ description: "Update a requirement: title, body, and/or summary APPENDS a new revision (nothing is edited in place; summary versions like title/body); optional note records why. Optional area (product-area label), cross_cutting, and decision_links (add/remove decision records this rule sits under \u2014 `KEY-D{n}`, from `list_decisions`) are metadata changes (no revision) \u2014 toggling cross_cutting ON is rejected while explicit links exist; unlink first. Supplying none of title/body/summary/area/cross_cutting/decision_links is an empty edit (400) (\xA77). Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CLink by key, never paste\u201D.",
18060
18026
  // Inline shape (updateRequirementInputSchema is a refine, no .shape) — add
18061
18027
  // summary/area here explicitly (§210) and decisionLinks (PIPE-84 §6); the
18062
18028
  // empty-edit refine is enforced by the API, not this shape.
@@ -18084,11 +18050,11 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18084
18050
  inputSchema: { convention: conventionRefSchema }
18085
18051
  }, (args) => run(() => client2().getConvention(args.convention), GUIDANCE_POINTER));
18086
18052
  registerStrict("create_convention", {
18087
- 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.",
18053
+ 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\u201D.",
18088
18054
  inputSchema: createConventionInputSchema.shape
18089
18055
  }, (args) => run(() => client2().createConvention(args)));
18090
18056
  registerStrict("update_convention", {
18091
- 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. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CConventions arrive in force; yours is a proposal\u201D.",
18057
+ 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. Writing guidance for this tool lives in the product doctrine, which `get_work_context` returns \u2014 see \u201CConventions need a person\u2019s approval\u201D.",
18092
18058
  // Inline shape (updateConventionInputSchema is a refine, so it has no `.shape`);
18093
18059
  // the empty-edit rule is enforced by the API, not by this shape.
18094
18060
  inputSchema: {
@@ -18170,7 +18136,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18170
18136
  inputSchema: recordPostInputSchema.shape
18171
18137
  }, ({ deliverable, ...fields }) => run(() => client2().recordPost(deliverable, fields)));
18172
18138
  registerStrict("promote_deliverable", {
18173
- description: "Promote a deliverable (DRAFT-KEY/platform, e.g. DWB-C3/bsky) to a NEW issue: creates the issue (`title` + `type` required; optional `status` defaults to triage, optional `milestone` schedules it, optional `body` seeds it \u2014 typically the current post text) AND a PROTECTED link pinning the deliverable\u2019s current revision. That link CANNOT be unlinked \u2014 the issue exists to ship this deliverable. (To link an EXISTING issue instead, use update_issue.deliverable_links, which stays removable.) Returns the created issue. A `status` other than `triage` or `killed` REQUIRES `milestone` (PIPE-R10 rule 9), exactly as in create_issue: work past triage belongs to a release, and the promotion is refused (`milestone_required`) without one. Seeding the body with the deliverable\u2019s own text is the exception to linking rather than pasting \u2014 it is the point of this tool, and the protected link pins the revision that text came from. 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.",
18139
+ description: "Promote a deliverable (DRAFT-KEY/platform, e.g. DWB-C3/bsky) to a NEW issue: creates the issue (`title` + `type` required; optional `status` defaults to triage, optional `milestone` schedules it, optional `body` seeds it \u2014 typically the current post text) AND a PROTECTED link pinning the deliverable\u2019s current revision. That link CANNOT be unlinked \u2014 the issue exists to ship this deliverable. (To link an EXISTING issue instead, use update_issue.deliverable_links, which stays removable.) Returns the created issue. A `status` other than `triage` or `killed` REQUIRES `milestone` (PIPE-R10 rule 9), exactly as in create_issue: work past triage belongs to a release, and the promotion is refused (`milestone_required`) without one. Seeding the body with the deliverable\u2019s own text is the exception to linking rather than pasting \u2014 it is the point of this tool, and the protected link pins the revision that text came from. 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\u201D.",
18174
18140
  inputSchema: {
18175
18141
  deliverable: external_exports.string().min(1),
18176
18142
  title: external_exports.string().min(1),
@@ -18350,7 +18316,7 @@ function loadMcpConfig() {
18350
18316
  }
18351
18317
 
18352
18318
  // src/version.ts
18353
- var VERSION = "0.48.0";
18319
+ var VERSION = "0.49.0";
18354
18320
 
18355
18321
  // src/main.ts
18356
18322
  async function scopedClient(base, org) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipebrain-mcp",
3
- "version": "0.48.0",
3
+ "version": "0.49.0",
4
4
  "description": "Pipebrain MCP server for Claude Code and Claude Desktop — issues, requirements, decision records, conventions, prompts, content, assets, metadata-only secrets, and more of the platform's modules over the Pipebrain API.",
5
5
  "type": "module",
6
6
  "license": "MIT",