@selfagency/beans-mcp 0.1.4 → 0.5.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.js CHANGED
@@ -14431,7 +14431,7 @@ var init_v4 = __esm({
14431
14431
  }
14432
14432
  });
14433
14433
 
14434
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
14434
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
14435
14435
  function assertCompleteRequestPrompt(request) {
14436
14436
  if (request.params.ref.type !== "ref/prompt") {
14437
14437
  throw new TypeError(`Expected CompleteRequestPrompt, but got ${request.params.ref.type}`);
@@ -14446,7 +14446,7 @@ function assertCompleteRequestResourceTemplate(request) {
14446
14446
  }
14447
14447
  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;
14448
14448
  var init_types = __esm({
14449
- "node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js"() {
14449
+ "node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js"() {
14450
14450
  "use strict";
14451
14451
  init_v4();
14452
14452
  LATEST_PROTOCOL_VERSION = "2025-11-25";
@@ -14458,10 +14458,9 @@ var init_types = __esm({
14458
14458
  CursorSchema = string2();
14459
14459
  TaskCreationParamsSchema = looseObject({
14460
14460
  /**
14461
- * Time in milliseconds to keep task results available after completion.
14462
- * If null, the task has unlimited lifetime until manually cleaned up.
14461
+ * Requested duration in milliseconds to retain task from creation.
14463
14462
  */
14464
- ttl: union([number2(), _null3()]).optional(),
14463
+ ttl: number2().optional(),
14465
14464
  /**
14466
14465
  * Time in milliseconds to wait between task status requests.
14467
14466
  */
@@ -14760,7 +14759,11 @@ var init_types = __esm({
14760
14759
  /**
14761
14760
  * Present if the client supports task creation.
14762
14761
  */
14763
- tasks: ClientTasksCapabilitySchema.optional()
14762
+ tasks: ClientTasksCapabilitySchema.optional(),
14763
+ /**
14764
+ * Extensions that the client supports. Keys are extension identifiers (vendor-prefix/extension-name).
14765
+ */
14766
+ extensions: record(string2(), AssertObjectSchema).optional()
14764
14767
  });
14765
14768
  InitializeRequestParamsSchema = BaseRequestParamsSchema.extend({
14766
14769
  /**
@@ -14821,7 +14824,11 @@ var init_types = __esm({
14821
14824
  /**
14822
14825
  * Present if the server supports task creation.
14823
14826
  */
14824
- tasks: ServerTasksCapabilitySchema.optional()
14827
+ tasks: ServerTasksCapabilitySchema.optional(),
14828
+ /**
14829
+ * Extensions that the server supports. Keys are extension identifiers (vendor-prefix/extension-name).
14830
+ */
14831
+ extensions: record(string2(), AssertObjectSchema).optional()
14825
14832
  });
14826
14833
  InitializeResultSchema = ResultSchema.extend({
14827
14834
  /**
@@ -15013,6 +15020,12 @@ var init_types = __esm({
15013
15020
  * The MIME type of this resource, if known.
15014
15021
  */
15015
15022
  mimeType: optional(string2()),
15023
+ /**
15024
+ * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known.
15025
+ *
15026
+ * This can be used by Hosts to display file sizes and estimate context window usage.
15027
+ */
15028
+ size: optional(number2()),
15016
15029
  /**
15017
15030
  * Optional annotations for the client.
15018
15031
  */
@@ -22744,7 +22757,7 @@ var init_utils = __esm({
22744
22757
  });
22745
22758
 
22746
22759
  // src/internal/graphql.ts
22747
- var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, DELETE_BEAN_MUTATION;
22760
+ var LIST_BEANS_QUERY, CREATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION, UPDATE_BEAN_MUTATION_WITH_IF_MATCH, DELETE_BEAN_MUTATION, LIST_BEANS_TIMESTAMPS_QUERY;
22748
22761
  var init_graphql = __esm({
22749
22762
  "src/internal/graphql.ts"() {
22750
22763
  "use strict";
@@ -22762,11 +22775,21 @@ var init_graphql = __esm({
22762
22775
  mutation($id: ID!, $input: UpdateBeanInput!) {
22763
22776
  updateBean(id: $id, input: $input) { id slug path title body status type priority tags parentId blockingIds blockedByIds createdAt updatedAt etag }
22764
22777
  }
22778
+ `;
22779
+ UPDATE_BEAN_MUTATION_WITH_IF_MATCH = `
22780
+ mutation($id: ID!, $input: UpdateBeanInput!, $ifMatch: String!) {
22781
+ updateBean(id: $id, input: $input, ifMatch: $ifMatch) { id slug path title body status type priority tags parentId blockingIds blockedByIds createdAt updatedAt etag }
22782
+ }
22765
22783
  `;
22766
22784
  DELETE_BEAN_MUTATION = `
22767
22785
  mutation($id: ID!) {
22768
22786
  deleteBean(id: $id)
22769
22787
  }
22788
+ `;
22789
+ LIST_BEANS_TIMESTAMPS_QUERY = `
22790
+ query {
22791
+ beans { id updatedAt }
22792
+ }
22770
22793
  `;
22771
22794
  }
22772
22795
  });
@@ -22789,12 +22812,26 @@ var init_backend = __esm({
22789
22812
  init_graphql();
22790
22813
  init_utils();
22791
22814
  execFileAsync = promisify(execFile);
22792
- BeansCliBackend = class {
22815
+ BeansCliBackend = class _BeansCliBackend {
22793
22816
  constructor(workspaceRoot, cliPath, logDir) {
22794
22817
  this.workspaceRoot = workspaceRoot;
22795
22818
  this.cliPath = cliPath;
22796
22819
  this.logDir = logDir;
22797
22820
  }
22821
+ // ---------------------------------------------------------------------------
22822
+ // Cache
22823
+ // ---------------------------------------------------------------------------
22824
+ /** Full unfiltered records keyed by bean ID, stored under the fixed cache key `'all'`. */
22825
+ _cache = /* @__PURE__ */ new Map();
22826
+ /** Last time the unfiltered cache entry `'all'` was fetched (ms). */
22827
+ _cacheTime = /* @__PURE__ */ new Map();
22828
+ /** Short-circuit TTL: skip even the timestamp check within this window (ms). */
22829
+ static BURST_TTL_MS = 5e3;
22830
+ /** Invalidate the unfiltered list cache so the next call does a full fetch. */
22831
+ invalidateCache() {
22832
+ this._cache.delete("all");
22833
+ this._cacheTime.delete("all");
22834
+ }
22798
22835
  /**
22799
22836
  * Returns a safe environment for executing the Beans CLI,
22800
22837
  * whitelisting only necessary variables.
@@ -22818,7 +22855,7 @@ var init_backend = __esm({
22818
22855
  return resolve2(this.workspaceRoot, ".beans");
22819
22856
  }
22820
22857
  resolveBeanFilePath(relativePath) {
22821
- const cleaned = relativePath.trim().replace(/^\/+/, "");
22858
+ const cleaned = relativePath.trim().replace(/^\/+/, "").replace(/^\.beans(?:[\\/]|$)/, "");
22822
22859
  if (!cleaned) {
22823
22860
  throw new Error("Path is required");
22824
22861
  }
@@ -22876,10 +22913,48 @@ Output: ${stdout.slice(0, 1e3)}`
22876
22913
  if (options?.search) {
22877
22914
  filter.search = options.search;
22878
22915
  }
22916
+ const isCacheable = !filter.status && !filter.type && !filter.search;
22917
+ const cacheKey = "all";
22918
+ if (isCacheable) {
22919
+ const lastFetch = this._cacheTime.get(cacheKey) ?? 0;
22920
+ const cached2 = this._cache.get(cacheKey);
22921
+ const age = Date.now() - lastFetch;
22922
+ if (cached2 && age < _BeansCliBackend.BURST_TTL_MS) {
22923
+ return Array.from(cached2.values());
22924
+ }
22925
+ if (cached2) {
22926
+ try {
22927
+ const { data: tsData } = await this.executeGraphQL(
22928
+ LIST_BEANS_TIMESTAMPS_QUERY
22929
+ );
22930
+ const timestamps = tsData.beans;
22931
+ let dirty = timestamps.length !== cached2.size;
22932
+ if (!dirty) {
22933
+ for (const { id, updatedAt } of timestamps) {
22934
+ const existing = cached2.get(id);
22935
+ if (!existing || existing.updatedAt !== updatedAt) {
22936
+ dirty = true;
22937
+ break;
22938
+ }
22939
+ }
22940
+ }
22941
+ if (!dirty) {
22942
+ this._cacheTime.set(cacheKey, Date.now());
22943
+ return Array.from(cached2.values());
22944
+ }
22945
+ } catch {
22946
+ }
22947
+ }
22948
+ }
22879
22949
  const { data, errors } = await this.executeGraphQL(LIST_BEANS_QUERY, { filter });
22880
22950
  if (errors && errors.length > 0) {
22881
22951
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22882
22952
  }
22953
+ if (isCacheable) {
22954
+ const byId = new Map(data.beans.map((b) => [b.id, b]));
22955
+ this._cache.set(cacheKey, byId);
22956
+ this._cacheTime.set(cacheKey, Date.now());
22957
+ }
22883
22958
  return data.beans;
22884
22959
  }
22885
22960
  async create(input) {
@@ -22888,7 +22963,7 @@ Output: ${stdout.slice(0, 1e3)}`
22888
22963
  type: input.type,
22889
22964
  status: input.status,
22890
22965
  priority: input.priority,
22891
- body: input.description,
22966
+ body: input.body ?? input.description,
22892
22967
  parent: input.parent
22893
22968
  };
22894
22969
  const { data, errors } = await this.executeGraphQL(CREATE_BEAN_MUTATION, {
@@ -22897,6 +22972,7 @@ Output: ${stdout.slice(0, 1e3)}`
22897
22972
  if (errors && errors.length > 0) {
22898
22973
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22899
22974
  }
22975
+ this.invalidateCache();
22900
22976
  return data.createBean;
22901
22977
  }
22902
22978
  async update(beanId, updates) {
@@ -22919,13 +22995,54 @@ Output: ${stdout.slice(0, 1e3)}`
22919
22995
  if (updates.body !== void 0) {
22920
22996
  updateInput.body = updates.body;
22921
22997
  }
22922
- const { data, errors } = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
22923
- id: beanId,
22924
- input: updateInput
22925
- });
22998
+ const bodyMod = {};
22999
+ if (updates.bodyAppend !== void 0) {
23000
+ bodyMod.append = updates.bodyAppend;
23001
+ }
23002
+ if (Array.isArray(updates.bodyReplace) && updates.bodyReplace.length > 0) {
23003
+ bodyMod.replace = updates.bodyReplace;
23004
+ }
23005
+ if (Object.keys(bodyMod).length > 0) {
23006
+ updateInput.bodyMod = bodyMod;
23007
+ }
23008
+ let data;
23009
+ let errors;
23010
+ if (updates.ifMatch) {
23011
+ try {
23012
+ const res = await this.executeGraphQL(UPDATE_BEAN_MUTATION_WITH_IF_MATCH, {
23013
+ id: beanId,
23014
+ input: updateInput,
23015
+ ifMatch: updates.ifMatch
23016
+ });
23017
+ data = res.data;
23018
+ errors = res.errors;
23019
+ } catch (error48) {
23020
+ const message = error48.message || "";
23021
+ const unsupportedIfMatch = /unknown argument.*ifMatch|unknown field.*ifMatch|ifMatch.*not defined|field .*updateBean.* argument .*ifMatch/i.test(
23022
+ message
23023
+ );
23024
+ if (!unsupportedIfMatch) {
23025
+ throw error48;
23026
+ }
23027
+ const fallback = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
23028
+ id: beanId,
23029
+ input: updateInput
23030
+ });
23031
+ data = fallback.data;
23032
+ errors = fallback.errors;
23033
+ }
23034
+ } else {
23035
+ const res = await this.executeGraphQL(UPDATE_BEAN_MUTATION, {
23036
+ id: beanId,
23037
+ input: updateInput
23038
+ });
23039
+ data = res.data;
23040
+ errors = res.errors;
23041
+ }
22926
23042
  if (errors && errors.length > 0) {
22927
23043
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22928
23044
  }
23045
+ this.invalidateCache();
22929
23046
  return data.updateBean;
22930
23047
  }
22931
23048
  async delete(beanId) {
@@ -22935,13 +23052,57 @@ Output: ${stdout.slice(0, 1e3)}`
22935
23052
  if (errors && errors.length > 0) {
22936
23053
  throw new Error(`GraphQL error: ${errors.map((e) => e.message).join(", ")}`);
22937
23054
  }
23055
+ this.invalidateCache();
22938
23056
  return { deleted: true, beanId };
22939
23057
  }
23058
+ async bulkCreate(beans, defaultParent) {
23059
+ const results = [];
23060
+ for (const item of beans) {
23061
+ try {
23062
+ const bean = await this.create({
23063
+ ...item,
23064
+ parent: item.parent ?? defaultParent
23065
+ });
23066
+ results.push({ bean });
23067
+ } catch (error48) {
23068
+ results.push({ error: error48.message });
23069
+ }
23070
+ }
23071
+ return results;
23072
+ }
23073
+ async bulkUpdate(beans, defaultParent) {
23074
+ const results = [];
23075
+ for (const { beanId, ...updates } of beans) {
23076
+ try {
23077
+ const resolvedParent = updates.parent ?? (updates.clearParent ? void 0 : defaultParent);
23078
+ const bean = await this.update(beanId, { ...updates, parent: resolvedParent });
23079
+ results.push({ beanId, bean });
23080
+ } catch (error48) {
23081
+ results.push({ beanId, error: error48.message });
23082
+ }
23083
+ }
23084
+ return results;
23085
+ }
22940
23086
  async openConfig() {
22941
23087
  const configPath = join(this.workspaceRoot, ".beans.yml");
22942
23088
  const content = await readFile(configPath, "utf8");
22943
23089
  return { configPath, content };
22944
23090
  }
23091
+ async primeInstructions() {
23092
+ const { stdout } = await execFileAsync(this.cliPath, ["prime"], {
23093
+ cwd: this.workspaceRoot,
23094
+ env: this.getSafeEnv(),
23095
+ maxBuffer: 10 * 1024 * 1024,
23096
+ timeout: 3e4
23097
+ });
23098
+ return stdout.trim();
23099
+ }
23100
+ async writeInstructions(instructions) {
23101
+ const instructionsPath = join(this.workspaceRoot, ".github", "instructions", "beans-prime.instructions.md");
23102
+ await mkdir(dirname(instructionsPath), { recursive: true });
23103
+ await writeFile(instructionsPath, instructions, "utf8");
23104
+ return instructionsPath;
23105
+ }
22945
23106
  async graphqlSchema() {
22946
23107
  const { stdout } = await execFileAsync(this.cliPath, ["graphql", "--schema"], {
22947
23108
  cwd: this.workspaceRoot,
@@ -22980,6 +23141,120 @@ Output: ${stdout.slice(0, 1e3)}`
22980
23141
  linesReturned: ringBuffer.length
22981
23142
  };
22982
23143
  }
23144
+ /**
23145
+ * Split a YAML scalar value from any trailing inline comment.
23146
+ * Understands single-quoted and double-quoted YAML strings so it won't
23147
+ * mistake a `#` inside a quoted value for a comment delimiter.
23148
+ */
23149
+ splitYamlInlineComment(value) {
23150
+ let inSingle = false;
23151
+ let inDouble = false;
23152
+ for (let i = 0; i < value.length; i += 1) {
23153
+ const char = value[i];
23154
+ if (inSingle) {
23155
+ if (char === "'") {
23156
+ if (value[i + 1] === "'") {
23157
+ i += 1;
23158
+ } else {
23159
+ inSingle = false;
23160
+ }
23161
+ }
23162
+ continue;
23163
+ }
23164
+ if (inDouble) {
23165
+ if (char === "\\") {
23166
+ i += 1;
23167
+ continue;
23168
+ }
23169
+ if (char === '"') {
23170
+ inDouble = false;
23171
+ }
23172
+ continue;
23173
+ }
23174
+ if (char === "'") {
23175
+ inSingle = true;
23176
+ continue;
23177
+ }
23178
+ if (char === '"') {
23179
+ inDouble = true;
23180
+ continue;
23181
+ }
23182
+ if (char === "#" && i > 0 && /\s/.test(value[i - 1])) {
23183
+ const valuePart = value.slice(0, i).trimEnd();
23184
+ return {
23185
+ valuePart,
23186
+ commentPart: value.slice(valuePart.length)
23187
+ };
23188
+ }
23189
+ }
23190
+ return { valuePart: value, commentPart: "" };
23191
+ }
23192
+ /** Returns true when `value` looks like a YAML block scalar indicator (`>`, `|`, `>-`, `|-`, etc.) */
23193
+ isYamlBlockScalarIndicator(value) {
23194
+ return /^[>|][+-]?[0-9]*$/.test(value) || /^[>|][0-9]*[+-]?$/.test(value);
23195
+ }
23196
+ /** Escape a plain string for use inside a YAML double-quoted scalar. */
23197
+ escapeForYamlDoubleQuoted(value) {
23198
+ return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
23199
+ }
23200
+ /**
23201
+ * Normalise a raw YAML title value to a double-quoted scalar.
23202
+ * Handles: empty, already double-quoted, single-quoted (unescaping `''`),
23203
+ * block-scalar indicators, and plain unquoted values.
23204
+ */
23205
+ normalizeFrontmatterTitleValue(value) {
23206
+ const trimmed = value.trim();
23207
+ if (trimmed === "") {
23208
+ return '""';
23209
+ }
23210
+ if (this.isYamlBlockScalarIndicator(trimmed)) {
23211
+ return value;
23212
+ }
23213
+ if (/^"(?:[^"\\]|\\[\s\S])*"$/.test(trimmed)) {
23214
+ return trimmed;
23215
+ }
23216
+ if (/^'(?:[^']|'')*'$/.test(trimmed)) {
23217
+ const inner = trimmed.slice(1, -1).replace(/''/g, "'");
23218
+ return `"${this.escapeForYamlDoubleQuoted(inner)}"`;
23219
+ }
23220
+ return `"${this.escapeForYamlDoubleQuoted(trimmed)}"`;
23221
+ }
23222
+ /**
23223
+ * Ensure every `title:` line in YAML frontmatter is double-quoted.
23224
+ * Handles already-quoted (single or double), multi-word, and special-char values.
23225
+ * Preserves inline comments and handles both LF and CRLF line endings.
23226
+ */
23227
+ quoteFrontmatterTitles(content) {
23228
+ const crlfOpen = content.startsWith("---\r\n");
23229
+ const lfOpen = content.startsWith("---\n");
23230
+ if (!crlfOpen && !lfOpen) {
23231
+ return content;
23232
+ }
23233
+ const eol = crlfOpen ? "\r\n" : "\n";
23234
+ const openEnd = `---${eol}`.length;
23235
+ const closeMarker = `${eol}---`;
23236
+ const closeIdx = content.indexOf(closeMarker, openEnd);
23237
+ if (closeIdx === -1) {
23238
+ return content;
23239
+ }
23240
+ const frontmatter = content.slice(openEnd, closeIdx);
23241
+ const rest = content.slice(closeIdx);
23242
+ const lines = frontmatter.split(eol);
23243
+ const fixedLines = lines.map((line) => {
23244
+ if (!line.startsWith("title:")) {
23245
+ return line;
23246
+ }
23247
+ const colonIdx = line.indexOf(":");
23248
+ const afterColon = line.slice(colonIdx + 1);
23249
+ const leadingSpace = afterColon.length - afterColon.trimStart().length;
23250
+ const raw = afterColon.trimStart();
23251
+ const { valuePart, commentPart } = this.splitYamlInlineComment(raw);
23252
+ const normalized = this.normalizeFrontmatterTitleValue(valuePart);
23253
+ const prefix = `title:${" ".repeat(Math.max(1, leadingSpace))}`;
23254
+ return `${prefix}${normalized}${commentPart}`;
23255
+ });
23256
+ return `---${eol}${fixedLines.join(eol)}${rest}`;
23257
+ }
22983
23258
  async readBeanFile(relativePath) {
22984
23259
  const absolutePath = this.resolveBeanFilePath(relativePath);
22985
23260
  const content = await readFile(absolutePath, "utf8");
@@ -22987,20 +23262,22 @@ Output: ${stdout.slice(0, 1e3)}`
22987
23262
  }
22988
23263
  async editBeanFile(relativePath, content) {
22989
23264
  const absolutePath = this.resolveBeanFilePath(relativePath);
23265
+ const fixed = this.quoteFrontmatterTitles(content);
22990
23266
  await mkdir(dirname(absolutePath), { recursive: true });
22991
- await writeFile(absolutePath, content, "utf8");
22992
- return { path: absolutePath, bytes: Buffer.byteLength(content, "utf8") };
23267
+ await writeFile(absolutePath, fixed, "utf8");
23268
+ return { path: absolutePath, bytes: Buffer.byteLength(fixed, "utf8") };
22993
23269
  }
22994
23270
  async createBeanFile(relativePath, content, options) {
22995
23271
  const absolutePath = this.resolveBeanFilePath(relativePath);
23272
+ const fixed = this.quoteFrontmatterTitles(content);
22996
23273
  await mkdir(dirname(absolutePath), { recursive: true });
22997
- await writeFile(absolutePath, content, {
23274
+ await writeFile(absolutePath, fixed, {
22998
23275
  encoding: "utf8",
22999
23276
  flag: options?.overwrite ? "w" : "wx"
23000
23277
  });
23001
23278
  return {
23002
23279
  path: absolutePath,
23003
- bytes: Buffer.byteLength(content, "utf8"),
23280
+ bytes: Buffer.byteLength(fixed, "utf8"),
23004
23281
  created: true
23005
23282
  };
23006
23283
  }
@@ -23013,7 +23290,7 @@ Output: ${stdout.slice(0, 1e3)}`
23013
23290
  }
23014
23291
  });
23015
23292
 
23016
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
23293
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
23017
23294
  function deserializeMessage(line) {
23018
23295
  return JSONRPCMessageSchema.parse(JSON.parse(line));
23019
23296
  }
@@ -23022,7 +23299,7 @@ function serializeMessage(message) {
23022
23299
  }
23023
23300
  var ReadBuffer;
23024
23301
  var init_stdio = __esm({
23025
- "node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js"() {
23302
+ "node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js"() {
23026
23303
  "use strict";
23027
23304
  init_types();
23028
23305
  ReadBuffer = class {
@@ -23048,7 +23325,7 @@ var init_stdio = __esm({
23048
23325
  }
23049
23326
  });
23050
23327
 
23051
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
23328
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
23052
23329
  var stdio_exports = {};
23053
23330
  __export(stdio_exports, {
23054
23331
  StdioServerTransport: () => StdioServerTransport
@@ -23056,7 +23333,7 @@ __export(stdio_exports, {
23056
23333
  import process3 from "process";
23057
23334
  var StdioServerTransport;
23058
23335
  var init_stdio2 = __esm({
23059
- "node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js"() {
23336
+ "node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js"() {
23060
23337
  "use strict";
23061
23338
  init_stdio();
23062
23339
  StdioServerTransport = class {
@@ -27063,7 +27340,7 @@ init_core2();
27063
27340
  // node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/mini/coerce.js
27064
27341
  init_core2();
27065
27342
 
27066
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
27343
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
27067
27344
  function isZ4Schema(s) {
27068
27345
  const schema = s;
27069
27346
  return !!schema._zod;
@@ -27207,10 +27484,10 @@ function getLiteralValue(schema) {
27207
27484
  return void 0;
27208
27485
  }
27209
27486
 
27210
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
27487
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
27211
27488
  init_types();
27212
27489
 
27213
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
27490
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js
27214
27491
  function isTerminal(status) {
27215
27492
  return status === "completed" || status === "failed" || status === "cancelled";
27216
27493
  }
@@ -28499,7 +28776,7 @@ var zodToJsonSchema = (schema, options) => {
28499
28776
  return combined;
28500
28777
  };
28501
28778
 
28502
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
28779
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js
28503
28780
  function mapMiniTarget(t) {
28504
28781
  if (!t)
28505
28782
  return "draft-7";
@@ -28541,7 +28818,7 @@ function parseWithCompat(schema, data) {
28541
28818
  return result.data;
28542
28819
  }
28543
28820
 
28544
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
28821
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js
28545
28822
  var DEFAULT_REQUEST_TIMEOUT_MSEC = 6e4;
28546
28823
  var Protocol = class {
28547
28824
  constructor(_options) {
@@ -28753,6 +29030,10 @@ var Protocol = class {
28753
29030
  this._progressHandlers.clear();
28754
29031
  this._taskProgressTokens.clear();
28755
29032
  this._pendingDebouncedNotifications.clear();
29033
+ for (const info of this._timeoutInfo.values()) {
29034
+ clearTimeout(info.timeoutId);
29035
+ }
29036
+ this._timeoutInfo.clear();
28756
29037
  for (const controller of this._requestHandlerAbortControllers.values()) {
28757
29038
  controller.abort();
28758
29039
  }
@@ -28883,7 +29164,9 @@ var Protocol = class {
28883
29164
  await capturedTransport?.send(errorResponse);
28884
29165
  }
28885
29166
  }).catch((error48) => this._onerror(new Error(`Failed to send response: ${error48}`))).finally(() => {
28886
- this._requestHandlerAbortControllers.delete(request.id);
29167
+ if (this._requestHandlerAbortControllers.get(request.id) === abortController) {
29168
+ this._requestHandlerAbortControllers.delete(request.id);
29169
+ }
28887
29170
  });
28888
29171
  }
28889
29172
  _onprogress(notification) {
@@ -29489,10 +29772,10 @@ function mergeCapabilities(base, additional) {
29489
29772
  return result;
29490
29773
  }
29491
29774
 
29492
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29775
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29493
29776
  init_types();
29494
29777
 
29495
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
29778
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js
29496
29779
  var import_ajv = __toESM(require_ajv(), 1);
29497
29780
  var import_ajv_formats = __toESM(require_dist(), 1);
29498
29781
  function createDefaultAjvInstance() {
@@ -29560,7 +29843,7 @@ var AjvJsonSchemaValidator = class {
29560
29843
  }
29561
29844
  };
29562
29845
 
29563
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
29846
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js
29564
29847
  init_types();
29565
29848
  var ExperimentalServerTasks = class {
29566
29849
  constructor(_server) {
@@ -29774,7 +30057,7 @@ var ExperimentalServerTasks = class {
29774
30057
  }
29775
30058
  };
29776
30059
 
29777
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
30060
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js
29778
30061
  function assertToolsCallTaskCapability(requests, method, entityName) {
29779
30062
  if (!requests) {
29780
30063
  throw new Error(`${entityName} does not support task creation (required for ${method})`);
@@ -29809,7 +30092,7 @@ function assertClientRequestTaskCapability(requests, method, entityName) {
29809
30092
  }
29810
30093
  }
29811
30094
 
29812
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
30095
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js
29813
30096
  var Server = class extends Protocol {
29814
30097
  /**
29815
30098
  * Initializes this server with the given name and version information.
@@ -30189,10 +30472,10 @@ var Server = class extends Protocol {
30189
30472
  }
30190
30473
  };
30191
30474
 
30192
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30475
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30193
30476
  init_types();
30194
30477
 
30195
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
30478
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js
30196
30479
  var COMPLETABLE_SYMBOL = /* @__PURE__ */ Symbol.for("mcp.completable");
30197
30480
  function isCompletable(schema) {
30198
30481
  return !!schema && typeof schema === "object" && COMPLETABLE_SYMBOL in schema;
@@ -30206,7 +30489,7 @@ var McpZodTypeKind;
30206
30489
  McpZodTypeKind2["Completable"] = "McpCompletable";
30207
30490
  })(McpZodTypeKind || (McpZodTypeKind = {}));
30208
30491
 
30209
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
30492
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js
30210
30493
  var TOOL_NAME_REGEX = /^[A-Za-z0-9._-]{1,128}$/;
30211
30494
  function validateToolName(name) {
30212
30495
  const warnings = [];
@@ -30264,7 +30547,7 @@ function validateAndWarnToolName(name) {
30264
30547
  return result.isValid;
30265
30548
  }
30266
30549
 
30267
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
30550
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js
30268
30551
  var ExperimentalMcpServerTasks = class {
30269
30552
  constructor(_mcpServer) {
30270
30553
  this._mcpServer = _mcpServer;
@@ -30283,7 +30566,7 @@ var ExperimentalMcpServerTasks = class {
30283
30566
  init_external();
30284
30567
  init_external();
30285
30568
 
30286
- // node_modules/.pnpm/@modelcontextprotocol+sdk@1.27.1_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30569
+ // node_modules/.pnpm/@modelcontextprotocol+sdk@1.29.0_zod@4.3.6/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js
30287
30570
  var McpServer = class {
30288
30571
  constructor(serverInfo, options) {
30289
30572
  this._registeredResources = {};
@@ -30907,6 +31190,9 @@ var McpServer = class {
30907
31190
  annotations = rest.shift();
30908
31191
  }
30909
31192
  } else if (typeof firstArg === "object" && firstArg !== null) {
31193
+ if (Object.values(firstArg).some((v) => typeof v === "object" && v !== null)) {
31194
+ throw new Error(`Tool ${name} expected a Zod schema or ToolAnnotations, but received an unrecognized object`);
31195
+ }
30910
31196
  annotations = rest.shift();
30911
31197
  }
30912
31198
  }
@@ -31025,6 +31311,9 @@ function getZodSchemaObject(schema) {
31025
31311
  if (isZodRawShapeCompat(schema)) {
31026
31312
  return objectFromShape(schema);
31027
31313
  }
31314
+ if (!isZodSchemaInstance(schema)) {
31315
+ throw new Error("inputSchema must be a Zod schema or raw shape, received an unrecognized object");
31316
+ }
31028
31317
  return schema;
31029
31318
  }
31030
31319
  function promptArgumentsFromSchema(schema) {
@@ -31069,6 +31358,94 @@ var EMPTY_COMPLETION_RESULT = {
31069
31358
  }
31070
31359
  };
31071
31360
 
31361
+ // src/server/BeansMcpServer.ts
31362
+ import { execFile as execFile2 } from "child_process";
31363
+ import { promisify as promisify2 } from "util";
31364
+
31365
+ // package.json
31366
+ var package_default = {
31367
+ name: "@selfagency/beans-mcp",
31368
+ version: "0.5.0",
31369
+ private: false,
31370
+ description: "MCP (Model Context Protocol) server for Beans issue tracker",
31371
+ author: {
31372
+ name: "Daniel Sieradski",
31373
+ email: "daniel@self.agency",
31374
+ url: "https://self.agency"
31375
+ },
31376
+ homepage: "https://github.com/selfagency/beans-mcp",
31377
+ bugs: {
31378
+ url: "https://github.com/selfagency/beans-mcp/issues"
31379
+ },
31380
+ repository: {
31381
+ type: "git",
31382
+ url: "git+https://github.com/selfagency/beans-mcp.git"
31383
+ },
31384
+ mcpName: "io.github.selfagency/beans-mcp",
31385
+ keywords: [
31386
+ "beans",
31387
+ "mcp",
31388
+ "model-context-protocol",
31389
+ "issue-tracker",
31390
+ "ai"
31391
+ ],
31392
+ license: "MIT",
31393
+ type: "module",
31394
+ exports: {
31395
+ ".": {
31396
+ types: "./dist/index.d.ts",
31397
+ import: "./dist/index.js",
31398
+ require: "./dist/index.cjs"
31399
+ }
31400
+ },
31401
+ main: "./dist/index.cjs",
31402
+ module: "./dist/index.js",
31403
+ types: "./dist/index.d.ts",
31404
+ bin: {
31405
+ "beans-mcp": "dist/beans-mcp-server.cjs"
31406
+ },
31407
+ scripts: {
31408
+ build: "tsup",
31409
+ format: "oxfmt",
31410
+ "lint:fix": "oxlint --fix",
31411
+ lint: "oxlint",
31412
+ postbuild: "node ./scripts/write-dist-package.js",
31413
+ prepare: "husky",
31414
+ release: "zx ./scripts/release.js",
31415
+ "test:coverage": "vitest run --coverage",
31416
+ "test:watch": "vitest",
31417
+ test: "vitest run",
31418
+ "type-check": "tsc --noEmit"
31419
+ },
31420
+ devDependencies: {
31421
+ "@modelcontextprotocol/sdk": "^1.29.0",
31422
+ "@octokit/rest": "^22.0.1",
31423
+ "@types/node": "25.5.2",
31424
+ "@vitest/coverage-v8": "^4.1.2",
31425
+ "@vitest/ui": "4.1.2",
31426
+ husky: "^9.1.7",
31427
+ "lint-staged": "^16.4.0",
31428
+ ora: "^9.3.0",
31429
+ oxfmt: "^0.43.0",
31430
+ oxlint: "^1.58.0",
31431
+ "oxlint-tsgolint": "^0.20.0",
31432
+ tsup: "8.5.1",
31433
+ typescript: "6.0.2",
31434
+ vitest: "4.1.2",
31435
+ zod: "4.3.6",
31436
+ zx: "^8.8.5"
31437
+ },
31438
+ engines: {
31439
+ node: ">=18"
31440
+ },
31441
+ "lint-staged": {
31442
+ "src/**/*.ts": [
31443
+ "pnpm run lint:fix",
31444
+ "pnpm run format"
31445
+ ]
31446
+ }
31447
+ };
31448
+
31072
31449
  // src/internal/queryHelpers.ts
31073
31450
  function sortBeansInternal(beans, mode) {
31074
31451
  const sorted = [...beans];
@@ -31124,15 +31501,23 @@ async function handleQueryOperation(backend, params) {
31124
31501
  const { operation, mode, statuses, types, search, tags, writeToWorkspaceInstructions, includeClosed } = params;
31125
31502
  if (operation === "llm_context") {
31126
31503
  const graphqlSchema = typeof backend.graphqlSchema === "function" ? await backend.graphqlSchema() : "";
31127
- const instructionsPath = writeToWorkspaceInstructions && typeof backend.writeInstructions === "function" ? await backend.writeInstructions("") : null;
31504
+ let generatedInstructions = "";
31505
+ if (typeof backend.primeInstructions === "function") {
31506
+ try {
31507
+ generatedInstructions = await backend.primeInstructions();
31508
+ } catch {
31509
+ generatedInstructions = "";
31510
+ }
31511
+ }
31512
+ const instructionsPath = writeToWorkspaceInstructions && typeof backend.writeInstructions === "function" ? await backend.writeInstructions(generatedInstructions) : null;
31128
31513
  return {
31129
31514
  content: [
31130
31515
  {
31131
31516
  type: "text",
31132
- text: JSON.stringify({ graphqlSchema, generatedInstructions: "", instructionsPath }, null, 2)
31517
+ text: JSON.stringify({ graphqlSchema, generatedInstructions, instructionsPath }, null, 2)
31133
31518
  }
31134
31519
  ],
31135
- structuredContent: { graphqlSchema, generatedInstructions: "", instructionsPath }
31520
+ structuredContent: { graphqlSchema, generatedInstructions, instructionsPath }
31136
31521
  };
31137
31522
  }
31138
31523
  if (operation === "open_config") {
@@ -31178,6 +31563,31 @@ async function handleQueryOperation(backend, params) {
31178
31563
  structuredContent: { query: search, count: beans2.length, beans: beans2 }
31179
31564
  };
31180
31565
  }
31566
+ if (operation === "ready") {
31567
+ const allBeans = await backend.list();
31568
+ const byId = new Map(allBeans.map((bean) => [bean.id, bean]));
31569
+ const candidates = await backend.list({ status: normalizedStatuses, type: normalizedTypes, search });
31570
+ const readyBeans = candidates.filter((bean) => {
31571
+ if (bean.status !== "todo") {
31572
+ return false;
31573
+ }
31574
+ const blockedBy = bean.blockedByIds || [];
31575
+ if (blockedBy.length === 0) {
31576
+ return true;
31577
+ }
31578
+ return blockedBy.every((blockerId) => {
31579
+ const blocker = byId.get(blockerId);
31580
+ if (!blocker) {
31581
+ return false;
31582
+ }
31583
+ return blocker.status === "completed" || blocker.status === "scrapped";
31584
+ });
31585
+ });
31586
+ return {
31587
+ content: [{ type: "text", text: JSON.stringify({ count: readyBeans.length, beans: readyBeans }, null, 2) }],
31588
+ structuredContent: { count: readyBeans.length, beans: readyBeans }
31589
+ };
31590
+ }
31181
31591
  const beans = await backend.list({ status: normalizedStatuses, type: normalizedTypes, search });
31182
31592
  const sorted = sortBeansInternal(beans, mode ?? "status-priority-type-title");
31183
31593
  return {
@@ -31196,92 +31606,45 @@ var MAX_PATH_LENGTH = 1024;
31196
31606
 
31197
31607
  // src/server/BeansMcpServer.ts
31198
31608
  init_utils();
31199
-
31200
- // package.json
31201
- var package_default = {
31202
- name: "@selfagency/beans-mcp",
31203
- version: "0.1.4",
31204
- private: false,
31205
- description: "MCP (Model Context Protocol) server for Beans issue tracker",
31206
- author: {
31207
- name: "Daniel Sieradski",
31208
- email: "daniel@self.agency",
31209
- url: "https://self.agency"
31210
- },
31211
- homepage: "https://github.com/hmans/beans",
31212
- bugs: {
31213
- url: "https://github.com/selfagency/beans-mcp/issues"
31214
- },
31215
- repository: {
31216
- type: "git",
31217
- url: "git+https://github.com/selfagency/beans-mcp.git"
31218
- },
31219
- mcpName: "io.github.selfagency/beans-mcp",
31220
- keywords: [
31221
- "beans",
31222
- "mcp",
31223
- "model-context-protocol",
31224
- "issue-tracker",
31225
- "ai"
31226
- ],
31227
- license: "MIT",
31228
- type: "module",
31229
- exports: {
31230
- ".": {
31231
- types: "./dist/index.d.ts",
31232
- import: "./dist/index.js",
31233
- require: "./dist/index.cjs"
31609
+ var execFileAsync2 = promisify2(execFile2);
31610
+ var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
31611
+ function getSafeCliEnv(env) {
31612
+ const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
31613
+ const safeEnv = {};
31614
+ for (const key of whitelist) {
31615
+ if (env[key]) {
31616
+ safeEnv[key] = env[key];
31234
31617
  }
31235
- },
31236
- main: "./dist/index.cjs",
31237
- module: "./dist/index.js",
31238
- types: "./dist/index.d.ts",
31239
- bin: {
31240
- "beans-mcp": "dist/beans-mcp-server.cjs"
31241
- },
31242
- scripts: {
31243
- build: "tsup",
31244
- format: "oxfmt",
31245
- "lint:fix": "oxlint --fix",
31246
- lint: "oxlint",
31247
- postbuild: "node ./scripts/write-dist-package.js",
31248
- prepare: "husky",
31249
- release: "zx ./scripts/release.js",
31250
- "test:coverage": "vitest run --coverage",
31251
- "test:watch": "vitest",
31252
- test: "vitest run",
31253
- "type-check": "tsc --noEmit"
31254
- },
31255
- devDependencies: {
31256
- "@modelcontextprotocol/sdk": "^1.27.1",
31257
- "@octokit/rest": "^22.0.1",
31258
- "@types/node": "^20.19.0",
31259
- "@vitest/coverage-v8": "^4.0.18",
31260
- "@vitest/ui": "4.0.18",
31261
- husky: "^9.1.7",
31262
- "lint-staged": "^16.2.7",
31263
- ora: "^9.3.0",
31264
- oxfmt: "^0.35.0",
31265
- oxlint: "^1.50.0",
31266
- "oxlint-tsgolint": "^0.15.0",
31267
- tsup: "8.5.1",
31268
- typescript: "^5.9.3",
31269
- vitest: "4.0.18",
31270
- zod: "4.3.6",
31271
- zx: "^8.8.5"
31272
- },
31273
- engines: {
31274
- node: ">=18"
31275
- },
31276
- "lint-staged": {
31277
- "src/**/*.ts": [
31278
- "pnpm run lint:fix",
31279
- "pnpm run format"
31280
- ]
31281
31618
  }
31282
- };
31283
-
31284
- // src/server/BeansMcpServer.ts
31619
+ for (const key in env) {
31620
+ if (key.startsWith("BEANS_")) {
31621
+ safeEnv[key] = env[key];
31622
+ }
31623
+ }
31624
+ return safeEnv;
31625
+ }
31626
+ function extractVersionFromOutput(output) {
31627
+ const trimmed = output.trim();
31628
+ if (!trimmed) {
31629
+ return null;
31630
+ }
31631
+ const match = trimmed.match(/(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/);
31632
+ return match?.[1] ?? null;
31633
+ }
31634
+ async function detectBeansCliVersion(cliPath, workspaceRoot) {
31635
+ try {
31636
+ const { stdout, stderr } = await execFileAsync2(cliPath, ["version"], {
31637
+ cwd: workspaceRoot,
31638
+ env: getSafeCliEnv(process.env),
31639
+ maxBuffer: 1024 * 1024,
31640
+ timeout: 5e3
31641
+ });
31642
+ return extractVersionFromOutput(`${stdout}
31643
+ ${stderr}`);
31644
+ } catch {
31645
+ return null;
31646
+ }
31647
+ }
31285
31648
  async function getBeanById(backend, beanId) {
31286
31649
  try {
31287
31650
  const beans = await backend.list();
@@ -31301,7 +31664,40 @@ function initHandler(backend) {
31301
31664
  };
31302
31665
  }
31303
31666
  function viewHandler(backend) {
31304
- return async ({ beanId }) => makeTextAndStructured({ bean: await getBeanById(backend, beanId) });
31667
+ return async ({ beanId, beanIds }) => {
31668
+ const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
31669
+ if (ids.length === 0) {
31670
+ throw new Error("Either beanId or beanIds must be provided");
31671
+ }
31672
+ if (ids.length === 1) {
31673
+ const bean = await getBeanById(backend, ids[0]);
31674
+ return makeTextAndStructured({ bean });
31675
+ }
31676
+ const beans = await backend.list();
31677
+ const byId = new Map(beans.map((b) => [b.id, b]));
31678
+ const found = ids.map((id) => byId.get(id)).filter(Boolean);
31679
+ const missingBeanIds = ids.filter((id) => !byId.has(id));
31680
+ return makeTextAndStructured({
31681
+ beans: found,
31682
+ missingBeanIds,
31683
+ count: found.length,
31684
+ requestedCount: ids.length
31685
+ });
31686
+ };
31687
+ }
31688
+ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
31689
+ const detectedBeansVersion = await detector(cliPath, workspaceRoot);
31690
+ if (!detectedBeansVersion) {
31691
+ console.error(
31692
+ `[beans-mcp] warning: unable to determine Beans CLI version from \`${cliPath}\`; proceeding without version compatibility checks.`
31693
+ );
31694
+ return;
31695
+ }
31696
+ if (detectedBeansVersion !== PACKAGE_VERSION) {
31697
+ console.error(
31698
+ `[beans-mcp] warning: version mismatch detected (beans=${detectedBeansVersion}, beans-mcp=${PACKAGE_VERSION}); continuing startup.`
31699
+ );
31700
+ }
31305
31701
  }
31306
31702
  function createHandler(backend) {
31307
31703
  return async (input) => makeTextAndStructured({ bean: await backend.create(input) });
@@ -31337,17 +31733,82 @@ function updateHandler(backend) {
31337
31733
  clearParent: input.clearParent,
31338
31734
  blocking: input.blocking,
31339
31735
  blockedBy: input.blockedBy,
31340
- body: input.body
31736
+ body: input.body,
31737
+ bodyAppend: input.bodyAppend,
31738
+ bodyReplace: input.bodyReplace,
31739
+ ifMatch: input.ifMatch
31341
31740
  })
31342
31741
  });
31343
31742
  }
31344
31743
  function deleteHandler(backend) {
31345
- return async ({ beanId, force }) => {
31346
- const bean = await getBeanById(backend, beanId);
31347
- if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
31348
- throw new Error("Only draft and scrapped beans are deletable unless force=true");
31744
+ return async ({ beanId, beanIds, force }) => {
31745
+ const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
31746
+ if (ids.length === 0) {
31747
+ throw new Error("Either beanId or beanIds must be provided");
31349
31748
  }
31350
- return makeTextAndStructured(await backend.delete(beanId));
31749
+ if (ids.length === 1) {
31750
+ const bean = await getBeanById(backend, ids[0]);
31751
+ if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
31752
+ throw new Error("Only draft and scrapped beans are deletable unless force=true");
31753
+ }
31754
+ return makeTextAndStructured(await backend.delete(ids[0]));
31755
+ }
31756
+ const beans = await backend.list();
31757
+ const byId = new Map(beans.map((b) => [b.id, b]));
31758
+ const results = [];
31759
+ for (const id of ids) {
31760
+ const bean = byId.get(id);
31761
+ if (!bean) {
31762
+ results.push({ beanId: id, deleted: false, error: "Bean not found" });
31763
+ continue;
31764
+ }
31765
+ if (!force && bean.status !== "draft" && bean.status !== "scrapped") {
31766
+ results.push({
31767
+ beanId: id,
31768
+ deleted: false,
31769
+ error: "Only draft and scrapped beans are deletable unless force=true"
31770
+ });
31771
+ continue;
31772
+ }
31773
+ try {
31774
+ await backend.delete(id);
31775
+ results.push({ beanId: id, deleted: true });
31776
+ } catch (error48) {
31777
+ results.push({
31778
+ beanId: id,
31779
+ deleted: false,
31780
+ error: error48.message
31781
+ });
31782
+ }
31783
+ }
31784
+ return makeTextAndStructured({
31785
+ results,
31786
+ requestedCount: ids.length,
31787
+ deletedCount: results.filter((r) => r.deleted).length,
31788
+ failedCount: results.filter((r) => !r.deleted).length
31789
+ });
31790
+ };
31791
+ }
31792
+ function bulkCreateHandler(backend) {
31793
+ return async (input) => {
31794
+ const results = await backend.bulkCreate(input.beans, input.parent);
31795
+ return makeTextAndStructured({
31796
+ results,
31797
+ requestedCount: input.beans.length,
31798
+ successCount: results.filter((r) => r.bean).length,
31799
+ failedCount: results.filter((r) => r.error).length
31800
+ });
31801
+ };
31802
+ }
31803
+ function bulkUpdateHandler(backend) {
31804
+ return async (input) => {
31805
+ const results = await backend.bulkUpdate(input.beans, input.parent);
31806
+ return makeTextAndStructured({
31807
+ results,
31808
+ requestedCount: input.beans.length,
31809
+ successCount: results.filter((r) => r.bean).length,
31810
+ failedCount: results.filter((r) => r.error).length
31811
+ });
31351
31812
  };
31352
31813
  }
31353
31814
  function queryHandler(backend) {
@@ -31408,7 +31869,12 @@ function registerTools(server, backend) {
31408
31869
  {
31409
31870
  title: "View Bean",
31410
31871
  description: "Fetch full bean details by ID.",
31411
- inputSchema: external_exports3.object({ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH) }),
31872
+ inputSchema: external_exports3.object({
31873
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31874
+ beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
31875
+ }).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
31876
+ message: "Either beanId or beanIds must be provided"
31877
+ }),
31412
31878
  annotations: {
31413
31879
  readOnlyHint: true,
31414
31880
  destructiveHint: false,
@@ -31428,7 +31894,8 @@ function registerTools(server, backend) {
31428
31894
  type: external_exports3.string().min(1).max(MAX_METADATA_LENGTH),
31429
31895
  status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31430
31896
  priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
31431
- description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
31897
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Body markdown content"),
31898
+ description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Deprecated alias for body"),
31432
31899
  parent: external_exports3.string().max(MAX_ID_LENGTH).optional()
31433
31900
  }),
31434
31901
  annotations: {
@@ -31497,8 +31964,21 @@ function registerTools(server, backend) {
31497
31964
  clearParent: external_exports3.boolean().optional(),
31498
31965
  blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
31499
31966
  blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
31500
- body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional()
31501
- }),
31967
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
31968
+ bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
31969
+ bodyReplace: external_exports3.array(
31970
+ external_exports3.object({
31971
+ old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH),
31972
+ new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH)
31973
+ })
31974
+ ).optional(),
31975
+ ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
31976
+ }).refine(
31977
+ (input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
31978
+ {
31979
+ message: "body cannot be combined with bodyAppend/bodyReplace"
31980
+ }
31981
+ ),
31502
31982
  annotations: {
31503
31983
  readOnlyHint: false,
31504
31984
  destructiveHint: false,
@@ -31514,8 +31994,11 @@ function registerTools(server, backend) {
31514
31994
  title: "Delete Bean",
31515
31995
  description: "Delete a bean (intended for draft/scrapped beans).",
31516
31996
  inputSchema: external_exports3.object({
31517
- beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
31997
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
31998
+ beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
31518
31999
  force: external_exports3.boolean().default(false)
32000
+ }).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
32001
+ message: "Either beanId or beanIds must be provided"
31519
32002
  }),
31520
32003
  annotations: {
31521
32004
  readOnlyHint: false,
@@ -31526,13 +32009,75 @@ function registerTools(server, backend) {
31526
32009
  },
31527
32010
  deleteHandler(backend)
31528
32011
  );
32012
+ const beanCreateItemSchema = external_exports3.object({
32013
+ title: external_exports3.string().min(1).max(MAX_TITLE_LENGTH),
32014
+ type: external_exports3.string().min(1).max(MAX_METADATA_LENGTH),
32015
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32016
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32017
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Body markdown content"),
32018
+ description: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional().describe("Deprecated alias for body"),
32019
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Override the top-level parent for this item")
32020
+ });
32021
+ server.registerTool(
32022
+ "beans_bulk_create",
32023
+ {
32024
+ title: "Bulk Create Beans",
32025
+ description: "Create multiple beans in one call. Optionally assign all of them (or a subset) to a shared parent.",
32026
+ inputSchema: external_exports3.object({
32027
+ beans: external_exports3.array(beanCreateItemSchema).min(1),
32028
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Default parent ID applied to any bean that does not specify its own parent")
32029
+ }),
32030
+ annotations: {
32031
+ readOnlyHint: false,
32032
+ destructiveHint: false,
32033
+ idempotentHint: false,
32034
+ openWorldHint: false
32035
+ }
32036
+ },
32037
+ bulkCreateHandler(backend)
32038
+ );
32039
+ const beanUpdateItemSchema = external_exports3.object({
32040
+ beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
32041
+ status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32042
+ type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32043
+ priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
32044
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Override the top-level parent for this item"),
32045
+ clearParent: external_exports3.boolean().optional(),
32046
+ blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
32047
+ blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
32048
+ body: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32049
+ bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
32050
+ bodyReplace: external_exports3.array(external_exports3.object({ old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH), new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH) })).optional(),
32051
+ ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
32052
+ }).refine(
32053
+ (input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
32054
+ { message: "body cannot be combined with bodyAppend/bodyReplace" }
32055
+ );
32056
+ server.registerTool(
32057
+ "beans_bulk_update",
32058
+ {
32059
+ title: "Bulk Update Beans",
32060
+ description: "Update multiple beans in one call. Optionally assign all of them (or a subset) to a shared parent.",
32061
+ inputSchema: external_exports3.object({
32062
+ beans: external_exports3.array(beanUpdateItemSchema).min(1),
32063
+ parent: external_exports3.string().max(MAX_ID_LENGTH).optional().describe("Default parent ID applied to any bean that does not specify its own parent")
32064
+ }),
32065
+ annotations: {
32066
+ readOnlyHint: false,
32067
+ destructiveHint: false,
32068
+ idempotentHint: false,
32069
+ openWorldHint: false
32070
+ }
32071
+ },
32072
+ bulkUpdateHandler(backend)
32073
+ );
31529
32074
  server.registerTool(
31530
32075
  "beans_query",
31531
32076
  {
31532
32077
  title: "Query Beans",
31533
32078
  description: "Unified query tool for refresh, filter, search, and sort operations.",
31534
32079
  inputSchema: external_exports3.object({
31535
- operation: external_exports3.enum(["refresh", "filter", "search", "sort", "llm_context", "open_config"]).default("refresh"),
32080
+ operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
31536
32081
  mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
31537
32082
  statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
31538
32083
  types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
@@ -31611,9 +32156,21 @@ var MutableBackend = class {
31611
32156
  delete(id) {
31612
32157
  return this.inner.delete(id);
31613
32158
  }
32159
+ bulkCreate(beans, defaultParent) {
32160
+ return this.inner.bulkCreate(beans, defaultParent);
32161
+ }
32162
+ bulkUpdate(beans, defaultParent) {
32163
+ return this.inner.bulkUpdate(beans, defaultParent);
32164
+ }
31614
32165
  openConfig() {
31615
32166
  return this.inner.openConfig();
31616
32167
  }
32168
+ primeInstructions() {
32169
+ return this.inner.primeInstructions?.() ?? Promise.resolve("");
32170
+ }
32171
+ writeInstructions(instructions) {
32172
+ return this.inner.writeInstructions?.(instructions) ?? Promise.resolve(null);
32173
+ }
31617
32174
  graphqlSchema() {
31618
32175
  return this.inner.graphqlSchema();
31619
32176
  }
@@ -31651,7 +32208,7 @@ async function createBeansMcpServer(opts) {
31651
32208
  const backend = opts.backend || new BeansCliBackend2(opts.workspaceRoot, opts.cliPath || "beans", opts.logDir);
31652
32209
  const server = new McpServer({
31653
32210
  name: opts.name || "beans-mcp-server",
31654
- version: opts.version || "0.1.0"
32211
+ version: opts.version || PACKAGE_VERSION
31655
32212
  });
31656
32213
  registerTools(server, backend);
31657
32214
  return { server, backend };
@@ -31723,17 +32280,17 @@ function parseCliArgs(argv) {
31723
32280
  }
31724
32281
  return { workspaceRoot, workspaceExplicit, cliPath, port, logDir };
31725
32282
  }
31726
- async function startBeansMcpServer(argv, _resolveRoots) {
32283
+ async function startBeansMcpServer(argv, _resolveRoots, _detectBeansVersion) {
31727
32284
  const { BeansCliBackend: BeansCliBackend2 } = await Promise.resolve().then(() => (init_backend(), backend_exports));
31728
32285
  const { StdioServerTransport: StdioServerTransport2 } = await Promise.resolve().then(() => (init_stdio2(), stdio_exports));
31729
32286
  const { workspaceRoot, workspaceExplicit, cliPath, port, logDir } = parseCliArgs(argv);
32287
+ let effectiveWorkspaceRoot = workspaceRoot;
31730
32288
  process.env.BEANS_VSCODE_MCP_PORT = String(port);
31731
32289
  process.env.BEANS_MCP_PORT = String(port);
31732
32290
  try {
31733
- const version2 = package_default.version ?? "0.0.0-dev";
31734
32291
  const workspaceLabel = workspaceExplicit ? workspaceRoot : "(auto from roots)";
31735
32292
  console.error(
31736
- `[beans-mcp] v${version2} starting (port=${port}, workspace=${workspaceLabel}, cli=${cliPath}, logDir=${logDir})`
32293
+ `[beans-mcp] v${PACKAGE_VERSION} starting (port=${port}, workspace=${workspaceLabel}, cli=${cliPath}, logDir=${logDir})`
31737
32294
  );
31738
32295
  } catch {
31739
32296
  }
@@ -31750,13 +32307,17 @@ async function startBeansMcpServer(argv, _resolveRoots) {
31750
32307
  const resolver = _resolveRoots ?? resolveWorkspaceFromRoots;
31751
32308
  const rootPath = await resolver(server);
31752
32309
  if (rootPath) {
31753
- mutable.setInner(new BeansCliBackend2(rootPath, cliPath));
32310
+ mutable.setInner(new BeansCliBackend2(rootPath, cliPath, logDir));
32311
+ effectiveWorkspaceRoot = rootPath;
31754
32312
  try {
31755
32313
  console.error(`[beans-mcp] workspace resolved from roots: ${rootPath}`);
31756
32314
  } catch {
31757
32315
  }
31758
32316
  }
31759
32317
  }
32318
+ const beansVersionDetector = _detectBeansVersion ?? detectBeansCliVersion;
32319
+ void checkVersionCompatibility(cliPath, effectiveWorkspaceRoot, beansVersionDetector).catch(() => {
32320
+ });
31760
32321
  }
31761
32322
 
31762
32323
  // src/index.ts