@vheins/local-memory-mcp 0.19.13 → 0.19.15
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/dist/{chunk-H7BLOZB6.js → chunk-BPQ42JEQ.js} +325 -297
- package/dist/dashboard/server.js +1 -1
- package/dist/mcp/server.js +124 -28
- package/package.json +1 -1
|
@@ -1748,7 +1748,7 @@ var TaskEntity = class extends BaseEntity {
|
|
|
1748
1748
|
handleDuplicateTaskCode(err, taskCode, repo) {
|
|
1749
1749
|
if (isSqliteError(err) && err.code === "SQLITE_CONSTRAINT_UNIQUE") {
|
|
1750
1750
|
throw new Error(
|
|
1751
|
-
`Duplicate task_code: '${taskCode}' already exists in repository '${repo}'. The task_code must be unique within the repository.`
|
|
1751
|
+
`Duplicate task_code: '${taskCode}' already exists in repository '${repo}'. The task_code must be unique within the repository. Omit task_code to auto-generate a new unique code.`
|
|
1752
1752
|
);
|
|
1753
1753
|
}
|
|
1754
1754
|
throw err;
|
|
@@ -3462,10 +3462,94 @@ var RealVectorStore = class {
|
|
|
3462
3462
|
}
|
|
3463
3463
|
};
|
|
3464
3464
|
|
|
3465
|
-
// src/mcp/
|
|
3465
|
+
// src/mcp/prompts/loader.ts
|
|
3466
3466
|
import fs4 from "fs";
|
|
3467
3467
|
import path3 from "path";
|
|
3468
3468
|
import { fileURLToPath } from "url";
|
|
3469
|
+
import matter from "gray-matter";
|
|
3470
|
+
var __filename = fileURLToPath(import.meta.url);
|
|
3471
|
+
var __dirname = path3.dirname(__filename);
|
|
3472
|
+
function findPromptDir() {
|
|
3473
|
+
const candidates = [
|
|
3474
|
+
// Production if chunked into dist/
|
|
3475
|
+
"./prompts",
|
|
3476
|
+
// Production if inlined into dist/mcp/
|
|
3477
|
+
"../prompts",
|
|
3478
|
+
// Dev: /src/mcp/prompts/definitions (next to loader.ts)
|
|
3479
|
+
"./definitions"
|
|
3480
|
+
].map((relPath) => path3.resolve(__dirname, relPath));
|
|
3481
|
+
for (const dir of candidates) {
|
|
3482
|
+
if (fs4.existsSync(dir)) {
|
|
3483
|
+
const files = fs4.readdirSync(dir);
|
|
3484
|
+
if (files.some((f) => f.endsWith(".md"))) {
|
|
3485
|
+
return dir;
|
|
3486
|
+
}
|
|
3487
|
+
}
|
|
3488
|
+
}
|
|
3489
|
+
return path3.resolve(__dirname, "./definitions");
|
|
3490
|
+
}
|
|
3491
|
+
var PROMPT_DIR = findPromptDir();
|
|
3492
|
+
var promptCache = /* @__PURE__ */ new Map();
|
|
3493
|
+
function listPromptFiles() {
|
|
3494
|
+
if (!fs4.existsSync(PROMPT_DIR)) return [];
|
|
3495
|
+
return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
3496
|
+
}
|
|
3497
|
+
function loadPromptFromMarkdown(name) {
|
|
3498
|
+
const cached = promptCache.get(name);
|
|
3499
|
+
if (cached) return cached;
|
|
3500
|
+
const filePath = path3.join(PROMPT_DIR, `${name}.md`);
|
|
3501
|
+
if (!fs4.existsSync(filePath)) {
|
|
3502
|
+
throw new Error(`Prompt file not found: ${filePath}`);
|
|
3503
|
+
}
|
|
3504
|
+
const fileContent = fs4.readFileSync(filePath, "utf-8");
|
|
3505
|
+
const { data, content } = matter(fileContent);
|
|
3506
|
+
const result = {
|
|
3507
|
+
name: data.name || name,
|
|
3508
|
+
description: data.description || "",
|
|
3509
|
+
arguments: data.arguments || [],
|
|
3510
|
+
agent: data.agent,
|
|
3511
|
+
content: content.trim()
|
|
3512
|
+
};
|
|
3513
|
+
promptCache.set(name, result);
|
|
3514
|
+
return result;
|
|
3515
|
+
}
|
|
3516
|
+
function findServerInstructionsDir() {
|
|
3517
|
+
const candidates = [
|
|
3518
|
+
// Production if chunked into dist/
|
|
3519
|
+
"./prompts/server",
|
|
3520
|
+
// Production if inlined into dist/mcp/
|
|
3521
|
+
"../prompts/server",
|
|
3522
|
+
// Dev: /src/mcp/prompts/server (next to loader.ts)
|
|
3523
|
+
"./server"
|
|
3524
|
+
].map((relPath) => path3.resolve(__dirname, relPath));
|
|
3525
|
+
for (const dir of candidates) {
|
|
3526
|
+
if (fs4.existsSync(dir)) {
|
|
3527
|
+
const filePath = path3.join(dir, "instructions.md");
|
|
3528
|
+
if (fs4.existsSync(filePath)) {
|
|
3529
|
+
return dir;
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
}
|
|
3533
|
+
return path3.resolve(__dirname, "./server");
|
|
3534
|
+
}
|
|
3535
|
+
var SERVER_DIR = findServerInstructionsDir();
|
|
3536
|
+
var serverInstructionsCache = null;
|
|
3537
|
+
function loadServerInstructions() {
|
|
3538
|
+
if (serverInstructionsCache !== null) return serverInstructionsCache;
|
|
3539
|
+
const filePath = path3.join(SERVER_DIR, "instructions.md");
|
|
3540
|
+
if (!fs4.existsSync(filePath)) {
|
|
3541
|
+
throw new Error(`Server instructions file not found: ${filePath}`);
|
|
3542
|
+
}
|
|
3543
|
+
const fileContent = fs4.readFileSync(filePath, "utf-8");
|
|
3544
|
+
const { content } = matter(fileContent);
|
|
3545
|
+
serverInstructionsCache = content.trim();
|
|
3546
|
+
return serverInstructionsCache;
|
|
3547
|
+
}
|
|
3548
|
+
|
|
3549
|
+
// src/mcp/session.ts
|
|
3550
|
+
import fs5 from "fs";
|
|
3551
|
+
import path4 from "path";
|
|
3552
|
+
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3469
3553
|
function createSessionContext() {
|
|
3470
3554
|
return {
|
|
3471
3555
|
roots: [],
|
|
@@ -3483,7 +3567,7 @@ function getFilesystemRoots(session) {
|
|
|
3483
3567
|
for (const root of session.roots) {
|
|
3484
3568
|
if (!root.uri.startsWith("file://")) continue;
|
|
3485
3569
|
try {
|
|
3486
|
-
resolved.push(
|
|
3570
|
+
resolved.push(path4.resolve(fileURLToPath2(root.uri)));
|
|
3487
3571
|
} catch {
|
|
3488
3572
|
}
|
|
3489
3573
|
}
|
|
@@ -3492,19 +3576,19 @@ function getFilesystemRoots(session) {
|
|
|
3492
3576
|
function isPathWithinRoots(targetPath, session) {
|
|
3493
3577
|
const roots = getFilesystemRoots(session);
|
|
3494
3578
|
if (roots.length === 0) return true;
|
|
3495
|
-
const normalizedTarget =
|
|
3579
|
+
const normalizedTarget = path4.resolve(targetPath);
|
|
3496
3580
|
return roots.some((rootPath) => {
|
|
3497
|
-
const relative =
|
|
3498
|
-
return relative === "" || !relative.startsWith("..") && !
|
|
3581
|
+
const relative = path4.relative(rootPath, normalizedTarget);
|
|
3582
|
+
return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
|
|
3499
3583
|
});
|
|
3500
3584
|
}
|
|
3501
3585
|
function findContainingRoot(targetPath, session) {
|
|
3502
3586
|
const roots = getFilesystemRoots(session);
|
|
3503
3587
|
if (roots.length === 0) return null;
|
|
3504
|
-
const normalizedTarget =
|
|
3588
|
+
const normalizedTarget = path4.resolve(targetPath);
|
|
3505
3589
|
for (const rootPath of roots) {
|
|
3506
|
-
const relative =
|
|
3507
|
-
if (relative === "" || !relative.startsWith("..") && !
|
|
3590
|
+
const relative = path4.relative(rootPath, normalizedTarget);
|
|
3591
|
+
if (relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative)) {
|
|
3508
3592
|
return rootPath;
|
|
3509
3593
|
}
|
|
3510
3594
|
}
|
|
@@ -3513,20 +3597,20 @@ function findContainingRoot(targetPath, session) {
|
|
|
3513
3597
|
function inferRepoFromSession(session) {
|
|
3514
3598
|
const roots = getFilesystemRoots(session);
|
|
3515
3599
|
if (roots.length === 1) {
|
|
3516
|
-
return
|
|
3600
|
+
return path4.basename(roots[0]);
|
|
3517
3601
|
}
|
|
3518
3602
|
if (roots.length === 0) {
|
|
3519
3603
|
if (!session) return void 0;
|
|
3520
3604
|
const cwd = process.cwd();
|
|
3521
|
-
return
|
|
3605
|
+
return path4.basename(cwd);
|
|
3522
3606
|
}
|
|
3523
3607
|
return void 0;
|
|
3524
3608
|
}
|
|
3525
3609
|
function inferOwnerFromGit(cwd) {
|
|
3526
3610
|
try {
|
|
3527
|
-
const gitConfigPath =
|
|
3528
|
-
if (!
|
|
3529
|
-
const content =
|
|
3611
|
+
const gitConfigPath = path4.join(cwd, ".git", "config");
|
|
3612
|
+
if (!fs5.existsSync(gitConfigPath)) return void 0;
|
|
3613
|
+
const content = fs5.readFileSync(gitConfigPath, "utf-8");
|
|
3530
3614
|
const match = content.match(
|
|
3531
3615
|
/url\s*=\s*(?:git@github\.com:|https?:\/\/github\.com\/|git:\/\/github\.com\/)([^\/\s]+)/
|
|
3532
3616
|
);
|
|
@@ -3548,7 +3632,7 @@ function inferOwnerFromSession(session) {
|
|
|
3548
3632
|
if (gitOwner) return gitOwner;
|
|
3549
3633
|
}
|
|
3550
3634
|
if (roots.length === 1) {
|
|
3551
|
-
const parts = roots[0].split(
|
|
3635
|
+
const parts = roots[0].split(path4.sep).filter(Boolean);
|
|
3552
3636
|
if (parts.length >= 2) {
|
|
3553
3637
|
return parts[parts.length - 2];
|
|
3554
3638
|
}
|
|
@@ -3556,7 +3640,7 @@ function inferOwnerFromSession(session) {
|
|
|
3556
3640
|
if (roots.length === 0) {
|
|
3557
3641
|
if (!session) return void 0;
|
|
3558
3642
|
const workdir = process.cwd();
|
|
3559
|
-
const parts = workdir.split(
|
|
3643
|
+
const parts = workdir.split(path4.sep).filter(Boolean);
|
|
3560
3644
|
if (parts.length >= 2) {
|
|
3561
3645
|
return parts[parts.length - 2];
|
|
3562
3646
|
}
|
|
@@ -3564,90 +3648,6 @@ function inferOwnerFromSession(session) {
|
|
|
3564
3648
|
return void 0;
|
|
3565
3649
|
}
|
|
3566
3650
|
|
|
3567
|
-
// src/mcp/prompts/loader.ts
|
|
3568
|
-
import fs5 from "fs";
|
|
3569
|
-
import path4 from "path";
|
|
3570
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3571
|
-
import matter from "gray-matter";
|
|
3572
|
-
var __filename = fileURLToPath2(import.meta.url);
|
|
3573
|
-
var __dirname = path4.dirname(__filename);
|
|
3574
|
-
function findPromptDir() {
|
|
3575
|
-
const candidates = [
|
|
3576
|
-
// Production if chunked into dist/
|
|
3577
|
-
"./prompts",
|
|
3578
|
-
// Production if inlined into dist/mcp/
|
|
3579
|
-
"../prompts",
|
|
3580
|
-
// Dev: /src/mcp/prompts/definitions (next to loader.ts)
|
|
3581
|
-
"./definitions"
|
|
3582
|
-
].map((relPath) => path4.resolve(__dirname, relPath));
|
|
3583
|
-
for (const dir of candidates) {
|
|
3584
|
-
if (fs5.existsSync(dir)) {
|
|
3585
|
-
const files = fs5.readdirSync(dir);
|
|
3586
|
-
if (files.some((f) => f.endsWith(".md"))) {
|
|
3587
|
-
return dir;
|
|
3588
|
-
}
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
return path4.resolve(__dirname, "./definitions");
|
|
3592
|
-
}
|
|
3593
|
-
var PROMPT_DIR = findPromptDir();
|
|
3594
|
-
var promptCache = /* @__PURE__ */ new Map();
|
|
3595
|
-
function listPromptFiles() {
|
|
3596
|
-
if (!fs5.existsSync(PROMPT_DIR)) return [];
|
|
3597
|
-
return fs5.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
|
|
3598
|
-
}
|
|
3599
|
-
function loadPromptFromMarkdown(name) {
|
|
3600
|
-
const cached = promptCache.get(name);
|
|
3601
|
-
if (cached) return cached;
|
|
3602
|
-
const filePath = path4.join(PROMPT_DIR, `${name}.md`);
|
|
3603
|
-
if (!fs5.existsSync(filePath)) {
|
|
3604
|
-
throw new Error(`Prompt file not found: ${filePath}`);
|
|
3605
|
-
}
|
|
3606
|
-
const fileContent = fs5.readFileSync(filePath, "utf-8");
|
|
3607
|
-
const { data, content } = matter(fileContent);
|
|
3608
|
-
const result = {
|
|
3609
|
-
name: data.name || name,
|
|
3610
|
-
description: data.description || "",
|
|
3611
|
-
arguments: data.arguments || [],
|
|
3612
|
-
agent: data.agent,
|
|
3613
|
-
content: content.trim()
|
|
3614
|
-
};
|
|
3615
|
-
promptCache.set(name, result);
|
|
3616
|
-
return result;
|
|
3617
|
-
}
|
|
3618
|
-
function findServerInstructionsDir() {
|
|
3619
|
-
const candidates = [
|
|
3620
|
-
// Production if chunked into dist/
|
|
3621
|
-
"./prompts/server",
|
|
3622
|
-
// Production if inlined into dist/mcp/
|
|
3623
|
-
"../prompts/server",
|
|
3624
|
-
// Dev: /src/mcp/prompts/server (next to loader.ts)
|
|
3625
|
-
"./server"
|
|
3626
|
-
].map((relPath) => path4.resolve(__dirname, relPath));
|
|
3627
|
-
for (const dir of candidates) {
|
|
3628
|
-
if (fs5.existsSync(dir)) {
|
|
3629
|
-
const filePath = path4.join(dir, "instructions.md");
|
|
3630
|
-
if (fs5.existsSync(filePath)) {
|
|
3631
|
-
return dir;
|
|
3632
|
-
}
|
|
3633
|
-
}
|
|
3634
|
-
}
|
|
3635
|
-
return path4.resolve(__dirname, "./server");
|
|
3636
|
-
}
|
|
3637
|
-
var SERVER_DIR = findServerInstructionsDir();
|
|
3638
|
-
var serverInstructionsCache = null;
|
|
3639
|
-
function loadServerInstructions() {
|
|
3640
|
-
if (serverInstructionsCache !== null) return serverInstructionsCache;
|
|
3641
|
-
const filePath = path4.join(SERVER_DIR, "instructions.md");
|
|
3642
|
-
if (!fs5.existsSync(filePath)) {
|
|
3643
|
-
throw new Error(`Server instructions file not found: ${filePath}`);
|
|
3644
|
-
}
|
|
3645
|
-
const fileContent = fs5.readFileSync(filePath, "utf-8");
|
|
3646
|
-
const { content } = matter(fileContent);
|
|
3647
|
-
serverInstructionsCache = content.trim();
|
|
3648
|
-
return serverInstructionsCache;
|
|
3649
|
-
}
|
|
3650
|
-
|
|
3651
3651
|
// src/mcp/tools/definitions/memory.ts
|
|
3652
3652
|
var MEMORY_TOOL_DEFINITIONS = [
|
|
3653
3653
|
{
|
|
@@ -4442,7 +4442,10 @@ var TASK_TOOL_DEFINITIONS = [
|
|
|
4442
4442
|
type: "string",
|
|
4443
4443
|
description: "Repository/project name (e.g., 'local-memory-mcp'). Auto-inferred from session when omitted."
|
|
4444
4444
|
},
|
|
4445
|
-
task_code: {
|
|
4445
|
+
task_code: {
|
|
4446
|
+
type: "string",
|
|
4447
|
+
description: "Unique task code (e.g. TASK-001, REFACTOR-2). Optional \u2014 auto-generated as TASK-xxx if omitted."
|
|
4448
|
+
},
|
|
4446
4449
|
phase: { type: "string", description: "Project phase (Required for single task)" },
|
|
4447
4450
|
title: {
|
|
4448
4451
|
type: "string",
|
|
@@ -6333,164 +6336,6 @@ var TOOL_DEFINITIONS = [
|
|
|
6333
6336
|
...KG_TOOL_DEFINITIONS
|
|
6334
6337
|
];
|
|
6335
6338
|
|
|
6336
|
-
// src/mcp/utils/completion.ts
|
|
6337
|
-
var MAX_COMPLETION_VALUES = 100;
|
|
6338
|
-
function rankCompletionValues(candidates, input) {
|
|
6339
|
-
const unique = [...new Set(candidates.filter(Boolean))];
|
|
6340
|
-
const needle = input.trim().toLowerCase();
|
|
6341
|
-
if (!needle) {
|
|
6342
|
-
return unique.slice(0, MAX_COMPLETION_VALUES);
|
|
6343
|
-
}
|
|
6344
|
-
return unique.map((value) => ({ value, score: scoreCompletionValue(value, needle) })).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.value.localeCompare(b.value)).map((entry) => entry.value);
|
|
6345
|
-
}
|
|
6346
|
-
function scoreCompletionValue(value, needle) {
|
|
6347
|
-
const haystack = value.toLowerCase();
|
|
6348
|
-
if (haystack === needle) return 100;
|
|
6349
|
-
if (haystack.startsWith(needle)) return 75;
|
|
6350
|
-
if (haystack.includes(needle)) return 50;
|
|
6351
|
-
const compactNeedle = needle.replace(/[\s_-]+/g, "");
|
|
6352
|
-
const compactHaystack = haystack.replace(/[\s_-]+/g, "");
|
|
6353
|
-
if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
|
|
6354
|
-
return 0;
|
|
6355
|
-
}
|
|
6356
|
-
|
|
6357
|
-
// src/mcp/utils/pagination.ts
|
|
6358
|
-
function encodeCursor(offset) {
|
|
6359
|
-
return Buffer.from(String(offset), "utf8").toString("base64");
|
|
6360
|
-
}
|
|
6361
|
-
function decodeCursor(cursor) {
|
|
6362
|
-
if (cursor === void 0 || cursor === null || cursor === "") {
|
|
6363
|
-
return 0;
|
|
6364
|
-
}
|
|
6365
|
-
if (typeof cursor !== "string" || cursor.trim() === "") {
|
|
6366
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
6367
|
-
}
|
|
6368
|
-
let decoded;
|
|
6369
|
-
try {
|
|
6370
|
-
decoded = Buffer.from(cursor, "base64").toString("utf8");
|
|
6371
|
-
} catch {
|
|
6372
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
6373
|
-
}
|
|
6374
|
-
if (!/^\d+$/.test(decoded)) {
|
|
6375
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
6376
|
-
}
|
|
6377
|
-
const offset = Number.parseInt(decoded, 10);
|
|
6378
|
-
if (!Number.isFinite(offset) || offset < 0) {
|
|
6379
|
-
throw invalidPaginationParams("Invalid cursor");
|
|
6380
|
-
}
|
|
6381
|
-
return offset;
|
|
6382
|
-
}
|
|
6383
|
-
function invalidPaginationParams(message) {
|
|
6384
|
-
const error = new Error(message);
|
|
6385
|
-
error.code = -32602;
|
|
6386
|
-
return error;
|
|
6387
|
-
}
|
|
6388
|
-
|
|
6389
|
-
// src/mcp/resources/index.ts
|
|
6390
|
-
var DEFAULT_PAGE_SIZE = 25;
|
|
6391
|
-
var MAX_PAGE_SIZE = 100;
|
|
6392
|
-
function listResources(session, params) {
|
|
6393
|
-
const resources = [
|
|
6394
|
-
{
|
|
6395
|
-
uri: "repository://index",
|
|
6396
|
-
name: "Repository Index",
|
|
6397
|
-
title: "Repository Index",
|
|
6398
|
-
description: "List of all known repositories with memory/task counts and last activity",
|
|
6399
|
-
mimeType: "application/json",
|
|
6400
|
-
annotations: {
|
|
6401
|
-
audience: ["assistant"],
|
|
6402
|
-
priority: 1,
|
|
6403
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
6404
|
-
}
|
|
6405
|
-
},
|
|
6406
|
-
{
|
|
6407
|
-
uri: "session://roots",
|
|
6408
|
-
name: "Session Roots",
|
|
6409
|
-
title: "Session Roots",
|
|
6410
|
-
description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
|
|
6411
|
-
mimeType: "application/json",
|
|
6412
|
-
size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
|
|
6413
|
-
annotations: {
|
|
6414
|
-
audience: ["assistant"],
|
|
6415
|
-
priority: 0.95,
|
|
6416
|
-
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
6417
|
-
}
|
|
6418
|
-
}
|
|
6419
|
-
];
|
|
6420
|
-
return paginateEntries("resources", resources, params);
|
|
6421
|
-
}
|
|
6422
|
-
function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
|
|
6423
|
-
if (resourceUri === "repository://{name}/memories" || resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}" || resourceUri === "repository://{name}/tasks" || resourceUri === "repository://{name}/tasks?status={status}&priority={priority}" || resourceUri === "repository://{name}/summary" || resourceUri === "repository://{name}/actions") {
|
|
6424
|
-
if (argumentName === "name") {
|
|
6425
|
-
return rankCompletionValues(dataSources.repos, argumentValue);
|
|
6426
|
-
}
|
|
6427
|
-
}
|
|
6428
|
-
if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
|
|
6429
|
-
if (argumentName === "tag") {
|
|
6430
|
-
return rankCompletionValues(dataSources.tags, argumentValue);
|
|
6431
|
-
}
|
|
6432
|
-
}
|
|
6433
|
-
throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
|
|
6434
|
-
}
|
|
6435
|
-
function paginateEntries(key, entries, params) {
|
|
6436
|
-
const limit = normalizeLimit(params?.limit);
|
|
6437
|
-
const offset = decodeCursor(params?.cursor);
|
|
6438
|
-
const sliced = entries.slice(offset, offset + limit);
|
|
6439
|
-
const nextOffset = offset + sliced.length;
|
|
6440
|
-
return {
|
|
6441
|
-
[key]: sliced,
|
|
6442
|
-
nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
|
|
6443
|
-
};
|
|
6444
|
-
}
|
|
6445
|
-
function normalizeLimit(limit) {
|
|
6446
|
-
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
6447
|
-
return DEFAULT_PAGE_SIZE;
|
|
6448
|
-
}
|
|
6449
|
-
return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
|
|
6450
|
-
}
|
|
6451
|
-
function invalidCompletionParams(message) {
|
|
6452
|
-
const error = new Error(message);
|
|
6453
|
-
error.code = -32602;
|
|
6454
|
-
return error;
|
|
6455
|
-
}
|
|
6456
|
-
|
|
6457
|
-
// src/mcp/prompts/registry.ts
|
|
6458
|
-
function createPromptDefinition(loaded) {
|
|
6459
|
-
return {
|
|
6460
|
-
name: loaded.name,
|
|
6461
|
-
description: loaded.description,
|
|
6462
|
-
arguments: loaded.arguments,
|
|
6463
|
-
agent: loaded.agent,
|
|
6464
|
-
messages: [
|
|
6465
|
-
{
|
|
6466
|
-
role: "user",
|
|
6467
|
-
content: {
|
|
6468
|
-
type: "text",
|
|
6469
|
-
text: loaded.content
|
|
6470
|
-
}
|
|
6471
|
-
}
|
|
6472
|
-
]
|
|
6473
|
-
};
|
|
6474
|
-
}
|
|
6475
|
-
var PROMPTS = {};
|
|
6476
|
-
var promptFiles = listPromptFiles();
|
|
6477
|
-
for (const name of promptFiles) {
|
|
6478
|
-
try {
|
|
6479
|
-
PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
|
|
6480
|
-
} catch (e) {
|
|
6481
|
-
logger.warn(`Failed to load prompt ${name}: ${e}`);
|
|
6482
|
-
}
|
|
6483
|
-
}
|
|
6484
|
-
async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
|
|
6485
|
-
void name;
|
|
6486
|
-
void contextArguments;
|
|
6487
|
-
if (argName === "task_id") {
|
|
6488
|
-
const values = dataSources.tasks.map((t) => t.id);
|
|
6489
|
-
return rankCompletionValues(values, value);
|
|
6490
|
-
}
|
|
6491
|
-
return [];
|
|
6492
|
-
}
|
|
6493
|
-
|
|
6494
6339
|
// src/mcp/tools/handoff.manage.ts
|
|
6495
6340
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
6496
6341
|
|
|
@@ -6690,7 +6535,7 @@ var MemoryStoreSchema = z3.object({
|
|
|
6690
6535
|
memories: z3.array(SingleMemorySchema).min(1).optional()
|
|
6691
6536
|
});
|
|
6692
6537
|
var MemoryUpdateSchema = z3.object({
|
|
6693
|
-
id: z3.string().
|
|
6538
|
+
id: z3.string().optional(),
|
|
6694
6539
|
code: z3.string().max(20).optional(),
|
|
6695
6540
|
owner: z3.string().min(1),
|
|
6696
6541
|
repo: z3.string().min(1).transform(normalizeRepo),
|
|
@@ -6730,7 +6575,7 @@ var MemorySearchSchema = z3.object({
|
|
|
6730
6575
|
structured: z3.boolean().default(false)
|
|
6731
6576
|
});
|
|
6732
6577
|
var MemoryAcknowledgeSchema = z3.object({
|
|
6733
|
-
memory_id: z3.string().
|
|
6578
|
+
memory_id: z3.string().optional(),
|
|
6734
6579
|
code: z3.string().max(20).optional(),
|
|
6735
6580
|
owner: z3.string().min(1),
|
|
6736
6581
|
repo: z3.string().min(1).transform(normalizeRepo),
|
|
@@ -6750,8 +6595,8 @@ var MemoryRecapSchema = z3.object({
|
|
|
6750
6595
|
var MemoryDeleteSchema = z3.object({
|
|
6751
6596
|
owner: z3.string().min(1),
|
|
6752
6597
|
repo: z3.string().min(1).transform(normalizeRepo),
|
|
6753
|
-
id: z3.string().
|
|
6754
|
-
ids: z3.array(z3.string()
|
|
6598
|
+
id: z3.string().optional(),
|
|
6599
|
+
ids: z3.array(z3.string()).min(1).optional(),
|
|
6755
6600
|
code: z3.string().max(20).optional(),
|
|
6756
6601
|
codes: z3.array(z3.string().max(20)).min(1).optional(),
|
|
6757
6602
|
structured: z3.boolean().default(false)
|
|
@@ -6762,7 +6607,7 @@ var MemoryDeleteSchema = z3.object({
|
|
|
6762
6607
|
}
|
|
6763
6608
|
);
|
|
6764
6609
|
var MemoryDetailSchema = z3.object({
|
|
6765
|
-
id: z3.string().
|
|
6610
|
+
id: z3.string().optional(),
|
|
6766
6611
|
code: z3.string().max(20).optional(),
|
|
6767
6612
|
owner: z3.string().min(1),
|
|
6768
6613
|
repo: z3.string().min(1).transform(normalizeRepo),
|
|
@@ -6899,8 +6744,8 @@ var TaskCreateInteractiveSchema = SingleTaskCreateSchema.partial().extend({
|
|
|
6899
6744
|
var TaskUpdateSchema = z4.object({
|
|
6900
6745
|
owner: z4.string().min(1),
|
|
6901
6746
|
repo: z4.string().min(1).transform(normalizeRepo),
|
|
6902
|
-
id: z4.string().
|
|
6903
|
-
ids: z4.array(z4.string()
|
|
6747
|
+
id: z4.string().optional(),
|
|
6748
|
+
ids: z4.array(z4.string()).min(1).optional(),
|
|
6904
6749
|
task_code: z4.string().optional(),
|
|
6905
6750
|
task_codes: z4.array(z4.string().min(1)).min(1).optional(),
|
|
6906
6751
|
phase: z4.string().optional(),
|
|
@@ -6926,7 +6771,7 @@ var TaskUpdateSchema = z4.object({
|
|
|
6926
6771
|
}).refine(
|
|
6927
6772
|
(data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0 || data.task_codes !== void 0,
|
|
6928
6773
|
{
|
|
6929
|
-
message: "Either 'id' (UUID), 'ids' (array of UUIDs), 'task_code' (string code like PERF-1), or 'task_codes' (array of string codes) must be provided.
|
|
6774
|
+
message: "Either 'id' (UUID), 'ids' (array of UUIDs or codes), 'task_code' (string code like PERF-1), or 'task_codes' (array of string codes) must be provided."
|
|
6930
6775
|
}
|
|
6931
6776
|
).refine((data) => Object.keys(data).length > 2, {
|
|
6932
6777
|
message: "At least one field besides repo and id/ids must be provided for update"
|
|
@@ -6955,21 +6800,21 @@ var TaskSearchSchema = z4.object({
|
|
|
6955
6800
|
var TaskDeleteSchema = z4.object({
|
|
6956
6801
|
owner: z4.string().min(1),
|
|
6957
6802
|
repo: z4.string().min(1).transform(normalizeRepo),
|
|
6958
|
-
id: z4.string().
|
|
6959
|
-
ids: z4.array(z4.string()
|
|
6803
|
+
id: z4.string().optional(),
|
|
6804
|
+
ids: z4.array(z4.string()).min(1).optional(),
|
|
6960
6805
|
task_code: z4.string().optional(),
|
|
6961
6806
|
task_codes: z4.array(z4.string().min(1)).min(1).optional(),
|
|
6962
6807
|
structured: z4.boolean().default(false)
|
|
6963
6808
|
}).refine(
|
|
6964
6809
|
(data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0 || data.task_codes !== void 0,
|
|
6965
6810
|
{
|
|
6966
|
-
message: "Either 'id' (UUID), 'ids' (array of UUIDs), 'task_code' (string code like PERF-1), or 'task_codes' (array of string codes) must be provided.
|
|
6811
|
+
message: "Either 'id' (UUID), 'ids' (array of UUIDs or codes), 'task_code' (string code like PERF-1), or 'task_codes' (array of string codes) must be provided."
|
|
6967
6812
|
}
|
|
6968
6813
|
);
|
|
6969
6814
|
var TaskGetSchema = z4.object({
|
|
6970
6815
|
owner: z4.string().min(1),
|
|
6971
6816
|
repo: z4.string().min(1).transform(normalizeRepo),
|
|
6972
|
-
id: z4.string().
|
|
6817
|
+
id: z4.string().optional(),
|
|
6973
6818
|
task_code: z4.string().optional(),
|
|
6974
6819
|
task_codes: z4.array(z4.string().min(1)).min(1).optional(),
|
|
6975
6820
|
structured: z4.boolean().default(false)
|
|
@@ -6984,7 +6829,7 @@ var HandoffCreateSchema = z5.object({
|
|
|
6984
6829
|
repo: z5.string().min(1, "repo is required \u2014 provide it explicitly or configure MCP workspace roots").transform(normalizeRepo),
|
|
6985
6830
|
from_agent: z5.string().min(1),
|
|
6986
6831
|
to_agent: z5.string().min(1).optional(),
|
|
6987
|
-
task_id: z5.string().
|
|
6832
|
+
task_id: z5.string().optional(),
|
|
6988
6833
|
task_code: z5.string().optional(),
|
|
6989
6834
|
summary: z5.string().min(1),
|
|
6990
6835
|
context: z5.record(z5.string(), z5.unknown()).optional(),
|
|
@@ -7016,7 +6861,7 @@ var HandoffListSchema = z5.object({
|
|
|
7016
6861
|
var TaskClaimSchema = z5.object({
|
|
7017
6862
|
owner: z5.string().min(1, "owner is required \u2014 provide it explicitly or configure MCP workspace roots"),
|
|
7018
6863
|
repo: z5.string().min(1, "repo is required \u2014 provide it explicitly or configure MCP workspace roots").transform(normalizeRepo),
|
|
7019
|
-
task_id: z5.string().
|
|
6864
|
+
task_id: z5.string().optional(),
|
|
7020
6865
|
task_code: z5.string().optional(),
|
|
7021
6866
|
agent: z5.string().min(1),
|
|
7022
6867
|
role: z5.string().optional(),
|
|
@@ -7039,7 +6884,7 @@ var ClaimListSchema = z5.object({
|
|
|
7039
6884
|
var ClaimReleaseSchema = z5.object({
|
|
7040
6885
|
owner: z5.string().min(1, "owner is required \u2014 provide it explicitly or configure MCP workspace roots"),
|
|
7041
6886
|
repo: z5.string().min(1, "repo is required \u2014 provide it explicitly or configure MCP workspace roots").transform(normalizeRepo),
|
|
7042
|
-
task_id: z5.string().
|
|
6887
|
+
task_id: z5.string().optional(),
|
|
7043
6888
|
task_code: z5.string().optional(),
|
|
7044
6889
|
agent: z5.string().min(1).optional(),
|
|
7045
6890
|
structured: z5.boolean().default(false)
|
|
@@ -7078,7 +6923,7 @@ var StandardStoreSchema = z6.object({
|
|
|
7078
6923
|
message: "repo is required for repo-specific standards"
|
|
7079
6924
|
});
|
|
7080
6925
|
var StandardUpdateSchema = z6.object({
|
|
7081
|
-
id: z6.string().
|
|
6926
|
+
id: z6.string().optional(),
|
|
7082
6927
|
code: z6.string().max(20).optional(),
|
|
7083
6928
|
name: z6.string().min(3).max(255).optional(),
|
|
7084
6929
|
content: z6.string().min(10).optional(),
|
|
@@ -7120,8 +6965,8 @@ var StandardSearchSchema = z6.object({
|
|
|
7120
6965
|
var StandardDeleteSchema = z6.object({
|
|
7121
6966
|
owner: z6.string().min(1),
|
|
7122
6967
|
repo: z6.string().min(1).transform(normalizeRepo),
|
|
7123
|
-
id: z6.string().
|
|
7124
|
-
ids: z6.array(z6.string()
|
|
6968
|
+
id: z6.string().optional(),
|
|
6969
|
+
ids: z6.array(z6.string()).min(1).optional(),
|
|
7125
6970
|
code: z6.string().max(20).optional(),
|
|
7126
6971
|
codes: z6.array(z6.string().max(20)).min(1).optional(),
|
|
7127
6972
|
structured: z6.boolean().default(false)
|
|
@@ -7132,7 +6977,7 @@ var StandardDeleteSchema = z6.object({
|
|
|
7132
6977
|
}
|
|
7133
6978
|
);
|
|
7134
6979
|
var StandardDetailSchema = z6.object({
|
|
7135
|
-
id: z6.string().
|
|
6980
|
+
id: z6.string().optional(),
|
|
7136
6981
|
code: z6.string().max(20).optional(),
|
|
7137
6982
|
owner: z6.string().min(1),
|
|
7138
6983
|
repo: z6.string().min(1).transform(normalizeRepo),
|
|
@@ -7215,6 +7060,9 @@ var KGBackfillSchema = z8.object({
|
|
|
7215
7060
|
source: z8.enum(["memories", "standards", "both"]).optional().default("both")
|
|
7216
7061
|
});
|
|
7217
7062
|
|
|
7063
|
+
// src/mcp/utils/uuid.ts
|
|
7064
|
+
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
7065
|
+
|
|
7218
7066
|
// src/mcp/tools/handoff.manage.ts
|
|
7219
7067
|
function buildHandoffListSummary(repo, count, status, fromAgent, toAgent) {
|
|
7220
7068
|
const parts = [`Found ${count} handoff${count === 1 ? "" : "s"} in repo "${repo}".`];
|
|
@@ -7243,6 +7091,13 @@ async function handleHandoffCreate(args, storage) {
|
|
|
7243
7091
|
const validated = HandoffCreateSchema.parse(args);
|
|
7244
7092
|
const { owner, repo, from_agent, to_agent, task_id, task_code, summary, context, expires_at, structured } = validated;
|
|
7245
7093
|
let resolvedTaskId = task_id ?? null;
|
|
7094
|
+
if (resolvedTaskId && !UUID_REGEX.test(resolvedTaskId)) {
|
|
7095
|
+
const task = storage.tasks.getTaskByCode(owner, repo, resolvedTaskId);
|
|
7096
|
+
if (!task) {
|
|
7097
|
+
throw new Error(`Task not found: ${resolvedTaskId} in repo ${repo}`);
|
|
7098
|
+
}
|
|
7099
|
+
resolvedTaskId = task.id;
|
|
7100
|
+
}
|
|
7246
7101
|
if (!resolvedTaskId && task_code) {
|
|
7247
7102
|
const task = storage.tasks.getTaskByCode(owner, repo, task_code);
|
|
7248
7103
|
if (!task) {
|
|
@@ -7361,6 +7216,13 @@ async function handleTaskClaim(args, storage) {
|
|
|
7361
7216
|
const validated = TaskClaimSchema.parse(args);
|
|
7362
7217
|
const { owner, repo, task_id, task_code, agent, role, metadata, structured } = validated;
|
|
7363
7218
|
let taskId = task_id;
|
|
7219
|
+
if (taskId && !UUID_REGEX.test(taskId)) {
|
|
7220
|
+
const task2 = storage.tasks.getTaskByCode(owner, repo, taskId);
|
|
7221
|
+
if (!task2) {
|
|
7222
|
+
throw new Error(`Task not found: ${taskId} in repo ${repo}`);
|
|
7223
|
+
}
|
|
7224
|
+
taskId = task2.id;
|
|
7225
|
+
}
|
|
7364
7226
|
let resolvedTaskCode;
|
|
7365
7227
|
let task;
|
|
7366
7228
|
if (taskId) {
|
|
@@ -7466,6 +7328,13 @@ async function handleClaimRelease(args, storage) {
|
|
|
7466
7328
|
const validated = ClaimReleaseSchema.parse(args);
|
|
7467
7329
|
const { owner, repo, task_id, task_code, agent, structured } = validated;
|
|
7468
7330
|
let resolvedTaskId = task_id;
|
|
7331
|
+
if (resolvedTaskId && !UUID_REGEX.test(resolvedTaskId)) {
|
|
7332
|
+
const task = storage.tasks.getTaskByCode(owner, repo, resolvedTaskId);
|
|
7333
|
+
if (!task) {
|
|
7334
|
+
throw new Error(`Task not found: ${resolvedTaskId} in repo ${repo}`);
|
|
7335
|
+
}
|
|
7336
|
+
resolvedTaskId = task.id;
|
|
7337
|
+
}
|
|
7469
7338
|
let resolvedTaskCode = task_code ?? null;
|
|
7470
7339
|
if (resolvedTaskId) {
|
|
7471
7340
|
const task = storage.tasks.getTaskById(resolvedTaskId);
|
|
@@ -7503,6 +7372,164 @@ async function handleClaimRelease(args, storage) {
|
|
|
7503
7372
|
});
|
|
7504
7373
|
}
|
|
7505
7374
|
|
|
7375
|
+
// src/mcp/utils/completion.ts
|
|
7376
|
+
var MAX_COMPLETION_VALUES = 100;
|
|
7377
|
+
function rankCompletionValues(candidates, input) {
|
|
7378
|
+
const unique = [...new Set(candidates.filter(Boolean))];
|
|
7379
|
+
const needle = input.trim().toLowerCase();
|
|
7380
|
+
if (!needle) {
|
|
7381
|
+
return unique.slice(0, MAX_COMPLETION_VALUES);
|
|
7382
|
+
}
|
|
7383
|
+
return unique.map((value) => ({ value, score: scoreCompletionValue(value, needle) })).filter((entry) => entry.score > 0).sort((a, b) => b.score - a.score || a.value.localeCompare(b.value)).map((entry) => entry.value);
|
|
7384
|
+
}
|
|
7385
|
+
function scoreCompletionValue(value, needle) {
|
|
7386
|
+
const haystack = value.toLowerCase();
|
|
7387
|
+
if (haystack === needle) return 100;
|
|
7388
|
+
if (haystack.startsWith(needle)) return 75;
|
|
7389
|
+
if (haystack.includes(needle)) return 50;
|
|
7390
|
+
const compactNeedle = needle.replace(/[\s_-]+/g, "");
|
|
7391
|
+
const compactHaystack = haystack.replace(/[\s_-]+/g, "");
|
|
7392
|
+
if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
|
|
7393
|
+
return 0;
|
|
7394
|
+
}
|
|
7395
|
+
|
|
7396
|
+
// src/mcp/utils/pagination.ts
|
|
7397
|
+
function encodeCursor(offset) {
|
|
7398
|
+
return Buffer.from(String(offset), "utf8").toString("base64");
|
|
7399
|
+
}
|
|
7400
|
+
function decodeCursor(cursor) {
|
|
7401
|
+
if (cursor === void 0 || cursor === null || cursor === "") {
|
|
7402
|
+
return 0;
|
|
7403
|
+
}
|
|
7404
|
+
if (typeof cursor !== "string" || cursor.trim() === "") {
|
|
7405
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
7406
|
+
}
|
|
7407
|
+
let decoded;
|
|
7408
|
+
try {
|
|
7409
|
+
decoded = Buffer.from(cursor, "base64").toString("utf8");
|
|
7410
|
+
} catch {
|
|
7411
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
7412
|
+
}
|
|
7413
|
+
if (!/^\d+$/.test(decoded)) {
|
|
7414
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
7415
|
+
}
|
|
7416
|
+
const offset = Number.parseInt(decoded, 10);
|
|
7417
|
+
if (!Number.isFinite(offset) || offset < 0) {
|
|
7418
|
+
throw invalidPaginationParams("Invalid cursor");
|
|
7419
|
+
}
|
|
7420
|
+
return offset;
|
|
7421
|
+
}
|
|
7422
|
+
function invalidPaginationParams(message) {
|
|
7423
|
+
const error = new Error(message);
|
|
7424
|
+
error.code = -32602;
|
|
7425
|
+
return error;
|
|
7426
|
+
}
|
|
7427
|
+
|
|
7428
|
+
// src/mcp/prompts/registry.ts
|
|
7429
|
+
function createPromptDefinition(loaded) {
|
|
7430
|
+
return {
|
|
7431
|
+
name: loaded.name,
|
|
7432
|
+
description: loaded.description,
|
|
7433
|
+
arguments: loaded.arguments,
|
|
7434
|
+
agent: loaded.agent,
|
|
7435
|
+
messages: [
|
|
7436
|
+
{
|
|
7437
|
+
role: "user",
|
|
7438
|
+
content: {
|
|
7439
|
+
type: "text",
|
|
7440
|
+
text: loaded.content
|
|
7441
|
+
}
|
|
7442
|
+
}
|
|
7443
|
+
]
|
|
7444
|
+
};
|
|
7445
|
+
}
|
|
7446
|
+
var PROMPTS = {};
|
|
7447
|
+
var promptFiles = listPromptFiles();
|
|
7448
|
+
for (const name of promptFiles) {
|
|
7449
|
+
try {
|
|
7450
|
+
PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
|
|
7451
|
+
} catch (e) {
|
|
7452
|
+
logger.warn(`Failed to load prompt ${name}: ${e}`);
|
|
7453
|
+
}
|
|
7454
|
+
}
|
|
7455
|
+
async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
|
|
7456
|
+
void name;
|
|
7457
|
+
void contextArguments;
|
|
7458
|
+
if (argName === "task_id") {
|
|
7459
|
+
const values = dataSources.tasks.map((t) => t.id);
|
|
7460
|
+
return rankCompletionValues(values, value);
|
|
7461
|
+
}
|
|
7462
|
+
return [];
|
|
7463
|
+
}
|
|
7464
|
+
|
|
7465
|
+
// src/mcp/resources/index.ts
|
|
7466
|
+
var DEFAULT_PAGE_SIZE = 25;
|
|
7467
|
+
var MAX_PAGE_SIZE = 100;
|
|
7468
|
+
function listResources(session, params) {
|
|
7469
|
+
const resources = [
|
|
7470
|
+
{
|
|
7471
|
+
uri: "repository://index",
|
|
7472
|
+
name: "Repository Index",
|
|
7473
|
+
title: "Repository Index",
|
|
7474
|
+
description: "List of all known repositories with memory/task counts and last activity",
|
|
7475
|
+
mimeType: "application/json",
|
|
7476
|
+
annotations: {
|
|
7477
|
+
audience: ["assistant"],
|
|
7478
|
+
priority: 1,
|
|
7479
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
7480
|
+
}
|
|
7481
|
+
},
|
|
7482
|
+
{
|
|
7483
|
+
uri: "session://roots",
|
|
7484
|
+
name: "Session Roots",
|
|
7485
|
+
title: "Session Roots",
|
|
7486
|
+
description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
|
|
7487
|
+
mimeType: "application/json",
|
|
7488
|
+
size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
|
|
7489
|
+
annotations: {
|
|
7490
|
+
audience: ["assistant"],
|
|
7491
|
+
priority: 0.95,
|
|
7492
|
+
lastModified: (/* @__PURE__ */ new Date()).toISOString()
|
|
7493
|
+
}
|
|
7494
|
+
}
|
|
7495
|
+
];
|
|
7496
|
+
return paginateEntries("resources", resources, params);
|
|
7497
|
+
}
|
|
7498
|
+
function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
|
|
7499
|
+
if (resourceUri === "repository://{name}/memories" || resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}" || resourceUri === "repository://{name}/tasks" || resourceUri === "repository://{name}/tasks?status={status}&priority={priority}" || resourceUri === "repository://{name}/summary" || resourceUri === "repository://{name}/actions") {
|
|
7500
|
+
if (argumentName === "name") {
|
|
7501
|
+
return rankCompletionValues(dataSources.repos, argumentValue);
|
|
7502
|
+
}
|
|
7503
|
+
}
|
|
7504
|
+
if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
|
|
7505
|
+
if (argumentName === "tag") {
|
|
7506
|
+
return rankCompletionValues(dataSources.tags, argumentValue);
|
|
7507
|
+
}
|
|
7508
|
+
}
|
|
7509
|
+
throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
|
|
7510
|
+
}
|
|
7511
|
+
function paginateEntries(key, entries, params) {
|
|
7512
|
+
const limit = normalizeLimit(params?.limit);
|
|
7513
|
+
const offset = decodeCursor(params?.cursor);
|
|
7514
|
+
const sliced = entries.slice(offset, offset + limit);
|
|
7515
|
+
const nextOffset = offset + sliced.length;
|
|
7516
|
+
return {
|
|
7517
|
+
[key]: sliced,
|
|
7518
|
+
nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
|
|
7519
|
+
};
|
|
7520
|
+
}
|
|
7521
|
+
function normalizeLimit(limit) {
|
|
7522
|
+
if (typeof limit !== "number" || !Number.isFinite(limit)) {
|
|
7523
|
+
return DEFAULT_PAGE_SIZE;
|
|
7524
|
+
}
|
|
7525
|
+
return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
|
|
7526
|
+
}
|
|
7527
|
+
function invalidCompletionParams(message) {
|
|
7528
|
+
const error = new Error(message);
|
|
7529
|
+
error.code = -32602;
|
|
7530
|
+
return error;
|
|
7531
|
+
}
|
|
7532
|
+
|
|
7506
7533
|
// src/mcp/tools/standard.shared.ts
|
|
7507
7534
|
function toContextSlug(value) {
|
|
7508
7535
|
return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
@@ -7521,30 +7548,20 @@ function buildStandardVectorText(standard) {
|
|
|
7521
7548
|
}
|
|
7522
7549
|
|
|
7523
7550
|
export {
|
|
7524
|
-
|
|
7525
|
-
|
|
7526
|
-
|
|
7527
|
-
parseRepoInput,
|
|
7528
|
-
normalizeRepo,
|
|
7529
|
-
SQLiteStore,
|
|
7530
|
-
RealVectorStore,
|
|
7531
|
-
TOOL_DEFINITIONS,
|
|
7532
|
-
rankCompletionValues,
|
|
7533
|
-
listResources,
|
|
7534
|
-
completeResourceArgument,
|
|
7551
|
+
listPromptFiles,
|
|
7552
|
+
loadPromptFromMarkdown,
|
|
7553
|
+
loadServerInstructions,
|
|
7535
7554
|
createSessionContext,
|
|
7536
7555
|
getFilesystemRoots,
|
|
7537
7556
|
isPathWithinRoots,
|
|
7538
7557
|
findContainingRoot,
|
|
7539
7558
|
inferRepoFromSession,
|
|
7540
7559
|
inferOwnerFromSession,
|
|
7541
|
-
|
|
7542
|
-
|
|
7543
|
-
|
|
7544
|
-
|
|
7545
|
-
|
|
7546
|
-
createMcpResponse,
|
|
7547
|
-
getPrimaryTextContent,
|
|
7560
|
+
logger,
|
|
7561
|
+
addLogSink,
|
|
7562
|
+
createFileSink,
|
|
7563
|
+
parseRepoInput,
|
|
7564
|
+
normalizeRepo,
|
|
7548
7565
|
TaskStatusSchema,
|
|
7549
7566
|
MemoryStoreSchema,
|
|
7550
7567
|
MemoryUpdateSchema,
|
|
@@ -7576,6 +7593,10 @@ export {
|
|
|
7576
7593
|
DeleteRelationSchema,
|
|
7577
7594
|
DeleteObservationSchema,
|
|
7578
7595
|
KGBackfillSchema,
|
|
7596
|
+
TOOL_DEFINITIONS,
|
|
7597
|
+
createMcpResponse,
|
|
7598
|
+
getPrimaryTextContent,
|
|
7599
|
+
UUID_REGEX,
|
|
7579
7600
|
handleHandoffCreate,
|
|
7580
7601
|
handleHandoffList,
|
|
7581
7602
|
handleHandoffUpdate,
|
|
@@ -7583,5 +7604,12 @@ export {
|
|
|
7583
7604
|
handleClaimList,
|
|
7584
7605
|
handleClaimRelease,
|
|
7585
7606
|
toContextSlug,
|
|
7586
|
-
buildStandardVectorText
|
|
7607
|
+
buildStandardVectorText,
|
|
7608
|
+
rankCompletionValues,
|
|
7609
|
+
PROMPTS,
|
|
7610
|
+
completePromptArgument,
|
|
7611
|
+
listResources,
|
|
7612
|
+
completeResourceArgument,
|
|
7613
|
+
SQLiteStore,
|
|
7614
|
+
RealVectorStore
|
|
7587
7615
|
};
|
package/dist/dashboard/server.js
CHANGED
package/dist/mcp/server.js
CHANGED
|
@@ -34,6 +34,7 @@ import {
|
|
|
34
34
|
TaskSearchSchema,
|
|
35
35
|
TaskStatusSchema,
|
|
36
36
|
TaskUpdateSchema,
|
|
37
|
+
UUID_REGEX,
|
|
37
38
|
addLogSink,
|
|
38
39
|
buildStandardVectorText,
|
|
39
40
|
completePromptArgument,
|
|
@@ -61,7 +62,7 @@ import {
|
|
|
61
62
|
parseRepoInput,
|
|
62
63
|
rankCompletionValues,
|
|
63
64
|
toContextSlug
|
|
64
|
-
} from "../chunk-
|
|
65
|
+
} from "../chunk-BPQ42JEQ.js";
|
|
65
66
|
|
|
66
67
|
// src/mcp/server.ts
|
|
67
68
|
import { serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
@@ -74,8 +75,8 @@ import path from "path";
|
|
|
74
75
|
import { fileURLToPath } from "url";
|
|
75
76
|
var __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
76
77
|
var pkgVersion = "0.1.0";
|
|
77
|
-
if ("0.19.
|
|
78
|
-
pkgVersion = "0.19.
|
|
78
|
+
if ("0.19.15") {
|
|
79
|
+
pkgVersion = "0.19.15";
|
|
79
80
|
} else {
|
|
80
81
|
let searchDir = __dirname;
|
|
81
82
|
for (let i = 0; i < 5; i++) {
|
|
@@ -422,7 +423,6 @@ async function saveExtractions(content, title, owner, repo, db2) {
|
|
|
422
423
|
}
|
|
423
424
|
|
|
424
425
|
// src/mcp/tools/memory.store.ts
|
|
425
|
-
var UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
426
426
|
function hasMetadataLikeTitle(title) {
|
|
427
427
|
const normalized = title.trim();
|
|
428
428
|
return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
|
|
@@ -657,14 +657,13 @@ async function handleMemoryStore(params, db2, vectors2) {
|
|
|
657
657
|
}
|
|
658
658
|
|
|
659
659
|
// src/mcp/tools/memory.update.ts
|
|
660
|
-
var UUID_REGEX2 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
661
660
|
function hasMetadataLikeTitle2(title) {
|
|
662
661
|
const normalized = title.trim();
|
|
663
662
|
return /^\[[^\]]{0,200}(agent:|role:|model:|\d{4}-\d{2}-\d{2}|source_)[^\]]*\]/i.test(normalized);
|
|
664
663
|
}
|
|
665
664
|
function resolveMemorySupersedes2(value, db2, owner, repo) {
|
|
666
665
|
if (!value) return null;
|
|
667
|
-
if (
|
|
666
|
+
if (UUID_REGEX.test(value)) return value;
|
|
668
667
|
const memory = db2.memories.getByCode(value, owner, repo);
|
|
669
668
|
if (!memory) throw new Error(`supersedes: memory with code '${value}' not found`);
|
|
670
669
|
return memory.id;
|
|
@@ -672,6 +671,11 @@ function resolveMemorySupersedes2(value, db2, owner, repo) {
|
|
|
672
671
|
async function handleMemoryUpdate(params, db2, vectors2) {
|
|
673
672
|
const validated = MemoryUpdateSchema.parse(params);
|
|
674
673
|
let resolvedId = validated.id;
|
|
674
|
+
if (resolvedId && !UUID_REGEX.test(resolvedId)) {
|
|
675
|
+
const byCode = db2.memories.getByCode(resolvedId, validated.owner, validated.repo);
|
|
676
|
+
if (!byCode) throw new Error(`Memory not found: ${resolvedId}`);
|
|
677
|
+
resolvedId = byCode.id;
|
|
678
|
+
}
|
|
675
679
|
if (!resolvedId && validated.code) {
|
|
676
680
|
const byCode = db2.memories.getByCode(validated.code, validated.owner, validated.repo);
|
|
677
681
|
if (!byCode) throw new Error(`Memory not found: ${validated.code}`);
|
|
@@ -1037,6 +1041,11 @@ function applyTimeFilter(memories, tunnel) {
|
|
|
1037
1041
|
async function handleMemoryAcknowledge(params, db2) {
|
|
1038
1042
|
const validated = MemoryAcknowledgeSchema.parse(params);
|
|
1039
1043
|
let memoryId = validated.memory_id;
|
|
1044
|
+
if (memoryId && !UUID_REGEX.test(memoryId)) {
|
|
1045
|
+
const byCode = db2.memories.getByCode(memoryId, validated.owner, validated.repo);
|
|
1046
|
+
if (!byCode) throw new Error(`Memory not found: ${memoryId}`);
|
|
1047
|
+
memoryId = byCode.id;
|
|
1048
|
+
}
|
|
1040
1049
|
if (!memoryId && validated.code) {
|
|
1041
1050
|
const byCode = db2.memories.getByCode(validated.code, validated.owner, validated.repo);
|
|
1042
1051
|
if (!byCode) throw new Error(`Memory not found: ${validated.code}`);
|
|
@@ -1541,8 +1550,26 @@ async function handleMemoryDelete(params, db2, vectors2, onProgress) {
|
|
|
1541
1550
|
const validated = MemoryDeleteSchema.parse(params);
|
|
1542
1551
|
const { id, ids, code, codes, owner, repo, structured } = validated;
|
|
1543
1552
|
const resolvedIds = [];
|
|
1544
|
-
if (ids)
|
|
1545
|
-
|
|
1553
|
+
if (ids) {
|
|
1554
|
+
for (const item of ids) {
|
|
1555
|
+
if (UUID_REGEX.test(item)) {
|
|
1556
|
+
resolvedIds.push(item);
|
|
1557
|
+
} else {
|
|
1558
|
+
const entry = db2.memories.getByCode(item, owner, repo);
|
|
1559
|
+
if (!entry) throw new Error(`Memory not found: ${item}`);
|
|
1560
|
+
resolvedIds.push(entry.id);
|
|
1561
|
+
}
|
|
1562
|
+
}
|
|
1563
|
+
}
|
|
1564
|
+
if (id) {
|
|
1565
|
+
if (!UUID_REGEX.test(id)) {
|
|
1566
|
+
const entry = db2.memories.getByCode(id, owner, repo);
|
|
1567
|
+
if (!entry) throw new Error(`Memory not found: ${id}`);
|
|
1568
|
+
resolvedIds.push(entry.id);
|
|
1569
|
+
} else {
|
|
1570
|
+
resolvedIds.push(id);
|
|
1571
|
+
}
|
|
1572
|
+
}
|
|
1546
1573
|
if (code) {
|
|
1547
1574
|
const entry = db2.memories.getByCode(code, owner, repo);
|
|
1548
1575
|
if (!entry) throw new Error(`Memory not found: ${code}`);
|
|
@@ -1573,7 +1600,7 @@ async function handleMemoryDelete(params, db2, vectors2, onProgress) {
|
|
|
1573
1600
|
lastRepo = existing.scope.repo;
|
|
1574
1601
|
deletedCodes.push(existing.code || existing.id);
|
|
1575
1602
|
validIdsToDelete.push(targetId);
|
|
1576
|
-
} else
|
|
1603
|
+
} else {
|
|
1577
1604
|
throw new Error(`Memory not found: ${targetId}`);
|
|
1578
1605
|
}
|
|
1579
1606
|
}
|
|
@@ -1615,7 +1642,7 @@ async function handleMemoryDetail(args, storage) {
|
|
|
1615
1642
|
const { id, code, owner, repo } = validated;
|
|
1616
1643
|
let memory;
|
|
1617
1644
|
if (id) {
|
|
1618
|
-
memory = storage.memories.getById(id);
|
|
1645
|
+
memory = UUID_REGEX.test(id) ? storage.memories.getById(id) : storage.memories.getByCode(id, owner, repo);
|
|
1619
1646
|
} else if (code) {
|
|
1620
1647
|
memory = storage.memories.getByCode(code, owner, repo);
|
|
1621
1648
|
}
|
|
@@ -1645,10 +1672,9 @@ async function handleMemoryDetail(args, storage) {
|
|
|
1645
1672
|
|
|
1646
1673
|
// src/mcp/tools/standard.store.ts
|
|
1647
1674
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
1648
|
-
var UUID_REGEX3 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
1649
1675
|
function resolveStandardParentId(value, db2, owner, repo) {
|
|
1650
1676
|
if (!value) return null;
|
|
1651
|
-
if (
|
|
1677
|
+
if (UUID_REGEX.test(value)) return value;
|
|
1652
1678
|
const standard = db2.standards.getByCode(value, owner, repo);
|
|
1653
1679
|
if (!standard) throw new Error(`parent_id: standard with code '${value}' not found`);
|
|
1654
1680
|
return standard.id;
|
|
@@ -2055,10 +2081,9 @@ async function handleStandardSearch(params, db2, vectors2) {
|
|
|
2055
2081
|
}
|
|
2056
2082
|
|
|
2057
2083
|
// src/mcp/tools/standard.update.ts
|
|
2058
|
-
var UUID_REGEX4 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2059
2084
|
function resolveStandardParentId2(value, db2, owner, repo) {
|
|
2060
2085
|
if (!value) return null;
|
|
2061
|
-
if (
|
|
2086
|
+
if (UUID_REGEX.test(value)) return value;
|
|
2062
2087
|
const standard = db2.standards.getByCode(value, owner, repo);
|
|
2063
2088
|
if (!standard) throw new Error(`parent_id: standard with code '${value}' not found`);
|
|
2064
2089
|
return standard.id;
|
|
@@ -2066,6 +2091,11 @@ function resolveStandardParentId2(value, db2, owner, repo) {
|
|
|
2066
2091
|
async function handleStandardUpdate(params, db2, vectors2) {
|
|
2067
2092
|
const validated = StandardUpdateSchema.parse(params);
|
|
2068
2093
|
let resolvedId = validated.id;
|
|
2094
|
+
if (resolvedId && !UUID_REGEX.test(resolvedId)) {
|
|
2095
|
+
const byCode = db2.standards.getByCode(resolvedId, validated.owner, validated.repo);
|
|
2096
|
+
if (!byCode) throw new Error(`Coding standard not found: ${resolvedId}`);
|
|
2097
|
+
resolvedId = byCode.id;
|
|
2098
|
+
}
|
|
2069
2099
|
if (!resolvedId && validated.code) {
|
|
2070
2100
|
const byCode = db2.standards.getByCode(validated.code, validated.owner, validated.repo);
|
|
2071
2101
|
if (!byCode) throw new Error(`Coding standard not found: ${validated.code}`);
|
|
@@ -2123,9 +2153,15 @@ async function handleStandardUpdate(params, db2, vectors2) {
|
|
|
2123
2153
|
// src/mcp/tools/standard.detail.ts
|
|
2124
2154
|
async function handleStandardDetail(args, storage) {
|
|
2125
2155
|
const validated = StandardDetailSchema.parse(args);
|
|
2126
|
-
const
|
|
2156
|
+
const { id, code, owner, repo } = validated;
|
|
2157
|
+
let standard;
|
|
2158
|
+
if (id) {
|
|
2159
|
+
standard = UUID_REGEX.test(id) ? storage.standards.getById(id) : storage.standards.getByCode(id, owner, repo);
|
|
2160
|
+
} else if (code) {
|
|
2161
|
+
standard = storage.standards.getByCode(code, owner, repo);
|
|
2162
|
+
}
|
|
2127
2163
|
if (!standard) {
|
|
2128
|
-
const identifier =
|
|
2164
|
+
const identifier = id ?? code;
|
|
2129
2165
|
throw new Error(`Coding standard not found: ${identifier}`);
|
|
2130
2166
|
}
|
|
2131
2167
|
storage.standards.incrementHitCounts([standard.id]);
|
|
@@ -2159,8 +2195,26 @@ async function handleStandardDelete(params, db2, vectors2) {
|
|
|
2159
2195
|
const validated = StandardDeleteSchema.parse(params);
|
|
2160
2196
|
const { id, ids, code, codes, owner, repo, structured } = validated;
|
|
2161
2197
|
const resolvedIds = [];
|
|
2162
|
-
if (ids)
|
|
2163
|
-
|
|
2198
|
+
if (ids) {
|
|
2199
|
+
for (const item of ids) {
|
|
2200
|
+
if (UUID_REGEX.test(item)) {
|
|
2201
|
+
resolvedIds.push(item);
|
|
2202
|
+
} else {
|
|
2203
|
+
const entry = db2.standards.getByCode(item, owner, repo);
|
|
2204
|
+
if (!entry) throw new Error(`Coding standard not found: ${item}`);
|
|
2205
|
+
resolvedIds.push(entry.id);
|
|
2206
|
+
}
|
|
2207
|
+
}
|
|
2208
|
+
}
|
|
2209
|
+
if (id) {
|
|
2210
|
+
if (!UUID_REGEX.test(id)) {
|
|
2211
|
+
const entry = db2.standards.getByCode(id, owner, repo);
|
|
2212
|
+
if (!entry) throw new Error(`Coding standard not found: ${id}`);
|
|
2213
|
+
resolvedIds.push(entry.id);
|
|
2214
|
+
} else {
|
|
2215
|
+
resolvedIds.push(id);
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2164
2218
|
if (code) {
|
|
2165
2219
|
const entry = db2.standards.getByCode(code, owner, repo);
|
|
2166
2220
|
if (!entry) throw new Error(`Coding standard not found: ${code}`);
|
|
@@ -2188,7 +2242,7 @@ async function handleStandardDelete(params, db2, vectors2) {
|
|
|
2188
2242
|
db2.standards.delete(targetId);
|
|
2189
2243
|
await vectors2.remove(targetId, "standard");
|
|
2190
2244
|
deletedCount++;
|
|
2191
|
-
} else
|
|
2245
|
+
} else {
|
|
2192
2246
|
throw new Error(`Coding standard not found: ${targetId}`);
|
|
2193
2247
|
}
|
|
2194
2248
|
}
|
|
@@ -2214,10 +2268,9 @@ async function handleStandardDelete(params, db2, vectors2) {
|
|
|
2214
2268
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
2215
2269
|
|
|
2216
2270
|
// src/mcp/tools/task.helpers.ts
|
|
2217
|
-
var UUID_REGEX5 = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
2218
2271
|
function resolveParentId(value, owner, repo, storage, localCodeMap) {
|
|
2219
2272
|
if (!value) return null;
|
|
2220
|
-
if (
|
|
2273
|
+
if (UUID_REGEX.test(value)) return value;
|
|
2221
2274
|
if (localCodeMap?.has(value)) return localCodeMap.get(value);
|
|
2222
2275
|
const parent = storage.tasks.getTaskByCode(owner, repo, value);
|
|
2223
2276
|
if (!parent) throw new Error(`parent_id: task with code '${value}' not found in repo '${repo}'`);
|
|
@@ -2225,7 +2278,7 @@ function resolveParentId(value, owner, repo, storage, localCodeMap) {
|
|
|
2225
2278
|
}
|
|
2226
2279
|
function resolveDependsOn(value, owner, repo, storage, localCodeMap) {
|
|
2227
2280
|
if (!value) return null;
|
|
2228
|
-
if (
|
|
2281
|
+
if (UUID_REGEX.test(value)) return value;
|
|
2229
2282
|
if (localCodeMap?.has(value)) return localCodeMap.get(value);
|
|
2230
2283
|
const task = storage.tasks.getTaskByCode(owner, repo, value);
|
|
2231
2284
|
if (!task) throw new Error(`depends_on: task with code '${value}' not found in repo '${repo}'`);
|
|
@@ -2338,7 +2391,9 @@ async function handleTaskCreate(args, storage) {
|
|
|
2338
2391
|
throw new Error(`Duplicate task_code in request: '${code}'`);
|
|
2339
2392
|
}
|
|
2340
2393
|
if (existingCodes.has(code)) {
|
|
2341
|
-
throw new Error(
|
|
2394
|
+
throw new Error(
|
|
2395
|
+
`Duplicate task_code: '${code}' already exists in repository '${repo}'. Omit task_code to auto-generate, or use task-list/task-search to find available codes.`
|
|
2396
|
+
);
|
|
2342
2397
|
}
|
|
2343
2398
|
codesInRequest.add(code);
|
|
2344
2399
|
let normalizedStatus = taskData.status || "backlog";
|
|
@@ -2419,7 +2474,9 @@ async function handleTaskCreate(args, storage) {
|
|
|
2419
2474
|
let effectiveStatus = requestedStatus || "backlog";
|
|
2420
2475
|
const resolvedCode = task_code || generateNextCode(owner ?? "", repo, "task", storage);
|
|
2421
2476
|
if (storage.tasks.isTaskCodeDuplicate(owner, repo, resolvedCode)) {
|
|
2422
|
-
throw new Error(
|
|
2477
|
+
throw new Error(
|
|
2478
|
+
`Duplicate task_code: '${resolvedCode}' already exists in repository '${repo}'. Omit task_code to auto-generate, or use task-list/task-search to find available codes.`
|
|
2479
|
+
);
|
|
2423
2480
|
}
|
|
2424
2481
|
if (requestedStatus !== "backlog" && requestedStatus !== "pending" && requestedStatus !== void 0) {
|
|
2425
2482
|
throw new Error("New tasks must be created with status 'backlog' or 'pending'.");
|
|
@@ -2583,12 +2640,29 @@ async function handleTaskUpdate(args, storage, vectors2) {
|
|
|
2583
2640
|
const updateData = TaskUpdateSchema.parse(args);
|
|
2584
2641
|
const { owner, repo, id, ids, comment, force, ...updates } = updateData;
|
|
2585
2642
|
let resolvedId = id;
|
|
2643
|
+
if (resolvedId && !UUID_REGEX.test(resolvedId)) {
|
|
2644
|
+
const found = storage.tasks.getTaskByCode(owner, repo, resolvedId);
|
|
2645
|
+
if (!found) throw new Error(`Task not found: ${resolvedId}`);
|
|
2646
|
+
resolvedId = found.id;
|
|
2647
|
+
}
|
|
2586
2648
|
if (!resolvedId && !ids && updates.task_code) {
|
|
2587
2649
|
const found = storage.tasks.getTaskByCode(owner, repo, updates.task_code);
|
|
2588
2650
|
if (!found) throw new Error(`Task not found: ${updates.task_code}`);
|
|
2589
2651
|
resolvedId = found.id;
|
|
2590
2652
|
}
|
|
2591
|
-
const
|
|
2653
|
+
const resolvedIdsArray = [];
|
|
2654
|
+
if (ids) {
|
|
2655
|
+
for (const item of ids) {
|
|
2656
|
+
if (UUID_REGEX.test(item)) {
|
|
2657
|
+
resolvedIdsArray.push(item);
|
|
2658
|
+
} else {
|
|
2659
|
+
const task = storage.tasks.getTaskByCode(owner, repo, item);
|
|
2660
|
+
if (!task) throw new Error(`Task not found: ${item}`);
|
|
2661
|
+
resolvedIdsArray.push(task.id);
|
|
2662
|
+
}
|
|
2663
|
+
}
|
|
2664
|
+
}
|
|
2665
|
+
const targetIds = resolvedIdsArray.length > 0 ? resolvedIdsArray : resolvedId ? [resolvedId] : [];
|
|
2592
2666
|
if (targetIds.length === 0) {
|
|
2593
2667
|
throw new Error("Either 'id' or 'ids' must be provided for update");
|
|
2594
2668
|
}
|
|
@@ -2731,8 +2805,26 @@ async function handleTaskDelete(args, storage) {
|
|
|
2731
2805
|
const validated = TaskDeleteSchema.parse(args);
|
|
2732
2806
|
const { owner, repo, id, ids, task_code } = validated;
|
|
2733
2807
|
const resolvedIds = [];
|
|
2734
|
-
if (ids)
|
|
2735
|
-
|
|
2808
|
+
if (ids) {
|
|
2809
|
+
for (const item of ids) {
|
|
2810
|
+
if (UUID_REGEX.test(item)) {
|
|
2811
|
+
resolvedIds.push(item);
|
|
2812
|
+
} else {
|
|
2813
|
+
const task = storage.tasks.getTaskByCode(owner, repo, item);
|
|
2814
|
+
if (!task) throw new Error(`Task not found: ${item}`);
|
|
2815
|
+
resolvedIds.push(task.id);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
if (id) {
|
|
2820
|
+
if (!UUID_REGEX.test(id)) {
|
|
2821
|
+
const task = storage.tasks.getTaskByCode(owner, repo, id);
|
|
2822
|
+
if (!task) throw new Error(`Task not found: ${id}`);
|
|
2823
|
+
resolvedIds.push(task.id);
|
|
2824
|
+
} else {
|
|
2825
|
+
resolvedIds.push(id);
|
|
2826
|
+
}
|
|
2827
|
+
}
|
|
2736
2828
|
if (task_code) {
|
|
2737
2829
|
const task = storage.tasks.getTaskByCode(owner, repo, task_code);
|
|
2738
2830
|
if (!task) throw new Error(`Task not found: ${task_code}`);
|
|
@@ -2768,7 +2860,7 @@ async function handleTaskGet(args, storage) {
|
|
|
2768
2860
|
const { owner, repo, id, task_code, structured: isStructuredRequest } = validated;
|
|
2769
2861
|
let task;
|
|
2770
2862
|
if (id) {
|
|
2771
|
-
task = storage.tasks.getTaskById(id);
|
|
2863
|
+
task = UUID_REGEX.test(id) ? storage.tasks.getTaskById(id) : storage.tasks.getTaskByCode(owner, repo, id);
|
|
2772
2864
|
} else if (task_code) {
|
|
2773
2865
|
task = storage.tasks.getTaskByCode(owner, repo, task_code);
|
|
2774
2866
|
} else {
|
|
@@ -3063,6 +3155,10 @@ async function handleDecisionLog(args, db2, vectors2) {
|
|
|
3063
3155
|
tags,
|
|
3064
3156
|
structured: validated.structured
|
|
3065
3157
|
};
|
|
3158
|
+
const conflict = await db2.memoryVectors.checkConflicts(formattedContent, owner, repo, "decision", vectors2, 0.55);
|
|
3159
|
+
if (conflict) {
|
|
3160
|
+
memoryStoreParams.supersedes = conflict.id;
|
|
3161
|
+
}
|
|
3066
3162
|
return handleMemoryStore(memoryStoreParams, db2, vectors2);
|
|
3067
3163
|
}
|
|
3068
3164
|
|