@selfagency/beans-mcp 0.5.0 → 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/README.md +180 -30
- package/beans-mcp-server.cjs +560 -93
- package/index.cjs +562 -93
- package/index.d.ts +55 -2
- package/index.js +562 -94
- package/package.json +4 -4
package/index.cjs
CHANGED
|
@@ -22905,6 +22905,26 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22905
22905
|
});
|
|
22906
22906
|
return { initialized: true };
|
|
22907
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
|
+
}
|
|
22908
22928
|
async list(options) {
|
|
22909
22929
|
const filter = {};
|
|
22910
22930
|
if (options?.status && options.status.length > 0) {
|
|
@@ -23059,32 +23079,39 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23059
23079
|
return { deleted: true, beanId };
|
|
23060
23080
|
}
|
|
23061
23081
|
async bulkCreate(beans, defaultParent) {
|
|
23062
|
-
const
|
|
23063
|
-
|
|
23064
|
-
|
|
23065
|
-
const bean = await this.create({
|
|
23082
|
+
const settled = await Promise.allSettled(
|
|
23083
|
+
beans.map(
|
|
23084
|
+
async (item) => this.create({
|
|
23066
23085
|
...item,
|
|
23067
23086
|
parent: item.parent ?? defaultParent
|
|
23068
|
-
})
|
|
23069
|
-
|
|
23070
|
-
|
|
23071
|
-
|
|
23072
|
-
}
|
|
23073
|
-
|
|
23074
|
-
return results;
|
|
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
|
+
);
|
|
23075
23093
|
}
|
|
23076
23094
|
async bulkUpdate(beans, defaultParent) {
|
|
23077
|
-
const
|
|
23078
|
-
|
|
23079
|
-
try {
|
|
23095
|
+
const settled = await Promise.allSettled(
|
|
23096
|
+
beans.map(async ({ beanId, ...updates }) => {
|
|
23080
23097
|
const resolvedParent = updates.parent ?? (updates.clearParent ? void 0 : defaultParent);
|
|
23081
23098
|
const bean = await this.update(beanId, { ...updates, parent: resolvedParent });
|
|
23082
|
-
|
|
23083
|
-
}
|
|
23084
|
-
|
|
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" };
|
|
23085
23106
|
}
|
|
23086
|
-
|
|
23087
|
-
|
|
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
|
+
});
|
|
23088
23115
|
}
|
|
23089
23116
|
async openConfig() {
|
|
23090
23117
|
const configPath = (0, import_node_path2.join)(this.workspaceRoot, ".beans.yml");
|
|
@@ -23119,9 +23146,12 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23119
23146
|
const outputPath = (0, import_node_path2.resolve)(
|
|
23120
23147
|
process.env.BEANS_VSCODE_OUTPUT_LOG || (0, import_node_path2.join)(this.workspaceRoot, ".vscode", "logs", "beans-output.log")
|
|
23121
23148
|
);
|
|
23122
|
-
const
|
|
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);
|
|
23123
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;
|
|
23124
|
-
const
|
|
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;
|
|
23125
23155
|
if (!isWithinWorkspace && !isWithinVscodeLogDir) {
|
|
23126
23156
|
throw new Error("Output log path must stay within the workspace or VS Code log directory");
|
|
23127
23157
|
}
|
|
@@ -23200,6 +23230,105 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23200
23230
|
escapeForYamlDoubleQuoted(value) {
|
|
23201
23231
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
23202
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
|
+
}
|
|
23203
23332
|
/**
|
|
23204
23333
|
* Normalise a raw YAML title value to a double-quoted scalar.
|
|
23205
23334
|
* Handles: empty, already double-quoted, single-quoted (unescaping `''`),
|
|
@@ -23270,14 +23399,66 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23270
23399
|
await (0, import_promises.writeFile)(absolutePath, fixed, "utf8");
|
|
23271
23400
|
return { path: absolutePath, bytes: Buffer.byteLength(fixed, "utf8") };
|
|
23272
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
|
+
};
|
|
23445
|
+
}
|
|
23273
23446
|
async createBeanFile(relativePath, content, options) {
|
|
23274
23447
|
const absolutePath = this.resolveBeanFilePath(relativePath);
|
|
23275
23448
|
const fixed = this.quoteFrontmatterTitles(content);
|
|
23276
23449
|
await (0, import_promises.mkdir)((0, import_node_path2.dirname)(absolutePath), { recursive: true });
|
|
23277
|
-
|
|
23278
|
-
|
|
23279
|
-
|
|
23280
|
-
|
|
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
|
+
}
|
|
23281
23462
|
return {
|
|
23282
23463
|
path: absolutePath,
|
|
23283
23464
|
bytes: Buffer.byteLength(fixed, "utf8"),
|
|
@@ -23408,6 +23589,7 @@ __export(src_exports, {
|
|
|
23408
23589
|
DEFAULT_MCP_PORT: () => DEFAULT_MCP_PORT,
|
|
23409
23590
|
MAX_ID_LENGTH: () => MAX_ID_LENGTH,
|
|
23410
23591
|
MAX_METADATA_LENGTH: () => MAX_METADATA_LENGTH,
|
|
23592
|
+
MAX_PATH_LENGTH: () => MAX_PATH_LENGTH,
|
|
23411
23593
|
MAX_TITLE_LENGTH: () => MAX_TITLE_LENGTH,
|
|
23412
23594
|
createBeansMcpServer: () => createBeansMcpServer,
|
|
23413
23595
|
isPathWithinRoot: () => isPathWithinRoot,
|
|
@@ -31385,32 +31567,43 @@ var import_node_util2 = require("util");
|
|
|
31385
31567
|
// package.json
|
|
31386
31568
|
var package_default = {
|
|
31387
31569
|
name: "@selfagency/beans-mcp",
|
|
31388
|
-
version: "0.
|
|
31570
|
+
version: "0.6.0",
|
|
31389
31571
|
private: false,
|
|
31390
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",
|
|
31391
31585
|
author: {
|
|
31392
31586
|
name: "Daniel Sieradski",
|
|
31393
31587
|
email: "daniel@self.agency",
|
|
31394
31588
|
url: "https://self.agency"
|
|
31395
31589
|
},
|
|
31396
|
-
homepage: "https://github.com/selfagency/beans-mcp",
|
|
31397
|
-
bugs: {
|
|
31398
|
-
url: "https://github.com/selfagency/beans-mcp/issues"
|
|
31399
|
-
},
|
|
31400
31590
|
repository: {
|
|
31401
31591
|
type: "git",
|
|
31402
31592
|
url: "git+https://github.com/selfagency/beans-mcp.git"
|
|
31403
31593
|
},
|
|
31404
|
-
|
|
31405
|
-
|
|
31406
|
-
|
|
31407
|
-
|
|
31408
|
-
"
|
|
31409
|
-
"
|
|
31410
|
-
"
|
|
31594
|
+
bin: {
|
|
31595
|
+
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31596
|
+
},
|
|
31597
|
+
files: [
|
|
31598
|
+
"dist",
|
|
31599
|
+
"skills",
|
|
31600
|
+
"README.md",
|
|
31601
|
+
"LICENSE.txt"
|
|
31411
31602
|
],
|
|
31412
|
-
license: "MIT",
|
|
31413
31603
|
type: "module",
|
|
31604
|
+
main: "./dist/index.cjs",
|
|
31605
|
+
module: "./dist/index.js",
|
|
31606
|
+
types: "./dist/index.d.ts",
|
|
31414
31607
|
exports: {
|
|
31415
31608
|
".": {
|
|
31416
31609
|
types: "./dist/index.d.ts",
|
|
@@ -31418,14 +31611,11 @@ var package_default = {
|
|
|
31418
31611
|
require: "./dist/index.cjs"
|
|
31419
31612
|
}
|
|
31420
31613
|
},
|
|
31421
|
-
main: "./dist/index.cjs",
|
|
31422
|
-
module: "./dist/index.js",
|
|
31423
|
-
types: "./dist/index.d.ts",
|
|
31424
|
-
bin: {
|
|
31425
|
-
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31426
|
-
},
|
|
31427
31614
|
scripts: {
|
|
31428
31615
|
build: "tsup",
|
|
31616
|
+
"docs:dev": "vitepress dev docs",
|
|
31617
|
+
"docs:build": "vitepress build docs",
|
|
31618
|
+
"docs:preview": "vitepress preview docs",
|
|
31429
31619
|
format: "oxfmt",
|
|
31430
31620
|
"lint:fix": "oxlint --fix",
|
|
31431
31621
|
lint: "oxlint",
|
|
@@ -31440,30 +31630,32 @@ var package_default = {
|
|
|
31440
31630
|
devDependencies: {
|
|
31441
31631
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
31442
31632
|
"@octokit/rest": "^22.0.1",
|
|
31443
|
-
"@types/node": "25.
|
|
31444
|
-
"@vitest/coverage-v8": "^4.1.
|
|
31445
|
-
"@vitest/ui": "4.1.
|
|
31633
|
+
"@types/node": "25.6.0",
|
|
31634
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
31635
|
+
"@vitest/ui": "4.1.4",
|
|
31446
31636
|
husky: "^9.1.7",
|
|
31447
31637
|
"lint-staged": "^16.4.0",
|
|
31448
31638
|
ora: "^9.3.0",
|
|
31449
|
-
oxfmt: "^0.
|
|
31450
|
-
oxlint: "^1.
|
|
31451
|
-
"oxlint-tsgolint": "^0.
|
|
31639
|
+
oxfmt: "^0.45.0",
|
|
31640
|
+
oxlint: "^1.60.0",
|
|
31641
|
+
"oxlint-tsgolint": "^0.21.1",
|
|
31452
31642
|
tsup: "8.5.1",
|
|
31453
|
-
typescript: "6.0.
|
|
31454
|
-
|
|
31643
|
+
typescript: "6.0.3",
|
|
31644
|
+
vitepress: "^1.6.4",
|
|
31645
|
+
vitest: "4.1.4",
|
|
31455
31646
|
zod: "4.3.6",
|
|
31456
31647
|
zx: "^8.8.5"
|
|
31457
31648
|
},
|
|
31458
|
-
engines: {
|
|
31459
|
-
node: ">=18"
|
|
31460
|
-
},
|
|
31461
31649
|
"lint-staged": {
|
|
31462
31650
|
"src/**/*.ts": [
|
|
31463
31651
|
"pnpm run lint:fix",
|
|
31464
31652
|
"pnpm run format"
|
|
31465
31653
|
]
|
|
31466
|
-
}
|
|
31654
|
+
},
|
|
31655
|
+
engines: {
|
|
31656
|
+
node: ">=18"
|
|
31657
|
+
},
|
|
31658
|
+
mcpName: "io.github.selfagency/beans-mcp"
|
|
31467
31659
|
};
|
|
31468
31660
|
|
|
31469
31661
|
// src/internal/queryHelpers.ts
|
|
@@ -31628,6 +31820,8 @@ var MAX_PATH_LENGTH = 1024;
|
|
|
31628
31820
|
init_utils();
|
|
31629
31821
|
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
31630
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`?";
|
|
31631
31825
|
function getSafeCliEnv(env) {
|
|
31632
31826
|
const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
|
|
31633
31827
|
const safeEnv = {};
|
|
@@ -31648,7 +31842,8 @@ function extractVersionFromOutput(output) {
|
|
|
31648
31842
|
if (!trimmed) {
|
|
31649
31843
|
return null;
|
|
31650
31844
|
}
|
|
31651
|
-
const
|
|
31845
|
+
const versionRegex = /(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
|
|
31846
|
+
const match = versionRegex.exec(trimmed);
|
|
31652
31847
|
return match?.[1] ?? null;
|
|
31653
31848
|
}
|
|
31654
31849
|
async function detectBeansCliVersion(cliPath, workspaceRoot) {
|
|
@@ -31665,10 +31860,10 @@ ${stderr}`);
|
|
|
31665
31860
|
return null;
|
|
31666
31861
|
}
|
|
31667
31862
|
}
|
|
31668
|
-
async function getBeanById(backend, beanId) {
|
|
31863
|
+
async function getBeanById(backend, beanId, beans) {
|
|
31669
31864
|
try {
|
|
31670
|
-
const
|
|
31671
|
-
const found =
|
|
31865
|
+
const allBeans = beans ?? await backend.list();
|
|
31866
|
+
const found = allBeans.find((b) => b.id === beanId);
|
|
31672
31867
|
if (!found) {
|
|
31673
31868
|
throw new Error(`Bean not found: ${beanId}`);
|
|
31674
31869
|
}
|
|
@@ -31677,12 +31872,111 @@ async function getBeanById(backend, beanId) {
|
|
|
31677
31872
|
throw new Error(`Failed to fetch bean ${beanId}: ${error48.message}`);
|
|
31678
31873
|
}
|
|
31679
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
|
+
}
|
|
31680
31966
|
function initHandler(backend) {
|
|
31681
31967
|
return async ({ prefix }) => {
|
|
31682
31968
|
const result = await backend.init(prefix);
|
|
31683
31969
|
return makeTextAndStructured(result);
|
|
31684
31970
|
};
|
|
31685
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
|
+
}
|
|
31686
31980
|
function viewHandler(backend) {
|
|
31687
31981
|
return async ({ beanId, beanIds }) => {
|
|
31688
31982
|
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
@@ -31720,7 +32014,14 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
|
|
|
31720
32014
|
}
|
|
31721
32015
|
}
|
|
31722
32016
|
function createHandler(backend) {
|
|
31723
|
-
return async (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
|
+
};
|
|
31724
32025
|
}
|
|
31725
32026
|
function editHandler(backend) {
|
|
31726
32027
|
return async ({
|
|
@@ -31734,18 +32035,30 @@ function reopenHandler(backend) {
|
|
|
31734
32035
|
requiredCurrentStatus,
|
|
31735
32036
|
targetStatus
|
|
31736
32037
|
}) => {
|
|
31737
|
-
const
|
|
31738
|
-
|
|
31739
|
-
|
|
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
|
+
});
|
|
31740
32055
|
}
|
|
31741
|
-
|
|
31742
|
-
bean: await backend.update(beanId, { status: targetStatus })
|
|
31743
|
-
});
|
|
32056
|
+
throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
|
|
31744
32057
|
};
|
|
31745
32058
|
}
|
|
31746
32059
|
function updateHandler(backend) {
|
|
31747
|
-
return async (input) =>
|
|
31748
|
-
|
|
32060
|
+
return async (input) => {
|
|
32061
|
+
const updatedBean = await backend.update(input.beanId, {
|
|
31749
32062
|
status: input.status,
|
|
31750
32063
|
type: input.type,
|
|
31751
32064
|
priority: input.priority,
|
|
@@ -31757,8 +32070,37 @@ function updateHandler(backend) {
|
|
|
31757
32070
|
bodyAppend: input.bodyAppend,
|
|
31758
32071
|
bodyReplace: input.bodyReplace,
|
|
31759
32072
|
ifMatch: input.ifMatch
|
|
31760
|
-
})
|
|
31761
|
-
|
|
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
|
+
};
|
|
31762
32104
|
}
|
|
31763
32105
|
function deleteHandler(backend) {
|
|
31764
32106
|
return async ({ beanId, beanIds, force }) => {
|
|
@@ -31812,11 +32154,17 @@ function deleteHandler(backend) {
|
|
|
31812
32154
|
function bulkCreateHandler(backend) {
|
|
31813
32155
|
return async (input) => {
|
|
31814
32156
|
const results = await backend.bulkCreate(input.beans, input.parent);
|
|
32157
|
+
const deprecatedDescriptionCount = input.beans.filter((bean) => bean.description !== void 0).length;
|
|
31815
32158
|
return makeTextAndStructured({
|
|
31816
32159
|
results,
|
|
31817
32160
|
requestedCount: input.beans.length,
|
|
31818
32161
|
successCount: results.filter((r) => r.bean).length,
|
|
31819
|
-
failedCount: results.filter((r) => r.error).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
|
+
} : {}
|
|
31820
32168
|
});
|
|
31821
32169
|
};
|
|
31822
32170
|
}
|
|
@@ -31832,14 +32180,24 @@ function bulkUpdateHandler(backend) {
|
|
|
31832
32180
|
};
|
|
31833
32181
|
}
|
|
31834
32182
|
function queryHandler(backend) {
|
|
31835
|
-
return async (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
|
+
};
|
|
31836
32193
|
}
|
|
31837
32194
|
function beanFileHandler(backend) {
|
|
31838
32195
|
return async ({
|
|
31839
32196
|
operation,
|
|
31840
32197
|
path,
|
|
31841
32198
|
content,
|
|
31842
|
-
overwrite
|
|
32199
|
+
overwrite,
|
|
32200
|
+
fields
|
|
31843
32201
|
}) => {
|
|
31844
32202
|
if (operation === "read") {
|
|
31845
32203
|
return makeTextAndStructured(await backend.readBeanFile(path));
|
|
@@ -31850,6 +32208,9 @@ function beanFileHandler(backend) {
|
|
|
31850
32208
|
if (operation === "create") {
|
|
31851
32209
|
return makeTextAndStructured(await backend.createBeanFile(path, content || "", { overwrite }));
|
|
31852
32210
|
}
|
|
32211
|
+
if (operation === "update_frontmatter") {
|
|
32212
|
+
return makeTextAndStructured(await backend.updateBeanFrontmatter(path, fields || {}));
|
|
32213
|
+
}
|
|
31853
32214
|
if (operation === "delete") {
|
|
31854
32215
|
return makeTextAndStructured(await backend.deleteBeanFile(path));
|
|
31855
32216
|
}
|
|
@@ -31884,6 +32245,21 @@ function registerTools(server, backend) {
|
|
|
31884
32245
|
},
|
|
31885
32246
|
initHandler(backend)
|
|
31886
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
|
+
);
|
|
31887
32263
|
server.registerTool(
|
|
31888
32264
|
"beans_view",
|
|
31889
32265
|
{
|
|
@@ -31893,7 +32269,7 @@ function registerTools(server, backend) {
|
|
|
31893
32269
|
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31894
32270
|
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
|
|
31895
32271
|
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31896
|
-
message:
|
|
32272
|
+
message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
|
|
31897
32273
|
}),
|
|
31898
32274
|
annotations: {
|
|
31899
32275
|
readOnlyHint: true,
|
|
@@ -31933,7 +32309,7 @@ function registerTools(server, backend) {
|
|
|
31933
32309
|
title: "Edit Bean Metadata",
|
|
31934
32310
|
description: "Update bean metadata fields (status/type/priority/parent/blocking).",
|
|
31935
32311
|
inputSchema: external_exports3.object({
|
|
31936
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32312
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31937
32313
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31938
32314
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31939
32315
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -31941,7 +32317,11 @@ function registerTools(server, backend) {
|
|
|
31941
32317
|
clearParent: external_exports3.boolean().optional(),
|
|
31942
32318
|
blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31943
32319
|
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional()
|
|
31944
|
-
}),
|
|
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 })),
|
|
31945
32325
|
annotations: {
|
|
31946
32326
|
readOnlyHint: false,
|
|
31947
32327
|
destructiveHint: false,
|
|
@@ -31957,10 +32337,14 @@ function registerTools(server, backend) {
|
|
|
31957
32337
|
title: "Reopen Bean",
|
|
31958
32338
|
description: "Reopen a completed or scrapped bean into a non-closed status.",
|
|
31959
32339
|
inputSchema: external_exports3.object({
|
|
31960
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32340
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31961
32341
|
requiredCurrentStatus: external_exports3.enum(["completed", "scrapped"]),
|
|
31962
32342
|
targetStatus: external_exports3.string().max(MAX_METADATA_LENGTH).default("todo")
|
|
31963
|
-
}),
|
|
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 })),
|
|
31964
32348
|
annotations: {
|
|
31965
32349
|
readOnlyHint: false,
|
|
31966
32350
|
destructiveHint: false,
|
|
@@ -31976,7 +32360,7 @@ function registerTools(server, backend) {
|
|
|
31976
32360
|
title: "Update Bean",
|
|
31977
32361
|
description: "Update bean metadata fields (status/type/priority/parent/blocking). Consolidated replacement for per-field update tools.",
|
|
31978
32362
|
inputSchema: external_exports3.object({
|
|
31979
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32363
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31980
32364
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31981
32365
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31982
32366
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -31993,12 +32377,16 @@ function registerTools(server, backend) {
|
|
|
31993
32377
|
})
|
|
31994
32378
|
).optional(),
|
|
31995
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
|
+
}
|
|
31996
32384
|
}).refine(
|
|
31997
32385
|
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
31998
32386
|
{
|
|
31999
32387
|
message: "body cannot be combined with bodyAppend/bodyReplace"
|
|
32000
32388
|
}
|
|
32001
|
-
),
|
|
32389
|
+
).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
32002
32390
|
annotations: {
|
|
32003
32391
|
readOnlyHint: false,
|
|
32004
32392
|
destructiveHint: false,
|
|
@@ -32018,7 +32406,7 @@ function registerTools(server, backend) {
|
|
|
32018
32406
|
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
|
|
32019
32407
|
force: external_exports3.boolean().default(false)
|
|
32020
32408
|
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
32021
|
-
message:
|
|
32409
|
+
message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
|
|
32022
32410
|
}),
|
|
32023
32411
|
annotations: {
|
|
32024
32412
|
readOnlyHint: false,
|
|
@@ -32057,7 +32445,7 @@ function registerTools(server, backend) {
|
|
|
32057
32445
|
bulkCreateHandler(backend)
|
|
32058
32446
|
);
|
|
32059
32447
|
const beanUpdateItemSchema = external_exports3.object({
|
|
32060
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32448
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
32061
32449
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32062
32450
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32063
32451
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -32069,10 +32457,14 @@ function registerTools(server, backend) {
|
|
|
32069
32457
|
bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32070
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(),
|
|
32071
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
|
+
}
|
|
32072
32464
|
}).refine(
|
|
32073
32465
|
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
32074
32466
|
{ message: "body cannot be combined with bodyAppend/bodyReplace" }
|
|
32075
|
-
);
|
|
32467
|
+
).transform((input) => ({ ...input, beanId: input.beanId }));
|
|
32076
32468
|
server.registerTool(
|
|
32077
32469
|
"beans_bulk_update",
|
|
32078
32470
|
{
|
|
@@ -32091,25 +32483,56 @@ function registerTools(server, backend) {
|
|
|
32091
32483
|
},
|
|
32092
32484
|
bulkUpdateHandler(backend)
|
|
32093
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
|
+
);
|
|
32094
32507
|
server.registerTool(
|
|
32095
32508
|
"beans_query",
|
|
32096
32509
|
{
|
|
32097
32510
|
title: "Query Beans",
|
|
32098
32511
|
description: "Unified query tool for refresh, filter, search, and sort operations.",
|
|
32099
32512
|
inputSchema: external_exports3.object({
|
|
32100
|
-
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"),
|
|
32101
32514
|
mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
|
|
32102
32515
|
statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32103
32516
|
types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32104
32517
|
search: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
|
|
32105
32518
|
includeClosed: external_exports3.boolean().optional(),
|
|
32106
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(),
|
|
32107
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
|
+
}
|
|
32108
32531
|
}),
|
|
32109
32532
|
annotations: {
|
|
32110
|
-
readOnlyHint:
|
|
32533
|
+
readOnlyHint: false,
|
|
32111
32534
|
destructiveHint: false,
|
|
32112
|
-
idempotentHint:
|
|
32535
|
+
idempotentHint: false,
|
|
32113
32536
|
openWorldHint: false
|
|
32114
32537
|
}
|
|
32115
32538
|
},
|
|
@@ -32121,10 +32544,33 @@ function registerTools(server, backend) {
|
|
|
32121
32544
|
title: "Bean File Operations",
|
|
32122
32545
|
description: "Read, create, edit, or delete files under .beans (operation param).",
|
|
32123
32546
|
inputSchema: external_exports3.object({
|
|
32124
|
-
operation: external_exports3.enum(["read", "edit", "create", "delete"]),
|
|
32547
|
+
operation: external_exports3.enum(["read", "edit", "create", "delete", "update_frontmatter"]),
|
|
32125
32548
|
path: external_exports3.string().min(1).max(MAX_PATH_LENGTH),
|
|
32126
32549
|
content: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32127
|
-
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
|
+
}
|
|
32128
32574
|
}),
|
|
32129
32575
|
annotations: {
|
|
32130
32576
|
readOnlyHint: false,
|
|
@@ -32164,6 +32610,18 @@ var MutableBackend = class {
|
|
|
32164
32610
|
init(prefix) {
|
|
32165
32611
|
return this.inner.init(prefix);
|
|
32166
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
|
+
}
|
|
32167
32625
|
list(opts) {
|
|
32168
32626
|
return this.inner.list(opts);
|
|
32169
32627
|
}
|
|
@@ -32203,6 +32661,9 @@ var MutableBackend = class {
|
|
|
32203
32661
|
editBeanFile(path, content) {
|
|
32204
32662
|
return this.inner.editBeanFile(path, content);
|
|
32205
32663
|
}
|
|
32664
|
+
updateBeanFrontmatter(path, updates) {
|
|
32665
|
+
return this.inner.updateBeanFrontmatter(path, updates);
|
|
32666
|
+
}
|
|
32206
32667
|
createBeanFile(path, content, opts) {
|
|
32207
32668
|
return this.inner.createBeanFile(path, content, opts);
|
|
32208
32669
|
}
|
|
@@ -32269,6 +32730,16 @@ function parseCliArgs(argv) {
|
|
|
32269
32730
|
const envPort = Number.parseInt(process.env.BEANS_VSCODE_MCP_PORT || process.env.BEANS_MCP_PORT || "", 10);
|
|
32270
32731
|
let port = Number.isInteger(envPort) && envPort > 0 ? envPort : DEFAULT_MCP_PORT;
|
|
32271
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
|
+
};
|
|
32272
32743
|
for (let i = 0; i < argv.length; i += 1) {
|
|
32273
32744
|
const arg = argv[i];
|
|
32274
32745
|
if ((arg === "--workspace" || arg === "--workspace-root") && argv[i + 1]) {
|
|
@@ -32282,10 +32753,7 @@ function parseCliArgs(argv) {
|
|
|
32282
32753
|
}
|
|
32283
32754
|
i += 1;
|
|
32284
32755
|
} else if (arg === "--port" && argv[i + 1]) {
|
|
32285
|
-
|
|
32286
|
-
if (Number.isInteger(parsedPort) && parsedPort > 0) {
|
|
32287
|
-
port = parsedPort;
|
|
32288
|
-
}
|
|
32756
|
+
port = parseStrictPositiveInt(argv[i + 1], "--port");
|
|
32289
32757
|
i += 1;
|
|
32290
32758
|
} else if (arg === "--log-dir" && argv[i + 1]) {
|
|
32291
32759
|
logDir = argv[i + 1];
|
|
@@ -32349,6 +32817,7 @@ init_utils();
|
|
|
32349
32817
|
DEFAULT_MCP_PORT,
|
|
32350
32818
|
MAX_ID_LENGTH,
|
|
32351
32819
|
MAX_METADATA_LENGTH,
|
|
32820
|
+
MAX_PATH_LENGTH,
|
|
32352
32821
|
MAX_TITLE_LENGTH,
|
|
32353
32822
|
createBeansMcpServer,
|
|
32354
32823
|
isPathWithinRoot,
|