pipebrain-mcp 0.50.0 → 0.52.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 +123 -30
  2. package/package.json +1 -1
package/dist/main.js CHANGED
@@ -383,9 +383,9 @@ var AGENT_CONFIG_ENTRIES = [
383
383
  { target: "skills/capture-correction/SKILL.md", sha256: "907ee8fc8e7ea15c8296acd23a816e1f9c6454edcf37a93f18af79b1c542fb75" },
384
384
  { target: "skills/pipebrain-header-pass/SKILL.md", sha256: "10545e3c1c5215b031e1c76281e65cd2ecf7420b3f07a4221824591a8263c5fc" },
385
385
  { target: "skills/pipebrain-secrets/SKILL.md", sha256: "52c2136a3cca5612981d50b926195eae3e804911e1e9723dd5eba76e66b52d6f" },
386
- { target: "skills/pipebrain-work-session/SKILL.md", sha256: "09d5741761b43fc13a12a04117edd8b194aac18974bfedb42dfc76c14030ba87" },
386
+ { target: "skills/pipebrain-work-session/SKILL.md", sha256: "c1b701d167c62123f6fe38dae35a7dc6c9d0a8f2a9881efabc6b98d0caf14863" },
387
387
  { target: "skills/pipebrain-writing/SKILL.md", sha256: "8e5ed4636afcca0cacc220d8fa5310364d2d6854a9fc68ffa03e4c428989f8b8" },
388
- { target: "skills/pipebrain/SKILL.md", sha256: "e53a1cb13d4e2e396c638d8f99055f4082edef030a40ffc8806b1f52f0824b57" },
388
+ { target: "skills/pipebrain/SKILL.md", sha256: "9fdad1b78bb506e9cd91ba55f4baee0adc5f4d2c5d9d418d0e8c922554e8df74" },
389
389
  { target: "skills/requirements-altitude/SKILL.md", sha256: "60d1916da22a52138c046fcf4e5cd4cc420b7a5679a6652eb76573b7a69a1cf4" }
390
390
  ];
391
391
 
@@ -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 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";
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## A branch and a worktree are derived from the issue\n\nReading an issue's work context returns its branch, its base and its worktree already resolved, each saying where it came from, and how the work leaves the branch. A value the issue, its ancestors and its project all leave unset is derived: the branch and the worktree label are the issue's display id in lower case, which is the project key, a hyphen and the number, and the base is main, which a project changes in its settings. Two sessions reading one issue compute the same names, so a session resuming abandoned work finds what the session before it used. Reading a project returns the work you could pick up and no placement, because no issue has been chosen yet.\n\nClaim the work naming the branch and the worktree you were given. Naming them is what reserves them: a claim is refused when another live claim in the project already holds either. A field you leave out keeps whatever the issue already records, so a claim that names nothing on an issue that records nothing reserves nothing, and a second session is free to take the same tree.\n\nA worktree is a label, not a path. Whoever runs the session maps that label to a directory, because a path recorded here would be true only on the machine that wrote it. Create a working tree when nothing on this machine answers to the label, and reuse the one already there when something does; a branch checked out in one tree cannot be checked out in another. Only the machine can answer that. What the placement tells you is a different thing worth knowing, and it says it for the branch and the worktree separately: whether another issue in this project is already placed on that same value, so you can see whether you are joining work or starting it.\n\nA claim you find under your own session identifier is work to resume rather than work to start. Read what the issue records, look at what the tree holds, and carry on from there. Claiming it again is refused, and starting it again abandons whatever the earlier session left.\n\nA working tree holding uncommitted changes is never removed. Report it and leave it alone. Nothing can tell deliberate work from residue, and removing it cannot be undone.\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. If your own work settles a question, answer it with `answer_question` \u2014 the answer is worth keeping, and withdrawing throws it away. Withdraw only a question that should never have been asked.\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\nThis choice also decides who can finish the work. What gates finishing is the work rather than the caller \u2014 the status tool names what the gate reads, and the server never asks who is calling. A manual row is the one a credential cannot pass, so work carrying one waits for a person; work whose criteria are all machine-checked does not, and the session that has just passed them can set it done.\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. A proposed record is still being written: correct it with `update_decision`, and accept it once it says what you decided. An accepted record is settled, so never edit one; 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;
@@ -15058,6 +15058,7 @@ var issueStatusSchema = external_exports.enum([
15058
15058
  "done",
15059
15059
  "killed"
15060
15060
  ]);
