@vheins/local-memory-mcp 0.9.4 → 0.9.5

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,3 +1,49 @@
1
+ // src/mcp/capabilities.ts
2
+ import { fileURLToPath } from "url";
3
+ import path from "path";
4
+ var __dirname = path.dirname(fileURLToPath(import.meta.url));
5
+ var pkgVersion = "0.1.0";
6
+ if ("0.9.5") {
7
+ pkgVersion = "0.9.5";
8
+ } else {
9
+ let searchDir = __dirname;
10
+ for (let i = 0; i < 5; i++) {
11
+ const candidate = path.join(searchDir, "package.json");
12
+ try {
13
+ if (fs.existsSync(candidate)) {
14
+ const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
15
+ if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
16
+ pkgVersion = pkg.version;
17
+ break;
18
+ }
19
+ }
20
+ } catch {
21
+ }
22
+ searchDir = path.dirname(searchDir);
23
+ }
24
+ }
25
+ var MCP_PROTOCOL_VERSION = "2025-03-26";
26
+ var CAPABILITIES = {
27
+ serverInfo: {
28
+ name: "mcp-memory-local",
29
+ version: pkgVersion
30
+ },
31
+ capabilities: {
32
+ completions: {},
33
+ logging: {},
34
+ resources: {
35
+ subscribe: true,
36
+ listChanged: true
37
+ },
38
+ tools: {
39
+ listChanged: false
40
+ },
41
+ prompts: {
42
+ listChanged: true
43
+ }
44
+ }
45
+ };
46
+
1
47
  // src/mcp/utils/logger.ts
2
48
  import fs from "fs";
3
49
  var LEVELS = {
@@ -139,108 +185,6 @@ function createFileSink(logDir, maxFiles = 5) {
139
185
  };
140
186
  }
141
187
 
142
- // src/mcp/session.ts
143
- import path from "path";
144
- import { fileURLToPath } from "url";
145
- function createSessionContext() {
146
- return {
147
- roots: [],
148
- supportsRoots: false,
149
- supportsSampling: false,
150
- supportsSamplingTools: false,
151
- supportsElicitation: false,
152
- supportsElicitationForm: false,
153
- supportsElicitationUrl: false
154
- };
155
- }
156
- function updateSessionFromInitialize(session, params) {
157
- const capabilities = params?.capabilities || {};
158
- session.clientInfo = params?.clientInfo;
159
- session.clientCapabilities = capabilities;
160
- session.supportsRoots = Boolean(capabilities.roots);
161
- session.supportsSampling = Boolean(capabilities.sampling);
162
- const sampling = capabilities.sampling;
163
- session.supportsSamplingTools = Boolean(sampling?.tools);
164
- session.supportsElicitation = Boolean(capabilities.elicitation);
165
- session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
166
- session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
167
- }
168
- function supportsElicitationMode(capability, mode) {
169
- if (!capability || typeof capability !== "object") {
170
- return false;
171
- }
172
- const cap = capability;
173
- if (mode === "form") {
174
- return Object.keys(cap).length === 0 || typeof cap.form === "object";
175
- }
176
- return typeof cap.url === "object";
177
- }
178
- function updateSessionRoots(session, roots) {
179
- const normalized = normalizeRoots(roots);
180
- const previous = JSON.stringify(session.roots);
181
- const next = JSON.stringify(normalized);
182
- session.roots = normalized;
183
- return previous !== next;
184
- }
185
- function normalizeRoots(roots) {
186
- if (!Array.isArray(roots)) return [];
187
- const seen = /* @__PURE__ */ new Set();
188
- const normalized = [];
189
- for (const root of roots) {
190
- if (!root || typeof root !== "object") continue;
191
- const r = root;
192
- const uri = typeof r.uri === "string" ? r.uri : void 0;
193
- const name = typeof r.name === "string" ? r.name : void 0;
194
- if (!uri || seen.has(uri)) continue;
195
- seen.add(uri);
196
- normalized.push({ uri, name });
197
- }
198
- return normalized;
199
- }
200
- function extractRootsFromResult(result) {
201
- return normalizeRoots(result?.roots);
202
- }
203
- function getFilesystemRoots(session) {
204
- if (!session) return [];
205
- const resolved = [];
206
- for (const root of session.roots) {
207
- if (!root.uri.startsWith("file://")) continue;
208
- try {
209
- resolved.push(path.resolve(fileURLToPath(root.uri)));
210
- } catch {
211
- }
212
- }
213
- return resolved;
214
- }
215
- function isPathWithinRoots(targetPath, session) {
216
- const roots = getFilesystemRoots(session);
217
- if (roots.length === 0) return true;
218
- const normalizedTarget = path.resolve(targetPath);
219
- return roots.some((rootPath) => {
220
- const relative = path.relative(rootPath, normalizedTarget);
221
- return relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative);
222
- });
223
- }
224
- function findContainingRoot(targetPath, session) {
225
- const roots = getFilesystemRoots(session);
226
- if (roots.length === 0) return null;
227
- const normalizedTarget = path.resolve(targetPath);
228
- for (const rootPath of roots) {
229
- const relative = path.relative(rootPath, normalizedTarget);
230
- if (relative === "" || !relative.startsWith("..") && !path.isAbsolute(relative)) {
231
- return rootPath;
232
- }
233
- }
234
- return null;
235
- }
236
- function inferRepoFromSession(session) {
237
- const roots = getFilesystemRoots(session);
238
- if (roots.length === 1) {
239
- return path.basename(roots[0]);
240
- }
241
- return void 0;
242
- }
243
-
244
188
  // src/mcp/storage/sqlite.ts
245
189
  import Database from "better-sqlite3";
246
190
  import path3 from "path";
@@ -690,6 +634,15 @@ var MigrationManager = class {
690
634
  this.run("CREATE INDEX IF NOT EXISTS idx_memories_repo_code ON memories(repo, code)");
691
635
  }
692
636
  }
637
+ addStandardCodeColumn() {
638
+ const tableInfo = this.all("PRAGMA table_info(coding_standards)");
639
+ const hasCode = tableInfo.some((col) => col.name === "code");
640
+ if (!hasCode) {
641
+ this.run("ALTER TABLE coding_standards ADD COLUMN code TEXT");
642
+ this.run("CREATE INDEX IF NOT EXISTS idx_coding_standards_code ON coding_standards(code)");
643
+ this.run("CREATE INDEX IF NOT EXISTS idx_coding_standards_repo_code ON coding_standards(repo, code)");
644
+ }
645
+ }
693
646
  };
694
647
 
695
648
  // src/mcp/utils/stopwords.ts
