@vheins/local-memory-mcp 0.18.12 → 0.19.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.
@@ -1,131 +1,5 @@
1
- // src/mcp/capabilities.ts
2
- import { fileURLToPath as fileURLToPath2 } from "url";
3
- import path2 from "path";
4
-
5
- // src/mcp/prompts/loader.ts
6
- import fs from "fs";
7
- import path from "path";
8
- import { fileURLToPath } from "url";
9
- import matter from "gray-matter";
10
- var __filename = fileURLToPath(import.meta.url);
11
- var __dirname = path.dirname(__filename);
12
- function findPromptDir() {
13
- const candidates = [
14
- // Production if chunked into dist/
15
- "./prompts",
16
- // Production if inlined into dist/mcp/
17
- "../prompts",
18
- // Dev: /src/mcp/prompts/definitions (next to loader.ts)
19
- "./definitions"
20
- ].map((relPath) => path.resolve(__dirname, relPath));
21
- for (const dir of candidates) {
22
- if (fs.existsSync(dir)) {
23
- const files = fs.readdirSync(dir);
24
- if (files.some((f) => f.endsWith(".md"))) {
25
- return dir;
26
- }
27
- }
28
- }
29
- return path.resolve(__dirname, "./definitions");
30
- }
31
- var PROMPT_DIR = findPromptDir();
32
- function listPromptFiles() {
33
- if (!fs.existsSync(PROMPT_DIR)) return [];
34
- return fs.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
35
- }
36
- function loadPromptFromMarkdown(name) {
37
- const filePath = path.join(PROMPT_DIR, `${name}.md`);
38
- if (!fs.existsSync(filePath)) {
39
- throw new Error(`Prompt file not found: ${filePath}`);
40
- }
41
- const fileContent = fs.readFileSync(filePath, "utf-8");
42
- const { data, content } = matter(fileContent);
43
- return {
44
- name: data.name || name,
45
- description: data.description || "",
46
- arguments: data.arguments || [],
47
- agent: data.agent,
48
- content: content.trim()
49
- };
50
- }
51
- function findServerInstructionsDir() {
52
- const candidates = [
53
- // Production if chunked into dist/
54
- "./prompts/server",
55
- // Production if inlined into dist/mcp/
56
- "../prompts/server",
57
- // Dev: /src/mcp/prompts/server (next to loader.ts)
58
- "./server"
59
- ].map((relPath) => path.resolve(__dirname, relPath));
60
- for (const dir of candidates) {
61
- if (fs.existsSync(dir)) {
62
- const filePath = path.join(dir, "instructions.md");
63
- if (fs.existsSync(filePath)) {
64
- return dir;
65
- }
66
- }
67
- }
68
- return path.resolve(__dirname, "./server");
69
- }
70
- var SERVER_DIR = findServerInstructionsDir();
71
- function loadServerInstructions() {
72
- const filePath = path.join(SERVER_DIR, "instructions.md");
73
- if (!fs.existsSync(filePath)) {
74
- throw new Error(`Server instructions file not found: ${filePath}`);
75
- }
76
- const fileContent = fs.readFileSync(filePath, "utf-8");
77
- const { content } = matter(fileContent);
78
- return content.trim();
79
- }
80
-
81
- // src/mcp/capabilities.ts
82
- var __dirname2 = path2.dirname(fileURLToPath2(import.meta.url));
83
- var pkgVersion = "0.1.0";
84
- if ("0.18.12") {
85
- pkgVersion = "0.18.12";
86
- } else {
87
- let searchDir = __dirname2;
88
- for (let i = 0; i < 5; i++) {
89
- const candidate = path2.join(searchDir, "package.json");
90
- try {
91
- if (fs.existsSync(candidate)) {
92
- const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
93
- if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
94
- pkgVersion = pkg.version;
95
- break;
96
- }
97
- }
98
- } catch {
99
- }
100
- searchDir = path2.dirname(searchDir);
101
- }
102
- }
103
- var MCP_PROTOCOL_VERSION = "2025-03-26";
104
- var SERVER_INSTRUCTIONS = loadServerInstructions();
105
- var CAPABILITIES = {
106
- serverInfo: {
107
- name: "local-memory-mcp",
108
- version: pkgVersion,
109
- instructions: SERVER_INSTRUCTIONS
110
- },
111
- capabilities: {
112
- completions: {},
113
- logging: {},
114
- resources: {
115
- subscribe: true,
116
- listChanged: true
117
- },
118
- tools: {
119
- listChanged: false
120
- },
121
- prompts: {
122
- listChanged: true
123
- }
124
- }
125
- };
126
-
127
1
  // src/mcp/utils/logger.ts
128
- import fs2 from "fs";
2
+ import fs from "fs";
129
3
  var LEVELS = {
130
4
  debug: 0,
131
5
  info: 1,
@@ -225,31 +99,17 @@ var logger = {
225
99
  log("emergency", message, context);
226
100
  }
227
101
  };
228
- function setLogLevel(level) {
229
- const raw = level.toLowerCase();
230
- const normalized = LEVEL_ALIASES[raw] || raw;
231
- if (!(normalized in LEVELS)) {
232
- const error = new Error(`Invalid log level: ${level}`);
233
- error.code = -32602;
234
- throw error;
235
- }
236
- currentLevel = normalized;
237
- return currentLevel;
238
- }
239
- function getLogLevel() {
240
- return currentLevel;
241
- }
242
102
  function addLogSink(sink) {
243
103
  sinks.add(sink);
244
104
  return () => sinks.delete(sink);
245
105
  }
246
106
  var LOG_LEVEL_VALUES = Object.keys(LEVELS);
