@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.js
CHANGED
|
@@ -22801,7 +22801,7 @@ __export(backend_exports, {
|
|
|
22801
22801
|
});
|
|
22802
22802
|
import { execFile } from "child_process";
|
|
22803
22803
|
import { createReadStream } from "fs";
|
|
22804
|
-
import { mkdir, readFile, rm, writeFile } from "fs/promises";
|
|
22804
|
+
import { mkdir, readFile, realpath, rename, rm, writeFile } from "fs/promises";
|
|
22805
22805
|
import { dirname, join, resolve as resolve2 } from "path";
|
|
22806
22806
|
import { createInterface } from "readline";
|
|
22807
22807
|
import { promisify } from "util";
|
|
@@ -22902,6 +22902,26 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
22902
22902
|
});
|
|
22903
22903
|
return { initialized: true };
|
|
22904
22904
|
}
|
|
22905
|
+
async archive() {
|
|
22906
|
+
const { stdout } = await execFileAsync(this.cliPath, ["archive", "--json"], {
|
|
22907
|
+
cwd: this.workspaceRoot,
|
|
22908
|
+
env: this.getSafeEnv(),
|
|
22909
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
22910
|
+
timeout: 3e4
|
|
22911
|
+
});
|
|
22912
|
+
this.invalidateCache();
|
|
22913
|
+
if (!stdout.trim()) {
|
|
22914
|
+
return { archived: true };
|
|
22915
|
+
}
|
|
22916
|
+
try {
|
|
22917
|
+
return JSON.parse(stdout);
|
|
22918
|
+
} catch {
|
|
22919
|
+
return { archived: true, output: stdout.trim() };
|
|
22920
|
+
}
|
|
22921
|
+
}
|
|
22922
|
+
async queryGraphql(query, variables) {
|
|
22923
|
+
return this.executeGraphQL(query, variables);
|
|
22924
|
+
}
|
|
22905
22925
|
async list(options) {
|
|
22906
22926
|
const filter = {};
|
|
22907
22927
|
if (options?.status && options.status.length > 0) {
|
|
@@ -23056,32 +23076,39 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23056
23076
|
return { deleted: true, beanId };
|
|
23057
23077
|
}
|
|
23058
23078
|
async bulkCreate(beans, defaultParent) {
|
|
23059
|
-
const
|
|
23060
|
-
|
|
23061
|
-
|
|
23062
|
-
const bean = await this.create({
|
|
23079
|
+
const settled = await Promise.allSettled(
|
|
23080
|
+
beans.map(
|
|
23081
|
+
async (item) => this.create({
|
|
23063
23082
|
...item,
|
|
23064
23083
|
parent: item.parent ?? defaultParent
|
|
23065
|
-
})
|
|
23066
|
-
|
|
23067
|
-
|
|
23068
|
-
|
|
23069
|
-
}
|
|
23070
|
-
|
|
23071
|
-
return results;
|
|
23084
|
+
})
|
|
23085
|
+
)
|
|
23086
|
+
);
|
|
23087
|
+
return settled.map(
|
|
23088
|
+
(result) => result.status === "fulfilled" ? { bean: result.value } : { error: result.reason instanceof Error ? result.reason.message : String(result.reason) }
|
|
23089
|
+
);
|
|
23072
23090
|
}
|
|
23073
23091
|
async bulkUpdate(beans, defaultParent) {
|
|
23074
|
-
const
|
|
23075
|
-
|
|
23076
|
-
try {
|
|
23092
|
+
const settled = await Promise.allSettled(
|
|
23093
|
+
beans.map(async ({ beanId, ...updates }) => {
|
|
23077
23094
|
const resolvedParent = updates.parent ?? (updates.clearParent ? void 0 : defaultParent);
|
|
23078
23095
|
const bean = await this.update(beanId, { ...updates, parent: resolvedParent });
|
|
23079
|
-
|
|
23080
|
-
}
|
|
23081
|
-
|
|
23096
|
+
return { beanId, bean };
|
|
23097
|
+
})
|
|
23098
|
+
);
|
|
23099
|
+
return settled.map((result, index) => {
|
|
23100
|
+
const beanId = beans[index]?.beanId;
|
|
23101
|
+
if (!beanId) {
|
|
23102
|
+
return { beanId: "unknown", error: "Unknown bean id" };
|
|
23082
23103
|
}
|
|
23083
|
-
|
|
23084
|
-
|
|
23104
|
+
if (result.status === "fulfilled") {
|
|
23105
|
+
return result.value;
|
|
23106
|
+
}
|
|
23107
|
+
return {
|
|
23108
|
+
beanId,
|
|
23109
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
23110
|
+
};
|
|
23111
|
+
});
|
|
23085
23112
|
}
|
|
23086
23113
|
async openConfig() {
|
|
23087
23114
|
const configPath = join(this.workspaceRoot, ".beans.yml");
|
|
@@ -23116,9 +23143,12 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23116
23143
|
const outputPath = resolve2(
|
|
23117
23144
|
process.env.BEANS_VSCODE_OUTPUT_LOG || join(this.workspaceRoot, ".vscode", "logs", "beans-output.log")
|
|
23118
23145
|
);
|
|
23119
|
-
const
|
|
23146
|
+
const canonicalOutputPath = await realpath(outputPath).catch(() => outputPath);
|
|
23147
|
+
const canonicalWorkspaceRoot = await realpath(this.workspaceRoot).catch(() => resolve2(this.workspaceRoot));
|
|
23148
|
+
const isWithinWorkspace = isPathWithinRoot(canonicalWorkspaceRoot, canonicalOutputPath);
|
|
23120
23149
|
const vscodeLogDir = process.env.BEANS_VSCODE_LOG_DIR || this.logDir ? resolve2(process.env.BEANS_VSCODE_LOG_DIR || this.logDir || "") : void 0;
|
|
23121
|
-
const
|
|
23150
|
+
const canonicalVscodeLogDir = vscodeLogDir ? await realpath(vscodeLogDir).catch(() => resolve2(vscodeLogDir)) : void 0;
|
|
23151
|
+
const isWithinVscodeLogDir = canonicalVscodeLogDir ? isPathWithinRoot(canonicalVscodeLogDir, canonicalOutputPath) : false;
|
|
23122
23152
|
if (!isWithinWorkspace && !isWithinVscodeLogDir) {
|
|
23123
23153
|
throw new Error("Output log path must stay within the workspace or VS Code log directory");
|
|
23124
23154
|
}
|
|
@@ -23197,6 +23227,105 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23197
23227
|
escapeForYamlDoubleQuoted(value) {
|
|
23198
23228
|
return value.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
|
|
23199
23229
|
}
|
|
23230
|
+
shouldQuoteFrontmatterValue(value) {
|
|
23231
|
+
return !/^[A-Za-z0-9._-]+$/.test(value);
|
|
23232
|
+
}
|
|
23233
|
+
parseFrontmatterLine(line) {
|
|
23234
|
+
const separatorIndex = line.indexOf(":");
|
|
23235
|
+
if (separatorIndex <= 0) {
|
|
23236
|
+
return null;
|
|
23237
|
+
}
|
|
23238
|
+
const key = line.slice(0, separatorIndex).trim();
|
|
23239
|
+
if (key.length === 0) {
|
|
23240
|
+
return null;
|
|
23241
|
+
}
|
|
23242
|
+
for (const character of key) {
|
|
23243
|
+
const isAlphaNumericUnderscore = character >= "a" && character <= "z" || character >= "A" && character <= "Z" || character >= "0" && character <= "9" || character === "_";
|
|
23244
|
+
if (!isAlphaNumericUnderscore) {
|
|
23245
|
+
return null;
|
|
23246
|
+
}
|
|
23247
|
+
}
|
|
23248
|
+
const rawValue = line.slice(separatorIndex + 1).trimStart();
|
|
23249
|
+
return { key, rawValue };
|
|
23250
|
+
}
|
|
23251
|
+
buildFrontmatterIndex(frontmatterLines) {
|
|
23252
|
+
const indexByKey = /* @__PURE__ */ new Map();
|
|
23253
|
+
frontmatterLines.forEach((line, index) => {
|
|
23254
|
+
const parsed = this.parseFrontmatterLine(line);
|
|
23255
|
+
if (!parsed) {
|
|
23256
|
+
return;
|
|
23257
|
+
}
|
|
23258
|
+
indexByKey.set(parsed.key, index);
|
|
23259
|
+
});
|
|
23260
|
+
return indexByKey;
|
|
23261
|
+
}
|
|
23262
|
+
serializeFrontmatterValue(key, value) {
|
|
23263
|
+
if (Array.isArray(value)) {
|
|
23264
|
+
return JSON.stringify(value);
|
|
23265
|
+
}
|
|
23266
|
+
if (key === "title") {
|
|
23267
|
+
return this.normalizeFrontmatterTitleValue(value);
|
|
23268
|
+
}
|
|
23269
|
+
if (this.shouldQuoteFrontmatterValue(value)) {
|
|
23270
|
+
return `"${this.escapeForYamlDoubleQuoted(value)}"`;
|
|
23271
|
+
}
|
|
23272
|
+
return value;
|
|
23273
|
+
}
|
|
23274
|
+
deserializeFrontmatterValue(value) {
|
|
23275
|
+
const trimmed = value.trim();
|
|
23276
|
+
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
23277
|
+
try {
|
|
23278
|
+
const parsed = JSON.parse(trimmed);
|
|
23279
|
+
if (Array.isArray(parsed) && parsed.every((item) => typeof item === "string")) {
|
|
23280
|
+
return parsed;
|
|
23281
|
+
}
|
|
23282
|
+
} catch {
|
|
23283
|
+
}
|
|
23284
|
+
}
|
|
23285
|
+
if (trimmed.startsWith('"') && trimmed.endsWith('"') || trimmed.startsWith("'") && trimmed.endsWith("'")) {
|
|
23286
|
+
return trimmed.slice(1, -1).replaceAll('\\"', '"').replaceAll("\\\\", "\\").replaceAll("''", "'");
|
|
23287
|
+
}
|
|
23288
|
+
return trimmed;
|
|
23289
|
+
}
|
|
23290
|
+
splitFrontmatterDocument(content) {
|
|
23291
|
+
const crlfOpen = content.startsWith("---\r\n");
|
|
23292
|
+
const lfOpen = content.startsWith("---\n");
|
|
23293
|
+
const eol = crlfOpen ? "\r\n" : "\n";
|
|
23294
|
+
if (!crlfOpen && !lfOpen) {
|
|
23295
|
+
return { eol: "\n", hasFrontmatter: false, frontmatterLines: [], body: content };
|
|
23296
|
+
}
|
|
23297
|
+
const openEnd = `---${eol}`.length;
|
|
23298
|
+
const closeMarker = `${eol}---`;
|
|
23299
|
+
const closeIdx = content.indexOf(closeMarker, openEnd);
|
|
23300
|
+
if (closeIdx === -1) {
|
|
23301
|
+
return { eol, hasFrontmatter: false, frontmatterLines: [], body: content };
|
|
23302
|
+
}
|
|
23303
|
+
const frontmatter = content.slice(openEnd, closeIdx);
|
|
23304
|
+
const body = content.slice(closeIdx + closeMarker.length);
|
|
23305
|
+
return {
|
|
23306
|
+
eol,
|
|
23307
|
+
hasFrontmatter: true,
|
|
23308
|
+
frontmatterLines: frontmatter.length > 0 ? frontmatter.split(eol) : [],
|
|
23309
|
+
body
|
|
23310
|
+
};
|
|
23311
|
+
}
|
|
23312
|
+
parseFrontmatterFields(frontmatterLines) {
|
|
23313
|
+
const fields = {};
|
|
23314
|
+
for (const line of frontmatterLines) {
|
|
23315
|
+
const parsed = this.parseFrontmatterLine(line);
|
|
23316
|
+
if (!parsed) {
|
|
23317
|
+
continue;
|
|
23318
|
+
}
|
|
23319
|
+
const { valuePart } = this.splitYamlInlineComment(parsed.rawValue);
|
|
23320
|
+
fields[parsed.key] = this.deserializeFrontmatterValue(valuePart);
|
|
23321
|
+
}
|
|
23322
|
+
return fields;
|
|
23323
|
+
}
|
|
23324
|
+
async writeFileAtomically(absolutePath, content) {
|
|
23325
|
+
const tempPath = `${absolutePath}.tmp-${process.pid}-${Date.now()}`;
|
|
23326
|
+
await writeFile(tempPath, content, "utf8");
|
|
23327
|
+
await rename(tempPath, absolutePath);
|
|
23328
|
+
}
|
|
23200
23329
|
/**
|
|
23201
23330
|
* Normalise a raw YAML title value to a double-quoted scalar.
|
|
23202
23331
|
* Handles: empty, already double-quoted, single-quoted (unescaping `''`),
|
|
@@ -23267,14 +23396,66 @@ Output: ${stdout.slice(0, 1e3)}`
|
|
|
23267
23396
|
await writeFile(absolutePath, fixed, "utf8");
|
|
23268
23397
|
return { path: absolutePath, bytes: Buffer.byteLength(fixed, "utf8") };
|
|
23269
23398
|
}
|
|
23399
|
+
async updateBeanFrontmatter(relativePath, updates) {
|
|
23400
|
+
const absolutePath = this.resolveBeanFilePath(relativePath);
|
|
23401
|
+
const content = await readFile(absolutePath, "utf8");
|
|
23402
|
+
const { eol, hasFrontmatter, frontmatterLines, body } = this.splitFrontmatterDocument(content);
|
|
23403
|
+
const updatedFields = Object.entries(updates).filter(([, value]) => value !== void 0).map(([key]) => key);
|
|
23404
|
+
if (updatedFields.length === 0) {
|
|
23405
|
+
throw new Error("At least one frontmatter field update is required");
|
|
23406
|
+
}
|
|
23407
|
+
const nextLines = [...frontmatterLines];
|
|
23408
|
+
let indexByKey = this.buildFrontmatterIndex(nextLines);
|
|
23409
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
23410
|
+
if (value === void 0) {
|
|
23411
|
+
continue;
|
|
23412
|
+
}
|
|
23413
|
+
const existingIndex = indexByKey.get(key);
|
|
23414
|
+
if (value === null) {
|
|
23415
|
+
if (existingIndex !== void 0) {
|
|
23416
|
+
nextLines.splice(existingIndex, 1);
|
|
23417
|
+
indexByKey = this.buildFrontmatterIndex(nextLines);
|
|
23418
|
+
}
|
|
23419
|
+
continue;
|
|
23420
|
+
}
|
|
23421
|
+
const serialized = `${key}: ${this.serializeFrontmatterValue(key, value)}`;
|
|
23422
|
+
if (existingIndex !== void 0) {
|
|
23423
|
+
const existingLine = nextLines[existingIndex] ?? "";
|
|
23424
|
+
const existingParsed = this.parseFrontmatterLine(existingLine);
|
|
23425
|
+
const commentPart = existingParsed ? this.splitYamlInlineComment(existingParsed.rawValue).commentPart : "";
|
|
23426
|
+
nextLines[existingIndex] = `${serialized}${commentPart}`;
|
|
23427
|
+
} else {
|
|
23428
|
+
nextLines.push(serialized);
|
|
23429
|
+
indexByKey.set(key, nextLines.length - 1);
|
|
23430
|
+
}
|
|
23431
|
+
}
|
|
23432
|
+
const frontmatterBlock = nextLines.length > 0 ? nextLines.join(eol) : "";
|
|
23433
|
+
const nextContent = hasFrontmatter ? `---${eol}${frontmatterBlock}${eol}---${body}` : `---${eol}${frontmatterBlock}${eol}---${eol}${body}`;
|
|
23434
|
+
const fixed = this.quoteFrontmatterTitles(nextContent);
|
|
23435
|
+
await this.writeFileAtomically(absolutePath, fixed);
|
|
23436
|
+
return {
|
|
23437
|
+
path: absolutePath,
|
|
23438
|
+
bytes: Buffer.byteLength(fixed, "utf8"),
|
|
23439
|
+
updatedFields,
|
|
23440
|
+
frontmatter: this.parseFrontmatterFields(this.splitFrontmatterDocument(fixed).frontmatterLines)
|
|
23441
|
+
};
|
|
23442
|
+
}
|
|
23270
23443
|
async createBeanFile(relativePath, content, options) {
|
|
23271
23444
|
const absolutePath = this.resolveBeanFilePath(relativePath);
|
|
23272
23445
|
const fixed = this.quoteFrontmatterTitles(content);
|
|
23273
23446
|
await mkdir(dirname(absolutePath), { recursive: true });
|
|
23274
|
-
|
|
23275
|
-
|
|
23276
|
-
|
|
23277
|
-
|
|
23447
|
+
try {
|
|
23448
|
+
await writeFile(absolutePath, fixed, {
|
|
23449
|
+
encoding: "utf8",
|
|
23450
|
+
flag: options?.overwrite ? "w" : "wx"
|
|
23451
|
+
});
|
|
23452
|
+
} catch (error48) {
|
|
23453
|
+
const maybeNodeError = error48;
|
|
23454
|
+
if (maybeNodeError.code === "EEXIST" && !options?.overwrite) {
|
|
23455
|
+
throw new Error("Bean file already exists. Pass overwrite=true to replace it.");
|
|
23456
|
+
}
|
|
23457
|
+
throw error48;
|
|
23458
|
+
}
|
|
23278
23459
|
return {
|
|
23279
23460
|
path: absolutePath,
|
|
23280
23461
|
bytes: Buffer.byteLength(fixed, "utf8"),
|
|
@@ -31365,32 +31546,43 @@ import { promisify as promisify2 } from "util";
|
|
|
31365
31546
|
// package.json
|
|
31366
31547
|
var package_default = {
|
|
31367
31548
|
name: "@selfagency/beans-mcp",
|
|
31368
|
-
version: "0.
|
|
31549
|
+
version: "0.6.0",
|
|
31369
31550
|
private: false,
|
|
31370
31551
|
description: "MCP (Model Context Protocol) server for Beans issue tracker",
|
|
31552
|
+
keywords: [
|
|
31553
|
+
"ai",
|
|
31554
|
+
"beans",
|
|
31555
|
+
"issue-tracker",
|
|
31556
|
+
"mcp",
|
|
31557
|
+
"model-context-protocol"
|
|
31558
|
+
],
|
|
31559
|
+
homepage: "https://github.com/selfagency/beans-mcp",
|
|
31560
|
+
bugs: {
|
|
31561
|
+
url: "https://github.com/selfagency/beans-mcp/issues"
|
|
31562
|
+
},
|
|
31563
|
+
license: "MIT",
|
|
31371
31564
|
author: {
|
|
31372
31565
|
name: "Daniel Sieradski",
|
|
31373
31566
|
email: "daniel@self.agency",
|
|
31374
31567
|
url: "https://self.agency"
|
|
31375
31568
|
},
|
|
31376
|
-
homepage: "https://github.com/selfagency/beans-mcp",
|
|
31377
|
-
bugs: {
|
|
31378
|
-
url: "https://github.com/selfagency/beans-mcp/issues"
|
|
31379
|
-
},
|
|
31380
31569
|
repository: {
|
|
31381
31570
|
type: "git",
|
|
31382
31571
|
url: "git+https://github.com/selfagency/beans-mcp.git"
|
|
31383
31572
|
},
|
|
31384
|
-
|
|
31385
|
-
|
|
31386
|
-
|
|
31387
|
-
|
|
31388
|
-
"
|
|
31389
|
-
"
|
|
31390
|
-
"
|
|
31573
|
+
bin: {
|
|
31574
|
+
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31575
|
+
},
|
|
31576
|
+
files: [
|
|
31577
|
+
"dist",
|
|
31578
|
+
"skills",
|
|
31579
|
+
"README.md",
|
|
31580
|
+
"LICENSE.txt"
|
|
31391
31581
|
],
|
|
31392
|
-
license: "MIT",
|
|
31393
31582
|
type: "module",
|
|
31583
|
+
main: "./dist/index.cjs",
|
|
31584
|
+
module: "./dist/index.js",
|
|
31585
|
+
types: "./dist/index.d.ts",
|
|
31394
31586
|
exports: {
|
|
31395
31587
|
".": {
|
|
31396
31588
|
types: "./dist/index.d.ts",
|
|
@@ -31398,14 +31590,11 @@ var package_default = {
|
|
|
31398
31590
|
require: "./dist/index.cjs"
|
|
31399
31591
|
}
|
|
31400
31592
|
},
|
|
31401
|
-
main: "./dist/index.cjs",
|
|
31402
|
-
module: "./dist/index.js",
|
|
31403
|
-
types: "./dist/index.d.ts",
|
|
31404
|
-
bin: {
|
|
31405
|
-
"beans-mcp": "dist/beans-mcp-server.cjs"
|
|
31406
|
-
},
|
|
31407
31593
|
scripts: {
|
|
31408
31594
|
build: "tsup",
|
|
31595
|
+
"docs:dev": "vitepress dev docs",
|
|
31596
|
+
"docs:build": "vitepress build docs",
|
|
31597
|
+
"docs:preview": "vitepress preview docs",
|
|
31409
31598
|
format: "oxfmt",
|
|
31410
31599
|
"lint:fix": "oxlint --fix",
|
|
31411
31600
|
lint: "oxlint",
|
|
@@ -31420,30 +31609,32 @@ var package_default = {
|
|
|
31420
31609
|
devDependencies: {
|
|
31421
31610
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
31422
31611
|
"@octokit/rest": "^22.0.1",
|
|
31423
|
-
"@types/node": "25.
|
|
31424
|
-
"@vitest/coverage-v8": "^4.1.
|
|
31425
|
-
"@vitest/ui": "4.1.
|
|
31612
|
+
"@types/node": "25.6.0",
|
|
31613
|
+
"@vitest/coverage-v8": "^4.1.4",
|
|
31614
|
+
"@vitest/ui": "4.1.4",
|
|
31426
31615
|
husky: "^9.1.7",
|
|
31427
31616
|
"lint-staged": "^16.4.0",
|
|
31428
31617
|
ora: "^9.3.0",
|
|
31429
|
-
oxfmt: "^0.
|
|
31430
|
-
oxlint: "^1.
|
|
31431
|
-
"oxlint-tsgolint": "^0.
|
|
31618
|
+
oxfmt: "^0.45.0",
|
|
31619
|
+
oxlint: "^1.60.0",
|
|
31620
|
+
"oxlint-tsgolint": "^0.21.1",
|
|
31432
31621
|
tsup: "8.5.1",
|
|
31433
|
-
typescript: "6.0.
|
|
31434
|
-
|
|
31622
|
+
typescript: "6.0.3",
|
|
31623
|
+
vitepress: "^1.6.4",
|
|
31624
|
+
vitest: "4.1.4",
|
|
31435
31625
|
zod: "4.3.6",
|
|
31436
31626
|
zx: "^8.8.5"
|
|
31437
31627
|
},
|
|
31438
|
-
engines: {
|
|
31439
|
-
node: ">=18"
|
|
31440
|
-
},
|
|
31441
31628
|
"lint-staged": {
|
|
31442
31629
|
"src/**/*.ts": [
|
|
31443
31630
|
"pnpm run lint:fix",
|
|
31444
31631
|
"pnpm run format"
|
|
31445
31632
|
]
|
|
31446
|
-
}
|
|
31633
|
+
},
|
|
31634
|
+
engines: {
|
|
31635
|
+
node: ">=18"
|
|
31636
|
+
},
|
|
31637
|
+
mcpName: "io.github.selfagency/beans-mcp"
|
|
31447
31638
|
};
|
|
31448
31639
|
|
|
31449
31640
|
// src/internal/queryHelpers.ts
|
|
@@ -31608,6 +31799,8 @@ var MAX_PATH_LENGTH = 1024;
|
|
|
31608
31799
|
init_utils();
|
|
31609
31800
|
var execFileAsync2 = promisify2(execFile2);
|
|
31610
31801
|
var PACKAGE_VERSION = package_default.version ?? "0.0.0-dev";
|
|
31802
|
+
var CLOSED_STATUSES = /* @__PURE__ */ new Set(["completed", "scrapped"]);
|
|
31803
|
+
var BEAN_ID_HINT = "Missing required field `beanId`. Did you mean `beanId`?";
|
|
31611
31804
|
function getSafeCliEnv(env) {
|
|
31612
31805
|
const whitelist = ["PATH", "HOME", "USER", "LANG", "LC_ALL", "LC_CTYPE", "SHELL"];
|
|
31613
31806
|
const safeEnv = {};
|
|
@@ -31628,7 +31821,8 @@ function extractVersionFromOutput(output) {
|
|
|
31628
31821
|
if (!trimmed) {
|
|
31629
31822
|
return null;
|
|
31630
31823
|
}
|
|
31631
|
-
const
|
|
31824
|
+
const versionRegex = /(?:^|[^\d])v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)/;
|
|
31825
|
+
const match = versionRegex.exec(trimmed);
|
|
31632
31826
|
return match?.[1] ?? null;
|
|
31633
31827
|
}
|
|
31634
31828
|
async function detectBeansCliVersion(cliPath, workspaceRoot) {
|
|
@@ -31645,10 +31839,10 @@ ${stderr}`);
|
|
|
31645
31839
|
return null;
|
|
31646
31840
|
}
|
|
31647
31841
|
}
|
|
31648
|
-
async function getBeanById(backend, beanId) {
|
|
31842
|
+
async function getBeanById(backend, beanId, beans) {
|
|
31649
31843
|
try {
|
|
31650
|
-
const
|
|
31651
|
-
const found =
|
|
31844
|
+
const allBeans = beans ?? await backend.list();
|
|
31845
|
+
const found = allBeans.find((b) => b.id === beanId);
|
|
31652
31846
|
if (!found) {
|
|
31653
31847
|
throw new Error(`Bean not found: ${beanId}`);
|
|
31654
31848
|
}
|
|
@@ -31657,12 +31851,111 @@ async function getBeanById(backend, beanId) {
|
|
|
31657
31851
|
throw new Error(`Failed to fetch bean ${beanId}: ${error48.message}`);
|
|
31658
31852
|
}
|
|
31659
31853
|
}
|
|
31854
|
+
function collectDescendantBeans(beans, rootBeanId) {
|
|
31855
|
+
const byParent = /* @__PURE__ */ new Map();
|
|
31856
|
+
const byId = new Map(beans.map((bean) => [bean.id, bean]));
|
|
31857
|
+
for (const bean of beans) {
|
|
31858
|
+
if (!bean.parentId) {
|
|
31859
|
+
continue;
|
|
31860
|
+
}
|
|
31861
|
+
const children = byParent.get(bean.parentId) ?? [];
|
|
31862
|
+
children.push(bean.id);
|
|
31863
|
+
byParent.set(bean.parentId, children);
|
|
31864
|
+
}
|
|
31865
|
+
const queue = [...byParent.get(rootBeanId) ?? []];
|
|
31866
|
+
const visited = /* @__PURE__ */ new Set();
|
|
31867
|
+
const descendants = [];
|
|
31868
|
+
while (queue.length > 0) {
|
|
31869
|
+
const currentId = queue.shift();
|
|
31870
|
+
if (!currentId || visited.has(currentId)) {
|
|
31871
|
+
continue;
|
|
31872
|
+
}
|
|
31873
|
+
visited.add(currentId);
|
|
31874
|
+
const currentBean = byId.get(currentId);
|
|
31875
|
+
if (!currentBean) {
|
|
31876
|
+
continue;
|
|
31877
|
+
}
|
|
31878
|
+
descendants.push(currentBean);
|
|
31879
|
+
const children = byParent.get(currentId);
|
|
31880
|
+
if (children && children.length > 0) {
|
|
31881
|
+
queue.push(...children);
|
|
31882
|
+
}
|
|
31883
|
+
}
|
|
31884
|
+
return descendants;
|
|
31885
|
+
}
|
|
31886
|
+
async function cascadeStatusToDescendants(backend, rootBeanId, targetStatus, options) {
|
|
31887
|
+
const beans = options?.beans ?? await backend.list();
|
|
31888
|
+
const descendants = collectDescendantBeans(beans, rootBeanId);
|
|
31889
|
+
const updatedBeanIds = [];
|
|
31890
|
+
const skippedBeanIds = [];
|
|
31891
|
+
const errors = [];
|
|
31892
|
+
const toUpdate = [];
|
|
31893
|
+
for (const bean of descendants) {
|
|
31894
|
+
if (options?.onlyCurrentStatuses && !options.onlyCurrentStatuses.has(bean.status)) {
|
|
31895
|
+
skippedBeanIds.push(bean.id);
|
|
31896
|
+
continue;
|
|
31897
|
+
}
|
|
31898
|
+
toUpdate.push(bean);
|
|
31899
|
+
}
|
|
31900
|
+
const settled = await Promise.allSettled(
|
|
31901
|
+
toUpdate.map(async (bean) => backend.update(bean.id, { status: targetStatus }))
|
|
31902
|
+
);
|
|
31903
|
+
settled.forEach((result, index) => {
|
|
31904
|
+
const bean = toUpdate[index];
|
|
31905
|
+
if (!bean) {
|
|
31906
|
+
return;
|
|
31907
|
+
}
|
|
31908
|
+
if (result.status === "fulfilled") {
|
|
31909
|
+
updatedBeanIds.push(bean.id);
|
|
31910
|
+
return;
|
|
31911
|
+
}
|
|
31912
|
+
errors.push({
|
|
31913
|
+
beanId: bean.id,
|
|
31914
|
+
error: result.reason instanceof Error ? result.reason.message : String(result.reason)
|
|
31915
|
+
});
|
|
31916
|
+
});
|
|
31917
|
+
return {
|
|
31918
|
+
totalDescendants: descendants.length,
|
|
31919
|
+
updatedBeanIds,
|
|
31920
|
+
skippedBeanIds,
|
|
31921
|
+
errors
|
|
31922
|
+
};
|
|
31923
|
+
}
|
|
31924
|
+
function completeMarkdownTasks(body) {
|
|
31925
|
+
const lines = body.split(/\r?\n/);
|
|
31926
|
+
let totalTaskCount = 0;
|
|
31927
|
+
let updatedTaskCount = 0;
|
|
31928
|
+
const taskLinePattern = /^\s*(?:[-*+]|\d+\.)\s+\[[ xX]\]/;
|
|
31929
|
+
const uncheckedTaskLinePattern = /^(\s*(?:[-*+]|\d+\.)\s+\[)\s(\].*)$/;
|
|
31930
|
+
const nextLines = lines.map((line) => {
|
|
31931
|
+
if (!taskLinePattern.test(line)) {
|
|
31932
|
+
return line;
|
|
31933
|
+
}
|
|
31934
|
+
totalTaskCount += 1;
|
|
31935
|
+
const uncheckedMatch = uncheckedTaskLinePattern.exec(line);
|
|
31936
|
+
if (!uncheckedMatch) {
|
|
31937
|
+
return line;
|
|
31938
|
+
}
|
|
31939
|
+
updatedTaskCount += 1;
|
|
31940
|
+
return `${uncheckedMatch[1]}x${uncheckedMatch[2]}`;
|
|
31941
|
+
});
|
|
31942
|
+
const nextBody = nextLines.join("\n");
|
|
31943
|
+
return { nextBody, totalTaskCount, updatedTaskCount };
|
|
31944
|
+
}
|
|
31660
31945
|
function initHandler(backend) {
|
|
31661
31946
|
return async ({ prefix }) => {
|
|
31662
31947
|
const result = await backend.init(prefix);
|
|
31663
31948
|
return makeTextAndStructured(result);
|
|
31664
31949
|
};
|
|
31665
31950
|
}
|
|
31951
|
+
function archiveHandler(backend) {
|
|
31952
|
+
return async () => {
|
|
31953
|
+
if (typeof backend.archive !== "function") {
|
|
31954
|
+
throw new TypeError("Archive is not supported by the current backend");
|
|
31955
|
+
}
|
|
31956
|
+
return makeTextAndStructured(await backend.archive());
|
|
31957
|
+
};
|
|
31958
|
+
}
|
|
31666
31959
|
function viewHandler(backend) {
|
|
31667
31960
|
return async ({ beanId, beanIds }) => {
|
|
31668
31961
|
const ids = Array.isArray(beanIds) && beanIds.length > 0 ? beanIds : beanId ? [beanId] : [];
|
|
@@ -31700,7 +31993,14 @@ async function checkVersionCompatibility(cliPath, workspaceRoot, detector) {
|
|
|
31700
31993
|
}
|
|
31701
31994
|
}
|
|
31702
31995
|
function createHandler(backend) {
|
|
31703
|
-
return async (input) =>
|
|
31996
|
+
return async (input) => {
|
|
31997
|
+
const bean = await backend.create(input);
|
|
31998
|
+
const warnings = input.description !== void 0 ? ["`description` is deprecated; use `body` instead."] : void 0;
|
|
31999
|
+
return makeTextAndStructured({
|
|
32000
|
+
bean,
|
|
32001
|
+
...warnings ? { warnings } : {}
|
|
32002
|
+
});
|
|
32003
|
+
};
|
|
31704
32004
|
}
|
|
31705
32005
|
function editHandler(backend) {
|
|
31706
32006
|
return async ({
|
|
@@ -31714,18 +32014,30 @@ function reopenHandler(backend) {
|
|
|
31714
32014
|
requiredCurrentStatus,
|
|
31715
32015
|
targetStatus
|
|
31716
32016
|
}) => {
|
|
31717
|
-
const
|
|
31718
|
-
|
|
31719
|
-
|
|
32017
|
+
const beans = await backend.list();
|
|
32018
|
+
const bean = await getBeanById(backend, beanId, beans);
|
|
32019
|
+
if (bean.status === requiredCurrentStatus) {
|
|
32020
|
+
const updatedParentBean = await backend.update(beanId, { status: targetStatus });
|
|
32021
|
+
const cascade = await cascadeStatusToDescendants(backend, beanId, targetStatus, {
|
|
32022
|
+
onlyCurrentStatuses: CLOSED_STATUSES,
|
|
32023
|
+
beans
|
|
32024
|
+
});
|
|
32025
|
+
return makeTextAndStructured({
|
|
32026
|
+
bean: updatedParentBean,
|
|
32027
|
+
cascade: {
|
|
32028
|
+
totalDescendants: cascade.totalDescendants,
|
|
32029
|
+
updatedBeanIds: cascade.updatedBeanIds,
|
|
32030
|
+
skippedBeanIds: cascade.skippedBeanIds,
|
|
32031
|
+
errors: cascade.errors
|
|
32032
|
+
}
|
|
32033
|
+
});
|
|
31720
32034
|
}
|
|
31721
|
-
|
|
31722
|
-
bean: await backend.update(beanId, { status: targetStatus })
|
|
31723
|
-
});
|
|
32035
|
+
throw new Error(`Bean ${beanId} is not ${requiredCurrentStatus}`);
|
|
31724
32036
|
};
|
|
31725
32037
|
}
|
|
31726
32038
|
function updateHandler(backend) {
|
|
31727
|
-
return async (input) =>
|
|
31728
|
-
|
|
32039
|
+
return async (input) => {
|
|
32040
|
+
const updatedBean = await backend.update(input.beanId, {
|
|
31729
32041
|
status: input.status,
|
|
31730
32042
|
type: input.type,
|
|
31731
32043
|
priority: input.priority,
|
|
@@ -31737,8 +32049,37 @@ function updateHandler(backend) {
|
|
|
31737
32049
|
bodyAppend: input.bodyAppend,
|
|
31738
32050
|
bodyReplace: input.bodyReplace,
|
|
31739
32051
|
ifMatch: input.ifMatch
|
|
31740
|
-
})
|
|
31741
|
-
|
|
32052
|
+
});
|
|
32053
|
+
const closeStatus = input.status;
|
|
32054
|
+
const shouldCascadeClose = Boolean(closeStatus && CLOSED_STATUSES.has(closeStatus));
|
|
32055
|
+
const cascade = shouldCascadeClose ? await cascadeStatusToDescendants(backend, input.beanId, closeStatus, {
|
|
32056
|
+
beans: await backend.list()
|
|
32057
|
+
}) : null;
|
|
32058
|
+
return makeTextAndStructured({
|
|
32059
|
+
bean: updatedBean,
|
|
32060
|
+
...cascade ? {
|
|
32061
|
+
cascade: {
|
|
32062
|
+
totalDescendants: cascade.totalDescendants,
|
|
32063
|
+
updatedBeanIds: cascade.updatedBeanIds,
|
|
32064
|
+
skippedBeanIds: cascade.skippedBeanIds,
|
|
32065
|
+
errors: cascade.errors
|
|
32066
|
+
}
|
|
32067
|
+
} : {}
|
|
32068
|
+
});
|
|
32069
|
+
};
|
|
32070
|
+
}
|
|
32071
|
+
function completeTasksHandler(backend) {
|
|
32072
|
+
return async ({ beanId }) => {
|
|
32073
|
+
const bean = await getBeanById(backend, beanId);
|
|
32074
|
+
const { nextBody, totalTaskCount, updatedTaskCount } = completeMarkdownTasks(bean.body || "");
|
|
32075
|
+
const updatedBean = updatedTaskCount > 0 ? await backend.update(beanId, { body: nextBody }) : bean;
|
|
32076
|
+
return makeTextAndStructured({
|
|
32077
|
+
bean: updatedBean,
|
|
32078
|
+
totalTaskCount,
|
|
32079
|
+
updatedTaskCount,
|
|
32080
|
+
unchangedTaskCount: totalTaskCount - updatedTaskCount
|
|
32081
|
+
});
|
|
32082
|
+
};
|
|
31742
32083
|
}
|
|
31743
32084
|
function deleteHandler(backend) {
|
|
31744
32085
|
return async ({ beanId, beanIds, force }) => {
|
|
@@ -31792,11 +32133,17 @@ function deleteHandler(backend) {
|
|
|
31792
32133
|
function bulkCreateHandler(backend) {
|
|
31793
32134
|
return async (input) => {
|
|
31794
32135
|
const results = await backend.bulkCreate(input.beans, input.parent);
|
|
32136
|
+
const deprecatedDescriptionCount = input.beans.filter((bean) => bean.description !== void 0).length;
|
|
31795
32137
|
return makeTextAndStructured({
|
|
31796
32138
|
results,
|
|
31797
32139
|
requestedCount: input.beans.length,
|
|
31798
32140
|
successCount: results.filter((r) => r.bean).length,
|
|
31799
|
-
failedCount: results.filter((r) => r.error).length
|
|
32141
|
+
failedCount: results.filter((r) => r.error).length,
|
|
32142
|
+
...deprecatedDescriptionCount > 0 ? {
|
|
32143
|
+
warnings: [
|
|
32144
|
+
`Found ${deprecatedDescriptionCount} bean(s) using deprecated field \`description\`; use \`body\` instead.`
|
|
32145
|
+
]
|
|
32146
|
+
} : {}
|
|
31800
32147
|
});
|
|
31801
32148
|
};
|
|
31802
32149
|
}
|
|
@@ -31812,14 +32159,24 @@ function bulkUpdateHandler(backend) {
|
|
|
31812
32159
|
};
|
|
31813
32160
|
}
|
|
31814
32161
|
function queryHandler(backend) {
|
|
31815
|
-
return async (opts) =>
|
|
32162
|
+
return async (opts) => {
|
|
32163
|
+
if (opts.operation === "graphql") {
|
|
32164
|
+
if (typeof backend.queryGraphql !== "function") {
|
|
32165
|
+
throw new TypeError("GraphQL passthrough is not supported by the current backend");
|
|
32166
|
+
}
|
|
32167
|
+
const result = await backend.queryGraphql(opts.graphql || "", opts.variables);
|
|
32168
|
+
return makeTextAndStructured({ data: result.data, errors: result.errors ?? [] });
|
|
32169
|
+
}
|
|
32170
|
+
return handleQueryOperation(backend, opts);
|
|
32171
|
+
};
|
|
31816
32172
|
}
|
|
31817
32173
|
function beanFileHandler(backend) {
|
|
31818
32174
|
return async ({
|
|
31819
32175
|
operation,
|
|
31820
32176
|
path,
|
|
31821
32177
|
content,
|
|
31822
|
-
overwrite
|
|
32178
|
+
overwrite,
|
|
32179
|
+
fields
|
|
31823
32180
|
}) => {
|
|
31824
32181
|
if (operation === "read") {
|
|
31825
32182
|
return makeTextAndStructured(await backend.readBeanFile(path));
|
|
@@ -31830,6 +32187,9 @@ function beanFileHandler(backend) {
|
|
|
31830
32187
|
if (operation === "create") {
|
|
31831
32188
|
return makeTextAndStructured(await backend.createBeanFile(path, content || "", { overwrite }));
|
|
31832
32189
|
}
|
|
32190
|
+
if (operation === "update_frontmatter") {
|
|
32191
|
+
return makeTextAndStructured(await backend.updateBeanFrontmatter(path, fields || {}));
|
|
32192
|
+
}
|
|
31833
32193
|
if (operation === "delete") {
|
|
31834
32194
|
return makeTextAndStructured(await backend.deleteBeanFile(path));
|
|
31835
32195
|
}
|
|
@@ -31864,6 +32224,21 @@ function registerTools(server, backend) {
|
|
|
31864
32224
|
},
|
|
31865
32225
|
initHandler(backend)
|
|
31866
32226
|
);
|
|
32227
|
+
server.registerTool(
|
|
32228
|
+
"beans_archive",
|
|
32229
|
+
{
|
|
32230
|
+
title: "Archive Beans",
|
|
32231
|
+
description: "Archive completed or scrapped beans, equivalent to the beans CLI archive command.",
|
|
32232
|
+
inputSchema: external_exports3.object({}),
|
|
32233
|
+
annotations: {
|
|
32234
|
+
readOnlyHint: false,
|
|
32235
|
+
destructiveHint: false,
|
|
32236
|
+
idempotentHint: false,
|
|
32237
|
+
openWorldHint: false
|
|
32238
|
+
}
|
|
32239
|
+
},
|
|
32240
|
+
archiveHandler(backend)
|
|
32241
|
+
);
|
|
31867
32242
|
server.registerTool(
|
|
31868
32243
|
"beans_view",
|
|
31869
32244
|
{
|
|
@@ -31873,7 +32248,7 @@ function registerTools(server, backend) {
|
|
|
31873
32248
|
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31874
32249
|
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional()
|
|
31875
32250
|
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
31876
|
-
message:
|
|
32251
|
+
message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
|
|
31877
32252
|
}),
|
|
31878
32253
|
annotations: {
|
|
31879
32254
|
readOnlyHint: true,
|
|
@@ -31913,7 +32288,7 @@ function registerTools(server, backend) {
|
|
|
31913
32288
|
title: "Edit Bean Metadata",
|
|
31914
32289
|
description: "Update bean metadata fields (status/type/priority/parent/blocking).",
|
|
31915
32290
|
inputSchema: external_exports3.object({
|
|
31916
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32291
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31917
32292
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31918
32293
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31919
32294
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -31921,7 +32296,11 @@ function registerTools(server, backend) {
|
|
|
31921
32296
|
clearParent: external_exports3.boolean().optional(),
|
|
31922
32297
|
blocking: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional(),
|
|
31923
32298
|
blockedBy: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).optional()
|
|
31924
|
-
}),
|
|
32299
|
+
}).superRefine((input, ctx) => {
|
|
32300
|
+
if (!input.beanId) {
|
|
32301
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32302
|
+
}
|
|
32303
|
+
}).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
31925
32304
|
annotations: {
|
|
31926
32305
|
readOnlyHint: false,
|
|
31927
32306
|
destructiveHint: false,
|
|
@@ -31937,10 +32316,14 @@ function registerTools(server, backend) {
|
|
|
31937
32316
|
title: "Reopen Bean",
|
|
31938
32317
|
description: "Reopen a completed or scrapped bean into a non-closed status.",
|
|
31939
32318
|
inputSchema: external_exports3.object({
|
|
31940
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32319
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31941
32320
|
requiredCurrentStatus: external_exports3.enum(["completed", "scrapped"]),
|
|
31942
32321
|
targetStatus: external_exports3.string().max(MAX_METADATA_LENGTH).default("todo")
|
|
31943
|
-
}),
|
|
32322
|
+
}).superRefine((input, ctx) => {
|
|
32323
|
+
if (!input.beanId) {
|
|
32324
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32325
|
+
}
|
|
32326
|
+
}).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
31944
32327
|
annotations: {
|
|
31945
32328
|
readOnlyHint: false,
|
|
31946
32329
|
destructiveHint: false,
|
|
@@ -31956,7 +32339,7 @@ function registerTools(server, backend) {
|
|
|
31956
32339
|
title: "Update Bean",
|
|
31957
32340
|
description: "Update bean metadata fields (status/type/priority/parent/blocking). Consolidated replacement for per-field update tools.",
|
|
31958
32341
|
inputSchema: external_exports3.object({
|
|
31959
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32342
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
31960
32343
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31961
32344
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
31962
32345
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -31973,12 +32356,16 @@ function registerTools(server, backend) {
|
|
|
31973
32356
|
})
|
|
31974
32357
|
).optional(),
|
|
31975
32358
|
ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
|
|
32359
|
+
}).superRefine((input, ctx) => {
|
|
32360
|
+
if (!input.beanId) {
|
|
32361
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32362
|
+
}
|
|
31976
32363
|
}).refine(
|
|
31977
32364
|
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
31978
32365
|
{
|
|
31979
32366
|
message: "body cannot be combined with bodyAppend/bodyReplace"
|
|
31980
32367
|
}
|
|
31981
|
-
),
|
|
32368
|
+
).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
31982
32369
|
annotations: {
|
|
31983
32370
|
readOnlyHint: false,
|
|
31984
32371
|
destructiveHint: false,
|
|
@@ -31998,7 +32385,7 @@ function registerTools(server, backend) {
|
|
|
31998
32385
|
beanIds: external_exports3.array(external_exports3.string().min(1).max(MAX_ID_LENGTH)).optional(),
|
|
31999
32386
|
force: external_exports3.boolean().default(false)
|
|
32000
32387
|
}).refine((input) => Boolean(input.beanId) || Array.isArray(input.beanIds) && input.beanIds.length > 0, {
|
|
32001
|
-
message:
|
|
32388
|
+
message: `Either beanId or beanIds must be provided. ${BEAN_ID_HINT}`
|
|
32002
32389
|
}),
|
|
32003
32390
|
annotations: {
|
|
32004
32391
|
readOnlyHint: false,
|
|
@@ -32037,7 +32424,7 @@ function registerTools(server, backend) {
|
|
|
32037
32424
|
bulkCreateHandler(backend)
|
|
32038
32425
|
);
|
|
32039
32426
|
const beanUpdateItemSchema = external_exports3.object({
|
|
32040
|
-
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH),
|
|
32427
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional(),
|
|
32041
32428
|
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32042
32429
|
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32043
32430
|
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
@@ -32049,10 +32436,14 @@ function registerTools(server, backend) {
|
|
|
32049
32436
|
bodyAppend: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32050
32437
|
bodyReplace: external_exports3.array(external_exports3.object({ old: external_exports3.string().max(MAX_DESCRIPTION_LENGTH), new: external_exports3.string().max(MAX_DESCRIPTION_LENGTH) })).optional(),
|
|
32051
32438
|
ifMatch: external_exports3.string().max(MAX_METADATA_LENGTH).optional()
|
|
32439
|
+
}).superRefine((input, ctx) => {
|
|
32440
|
+
if (!input.beanId) {
|
|
32441
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32442
|
+
}
|
|
32052
32443
|
}).refine(
|
|
32053
32444
|
(input) => !(input.body !== void 0 && (input.bodyAppend !== void 0 || input.bodyReplace !== void 0)),
|
|
32054
32445
|
{ message: "body cannot be combined with bodyAppend/bodyReplace" }
|
|
32055
|
-
);
|
|
32446
|
+
).transform((input) => ({ ...input, beanId: input.beanId }));
|
|
32056
32447
|
server.registerTool(
|
|
32057
32448
|
"beans_bulk_update",
|
|
32058
32449
|
{
|
|
@@ -32071,25 +32462,56 @@ function registerTools(server, backend) {
|
|
|
32071
32462
|
},
|
|
32072
32463
|
bulkUpdateHandler(backend)
|
|
32073
32464
|
);
|
|
32465
|
+
server.registerTool(
|
|
32466
|
+
"beans_complete_tasks",
|
|
32467
|
+
{
|
|
32468
|
+
title: "Complete Markdown Tasks",
|
|
32469
|
+
description: "Mark all markdown checklist tasks within a bean as completed.",
|
|
32470
|
+
inputSchema: external_exports3.object({
|
|
32471
|
+
beanId: external_exports3.string().min(1).max(MAX_ID_LENGTH).optional()
|
|
32472
|
+
}).superRefine((input, ctx) => {
|
|
32473
|
+
if (!input.beanId) {
|
|
32474
|
+
ctx.addIssue({ code: external_exports3.ZodIssueCode.custom, path: ["beanId"], message: BEAN_ID_HINT });
|
|
32475
|
+
}
|
|
32476
|
+
}).transform((input) => ({ ...input, beanId: input.beanId })),
|
|
32477
|
+
annotations: {
|
|
32478
|
+
readOnlyHint: false,
|
|
32479
|
+
destructiveHint: false,
|
|
32480
|
+
idempotentHint: true,
|
|
32481
|
+
openWorldHint: false
|
|
32482
|
+
}
|
|
32483
|
+
},
|
|
32484
|
+
completeTasksHandler(backend)
|
|
32485
|
+
);
|
|
32074
32486
|
server.registerTool(
|
|
32075
32487
|
"beans_query",
|
|
32076
32488
|
{
|
|
32077
32489
|
title: "Query Beans",
|
|
32078
32490
|
description: "Unified query tool for refresh, filter, search, and sort operations.",
|
|
32079
32491
|
inputSchema: external_exports3.object({
|
|
32080
|
-
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config"]).default("refresh"),
|
|
32492
|
+
operation: external_exports3.enum(["refresh", "filter", "search", "sort", "ready", "llm_context", "open_config", "graphql"]).default("refresh"),
|
|
32081
32493
|
mode: external_exports3.enum(["status-priority-type-title", "updated", "created", "id"]).optional(),
|
|
32082
32494
|
statuses: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32083
32495
|
types: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32084
32496
|
search: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
|
|
32085
32497
|
includeClosed: external_exports3.boolean().optional(),
|
|
32086
32498
|
tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32499
|
+
graphql: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32500
|
+
variables: external_exports3.record(external_exports3.string(), external_exports3.unknown()).optional(),
|
|
32087
32501
|
writeToWorkspaceInstructions: external_exports3.boolean().optional()
|
|
32502
|
+
}).superRefine((input, ctx) => {
|
|
32503
|
+
if (input.operation === "graphql" && (!input.graphql || input.graphql.trim().length === 0)) {
|
|
32504
|
+
ctx.addIssue({
|
|
32505
|
+
code: external_exports3.ZodIssueCode.custom,
|
|
32506
|
+
path: ["graphql"],
|
|
32507
|
+
message: "graphql query string is required when operation is graphql"
|
|
32508
|
+
});
|
|
32509
|
+
}
|
|
32088
32510
|
}),
|
|
32089
32511
|
annotations: {
|
|
32090
|
-
readOnlyHint:
|
|
32512
|
+
readOnlyHint: false,
|
|
32091
32513
|
destructiveHint: false,
|
|
32092
|
-
idempotentHint:
|
|
32514
|
+
idempotentHint: false,
|
|
32093
32515
|
openWorldHint: false
|
|
32094
32516
|
}
|
|
32095
32517
|
},
|
|
@@ -32101,10 +32523,33 @@ function registerTools(server, backend) {
|
|
|
32101
32523
|
title: "Bean File Operations",
|
|
32102
32524
|
description: "Read, create, edit, or delete files under .beans (operation param).",
|
|
32103
32525
|
inputSchema: external_exports3.object({
|
|
32104
|
-
operation: external_exports3.enum(["read", "edit", "create", "delete"]),
|
|
32526
|
+
operation: external_exports3.enum(["read", "edit", "create", "delete", "update_frontmatter"]),
|
|
32105
32527
|
path: external_exports3.string().min(1).max(MAX_PATH_LENGTH),
|
|
32106
32528
|
content: external_exports3.string().max(MAX_DESCRIPTION_LENGTH).optional(),
|
|
32107
|
-
overwrite: external_exports3.boolean().optional()
|
|
32529
|
+
overwrite: external_exports3.boolean().optional(),
|
|
32530
|
+
fields: external_exports3.object({
|
|
32531
|
+
title: external_exports3.string().max(MAX_TITLE_LENGTH).optional(),
|
|
32532
|
+
status: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32533
|
+
type: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32534
|
+
priority: external_exports3.string().max(MAX_METADATA_LENGTH).optional(),
|
|
32535
|
+
parent_id: external_exports3.string().max(MAX_ID_LENGTH).nullable().optional(),
|
|
32536
|
+
tags: external_exports3.array(external_exports3.string().max(MAX_METADATA_LENGTH)).nullable().optional(),
|
|
32537
|
+
blocking_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
|
|
32538
|
+
blocked_by_ids: external_exports3.array(external_exports3.string().max(MAX_ID_LENGTH)).nullable().optional(),
|
|
32539
|
+
pr: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional(),
|
|
32540
|
+
branch: external_exports3.string().max(MAX_TITLE_LENGTH).nullable().optional()
|
|
32541
|
+
}).optional()
|
|
32542
|
+
}).superRefine((input, ctx) => {
|
|
32543
|
+
if (input.operation === "update_frontmatter") {
|
|
32544
|
+
const fieldCount = Object.values(input.fields || {}).filter((value) => value !== void 0).length;
|
|
32545
|
+
if (fieldCount === 0) {
|
|
32546
|
+
ctx.addIssue({
|
|
32547
|
+
code: external_exports3.ZodIssueCode.custom,
|
|
32548
|
+
path: ["fields"],
|
|
32549
|
+
message: "At least one frontmatter field update is required"
|
|
32550
|
+
});
|
|
32551
|
+
}
|
|
32552
|
+
}
|
|
32108
32553
|
}),
|
|
32109
32554
|
annotations: {
|
|
32110
32555
|
readOnlyHint: false,
|
|
@@ -32144,6 +32589,18 @@ var MutableBackend = class {
|
|
|
32144
32589
|
init(prefix) {
|
|
32145
32590
|
return this.inner.init(prefix);
|
|
32146
32591
|
}
|
|
32592
|
+
archive() {
|
|
32593
|
+
if (typeof this.inner.archive === "function") {
|
|
32594
|
+
return this.inner.archive();
|
|
32595
|
+
}
|
|
32596
|
+
throw new TypeError("Archive is not supported by backend");
|
|
32597
|
+
}
|
|
32598
|
+
queryGraphql(query, variables) {
|
|
32599
|
+
if (typeof this.inner.queryGraphql === "function") {
|
|
32600
|
+
return this.inner.queryGraphql(query, variables);
|
|
32601
|
+
}
|
|
32602
|
+
throw new TypeError("GraphQL passthrough is not supported by backend");
|
|
32603
|
+
}
|
|
32147
32604
|
list(opts) {
|
|
32148
32605
|
return this.inner.list(opts);
|
|
32149
32606
|
}
|
|
@@ -32183,6 +32640,9 @@ var MutableBackend = class {
|
|
|
32183
32640
|
editBeanFile(path, content) {
|
|
32184
32641
|
return this.inner.editBeanFile(path, content);
|
|
32185
32642
|
}
|
|
32643
|
+
updateBeanFrontmatter(path, updates) {
|
|
32644
|
+
return this.inner.updateBeanFrontmatter(path, updates);
|
|
32645
|
+
}
|
|
32186
32646
|
createBeanFile(path, content, opts) {
|
|
32187
32647
|
return this.inner.createBeanFile(path, content, opts);
|
|
32188
32648
|
}
|
|
@@ -32249,6 +32709,16 @@ function parseCliArgs(argv) {
|
|
|
32249
32709
|
const envPort = Number.parseInt(process.env.BEANS_VSCODE_MCP_PORT || process.env.BEANS_MCP_PORT || "", 10);
|
|
32250
32710
|
let port = Number.isInteger(envPort) && envPort > 0 ? envPort : DEFAULT_MCP_PORT;
|
|
32251
32711
|
let logDir;
|
|
32712
|
+
const parseStrictPositiveInt = (raw, flagName) => {
|
|
32713
|
+
if (!/^\d+$/.test(raw)) {
|
|
32714
|
+
throw new Error(`Invalid value for ${flagName}: ${raw}`);
|
|
32715
|
+
}
|
|
32716
|
+
const parsed = Number.parseInt(raw, 10);
|
|
32717
|
+
if (!Number.isInteger(parsed) || parsed <= 0) {
|
|
32718
|
+
throw new Error(`Invalid value for ${flagName}: ${raw}`);
|
|
32719
|
+
}
|
|
32720
|
+
return parsed;
|
|
32721
|
+
};
|
|
32252
32722
|
for (let i = 0; i < argv.length; i += 1) {
|
|
32253
32723
|
const arg = argv[i];
|
|
32254
32724
|
if ((arg === "--workspace" || arg === "--workspace-root") && argv[i + 1]) {
|
|
@@ -32262,10 +32732,7 @@ function parseCliArgs(argv) {
|
|
|
32262
32732
|
}
|
|
32263
32733
|
i += 1;
|
|
32264
32734
|
} else if (arg === "--port" && argv[i + 1]) {
|
|
32265
|
-
|
|
32266
|
-
if (Number.isInteger(parsedPort) && parsedPort > 0) {
|
|
32267
|
-
port = parsedPort;
|
|
32268
|
-
}
|
|
32735
|
+
port = parseStrictPositiveInt(argv[i + 1], "--port");
|
|
32269
32736
|
i += 1;
|
|
32270
32737
|
} else if (arg === "--log-dir" && argv[i + 1]) {
|
|
32271
32738
|
logDir = argv[i + 1];
|
|
@@ -32328,6 +32795,7 @@ export {
|
|
|
32328
32795
|
DEFAULT_MCP_PORT,
|
|
32329
32796
|
MAX_ID_LENGTH,
|
|
32330
32797
|
MAX_METADATA_LENGTH,
|
|
32798
|
+
MAX_PATH_LENGTH,
|
|
32331
32799
|
MAX_TITLE_LENGTH,
|
|
32332
32800
|
createBeansMcpServer,
|
|
32333
32801
|
isPathWithinRoot,
|