@@ -2171,11 +2124,12 @@ var StandardEntity = class extends BaseEntity {
2171
2124
  insert(entry) {
2172
2125
  this.run(
2173
2126
  `INSERT INTO coding_standards (
2174
- id, title, content, parent_id, context, version, language, stack,
2127
+ id, code, title, content, parent_id, context, version, language, stack,
2175
2128
  is_global, repo, tags, metadata, created_at, updated_at, hit_count, last_used_at, agent, model
2176
- ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2129
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
2177
2130
  [
2178
2131
  entry.id,
2132
+ entry.code ?? null,
2179
2133
  entry.title,
2180
2134
  entry.content,
2181
2135
  entry.parent_id,
@@ -2200,6 +2154,10 @@ var StandardEntity = class extends BaseEntity {
2200
2154
  const row = this.get("SELECT * FROM coding_standards WHERE id = ?", [id]);
2201
2155
  return row ? this.rowToEntry(row) : null;
2202
2156
  }
2157
+ getByCode(code) {
2158
+ const row = this.get("SELECT * FROM coding_standards WHERE code = ?", [code]);
2159
+ return row ? this.rowToEntry(row) : null;
2160
+ }
2203
2161
  search(options) {
2204
2162
  const { query, context, version, language, stack, tag, repo, is_global, limit = 20, offset = 0 } = options;
2205
2163
  const where = [];
@@ -2270,6 +2228,38 @@ var StandardEntity = class extends BaseEntity {
2270
2228
  return { ...standard, similarity };
2271
2229
  }).sort((a, b) => b.similarity - a.similarity);
2272
2230
  }
2231
+ /**
2232
+ * Check if a new coding standard's content conflicts with an existing one.
2233
+ *
2234
+ * Returns the first conflicting entry whose cosine similarity exceeds `threshold`.
2235
+ * A conflict is SKIPPED (returns null) when the incoming version differs from
2236
+ * the conflicting entry's version — this allows intentional version bumps.
2237
+ *
2238
+ * @param content Raw content of the new standard to check.
2239
+ * @param incomingVersion Version of the new standard (e.g. "2.0.0").
2240
+ * @param repo Repo filter; pass undefined for global standards.
2241
+ * @param threshold Cosine-similarity cutoff (default 0.82 — stricter than memory).
2242
+ */
2243
+ checkConflicts(content, incomingVersion, repo, incomingLanguage, incomingStack, threshold = 0.82) {
2244
+ const candidates = this.search({ repo, limit: 80, offset: 0 });
2245
+ if (candidates.length === 0) return null;
2246
+ const queryVector = this.computeVector(content);
2247
+ for (const standard of candidates) {
2248
+ const similarity = this.cosineSimilarity(queryVector, this.computeVector(standard.content));
2249
+ if (similarity < threshold) continue;
2250
+ if (incomingVersion && standard.version && incomingVersion !== standard.version) {
2251
+ continue;
2252
+ }
2253
+ if (incomingLanguage && standard.language && incomingLanguage !== standard.language) {
2254
+ continue;
2255
+ }
2256
+ if (incomingStack.length > 0 && standard.stack.length > 0 && !incomingStack.some((s) => standard.stack.includes(s))) {
2257
+ continue;
2258
+ }
2259
+ return { ...standard, similarity };
2260
+ }
2261
+ return null;
2262
+ }
2273
2263
  getByIds(ids) {
2274
2264
  if (ids.length === 0) return [];
2275
2265
  const placeholders = ids.map(() => "?").join(",");
@@ -2346,6 +2336,7 @@ var StandardEntity = class extends BaseEntity {
2346
2336
  rowToEntry(row) {
2347
2337
  return {
2348
2338
  id: row.id,
2339
+ code: row.code ?? void 0,
2349
2340
  title: row.title,
2350
2341
  content: row.content,
2351
2342
  parent_id: row.parent_id ?? null,
@@ -2638,6 +2629,7 @@ var SQLiteStore = class _SQLiteStore {
2638
2629
  const migrator = new MigrationManager(this.db);
2639
2630
  migrator.migrate();
2640
2631
  migrator.addMemoryCodeColumn();
2632
+ migrator.addStandardCodeColumn();
2641
2633
  this.memories = new MemoryEntity(this.db);
2642
2634
  this.tasks = new TaskEntity(this.db);
2643
2635
  this.actions = new ActionEntity(this.db);
@@ -2797,7 +2789,8 @@ var RealVectorStore = class {
2797
2789
  throw error;
2798
2790
  }
2799
2791
  }
2800
- async remove(id, _kind = "memory") {
2792
+ async remove(id, kind = "memory") {
2793
+ void kind;
2801
2794
  if (!id) return;
2802
2795
  }
2803
2796
  async search(query, limit, repo, kind = "memory") {
@@ -2821,616 +2814,106 @@ var RealVectorStore = class {
2821
2814
  }
2822
2815
  };
2823
2816
 
2824
- // src/mcp/capabilities.ts
2825
- import { fileURLToPath as fileURLToPath2 } from "url";
2817
+ // src/mcp/session.ts
2826
2818
  import path4 from "path";
2827
- var __dirname = path4.dirname(fileURLToPath2(import.meta.url));
2828
- var pkgVersion = "0.1.0";
2829
- if ("0.9.4") {
2830
- pkgVersion = "0.9.4";
2831
- } else {
2832
- let searchDir = __dirname;
2833
- for (let i = 0; i < 5; i++) {
2834
- const candidate = path4.join(searchDir, "package.json");
2835
- try {
2836
- if (fs.existsSync(candidate)) {
2837
- const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
2838
- if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
2839
- pkgVersion = pkg.version;
2840
- break;
2841
- }
2842
- }
2843
- } catch {
2844
- }
2845
- searchDir = path4.dirname(searchDir);
2846
- }
2819
+ import { fileURLToPath as fileURLToPath2 } from "url";
2820
+ function createSessionContext() {
2821
+ return {
2822
+ roots: [],
2823
+ supportsRoots: false,
2824
+ supportsSampling: false,
2825
+ supportsSamplingTools: false,
2826
+ supportsElicitation: false,
2827
+ supportsElicitationForm: false,
2828
+ supportsElicitationUrl: false
2829
+ };
2847
2830
  }
2848
- var MCP_PROTOCOL_VERSION = "2025-03-26";
2849
- var CAPABILITIES = {
2850
- serverInfo: {
2851
- name: "mcp-memory-local",
2852
- version: pkgVersion
2853
- },
2854
- capabilities: {
2855
- completions: {},
2856
- logging: {},
2857
- resources: {
2858
- subscribe: true,
2859
- listChanged: true
2860
- },
2861
- tools: {
2862
- listChanged: false
2863
- },
2864
- prompts: {
2865
- listChanged: true
2866
- }
2867
- }
2868
- };
2869
-
2870
- // src/mcp/utils/pagination.ts
2871
- function encodeCursor(offset) {
2872
- return Buffer.from(String(offset), "utf8").toString("base64");
2831
+ function updateSessionFromInitialize(session, params) {
2832
+ const capabilities = params?.capabilities || {};
2833
+ session.clientInfo = params?.clientInfo;
2834
+ session.clientCapabilities = capabilities;
2835
+ session.supportsRoots = Boolean(capabilities.roots);
2836
+ session.supportsSampling = Boolean(capabilities.sampling);
2837
+ const sampling = capabilities.sampling;
2838
+ session.supportsSamplingTools = Boolean(sampling?.tools);
2839
+ session.supportsElicitation = Boolean(capabilities.elicitation);
2840
+ session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
2841
+ session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
2873
2842
  }
2874
- function decodeCursor(cursor) {
2875
- if (cursor === void 0 || cursor === null || cursor === "") {
2876
- return 0;
2877
- }
2878
- if (typeof cursor !== "string" || cursor.trim() === "") {
2879
- throw invalidPaginationParams("Invalid cursor");
2880
- }
2881
- let decoded;
2882
- try {
2883
- decoded = Buffer.from(cursor, "base64").toString("utf8");
2884
- } catch {
2885
- throw invalidPaginationParams("Invalid cursor");
2886
- }
2887
- if (!/^\d+$/.test(decoded)) {
2888
- throw invalidPaginationParams("Invalid cursor");
2843
+ function supportsElicitationMode(capability, mode) {
2844
+ if (!capability || typeof capability !== "object") {
2845
+ return false;
2889
2846
  }
2890
- const offset = Number.parseInt(decoded, 10);
2891
- if (!Number.isFinite(offset) || offset < 0) {
2892
- throw invalidPaginationParams("Invalid cursor");
2847
+ const cap = capability;
2848
+ if (mode === "form") {
2849
+ return Object.keys(cap).length === 0 || typeof cap.form === "object";
2893
2850
  }
2894
- return offset;
2851
+ return typeof cap.url === "object";
2895
2852
  }
2896
- function invalidPaginationParams(message) {
2897
- const error = new Error(message);
2898
- error.code = -32602;
2899
- return error;
2853
+ function updateSessionRoots(session, roots) {
2854
+ const normalized = normalizeRoots(roots);
2855
+ const previous = JSON.stringify(session.roots);
2856
+ const next = JSON.stringify(normalized);
2857
+ session.roots = normalized;
2858
+ return previous !== next;
2900
2859
  }
2901
-
2902
- // src/mcp/utils/completion.ts
2903
- var MAX_COMPLETION_VALUES = 100;
2904
- function rankCompletionValues(candidates, input) {
2905
- const unique = [...new Set(candidates.filter(Boolean))];
2906
- const needle = input.trim().toLowerCase();
2907
- if (!needle) {
2908
- return unique.slice(0, MAX_COMPLETION_VALUES);
2860
+ function normalizeRoots(roots) {
2861
+ if (!Array.isArray(roots)) return [];
2862
+ const seen = /* @__PURE__ */ new Set();
2863
+ const normalized = [];
2864
+ for (const root of roots) {
2865
+ if (!root || typeof root !== "object") continue;
2866
+ const r = root;
2867
+ const uri = typeof r.uri === "string" ? r.uri : void 0;
2868
+ const name = typeof r.name === "string" ? r.name : void 0;
2869
+ if (!uri || seen.has(uri)) continue;
2870
+ seen.add(uri);
2871
+ normalized.push({ uri, name });
2909
2872
  }
2910
- 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);
2873
+ return normalized;
2911
2874
  }
2912
- function scoreCompletionValue(value, needle) {
2913
- const haystack = value.toLowerCase();
2914
- if (haystack === needle) return 100;
2915
- if (haystack.startsWith(needle)) return 75;
2916
- if (haystack.includes(needle)) return 50;
2917
- const compactNeedle = needle.replace(/[\s_-]+/g, "");
2918
- const compactHaystack = haystack.replace(/[\s_-]+/g, "");
2919
- if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
2920
- return 0;
2875
+ function extractRootsFromResult(result) {
2876
+ return normalizeRoots(result?.roots);
2921
2877
  }
2922
-
2923
- // src/mcp/resources/index.ts
2924
- var DEFAULT_PAGE_SIZE = 25;
2925
- var MAX_PAGE_SIZE = 100;
2926
- function listResources(session, params) {
2927
- const resources = [
2928
- {
2929
- uri: "repository://index",
2930
- name: "Repository Index",
2931
- title: "Repository Index",
2932
- description: "List of all known repositories with memory/task counts and last activity",
2933
- mimeType: "application/json",
2934
- annotations: {
2935
- audience: ["assistant"],
2936
- priority: 1,
2937
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
2938
- }
2939
- },
2940
- {
2941
- uri: "session://roots",
2942
- name: "Session Roots",
2943
- title: "Session Roots",
2944
- description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
2945
- mimeType: "application/json",
2946
- size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
2947
- annotations: {
2948
- audience: ["assistant"],
2949
- priority: 0.95,
2950
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
2951
- }
2878
+ function getFilesystemRoots(session) {
2879
+ if (!session) return [];
2880
+ const resolved = [];
2881
+ for (const root of session.roots) {
2882
+ if (!root.uri.startsWith("file://")) continue;
2883
+ try {
2884
+ resolved.push(path4.resolve(fileURLToPath2(root.uri)));
2885
+ } catch {
2952
2886
  }
2953
- ];
2954
- return paginateEntries("resources", resources, params);
2887
+ }
2888
+ return resolved;
2955
2889
  }
2956
- function listResourceTemplates(params) {
2957
- const templates = [
2958
- // ── Memory ──────────────────────────────────────────────────────────────
2959
- {
2960
- uriTemplate: "repository://{name}/memories",
2961
- name: "Repository Memories",
2962
- title: "Repository Memories",
2963
- description: "All active memory entries for a specific repository",
2964
- mimeType: "application/json",
2965
- annotations: { audience: ["assistant"], priority: 0.85 }
2966
- },
2967
- {
2968
- uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
2969
- name: "Filtered Repository Memories",
2970
- title: "Filtered Repository Memories",
2971
- description: "Filter or search memories within a repository by keyword, type, or tag",
2972
- mimeType: "application/json",
2973
- annotations: { audience: ["assistant"], priority: 0.8 }
2974
- },
2975
- {
2976
- uriTemplate: "memory://{id}",
2977
- name: "Memory Detail",
2978
- title: "Memory Detail",
2979
- description: "Full content and statistics for a specific memory UUID",
2980
- mimeType: "application/json",
2981
- annotations: { audience: ["assistant"], priority: 0.75 }
2982
- },
2983
- // ── Tasks ────────────────────────────────────────────────────────────────
2984
- {
2985
- uriTemplate: "repository://{name}/tasks",
2986
- name: "Repository Tasks",
2987
- title: "Repository Tasks",
2988
- description: "All active tasks for a specific repository",
2989
- mimeType: "application/json",
2990
- annotations: { audience: ["assistant"], priority: 0.9 }
2991
- },
2992
- {
2993
- uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
2994
- name: "Filtered Repository Tasks",
2995
- title: "Filtered Repository Tasks",
2996
- description: "Filter tasks within a repository by status or priority level",
2997
- mimeType: "application/json",
2998
- annotations: { audience: ["assistant"], priority: 0.85 }
2999
- },
3000
- {
3001
- uriTemplate: "task://{id}",
3002
- name: "Task Detail",
3003
- title: "Task Detail",
3004
- description: "Full content and comments for a specific task UUID",
3005
- mimeType: "application/json",
3006
- annotations: { audience: ["assistant"], priority: 0.8 }
3007
- },
3008
- // ── Repository extras ────────────────────────────────────────────────────
3009
- {
3010
- uriTemplate: "repository://{name}/summary",
3011
- name: "Repository Summary",
3012
- title: "Repository Summary",
3013
- description: "High-level architectural summary for a repository",
3014
- mimeType: "text/plain",
3015
- annotations: { audience: ["assistant"], priority: 0.95 }
3016
- },
3017
- {
3018
- uriTemplate: "repository://{name}/actions",
3019
- name: "Repository Actions",
3020
- title: "Repository Actions",
3021
- description: "Audit log of agent tool actions scoped to a repository",
3022
- mimeType: "application/json",
3023
- annotations: { audience: ["assistant"], priority: 0.6 }
3024
- },
3025
- // ── Action detail ────────────────────────────────────────────────────────
3026
- {
3027
- uriTemplate: "action://{id}",
3028
- name: "Action Detail",
3029
- title: "Action Detail",
3030
- description: "Full details of a specific audit log entry by integer ID",
3031
- mimeType: "application/json",
3032
- annotations: { audience: ["assistant"], priority: 0.55 }
3033
- }
3034
- ];
3035
- return paginateEntries("resourceTemplates", templates, params);
2890
+ function isPathWithinRoots(targetPath, session) {
2891
+ const roots = getFilesystemRoots(session);
2892
+ if (roots.length === 0) return true;
2893
+ const normalizedTarget = path4.resolve(targetPath);
2894
+ return roots.some((rootPath) => {
2895
+ const relative = path4.relative(rootPath, normalizedTarget);
2896
+ return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
2897
+ });
3036
2898
  }
3037
- function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
3038
- 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") {
3039
- if (argumentName === "name") {
3040
- return rankCompletionValues(dataSources.repos, argumentValue);
3041
- }
3042
- }
3043
- if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
3044
- if (argumentName === "tag") {
3045
- return rankCompletionValues(dataSources.tags, argumentValue);
2899
+ function findContainingRoot(targetPath, session) {
2900
+ const roots = getFilesystemRoots(session);
2901
+ if (roots.length === 0) return null;
2902
+ const normalizedTarget = path4.resolve(targetPath);
2903
+ for (const rootPath of roots) {
2904
+ const relative = path4.relative(rootPath, normalizedTarget);
2905
+ if (relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative)) {
2906
+ return rootPath;
3046
2907
  }
3047
2908
  }
3048
- throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
2909
+ return null;
3049
2910
  }
3050
- function readResource(uri, db, session) {
3051
- logger.info("[Tool] resource.read", { uri });
3052
- if (uri === "repository://index") {
3053
- const repos = db.system.listRepoNavigation();
3054
- const payload = JSON.stringify(repos, null, 2);
3055
- return {
3056
- contents: [
3057
- {
3058
- uri,
3059
- mimeType: "application/json",
3060
- text: payload,
3061
- size: Buffer.byteLength(payload, "utf8"),
3062
- annotations: {
3063
- audience: ["assistant"],
3064
- priority: 1,
3065
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
3066
- }
3067
- }
3068
- ]
3069
- };
3070
- }
3071
- if (uri === "session://roots") {
3072
- const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
3073
- return {
3074
- contents: [
3075
- {
3076
- uri,
3077
- mimeType: "application/json",
3078
- text: payload,
3079
- size: Buffer.byteLength(payload, "utf8"),
3080
- annotations: {
3081
- audience: ["assistant"],
3082
- priority: 0.95,
3083
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
3084
- }
3085
- }
3086
- ]
3087
- };
3088
- }
3089
- const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
3090
- if (memoryIdMatch) {
3091
- const id = memoryIdMatch[1];
3092
- const entry = db.memories.getByIdWithStats(id);
3093
- if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
3094
- const payload = JSON.stringify(entry, null, 2);
3095
- return {
3096
- contents: [
3097
- {
3098
- uri,
3099
- mimeType: "application/json",
3100
- text: payload,
3101
- size: Buffer.byteLength(payload, "utf8"),
3102
- annotations: {
3103
- audience: ["assistant"],
3104
- priority: 0.75,
3105
- lastModified: entry.updated_at || entry.created_at
3106
- }
3107
- }
3108
- ]
3109
- };
2911
+ function inferRepoFromSession(session) {
2912
+ const roots = getFilesystemRoots(session);
2913
+ if (roots.length === 1) {
2914
+ return path4.basename(roots[0]);
3110
2915
  }
3111
- const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
3112
- if (taskIdMatch) {
3113
- const id = taskIdMatch[1];
3114
- const task = db.tasks.getTaskById(id);
3115
- if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
3116
- const payload = JSON.stringify(task, null, 2);
3117
- return {
3118
- contents: [
3119
- {
3120
- uri,
3121
- mimeType: "application/json",
3122
- text: payload,
3123
- size: Buffer.byteLength(payload, "utf8"),
3124
- annotations: {
3125
- audience: ["assistant"],
3126
- priority: 0.8,
3127
- lastModified: task.updated_at || task.created_at
3128
- }
3129
- }
3130
- ]
3131
- };
3132
- }
3133
- const repoBase = parseRepoUri(uri);
3134
- if (repoBase) {
3135
- const { name, path: repoPath, query } = repoBase;
3136
- if (repoPath === "summary") {
3137
- const summary = db.summaries.getSummary(name);
3138
- const text = summary?.summary || `No summary available for repository: ${name}`;
3139
- return {
3140
- contents: [
3141
- {
3142
- uri,
3143
- mimeType: "text/plain",
3144
- text,
3145
- size: Buffer.byteLength(text, "utf8"),
3146
- annotations: {
3147
- audience: ["assistant"],
3148
- priority: 0.95,
3149
- lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
3150
- }
3151
- }
3152
- ]
3153
- };
3154
- }
3155
- if (repoPath === "memories") {
3156
- const search = query.get("search") || "";
3157
- const type = query.get("type");
3158
- const tag = query.get("tag");
3159
- const result = db.memories.listMemoriesForDashboard({
3160
- repo: name,
3161
- type: type || void 0,
3162
- tag: tag || void 0,
3163
- search: search || void 0,
3164
- limit: 50
3165
- });
3166
- const entries = result.items;
3167
- const payload = JSON.stringify(entries, null, 2);
3168
- return {
3169
- contents: [
3170
- {
3171
- uri,
3172
- mimeType: "application/json",
3173
- text: payload,
3174
- size: Buffer.byteLength(payload, "utf8"),
3175
- annotations: {
3176
- audience: ["assistant"],
3177
- priority: 0.85,
3178
- lastModified: deriveLastModifiedFromCollection(
3179
- entries.map((e) => e.updated_at || e.created_at)
3180
- )
3181
- }
3182
- }
3183
- ]
3184
- };
3185
- }
3186
- if (repoPath === "tasks") {
3187
- const status = query.get("status");
3188
- const priority = query.get("priority");
3189
- let tasks;
3190
- if (status && status !== "all") {
3191
- const statuses = status.split(",").map((s) => s.trim());
3192
- tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
3193
- } else {
3194
- tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
3195
- }
3196
- if (priority) {
3197
- const p = Number(priority);
3198
- if (!isNaN(p)) {
3199
- tasks = tasks.filter((t) => t.priority === p);
3200
- }
3201
- }
3202
- const payload = JSON.stringify(tasks, null, 2);
3203
- return {
3204
- contents: [
3205
- {
3206
- uri,
3207
- mimeType: "application/json",
3208
- text: payload,
3209
- size: Buffer.byteLength(payload, "utf8"),
3210
- annotations: {
3211
- audience: ["assistant"],
3212
- priority: 0.9,
3213
- lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
3214
- }
3215
- }
3216
- ]
3217
- };
3218
- }
3219
- if (repoPath === "actions") {
3220
- const actions = db.actions.getRecentActions(name, 100);
3221
- const payload = JSON.stringify(actions, null, 2);
3222
- return {
3223
- contents: [
3224
- {
3225
- uri,
3226
- mimeType: "application/json",
3227
- text: payload,
3228
- size: Buffer.byteLength(payload, "utf8"),
3229
- annotations: {
3230
- audience: ["assistant"],
3231
- priority: 0.6,
3232
- lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
3233
- }
3234
- }
3235
- ]
3236
- };
3237
- }
3238
- }
3239
- const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
3240
- if (actionIdMatch) {
3241
- const id = Number(actionIdMatch[1]);
3242
- const action = db.actions.getActionById(id);
3243
- if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
3244
- const payload = JSON.stringify(action, null, 2);
3245
- return {
3246
- contents: [
3247
- {
3248
- uri,
3249
- mimeType: "application/json",
3250
- text: payload,
3251
- size: Buffer.byteLength(payload, "utf8"),
3252
- annotations: {
3253
- audience: ["assistant"],
3254
- priority: 0.55,
3255
- lastModified: action.created_at
3256
- }
3257
- }
3258
- ]
3259
- };
3260
- }
3261
- throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
3262
- }
3263
- function parseRepoUri(uri) {
3264
- const prefix = "repository://";
3265
- if (!uri.startsWith(prefix)) return null;
3266
- const rest = uri.slice(prefix.length);
3267
- const queryStart = rest.indexOf("?");
3268
- const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
3269
- const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
3270
- const slashIdx = withoutQuery.indexOf("/");
3271
- if (slashIdx === -1) return null;
3272
- const name = withoutQuery.slice(0, slashIdx);
3273
- const path6 = withoutQuery.slice(slashIdx + 1);
3274
- if (!name || !path6) return null;
3275
- return { name, path: path6, query: new URLSearchParams(queryString) };
3276
- }
3277
- function paginateEntries(key, entries, params) {
3278
- const limit = normalizeLimit(params?.limit);
3279
- const offset = decodeCursor(params?.cursor);
3280
- const sliced = entries.slice(offset, offset + limit);
3281
- const nextOffset = offset + sliced.length;
3282
- return {
3283
- [key]: sliced,
3284
- nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
3285
- };
3286
- }
3287
- function normalizeLimit(limit) {
3288
- if (typeof limit !== "number" || !Number.isFinite(limit)) {
3289
- return DEFAULT_PAGE_SIZE;
3290
- }
3291
- return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
3292
- }
3293
- function deriveLastModifiedFromCollection(values) {
3294
- const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
3295
- return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
3296
- }
3297
- function resourceNotFound(message, uri) {
3298
- const error = new Error(message);
3299
- error.code = -32002;
3300
- error.data = { uri };
3301
- return error;
3302
- }
3303
- function invalidCompletionParams(message) {
3304
- const error = new Error(message);
3305
- error.code = -32602;
3306
- return error;
3307
- }
3308
-
3309
- // src/mcp/prompts/loader.ts
3310
- import fs4 from "fs";
3311
- import path5 from "path";
3312
- import { fileURLToPath as fileURLToPath3 } from "url";
3313
- import matter from "gray-matter";
3314
- var __filename = fileURLToPath3(import.meta.url);
3315
- var __dirname2 = path5.dirname(__filename);
3316
- function findPromptDir() {
3317
- const candidates = [
3318
- // Production if chunked into dist/
3319
- "./prompts",
3320
- // Production if inlined into dist/mcp/
3321
- "../prompts",
3322
- // Dev: /src/mcp/prompts/definitions (next to loader.ts)
3323
- "./definitions"
3324
- ].map((relPath) => path5.resolve(__dirname2, relPath));
3325
- for (const dir of candidates) {
3326
- if (fs4.existsSync(dir)) {
3327
- const files = fs4.readdirSync(dir);
3328
- if (files.some((f) => f.endsWith(".md"))) {
3329
- return dir;
3330
- }
3331
- }
3332
- }
3333
- return path5.resolve(__dirname2, "./definitions");
3334
- }
3335
- var PROMPT_DIR = findPromptDir();
3336
- function listPromptFiles() {
3337
- if (!fs4.existsSync(PROMPT_DIR)) return [];
3338
- return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
3339
- }
3340
- function loadPromptFromMarkdown(name) {
3341
- const filePath = path5.join(PROMPT_DIR, `${name}.md`);
3342
- if (!fs4.existsSync(filePath)) {
3343
- throw new Error(`Prompt file not found: ${filePath}`);
3344
- }
3345
- const fileContent = fs4.readFileSync(filePath, "utf-8");
3346
- const { data, content } = matter(fileContent);
3347
- return {
3348
- name: data.name || name,
3349
- description: data.description || "",
3350
- arguments: data.arguments || [],
3351
- agent: data.agent,
3352
- content: content.trim()
3353
- };
3354
- }
3355
-
3356
- // src/mcp/prompts/registry.ts
3357
- function createPromptDefinition(loaded) {
3358
- return {
3359
- name: loaded.name,
3360
- description: loaded.description,
3361
- arguments: loaded.arguments,
3362
- agent: loaded.agent,
3363
- messages: [
3364
- {
3365
- role: "user",
3366
- content: {
3367
- type: "text",
3368
- text: loaded.content
3369
- }
3370
- }
3371
- ]
3372
- };
3373
- }
3374
- var PROMPTS = {};
3375
- var promptFiles = listPromptFiles();
3376
- for (const name of promptFiles) {
3377
- try {
3378
- PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
3379
- } catch (e) {
3380
- logger.warn(`Failed to load prompt ${name}: ${e}`);
3381
- }
3382
- }
3383
- async function listPrompts(db, session, params) {
3384
- const allPrompts = Object.values(PROMPTS).map((p) => ({
3385
- name: p.name,
3386
- description: p.description,
3387
- arguments: p.arguments,
3388
- metadata: p.agent ? { agent: p.agent } : void 0
3389
- }));
3390
- const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
3391
- const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
3392
- const offset = decodeCursor(params?.cursor);
3393
- const sliced = allPrompts.slice(offset, offset + limit);
3394
- const nextOffset = offset + sliced.length;
3395
- return {
3396
- prompts: sliced,
3397
- nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
3398
- };
3399
- }
3400
- async function getPrompt(name, args = {}, db, session) {
3401
- const prompt = PROMPTS[name];
3402
- if (!prompt) {
3403
- throw new Error(`Prompt not found: ${name}`);
3404
- }
3405
- const inferredRepo = inferRepoFromSession(session);
3406
- const messages = prompt.messages.map((m) => {
3407
- let text = m.content.text;
3408
- for (const [key, value] of Object.entries(args)) {
3409
- text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
3410
- }
3411
- text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
3412
- return {
3413
- ...m,
3414
- content: {
3415
- ...m.content,
3416
- text
3417
- }
3418
- };
3419
- });
3420
- return {
3421
- description: prompt.description,
3422
- messages,
3423
- metadata: prompt.agent ? { agent: prompt.agent } : void 0
3424
- };
3425
- }
3426
- async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
3427
- void name;
3428
- void contextArguments;
3429
- if (argName === "task_id") {
3430
- const values = dataSources.tasks.map((t) => t.id);
3431
- return rankCompletionValues(values, value);
3432
- }
3433
- return [];
2916
+ return void 0;
3434
2917
  }
3435
2918
 
3436
2919
  // src/mcp/tools/schemas.ts
@@ -3643,10 +3126,13 @@ var MemoryDetailSchema = z.object({
3643
3126
  }).refine((data) => data.id !== void 0 || data.code !== void 0, {
3644
3127
  message: "Either id or code must be provided"
3645
3128
  });
3646
- var StandardDetailSchema = z.object({
3647
- id: z.string().uuid(),
3648
- structured: z.boolean().default(false)
3649
- });
3129
+ var StandardDetailSchema = z.object({
3130
+ id: z.string().uuid().optional(),
3131
+ code: z.string().max(20).optional(),
3132
+ structured: z.boolean().default(false)
3133
+ }).refine((data) => data.id !== void 0 || data.code !== void 0, {
3134
+ message: "Either id or code must be provided"
3135
+ });
3650
3136
  var StandardDeleteSchema = z.object({
3651
3137
  repo: z.string().min(1).transform(normalizeRepo).optional(),
3652
3138
  id: z.string().uuid().optional(),
@@ -3763,241 +3249,669 @@ var StandardSearchSchema = z.object({
3763
3249
  });
3764
3250
  var TOOL_DEFINITIONS = [
3765
3251
  {
3766
- name: "memory-synthesize",
3767
- title: "Memory Synthesize",
3768
- description: "Use client sampling to synthesize a grounded answer from local memory and tasks. Best for project briefings, tradeoff summaries, and context-aware answers.",
3252
+ name: "memory-synthesize",
3253
+ title: "Memory Synthesize",
3254
+ description: "Use client sampling to synthesize a grounded answer from local memory and tasks. Best for project briefings, tradeoff summaries, and context-aware answers.",
3255
+ annotations: {
3256
+ readOnlyHint: true,
3257
+ idempotentHint: true,
3258
+ openWorldHint: false
3259
+ },
3260
+ execution: {
3261
+ taskSupport: "optional"
3262
+ },
3263
+ inputSchema: {
3264
+ type: "object",
3265
+ properties: {
3266
+ repo: { type: "string", description: "Repository name. Optional when a single MCP root is active." },
3267
+ objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
3268
+ current_file_path: {
3269
+ type: "string",
3270
+ description: "Optional absolute file path for workspace-local grounding."
3271
+ },
3272
+ include_summary: { type: "boolean", default: true },
3273
+ include_tasks: { type: "boolean", default: true },
3274
+ use_tools: {
3275
+ type: "boolean",
3276
+ default: true,
3277
+ description: "Allow the sampled model to call local memory/task tools during synthesis when the client supports sampling.tools."
3278
+ },
3279
+ max_iterations: { type: "number", minimum: 1, maximum: 5, default: 3 },
3280
+ max_tokens: { type: "number", minimum: 128, maximum: 4e3, default: 1200 },
3281
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON results." }
3282
+ },
3283
+ required: ["objective"]
3284
+ },
3285
+ outputSchema: {
3286
+ type: "object",
3287
+ properties: {
3288
+ repo: { type: "string" },
3289
+ objective: { type: "string" },
3290
+ answer: { type: "string" },
3291
+ model: { type: "string" },
3292
+ stopReason: { type: "string" },
3293
+ iterations: { type: "number" },
3294
+ toolCalls: { type: "number" }
3295
+ },
3296
+ required: ["repo", "objective", "answer", "iterations", "toolCalls"]
3297
+ }
3298
+ },
3299
+ {
3300
+ name: "task-create-interactive",
3301
+ title: "Interactive Task Create",
3302
+ 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.",
3303
+ annotations: {
3304
+ readOnlyHint: false,
3305
+ idempotentHint: false,
3306
+ destructiveHint: false,
3307
+ openWorldHint: false
3308
+ },
3309
+ inputSchema: {
3310
+ type: "object",
3311
+ properties: {
3312
+ repo: {
3313
+ type: "string",
3314
+ description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
3315
+ },
3316
+ task_code: { type: "string" },
3317
+ phase: { type: "string" },
3318
+ title: { type: "string", minLength: 3, maxLength: 100 },
3319
+ description: { type: "string", minLength: 1 },
3320
+ status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3321
+ priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3322
+ agent: { type: "string" },
3323
+ role: { type: "string" },
3324
+ doc_path: { type: "string" },
3325
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3326
+ }
3327
+ },
3328
+ outputSchema: {
3329
+ type: "object",
3330
+ properties: {
3331
+ repo: { type: "string" },
3332
+ task_code: { type: "string" },
3333
+ phase: { type: "string" },
3334
+ title: { type: "string" },
3335
+ status: { type: "string" },
3336
+ priority: { type: "number" }
3337
+ },
3338
+ required: ["repo", "task_code", "phase", "title", "status", "priority"]
3339
+ }
3340
+ },
3341
+ {
3342
+ name: "memory-detail",
3343
+ title: "Memory Detail",
3344
+ description: "Fetch full details of a specific memory by ID or short code. Use after memory-recap or memory-search when a pointer row is relevant and full content is needed.",
3345
+ inputSchema: {
3346
+ type: "object",
3347
+ properties: {
3348
+ id: { type: "string", format: "uuid", description: "Memory entry ID. Optional if code is provided." },
3349
+ code: { type: "string", description: "Short memory code. Optional if id is provided." },
3350
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3351
+ }
3352
+ }
3353
+ },
3354
+ {
3355
+ name: "standard-detail",
3356
+ title: "Standard Detail",
3357
+ 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.",
3358
+ inputSchema: {
3359
+ type: "object",
3360
+ properties: {
3361
+ id: { type: "string", format: "uuid", description: "Coding standard ID. Optional if code is provided." },
3362
+ code: { type: "string", description: "Short standard code (e.g. 'A3KPQ2'). Optional if id is provided." },
3363
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3364
+ }
3365
+ }
3366
+ },
3367
+ {
3368
+ name: "task-detail",
3369
+ title: "Task Detail",
3370
+ 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.",
3371
+ inputSchema: {
3372
+ type: "object",
3373
+ properties: {
3374
+ repo: { type: "string", description: "Repository name" },
3375
+ id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
3376
+ task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
3377
+ structured: {
3378
+ type: "boolean",
3379
+ default: false,
3380
+ description: "If true, returns structured JSON without the text content details."
3381
+ }
3382
+ },
3383
+ required: ["repo"]
3384
+ }
3385
+ },
3386
+ {
3387
+ name: "memory-store",
3388
+ title: "Memory Store",
3389
+ 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'.",
3390
+ annotations: {
3391
+ readOnlyHint: false,
3392
+ idempotentHint: false,
3393
+ destructiveHint: false,
3394
+ openWorldHint: false
3395
+ },
3396
+ inputSchema: {
3397
+ type: "object",
3398
+ properties: {
3399
+ type: {
3400
+ type: "string",
3401
+ enum: [
3402
+ "code_fact",
3403
+ "decision",
3404
+ "mistake",
3405
+ "pattern",
3406
+ "task_archive"
3407
+ ],
3408
+ description: "Type of durable knowledge being stored. Coordination types such as file_claim are intentionally unsupported."
3409
+ },
3410
+ title: {
3411
+ type: "string",
3412
+ minLength: 3,
3413
+ maxLength: 100,
3414
+ description: "Short human-readable title for the memory. Do not embed bracketed metadata like agent/role/date prefixes here."
3415
+ },
3416
+ content: {
3417
+ type: "string",
3418
+ minLength: 10,
3419
+ description: "The memory content"
3420
+ },
3421
+ importance: {
3422
+ type: "number",
3423
+ minimum: 1,
3424
+ maximum: 5,
3425
+ description: "Importance score (1-5)"
3426
+ },
3427
+ agent: {
3428
+ type: "string",
3429
+ description: "Name of the agent creating this memory"
3430
+ },
3431
+ role: {
3432
+ type: "string",
3433
+ description: "Role of the agent creating this memory"
3434
+ },
3435
+ model: {
3436
+ type: "string",
3437
+ description: "AI model used by the agent"
3438
+ },
3439
+ scope: {
3440
+ type: "object",
3441
+ properties: {
3442
+ repo: { type: "string", description: "Repository name" },
3443
+ branch: { type: "string" },
3444
+ folder: { type: "string" },
3445
+ language: { type: "string" }
3446
+ },
3447
+ required: ["repo"]
3448
+ },
3449
+ tags: {
3450
+ type: "array",
3451
+ items: { type: "string" },
3452
+ description: "Technology stack tags (e.g., ['filament', 'laravel'])"
3453
+ },
3454
+ metadata: {
3455
+ type: "object",
3456
+ description: "Structured metadata for non-title context such as source agent, claim fields, or timestamps"
3457
+ },
3458
+ is_global: {
3459
+ type: "boolean",
3460
+ description: "If true, this memory is shared across all repositories"
3461
+ },
3462
+ ttlDays: { type: "number", minimum: 1 },
3463
+ supersedes: { type: "string", format: "uuid" },
3464
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the stored memory." }
3465
+ },
3466
+ required: ["type", "title", "content", "importance", "scope", "agent", "model"]
3467
+ },
3468
+ outputSchema: {
3469
+ type: "object",
3470
+ properties: {
3471
+ success: { type: "boolean" },
3472
+ id: { type: "string" },
3473
+ code: { type: "string" },
3474
+ repo: { type: "string" },
3475
+ type: { type: "string" },
3476
+ title: { type: "string" },
3477
+ error: { type: "string" },
3478
+ message: { type: "string" }
3479
+ },
3480
+ required: ["success"]
3481
+ }
3482
+ },
3483
+ {
3484
+ name: "memory-acknowledge",
3485
+ title: "Memory Acknowledge",
3486
+ description: "Acknowledge the use of a memory or report its irrelevance/contradiction. Mandatory after using memory to generate code.",
3487
+ annotations: {
3488
+ readOnlyHint: false,
3489
+ idempotentHint: false,
3490
+ openWorldHint: false
3491
+ },
3492
+ inputSchema: {
3493
+ type: "object",
3494
+ properties: {
3495
+ memory_id: { type: "string", format: "uuid" },
3496
+ status: { type: "string", enum: ["used", "irrelevant", "contradictory"] },
3497
+ application_context: { type: "string", minLength: 10 },
3498
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3499
+ },
3500
+ required: ["memory_id", "status"]
3501
+ },
3502
+ outputSchema: {
3503
+ type: "object",
3504
+ properties: {
3505
+ success: { type: "boolean" },
3506
+ id: { type: "string" },
3507
+ status: { type: "string" }
3508
+ },
3509
+ required: ["success", "id", "status"]
3510
+ }
3511
+ },
3512
+ {
3513
+ name: "memory-update",
3514
+ title: "Memory Update",
3515
+ description: "Update an existing memory entry. Keep 'title' concise and move agent/role/date or claim context into 'metadata' instead of the title.",
3516
+ annotations: {
3517
+ readOnlyHint: false,
3518
+ idempotentHint: false,
3519
+ destructiveHint: false,
3520
+ openWorldHint: false
3521
+ },
3522
+ inputSchema: {
3523
+ type: "object",
3524
+ properties: {
3525
+ id: { type: "string", format: "uuid" },
3526
+ type: {
3527
+ type: "string",
3528
+ enum: [
3529
+ "code_fact",
3530
+ "decision",
3531
+ "mistake",
3532
+ "pattern",
3533
+ "task_archive"
3534
+ ]
3535
+ },
3536
+ title: { type: "string", minLength: 3, maxLength: 100 },
3537
+ content: { type: "string", minLength: 10 },
3538
+ importance: { type: "number", minimum: 1, maximum: 5 },
3539
+ agent: { type: "string" },
3540
+ role: { type: "string" },
3541
+ status: { type: "string", enum: ["active", "archived"] },
3542
+ supersedes: { type: "string", format: "uuid" },
3543
+ tags: { type: "array", items: { type: "string" } },
3544
+ metadata: { type: "object" },
3545
+ is_global: { type: "boolean" },
3546
+ completed_at: { type: "string" },
3547
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the updated memory." }
3548
+ },
3549
+ required: ["id"]
3550
+ },
3551
+ outputSchema: {
3552
+ type: "object",
3553
+ properties: {
3554
+ success: { type: "boolean" },
3555
+ id: { type: "string" },
3556
+ repo: { type: "string" },
3557
+ updatedFields: {
3558
+ type: "array",
3559
+ items: { type: "string" }
3560
+ }
3561
+ },
3562
+ required: ["success", "id", "repo", "updatedFields"]
3563
+ }
3564
+ },
3565
+ {
3566
+ name: "memory-search",
3567
+ title: "Memory Search",
3568
+ description: "NAVIGATION LAYER: Returns a pointer table of matching memory IDs only. Returns columns [id, title, type, importance] \u2014 NO content. Retrieve full memory via memory-detail. Use 'current_tags' to find tech-stack specific knowledge from other projects.",
3769
3569
  annotations: {
3770
3570
  readOnlyHint: true,
3771
3571
  idempotentHint: true,
3772
3572
  openWorldHint: false
3773
3573
  },
3774
- execution: {
3775
- taskSupport: "optional"
3776
- },
3777
3574
  inputSchema: {
3778
3575
  type: "object",
3779
3576
  properties: {
3780
- repo: { type: "string", description: "Repository name. Optional when a single MCP root is active." },
3781
- objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
3782
- current_file_path: {
3783
- type: "string",
3784
- description: "Optional absolute file path for workspace-local grounding."
3577
+ query: { type: "string", minLength: 3 },
3578
+ prompt: { type: "string" },
3579
+ repo: { type: "string" },
3580
+ current_tags: {
3581
+ type: "array",
3582
+ items: { type: "string" },
3583
+ description: "Active tech stack tags (e.g., ['filament', 'react'])"
3785
3584
  },
3786
- include_summary: { type: "boolean", default: true },
3787
- include_tasks: { type: "boolean", default: true },
3788
- use_tools: {
3789
- type: "boolean",
3790
- default: true,
3791
- description: "Allow the sampled model to call local memory/task tools during synthesis when the client supports sampling.tools."
3585
+ types: {
3586
+ type: "array",
3587
+ items: {
3588
+ type: "string",
3589
+ enum: [
3590
+ "code_fact",
3591
+ "decision",
3592
+ "mistake",
3593
+ "pattern",
3594
+ "task_archive"
3595
+ ]
3596
+ }
3792
3597
  },
3793
- max_iterations: { type: "number", minimum: 1, maximum: 5, default: 3 },
3794
- max_tokens: { type: "number", minimum: 128, maximum: 4e3, default: 1200 },
3795
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON results." }
3598
+ minImportance: { type: "number", minimum: 1, maximum: 5 },
3599
+ limit: { type: "number", minimum: 1, maximum: 100, default: 5 },
3600
+ offset: { type: "number", minimum: 0, default: 0 },
3601
+ includeRecap: { type: "boolean", default: false },
3602
+ current_file_path: { type: "string" },
3603
+ include_archived: { type: "boolean", default: false },
3604
+ scope: {
3605
+ type: "object",
3606
+ properties: {
3607
+ repo: { type: "string" },
3608
+ branch: { type: "string" },
3609
+ folder: { type: "string" },
3610
+ language: { type: "string" }
3611
+ }
3612
+ },
3613
+ structured: {
3614
+ type: "boolean",
3615
+ default: false,
3616
+ description: "If true, returns structured JSON without the text content summary."
3617
+ }
3796
3618
  },
3797
- required: ["objective"]
3619
+ required: ["query", "repo"]
3798
3620
  },
3799
3621
  outputSchema: {
3800
3622
  type: "object",
3801
3623
  properties: {
3802
- repo: { type: "string" },
3803
- objective: { type: "string" },
3804
- answer: { type: "string" },
3805
- model: { type: "string" },
3806
- stopReason: { type: "string" },
3807
- iterations: { type: "number" },
3808
- toolCalls: { type: "number" }
3624
+ schema: { type: "string", enum: ["memory-search"] },
3625
+ query: { type: "string" },
3626
+ count: { type: "number", description: "Number of rows returned" },
3627
+ total: { type: "number", description: "Total matching memories" },
3628
+ offset: { type: "number" },
3629
+ limit: { type: "number" },
3630
+ results: {
3631
+ type: "object",
3632
+ properties: {
3633
+ columns: {
3634
+ type: "array",
3635
+ items: { type: "string" },
3636
+ description: "Column names: [id, title, type, importance]"
3637
+ },
3638
+ rows: {
3639
+ type: "array",
3640
+ items: { type: "array" },
3641
+ description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
3642
+ }
3643
+ },
3644
+ required: ["columns", "rows"]
3645
+ }
3809
3646
  },
3810
- required: ["repo", "objective", "answer", "iterations", "toolCalls"]
3647
+ required: ["schema", "query", "count", "total", "offset", "limit", "results"]
3811
3648
  }
3812
3649
  },
3813
3650
  {
3814
- name: "task-create-interactive",
3815
- title: "Interactive Task Create",
3816
- 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.",
3651
+ name: "memory-summarize",
3652
+ title: "Memory Summarize",
3653
+ description: "Update the summary for a repository",
3817
3654
  annotations: {
3818
3655
  readOnlyHint: false,
3819
3656
  idempotentHint: false,
3820
- destructiveHint: false,
3821
3657
  openWorldHint: false
3822
3658
  },
3823
3659
  inputSchema: {
3824
3660
  type: "object",
3825
3661
  properties: {
3826
- repo: {
3827
- type: "string",
3828
- description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
3662
+ repo: { type: "string", description: "Repository name" },
3663
+ signals: {
3664
+ type: "array",
3665
+ items: { type: "string", maxLength: 200 },
3666
+ minItems: 1,
3667
+ description: "High-level signals to include in summary"
3829
3668
  },
3830
- task_code: { type: "string" },
3831
- phase: { type: "string" },
3832
- title: { type: "string", minLength: 3, maxLength: 100 },
3833
- description: { type: "string", minLength: 1 },
3834
- status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3835
- priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3836
- agent: { type: "string" },
3837
- role: { type: "string" },
3838
- doc_path: { type: "string" },
3839
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3840
- }
3669
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the summary." }
3670
+ },
3671
+ required: ["repo", "signals"]
3841
3672
  },
3842
3673
  outputSchema: {
3843
3674
  type: "object",
3844
3675
  properties: {
3676
+ success: { type: "boolean" },
3845
3677
  repo: { type: "string" },
3846
- task_code: { type: "string" },
3847
- phase: { type: "string" },
3848
- title: { type: "string" },
3849
- status: { type: "string" },
3850
- priority: { type: "number" }
3678
+ summary: { type: "string" },
3679
+ signalCount: { type: "number" }
3851
3680
  },
3852
- required: ["repo", "task_code", "phase", "title", "status", "priority"]
3681
+ required: ["success", "repo", "summary", "signalCount"]
3853
3682
  }
3854
3683
  },
3855
3684
  {
3856
- name: "memory-detail",
3857
- title: "Memory Detail",
3858
- description: "Fetch full details of a specific memory by ID or short code. Use after memory-recap or memory-search when a pointer row is relevant and full content is needed.",
3685
+ name: "memory-delete",
3686
+ title: "Memory Delete",
3687
+ description: "Soft-delete one or more memory entries. Supports single 'id' or bulk 'ids'.",
3688
+ annotations: {
3689
+ readOnlyHint: false,
3690
+ idempotentHint: false,
3691
+ destructiveHint: true,
3692
+ openWorldHint: false
3693
+ },
3859
3694
  inputSchema: {
3860
3695
  type: "object",
3861
3696
  properties: {
3862
- id: { type: "string", format: "uuid", description: "Memory entry ID. Optional if code is provided." },
3863
- code: { type: "string", description: "Short memory code. Optional if id is provided." },
3864
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3697
+ repo: { type: "string", description: "Repository name (optional for single id)" },
3698
+ id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
3699
+ ids: {
3700
+ type: "array",
3701
+ items: { type: "string", format: "uuid" },
3702
+ minItems: 1,
3703
+ description: "Array of memory IDs to delete"
3704
+ },
3705
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3865
3706
  }
3707
+ },
3708
+ outputSchema: {
3709
+ type: "object",
3710
+ properties: {
3711
+ success: { type: "boolean" },
3712
+ id: { type: "string" },
3713
+ ids: { type: "array", items: { type: "string" } },
3714
+ repo: { type: "string" },
3715
+ deletedCount: { type: "number" }
3716
+ },
3717
+ required: ["success"]
3866
3718
  }
3867
3719
  },
3868
3720
  {
3869
- name: "standard-detail",
3870
- title: "Standard Detail",
3871
- description: "Fetch full details of a specific coding standard by ID. Use after standard-search when a result is relevant and full guidance is needed.",
3721
+ name: "standard-delete",
3722
+ title: "Standard Delete",
3723
+ description: "Delete one or more coding standards. Supports single 'id' or bulk 'ids'.",
3724
+ annotations: {
3725
+ readOnlyHint: false,
3726
+ idempotentHint: false,
3727
+ destructiveHint: true,
3728
+ openWorldHint: false
3729
+ },
3872
3730
  inputSchema: {
3873
3731
  type: "object",
3874
3732
  properties: {
3875
- id: { type: "string", format: "uuid", description: "Coding standard ID." },
3876
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3733
+ repo: { type: "string", description: "Repository name (optional for single id)" },
3734
+ id: { type: "string", format: "uuid", description: "Coding standard ID to delete" },
3735
+ ids: {
3736
+ type: "array",
3737
+ items: { type: "string", format: "uuid" },
3738
+ minItems: 1,
3739
+ description: "Array of coding standard IDs to delete"
3740
+ },
3741
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3742
+ }
3743
+ },
3744
+ outputSchema: {
3745
+ type: "object",
3746
+ properties: {
3747
+ success: { type: "boolean" },
3748
+ id: { type: "string" },
3749
+ ids: { type: "array", items: { type: "string" } },
3750
+ repo: { type: "string" },
3751
+ deletedCount: { type: "number" }
3877
3752
  },
3878
- required: ["id"]
3753
+ required: ["success"]
3879
3754
  }
3880
3755
  },
3881
3756
  {
3882
- name: "task-detail",
3883
- title: "Task Detail",
3884
- 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
+ name: "memory-recap",
3758
+ title: "Memory Recap",
3759
+ description: "AGGREGATED OVERVIEW LAYER: Returns stats (counts by type) and a pointer table of top memories [id, code, title, type, importance]. NO content. Use for orientation only \u2014 retrieve full memory via memory-detail.",
3760
+ annotations: {
3761
+ readOnlyHint: true,
3762
+ idempotentHint: true,
3763
+ openWorldHint: false
3764
+ },
3885
3765
  inputSchema: {
3886
3766
  type: "object",
3887
3767
  properties: {
3888
- repo: { type: "string", description: "Repository name" },
3889
- id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
3890
- task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
3768
+ repo: { type: "string", description: "Repository name (required)" },
3769
+ limit: {
3770
+ type: "number",
3771
+ minimum: 1,
3772
+ maximum: 50,
3773
+ default: 20,
3774
+ description: "Maximum number of top memories to return in the pointer table"
3775
+ },
3776
+ offset: {
3777
+ type: "number",
3778
+ minimum: 0,
3779
+ default: 0,
3780
+ description: "Number of memories to skip for pagination (optional, default 0)"
3781
+ },
3891
3782
  structured: {
3892
3783
  type: "boolean",
3893
3784
  default: false,
3894
- description: "If true, returns structured JSON without the text content details."
3785
+ description: "If true, returns structured JSON without the text content summary."
3895
3786
  }
3896
3787
  },
3897
3788
  required: ["repo"]
3789
+ },
3790
+ outputSchema: {
3791
+ type: "object",
3792
+ properties: {
3793
+ schema: { type: "string", enum: ["memory-recap"] },
3794
+ repo: { type: "string" },
3795
+ count: { type: "number", description: "Number of rows in the top pointer table" },
3796
+ total: { type: "number", description: "Total active memories in repo" },
3797
+ offset: { type: "number" },
3798
+ limit: { type: "number" },
3799
+ stats: {
3800
+ type: "object",
3801
+ properties: {
3802
+ byType: {
3803
+ type: "object",
3804
+ description: "Count of active memories per type (e.g. { decision: 3, code_fact: 7 })"
3805
+ }
3806
+ },
3807
+ required: ["byType"]
3808
+ },
3809
+ top: {
3810
+ type: "object",
3811
+ properties: {
3812
+ columns: {
3813
+ type: "array",
3814
+ items: { type: "string" },
3815
+ description: "Column names: [id, code, title, type, importance]"
3816
+ },
3817
+ rows: {
3818
+ type: "array",
3819
+ items: { type: "array" },
3820
+ description: "Each row: [id, code, title, type, importance]. Fetch full content via memory-detail"
3821
+ }
3822
+ },
3823
+ required: ["columns", "rows"]
3824
+ }
3825
+ },
3826
+ required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
3898
3827
  }
3899
3828
  },
3900
3829
  {
3901
- name: "memory-store",
3902
- title: "Memory Store",
3903
- 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'.",
3830
+ name: "task-create",
3831
+ title: "Task Create",
3832
+ 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.",
3904
3833
  annotations: {
3905
3834
  readOnlyHint: false,
3906
3835
  idempotentHint: false,
3907
- destructiveHint: false,
3908
3836
  openWorldHint: false
3909
3837
  },
3910
3838
  inputSchema: {
3911
3839
  type: "object",
3912
3840
  properties: {
3913
- type: {
3914
- type: "string",
3915
- enum: [
3916
- "code_fact",
3917
- "decision",
3918
- "mistake",
3919
- "pattern",
3920
- "task_archive"
3921
- ],
3922
- description: "Type of durable knowledge being stored. Coordination types such as file_claim are intentionally unsupported."
3923
- },
3841
+ repo: { type: "string", description: "Repository name" },
3842
+ task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
3843
+ phase: { type: "string", description: "Project phase (Required for single task)" },
3924
3844
  title: {
3925
3845
  type: "string",
3926
3846
  minLength: 3,
3927
3847
  maxLength: 100,
3928
- description: "Short human-readable title for the memory. Do not embed bracketed metadata like agent/role/date prefixes here."
3929
- },
3930
- content: {
3931
- type: "string",
3932
- minLength: 10,
3933
- description: "The memory content"
3934
- },
3935
- importance: {
3936
- type: "number",
3937
- minimum: 1,
3938
- maximum: 5,
3939
- description: "Importance score (1-5)"
3940
- },
3941
- agent: {
3942
- type: "string",
3943
- description: "Name of the agent creating this memory"
3944
- },
3945
- role: {
3946
- type: "string",
3947
- description: "Role of the agent creating this memory"
3848
+ description: "Task objective (Required for single task)"
3948
3849
  },
3949
- model: {
3850
+ description: { type: "string", description: "Detailed description (Required for single task)" },
3851
+ status: {
3950
3852
  type: "string",
3951
- description: "AI model used by the agent"
3952
- },
3953
- scope: {
3954
- type: "object",
3955
- properties: {
3956
- repo: { type: "string", description: "Repository name" },
3957
- branch: { type: "string" },
3958
- folder: { type: "string" },
3959
- language: { type: "string" }
3960
- },
3961
- required: ["repo"]
3853
+ enum: ["backlog", "pending"],
3854
+ default: "backlog",
3855
+ description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
3962
3856
  },
3963
- tags: {
3857
+ priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3858
+ agent: { type: "string" },
3859
+ role: { type: "string" },
3860
+ doc_path: { type: "string" },
3861
+ tags: { type: "array", items: { type: "string" } },
3862
+ metadata: { type: "object" },
3863
+ parent_id: { type: "string", format: "uuid" },
3864
+ depends_on: { type: "string", format: "uuid" },
3865
+ est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
3866
+ tasks: {
3964
3867
  type: "array",
3965
- items: { type: "string" },
3966
- description: "Technology stack tags (e.g., ['filament', 'laravel'])"
3967
- },
3968
- metadata: {
3969
- type: "object",
3970
- description: "Structured metadata for non-title context such as source agent, claim fields, or timestamps"
3971
- },
3972
- is_global: {
3973
- type: "boolean",
3974
- description: "If true, this memory is shared across all repositories"
3868
+ items: {
3869
+ type: "object",
3870
+ properties: {
3871
+ task_code: { type: "string" },
3872
+ phase: { type: "string" },
3873
+ title: { type: "string", minLength: 3, maxLength: 100 },
3874
+ description: { type: "string" },
3875
+ status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3876
+ priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3877
+ agent: { type: "string" },
3878
+ role: { type: "string" },
3879
+ doc_path: { type: "string" },
3880
+ tags: { type: "array", items: { type: "string" } },
3881
+ metadata: { type: "object" },
3882
+ parent_id: { type: "string", format: "uuid" },
3883
+ depends_on: { type: "string", format: "uuid" },
3884
+ est_tokens: { type: "number", minimum: 0 }
3885
+ },
3886
+ required: ["task_code", "phase", "title", "description"]
3887
+ },
3888
+ description: "Array of tasks for bulk creation"
3975
3889
  },
3976
- ttlDays: { type: "number", minimum: 1 },
3977
- supersedes: { type: "string", format: "uuid" },
3978
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the stored memory." }
3890
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3979
3891
  },
3980
- required: ["type", "title", "content", "importance", "scope", "agent", "model"]
3892
+ required: ["repo"]
3981
3893
  },
3982
3894
  outputSchema: {
3983
3895
  type: "object",
3984
3896
  properties: {
3985
3897
  success: { type: "boolean" },
3986
3898
  id: { type: "string" },
3987
- code: { type: "string" },
3899
+ task_code: { type: "string" },
3988
3900
  repo: { type: "string" },
3989
- type: { type: "string" },
3901
+ phase: { type: "string" },
3990
3902
  title: { type: "string" },
3991
- error: { type: "string" },
3992
- message: { type: "string" }
3903
+ status: { type: "string" },
3904
+ priority: { type: "number" },
3905
+ createdCount: { type: "number" },
3906
+ taskCodes: { type: "array", items: { type: "string" } }
3993
3907
  },
3994
- required: ["success"]
3908
+ required: ["success", "repo"]
3995
3909
  }
3996
3910
  },
3997
3911
  {
3998
- name: "memory-acknowledge",
3999
- title: "Memory Acknowledge",
4000
- description: "Acknowledge the use of a memory or report its irrelevance/contradiction. Mandatory after using memory to generate code.",
3912
+ name: "task-update",
3913
+ title: "Task Update",
3914
+ description: "Update one or more tasks. Supports single update via 'id' or bulk update via 'ids'. Provide only the fields that need to be changed. MANDATORY WORKFLOW: You cannot move a task from 'pending' or 'blocked' directly to 'completed'. You MUST move it to 'in_progress' first. When changing status to 'completed', include 'est_tokens' with the estimated total tokens actually used for the task.",
4001
3915
  annotations: {
4002
3916
  readOnlyHint: false,
4003
3917
  idempotentHint: false,
@@ -4006,80 +3920,98 @@ var TOOL_DEFINITIONS = [
4006
3920
  inputSchema: {
4007
3921
  type: "object",
4008
3922
  properties: {
4009
- memory_id: { type: "string", format: "uuid" },
4010
- status: { type: "string", enum: ["used", "irrelevant", "contradictory"] },
4011
- application_context: { type: "string", minLength: 10 },
3923
+ repo: { type: "string", description: "Repository name" },
3924
+ id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
3925
+ ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
3926
+ task_code: { type: "string" },
3927
+ phase: { type: "string" },
3928
+ title: { type: "string", minLength: 3, maxLength: 100 },
3929
+ description: { type: "string" },
3930
+ status: {
3931
+ type: "string",
3932
+ enum: ["backlog", "pending", "in_progress", "completed", "canceled", "blocked"],
3933
+ description: "New status. Transitions from 'backlog', 'pending' or 'blocked' to 'completed' are NOT allowed."
3934
+ },
3935
+ priority: { type: "number", minimum: 1, maximum: 5 },
3936
+ agent: { type: "string" },
3937
+ role: { type: "string" },
3938
+ model: { type: "string" },
3939
+ comment: {
3940
+ type: "string",
3941
+ description: "REQUIRED when changing task status. Explain WHY the status is changing (e.g., 'Starting implementation', 'Blocked by missing API docs', 'Verified fix')."
3942
+ },
3943
+ doc_path: { type: "string" },
3944
+ tags: { type: "array", items: { type: "string" } },
3945
+ metadata: { type: "object" },
3946
+ parent_id: { type: "string", format: "uuid" },
3947
+ depends_on: { type: "string", format: "uuid" },
3948
+ est_tokens: {
3949
+ type: "number",
3950
+ minimum: 0,
3951
+ description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
3952
+ },
3953
+ force: {
3954
+ type: "boolean",
3955
+ description: "If true, bypasses status transition validation (e.g. pending -> completed)."
3956
+ },
4012
3957
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4013
3958
  },
4014
- required: ["memory_id", "status"]
3959
+ required: ["repo"]
4015
3960
  },
4016
3961
  outputSchema: {
4017
3962
  type: "object",
4018
3963
  properties: {
4019
3964
  success: { type: "boolean" },
4020
3965
  id: { type: "string" },
4021
- status: { type: "string" }
3966
+ ids: { type: "array", items: { type: "string" } },
3967
+ repo: { type: "string" },
3968
+ status: { type: "string" },
3969
+ archivedToMemory: { type: "boolean" },
3970
+ updatedFields: {
3971
+ type: "array",
3972
+ items: { type: "string" }
3973
+ },
3974
+ updatedCount: { type: "number" }
4022
3975
  },
4023
- required: ["success", "id", "status"]
3976
+ required: ["success", "repo"]
4024
3977
  }
4025
3978
  },
4026
3979
  {
4027
- name: "memory-update",
4028
- title: "Memory Update",
4029
- description: "Update an existing memory entry. Keep 'title' concise and move agent/role/date or claim context into 'metadata' instead of the title.",
3980
+ name: "task-delete",
3981
+ title: "Task Delete",
3982
+ description: "Delete one or more tasks from a repository. Supports single 'id' or bulk 'ids'.",
4030
3983
  annotations: {
4031
3984
  readOnlyHint: false,
4032
3985
  idempotentHint: false,
4033
- destructiveHint: false,
3986
+ destructiveHint: true,
4034
3987
  openWorldHint: false
4035
3988
  },
4036
3989
  inputSchema: {
4037
3990
  type: "object",
4038
3991
  properties: {
4039
- id: { type: "string", format: "uuid" },
4040
- type: {
4041
- type: "string",
4042
- enum: [
4043
- "code_fact",
4044
- "decision",
4045
- "mistake",
4046
- "pattern",
4047
- "task_archive"
4048
- ]
4049
- },
4050
- title: { type: "string", minLength: 3, maxLength: 100 },
4051
- content: { type: "string", minLength: 10 },
4052
- importance: { type: "number", minimum: 1, maximum: 5 },
4053
- agent: { type: "string" },
4054
- role: { type: "string" },
4055
- status: { type: "string", enum: ["active", "archived"] },
4056
- supersedes: { type: "string", format: "uuid" },
4057
- tags: { type: "array", items: { type: "string" } },
4058
- metadata: { type: "object" },
4059
- is_global: { type: "boolean" },
4060
- completed_at: { type: "string" },
4061
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the updated memory." }
3992
+ repo: { type: "string", description: "Repository name" },
3993
+ id: { type: "string", format: "uuid", description: "Task ID (for single deletion)" },
3994
+ ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk deletion)" },
3995
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4062
3996
  },
4063
- required: ["id"]
3997
+ required: ["repo"]
4064
3998
  },
4065
3999
  outputSchema: {
4066
4000
  type: "object",
4067
4001
  properties: {
4068
4002
  success: { type: "boolean" },
4069
4003
  id: { type: "string" },
4004
+ ids: { type: "array", items: { type: "string" } },
4070
4005
  repo: { type: "string" },
4071
- updatedFields: {
4072
- type: "array",
4073
- items: { type: "string" }
4074
- }
4006
+ deletedCount: { type: "number" }
4075
4007
  },
4076
- required: ["success", "id", "repo", "updatedFields"]
4008
+ required: ["success", "repo"]
4077
4009
  }
4078
4010
  },
4079
4011
  {
4080
- name: "memory-search",
4081
- title: "Memory Search",
4082
- description: "NAVIGATION LAYER: Returns a pointer table of matching memory IDs only. Returns columns [id, title, type, importance] \u2014 NO content. Retrieve full memory via memory-detail. Use 'current_tags' to find tech-stack specific knowledge from other projects.",
4012
+ name: "task-list",
4013
+ title: "Task List",
4014
+ description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority, updated_at, comments_count). Defaults to in_progress and pending tasks. Use 'query' to filter by code, title, or description. Use 'status' (comma-separated) for specific filters. AGENTS: call this once at start, pick ONE task, then call task-detail.",
4083
4015
  annotations: {
4084
4016
  readOnlyHint: true,
4085
4017
  idempotentHint: true,
@@ -4088,41 +4020,35 @@ var TOOL_DEFINITIONS = [
4088
4020
  inputSchema: {
4089
4021
  type: "object",
4090
4022
  properties: {
4091
- query: { type: "string", minLength: 3 },
4092
- prompt: { type: "string" },
4093
- repo: { type: "string" },
4094
- current_tags: {
4095
- type: "array",
4096
- items: { type: "string" },
4097
- description: "Active tech stack tags (e.g., ['filament', 'react'])"
4023
+ repo: {
4024
+ type: "string",
4025
+ description: "Repository name"
4098
4026
  },
4099
- types: {
4100
- type: "array",
4101
- items: {
4102
- type: "string",
4103
- enum: [
4104
- "code_fact",
4105
- "decision",
4106
- "mistake",
4107
- "pattern",
4108
- "task_archive"
4109
- ]
4110
- }
4027
+ status: {
4028
+ type: "string",
4029
+ default: "in_progress,pending",
4030
+ description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,pending'."
4111
4031
  },
4112
- minImportance: { type: "number", minimum: 1, maximum: 5 },
4113
- limit: { type: "number", minimum: 1, maximum: 100, default: 5 },
4114
- offset: { type: "number", minimum: 0, default: 0 },
4115
- includeRecap: { type: "boolean", default: false },
4116
- current_file_path: { type: "string" },
4117
- include_archived: { type: "boolean", default: false },
4118
- scope: {
4119
- type: "object",
4120
- properties: {
4121
- repo: { type: "string" },
4122
- branch: { type: "string" },
4123
- folder: { type: "string" },
4124
- language: { type: "string" }
4125
- }
4032
+ phase: {
4033
+ type: "string",
4034
+ description: "Filter by phase (e.g., 'research', 'implementation')"
4035
+ },
4036
+ query: {
4037
+ type: "string",
4038
+ description: "Search keyword matching task code, title, or description"
4039
+ },
4040
+ limit: {
4041
+ type: "number",
4042
+ minimum: 1,
4043
+ maximum: 100,
4044
+ default: 5,
4045
+ description: "Maximum rows to return (default 5)"
4046
+ },
4047
+ offset: {
4048
+ type: "number",
4049
+ minimum: 0,
4050
+ default: 0,
4051
+ description: "Offset for pagination"
4126
4052
  },
4127
4053
  structured: {
4128
4054
  type: "boolean",
@@ -4130,402 +4056,328 @@ var TOOL_DEFINITIONS = [
4130
4056
  description: "If true, returns structured JSON without the text content summary."
4131
4057
  }
4132
4058
  },
4133
- required: ["query", "repo"]
4059
+ required: ["repo"]
4134
4060
  },
4135
4061
  outputSchema: {
4136
4062
  type: "object",
4137
4063
  properties: {
4138
- schema: { type: "string", enum: ["memory-search"] },
4139
- query: { type: "string" },
4140
- count: { type: "number", description: "Number of rows returned" },
4141
- total: { type: "number", description: "Total matching memories" },
4142
- offset: { type: "number" },
4143
- limit: { type: "number" },
4144
- results: {
4064
+ schema: { type: "string", enum: ["task-list"] },
4065
+ tasks: {
4145
4066
  type: "object",
4146
4067
  properties: {
4147
4068
  columns: {
4148
4069
  type: "array",
4149
4070
  items: { type: "string" },
4150
- description: "Column names: [id, title, type, importance]"
4071
+ description: "Column names in order: id, task_code, title, status, priority, updated_at, comments_count"
4151
4072
  },
4152
4073
  rows: {
4153
4074
  type: "array",
4154
4075
  items: { type: "array" },
4155
- description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
4076
+ description: "Each row: [id, task_code, title, status, priority, updated_at, comments_count]. Use task-detail to fetch full task."
4156
4077
  }
4157
4078
  },
4158
4079
  required: ["columns", "rows"]
4159
- }
4080
+ },
4081
+ count: { type: "number" },
4082
+ offset: { type: "number" }
4160
4083
  },
4161
- required: ["schema", "query", "count", "total", "offset", "limit", "results"]
4084
+ required: ["schema", "tasks", "count"]
4162
4085
  }
4163
4086
  },
4164
4087
  {
4165
- name: "memory-summarize",
4166
- title: "Memory Summarize",
4167
- description: "Update the summary for a repository",
4088
+ name: "handoff-create",
4089
+ title: "Handoff Create",
4090
+ description: "Create a pending handoff only when unfinished work needs context transfer between agents. Do not use this for completed-work summaries, release notes, validation notes, or archives; put those on task-update/task comments or durable memory.",
4168
4091
  annotations: {
4169
4092
  readOnlyHint: false,
4170
4093
  idempotentHint: false,
4094
+ destructiveHint: false,
4171
4095
  openWorldHint: false
4172
4096
  },
4173
4097
  inputSchema: {
4174
4098
  type: "object",
4175
4099
  properties: {
4176
4100
  repo: { type: "string", description: "Repository name" },
4177
- signals: {
4178
- type: "array",
4179
- items: { type: "string", maxLength: 200 },
4180
- minItems: 1,
4181
- description: "High-level signals to include in summary"
4101
+ from_agent: { type: "string", description: "Agent creating the handoff" },
4102
+ to_agent: { type: "string", description: "Optional target agent" },
4103
+ task_id: { type: "string", format: "uuid", description: "Optional task id to associate" },
4104
+ task_code: { type: "string", description: "Optional task code to associate" },
4105
+ summary: { type: "string", minLength: 1, description: "Concise human-readable transfer summary" },
4106
+ context: {
4107
+ type: "object",
4108
+ description: "Structured handoff context. Include next_steps, blockers, or remaining_work unless a target agent or task is provided."
4182
4109
  },
4183
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the summary." }
4110
+ expires_at: { type: "string", description: "Optional expiration timestamp" },
4111
+ structured: { type: "boolean", default: false }
4184
4112
  },
4185
- required: ["repo", "signals"]
4113
+ required: ["repo", "from_agent", "summary"]
4186
4114
  },
4187
4115
  outputSchema: {
4188
4116
  type: "object",
4189
4117
  properties: {
4190
- success: { type: "boolean" },
4118
+ id: { type: "string" },
4191
4119
  repo: { type: "string" },
4120
+ from_agent: { type: "string" },
4121
+ to_agent: { type: "string", nullable: true },
4122
+ task_id: { type: "string", nullable: true },
4192
4123
  summary: { type: "string" },
4193
- signalCount: { type: "number" }
4124
+ context: { type: "object" },
4125
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4126
+ created_at: { type: "string" },
4127
+ updated_at: { type: "string" },
4128
+ expires_at: { type: "string", nullable: true }
4194
4129
  },
4195
- required: ["success", "repo", "summary", "signalCount"]
4130
+ required: ["id", "repo", "from_agent", "summary", "context", "status", "created_at", "updated_at"]
4196
4131
  }
4197
4132
  },
4198
4133
  {
4199
- name: "memory-delete",
4200
- title: "Memory Delete",
4201
- description: "Soft-delete one or more memory entries. Supports single 'id' or bulk 'ids'.",
4134
+ name: "handoff-update",
4135
+ title: "Handoff Update",
4136
+ description: "Close or reclassify a handoff after it has been consumed or found stale. Use accepted when transfer context was consumed, rejected when intentionally declined, and expired when the handoff is obsolete or only described completed work.",
4202
4137
  annotations: {
4203
4138
  readOnlyHint: false,
4204
4139
  idempotentHint: false,
4205
- destructiveHint: true,
4140
+ destructiveHint: false,
4206
4141
  openWorldHint: false
4207
4142
  },
4208
4143
  inputSchema: {
4209
4144
  type: "object",
4210
4145
  properties: {
4211
- repo: { type: "string", description: "Repository name (optional for single id)" },
4212
- id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
4213
- ids: {
4214
- type: "array",
4215
- items: { type: "string", format: "uuid" },
4216
- minItems: 1,
4217
- description: "Array of memory IDs to delete"
4218
- },
4219
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4220
- }
4221
- },
4222
- outputSchema: {
4223
- type: "object",
4224
- properties: {
4225
- success: { type: "boolean" },
4226
- id: { type: "string" },
4227
- ids: { type: "array", items: { type: "string" } },
4228
- repo: { type: "string" },
4229
- deletedCount: { type: "number" }
4146
+ id: { type: "string", format: "uuid", description: "Handoff ID" },
4147
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4148
+ structured: { type: "boolean", default: false }
4230
4149
  },
4231
- required: ["success"]
4232
- }
4233
- },
4234
- {
4235
- name: "standard-delete",
4236
- title: "Standard Delete",
4237
- description: "Delete one or more coding standards. Supports single 'id' or bulk 'ids'.",
4238
- annotations: {
4239
- readOnlyHint: false,
4240
- idempotentHint: false,
4241
- destructiveHint: true,
4242
- openWorldHint: false
4243
- },
4244
- inputSchema: {
4245
- type: "object",
4246
- properties: {
4247
- repo: { type: "string", description: "Repository name (optional for single id)" },
4248
- id: { type: "string", format: "uuid", description: "Coding standard ID to delete" },
4249
- ids: {
4250
- type: "array",
4251
- items: { type: "string", format: "uuid" },
4252
- minItems: 1,
4253
- description: "Array of coding standard IDs to delete"
4254
- },
4255
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4256
- }
4150
+ required: ["id", "status"]
4257
4151
  },
4258
4152
  outputSchema: {
4259
4153
  type: "object",
4260
4154
  properties: {
4261
4155
  success: { type: "boolean" },
4262
4156
  id: { type: "string" },
4263
- ids: { type: "array", items: { type: "string" } },
4264
- repo: { type: "string" },
4265
- deletedCount: { type: "number" }
4157
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] }
4266
4158
  },
4267
- required: ["success"]
4159
+ required: ["success", "id", "status"]
4268
4160
  }
4269
4161
  },
4270
4162
  {
4271
- name: "memory-recap",
4272
- title: "Memory Recap",
4273
- description: "AGGREGATED OVERVIEW LAYER: Returns stats (counts by type) and a pointer table of top memories [id, code, title, type, importance]. NO content. Use for orientation only \u2014 retrieve full memory via memory-detail.",
4163
+ name: "handoff-list",
4164
+ title: "Handoff List",
4165
+ description: "Navigation layer for handoff queues. List repository handoffs with optional status and agent filters, then inspect selected rows before acting.",
4274
4166
  annotations: {
4275
4167
  readOnlyHint: true,
4276
4168
  idempotentHint: true,
4277
4169
  openWorldHint: false
4278
- },
4279
- inputSchema: {
4280
- type: "object",
4281
- properties: {
4282
- repo: { type: "string", description: "Repository name (required)" },
4283
- limit: {
4284
- type: "number",
4285
- minimum: 1,
4286
- maximum: 50,
4287
- default: 20,
4288
- description: "Maximum number of top memories to return in the pointer table"
4289
- },
4290
- offset: {
4291
- type: "number",
4292
- minimum: 0,
4293
- default: 0,
4294
- description: "Number of memories to skip for pagination (optional, default 0)"
4295
- },
4296
- structured: {
4297
- type: "boolean",
4298
- default: false,
4299
- description: "If true, returns structured JSON without the text content summary."
4300
- }
4170
+ },
4171
+ inputSchema: {
4172
+ type: "object",
4173
+ properties: {
4174
+ repo: { type: "string", description: "Repository name" },
4175
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4176
+ from_agent: { type: "string" },
4177
+ to_agent: { type: "string" },
4178
+ limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4179
+ offset: { type: "number", minimum: 0, default: 0 },
4180
+ structured: { type: "boolean", default: false }
4301
4181
  },
4302
4182
  required: ["repo"]
4303
4183
  },
4304
4184
  outputSchema: {
4305
4185
  type: "object",
4306
4186
  properties: {
4307
- schema: { type: "string", enum: ["memory-recap"] },
4308
- repo: { type: "string" },
4309
- count: { type: "number", description: "Number of rows in the top pointer table" },
4310
- total: { type: "number", description: "Total active memories in repo" },
4311
- offset: { type: "number" },
4312
- limit: { type: "number" },
4313
- stats: {
4314
- type: "object",
4315
- properties: {
4316
- byType: {
4317
- type: "object",
4318
- description: "Count of active memories per type (e.g. { decision: 3, code_fact: 7 })"
4319
- }
4320
- },
4321
- required: ["byType"]
4322
- },
4323
- top: {
4187
+ schema: { type: "string", enum: ["handoff-list"] },
4188
+ handoffs: {
4324
4189
  type: "object",
4325
4190
  properties: {
4326
4191
  columns: {
4327
4192
  type: "array",
4328
4193
  items: { type: "string" },
4329
- description: "Column names: [id, code, title, type, importance]"
4194
+ description: "Column names: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4330
4195
  },
4331
4196
  rows: {
4332
4197
  type: "array",
4333
4198
  items: { type: "array" },
4334
- description: "Each row: [id, code, title, type, importance]. Fetch full content via memory-detail"
4199
+ description: "Each row: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4335
4200
  }
4336
4201
  },
4337
4202
  required: ["columns", "rows"]
4338
- }
4203
+ },
4204
+ count: { type: "number" },
4205
+ offset: { type: "number" }
4339
4206
  },
4340
- required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
4207
+ required: ["schema", "handoffs", "count", "offset"]
4341
4208
  }
4342
4209
  },
4343
4210
  {
4344
- name: "task-create",
4345
- title: "Task Create",
4346
- 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.",
4211
+ name: "task-claim",
4212
+ title: "Task Claim",
4213
+ description: "Claim task ownership for an agent using the dedicated claims table. Use this before taking work from task-list; provide either task_id or task_code.",
4347
4214
  annotations: {
4348
4215
  readOnlyHint: false,
4349
4216
  idempotentHint: false,
4217
+ destructiveHint: false,
4350
4218
  openWorldHint: false
4351
4219
  },
4352
4220
  inputSchema: {
4353
4221
  type: "object",
4354
4222
  properties: {
4355
4223
  repo: { type: "string", description: "Repository name" },
4356
- task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
4357
- phase: { type: "string", description: "Project phase (Required for single task)" },
4358
- title: {
4359
- type: "string",
4360
- minLength: 3,
4361
- maxLength: 100,
4362
- description: "Task objective (Required for single task)"
4363
- },
4364
- description: { type: "string", description: "Detailed description (Required for single task)" },
4365
- status: {
4366
- type: "string",
4367
- enum: ["backlog", "pending"],
4368
- default: "backlog",
4369
- description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
4370
- },
4371
- priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
4372
- agent: { type: "string" },
4373
- role: { type: "string" },
4374
- doc_path: { type: "string" },
4375
- tags: { type: "array", items: { type: "string" } },
4376
- metadata: { type: "object" },
4377
- parent_id: { type: "string", format: "uuid" },
4378
- depends_on: { type: "string", format: "uuid" },
4379
- est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
4380
- tasks: {
4381
- type: "array",
4382
- items: {
4383
- type: "object",
4384
- properties: {
4385
- task_code: { type: "string" },
4386
- phase: { type: "string" },
4387
- title: { type: "string", minLength: 3, maxLength: 100 },
4388
- description: { type: "string" },
4389
- status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
4390
- priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
4391
- agent: { type: "string" },
4392
- role: { type: "string" },
4393
- doc_path: { type: "string" },
4394
- tags: { type: "array", items: { type: "string" } },
4395
- metadata: { type: "object" },
4396
- parent_id: { type: "string", format: "uuid" },
4397
- depends_on: { type: "string", format: "uuid" },
4398
- est_tokens: { type: "number", minimum: 0 }
4399
- },
4400
- required: ["task_code", "phase", "title", "description"]
4401
- },
4402
- description: "Array of tasks for bulk creation"
4403
- },
4404
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4224
+ task_id: { type: "string", format: "uuid", description: "Task id to claim. Optional if task_code is provided." },
4225
+ task_code: { type: "string", description: "Task code to claim. Optional if task_id is provided." },
4226
+ agent: { type: "string", description: "Claiming agent name" },
4227
+ role: { type: "string", description: "Claiming agent role" },
4228
+ metadata: { type: "object", description: "Optional claim metadata" },
4229
+ structured: { type: "boolean", default: false }
4405
4230
  },
4406
- required: ["repo"]
4231
+ required: ["repo", "agent"]
4407
4232
  },
4408
4233
  outputSchema: {
4409
4234
  type: "object",
4410
4235
  properties: {
4411
- success: { type: "boolean" },
4412
4236
  id: { type: "string" },
4413
- task_code: { type: "string" },
4414
4237
  repo: { type: "string" },
4415
- phase: { type: "string" },
4416
- title: { type: "string" },
4417
- status: { type: "string" },
4418
- priority: { type: "number" },
4419
- createdCount: { type: "number" },
4420
- taskCodes: { type: "array", items: { type: "string" } }
4238
+ task_id: { type: "string" },
4239
+ task_code: { type: "string", nullable: true },
4240
+ agent: { type: "string" },
4241
+ role: { type: "string" },
4242
+ claimed_at: { type: "string" },
4243
+ released_at: { type: "string", nullable: true },
4244
+ metadata: { type: "object" }
4421
4245
  },
4422
- required: ["success", "repo"]
4246
+ required: ["id", "repo", "task_id", "agent", "role", "claimed_at", "metadata"]
4423
4247
  }
4424
4248
  },
4425
4249
  {
4426
- name: "task-update",
4427
- title: "Task Update",
4428
- description: "Update one or more tasks. Supports single update via 'id' or bulk update via 'ids'. Provide only the fields that need to be changed. MANDATORY WORKFLOW: You cannot move a task from 'pending' or 'blocked' directly to 'completed'. You MUST move it to 'in_progress' first. When changing status to 'completed', include 'est_tokens' with the estimated total tokens actually used for the task.",
4250
+ name: "standard-store",
4251
+ title: "Standard Store",
4252
+ description: "Store one atomic coding standard. Use for durable implementation rules with explicit context, stack/language filters, and repo/global scope.",
4429
4253
  annotations: {
4430
4254
  readOnlyHint: false,
4431
4255
  idempotentHint: false,
4256
+ destructiveHint: false,
4432
4257
  openWorldHint: false
4433
4258
  },
4434
4259
  inputSchema: {
4435
4260
  type: "object",
4436
4261
  properties: {
4437
- repo: { type: "string", description: "Repository name" },
4438
- id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
4439
- ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
4440
- task_code: { type: "string" },
4441
- phase: { type: "string" },
4442
- title: { type: "string", minLength: 3, maxLength: 100 },
4443
- description: { type: "string" },
4444
- status: {
4445
- type: "string",
4446
- enum: ["backlog", "pending", "in_progress", "completed", "canceled", "blocked"],
4447
- description: "New status. Transitions from 'backlog', 'pending' or 'blocked' to 'completed' are NOT allowed."
4448
- },
4449
- priority: { type: "number", minimum: 1, maximum: 5 },
4450
- agent: { type: "string" },
4451
- role: { type: "string" },
4452
- model: { type: "string" },
4453
- comment: {
4454
- type: "string",
4455
- description: "REQUIRED when changing task status. Explain WHY the status is changing (e.g., 'Starting implementation', 'Blocked by missing API docs', 'Verified fix')."
4262
+ name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
4263
+ content: { type: "string", minLength: 10, description: "One atomic, actionable standard written as concise Markdown" },
4264
+ parent_id: { type: "string", format: "uuid", description: "Optional parent standard ID when this rule is a child/specialization." },
4265
+ context: { type: "string", description: "Context or category (e.g., 'error-handling', 'security')" },
4266
+ version: { type: "string", description: "Version of the standard (e.g., '1.0.0')" },
4267
+ language: { type: "string", description: "Programming language (e.g., 'typescript', 'python')" },
4268
+ stack: {
4269
+ type: "array",
4270
+ items: { type: "string" },
4271
+ description: "Technology stack (e.g., ['react', 'nextjs'])"
4456
4272
  },
4457
- doc_path: { type: "string" },
4458
- tags: { type: "array", items: { type: "string" } },
4459
- metadata: { type: "object" },
4460
- parent_id: { type: "string", format: "uuid" },
4461
- depends_on: { type: "string", format: "uuid" },
4462
- est_tokens: {
4463
- type: "number",
4464
- minimum: 0,
4465
- description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
4273
+ repo: { type: "string", description: "Repository name for repo-specific standards. Omit only for global standards." },
4274
+ is_global: { type: "boolean", description: "Whether standard applies globally or repo-specific" },
4275
+ tags: {
4276
+ type: "array",
4277
+ items: { type: "string" },
4278
+ description: "Tags for categorization"
4466
4279
  },
4467
- force: {
4468
- type: "boolean",
4469
- description: "If true, bypasses status transition validation (e.g. pending -> completed)."
4280
+ metadata: {
4281
+ type: "object",
4282
+ description: "Additional metadata"
4470
4283
  },
4471
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4284
+ agent: { type: "string", description: "Agent creating the standard" },
4285
+ model: { type: "string", description: "AI model used" },
4286
+ structured: { type: "boolean", default: false }
4472
4287
  },
4473
- required: ["repo"]
4288
+ required: ["name", "content", "tags", "metadata"]
4474
4289
  },
4475
4290
  outputSchema: {
4476
4291
  type: "object",
4477
4292
  properties: {
4478
4293
  success: { type: "boolean" },
4479
- id: { type: "string" },
4480
- ids: { type: "array", items: { type: "string" } },
4481
- repo: { type: "string" },
4482
- status: { type: "string" },
4483
- archivedToMemory: { type: "boolean" },
4484
- updatedFields: {
4485
- type: "array",
4486
- items: { type: "string" }
4294
+ standard: {
4295
+ type: "object",
4296
+ properties: {
4297
+ id: { type: "string" },
4298
+ title: { type: "string" },
4299
+ content: { type: "string" },
4300
+ parent_id: { type: "string", nullable: true },
4301
+ context: { type: "string" },
4302
+ version: { type: "string" },
4303
+ language: { type: "string", nullable: true },
4304
+ stack: { type: "array", items: { type: "string" } },
4305
+ is_global: { type: "boolean" },
4306
+ repo: { type: "string", nullable: true },
4307
+ tags: { type: "array", items: { type: "string" } },
4308
+ metadata: { type: "object" },
4309
+ created_at: { type: "string" },
4310
+ updated_at: { type: "string" },
4311
+ agent: { type: "string" },
4312
+ model: { type: "string" }
4313
+ },
4314
+ required: [
4315
+ "id",
4316
+ "title",
4317
+ "content",
4318
+ "parent_id",
4319
+ "context",
4320
+ "version",
4321
+ "stack",
4322
+ "is_global",
4323
+ "tags",
4324
+ "metadata",
4325
+ "created_at",
4326
+ "updated_at",
4327
+ "agent",
4328
+ "model"
4329
+ ]
4487
4330
  },
4488
- updatedCount: { type: "number" }
4331
+ message: { type: "string" }
4489
4332
  },
4490
- required: ["success", "repo"]
4333
+ required: ["success", "standard", "message"]
4491
4334
  }
4492
4335
  },
4493
4336
  {
4494
- name: "task-delete",
4495
- title: "Task Delete",
4496
- description: "Delete one or more tasks from a repository. Supports single 'id' or bulk 'ids'.",
4337
+ name: "standard-update",
4338
+ title: "Standard Update",
4339
+ description: "Update an existing coding standard. Use this when the rule changes, expands scope, or metadata/tags need correction.",
4497
4340
  annotations: {
4498
4341
  readOnlyHint: false,
4499
4342
  idempotentHint: false,
4500
- destructiveHint: true,
4343
+ destructiveHint: false,
4501
4344
  openWorldHint: false
4502
4345
  },
4503
4346
  inputSchema: {
4504
4347
  type: "object",
4505
4348
  properties: {
4506
- repo: { type: "string", description: "Repository name" },
4507
- id: { type: "string", format: "uuid", description: "Task ID (for single deletion)" },
4508
- ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk deletion)" },
4509
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4349
+ id: { type: "string", description: "Standard ID to update" },
4350
+ name: { type: "string", minLength: 3, maxLength: 255 },
4351
+ content: { type: "string", minLength: 10 },
4352
+ parent_id: { type: "string", format: "uuid", nullable: true },
4353
+ context: { type: "string" },
4354
+ version: { type: "string" },
4355
+ language: { type: "string" },
4356
+ stack: { type: "array", items: { type: "string" } },
4357
+ repo: { type: "string" },
4358
+ is_global: { type: "boolean" },
4359
+ tags: { type: "array", items: { type: "string" } },
4360
+ metadata: { type: "object" },
4361
+ agent: { type: "string" },
4362
+ model: { type: "string" },
4363
+ structured: { type: "boolean", default: false }
4510
4364
  },
4511
- required: ["repo"]
4365
+ required: ["id"]
4512
4366
  },
4513
4367
  outputSchema: {
4514
4368
  type: "object",
4515
4369
  properties: {
4516
4370
  success: { type: "boolean" },
4517
4371
  id: { type: "string" },
4518
- ids: { type: "array", items: { type: "string" } },
4519
- repo: { type: "string" },
4520
- deletedCount: { type: "number" }
4372
+ updatedFields: { type: "array", items: { type: "string" } }
4521
4373
  },
4522
- required: ["success", "repo"]
4374
+ required: ["success", "id", "updatedFields"]
4523
4375
  }
4524
4376
  },
4525
4377
  {
4526
- name: "task-list",
4527
- title: "Task List",
4528
- description: "PRIMARY navigation and search tool for tasks. Returns a compact tabular list of tasks (id, task_code, title, status, priority, updated_at, comments_count). Defaults to in_progress and pending tasks. Use 'query' to filter by code, title, or description. Use 'status' (comma-separated) for specific filters. AGENTS: call this once at start, pick ONE task, then call task-detail.",
4378
+ name: "standard-search",
4379
+ title: "Standard Search",
4380
+ description: "NAVIGATION LAYER: Returns a compact pointer table of matching coding standards. Use `standard-detail` to fetch full content for a selected result.",
4529
4381
  annotations: {
4530
4382
  readOnlyHint: true,
4531
4383
  idempotentHint: true,
@@ -4534,425 +4386,628 @@ var TOOL_DEFINITIONS = [
4534
4386
  inputSchema: {
4535
4387
  type: "object",
4536
4388
  properties: {
4537
- repo: {
4538
- type: "string",
4539
- description: "Repository name"
4540
- },
4541
- status: {
4542
- type: "string",
4543
- default: "in_progress,pending",
4544
- description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,pending'."
4545
- },
4546
- phase: {
4547
- type: "string",
4548
- description: "Filter by phase (e.g., 'research', 'implementation')"
4549
- },
4550
- query: {
4551
- type: "string",
4552
- description: "Search keyword matching task code, title, or description"
4553
- },
4554
- limit: {
4555
- type: "number",
4556
- minimum: 1,
4557
- maximum: 100,
4558
- default: 5,
4559
- description: "Maximum rows to return (default 5)"
4389
+ query: { type: "string", description: "Search query (optional, searches title/content)" },
4390
+ stack: {
4391
+ type: "array",
4392
+ items: { type: "string" },
4393
+ description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
4560
4394
  },
4561
- offset: {
4562
- type: "number",
4563
- minimum: 0,
4564
- default: 0,
4565
- description: "Offset for pagination"
4395
+ tags: {
4396
+ type: "array",
4397
+ items: { type: "string" },
4398
+ description: "Tag filter"
4566
4399
  },
4567
- structured: {
4568
- type: "boolean",
4569
- default: false,
4570
- description: "If true, returns structured JSON without the text content summary."
4571
- }
4400
+ language: { type: "string", description: "Programming language filter" },
4401
+ context: { type: "string", description: "Context/category filter" },
4402
+ version: { type: "string", description: "Version filter" },
4403
+ repo: { type: "string", description: "Repository filter (optional)" },
4404
+ is_global: { type: "boolean", description: "Filter by global/repo-specific" },
4405
+ limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4406
+ offset: { type: "number", minimum: 0, default: 0 },
4407
+ structured: { type: "boolean", default: false }
4572
4408
  },
4573
- required: ["repo"]
4409
+ required: []
4574
4410
  },
4575
4411
  outputSchema: {
4576
4412
  type: "object",
4577
4413
  properties: {
4578
- schema: { type: "string", enum: ["task-list"] },
4579
- tasks: {
4414
+ schema: { type: "string", enum: ["standard-search"] },
4415
+ query: { type: "string" },
4416
+ count: { type: "number", description: "Number of rows returned" },
4417
+ total: { type: "number", description: "Total number of matches before pagination" },
4418
+ offset: { type: "number" },
4419
+ limit: { type: "number" },
4420
+ results: {
4580
4421
  type: "object",
4581
4422
  properties: {
4582
4423
  columns: {
4583
4424
  type: "array",
4584
- items: { type: "string" },
4585
- description: "Column names in order: id, task_code, title, status, priority, updated_at, comments_count"
4425
+ items: { type: "string" }
4586
4426
  },
4587
4427
  rows: {
4588
4428
  type: "array",
4589
4429
  items: { type: "array" },
4590
- description: "Each row: [id, task_code, title, status, priority, updated_at, comments_count]. Use task-detail to fetch full task."
4430
+ description: "Each row includes standard id and pointer metadata. Fetch full content via standard-detail."
4591
4431
  }
4592
4432
  },
4593
4433
  required: ["columns", "rows"]
4594
- },
4595
- count: { type: "number" },
4596
- offset: { type: "number" }
4434
+ }
4597
4435
  },
4598
- required: ["schema", "tasks", "count"]
4436
+ required: ["schema", "query", "count", "total", "offset", "limit", "results"]
4599
4437
  }
4600
- },
4601
- {
4602
- name: "handoff-create",
4603
- title: "Handoff Create",
4604
- description: "Create a pending handoff only when unfinished work needs context transfer between agents. Do not use this for completed-work summaries, release notes, validation notes, or archives; put those on task-update/task comments or durable memory.",
4605
- annotations: {
4606
- readOnlyHint: false,
4607
- idempotentHint: false,
4608
- destructiveHint: false,
4609
- openWorldHint: false
4610
- },
4611
- inputSchema: {
4612
- type: "object",
4613
- properties: {
4614
- repo: { type: "string", description: "Repository name" },
4615
- from_agent: { type: "string", description: "Agent creating the handoff" },
4616
- to_agent: { type: "string", description: "Optional target agent" },
4617
- task_id: { type: "string", format: "uuid", description: "Optional task id to associate" },
4618
- task_code: { type: "string", description: "Optional task code to associate" },
4619
- summary: { type: "string", minLength: 1, description: "Concise human-readable transfer summary" },
4620
- context: {
4621
- type: "object",
4622
- description: "Structured handoff context. Include next_steps, blockers, or remaining_work unless a target agent or task is provided."
4623
- },
4624
- expires_at: { type: "string", description: "Optional expiration timestamp" },
4625
- structured: { type: "boolean", default: false }
4626
- },
4627
- required: ["repo", "from_agent", "summary"]
4438
+ }
4439
+ ];
4440
+
4441
+ // src/mcp/utils/pagination.ts
4442
+ function encodeCursor(offset) {
4443
+ return Buffer.from(String(offset), "utf8").toString("base64");
4444
+ }
4445
+ function decodeCursor(cursor) {
4446
+ if (cursor === void 0 || cursor === null || cursor === "") {
4447
+ return 0;
4448
+ }
4449
+ if (typeof cursor !== "string" || cursor.trim() === "") {
4450
+ throw invalidPaginationParams("Invalid cursor");
4451
+ }
4452
+ let decoded;
4453
+ try {
4454
+ decoded = Buffer.from(cursor, "base64").toString("utf8");
4455
+ } catch {
4456
+ throw invalidPaginationParams("Invalid cursor");
4457
+ }
4458
+ if (!/^\d+$/.test(decoded)) {
4459
+ throw invalidPaginationParams("Invalid cursor");
4460
+ }
4461
+ const offset = Number.parseInt(decoded, 10);
4462
+ if (!Number.isFinite(offset) || offset < 0) {
4463
+ throw invalidPaginationParams("Invalid cursor");
4464
+ }
4465
+ return offset;
4466
+ }
4467
+ function invalidPaginationParams(message) {
4468
+ const error = new Error(message);
4469
+ error.code = -32602;
4470
+ return error;
4471
+ }
4472
+
4473
+ // src/mcp/utils/completion.ts
4474
+ var MAX_COMPLETION_VALUES = 100;
4475
+ function rankCompletionValues(candidates, input) {
4476
+ const unique = [...new Set(candidates.filter(Boolean))];
4477
+ const needle = input.trim().toLowerCase();
4478
+ if (!needle) {
4479
+ return unique.slice(0, MAX_COMPLETION_VALUES);
4480
+ }
4481
+ 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);
4482
+ }
4483
+ function scoreCompletionValue(value, needle) {
4484
+ const haystack = value.toLowerCase();
4485
+ if (haystack === needle) return 100;
4486
+ if (haystack.startsWith(needle)) return 75;
4487
+ if (haystack.includes(needle)) return 50;
4488
+ const compactNeedle = needle.replace(/[\s_-]+/g, "");
4489
+ const compactHaystack = haystack.replace(/[\s_-]+/g, "");
4490
+ if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
4491
+ return 0;
4492
+ }
4493
+
4494
+ // src/mcp/resources/index.ts
4495
+ var DEFAULT_PAGE_SIZE = 25;
4496
+ var MAX_PAGE_SIZE = 100;
4497
+ function listResources(session, params) {
4498
+ const resources = [
4499
+ {
4500
+ uri: "repository://index",
4501
+ name: "Repository Index",
4502
+ title: "Repository Index",
4503
+ description: "List of all known repositories with memory/task counts and last activity",
4504
+ mimeType: "application/json",
4505
+ annotations: {
4506
+ audience: ["assistant"],
4507
+ priority: 1,
4508
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4509
+ }
4628
4510
  },
4629
- outputSchema: {
4630
- type: "object",
4631
- properties: {
4632
- id: { type: "string" },
4633
- repo: { type: "string" },
4634
- from_agent: { type: "string" },
4635
- to_agent: { type: "string", nullable: true },
4636
- task_id: { type: "string", nullable: true },
4637
- summary: { type: "string" },
4638
- context: { type: "object" },
4639
- status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4640
- created_at: { type: "string" },
4641
- updated_at: { type: "string" },
4642
- expires_at: { type: "string", nullable: true }
4643
- },
4644
- required: ["id", "repo", "from_agent", "summary", "context", "status", "created_at", "updated_at"]
4511
+ {
4512
+ uri: "session://roots",
4513
+ name: "Session Roots",
4514
+ title: "Session Roots",
4515
+ description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
4516
+ mimeType: "application/json",
4517
+ size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
4518
+ annotations: {
4519
+ audience: ["assistant"],
4520
+ priority: 0.95,
4521
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4522
+ }
4645
4523
  }
4646
- },
4647
- {
4648
- name: "handoff-update",
4649
- title: "Handoff Update",
4650
- description: "Close or reclassify a handoff after it has been consumed or found stale. Use accepted when transfer context was consumed, rejected when intentionally declined, and expired when the handoff is obsolete or only described completed work.",
4651
- annotations: {
4652
- readOnlyHint: false,
4653
- idempotentHint: false,
4654
- destructiveHint: false,
4655
- openWorldHint: false
4524
+ ];
4525
+ return paginateEntries("resources", resources, params);
4526
+ }
4527
+ function listResourceTemplates(params) {
4528
+ const templates = [
4529
+ // ── Memory ──────────────────────────────────────────────────────────────
4530
+ {
4531
+ uriTemplate: "repository://{name}/memories",
4532
+ name: "Repository Memories",
4533
+ title: "Repository Memories",
4534
+ description: "All active memory entries for a specific repository",
4535
+ mimeType: "application/json",
4536
+ annotations: { audience: ["assistant"], priority: 0.85 }
4537
+ },
4538
+ {
4539
+ uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
4540
+ name: "Filtered Repository Memories",
4541
+ title: "Filtered Repository Memories",
4542
+ description: "Filter or search memories within a repository by keyword, type, or tag",
4543
+ mimeType: "application/json",
4544
+ annotations: { audience: ["assistant"], priority: 0.8 }
4545
+ },
4546
+ {
4547
+ uriTemplate: "memory://{id}",
4548
+ name: "Memory Detail",
4549
+ title: "Memory Detail",
4550
+ description: "Full content and statistics for a specific memory UUID",
4551
+ mimeType: "application/json",
4552
+ annotations: { audience: ["assistant"], priority: 0.75 }
4656
4553
  },
4657
- inputSchema: {
4658
- type: "object",
4659
- properties: {
4660
- id: { type: "string", format: "uuid", description: "Handoff ID" },
4661
- status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4662
- structured: { type: "boolean", default: false }
4663
- },
4664
- required: ["id", "status"]
4554
+ // ── Tasks ────────────────────────────────────────────────────────────────
4555
+ {
4556
+ uriTemplate: "repository://{name}/tasks",
4557
+ name: "Repository Tasks",
4558
+ title: "Repository Tasks",
4559
+ description: "All active tasks for a specific repository",
4560
+ mimeType: "application/json",
4561
+ annotations: { audience: ["assistant"], priority: 0.9 }
4665
4562
  },
4666
- outputSchema: {
4667
- type: "object",
4668
- properties: {
4669
- success: { type: "boolean" },
4670
- id: { type: "string" },
4671
- status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] }
4672
- },
4673
- required: ["success", "id", "status"]
4674
- }
4675
- },
4676
- {
4677
- name: "handoff-list",
4678
- title: "Handoff List",
4679
- description: "Navigation layer for handoff queues. List repository handoffs with optional status and agent filters, then inspect selected rows before acting.",
4680
- annotations: {
4681
- readOnlyHint: true,
4682
- idempotentHint: true,
4683
- openWorldHint: false
4563
+ {
4564
+ uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
4565
+ name: "Filtered Repository Tasks",
4566
+ title: "Filtered Repository Tasks",
4567
+ description: "Filter tasks within a repository by status or priority level",
4568
+ mimeType: "application/json",
4569
+ annotations: { audience: ["assistant"], priority: 0.85 }
4684
4570
  },
4685
- inputSchema: {
4686
- type: "object",
4687
- properties: {
4688
- repo: { type: "string", description: "Repository name" },
4689
- status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4690
- from_agent: { type: "string" },
4691
- to_agent: { type: "string" },
4692
- limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4693
- offset: { type: "number", minimum: 0, default: 0 },
4694
- structured: { type: "boolean", default: false }
4695
- },
4696
- required: ["repo"]
4571
+ {
4572
+ uriTemplate: "task://{id}",
4573
+ name: "Task Detail",
4574
+ title: "Task Detail",
4575
+ description: "Full content and comments for a specific task UUID",
4576
+ mimeType: "application/json",
4577
+ annotations: { audience: ["assistant"], priority: 0.8 }
4697
4578
  },
4698
- outputSchema: {
4699
- type: "object",
4700
- properties: {
4701
- schema: { type: "string", enum: ["handoff-list"] },
4702
- handoffs: {
4703
- type: "object",
4704
- properties: {
4705
- columns: {
4706
- type: "array",
4707
- items: { type: "string" },
4708
- description: "Column names: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4709
- },
4710
- rows: {
4711
- type: "array",
4712
- items: { type: "array" },
4713
- description: "Each row: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4714
- }
4715
- },
4716
- required: ["columns", "rows"]
4717
- },
4718
- count: { type: "number" },
4719
- offset: { type: "number" }
4720
- },
4721
- required: ["schema", "handoffs", "count", "offset"]
4722
- }
4723
- },
4724
- {
4725
- name: "task-claim",
4726
- title: "Task Claim",
4727
- description: "Claim task ownership for an agent using the dedicated claims table. Use this before taking work from task-list; provide either task_id or task_code.",
4728
- annotations: {
4729
- readOnlyHint: false,
4730
- idempotentHint: false,
4731
- destructiveHint: false,
4732
- openWorldHint: false
4579
+ // ── Repository extras ────────────────────────────────────────────────────
4580
+ {
4581
+ uriTemplate: "repository://{name}/summary",
4582
+ name: "Repository Summary",
4583
+ title: "Repository Summary",
4584
+ description: "High-level architectural summary for a repository",
4585
+ mimeType: "text/plain",
4586
+ annotations: { audience: ["assistant"], priority: 0.95 }
4733
4587
  },
4734
- inputSchema: {
4735
- type: "object",
4736
- properties: {
4737
- repo: { type: "string", description: "Repository name" },
4738
- task_id: { type: "string", format: "uuid", description: "Task id to claim. Optional if task_code is provided." },
4739
- task_code: { type: "string", description: "Task code to claim. Optional if task_id is provided." },
4740
- agent: { type: "string", description: "Claiming agent name" },
4741
- role: { type: "string", description: "Claiming agent role" },
4742
- metadata: { type: "object", description: "Optional claim metadata" },
4743
- structured: { type: "boolean", default: false }
4744
- },
4745
- required: ["repo", "agent"]
4588
+ {
4589
+ uriTemplate: "repository://{name}/actions",
4590
+ name: "Repository Actions",
4591
+ title: "Repository Actions",
4592
+ description: "Audit log of agent tool actions scoped to a repository",
4593
+ mimeType: "application/json",
4594
+ annotations: { audience: ["assistant"], priority: 0.6 }
4746
4595
  },
4747
- outputSchema: {
4748
- type: "object",
4749
- properties: {
4750
- id: { type: "string" },
4751
- repo: { type: "string" },
4752
- task_id: { type: "string" },
4753
- task_code: { type: "string", nullable: true },
4754
- agent: { type: "string" },
4755
- role: { type: "string" },
4756
- claimed_at: { type: "string" },
4757
- released_at: { type: "string", nullable: true },
4758
- metadata: { type: "object" }
4759
- },
4760
- required: ["id", "repo", "task_id", "agent", "role", "claimed_at", "metadata"]
4596
+ // ── Action detail ────────────────────────────────────────────────────────
4597
+ {
4598
+ uriTemplate: "action://{id}",
4599
+ name: "Action Detail",
4600
+ title: "Action Detail",
4601
+ description: "Full details of a specific audit log entry by integer ID",
4602
+ mimeType: "application/json",
4603
+ annotations: { audience: ["assistant"], priority: 0.55 }
4604
+ }
4605
+ ];
4606
+ return paginateEntries("resourceTemplates", templates, params);
4607
+ }
4608
+ function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
4609
+ 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") {
4610
+ if (argumentName === "name") {
4611
+ return rankCompletionValues(dataSources.repos, argumentValue);
4612
+ }
4613
+ }
4614
+ if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
4615
+ if (argumentName === "tag") {
4616
+ return rankCompletionValues(dataSources.tags, argumentValue);
4617
+ }
4618
+ }
4619
+ throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
4620
+ }
4621
+ function readResource(uri, db, session) {
4622
+ logger.info("[Tool] resource.read", { uri });
4623
+ if (uri === "repository://index") {
4624
+ const repos = db.system.listRepoNavigation();
4625
+ const payload = JSON.stringify(repos, null, 2);
4626
+ return {
4627
+ contents: [
4628
+ {
4629
+ uri,
4630
+ mimeType: "application/json",
4631
+ text: payload,
4632
+ size: Buffer.byteLength(payload, "utf8"),
4633
+ annotations: {
4634
+ audience: ["assistant"],
4635
+ priority: 1,
4636
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4637
+ }
4638
+ }
4639
+ ]
4640
+ };
4641
+ }
4642
+ if (uri === "session://roots") {
4643
+ const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
4644
+ return {
4645
+ contents: [
4646
+ {
4647
+ uri,
4648
+ mimeType: "application/json",
4649
+ text: payload,
4650
+ size: Buffer.byteLength(payload, "utf8"),
4651
+ annotations: {
4652
+ audience: ["assistant"],
4653
+ priority: 0.95,
4654
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4655
+ }
4656
+ }
4657
+ ]
4658
+ };
4659
+ }
4660
+ const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
4661
+ if (memoryIdMatch) {
4662
+ const id = memoryIdMatch[1];
4663
+ const entry = db.memories.getByIdWithStats(id);
4664
+ if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
4665
+ const payload = JSON.stringify(entry, null, 2);
4666
+ return {
4667
+ contents: [
4668
+ {
4669
+ uri,
4670
+ mimeType: "application/json",
4671
+ text: payload,
4672
+ size: Buffer.byteLength(payload, "utf8"),
4673
+ annotations: {
4674
+ audience: ["assistant"],
4675
+ priority: 0.75,
4676
+ lastModified: entry.updated_at || entry.created_at
4677
+ }
4678
+ }
4679
+ ]
4680
+ };
4681
+ }
4682
+ const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
4683
+ if (taskIdMatch) {
4684
+ const id = taskIdMatch[1];
4685
+ const task = db.tasks.getTaskById(id);
4686
+ if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
4687
+ const payload = JSON.stringify(task, null, 2);
4688
+ return {
4689
+ contents: [
4690
+ {
4691
+ uri,
4692
+ mimeType: "application/json",
4693
+ text: payload,
4694
+ size: Buffer.byteLength(payload, "utf8"),
4695
+ annotations: {
4696
+ audience: ["assistant"],
4697
+ priority: 0.8,
4698
+ lastModified: task.updated_at || task.created_at
4699
+ }
4700
+ }
4701
+ ]
4702
+ };
4703
+ }
4704
+ const repoBase = parseRepoUri(uri);
4705
+ if (repoBase) {
4706
+ const { name, path: repoPath, query } = repoBase;
4707
+ if (repoPath === "summary") {
4708
+ const summary = db.summaries.getSummary(name);
4709
+ const text = summary?.summary || `No summary available for repository: ${name}`;
4710
+ return {
4711
+ contents: [
4712
+ {
4713
+ uri,
4714
+ mimeType: "text/plain",
4715
+ text,
4716
+ size: Buffer.byteLength(text, "utf8"),
4717
+ annotations: {
4718
+ audience: ["assistant"],
4719
+ priority: 0.95,
4720
+ lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
4721
+ }
4722
+ }
4723
+ ]
4724
+ };
4761
4725
  }
4762
- },
4763
- {
4764
- name: "standard-store",
4765
- title: "Standard Store",
4766
- description: "Store one atomic coding standard. Use for durable implementation rules with explicit context, stack/language filters, and repo/global scope.",
4767
- annotations: {
4768
- readOnlyHint: false,
4769
- idempotentHint: false,
4770
- destructiveHint: false,
4771
- openWorldHint: false
4772
- },
4773
- inputSchema: {
4774
- type: "object",
4775
- properties: {
4776
- name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
4777
- content: { type: "string", minLength: 10, description: "One atomic, actionable standard written as concise Markdown" },
4778
- parent_id: { type: "string", format: "uuid", description: "Optional parent standard ID when this rule is a child/specialization." },
4779
- context: { type: "string", description: "Context or category (e.g., 'error-handling', 'security')" },
4780
- version: { type: "string", description: "Version of the standard (e.g., '1.0.0')" },
4781
- language: { type: "string", description: "Programming language (e.g., 'typescript', 'python')" },
4782
- stack: {
4783
- type: "array",
4784
- items: { type: "string" },
4785
- description: "Technology stack (e.g., ['react', 'nextjs'])"
4786
- },
4787
- repo: { type: "string", description: "Repository name for repo-specific standards. Omit only for global standards." },
4788
- is_global: { type: "boolean", description: "Whether standard applies globally or repo-specific" },
4789
- tags: {
4790
- type: "array",
4791
- items: { type: "string" },
4792
- description: "Tags for categorization"
4793
- },
4794
- metadata: {
4795
- type: "object",
4796
- description: "Additional metadata"
4797
- },
4798
- agent: { type: "string", description: "Agent creating the standard" },
4799
- model: { type: "string", description: "AI model used" },
4800
- structured: { type: "boolean", default: false }
4801
- },
4802
- required: ["name", "content", "tags", "metadata"]
4803
- },
4804
- outputSchema: {
4805
- type: "object",
4806
- properties: {
4807
- success: { type: "boolean" },
4808
- standard: {
4809
- type: "object",
4810
- properties: {
4811
- id: { type: "string" },
4812
- title: { type: "string" },
4813
- content: { type: "string" },
4814
- parent_id: { type: "string", nullable: true },
4815
- context: { type: "string" },
4816
- version: { type: "string" },
4817
- language: { type: "string", nullable: true },
4818
- stack: { type: "array", items: { type: "string" } },
4819
- is_global: { type: "boolean" },
4820
- repo: { type: "string", nullable: true },
4821
- tags: { type: "array", items: { type: "string" } },
4822
- metadata: { type: "object" },
4823
- created_at: { type: "string" },
4824
- updated_at: { type: "string" },
4825
- agent: { type: "string" },
4826
- model: { type: "string" }
4827
- },
4828
- required: [
4829
- "id",
4830
- "title",
4831
- "content",
4832
- "parent_id",
4833
- "context",
4834
- "version",
4835
- "stack",
4836
- "is_global",
4837
- "tags",
4838
- "metadata",
4839
- "created_at",
4840
- "updated_at",
4841
- "agent",
4842
- "model"
4843
- ]
4844
- },
4845
- message: { type: "string" }
4846
- },
4847
- required: ["success", "standard", "message"]
4726
+ if (repoPath === "memories") {
4727
+ const search = query.get("search") || "";
4728
+ const type = query.get("type");
4729
+ const tag = query.get("tag");
4730
+ const result = db.memories.listMemoriesForDashboard({
4731
+ repo: name,
4732
+ type: type || void 0,
4733
+ tag: tag || void 0,
4734
+ search: search || void 0,
4735
+ limit: 50
4736
+ });
4737
+ const entries = result.items;
4738
+ const payload = JSON.stringify(entries, null, 2);
4739
+ return {
4740
+ contents: [
4741
+ {
4742
+ uri,
4743
+ mimeType: "application/json",
4744
+ text: payload,
4745
+ size: Buffer.byteLength(payload, "utf8"),
4746
+ annotations: {
4747
+ audience: ["assistant"],
4748
+ priority: 0.85,
4749
+ lastModified: deriveLastModifiedFromCollection(
4750
+ entries.map((e) => e.updated_at || e.created_at)
4751
+ )
4752
+ }
4753
+ }
4754
+ ]
4755
+ };
4848
4756
  }
4849
- },
4850
- {
4851
- name: "standard-update",
4852
- title: "Standard Update",
4853
- description: "Update an existing coding standard. Use this when the rule changes, expands scope, or metadata/tags need correction.",
4854
- annotations: {
4855
- readOnlyHint: false,
4856
- idempotentHint: false,
4857
- destructiveHint: false,
4858
- openWorldHint: false
4859
- },
4860
- inputSchema: {
4861
- type: "object",
4862
- properties: {
4863
- id: { type: "string", description: "Standard ID to update" },
4864
- name: { type: "string", minLength: 3, maxLength: 255 },
4865
- content: { type: "string", minLength: 10 },
4866
- parent_id: { type: "string", format: "uuid", nullable: true },
4867
- context: { type: "string" },
4868
- version: { type: "string" },
4869
- language: { type: "string" },
4870
- stack: { type: "array", items: { type: "string" } },
4871
- repo: { type: "string" },
4872
- is_global: { type: "boolean" },
4873
- tags: { type: "array", items: { type: "string" } },
4874
- metadata: { type: "object" },
4875
- agent: { type: "string" },
4876
- model: { type: "string" },
4877
- structured: { type: "boolean", default: false }
4878
- },
4879
- required: ["id"]
4880
- },
4881
- outputSchema: {
4882
- type: "object",
4883
- properties: {
4884
- success: { type: "boolean" },
4885
- id: { type: "string" },
4886
- updatedFields: { type: "array", items: { type: "string" } }
4887
- },
4888
- required: ["success", "id", "updatedFields"]
4757
+ if (repoPath === "tasks") {
4758
+ const status = query.get("status");
4759
+ const priority = query.get("priority");
4760
+ let tasks;
4761
+ if (status && status !== "all") {
4762
+ const statuses = status.split(",").map((s) => s.trim());
4763
+ tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
4764
+ } else {
4765
+ tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
4766
+ }
4767
+ if (priority) {
4768
+ const p = Number(priority);
4769
+ if (!isNaN(p)) {
4770
+ tasks = tasks.filter((t) => t.priority === p);
4771
+ }
4772
+ }
4773
+ const payload = JSON.stringify(tasks, null, 2);
4774
+ return {
4775
+ contents: [
4776
+ {
4777
+ uri,
4778
+ mimeType: "application/json",
4779
+ text: payload,
4780
+ size: Buffer.byteLength(payload, "utf8"),
4781
+ annotations: {
4782
+ audience: ["assistant"],
4783
+ priority: 0.9,
4784
+ lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
4785
+ }
4786
+ }
4787
+ ]
4788
+ };
4889
4789
  }
4890
- },
4891
- {
4892
- name: "standard-search",
4893
- title: "Standard Search",
4894
- description: "NAVIGATION LAYER: Returns a compact pointer table of matching coding standards. Use `standard-detail` to fetch full content for a selected result.",
4895
- annotations: {
4896
- readOnlyHint: true,
4897
- idempotentHint: true,
4898
- openWorldHint: false
4899
- },
4900
- inputSchema: {
4901
- type: "object",
4902
- properties: {
4903
- query: { type: "string", description: "Search query (optional, searches title/content)" },
4904
- stack: {
4905
- type: "array",
4906
- items: { type: "string" },
4907
- description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
4908
- },
4909
- tags: {
4910
- type: "array",
4911
- items: { type: "string" },
4912
- description: "Tag filter"
4913
- },
4914
- language: { type: "string", description: "Programming language filter" },
4915
- context: { type: "string", description: "Context/category filter" },
4916
- version: { type: "string", description: "Version filter" },
4917
- repo: { type: "string", description: "Repository filter (optional)" },
4918
- is_global: { type: "boolean", description: "Filter by global/repo-specific" },
4919
- limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4920
- offset: { type: "number", minimum: 0, default: 0 },
4921
- structured: { type: "boolean", default: false }
4922
- },
4923
- required: []
4924
- },
4925
- outputSchema: {
4926
- type: "object",
4927
- properties: {
4928
- schema: { type: "string", enum: ["standard-search"] },
4929
- query: { type: "string" },
4930
- count: { type: "number", description: "Number of rows returned" },
4931
- total: { type: "number", description: "Total number of matches before pagination" },
4932
- offset: { type: "number" },
4933
- limit: { type: "number" },
4934
- results: {
4935
- type: "object",
4936
- properties: {
4937
- columns: {
4938
- type: "array",
4939
- items: { type: "string" }
4940
- },
4941
- rows: {
4942
- type: "array",
4943
- items: { type: "array" },
4944
- description: "Each row includes standard id and pointer metadata. Fetch full content via standard-detail."
4790
+ if (repoPath === "actions") {
4791
+ const actions = db.actions.getRecentActions(name, 100);
4792
+ const payload = JSON.stringify(actions, null, 2);
4793
+ return {
4794
+ contents: [
4795
+ {
4796
+ uri,
4797
+ mimeType: "application/json",
4798
+ text: payload,
4799
+ size: Buffer.byteLength(payload, "utf8"),
4800
+ annotations: {
4801
+ audience: ["assistant"],
4802
+ priority: 0.6,
4803
+ lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
4945
4804
  }
4946
- },
4947
- required: ["columns", "rows"]
4805
+ }
4806
+ ]
4807
+ };
4808
+ }
4809
+ }
4810
+ const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
4811
+ if (actionIdMatch) {
4812
+ const id = Number(actionIdMatch[1]);
4813
+ const action = db.actions.getActionById(id);
4814
+ if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
4815
+ const payload = JSON.stringify(action, null, 2);
4816
+ return {
4817
+ contents: [
4818
+ {
4819
+ uri,
4820
+ mimeType: "application/json",
4821
+ text: payload,
4822
+ size: Buffer.byteLength(payload, "utf8"),
4823
+ annotations: {
4824
+ audience: ["assistant"],
4825
+ priority: 0.55,
4826
+ lastModified: action.created_at
4827
+ }
4948
4828
  }
4949
- },
4950
- required: ["schema", "query", "count", "total", "offset", "limit", "results"]
4829
+ ]
4830
+ };
4831
+ }
4832
+ throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
4833
+ }
4834
+ function parseRepoUri(uri) {
4835
+ const prefix = "repository://";
4836
+ if (!uri.startsWith(prefix)) return null;
4837
+ const rest = uri.slice(prefix.length);
4838
+ const queryStart = rest.indexOf("?");
4839
+ const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
4840
+ const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
4841
+ const slashIdx = withoutQuery.indexOf("/");
4842
+ if (slashIdx === -1) return null;
4843
+ const name = withoutQuery.slice(0, slashIdx);
4844
+ const path6 = withoutQuery.slice(slashIdx + 1);
4845
+ if (!name || !path6) return null;
4846
+ return { name, path: path6, query: new URLSearchParams(queryString) };
4847
+ }
4848
+ function paginateEntries(key, entries, params) {
4849
+ const limit = normalizeLimit(params?.limit);
4850
+ const offset = decodeCursor(params?.cursor);
4851
+ const sliced = entries.slice(offset, offset + limit);
4852
+ const nextOffset = offset + sliced.length;
4853
+ return {
4854
+ [key]: sliced,
4855
+ nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
4856
+ };
4857
+ }
4858
+ function normalizeLimit(limit) {
4859
+ if (typeof limit !== "number" || !Number.isFinite(limit)) {
4860
+ return DEFAULT_PAGE_SIZE;
4861
+ }
4862
+ return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
4863
+ }
4864
+ function deriveLastModifiedFromCollection(values) {
4865
+ const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
4866
+ return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
4867
+ }
4868
+ function resourceNotFound(message, uri) {
4869
+ const error = new Error(message);
4870
+ error.code = -32002;
4871
+ error.data = { uri };
4872
+ return error;
4873
+ }
4874
+ function invalidCompletionParams(message) {
4875
+ const error = new Error(message);
4876
+ error.code = -32602;
4877
+ return error;
4878
+ }
4879
+
4880
+ // src/mcp/prompts/loader.ts
4881
+ import fs4 from "fs";
4882
+ import path5 from "path";
4883
+ import { fileURLToPath as fileURLToPath3 } from "url";
4884
+ import matter from "gray-matter";
4885
+ var __filename = fileURLToPath3(import.meta.url);
4886
+ var __dirname2 = path5.dirname(__filename);
4887
+ function findPromptDir() {
4888
+ const candidates = [
4889
+ // Production if chunked into dist/
4890
+ "./prompts",
4891
+ // Production if inlined into dist/mcp/
4892
+ "../prompts",
4893
+ // Dev: /src/mcp/prompts/definitions (next to loader.ts)
4894
+ "./definitions"
4895
+ ].map((relPath) => path5.resolve(__dirname2, relPath));
4896
+ for (const dir of candidates) {
4897
+ if (fs4.existsSync(dir)) {
4898
+ const files = fs4.readdirSync(dir);
4899
+ if (files.some((f) => f.endsWith(".md"))) {
4900
+ return dir;
4901
+ }
4951
4902
  }
4952
4903
  }
4953
- ];
4904
+ return path5.resolve(__dirname2, "./definitions");
4905
+ }
4906
+ var PROMPT_DIR = findPromptDir();
4907
+ function listPromptFiles() {
4908
+ if (!fs4.existsSync(PROMPT_DIR)) return [];
4909
+ return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
4910
+ }
4911
+ function loadPromptFromMarkdown(name) {
4912
+ const filePath = path5.join(PROMPT_DIR, `${name}.md`);
4913
+ if (!fs4.existsSync(filePath)) {
4914
+ throw new Error(`Prompt file not found: ${filePath}`);
4915
+ }
4916
+ const fileContent = fs4.readFileSync(filePath, "utf-8");
4917
+ const { data, content } = matter(fileContent);
4918
+ return {
4919
+ name: data.name || name,
4920
+ description: data.description || "",
4921
+ arguments: data.arguments || [],
4922
+ agent: data.agent,
4923
+ content: content.trim()
4924
+ };
4925
+ }
4926
+
4927
+ // src/mcp/prompts/registry.ts
4928
+ function createPromptDefinition(loaded) {
4929
+ return {
4930
+ name: loaded.name,
4931
+ description: loaded.description,
4932
+ arguments: loaded.arguments,
4933
+ agent: loaded.agent,
4934
+ messages: [
4935
+ {
4936
+ role: "user",
4937
+ content: {
4938
+ type: "text",
4939
+ text: loaded.content
4940
+ }
4941
+ }
4942
+ ]
4943
+ };
4944
+ }
4945
+ var PROMPTS = {};
4946
+ var promptFiles = listPromptFiles();
4947
+ for (const name of promptFiles) {
4948
+ try {
4949
+ PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
4950
+ } catch (e) {
4951
+ logger.warn(`Failed to load prompt ${name}: ${e}`);
4952
+ }
4953
+ }
4954
+ async function listPrompts(db, session, params) {
4955
+ const allPrompts = Object.values(PROMPTS).map((p) => ({
4956
+ name: p.name,
4957
+ description: p.description,
4958
+ arguments: p.arguments,
4959
+ metadata: p.agent ? { agent: p.agent } : void 0
4960
+ }));
4961
+ const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
4962
+ const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
4963
+ const offset = decodeCursor(params?.cursor);
4964
+ const sliced = allPrompts.slice(offset, offset + limit);
4965
+ const nextOffset = offset + sliced.length;
4966
+ return {
4967
+ prompts: sliced,
4968
+ nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
4969
+ };
4970
+ }
4971
+ async function getPrompt(name, args = {}, db, session) {
4972
+ const prompt = PROMPTS[name];
4973
+ if (!prompt) {
4974
+ throw new Error(`Prompt not found: ${name}`);
4975
+ }
4976
+ const inferredRepo = inferRepoFromSession(session);
4977
+ const messages = prompt.messages.map((m) => {
4978
+ let text = m.content.text;
4979
+ for (const [key, value] of Object.entries(args)) {
4980
+ text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
4981
+ }
4982
+ text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
4983
+ return {
4984
+ ...m,
4985
+ content: {
4986
+ ...m.content,
4987
+ text
4988
+ }
4989
+ };
4990
+ });
4991
+ return {
4992
+ description: prompt.description,
4993
+ messages,
4994
+ metadata: prompt.agent ? { agent: prompt.agent } : void 0
4995
+ };
4996
+ }
4997
+ async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
4998
+ void name;
4999
+ void contextArguments;
5000
+ if (argName === "task_id") {
5001
+ const values = dataSources.tasks.map((t) => t.id);
5002
+ return rankCompletionValues(values, value);
5003
+ }
5004
+ return [];
5005
+ }
4954
5006
 
4955
5007
  // src/mcp/tools/standard.shared.ts
5008
+ function toContextSlug(value) {
5009
+ return value.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
5010
+ }
4956
5011
  function buildStandardVectorText(standard) {
4957
5012
  return [
4958
5013
  standard.title,
@@ -4967,31 +5022,17 @@ function buildStandardVectorText(standard) {
4967
5022
  }
4968
5023
 
4969
5024
  export {
5025
+ MCP_PROTOCOL_VERSION,
5026
+ CAPABILITIES,
4970
5027
  logger,
4971
5028
  setLogLevel,
4972
5029
  getLogLevel,
4973
5030
  addLogSink,
4974
5031
  LOG_LEVEL_VALUES,
4975
5032
  createFileSink,
4976
- encodeCursor,
4977
- decodeCursor,
4978
- listResources,
4979
- listResourceTemplates,
4980
- completeResourceArgument,
4981
- readResource,
4982
- createSessionContext,
4983
- updateSessionFromInitialize,
4984
- updateSessionRoots,
4985
- extractRootsFromResult,
4986
- getFilesystemRoots,
4987
- isPathWithinRoots,
4988
- findContainingRoot,
4989
- inferRepoFromSession,
4990
- PROMPTS,
4991
- listPrompts,
4992
- getPrompt,
4993
- completePromptArgument,
4994
5033
  normalizeRepo,
5034
+ SQLiteStore,
5035
+ RealVectorStore,
4995
5036
  MemoryStoreSchema,
4996
5037
  MemoryUpdateSchema,
4997
5038
  MemorySearchSchema,
@@ -5017,9 +5058,24 @@ export {
5017
5058
  StandardUpdateSchema,
5018
5059
  StandardSearchSchema,
5019
5060
  TOOL_DEFINITIONS,
5020
- buildStandardVectorText,
5021
- SQLiteStore,
5022
- RealVectorStore,
5023
- MCP_PROTOCOL_VERSION,
5024
- CAPABILITIES
5061
+ encodeCursor,
5062
+ decodeCursor,
5063
+ listResources,
5064
+ listResourceTemplates,
5065
+ completeResourceArgument,
5066
+ readResource,
5067
+ createSessionContext,
5068
+ updateSessionFromInitialize,
5069
+ updateSessionRoots,
5070
+ extractRootsFromResult,
5071
+ getFilesystemRoots,
5072
+ isPathWithinRoots,
5073
+ findContainingRoot,
5074
+ inferRepoFromSession,
5075
+ PROMPTS,
5076
+ listPrompts,
5077
+ getPrompt,
5078
+ completePromptArgument,
5079
+ toContextSlug,
5080
+ buildStandardVectorText
5025
5081
  };