@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/beans-mcp-server.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"),
|
|
@@ -31368,32 +31549,43 @@ var import_node_util2 = require("util");
|
|
|
31368
31549
|
// package.json
|
|
31369
31550
|
var package_default = {
|
|
31370
31551
|
name: "@selfagency/beans-mcp",
|
|
31371
|
-
version: "0.
|
|
31552
|
+
version: "0.6.0",
|
|
31372
31553
|
private: false,
|
|
31373
31554
|
description: "MCP (Model Context Protocol) server for Beans issue tracker",
|
|
31555
|
+
keywords: [
|
|
31556
|
+
"ai",
|
|
31557
|
+
"beans",
|
|
31558
|
+
"issue-tracker",
|
|
31559
|
+
"mcp",
|
|
31560
|
+
"model-context-protocol"
|
|
31561
|
+
],
|
|
31562
|
+
homepage: "https://github.com/selfagency/beans-mcp",
|
|
31563
|
+
bugs: {
|
|
31564
|
+
url: "https://github.com/selfagency/beans-mcp/issues"
|
|
31565
|
+
},
|
|
31566
|
+
license: "MIT",
|
|
31374
31567
|
author: {
|
|
31375
31568
|
name: "Daniel Sieradski",
|
|
31376
31569
|
email: "daniel@self.agency",
|
|
31377
31570
|
url: "https://self.agency"
|
|
31378
31571
|
},
|
|
31379
|
-
homepage: "https://github.com/selfagency/beans-mcp",
|
|
31380
|
-
bugs: {
|
|
31381
|
-
url: "https://github.com/selfagency/beans-mcp/issues"
|
|
31382
|
-
},
|
|
31383
31572
|
repository: {
|
|
31384
31573
|
type: "git",
|
|
31385
31574
|
url: "git+https://github.com/selfagency/beans-mcp.git"
|
|
31386
31575
|
},
|
|
31387
|
-
|
|
31388
|
-
|
|
31389
|
-
|
|
31390
|
-
|
|
31391
|
-
"
|
|
31392
|
-
"
|
|
31393
|
-
"
|
|
31576
|
+
bin: {
|
|
31577
|
+
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31578
|
+
},
|
|
31579
|
+
files: [
|
|
31580
|
+
"dist",
|
|
31581
|
+
"skills",
|
|
31582
|
+
"README.md",
|
|
31583
|
+
"LICENSE.txt"
|
|
31394
31584
|
],
|
|
31395
|
-
license: "MIT",
|
|
31396
31585
|
type: "module",
|
|
31586
|
+
main: "./dist/index.cjs",
|
|
31587
|
+
module: "./dist/index.js",
|
|
31588
|
+
types: "./dist/index.d.ts",
|
|
31397
31589
|
exports: {
|
|
31398
31590
|
".": {
|
|
31399
31591
|
types: "./dist/index.d.ts",
|
|
@@ -31401,14 +31593,11 @@ var package_default = {
|
|
|
31401
31593
|
require: "./dist/index.cjs"
|
|
31402
31594
|
}
|
|
31403
31595
|
},
|
|
31404
|
-
main: "./dist/index.cjs",
|
|
31405
|
-
module: "./dist/index.js",
|
|
31406
|
-
types: "./dist/index.d.ts",
|
|
31407
|
-
bin: {
|
|
31408
|
-
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31409
|
-
},
|
|
31410
31596
|
scripts: {
|
|
31411
31597
|
build: "tsup",
|
|
31598
|
+
"docs:dev": "vitepress dev docs",
|
|
31599
|
+
"docs:build": "vitepress build docs",
|
|
31600
|
+
"docs:preview": "vitepress preview docs",
|
|
31412
31601
|
format: "oxfmt",
|
|
31413
31602
|
"lint:fix": "oxlint --fix",
|
|
31414
31603
|
lint: "oxlint",
|
|
@@ -31423,30 +31612,32 @@ var package_default = {
|
|
|
31423
31612
|
devDependencies: {
|
|
31424
31613
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
31425
31614
|
"@octokit/rest": "^22.0.1",
|
|
31426
|
-
"@types/node": "25.
|
|
31427
|
-
"@vitest/coverage-v8": "^4.1.
|
|
31428
|
-
"@vitest/ui": "4.1.
|
|
31615
|
+
"@types/node": "25.6.0",
|
|
31616
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
31617
|
+
"@vitest/ui": "4.1.4",
|
|
31429
31618
|
husky: "^9.1.7",
|
|
31430
31619
|
"lint-staged": "^16.4.0",
|
|
31431
31620
|
ora: "^9.3.0",
|
|
31432
|
-
oxfmt: "^0.
|
|
31433
|
-
oxlint: "^1.
|
|
31434
|
-
"oxlint-tsgolint": "^0.
|
|
31621
|
+
oxfmt: "^0.45.0",
|
|
31622
|
+
oxlint: "^1.60.0",
|
|
31623
|
+
"oxlint-tsgolint": "^0.21.1",
|
|
31435
31624
|
tsup: "8.5.1",
|
|
31436
|
-
typescript: "6.0.
|
|
31437
|
-
|
|
31625
|
+
typescript: "6.0.3",
|
|
31626
|
+
vitepress: "^1.6.4",
|
|
31627
|
+
vitest: "4.1.4",
|
|
31438
31628
|
zod: "4.3.6",
|
|
31439
31629
|
zx: "^8.8.5"
|
|
31440
31630
|
},
|
|
31441
|
-
engines: {
|
|
31442
|
-
node: ">=18"
|
|
31443
|
-
},
|
|
31444
31631
|
"lint-staged": {
|
|
31445
31632
|
"src/**/*.ts": [
|
|
31446
31633
|
"pnpm run lint:fix",
|
|
31447
31634
|
"pnpm run format"
|
|
31448
31635
|
]
|
|
31449
|
-
}
|
|
31636
|
+
},
|
|
31637
|
+
engines: {
|
|
31638
|
+
node: ">=18"
|
|
31639
|
+
},
|
|
31640
|
+
mcpName: "io.github.selfagency/beans-mcp"
|
|
31450
31641
|
};
|
|
31451
31642
|
|
|
31452
31643
|
// src/internal/queryHelpers.ts
|
|
@@ -31611,6 +31802,8 @@ var MAX_PATH_LENGTH = 1024;
|
|
|
31611
31802
|
init_utils();
|
|
31612
31803
|
var execFileAsync2 = (0, import_node_util2.promisify)(import_node_child_process2.execFile);
|
|
31613
31804
|
var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
|
|
31805
|
+
var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
|
|
31806
|
+
var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
|
|
31614
31807
|
function getSafeCliEnv(env) {
|
|
31615
31808
|
const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
|
|
31616
31809
|
const safeEnv = {};
|
|
@@ -31631,7 +31824,8 @@ function extractVersionFromOutput(output) {
|
|
|
31631
31824
|
if (!trimmed) {
|
|
31632
31825
|
return null;
|
|
31633
31826
|
}
|
|
31634
|
-
const
|
|
31827
|
+
const versionRegex = /(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
|
|
31828
|
+
const match = versionRegex.exec(trimmed);
|
|
31635
31829
|
return match?.[1] ?? null;
|
|
31636
31830
|
}
|
|
31637
31831
|
async function detectBeansCliVersion(cliPath, workspaceRoot) {
|
|
@@ -31648,10 +31842,10 @@ ${stderr}`);
|
|
|
31648
31842
|
return null;
|
|
31649
31843
|
}
|
|
31650
31844
|
}
|
|
31651
|
-
async function getBeanById(backend, beanId) {
|
|
31845
|
+
async function getBeanById(backend, beanId, beans) {
|
|
31652
31846
|
try {
|
|
31653
|
-
const
|
|
31654
|
-
const found =
|
|
31847
|
+
const allBeans = beans ?? await backend.list();
|
|
31848
|
+
const found = allBeans.find((b) => b.id === beanId);
|
|
31655
31849
|
if (!found) {
|
|
31656
31850
|
throw new Error(`Bean not found: ${beanId}`);
|
|
31657
31851
|
}
|
|
@@ -31660,12 +31854,111 @@ async function getBeanById(backend, beanId) {
|
|
|
31660
31854
|
throw new Error(`Failed to fetch bean ${beanId}: ${error48.message}`);
|
|
31661
31855
|
}
|
|
31662
31856
|
}
|
|
31857
|
+
function collectDescendantBeans(beans, rootBeanId) {
|
|
31858
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
31859
|
+
const byId = new Map(beans.map((bean) => [bean.id, bean]));
|
|
31860
|
+
for (const bean of beans) {
|
|
31861
|
+
if (!bean.parentId) {
|
|
31862
|
+
continue;
|
|
31863
|
+
}
|
|
31864
|
+
const children = byParent.get(bean.parentId) ?? [];
|
|
31865
|
+
children.push(bean.id);
|
|
31866
|
+
byParent.set(bean.parentId, children);
|
|
31867
|
+
}
|
|
31868
|
+
const queue = [...byParent.get(rootBeanId) ?? []];
|
|
31869
|
+
const visited = /* @__PURE__ */ new Set();
|
|
31870
|
+
const descendants = [];
|
|
31871
|
+
while (queue.length > 0) {
|
|
31872
|
+
const currentId = queue.shift();
|
|
31873
|
+
if (!currentId || visited.has(currentId)) {
|
|
31874
|
+
continue;
|
|
31875
|
+
}
|
|
31876
|
+
visited.add(currentId);
|
|
31877
|
+
const currentBean = byId.get(currentId);
|
|
31878
|
+
if (!currentBean) {
|
|
31879
|
+
continue;
|
|
31880
|
+
}
|
|
31881
|
+
descendants.push(currentBean);
|
|
31882
|
+
const children = byParent.get(currentId);
|
|
31883
|
+
if (children && children.length > 0) {
|
|
31884
|
+
queue.push(...children);
|
|
31885
|
+
}
|
|
31886
|
+
}
|
|
31887
|
+
return descendants;
|
|
31888
|
+
}
|
|
31889
|
+
async function cascadeStatusToDescendants(backend, rootBeanId, targetStatus, options) {
|
|
31890
|
+
const beans = options?.beans ?? await backend.list();
|
|
31891
|
+
const descendants = collectDescendantBeans(beans, rootBeanId);
|
|
31892
|
+
const updatedBeanIds = [];
|
|
31893
|
+
const skippedBeanIds = [];
|
|
31894
|
+
const errors = [];
|
|
31895
|
+
const toUpdate = [];
|
|
31896
|
+
for (const bean of descendants) {
|
|
31897
|
+
if (options?.onlyCurrentStatuses && !options.onlyCurrentStatuses.has(bean.status)) {
|
|
31898
|
+
skippedBeanIds.push(bean.id);
|
|
31899
|
+
continue;
|
|
31900
|
+
}
|
|
31901
|
+
toUpdate.push(bean);
|
|
31902
|
+
}
|
|
31903
|
+
const settled = await Promise.allSettled(
|
|
31904
|
+
toUpdate.map(async (bean) => backend.update(bean.id, { status: targetStatus }))
|
|
31905
|
+
);
|
|
31906
|
+
settled.forEach((result, index) => {
|
|
31907
|
+
const bean = toUpdate[index];
|
|
31908
|
+
if (!bean) {
|
|
31909
|
+
return;
|
|
31910
|
+
}
|
|
31911
|
+
if (result.status === "fulfilled") {
|
|
31912
|
+
updatedBeanIds.push(bean.id);
|
|
31913
|
+
return;
|
|
31914
|
+
}
|
|
31915
|
+
errors.push({
|
|
31916
|
+
beanId: bean.id,
|
|
31917
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
31918
|
+
});
|
|
31919
|
+
});
|
|
31920
|
+
return {
|
|
31921
|
+
totalDescendants: descendants.length,
|
|
31922
|
+
updatedBeanIds,
|
|
31923
|
+
skippedBeanIds,
|
|
31924
|
+
errors
|
|
31925
|
+
};
|
|
31926
|
+
}
|
|
31927
|
+
function completeMarkdownTasks(body) {
|
|
31928
|
+
const lines = body.split(/\r?\n/);
|
|
31929
|
+
let totalTaskCount = 0;
|
|
31930
|
+
let updatedTaskCount = 0;
|
|
31931
|
+
const taskLinePattern = /^\s*(?:[-*+]|\d+\.)\s+\[[ xX]\]/;
|
|
31932
|
+
const uncheckedTaskLinePattern = /^(\s*(?:[-*+]|\d+\.)\s+\[)\s(\].*)$/;
|
|
31933
|
+
const nextLines = lines.map((line) => {
|
|
31934
|
+
if (!taskLinePattern.test(line)) {
|
|
31935
|
+
return line;
|
|
31936
|
+
}
|
|
31937
|
+
totalTaskCount += 1;
|
|
31938
|
+
const uncheckedMatch = uncheckedTaskLinePattern.exec(line);
|
|
31939
|
+
if (!uncheckedMatch) {
|
|
31940
|
+
return line;
|
|
31941
|
+
}
|
|
31942
|
+
updatedTaskCount += 1;
|
|
31943
|
+
return `${uncheckedMatch[1]}x${uncheckedMatch[2]}`;
|
|
31944
|
+
});
|
|
31945
|
+
const nextBody = nextLines.join("\n");
|
|
31946
|
+
return { nextBody, totalTaskCount, updatedTaskCount };
|
|
31947
|
+
}
|
|
31663
31948
|
function initHandler(backend) {
|
|
31664
31949
|
return async ({ prefix }) => {
|
|
31665
31950
|
const result = await backend.init(prefix);
|
|
31666
31951
|
return makeTextAndStructured(result);
|
|
31667
31952
|
};
|
|
31668
31953
|
}
|
|
31954
|
+
function archiveHandler(backend) {
|
|
31955
|
+
return async () => {
|
|
31956
|
+
if (typeof backend.archive !== "function") {
|
|
31957
|
+
throw new TypeError("Archive is not supported by the current backend");
|
|
31958
|
+
}
|
|
31959
|
+
return makeTextAndStructured(await backend.archive());
|
|
31960
|
+
};
|
|
31961
|
+
}
|
|
31669
31962
|
function viewHandler(backend) {
|
|
31670
31963
|
return async ({ beanId, beanIds }) => {
|
|
31671
31964
|
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
@@ -31703,7 +31996,14 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
|
|
|
31703
31996
|
}
|
|
31704
31997
|
}
|
|
31705
31998
|
function createHandler(backend) {
|
|
31706
|
-
return async (input) =>
|
|
31999
|
+
return async (input) => {
|
|
32000
|
+
const bean = await backend.create(input);
|
|
32001
|
+
const warnings = input.description !== void 0 ? ["`description` is deprecated; use `body` instead."] : void 0;
|
|
32002
|
+
return makeTextAndStructured({
|
|
32003
|
+
bean,
|
|
32004
|
+
...warnings ? { warnings } : {}
|
|
32005
|
+
});
|
|
32006
|
+
};
|
|
31707
32007
|
}
|
|
31708
32008
|
function editHandler(backend) {
|
|
31709
32009
|
return async ({
|
|
@@ -31717,18 +32017,30 @@ function reopenHandler(backend) {
|
|
|
31717
32017
|
requiredCurrentStatus,
|
|
31718
32018
|
targetStatus
|
|
31719
32019
|
}) => {
|
|
31720
|
-
const
|
|
31721
|
-
|
|
31722
|
-
|
|
32020
|
+
const beans = await backend.list();
|
|
32021
|
+
const bean = await getBeanById(backend, beanId, beans);
|
|
32022
|
+
if (bean.status === requiredCurrentStatus) {
|
|
32023
|
+
const updatedParentBean = await backend.update(beanId, { status: targetStatus });
|
|
32024
|
+
const cascade = await cascadeStatusToDescendants(backend, beanId, targetStatus, {
|
|
32025
|
+
onlyCurrentStatuses: CLOSED_STATUSES,
|
|
32026
|
+
beans
|
|
32027
|
+
});
|
|
32028
|
+
return makeTextAndStructured({
|
|
32029
|
+
bean: updatedParentBean,
|
|
32030
|
+
cascade: {
|
|
32031
|
+
totalDescendants: cascade.totalDescendants,
|
|
32032
|
+
updatedBeanIds: cascade.updatedBeanIds,
|
|
32033
|
+
skippedBeanIds: cascade.skippedBeanIds,
|
|
32034
|
+
errors: cascade.errors
|
|
32035
|
+
}
|
|
32036
|
+
});
|
|
31723
32037
|
}
|
|
31724
|
-
|
|
31725
|
-
bean: await backend.update(beanId, { status: targetStatus })
|
|
31726
|
-
});
|
|
32038
|
+
throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
|
|
31727
32039
|
};
|
|
31728
32040
|
}
|
|
31729
32041
|
function updateHandler(backend) {
|
|
31730
|
-
return async (input) =>
|
|
31731
|
-
|
|
32042
|
+
return async (input) => {
|
|
32043
|
+
const updatedBean = await backend.update(input.beanId, {
|
|
31732
32044
|
status: input.status,
|
|
31733
32045
|
type: input.type,
|
|
31734
32046
|
priority: input.priority,
|
|
@@ -31740,8 +32052,37 @@ function updateHandler(backend) {
|
|
|
31740
32052
|
bodyAppend: input.bodyAppend,
|
|
31741
32053
|
bodyReplace: input.bodyReplace,
|
|
31742
32054
|
ifMatch: input.ifMatch
|
|
31743
|
-
})
|
|
31744
|
-
|
|
32055
|
+
});
|
|
32056
|
+
const closeStatus = input.status;
|
|
32057
|
+
const shouldCascadeClose = Boolean(closeStatus && CLOSED_STATUSES.has(closeStatus));
|
|
32058
|
+
const cascade = shouldCascadeClose ? await cascadeStatusToDescendants(backend, input.beanId, closeStatus, {
|
|
32059
|
+
beans: await backend.list()
|
|
32060
|
+
}) : null;
|
|
32061
|
+
return makeTextAndStructured({
|
|
32062
|
+
bean: updatedBean,
|
|
32063
|
+
...cascade ? {
|
|
32064
|
+
cascade: {
|
|
32065
|
+
totalDescendants: cascade.totalDescendants,
|
|
32066
|
+
updatedBeanIds: cascade.updatedBeanIds,
|
|
32067
|
+
skippedBeanIds: cascade.skippedBeanIds,
|
|
32068
|
+
errors: cascade.errors
|
|
32069
|
+
}
|
|
32070
|
+
} : {}
|
|
32071
|
+
});
|
|
32072
|
+
};
|
|
32073
|
+
}
|
|
32074
|
+
function completeTasksHandler(backend) {
|
|
32075
|
+
return async ({ beanId }) => {
|
|
32076
|
+
const bean = await getBeanById(backend, beanId);
|
|
32077
|
+
const { nextBody, totalTaskCount, updatedTaskCount } = completeMarkdownTasks(bean.body || "");
|
|
32078
|
+
const updatedBean = updatedTaskCount > 0 ? await backend.update(beanId, { body: nextBody }) : bean;
|
|
32079
|
+
return makeTextAndStructured({
|
|
32080
|
+
bean: updatedBean,
|
|
32081
|
+
totalTaskCount,
|
|
32082
|
+
updatedTaskCount,
|
|
32083
|
+
unchangedTaskCount: totalTaskCount - updatedTaskCount
|
|
32084
|
+
});
|
|
32085
|
+
};
|
|
31745
32086
|
}
|
|
31746
32087
|
function deleteHandler(backend) {
|
|
31747
32088
|
return async ({ beanId, beanIds, force }) => {
|
|
@@ -31795,11 +32136,17 @@ function deleteHandler(backend) {
|
|
|
31795
32136
|
function bulkCreateHandler(backend) {
|
|
31796
32137
|
return async (input) => {
|
|
31797
32138
|
const results = await backend.bulkCreate(input.beans, input.parent);
|
|
32139
|
+
const deprecatedDescriptionCount = input.beans.filter((bean) => bean.description !== void 0).length;
|
|
31798
32140
|
return makeTextAndStructured({
|
|
31799
32141
|
results,
|
|
31800
32142
|
requestedCount: input.beans.length,
|
|
31801
32143
|
successCount: results.filter((r) => r.bean).length,
|
|
31802
|
-
failedCount: results.filter((r) => r.error).length
|
|
32144
|
+
failedCount: results.filter((r) => r.error).length,
|
|
32145
|
+
...deprecatedDescriptionCount > 0 ? {
|
|
32146
|
+
warnings: [
|
|
32147
|
+
`Found ${deprecatedDescriptionCount} bean(s) using deprecated field \`description\`; use \`body\` instead.`
|
|
32148
|
+
]
|
|
32149
|
+
} : {}
|
|
31803
32150
|
});
|
|
31804
32151
|
};
|
|
31805
32152
|
}
|
|
@@ -31815,14 +32162,24 @@ function bulkUpdateHandler(backend) {
|
|
|
31815
32162
|
};
|
|
31816
32163
|
}
|
|
31817
32164
|
function queryHandler(backend) {
|
|
31818
|
-
return async (opts) =>
|
|
32165
|
+
return async (opts) => {
|
|
32166
|
+
if (opts.operation === "graphql") {
|
|
32167
|
+
if (typeof backend.queryGraphql !== "function") {
|
|
32168
|
+
throw new TypeError("GraphQL passthrough is not supported by the current backend");
|
|
32169
|
+
}
|
|
32170
|
+
const result = await backend.queryGraphql(opts.graphql || "", opts.variables);
|
|
32171
|
+
return makeTextAndStructured({ data: result.data, errors: result.errors ?? [] });
|
|
32172
|
+
}
|
|
32173
|
+
return handleQueryOperation(backend, opts);
|
|
32174
|
+
};
|
|
31819
32175
|
}
|
|
31820
32176
|
function beanFileHandler(backend) {
|
|
31821
32177
|
return async ({
|
|
31822
32178
|
operation,
|
|
31823
32179
|
path,
|
|
31824
32180
|
content,
|
|
31825
|
-
overwrite
|
|
32181
|
+
overwrite,
|
|
32182
|
+
fields
|
|
31826
32183
|
}) => {
|
|
31827
32184
|
if (operation === "read") {
|
|
31828
32185
|
return makeTextAndStructured(await backend.readBeanFile(path));
|
|
@@ -31833,6 +32190,9 @@ function beanFileHandler(backend) {
|
|
|
31833
32190
|
if (operation === "create") {
|
|
31834
32191
|
return makeTextAndStructured(await backend.createBeanFile(path, content || "", { overwrite }));
|
|
31835
32192
|
}
|
|
32193
|
+
if (operation === "update_frontmatter") {
|
|
32194
|
+
return makeTextAndStructured(await backend.updateBeanFrontmatter(path, fields || {}));
|
|
32195
|
+
}
|
|
31836
32196
|
if (operation === "delete") {
|
|
31837
32197
|
return makeTextAndStructured(await backend.deleteBeanFile(path));
|
|
31838
32198
|
}
|
|
@@ -31867,6 +32227,21 @@ function registerTools(server, backend) {
|
|
|
31867
32227
|
},
|
|
31868
32228
|
initHandler(backend)
|
|
31869
32229
|
);
|
|
32230
|
+
server.registerTool(
|
|
32231
|
+
"beans_archive",
|
|
32232
|
+
{
|
|
32233
|
+
title: "Archive Beans",
|
|
32234
|
+
description: "Archive completed or scrapped beans, equivalent to the beans CLI archive command.",
|
|
32235
|
+
inputSchema: external_exports3.object({}),
|
|
32236
|
+
annotations: {
|
|
32237
|
+
readOnlyHint: false,
|
|
32238
|
+
destructiveHint: false,
|
|
32239
|
+
idempotentHint: false,
|
|
32240
|
+
openWorldHint: false
|
|
32241
|
+
}
|
|
32242
|
+
},
|
|
32243
|
+
archiveHandler(backend)
|
|
32244
|
+
);
|
|
31870
32245
|
server.registerTool(
|
|
31871
32246
|
"beans_view",
|
|
31872
32247
|
{
|
|
@@ -31876,7 +32251,7 @@ function registerTools(server, backend) {
|
|
|
31876
32251
|
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31877
32252
|
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
|
|
31878
32253
|
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31879
|
-
message:
|
|
32254
|
+
message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
|
|
31880
32255
|
}),
|
|
31881
32256
|
annotations: {
|
|
31882
32257
|
readOnlyHint: true,
|
|
@@ -31916,7 +32291,7 @@ function registerTools(server, backend) {
|
|
|
31916
32291
|
title: "Edit Bean Metadata",
|
|
31917
32292
|
description: "Update bean metadata fields (status/type/priority/parent/blocking).",
|
|
31918
32293
|
inputSchema: external_exports3.object({
|
|
31919
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32294
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31920
32295
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31921
32296
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31922
32297
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -31924,7 +32299,11 @@ function registerTools(server, backend) {
|
|
|
31924
32299
|
clearParent: external_exports3.boolean().optional(),
|
|
31925
32300
|
blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31926
32301
|
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional()
|
|
31927
|
-
}),
|
|
32302
|
+
}).superRefine((input, ctx) => {
|
|
32303
|
+
if (!input.beanId) {
|
|
32304
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32305
|
+
}
|
|
32306
|
+
}).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
31928
32307
|
annotations: {
|
|
31929
32308
|
readOnlyHint: false,
|
|
31930
32309
|
destructiveHint: false,
|
|
@@ -31940,10 +32319,14 @@ function registerTools(server, backend) {
|
|
|
31940
32319
|
title: "Reopen Bean",
|
|
31941
32320
|
description: "Reopen a completed or scrapped bean into a non-closed status.",
|
|
31942
32321
|
inputSchema: external_exports3.object({
|
|
31943
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32322
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31944
32323
|
requiredCurrentStatus: external_exports3.enum(["completed", "scrapped"]),
|
|
31945
32324
|
targetStatus: external_exports3.string().max(MAX_METADATA_LENGTH).default("todo")
|
|
31946
|
-
}),
|
|
32325
|
+
}).superRefine((input, ctx) => {
|
|
32326
|
+
if (!input.beanId) {
|
|
32327
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32328
|
+
}
|
|
32329
|
+
}).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
31947
32330
|
annotations: {
|
|
31948
32331
|
readOnlyHint: false,
|
|
31949
32332
|
destructiveHint: false,
|
|
@@ -31959,7 +32342,7 @@ function registerTools(server, backend) {
|
|
|
31959
32342
|
title: "Update Bean",
|
|
31960
32343
|
description: "Update bean metadata fields (status/type/priority/parent/blocking). Consolidated replacement for per-field update tools.",
|
|
31961
32344
|
inputSchema: external_exports3.object({
|
|
31962
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32345
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31963
32346
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31964
32347
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31965
32348
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -31976,12 +32359,16 @@ function registerTools(server, backend) {
|
|
|
31976
32359
|
})
|
|
31977
32360
|
).optional(),
|
|
31978
32361
|
ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
|
|
32362
|
+
}).superRefine((input, ctx) => {
|
|
32363
|
+
if (!input.beanId) {
|
|
32364
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32365
|
+
}
|
|
31979
32366
|
}).refine(
|
|
31980
32367
|
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
31981
32368
|
{
|
|
31982
32369
|
message: "body cannot be combined with bodyAppend/bodyReplace"
|
|
31983
32370
|
}
|
|
31984
|
-
),
|
|
32371
|
+
).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
31985
32372
|
annotations: {
|
|
31986
32373
|
readOnlyHint: false,
|
|
31987
32374
|
destructiveHint: false,
|
|
@@ -32001,7 +32388,7 @@ function registerTools(server, backend) {
|
|
|
32001
32388
|
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
|
|
32002
32389
|
force: external_exports3.boolean().default(false)
|
|
32003
32390
|
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
32004
|
-
message:
|
|
32391
|
+
message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
|
|
32005
32392
|
}),
|
|
32006
32393
|
annotations: {
|
|
32007
32394
|
readOnlyHint: false,
|
|
@@ -32040,7 +32427,7 @@ function registerTools(server, backend) {
|
|
|
32040
32427
|
bulkCreateHandler(backend)
|
|
32041
32428
|
);
|
|
32042
32429
|
const beanUpdateItemSchema = external_exports3.object({
|
|
32043
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32430
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
32044
32431
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32045
32432
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32046
32433
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -32052,10 +32439,14 @@ function registerTools(server, backend) {
|
|
|
32052
32439
|
bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32053
32440
|
bodyReplace: external_exports3.array(external_exports3.object({ old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH), new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH) })).optional(),
|
|
32054
32441
|
ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
|
|
32442
|
+
}).superRefine((input, ctx) => {
|
|
32443
|
+
if (!input.beanId) {
|
|
32444
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32445
|
+
}
|
|
32055
32446
|
}).refine(
|
|
32056
32447
|
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
32057
32448
|
{ message: "body cannot be combined with bodyAppend/bodyReplace" }
|
|
32058
|
-
);
|
|
32449
|
+
).transform((input) => ({ ...input, beanId: input.beanId }));
|
|
32059
32450
|
server.registerTool(
|
|
32060
32451
|
"beans_bulk_update",
|
|
32061
32452
|
{
|
|
@@ -32074,25 +32465,56 @@ function registerTools(server, backend) {
|
|
|
32074
32465
|
},
|
|
32075
32466
|
bulkUpdateHandler(backend)
|
|
32076
32467
|
);
|
|
32468
|
+
server.registerTool(
|
|
32469
|
+
"beans_complete_tasks",
|
|
32470
|
+
{
|
|
32471
|
+
title: "Complete Markdown Tasks",
|
|
32472
|
+
description: "Mark all markdown checklist tasks within a bean as completed.",
|
|
32473
|
+
inputSchema: external_exports3.object({
|
|
32474
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional()
|
|
32475
|
+
}).superRefine((input, ctx) => {
|
|
32476
|
+
if (!input.beanId) {
|
|
32477
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32478
|
+
}
|
|
32479
|
+
}).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
32480
|
+
annotations: {
|
|
32481
|
+
readOnlyHint: false,
|
|
32482
|
+
destructiveHint: false,
|
|
32483
|
+
idempotentHint: true,
|
|
32484
|
+
openWorldHint: false
|
|
32485
|
+
}
|
|
32486
|
+
},
|
|
32487
|
+
completeTasksHandler(backend)
|
|
32488
|
+
);
|
|
32077
32489
|
server.registerTool(
|
|
32078
32490
|
"beans_query",
|
|
32079
32491
|
{
|
|
32080
32492
|
title: "Query Beans",
|
|
32081
32493
|
description: "Unified query tool for refresh, filter, search, and sort operations.",
|
|
32082
32494
|
inputSchema: external_exports3.object({
|
|
32083
|
-
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
|
|
32495
|
+
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config", "graphql"]).default("refresh"),
|
|
32084
32496
|
mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
|
|
32085
32497
|
statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32086
32498
|
types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32087
32499
|
search: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
|
|
32088
32500
|
includeClosed: external_exports3.boolean().optional(),
|
|
32089
32501
|
tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32502
|
+
graphql: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32503
|
+
variables: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
|
|
32090
32504
|
writeToWorkspaceInstructions: external_exports3.boolean().optional()
|
|
32505
|
+
}).superRefine((input, ctx) => {
|
|
32506
|
+
if (input.operation === "graphql" && (!input.graphql || input.graphql.trim().length === 0)) {
|
|
32507
|
+
ctx.addIssue({
|
|
32508
|
+
code: external_exports3.ZodIssueCode.custom,
|
|
32509
|
+
path: ["graphql"],
|
|
32510
|
+
message: "graphql query string is required when operation is graphql"
|
|
32511
|
+
});
|
|
32512
|
+
}
|
|
32091
32513
|
}),
|
|
32092
32514
|
annotations: {
|
|
32093
|
-
readOnlyHint:
|
|
32515
|
+
readOnlyHint: false,
|
|
32094
32516
|
destructiveHint: false,
|
|
32095
|
-
idempotentHint:
|
|
32517
|
+
idempotentHint: false,
|
|
32096
32518
|
openWorldHint: false
|
|
32097
32519
|
}
|
|
32098
32520
|
},
|
|
@@ -32104,10 +32526,33 @@ function registerTools(server, backend) {
|
|
|
32104
32526
|
title: "Bean File Operations",
|
|
32105
32527
|
description: "Read, create, edit, or delete files under .beans (operation param).",
|
|
32106
32528
|
inputSchema: external_exports3.object({
|
|
32107
|
-
operation: external_exports3.enum(["read", "edit", "create", "delete"]),
|
|
32529
|
+
operation: external_exports3.enum(["read", "edit", "create", "delete", "update_frontmatter"]),
|
|
32108
32530
|
path: external_exports3.string().min(1).max(MAX_PATH_LENGTH),
|
|
32109
32531
|
content: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32110
|
-
overwrite: external_exports3.boolean().optional()
|
|
32532
|
+
overwrite: external_exports3.boolean().optional(),
|
|
32533
|
+
fields: external_exports3.object({
|
|
32534
|
+
title: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
|
|
32535
|
+
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32536
|
+
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32537
|
+
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32538
|
+
parent_id: external_exports3.string().max(MAX_ID_LENGTH).nullable().optional(),
|
|
32539
|
+
tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32540
|
+
blocking_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
|
|
32541
|
+
blocked_by_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
|
|
32542
|
+
pr: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional(),
|
|
32543
|
+
branch: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional()
|
|
32544
|
+
}).optional()
|
|
32545
|
+
}).superRefine((input, ctx) => {
|
|
32546
|
+
if (input.operation === "update_frontmatter") {
|
|
32547
|
+
const fieldCount = Object.values(input.fields || {}).filter((value) => value !== void 0).length;
|
|
32548
|
+
if (fieldCount === 0) {
|
|
32549
|
+
ctx.addIssue({
|
|
32550
|
+
code: external_exports3.ZodIssueCode.custom,
|
|
32551
|
+
path: ["fields"],
|
|
32552
|
+
message: "At least one frontmatter field update is required"
|
|
32553
|
+
});
|
|
32554
|
+
}
|
|
32555
|
+
}
|
|
32111
32556
|
}),
|
|
32112
32557
|
annotations: {
|
|
32113
32558
|
readOnlyHint: false,
|
|
@@ -32147,6 +32592,18 @@ var MutableBackend = class {
|
|
|
32147
32592
|
init(prefix) {
|
|
32148
32593
|
return this.inner.init(prefix);
|
|
32149
32594
|
}
|
|
32595
|
+
archive() {
|
|
32596
|
+
if (typeof this.inner.archive === "function") {
|
|
32597
|
+
return this.inner.archive();
|
|
32598
|
+
}
|
|
32599
|
+
throw new TypeError("Archive is not supported by backend");
|
|
32600
|
+
}
|
|
32601
|
+
queryGraphql(query, variables) {
|
|
32602
|
+
if (typeof this.inner.queryGraphql === "function") {
|
|
32603
|
+
return this.inner.queryGraphql(query, variables);
|
|
32604
|
+
}
|
|
32605
|
+
throw new TypeError("GraphQL passthrough is not supported by backend");
|
|
32606
|
+
}
|
|
32150
32607
|
list(opts) {
|
|
32151
32608
|
return this.inner.list(opts);
|
|
32152
32609
|
}
|
|
@@ -32186,6 +32643,9 @@ var MutableBackend = class {
|
|
|
32186
32643
|
editBeanFile(path, content) {
|
|
32187
32644
|
return this.inner.editBeanFile(path, content);
|
|
32188
32645
|
}
|
|
32646
|
+
updateBeanFrontmatter(path, updates) {
|
|
32647
|
+
return this.inner.updateBeanFrontmatter(path, updates);
|
|
32648
|
+
}
|
|
32189
32649
|
createBeanFile(path, content, opts) {
|
|
32190
32650
|
return this.inner.createBeanFile(path, content, opts);
|
|
32191
32651
|
}
|
|
@@ -32252,6 +32712,16 @@ function parseCliArgs(argv) {
|
|
|
32252
32712
|
const envPort = Number.parseInt(process.env.BEANS_VSCODE_MCP_PORT || process.env.BEANS_MCP_PORT || "", 10);
|
|
32253
32713
|
let port = Number.isInteger(envPort) && envPort > 0 ? envPort : DEFAULT_MCP_PORT;
|
|
32254
32714
|
let logDir;
|
|
32715
|
+
const parseStrictPositiveInt = (raw, flagName) => {
|
|
32716
|
+
if (!/^\d+$/.test(raw)) {
|
|
32717
|
+
throw new Error(`Invalid value for ${flagName}: ${raw}`);
|
|
32718
|
+
}
|
|
32719
|
+
const parsed = Number.parseInt(raw, 10);
|
|
32720
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
32721
|
+
throw new Error(`Invalid value for ${flagName}: ${raw}`);
|
|
32722
|
+
}
|
|
32723
|
+
return parsed;
|
|
32724
|
+
};
|
|
32255
32725
|
for (let i = 0; i < argv.length; i += 1) {
|
|
32256
32726
|
const arg = argv[i];
|
|
32257
32727
|
if ((arg === "--workspace" || arg === "--workspace-root") && argv[i + 1]) {
|
|
@@ -32265,10 +32735,7 @@ function parseCliArgs(argv) {
|
|
|
32265
32735
|
}
|
|
32266
32736
|
i += 1;
|
|
32267
32737
|
} else if (arg === "--port" && argv[i + 1]) {
|
|
32268
|
-
|
|
32269
|
-
if (Number.isInteger(parsedPort) && parsedPort > 0) {
|
|
32270
|
-
port = parsedPort;
|
|
32271
|
-
}
|
|
32738
|
+
port = parseStrictPositiveInt(argv[i + 1], "--port");
|
|
32272
32739
|
i += 1;
|
|
32273
32740
|
} else if (arg === "--log-dir" && argv[i + 1]) {
|
|
32274
32741
|
logDir = argv[i + 1];
|