@selfagency/beans-mcp 0.4.2 → 0.6.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.
@@ -14433,7 +14433,7 @@ var init_v4 = __esm({
14433
14433
  }
14434
14434
  });
14435
14435
 
14436
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
14436
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
14437
14437
  function assertCompleteRequestPrompt(request) {
14438
14438
  if (request.params.ref.type !== "ref/prompt") {
14439
14439
  throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`);
@@ -14448,7 +14448,7 @@ function assertCompleteRequestResourceTemplate(request) {
14448
14448
  }
14449
14449
  var LATEST_PROTOCOL_VERSION, SUPPORTED_PROTOCOL_VERSIONS, RELATED_TASK_META_KEY, JSONRPC_VERSION, AssertObjectSchema, ProgressTokenSchema, CursorSchema, TaskCreationParamsSchema, TaskMetadataSchema, RelatedTaskMetadataSchema, RequestMetaSchema, BaseRequestParamsSchema, TaskAugmentedRequestParamsSchema, isTaskAugmentedRequestParams, RequestSchema, NotificationsParamsSchema, NotificationSchema, ResultSchema, RequestIdSchema, JSONRPCRequestSchema, isJSONRPCRequest, JSONRPCNotificationSchema, isJSONRPCNotification, JSONRPCResultResponseSchema, isJSONRPCResultResponse, ErrorCode, JSONRPCErrorResponseSchema, isJSONRPCErrorResponse, JSONRPCMessageSchema, JSONRPCResponseSchema, EmptyResultSchema, CancelledNotificationParamsSchema, CancelledNotificationSchema, IconSchema, IconsSchema, BaseMetadataSchema, ImplementationSchema, FormElicitationCapabilitySchema, ElicitationCapabilitySchema, ClientTasksCapabilitySchema, ServerTasksCapabilitySchema, ClientCapabilitiesSchema, InitializeRequestParamsSchema, InitializeRequestSchema, ServerCapabilitiesSchema, InitializeResultSchema, InitializedNotificationSchema, PingRequestSchema, ProgressSchema, ProgressNotificationParamsSchema, ProgressNotificationSchema, PaginatedRequestParamsSchema, PaginatedRequestSchema, PaginatedResultSchema, TaskStatusSchema, TaskSchema, CreateTaskResultSchema, TaskStatusNotificationParamsSchema, TaskStatusNotificationSchema, GetTaskRequestSchema, GetTaskResultSchema, GetTaskPayloadRequestSchema, GetTaskPayloadResultSchema, ListTasksRequestSchema, ListTasksResultSchema, CancelTaskRequestSchema, CancelTaskResultSchema, ResourceContentsSchema, TextResourceContentsSchema, Base64Schema, BlobResourceContentsSchema, RoleSchema, AnnotationsSchema, ResourceSchema, ResourceTemplateSchema, ListResourcesRequestSchema, ListResourcesResultSchema, ListResourceTemplatesRequestSchema, ListResourceTemplatesResultSchema, ResourceRequestParamsSchema, ReadResourceRequestParamsSchema, ReadResourceRequestSchema, ReadResourceResultSchema, ResourceListChangedNotificationSchema, SubscribeRequestParamsSchema, SubscribeRequestSchema, UnsubscribeRequestParamsSchema, UnsubscribeRequestSchema, ResourceUpdatedNotificationParamsSchema, ResourceUpdatedNotificationSchema, PromptArgumentSchema, PromptSchema, ListPromptsRequestSchema, ListPromptsResultSchema, GetPromptRequestParamsSchema, GetPromptRequestSchema, TextContentSchema, ImageContentSchema, AudioContentSchema, ToolUseContentSchema, EmbeddedResourceSchema, ResourceLinkSchema, ContentBlockSchema, PromptMessageSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ToolAnnotationsSchema, ToolExecutionSchema, ToolSchema, ListToolsRequestSchema, ListToolsResultSchema, CallToolResultSchema, CompatibilityCallToolResultSchema, CallToolRequestParamsSchema, CallToolRequestSchema, ToolListChangedNotificationSchema, ListChangedOptionsBaseSchema, LoggingLevelSchema, SetLevelRequestParamsSchema, SetLevelRequestSchema, LoggingMessageNotificationParamsSchema, LoggingMessageNotificationSchema, ModelHintSchema, ModelPreferencesSchema, ToolChoiceSchema, ToolResultContentSchema, SamplingContentSchema, SamplingMessageContentBlockSchema, SamplingMessageSchema, CreateMessageRequestParamsSchema, CreateMessageRequestSchema, CreateMessageResultSchema, CreateMessageResultWithToolsSchema, BooleanSchemaSchema, StringSchemaSchema, NumberSchemaSchema, UntitledSingleSelectEnumSchemaSchema, TitledSingleSelectEnumSchemaSchema, LegacyTitledEnumSchemaSchema, SingleSelectEnumSchemaSchema, UntitledMultiSelectEnumSchemaSchema, TitledMultiSelectEnumSchemaSchema, MultiSelectEnumSchemaSchema, EnumSchemaSchema, PrimitiveSchemaDefinitionSchema, ElicitRequestFormParamsSchema, ElicitRequestURLParamsSchema, ElicitRequestParamsSchema, ElicitRequestSchema, ElicitationCompleteNotificationParamsSchema, ElicitationCompleteNotificationSchema, ElicitResultSchema, ResourceTemplateReferenceSchema, PromptReferenceSchema, CompleteRequestParamsSchema, CompleteRequestSchema, CompleteResultSchema, RootSchema, ListRootsRequestSchema, ListRootsResultSchema, RootsListChangedNotificationSchema, ClientRequestSchema, ClientNotificationSchema, ClientResultSchema, ServerRequestSchema, ServerNotificationSchema, ServerResultSchema, McpError, UrlElicitationRequiredError;
14450
14450
  var init_types = __esm({
14451
- "node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js"() {
14451
+ "node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js"() {
14452
14452
  "use strict";
14453
14453
  init_v4();
14454
14454
  LATEST_PROTOCOL_VERSION = "2025-11-25";
@@ -14460,10 +14460,9 @@ var init_types = __esm({
14460
14460
  CursorSchema = string2();
14461
14461
  TaskCreationParamsSchema = looseObject({
14462
14462
  /**
14463
- * Time in milliseconds to keep task results available after completion.
14464
- * If null, the task has unlimited lifetime until manually cleaned up.
14463
+ * Requested duration in milliseconds to retain task from creation.
14465
14464
  */
14466
- ttl: union([number2(), _null3()]).optional(),
14465
+ ttl: number2().optional(),
14467
14466
  /**
14468
14467
  * Time in milliseconds to wait between task status requests.
14469
14468
  */
@@ -14762,7 +14761,11 @@ var init_types = __esm({
14762
14761
  /**
14763
14762
  * Present if the client supports task creation.
14764
14763
  */
14765
- tasks: ClientTasksCapabilitySchema.optional()
14764
+ tasks: ClientTasksCapabilitySchema.optional(),
14765
+ /**
14766
+ * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
14767
+ */
14768
+ extensions: record(string2(), AssertObjectSchema).optional()
14766
14769
  });
14767
14770
  InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
14768
14771
  /**
@@ -14823,7 +14826,11 @@ var init_types = __esm({
14823
14826
  /**
14824
14827
  * Present if the server supports task creation.
14825
14828
  */
14826
- tasks: ServerTasksCapabilitySchema.optional()
14829
+ tasks: ServerTasksCapabilitySchema.optional(),
14830
+ /**
14831
+ * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
14832
+ */
14833
+ extensions: record(string2(), AssertObjectSchema).optional()
14827
14834
  });
14828
14835
  InitializeResultSchema = ResultSchema.extend({
14829
14836
  /**
@@ -15015,6 +15022,12 @@ var init_types = __esm({
15015
15022
  * The MIME type of this resource, if known.
15016
15023
  */
15017
15024
  mimeType: optional(string2()),
15025
+ /**
15026
+ * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
15027
+ *
15028
+ * This can be used by Hosts to display file sizes and estimate context window usage.
15029
+ */
15030
+ size: optional(number2()),
15018
15031
  /**
15019
15032
  * Optional annotations for the client.
15020
15033
  */
@@ -22747,7 +22760,7 @@ var init_utils = __esm({
22747
22760
  });
22748
22761
 
22749
22762
  // src/internal/graphql.ts
22750
- var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION_WITH_IF_MATCH, DELETE_BEAN_MUTATION;
22763
+ var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION_WITH_IF_MATCH, DELETE_BEAN_MUTATION, LIST_BEANS_TIMESTAMPS_QUERY;
22751
22764
  var init_graphql = __esm({
22752
22765
  "src/internal/graphql.ts"() {
22753
22766
  "use strict";
@@ -22775,6 +22788,11 @@ var init_graphql = __esm({
22775
22788
  mutation($id: ID!) {
22776
22789
  deleteBean(id: $id)
22777
22790
  }
22791
+ `;
22792
+ LIST_BEANS_TIMESTAMPS_QUERY = `
22793
+ query {
22794
+ beans { id updatedAt }
22795
+ }
22778
22796
  `;
22779
22797
  }
22780
22798
  });