15061
+ var integrationModeSchema = external_exports.enum(["pull_request", "push", "none"]);
15061
15062
  var OPEN_STATUSES = [
15062
15063
  "triage",
15063
15064
  "backlog",
@@ -15184,6 +15185,7 @@ var deliverableTargetLabelSchema = external_exports.string().min(1, { message: "
15184
15185
  var projectNameSchema = external_exports.string().min(1, { message: "project name must not be empty" });
15185
15186
  var tokenLabelSchema = external_exports.string().min(1, { message: "token label must not be empty" });
15186
15187
  var contentMetadataSchema = external_exports.record(external_exports.string(), external_exports.unknown());
15188
+ var sessionIdSchema = external_exports.string().min(1, { message: "session must not be empty \u2014 omit it to read without a session" });
15187
15189
 
15188
15190
  // ../../libs/domain/dist/display-id.js
15189
15191
  var DisplayIdError = class extends Error {
@@ -15442,7 +15444,28 @@ var projectSchema = external_exports.object({
15442
15444
  environments: external_exports.array(projectEnvironmentSchema),
15443
15445
  milestones: external_exports.array(projectMilestoneSchema),
15444
15446
  deliverableTargets: external_exports.array(projectDeliverableTargetSchema),
15445
- openIssueCount: external_exports.number().int().nonnegative()
15447
+ openIssueCount: external_exports.number().int().nonnegative(),
15448
+ /**
15449
+ * What an issue's placement falls back to when neither it nor any ancestor carries a
15450
+ * value (PIPE-444). A person edits these in project settings; agents never write them,
15451
+ * which is what keeps one writer per field (PIPE-D46).
15452
+ *
15453
+ * `.nullable()` and REQUIRED, like `defaultEnvironmentId` above rather than optional
15454
+ * like the issue header fields: the columns exist on every project and the wire mapper
15455
+ * sends all three on every row, so an absent key would mean a server predating them
15456
+ * while `null` means this project sets no default. Optional would make those two
15457
+ * indistinguishable.
15458
+ */
15459
+ defaultBranch: external_exports.string().nullable(),
15460
+ defaultWorktree: external_exports.string().nullable(),
15461
+ defaultBase: external_exports.string().nullable(),
15462
+ /**
15463
+ * How work leaves its branch (PIPE-445). NOT nullable, unlike the three above: those
15464
+ * are nullable because null there means "ask the next level up" and the walk is the
15465
+ * feature. There is no walk here — the value is read from the project and nowhere
15466
+ * else — so a null could only mean "no answer", and work context has to return one.
15467
+ */
15468
+ defaultIntegrationMode: integrationModeSchema
15446
15469
  });
15447
15470
  var environmentSchema = external_exports.object({
15448
15471
  id: uuidSchema,
@@ -16041,7 +16064,20 @@ var updateIssueInputSchema = external_exports.strictObject({
16041
16064
  parent: issueRefSchema.nullable().optional(),
16042
16065
  requirementLinks: requirementLinkChangesSchema.optional(),
16043
16066
  deliverableLinks: deliverableLinkChangesSchema.optional(),
16044
- decisionLinks: decisionLinkChangesSchema.optional()
16067
+ decisionLinks: decisionLinkChangesSchema.optional(),
16068
+ /**
16069
+ * Where the work happens (PIPE-444) — this is the placement write. An agent records
16070
+ * here what a claim did not name, or corrects what it did.
16071
+ *
16072
+ * `.nullable().optional()`, like `milestone` and `parent` above: omitting a field
16073
+ * leaves it, and passing `null` CLEARS the issue's own value so it falls back to the
16074
+ * nearest ancestor carrying one and then to the project default. The two are different
16075
+ * asks and a schema that could not tell them apart would make an issue's own value
16076
+ * impossible to remove.
16077
+ */
16078
+ branch: external_exports.string().min(1, { message: "branch must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("The branch this issue\u2019s work happens on. Pass null to clear it, so the issue inherits from the nearest ancestor carrying one and then from the project default."),
16079
+ worktree: external_exports.string().min(1, { message: "worktree must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("A label for the working tree this issue\u2019s work happens in \u2014 never a path, which is true only on the machine that wrote it. Pass null to clear it."),
16080
+ base: external_exports.string().min(1, { message: "base must not be empty \u2014 pass null to clear it" }).nullable().optional().describe("What this issue\u2019s work integrates into. Pass null to clear it, so the issue inherits from its chain and then from the project default.")
16045
16081
  });
16046
16082
  var ISSUE_SUMMARY_MAX = 350;
16047
16083
  function inPlainWords(schema, field) {
@@ -16079,7 +16115,18 @@ var getIssueInputSchema = external_exports.strictObject({
16079
16115
  });
16080
16116
  var getWorkContextInputSchema = external_exports.strictObject({
16081
16117
  issue: issueRefSchema.optional(),
16082
- project: projectRefSchema.optional()
16118
+ project: projectRefSchema.optional(),
16119
+ /**
16120
+ * The reader's own agent session (PIPE-443). It narrows the project form's
16121
+ * `available` list to work no OTHER session of the same person is holding.
16122
+ *
16123
+ * Accepted on BOTH forms rather than refused alongside `issue`, even though the item
16124
+ * form returns no list for it to narrow. A session that knows its own identifier
16125
+ * passes it on every call it makes; refusing it here would make that one call the
16126
+ * exception and buy nothing, since naming a session cannot change what an item read
16127
+ * returns.
16128
+ */
16129
+ session: sessionIdSchema.optional()
16083
16130
  }).refine((v) => v.issue === void 0 !== (v.project === void 0), {
16084
16131
  message: "pass exactly one of `issue` or `project` \u2014 an issue names its own project, and passing neither names nothing to read"
16085
16132
  });
@@ -16095,7 +16142,12 @@ var openReviewInputSchema = external_exports.strictObject({
16095
16142
  message: "pass exactly one of `epic` or `project` + `milestone` \u2014 an epic names its own project and milestone, and passing neither names nothing to review"
16096
16143
  });
16097
16144
  var claimInputSchema = external_exports.strictObject({
16098
- issue: issueRefSchema
16145
+ issue: issueRefSchema,
16146
+ session: sessionIdSchema.optional(),
16147
+ branch: external_exports.string().min(1, { message: "branch must not be empty \u2014 omit it to keep the branch already recorded" }).optional(),
16148
+ worktree: external_exports.string().min(1, {
16149
+ message: "worktree must not be empty \u2014 omit it to keep the worktree already recorded"
16150
+ }).optional()
16099
16151
  });
16100
16152
  var setBlockedInputSchema = external_exports.strictObject({
16101
16153
  issue: issueRefSchema,
@@ -16273,6 +16325,17 @@ var listIssuesFilterSchema = external_exports.strictObject({
16273
16325
  // the same reason `blocked` is one — a filter the server quietly dropped returns
16274
16326
  // everything and reads exactly like one that matched everything.
16275
16327
  needsHeader: external_exports.boolean().optional(),
16328
+ /**
16329
+ * The reader's own agent session (PIPE-443). Naming one drops every issue held under a
16330
+ * DIFFERENT session identifier, so an operator running several agents against one
16331
+ * repository stops having each of them offered the work the others hold. Omitting it
16332
+ * is today's behaviour exactly: a person in a browser has no session, and their own
16333
+ * claims stay in the list.
16334
+ *
16335
+ * An issue whose claim named no session is never dropped by this, whichever session
16336
+ * asks — the filter narrows to sessions that said who they were.
16337
+ */
16338
+ session: sessionIdSchema.optional(),
16276
16339
  // Pagination — server default 200, max 500 (§9); default applied server-side.
16277
16340
  limit: external_exports.number().int().min(1, { message: "limit must be at least 1" }).max(500, { message: "limit must be at most 500" }).optional(),
16278
16341
  offset: external_exports.number().int().nonnegative().optional()
@@ -16777,8 +16840,16 @@ var ApiClient = class _ApiClient {
16777
16840
  updateIssue(ref, fields) {
16778
16841
  return this.request("PATCH", `/api/v1/issues/${enc(ref)}`, { body: fields });
16779
16842
  }
16780
- claimIssue(ref) {
16781
- return this.request("POST", `/api/v1/issues/${enc(ref)}/claim`);
16843
+ /**
16844
+ * Claims an issue, optionally reserving where the work happens (PIPE-442). A field
16845
+ * `placement` omits keeps whatever the issue already recorded; the call never clears
16846
+ * one. Refused 409 `placement_conflict` when another live claim in the project holds
16847
+ * the branch or the worktree.
16848
+ */
16849
+ claimIssue(ref, placement) {
16850
+ return this.request("POST", `/api/v1/issues/${enc(ref)}/claim`, {
16851
+ body: placement ?? {}
16852
+ });
16782
16853
  }
16783
16854
  releaseIssue(ref) {
16784
16855
  return this.request("POST", `/api/v1/issues/${enc(ref)}/release`);
@@ -16877,8 +16948,9 @@ var ApiClient = class _ApiClient {
16877
16948
  /**
16878
16949
  * Answers a question by its stable `Q-{n}` number with EXACTLY one of `option`
16879
16950
  * (1-based, into the question's options) or `text` (PIPE-93); answering again
16880
- * revises. Session only — a bearer caller is refused, so this is the web app's
16881
- * call, never the MCP's. Returns the updated detail.
16951
+ * revises. Token OR session (PIPE-450): the bearer refusal PIPE-R22 r2 withdrew is
16952
+ * gone, and the answer records the credential behind it instead. Returns the updated
16953
+ * detail.
16882
16954
  */
16883
16955
  answerQuestion(ref, number4, answer) {
16884
16956
  return this.request("POST", `/api/v1/issues/${enc(ref)}/questions/${number4}/answer`, { body: answer });
@@ -17208,9 +17280,12 @@ var ApiClient = class _ApiClient {
17208
17280
  }
17209
17281
  // --- the waiting queue -----------------------------------------------------
17210
17282
  /**
17211
- * Every gate awaiting a person across ALL projects of the caller's organisation
17283
+ * Everything not moving on its own across ALL projects of the caller's organisation
17212
17284
  * (PIPE-87): revisions awaiting approval, questions awaiting an answer, and work
17213
- * in `verify` with a criterion not yet passed, longest wait first.
17285
+ * in `verify` with a criterion not yet passed, longest wait first. Not all of it
17286
+ * waits on a person — a question clears with `answer_question` and an `auto`
17287
+ * criterion with `set_criterion_state`; the one a credential cannot pass is a
17288
+ * criterion marked for human judgment.
17214
17289
  *
17215
17290
  * Takes NO project and NO org PARAMETER: a queue you point at a project is the
17216
17291
  * per-project surface this replaces, and the organization is a property of the CLIENT,
@@ -17241,7 +17316,9 @@ var ApiClient = class _ApiClient {
17241
17316
  * answer.
17242
17317
  */
17243
17318
  getWorkContext(input) {
17244
- return input.issue !== void 0 ? this.request("GET", `/api/v1/issues/${enc(input.issue)}/work-context`) : this.request("GET", `/api/v1/projects/${enc(input.project)}/work-context`);
17319
+ return input.issue !== void 0 ? this.request("GET", `/api/v1/issues/${enc(input.issue)}/work-context`) : this.request("GET", `/api/v1/projects/${enc(input.project)}/work-context`, {
17320
+ query: input.session === void 0 ? {} : { session: input.session }
17321
+ });
17245
17322
  }
17246
17323
  // --- secrets ---------------------------------------------------------------
17247
17324
  // Metadata only: no method here returns a secret VALUE (the reveal plane is a
@@ -17675,7 +17752,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17675
17752
  );
17676
17753
  }
17677
17754
  registerStrict("list_orgs", {
17678
- description: "List the organizations this credential may act in, with each name, slug, id, kind and your role. This is the one tool that needs no `org`: every other call names the organization it acts in and is refused without one, and this is how you find the names you may use. The answer is your memberships right now, which is what that check reads, so a joined organization appears and a left one does not."
17755
+ description: "List the organizations this credential may act in, with each name, slug, id, kind and your role. Use it to find the names you may pass. This is the one tool that needs no `org`: every other call names the organization it acts in, and omitting it is refused unless this session has a default. The answer is your memberships right now, so a joined organization appears and a left one does not."
17679
17756
  }, () => (
17680
17757
  // A BARE ARRAY, unlike `list_waiting`'s `{ items }`, and for the reason that tool
17681
17758
  // gives rather than against it: the envelope has already been unwrapped by
@@ -17715,7 +17792,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17715
17792
  }
17716
17793
  }, (args) => run(() => client2().createProject(args)));
17717
17794
  registerStrict("list_waiting", {
17718
- description: "Every gate awaiting a person across all projects in the organization named by `org`, longest wait first: a requirement revision awaiting approval, a question awaiting an answer, and work at verify still holding a criterion that has not passed. Each entry names its project, the ref to act on, and when the wait began. Read-only, and it clears nothing. A revision clears with `approve_requirement`. Use it to see what is waiting before you ask."
17795
+ description: "Everything not moving on its own across all projects in the organization named by `org`, longest wait first: a requirement revision awaiting approval, an unanswered question, and work at verify holding a criterion that has not passed. Each entry names its project, the ref to act on, and when the wait began. Read-only. A revision clears with `approve_requirement`, a question with `answer_question`, an `auto` criterion with `set_criterion_state`. A criterion marked for human judgment waits for a person."
17719
17796
  }, () => (
17720
17797
  // `{ items }`, mirroring the HTTP envelope rather than unwrapping to a bare array
17721
17798
  // like the sibling list tools. The reason is the envelope itself: this tool is a
@@ -17738,7 +17815,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17738
17815
  // one description loaded into EVERY session by `anthropic/alwaysLoad`. The clauses
17739
17816
  // that went were the ones arguing for the rule rather than stating it; what a caller
17740
17817
  // needs to predict a refusal all stayed.
17741
- description: "Everything a session needs before starting work. Call it first: it alone returns the doctrine and the organization\u2019s writing rules in full. Address it by `issue` or `project`, never both. An issue returns its detail plus the text of each requirement it links, comments capped at `commentLimit` (default " + WORK_CONTEXT_COMMENT_LIMIT + "), with `commentCount` the true total. A project returns `available` \u2014 startable work, excluding done, killed, blocked, person-reserved and others\u2019 claims \u2014 capped at `availableLimit` with `availableCount` as the true total. Read-only.",
17818
+ description: "Everything a session needs before starting work. Call it first: it alone returns the doctrine and your organization\u2019s writing rules in full. Pass `issue` or `project`, never both. An issue returns its detail plus each linked requirement\u2019s text, comments capped at `commentLimit` (default " + WORK_CONTEXT_COMMENT_LIMIT + "), with `commentCount` the true total. A project returns `available` \u2014 startable work, excluding done, killed, blocked, person-reserved, others\u2019 claims and, with `session`, your other sessions\u2019 \u2014 capped at `availableLimit` beside the true `availableCount`. Read-only.",
17742
17819
  inputSchema: getWorkContextInputSchema,
17743
17820
  // THE TWO ANNOTATIONS PIPE-D30 REQUIRES, and the reason each key is spelled out
17744
17821
  // rather than built: an unknown `_meta` key is IGNORED — no error, no warning — so a
@@ -17851,7 +17928,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17851
17928
  registerStrict(
17852
17929
  "list_issues",
17853
17930
  {
17854
- description: "List issue summaries \u2014 status, priority, milestone, claimer, criteria progress and the header \u2014 filtered by project, status, type, claimer, priority, milestone, text, parent, blocked and needsHeader. With no status filter the default is buildable work only: " + OPEN_STATUSES.join(", ") + '. Terminal work is excluded until you name it, so pass status ["done","killed"] for finished and abandoned work. An explicit filter is used verbatim, never merged. needsHeader true returns the issues whose header is missing or stale.',
17931
+ description: "List issue summaries \u2014 status, priority, milestone, claimer, criteria progress and the header \u2014 filtered by project, status, type, claimer, priority, milestone, text, parent, blocked, needsHeader and session. With no status filter the default is buildable work: " + OPEN_STATUSES.join(", ") + '. Terminal work is excluded until you name it: pass status ["done","killed"]. An explicit filter is used verbatim, never merged. needsHeader true returns issues whose header is missing or stale; session drops work another session holds.',
17855
17932
  inputSchema: listIssuesFilterSchema.shape
17856
17933
  },
17857
17934
  // The default lives in the API now, not here (PIPE-D35, Listing issues
@@ -17895,22 +17972,22 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17895
17972
  })
17896
17973
  ), GUIDANCE_POINTER));
17897
17974
  registerStrict("create_issue", {
17898
- description: "Create an epic, task, bug or feedback. Status defaults to triage; anything but triage or killed needs a milestone, else refused `milestone_required`. Optional parent, priority, milestone, links and ordered criteria. An open issue under a done or killed parent is refused `parent_terminal`. Epics, tasks and bugs carry criteria; feedback is refused `criteria_not_supported`. A `manual` criterion needs `howTo`, else refused `how_to_required`. A bug may name the criterion it was filed against. See the doctrine `get_work_context` returns, under \u201CSearch before you create\u201D.",
17975
+ description: "Create an epic, task, bug or feedback. Status defaults to triage; anything but triage or killed needs a milestone, else refused `milestone_required`. An archived milestone is refused `milestone_archived_write`. An open issue under a done or killed parent is refused `parent_terminal`. Epics, tasks and bugs carry criteria; feedback is refused `criteria_not_supported`. A `manual` criterion needs `howTo`, else refused `how_to_required`. A bug may name the criterion it was filed against. See the doctrine `get_work_context` returns, under \u201CSearch before you create\u201D.",
17899
17976
  inputSchema: createIssueInputSchema.shape
17900
17977
  }, (args) => run(() => client2().createIssue(args)));
17901
17978
  registerStrict("promote_feedback", {
17902
- description: "Retype a feedback report as real work \u2014 an epic or a task \u2014 so it can carry criteria and be built. Feedback itself is refused `criteria_not_supported`, which is what this call resolves. Closing the source feedback is refused `status_guard` when anything beneath it, at any depth, is not done or killed, and the refusal names it. A `manual` criterion needs `howTo`, or it is refused `how_to_required`. See the doctrine `get_work_context` returns, under \u201CThe bar for filing a bug\u201D.",
17979
+ description: "Retype a feedback report as real work \u2014 an epic or a task \u2014 so it can carry criteria and be built. Feedback itself is refused `criteria_not_supported`. Scheduling the new work into an archived milestone is refused `milestone_archived_write`. Closing the source feedback is refused `status_guard` when anything beneath it, at any depth, is not done or killed. A `manual` criterion needs `howTo`, else refused `how_to_required`. See the doctrine `get_work_context` returns, under \u201CThe bar for filing a bug\u201D.",
17903
17980
  inputSchema: {
17904
17981
  issue: external_exports.string().min(1),
17905
17982
  ...promoteFeedbackInputSchema.shape
17906
17983
  }
17907
17984
  }, ({ issue: issue2, ...fields }) => run(() => client2().promoteFeedback(issue2, fields)));
17908
17985
  registerStrict("open_review", {
17909
- description: "Open the completion review of an epic\u2019s whole change, by a session that neither built nor evaluated it. It creates one task at ready carrying the checklist. Pass exactly one of `epic`, or `project` plus `milestone`. One review stands per scope; a second is refused `review_already_open`. An epic-form review also holds that milestone scope; a milestone-form review blocks no epic. File findings as children of the epic, not the review. See the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
17986
+ description: "Open an epic\u2019s completion review, read by whoever neither built nor evaluated it: one ready task carrying the checklist. Pass exactly one of `epic`, or `project` plus `milestone`. A first call is refused `review_target_not_epic`, `review_target_terminal` (finished epic), `milestone_required` (no milestone), `milestone_not_found` (unknown token). One review per scope; a second is refused `review_already_open`. Epic-form also holds that milestone scope; milestone-form blocks no epic. File findings under the epic, not the review. See the doctrine `get_work_context` returns, under \u201CHow to write here\u201D.",
17910
17987
  inputSchema: openReviewInputSchema
17911
17988
  }, (args) => run(() => client2().openReview(args)));
17912
17989
  registerStrict("update_issue", {
17913
- description: "Change an issue\u2019s fields, parent and links. Header fields are refused here; criteria are set with `set_criteria`, which epics, tasks and bugs all carry. Moving an open issue onto a done or killed parent is refused `parent_terminal`; one with open work beneath it is refused `parent_terminal_subtree`. A cycle-closing re-parent is refused `parent_cycle`. Attaching open work starts its new parent unless that parent is blocked, unscheduled or under a finished parent. See the doctrine `get_work_context` returns, under \u201CMost choices are yours\u201D.",
17990
+ description: "Change an issue\u2019s fields, parent and links. Header fields are refused; `set_criteria` sets criteria, which epics, tasks and bugs carry. An archived milestone is refused `milestone_archived_write`, unless the issue or its parent carries it. A cycle-closing re-parent is refused `parent_cycle`. A finished parent refuses an open issue, `parent_terminal`; a finished one holding open work, `parent_terminal_subtree`. Attaching open work starts a parent that is not blocked, unscheduled or under finished work. See the doctrine `get_work_context` returns, under \u201CMost choices are yours\u201D.",
17914
17991
  inputSchema: updateIssueInputSchema.shape
17915
17992
  }, ({ issue: issue2, ...fields }) => run(() => client2().updateIssue(issue2, fields)));
17916
17993
  registerStrict("set_issue_header", {
@@ -17918,13 +17995,13 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17918
17995
  inputSchema: setIssueHeaderInputSchema.shape
17919
17996
  }, ({ issue: issue2, ...fields }) => run(() => client2().setIssueHeader(issue2, fields)));
17920
17997
  registerStrict("set_status", {
17921
- description: "Move an issue to a new status. verify needs every auto criterion passed; done needs every criterion passed, and every issue beneath it, at any depth, done or killed \u2014 epic, task or bug alike \u2014 read from status. An epic with no criteria is refused at both gates; a task or bug with none moves through. A blocked issue cannot reach ready or in_progress until its blockers clear. An issue with no milestone can only be triage or killed.",
17998
+ description: "Move an issue to another status. An open status under a done or killed parent is refused `parent_terminal_reopen`. verify needs every auto criterion passed; done needs every criterion passed and everything beneath it, at any depth, done or killed. An epic with no criteria is refused at both gates; a task or bug with none moves through. Blocked work cannot reach ready or in_progress until blockers clear. Work with no milestone can only be triage or killed.",
17922
17999
  inputSchema: setStatusInputSchema.shape
17923
18000
  }, (args) => run(() => client2().setStatus(args.issue, args.status)));
17924
18001
  registerStrict("claim_issue", {
17925
- description: "Claim an issue for this session. It succeeds only if unclaimed; under a race one caller wins and the loser is told who holds it. Claiming is separate from status. It is refused `reserved_for_person` when an operator has reserved the work for a person \u2014 that mark refuses the claim and nothing else, so reading, commenting and moving status all still work. Only a person in the browser can lift it; say in a comment why the work looks agent-ready.",
17926
- inputSchema: { issue: issueRefSchema }
17927
- }, (args) => run(() => client2().claimIssue(args.issue)));
18002
+ description: "Claim an issue for this session and reserve where the work happens. It succeeds only if unclaimed; under a race one caller wins and the loser is told who holds it. `branch` and `worktree` are refused `placement_conflict` when another live claim in the project holds either, naming that issue. A field you omit keeps what the issue already recorded. Claiming is separate from status, and is refused `reserved_for_person` when an operator has kept the work for a person.",
18003
+ inputSchema: claimInputSchema.shape
18004
+ }, ({ issue: issue2, ...placement }) => run(() => client2().claimIssue(issue2, placement)));
17928
18005
  registerStrict("release_issue", {
17929
18006
  description: "Release your claim on an issue, leaving it unclaimed and its status unchanged.",
17930
18007
  inputSchema: { issue: issueRefSchema }
@@ -17977,13 +18054,17 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
17977
18054
  });
17978
18055
  }));
17979
18056
  registerStrict("ask_questions", {
17980
- description: "Ask the operator one or more questions on an issue. Send the batch in one call \u2014 a half-asked set costs a second pass. Each question gets a stable `Q-{n}` number that is never reused, and `options` are the choices you propose. Answers come back on every later read of the issue, so read them before asking again rather than re-asking a settled decision. Withdraw one with `withdraw_question`. See the doctrine `get_work_context` returns, under \u201CFile decisions as questions\u201D.",
18057
+ description: "Ask the operator questions on an issue. Send them in one call \u2014 a half-asked set costs a second pass. Each question gets a stable `Q-{n}` number, never reused; `options` are the choices you propose. Answers come back on every later read; read them before asking again. Answer one yourself with `answer_question` when your work settles it; withdraw one with `withdraw_question` if it should never have been asked. See the doctrine `get_work_context` returns, under \u201CFile decisions as questions\u201D.",
17981
18058
  inputSchema: askQuestionsInputSchema.shape
17982
18059
  }, (args) => run(() => client2().askQuestions(args.issue, args.questions)));
17983
18060
  registerStrict("withdraw_question", {
17984
- description: "Withdraw a question by its `Q-{n}` number, in the organization named by `org`. Only the asker may withdraw, and the asker is the person your credential belongs to, so another agent\u2019s question is not yours to clear. Use it when your own work settled the question or it was wrong to ask, rather than leaving the operator to answer something nobody awaits. The question is kept for the audit trail, its number retires, and later reads no longer carry it.",
18061
+ description: "Withdraw a question by its `Q-{n}` number, in the organization named by `org`. Only the asker may withdraw, and the asker is the person your credential belongs to, so another agent\u2019s question is not yours to clear. Use it for a question that should never have been asked; one your own work settled deserves `answer_question` instead, which keeps the answer. The question is kept for the audit trail, its number retires, and later reads no longer carry it.",
17985
18062
  inputSchema: withdrawQuestionInputSchema.shape
17986
18063
  }, (args) => run(() => client2().withdrawQuestion(args.issue, args.number)));
18064
+ registerStrict("answer_question", {
18065
+ description: "Answer a question by its `Q-{n}` number, in the organization named by `org`. Pass exactly one of `option` (a 1-based index into the choices offered) or `text` \u2014 both, or neither, is refused here before anything is sent. Answering again revises; every answer records who answered and which credential. An out-of-range index is refused `option_out_of_range`, naming how many choices there are; a withdrawn or unknown number, `question_not_found`. Answer what your own work settled; leave the rest to the operator.",
18066
+ inputSchema: answerQuestionInputSchema
18067
+ }, (args) => run(() => client2().answerQuestion(args.issue, args.number, { option: args.option, text: args.text })));
17987
18068
  registerStrict("list_requirements", {
17988
18069
  description: "List requirements filtered by project and text: lifecycle (draft/approved/amended), the latest and approved revision numbers, the cross-cutting flag and the linked-epic count.",
17989
18070
  inputSchema: listRequirementsFilterSchema.shape
@@ -18078,6 +18159,18 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18078
18159
  description: "List decision records filtered by `project` and `text`: each one\u2019s state, its replacement when superseded, and how many requirements and work items cite it. `text` matches title and body.",
18079
18160
  inputSchema: listDecisionsFilterSchema.shape
18080
18161
  }, (args) => run(() => client2().listDecisions(args), GUIDANCE_POINTER));
18162
+ registerStrict("update_decision", {
18163
+ description: "Correct a decision record while it is still `proposed`: `title`, `body`, or both. An edit naming nothing to change is refused here, before anything is sent. Once a record is `accepted` or `superseded` its text is frozen, and the correction is refused `decision_frozen`, which says to write the change as a new record and supersede this one. Use `accept_decision` to accept; asking for `superseded` here is refused, because superseding must name the replacement.",
18164
+ inputSchema: updateDecisionInputSchema
18165
+ }, (args) => run(() => client2().updateDecision(args.decision, {
18166
+ title: args.title,
18167
+ body: args.body,
18168
+ status: args.status
18169
+ })));
18170
+ registerStrict("accept_decision", {
18171
+ description: "Accept a decision record that is still `proposed`: it becomes `accepted`, stamped with who accepted it and when, and its text is frozen from then on \u2014 changing it later means a new record plus `supersede_decision`. The tool names the move, so the record is the only argument. A record that is already `accepted` or `superseded` is refused `decision_transition_not_allowed`, naming what it can still do.",
18172
+ inputSchema: { decision: decisionRefSchema }
18173
+ }, (args) => run(() => client2().updateDecision(args.decision, { status: "accepted" })));
18081
18174
  registerStrict("supersede_decision", {
18082
18175
  description: "Replace an accepted decision record with another: `decision` becomes `superseded` and points at `replacedBy`, where the current decision lives. Refused while the record is still `proposed` (correct it instead), when a record replaces itself, when the replacement is in another project, or when its chain already leads back to this record.",
18083
18176
  inputSchema: supersedeDecisionInputSchema.shape
@@ -18113,7 +18206,7 @@ function createMcpServer(baseClient, apiBaseUrl, orgInstructions2 = /* @__PURE__
18113
18206
  registerStrict("promote_deliverable", {
18114
18207
  // The paste exception is stated because without it an agent holding the doctrine
18115
18208
  // would refuse this tool's whole purpose.
18116
- description: "Create a NEW issue from a deliverable (`DRAFT-KEY/platform`) with a protected link pinning its current revision, which cannot be removed. Needs `title` and `type`; `status` defaults to triage, `body` seeds the issue. A `status` past triage or killed needs `milestone`, or it is refused `milestone_required`. Seeding `body` with the post text is the exception to linking rather than pasting. See the doctrine `get_work_context` returns, under \u201CLink by key, never paste\u201D.",
18209
+ description: "Create a NEW issue from a deliverable (`DRAFT-KEY/platform`) with a protected link pinning its current revision, which cannot be removed. Needs `title` and `type`; `status` defaults to triage, `body` seeds the issue. A `status` past triage or killed needs a `milestone`, else refused `milestone_required`. An archived milestone is refused `milestone_archived_write`. Seeding `body` with the post text is the exception to linking rather than pasting. See the doctrine `get_work_context` returns, under \u201CLink by key, never paste\u201D.",
18117
18210
  inputSchema: {
18118
18211
  deliverable: external_exports.string().min(1),
18119
18212
  title: external_exports.string().min(1),
@@ -18293,7 +18386,7 @@ function loadMcpConfig() {
18293
18386
  }
18294
18387
 
18295
18388
  // src/version.ts
18296
- var VERSION = "0.50.0";
18389
+ var VERSION = "0.52.0";
18297
18390
 
18298
18391
  // src/main.ts
18299
18392
  async function scopedClient(base, org) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pipebrain-mcp",
3
- "version": "0.50.0",
3
+ "version": "0.52.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",