@productbrain/mcp 0.0.1-beta.4239 → 0.0.1-beta.4243

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.
@@ -42,7 +42,7 @@ import {
42
42
  trackSessionCaptureRate,
43
43
  trackWriteBackHintServed,
44
44
  trackZeroCaptureAuditFired
45
- } from "./chunk-LSOA2AH2.js";
45
+ } from "./chunk-WTGHEQHB.js";
46
46
 
47
47
  // src/server.ts
48
48
  import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
@@ -790,8 +790,8 @@ function formatFieldGuidance(fields) {
790
790
  if (guidedFields.length === 0) return "";
791
791
  const lines = ["## Writing Guidance"];
792
792
  for (const field of guidedFields) {
793
- const displayName = field.label || field.key;
794
- lines.push(`- ${displayName}: ${field.writingGuidance}`);
793
+ const displayName2 = field.label || field.key;
794
+ lines.push(`- ${displayName2}: ${field.writingGuidance}`);
795
795
  if (field.writingExamples && field.writingExamples.length > 0) {
796
796
  lines.push(` Examples: ${field.writingExamples.join(" | ")}`);
797
797
  }
@@ -2174,7 +2174,7 @@ async function handleCapture(server, { collection, name, description, context, e
2174
2174
  const profile = await getProfile(resolvedCollection);
2175
2175
  const col = await kernelQuery("chain.getCollection", { slug: resolvedCollection });
2176
2176
  if (!col) {
2177
- const displayName = resolvedCollection.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2177
+ const displayName2 = resolvedCollection.split("-").map((w) => w.charAt(0).toUpperCase() + w.slice(1)).join(" ");
2178
2178
  return {
2179
2179
  content: [{
2180
2180
  type: "text",
@@ -2182,7 +2182,7 @@ async function handleCapture(server, { collection, name, description, context, e
2182
2182
 
2183
2183
  **To create it**, run:
2184
2184
  \`\`\`
2185
- collections action=create slug="${resolvedCollection}" name="${displayName}" description="..."
2185
+ collections action=create slug="${resolvedCollection}" name="${displayName2}" description="..."
2186
2186
  \`\`\`
2187
2187
 
2188
2188
  Or use \`collections action=list\` to see available collections.`
@@ -2192,7 +2192,7 @@ Or use \`collections action=list\` to see available collections.`
2192
2192
  `Collection '${resolvedCollection}' not found.`,
2193
2193
  "Create the collection first, or use collections action=list to see available ones.",
2194
2194
  [
2195
- { tool: "collections", description: "Create collection", parameters: { action: "create", slug: resolvedCollection, name: displayName } },
2195
+ { tool: "collections", description: "Create collection", parameters: { action: "create", slug: resolvedCollection, name: displayName2 } },
2196
2196
  { tool: "collections", description: "List collections", parameters: { action: "list" } }
2197
2197
  ]
2198
2198
  )
@@ -15809,6 +15809,243 @@ function registerShapeTools(server) {
15809
15809
  );
15810
15810
  }
15811
15811
 
15812
+ // src/tools/question.ts
15813
+ import { z as z34 } from "zod/v3";
15814
+ var QUESTION_ACTIONS = ["create", "adopt", "assign", "snooze", "decline", "answer", "force-close"];
15815
+ var questionSchema = z34.object({
15816
+ action: z34.enum(QUESTION_ACTIONS).describe(
15817
+ "'create': create a new routed question from text (domains come from `pb question --help` / this tool's own refusal \u2014 never hardcode a slug). 'adopt': attach routing to a tension already captured (e.g. via `pb capture \"TEN: ...\"`) \u2014 the pilot's real birth path; without adopt, captured tensions stay invisible to routing forever. 'assign': confirm ('take it', omit toRole/toPerson) or reassign (supply toRole and/or toPerson) ownership. 'snooze': defer a routed/escalated question (max 2, then it escalates). 'decline': pass on a suggested question, or (as owner/lead) decline an owned one \u2014 reason required. 'answer': close the loop \u2014 creates the answering entry itself unless --entry is given. 'force-close': Team-Lead-only escape hatch \u2014 reason required."
15818
+ ),
15819
+ entryId: z34.string().max(200).optional().describe("Target question's id (human TEN-nnn form or a raw entry id). Required for every action except 'create'."),
15820
+ text: z34.string().max(5e3).optional().describe("'create': the question text. 'answer': the answer text (mutually exclusive with answerEntryId, max 2000 chars)."),
15821
+ domain: z34.string().max(200).optional().describe("'create'/'adopt': the domain slug (see this tool's refusal for the live list)."),
15822
+ blocks: z34.array(z34.string().max(200)).max(50).optional().describe("'create'/'adopt': entry ids this question is blocking (repeatable)."),
15823
+ toRole: z34.string().max(200).optional().describe("'assign': reassign to this role (human ROL-nnn form or a raw entry id)."),
15824
+ toPerson: z34.string().max(200).optional().describe("'assign': reassign to this person (a raw person id)."),
15825
+ reason: z34.string().max(2e3).optional().describe("'decline'/'force-close': required reason."),
15826
+ answerEntryId: z34.string().max(200).optional().describe("'answer': point at an already-written entry instead of supplying text (mutually exclusive with text).")
15827
+ });
15828
+ var createVariant = z34.object({ action: z34.literal("create"), text: z34.string().min(1).max(5e3), domain: z34.string().max(200).optional(), blocks: z34.array(z34.string().max(200)).max(50).optional() });
15829
+ var adoptVariant = z34.object({ action: z34.literal("adopt"), entryId: z34.string().max(200), domain: z34.string().max(200).optional(), blocks: z34.array(z34.string().max(200)).max(50).optional() });
15830
+ var assignVariant = z34.object({ action: z34.literal("assign"), entryId: z34.string().max(200), toRole: z34.string().max(200).optional(), toPerson: z34.string().max(200).optional() });
15831
+ var snoozeVariant = z34.object({ action: z34.literal("snooze"), entryId: z34.string().max(200) });
15832
+ var declineVariant = z34.object({ action: z34.literal("decline"), entryId: z34.string().max(200), reason: z34.string().min(1).max(2e3) });
15833
+ var answerVariant = z34.object({ action: z34.literal("answer"), entryId: z34.string().max(200), text: z34.string().max(2e3).optional(), answerEntryId: z34.string().max(200).optional() });
15834
+ var forceCloseVariant = z34.object({ action: z34.literal("force-close"), entryId: z34.string().max(200), reason: z34.string().min(1).max(2e3) });
15835
+ var questionActionUnion = z34.discriminatedUnion("action", [
15836
+ createVariant,
15837
+ adoptVariant,
15838
+ assignVariant,
15839
+ snoozeVariant,
15840
+ declineVariant,
15841
+ answerVariant,
15842
+ forceCloseVariant
15843
+ ]);
15844
+ var QUESTION_ACTION_SPECS = {
15845
+ create: { params: ["text", "domain", "blocks"], description: "text is required; domain/blocks are optional." },
15846
+ adopt: { params: ["entryId", "domain", "blocks"], description: "entryId is required (the already-captured tension); domain/blocks are optional." },
15847
+ assign: { params: ["entryId", "toRole", "toPerson"], description: "entryId is required; omit toRole/toPerson to self-confirm, or supply one/both to reassign." },
15848
+ snooze: { params: ["entryId"], description: "entryId is required." },
15849
+ decline: { params: ["entryId", "reason"], description: "entryId and reason are both required." },
15850
+ answer: { params: ["entryId", "text", "answerEntryId"], description: "entryId is required; supply exactly one of text or answerEntryId." },
15851
+ "force-close": { params: ["entryId", "reason"], description: "entryId and reason are both required." }
15852
+ };
15853
+ var HUMAN_ID_PATTERN = /^[A-Za-z]+-\d+$/;
15854
+ async function resolveEntryRef(raw, label) {
15855
+ if (!HUMAN_ID_PATTERN.test(raw)) return raw;
15856
+ const entry = await kernelQuery("chain.getEntry", { entryId: raw });
15857
+ if (!entry) throw new Error(`${label} '${raw}' not found.`);
15858
+ return entry._id;
15859
+ }
15860
+ async function displayName(id) {
15861
+ if (!id) return void 0;
15862
+ try {
15863
+ const entry = await kernelQuery("chain.getEntry", { id });
15864
+ return entry?.name;
15865
+ } catch {
15866
+ return void 0;
15867
+ }
15868
+ }
15869
+ function domainListSuffix(domainSlugs) {
15870
+ if (!domainSlugs) return "";
15871
+ return domainSlugs.length > 0 ? ` Domains in this workspace: ${domainSlugs.join(", ")}.` : " No domains exist in this workspace yet.";
15872
+ }
15873
+ async function renderCreateAdoptConfirmation(result2, isAdopt) {
15874
+ const lead = isAdopt ? `${result2.entryId} is now a routed question,` : "Question created and";
15875
+ let base;
15876
+ if (result2.status === "unrouted") {
15877
+ const roleName = await displayName(result2.suggestedRoleId) ?? result2.suggestedRoleId;
15878
+ base = isAdopt ? `${lead} suggested to ${roleName}.` : `${lead} suggested to ${roleName}. Confirm with: pb question assign ${result2.entryId}`;
15879
+ } else {
15880
+ const leadName = await displayName(result2.ownerRoleId) ?? result2.ownerRoleId;
15881
+ base = isAdopt ? `${result2.entryId} is now a routed question. Nothing here says who owns it, so it has gone to ${leadName}.` : `Question created. Nothing here says who owns it, so it has gone to ${leadName}.`;
15882
+ }
15883
+ const blocksHint = " If something is blocked on this, add blocks:[<entry-id>...].";
15884
+ const nudge = result2.nudge ? ` ${result2.nudge}` : "";
15885
+ return `${base}${domainListSuffix(result2.domainSlugs)}${blocksHint}${nudge}`;
15886
+ }
15887
+ async function handleQuestionCreate(text, domain, blocks) {
15888
+ requireWriteAccess();
15889
+ try {
15890
+ const resolvedBlocks = blocks ? await Promise.all(blocks.map((b) => resolveEntryRef(b, "Blocked entry"))) : void 0;
15891
+ const result2 = await kernelMutation("question.create", { text, domainSlug: domain, blocks: resolvedBlocks });
15892
+ const message = await renderCreateAdoptConfirmation(result2, false);
15893
+ const next = result2.status === "unrouted" ? [{ tool: "question", description: "Confirm or reassign the suggestion", parameters: { action: "assign", entryId: result2.entryId } }] : void 0;
15894
+ return successResult(message, message, result2, next);
15895
+ } catch (err) {
15896
+ return questionErrorResult(err, "create", { text, domain, blocks });
15897
+ }
15898
+ }
15899
+ async function handleQuestionAdopt(entryId, domain, blocks) {
15900
+ requireWriteAccess();
15901
+ try {
15902
+ const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
15903
+ const resolvedBlocks = blocks ? await Promise.all(blocks.map((b) => resolveEntryRef(b, "Blocked entry"))) : void 0;
15904
+ const result2 = await kernelMutation("question.adopt", { entryId: resolvedEntryId, domainSlug: domain, blocks: resolvedBlocks });
15905
+ const message = await renderCreateAdoptConfirmation(result2, true);
15906
+ return successResult(message, message, result2);
15907
+ } catch (err) {
15908
+ return questionErrorResult(err, "adopt", { entryId, domain, blocks });
15909
+ }
15910
+ }
15911
+ async function handleQuestionAssign(entryId, toRole, toPerson) {
15912
+ requireWriteAccess();
15913
+ try {
15914
+ const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
15915
+ const resolvedToRoleId = toRole ? await resolveEntryRef(toRole, "Role") : void 0;
15916
+ const result2 = await kernelMutation("question.assign", { entryId: resolvedEntryId, toRoleId: resolvedToRoleId, toPersonId: toPerson });
15917
+ const isReassign = Boolean(toRole || toPerson);
15918
+ let message;
15919
+ if (isReassign) {
15920
+ const who = toPerson ? result2.ownerPersonName ?? result2.ownerPersonId ?? "the target" : await displayName(result2.ownerRoleId) ?? result2.ownerRoleId ?? "the target";
15921
+ message = `${entryId} reassigned to ${who}. They'll see it at their next session.`;
15922
+ } else {
15923
+ message = `${entryId} is yours now.`;
15924
+ }
15925
+ return successResult(message, message, result2);
15926
+ } catch (err) {
15927
+ return questionErrorResult(err, "assign", { entryId, toRole, toPerson });
15928
+ }
15929
+ }
15930
+ async function handleQuestionSnooze(entryId) {
15931
+ requireWriteAccess();
15932
+ try {
15933
+ const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
15934
+ const result2 = await kernelMutation("question.snooze", { entryId: resolvedEntryId });
15935
+ let message;
15936
+ if (result2.snoozeUntil) {
15937
+ message = `Snoozed until ${new Date(result2.snoozeUntil).toISOString().slice(0, 10)}. ${result2.snoozeCount ?? "?"} of 2 snoozes used.`;
15938
+ } else if (result2.status === "escalated") {
15939
+ const leadName = await displayName(result2.ownerRoleId) ?? "the team lead";
15940
+ message = `You have used both snoozes on this question \u2014 it has been escalated to ${leadName}.`;
15941
+ } else {
15942
+ message = "No snoozes left \u2014 answer it or force-close it.";
15943
+ }
15944
+ return successResult(message, message, result2);
15945
+ } catch (err) {
15946
+ return questionErrorResult(err, "snooze", { entryId });
15947
+ }
15948
+ }
15949
+ async function handleQuestionDecline(entryId, reason) {
15950
+ requireWriteAccess();
15951
+ try {
15952
+ const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
15953
+ const result2 = await kernelMutation("question.decline", { entryId: resolvedEntryId, reason });
15954
+ let message;
15955
+ if (result2.status === "processed") {
15956
+ message = "Declined and closed.";
15957
+ } else if (result2.isRoot === true) {
15958
+ message = `Declined \u2014 but ${result2.dependentsCount ?? 0} item(s) still depend on it, so it stays with you. Answer it or force-close it.`;
15959
+ } else if (result2.isRoot === false) {
15960
+ const leadName = await displayName(result2.ownerRoleId) ?? result2.ownerRoleId;
15961
+ message = `Declined and escalated to ${leadName}.`;
15962
+ } else if (result2.suggestedRoleId) {
15963
+ const roleName = await displayName(result2.suggestedRoleId) ?? result2.suggestedRoleId;
15964
+ message = `Passed. ${entryId} is now suggested to ${roleName}.`;
15965
+ } else if (result2.ownerRoleId) {
15966
+ const leadName = await displayName(result2.ownerRoleId) ?? result2.ownerRoleId;
15967
+ message = `Passed. Nobody else here owns this, so it has gone to ${leadName}.`;
15968
+ } else {
15969
+ message = "Declined.";
15970
+ }
15971
+ return successResult(message, message, result2);
15972
+ } catch (err) {
15973
+ return questionErrorResult(err, "decline", { entryId, reason });
15974
+ }
15975
+ }
15976
+ async function handleQuestionAnswer(entryId, text, answerEntryId) {
15977
+ if (text === void 0 && answerEntryId === void 0) {
15978
+ return backpressureResult("question", "answer", QUESTION_ACTION_SPECS.answer, "Give the answer as text, or point at an entry with --entry <id>.");
15979
+ }
15980
+ if (text !== void 0 && answerEntryId !== void 0) {
15981
+ return backpressureResult("question", "answer", QUESTION_ACTION_SPECS.answer, "Use either answer text or --entry, not both.");
15982
+ }
15983
+ requireWriteAccess();
15984
+ try {
15985
+ const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
15986
+ const resolvedAnswerEntryId = answerEntryId ? await resolveEntryRef(answerEntryId, "Answer entry") : void 0;
15987
+ const result2 = await kernelMutation("question.answer", { entryId: resolvedEntryId, text, answerEntryId: resolvedAnswerEntryId });
15988
+ const askerName = result2.askerName ?? "The asker";
15989
+ const pendingNote = result2.answerPending ? " The answer is a draft awaiting accept." : "";
15990
+ const message = result2.idempotent ? `${entryId} is already answered \u2014 no change.` : `Answered. ${askerName} will see the outcome at their next session.${pendingNote}`;
15991
+ return successResult(message, message, result2);
15992
+ } catch (err) {
15993
+ return questionErrorResult(err, "answer", { entryId, text, answerEntryId });
15994
+ }
15995
+ }
15996
+ async function handleQuestionForceClose(entryId, reason) {
15997
+ requireWriteAccess();
15998
+ try {
15999
+ const resolvedEntryId = await resolveEntryRef(entryId, "Entry");
16000
+ const result2 = await kernelMutation("question.forceClose", { entryId: resolvedEntryId, reason });
16001
+ const message = result2.idempotent ? `${entryId} is already closed \u2014 no change.` : "Force-closed. Reason recorded.";
16002
+ return successResult(message, message, result2);
16003
+ } catch (err) {
16004
+ return questionErrorResult(err, "force-close", { entryId, reason });
16005
+ }
16006
+ }
16007
+ function questionErrorResult(err, action, params = {}) {
16008
+ const classified = classifyError(err);
16009
+ const definedParams = Object.fromEntries(Object.entries(params).filter(([, v]) => v !== void 0));
16010
+ return failureResult(
16011
+ classified.message,
16012
+ classified.code,
16013
+ classified.message,
16014
+ classified.recovery ?? "Use action='create' with no --domain, or `pb question --help` / this tool's description, to find valid domains and next steps.",
16015
+ classified.availableActions ?? [{ tool: "question", description: `Retry ${action}`, parameters: { action, ...definedParams } }],
16016
+ classified.diagnostics
16017
+ );
16018
+ }
16019
+ function registerQuestionTools(server) {
16020
+ const questionHandlers = {
16021
+ create: (data) => handleQuestionCreate(data.text, data.domain, data.blocks),
16022
+ adopt: (data) => handleQuestionAdopt(data.entryId, data.domain, data.blocks),
16023
+ assign: (data) => handleQuestionAssign(data.entryId, data.toRole, data.toPerson),
16024
+ snooze: (data) => handleQuestionSnooze(data.entryId),
16025
+ decline: (data) => handleQuestionDecline(data.entryId, data.reason),
16026
+ answer: (data) => handleQuestionAnswer(data.entryId, data.text, data.answerEntryId),
16027
+ "force-close": (data) => handleQuestionForceClose(data.entryId, data.reason)
16028
+ };
16029
+ server.registerTool(
16030
+ "question",
16031
+ {
16032
+ title: "Question",
16033
+ description: "Routed questions \u2014 a question with no owner routes itself to the owning role, the team lead when ambiguous, and up the team-lead chain until someone owns it. Seven write actions:\n\n- **create**: create a new routed question from text. Domains are discovered by using this tool \u2014 an unresolvable `domain` refusal lists the workspace's real slugs; there is no fixed list to memorize.\n- **adopt**: attach routing to a tension already captured (e.g. via `capture` with `collection: tensions`) \u2014 the real birth path for most questions. Without adopt, a captured tension stays invisible to routing forever.\n- **assign**: confirm ('take it', no `toRole`/`toPerson`) or reassign (`toRole`/`toPerson`) ownership.\n- **snooze**: defer a routed/escalated question \u2014 max 2, then it escalates to the team lead.\n- **decline**: pass on a suggested question (never closes it), or \u2014 as the owner/team lead \u2014 decline an owned one (reason required; closes it, or escalates it, depending on dependents).\n- **answer**: close the loop. Creates the answering entry itself from `text`, or points at an existing one via `answerEntryId` \u2014 never both.\n- **force-close**: Team-Lead-only escape hatch (reason required) for a question that cannot otherwise be resolved.\n\n`blocks` (repeatable, on create/adopt) records what a question is holding up \u2014 pass entry ids it blocks.",
16034
+ inputSchema: questionSchema,
16035
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false }
16036
+ },
16037
+ thinWrapper(async (args) => {
16038
+ const parsed = parseOrFail(questionSchema, args);
16039
+ if (!parsed.ok) return parsed.result;
16040
+ const { action } = parsed.data;
16041
+ return runWithToolContext(
16042
+ { tool: "question", action },
16043
+ () => dispatchDiscriminated("question", questionActionUnion, parsed.data, QUESTION_ACTION_SPECS, questionHandlers)
16044
+ );
16045
+ })
16046
+ );
16047
+ }
16048
+
15812
16049
  // src/resources/index.ts
15813
16050
  import { existsSync as existsSync3 } from "fs";
15814
16051
  import { dirname, join, resolve as resolve4 } from "path";
@@ -15818,7 +16055,7 @@ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
15818
16055
  // src/resources/agentCheatsheet.ts
15819
16056
  var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
15820
16057
 
15821
- ## Core Tools (Serves 12 default compound tools; 3 more (chain, chain-review, map) behind PB_MODULES=gitchain)
16058
+ ## Core Tools (Serves 13 default compound tools; 3 more (chain, chain-review, map) behind PB_MODULES=gitchain)
15822
16059
  | Tool | Purpose | Key params |
15823
16060
  |---|---|---|
15824
16061
  | \`orient\` | Workspace context, governance, active bets, session start | \`action\`: start, task, record-activation |
@@ -15833,6 +16070,7 @@ var AGENT_CHEATSHEET = `# Product Brain \u2014 Agent Cheatsheet
15833
16070
  | \`workspace\` | Check / whoami / status / self-test, usage, governance proposals | \`action\`: check, whoami, status, audit, self-test, usage, proposals-* |
15834
16071
  | \`feedback\` | Submit product feedback (any key, no session required); \`list\` reads back your own workspace's feedback (no gate); vendor triage: queue / note / group / status | \`action\`: submit, list, queue, note, group, status |
15835
16072
  | \`shape\` | Review / disposition write-shape (single-concern split) advisories | \`action\`: list, show, agree, dismiss |
16073
+ | \`question\` | Routed questions \u2014 create/adopt, own, snooze, decline, answer, force-close | \`action\`: create, adopt, assign, snooze, decline, answer, force-close |
15836
16074
 
15837
16075
  ## Collection Prefixes
15838
16076
  GLO (glossary), BR (business-rules), PRI (principles), STD (standards),
@@ -16330,12 +16568,12 @@ ${entry.labels.map((l) => `- ${l.name ?? l.slug}`).join("\n")}`);
16330
16568
  }
16331
16569
 
16332
16570
  // src/prompts/index.ts
16333
- import { z as z34 } from "zod/v3";
16571
+ import { z as z35 } from "zod/v3";
16334
16572
  function registerPrompts(server) {
16335
16573
  server.prompt(
16336
16574
  "review-against-rules",
16337
16575
  "Review code or a design decision against all business rules for a given domain. Fetches the rules and asks you to do a structured compliance review.",
16338
- { domain: z34.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
16576
+ { domain: z35.string().describe("Business rule domain (e.g. 'Identity & Access', 'Governance & Decision-Making')") },
16339
16577
  async ({ domain }) => {
16340
16578
  const entries = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
16341
16579
  const rules = entries.filter((e) => e.data?.domain === domain);
@@ -16388,7 +16626,7 @@ Provide a structured review with a compliance status for each rule (COMPLIANT /
16388
16626
  server.prompt(
16389
16627
  "name-check",
16390
16628
  "Check variable names, field names, or API names against the glossary for terminology alignment. Flags drift from canonical terms.",
16391
- { names: z34.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
16629
+ { names: z35.string().describe("Comma-separated list of names to check (e.g. 'vendor_id, compliance_level, formulator_type')") },
16392
16630
  async ({ names }) => {
16393
16631
  const terms = await kernelQuery("chain.listEntries", { collectionSlug: "glossary" });
16394
16632
  const glossaryContext = terms.map(
@@ -16424,7 +16662,7 @@ Format as a table: Name | Status | Canonical Form | Action Needed`
16424
16662
  server.prompt(
16425
16663
  "draft-decision-record",
16426
16664
  "Draft a structured decision record from a description of what was decided. Includes context from recent decisions and relevant rules.",
16427
- { context: z34.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
16665
+ { context: z35.string().describe("Description of the decision (e.g. 'We decided to use MRSL v3.1 as the conformance baseline because...')") },
16428
16666
  async ({ context }) => {
16429
16667
  const recentDecisions = await kernelQuery("chain.listEntries", { collectionSlug: "decisions" });
16430
16668
  const sorted = [...recentDecisions].sort((a, b) => (b.data?.date ?? "") > (a.data?.date ?? "") ? 1 : -1).slice(0, 5);
@@ -16462,8 +16700,8 @@ After drafting, I can log it using the capture tool with collection "decisions".
16462
16700
  "draft-rule-from-context",
16463
16701
  "Draft a new business rule from an observation or discovery made while coding. Fetches existing rules for the domain to ensure consistency.",
16464
16702
  {
16465
- observation: z34.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
16466
- domain: z34.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
16703
+ observation: z35.string().describe("What you observed or discovered (e.g. 'Suppliers can have multiple org types in Gateway')"),
16704
+ domain: z35.string().describe("Which domain this rule belongs to (e.g. 'Governance & Decision-Making')")
16467
16705
  },
16468
16706
  async ({ observation, domain }) => {
16469
16707
  const allRules = await kernelQuery("chain.listEntries", { collectionSlug: "business-rules" });
@@ -16529,7 +16767,7 @@ var INSTRUCTIONS = [
16529
16767
  "- **Self-documenting**: orient and server instructions teach agents how PB works. Cursor rules supplement but don't replace.",
16530
16768
  "",
16531
16769
  "### Tool Rename Notice (WP-484 S3, one release)",
16532
- "Serves 12 default compound tools (WP-559 adds `shape` \u2014 review/disposition for write-shape advisories); 3 more (chain, chain-review, map) behind PB_MODULES=gitchain.",
16770
+ "Serves 13 default compound tools (WP-559 adds `shape` \u2014 review/disposition for write-shape advisories; WP-638 S3 adds `question` \u2014 routed-question routing/ownership/lifecycle); 3 more (chain, chain-review, map) behind PB_MODULES=gitchain.",
16533
16771
  "Old standalone names are gone \u2014 no alias window (ASM-43: zero external consumers). Map: `start_pb`\u2192`orient action=start`, `record_activation`\u2192`orient action=record-activation`, `session-wrapup`\u2192`session action=wrapup-review|wrapup-commit`, `facilitate`\u2192`session action=resume|commit-constellation`, `update-entry`\u2192`entries action=update`, `commit-entry`\u2192`entries action=commit`, `get-history`\u2192`entries action=history`, `move-entry`\u2192`entries action=move`, `verify-entry`\u2192`entries action=verify`, `graph`\u2192`relations action=find|suggest`, `documents`\u2192`context action=last-verified-brief`, `labels`\u2192`collections action=label-*`, `verify`\u2192`quality action=verify-chain`, `audit`\u2192`quality action=audit`, `health`\u2192`workspace action=check|whoami|status|self-test`, `get-usage-summary`\u2192`workspace action=usage`, `governance-proposals`\u2192`workspace action=proposals-*`, `skills`\u2192`workflows action=load-skill`, `chain-version`\u2192`chain action=version.*`, `chain-branch`\u2192`chain-review action=branch.*`, `create-audience-map-set`/`map-slot`/`map-version`/`map-suggest`\u2192`map action=create-audience-set|slot.*|version.*|suggest`.",
16534
16772
  "",
16535
16773
  "## Your Workspace Principles",
@@ -16599,6 +16837,7 @@ function createProductBrainServer() {
16599
16837
  registerWorkspaceTools(server);
16600
16838
  registerFeedbackTool(server);
16601
16839
  registerShapeTools(server);
16840
+ registerQuestionTools(server);
16602
16841
  if (enabledModules.has("gitchain")) registerGitChainTools(server);
16603
16842
  if (enabledModules.has("gitchain")) registerMapTools(server);
16604
16843
  registerResources(server);
@@ -16739,4 +16978,4 @@ export {
16739
16978
  createProductBrainServer,
16740
16979
  initFeatureFlags
16741
16980
  };
16742
- //# sourceMappingURL=chunk-522272VK.js.map
16981
+ //# sourceMappingURL=chunk-3NAU25R5.js.map