@@ -22797,12 +22815,26 @@ var init_backend = __esm({
22797
22815
  init_graphql();
22798
22816
  init_utils();
22799
22817
  execFileAsync = (0, import_node_util.promisify)(import_node_child_process.execFile);
22800
- BeansCliBackend = class {
22818
+ BeansCliBackend = class _BeansCliBackend {
22801
22819
  constructor(workspaceRoot, cliPath, logDir) {
22802
22820
  this.workspaceRoot = workspaceRoot;
22803
22821
  this.cliPath = cliPath;
22804
22822
  this.logDir = logDir;
22805
22823
  }
22824
+ // ---------------------------------------------------------------------------
22825
+ // Cache
22826
+ // ---------------------------------------------------------------------------
22827
+ /** Full unfiltered records keyed by bean ID, stored under the fixed cache key `'all'`. */
22828
+ _cache = /* @__PURE__ */ new Map();
22829
+ /** Last time the unfiltered cache entry `'all'` was fetched (ms). */
22830
+ _cacheTime = /* @__PURE__ */ new Map();
22831
+ /** Short-circuit TTL: skip even the timestamp check within this window (ms). */
22832
+ static BURST_TTL_MS = 5e3;
22833
+ /** Invalidate the unfiltered list cache so the next call does a full fetch. */
22834
+ invalidateCache() {
22835
+ this._cache.delete("all");
22836
+ this._cacheTime.delete("all");
22837
+ }
22806
22838
  /**
22807
22839
  * Returns a safe environment for executing the Beans CLI,
22808
22840
  * whitelisting only necessary variables.
@@ -22826,7 +22858,7 @@ var init_backend = __esm({
22826
22858
  return (0, import_node_path2.resolve)(this.workspaceRoot, ".beans");
22827
22859
  }
22828
22860
  resolveBeanFilePath(relativePath) {
22829
- const cleaned = relativePath.trim().replace(/^\/+/, "");
22861
+ const cleaned = relativePath.trim().replace(/^\/+/, "").replace(/^\.beans(?:[\\/]|$)/, "");
22830
22862
  if (!cleaned) {
22831
22863
  throw new Error("Path is required");
22832
22864
  }
@@ -22873,6 +22905,26 @@ Output: ${stdout.slice(0, 1e3)}`
22873
22905
  });
22874
22906
  return { initialized: true };
22875
22907
  }
22908
+ async archive() {
22909
+ const { stdout } = await execFileAsync(this.cliPath, ["archive", "--json"], {
22910
+ cwd: this.workspaceRoot,
22911
+ env: this.getSafeEnv(),
22912
+ maxBuffer: 10 * 1024 * 1024,
22913
+ timeout: 3e4
22914
+ });
22915
+ this.invalidateCache();
22916
+ if (!stdout.trim()) {
22917
+ return { archived: true };
22918
+ }
22919
+ try {
22920
+ return JSON.parse(stdout);
22921
+ } catch {
22922
+ return { archived: true, output: stdout.trim() };
22923
+ }
22924
+ }
22925
+ async queryGraphql(query, variables) {
22926
+ return this.executeGraphQL(query, variables);
22927
+ }
22876
22928
  async list(options) {
22877
22929
  const filter = {};
22878
22930
  if (options?.status && options.status.length > 0) {
@@ -22884,10 +22936,48 @@ Output: ${stdout.slice(0, 1e3)}`
22884
22936
  if (options?.search) {
22885
22937
  filter.search = options.search;
22886
22938
  }
22939
+ const isCacheable = !filter.status && !filter.type && !filter.search;
22940
+ const cacheKey = "all";
22941
+ if (isCacheable) {
22942
+ const lastFetch = this._cacheTime.get(cacheKey) ?? 0;
22943
+ const cached2 = this._cache.get(cacheKey);
22944
+ const age = Date.now() - lastFetch;
22945
+ if (cached2 && age < _BeansCliBackend.BURST_TTL_MS) {
22946
+ return Array.from(cached2.values());
22947
+ }
22948
+ if (cached2) {
22949
+ try {
22950
+ const { data: tsData } = await this.executeGraphQL(
22951
+ LIST_BEANS_TIMESTAMPS_QUERY
22952
+ );
22953
+ const timestamps = tsData.beans;
22954
+ let dirty = timestamps.length !== cached2.size;
22955
+ if (!dirty) {
22956
+ for (const { id, updatedAt } of timestamps) {
22957
+ const existing = cached2.get(id);
22958
+ if (!existing || existing.updatedAt !== updatedAt) {
22959
+ dirty = true;
22960
+ break;
22961
+ }
22962
+ }
22963
+ }
22964
+ if (!dirty) {
22965
+ this._cacheTime.set(cacheKey, Date.now());
22966
+ return Array.from(cached2.values());
22967
+ }
22968
+ } catch {
22969
+ }
22970
+ }
22971
+ }
22887
22972
  const { data, errors } = await this.executeGraphQL(LIST_BEANS_QUERY, { filter });
22888
22973
  if (errors && errors.length > 0) {
22889
22974
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22890
22975
  }
22976
+ if (isCacheable) {
22977
+ const byId = new Map(data.beans.map((b) => [b.id, b]));
22978
+ this._cache.set(cacheKey, byId);
22979
+ this._cacheTime.set(cacheKey, Date.now());
22980
+ }
22891
22981
  return data.beans;
22892
22982
  }
22893
22983
  async create(input) {
@@ -22896,7 +22986,7 @@ Output: ${stdout.slice(0, 1e3)}`
22896
22986
  type: input.type,
22897
22987
  status: input.status,
22898
22988
  priority: input.priority,
22899
- body: input.description,
22989
+ body: input.body ?? input.description,
22900
22990
  parent: input.parent
22901
22991
  };
22902
22992
  const { data, errors } = await this.executeGraphQL(CREATE_BEAN_MUTATION, {
@@ -22905,6 +22995,7 @@ Output: ${stdout.slice(0, 1e3)}`
22905
22995
  if (errors && errors.length > 0) {
22906
22996
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22907
22997
  }
22998
+ this.invalidateCache();
22908
22999
  return data.createBean;
22909
23000
  }
22910
23001
  async update(beanId, updates) {
@@ -22974,6 +23065,7 @@ Output: ${stdout.slice(0, 1e3)}`
22974
23065
  if (errors && errors.length > 0) {
22975
23066
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22976
23067
  }
23068
+ this.invalidateCache();
22977
23069
  return data.updateBean;
22978
23070
  }
22979
23071
  async delete(beanId) {
@@ -22983,8 +23075,44 @@ Output: ${stdout.slice(0, 1e3)}`
22983
23075
  if (errors && errors.length > 0) {
22984
23076
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22985
23077
  }
23078
+ this.invalidateCache();
22986
23079
  return { deleted: true, beanId };
22987
23080
  }
23081
+ async bulkCreate(beans, defaultParent) {
23082
+ const settled = await Promise.allSettled(
23083
+ beans.map(
23084
+ async (item) => this.create({
23085
+ ...item,
23086
+ parent: item.parent ?? defaultParent
23087
+ })
23088
+ )
23089
+ );
23090
+ return settled.map(
23091
+ (result) => result.status === "fulfilled" ? { bean: result.value } : { error: result.reason instanceof Error ? result.reason.message : String(result.reason) }
23092
+ );
23093
+ }
23094
+ async bulkUpdate(beans, defaultParent) {
23095
+ const settled = await Promise.allSettled(
23096
+ beans.map(async ({ beanId, ...updates }) => {
23097
+ const resolvedParent = updates.parent ?? (updates.clearParent ? void 0 : defaultParent);
23098
+ const bean = await this.update(beanId, { ...updates, parent: resolvedParent });
23099
+ return { beanId, bean };
23100
+ })
23101
+ );
23102
+ return settled.map((result, index) => {
23103
+ const beanId = beans[index]?.beanId;
23104
+ if (!beanId) {
23105
+ return { beanId: "unknown", error: "Unknown bean id" };
23106
+ }
23107
+ if (result.status === "fulfilled") {
23108
+ return result.value;
23109
+ }
23110
+ return {
23111
+ beanId,
23112
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
23113
+ };
23114
+ });
23115
+ }
22988
23116
  async openConfig() {
22989
23117
  const configPath = (0, import_node_path2.join)(this.workspaceRoot, ".beans.yml");
22990
23118
  const content = await (0, import_promises.readFile)(configPath, "utf8");
@@ -23018,9 +23146,12 @@ Output: ${stdout.slice(0, 1e3)}`
23018
23146
  const outputPath = (0, import_node_path2.resolve)(
23019
23147
  process.env.BEANS_VSCODE_OUTPUT_LOG || (0, import_node_path2.join)(this.workspaceRoot, ".vscode", "logs", "beans-output.log")
23020
23148
  );
23021
- const isWithinWorkspace = isPathWithinRoot(this.workspaceRoot, outputPath);
23149
+ const canonicalOutputPath = await (0, import_promises.realpath)(outputPath).catch(() => outputPath);
23150
+ const canonicalWorkspaceRoot = await (0, import_promises.realpath)(this.workspaceRoot).catch(() => (0, import_node_path2.resolve)(this.workspaceRoot));
23151
+ const isWithinWorkspace = isPathWithinRoot(canonicalWorkspaceRoot, canonicalOutputPath);
23022
23152
  const vscodeLogDir = process.env.BEANS_VSCODE_LOG_DIR || this.logDir ? (0, import_node_path2.resolve)(process.env.BEANS_VSCODE_LOG_DIR || this.logDir || "") : void 0;
23023
- const isWithinVscodeLogDir = vscodeLogDir ? isPathWithinRoot(vscodeLogDir, outputPath) : false;
23153
+ const canonicalVscodeLogDir = vscodeLogDir ? await (0, import_promises.realpath)(vscodeLogDir).catch(() => (0, import_node_path2.resolve)(vscodeLogDir)) : void 0;
23154
+ const isWithinVscodeLogDir = canonicalVscodeLogDir ? isPathWithinRoot(canonicalVscodeLogDir, canonicalOutputPath) : false;
23024
23155
  if (!isWithinWorkspace && !isWithinVscodeLogDir) {
23025
23156
  throw new Error("Output log path must stay within the workspace or VS Code log directory");
23026
23157
  }
@@ -23043,6 +23174,219 @@ Output: ${stdout.slice(0, 1e3)}`
23043
23174
  linesReturned: ringBuffer.length
23044
23175
  };
23045
23176
  }
23177
+ /**
23178
+ * Split a YAML scalar value from any trailing inline comment.
23179
+ * Understands single-quoted and double-quoted YAML strings so it won't
23180
+ * mistake a `#` inside a quoted value for a comment delimiter.
23181
+ */
23182
+ splitYamlInlineComment(value) {
23183
+ let inSingle = false;
23184
+ let inDouble = false;
23185
+ for (let i = 0; i < value.length; i += 1) {
23186
+ const char = value[i];
23187
+ if (inSingle) {
23188
+ if (char === "'") {
23189
+ if (value[i + 1] === "'") {
23190
+ i += 1;
23191
+ } else {
23192
+ inSingle = false;
23193
+ }
23194
+ }
23195
+ continue;
23196
+ }
23197
+ if (inDouble) {
23198
+ if (char === "\\") {
23199
+ i += 1;
23200
+ continue;
23201
+ }
23202
+ if (char === '"') {
23203
+ inDouble = false;
23204
+ }
23205
+ continue;
23206
+ }
23207
+ if (char === "'") {
23208
+ inSingle = true;
23209
+ continue;
23210
+ }
23211
+ if (char === '"') {
23212
+ inDouble = true;
23213
+ continue;
23214
+ }
23215
+ if (char === "#" && i > 0 && /\s/.test(value[i - 1])) {
23216
+ const valuePart = value.slice(0, i).trimEnd();
23217
+ return {
23218
+ valuePart,
23219
+ commentPart: value.slice(valuePart.length)
23220
+ };
23221
+ }
23222
+ }
23223
+ return { valuePart: value, commentPart: "" };
23224
+ }
23225
+ /** Returns true when `value` looks like a YAML block scalar indicator (`>`, `|`, `>-`, `|-`, etc.) */
23226
+ isYamlBlockScalarIndicator(value) {
23227
+ return /^[>|][+-]?[0-9]*$/.test(value) || /^[>|][0-9]*[+-]?$/.test(value);
23228
+ }
23229
+ /** Escape a plain string for use inside a YAML double-quoted scalar. */
23230
+ escapeForYamlDoubleQuoted(value) {
23231
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
23232
+ }
23233
+ shouldQuoteFrontmatterValue(value) {
23234
+ return !/^[A-Za-z0-9._-]+$/.test(value);
23235
+ }
23236
+ parseFrontmatterLine(line) {
23237
+ const separatorIndex = line.indexOf(":");
23238
+ if (separatorIndex <= 0) {
23239
+ return null;
23240
+ }
23241
+ const key = line.slice(0, separatorIndex).trim();
23242
+ if (key.length === 0) {
23243
+ return null;
23244
+ }
23245
+ for (const character of key) {
23246
+ const isAlphaNumericUnderscore = character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character >= "0" && character <= "9" || character === "_";
23247
+ if (!isAlphaNumericUnderscore) {
23248
+ return null;
23249
+ }
23250
+ }
23251
+ const rawValue = line.slice(separatorIndex + 1).trimStart();
23252
+ return { key, rawValue };
23253
+ }
23254
+ buildFrontmatterIndex(frontmatterLines) {
23255
+ const indexByKey = /* @__PURE__ */ new Map();
23256
+ frontmatterLines.forEach((line, index) => {
23257
+ const parsed = this.parseFrontmatterLine(line);
23258
+ if (!parsed) {
23259
+ return;
23260
+ }
23261
+ indexByKey.set(parsed.key, index);
23262
+ });
23263
+ return indexByKey;
23264
+ }
23265
+ serializeFrontmatterValue(key, value) {
23266
+ if (Array.isArray(value)) {
23267
+ return JSON.stringify(value);
23268
+ }
23269
+ if (key === "title") {
23270
+ return this.normalizeFrontmatterTitleValue(value);
23271
+ }
23272
+ if (this.shouldQuoteFrontmatterValue(value)) {
23273
+ return `"${this.escapeForYamlDoubleQuoted(value)}"`;
23274
+ }
23275
+ return value;
23276
+ }
23277
+ deserializeFrontmatterValue(value) {
23278
+ const trimmed = value.trim();
23279
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
23280
+ try {
23281
+ const parsed = JSON.parse(trimmed);
23282
+ if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) {
23283
+ return parsed;
23284
+ }
23285
+ } catch {
23286
+ }
23287
+ }
23288
+ if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
23289
+ return trimmed.slice(1, -1).replaceAll('\\"', '"').replaceAll("\\\\", "\\").replaceAll("''", "'");
23290
+ }
23291
+ return trimmed;
23292
+ }
23293
+ splitFrontmatterDocument(content) {
23294
+ const crlfOpen = content.startsWith("---\r\n");
23295
+ const lfOpen = content.startsWith("---\n");
23296
+ const eol = crlfOpen ? "\r\n" : "\n";
23297
+ if (!crlfOpen && !lfOpen) {
23298
+ return { eol: "\n", hasFrontmatter: false, frontmatterLines: [], body: content };
23299
+ }
23300
+ const openEnd = `---${eol}`.length;
23301
+ const closeMarker = `${eol}---`;
23302
+ const closeIdx = content.indexOf(closeMarker, openEnd);
23303
+ if (closeIdx === -1) {
23304
+ return { eol, hasFrontmatter: false, frontmatterLines: [], body: content };
23305
+ }
23306
+ const frontmatter = content.slice(openEnd, closeIdx);
23307
+ const body = content.slice(closeIdx + closeMarker.length);
23308
+ return {
23309
+ eol,
23310
+ hasFrontmatter: true,
23311
+ frontmatterLines: frontmatter.length > 0 ? frontmatter.split(eol) : [],
23312
+ body
23313
+ };
23314
+ }
23315
+ parseFrontmatterFields(frontmatterLines) {
23316
+ const fields = {};
23317
+ for (const line of frontmatterLines) {
23318
+ const parsed = this.parseFrontmatterLine(line);
23319
+ if (!parsed) {
23320
+ continue;
23321
+ }
23322
+ const { valuePart } = this.splitYamlInlineComment(parsed.rawValue);
23323
+ fields[parsed.key] = this.deserializeFrontmatterValue(valuePart);
23324
+ }
23325
+ return fields;
23326
+ }
23327
+ async writeFileAtomically(absolutePath, content) {
23328
+ const tempPath = `${absolutePath}.tmp-${process.pid}-${Date.now()}`;
23329
+ await (0, import_promises.writeFile)(tempPath, content, "utf8");
23330
+ await (0, import_promises.rename)(tempPath, absolutePath);
23331
+ }
23332
+ /**
23333
+ * Normalise a raw YAML title value to a double-quoted scalar.
23334
+ * Handles: empty, already double-quoted, single-quoted (unescaping `''`),
23335
+ * block-scalar indicators, and plain unquoted values.
23336
+ */
23337
+ normalizeFrontmatterTitleValue(value) {
23338
+ const trimmed = value.trim();
23339
+ if (trimmed === "") {
23340
+ return '""';
23341
+ }
23342
+ if (this.isYamlBlockScalarIndicator(trimmed)) {
23343
+ return value;
23344
+ }
23345
+ if (/^"(?:[^"\\]|\\[\s\S])*"$/.test(trimmed)) {
23346
+ return trimmed;
23347
+ }
23348
+ if (/^'(?:[^']|'')*'$/.test(trimmed)) {
23349
+ const inner = trimmed.slice(1, -1).replace(/''/g, "'");
23350
+ return `"${this.escapeForYamlDoubleQuoted(inner)}"`;
23351
+ }
23352
+ return `"${this.escapeForYamlDoubleQuoted(trimmed)}"`;
23353
+ }
23354
+ /**
23355
+ * Ensure every `title:` line in YAML frontmatter is double-quoted.
23356
+ * Handles already-quoted (single or double), multi-word, and special-char values.
23357
+ * Preserves inline comments and handles both LF and CRLF line endings.
23358
+ */
23359
+ quoteFrontmatterTitles(content) {
23360
+ const crlfOpen = content.startsWith("---\r\n");
23361
+ const lfOpen = content.startsWith("---\n");
23362
+ if (!crlfOpen && !lfOpen) {
23363
+ return content;
23364
+ }
23365
+ const eol = crlfOpen ? "\r\n" : "\n";
23366
+ const openEnd = `---${eol}`.length;
23367
+ const closeMarker = `${eol}---`;
23368
+ const closeIdx = content.indexOf(closeMarker, openEnd);
23369
+ if (closeIdx === -1) {
23370
+ return content;
23371
+ }
23372
+ const frontmatter = content.slice(openEnd, closeIdx);
23373
+ const rest = content.slice(closeIdx);
23374
+ const lines = frontmatter.split(eol);
23375
+ const fixedLines = lines.map((line) => {
23376
+ if (!line.startsWith("title:")) {
23377
+ return line;
23378
+ }
23379
+ const colonIdx = line.indexOf(":");
23380
+ const afterColon = line.slice(colonIdx + 1);
23381
+ const leadingSpace = afterColon.length - afterColon.trimStart().length;
23382
+ const raw = afterColon.trimStart();
23383
+ const { valuePart, commentPart } = this.splitYamlInlineComment(raw);
23384
+ const normalized = this.normalizeFrontmatterTitleValue(valuePart);
23385
+ const prefix = `title:${" ".repeat(Math.max(1, leadingSpace))}`;
23386
+ return `${prefix}${normalized}${commentPart}`;
23387
+ });
23388
+ return `---${eol}${fixedLines.join(eol)}${rest}`;
23389
+ }
23046
23390
  async readBeanFile(relativePath) {
23047
23391
  const absolutePath = this.resolveBeanFilePath(relativePath);
23048
23392
  const content = await (0, import_promises.readFile)(absolutePath, "utf8");
@@ -23050,20 +23394,74 @@ Output: ${stdout.slice(0, 1e3)}`
23050
23394
  }
23051
23395
  async editBeanFile(relativePath, content) {
23052
23396
  const absolutePath = this.resolveBeanFilePath(relativePath);
23397
+ const fixed = this.quoteFrontmatterTitles(content);
23053
23398
  await (0, import_promises.mkdir)((0, import_node_path2.dirname)(absolutePath), { recursive: true });
23054
- await (0, import_promises.writeFile)(absolutePath, content, "utf8");
23055
- return { path: absolutePath, bytes: Buffer.byteLength(content, "utf8") };
23399
+ await (0, import_promises.writeFile)(absolutePath, fixed, "utf8");
23400
+ return { path: absolutePath, bytes: Buffer.byteLength(fixed, "utf8") };
23401
+ }
23402
+ async updateBeanFrontmatter(relativePath, updates) {
23403
+ const absolutePath = this.resolveBeanFilePath(relativePath);
23404
+ const content = await (0, import_promises.readFile)(absolutePath, "utf8");
23405
+ const { eol, hasFrontmatter, frontmatterLines, body } = this.splitFrontmatterDocument(content);
23406
+ const updatedFields = Object.entries(updates).filter(([, value]) => value !== void 0).map(([key]) => key);
23407
+ if (updatedFields.length === 0) {
23408
+ throw new Error("At least one frontmatter field update is required");
23409
+ }
23410
+ const nextLines = [...frontmatterLines];
23411
+ let indexByKey = this.buildFrontmatterIndex(nextLines);
23412
+ for (const [key, value] of Object.entries(updates)) {
23413
+ if (value === void 0) {
23414
+ continue;
23415
+ }
23416
+ const existingIndex = indexByKey.get(key);
23417
+ if (value === null) {
23418
+ if (existingIndex !== void 0) {
23419
+ nextLines.splice(existingIndex, 1);
23420
+ indexByKey = this.buildFrontmatterIndex(nextLines);
23421
+ }
23422
+ continue;
23423
+ }
23424
+ const serialized = `${key}: ${this.serializeFrontmatterValue(key, value)}`;
23425
+ if (existingIndex !== void 0) {
23426
+ const existingLine = nextLines[existingIndex] ?? "";
23427
+ const existingParsed = this.parseFrontmatterLine(existingLine);
23428
+ const commentPart = existingParsed ? this.splitYamlInlineComment(existingParsed.rawValue).commentPart : "";
23429
+ nextLines[existingIndex] = `${serialized}${commentPart}`;
23430
+ } else {
23431
+ nextLines.push(serialized);
23432
+ indexByKey.set(key, nextLines.length - 1);
23433
+ }
23434
+ }
23435
+ const frontmatterBlock = nextLines.length > 0 ? nextLines.join(eol) : "";
23436
+ const nextContent = hasFrontmatter ? `---${eol}${frontmatterBlock}${eol}---${body}` : `---${eol}${frontmatterBlock}${eol}---${eol}${body}`;
23437
+ const fixed = this.quoteFrontmatterTitles(nextContent);
23438
+ await this.writeFileAtomically(absolutePath, fixed);
23439
+ return {
23440
+ path: absolutePath,
23441
+ bytes: Buffer.byteLength(fixed, "utf8"),
23442
+ updatedFields,
23443
+ frontmatter: this.parseFrontmatterFields(this.splitFrontmatterDocument(fixed).frontmatterLines)
23444
+ };
23056
23445
  }
23057
23446
  async createBeanFile(relativePath, content, options) {
23058
23447
  const absolutePath = this.resolveBeanFilePath(relativePath);
23448
+ const fixed = this.quoteFrontmatterTitles(content);
23059
23449
  await (0, import_promises.mkdir)((0, import_node_path2.dirname)(absolutePath), { recursive: true });
23060
- await (0, import_promises.writeFile)(absolutePath, content, {
23061
- encoding: "utf8",
23062
- flag: options?.overwrite ? "w" : "wx"
23063
- });
23450
+ try {
23451
+ await (0, import_promises.writeFile)(absolutePath, fixed, {
23452
+ encoding: "utf8",
23453
+ flag: options?.overwrite ? "w" : "wx"
23454
+ });
23455
+ } catch (error48) {
23456
+ const maybeNodeError = error48;
23457
+ if (maybeNodeError.code === "EEXIST" && !options?.overwrite) {
23458
+ throw new Error("Bean file already exists. Pass overwrite=true to replace it.");
23459
+ }
23460
+ throw error48;
23461
+ }
23064
23462
  return {
23065
23463
  path: absolutePath,
23066
- bytes: Buffer.byteLength(content, "utf8"),
23464
+ bytes: Buffer.byteLength(fixed, "utf8"),
23067
23465
  created: true
23068
23466
  };
23069
23467
  }
@@ -23076,7 +23474,7 @@ Output: ${stdout.slice(0, 1e3)}`
23076
23474
  }
