@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.
package/index.cjs CHANGED
@@ -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();
@@ -23191,6 +23589,7 @@ __export(src_exports, {
23191
23589
  DEFAULT_MCP_PORT: () => DEFAULT_MCP_PORT,
23192
23590
  MAX_ID_LENGTH: () => MAX_ID_LENGTH,
23193
23591
  MAX_METADATA_LENGTH: () => MAX_METADATA_LENGTH,
23592
+ MAX_PATH_LENGTH: () => MAX_PATH_LENGTH,
23194
23593
  MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
23195
23594
  createBeansMcpServer: () => createBeansMcpServer,
23196
23595
  isPathWithinRoot: () => isPathWithinRoot,
@@ -27143,7 +27542,7 @@ init_core2();
27143
27542
  // node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/mini/coerce.js
27144
27543
  init_core2();
27145
27544
 
27146
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
27545
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
27147
27546
  function isZ4Schema(s) {
27148
27547
  const schema = s;
27149
27548
  return !!schema._zod;
@@ -27287,10 +27686,10 @@ function getLiteralValue(schema) {
27287
27686
  return void 0;
27288
27687
  }
27289
27688
 
27290
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
27689
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
27291
27690
  init_types();
27292
27691
 
27293
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
27692
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
27294
27693
  function isTerminal(status) {
27295
27694
  return status === "completed" || status === "failed" || status === "cancelled";
27296
27695
  }
@@ -28579,7 +28978,7 @@ var zodToJsonSchema = (schema, options) => {
28579
28978
  return combined;
28580
28979
  };
28581
28980
 
28582
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
28981
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
28583
28982
  function mapMiniTarget(t) {
28584
28983
  if (!t)
28585
28984
  return "draft-7";
@@ -28621,7 +29020,7 @@ function parseWithCompat(schema, data) {
28621
29020
  return result.data;
28622
29021
  }
28623
29022
 
28624
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
29023
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
28625
29024
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
28626
29025
  var Protocol = class {
28627
29026
  constructor(_options) {
@@ -28833,6 +29232,10 @@ var Protocol = class {
28833
29232
  this._progressHandlers.clear();
28834
29233
  this._taskProgressTokens.clear();
28835
29234
  this._pendingDebouncedNotifications.clear();
29235
+ for (const info of this._timeoutInfo.values()) {
29236
+ clearTimeout(info.timeoutId);
29237
+ }
29238
+ this._timeoutInfo.clear();
28836
29239
  for (const controller of this._requestHandlerAbortControllers.values()) {
28837
29240
  controller.abort();
28838
29241
  }
@@ -28963,7 +29366,9 @@ var Protocol = class {
28963
29366
  await capturedTransport?.send(errorResponse);
28964
29367
  }
28965
29368
  }).catch((error48) => this._onerror(new Error(`Failed to send response: ${error48}`))).finally(() => {
28966
- this._requestHandlerAbortControllers.delete(request.id);
29369
+ if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
29370
+ this._requestHandlerAbortControllers.delete(request.id);
29371
+ }
28967
29372
  });
28968
29373
  }
28969
29374
  _onprogress(notification) {
@@ -29569,10 +29974,10 @@ function mergeCapabilities(base, additional) {
29569
29974
  return result;
29570
29975
  }
29571
29976
 
29572
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29977
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29573
29978
  init_types();
29574
29979
 
29575
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
29980
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
29576
29981
  var import_ajv = __toESM(require_ajv(), 1);
29577
29982
  var import_ajv_formats = __toESM(require_dist(), 1);
29578
29983
  function createDefaultAjvInstance() {
@@ -29640,7 +30045,7 @@ var AjvJsonSchemaValidator = class {
29640
30045
  }
29641
30046
  };
29642
30047
 
29643
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
30048
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
29644
30049
  init_types();
29645
30050
  var ExperimentalServerTasks = class {
29646
30051
  constructor(_server) {
@@ -29854,7 +30259,7 @@ var ExperimentalServerTasks = class {
29854
30259
  }
29855
30260
  };
29856
30261
 
29857
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30262
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
29858
30263
  function assertToolsCallTaskCapability(requests, method, entityName) {
29859
30264
  if (!requests) {
29860
30265
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -29889,7 +30294,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
29889
30294
  }
29890
30295
  }
29891
30296
 
29892
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30297
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29893
30298
  var Server = class extends Protocol {
29894
30299
  /**
29895
30300
  * Initializes this server with the given name and version information.
@@ -30269,10 +30674,10 @@ var Server = class extends Protocol {
30269
30674
  }
30270
30675
  };
30271
30676
 
30272
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30677
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30273
30678
  init_types();
30274
30679
 
30275
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
30680
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
30276
30681
  var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
30277
30682
  function isCompletable(schema) {
30278
30683
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -30286,7 +30691,7 @@ var McpZodTypeKind;
30286
30691
  McpZodTypeKind2["Completable"] = "McpCompletable";
30287
30692
  })(McpZodTypeKind || (McpZodTypeKind = {}));
30288
30693
 
30289
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
30694
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
30290
30695
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
30291
30696
  function validateToolName(name) {
30292
30697
  const warnings = [];
@@ -30344,7 +30749,7 @@ function validateAndWarnToolName(name) {
30344
30749
  return result.isValid;
30345
30750
  }
30346
30751
 
30347
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
30752
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
30348
30753
  var ExperimentalMcpServerTasks = class {
30349
30754
  constructor(_mcpServer) {
30350
30755
  this._mcpServer = _mcpServer;
@@ -30363,7 +30768,7 @@ var ExperimentalMcpServerTasks = class {
30363
30768
  init_external();
30364
30769
  init_external();
30365
30770
 
30366
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30771
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30367
30772
  var McpServer = class {
30368
30773
  constructor(serverInfo, options) {
30369
30774
  this._registeredResources = {};
@@ -30987,6 +31392,9 @@ var McpServer = class {
30987
31392
  annotations = rest.shift();
30988
31393
  }
30989
31394
  } else if (typeof firstArg === "object" && firstArg !== null) {
31395
+ if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
31396
+ throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
31397
+ }
30990
31398
  annotations = rest.shift();
30991
31399
  }
30992
31400
  }
@@ -31105,6 +31513,9 @@ function getZodSchemaObject(schema) {
31105
31513
  if (isZodRawShapeCompat(schema)) {
31106
31514
  return objectFromShape(schema);
31107
31515
  }
31516
+ if (!isZodSchemaInstance(schema)) {
31517
+ throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
31518
+ }
31108
31519
  return schema;
31109
31520
  }
31110
31521
  function promptArgumentsFromSchema(schema) {
@@ -31153,6 +31564,100 @@ var EMPTY_COMPLETION_RESULT = {
31153
31564
  var import_node_child_process2 = require("child_process");
31154
31565
  var import_node_util2 = require("util");
31155
31566
 
31567
+ // package.json
31568
+ var package_default = {
31569
+ name: "@selfagency/beans-mcp",
31570
+ version: "0.6.0",
31571
+ private: false,
31572
+ description: "MCP (Model Context Protocol) server for Beans issue tracker",
31573
+ keywords: [
31574
+ "ai",
31575
+ "beans",
31576
+ "issue-tracker",
31577
+ "mcp",
31578
+ "model-context-protocol"
31579
+ ],
31580
+ homepage: "https://github.com/selfagency/beans-mcp",
31581
+ bugs: {
31582
+ url: "https://github.com/selfagency/beans-mcp/issues"
31583
+ },
31584
+ license: "MIT",
31585
+ author: {
31586
+ name: "Daniel Sieradski",
31587
+ email: "daniel@self.agency",
31588
+ url: "https://self.agency"
31589
+ },
31590
+ repository: {
31591
+ type: "git",
31592
+ url: "git+https://github.com/selfagency/beans-mcp.git"
31593
+ },
31594
+ bin: {
31595
+ "beans-mcp": "dist/beans-mcp-server.cjs"
31596
+ },
31597
+ files: [
31598
+ "dist",
31599
+ "skills",
31600
+ "README.md",
31601
+ "LICENSE.txt"
31602
+ ],
31603
+ type: "module",
31604
+ main: "./dist/index.cjs",
31605
+ module: "./dist/index.js",
31606
+ types: "./dist/index.d.ts",
31607
+ exports: {
31608
+ ".": {
31609
+ types: "./dist/index.d.ts",
31610
+ import: "./dist/index.js",
31611
+ require: "./dist/index.cjs"
31612
+ }
31613
+ },
31614
+ scripts: {
31615
+ build: "tsup",
31616
+ "docs:dev": "vitepress dev docs",
31617
+ "docs:build": "vitepress build docs",
31618
+ "docs:preview": "vitepress preview docs",
31619
+ format: "oxfmt",
31620
+ "lint:fix": "oxlint --fix",
31621
+ lint: "oxlint",
31622
+ postbuild: "node ./scripts/write-dist-package.js",
31623
+ prepare: "husky",
31624
+ release: "zx ./scripts/release.js",
31625
+ "test:coverage": "vitest run --coverage",
31626
+ "test:watch": "vitest",
31627
+ test: "vitest run",
31628
+ "type-check": "tsc --noEmit"
31629
+ },
31630
+ devDependencies: {
31631
+ "@modelcontextprotocol/sdk": "^1.29.0",
31632
+ "@octokit/rest": "^22.0.1",
31633
+ "@types/node": "25.6.0",
31634
+ "@vitest/coverage-v8": "^4.1.4",
31635
+ "@vitest/ui": "4.1.4",
31636
+ husky: "^9.1.7",
31637
+ "lint-staged": "^16.4.0",
31638
+ ora: "^9.3.0",
31639
+ oxfmt: "^0.45.0",
31640
+ oxlint: "^1.60.0",
31641
+ "oxlint-tsgolint": "^0.21.1",
31642
+ tsup: "8.5.1",
31643
+ typescript: "6.0.3",
31644
+ vitepress: "^1.6.4",
31645
+ vitest: "4.1.4",
31646
+ zod: "4.3.6",
31647
+ zx: "^8.8.5"
31648
+ },
31649
+ "lint-staged": {
31650
+ "src/**/*.ts": [
31651
+ "pnpm run lint:fix",
31652
+ "pnpm run format"
31653
+ ]
31654
+ },
31655
+ engines: {
31656
+ node: ">=18"
31657
+ },
31658
+ mcpName: "io.github.selfagency/beans-mcp"
31659
+ };
31660
+
31156
31661
  // src/internal/queryHelpers.ts
31157
31662
  function sortBeansInternal(beans, mode) {
31158
31663
  const sorted = [...beans];
@@ -31313,94 +31818,10 @@ var MAX_PATH_LENGTH = 1024;
31313
31818
 
31314
31819
  // src/server/BeansMcpServer.ts
31315
31820
  init_utils();
31316
-
31317
- // package.json
31318
- var package_default = {
31319
- name: "@selfagency/beans-mcp",
31320
- version: "0.4.2",
31321
- private: false,
31322
- description: "MCP (Model Context Protocol) server for Beans issue tracker",
31323
- author: {
31324
- name: "Daniel Sieradski",
31325
- email: "daniel@self.agency",
31326
- url: "https://self.agency"
31327
- },
31328
- homepage: "https://github.com/hmans/beans",
31329
- bugs: {
31330
- url: "https://github.com/selfagency/beans-mcp/issues"
31331
- },
31332
- repository: {
31333
- type: "git",
31334
- url: "git+https://github.com/selfagency/beans-mcp.git"
31335
- },
31336
- mcpName: "io.github.selfagency/beans-mcp",
31337
- keywords: [
31338
- "beans",
31339
- "mcp",
31340
- "model-context-protocol",
31341
- "issue-tracker",
31342
- "ai"
31343
- ],
31344
- license: "MIT",
31345
- type: "module",
31346
- exports: {
31347
- ".": {
31348
- types: "./dist/index.d.ts",
31349
- import: "./dist/index.js",
31350
- require: "./dist/index.cjs"
31351
- }
31352
- },
31353
- main: "./dist/index.cjs",
31354
- module: "./dist/index.js",
31355
- types: "./dist/index.d.ts",
31356
- bin: {
31357
- "beans-mcp": "dist/beans-mcp-server.cjs"
31358
- },
31359
- scripts: {
31360
- build: "tsup",
31361
- format: "oxfmt",
31362
- "lint:fix": "oxlint --fix",
31363
- lint: "oxlint",
31364
- postbuild: "node ./scripts/write-dist-package.js",
31365
- prepare: "husky",
31366
- release: "zx ./scripts/release.js",
31367
- "test:coverage": "vitest run --coverage",
31368
- "test:watch": "vitest",
31369
- test: "vitest run",
31370
- "type-check": "tsc --noEmit"
31371
- },
31372
- devDependencies: {
31373
- "@modelcontextprotocol/sdk": "^1.27.1",
31374
- "@octokit/rest": "^22.0.1",
31375
- "@types/node": "25.5.0",
31376
- "@vitest/coverage-v8": "^4.1.0",
31377
- "@vitest/ui": "4.1.0",
31378
- husky: "^9.1.7",
31379
- "lint-staged": "^16.3.3",
31380
- ora: "^9.3.0",
31381
- oxfmt: "^0.40.0",
31382
- oxlint: "^1.55.0",
31383
- "oxlint-tsgolint": "^0.16.0",
31384
- tsup: "8.5.1",
31385
- typescript: "^5.9.3",
31386
- vitest: "4.1.0",
31387
- zod: "4.3.6",
31388
- zx: "^8.8.5"
31389
- },
31390
- engines: {
31391
- node: ">=18"
31392
- },
31393
- "lint-staged": {
31394
- "src/**/*.ts": [
31395
- "pnpm run lint:fix",
31396
- "pnpm run format"
31397
- ]
31398
- }
31399
- };
31400
-
31401
- // src/server/BeansMcpServer.ts
31402
31821
  var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
31403
31822
  var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
31823
+ var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
31824
+ var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
31404
31825
  function getSafeCliEnv(env) {
31405
31826
  const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
31406
31827
  const safeEnv = {};
@@ -31421,7 +31842,8 @@ function extractVersionFromOutput(output) {
31421
31842
  if (!trimmed) {
31422
31843
  return null;
31423
31844
  }
31424
- const match = trimmed.match(/(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
31845
+ const versionRegex = /(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
31846
+ const match = versionRegex.exec(trimmed);
31425
31847
  return match?.[1] ?? null;
31426
31848
  }
31427
31849
  async function detectBeansCliVersion(cliPath, workspaceRoot) {
@@ -31438,10 +31860,10 @@ ${stderr}`);
31438
31860
  return null;
31439
31861
  }
31440
31862
  }
31441
- async function getBeanById(backend, beanId) {
31863
+ async function getBeanById(backend, beanId, beans) {
31442
31864
  try {
31443
- const beans = await backend.list();
31444
- const found = beans.find((b) => b.id === beanId);
31865
+ const allBeans = beans ?? await backend.list();
31866
+ const found = allBeans.find((b) => b.id === beanId);
31445
31867
  if (!found) {
31446
31868
  throw new Error(`Bean not found: ${beanId}`);
31447
31869
  }
@@ -31450,12 +31872,111 @@ async function getBeanById(backend, beanId) {
31450
31872
  throw new Error(`Failed to fetch bean ${beanId}: ${error48.message}`);
31451
31873
  }
31452
31874
  }
31875
+ function collectDescendantBeans(beans, rootBeanId) {
31876
+ const byParent = /* @__PURE__ */ new Map();
31877
+ const byId = new Map(beans.map((bean) => [bean.id, bean]));
31878
+ for (const bean of beans) {
31879
+ if (!bean.parentId) {
31880
+ continue;
31881
+ }
31882
+ const children = byParent.get(bean.parentId) ?? [];
31883
+ children.push(bean.id);
31884
+ byParent.set(bean.parentId, children);
31885
+ }
31886
+ const queue = [...byParent.get(rootBeanId) ?? []];
31887
+ const visited = /* @__PURE__ */ new Set();
31888
+ const descendants = [];
31889
+ while (queue.length > 0) {
31890
+ const currentId = queue.shift();
31891
+ if (!currentId || visited.has(currentId)) {
31892
+ continue;
31893
+ }
31894
+ visited.add(currentId);
31895
+ const currentBean = byId.get(currentId);
31896
+ if (!currentBean) {
31897
+ continue;
31898
+ }
31899
+ descendants.push(currentBean);
31900
+ const children = byParent.get(currentId);
31901
+ if (children && children.length > 0) {
31902
+ queue.push(...children);
31903
+ }
31904
+ }
31905
+ return descendants;
31906
+ }
31907
+ async function cascadeStatusToDescendants(backend, rootBeanId, targetStatus, options) {
31908
+ const beans = options?.beans ?? await backend.list();
31909
+ const descendants = collectDescendantBeans(beans, rootBeanId);
31910
+ const updatedBeanIds = [];
31911
+ const skippedBeanIds = [];
31912
+ const errors = [];
31913
+ const toUpdate = [];
31914
+ for (const bean of descendants) {
31915
+ if (options?.onlyCurrentStatuses && !options.onlyCurrentStatuses.has(bean.status)) {
31916
+ skippedBeanIds.push(bean.id);
31917
+ continue;
31918
+ }
31919
+ toUpdate.push(bean);
31920
+ }
31921
+ const settled = await Promise.allSettled(
31922
+ toUpdate.map(async (bean) => backend.update(bean.id, { status: targetStatus }))
31923
+ );
31924
+ settled.forEach((result, index) => {
31925
+ const bean = toUpdate[index];
31926
+ if (!bean) {
31927
+ return;
31928
+ }
31929
+ if (result.status === "fulfilled") {
31930
+ updatedBeanIds.push(bean.id);
31931
+ return;
31932
+ }
31933
+ errors.push({
31934
+ beanId: bean.id,
31935
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason)
31936
+ });
31937
+ });
31938
+ return {
31939
+ totalDescendants: descendants.length,
31940
+ updatedBeanIds,
31941
+ skippedBeanIds,
31942
+ errors
31943
+ };
31944
+ }
31945
+ function completeMarkdownTasks(body) {
31946
+ const lines = body.split(/\r?\n/);
31947
+ let totalTaskCount = 0;
31948
+ let updatedTaskCount = 0;
31949
+ const taskLinePattern = /^\s*(?:[-*+]|\d+\.)\s+\[[ xX]\]/;
31950
+ const uncheckedTaskLinePattern = /^(\s*(?:[-*+]|\d+\.)\s+\[)\s(\].*)$/;
31951
+ const nextLines = lines.map((line) => {
31952
+ if (!taskLinePattern.test(line)) {
31953
+ return line;
31954
+ }
31955
+ totalTaskCount += 1;
31956
+ const uncheckedMatch = uncheckedTaskLinePattern.exec(line);
31957
+ if (!uncheckedMatch) {
31958
+ return line;
31959
+ }
31960
+ updatedTaskCount += 1;
31961
+ return `${uncheckedMatch[1]}x${uncheckedMatch[2]}`;
31962
+ });
31963
+ const nextBody = nextLines.join("\n");
31964
+ return { nextBody, totalTaskCount, updatedTaskCount };
31965
+ }
31453
31966
  function initHandler(backend) {
31454
31967
  return async ({ prefix }) => {
31455
31968
  const result = await backend.init(prefix);
31456
31969
  return makeTextAndStructured(result);
31457
31970
  };
31458
31971
  }
31972
+ function archiveHandler(backend) {
31973
+ return async () => {
31974
+ if (typeof backend.archive !== "function") {
31975
+ throw new TypeError("Archive is not supported by the current backend");
31976
+ }
31977
+ return makeTextAndStructured(await backend.archive());
31978
+ };
31979
+ }
31459
31980
  function viewHandler(backend) {
31460
31981
  return async ({ beanId, beanIds }) => {
31461
31982
  const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
@@ -31470,7 +31991,12 @@ function viewHandler(backend) {
31470
31991
  const byId = new Map(beans.map((b) => [b.id, b]));
31471
31992
  const found = ids.map((id) => byId.get(id)).filter(Boolean);
31472
31993
  const missingBeanIds = ids.filter((id) => !byId.has(id));
31473
- return makeTextAndStructured({ beans: found, missingBeanIds, count: found.length, requestedCount: ids.length });
31994
+ return makeTextAndStructured({
31995
+ beans: found,
31996
+ missingBeanIds,
31997
+ count: found.length,
31998
+ requestedCount: ids.length
31999
+ });
31474
32000
  };
31475
32001
  }
31476
32002
  async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
@@ -31488,7 +32014,14 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
31488
32014
  }
31489
32015
  }
31490
32016
  function createHandler(backend) {
31491
- return async (input) => makeTextAndStructured({ bean: await backend.create(input) });
32017
+ return async (input) => {
32018
+ const bean = await backend.create(input);
32019
+ const warnings = input.description !== void 0 ? ["`description` is deprecated; use `body` instead."] : void 0;
32020
+ return makeTextAndStructured({
32021
+ bean,
32022
+ ...warnings ? { warnings } : {}
32023
+ });
32024
+ };
31492
32025
  }
31493
32026
  function editHandler(backend) {
31494
32027
  return async ({
@@ -31502,18 +32035,30 @@ function reopenHandler(backend) {
31502
32035
  requiredCurrentStatus,
31503
32036
  targetStatus
31504
32037
  }) => {
31505
- const bean = await getBeanById(backend, beanId);
31506
- if (bean.status !== requiredCurrentStatus) {
31507
- throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
32038
+ const beans = await backend.list();
32039
+ const bean = await getBeanById(backend, beanId, beans);
32040
+ if (bean.status === requiredCurrentStatus) {
32041
+ const updatedParentBean = await backend.update(beanId, { status: targetStatus });
32042
+ const cascade = await cascadeStatusToDescendants(backend, beanId, targetStatus, {
32043
+ onlyCurrentStatuses: CLOSED_STATUSES,
32044
+ beans
32045
+ });
32046
+ return makeTextAndStructured({
32047
+ bean: updatedParentBean,
32048
+ cascade: {
32049
+ totalDescendants: cascade.totalDescendants,
32050
+ updatedBeanIds: cascade.updatedBeanIds,
32051
+ skippedBeanIds: cascade.skippedBeanIds,
32052
+ errors: cascade.errors
32053
+ }
32054
+ });
31508
32055
  }
31509
- return makeTextAndStructured({
31510
- bean: await backend.update(beanId, { status: targetStatus })
31511
- });
32056
+ throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
31512
32057
  };
31513
32058
  }
31514
32059
  function updateHandler(backend) {
31515
- return async (input) => makeTextAndStructured({
31516
- bean: await backend.update(input.beanId, {
32060
+ return async (input) => {
32061
+ const updatedBean = await backend.update(input.beanId, {
31517
32062
  status: input.status,
31518
32063
  type: input.type,
31519
32064
  priority: input.priority,
@@ -31525,8 +32070,37 @@ function updateHandler(backend) {
31525
32070
  bodyAppend: input.bodyAppend,
31526
32071
  bodyReplace: input.bodyReplace,
31527
32072
  ifMatch: input.ifMatch
31528
- })
31529
- });
32073
+ });
32074
+ const closeStatus = input.status;
32075
+ const shouldCascadeClose = Boolean(closeStatus && CLOSED_STATUSES.has(closeStatus));
32076
+ const cascade = shouldCascadeClose ? await cascadeStatusToDescendants(backend, input.beanId, closeStatus, {
32077
+ beans: await backend.list()
32078
+ }) : null;
32079
+ return makeTextAndStructured({
32080
+ bean: updatedBean,
32081
+ ...cascade ? {
32082
+ cascade: {
32083
+ totalDescendants: cascade.totalDescendants,
32084
+ updatedBeanIds: cascade.updatedBeanIds,
32085
+ skippedBeanIds: cascade.skippedBeanIds,
32086
+ errors: cascade.errors
32087
+ }
32088
+ } : {}
32089
+ });
32090
+ };
32091
+ }
32092
+ function completeTasksHandler(backend) {
32093
+ return async ({ beanId }) => {
32094
+ const bean = await getBeanById(backend, beanId);
32095
+ const { nextBody, totalTaskCount, updatedTaskCount } = completeMarkdownTasks(bean.body || "");
32096
+ const updatedBean = updatedTaskCount > 0 ? await backend.update(beanId, { body: nextBody }) : bean;
32097
+ return makeTextAndStructured({
32098
+ bean: updatedBean,
32099
+ totalTaskCount,
32100
+ updatedTaskCount,
32101
+ unchangedTaskCount: totalTaskCount - updatedTaskCount
32102
+ });
32103
+ };
31530
32104
  }
31531
32105
  function deleteHandler(backend) {
31532
32106
  return async ({ beanId, beanIds, force }) => {
@@ -31562,7 +32136,11 @@ function deleteHandler(backend) {
31562
32136
  await backend.delete(id);
31563
32137
  results.push({ beanId: id, deleted: true });
31564
32138
  } catch (error48) {
31565
- results.push({ beanId: id, deleted: false, error: error48.message });
32139
+ results.push({
32140
+ beanId: id,
32141
+ deleted: false,
32142
+ error: error48.message
32143
+ });
31566
32144
  }
31567
32145
  }
31568
32146
  return makeTextAndStructured({
@@ -31573,15 +32151,53 @@ function deleteHandler(backend) {
31573
32151
  });
31574
32152
  };
31575
32153
  }
32154
+ function bulkCreateHandler(backend) {
32155
+ return async (input) => {
32156
+ const results = await backend.bulkCreate(input.beans, input.parent);
32157
+ const deprecatedDescriptionCount = input.beans.filter((bean) => bean.description !== void 0).length;
32158
+ return makeTextAndStructured({
32159
+ results,
32160
+ requestedCount: input.beans.length,
32161
+ successCount: results.filter((r) => r.bean).length,
32162
+ failedCount: results.filter((r) => r.error).length,
32163
+ ...deprecatedDescriptionCount > 0 ? {
32164
+ warnings: [
32165
+ `Found ${deprecatedDescriptionCount} bean(s) using deprecated field \`description\`; use \`body\` instead.`
32166
+ ]
32167
+ } : {}
32168
+ });
32169
+ };
32170
+ }
32171
+ function bulkUpdateHandler(backend) {
32172
+ return async (input) => {
32173
+ const results = await backend.bulkUpdate(input.beans, input.parent);
32174
+ return makeTextAndStructured({
32175
+ results,
32176
+ requestedCount: input.beans.length,
32177
+ successCount: results.filter((r) => r.bean).length,
32178
+ failedCount: results.filter((r) => r.error).length
32179
+ });
32180
+ };
32181
+ }
31576
32182
  function queryHandler(backend) {
31577
- return async (opts) => handleQueryOperation(backend, opts);
32183
+ return async (opts) => {
32184
+ if (opts.operation === "graphql") {
32185
+ if (typeof backend.queryGraphql !== "function") {
32186
+ throw new TypeError("GraphQL passthrough is not supported by the current backend");
32187
+ }
32188
+ const result = await backend.queryGraphql(opts.graphql || "", opts.variables);
32189
+ return makeTextAndStructured({ data: result.data, errors: result.errors ?? [] });
32190
+ }
32191
+ return handleQueryOperation(backend, opts);
32192
+ };
31578
32193
  }
31579
32194
  function beanFileHandler(backend) {
31580
32195
  return async ({
31581
32196
  operation,
31582
32197
  path,
31583
32198
  content,
31584
- overwrite
32199
+ overwrite,
32200
+ fields
31585
32201
  }) => {
31586
32202
  if (operation === "read") {
31587
32203
  return makeTextAndStructured(await backend.readBeanFile(path));
@@ -31592,6 +32208,9 @@ function beanFileHandler(backend) {
31592
32208
  if (operation === "create") {
31593
32209
  return makeTextAndStructured(await backend.createBeanFile(path, content || "", { overwrite }));
31594
32210
  }
32211
+ if (operation === "update_frontmatter") {
32212
+ return makeTextAndStructured(await backend.updateBeanFrontmatter(path, fields || {}));
32213
+ }
31595
32214
  if (operation === "delete") {
31596
32215
  return makeTextAndStructured(await backend.deleteBeanFile(path));
31597
32216
  }
@@ -31626,6 +32245,21 @@ function registerTools(server, backend) {
31626
32245
  },
31627
32246
  initHandler(backend)
31628
32247
  );
32248
+ server.registerTool(
32249
+ "beans_archive",
32250
+ {
32251
+ title: "Archive Beans",
32252
+ description: "Archive completed or scrapped beans, equivalent to the beans CLI archive command.",
32253
+ inputSchema: external_exports3.object({}),
32254
+ annotations: {
32255
+ readOnlyHint: false,
32256
+ destructiveHint: false,
32257
+ idempotentHint: false,
32258
+ openWorldHint: false
32259
+ }
32260
+ },
32261
+ archiveHandler(backend)
32262
+ );
31629
32263
  server.registerTool(
31630
32264
  "beans_view",
31631
32265
  {
@@ -31635,7 +32269,7 @@ function registerTools(server, backend) {
31635
32269
  beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31636
32270
  beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
31637
32271
  }).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
31638
- message: "Either beanId or beanIds must be provided"
32272
+ message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
31639
32273
  }),
31640
32274
  annotations: {
31641
32275
  readOnlyHint: true,
@@ -31656,7 +32290,8 @@ function registerTools(server, backend) {
31656
32290
  type: external_exports3.string().min(1).max(MAX_METADATA_LENGTH),
31657
32291
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31658
32292
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31659
- description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32293
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Body markdown content"),
32294
+ description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Deprecated alias for body"),
31660
32295
  parent: external_exports3.string().max(MAX_ID_LENGTH).optional()
31661
32296
  }),
31662
32297
  annotations: {
@@ -31674,7 +32309,7 @@ function registerTools(server, backend) {
31674
32309
  title: "Edit Bean Metadata",
31675
32310
  description: "Update bean metadata fields (status/type/priority/parent/blocking).",
31676
32311
  inputSchema: external_exports3.object({
31677
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32312
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31678
32313
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31679
32314
  type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31680
32315
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
@@ -31682,7 +32317,11 @@ function registerTools(server, backend) {
31682
32317
  clearParent: external_exports3.boolean().optional(),
31683
32318
  blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
31684
32319
  blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional()
31685
- }),
32320
+ }).superRefine((input, ctx) => {
32321
+ if (!input.beanId) {
32322
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32323
+ }
32324
+ }).transform((input) => ({ ...input, beanId: input.beanId })),
31686
32325
  annotations: {
31687
32326
  readOnlyHint: false,
31688
32327
  destructiveHint: false,
@@ -31698,10 +32337,14 @@ function registerTools(server, backend) {
31698
32337
  title: "Reopen Bean",
31699
32338
  description: "Reopen a completed or scrapped bean into a non-closed status.",
31700
32339
  inputSchema: external_exports3.object({
31701
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32340
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31702
32341
  requiredCurrentStatus: external_exports3.enum(["completed", "scrapped"]),
31703
32342
  targetStatus: external_exports3.string().max(MAX_METADATA_LENGTH).default("todo")
31704
- }),
32343
+ }).superRefine((input, ctx) => {
32344
+ if (!input.beanId) {
32345
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32346
+ }
32347
+ }).transform((input) => ({ ...input, beanId: input.beanId })),
31705
32348
  annotations: {
31706
32349
  readOnlyHint: false,
31707
32350
  destructiveHint: false,
@@ -31717,7 +32360,7 @@ function registerTools(server, backend) {
31717
32360
  title: "Update Bean",
31718
32361
  description: "Update bean metadata fields (status/type/priority/parent/blocking). Consolidated replacement for per-field update tools.",
31719
32362
  inputSchema: external_exports3.object({
31720
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32363
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31721
32364
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31722
32365
  type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31723
32366
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
@@ -31734,12 +32377,16 @@ function registerTools(server, backend) {
31734
32377
  })
31735
32378
  ).optional(),
31736
32379
  ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
32380
+ }).superRefine((input, ctx) => {
32381
+ if (!input.beanId) {
32382
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32383
+ }
31737
32384
  }).refine(
31738
32385
  (input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
31739
32386
  {
31740
32387
  message: "body cannot be combined with bodyAppend/bodyReplace"
31741
32388
  }
31742
- ),
32389
+ ).transform((input) => ({ ...input, beanId: input.beanId })),
31743
32390
  annotations: {
31744
32391
  readOnlyHint: false,
31745
32392
  destructiveHint: false,
@@ -31759,7 +32406,7 @@ function registerTools(server, backend) {
31759
32406
  beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
31760
32407
  force: external_exports3.boolean().default(false)
31761
32408
  }).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
31762
- message: "Either beanId or beanIds must be provided"
32409
+ message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
31763
32410
  }),
31764
32411
  annotations: {
31765
32412
  readOnlyHint: false,
@@ -31770,25 +32417,122 @@ function registerTools(server, backend) {
31770
32417
  },
31771
32418
  deleteHandler(backend)
31772
32419
  );
32420
+ const beanCreateItemSchema = external_exports3.object({
32421
+ title: external_exports3.string().min(1).max(MAX_TITLE_LENGTH),
32422
+ type: external_exports3.string().min(1).max(MAX_METADATA_LENGTH),
32423
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32424
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32425
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Body markdown content"),
32426
+ description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Deprecated alias for body"),
32427
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Override the top-level parent for this item")
32428
+ });
32429
+ server.registerTool(
32430
+ "beans_bulk_create",
32431
+ {
32432
+ title: "Bulk Create Beans",
32433
+ description: "Create multiple beans in one call. Optionally assign all of them (or a subset) to a shared parent.",
32434
+ inputSchema: external_exports3.object({
32435
+ beans: external_exports3.array(beanCreateItemSchema).min(1),
32436
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Default parent ID applied to any bean that does not specify its own parent")
32437
+ }),
32438
+ annotations: {
32439
+ readOnlyHint: false,
32440
+ destructiveHint: false,
32441
+ idempotentHint: false,
32442
+ openWorldHint: false
32443
+ }
32444
+ },
32445
+ bulkCreateHandler(backend)
32446
+ );
32447
+ const beanUpdateItemSchema = external_exports3.object({
32448
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
32449
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32450
+ type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32451
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32452
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Override the top-level parent for this item"),
32453
+ clearParent: external_exports3.boolean().optional(),
32454
+ blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
32455
+ blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
32456
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32457
+ bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32458
+ bodyReplace: external_exports3.array(external_exports3.object({ old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH), new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH) })).optional(),
32459
+ ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
32460
+ }).superRefine((input, ctx) => {
32461
+ if (!input.beanId) {
32462
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32463
+ }
32464
+ }).refine(
32465
+ (input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
32466
+ { message: "body cannot be combined with bodyAppend/bodyReplace" }
32467
+ ).transform((input) => ({ ...input, beanId: input.beanId }));
32468
+ server.registerTool(
32469
+ "beans_bulk_update",
32470
+ {
32471
+ title: "Bulk Update Beans",
32472
+ description: "Update multiple beans in one call. Optionally assign all of them (or a subset) to a shared parent.",
32473
+ inputSchema: external_exports3.object({
32474
+ beans: external_exports3.array(beanUpdateItemSchema).min(1),
32475
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Default parent ID applied to any bean that does not specify its own parent")
32476
+ }),
32477
+ annotations: {
32478
+ readOnlyHint: false,
32479
+ destructiveHint: false,
32480
+ idempotentHint: false,
32481
+ openWorldHint: false
32482
+ }
32483
+ },
32484
+ bulkUpdateHandler(backend)
32485
+ );
32486
+ server.registerTool(
32487
+ "beans_complete_tasks",
32488
+ {
32489
+ title: "Complete Markdown Tasks",
32490
+ description: "Mark all markdown checklist tasks within a bean as completed.",
32491
+ inputSchema: external_exports3.object({
32492
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional()
32493
+ }).superRefine((input, ctx) => {
32494
+ if (!input.beanId) {
32495
+ ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
32496
+ }
32497
+ }).transform((input) => ({ ...input, beanId: input.beanId })),
32498
+ annotations: {
32499
+ readOnlyHint: false,
32500
+ destructiveHint: false,
32501
+ idempotentHint: true,
32502
+ openWorldHint: false
32503
+ }
32504
+ },
32505
+ completeTasksHandler(backend)
32506
+ );
31773
32507
  server.registerTool(
31774
32508
  "beans_query",
31775
32509
  {
31776
32510
  title: "Query Beans",
31777
32511
  description: "Unified query tool for refresh, filter, search, and sort operations.",
31778
32512
  inputSchema: external_exports3.object({
31779
- operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
32513
+ operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config", "graphql"]).default("refresh"),
31780
32514
  mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
31781
32515
  statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
31782
32516
  types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
31783
32517
  search: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
31784
32518
  includeClosed: external_exports3.boolean().optional(),
31785
32519
  tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
32520
+ graphql: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32521
+ variables: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
31786
32522
  writeToWorkspaceInstructions: external_exports3.boolean().optional()
32523
+ }).superRefine((input, ctx) => {
32524
+ if (input.operation === "graphql" && (!input.graphql || input.graphql.trim().length === 0)) {
32525
+ ctx.addIssue({
32526
+ code: external_exports3.ZodIssueCode.custom,
32527
+ path: ["graphql"],
32528
+ message: "graphql query string is required when operation is graphql"
32529
+ });
32530
+ }
31787
32531
  }),
31788
32532
  annotations: {
31789
- readOnlyHint: true,
32533
+ readOnlyHint: false,
31790
32534
  destructiveHint: false,
31791
- idempotentHint: true,
32535
+ idempotentHint: false,
31792
32536
  openWorldHint: false
31793
32537
  }
31794
32538
  },
@@ -31800,10 +32544,33 @@ function registerTools(server, backend) {
31800
32544
  title: "Bean File Operations",
31801
32545
  description: "Read, create, edit, or delete files under .beans (operation param).",
31802
32546
  inputSchema: external_exports3.object({
31803
- operation: external_exports3.enum(["read", "edit", "create", "delete"]),
32547
+ operation: external_exports3.enum(["read", "edit", "create", "delete", "update_frontmatter"]),
31804
32548
  path: external_exports3.string().min(1).max(MAX_PATH_LENGTH),
31805
32549
  content: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
31806
- overwrite: external_exports3.boolean().optional()
32550
+ overwrite: external_exports3.boolean().optional(),
32551
+ fields: external_exports3.object({
32552
+ title: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
32553
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32554
+ type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32555
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32556
+ parent_id: external_exports3.string().max(MAX_ID_LENGTH).nullable().optional(),
32557
+ tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
32558
+ blocking_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
32559
+ blocked_by_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
32560
+ pr: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional(),
32561
+ branch: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional()
32562
+ }).optional()
32563
+ }).superRefine((input, ctx) => {
32564
+ if (input.operation === "update_frontmatter") {
32565
+ const fieldCount = Object.values(input.fields || {}).filter((value) => value !== void 0).length;
32566
+ if (fieldCount === 0) {
32567
+ ctx.addIssue({
32568
+ code: external_exports3.ZodIssueCode.custom,
32569
+ path: ["fields"],
32570
+ message: "At least one frontmatter field update is required"
32571
+ });
32572
+ }
32573
+ }
31807
32574
  }),
31808
32575
  annotations: {
31809
32576
  readOnlyHint: false,
@@ -31843,6 +32610,18 @@ var MutableBackend = class {
31843
32610
  init(prefix) {
31844
32611
  return this.inner.init(prefix);
31845
32612
  }
32613
+ archive() {
32614
+ if (typeof this.inner.archive === "function") {
32615
+ return this.inner.archive();
32616
+ }
32617
+ throw new TypeError("Archive is not supported by backend");
32618
+ }
32619
+ queryGraphql(query, variables) {
32620
+ if (typeof this.inner.queryGraphql === "function") {
32621
+ return this.inner.queryGraphql(query, variables);
32622
+ }
32623
+ throw new TypeError("GraphQL passthrough is not supported by backend");
32624
+ }
31846
32625
  list(opts) {
31847
32626
  return this.inner.list(opts);
31848
32627
  }
@@ -31855,6 +32634,12 @@ var MutableBackend = class {
31855
32634
  delete(id) {
31856
32635
  return this.inner.delete(id);
31857
32636
  }
32637
+ bulkCreate(beans, defaultParent) {
32638
+ return this.inner.bulkCreate(beans, defaultParent);
32639
+ }
32640
+ bulkUpdate(beans, defaultParent) {
32641
+ return this.inner.bulkUpdate(beans, defaultParent);
32642
+ }
31858
32643
  openConfig() {
31859
32644
  return this.inner.openConfig();
31860
32645
  }
@@ -31876,6 +32661,9 @@ var MutableBackend = class {
31876
32661
  editBeanFile(path, content) {
31877
32662
  return this.inner.editBeanFile(path, content);
31878
32663
  }
32664
+ updateBeanFrontmatter(path, updates) {
32665
+ return this.inner.updateBeanFrontmatter(path, updates);
32666
+ }
31879
32667
  createBeanFile(path, content, opts) {
31880
32668
  return this.inner.createBeanFile(path, content, opts);
31881
32669
  }
@@ -31942,6 +32730,16 @@ function parseCliArgs(argv) {
31942
32730
  const envPort = Number.parseInt(process.env.BEANS_VSCODE_MCP_PORT || process.env.BEANS_MCP_PORT || "", 10);
31943
32731
  let port = Number.isInteger(envPort) && envPort > 0 ? envPort : DEFAULT_MCP_PORT;
31944
32732
  let logDir;
32733
+ const parseStrictPositiveInt = (raw, flagName) => {
32734
+ if (!/^\d+$/.test(raw)) {
32735
+ throw new Error(`Invalid value for ${flagName}: ${raw}`);
32736
+ }
32737
+ const parsed = Number.parseInt(raw, 10);
32738
+ if (!Number.isInteger(parsed) || parsed <= 0) {
32739
+ throw new Error(`Invalid value for ${flagName}: ${raw}`);
32740
+ }
32741
+ return parsed;
32742
+ };
31945
32743
  for (let i = 0; i < argv.length; i += 1) {
31946
32744
  const arg = argv[i];
31947
32745
  if ((arg === "--workspace" || arg === "--workspace-root") && argv[i + 1]) {
@@ -31955,10 +32753,7 @@ function parseCliArgs(argv) {
31955
32753
  }
31956
32754
  i += 1;
31957
32755
  } else if (arg === "--port" && argv[i + 1]) {
31958
- const parsedPort = Number.parseInt(argv[i + 1], 10);
31959
- if (Number.isInteger(parsedPort) && parsedPort > 0) {
31960
- port = parsedPort;
31961
- }
32756
+ port = parseStrictPositiveInt(argv[i + 1], "--port");
31962
32757
  i += 1;
31963
32758
  } else if (arg === "--log-dir" && argv[i + 1]) {
31964
32759
  logDir = argv[i + 1];
@@ -32022,6 +32817,7 @@ init_utils();
32022
32817
  DEFAULT_MCP_PORT,
32023
32818
  MAX_ID_LENGTH,
32024
32819
  MAX_METADATA_LENGTH,
32820
+ MAX_PATH_LENGTH,
32025
32821
  MAX_TITLE_LENGTH,
32026
32822
  createBeansMcpServer,
32027
32823
  isPathWithinRoot,