247
107
  function createFileSink(logDir, maxFiles = 5) {
248
- fs2.mkdirSync(logDir, { recursive: true });
249
- const existing = fs2.readdirSync(logDir).filter((f) => f.startsWith("mcp-") && f.endsWith(".log")).sort();
108
+ fs.mkdirSync(logDir, { recursive: true });
109
+ const existing = fs.readdirSync(logDir).filter((f) => f.startsWith("mcp-") && f.endsWith(".log")).sort();
250
110
  while (existing.length >= maxFiles) {
251
111
  try {
252
- fs2.unlinkSync(`${logDir}/${existing.shift()}`);
112
+ fs.unlinkSync(`${logDir}/${existing.shift()}`);
253
113
  } catch {
254
114
  }
255
115
  }
@@ -259,7 +119,7 @@ function createFileSink(logDir, maxFiles = 5) {
259
119
  const line = `${(/* @__PURE__ */ new Date()).toISOString()} [${payload.level.toUpperCase()}] [pid:${process.pid}] ${JSON.stringify(payload.data)}
260
120
  `;
261
121
  try {
262
- fs2.appendFileSync(logFile, line);
122
+ fs.appendFileSync(logFile, line);
263
123
  } catch {
264
124
  }
265
125
  };
@@ -267,8 +127,8 @@ function createFileSink(logDir, maxFiles = 5) {
267
127
 
268
128
  // src/mcp/storage/sqlite.ts
269
129
  import Database from "better-sqlite3";
270
- import path4 from "path";
271
- import fs4 from "fs";
130
+ import path2 from "path";
131
+ import fs3 from "fs";
272
132
  import os from "os";
273
133
 
274
134
  // src/mcp/storage/migrations.ts
@@ -504,6 +364,50 @@ var MigrationManager = class {
504
364
  CREATE INDEX IF NOT EXISTS idx_claims_task_id ON claims(task_id);
505
365
  CREATE INDEX IF NOT EXISTS idx_claims_agent ON claims(agent);
506
366
  CREATE INDEX IF NOT EXISTS idx_claims_claimed_at ON claims(claimed_at);
367
+
368
+ CREATE TABLE IF NOT EXISTS entities (
369
+ name TEXT PRIMARY KEY,
370
+ type TEXT NOT NULL DEFAULT 'unknown',
371
+ description TEXT,
372
+ repo TEXT NOT NULL DEFAULT '',
373
+ owner TEXT NOT NULL DEFAULT '',
374
+ created_at TEXT NOT NULL,
375
+ updated_at TEXT NOT NULL
376
+ );
377
+
378
+ CREATE INDEX IF NOT EXISTS idx_entities_type ON entities(type);
379
+ CREATE INDEX IF NOT EXISTS idx_entities_repo ON entities(repo);
380
+
381
+ CREATE TABLE IF NOT EXISTS relations (
382
+ from_entity TEXT NOT NULL,
383
+ to_entity TEXT NOT NULL,
384
+ relation_type TEXT NOT NULL,
385
+ repo TEXT NOT NULL DEFAULT '',
386
+ owner TEXT NOT NULL DEFAULT '',
387
+ created_at TEXT NOT NULL,
388
+ PRIMARY KEY (from_entity, to_entity, relation_type),
389
+ FOREIGN KEY (from_entity) REFERENCES entities(name) ON DELETE CASCADE,
390
+ FOREIGN KEY (to_entity) REFERENCES entities(name) ON DELETE CASCADE
391
+ );
392
+
393
+ CREATE INDEX IF NOT EXISTS idx_relations_from ON relations(from_entity);
394
+ CREATE INDEX IF NOT EXISTS idx_relations_to ON relations(to_entity);
395
+ CREATE INDEX IF NOT EXISTS idx_relations_type ON relations(relation_type);
396
+ CREATE INDEX IF NOT EXISTS idx_relations_repo ON relations(repo);
397
+
398
+ CREATE TABLE IF NOT EXISTS observations (
399
+ id TEXT PRIMARY KEY,
400
+ entity_name TEXT NOT NULL,
401
+ observation TEXT NOT NULL,
402
+ repo TEXT NOT NULL DEFAULT '',
403
+ owner TEXT NOT NULL DEFAULT '',
404
+ created_at TEXT NOT NULL,
405
+ FOREIGN KEY (entity_name) REFERENCES entities(name) ON DELETE CASCADE
406
+ );
407
+
408
+ CREATE INDEX IF NOT EXISTS idx_observations_entity ON observations(entity_name);
409
+ CREATE INDEX IF NOT EXISTS idx_observations_repo ON observations(repo);
410
+ CREATE INDEX IF NOT EXISTS idx_observations_created_at ON observations(created_at);
507
411
  `);
508
412
  const columnsToAdd = [
509
413
  { name: "title", table: "memories", definition: "ALTER TABLE memories ADD COLUMN title TEXT" },
@@ -672,7 +576,7 @@ var MigrationManager = class {
672
576
  HAVING cnt > 1
673
577
  `);
674
578
  if (dupRows.length > 0) {
675
- console.log(`Found ${dupRows.length} duplicate task_code(s). Deduplicating by suffix...`);
579
+ logger.info(`Found ${dupRows.length} duplicate task_code(s). Deduplicating by suffix...`);
676
580
  for (const dup of dupRows) {
677
581
  const rows = this.all(
678
582
  `SELECT id, task_code, created_at FROM tasks
@@ -686,7 +590,7 @@ var MigrationManager = class {
686
590
  const newCode = `${dup.task_code}-${i + 1}`;
687
591
  this.run("UPDATE tasks SET task_code = ? WHERE id = ?", newCode, rows[i].id);
688
592
  }
689
- console.log(` Deduplicated ${dup.task_code}: kept 1 (${rows[0].id}), renamed ${rows.length - 1} rows`);
593
+ logger.info(` Deduplicated ${dup.task_code}: kept 1 (${rows[0].id}), renamed ${rows.length - 1} rows`);
690
594
  }
691
595
  }
692
596
  this.exec(`CREATE UNIQUE INDEX IF NOT EXISTS idx_tasks_code_owner_repo ON tasks(owner, repo, task_code);`);
@@ -1323,7 +1227,7 @@ var MemoryEntity = class extends BaseEntity {
1323
1227
  let sql = "SELECT * FROM memories WHERE code = ?";
1324
1228
  const params = [code];
1325
1229
  if (owner && repo) {
1326
- sql += " AND owner = ? AND repo = ?";
1230
+ sql += " AND ((owner = ? AND repo = ?) OR is_global = 1)";
1327
1231
  params.push(owner, repo);
1328
1232
  }
1329
1233
  const row = this.get(sql, params);
@@ -1523,9 +1427,11 @@ var MemoryEntity = class extends BaseEntity {
1523
1427
  incrementHitCounts(ids) {
1524
1428
  if (!ids || ids.length === 0) return;
1525
1429
  const now = (/* @__PURE__ */ new Date()).toISOString();
1526
- for (const id of ids) {
1527
- this.run("UPDATE memories SET hit_count = hit_count + 1, last_used_at = ? WHERE id = ?", [now, id]);
1528
- }
1430
+ const placeholders = ids.map(() => "?").join(",");
1431
+ this.run(`UPDATE memories SET hit_count = hit_count + 1, last_used_at = ? WHERE id IN (${placeholders})`, [
1432
+ now,
1433
+ ...ids
1434
+ ]);
1529
1435
  }
1530
1436
  incrementRecallCount(id) {
1531
1437
  this.run("UPDATE memories SET recall_count = recall_count + 1, last_used_at = ? WHERE id = ?", [
@@ -1570,9 +1476,20 @@ var MemoryEntity = class extends BaseEntity {
1570
1476
  search,
1571
1477
  offset = 0,
1572
1478
  limit = 50,
1573
- sortBy = "created_at",
1574
1479
  sortOrder = "DESC"
1575
1480
  } = options;
1481
+ let sortBy = options.sortBy ?? "created_at";
1482
+ const ALLOWED_SORT_COLUMNS = /* @__PURE__ */ new Set([
1483
+ "created_at",
1484
+ "updated_at",
1485
+ "importance",
1486
+ "hit_count",
1487
+ "title",
1488
+ "recall_rate"
1489
+ ]);
1490
+ if (!ALLOWED_SORT_COLUMNS.has(sortBy)) {
1491
+ sortBy = "created_at";
1492
+ }
1576
1493
  const where = ["1=1"];
1577
1494
  const params = [];
1578
1495
  if (owner) {
@@ -1616,7 +1533,7 @@ var MemoryEntity = class extends BaseEntity {
1616
1533
  ...this.rowToMemoryEntry(row),
1617
1534
  recall_rate: row.recall_rate || 0
1618
1535
  }));
1619
- return { items, memories: items, total, limit, offset };
1536
+ return { items, total, limit, offset };
1620
1537
  }
1621
1538
  };
1622
1539
 
@@ -2479,34 +2396,45 @@ var SystemEntity = class extends BaseEntity {
2479
2396
  const pendingHandoffsByRepo = Object.fromEntries(pendingHandoffRows.map((row) => [row.repo, row.count]));
2480
2397
  const unassignedHandoffsByRepo = Object.fromEntries(unassignedHandoffRows.map((row) => [row.repo, row.count]));
2481
2398
  const staleClaimsByRepo = Object.fromEntries(staleClaimRows.map((row) => [row.repo, row.count]));
2482
- const repoOwnerFilter = owner ? " AND owner = ?" : "";
2399
+ const ownerWhere = owner ? " WHERE owner = ?" : "";
2400
+ const memoryCountRows = this.all(
2401
+ `SELECT repo, COUNT(*) as count FROM memories${ownerWhere} GROUP BY repo`,
2402
+ ownerParams
2403
+ );
2404
+ const memoryCountByRepo = Object.fromEntries(memoryCountRows.map((r) => [r.repo, r.count]));
2405
+ const taskAggRows = this.all(
2406
+ `SELECT repo, status, COUNT(*) as count FROM tasks${ownerWhere} GROUP BY repo, status`,
2407
+ ownerParams
2408
+ );
2409
+ const taskCountsByRepo = {};
2410
+ for (const row of taskAggRows) {
2411
+ if (!taskCountsByRepo[row.repo]) {
2412
+ taskCountsByRepo[row.repo] = { total: 0, byStatus: {} };
2413
+ }
2414
+ taskCountsByRepo[row.repo].total += row.count;
2415
+ taskCountsByRepo[row.repo].byStatus[row.status] = row.count;
2416
+ }
2417
+ const lastActivityRows = this.all(
2418
+ `SELECT repo, MAX(updated_at) as last FROM (
2419
+ SELECT repo, updated_at FROM memories${ownerWhere}
2420
+ UNION ALL
2421
+ SELECT repo, updated_at FROM tasks${ownerWhere}
2422
+ ) GROUP BY repo`,
2423
+ owner ? [owner, owner] : []
2424
+ );
2425
+ const lastActivityByRepo = Object.fromEntries(lastActivityRows.map((r) => [r.repo, r.last]));
2483
2426
  return repos.map((repo) => {
2484
- const memoryCountRow = this.get(
2485
- `SELECT COUNT(*) as count FROM memories WHERE repo = ?${repoOwnerFilter}`,
2486
- owner ? [repo, owner] : [repo]
2487
- );
2488
- const lastActivityRow = this.get(
2489
- `SELECT MAX(created_at) as last FROM (SELECT created_at FROM memories WHERE repo = ?${repoOwnerFilter} UNION ALL SELECT created_at FROM tasks WHERE repo = ?${repoOwnerFilter} UNION ALL SELECT created_at FROM action_log WHERE repo = ?${repoOwnerFilter})`,
2490
- owner ? [repo, owner, repo, owner, repo, owner] : [repo, repo, repo]
2491
- );
2492
- const taskStatusRows = this.all(
2493
- `SELECT status, COUNT(*) as count FROM tasks WHERE repo = ?${repoOwnerFilter} GROUP BY status`,
2494
- owner ? [repo, owner] : [repo]
2495
- );
2496
- const taskStatusMap = {};
2497
- taskStatusRows.forEach((r) => {
2498
- taskStatusMap[r.status] = r.count;
2499
- });
2500
- const taskCount = taskStatusRows.reduce((sum, r) => sum + r.count, 0);
2427
+ const memCount = memoryCountByRepo[repo] ?? 0;
2428
+ const taskInfo = taskCountsByRepo[repo] ?? { total: 0, byStatus: {} };
2501
2429
  return {
2502
2430
  repo,
2503
- memoryCount: memoryCountRow?.count ?? 0,
2504
- taskCount,
2505
- inProgressCount: taskStatusMap["in_progress"] ?? 0,
2506
- pendingCount: taskStatusMap["pending"] ?? 0,
2507
- blockedCount: taskStatusMap["blocked"] ?? 0,
2508
- backlogCount: taskStatusMap["backlog"] ?? 0,
2509
- lastActivity: lastActivityRow?.last ?? null,
2431
+ memoryCount: memCount,
2432
+ taskCount: taskInfo.total,
2433
+ inProgressCount: taskInfo.byStatus["in_progress"] ?? 0,
2434
+ pendingCount: taskInfo.byStatus["pending"] ?? 0,
2435
+ blockedCount: taskInfo.byStatus["blocked"] ?? 0,
2436
+ backlogCount: taskInfo.byStatus["backlog"] ?? 0,
2437
+ lastActivity: lastActivityByRepo[repo] ?? null,
2510
2438
  activeClaims: activeClaimsByRepo[repo] ?? 0,
2511
2439
  pendingHandoffs: pendingHandoffsByRepo[repo] ?? 0,
2512
2440
  unassignedHandoffs: unassignedHandoffsByRepo[repo] ?? 0,
@@ -2766,7 +2694,7 @@ var StandardEntity = class extends BaseEntity {
2766
2694
  let sql = "SELECT * FROM coding_standards WHERE code = ?";
2767
2695
  const params = [code];
2768
2696
  if (owner && repo) {
2769
- sql += " AND owner = ? AND repo = ?";
2697
+ sql += " AND ((owner = ? AND repo = ?) OR is_global = 1)";
2770
2698
  params.push(owner, repo);
2771
2699
  }
2772
2700
  const row = this.get(sql, params);
@@ -3175,8 +3103,8 @@ var HandoffEntity = class extends BaseEntity {
3175
3103
 
3176
3104
  // src/mcp/storage/write-lock.ts
3177
3105
  import lockfile from "proper-lockfile";
3178
- import path3 from "path";
3179
- import fs3 from "fs";
3106
+ import path from "path";
3107
+ import fs2 from "fs";
3180
3108
  var LOCK_STALE_MS = 3e4;
3181
3109
  var LOCK_RETRY_DELAY_MS = 200;
3182
3110
  var LOCK_RETRY_COUNT = 250;
@@ -3185,9 +3113,9 @@ var WriteLock = class {
3185
3113
  locked = false;
3186
3114
  constructor(dbPath) {
3187
3115
  this.lockTarget = dbPath;
3188
- if (!fs3.existsSync(dbPath)) {
3189
- fs3.mkdirSync(path3.dirname(dbPath), { recursive: true });
3190
- fs3.writeFileSync(dbPath, "");
3116
+ if (!fs2.existsSync(dbPath)) {
3117
+ fs2.mkdirSync(path.dirname(dbPath), { recursive: true });
3118
+ fs2.writeFileSync(dbPath, "");
3191
3119
  }
3192
3120
  }
3193
3121
  /**
@@ -3239,13 +3167,13 @@ var WriteLock = class {
3239
3167
  // src/mcp/storage/sqlite.ts
3240
3168
  function resolveDbPath() {
3241
3169
  if (process.env.MEMORY_DB_PATH) return process.env.MEMORY_DB_PATH;
3242
- const standardConfigDir = process.platform === "win32" ? path4.join(os.homedir(), ".local-memory-mcp") : process.platform === "darwin" ? path4.join(os.homedir(), "Library", "Application Support", "local-memory-mcp") : path4.join(os.homedir(), ".config", "local-memory-mcp");
3243
- const standardPath = path4.join(standardConfigDir, "memory.db");
3244
- if (fs4.existsSync(standardPath)) return standardPath;
3245
- const legacyPath = path4.join(os.homedir(), ".config", "local-memory-mcp", "memory.db");
3246
- if (fs4.existsSync(legacyPath)) return legacyPath;
3247
- const localCwdFile = path4.join(process.cwd(), "storage", "memory.db");
3248
- if (fs4.existsSync(localCwdFile)) return localCwdFile;
3170
+ const standardConfigDir = process.platform === "win32" ? path2.join(os.homedir(), ".local-memory-mcp") : process.platform === "darwin" ? path2.join(os.homedir(), "Library", "Application Support", "local-memory-mcp") : path2.join(os.homedir(), ".config", "local-memory-mcp");
3171
+ const standardPath = path2.join(standardConfigDir, "memory.db");
3172
+ if (fs3.existsSync(standardPath)) return standardPath;
3173
+ const legacyPath = path2.join(os.homedir(), ".config", "local-memory-mcp", "memory.db");
3174
+ if (fs3.existsSync(legacyPath)) return legacyPath;
3175
+ const localCwdFile = path2.join(process.cwd(), "storage", "memory.db");
3176
+ if (fs3.existsSync(localCwdFile)) return localCwdFile;
3249
3177
  return standardPath;
3250
3178
  }
3251
3179
  var DB_PATH = resolveDbPath();
@@ -3268,9 +3196,9 @@ var SQLiteStore = class _SQLiteStore {
3268
3196
  const finalPath = dbPath ?? DB_PATH;
3269
3197
  this.dbPathInstance = finalPath;
3270
3198
  if (finalPath !== ":memory:") {
3271
- const dbDir = path4.dirname(finalPath);
3272
- if (!fs4.existsSync(dbDir)) {
3273
- fs4.mkdirSync(dbDir, { recursive: true });
3199
+ const dbDir = path2.dirname(finalPath);
3200
+ if (!fs3.existsSync(dbDir)) {
3201
+ fs3.mkdirSync(dbDir, { recursive: true });
3274
3202
  }
3275
3203
  }
3276
3204
  this.db = new Database(finalPath);
@@ -3329,12 +3257,12 @@ var SQLiteStore = class _SQLiteStore {
3329
3257
  */
3330
3258
  _attemptRecovery(dbPath) {
3331
3259
  const backupPath = dbPath + ".backup";
3332
- if (fs4.existsSync(backupPath)) {
3260
+ if (fs3.existsSync(backupPath)) {
3333
3261
  logger.warn("[SQLiteStore] Attempting recovery from backup", { backupPath });
3334
3262
  try {
3335
3263
  const corruptPath = `${dbPath}.corrupt_${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "").slice(0, 15)}`;
3336
- fs4.copyFileSync(dbPath, corruptPath);
3337
- fs4.copyFileSync(backupPath, dbPath);
3264
+ fs3.copyFileSync(dbPath, corruptPath);
3265
+ fs3.copyFileSync(backupPath, dbPath);
3338
3266
  logger.warn("[SQLiteStore] Recovery successful. Corrupt file saved to", { corruptPath });
3339
3267
  } catch (err) {
3340
3268
  logger.error("[SQLiteStore] Recovery failed", { error: String(err) });
@@ -3352,7 +3280,7 @@ var SQLiteStore = class _SQLiteStore {
3352
3280
  try {
3353
3281
  this.db.pragma("wal_checkpoint(PASSIVE)");
3354
3282
  const backupPath = this.dbPathInstance + ".backup";
3355
- fs4.copyFileSync(this.dbPathInstance, backupPath);
3283
+ fs3.copyFileSync(this.dbPathInstance, backupPath);
3356
3284
  } catch (err) {
3357
3285
  logger.warn("[SQLiteStore] Backup failed", { error: String(err) });
3358
3286
  }
@@ -3475,8 +3403,8 @@ var RealVectorStore = class {
3475
3403
  };
3476
3404
 
3477
3405
  // src/mcp/session.ts
3478
- import path5 from "path";
3479
- import { fileURLToPath as fileURLToPath3 } from "url";
3406
+ import path3 from "path";
3407
+ import { fileURLToPath } from "url";
3480
3408
  function createSessionContext() {
3481
3409
  return {
3482
3410
  roots: [],
@@ -3488,60 +3416,13 @@ function createSessionContext() {
3488
3416
  supportsElicitationUrl: false
3489
3417
  };
3490
3418
  }
3491
- function updateSessionFromInitialize(session, params) {
3492
- const capabilities = params?.capabilities || {};
3493
- session.clientInfo = params?.clientInfo;
3494
- session.clientCapabilities = capabilities;
3495
- session.supportsRoots = Boolean(capabilities.roots);
3496
- session.supportsSampling = Boolean(capabilities.sampling);
3497
- const sampling = capabilities.sampling;
3498
- session.supportsSamplingTools = Boolean(sampling?.tools);
3499
- session.supportsElicitation = Boolean(capabilities.elicitation);
3500
- session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
3501
- session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
3502
- }
3503
- function supportsElicitationMode(capability, mode) {
3504
- if (!capability || typeof capability !== "object") {
3505
- return false;
3506
- }
3507
- const cap = capability;
3508
- if (mode === "form") {
3509
- return Object.keys(cap).length === 0 || typeof cap.form === "object";
3510
- }
3511
- return typeof cap.url === "object";
3512
- }
3513
- function updateSessionRoots(session, roots) {
3514
- const normalized = normalizeRoots(roots);
3515
- const previous = JSON.stringify(session.roots);
3516
- const next = JSON.stringify(normalized);
3517
- session.roots = normalized;
3518
- return previous !== next;
3519
- }
3520
- function normalizeRoots(roots) {
3521
- if (!Array.isArray(roots)) return [];
3522
- const seen = /* @__PURE__ */ new Set();
3523
- const normalized = [];
3524
- for (const root of roots) {
3525
- if (!root || typeof root !== "object") continue;
3526
- const r = root;
3527
- const uri = typeof r.uri === "string" ? r.uri : void 0;
3528
- const name = typeof r.name === "string" ? r.name : void 0;
3529
- if (!uri || seen.has(uri)) continue;
3530
- seen.add(uri);
3531
- normalized.push({ uri, name });
3532
- }
3533
- return normalized;
3534
- }
3535
- function extractRootsFromResult(result) {
3536
- return normalizeRoots(result?.roots);
3537
- }
3538
3419
  function getFilesystemRoots(session) {
3539
3420
  if (!session) return [];
3540
3421
  const resolved = [];
3541
3422
  for (const root of session.roots) {
3542
3423
  if (!root.uri.startsWith("file://")) continue;
3543
3424
  try {
3544
- resolved.push(path5.resolve(fileURLToPath3(root.uri)));
3425
+ resolved.push(path3.resolve(fileURLToPath(root.uri)));
3545
3426
  } catch {
3546
3427
  }
3547
3428
  }
@@ -3550,19 +3431,19 @@ function getFilesystemRoots(session) {
3550
3431
  function isPathWithinRoots(targetPath, session) {
3551
3432
  const roots = getFilesystemRoots(session);
3552
3433
  if (roots.length === 0) return true;
3553
- const normalizedTarget = path5.resolve(targetPath);
3434
+ const normalizedTarget = path3.resolve(targetPath);
3554
3435
  return roots.some((rootPath) => {
3555
- const relative = path5.relative(rootPath, normalizedTarget);
3556
- return relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative);
3436
+ const relative = path3.relative(rootPath, normalizedTarget);
3437
+ return relative === "" || !relative.startsWith("..") && !path3.isAbsolute(relative);
3557
3438
  });
3558
3439
  }
3559
3440
  function findContainingRoot(targetPath, session) {
3560
3441
  const roots = getFilesystemRoots(session);
3561
3442
  if (roots.length === 0) return null;
3562
- const normalizedTarget = path5.resolve(targetPath);
3443
+ const normalizedTarget = path3.resolve(targetPath);
3563
3444
  for (const rootPath of roots) {
3564
- const relative = path5.relative(rootPath, normalizedTarget);
3565
- if (relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative)) {
3445
+ const relative = path3.relative(rootPath, normalizedTarget);
3446
+ if (relative === "" || !relative.startsWith("..") && !path3.isAbsolute(relative)) {
3566
3447
  return rootPath;
3567
3448
  }
3568
3449
  }
@@ -3571,14 +3452,14 @@ function findContainingRoot(targetPath, session) {
3571
3452
  function inferRepoFromSession(session) {
3572
3453
  const roots = getFilesystemRoots(session);
3573
3454
  if (roots.length === 1) {
3574
- return path5.basename(roots[0]);
3455
+ return path3.basename(roots[0]);
3575
3456
  }
3576
3457
  return void 0;
3577
3458
  }
3578
3459
  function inferOwnerFromSession(session) {
3579
3460
  const roots = getFilesystemRoots(session);
3580
3461
  if (roots.length === 1) {
3581
- const parts = roots[0].split(path5.sep).filter(Boolean);
3462
+ const parts = roots[0].split(path3.sep).filter(Boolean);
3582
3463
  if (parts.length >= 2) {
3583
3464
  return parts[parts.length - 2];
3584
3465
  }
@@ -3586,8 +3467,84 @@ function inferOwnerFromSession(session) {
3586
3467
  return void 0;
3587
3468
  }
3588
3469
 
3589
- // src/mcp/tools/tool-definitions.ts
3590
- var TOOL_DEFINITIONS = [
3470
+ // src/mcp/prompts/loader.ts
3471
+ import fs4 from "fs";
3472
+ import path4 from "path";
3473
+ import { fileURLToPath as fileURLToPath2 } from "url";
3474
+ import matter from "gray-matter";
3475
+ var __filename = fileURLToPath2(import.meta.url);
3476
+ var __dirname = path4.dirname(__filename);
3477
+ function findPromptDir() {
3478
+ const candidates = [
3479
+ // Production if chunked into dist/
3480
+ "./prompts",
3481
+ // Production if inlined into dist/mcp/
3482
+ "../prompts",
3483
+ // Dev: /src/mcp/prompts/definitions (next to loader.ts)
3484
+ "./definitions"
3485
+ ].map((relPath) => path4.resolve(__dirname, relPath));
3486
+ for (const dir of candidates) {
3487
+ if (fs4.existsSync(dir)) {
3488
+ const files = fs4.readdirSync(dir);
3489
+ if (files.some((f) => f.endsWith(".md"))) {
3490
+ return dir;
3491
+ }
3492
+ }
3493
+ }
3494
+ return path4.resolve(__dirname, "./definitions");
3495
+ }
3496
+ var PROMPT_DIR = findPromptDir();
3497
+ function listPromptFiles() {
3498
+ if (!fs4.existsSync(PROMPT_DIR)) return [];
3499
+ return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
3500
+ }
3501
+ function loadPromptFromMarkdown(name) {
3502
+ const filePath = path4.join(PROMPT_DIR, `${name}.md`);
3503
+ if (!fs4.existsSync(filePath)) {
3504
+ throw new Error(`Prompt file not found: ${filePath}`);
3505
+ }
3506
+ const fileContent = fs4.readFileSync(filePath, "utf-8");
3507
+ const { data, content } = matter(fileContent);
3508
+ return {
3509
+ name: data.name || name,
3510
+ description: data.description || "",
3511
+ arguments: data.arguments || [],
3512
+ agent: data.agent,
3513
+ content: content.trim()
3514
+ };
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) => path4.resolve(__dirname, relPath));
3525
+ for (const dir of candidates) {
3526
+ if (fs4.existsSync(dir)) {
3527
+ const filePath = path4.join(dir, "instructions.md");
3528
+ if (fs4.existsSync(filePath)) {
3529
+ return dir;
3530
+ }
3531
+ }
3532
+ }
3533
+ return path4.resolve(__dirname, "./server");
3534
+ }
3535
+ var SERVER_DIR = findServerInstructionsDir();
3536
+ function loadServerInstructions() {
3537
+ const filePath = path4.join(SERVER_DIR, "instructions.md");
3538
+ if (!fs4.existsSync(filePath)) {
3539
+ throw new Error(`Server instructions file not found: ${filePath}`);
3540
+ }
3541
+ const fileContent = fs4.readFileSync(filePath, "utf-8");
3542
+ const { content } = matter(fileContent);
3543
+ return content.trim();
3544
+ }
3545
+
3546
+ // src/mcp/tools/definitions/memory.ts
3547
+ var MEMORY_TOOL_DEFINITIONS = [
3591
3548
  {
3592
3549
  name: "memory-synthesize",
3593
3550
  title: "Memory Synthesize",
@@ -3640,60 +3597,6 @@ var TOOL_DEFINITIONS = [
3640
3597
  required: ["repo", "objective", "answer", "iterations", "toolCalls"]
3641
3598
  }
3642
3599
  },
3643
- {
3644
- name: "task-create-interactive",
3645
- title: "Interactive Task Create",
3646
- description: "Create a task with MCP elicitation fallback for any missing required fields. Best when an agent knows a task is needed but still needs user confirmation for repo, title, or phase.",
3647
- annotations: {
3648
- readOnlyHint: false,
3649
- idempotentHint: false,
3650
- destructiveHint: false,
3651
- openWorldHint: false
3652
- },
3653
- inputSchema: {
3654
- type: "object",
3655
- properties: {
3656
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
3657
- repo: {
3658
- type: "string",
3659
- description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
3660
- },
3661
- task_code: { type: "string" },
3662
- phase: { type: "string" },
3663
- title: { type: "string", minLength: 3, maxLength: 100 },
3664
- description: {
3665
- type: "string",
3666
- minLength: 1,
3667
- description: "Detailed description. MUST follow format: 1. Context & Analysis, 2. Step & Implementation, 3. Acceptance & Verification"
3668
- },
3669
- status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3670
- priority: {
3671
- type: "number",
3672
- minimum: 1,
3673
- maximum: 5,
3674
- default: 3,
3675
- description: "Task priority where 1=Low, 2=Normal, 3=Medium, 4=High, 5=Critical."
3676
- },
3677
- agent: { type: "string" },
3678
- role: { type: "string" },
3679
- doc_path: { type: "string" },
3680
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3681
- },
3682
- required: ["owner"]
3683
- },
3684
- outputSchema: {
3685
- type: "object",
3686
- properties: {
3687
- repo: { type: "string" },
3688
- task_code: { type: "string" },
3689
- phase: { type: "string" },
3690
- title: { type: "string" },
3691
- status: { type: "string" },
3692
- priority: { type: "number" }
3693
- },
3694
- required: ["repo", "task_code", "phase", "title", "status", "priority"]
3695
- }
3696
- },
3697
3600
  {
3698
3601
  name: "memory-detail",
3699
3602
  title: "Memory Detail",
@@ -3723,100 +3626,15 @@ var TOOL_DEFINITIONS = [
3723
3626
  }
3724
3627
  },
3725
3628
  {
3726
- name: "standard-detail",
3727
- title: "Standard Detail",
3728
- description: "Fetch full details of a specific coding standard by ID or short code. Use after standard-search when a result is relevant and full guidance is needed.",
3729
- inputSchema: {
3730
- type: "object",
3731
- oneOf: [
3732
- {
3733
- title: "By ID",
3734
- required: ["id"],
3735
- properties: {
3736
- id: { type: "string", format: "uuid", description: "Coding standard ID." },
3737
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3738
- }
3739
- },
3740
- {
3741
- title: "By code",
3742
- required: ["code", "owner", "repo"],
3743
- properties: {
3744
- code: { type: "string", description: "Short standard code (e.g. 'A3KPQ2')." },
3745
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)." },
3746
- repo: { type: "string", description: "Repository/project name." },
3747
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3748
- }
3749
- }
3750
- ]
3751
- }
3752
- },
3753
- {
3754
- name: "task-detail",
3755
- title: "Task Detail",
3756
- description: "Fetch full details of a specific task by ID or task code. Use this when you have a task ID or code and need to read the full description and comments.",
3757
- inputSchema: {
3758
- type: "object",
3759
- oneOf: [
3760
- {
3761
- title: "By ID",
3762
- required: ["owner", "repo", "id"],
3763
- properties: {
3764
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
3765
- repo: { type: "string", description: "Repository name" },
3766
- id: { type: "string", format: "uuid", description: "Task ID" },
3767
- structured: {
3768
- type: "boolean",
3769
- default: false,
3770
- description: "If true, returns structured JSON without the text content details."
3771
- }
3772
- }
3773
- },
3774
- {
3775
- title: "By task_code",
3776
- required: ["owner", "repo", "task_code"],
3777
- properties: {
3778
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
3779
- repo: { type: "string", description: "Repository name" },
3780
- task_code: { type: "string", description: "Task code (e.g. TASK-001)" },
3781
- structured: {
3782
- type: "boolean",
3783
- default: false,
3784
- description: "If true, returns structured JSON without the text content details."
3785
- }
3786
- }
3787
- },
3788
- {
3789
- title: "By task_codes",
3790
- required: ["owner", "repo", "task_codes"],
3791
- properties: {
3792
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
3793
- repo: { type: "string", description: "Repository name" },
3794
- task_codes: {
3795
- type: "array",
3796
- items: { type: "string" },
3797
- minItems: 1,
3798
- description: "Array of task codes (e.g. TASK-001)"
3799
- },
3800
- structured: {
3801
- type: "boolean",
3802
- default: false,
3803
- description: "If true, returns structured JSON without the text content details."
3804
- }
3805
- }
3806
- }
3807
- ]
3808
- }
3809
- },
3810
- {
3811
- name: "memory-store",
3812
- title: "Memory Store",
3813
- description: "Store a new durable knowledge entry. Do not store coordination state here: task claims, file claims, agent registration, and handoffs belong to task-claim, task-update, and handoff-* tools. Keep 'title' concise and human-readable; put auxiliary context into 'metadata'.",
3814
- annotations: {
3815
- readOnlyHint: false,
3816
- idempotentHint: false,
3817
- destructiveHint: false,
3818
- openWorldHint: false
3819
- },
3629
+ name: "memory-store",
3630
+ title: "Memory Store",
3631
+ description: "Store a new durable knowledge entry. Do not store coordination state here: task claims, file claims, agent registration, and handoffs belong to task-claim, task-update, and handoff-* tools. Keep 'title' concise and human-readable; put auxiliary context into 'metadata'.",
3632
+ annotations: {
3633
+ readOnlyHint: false,
3634
+ idempotentHint: false,
3635
+ destructiveHint: false,
3636
+ openWorldHint: false
3637
+ },
3820
3638
  inputSchema: {
3821
3639
  type: "object",
3822
3640
  oneOf: [
@@ -4278,85 +4096,6 @@ var TOOL_DEFINITIONS = [
4278
4096
  required: ["success"]
4279
4097
  }
4280
4098
  },
4281
- {
4282
- name: "standard-delete",
4283
- title: "Standard Delete",
4284
- description: "Delete one or more coding standards. Supports single 'id' or bulk 'ids'.",
4285
- annotations: {
4286
- readOnlyHint: false,
4287
- idempotentHint: false,
4288
- destructiveHint: true,
4289
- openWorldHint: false
4290
- },
4291
- inputSchema: {
4292
- type: "object",
4293
- oneOf: [
4294
- {
4295
- title: "By single ID",
4296
- required: ["owner", "repo", "id"],
4297
- properties: {
4298
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4299
- repo: { type: "string", description: "Repository name." },
4300
- id: { type: "string", format: "uuid", description: "Coding standard ID to delete." },
4301
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4302
- }
4303
- },
4304
- {
4305
- title: "By bulk IDs",
4306
- required: ["owner", "repo", "ids"],
4307
- properties: {
4308
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4309
- repo: { type: "string", description: "Repository name." },
4310
- ids: {
4311
- type: "array",
4312
- items: { type: "string", format: "uuid" },
4313
- minItems: 1,
4314
- description: "Array of coding standard IDs to delete"
4315
- },
4316
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4317
- }
4318
- },
4319
- {
4320
- title: "By single code",
4321
- required: ["owner", "repo", "code"],
4322
- properties: {
4323
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4324
- repo: { type: "string", description: "Repository name." },
4325
- code: { type: "string", maxLength: 20, description: "Short standard code." },
4326
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4327
- }
4328
- },
4329
- {
4330
- title: "By bulk codes",
4331
- required: ["owner", "repo", "codes"],
4332
- properties: {
4333
- owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4334
- repo: { type: "string", description: "Repository name." },
4335
- codes: {
4336
- type: "array",
4337
- items: { type: "string", maxLength: 20 },
4338
- minItems: 1,
4339
- description: "Array of standard codes to delete"
4340
- },
4341
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4342
- }
4343
- }
4344
- ]
4345
- },
4346
- outputSchema: {
4347
- type: "object",
4348
- properties: {
4349
- success: { type: "boolean" },
4350
- id: { type: "string" },
4351
- code: { type: "string" },
4352
- ids: { type: "array", items: { type: "string" } },
4353
- codes: { type: "array", items: { type: "string" } },
4354
- repo: { type: "string" },
4355
- deletedCount: { type: "number" }
4356
- },
4357
- required: ["success"]
4358
- }
4359
- },
4360
4099
  {
4361
4100
  name: "memory-recap",
4362
4101
  title: "Memory Recap",
@@ -4430,39 +4169,38 @@ var TOOL_DEFINITIONS = [
4430
4169
  },
4431
4170
  required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
4432
4171
  }
4433
- },
4172
+ }
4173
+ ];
4174
+
4175
+ // src/mcp/tools/definitions/task.ts
4176
+ var TASK_TOOL_DEFINITIONS = [
4434
4177
  {
4435
- name: "task-create",
4436
- title: "Task Create",
4437
- description: "Register one or more new tasks in a repository. task_code must be unique within the repository. Supports single task object or an array of tasks for bulk creation.",
4178
+ name: "task-create-interactive",
4179
+ title: "Interactive Task Create",
4180
+ description: "Create a task with MCP elicitation fallback for any missing required fields. Best when an agent knows a task is needed but still needs user confirmation for repo, title, or phase.",
4438
4181
  annotations: {
4439
4182
  readOnlyHint: false,
4440
4183
  idempotentHint: false,
4184
+ destructiveHint: false,
4441
4185
  openWorldHint: false
4442
4186
  },
4443
4187
  inputSchema: {
4444
4188
  type: "object",
4445
4189
  properties: {
4446
4190
  owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4447
- repo: { type: "string", description: "Repository/project name" },
4448
- task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
4449
- phase: { type: "string", description: "Project phase (Required for single task)" },
4450
- title: {
4191
+ repo: {
4451
4192
  type: "string",
4452
- minLength: 3,
4453
- maxLength: 100,
4454
- description: "Task objective (Required for single task)"
4193
+ description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
4455
4194
  },
4195
+ task_code: { type: "string" },
4196
+ phase: { type: "string" },
4197
+ title: { type: "string", minLength: 3, maxLength: 100 },
4456
4198
  description: {
4457
4199
  type: "string",
4200
+ minLength: 1,
4458
4201
  description: "Detailed description. MUST follow format: 1. Context & Analysis, 2. Step & Implementation, 3. Acceptance & Verification"
4459
4202
  },
4460
- status: {
4461
- type: "string",
4462
- enum: ["backlog", "pending"],
4463
- default: "backlog",
4464
- description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
4465
- },
4203
+ status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
4466
4204
  priority: {
4467
4205
  type: "number",
4468
4206
  minimum: 1,
@@ -4470,22 +4208,138 @@ var TOOL_DEFINITIONS = [
4470
4208
  default: 3,
4471
4209
  description: "Task priority where 1=Low, 2=Normal, 3=Medium, 4=High, 5=Critical."
4472
4210
  },
4473
- agent: { type: "string", description: "Agent assigned to this task" },
4474
- role: { type: "string", description: "Role of the assigned agent" },
4475
- doc_path: { type: "string", description: "Path to related documentation file" },
4476
- tags: { type: "array", items: { type: "string" }, description: "Tags for categorization" },
4477
- metadata: { type: "object", description: "Structured metadata for additional context" },
4478
- parent_id: {
4479
- type: "string",
4480
- description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
4481
- },
4482
- depends_on: {
4483
- type: "string",
4484
- description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing."
4211
+ agent: { type: "string" },
4212
+ role: { type: "string" },
4213
+ doc_path: { type: "string" },
4214
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4215
+ },
4216
+ required: ["owner"]
4217
+ },
4218
+ outputSchema: {
4219
+ type: "object",
4220
+ properties: {
4221
+ repo: { type: "string" },
4222
+ task_code: { type: "string" },
4223
+ phase: { type: "string" },
4224
+ title: { type: "string" },
4225
+ status: { type: "string" },
4226
+ priority: { type: "number" }
4227
+ },
4228
+ required: ["repo", "task_code", "phase", "title", "status", "priority"]
4229
+ }
4230
+ },
4231
+ {
4232
+ name: "task-detail",
4233
+ title: "Task Detail",
4234
+ description: "Fetch full details of a specific task by ID or task code. Use this when you have a task ID or code and need to read the full description and comments.",
4235
+ inputSchema: {
4236
+ type: "object",
4237
+ oneOf: [
4238
+ {
4239
+ title: "By ID",
4240
+ required: ["owner", "repo", "id"],
4241
+ properties: {
4242
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4243
+ repo: { type: "string", description: "Repository name" },
4244
+ id: { type: "string", format: "uuid", description: "Task ID" },
4245
+ structured: {
4246
+ type: "boolean",
4247
+ default: false,
4248
+ description: "If true, returns structured JSON without the text content details."
4249
+ }
4250
+ }
4485
4251
  },
4486
- est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
4487
- tasks: {
4488
- type: "array",
4252
+ {
4253
+ title: "By task_code",
4254
+ required: ["owner", "repo", "task_code"],
4255
+ properties: {
4256
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4257
+ repo: { type: "string", description: "Repository name" },
4258
+ task_code: { type: "string", description: "Task code (e.g. TASK-001)" },
4259
+ structured: {
4260
+ type: "boolean",
4261
+ default: false,
4262
+ description: "If true, returns structured JSON without the text content details."
4263
+ }
4264
+ }
4265
+ },
4266
+ {
4267
+ title: "By task_codes",
4268
+ required: ["owner", "repo", "task_codes"],
4269
+ properties: {
4270
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4271
+ repo: { type: "string", description: "Repository name" },
4272
+ task_codes: {
4273
+ type: "array",
4274
+ items: { type: "string" },
4275
+ minItems: 1,
4276
+ description: "Array of task codes (e.g. TASK-001)"
4277
+ },
4278
+ structured: {
4279
+ type: "boolean",
4280
+ default: false,
4281
+ description: "If true, returns structured JSON without the text content details."
4282
+ }
4283
+ }
4284
+ }
4285
+ ]
4286
+ }
4287
+ },
4288
+ {
4289
+ name: "task-create",
4290
+ title: "Task Create",
4291
+ description: "Register one or more new tasks in a repository. task_code must be unique within the repository. Supports single task object or an array of tasks for bulk creation.",
4292
+ annotations: {
4293
+ readOnlyHint: false,
4294
+ idempotentHint: false,
4295
+ openWorldHint: false
4296
+ },
4297
+ inputSchema: {
4298
+ type: "object",
4299
+ properties: {
4300
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
4301
+ repo: { type: "string", description: "Repository/project name" },
4302
+ task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
4303
+ phase: { type: "string", description: "Project phase (Required for single task)" },
4304
+ title: {
4305
+ type: "string",
4306
+ minLength: 3,
4307
+ maxLength: 100,
4308
+ description: "Task objective (Required for single task)"
4309
+ },
4310
+ description: {
4311
+ type: "string",
4312
+ description: "Detailed description. MUST follow format: 1. Context & Analysis, 2. Step & Implementation, 3. Acceptance & Verification"
4313
+ },
4314
+ status: {
4315
+ type: "string",
4316
+ enum: ["backlog", "pending"],
4317
+ default: "backlog",
4318
+ description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
4319
+ },
4320
+ priority: {
4321
+ type: "number",
4322
+ minimum: 1,
4323
+ maximum: 5,
4324
+ default: 3,
4325
+ description: "Task priority where 1=Low, 2=Normal, 3=Medium, 4=High, 5=Critical."
4326
+ },
4327
+ agent: { type: "string", description: "Agent assigned to this task" },
4328
+ role: { type: "string", description: "Role of the assigned agent" },
4329
+ doc_path: { type: "string", description: "Path to related documentation file" },
4330
+ tags: { type: "array", items: { type: "string" }, description: "Tags for categorization" },
4331
+ metadata: { type: "object", description: "Structured metadata for additional context" },
4332
+ parent_id: {
4333
+ type: "string",
4334
+ description: "Optional parent task ID (UUID) or parent task code (e.g. TASK-001). Resolved to UUID before storing."
4335
+ },
4336
+ depends_on: {
4337
+ type: "string",
4338
+ description: "Optional task ID (UUID) or task code (e.g. TASK-001). Resolved to UUID before storing."
4339
+ },
4340
+ est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
4341
+ tasks: {
4342
+ type: "array",
4489
4343
  items: {
4490
4344
  type: "object",
4491
4345
  properties: {
@@ -5047,7 +4901,11 @@ var TOOL_DEFINITIONS = [
5047
4901
  },
5048
4902
  required: ["schema", "query", "count", "total", "offset", "limit", "results"]
5049
4903
  }
5050
- },
4904
+ }
4905
+ ];
4906
+
4907
+ // src/mcp/tools/definitions/handoff.ts
4908
+ var HANDOFF_TOOL_DEFINITIONS = [
5051
4909
  {
5052
4910
  name: "handoff-create",
5053
4911
  title: "Handoff Create",
@@ -5303,6 +5161,117 @@ var TOOL_DEFINITIONS = [
5303
5161
  },
5304
5162
  required: ["success", "repo", "task_id"]
5305
5163
  }
5164
+ }
5165
+ ];
5166
+
5167
+ // src/mcp/tools/definitions/standard.ts
5168
+ var STANDARD_TOOL_DEFINITIONS = [
5169
+ {
5170
+ name: "standard-detail",
5171
+ title: "Standard Detail",
5172
+ description: "Fetch full details of a specific coding standard by ID or short code. Use after standard-search when a result is relevant and full guidance is needed.",
5173
+ inputSchema: {
5174
+ type: "object",
5175
+ oneOf: [
5176
+ {
5177
+ title: "By ID",
5178
+ required: ["id"],
5179
+ properties: {
5180
+ id: { type: "string", format: "uuid", description: "Coding standard ID." },
5181
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
5182
+ }
5183
+ },
5184
+ {
5185
+ title: "By code",
5186
+ required: ["code", "owner", "repo"],
5187
+ properties: {
5188
+ code: { type: "string", description: "Short standard code (e.g. 'A3KPQ2')." },
5189
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)." },
5190
+ repo: { type: "string", description: "Repository/project name." },
5191
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
5192
+ }
5193
+ }
5194
+ ]
5195
+ }
5196
+ },
5197
+ {
5198
+ name: "standard-delete",
5199
+ title: "Standard Delete",
5200
+ description: "Delete one or more coding standards. Supports single 'id' or bulk 'ids'.",
5201
+ annotations: {
5202
+ readOnlyHint: false,
5203
+ idempotentHint: false,
5204
+ destructiveHint: true,
5205
+ openWorldHint: false
5206
+ },
5207
+ inputSchema: {
5208
+ type: "object",
5209
+ oneOf: [
5210
+ {
5211
+ title: "By single ID",
5212
+ required: ["owner", "repo", "id"],
5213
+ properties: {
5214
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5215
+ repo: { type: "string", description: "Repository name." },
5216
+ id: { type: "string", format: "uuid", description: "Coding standard ID to delete." },
5217
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
5218
+ }
5219
+ },
5220
+ {
5221
+ title: "By bulk IDs",
5222
+ required: ["owner", "repo", "ids"],
5223
+ properties: {
5224
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5225
+ repo: { type: "string", description: "Repository name." },
5226
+ ids: {
5227
+ type: "array",
5228
+ items: { type: "string", format: "uuid" },
5229
+ minItems: 1,
5230
+ description: "Array of coding standard IDs to delete"
5231
+ },
5232
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
5233
+ }
5234
+ },
5235
+ {
5236
+ title: "By single code",
5237
+ required: ["owner", "repo", "code"],
5238
+ properties: {
5239
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5240
+ repo: { type: "string", description: "Repository name." },
5241
+ code: { type: "string", maxLength: 20, description: "Short standard code." },
5242
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
5243
+ }
5244
+ },
5245
+ {
5246
+ title: "By bulk codes",
5247
+ required: ["owner", "repo", "codes"],
5248
+ properties: {
5249
+ owner: { type: "string", description: "Organization/namespace (e.g., GitHub org or username)" },
5250
+ repo: { type: "string", description: "Repository name." },
5251
+ codes: {
5252
+ type: "array",
5253
+ items: { type: "string", maxLength: 20 },
5254
+ minItems: 1,
5255
+ description: "Array of standard codes to delete"
5256
+ },
5257
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
5258
+ }
5259
+ }
5260
+ ]
5261
+ },
5262
+ outputSchema: {
5263
+ type: "object",
5264
+ properties: {
5265
+ success: { type: "boolean" },
5266
+ id: { type: "string" },
5267
+ code: { type: "string" },
5268
+ ids: { type: "array", items: { type: "string" } },
5269
+ codes: { type: "array", items: { type: "string" } },
5270
+ repo: { type: "string" },
5271
+ deletedCount: { type: "number" }
5272
+ },
5273
+ required: ["success"]
5274
+ }
5306
5275
  },
5307
5276
  {
5308
5277
  name: "standard-store",
@@ -5483,82 +5452,652 @@ var TOOL_DEFINITIONS = [
5483
5452
  structured: { type: "boolean", default: false }
5484
5453
  }
5485
5454
  }
5486
- ]
5455
+ ]
5456
+ },
5457
+ outputSchema: {
5458
+ type: "object",
5459
+ properties: {
5460
+ success: { type: "boolean" },
5461
+ id: { type: "string" },
5462
+ updatedFields: { type: "array", items: { type: "string" } }
5463
+ },
5464
+ required: ["success", "id", "updatedFields"]
5465
+ }
5466
+ },
5467
+ {
5468
+ name: "standard-search",
5469
+ title: "Standard Search",
5470
+ description: "MANDATORY PRE-IMPLEMENTATION CHECK: Call before any code edit, test edit, refactor, migration, or implementation decision to find applicable coding standards. Returns a compact pointer table; use `standard-detail` for relevant results. If no relevant standards are returned, continue and state that no applicable standards were found.",
5471
+ annotations: {
5472
+ readOnlyHint: true,
5473
+ idempotentHint: true,
5474
+ openWorldHint: false
5475
+ },
5476
+ inputSchema: {
5477
+ type: "object",
5478
+ properties: {
5479
+ query: { type: "string", description: "Search query (optional, searches title/content)" },
5480
+ stack: {
5481
+ type: "array",
5482
+ items: { type: "string" },
5483
+ description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
5484
+ },
5485
+ tags: {
5486
+ type: "array",
5487
+ items: { type: "string" },
5488
+ description: "Tag filter"
5489
+ },
5490
+ language: { type: "string", description: "Programming language filter" },
5491
+ context: { type: "string", description: "Context/category filter" },
5492
+ version: { type: "string", description: "Version filter" },
5493
+ repo: { type: "string", description: "Repository filter (optional)" },
5494
+ is_global: { type: "boolean", description: "Filter by global/repo-specific" },
5495
+ limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
5496
+ offset: { type: "number", minimum: 0, default: 0 },
5497
+ structured: { type: "boolean", default: false }
5498
+ },
5499
+ required: []
5500
+ },
5501
+ outputSchema: {
5502
+ type: "object",
5503
+ properties: {
5504
+ schema: { type: "string", enum: ["standard-search"] },
5505
+ query: { type: "string" },
5506
+ count: { type: "number", description: "Number of rows returned" },
5507
+ total: { type: "number", description: "Total number of matches before pagination" },
5508
+ offset: { type: "number" },
5509
+ limit: { type: "number" },
5510
+ results: {
5511
+ type: "object",
5512
+ properties: {
5513
+ columns: {
5514
+ type: "array",
5515
+ items: { type: "string" }
5516
+ },
5517
+ rows: {
5518
+ type: "array",
5519
+ items: { type: "array" },
5520
+ description: "Each row includes standard id and pointer metadata. Fetch full content via standard-detail."
5521
+ }
5522
+ },
5523
+ required: ["columns", "rows"]
5524
+ }
5525
+ },
5526
+ required: ["schema", "query", "count", "total", "offset", "limit", "results"]
5527
+ }
5528
+ }
5529
+ ];
5530
+
5531
+ // src/mcp/tools/definitions/agent.ts
5532
+ var AGENT_TOOL_DEFINITIONS = [
5533
+ {
5534
+ name: "agent-context",
5535
+ title: "Agent Context Recall",
5536
+ description: "Returns relevant context for the current agent session. Auto-detects active repo, searches relevant memories, lists active tasks, and surfaces recent decisions formatted as a single context block ready for injection into agent prompts.",
5537
+ annotations: {
5538
+ readOnlyHint: true,
5539
+ idempotentHint: true,
5540
+ openWorldHint: false
5541
+ },
5542
+ inputSchema: {
5543
+ type: "object",
5544
+ properties: {
5545
+ owner: {
5546
+ type: "string",
5547
+ description: "Organization/namespace (e.g., GitHub org or username). Auto-detected from session when omitted."
5548
+ },
5549
+ repo: {
5550
+ type: "string",
5551
+ description: "Repository/project name. Auto-detected from session when omitted."
5552
+ },
5553
+ objective: {
5554
+ type: "string",
5555
+ description: "Current agent objective to search relevant memories around."
5556
+ },
5557
+ type_filter: {
5558
+ type: "string",
5559
+ description: "Memory type to filter by (e.g., 'decision', 'code_fact', 'pattern', 'mistake'). Default: all."
5560
+ },
5561
+ limit: {
5562
+ type: "number",
5563
+ minimum: 1,
5564
+ maximum: 100,
5565
+ default: 5,
5566
+ description: "Maximum number of memories to return."
5567
+ },
5568
+ structured: {
5569
+ type: "boolean",
5570
+ default: false,
5571
+ description: "If true, returns structured JSON results."
5572
+ }
5573
+ }
5574
+ },
5575
+ outputSchema: {
5576
+ type: "object",
5577
+ properties: {
5578
+ schema: { type: "string", enum: ["agent-context"] },
5579
+ repo: { type: "string" },
5580
+ objective: { type: "string" },
5581
+ memories: {
5582
+ type: "array",
5583
+ items: {
5584
+ type: "object",
5585
+ properties: {
5586
+ id: { type: "string" },
5587
+ code: { type: "string" },
5588
+ title: { type: "string" },
5589
+ type: { type: "string" },
5590
+ importance: { type: "number" }
5591
+ }
5592
+ }
5593
+ },
5594
+ decisions: {
5595
+ type: "array",
5596
+ items: {
5597
+ type: "object",
5598
+ properties: {
5599
+ id: { type: "string" },
5600
+ code: { type: "string" },
5601
+ title: { type: "string" },
5602
+ importance: { type: "number" }
5603
+ }
5604
+ }
5605
+ },
5606
+ tasks: {
5607
+ type: "array",
5608
+ items: {
5609
+ type: "object",
5610
+ properties: {
5611
+ task_code: { type: "string" },
5612
+ title: { type: "string" },
5613
+ status: { type: "string" },
5614
+ priority: { type: "number" }
5615
+ }
5616
+ }
5617
+ }
5618
+ },
5619
+ required: ["schema", "repo", "memories", "decisions", "tasks"]
5620
+ }
5621
+ },
5622
+ {
5623
+ name: "decision-log",
5624
+ title: "Decision Logger",
5625
+ description: "Logs a structured decision as a memory entry. Accepts a summary of what was decided, the context/situation, the rationale, and optional alternatives considered. Internally reuses the memory-store handler with type='decision' and importance=4. Owner and repo are auto-inferred from session when omitted.",
5626
+ annotations: {
5627
+ readOnlyHint: false,
5628
+ idempotentHint: false,
5629
+ openWorldHint: false
5630
+ },
5631
+ inputSchema: {
5632
+ type: "object",
5633
+ properties: {
5634
+ summary: {
5635
+ type: "string",
5636
+ description: "What was decided (used as the memory title). Max 255 characters."
5637
+ },
5638
+ context: {
5639
+ type: "string",
5640
+ description: "The situation or context surrounding the decision. Min 10 characters."
5641
+ },
5642
+ rationale: {
5643
+ type: "string",
5644
+ description: "Why this decision was made. Min 10 characters."
5645
+ },
5646
+ alternatives: {
5647
+ type: "array",
5648
+ items: { type: "string" },
5649
+ description: "Alternatives that were considered (optional)."
5650
+ },
5651
+ tags: {
5652
+ type: "array",
5653
+ items: { type: "string" },
5654
+ description: "Additional tags to apply (auto-includes 'decision')."
5655
+ },
5656
+ owner: {
5657
+ type: "string",
5658
+ description: "Organization/namespace. Auto-detected from session when omitted."
5659
+ },
5660
+ repo: {
5661
+ type: "string",
5662
+ description: "Repository/project name. Auto-detected from session when omitted."
5663
+ },
5664
+ structured: {
5665
+ type: "boolean",
5666
+ default: false,
5667
+ description: "If true, returns structured JSON results."
5668
+ }
5669
+ },
5670
+ required: ["summary", "context", "rationale"]
5671
+ },
5672
+ outputSchema: {
5673
+ type: "object",
5674
+ properties: {
5675
+ success: { type: "boolean" },
5676
+ id: { type: "string" },
5677
+ code: { type: "string" },
5678
+ repo: { type: "string" },
5679
+ type: { type: "string", enum: ["decision"] },
5680
+ title: { type: "string" }
5681
+ }
5682
+ }
5683
+ },
5684
+ {
5685
+ name: "session-summarize",
5686
+ title: "Session Summarizer",
5687
+ description: "Persists a session summary as a task_archive memory entry. Accepts a session summary, optional key decisions made, optional next steps, and optional tags. Internally reuses the memory-store handler with type='task_archive' and importance=3. Owner and repo are auto-inferred from session when omitted.",
5688
+ annotations: {
5689
+ readOnlyHint: false,
5690
+ idempotentHint: false,
5691
+ openWorldHint: false
5692
+ },
5693
+ inputSchema: {
5694
+ type: "object",
5695
+ properties: {
5696
+ summary: {
5697
+ type: "string",
5698
+ description: "Session summary text. Min 10 characters."
5699
+ },
5700
+ key_decisions: {
5701
+ type: "array",
5702
+ items: { type: "string" },
5703
+ description: "Key decisions made during this session (optional)."
5704
+ },
5705
+ next_steps: {
5706
+ type: "array",
5707
+ items: { type: "string" },
5708
+ description: "Next steps or follow-up actions (optional)."
5709
+ },
5710
+ tags: {
5711
+ type: "array",
5712
+ items: { type: "string" },
5713
+ description: "Additional tags to apply (auto-includes 'session-summary')."
5714
+ },
5715
+ owner: {
5716
+ type: "string",
5717
+ description: "Organization/namespace. Auto-detected from session when omitted."
5718
+ },
5719
+ repo: {
5720
+ type: "string",
5721
+ description: "Repository/project name. Auto-detected from session when omitted."
5722
+ },
5723
+ structured: {
5724
+ type: "boolean",
5725
+ default: false,
5726
+ description: "If true, returns structured JSON results."
5727
+ }
5728
+ },
5729
+ required: ["summary"]
5730
+ },
5731
+ outputSchema: {
5732
+ type: "object",
5733
+ properties: {
5734
+ success: { type: "boolean" },
5735
+ id: { type: "string" },
5736
+ code: { type: "string" },
5737
+ repo: { type: "string" },
5738
+ type: { type: "string", enum: ["task_archive"] },
5739
+ title: { type: "string" }
5740
+ }
5741
+ }
5742
+ },
5743
+ // ── Upstream compatibility aliases ──────────────────────────────────────
5744
+ {
5745
+ name: "remember_fact",
5746
+ title: "Remember Fact (Upstream)",
5747
+ description: "Upstream-compatible alias for memory-store. Stores a single fact as a memory entry. Accepts the same arguments as memory-store. Owner and repo are auto-inferred from session when omitted.",
5748
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
5749
+ inputSchema: {
5750
+ type: "object",
5751
+ properties: {
5752
+ owner: { type: "string", description: "Auto-detected from session." },
5753
+ repo: { type: "string", description: "Auto-detected from session." },
5754
+ title: { type: "string", description: "Title for the fact/memory." },
5755
+ content: { type: "string", description: "The fact content to store." },
5756
+ type: { type: "string", default: "code_fact" },
5757
+ importance: { type: "number", default: 3 },
5758
+ tags: { type: "array", items: { type: "string" } }
5759
+ },
5760
+ required: ["content"]
5761
+ },
5762
+ outputSchema: {
5763
+ type: "object",
5764
+ properties: { success: { type: "boolean" }, id: { type: "string" }, code: { type: "string" } }
5765
+ }
5766
+ },
5767
+ {
5768
+ name: "remember_facts",
5769
+ title: "Remember Facts (Upstream Bulk)",
5770
+ description: "Upstream-compatible alias for bulk memory-store. Stores multiple facts at once.",
5771
+ annotations: { readOnlyHint: false, idempotentHint: false, openWorldHint: false },
5772
+ inputSchema: {
5773
+ type: "object",
5774
+ properties: {
5775
+ owner: { type: "string" },
5776
+ repo: { type: "string" },
5777
+ facts: { type: "array", items: { type: "object" }, description: "Array of fact objects." }
5778
+ },
5779
+ required: ["facts"]
5780
+ },
5781
+ outputSchema: { type: "object", properties: { success: { type: "boolean" }, count: { type: "number" } } }
5782
+ },
5783
+ {
5784
+ name: "recall",
5785
+ title: "Recall (Upstream)",
5786
+ description: "Upstream-compatible alias for memory-search. Searches stored memories by query.",
5787
+ annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: false },
5788
+ inputSchema: {
5789
+ type: "object",
5790
+ properties: {
5791
+ owner: { type: "string" },
5792
+ repo: { type: "string" },
5793
+ query: { type: "string", description: "Search query." },
5794
+ type: { type: "string", description: "Filter by memory type." },
5795
+ limit: { type: "number", default: 10 }
5796
+ },
5797
+ required: ["query"]
5798
+ },
5799
+ outputSchema: { type: "object", properties: { memories: { type: "array", items: { type: "object" } } } }
5800
+ },
5801
+ {
5802
+ name: "forget",
5803
+ title: "Forget (Upstream)",
5804
+ description: "Upstream-compatible alias for memory-delete. Deletes a stored memory by id or code.",
5805
+ annotations: { readOnlyHint: false, idempotentHint: false, destructiveHint: true, openWorldHint: true },
5806
+ inputSchema: {
5807
+ type: "object",
5808
+ properties: {
5809
+ owner: { type: "string" },
5810
+ repo: { type: "string" },
5811
+ id: { type: "string", description: "Memory ID to delete." },
5812
+ code: { type: "string", description: "Memory code to delete." }
5813
+ },
5814
+ required: []
5815
+ },
5816
+ outputSchema: { type: "object", properties: { success: { type: "boolean" } } }
5817
+ }
5818
+ ];
5819
+
5820
+ // src/mcp/tools/definitions/knowledge-graph.ts
5821
+ var KG_TOOL_DEFINITIONS = [
5822
+ {
5823
+ name: "create_entity",
5824
+ title: "KG Create Entity",
5825
+ description: "Creates a new knowledge graph entity. Entities represent nodes in the knowledge graph (e.g., concepts, people, systems). Name must be unique per repo. Owner and repo are auto-inferred from session when omitted.",
5826
+ annotations: {
5827
+ readOnlyHint: false,
5828
+ idempotentHint: false,
5829
+ destructiveHint: false,
5830
+ openWorldHint: false
5831
+ },
5832
+ inputSchema: {
5833
+ type: "object",
5834
+ properties: {
5835
+ name: {
5836
+ type: "string",
5837
+ description: "Unique entity name (acts as primary key per repo)."
5838
+ },
5839
+ type: {
5840
+ type: "string",
5841
+ default: "unknown",
5842
+ description: "Entity type for categorization (e.g., 'person', 'system', 'concept')."
5843
+ },
5844
+ description: {
5845
+ type: "string",
5846
+ description: "Optional description of the entity."
5847
+ },
5848
+ owner: {
5849
+ type: "string",
5850
+ description: "Organization/namespace. Auto-detected from session when omitted."
5851
+ },
5852
+ repo: {
5853
+ type: "string",
5854
+ description: "Repository/project name. Auto-detected from session when omitted."
5855
+ },
5856
+ structured: {
5857
+ type: "boolean",
5858
+ default: false,
5859
+ description: "If true, returns structured JSON results."
5860
+ }
5861
+ },
5862
+ required: ["name"]
5863
+ },
5864
+ outputSchema: {
5865
+ type: "object",
5866
+ properties: {
5867
+ success: { type: "boolean" },
5868
+ entity: {
5869
+ type: "object",
5870
+ properties: {
5871
+ name: { type: "string" },
5872
+ type: { type: "string" },
5873
+ description: { type: "string" },
5874
+ repo: { type: "string" },
5875
+ owner: { type: "string" }
5876
+ }
5877
+ }
5878
+ }
5879
+ }
5880
+ },
5881
+ {
5882
+ name: "delete_entity",
5883
+ title: "KG Delete Entity",
5884
+ description: "Deletes a knowledge graph entity by name. All related relations and observations are automatically removed via CASCADE. Owner and repo are auto-inferred from session when omitted.",
5885
+ annotations: {
5886
+ readOnlyHint: false,
5887
+ idempotentHint: false,
5888
+ destructiveHint: true,
5889
+ openWorldHint: false
5890
+ },
5891
+ inputSchema: {
5892
+ type: "object",
5893
+ properties: {
5894
+ name: {
5895
+ type: "string",
5896
+ description: "Name of the entity to delete."
5897
+ },
5898
+ owner: {
5899
+ type: "string",
5900
+ description: "Organization/namespace. Auto-detected from session when omitted."
5901
+ },
5902
+ repo: {
5903
+ type: "string",
5904
+ description: "Repository/project name. Auto-detected from session when omitted."
5905
+ },
5906
+ structured: {
5907
+ type: "boolean",
5908
+ default: false,
5909
+ description: "If true, returns structured JSON results."
5910
+ }
5911
+ },
5912
+ required: ["name"]
5913
+ },
5914
+ outputSchema: {
5915
+ type: "object",
5916
+ properties: {
5917
+ success: { type: "boolean" },
5918
+ deletedCount: { type: "number" }
5919
+ }
5920
+ }
5921
+ },
5922
+ {
5923
+ name: "create_relation",
5924
+ title: "KG Create Relation",
5925
+ description: "Creates a directed relation between two existing knowledge graph entities. Both from_entity and to_entity must already exist. The composite key (from_entity, to_entity, relation_type) must be unique. Owner and repo are auto-inferred from session when omitted.",
5926
+ annotations: {
5927
+ readOnlyHint: false,
5928
+ idempotentHint: false,
5929
+ destructiveHint: false,
5930
+ openWorldHint: false
5931
+ },
5932
+ inputSchema: {
5933
+ type: "object",
5934
+ properties: {
5935
+ from_entity: {
5936
+ type: "string",
5937
+ description: "Source entity name (must exist)."
5938
+ },
5939
+ to_entity: {
5940
+ type: "string",
5941
+ description: "Target entity name (must exist)."
5942
+ },
5943
+ relation_type: {
5944
+ type: "string",
5945
+ description: "Type of relation (e.g., 'depends_on', 'implements', 'part_of')."
5946
+ },
5947
+ owner: {
5948
+ type: "string",
5949
+ description: "Organization/namespace. Auto-detected from session when omitted."
5950
+ },
5951
+ repo: {
5952
+ type: "string",
5953
+ description: "Repository/project name. Auto-detected from session when omitted."
5954
+ },
5955
+ structured: {
5956
+ type: "boolean",
5957
+ default: false,
5958
+ description: "If true, returns structured JSON results."
5959
+ }
5960
+ },
5961
+ required: ["from_entity", "to_entity", "relation_type"]
5962
+ },
5963
+ outputSchema: {
5964
+ type: "object",
5965
+ properties: {
5966
+ success: { type: "boolean" },
5967
+ relation: {
5968
+ type: "object",
5969
+ properties: {
5970
+ from_entity: { type: "string" },
5971
+ to_entity: { type: "string" },
5972
+ relation_type: { type: "string" }
5973
+ }
5974
+ }
5975
+ }
5976
+ }
5977
+ },
5978
+ {
5979
+ name: "delete_relation",
5980
+ title: "KG Delete Relation",
5981
+ description: "Deletes a relation by its composite key (from_entity, to_entity, relation_type). Owner and repo are auto-inferred from session when omitted.",
5982
+ annotations: {
5983
+ readOnlyHint: false,
5984
+ idempotentHint: false,
5985
+ destructiveHint: true,
5986
+ openWorldHint: false
5987
+ },
5988
+ inputSchema: {
5989
+ type: "object",
5990
+ properties: {
5991
+ from_entity: {
5992
+ type: "string",
5993
+ description: "Source entity name."
5994
+ },
5995
+ to_entity: {
5996
+ type: "string",
5997
+ description: "Target entity name."
5998
+ },
5999
+ relation_type: {
6000
+ type: "string",
6001
+ description: "Relation type."
6002
+ },
6003
+ owner: {
6004
+ type: "string",
6005
+ description: "Organization/namespace. Auto-detected from session when omitted."
6006
+ },
6007
+ repo: {
6008
+ type: "string",
6009
+ description: "Repository/project name. Auto-detected from session when omitted."
6010
+ },
6011
+ structured: {
6012
+ type: "boolean",
6013
+ default: false,
6014
+ description: "If true, returns structured JSON results."
6015
+ }
6016
+ },
6017
+ required: ["from_entity", "to_entity", "relation_type"]
5487
6018
  },
5488
6019
  outputSchema: {
5489
6020
  type: "object",
5490
6021
  properties: {
5491
6022
  success: { type: "boolean" },
5492
- id: { type: "string" },
5493
- updatedFields: { type: "array", items: { type: "string" } }
5494
- },
5495
- required: ["success", "id", "updatedFields"]
6023
+ deletedCount: { type: "number" }
6024
+ }
5496
6025
  }
5497
6026
  },
5498
6027
  {
5499
- name: "standard-search",
5500
- title: "Standard Search",
5501
- description: "MANDATORY PRE-IMPLEMENTATION CHECK: Call before any code edit, test edit, refactor, migration, or implementation decision to find applicable coding standards. Returns a compact pointer table; use `standard-detail` for relevant results. If no relevant standards are returned, continue and state that no applicable standards were found.",
6028
+ name: "delete_observation",
6029
+ title: "KG Delete Observation",
6030
+ description: "Deletes an observation by its ID. Observation IDs are UUIDs returned when observations are created. Owner and repo are auto-inferred from session when omitted.",
5502
6031
  annotations: {
5503
- readOnlyHint: true,
5504
- idempotentHint: true,
6032
+ readOnlyHint: false,
6033
+ idempotentHint: false,
6034
+ destructiveHint: true,
5505
6035
  openWorldHint: false
5506
6036
  },
5507
6037
  inputSchema: {
5508
6038
  type: "object",
5509
6039
  properties: {
5510
- query: { type: "string", description: "Search query (optional, searches title/content)" },
5511
- stack: {
5512
- type: "array",
5513
- items: { type: "string" },
5514
- description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
6040
+ id: {
6041
+ type: "string",
6042
+ description: "UUID of the observation to delete."
5515
6043
  },
5516
- tags: {
5517
- type: "array",
5518
- items: { type: "string" },
5519
- description: "Tag filter"
6044
+ owner: {
6045
+ type: "string",
6046
+ description: "Organization/namespace. Auto-detected from session when omitted."
5520
6047
  },
5521
- language: { type: "string", description: "Programming language filter" },
5522
- context: { type: "string", description: "Context/category filter" },
5523
- version: { type: "string", description: "Version filter" },
5524
- repo: { type: "string", description: "Repository filter (optional)" },
5525
- is_global: { type: "boolean", description: "Filter by global/repo-specific" },
5526
- limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
5527
- offset: { type: "number", minimum: 0, default: 0 },
5528
- structured: { type: "boolean", default: false }
6048
+ repo: {
6049
+ type: "string",
6050
+ description: "Repository/project name. Auto-detected from session when omitted."
6051
+ },
6052
+ structured: {
6053
+ type: "boolean",
6054
+ default: false,
6055
+ description: "If true, returns structured JSON results."
6056
+ }
5529
6057
  },
5530
- required: []
6058
+ required: ["id"]
5531
6059
  },
5532
6060
  outputSchema: {
5533
6061
  type: "object",
5534
6062
  properties: {
5535
- schema: { type: "string", enum: ["standard-search"] },
5536
- query: { type: "string" },
5537
- count: { type: "number", description: "Number of rows returned" },
5538
- total: { type: "number", description: "Total number of matches before pagination" },
5539
- offset: { type: "number" },
5540
- limit: { type: "number" },
5541
- results: {
5542
- type: "object",
5543
- properties: {
5544
- columns: {
5545
- type: "array",
5546
- items: { type: "string" }
5547
- },
5548
- rows: {
5549
- type: "array",
5550
- items: { type: "array" },
5551
- description: "Each row includes standard id and pointer metadata. Fetch full content via standard-detail."
5552
- }
5553
- },
5554
- required: ["columns", "rows"]
5555
- }
5556
- },
5557
- required: ["schema", "query", "count", "total", "offset", "limit", "results"]
6063
+ success: { type: "boolean" },
6064
+ deletedCount: { type: "number" }
6065
+ }
5558
6066
  }
5559
6067
  }
5560
6068
  ];
5561
6069
 
6070
+ // src/mcp/tools/definitions/index.ts
6071
+ var TOOL_DEFINITIONS = [
6072
+ ...MEMORY_TOOL_DEFINITIONS,
6073
+ ...TASK_TOOL_DEFINITIONS,
6074
+ ...HANDOFF_TOOL_DEFINITIONS,
6075
+ ...STANDARD_TOOL_DEFINITIONS,
6076
+ ...AGENT_TOOL_DEFINITIONS,
6077
+ ...KG_TOOL_DEFINITIONS
6078
+ ];
6079
+
6080
+ // src/mcp/utils/completion.ts
6081
+ var MAX_COMPLETION_VALUES = 100;
6082
+ function rankCompletionValues(candidates, input) {
6083
+ const unique = [...new Set(candidates.filter(Boolean))];
6084
+ const needle = input.trim().toLowerCase();
6085
+ if (!needle) {
6086
+ return unique.slice(0, MAX_COMPLETION_VALUES);
6087
+ }
6088
+ 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);
6089
+ }
6090
+ function scoreCompletionValue(value, needle) {
6091
+ const haystack = value.toLowerCase();
6092
+ if (haystack === needle) return 100;
6093
+ if (haystack.startsWith(needle)) return 75;
6094
+ if (haystack.includes(needle)) return 50;
6095
+ const compactNeedle = needle.replace(/[\s_-]+/g, "");
6096
+ const compactHaystack = haystack.replace(/[\s_-]+/g, "");
6097
+ if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
6098
+ return 0;
6099
+ }
6100
+
5562
6101
  // src/mcp/utils/pagination.ts
5563
6102
  function encodeCursor(offset) {
5564
6103
  return Buffer.from(String(offset), "utf8").toString("base64");
@@ -5591,27 +6130,6 @@ function invalidPaginationParams(message) {
5591
6130
  return error;
5592
6131
  }
5593
6132
 
5594
- // src/mcp/utils/completion.ts
5595
- var MAX_COMPLETION_VALUES = 100;
5596
- function rankCompletionValues(candidates, input) {
5597
- const unique = [...new Set(candidates.filter(Boolean))];
5598
- const needle = input.trim().toLowerCase();
5599
- if (!needle) {
5600
- return unique.slice(0, MAX_COMPLETION_VALUES);
5601
- }
5602
- 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);
5603
- }
5604
- function scoreCompletionValue(value, needle) {
5605
- const haystack = value.toLowerCase();
5606
- if (haystack === needle) return 100;
5607
- if (haystack.startsWith(needle)) return 75;
5608
- if (haystack.includes(needle)) return 50;
5609
- const compactNeedle = needle.replace(/[\s_-]+/g, "");
5610
- const compactHaystack = haystack.replace(/[\s_-]+/g, "");
5611
- if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
5612
- return 0;
5613
- }
5614
-
5615
6133
  // src/mcp/resources/index.ts
5616
6134
  var DEFAULT_PAGE_SIZE = 25;
5617
6135
  var MAX_PAGE_SIZE = 100;
@@ -5645,87 +6163,6 @@ function listResources(session, params) {
5645
6163
  ];
5646
6164
  return paginateEntries("resources", resources, params);
5647
6165
  }
5648
- function listResourceTemplates(params) {
5649
- const templates = [
5650
- // ── Memory ──────────────────────────────────────────────────────────────
5651
- {
5652
- uriTemplate: "repository://{name}/memories",
5653
- name: "Repository Memories",
5654
- title: "Repository Memories",
5655
- description: "All active memory entries for a specific repository",
5656
- mimeType: "application/json",
5657
- annotations: { audience: ["assistant"], priority: 0.85 }
5658
- },
5659
- {
5660
- uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
5661
- name: "Filtered Repository Memories",
5662
- title: "Filtered Repository Memories",
5663
- description: "Filter or search memories within a repository by keyword, type, or tag",
5664
- mimeType: "application/json",
5665
- annotations: { audience: ["assistant"], priority: 0.8 }
5666
- },
5667
- {
5668
- uriTemplate: "memory://{id}",
5669
- name: "Memory Detail",
5670
- title: "Memory Detail",
5671
- description: "Full content and statistics for a specific memory UUID",
5672
- mimeType: "application/json",
5673
- annotations: { audience: ["assistant"], priority: 0.75 }
5674
- },
5675
- // ── Tasks ────────────────────────────────────────────────────────────────
5676
- {
5677
- uriTemplate: "repository://{name}/tasks",
5678
- name: "Repository Tasks",
5679
- title: "Repository Tasks",
5680
- description: "All active tasks for a specific repository",
5681
- mimeType: "application/json",
5682
- annotations: { audience: ["assistant"], priority: 0.9 }
5683
- },
5684
- {
5685
- uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
5686
- name: "Filtered Repository Tasks",
5687
- title: "Filtered Repository Tasks",
5688
- description: "Filter tasks within a repository by status or priority level",
5689
- mimeType: "application/json",
5690
- annotations: { audience: ["assistant"], priority: 0.85 }
5691
- },
5692
- {
5693
- uriTemplate: "task://{id}",
5694
- name: "Task Detail",
5695
- title: "Task Detail",
5696
- description: "Full content and comments for a specific task UUID",
5697
- mimeType: "application/json",
5698
- annotations: { audience: ["assistant"], priority: 0.8 }
5699
- },
5700
- // ── Repository extras ────────────────────────────────────────────────────
5701
- {
5702
- uriTemplate: "repository://{name}/summary",
5703
- name: "Repository Summary",
5704
- title: "Repository Summary",
5705
- description: "High-level architectural summary for a repository",
5706
- mimeType: "text/plain",
5707
- annotations: { audience: ["assistant"], priority: 0.95 }
5708
- },
5709
- {
5710
- uriTemplate: "repository://{name}/actions",
5711
- name: "Repository Actions",
5712
- title: "Repository Actions",
5713
- description: "Audit log of agent tool actions scoped to a repository",
5714
- mimeType: "application/json",
5715
- annotations: { audience: ["assistant"], priority: 0.6 }
5716
- },
5717
- // ── Action detail ────────────────────────────────────────────────────────
5718
- {
5719
- uriTemplate: "action://{id}",
5720
- name: "Action Detail",
5721
- title: "Action Detail",
5722
- description: "Full details of a specific audit log entry by integer ID",
5723
- mimeType: "application/json",
5724
- annotations: { audience: ["assistant"], priority: 0.55 }
5725
- }
5726
- ];
5727
- return paginateEntries("resourceTemplates", templates, params);
5728
- }
5729
6166
  function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
5730
6167
  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") {
5731
6168
  if (argumentName === "name") {
@@ -5739,234 +6176,6 @@ function completeResourceArgument(resourceUri, argumentName, argumentValue, _con
5739
6176
  }
5740
6177
  throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
5741
6178
  }
5742
- function readResource(uri, db, session) {
5743
- logger.info("[Tool] resource.read", { uri });
5744
- if (uri === "repository://index") {
5745
- const repos = db.system.listRepoNavigation();
5746
- const payload = JSON.stringify(repos, null, 2);
5747
- return {
5748
- contents: [
5749
- {
5750
- uri,
5751
- mimeType: "application/json",
5752
- text: payload,
5753
- size: Buffer.byteLength(payload, "utf8"),
5754
- annotations: {
5755
- audience: ["assistant"],
5756
- priority: 1,
5757
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
5758
- }
5759
- }
5760
- ]
5761
- };
5762
- }
5763
- if (uri === "session://roots") {
5764
- const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
5765
- return {
5766
- contents: [
5767
- {
5768
- uri,
5769
- mimeType: "application/json",
5770
- text: payload,
5771
- size: Buffer.byteLength(payload, "utf8"),
5772
- annotations: {
5773
- audience: ["assistant"],
5774
- priority: 0.95,
5775
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
5776
- }
5777
- }
5778
- ]
5779
- };
5780
- }
5781
- const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
5782
- if (memoryIdMatch) {
5783
- const id = memoryIdMatch[1];
5784
- const entry = db.memories.getByIdWithStats(id);
5785
- if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
5786
- const payload = JSON.stringify(entry, null, 2);
5787
- return {
5788
- contents: [
5789
- {
5790
- uri,
5791
- mimeType: "application/json",
5792
- text: payload,
5793
- size: Buffer.byteLength(payload, "utf8"),
5794
- annotations: {
5795
- audience: ["assistant"],
5796
- priority: 0.75,
5797
- lastModified: entry.updated_at || entry.created_at
5798
- }
5799
- }
5800
- ]
5801
- };
5802
- }
5803
- const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
5804
- if (taskIdMatch) {
5805
- const id = taskIdMatch[1];
5806
- const task = db.tasks.getTaskById(id);
5807
- if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
5808
- const payload = JSON.stringify(task, null, 2);
5809
- return {
5810
- contents: [
5811
- {
5812
- uri,
5813
- mimeType: "application/json",
5814
- text: payload,
5815
- size: Buffer.byteLength(payload, "utf8"),
5816
- annotations: {
5817
- audience: ["assistant"],
5818
- priority: 0.8,
5819
- lastModified: task.updated_at || task.created_at
5820
- }
5821
- }
5822
- ]
5823
- };
5824
- }
5825
- const repoBase = parseRepoUri(uri);
5826
- if (repoBase) {
5827
- const { name, path: repoPath, query } = repoBase;
5828
- if (repoPath === "summary") {
5829
- const summary = db.summaries.getSummary("", name);
5830
- const text = summary?.summary || `No summary available for repository: ${name}`;
5831
- return {
5832
- contents: [
5833
- {
5834
- uri,
5835
- mimeType: "text/plain",
5836
- text,
5837
- size: Buffer.byteLength(text, "utf8"),
5838
- annotations: {
5839
- audience: ["assistant"],
5840
- priority: 0.95,
5841
- lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
5842
- }
5843
- }
5844
- ]
5845
- };
5846
- }
5847
- if (repoPath === "memories") {
5848
- const search = query.get("search") || "";
5849
- const type = query.get("type");
5850
- const tag = query.get("tag");
5851
- const result = db.memories.listMemoriesForDashboard({
5852
- repo: name,
5853
- type: type || void 0,
5854
- tag: tag || void 0,
5855
- search: search || void 0,
5856
- limit: 50
5857
- });
5858
- const entries = result.items;
5859
- const payload = JSON.stringify(entries, null, 2);
5860
- return {
5861
- contents: [
5862
- {
5863
- uri,
5864
- mimeType: "application/json",
5865
- text: payload,
5866
- size: Buffer.byteLength(payload, "utf8"),
5867
- annotations: {
5868
- audience: ["assistant"],
5869
- priority: 0.85,
5870
- lastModified: deriveLastModifiedFromCollection(
5871
- entries.map((e) => e.updated_at || e.created_at)
5872
- )
5873
- }
5874
- }
5875
- ]
5876
- };
5877
- }
5878
- if (repoPath === "tasks") {
5879
- const status = query.get("status");
5880
- const priority = query.get("priority");
5881
- const owner = parseRepoInput(name).owner;
5882
- let tasks;
5883
- if (status && status !== "all") {
5884
- const statuses = status.split(",").map((s) => s.trim());
5885
- tasks = db.tasks.getTasksByMultipleStatuses(owner, name, statuses);
5886
- } else {
5887
- tasks = db.tasks.getTasksByMultipleStatuses(owner, name, ["backlog", "pending", "in_progress", "blocked"]);
5888
- }
5889
- if (priority) {
5890
- const p = Number(priority);
5891
- if (!isNaN(p)) {
5892
- tasks = tasks.filter((t) => t.priority === p);
5893
- }
5894
- }
5895
- const payload = JSON.stringify(tasks, null, 2);
5896
- return {
5897
- contents: [
5898
- {
5899
- uri,
5900
- mimeType: "application/json",
5901
- text: payload,
5902
- size: Buffer.byteLength(payload, "utf8"),
5903
- annotations: {
5904
- audience: ["assistant"],
5905
- priority: 0.9,
5906
- lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
5907
- }
5908
- }
5909
- ]
5910
- };
5911
- }
5912
- if (repoPath === "actions") {
5913
- const actions = db.actions.getRecentActions("", name, 100);
5914
- const payload = JSON.stringify(actions, null, 2);
5915
- return {
5916
- contents: [
5917
- {
5918
- uri,
5919
- mimeType: "application/json",
5920
- text: payload,
5921
- size: Buffer.byteLength(payload, "utf8"),
5922
- annotations: {
5923
- audience: ["assistant"],
5924
- priority: 0.6,
5925
- lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
5926
- }
5927
- }
5928
- ]
5929
- };
5930
- }
5931
- }
5932
- const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
5933
- if (actionIdMatch) {
5934
- const id = Number(actionIdMatch[1]);
5935
- const action = db.actions.getActionById(id);
5936
- if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
5937
- const payload = JSON.stringify(action, null, 2);
5938
- return {
5939
- contents: [
5940
- {
5941
- uri,
5942
- mimeType: "application/json",
5943
- text: payload,
5944
- size: Buffer.byteLength(payload, "utf8"),
5945
- annotations: {
5946
- audience: ["assistant"],
5947
- priority: 0.55,
5948
- lastModified: action.created_at
5949
- }
5950
- }
5951
- ]
5952
- };
5953
- }
5954
- throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
5955
- }
5956
- function parseRepoUri(uri) {
5957
- const prefix = "repository://";
5958
- if (!uri.startsWith(prefix)) return null;
5959
- const rest = uri.slice(prefix.length);
5960
- const queryStart = rest.indexOf("?");
5961
- const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
5962
- const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
5963
- const slashIdx = withoutQuery.indexOf("/");
5964
- if (slashIdx === -1) return null;
5965
- const name = withoutQuery.slice(0, slashIdx);
5966
- const path6 = withoutQuery.slice(slashIdx + 1);
5967
- if (!name || !path6) return null;
5968
- return { name, path: path6, query: new URLSearchParams(queryString) };
5969
- }
5970
6179
  function paginateEntries(key, entries, params) {
5971
6180
  const limit = normalizeLimit(params?.limit);
5972
6181
  const offset = decodeCursor(params?.cursor);
@@ -5983,16 +6192,6 @@ function normalizeLimit(limit) {
5983
6192
  }
5984
6193
  return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
5985
6194
  }
5986
- function deriveLastModifiedFromCollection(values) {
5987
- const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
5988
- return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
5989
- }
5990
- function resourceNotFound(message, uri) {
5991
- const error = new Error(message);
5992
- error.code = -32002;
5993
- error.data = { uri };
5994
- return error;
5995
- }
5996
6195
  function invalidCompletionParams(message) {
5997
6196
  const error = new Error(message);
5998
6197
  error.code = -32602;
@@ -6026,51 +6225,6 @@ for (const name of promptFiles) {
6026
6225
  logger.warn(`Failed to load prompt ${name}: ${e}`);
6027
6226
  }
6028
6227
  }
6029
- async function listPrompts(db, session, params) {
6030
- const allPrompts = Object.values(PROMPTS).map((p) => ({
6031
- name: p.name,
6032
- description: p.description,
6033
- arguments: p.arguments,
6034
- metadata: p.agent ? { agent: p.agent } : void 0
6035
- }));
6036
- const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 50;
6037
- const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
6038
- const offset = decodeCursor(params?.cursor);
6039
- const sliced = allPrompts.slice(offset, offset + limit);
6040
- const nextOffset = offset + sliced.length;
6041
- return {
6042
- prompts: sliced,
6043
- nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
6044
- };
6045
- }
6046
- async function getPrompt(name, args = {}, db, session) {
6047
- const prompt = PROMPTS[name];
6048
- if (!prompt) {
6049
- throw new Error(`Prompt not found: ${name}`);
6050
- }
6051
- const inferredRepo = inferRepoFromSession(session);
6052
- const inferredOwner = inferOwnerFromSession(session);
6053
- const messages = prompt.messages.map((m) => {
6054
- let text = m.content.text;
6055
- for (const [key, value] of Object.entries(args)) {
6056
- text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
6057
- }
6058
- text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
6059
- text = text.replace(/{{current_owner}}/g, inferredOwner || "unknown-owner");
6060
- return {
6061
- ...m,
6062
- content: {
6063
- ...m.content,
6064
- text
6065
- }
6066
- };
6067
- });
6068
- return {
6069
- description: prompt.description,
6070
- messages,
6071
- metadata: prompt.agent ? { agent: prompt.agent } : void 0
6072
- };
6073
- }
6074
6228
  async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
6075
6229
  void name;
6076
6230
  void contextArguments;
@@ -6216,7 +6370,7 @@ var SingleMemorySchema = z2.object({
6216
6370
  ttlDays: z2.number().min(1).optional(),
6217
6371
  supersedes: z2.string().optional(),
6218
6372
  tags: z2.array(z2.string()).optional(),
6219
- metadata: z2.record(z2.string(), z2.any()).optional(),
6373
+ metadata: z2.record(z2.string(), z2.unknown()).optional(),
6220
6374
  is_global: z2.boolean().default(false)
6221
6375
  });
6222
6376
  var SingleStandardSchema = z2.object({
@@ -6229,7 +6383,7 @@ var SingleStandardSchema = z2.object({
6229
6383
  stack: z2.array(z2.string()).optional(),
6230
6384
  is_global: z2.boolean().optional(),
6231
6385
  tags: z2.array(z2.string().min(1)).min(1),
6232
- metadata: z2.record(z2.string(), z2.any()).refine((value) => Object.keys(value).length > 0, {
6386
+ metadata: z2.record(z2.string(), z2.unknown()).refine((value) => Object.keys(value).length > 0, {
6233
6387
  message: "metadata must contain at least one key"
6234
6388
  }),
6235
6389
  agent: z2.string().optional(),
@@ -6252,7 +6406,7 @@ var MemoryStoreSchema = z3.object({
6252
6406
  ttlDays: z3.number().min(1).optional(),
6253
6407
  supersedes: z3.string().optional(),
6254
6408
  tags: z3.array(z3.string()).optional(),
6255
- metadata: z3.record(z3.string(), z3.any()).optional(),
6409
+ metadata: z3.record(z3.string(), z3.unknown()).optional(),
6256
6410
  is_global: z3.boolean().default(false),
6257
6411
  structured: z3.boolean().default(false),
6258
6412
  memories: z3.array(SingleMemorySchema).min(1).optional()
@@ -6279,7 +6433,7 @@ var MemoryUpdateSchema = z3.object({
6279
6433
  status: z3.enum(["active", "archived"]).optional(),
6280
6434
  supersedes: z3.string().optional(),
6281
6435
  tags: z3.array(z3.string()).optional(),
6282
- metadata: z3.record(z3.string(), z3.any()).optional(),
6436
+ metadata: z3.record(z3.string(), z3.unknown()).optional(),
6283
6437
  is_global: z3.boolean().optional(),
6284
6438
  completed_at: z3.string().optional(),
6285
6439
  structured: z3.boolean().default(false)
@@ -6367,7 +6521,7 @@ var MemorySynthesizeSchema = z3.object({
6367
6521
 
6368
6522
  // src/mcp/tools/schemas/task.ts
6369
6523
  import { z as z4 } from "zod";
6370
- var TaskMetadataSchema = z4.record(z4.string(), z4.any()).optional().superRefine((metadata, ctx) => {
6524
+ var TaskMetadataSchema = z4.record(z4.string(), z4.unknown()).optional().superRefine((metadata, ctx) => {
6371
6525
  if (!metadata) return;
6372
6526
  if (metadata.required_skills !== void 0) {
6373
6527
  if (!Array.isArray(metadata.required_skills)) {
@@ -6453,7 +6607,7 @@ var TaskCreateSchema = z4.object({
6453
6607
  doc_path: z4.string().optional(),
6454
6608
  tags: z4.array(z4.string()).optional(),
6455
6609
  suggested_skills: z4.array(z4.string()).optional(),
6456
- metadata: z4.record(z4.string(), z4.any()).optional(),
6610
+ metadata: z4.record(z4.string(), z4.unknown()).optional(),
6457
6611
  parent_id: z4.string().optional(),
6458
6612
  depends_on: z4.string().optional(),
6459
6613
  est_tokens: z4.number().int().min(0).optional(),
@@ -6490,7 +6644,7 @@ var TaskUpdateSchema = z4.object({
6490
6644
  comment: z4.string().min(1).optional(),
6491
6645
  doc_path: z4.string().optional(),
6492
6646
  tags: z4.array(z4.string()).optional(),
6493
- metadata: z4.record(z4.string(), z4.any()).optional(),
6647
+ metadata: z4.record(z4.string(), z4.unknown()).optional(),
6494
6648
  suggested_skills: z4.array(z4.string()).optional(),
6495
6649
  parent_id: z4.string().optional(),
6496
6650
  depends_on: z4.string().optional(),
@@ -6559,7 +6713,7 @@ var HandoffCreateSchema = z5.object({
6559
6713
  task_id: z5.string().uuid().optional(),
6560
6714
  task_code: z5.string().optional(),
6561
6715
  summary: z5.string().min(1),
6562
- context: z5.record(z5.string(), z5.any()).optional(),
6716
+ context: z5.record(z5.string(), z5.unknown()).optional(),
6563
6717
  expires_at: z5.string().optional(),
6564
6718
  structured: z5.boolean().default(false)
6565
6719
  }).refine((data) => !(data.task_id && data.task_code), {
@@ -6592,7 +6746,7 @@ var TaskClaimSchema = z5.object({
6592
6746
  task_code: z5.string().optional(),
6593
6747
  agent: z5.string().min(1),
6594
6748
  role: z5.string().optional(),
6595
- metadata: z5.record(z5.string(), z5.any()).optional(),
6749
+ metadata: z5.record(z5.string(), z5.unknown()).optional(),
6596
6750
  structured: z5.boolean().default(false)
6597
6751
  }).refine((data) => data.task_id !== void 0 || data.task_code !== void 0, {
6598
6752
  message: "Either task_id or task_code must be provided"
@@ -6635,7 +6789,7 @@ var StandardStoreSchema = z6.object({
6635
6789
  repo: z6.string().transform(normalizeRepo).optional(),
6636
6790
  is_global: z6.boolean().optional(),
6637
6791
  tags: z6.array(z6.string().min(1)).min(1).optional(),
6638
- metadata: z6.record(z6.string(), z6.any()).refine((value) => Object.keys(value).length > 0, { message: "metadata must contain at least one key" }).optional(),
6792
+ metadata: z6.record(z6.string(), z6.unknown()).refine((value) => Object.keys(value).length > 0, { message: "metadata must contain at least one key" }).optional(),
6639
6793
  agent: z6.string().optional(),
6640
6794
  model: z6.string().optional(),
6641
6795
  structured: z6.boolean().default(false),
@@ -6663,7 +6817,7 @@ var StandardUpdateSchema = z6.object({
6663
6817
  repo: z6.string().transform(normalizeRepo),
6664
6818
  is_global: z6.boolean().optional(),
6665
6819
  tags: z6.array(z6.string().min(1)).min(1).optional(),
6666
- metadata: z6.record(z6.string(), z6.any()).refine((value) => Object.keys(value).length > 0, { message: "metadata must contain at least one key" }).optional(),
6820
+ metadata: z6.record(z6.string(), z6.unknown()).refine((value) => Object.keys(value).length > 0, { message: "metadata must contain at least one key" }).optional(),
6667
6821
  agent: z6.string().optional(),
6668
6822
  model: z6.string().optional(),
6669
6823
  structured: z6.boolean().default(false)
@@ -6713,6 +6867,75 @@ var StandardDetailSchema = z6.object({
6713
6867
  message: "Either id or code must be provided"
6714
6868
  });
6715
6869
 
6870
+ // src/mcp/tools/schemas/agent.ts
6871
+ import { z as z7 } from "zod";
6872
+ var AgentContextSchema = z7.object({
6873
+ owner: z7.string().min(1),
6874
+ repo: z7.string().min(1),
6875
+ objective: z7.string().optional(),
6876
+ type_filter: z7.enum(["code_fact", "decision", "mistake", "pattern", "task_archive"]).optional(),
6877
+ limit: z7.number().min(1).max(100).default(5),
6878
+ structured: z7.boolean().default(false)
6879
+ });
6880
+ var DecisionLogSchema = z7.object({
6881
+ summary: z7.string().min(3).max(255, { message: "Summary must be at most 255 characters" }),
6882
+ context: z7.string().min(10, { message: "Context must be at least 10 characters" }),
6883
+ rationale: z7.string().min(10, { message: "Rationale must be at least 10 characters" }),
6884
+ alternatives: z7.array(z7.string()).optional(),
6885
+ tags: z7.array(z7.string()).optional(),
6886
+ owner: z7.string().optional(),
6887
+ repo: z7.string().optional(),
6888
+ structured: z7.boolean().default(false)
6889
+ });
6890
+ var SessionSummarizeSchema = z7.object({
6891
+ summary: z7.string().min(10, { message: "Summary must be at least 10 characters" }),
6892
+ key_decisions: z7.array(z7.string()).optional(),
6893
+ next_steps: z7.array(z7.string()).optional(),
6894
+ tags: z7.array(z7.string()).optional(),
6895
+ owner: z7.string().optional(),
6896
+ repo: z7.string().optional(),
6897
+ structured: z7.boolean().default(false)
6898
+ });
6899
+
6900
+ // src/mcp/tools/schemas/knowledge-graph.ts
6901
+ import { z as z8 } from "zod";
6902
+ var CreateEntitySchema = z8.object({
6903
+ name: z8.string().min(1).max(255),
6904
+ type: z8.string().default("unknown"),
6905
+ description: z8.string().optional(),
6906
+ owner: z8.string().optional(),
6907
+ repo: z8.string().optional(),
6908
+ structured: z8.boolean().default(false)
6909
+ });
6910
+ var DeleteEntitySchema = z8.object({
6911
+ name: z8.string().min(1),
6912
+ owner: z8.string().optional(),
6913
+ repo: z8.string().optional(),
6914
+ structured: z8.boolean().default(false)
6915
+ });
6916
+ var CreateRelationSchema = z8.object({
6917
+ from_entity: z8.string().min(1),
6918
+ to_entity: z8.string().min(1),
6919
+ relation_type: z8.string().min(1),
6920
+ owner: z8.string().optional(),
6921
+ repo: z8.string().optional(),
6922
+ structured: z8.boolean().default(false)
6923
+ });
6924
+ var DeleteRelationSchema = z8.object({
6925
+ from_entity: z8.string().min(1),
6926
+ to_entity: z8.string().min(1),
6927
+ relation_type: z8.string().min(1),
6928
+ owner: z8.string().optional(),
6929
+ repo: z8.string().optional(),
6930
+ structured: z8.boolean().default(false)
6931
+ });
6932
+ var DeleteObservationSchema = z8.object({
6933
+ id: z8.string().min(1),
6934
+ owner: z8.string().optional(),
6935
+ repo: z8.string().optional(),
6936
+ structured: z8.boolean().default(false)
6937
+ });
6938
+
6716
6939
  // src/mcp/tools/handoff.manage.ts
6717
6940
  function buildHandoffListSummary(repo, count, status, fromAgent, toAgent) {
6718
6941
  const parts = [`Found ${count} handoff${count === 1 ? "" : "s"} in repo "${repo}".`];
@@ -7013,37 +7236,27 @@ function buildStandardVectorText(standard) {
7013
7236
  }
7014
7237
 
7015
7238
  export {
7016
- MCP_PROTOCOL_VERSION,
7017
- CAPABILITIES,
7018
7239
  logger,
7019
- setLogLevel,
7020
- getLogLevel,
7021
7240
  addLogSink,
7022
- LOG_LEVEL_VALUES,
7023
7241
  createFileSink,
7024
7242
  parseRepoInput,
7025
7243
  normalizeRepo,
7026
7244
  SQLiteStore,
7027
7245
  RealVectorStore,
7028
7246
  TOOL_DEFINITIONS,
7029
- encodeCursor,
7030
- decodeCursor,
7247
+ rankCompletionValues,
7031
7248
  listResources,
7032
- listResourceTemplates,
7033
7249
  completeResourceArgument,
7034
- readResource,
7035
7250
  createSessionContext,
7036
- updateSessionFromInitialize,
7037
- updateSessionRoots,
7038
- extractRootsFromResult,
7039
7251
  getFilesystemRoots,
7040
7252
  isPathWithinRoots,
7041
7253
  findContainingRoot,
7042
7254
  inferRepoFromSession,
7043
7255
  inferOwnerFromSession,
7256
+ listPromptFiles,
7257
+ loadPromptFromMarkdown,
7258
+ loadServerInstructions,
7044
7259
  PROMPTS,
7045
- listPrompts,
7046
- getPrompt,
7047
7260
  completePromptArgument,
7048
7261
  createMcpResponse,
7049
7262
  getPrimaryTextContent,
@@ -7069,6 +7282,14 @@ export {
7069
7282
  StandardSearchSchema,
7070
7283
  StandardDeleteSchema,
7071
7284
  StandardDetailSchema,
7285
+ AgentContextSchema,
7286
+ DecisionLogSchema,
7287
+ SessionSummarizeSchema,
7288
+ CreateEntitySchema,
7289
+ DeleteEntitySchema,
7290
+ CreateRelationSchema,
7291
+ DeleteRelationSchema,
7292
+ DeleteObservationSchema,
7072
7293
  handleHandoffCreate,
7073
7294
  handleHandoffList,
7074
7295
  handleHandoffUpdate,