23077
23475
  });
23078
23476
 
23079
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
23477
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
23080
23478
  function deserializeMessage(line) {
23081
23479
  return JSONRPCMessageSchema.parse(JSON.parse(line));
23082
23480
  }
@@ -23085,7 +23483,7 @@ function serializeMessage(message) {
23085
23483
  }
23086
23484
  var ReadBuffer;
23087
23485
  var init_stdio = __esm({
23088
- "node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js"() {
23486
+ "node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js"() {
23089
23487
  "use strict";
23090
23488
  init_types();
23091
23489
  ReadBuffer = class {
@@ -23111,14 +23509,14 @@ var init_stdio = __esm({
23111
23509
  }
23112
23510
  });
23113
23511
 
23114
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
23512
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
23115
23513
  var stdio_exports = {};
23116
23514
  __export(stdio_exports, {
23117
23515
  StdioServerTransport: () => StdioServerTransport
23118
23516
  });
23119
23517
  var import_node_process, StdioServerTransport;
23120
23518
  var init_stdio2 = __esm({
23121
- "node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js"() {
23519
+ "node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js"() {
23122
23520
  "use strict";
23123
23521
  import_node_process = __toESM(require("process"), 1);
23124
23522
  init_stdio();
@@ -27126,7 +27524,7 @@ init_core2();
27126
27524
  // node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/mini/coerce.js
27127
27525
  init_core2();
27128
27526
 
27129
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
27527
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
27130
27528
  function isZ4Schema(s) {
27131
27529
  const schema = s;
27132
27530
  return !!schema._zod;
@@ -27270,10 +27668,10 @@ function getLiteralValue(schema) {
27270
27668
  return void 0;
27271
27669
  }
27272
27670
 
27273
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
27671
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
27274
27672
  init_types();
27275
27673
 
27276
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
27674
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
27277
27675
  function isTerminal(status) {
27278
27676
  return status === "completed" || status === "failed" || status === "cancelled";
27279
27677
  }
@@ -28562,7 +28960,7 @@ var zodToJsonSchema = (schema, options) => {
28562
28960
  return combined;
28563
28961
  };
28564
28962
 
28565
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
28963
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
28566
28964
  function mapMiniTarget(t) {
28567
28965
  if (!t)
28568
28966
  return "draft-7";
@@ -28604,7 +29002,7 @@ function parseWithCompat(schema, data) {
28604
29002
  return result.data;
28605
29003
  }
28606
29004
 
28607
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
29005
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
28608
29006
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
28609
29007
  var Protocol = class {
28610
29008
  constructor(_options) {
@@ -28816,6 +29214,10 @@ var Protocol = class {
28816
29214
  this._progressHandlers.clear();
28817
29215
  this._taskProgressTokens.clear();
28818
29216
  this._pendingDebouncedNotifications.clear();
29217
+ for (const info of this._timeoutInfo.values()) {
29218
+ clearTimeout(info.timeoutId);
29219
+ }
29220
+ this._timeoutInfo.clear();
28819
29221
  for (const controller of this._requestHandlerAbortControllers.values()) {
28820
29222
  controller.abort();
28821
29223
  }
@@ -28946,7 +29348,9 @@ var Protocol = class {
28946
29348
  await capturedTransport?.send(errorResponse);
28947
29349
  }
28948
29350
  }).catch((error48) => this._onerror(new Error(`Failed to send response: ${error48}`))).finally(() => {
28949
- this._requestHandlerAbortControllers.delete(request.id);
29351
+ if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
29352
+ this._requestHandlerAbortControllers.delete(request.id);
29353
+ }
28950
29354
  });
28951
29355
  }
28952
29356
  _onprogress(notification) {
@@ -29552,10 +29956,10 @@ function mergeCapabilities(base, additional) {
29552
29956
  return result;
29553
29957
  }
29554
29958
 
29555
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29959
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29556
29960
  init_types();
29557
29961
 
29558
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
29962
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
29559
29963
  var import_ajv = __toESM(require_ajv(), 1);
29560
29964
  var import_ajv_formats = __toESM(require_dist(), 1);
29561
29965
  function createDefaultAjvInstance() {
@@ -29623,7 +30027,7 @@ var AjvJsonSchemaValidator = class {
29623
30027
  }
29624
30028
  };
29625
30029
 
29626
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
30030
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
29627
30031
  init_types();
29628
30032
  var ExperimentalServerTasks = class {
29629
30033
  constructor(_server) {
@@ -29837,7 +30241,7 @@ var ExperimentalServerTasks = class {
29837
30241
  }
29838
30242
  };
29839
30243
 
29840
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30244
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
29841
30245
  function assertToolsCallTaskCapability(requests, method, entityName) {
29842
30246
  if (!requests) {
29843
30247
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -29872,7 +30276,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
29872
30276
  }
29873
30277
  }
29874
30278
 
29875
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30279
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29876
30280
  var Server = class extends Protocol {
29877
30281
  /**
29878
30282
  * Initializes this server with the given name and version information.
@@ -30252,10 +30656,10 @@ var Server = class extends Protocol {
30252
30656
  }
30253
30657
  };
30254
30658
 
30255
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30659
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30256
30660
  init_types();
30257
30661
 
30258
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
30662
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
30259
30663
  var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
30260
30664
  function isCompletable(schema) {
30261
30665
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -30269,7 +30673,7 @@ var McpZodTypeKind;
30269
30673
  McpZodTypeKind2["Completable"] = "McpCompletable";
30270
30674
  })(McpZodTypeKind || (McpZodTypeKind = {}));
30271
30675
 
30272
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
30676
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
30273
30677
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
30274
30678
  function validateToolName(name) {
30275
30679
  const warnings = [];
@@ -30327,7 +30731,7 @@ function validateAndWarnToolName(name) {
30327
30731
  return result.isValid;
30328
30732
  }
30329
30733
 
30330
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
30734
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
30331
30735
  var ExperimentalMcpServerTasks = class {
30332
30736
  constructor(_mcpServer) {
30333
30737
  this._mcpServer = _mcpServer;
@@ -30346,7 +30750,7 @@ var ExperimentalMcpServerTasks = class {
30346
30750
  init_external();
30347
30751
  init_external();
30348
30752
 
30349
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30753
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30350
30754
  var McpServer = class {
30351
30755
  constructor(serverInfo, options) {
30352
30756
  this._registeredResources = {};
@@ -30970,6 +31374,9 @@ var McpServer = class {
30970
31374
  annotations = rest.shift();
30971
31375
  }
30972
31376
  } else if (typeof firstArg === "object" && firstArg !== null) {
31377
+ if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
31378
+ throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
31379
+ }
30973
31380
  annotations = rest.shift();
30974
31381
  }
30975
31382
  }
@@ -31088,6 +31495,9 @@ function getZodSchemaObject(schema) {
31088
31495
  if (isZodRawShapeCompat(schema)) {
31089
31496
  return objectFromShape(schema);
31090
31497
  }
31498
+ if (!isZodSchemaInstance(schema)) {
31499
+ throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
31500
+ }
31091
31501
  return schema;
31092
31502
  }
31093
31503
  function promptArgumentsFromSchema(schema) {
@@ -31136,6 +31546,100 @@ var EMPTY_COMPLETION_RESULT = {
31136
31546
  var import_node_child_process2 = require("child_process");
31137
31547
  var import_node_util2 = require("util");
31138
31548
 
31549
+ // package.json
31550
+ var package_default = {
31551
+ name: "@selfagency/beans-mcp",
31552
+ version: "0.6.0",
31553
+ private: false,
31554
+ description: "MCP (Model Context Protocol) server for Beans issue tracker",
31555
+ keywords: [
31556
+ "ai",
31557
+ "beans",
31558
+ "issue-tracker",
31559
+ "mcp",
31560
+ "model-context-protocol"
31561
+ ],
31562
+ homepage: "https://github.com/selfagency/beans-mcp",
31563
+ bugs: {
31564
+ url: "https://github.com/selfagency/beans-mcp/issues"
31565
+ },
31566
+ license: "MIT",
31567
+ author: {
31568
+ name: "Daniel Sieradski",
31569
+ email: "daniel@self.agency",
31570
+ url: "https://self.agency"
31571
+ },
31572
+ repository: {
31573
+ type: "git",
31574
+ url: "git+https://github.com/selfagency/beans-mcp.git"
31575
+ },
31576
+ bin: {
31577
+ "beans-mcp": "dist/beans-mcp-server.cjs"
31578
+ },
31579
+ files: [
31580
+ "dist",
31581
+ "skills",
31582
+ "README.md",
31583
+ "LICENSE.txt"
31584
+ ],
31585
+ type: "module",
31586
+ main: "./dist/index.cjs",
31587
+ module: "./dist/index.js",
31588
+ types: "./dist/index.d.ts",
31589
+ exports: {
31590
+ ".": {
31591
+ types: "./dist/index.d.ts",
31592
+ import: "./dist/index.js",
31593
+ require: "./dist/index.cjs"
31594
+ }
31595
+ },
31596
+ scripts: {
31597
+ build: "tsup",
31598
+ "docs:dev": "vitepress dev docs",
31599
+ "docs:build": "vitepress build docs",
31600
+ "docs:preview": "vitepress preview docs",
31601
+ format: "oxfmt",
31602
+ "lint:fix": "oxlint --fix",
31603
+ lint: "oxlint",
31604
+ postbuild: "node ./scripts/write-dist-package.js",
31605
+ prepare: "husky",
31606
+ release: "zx ./scripts/release.js",
31607
+ "test:coverage": "vitest run --coverage",
31608
+ "test:watch": "vitest",
31609
+ test: "vitest run",
31610
+ "type-check": "tsc --noEmit"
31611
+ },
31612
+ devDependencies: {
31613
+ "@modelcontextprotocol/sdk": "^1.29.0",
31614
+ "@octokit/rest": "^22.0.1",
31615
+ "@types/node": "25.6.0",
31616
+ "@vitest/coverage-v8": "^4.1.4",
31617
+ "@vitest/ui": "4.1.4",
31618
+ husky: "^9.1.7",
31619
+ "lint-staged": "^16.4.0",
31620
+ ora: "^9.3.0",
31621
+ oxfmt: "^0.45.0",
31622
+ oxlint: "^1.60.0",
31623
+ "oxlint-tsgolint": "^0.21.1",
31624
+ tsup: "8.5.1",
31625
+ typescript: "6.0.3",
31626
+ vitepress: "^1.6.4",
31627
+ vitest: "4.1.4",
31628
+ zod: "4.3.6",
31629
+ zx: "^8.8.5"
31630
+ },
31631
+ "lint-staged": {
31632
+ "src/**/*.ts": [
31633
+ "pnpm run lint:fix",
31634
+ "pnpm run format"
31635
+ ]
31636
+ },
31637
+ engines: {
31638
+ node: ">=18"
31639
+ },
31640
+ mcpName: "io.github.selfagency/beans-mcp"
31641
+ };
31642
+
31139
31643
  // src/internal/queryHelpers.ts
31140
31644
  function sortBeansInternal(beans, mode) {
31141
31645
  const sorted = [...beans];
@@ -31296,94 +31800,10 @@ var MAX_PATH_LENGTH = 1024;
31296
31800
 
31297
31801
  // src/server/BeansMcpServer.ts
31298
31802
  init_utils();
31299
-
31300
- // package.json
31301
- var package_default = {
31302
- name: "@selfagency/beans-mcp",
31303
- version: "0.4.2",
31304
- private: false,
31305
- description: "MCP (Model Context Protocol) server for Beans issue tracker",
31306
- author: {
31307
- name: "Daniel Sieradski",
31308
- email: "daniel@self.agency",
31309
- url: "https://self.agency"
31310
- },
31311
- homepage: "https://github.com/hmans/beans",
31312
- bugs: {
31313
- url: "https://github.com/selfagency/beans-mcp/issues"
31314
- },
31315
- repository: {
31316
- type: "git",
31317
- url: "git+https://github.com/selfagency/beans-mcp.git"
31318
- },
31319
- mcpName: "io.github.selfagency/beans-mcp",
31320
- keywords: [
31321
- "beans",
31322
- "mcp",
31323
- "model-context-protocol",
31324
- "issue-tracker",
31325
- "ai"
31326
- ],
31327
- license: "MIT",
31328
- type: "module",
31329
- exports: {
31330
- ".": {
31331
- types: "./dist/index.d.ts",
31332
- import: "./dist/index.js",
31333
- require: "./dist/index.cjs"
31334
- }
31335
- },
31336
- main: "./dist/index.cjs",
31337
- module: "./dist/index.js",
31338
- types: "./dist/index.d.ts",
31339
- bin: {
31340
- "beans-mcp": "dist/beans-mcp-server.cjs"
31341
- },
31342
- scripts: {
31343
- build: "tsup",
31344
- format: "oxfmt",
31345
- "lint:fix": "oxlint --fix",
31346
- lint: "oxlint",
31347
- postbuild: "node ./scripts/write-dist-package.js",
31348
- prepare: "husky",
31349
- release: "zx ./scripts/release.js",
31350
- "test:coverage": "vitest run --coverage",
31351
- "test:watch": "vitest",
31352
- test: "vitest run",
31353
- "type-check": "tsc --noEmit"
31354
- },
31355
- devDependencies: {
31356
- "@modelcontextprotocol/sdk": "^1.27.1",
31357
- "@octokit/rest": "^22.0.1",
31358
- "@types/node": "25.5.0",
31359
- "@vitest/coverage-v8": "^4.1.0",
31360
- "@vitest/ui": "4.1.0",
31361
- husky: "^9.1.7",
31362
- "lint-staged": "^16.3.3",
31363
- ora: "^9.3.0",
31364
- oxfmt: "^0.40.0",
31365
- oxlint: "^1.55.0",
31366
- "oxlint-tsgolint": "^0.16.0",
31367
- tsup: "8.5.1",
31368
- typescript: "^5.9.3",
31369
- vitest: "4.1.0",
31370
- zod: "4.3.6",
31371
- zx: "^8.8.5"
31372
- },
31373
- engines: {
31374
- node: ">=18"
31375
- },
31376
- "lint-staged": {
31377
- "src/**/*.ts": [
31378
- "pnpm run lint:fix",
31379
- "pnpm run format"
31380
- ]
31381
- }
31382
- };
31383
-
31384
- // src/server/BeansMcpServer.ts
31385
31803
  var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
31386
31804
  var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
31805
+ var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
31806
+ var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
31387
31807
  function getSafeCliEnv(env) {
31388
31808
  const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
31389
31809
  const safeEnv = {};
@@ -31404,7 +31824,8 @@ function extractVersionFromOutput(output) {
31404
31824
  if (!trimmed) {
31405
31825
  return null;
31406
31826
  }
31407
- const match = trimmed.match(/(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
31827
+ const versionRegex = /(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
31828
+ const match = versionRegex.exec(trimmed);
31408
31829
  return match?.[1] ?? null;
31409
31830
  }
31410
31831
  async function detectBeansCliVersion(cliPath, workspaceRoot) {
@@ -31421,10 +31842,10 @@ ${stderr}`);
31421
31842
  return null;
31422
31843
  }
31423
31844
  }
31424
- async function getBeanById(backend, beanId) {
31845
+ async function getBeanById(backend, beanId, beans) {
31425
31846
  try {
31426
- const beans = await backend.list();
31427
- const found = beans.find((b) => b.id === beanId);
31847
+ const allBeans = beans ?? await backend.list();
31848
+ const found = allBeans.find((b) => b.id === beanId);
31428
31849
  if (!found) {
31429
31850
  throw new Error(`Bean not found: ${beanId}`);
31430
31851
  }
@@ -31433,12 +31854,111 @@ async function getBeanById(backend, beanId) {
31433
31854
  throw new Error(`Failed to fetch bean ${beanId}: ${error48.message}`);
31434
31855
  }
31435
31856
  }
31857
+ function collectDescendantBeans(beans, rootBeanId) {
31858
+ const byParent = /* @__PURE__ */ new Map();
31859
+ const byId = new Map(beans.map((bean) => [bean.id, bean]));
31860
+ for (const bean of beans) {
31861
+ if (!bean.parentId) {
31862
+ continue;
31863
+ }
31864
+ const children = byParent.get(bean.parentId) ?? [];
31865
+ children.push(bean.id);
31866
+ byParent.set(bean.parentId, children);
31867
+ }
31868
+ const queue = [...byParent.get(rootBeanId) ?? []];
31869
+ const visited = /* @__PURE__ */ new Set();
31870
+ const descendants = [];
31871
+ while (queue.length > 0) {
31872
+ const currentId = queue.shift();
31873
+ if (!currentId || visited.has(currentId)) {
31874
+ continue;
31875
+ }
31876
+ visited.add(currentId);
31877
+ const currentBean = byId.get(currentId);
31878
+ if (!currentBean) {
31879
+ continue;
31880
+ }
31881
+ descendants.push(currentBean);
31882
+ const children = byParent.get(currentId);
31883
+ if (children && children.length > 0) {
31884
+ queue.push(...children);
31885
+ }
31886
+ }
31887
+ return descendants;
31888
+ }
31889
+ async function cascadeStatusToDescendants(backend, rootBeanId, targetStatus, options) {
31890
+ const beans = options?.beans ?? await backend.list();
31891
+ const descendants = collectDescendantBeans(beans, rootBeanId);
31892
+ const updatedBeanIds = [];
31893
+ const skippedBeanIds = [];
31894
+ const errors = [];
31895
+ const toUpdate = [];
31896
+ for (const bean of descendants) {
31897
+ if (options?.onlyCurrentStatuses && !options.onlyCurrentStatuses.has(bean.status)) {
31898
+ skippedBeanIds.push(bean.id);
31899
+ continue;
31900
+ }
31901
+ toUpdate.push(bean);
31902
+ }
31903
+ const settled = await Promise.allSettled(
31904
+ toUpdate.map(async (bean) => backend.update(bean.id, { status: targetStatus }))
31905
+ );
31906
+ settled.forEach((result, index) => {
31907
+ const bean = toUpdate[index];
31908
+ if (!bean) {
31909
+ return;
31910
+ }
31911
+ if (result.status === "fulfilled") {
31912
+ updatedBeanIds.push(bean.id);
31913
+ return;
31914
+ }
31915
+ errors.push({
31916
+ beanId: bean.id,
31917
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
31918
+ });
31919
+ });
31920
+ return {
31921
+ totalDescendants: descendants.length,
31922
+ updatedBeanIds,
31923
+ skippedBeanIds,
31924
+ errors
31925
+ };
31926
+ }
31927
+ function completeMarkdownTasks(body) {
31928
+ const lines = body.split(/\r?\n/);
31929
+ let totalTaskCount = 0;
31930
+ let updatedTaskCount = 0;
31931
+ const taskLinePattern = /^\s*(?:[-*+]|\d+\.)\s+\[[ xX]\]/;
31932
+ const uncheckedTaskLinePattern = /^(\s*(?:[-*+]|\d+\.)\s+\[)\s(\].*)$/;
31933
+ const nextLines = lines.map((line) => {
31934
+ if (!taskLinePattern.test(line)) {
31935
+ return line;
31936
+ }
31937
+ totalTaskCount += 1;
31938
+ const uncheckedMatch = uncheckedTaskLinePattern.exec(line);
31939
+ if (!uncheckedMatch) {
31940
+ return line;
31941
+ }
31942
+ updatedTaskCount += 1;
31943
+ return `${uncheckedMatch[1]}x${uncheckedMatch[2]}`;
31944
+ });
31945
+ const nextBody = nextLines.join("\n");
31946
+ return { nextBody, totalTaskCount, updatedTaskCount };
31947
+ }
31436
31948
  function initHandler(backend) {
31437
31949
  return async ({ prefix }) => {
31438
31950
  const result = await backend.init(prefix);
31439
31951
  return makeTextAndStructured(result);
31440
31952
  };
31441
31953
  }
31954
+ function archiveHandler(backend) {
31955
+ return async () => {
31956
+ if (typeof backend.archive !== "function") {
31957
+ throw new TypeError("Archive is not supported by the current backend");
31958
+ }
31959
+ return makeTextAndStructured(await backend.archive());
31960
+ };
31961
+ }
31442
31962
  function viewHandler(backend) {
31443
31963
  return async ({ beanId, beanIds }) => {
31444
31964
  const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
@@ -31453,7 +31973,12 @@ function viewHandler(backend) {
31453
31973
  const byId = new Map(beans.map((b) => [b.id, b]));
31454
31974
  const found = ids.map((id) => byId.get(id)).filter(Boolean);
31455
31975
  const missingBeanIds = ids.filter((id) => !byId.has(id));
31456
- return makeTextAndStructured({ beans: found, missingBeanIds, count: found.length, requestedCount: ids.length });
31976
+ return makeTextAndStructured({
31977
+ beans: found,
31978
+ missingBeanIds,
31979
+ count: found.length,
31980
+ requestedCount: ids.length
31981
+ });
31457
31982
  };
31458
31983
  }
31459
31984
  async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
@@ -31471,7 +31996,14 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
31471
31996
  }
31472
31997
  }
31473
31998
  function createHandler(backend) {
31474
- return async (input) => makeTextAndStructured({ bean: await backend.create(input) });
31999
+ return async (input) => {
32000
+ const bean = await backend.create(input);
32001
+ const warnings = input.description !== void 0 ? ["`description` is deprecated; use `body` instead."] : void 0;
32002
+ return makeTextAndStructured({
32003
+ bean,
32004
+ ...warnings ? { warnings } : {}
32005
+ });
32006
+ };
31475
32007
  }
31476
32008
  function editHandler(backend) {
31477
32009
  return async ({
@@ -31485,18 +32017,30 @@ function reopenHandler(backend) {
31485
32017
  requiredCurrentStatus,
31486
32018
  targetStatus
31487
32019
  }) => {
31488
- const bean = await getBeanById(backend, beanId);
31489
- if (bean.status !== requiredCurrentStatus) {
31490
- throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
32020
+ const beans = await backend.list();
32021
+ const bean = await getBeanById(backend, beanId, beans);
32022
+ if (bean.status === requiredCurrentStatus) {
32023
+ const updatedParentBean = await backend.update(beanId, { status: targetStatus });
32024
+ const cascade = await cascadeStatusToDescendants(backend, beanId, targetStatus, {
32025
+ onlyCurrentStatuses: CLOSED_STATUSES,
32026
+ beans
32027
+ });
32028
+ return makeTextAndStructured({
32029
+ bean: updatedParentBean,
32030
+ cascade: {
32031
+ totalDescendants: cascade.totalDescendants,
32032
+ updatedBeanIds: cascade.updatedBeanIds,
32033
+ skippedBeanIds: cascade.skippedBeanIds,
32034
+ errors: cascade.errors
32035
+ }
32036
+ });
31491
32037
  }
31492
- return makeTextAndStructured({
31493
- bean: await backend.update(beanId, { status: targetStatus })
31494
- });
32038
+ throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
31495
32039
  };
31496
32040
  }
31497
32041
  function updateHandler(backend) {
31498
- return async (input) => makeTextAndStructured({
31499
- bean: await backend.update(input.beanId, {
32042
+ return async (input) => {
32043
+ const updatedBean = await backend.update(input.beanId, {
31500
32044
  status: input.status,
31501
32045
  type: input.type,
31502
32046
  priority: input.priority,
@@ -31508,8 +32052,37 @@ function updateHandler(backend) {
31508
32052
  bodyAppend: input.bodyAppend,
31509
32053
  bodyReplace: input.bodyReplace,
31510
32054
  ifMatch: input.ifMatch
31511
- })
31512
- });
32055
+ });
32056
+ const closeStatus = input.status;
32057
+ const shouldCascadeClose = Boolean(closeStatus && CLOSED_STATUSES.has(closeStatus));
32058
+ const cascade = shouldCascadeClose ? await cascadeStatusToDescendants(backend, input.beanId, closeStatus, {
32059
+ beans: await backend.list()
32060
+ }) : null;
32061
+ return makeTextAndStructured({
32062
+ bean: updatedBean,
32063
+ ...cascade ? {
32064
+ cascade: {
32065
+ totalDescendants: cascade.totalDescendants,
32066
+ updatedBeanIds: cascade.updatedBeanIds,
32067
+ skippedBeanIds: cascade.skippedBeanIds,
32068
+ errors: cascade.errors
32069
+ }
32070
+ } : {}
32071
+ });
32072
+ };
32073
+ }
32074
+ function completeTasksHandler(backend) {
32075
+ return async ({ beanId }) => {
32076
+ const bean = await getBeanById(backend, beanId);
32077
+ const { nextBody, totalTaskCount, updatedTaskCount } = completeMarkdownTasks(bean.body || "");
32078
+ const updatedBean = updatedTaskCount > 0 ? await backend.update(beanId, { body: nextBody }) : bean;
32079
+ return makeTextAndStructured({
32080
+ bean: updatedBean,
32081
+ totalTaskCount,
32082
+ updatedTaskCount,
32083
+ unchangedTaskCount: totalTaskCount - updatedTaskCount
32084
+ });
32085
+ };
31513
32086
  }
31514
32087
  function deleteHandler(backend) {
31515
32088
  return async ({ beanId, beanIds, force }) => {
@@ -31545,7 +32118,11 @@ function deleteHandler(backend) {
31545
32118
  await backend.delete(id);
31546
32119
  results.push({ beanId: id, deleted: true });
31547
32120
  } catch (error48) {
31548
- results.push({ beanId: id, deleted: false, error: error48.message });
32121
+ results.push({
32122
+ beanId: id,
32123
+ deleted: false,
32124
+ error: error48.message
32125
+ });
31549
32126
  }
31550
32127
  }
31551
32128
  return makeTextAndStructured({
@@ -31556,15 +32133,53 @@ function deleteHandler(backend) {
31556
32133
  });
31557
32134
  };
31558
32135
  }
32136
+ function bulkCreateHandler(backend) {
32137
+ return async (input) => {
32138
+ const results = await backend.bulkCreate(input.beans, input.parent);
32139
+ const deprecatedDescriptionCount = input.beans.filter((bean) => bean.description !== void 0).length;
32140
+ return makeTextAndStructured({
32141
+ results,
32142
+ requestedCount: input.beans.length,
32143
+ successCount: results.filter((r) => r.bean).length,
32144
+ failedCount: results.filter((r) => r.error).length,
32145
+ ...deprecatedDescriptionCount > 0 ? {
32146
+ warnings: [
32147
+ `Found ${deprecatedDescriptionCount} bean(s) using deprecated field \`description\`; use \`body\` instead.`
32148
+ ]
32149
+ } : {}
32150
+ });
32151
+ };
32152
+ }
32153
+ function bulkUpdateHandler(backend) {
32154
+ return async (input) => {
32155
+ const results = await backend.bulkUpdate(input.beans, input.parent);
32156
+ return makeTextAndStructured({
32157
+ results,
32158
+ requestedCount: input.beans.length,
32159
+ successCount: results.filter((r) => r.bean).length,
32160
+ failedCount: results.filter((r) => r.error).length
32161
+ });
32162
+ };
32163
+ }
31559
32164
  function queryHandler(backend) {
31560
- return async (opts) => handleQueryOperation(backend, opts);
32165
+ return async (opts) => {
32166
+ if (opts.operation === "graphql") {
32167
+ if (typeof backend.queryGraphql !== "function") {
32168
+ throw new TypeError("GraphQL passthrough is not supported by the current backend");
32169
+ }
32170
+ const result = await backend.queryGraphql(opts.graphql || "", opts.variables);
32171
+ return makeTextAndStructured({ data: result.data, errors: result.errors ?? [] });
32172
+ }
32173
+ return handleQueryOperation(backend, opts);
32174
+ };
31561
32175
  }
31562
32176
  function beanFileHandler(backend) {
31563
32177
  return async ({
31564
32178
  operation,
31565
32179
  path,
31566
32180
  content,
31567
- overwrite
32181
+ overwrite,
32182
+ fields
31568
32183
  }) => {
31569
32184
  if (operation === "read") {
31570
32185
  return makeTextAndStructured(await backend.readBeanFile(path));
@@ -31575,6 +32190,9 @@ function beanFileHandler(backend) {
31575
32190
  if (operation === "create") {
31576
32191
  return makeTextAndStructured(await backend.createBeanFile(path, content || "", { overwrite }));
31577
32192
  }
32193
+ if (operation === "update_frontmatter") {
32194
+ return makeTextAndStructured(await backend.updateBeanFrontmatter(path, fields || {}));
32195
+ }
31578
32196
  if (operation === "delete") {
31579
32197
  return makeTextAndStructured(await backend.deleteBeanFile(path));
31580
32198
  }
@@ -31609,6 +32227,21 @@ function registerTools(server, backend) {
31609
32227
  },
31610
32228
  initHandler(backend)
31611
32229
  );
32230
+ server.registerTool(
32231
+ "beans_archive",
32232
+ {
32233
+ title: "Archive Beans",
32234
+ description: "Archive completed or scrapped beans, equivalent to the beans CLI archive command.",
32235
+ inputSchema: external_exports3.object({}),
32236
+ annotations: {
32237
+ readOnlyHint: false,
32238
+ destructiveHint: false,
32239
+ idempotentHint: false,
32240
+ openWorldHint: false
32241
+ }
32242
+ },
32243
+ archiveHandler(backend)
32244
+ );
31612
32245
  server.registerTool(
31613
32246
  "beans_view",
31614
32247
  {
@@ -31618,7 +32251,7 @@ function registerTools(server, backend) {
31618
32251
  beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31619
32252
  beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
31620
32253
  }).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
31621
- message: "Either beanId or beanIds must be provided"
32254
+ message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
31622
32255
  }),
31623
32256
  annotations: {
31624
32257
  readOnlyHint: true,
@@ -31639,7 +32272,8 @@ function registerTools(server, backend) {
31639
32272
  type: external_exports3.string().min(1).max(MAX_METADATA_LENGTH),
31640
32273
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31641
32274
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31642
- description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32275
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Body markdown content"),
32276
+ description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Deprecated alias for body"),
31643
32277
  parent: external_exports3.string().max(MAX_ID_LENGTH).optional()
31644
32278
  }),
31645
32279
  annotations: {
@@ -31657,7 +32291,7 @@ function registerTools(server, backend) {
31657
32291
  title: "Edit Bean Metadata",
31658
32292
  description: "Update bean metadata fields (status/type/priority/parent/blocking).",
31659
32293
  inputSchema: external_exports3.object({
31660
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32294
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31661
32295
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31662
32296
  type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31663
32297
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
@@ -31665,7 +32299,11 @@ function registerTools(server, backend) {
31665
32299
  clearParent: external_exports3.boolean().optional(),
31666
32300
  blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
31667
32301
  blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional()
31668
- }),
32302
+ }).superRefine((input, ctx) => {
32303
+ if (!input.beanId) {
32304
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32305
+ }
32306
+ }).transform((input) => ({ ...input, beanId: input.beanId })),
31669
32307
  annotations: {
31670
32308
  readOnlyHint: false,
31671
32309
  destructiveHint: false,
@@ -31681,10 +32319,14 @@ function registerTools(server, backend) {
31681
32319
  title: "Reopen Bean",
31682
32320
  description: "Reopen a completed or scrapped bean into a non-closed status.",
31683
32321
  inputSchema: external_exports3.object({
31684
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32322
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31685
32323
  requiredCurrentStatus: external_exports3.enum(["completed", "scrapped"]),
31686
32324
  targetStatus: external_exports3.string().max(MAX_METADATA_LENGTH).default("todo")
31687
- }),
32325
+ }).superRefine((input, ctx) => {
32326
+ if (!input.beanId) {
32327
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32328
+ }
32329
+ }).transform((input) => ({ ...input, beanId: input.beanId })),
31688
32330
  annotations: {
31689
32331
  readOnlyHint: false,
31690
32332
  destructiveHint: false,
@@ -31700,7 +32342,7 @@ function registerTools(server, backend) {
31700
32342
  title: "Update Bean",
31701
32343
  description: "Update bean metadata fields (status/type/priority/parent/blocking). Consolidated replacement for per-field update tools.",
31702
32344
  inputSchema: external_exports3.object({
31703
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32345
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31704
32346
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31705
32347
  type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31706
32348
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
@@ -31717,12 +32359,16 @@ function registerTools(server, backend) {
31717
32359
  })
31718
32360
  ).optional(),
31719
32361
  ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
32362
+ }).superRefine((input, ctx) => {
32363
+ if (!input.beanId) {
32364
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32365
+ }
31720
32366
  }).refine(
31721
32367
  (input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
31722
32368
  {
31723
32369
  message: "body cannot be combined with bodyAppend/bodyReplace"
31724
32370
  }
31725
- ),
32371
+ ).transform((input) => ({ ...input, beanId: input.beanId })),
31726
32372
  annotations: {
31727
32373
  readOnlyHint: false,
31728
32374
  destructiveHint: false,
@@ -31742,7 +32388,7 @@ function registerTools(server, backend) {
31742
32388
  beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
31743
32389
  force: external_exports3.boolean().default(false)
31744
32390
  }).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
31745
- message: "Either beanId or beanIds must be provided"
32391
+ message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
31746
32392
  }),
31747
32393
  annotations: {
31748
32394
  readOnlyHint: false,
@@ -31753,25 +32399,122 @@ function registerTools(server, backend) {
31753
32399
  },
31754
32400
  deleteHandler(backend)
31755
32401
  );
32402
+ const beanCreateItemSchema = external_exports3.object({
32403
+ title: external_exports3.string().min(1).max(MAX_TITLE_LENGTH),
32404
+ type: external_exports3.string().min(1).max(MAX_METADATA_LENGTH),
32405
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32406
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32407
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Body markdown content"),
32408
+ description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Deprecated alias for body"),
32409
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Override the top-level parent for this item")
32410
+ });
32411
+ server.registerTool(
32412
+ "beans_bulk_create",
32413
+ {
32414
+ title: "Bulk Create Beans",
32415
+ description: "Create multiple beans in one call. Optionally assign all of them (or a subset) to a shared parent.",
32416
+ inputSchema: external_exports3.object({
32417
+ beans: external_exports3.array(beanCreateItemSchema).min(1),
32418
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Default parent ID applied to any bean that does not specify its own parent")
32419
+ }),
32420
+ annotations: {
32421
+ readOnlyHint: false,
32422
+ destructiveHint: false,
32423
+ idempotentHint: false,
32424
+ openWorldHint: false
32425
+ }
32426
+ },
32427
+ bulkCreateHandler(backend)
32428
+ );
32429
+ const beanUpdateItemSchema = external_exports3.object({
32430
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
32431
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32432
+ type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32433
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32434
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Override the top-level parent for this item"),
32435
+ clearParent: external_exports3.boolean().optional(),
32436
+ blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
32437
+ blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
32438
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32439
+ bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32440
+ bodyReplace: external_exports3.array(external_exports3.object({ old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH), new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH) })).optional(),
32441
+ ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
32442
+ }).superRefine((input, ctx) => {
32443
+ if (!input.beanId) {
32444
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32445
+ }
32446
+ }).refine(
32447
+ (input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
32448
+ { message: "body cannot be combined with bodyAppend/bodyReplace" }
32449
+ ).transform((input) => ({ ...input, beanId: input.beanId }));
32450
+ server.registerTool(
32451
+ "beans_bulk_update",
32452
+ {
32453
+ title: "Bulk Update Beans",
32454
+ description: "Update multiple beans in one call. Optionally assign all of them (or a subset) to a shared parent.",
32455
+ inputSchema: external_exports3.object({
32456
+ beans: external_exports3.array(beanUpdateItemSchema).min(1),
32457
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Default parent ID applied to any bean that does not specify its own parent")
32458
+ }),
32459
+ annotations: {
32460
+ readOnlyHint: false,
32461
+ destructiveHint: false,
32462
+ idempotentHint: false,
32463
+ openWorldHint: false
32464
+ }
32465
+ },
32466
+ bulkUpdateHandler(backend)
32467
+ );
32468
+ server.registerTool(
32469
+ "beans_complete_tasks",
32470
+ {
32471
+ title: "Complete Markdown Tasks",
32472
+ description: "Mark all markdown checklist tasks within a bean as completed.",
32473
+ inputSchema: external_exports3.object({
32474
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional()
32475
+ }).superRefine((input, ctx) => {
32476
+ if (!input.beanId) {
32477
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32478
+ }
32479
+ }).transform((input) => ({ ...input, beanId: input.beanId })),
32480
+ annotations: {
32481
+ readOnlyHint: false,
32482
+ destructiveHint: false,
32483
+ idempotentHint: true,
32484
+ openWorldHint: false
32485
+ }
32486
+ },
32487
+ completeTasksHandler(backend)
32488
+ );
31756
32489
  server.registerTool(
31757
32490
  "beans_query",
31758
32491
  {
31759
32492
  title: "Query Beans",
31760
32493
  description: "Unified query tool for refresh, filter, search, and sort operations.",
31761
32494
  inputSchema: external_exports3.object({
31762
- operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
32495
+ operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config", "graphql"]).default("refresh"),
31763
32496
  mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
31764
32497
  statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
31765
32498
  types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
31766
32499
  search: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
31767
32500
  includeClosed: external_exports3.boolean().optional(),
31768
32501
  tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
32502
+ graphql: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32503
+ variables: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
31769
32504
  writeToWorkspaceInstructions: external_exports3.boolean().optional()
32505
+ }).superRefine((input, ctx) => {
32506
+ if (input.operation === "graphql" && (!input.graphql || input.graphql.trim().length === 0)) {
32507
+ ctx.addIssue({
32508
+ code: external_exports3.ZodIssueCode.custom,
32509
+ path: ["graphql"],
32510
+ message: "graphql query string is required when operation is graphql"
32511
+ });
32512
+ }
31770
32513
  }),
31771
32514
  annotations: {
31772
- readOnlyHint: true,
32515
+ readOnlyHint: false,
31773
32516
  destructiveHint: false,
31774
- idempotentHint: true,
32517
+ idempotentHint: false,
31775
32518
  openWorldHint: false
31776
32519
  }
31777
32520
  },
@@ -31783,10 +32526,33 @@ function registerTools(server, backend) {
31783
32526
  title: "Bean File Operations",
31784
32527
  description: "Read, create, edit, or delete files under .beans (operation param).",
31785
32528
  inputSchema: external_exports3.object({
31786
- operation: external_exports3.enum(["read", "edit", "create", "delete"]),
32529
+ operation: external_exports3.enum(["read", "edit", "create", "delete", "update_frontmatter"]),
31787
32530
  path: external_exports3.string().min(1).max(MAX_PATH_LENGTH),
31788
32531
  content: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
31789
- overwrite: external_exports3.boolean().optional()
32532
+ overwrite: external_exports3.boolean().optional(),
32533
+ fields: external_exports3.object({
32534
+ title: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
32535
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32536
+ type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32537
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32538
+ parent_id: external_exports3.string().max(MAX_ID_LENGTH).nullable().optional(),
32539
+ tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
32540
+ blocking_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
32541
+ blocked_by_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
32542
+ pr: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional(),
32543
+ branch: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional()
32544
+ }).optional()
32545
+ }).superRefine((input, ctx) => {
32546
+ if (input.operation === "update_frontmatter") {
32547
+ const fieldCount = Object.values(input.fields || {}).filter((value) => value !== void 0).length;
32548
+ if (fieldCount === 0) {
32549
+ ctx.addIssue({
32550
+ code: external_exports3.ZodIssueCode.custom,
32551
+ path: ["fields"],
32552
+ message: "At least one frontmatter field update is required"
32553
+ });
32554
+ }
32555
+ }
31790
32556
  }),
31791
32557
  annotations: {
31792
32558
  readOnlyHint: false,
@@ -31826,6 +32592,18 @@ var MutableBackend = class {
31826
32592
  init(prefix) {
31827
32593
  return this.inner.init(prefix);
31828
32594
  }
32595
+ archive() {
32596
+ if (typeof this.inner.archive === "function") {
32597
+ return this.inner.archive();
32598
+ }
32599
+ throw new TypeError("Archive is not supported by backend");
32600
+ }
32601
+ queryGraphql(query, variables) {
32602
+ if (typeof this.inner.queryGraphql === "function") {
32603
+ return this.inner.queryGraphql(query, variables);
32604
+ }
32605
+ throw new TypeError("GraphQL passthrough is not supported by backend");
32606
+ }
31829
32607
  list(opts) {
31830
32608
  return this.inner.list(opts);
31831
32609
  }
@@ -31838,6 +32616,12 @@ var MutableBackend = class {
31838
32616
  delete(id) {
31839
32617
  return this.inner.delete(id);
31840
32618
  }
32619
+ bulkCreate(beans, defaultParent) {
32620
+ return this.inner.bulkCreate(beans, defaultParent);
32621
+ }
32622
+ bulkUpdate(beans, defaultParent) {
32623
+ return this.inner.bulkUpdate(beans, defaultParent);
32624
+ }
31841
32625
  openConfig() {
31842
32626
  return this.inner.openConfig();
31843
32627
  }
@@ -31859,6 +32643,9 @@ var MutableBackend = class {
31859
32643
  editBeanFile(path, content) {
31860
32644
  return this.inner.editBeanFile(path, content);
31861
32645
  }
32646
+ updateBeanFrontmatter(path, updates) {
32647
+ return this.inner.updateBeanFrontmatter(path, updates);
32648
+ }
31862
32649
  createBeanFile(path, content, opts) {
31863
32650
  return this.inner.createBeanFile(path, content, opts);
31864
32651
  }
@@ -31925,6 +32712,16 @@ function parseCliArgs(argv) {
31925
32712
  const envPort = Number.parseInt(process.env.BEANS_VSCODE_MCP_PORT || process.env.BEANS_MCP_PORT || "", 10);
31926
32713
  let port = Number.isInteger(envPort) && envPort > 0 ? envPort : DEFAULT_MCP_PORT;
31927
32714
  let logDir;
32715
+ const parseStrictPositiveInt = (raw, flagName) => {
32716
+ if (!/^\d+$/.test(raw)) {
32717
+ throw new Error(`Invalid value for ${flagName}: ${raw}`);
32718
+ }
32719
+ const parsed = Number.parseInt(raw, 10);
32720
+ if (!Number.isInteger(parsed) || parsed <= 0) {
32721
+ throw new Error(`Invalid value for ${flagName}: ${raw}`);
32722
+ }
32723
+ return parsed;
32724
+ };
31928
32725
  for (let i = 0; i < argv.length; i += 1) {
31929
32726
  const arg = argv[i];
31930
32727
  if ((arg === "--workspace" || arg === "--workspace-root") && argv[i + 1]) {
@@ -31938,10 +32735,7 @@ function parseCliArgs(argv) {
31938
32735
  }
31939
32736
  i += 1;
31940
32737
  } else if (arg === "--port" && argv[i + 1]) {
31941
- const parsedPort = Number.parseInt(argv[i + 1], 10);
31942
- if (Number.isInteger(parsedPort) && parsedPort > 0) {
31943
- port = parsedPort;
31944
- }
32738
+ port = parseStrictPositiveInt(argv[i + 1], "--port");
31945
32739
  i += 1;
31946
32740
  } else if (arg === "--log-dir" && argv[i + 1]) {
31947
32741
  logDir = argv[i + 1];