@vheins/local-memory-mcp 0.8.45 → 0.8.46

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.8.46") {
7
+ pkgVersion = "0.8.46";
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";
@@ -2343,6 +2287,13 @@ var HandoffEntity = class extends BaseEntity {
2343
2287
  ]);
2344
2288
  return result.changes > 0;
2345
2289
  }
2290
+ updatePendingHandoffsForTask(task_id, status) {
2291
+ const result = this.run(
2292
+ "UPDATE handoffs SET status = ?, updated_at = ? WHERE task_id = ? AND status = 'pending'",
2293
+ [status, (/* @__PURE__ */ new Date()).toISOString(), task_id]
2294
+ );
2295
+ return result.changes;
2296
+ }
2346
2297
  claimTask(params) {
2347
2298
  const now = (/* @__PURE__ */ new Date()).toISOString();
2348
2299
  const id = randomUUID();
@@ -2383,6 +2334,13 @@ var HandoffEntity = class extends BaseEntity {
2383
2334
  const result = this.run(sql, params);
2384
2335
  return result.changes > 0;
2385
2336
  }
2337
+ releaseClaimsForTask(task_id) {
2338
+ const result = this.run("UPDATE claims SET released_at = ? WHERE task_id = ? AND released_at IS NULL", [
2339
+ (/* @__PURE__ */ new Date()).toISOString(),
2340
+ task_id
2341
+ ]);
2342
+ return result.changes;
2343
+ }
2386
2344
  listClaims(params) {
2387
2345
  const conditions = ["repo = ?"];
2388
2346
  const values = [params.repo];
@@ -2619,1124 +2577,1011 @@ var SQLiteStore = class _SQLiteStore {
2619
2577
  }
2620
2578
  };
2621
2579
 
2622
- // src/mcp/capabilities.ts
2623
- import { fileURLToPath as fileURLToPath2 } from "url";
2580
+ // src/mcp/session.ts
2624
2581
  import path4 from "path";
2625
- var __dirname = path4.dirname(fileURLToPath2(import.meta.url));
2626
- var pkgVersion = "0.1.0";
2627
- if ("0.8.45") {
2628
- pkgVersion = "0.8.45";
2629
- } else {
2630
- let searchDir = __dirname;
2631
- for (let i = 0; i < 5; i++) {
2632
- const candidate = path4.join(searchDir, "package.json");
2582
+ import { fileURLToPath as fileURLToPath2 } from "url";
2583
+ function createSessionContext() {
2584
+ return {
2585
+ roots: [],
2586
+ supportsRoots: false,
2587
+ supportsSampling: false,
2588
+ supportsSamplingTools: false,
2589
+ supportsElicitation: false,
2590
+ supportsElicitationForm: false,
2591
+ supportsElicitationUrl: false
2592
+ };
2593
+ }
2594
+ function updateSessionFromInitialize(session, params) {
2595
+ const capabilities = params?.capabilities || {};
2596
+ session.clientInfo = params?.clientInfo;
2597
+ session.clientCapabilities = capabilities;
2598
+ session.supportsRoots = Boolean(capabilities.roots);
2599
+ session.supportsSampling = Boolean(capabilities.sampling);
2600
+ const sampling = capabilities.sampling;
2601
+ session.supportsSamplingTools = Boolean(sampling?.tools);
2602
+ session.supportsElicitation = Boolean(capabilities.elicitation);
2603
+ session.supportsElicitationForm = supportsElicitationMode(capabilities.elicitation, "form");
2604
+ session.supportsElicitationUrl = supportsElicitationMode(capabilities.elicitation, "url");
2605
+ }
2606
+ function supportsElicitationMode(capability, mode) {
2607
+ if (!capability || typeof capability !== "object") {
2608
+ return false;
2609
+ }
2610
+ const cap = capability;
2611
+ if (mode === "form") {
2612
+ return Object.keys(cap).length === 0 || typeof cap.form === "object";
2613
+ }
2614
+ return typeof cap.url === "object";
2615
+ }
2616
+ function updateSessionRoots(session, roots) {
2617
+ const normalized = normalizeRoots(roots);
2618
+ const previous = JSON.stringify(session.roots);
2619
+ const next = JSON.stringify(normalized);
2620
+ session.roots = normalized;
2621
+ return previous !== next;
2622
+ }
2623
+ function normalizeRoots(roots) {
2624
+ if (!Array.isArray(roots)) return [];
2625
+ const seen = /* @__PURE__ */ new Set();
2626
+ const normalized = [];
2627
+ for (const root of roots) {
2628
+ if (!root || typeof root !== "object") continue;
2629
+ const r = root;
2630
+ const uri = typeof r.uri === "string" ? r.uri : void 0;
2631
+ const name = typeof r.name === "string" ? r.name : void 0;
2632
+ if (!uri || seen.has(uri)) continue;
2633
+ seen.add(uri);
2634
+ normalized.push({ uri, name });
2635
+ }
2636
+ return normalized;
2637
+ }
2638
+ function extractRootsFromResult(result) {
2639
+ return normalizeRoots(result?.roots);
2640
+ }
2641
+ function getFilesystemRoots(session) {
2642
+ if (!session) return [];
2643
+ const resolved = [];
2644
+ for (const root of session.roots) {
2645
+ if (!root.uri.startsWith("file://")) continue;
2633
2646
  try {
2634
- if (fs.existsSync(candidate)) {
2635
- const pkg = JSON.parse(fs.readFileSync(candidate, "utf8"));
2636
- if (pkg.name === "@vheins/local-memory-mcp" && pkg.version) {
2637
- pkgVersion = pkg.version;
2638
- break;
2639
- }
2640
- }
2647
+ resolved.push(path4.resolve(fileURLToPath2(root.uri)));
2641
2648
  } catch {
2642
2649
  }
2643
- searchDir = path4.dirname(searchDir);
2644
2650
  }
2651
+ return resolved;
2645
2652
  }
2646
- var MCP_PROTOCOL_VERSION = "2025-03-26";
2647
- var CAPABILITIES = {
2648
- serverInfo: {
2649
- name: "mcp-memory-local",
2650
- version: pkgVersion
2651
- },
2652
- capabilities: {
2653
- completions: {},
2654
- logging: {},
2655
- resources: {
2656
- subscribe: true,
2657
- listChanged: true
2658
- },
2659
- tools: {
2660
- listChanged: false
2661
- },
2662
- prompts: {
2663
- listChanged: true
2653
+ function isPathWithinRoots(targetPath, session) {
2654
+ const roots = getFilesystemRoots(session);
2655
+ if (roots.length === 0) return true;
2656
+ const normalizedTarget = path4.resolve(targetPath);
2657
+ return roots.some((rootPath) => {
2658
+ const relative = path4.relative(rootPath, normalizedTarget);
2659
+ return relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative);
2660
+ });
2661
+ }
2662
+ function findContainingRoot(targetPath, session) {
2663
+ const roots = getFilesystemRoots(session);
2664
+ if (roots.length === 0) return null;
2665
+ const normalizedTarget = path4.resolve(targetPath);
2666
+ for (const rootPath of roots) {
2667
+ const relative = path4.relative(rootPath, normalizedTarget);
2668
+ if (relative === "" || !relative.startsWith("..") && !path4.isAbsolute(relative)) {
2669
+ return rootPath;
2664
2670
  }
2665
2671
  }
2666
- };
2667
-
2668
- // src/mcp/utils/pagination.ts
2669
- function encodeCursor(offset) {
2670
- return Buffer.from(String(offset), "utf8").toString("base64");
2671
- }
2672
- function decodeCursor(cursor) {
2673
- if (cursor === void 0 || cursor === null || cursor === "") {
2674
- return 0;
2675
- }
2676
- if (typeof cursor !== "string" || cursor.trim() === "") {
2677
- throw invalidPaginationParams("Invalid cursor");
2678
- }
2679
- let decoded;
2680
- try {
2681
- decoded = Buffer.from(cursor, "base64").toString("utf8");
2682
- } catch {
2683
- throw invalidPaginationParams("Invalid cursor");
2684
- }
2685
- if (!/^\d+$/.test(decoded)) {
2686
- throw invalidPaginationParams("Invalid cursor");
2687
- }
2688
- const offset = Number.parseInt(decoded, 10);
2689
- if (!Number.isFinite(offset) || offset < 0) {
2690
- throw invalidPaginationParams("Invalid cursor");
2691
- }
2692
- return offset;
2693
- }
2694
- function invalidPaginationParams(message) {
2695
- const error = new Error(message);
2696
- error.code = -32602;
2697
- return error;
2672
+ return null;
2698
2673
  }
2699
-
2700
- // src/mcp/utils/completion.ts
2701
- var MAX_COMPLETION_VALUES = 100;
2702
- function rankCompletionValues(candidates, input) {
2703
- const unique = [...new Set(candidates.filter(Boolean))];
2704
- const needle = input.trim().toLowerCase();
2705
- if (!needle) {
2706
- return unique.slice(0, MAX_COMPLETION_VALUES);
2674
+ function inferRepoFromSession(session) {
2675
+ const roots = getFilesystemRoots(session);
2676
+ if (roots.length === 1) {
2677
+ return path4.basename(roots[0]);
2707
2678
  }
2708
- 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);
2709
- }
2710
- function scoreCompletionValue(value, needle) {
2711
- const haystack = value.toLowerCase();
2712
- if (haystack === needle) return 100;
2713
- if (haystack.startsWith(needle)) return 75;
2714
- if (haystack.includes(needle)) return 50;
2715
- const compactNeedle = needle.replace(/[\s_-]+/g, "");
2716
- const compactHaystack = haystack.replace(/[\s_-]+/g, "");
2717
- if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
2718
- return 0;
2679
+ return void 0;
2719
2680
  }
2720
2681
 
2721
- // src/mcp/resources/index.ts
2722
- var DEFAULT_PAGE_SIZE = 25;
2723
- var MAX_PAGE_SIZE = 100;
2724
- function listResources(session, params) {
2725
- const resources = [
2726
- {
2727
- uri: "repository://index",
2728
- name: "Repository Index",
2729
- title: "Repository Index",
2730
- description: "List of all known repositories with memory/task counts and last activity",
2731
- mimeType: "application/json",
2732
- annotations: {
2733
- audience: ["assistant"],
2734
- priority: 1,
2735
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
2736
- }
2737
- },
2738
- {
2739
- uri: "session://roots",
2740
- name: "Session Roots",
2741
- title: "Session Roots",
2742
- description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
2743
- mimeType: "application/json",
2744
- size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
2745
- annotations: {
2746
- audience: ["assistant"],
2747
- priority: 0.95,
2748
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
2749
- }
2750
- }
2751
- ];
2752
- return paginateEntries("resources", resources, params);
2753
- }
2754
- function listResourceTemplates(params) {
2755
- const templates = [
2756
- // ── Memory ──────────────────────────────────────────────────────────────
2757
- {
2758
- uriTemplate: "repository://{name}/memories",
2759
- name: "Repository Memories",
2760
- title: "Repository Memories",
2761
- description: "All active memory entries for a specific repository",
2762
- mimeType: "application/json",
2763
- annotations: { audience: ["assistant"], priority: 0.85 }
2764
- },
2765
- {
2766
- uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
2767
- name: "Filtered Repository Memories",
2768
- title: "Filtered Repository Memories",
2769
- description: "Filter or search memories within a repository by keyword, type, or tag",
2770
- mimeType: "application/json",
2771
- annotations: { audience: ["assistant"], priority: 0.8 }
2772
- },
2773
- {
2774
- uriTemplate: "memory://{id}",
2775
- name: "Memory Detail",
2776
- title: "Memory Detail",
2777
- description: "Full content and statistics for a specific memory UUID",
2778
- mimeType: "application/json",
2779
- annotations: { audience: ["assistant"], priority: 0.75 }
2780
- },
2781
- // ── Tasks ────────────────────────────────────────────────────────────────
2782
- {
2783
- uriTemplate: "repository://{name}/tasks",
2784
- name: "Repository Tasks",
2785
- title: "Repository Tasks",
2786
- description: "All active tasks for a specific repository",
2787
- mimeType: "application/json",
2788
- annotations: { audience: ["assistant"], priority: 0.9 }
2789
- },
2790
- {
2791
- uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
2792
- name: "Filtered Repository Tasks",
2793
- title: "Filtered Repository Tasks",
2794
- description: "Filter tasks within a repository by status or priority level",
2795
- mimeType: "application/json",
2796
- annotations: { audience: ["assistant"], priority: 0.85 }
2797
- },
2798
- {
2799
- uriTemplate: "task://{id}",
2800
- name: "Task Detail",
2801
- title: "Task Detail",
2802
- description: "Full content and comments for a specific task UUID",
2803
- mimeType: "application/json",
2804
- annotations: { audience: ["assistant"], priority: 0.8 }
2805
- },
2806
- // ── Repository extras ────────────────────────────────────────────────────
2807
- {
2808
- uriTemplate: "repository://{name}/summary",
2809
- name: "Repository Summary",
2810
- title: "Repository Summary",
2811
- description: "High-level architectural summary for a repository",
2812
- mimeType: "text/plain",
2813
- annotations: { audience: ["assistant"], priority: 0.95 }
2814
- },
2815
- {
2816
- uriTemplate: "repository://{name}/actions",
2817
- name: "Repository Actions",
2818
- title: "Repository Actions",
2819
- description: "Audit log of agent tool actions scoped to a repository",
2820
- mimeType: "application/json",
2821
- annotations: { audience: ["assistant"], priority: 0.6 }
2822
- },
2823
- // ── Action detail ────────────────────────────────────────────────────────
2824
- {
2825
- uriTemplate: "action://{id}",
2826
- name: "Action Detail",
2827
- title: "Action Detail",
2828
- description: "Full details of a specific audit log entry by integer ID",
2829
- mimeType: "application/json",
2830
- annotations: { audience: ["assistant"], priority: 0.55 }
2831
- }
2832
- ];
2833
- return paginateEntries("resourceTemplates", templates, params);
2834
- }
2835
- function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
2836
- 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") {
2837
- if (argumentName === "name") {
2838
- return rankCompletionValues(dataSources.repos, argumentValue);
2839
- }
2840
- }
2841
- if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
2842
- if (argumentName === "tag") {
2843
- return rankCompletionValues(dataSources.tags, argumentValue);
2844
- }
2845
- }
2846
- throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
2847
- }
2848
- function readResource(uri, db, session) {
2849
- logger.info("[Tool] resource.read", { uri });
2850
- if (uri === "repository://index") {
2851
- const repos = db.system.listRepoNavigation();
2852
- const payload = JSON.stringify(repos, null, 2);
2853
- return {
2854
- contents: [
2855
- {
2856
- uri,
2857
- mimeType: "application/json",
2858
- text: payload,
2859
- size: Buffer.byteLength(payload, "utf8"),
2860
- annotations: {
2861
- audience: ["assistant"],
2862
- priority: 1,
2863
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
2864
- }
2865
- }
2866
- ]
2867
- };
2868
- }
2869
- if (uri === "session://roots") {
2870
- const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
2871
- return {
2872
- contents: [
2873
- {
2874
- uri,
2875
- mimeType: "application/json",
2876
- text: payload,
2877
- size: Buffer.byteLength(payload, "utf8"),
2878
- annotations: {
2879
- audience: ["assistant"],
2880
- priority: 0.95,
2881
- lastModified: (/* @__PURE__ */ new Date()).toISOString()
2882
- }
2883
- }
2884
- ]
2885
- };
2886
- }
2887
- const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
2888
- if (memoryIdMatch) {
2889
- const id = memoryIdMatch[1];
2890
- const entry = db.memories.getByIdWithStats(id);
2891
- if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
2892
- const payload = JSON.stringify(entry, null, 2);
2893
- return {
2894
- contents: [
2895
- {
2896
- uri,
2897
- mimeType: "application/json",
2898
- text: payload,
2899
- size: Buffer.byteLength(payload, "utf8"),
2900
- annotations: {
2901
- audience: ["assistant"],
2902
- priority: 0.75,
2903
- lastModified: entry.updated_at || entry.created_at
2904
- }
2905
- }
2906
- ]
2907
- };
2908
- }
2909
- const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
2910
- if (taskIdMatch) {
2911
- const id = taskIdMatch[1];
2912
- const task = db.tasks.getTaskById(id);
2913
- if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
2914
- const payload = JSON.stringify(task, null, 2);
2915
- return {
2916
- contents: [
2917
- {
2918
- uri,
2919
- mimeType: "application/json",
2920
- text: payload,
2921
- size: Buffer.byteLength(payload, "utf8"),
2922
- annotations: {
2923
- audience: ["assistant"],
2924
- priority: 0.8,
2925
- lastModified: task.updated_at || task.created_at
2926
- }
2927
- }
2928
- ]
2929
- };
2930
- }
2931
- const repoBase = parseRepoUri(uri);
2932
- if (repoBase) {
2933
- const { name, path: repoPath, query } = repoBase;
2934
- if (repoPath === "summary") {
2935
- const summary = db.summaries.getSummary(name);
2936
- const text = summary?.summary || `No summary available for repository: ${name}`;
2937
- return {
2938
- contents: [
2939
- {
2940
- uri,
2941
- mimeType: "text/plain",
2942
- text,
2943
- size: Buffer.byteLength(text, "utf8"),
2944
- annotations: {
2945
- audience: ["assistant"],
2946
- priority: 0.95,
2947
- lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
2948
- }
2949
- }
2950
- ]
2951
- };
2952
- }
2953
- if (repoPath === "memories") {
2954
- const search = query.get("search") || "";
2955
- const type = query.get("type");
2956
- const tag = query.get("tag");
2957
- const result = db.memories.listMemoriesForDashboard({
2958
- repo: name,
2959
- type: type || void 0,
2960
- tag: tag || void 0,
2961
- search: search || void 0,
2962
- limit: 50
2963
- });
2964
- const entries = result.items;
2965
- const payload = JSON.stringify(entries, null, 2);
2966
- return {
2967
- contents: [
2968
- {
2969
- uri,
2970
- mimeType: "application/json",
2971
- text: payload,
2972
- size: Buffer.byteLength(payload, "utf8"),
2973
- annotations: {
2974
- audience: ["assistant"],
2975
- priority: 0.85,
2976
- lastModified: deriveLastModifiedFromCollection(
2977
- entries.map((e) => e.updated_at || e.created_at)
2978
- )
2979
- }
2980
- }
2981
- ]
2982
- };
2682
+ // src/mcp/tools/schemas.ts
2683
+ import { z } from "zod";
2684
+ var MemoryScopeSchema = z.object({
2685
+ repo: z.string().min(1).transform(normalizeRepo),
2686
+ branch: z.string().optional(),
2687
+ folder: z.string().optional(),
2688
+ language: z.string().optional()
2689
+ });
2690
+ var MemoryTypeSchema = z.enum([
2691
+ "code_fact",
2692
+ "decision",
2693
+ "mistake",
2694
+ "pattern",
2695
+ "task_archive"
2696
+ ]);
2697
+ var MemoryStoreSchema = z.object({
2698
+ code: z.string().max(20).optional(),
2699
+ type: MemoryTypeSchema,
2700
+ title: z.string().min(3).max(255),
2701
+ content: z.string().min(10),
2702
+ importance: z.number().min(1).max(5),
2703
+ agent: z.string().min(1),
2704
+ role: z.string().optional().default("unknown"),
2705
+ model: z.string().min(1),
2706
+ scope: MemoryScopeSchema,
2707
+ ttlDays: z.number().min(1).optional(),
2708
+ supersedes: z.string().uuid().optional(),
2709
+ tags: z.array(z.string()).optional(),
2710
+ metadata: z.record(z.string(), z.any()).optional(),
2711
+ is_global: z.boolean().default(false),
2712
+ structured: z.boolean().default(false)
2713
+ });
2714
+ var MemoryUpdateSchema = z.object({
2715
+ id: z.string().uuid(),
2716
+ type: MemoryTypeSchema.optional(),
2717
+ title: z.string().min(3).max(255).optional(),
2718
+ content: z.string().min(10).optional(),
2719
+ importance: z.number().min(1).max(5).optional(),
2720
+ agent: z.string().optional(),
2721
+ role: z.string().optional(),
2722
+ status: z.enum(["active", "archived"]).optional(),
2723
+ supersedes: z.string().uuid().optional(),
2724
+ tags: z.array(z.string()).optional(),
2725
+ metadata: z.record(z.string(), z.any()).optional(),
2726
+ is_global: z.boolean().optional(),
2727
+ completed_at: z.string().optional(),
2728
+ structured: z.boolean().default(false)
2729
+ }).refine(
2730
+ (data) => data.type !== void 0 || data.content !== void 0 || data.title !== void 0 || data.importance !== void 0 || data.status !== void 0 || data.supersedes !== void 0 || data.tags !== void 0 || data.metadata !== void 0 || data.is_global !== void 0 || data.agent !== void 0 || data.role !== void 0 || data.completed_at !== void 0,
2731
+ { message: "At least one field must be provided for update" }
2732
+ );
2733
+ var MemorySearchSchema = z.object({
2734
+ query: z.string().min(3),
2735
+ prompt: z.string().optional(),
2736
+ repo: z.string().min(1).transform(normalizeRepo),
2737
+ types: z.array(MemoryTypeSchema).optional(),
2738
+ minImportance: z.number().min(1).max(5).optional(),
2739
+ limit: z.number().min(1).max(100).default(5),
2740
+ offset: z.number().min(0).default(0),
2741
+ includeRecap: z.boolean().default(false),
2742
+ current_file_path: z.string().optional(),
2743
+ include_archived: z.boolean().default(false),
2744
+ current_tags: z.array(z.string()).optional(),
2745
+ scope: MemoryScopeSchema.partial().optional(),
2746
+ structured: z.boolean().default(false)
2747
+ });
2748
+ var MemoryAcknowledgeSchema = z.object({
2749
+ memory_id: z.string().uuid(),
2750
+ status: z.enum(["used", "irrelevant", "contradictory"]),
2751
+ application_context: z.string().min(10).optional(),
2752
+ structured: z.boolean().default(false)
2753
+ });
2754
+ var MemoryRecapSchema = z.object({
2755
+ repo: z.string().min(1).transform(normalizeRepo),
2756
+ limit: z.number().min(1).max(50).default(20),
2757
+ offset: z.number().min(0).default(0),
2758
+ structured: z.boolean().default(false)
2759
+ });
2760
+ var MemoryDeleteSchema = z.object({
2761
+ repo: z.string().min(1).transform(normalizeRepo).optional(),
2762
+ id: z.string().uuid().optional(),
2763
+ ids: z.array(z.string().uuid()).min(1).optional(),
2764
+ structured: z.boolean().default(false)
2765
+ }).refine((data) => data.id !== void 0 || data.ids !== void 0, {
2766
+ message: "Either 'id' or 'ids' must be provided for deletion"
2767
+ });
2768
+ var MemorySummarizeSchema = z.object({
2769
+ repo: z.string().min(1).transform(normalizeRepo),
2770
+ signals: z.array(z.string().max(200)).min(1),
2771
+ structured: z.boolean().default(false)
2772
+ });
2773
+ var MemorySynthesizeSchema = z.object({
2774
+ repo: z.string().min(1).transform(normalizeRepo).optional(),
2775
+ objective: z.string().min(5),
2776
+ current_file_path: z.string().optional(),
2777
+ include_summary: z.boolean().default(true),
2778
+ include_tasks: z.boolean().default(true),
2779
+ use_tools: z.boolean().default(true),
2780
+ max_iterations: z.number().int().min(1).max(5).default(3),
2781
+ max_tokens: z.number().int().min(128).max(4e3).default(1200),
2782
+ structured: z.boolean().default(false)
2783
+ });
2784
+ var TaskStatusSchema = z.enum(["backlog", "pending", "in_progress", "completed", "canceled", "blocked"]);
2785
+ var TaskPrioritySchema = z.number().min(1).max(5);
2786
+ var SingleTaskCreateSchema = z.object({
2787
+ task_code: z.string().min(1),
2788
+ phase: z.string().min(1),
2789
+ title: z.string().min(3).max(100),
2790
+ description: z.string().min(1),
2791
+ status: TaskStatusSchema.default("backlog"),
2792
+ priority: TaskPrioritySchema.default(3),
2793
+ agent: z.string().optional(),
2794
+ role: z.string().optional(),
2795
+ doc_path: z.string().optional(),
2796
+ tags: z.array(z.string()).optional(),
2797
+ metadata: z.record(z.string(), z.any()).optional(),
2798
+ parent_id: z.string().uuid().optional(),
2799
+ depends_on: z.string().uuid().optional(),
2800
+ est_tokens: z.number().int().min(0).optional()
2801
+ });
2802
+ var TaskCreateSchema = z.object({
2803
+ repo: z.string().min(1).transform(normalizeRepo),
2804
+ // Allow single task fields at top level (backward compatibility & single use)
2805
+ task_code: z.string().min(1).optional(),
2806
+ phase: z.string().min(1).optional(),
2807
+ title: z.string().min(3).max(100).optional(),
2808
+ description: z.string().min(1).optional(),
2809
+ status: TaskStatusSchema.optional(),
2810
+ priority: TaskPrioritySchema.optional(),
2811
+ agent: z.string().optional(),
2812
+ role: z.string().optional(),
2813
+ doc_path: z.string().optional(),
2814
+ tags: z.array(z.string()).optional(),
2815
+ metadata: z.record(z.string(), z.any()).optional(),
2816
+ parent_id: z.string().uuid().optional(),
2817
+ depends_on: z.string().uuid().optional(),
2818
+ est_tokens: z.number().int().min(0).optional(),
2819
+ // Allow bulk tasks
2820
+ tasks: z.array(SingleTaskCreateSchema).min(1).optional(),
2821
+ structured: z.boolean().default(false)
2822
+ }).refine(
2823
+ (data) => {
2824
+ if (data.tasks) return true;
2825
+ return !!(data.task_code && data.phase && data.title && data.description);
2826
+ },
2827
+ { message: "Either 'tasks' array or single task fields (task_code, phase, title, description) must be provided" }
2828
+ );
2829
+ var TaskCreateInteractiveSchema = SingleTaskCreateSchema.partial().extend({
2830
+ repo: z.string().min(1).transform(normalizeRepo).optional(),
2831
+ structured: z.boolean().default(false)
2832
+ });
2833
+ var TaskUpdateSchema = z.object({
2834
+ repo: z.string().min(1).transform(normalizeRepo),
2835
+ id: z.string().uuid().optional(),
2836
+ ids: z.array(z.string().uuid()).min(1).optional(),
2837
+ task_code: z.string().optional(),
2838
+ phase: z.string().optional(),
2839
+ title: z.string().min(3).max(100).optional(),
2840
+ description: z.string().optional(),
2841
+ status: TaskStatusSchema.optional(),
2842
+ priority: TaskPrioritySchema.optional(),
2843
+ agent: z.string().min(1, "agent name is required").optional(),
2844
+ role: z.string().min(1, "agent role is required").optional(),
2845
+ model: z.string().optional(),
2846
+ comment: z.string().min(1).optional(),
2847
+ doc_path: z.string().optional(),
2848
+ tags: z.array(z.string()).optional(),
2849
+ metadata: z.record(z.string(), z.any()).optional(),
2850
+ parent_id: z.string().uuid().optional(),
2851
+ depends_on: z.string().uuid().optional(),
2852
+ est_tokens: z.number().int().min(0).optional(),
2853
+ force: z.boolean().optional(),
2854
+ structured: z.boolean().default(false)
2855
+ }).refine((data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0, {
2856
+ message: "Either 'id', 'ids', or 'task_code' must be provided for update"
2857
+ }).refine((data) => Object.keys(data).length > 2, {
2858
+ message: "At least one field besides repo and id/ids must be provided for update"
2859
+ });
2860
+ var TaskListSchema = z.object({
2861
+ repo: z.string().min(1).transform(normalizeRepo),
2862
+ status: z.string().optional(),
2863
+ phase: z.string().optional(),
2864
+ query: z.string().optional(),
2865
+ limit: z.number().min(1).max(100).default(15),
2866
+ offset: z.number().min(0).default(0),
2867
+ structured: z.boolean().default(false)
2868
+ });
2869
+ var TaskSearchSchema = z.object({
2870
+ repo: z.string().min(1).transform(normalizeRepo),
2871
+ query: z.string().min(1),
2872
+ status: z.string().optional(),
2873
+ limit: z.number().min(1).max(100).default(10),
2874
+ offset: z.number().min(0).default(0),
2875
+ structured: z.boolean().default(false)
2876
+ });
2877
+ var TaskDeleteSchema = z.object({
2878
+ repo: z.string().min(1).transform(normalizeRepo),
2879
+ id: z.string().uuid().optional(),
2880
+ ids: z.array(z.string().uuid()).min(1).optional(),
2881
+ structured: z.boolean().default(false)
2882
+ }).refine((data) => data.id !== void 0 || data.ids !== void 0, {
2883
+ message: "Either 'id' or 'ids' must be provided for deletion"
2884
+ });
2885
+ var MemoryDetailSchema = z.object({
2886
+ id: z.string().uuid().optional(),
2887
+ code: z.string().max(20).optional(),
2888
+ structured: z.boolean().default(false)
2889
+ }).refine((data) => data.id !== void 0 || data.code !== void 0, {
2890
+ message: "Either id or code must be provided"
2891
+ });
2892
+ var TaskGetSchema = z.object({
2893
+ repo: z.string().min(1).transform(normalizeRepo),
2894
+ id: z.string().uuid().optional(),
2895
+ task_code: z.string().optional(),
2896
+ structured: z.boolean().default(false)
2897
+ }).refine((data) => data.id !== void 0 || data.task_code !== void 0, {
2898
+ message: "Either id or task_code must be provided"
2899
+ });
2900
+ var HandoffStatusSchema = z.enum(["pending", "accepted", "rejected", "expired"]);
2901
+ var HandoffCreateSchema = z.object({
2902
+ repo: z.string().min(1).transform(normalizeRepo),
2903
+ from_agent: z.string().min(1),
2904
+ to_agent: z.string().min(1).optional(),
2905
+ task_id: z.string().uuid().optional(),
2906
+ task_code: z.string().optional(),
2907
+ summary: z.string().min(1),
2908
+ context: z.record(z.string(), z.any()).optional(),
2909
+ expires_at: z.string().optional(),
2910
+ structured: z.boolean().default(false)
2911
+ }).refine((data) => !(data.task_id && data.task_code), {
2912
+ message: "Provide either task_id or task_code, not both"
2913
+ }).refine((data) => data.to_agent || data.task_id || data.task_code || data.context?.next_steps || data.context?.blockers || data.context?.remaining_work, {
2914
+ message: "Handoffs must identify a target agent, linked task, next_steps, blockers, or remaining_work. Do not create pending handoffs for completed-work summaries."
2915
+ });
2916
+ var HandoffUpdateSchema = z.object({
2917
+ id: z.string().uuid(),
2918
+ status: HandoffStatusSchema,
2919
+ structured: z.boolean().default(false)
2920
+ });
2921
+ var HandoffListSchema = z.object({
2922
+ repo: z.string().min(1).transform(normalizeRepo),
2923
+ status: HandoffStatusSchema.optional(),
2924
+ from_agent: z.string().min(1).optional(),
2925
+ to_agent: z.string().min(1).optional(),
2926
+ limit: z.number().min(1).max(100).default(20),
2927
+ offset: z.number().min(0).default(0),
2928
+ structured: z.boolean().default(false)
2929
+ });
2930
+ var TaskClaimSchema = z.object({
2931
+ repo: z.string().min(1).transform(normalizeRepo),
2932
+ task_id: z.string().uuid().optional(),
2933
+ task_code: z.string().optional(),
2934
+ agent: z.string().min(1),
2935
+ role: z.string().optional(),
2936
+ metadata: z.record(z.string(), z.any()).optional(),
2937
+ structured: z.boolean().default(false)
2938
+ }).refine((data) => data.task_id !== void 0 || data.task_code !== void 0, {
2939
+ message: "Either task_id or task_code must be provided"
2940
+ }).refine((data) => !(data.task_id && data.task_code), {
2941
+ message: "Provide either task_id or task_code, not both"
2942
+ });
2943
+ var StandardStoreSchema = z.object({
2944
+ name: z.string().min(3).max(255),
2945
+ content: z.string().min(10),
2946
+ context: z.string().optional(),
2947
+ version: z.string().optional(),
2948
+ language: z.string().optional(),
2949
+ stack: z.array(z.string()).optional(),
2950
+ repo: z.string().optional(),
2951
+ is_global: z.boolean().optional(),
2952
+ tags: z.array(z.string()).optional(),
2953
+ metadata: z.record(z.string(), z.any()).optional(),
2954
+ agent: z.string().optional(),
2955
+ model: z.string().optional(),
2956
+ structured: z.boolean().default(false)
2957
+ });
2958
+ var StandardSearchSchema = z.object({
2959
+ query: z.string().optional(),
2960
+ stack: z.array(z.string()).optional(),
2961
+ language: z.string().optional(),
2962
+ version: z.string().optional(),
2963
+ repo: z.string().optional(),
2964
+ is_global: z.boolean().optional(),
2965
+ limit: z.number().min(1).max(100).default(20),
2966
+ offset: z.number().min(0).default(0),
2967
+ structured: z.boolean().default(false)
2968
+ });
2969
+ var TOOL_DEFINITIONS = [
2970
+ {
2971
+ name: "memory-synthesize",
2972
+ title: "Memory Synthesize",
2973
+ description: "Use client sampling to synthesize a grounded answer from local memory and tasks. Best for project briefings, tradeoff summaries, and context-aware answers.",
2974
+ annotations: {
2975
+ readOnlyHint: true,
2976
+ idempotentHint: true,
2977
+ openWorldHint: false
2978
+ },
2979
+ execution: {
2980
+ taskSupport: "optional"
2981
+ },
2982
+ inputSchema: {
2983
+ type: "object",
2984
+ properties: {
2985
+ repo: { type: "string", description: "Repository name. Optional when a single MCP root is active." },
2986
+ objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
2987
+ current_file_path: {
2988
+ type: "string",
2989
+ description: "Optional absolute file path for workspace-local grounding."
2990
+ },
2991
+ include_summary: { type: "boolean", default: true },
2992
+ include_tasks: { type: "boolean", default: true },
2993
+ use_tools: {
2994
+ type: "boolean",
2995
+ default: true,
2996
+ description: "Allow the sampled model to call local memory/task tools during synthesis when the client supports sampling.tools."
2997
+ },
2998
+ max_iterations: { type: "number", minimum: 1, maximum: 5, default: 3 },
2999
+ max_tokens: { type: "number", minimum: 128, maximum: 4e3, default: 1200 },
3000
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON results." }
3001
+ },
3002
+ required: ["objective"]
3003
+ },
3004
+ outputSchema: {
3005
+ type: "object",
3006
+ properties: {
3007
+ repo: { type: "string" },
3008
+ objective: { type: "string" },
3009
+ answer: { type: "string" },
3010
+ model: { type: "string" },
3011
+ stopReason: { type: "string" },
3012
+ iterations: { type: "number" },
3013
+ toolCalls: { type: "number" }
3014
+ },
3015
+ required: ["repo", "objective", "answer", "iterations", "toolCalls"]
2983
3016
  }
2984
- if (repoPath === "tasks") {
2985
- const status = query.get("status");
2986
- const priority = query.get("priority");
2987
- let tasks;
2988
- if (status && status !== "all") {
2989
- const statuses = status.split(",").map((s) => s.trim());
2990
- tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
2991
- } else {
2992
- tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
2993
- }
2994
- if (priority) {
2995
- const p = Number(priority);
2996
- if (!isNaN(p)) {
2997
- tasks = tasks.filter((t) => t.priority === p);
2998
- }
3017
+ },
3018
+ {
3019
+ name: "task-create-interactive",
3020
+ title: "Interactive Task Create",
3021
+ 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.",
3022
+ annotations: {
3023
+ readOnlyHint: false,
3024
+ idempotentHint: false,
3025
+ destructiveHint: false,
3026
+ openWorldHint: false
3027
+ },
3028
+ inputSchema: {
3029
+ type: "object",
3030
+ properties: {
3031
+ repo: {
3032
+ type: "string",
3033
+ description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
3034
+ },
3035
+ task_code: { type: "string" },
3036
+ phase: { type: "string" },
3037
+ title: { type: "string", minLength: 3, maxLength: 100 },
3038
+ description: { type: "string", minLength: 1 },
3039
+ status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3040
+ priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3041
+ agent: { type: "string" },
3042
+ role: { type: "string" },
3043
+ doc_path: { type: "string" },
3044
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
2999
3045
  }
3000
- const payload = JSON.stringify(tasks, null, 2);
3001
- return {
3002
- contents: [
3003
- {
3004
- uri,
3005
- mimeType: "application/json",
3006
- text: payload,
3007
- size: Buffer.byteLength(payload, "utf8"),
3008
- annotations: {
3009
- audience: ["assistant"],
3010
- priority: 0.9,
3011
- lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
3012
- }
3013
- }
3014
- ]
3015
- };
3016
- }
3017
- if (repoPath === "actions") {
3018
- const actions = db.actions.getRecentActions(name, 100);
3019
- const payload = JSON.stringify(actions, null, 2);
3020
- return {
3021
- contents: [
3022
- {
3023
- uri,
3024
- mimeType: "application/json",
3025
- text: payload,
3026
- size: Buffer.byteLength(payload, "utf8"),
3027
- annotations: {
3028
- audience: ["assistant"],
3029
- priority: 0.6,
3030
- lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
3031
- }
3032
- }
3033
- ]
3034
- };
3046
+ },
3047
+ outputSchema: {
3048
+ type: "object",
3049
+ properties: {
3050
+ repo: { type: "string" },
3051
+ task_code: { type: "string" },
3052
+ phase: { type: "string" },
3053
+ title: { type: "string" },
3054
+ status: { type: "string" },
3055
+ priority: { type: "number" }
3056
+ },
3057
+ required: ["repo", "task_code", "phase", "title", "status", "priority"]
3035
3058
  }
3036
- }
3037
- const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
3038
- if (actionIdMatch) {
3039
- const id = Number(actionIdMatch[1]);
3040
- const action = db.actions.getActionById(id);
3041
- if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
3042
- const payload = JSON.stringify(action, null, 2);
3043
- return {
3044
- contents: [
3045
- {
3046
- uri,
3047
- mimeType: "application/json",
3048
- text: payload,
3049
- size: Buffer.byteLength(payload, "utf8"),
3050
- annotations: {
3051
- audience: ["assistant"],
3052
- priority: 0.55,
3053
- lastModified: action.created_at
3054
- }
3055
- }
3056
- ]
3057
- };
3058
- }
3059
- throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
3060
- }
3061
- function parseRepoUri(uri) {
3062
- const prefix = "repository://";
3063
- if (!uri.startsWith(prefix)) return null;
3064
- const rest = uri.slice(prefix.length);
3065
- const queryStart = rest.indexOf("?");
3066
- const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
3067
- const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
3068
- const slashIdx = withoutQuery.indexOf("/");
3069
- if (slashIdx === -1) return null;
3070
- const name = withoutQuery.slice(0, slashIdx);
3071
- const path6 = withoutQuery.slice(slashIdx + 1);
3072
- if (!name || !path6) return null;
3073
- return { name, path: path6, query: new URLSearchParams(queryString) };
3074
- }
3075
- function paginateEntries(key, entries, params) {
3076
- const limit = normalizeLimit(params?.limit);
3077
- const offset = decodeCursor(params?.cursor);
3078
- const sliced = entries.slice(offset, offset + limit);
3079
- const nextOffset = offset + sliced.length;
3080
- return {
3081
- [key]: sliced,
3082
- nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
3083
- };
3084
- }
3085
- function normalizeLimit(limit) {
3086
- if (typeof limit !== "number" || !Number.isFinite(limit)) {
3087
- return DEFAULT_PAGE_SIZE;
3088
- }
3089
- return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
3090
- }
3091
- function deriveLastModifiedFromCollection(values) {
3092
- const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
3093
- return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
3094
- }
3095
- function resourceNotFound(message, uri) {
3096
- const error = new Error(message);
3097
- error.code = -32002;
3098
- error.data = { uri };
3099
- return error;
3100
- }
3101
- function invalidCompletionParams(message) {
3102
- const error = new Error(message);
3103
- error.code = -32602;
3104
- return error;
3105
- }
3106
-
3107
- // src/mcp/prompts/loader.ts
3108
- import fs4 from "fs";
3109
- import path5 from "path";
3110
- import { fileURLToPath as fileURLToPath3 } from "url";
3111
- import matter from "gray-matter";
3112
- var __filename = fileURLToPath3(import.meta.url);
3113
- var __dirname2 = path5.dirname(__filename);
3114
- function findPromptDir() {
3115
- const candidates = [
3116
- // Production if chunked into dist/
3117
- "./prompts",
3118
- // Production if inlined into dist/mcp/
3119
- "../prompts",
3120
- // Dev: /src/mcp/prompts/definitions (next to loader.ts)
3121
- "./definitions"
3122
- ].map((relPath) => path5.resolve(__dirname2, relPath));
3123
- for (const dir of candidates) {
3124
- if (fs4.existsSync(dir)) {
3125
- const files = fs4.readdirSync(dir);
3126
- if (files.some((f) => f.endsWith(".md"))) {
3127
- return dir;
3059
+ },
3060
+ {
3061
+ name: "memory-detail",
3062
+ title: "Memory Detail",
3063
+ 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.",
3064
+ inputSchema: {
3065
+ type: "object",
3066
+ properties: {
3067
+ id: { type: "string", format: "uuid", description: "Memory entry ID. Optional if code is provided." },
3068
+ code: { type: "string", description: "Short memory code. Optional if id is provided." },
3069
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3128
3070
  }
3129
3071
  }
3130
- }
3131
- return path5.resolve(__dirname2, "./definitions");
3132
- }
3133
- var PROMPT_DIR = findPromptDir();
3134
- function listPromptFiles() {
3135
- if (!fs4.existsSync(PROMPT_DIR)) return [];
3136
- return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
3137
- }
3138
- function loadPromptFromMarkdown(name) {
3139
- const filePath = path5.join(PROMPT_DIR, `${name}.md`);
3140
- if (!fs4.existsSync(filePath)) {
3141
- throw new Error(`Prompt file not found: ${filePath}`);
3142
- }
3143
- const fileContent = fs4.readFileSync(filePath, "utf-8");
3144
- const { data, content } = matter(fileContent);
3145
- return {
3146
- name: data.name || name,
3147
- description: data.description || "",
3148
- arguments: data.arguments || [],
3149
- agent: data.agent,
3150
- content: content.trim()
3151
- };
3152
- }
3153
-
3154
- // src/mcp/prompts/registry.ts
3155
- function createPromptDefinition(loaded) {
3156
- return {
3157
- name: loaded.name,
3158
- description: loaded.description,
3159
- arguments: loaded.arguments,
3160
- agent: loaded.agent,
3161
- messages: [
3162
- {
3163
- role: "user",
3164
- content: {
3165
- type: "text",
3166
- text: loaded.content
3072
+ },
3073
+ {
3074
+ name: "task-detail",
3075
+ title: "Task Detail",
3076
+ 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.",
3077
+ inputSchema: {
3078
+ type: "object",
3079
+ properties: {
3080
+ repo: { type: "string", description: "Repository name" },
3081
+ id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
3082
+ task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
3083
+ structured: {
3084
+ type: "boolean",
3085
+ default: false,
3086
+ description: "If true, returns structured JSON without the text content details."
3167
3087
  }
3168
- }
3169
- ]
3170
- };
3171
- }
3172
- var PROMPTS = {};
3173
- var promptFiles = listPromptFiles();
3174
- for (const name of promptFiles) {
3175
- try {
3176
- PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
3177
- } catch (e) {
3178
- logger.warn(`Failed to load prompt ${name}: ${e}`);
3179
- }
3180
- }
3181
- async function listPrompts(db, session, params) {
3182
- const allPrompts = Object.values(PROMPTS).map((p) => ({
3183
- name: p.name,
3184
- description: p.description,
3185
- arguments: p.arguments,
3186
- metadata: p.agent ? { agent: p.agent } : void 0
3187
- }));
3188
- const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
3189
- const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
3190
- const offset = decodeCursor(params?.cursor);
3191
- const sliced = allPrompts.slice(offset, offset + limit);
3192
- const nextOffset = offset + sliced.length;
3193
- return {
3194
- prompts: sliced,
3195
- nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
3196
- };
3197
- }
3198
- async function getPrompt(name, args = {}, db, session) {
3199
- const prompt = PROMPTS[name];
3200
- if (!prompt) {
3201
- throw new Error(`Prompt not found: ${name}`);
3202
- }
3203
- const inferredRepo = inferRepoFromSession(session);
3204
- const messages = prompt.messages.map((m) => {
3205
- let text = m.content.text;
3206
- for (const [key, value] of Object.entries(args)) {
3207
- text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
3088
+ },
3089
+ required: ["repo"]
3090
+ }
3091
+ },
3092
+ {
3093
+ name: "memory-store",
3094
+ title: "Memory Store",
3095
+ 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'.",
3096
+ annotations: {
3097
+ readOnlyHint: false,
3098
+ idempotentHint: false,
3099
+ destructiveHint: false,
3100
+ openWorldHint: false
3101
+ },
3102
+ inputSchema: {
3103
+ type: "object",
3104
+ properties: {
3105
+ type: {
3106
+ type: "string",
3107
+ enum: [
3108
+ "code_fact",
3109
+ "decision",
3110
+ "mistake",
3111
+ "pattern",
3112
+ "task_archive"
3113
+ ],
3114
+ description: "Type of durable knowledge being stored. Coordination types such as file_claim are intentionally unsupported."
3115
+ },
3116
+ title: {
3117
+ type: "string",
3118
+ minLength: 3,
3119
+ maxLength: 100,
3120
+ description: "Short human-readable title for the memory. Do not embed bracketed metadata like agent/role/date prefixes here."
3121
+ },
3122
+ content: {
3123
+ type: "string",
3124
+ minLength: 10,
3125
+ description: "The memory content"
3126
+ },
3127
+ importance: {
3128
+ type: "number",
3129
+ minimum: 1,
3130
+ maximum: 5,
3131
+ description: "Importance score (1-5)"
3132
+ },
3133
+ agent: {
3134
+ type: "string",
3135
+ description: "Name of the agent creating this memory"
3136
+ },
3137
+ role: {
3138
+ type: "string",
3139
+ description: "Role of the agent creating this memory"
3140
+ },
3141
+ model: {
3142
+ type: "string",
3143
+ description: "AI model used by the agent"
3144
+ },
3145
+ scope: {
3146
+ type: "object",
3147
+ properties: {
3148
+ repo: { type: "string", description: "Repository name" },
3149
+ branch: { type: "string" },
3150
+ folder: { type: "string" },
3151
+ language: { type: "string" }
3152
+ },
3153
+ required: ["repo"]
3154
+ },
3155
+ tags: {
3156
+ type: "array",
3157
+ items: { type: "string" },
3158
+ description: "Technology stack tags (e.g., ['filament', 'laravel'])"
3159
+ },
3160
+ metadata: {
3161
+ type: "object",
3162
+ description: "Structured metadata for non-title context such as source agent, claim fields, or timestamps"
3163
+ },
3164
+ is_global: {
3165
+ type: "boolean",
3166
+ description: "If true, this memory is shared across all repositories"
3167
+ },
3168
+ ttlDays: { type: "number", minimum: 1 },
3169
+ supersedes: { type: "string", format: "uuid" },
3170
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the stored memory." }
3171
+ },
3172
+ required: ["type", "title", "content", "importance", "scope", "agent", "model"]
3173
+ },
3174
+ outputSchema: {
3175
+ type: "object",
3176
+ properties: {
3177
+ success: { type: "boolean" },
3178
+ id: { type: "string" },
3179
+ code: { type: "string" },
3180
+ repo: { type: "string" },
3181
+ type: { type: "string" },
3182
+ title: { type: "string" },
3183
+ error: { type: "string" },
3184
+ message: { type: "string" }
3185
+ },
3186
+ required: ["success"]
3187
+ }
3188
+ },
3189
+ {
3190
+ name: "memory-acknowledge",
3191
+ title: "Memory Acknowledge",
3192
+ description: "Acknowledge the use of a memory or report its irrelevance/contradiction. Mandatory after using memory to generate code.",
3193
+ annotations: {
3194
+ readOnlyHint: false,
3195
+ idempotentHint: false,
3196
+ openWorldHint: false
3197
+ },
3198
+ inputSchema: {
3199
+ type: "object",
3200
+ properties: {
3201
+ memory_id: { type: "string", format: "uuid" },
3202
+ status: { type: "string", enum: ["used", "irrelevant", "contradictory"] },
3203
+ application_context: { type: "string", minLength: 10 },
3204
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3205
+ },
3206
+ required: ["memory_id", "status"]
3207
+ },
3208
+ outputSchema: {
3209
+ type: "object",
3210
+ properties: {
3211
+ success: { type: "boolean" },
3212
+ id: { type: "string" },
3213
+ status: { type: "string" }
3214
+ },
3215
+ required: ["success", "id", "status"]
3216
+ }
3217
+ },
3218
+ {
3219
+ name: "memory-update",
3220
+ title: "Memory Update",
3221
+ description: "Update an existing memory entry. Keep 'title' concise and move agent/role/date or claim context into 'metadata' instead of the title.",
3222
+ annotations: {
3223
+ readOnlyHint: false,
3224
+ idempotentHint: false,
3225
+ destructiveHint: false,
3226
+ openWorldHint: false
3227
+ },
3228
+ inputSchema: {
3229
+ type: "object",
3230
+ properties: {
3231
+ id: { type: "string", format: "uuid" },
3232
+ type: {
3233
+ type: "string",
3234
+ enum: [
3235
+ "code_fact",
3236
+ "decision",
3237
+ "mistake",
3238
+ "pattern",
3239
+ "task_archive"
3240
+ ]
3241
+ },
3242
+ title: { type: "string", minLength: 3, maxLength: 100 },
3243
+ content: { type: "string", minLength: 10 },
3244
+ importance: { type: "number", minimum: 1, maximum: 5 },
3245
+ agent: { type: "string" },
3246
+ role: { type: "string" },
3247
+ status: { type: "string", enum: ["active", "archived"] },
3248
+ supersedes: { type: "string", format: "uuid" },
3249
+ tags: { type: "array", items: { type: "string" } },
3250
+ metadata: { type: "object" },
3251
+ is_global: { type: "boolean" },
3252
+ completed_at: { type: "string" },
3253
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the updated memory." }
3254
+ },
3255
+ required: ["id"]
3256
+ },
3257
+ outputSchema: {
3258
+ type: "object",
3259
+ properties: {
3260
+ success: { type: "boolean" },
3261
+ id: { type: "string" },
3262
+ repo: { type: "string" },
3263
+ updatedFields: {
3264
+ type: "array",
3265
+ items: { type: "string" }
3266
+ }
3267
+ },
3268
+ required: ["success", "id", "repo", "updatedFields"]
3269
+ }
3270
+ },
3271
+ {
3272
+ name: "memory-search",
3273
+ title: "Memory Search",
3274
+ 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.",
3275
+ annotations: {
3276
+ readOnlyHint: true,
3277
+ idempotentHint: true,
3278
+ openWorldHint: false
3279
+ },
3280
+ inputSchema: {
3281
+ type: "object",
3282
+ properties: {
3283
+ query: { type: "string", minLength: 3 },
3284
+ prompt: { type: "string" },
3285
+ repo: { type: "string" },
3286
+ current_tags: {
3287
+ type: "array",
3288
+ items: { type: "string" },
3289
+ description: "Active tech stack tags (e.g., ['filament', 'react'])"
3290
+ },
3291
+ types: {
3292
+ type: "array",
3293
+ items: {
3294
+ type: "string",
3295
+ enum: [
3296
+ "code_fact",
3297
+ "decision",
3298
+ "mistake",
3299
+ "pattern",
3300
+ "task_archive"
3301
+ ]
3302
+ }
3303
+ },
3304
+ minImportance: { type: "number", minimum: 1, maximum: 5 },
3305
+ limit: { type: "number", minimum: 1, maximum: 100, default: 5 },
3306
+ offset: { type: "number", minimum: 0, default: 0 },
3307
+ includeRecap: { type: "boolean", default: false },
3308
+ current_file_path: { type: "string" },
3309
+ include_archived: { type: "boolean", default: false },
3310
+ scope: {
3311
+ type: "object",
3312
+ properties: {
3313
+ repo: { type: "string" },
3314
+ branch: { type: "string" },
3315
+ folder: { type: "string" },
3316
+ language: { type: "string" }
3317
+ }
3318
+ },
3319
+ structured: {
3320
+ type: "boolean",
3321
+ default: false,
3322
+ description: "If true, returns structured JSON without the text content summary."
3323
+ }
3324
+ },
3325
+ required: ["query", "repo"]
3326
+ },
3327
+ outputSchema: {
3328
+ type: "object",
3329
+ properties: {
3330
+ schema: { type: "string", enum: ["memory-search"] },
3331
+ query: { type: "string" },
3332
+ count: { type: "number", description: "Number of rows returned" },
3333
+ total: { type: "number", description: "Total matching memories" },
3334
+ offset: { type: "number" },
3335
+ limit: { type: "number" },
3336
+ results: {
3337
+ type: "object",
3338
+ properties: {
3339
+ columns: {
3340
+ type: "array",
3341
+ items: { type: "string" },
3342
+ description: "Column names: [id, title, type, importance]"
3343
+ },
3344
+ rows: {
3345
+ type: "array",
3346
+ items: { type: "array" },
3347
+ description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
3348
+ }
3349
+ },
3350
+ required: ["columns", "rows"]
3351
+ }
3352
+ },
3353
+ required: ["schema", "query", "count", "total", "offset", "limit", "results"]
3208
3354
  }
3209
- text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
3210
- return {
3211
- ...m,
3212
- content: {
3213
- ...m.content,
3214
- text
3215
- }
3216
- };
3217
- });
3218
- return {
3219
- description: prompt.description,
3220
- messages,
3221
- metadata: prompt.agent ? { agent: prompt.agent } : void 0
3222
- };
3223
- }
3224
- async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
3225
- void name;
3226
- void contextArguments;
3227
- if (argName === "task_id") {
3228
- const values = dataSources.tasks.map((t) => t.id);
3229
- return rankCompletionValues(values, value);
3230
- }
3231
- return [];
3232
- }
3233
-
3234
- // src/mcp/tools/schemas.ts
3235
- import { z } from "zod";
3236
- var MemoryScopeSchema = z.object({
3237
- repo: z.string().min(1).transform(normalizeRepo),
3238
- branch: z.string().optional(),
3239
- folder: z.string().optional(),
3240
- language: z.string().optional()
3241
- });
3242
- var MemoryTypeSchema = z.enum([
3243
- "code_fact",
3244
- "decision",
3245
- "mistake",
3246
- "pattern",
3247
- "file_claim",
3248
- "task_archive"
3249
- ]);
3250
- var MemoryStoreSchema = z.object({
3251
- code: z.string().max(20).optional(),
3252
- type: MemoryTypeSchema,
3253
- title: z.string().min(3).max(255),
3254
- content: z.string().min(10),
3255
- importance: z.number().min(1).max(5),
3256
- agent: z.string().min(1),
3257
- role: z.string().optional().default("unknown"),
3258
- model: z.string().min(1),
3259
- scope: MemoryScopeSchema,
3260
- ttlDays: z.number().min(1).optional(),
3261
- supersedes: z.string().uuid().optional(),
3262
- tags: z.array(z.string()).optional(),
3263
- metadata: z.record(z.string(), z.any()).optional(),
3264
- is_global: z.boolean().default(false),
3265
- structured: z.boolean().default(false)
3266
- });
3267
- var MemoryUpdateSchema = z.object({
3268
- id: z.string().uuid(),
3269
- type: MemoryTypeSchema.optional(),
3270
- title: z.string().min(3).max(255).optional(),
3271
- content: z.string().min(10).optional(),
3272
- importance: z.number().min(1).max(5).optional(),
3273
- agent: z.string().optional(),
3274
- role: z.string().optional(),
3275
- status: z.enum(["active", "archived"]).optional(),
3276
- supersedes: z.string().uuid().optional(),
3277
- tags: z.array(z.string()).optional(),
3278
- metadata: z.record(z.string(), z.any()).optional(),
3279
- is_global: z.boolean().optional(),
3280
- completed_at: z.string().optional(),
3281
- structured: z.boolean().default(false)
3282
- }).refine(
3283
- (data) => data.type !== void 0 || data.content !== void 0 || data.title !== void 0 || data.importance !== void 0 || data.status !== void 0 || data.supersedes !== void 0 || data.tags !== void 0 || data.metadata !== void 0 || data.is_global !== void 0 || data.agent !== void 0 || data.role !== void 0 || data.completed_at !== void 0,
3284
- { message: "At least one field must be provided for update" }
3285
- );
3286
- var MemorySearchSchema = z.object({
3287
- query: z.string().min(3),
3288
- prompt: z.string().optional(),
3289
- repo: z.string().min(1).transform(normalizeRepo),
3290
- types: z.array(MemoryTypeSchema).optional(),
3291
- minImportance: z.number().min(1).max(5).optional(),
3292
- limit: z.number().min(1).max(100).default(5),
3293
- offset: z.number().min(0).default(0),
3294
- includeRecap: z.boolean().default(false),
3295
- current_file_path: z.string().optional(),
3296
- include_archived: z.boolean().default(false),
3297
- current_tags: z.array(z.string()).optional(),
3298
- scope: MemoryScopeSchema.partial().optional(),
3299
- structured: z.boolean().default(false)
3300
- });
3301
- var MemoryAcknowledgeSchema = z.object({
3302
- memory_id: z.string().uuid(),
3303
- status: z.enum(["used", "irrelevant", "contradictory"]),
3304
- application_context: z.string().min(10).optional(),
3305
- structured: z.boolean().default(false)
3306
- });
3307
- var MemoryRecapSchema = z.object({
3308
- repo: z.string().min(1).transform(normalizeRepo),
3309
- limit: z.number().min(1).max(50).default(20),
3310
- offset: z.number().min(0).default(0),
3311
- structured: z.boolean().default(false)
3312
- });
3313
- var MemoryDeleteSchema = z.object({
3314
- repo: z.string().min(1).transform(normalizeRepo).optional(),
3315
- id: z.string().uuid().optional(),
3316
- ids: z.array(z.string().uuid()).min(1).optional(),
3317
- structured: z.boolean().default(false)
3318
- }).refine((data) => data.id !== void 0 || data.ids !== void 0, {
3319
- message: "Either 'id' or 'ids' must be provided for deletion"
3320
- });
3321
- var MemorySummarizeSchema = z.object({
3322
- repo: z.string().min(1).transform(normalizeRepo),
3323
- signals: z.array(z.string().max(200)).min(1),
3324
- structured: z.boolean().default(false)
3325
- });
3326
- var MemorySynthesizeSchema = z.object({
3327
- repo: z.string().min(1).transform(normalizeRepo).optional(),
3328
- objective: z.string().min(5),
3329
- current_file_path: z.string().optional(),
3330
- include_summary: z.boolean().default(true),
3331
- include_tasks: z.boolean().default(true),
3332
- use_tools: z.boolean().default(true),
3333
- max_iterations: z.number().int().min(1).max(5).default(3),
3334
- max_tokens: z.number().int().min(128).max(4e3).default(1200),
3335
- structured: z.boolean().default(false)
3336
- });
3337
- var TaskStatusSchema = z.enum(["backlog", "pending", "in_progress", "completed", "canceled", "blocked"]);
3338
- var TaskPrioritySchema = z.number().min(1).max(5);
3339
- var SingleTaskCreateSchema = z.object({
3340
- task_code: z.string().min(1),
3341
- phase: z.string().min(1),
3342
- title: z.string().min(3).max(100),
3343
- description: z.string().min(1),
3344
- status: TaskStatusSchema.default("backlog"),
3345
- priority: TaskPrioritySchema.default(3),
3346
- agent: z.string().optional(),
3347
- role: z.string().optional(),
3348
- doc_path: z.string().optional(),
3349
- tags: z.array(z.string()).optional(),
3350
- metadata: z.record(z.string(), z.any()).optional(),
3351
- parent_id: z.string().uuid().optional(),
3352
- depends_on: z.string().uuid().optional(),
3353
- est_tokens: z.number().int().min(0).optional()
3354
- });
3355
- var TaskCreateSchema = z.object({
3356
- repo: z.string().min(1).transform(normalizeRepo),
3357
- // Allow single task fields at top level (backward compatibility & single use)
3358
- task_code: z.string().min(1).optional(),
3359
- phase: z.string().min(1).optional(),
3360
- title: z.string().min(3).max(100).optional(),
3361
- description: z.string().min(1).optional(),
3362
- status: TaskStatusSchema.optional(),
3363
- priority: TaskPrioritySchema.optional(),
3364
- agent: z.string().optional(),
3365
- role: z.string().optional(),
3366
- doc_path: z.string().optional(),
3367
- tags: z.array(z.string()).optional(),
3368
- metadata: z.record(z.string(), z.any()).optional(),
3369
- parent_id: z.string().uuid().optional(),
3370
- depends_on: z.string().uuid().optional(),
3371
- est_tokens: z.number().int().min(0).optional(),
3372
- // Allow bulk tasks
3373
- tasks: z.array(SingleTaskCreateSchema).min(1).optional(),
3374
- structured: z.boolean().default(false)
3375
- }).refine(
3376
- (data) => {
3377
- if (data.tasks) return true;
3378
- return !!(data.task_code && data.phase && data.title && data.description);
3379
3355
  },
3380
- { message: "Either 'tasks' array or single task fields (task_code, phase, title, description) must be provided" }
3381
- );
3382
- var TaskCreateInteractiveSchema = SingleTaskCreateSchema.partial().extend({
3383
- repo: z.string().min(1).transform(normalizeRepo).optional(),
3384
- structured: z.boolean().default(false)
3385
- });
3386
- var TaskUpdateSchema = z.object({
3387
- repo: z.string().min(1).transform(normalizeRepo),
3388
- id: z.string().uuid().optional(),
3389
- ids: z.array(z.string().uuid()).min(1).optional(),
3390
- task_code: z.string().optional(),
3391
- phase: z.string().optional(),
3392
- title: z.string().min(3).max(100).optional(),
3393
- description: z.string().optional(),
3394
- status: TaskStatusSchema.optional(),
3395
- priority: TaskPrioritySchema.optional(),
3396
- agent: z.string().min(1, "agent name is required").optional(),
3397
- role: z.string().min(1, "agent role is required").optional(),
3398
- model: z.string().optional(),
3399
- comment: z.string().min(1).optional(),
3400
- doc_path: z.string().optional(),
3401
- tags: z.array(z.string()).optional(),
3402
- metadata: z.record(z.string(), z.any()).optional(),
3403
- parent_id: z.string().uuid().optional(),
3404
- depends_on: z.string().uuid().optional(),
3405
- est_tokens: z.number().int().min(0).optional(),
3406
- force: z.boolean().optional(),
3407
- structured: z.boolean().default(false)
3408
- }).refine((data) => data.id !== void 0 || data.ids !== void 0 || data.task_code !== void 0, {
3409
- message: "Either 'id', 'ids', or 'task_code' must be provided for update"
3410
- }).refine((data) => Object.keys(data).length > 2, {
3411
- message: "At least one field besides repo and id/ids must be provided for update"
3412
- });
3413
- var TaskListSchema = z.object({
3414
- repo: z.string().min(1).transform(normalizeRepo),
3415
- status: z.string().optional(),
3416
- phase: z.string().optional(),
3417
- query: z.string().optional(),
3418
- limit: z.number().min(1).max(100).default(15),
3419
- offset: z.number().min(0).default(0),
3420
- structured: z.boolean().default(false)
3421
- });
3422
- var TaskSearchSchema = z.object({
3423
- repo: z.string().min(1).transform(normalizeRepo),
3424
- query: z.string().min(1),
3425
- status: z.string().optional(),
3426
- limit: z.number().min(1).max(100).default(10),
3427
- offset: z.number().min(0).default(0),
3428
- structured: z.boolean().default(false)
3429
- });
3430
- var TaskDeleteSchema = z.object({
3431
- repo: z.string().min(1).transform(normalizeRepo),
3432
- id: z.string().uuid().optional(),
3433
- ids: z.array(z.string().uuid()).min(1).optional(),
3434
- structured: z.boolean().default(false)
3435
- }).refine((data) => data.id !== void 0 || data.ids !== void 0, {
3436
- message: "Either 'id' or 'ids' must be provided for deletion"
3437
- });
3438
- var MemoryDetailSchema = z.object({
3439
- id: z.string().uuid().optional(),
3440
- code: z.string().max(20).optional(),
3441
- structured: z.boolean().default(false)
3442
- }).refine((data) => data.id !== void 0 || data.code !== void 0, {
3443
- message: "Either id or code must be provided"
3444
- });
3445
- var TaskGetSchema = z.object({
3446
- repo: z.string().min(1).transform(normalizeRepo),
3447
- id: z.string().uuid().optional(),
3448
- task_code: z.string().optional(),
3449
- structured: z.boolean().default(false)
3450
- }).refine((data) => data.id !== void 0 || data.task_code !== void 0, {
3451
- message: "Either id or task_code must be provided"
3452
- });
3453
- var HandoffStatusSchema = z.enum(["pending", "accepted", "rejected", "expired"]);
3454
- var HandoffCreateSchema = z.object({
3455
- repo: z.string().min(1).transform(normalizeRepo),
3456
- from_agent: z.string().min(1),
3457
- to_agent: z.string().min(1).optional(),
3458
- task_id: z.string().uuid().optional(),
3459
- task_code: z.string().optional(),
3460
- summary: z.string().min(1),
3461
- context: z.record(z.string(), z.any()).optional(),
3462
- expires_at: z.string().optional(),
3463
- structured: z.boolean().default(false)
3464
- }).refine((data) => !(data.task_id && data.task_code), {
3465
- message: "Provide either task_id or task_code, not both"
3466
- });
3467
- var HandoffListSchema = z.object({
3468
- repo: z.string().min(1).transform(normalizeRepo),
3469
- status: HandoffStatusSchema.optional(),
3470
- from_agent: z.string().min(1).optional(),
3471
- to_agent: z.string().min(1).optional(),
3472
- limit: z.number().min(1).max(100).default(20),
3473
- offset: z.number().min(0).default(0),
3474
- structured: z.boolean().default(false)
3475
- });
3476
- var TaskClaimSchema = z.object({
3477
- repo: z.string().min(1).transform(normalizeRepo),
3478
- task_id: z.string().uuid().optional(),
3479
- task_code: z.string().optional(),
3480
- agent: z.string().min(1),
3481
- role: z.string().optional(),
3482
- metadata: z.record(z.string(), z.any()).optional(),
3483
- structured: z.boolean().default(false)
3484
- }).refine((data) => data.task_id !== void 0 || data.task_code !== void 0, {
3485
- message: "Either task_id or task_code must be provided"
3486
- }).refine((data) => !(data.task_id && data.task_code), {
3487
- message: "Provide either task_id or task_code, not both"
3488
- });
3489
- var StandardStoreSchema = z.object({
3490
- name: z.string().min(3).max(255),
3491
- content: z.string().min(10),
3492
- context: z.string().optional(),
3493
- version: z.string().optional(),
3494
- language: z.string().optional(),
3495
- stack: z.array(z.string()).optional(),
3496
- repo: z.string().optional(),
3497
- is_global: z.boolean().optional(),
3498
- tags: z.array(z.string()).optional(),
3499
- metadata: z.record(z.string(), z.any()).optional(),
3500
- agent: z.string().optional(),
3501
- model: z.string().optional(),
3502
- structured: z.boolean().default(false)
3503
- });
3504
- var StandardSearchSchema = z.object({
3505
- query: z.string().optional(),
3506
- stack: z.array(z.string()).optional(),
3507
- language: z.string().optional(),
3508
- version: z.string().optional(),
3509
- repo: z.string().optional(),
3510
- is_global: z.boolean().optional(),
3511
- limit: z.number().min(1).max(100).default(20),
3512
- offset: z.number().min(0).default(0),
3513
- structured: z.boolean().default(false)
3514
- });
3515
- var TOOL_DEFINITIONS = [
3516
3356
  {
3517
- name: "memory-synthesize",
3518
- title: "Memory Synthesize",
3519
- description: "Use client sampling to synthesize a grounded answer from local memory and tasks. Best for project briefings, tradeoff summaries, and context-aware answers.",
3357
+ name: "memory-summarize",
3358
+ title: "Memory Summarize",
3359
+ description: "Update the summary for a repository",
3520
3360
  annotations: {
3521
- readOnlyHint: true,
3522
- idempotentHint: true,
3361
+ readOnlyHint: false,
3362
+ idempotentHint: false,
3523
3363
  openWorldHint: false
3524
3364
  },
3525
- execution: {
3526
- taskSupport: "optional"
3527
- },
3528
3365
  inputSchema: {
3529
3366
  type: "object",
3530
3367
  properties: {
3531
- repo: { type: "string", description: "Repository name. Optional when a single MCP root is active." },
3532
- objective: { type: "string", minLength: 5, description: "Question or synthesis objective." },
3533
- current_file_path: {
3534
- type: "string",
3535
- description: "Optional absolute file path for workspace-local grounding."
3536
- },
3537
- include_summary: { type: "boolean", default: true },
3538
- include_tasks: { type: "boolean", default: true },
3539
- use_tools: {
3540
- type: "boolean",
3541
- default: true,
3542
- description: "Allow the sampled model to call local memory/task tools during synthesis when the client supports sampling.tools."
3368
+ repo: { type: "string", description: "Repository name" },
3369
+ signals: {
3370
+ type: "array",
3371
+ items: { type: "string", maxLength: 200 },
3372
+ minItems: 1,
3373
+ description: "High-level signals to include in summary"
3543
3374
  },
3544
- max_iterations: { type: "number", minimum: 1, maximum: 5, default: 3 },
3545
- max_tokens: { type: "number", minimum: 128, maximum: 4e3, default: 1200 },
3546
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON results." }
3375
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the summary." }
3547
3376
  },
3548
- required: ["objective"]
3377
+ required: ["repo", "signals"]
3549
3378
  },
3550
3379
  outputSchema: {
3551
3380
  type: "object",
3552
3381
  properties: {
3382
+ success: { type: "boolean" },
3553
3383
  repo: { type: "string" },
3554
- objective: { type: "string" },
3555
- answer: { type: "string" },
3556
- model: { type: "string" },
3557
- stopReason: { type: "string" },
3558
- iterations: { type: "number" },
3559
- toolCalls: { type: "number" }
3384
+ summary: { type: "string" },
3385
+ signalCount: { type: "number" }
3560
3386
  },
3561
- required: ["repo", "objective", "answer", "iterations", "toolCalls"]
3387
+ required: ["success", "repo", "summary", "signalCount"]
3562
3388
  }
3563
3389
  },
3564
3390
  {
3565
- name: "task-create-interactive",
3566
- title: "Interactive Task Create",
3567
- 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.",
3391
+ name: "memory-delete",
3392
+ title: "Memory Delete",
3393
+ description: "Soft-delete one or more memory entries. Supports single 'id' or bulk 'ids'.",
3568
3394
  annotations: {
3569
3395
  readOnlyHint: false,
3570
3396
  idempotentHint: false,
3571
- destructiveHint: false,
3397
+ destructiveHint: true,
3572
3398
  openWorldHint: false
3573
3399
  },
3574
3400
  inputSchema: {
3575
3401
  type: "object",
3576
3402
  properties: {
3577
- repo: {
3578
- type: "string",
3579
- description: "Repository name. Optional when it can be inferred from MCP roots or elicited from the user."
3403
+ repo: { type: "string", description: "Repository name (optional for single id)" },
3404
+ id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
3405
+ ids: {
3406
+ type: "array",
3407
+ items: { type: "string", format: "uuid" },
3408
+ minItems: 1,
3409
+ description: "Array of memory IDs to delete"
3580
3410
  },
3581
- task_code: { type: "string" },
3582
- phase: { type: "string" },
3583
- title: { type: "string", minLength: 3, maxLength: 100 },
3584
- description: { type: "string", minLength: 1 },
3585
- status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3586
- priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3587
- agent: { type: "string" },
3588
- role: { type: "string" },
3589
- doc_path: { type: "string" },
3590
3411
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3591
3412
  }
3592
3413
  },
3593
3414
  outputSchema: {
3594
3415
  type: "object",
3595
3416
  properties: {
3417
+ success: { type: "boolean" },
3418
+ id: { type: "string" },
3419
+ ids: { type: "array", items: { type: "string" } },
3596
3420
  repo: { type: "string" },
3597
- task_code: { type: "string" },
3598
- phase: { type: "string" },
3599
- title: { type: "string" },
3600
- status: { type: "string" },
3601
- priority: { type: "number" }
3421
+ deletedCount: { type: "number" }
3602
3422
  },
3603
- required: ["repo", "task_code", "phase", "title", "status", "priority"]
3604
- }
3605
- },
3606
- {
3607
- name: "memory-detail",
3608
- title: "Memory Detail",
3609
- 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.",
3610
- inputSchema: {
3611
- type: "object",
3612
- properties: {
3613
- id: { type: "string", format: "uuid", description: "Memory entry ID. Optional if code is provided." },
3614
- code: { type: "string", description: "Short memory code. Optional if id is provided." },
3615
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON details." }
3616
- }
3423
+ required: ["success"]
3617
3424
  }
3618
3425
  },
3619
3426
  {
3620
- name: "task-detail",
3621
- title: "Task Detail",
3622
- 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.",
3427
+ name: "memory-recap",
3428
+ title: "Memory Recap",
3429
+ 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.",
3430
+ annotations: {
3431
+ readOnlyHint: true,
3432
+ idempotentHint: true,
3433
+ openWorldHint: false
3434
+ },
3623
3435
  inputSchema: {
3624
3436
  type: "object",
3625
3437
  properties: {
3626
- repo: { type: "string", description: "Repository name" },
3627
- id: { type: "string", format: "uuid", description: "Task ID (optional if task_code is provided)" },
3628
- task_code: { type: "string", description: "Task code (e.g. TASK-001) (optional if id is provided)" },
3438
+ repo: { type: "string", description: "Repository name (required)" },
3439
+ limit: {
3440
+ type: "number",
3441
+ minimum: 1,
3442
+ maximum: 50,
3443
+ default: 20,
3444
+ description: "Maximum number of top memories to return in the pointer table"
3445
+ },
3446
+ offset: {
3447
+ type: "number",
3448
+ minimum: 0,
3449
+ default: 0,
3450
+ description: "Number of memories to skip for pagination (optional, default 0)"
3451
+ },
3629
3452
  structured: {
3630
3453
  type: "boolean",
3631
3454
  default: false,
3632
- description: "If true, returns structured JSON without the text content details."
3455
+ description: "If true, returns structured JSON without the text content summary."
3633
3456
  }
3634
3457
  },
3635
3458
  required: ["repo"]
3459
+ },
3460
+ outputSchema: {
3461
+ type: "object",
3462
+ properties: {
3463
+ schema: { type: "string", enum: ["memory-recap"] },
3464
+ repo: { type: "string" },
3465
+ count: { type: "number", description: "Number of rows in the top pointer table" },
3466
+ total: { type: "number", description: "Total active memories in repo" },
3467
+ offset: { type: "number" },
3468
+ limit: { type: "number" },
3469
+ stats: {
3470
+ type: "object",
3471
+ properties: {
3472
+ byType: {
3473
+ type: "object",
3474
+ description: "Count of active memories per type (e.g. { decision: 3, code_fact: 7 })"
3475
+ }
3476
+ },
3477
+ required: ["byType"]
3478
+ },
3479
+ top: {
3480
+ type: "object",
3481
+ properties: {
3482
+ columns: {
3483
+ type: "array",
3484
+ items: { type: "string" },
3485
+ description: "Column names: [id, code, title, type, importance]"
3486
+ },
3487
+ rows: {
3488
+ type: "array",
3489
+ items: { type: "array" },
3490
+ description: "Each row: [id, code, title, type, importance]. Fetch full content via memory-detail"
3491
+ }
3492
+ },
3493
+ required: ["columns", "rows"]
3494
+ }
3495
+ },
3496
+ required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
3636
3497
  }
3637
3498
  },
3638
3499
  {
3639
- name: "memory-store",
3640
- title: "Memory Store",
3641
- description: "Store a new memory entry. Keep 'title' concise and human-readable; do not embed agent/role/date metadata in the title. Put auxiliary context into 'metadata'. Use 'tags' for tech-stack and 'is_global' for universal rules.",
3500
+ name: "task-create",
3501
+ title: "Task Create",
3502
+ 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.",
3642
3503
  annotations: {
3643
3504
  readOnlyHint: false,
3644
3505
  idempotentHint: false,
3645
- destructiveHint: false,
3646
3506
  openWorldHint: false
3647
3507
  },
3648
3508
  inputSchema: {
3649
3509
  type: "object",
3650
3510
  properties: {
3651
- type: {
3652
- type: "string",
3653
- enum: [
3654
- "code_fact",
3655
- "decision",
3656
- "mistake",
3657
- "pattern",
3658
- "file_claim",
3659
- "task_archive"
3660
- ],
3661
- description: "Type of memory being stored"
3662
- },
3511
+ repo: { type: "string", description: "Repository name" },
3512
+ task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
3513
+ phase: { type: "string", description: "Project phase (Required for single task)" },
3663
3514
  title: {
3664
3515
  type: "string",
3665
3516
  minLength: 3,
3666
3517
  maxLength: 100,
3667
- description: "Short human-readable title for the memory. Do not embed bracketed metadata like agent/role/date prefixes here."
3668
- },
3669
- content: {
3670
- type: "string",
3671
- minLength: 10,
3672
- description: "The memory content"
3673
- },
3674
- importance: {
3675
- type: "number",
3676
- minimum: 1,
3677
- maximum: 5,
3678
- description: "Importance score (1-5)"
3679
- },
3680
- agent: {
3681
- type: "string",
3682
- description: "Name of the agent creating this memory"
3683
- },
3684
- role: {
3685
- type: "string",
3686
- description: "Role of the agent creating this memory"
3518
+ description: "Task objective (Required for single task)"
3687
3519
  },
3688
- model: {
3520
+ description: { type: "string", description: "Detailed description (Required for single task)" },
3521
+ status: {
3689
3522
  type: "string",
3690
- description: "AI model used by the agent"
3691
- },
3692
- scope: {
3693
- type: "object",
3694
- properties: {
3695
- repo: { type: "string", description: "Repository name" },
3696
- branch: { type: "string" },
3697
- folder: { type: "string" },
3698
- language: { type: "string" }
3699
- },
3700
- required: ["repo"]
3523
+ enum: ["backlog", "pending"],
3524
+ default: "backlog",
3525
+ description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
3701
3526
  },
3702
- tags: {
3527
+ priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3528
+ agent: { type: "string" },
3529
+ role: { type: "string" },
3530
+ doc_path: { type: "string" },
3531
+ tags: { type: "array", items: { type: "string" } },
3532
+ metadata: { type: "object" },
3533
+ parent_id: { type: "string", format: "uuid" },
3534
+ depends_on: { type: "string", format: "uuid" },
3535
+ est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
3536
+ tasks: {
3703
3537
  type: "array",
3704
- items: { type: "string" },
3705
- description: "Technology stack tags (e.g., ['filament', 'laravel'])"
3706
- },
3707
- metadata: {
3708
- type: "object",
3709
- description: "Structured metadata for non-title context such as source agent, claim fields, or timestamps"
3710
- },
3711
- is_global: {
3712
- type: "boolean",
3713
- description: "If true, this memory is shared across all repositories"
3538
+ items: {
3539
+ type: "object",
3540
+ properties: {
3541
+ task_code: { type: "string" },
3542
+ phase: { type: "string" },
3543
+ title: { type: "string", minLength: 3, maxLength: 100 },
3544
+ description: { type: "string" },
3545
+ status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
3546
+ priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
3547
+ agent: { type: "string" },
3548
+ role: { type: "string" },
3549
+ doc_path: { type: "string" },
3550
+ tags: { type: "array", items: { type: "string" } },
3551
+ metadata: { type: "object" },
3552
+ parent_id: { type: "string", format: "uuid" },
3553
+ depends_on: { type: "string", format: "uuid" },
3554
+ est_tokens: { type: "number", minimum: 0 }
3555
+ },
3556
+ required: ["task_code", "phase", "title", "description"]
3557
+ },
3558
+ description: "Array of tasks for bulk creation"
3714
3559
  },
3715
- ttlDays: { type: "number", minimum: 1 },
3716
- supersedes: { type: "string", format: "uuid" },
3717
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the stored memory." }
3560
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3718
3561
  },
3719
- required: ["type", "title", "content", "importance", "scope", "agent", "model"]
3562
+ required: ["repo"]
3720
3563
  },
3721
3564
  outputSchema: {
3722
3565
  type: "object",
3723
3566
  properties: {
3724
3567
  success: { type: "boolean" },
3725
3568
  id: { type: "string" },
3726
- code: { type: "string" },
3569
+ task_code: { type: "string" },
3727
3570
  repo: { type: "string" },
3728
- type: { type: "string" },
3571
+ phase: { type: "string" },
3729
3572
  title: { type: "string" },
3730
- error: { type: "string" },
3731
- message: { type: "string" }
3573
+ status: { type: "string" },
3574
+ priority: { type: "number" },
3575
+ createdCount: { type: "number" },
3576
+ taskCodes: { type: "array", items: { type: "string" } }
3732
3577
  },
3733
- required: ["success"]
3578
+ required: ["success", "repo"]
3734
3579
  }
3735
3580
  },
3736
3581
  {
3737
- name: "memory-acknowledge",
3738
- title: "Memory Acknowledge",
3739
- description: "Acknowledge the use of a memory or report its irrelevance/contradiction. Mandatory after using memory to generate code.",
3582
+ name: "task-update",
3583
+ title: "Task Update",
3584
+ 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.",
3740
3585
  annotations: {
3741
3586
  readOnlyHint: false,
3742
3587
  idempotentHint: false,
@@ -3745,81 +3590,98 @@ var TOOL_DEFINITIONS = [
3745
3590
  inputSchema: {
3746
3591
  type: "object",
3747
3592
  properties: {
3748
- memory_id: { type: "string", format: "uuid" },
3749
- status: { type: "string", enum: ["used", "irrelevant", "contradictory"] },
3750
- application_context: { type: "string", minLength: 10 },
3593
+ repo: { type: "string", description: "Repository name" },
3594
+ id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
3595
+ ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
3596
+ task_code: { type: "string" },
3597
+ phase: { type: "string" },
3598
+ title: { type: "string", minLength: 3, maxLength: 100 },
3599
+ description: { type: "string" },
3600
+ status: {
3601
+ type: "string",
3602
+ enum: ["backlog", "pending", "in_progress", "completed", "canceled", "blocked"],
3603
+ description: "New status. Transitions from 'backlog', 'pending' or 'blocked' to 'completed' are NOT allowed."
3604
+ },
3605
+ priority: { type: "number", minimum: 1, maximum: 5 },
3606
+ agent: { type: "string" },
3607
+ role: { type: "string" },
3608
+ model: { type: "string" },
3609
+ comment: {
3610
+ type: "string",
3611
+ description: "REQUIRED when changing task status. Explain WHY the status is changing (e.g., 'Starting implementation', 'Blocked by missing API docs', 'Verified fix')."
3612
+ },
3613
+ doc_path: { type: "string" },
3614
+ tags: { type: "array", items: { type: "string" } },
3615
+ metadata: { type: "object" },
3616
+ parent_id: { type: "string", format: "uuid" },
3617
+ depends_on: { type: "string", format: "uuid" },
3618
+ est_tokens: {
3619
+ type: "number",
3620
+ minimum: 0,
3621
+ description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
3622
+ },
3623
+ force: {
3624
+ type: "boolean",
3625
+ description: "If true, bypasses status transition validation (e.g. pending -> completed)."
3626
+ },
3751
3627
  structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3752
3628
  },
3753
- required: ["memory_id", "status"]
3629
+ required: ["repo"]
3754
3630
  },
3755
3631
  outputSchema: {
3756
3632
  type: "object",
3757
3633
  properties: {
3758
3634
  success: { type: "boolean" },
3759
3635
  id: { type: "string" },
3760
- status: { type: "string" }
3636
+ ids: { type: "array", items: { type: "string" } },
3637
+ repo: { type: "string" },
3638
+ status: { type: "string" },
3639
+ archivedToMemory: { type: "boolean" },
3640
+ updatedFields: {
3641
+ type: "array",
3642
+ items: { type: "string" }
3643
+ },
3644
+ updatedCount: { type: "number" }
3761
3645
  },
3762
- required: ["success", "id", "status"]
3646
+ required: ["success", "repo"]
3763
3647
  }
3764
3648
  },
3765
3649
  {
3766
- name: "memory-update",
3767
- title: "Memory Update",
3768
- description: "Update an existing memory entry. Keep 'title' concise and move agent/role/date or claim context into 'metadata' instead of the title.",
3650
+ name: "task-delete",
3651
+ title: "Task Delete",
3652
+ description: "Delete one or more tasks from a repository. Supports single 'id' or bulk 'ids'.",
3769
3653
  annotations: {
3770
3654
  readOnlyHint: false,
3771
3655
  idempotentHint: false,
3772
- destructiveHint: false,
3656
+ destructiveHint: true,
3773
3657
  openWorldHint: false
3774
3658
  },
3775
3659
  inputSchema: {
3776
3660
  type: "object",
3777
3661
  properties: {
3778
- id: { type: "string", format: "uuid" },
3779
- type: {
3780
- type: "string",
3781
- enum: [
3782
- "code_fact",
3783
- "decision",
3784
- "mistake",
3785
- "pattern",
3786
- "file_claim",
3787
- "task_archive"
3788
- ]
3789
- },
3790
- title: { type: "string", minLength: 3, maxLength: 100 },
3791
- content: { type: "string", minLength: 10 },
3792
- importance: { type: "number", minimum: 1, maximum: 5 },
3793
- agent: { type: "string" },
3794
- role: { type: "string" },
3795
- status: { type: "string", enum: ["active", "archived"] },
3796
- supersedes: { type: "string", format: "uuid" },
3797
- tags: { type: "array", items: { type: "string" } },
3798
- metadata: { type: "object" },
3799
- is_global: { type: "boolean" },
3800
- completed_at: { type: "string" },
3801
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the updated memory." }
3662
+ repo: { type: "string", description: "Repository name" },
3663
+ id: { type: "string", format: "uuid", description: "Task ID (for single deletion)" },
3664
+ ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk deletion)" },
3665
+ structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3802
3666
  },
3803
- required: ["id"]
3667
+ required: ["repo"]
3804
3668
  },
3805
3669
  outputSchema: {
3806
3670
  type: "object",
3807
3671
  properties: {
3808
3672
  success: { type: "boolean" },
3809
3673
  id: { type: "string" },
3674
+ ids: { type: "array", items: { type: "string" } },
3810
3675
  repo: { type: "string" },
3811
- updatedFields: {
3812
- type: "array",
3813
- items: { type: "string" }
3814
- }
3676
+ deletedCount: { type: "number" }
3815
3677
  },
3816
- required: ["success", "id", "repo", "updatedFields"]
3678
+ required: ["success", "repo"]
3817
3679
  }
3818
3680
  },
3819
3681
  {
3820
- name: "memory-search",
3821
- title: "Memory Search",
3822
- 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.",
3682
+ name: "task-list",
3683
+ title: "Task List",
3684
+ 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.",
3823
3685
  annotations: {
3824
3686
  readOnlyHint: true,
3825
3687
  idempotentHint: true,
@@ -3828,42 +3690,35 @@ var TOOL_DEFINITIONS = [
3828
3690
  inputSchema: {
3829
3691
  type: "object",
3830
3692
  properties: {
3831
- query: { type: "string", minLength: 3 },
3832
- prompt: { type: "string" },
3833
- repo: { type: "string" },
3834
- current_tags: {
3835
- type: "array",
3836
- items: { type: "string" },
3837
- description: "Active tech stack tags (e.g., ['filament', 'react'])"
3693
+ repo: {
3694
+ type: "string",
3695
+ description: "Repository name"
3838
3696
  },
3839
- types: {
3840
- type: "array",
3841
- items: {
3842
- type: "string",
3843
- enum: [
3844
- "code_fact",
3845
- "decision",
3846
- "mistake",
3847
- "pattern",
3848
- "file_claim",
3849
- "task_archive"
3850
- ]
3851
- }
3697
+ status: {
3698
+ type: "string",
3699
+ default: "in_progress,pending",
3700
+ description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,pending'."
3852
3701
  },
3853
- minImportance: { type: "number", minimum: 1, maximum: 5 },
3854
- limit: { type: "number", minimum: 1, maximum: 100, default: 5 },
3855
- offset: { type: "number", minimum: 0, default: 0 },
3856
- includeRecap: { type: "boolean", default: false },
3857
- current_file_path: { type: "string" },
3858
- include_archived: { type: "boolean", default: false },
3859
- scope: {
3860
- type: "object",
3861
- properties: {
3862
- repo: { type: "string" },
3863
- branch: { type: "string" },
3864
- folder: { type: "string" },
3865
- language: { type: "string" }
3866
- }
3702
+ phase: {
3703
+ type: "string",
3704
+ description: "Filter by phase (e.g., 'research', 'implementation')"
3705
+ },
3706
+ query: {
3707
+ type: "string",
3708
+ description: "Search keyword matching task code, title, or description"
3709
+ },
3710
+ limit: {
3711
+ type: "number",
3712
+ minimum: 1,
3713
+ maximum: 100,
3714
+ default: 5,
3715
+ description: "Maximum rows to return (default 5)"
3716
+ },
3717
+ offset: {
3718
+ type: "number",
3719
+ minimum: 0,
3720
+ default: 0,
3721
+ description: "Offset for pagination"
3867
3722
  },
3868
3723
  structured: {
3869
3724
  type: "boolean",
@@ -3871,111 +3726,113 @@ var TOOL_DEFINITIONS = [
3871
3726
  description: "If true, returns structured JSON without the text content summary."
3872
3727
  }
3873
3728
  },
3874
- required: ["query", "repo"]
3729
+ required: ["repo"]
3875
3730
  },
3876
3731
  outputSchema: {
3877
3732
  type: "object",
3878
3733
  properties: {
3879
- schema: { type: "string", enum: ["memory-search"] },
3880
- query: { type: "string" },
3881
- count: { type: "number", description: "Number of rows returned" },
3882
- total: { type: "number", description: "Total matching memories" },
3883
- offset: { type: "number" },
3884
- limit: { type: "number" },
3885
- results: {
3734
+ schema: { type: "string", enum: ["task-list"] },
3735
+ tasks: {
3886
3736
  type: "object",
3887
3737
  properties: {
3888
3738
  columns: {
3889
3739
  type: "array",
3890
3740
  items: { type: "string" },
3891
- description: "Column names: [id, title, type, importance]"
3741
+ description: "Column names in order: id, task_code, title, status, priority, updated_at, comments_count"
3892
3742
  },
3893
3743
  rows: {
3894
3744
  type: "array",
3895
3745
  items: { type: "array" },
3896
- description: "Each row: [id, title, type, importance]. Fetch full content via memory-detail"
3746
+ description: "Each row: [id, task_code, title, status, priority, updated_at, comments_count]. Use task-detail to fetch full task."
3897
3747
  }
3898
3748
  },
3899
3749
  required: ["columns", "rows"]
3900
- }
3750
+ },
3751
+ count: { type: "number" },
3752
+ offset: { type: "number" }
3901
3753
  },
3902
- required: ["schema", "query", "count", "total", "offset", "limit", "results"]
3754
+ required: ["schema", "tasks", "count"]
3903
3755
  }
3904
3756
  },
3905
3757
  {
3906
- name: "memory-summarize",
3907
- title: "Memory Summarize",
3908
- description: "Update the summary for a repository",
3758
+ name: "handoff-create",
3759
+ title: "Handoff Create",
3760
+ 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.",
3909
3761
  annotations: {
3910
3762
  readOnlyHint: false,
3911
3763
  idempotentHint: false,
3764
+ destructiveHint: false,
3912
3765
  openWorldHint: false
3913
3766
  },
3914
3767
  inputSchema: {
3915
3768
  type: "object",
3916
3769
  properties: {
3917
3770
  repo: { type: "string", description: "Repository name" },
3918
- signals: {
3919
- type: "array",
3920
- items: { type: "string", maxLength: 200 },
3921
- minItems: 1,
3922
- description: "High-level signals to include in summary"
3771
+ from_agent: { type: "string", description: "Agent creating the handoff" },
3772
+ to_agent: { type: "string", description: "Optional target agent" },
3773
+ task_id: { type: "string", format: "uuid", description: "Optional task id to associate" },
3774
+ task_code: { type: "string", description: "Optional task code to associate" },
3775
+ summary: { type: "string", minLength: 1, description: "Concise human-readable transfer summary" },
3776
+ context: {
3777
+ type: "object",
3778
+ description: "Structured handoff context. Include next_steps, blockers, or remaining_work unless a target agent or task is provided."
3923
3779
  },
3924
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON of the summary." }
3780
+ expires_at: { type: "string", description: "Optional expiration timestamp" },
3781
+ structured: { type: "boolean", default: false }
3925
3782
  },
3926
- required: ["repo", "signals"]
3783
+ required: ["repo", "from_agent", "summary"]
3927
3784
  },
3928
3785
  outputSchema: {
3929
3786
  type: "object",
3930
3787
  properties: {
3931
- success: { type: "boolean" },
3788
+ id: { type: "string" },
3932
3789
  repo: { type: "string" },
3790
+ from_agent: { type: "string" },
3791
+ to_agent: { type: "string", nullable: true },
3792
+ task_id: { type: "string", nullable: true },
3933
3793
  summary: { type: "string" },
3934
- signalCount: { type: "number" }
3794
+ context: { type: "object" },
3795
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
3796
+ created_at: { type: "string" },
3797
+ updated_at: { type: "string" },
3798
+ expires_at: { type: "string", nullable: true }
3935
3799
  },
3936
- required: ["success", "repo", "summary", "signalCount"]
3800
+ required: ["id", "repo", "from_agent", "summary", "context", "status", "created_at", "updated_at"]
3937
3801
  }
3938
3802
  },
3939
3803
  {
3940
- name: "memory-delete",
3941
- title: "Memory Delete",
3942
- description: "Soft-delete one or more memory entries. Supports single 'id' or bulk 'ids'.",
3804
+ name: "handoff-update",
3805
+ title: "Handoff Update",
3806
+ 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.",
3943
3807
  annotations: {
3944
3808
  readOnlyHint: false,
3945
3809
  idempotentHint: false,
3946
- destructiveHint: true,
3810
+ destructiveHint: false,
3947
3811
  openWorldHint: false
3948
3812
  },
3949
3813
  inputSchema: {
3950
3814
  type: "object",
3951
3815
  properties: {
3952
- repo: { type: "string", description: "Repository name (optional for single id)" },
3953
- id: { type: "string", format: "uuid", description: "Memory entry ID to delete" },
3954
- ids: {
3955
- type: "array",
3956
- items: { type: "string", format: "uuid" },
3957
- minItems: 1,
3958
- description: "Array of memory IDs to delete"
3959
- },
3960
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3961
- }
3816
+ id: { type: "string", format: "uuid", description: "Handoff ID" },
3817
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
3818
+ structured: { type: "boolean", default: false }
3819
+ },
3820
+ required: ["id", "status"]
3962
3821
  },
3963
3822
  outputSchema: {
3964
3823
  type: "object",
3965
3824
  properties: {
3966
3825
  success: { type: "boolean" },
3967
3826
  id: { type: "string" },
3968
- ids: { type: "array", items: { type: "string" } },
3969
- repo: { type: "string" },
3970
- deletedCount: { type: "number" }
3827
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] }
3971
3828
  },
3972
- required: ["success"]
3829
+ required: ["success", "id", "status"]
3973
3830
  }
3974
3831
  },
3975
3832
  {
3976
- name: "memory-recap",
3977
- title: "Memory Recap",
3978
- 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.",
3833
+ name: "handoff-list",
3834
+ title: "Handoff List",
3835
+ description: "Navigation layer for handoff queues. List repository handoffs with optional status and agent filters, then inspect selected rows before acting.",
3979
3836
  annotations: {
3980
3837
  readOnlyHint: true,
3981
3838
  idempotentHint: true,
@@ -3984,253 +3841,169 @@ var TOOL_DEFINITIONS = [
3984
3841
  inputSchema: {
3985
3842
  type: "object",
3986
3843
  properties: {
3987
- repo: { type: "string", description: "Repository name (required)" },
3988
- limit: {
3989
- type: "number",
3990
- minimum: 1,
3991
- maximum: 50,
3992
- default: 20,
3993
- description: "Maximum number of top memories to return in the pointer table"
3994
- },
3995
- offset: {
3996
- type: "number",
3997
- minimum: 0,
3998
- default: 0,
3999
- description: "Number of memories to skip for pagination (optional, default 0)"
4000
- },
4001
- structured: {
4002
- type: "boolean",
4003
- default: false,
4004
- description: "If true, returns structured JSON without the text content summary."
4005
- }
3844
+ repo: { type: "string", description: "Repository name" },
3845
+ status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
3846
+ from_agent: { type: "string" },
3847
+ to_agent: { type: "string" },
3848
+ limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
3849
+ offset: { type: "number", minimum: 0, default: 0 },
3850
+ structured: { type: "boolean", default: false }
4006
3851
  },
4007
3852
  required: ["repo"]
4008
3853
  },
4009
3854
  outputSchema: {
4010
3855
  type: "object",
4011
3856
  properties: {
4012
- schema: { type: "string", enum: ["memory-recap"] },
4013
- repo: { type: "string" },
4014
- count: { type: "number", description: "Number of rows in the top pointer table" },
4015
- total: { type: "number", description: "Total active memories in repo" },
4016
- offset: { type: "number" },
4017
- limit: { type: "number" },
4018
- stats: {
4019
- type: "object",
4020
- properties: {
4021
- byType: {
4022
- type: "object",
4023
- description: "Count of active memories per type (e.g. { decision: 3, code_fact: 7 })"
4024
- }
4025
- },
4026
- required: ["byType"]
4027
- },
4028
- top: {
3857
+ schema: { type: "string", enum: ["handoff-list"] },
3858
+ handoffs: {
4029
3859
  type: "object",
4030
3860
  properties: {
4031
3861
  columns: {
4032
3862
  type: "array",
4033
3863
  items: { type: "string" },
4034
- description: "Column names: [id, code, title, type, importance]"
3864
+ description: "Column names: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4035
3865
  },
4036
3866
  rows: {
4037
3867
  type: "array",
4038
3868
  items: { type: "array" },
4039
- description: "Each row: [id, code, title, type, importance]. Fetch full content via memory-detail"
3869
+ description: "Each row: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4040
3870
  }
4041
3871
  },
4042
3872
  required: ["columns", "rows"]
4043
- }
3873
+ },
3874
+ count: { type: "number" },
3875
+ offset: { type: "number" }
4044
3876
  },
4045
- required: ["schema", "repo", "count", "total", "offset", "limit", "stats", "top"]
3877
+ required: ["schema", "handoffs", "count", "offset"]
4046
3878
  }
4047
3879
  },
4048
3880
  {
4049
- name: "task-create",
4050
- title: "Task Create",
4051
- 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.",
3881
+ name: "task-claim",
3882
+ title: "Task Claim",
3883
+ 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.",
4052
3884
  annotations: {
4053
3885
  readOnlyHint: false,
4054
3886
  idempotentHint: false,
3887
+ destructiveHint: false,
4055
3888
  openWorldHint: false
4056
3889
  },
4057
3890
  inputSchema: {
4058
3891
  type: "object",
4059
3892
  properties: {
4060
3893
  repo: { type: "string", description: "Repository name" },
4061
- task_code: { type: "string", description: "Unique task code (e.g. TASK-001) (Required for single task)" },
4062
- phase: { type: "string", description: "Project phase (Required for single task)" },
4063
- title: {
4064
- type: "string",
4065
- minLength: 3,
4066
- maxLength: 100,
4067
- description: "Task objective (Required for single task)"
4068
- },
4069
- description: { type: "string", description: "Detailed description (Required for single task)" },
4070
- status: {
4071
- type: "string",
4072
- enum: ["backlog", "pending"],
4073
- default: "backlog",
4074
- description: "New tasks MUST start in 'backlog' if there are already 10 pending tasks. Otherwise can start in 'pending'."
4075
- },
4076
- priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
4077
- agent: { type: "string" },
4078
- role: { type: "string" },
4079
- doc_path: { type: "string" },
4080
- tags: { type: "array", items: { type: "string" } },
4081
- metadata: { type: "object" },
4082
- parent_id: { type: "string", format: "uuid" },
4083
- depends_on: { type: "string", format: "uuid" },
4084
- est_tokens: { type: "number", minimum: 0, description: "Estimated tokens budget for this task" },
4085
- tasks: {
4086
- type: "array",
4087
- items: {
4088
- type: "object",
4089
- properties: {
4090
- task_code: { type: "string" },
4091
- phase: { type: "string" },
4092
- title: { type: "string", minLength: 3, maxLength: 100 },
4093
- description: { type: "string" },
4094
- status: { type: "string", enum: ["backlog", "pending"], default: "backlog" },
4095
- priority: { type: "number", minimum: 1, maximum: 5, default: 3 },
4096
- agent: { type: "string" },
4097
- role: { type: "string" },
4098
- doc_path: { type: "string" },
4099
- tags: { type: "array", items: { type: "string" } },
4100
- metadata: { type: "object" },
4101
- parent_id: { type: "string", format: "uuid" },
4102
- depends_on: { type: "string", format: "uuid" },
4103
- est_tokens: { type: "number", minimum: 0 }
4104
- },
4105
- required: ["task_code", "phase", "title", "description"]
4106
- },
4107
- description: "Array of tasks for bulk creation"
4108
- },
4109
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3894
+ task_id: { type: "string", format: "uuid", description: "Task id to claim. Optional if task_code is provided." },
3895
+ task_code: { type: "string", description: "Task code to claim. Optional if task_id is provided." },
3896
+ agent: { type: "string", description: "Claiming agent name" },
3897
+ role: { type: "string", description: "Claiming agent role" },
3898
+ metadata: { type: "object", description: "Optional claim metadata" },
3899
+ structured: { type: "boolean", default: false }
4110
3900
  },
4111
- required: ["repo"]
3901
+ required: ["repo", "agent"]
4112
3902
  },
4113
3903
  outputSchema: {
4114
3904
  type: "object",
4115
3905
  properties: {
4116
- success: { type: "boolean" },
4117
3906
  id: { type: "string" },
4118
- task_code: { type: "string" },
4119
3907
  repo: { type: "string" },
4120
- phase: { type: "string" },
4121
- title: { type: "string" },
4122
- status: { type: "string" },
4123
- priority: { type: "number" },
4124
- createdCount: { type: "number" },
4125
- taskCodes: { type: "array", items: { type: "string" } }
3908
+ task_id: { type: "string" },
3909
+ task_code: { type: "string", nullable: true },
3910
+ agent: { type: "string" },
3911
+ role: { type: "string" },
3912
+ claimed_at: { type: "string" },
3913
+ released_at: { type: "string", nullable: true },
3914
+ metadata: { type: "object" }
4126
3915
  },
4127
- required: ["success", "repo"]
3916
+ required: ["id", "repo", "task_id", "agent", "role", "claimed_at", "metadata"]
4128
3917
  }
4129
3918
  },
4130
3919
  {
4131
- name: "task-update",
4132
- title: "Task Update",
4133
- 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.",
3920
+ name: "standard-store",
3921
+ title: "Standard Store",
3922
+ description: "Store one atomic coding standard. Use for durable implementation rules with explicit context, stack/language filters, and repo/global scope.",
4134
3923
  annotations: {
4135
3924
  readOnlyHint: false,
4136
3925
  idempotentHint: false,
3926
+ destructiveHint: false,
4137
3927
  openWorldHint: false
4138
3928
  },
4139
3929
  inputSchema: {
4140
3930
  type: "object",
4141
3931
  properties: {
4142
- repo: { type: "string", description: "Repository name" },
4143
- id: { type: "string", format: "uuid", description: "Task ID (for single update)" },
4144
- ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk update)" },
4145
- task_code: { type: "string" },
4146
- phase: { type: "string" },
4147
- title: { type: "string", minLength: 3, maxLength: 100 },
4148
- description: { type: "string" },
4149
- status: {
4150
- type: "string",
4151
- enum: ["backlog", "pending", "in_progress", "completed", "canceled", "blocked"],
4152
- description: "New status. Transitions from 'backlog', 'pending' or 'blocked' to 'completed' are NOT allowed."
4153
- },
4154
- priority: { type: "number", minimum: 1, maximum: 5 },
4155
- agent: { type: "string" },
4156
- role: { type: "string" },
4157
- model: { type: "string" },
4158
- comment: {
4159
- type: "string",
4160
- description: "REQUIRED when changing task status. Explain WHY the status is changing (e.g., 'Starting implementation', 'Blocked by missing API docs', 'Verified fix')."
4161
- },
4162
- doc_path: { type: "string" },
4163
- tags: { type: "array", items: { type: "string" } },
4164
- metadata: { type: "object" },
4165
- parent_id: { type: "string", format: "uuid" },
4166
- depends_on: { type: "string", format: "uuid" },
4167
- est_tokens: {
4168
- type: "number",
4169
- minimum: 0,
4170
- description: "Estimated total tokens actually used for this task. Required when status changes to 'completed'."
4171
- },
4172
- force: {
4173
- type: "boolean",
4174
- description: "If true, bypasses status transition validation (e.g. pending -> completed)."
3932
+ name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
3933
+ content: { type: "string", minLength: 10, description: "One atomic, actionable standard written as concise Markdown" },
3934
+ context: { type: "string", description: "Context or category (e.g., 'error-handling', 'security')" },
3935
+ version: { type: "string", description: "Version of the standard (e.g., '1.0.0')" },
3936
+ language: { type: "string", description: "Programming language (e.g., 'typescript', 'python')" },
3937
+ stack: {
3938
+ type: "array",
3939
+ items: { type: "string" },
3940
+ description: "Technology stack (e.g., ['react', 'nextjs'])"
4175
3941
  },
4176
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
4177
- },
4178
- required: ["repo"]
4179
- },
4180
- outputSchema: {
4181
- type: "object",
4182
- properties: {
4183
- success: { type: "boolean" },
4184
- id: { type: "string" },
4185
- ids: { type: "array", items: { type: "string" } },
4186
- repo: { type: "string" },
4187
- status: { type: "string" },
4188
- archivedToMemory: { type: "boolean" },
4189
- updatedFields: {
3942
+ repo: { type: "string", description: "Repository name for repo-specific standards. Omit only for global standards." },
3943
+ is_global: { type: "boolean", description: "Whether standard applies globally or repo-specific" },
3944
+ tags: {
4190
3945
  type: "array",
4191
- items: { type: "string" }
3946
+ items: { type: "string" },
3947
+ description: "Tags for categorization"
4192
3948
  },
4193
- updatedCount: { type: "number" }
4194
- },
4195
- required: ["success", "repo"]
4196
- }
4197
- },
4198
- {
4199
- name: "task-delete",
4200
- title: "Task Delete",
4201
- description: "Delete one or more tasks from a repository. Supports single 'id' or bulk 'ids'.",
4202
- annotations: {
4203
- readOnlyHint: false,
4204
- idempotentHint: false,
4205
- destructiveHint: true,
4206
- openWorldHint: false
4207
- },
4208
- inputSchema: {
4209
- type: "object",
4210
- properties: {
4211
- repo: { type: "string", description: "Repository name" },
4212
- id: { type: "string", format: "uuid", description: "Task ID (for single deletion)" },
4213
- ids: { type: "array", items: { type: "string", format: "uuid" }, description: "Task IDs (for bulk deletion)" },
4214
- structured: { type: "boolean", default: false, description: "If true, returns structured JSON result." }
3949
+ metadata: {
3950
+ type: "object",
3951
+ description: "Additional metadata"
3952
+ },
3953
+ agent: { type: "string", description: "Agent creating the standard" },
3954
+ model: { type: "string", description: "AI model used" },
3955
+ structured: { type: "boolean", default: false }
4215
3956
  },
4216
- required: ["repo"]
3957
+ required: ["name", "content"]
4217
3958
  },
4218
3959
  outputSchema: {
4219
3960
  type: "object",
4220
3961
  properties: {
4221
3962
  success: { type: "boolean" },
4222
- id: { type: "string" },
4223
- ids: { type: "array", items: { type: "string" } },
4224
- repo: { type: "string" },
4225
- deletedCount: { type: "number" }
3963
+ standard: {
3964
+ type: "object",
3965
+ properties: {
3966
+ id: { type: "string" },
3967
+ title: { type: "string" },
3968
+ content: { type: "string" },
3969
+ context: { type: "string" },
3970
+ version: { type: "string" },
3971
+ language: { type: "string", nullable: true },
3972
+ stack: { type: "array", items: { type: "string" } },
3973
+ is_global: { type: "boolean" },
3974
+ repo: { type: "string", nullable: true },
3975
+ tags: { type: "array", items: { type: "string" } },
3976
+ metadata: { type: "object" },
3977
+ created_at: { type: "string" },
3978
+ updated_at: { type: "string" },
3979
+ agent: { type: "string" },
3980
+ model: { type: "string" }
3981
+ },
3982
+ required: [
3983
+ "id",
3984
+ "title",
3985
+ "content",
3986
+ "context",
3987
+ "version",
3988
+ "stack",
3989
+ "is_global",
3990
+ "tags",
3991
+ "metadata",
3992
+ "created_at",
3993
+ "updated_at",
3994
+ "agent",
3995
+ "model"
3996
+ ]
3997
+ },
3998
+ message: { type: "string" }
4226
3999
  },
4227
- required: ["success", "repo"]
4000
+ required: ["success", "standard", "message"]
4228
4001
  }
4229
4002
  },
4230
4003
  {
4231
- name: "task-list",
4232
- title: "Task List",
4233
- 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.",
4004
+ name: "standard-search",
4005
+ title: "Standard Search",
4006
+ description: "Navigation and lookup layer for coding standards. Query by text, stack, language, version, repo, and global scope before applying or creating standards.",
4234
4007
  annotations: {
4235
4008
  readOnlyHint: true,
4236
4009
  idempotentHint: true,
@@ -4239,377 +4012,635 @@ var TOOL_DEFINITIONS = [
4239
4012
  inputSchema: {
4240
4013
  type: "object",
4241
4014
  properties: {
4242
- repo: {
4243
- type: "string",
4244
- description: "Repository name"
4245
- },
4246
- status: {
4247
- type: "string",
4248
- default: "in_progress,pending",
4249
- description: "Comma-separated status filter (backlog, pending, in_progress, completed, canceled, blocked). Defaults to 'in_progress,pending'."
4250
- },
4251
- phase: {
4252
- type: "string",
4253
- description: "Filter by phase (e.g., 'research', 'implementation')"
4254
- },
4255
- query: {
4256
- type: "string",
4257
- description: "Search keyword matching task code, title, or description"
4258
- },
4259
- limit: {
4260
- type: "number",
4261
- minimum: 1,
4262
- maximum: 100,
4263
- default: 5,
4264
- description: "Maximum rows to return (default 5)"
4015
+ query: { type: "string", description: "Search query (optional, searches title/content)" },
4016
+ stack: {
4017
+ type: "array",
4018
+ items: { type: "string" },
4019
+ description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
4265
4020
  },
4266
- offset: {
4267
- type: "number",
4268
- minimum: 0,
4269
- default: 0,
4270
- description: "Offset for pagination"
4021
+ language: { type: "string", description: "Programming language filter" },
4022
+ version: { type: "string", description: "Version filter" },
4023
+ repo: { type: "string", description: "Repository filter (optional)" },
4024
+ is_global: { type: "boolean", description: "Filter by global/repo-specific" },
4025
+ limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4026
+ offset: { type: "number", minimum: 0, default: 0 },
4027
+ structured: { type: "boolean", default: false }
4028
+ },
4029
+ required: []
4030
+ },
4031
+ outputSchema: {
4032
+ type: "object",
4033
+ properties: {
4034
+ success: { type: "boolean" },
4035
+ standards: {
4036
+ type: "array",
4037
+ items: {
4038
+ type: "object",
4039
+ properties: {
4040
+ id: { type: "string" },
4041
+ title: { type: "string" },
4042
+ content: { type: "string" },
4043
+ context: { type: "string" },
4044
+ version: { type: "string" },
4045
+ language: { type: "string", nullable: true },
4046
+ stack: { type: "array", items: { type: "string" } },
4047
+ is_global: { type: "boolean" },
4048
+ repo: { type: "string", nullable: true },
4049
+ tags: { type: "array", items: { type: "string" } },
4050
+ metadata: { type: "object" },
4051
+ created_at: { type: "string" },
4052
+ updated_at: { type: "string" },
4053
+ agent: { type: "string" },
4054
+ model: { type: "string" }
4055
+ }
4056
+ },
4057
+ description: "Matching coding standards"
4271
4058
  },
4272
- structured: {
4273
- type: "boolean",
4274
- default: false,
4275
- description: "If true, returns structured JSON without the text content summary."
4276
- }
4059
+ count: { type: "number", description: "Number of results returned" },
4060
+ message: { type: "string" }
4277
4061
  },
4278
- required: ["repo"]
4062
+ required: ["success", "standards", "count", "message"]
4063
+ }
4064
+ }
4065
+ ];
4066
+
4067
+ // src/mcp/utils/pagination.ts
4068
+ function encodeCursor(offset) {
4069
+ return Buffer.from(String(offset), "utf8").toString("base64");
4070
+ }
4071
+ function decodeCursor(cursor) {
4072
+ if (cursor === void 0 || cursor === null || cursor === "") {
4073
+ return 0;
4074
+ }
4075
+ if (typeof cursor !== "string" || cursor.trim() === "") {
4076
+ throw invalidPaginationParams("Invalid cursor");
4077
+ }
4078
+ let decoded;
4079
+ try {
4080
+ decoded = Buffer.from(cursor, "base64").toString("utf8");
4081
+ } catch {
4082
+ throw invalidPaginationParams("Invalid cursor");
4083
+ }
4084
+ if (!/^\d+$/.test(decoded)) {
4085
+ throw invalidPaginationParams("Invalid cursor");
4086
+ }
4087
+ const offset = Number.parseInt(decoded, 10);
4088
+ if (!Number.isFinite(offset) || offset < 0) {
4089
+ throw invalidPaginationParams("Invalid cursor");
4090
+ }
4091
+ return offset;
4092
+ }
4093
+ function invalidPaginationParams(message) {
4094
+ const error = new Error(message);
4095
+ error.code = -32602;
4096
+ return error;
4097
+ }
4098
+
4099
+ // src/mcp/utils/completion.ts
4100
+ var MAX_COMPLETION_VALUES = 100;
4101
+ function rankCompletionValues(candidates, input) {
4102
+ const unique = [...new Set(candidates.filter(Boolean))];
4103
+ const needle = input.trim().toLowerCase();
4104
+ if (!needle) {
4105
+ return unique.slice(0, MAX_COMPLETION_VALUES);
4106
+ }
4107
+ 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);
4108
+ }
4109
+ function scoreCompletionValue(value, needle) {
4110
+ const haystack = value.toLowerCase();
4111
+ if (haystack === needle) return 100;
4112
+ if (haystack.startsWith(needle)) return 75;
4113
+ if (haystack.includes(needle)) return 50;
4114
+ const compactNeedle = needle.replace(/[\s_-]+/g, "");
4115
+ const compactHaystack = haystack.replace(/[\s_-]+/g, "");
4116
+ if (compactNeedle && compactHaystack.includes(compactNeedle)) return 25;
4117
+ return 0;
4118
+ }
4119
+
4120
+ // src/mcp/resources/index.ts
4121
+ var DEFAULT_PAGE_SIZE = 25;
4122
+ var MAX_PAGE_SIZE = 100;
4123
+ function listResources(session, params) {
4124
+ const resources = [
4125
+ {
4126
+ uri: "repository://index",
4127
+ name: "Repository Index",
4128
+ title: "Repository Index",
4129
+ description: "List of all known repositories with memory/task counts and last activity",
4130
+ mimeType: "application/json",
4131
+ annotations: {
4132
+ audience: ["assistant"],
4133
+ priority: 1,
4134
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4135
+ }
4136
+ },
4137
+ {
4138
+ uri: "session://roots",
4139
+ name: "Session Roots",
4140
+ title: "Session Roots",
4141
+ description: session?.roots.length ? "Active workspace roots provided by the MCP client" : "No active workspace roots were provided by the MCP client",
4142
+ mimeType: "application/json",
4143
+ size: Buffer.byteLength(JSON.stringify({ roots: session?.roots ?? [] }), "utf8"),
4144
+ annotations: {
4145
+ audience: ["assistant"],
4146
+ priority: 0.95,
4147
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4148
+ }
4149
+ }
4150
+ ];
4151
+ return paginateEntries("resources", resources, params);
4152
+ }
4153
+ function listResourceTemplates(params) {
4154
+ const templates = [
4155
+ // ── Memory ──────────────────────────────────────────────────────────────
4156
+ {
4157
+ uriTemplate: "repository://{name}/memories",
4158
+ name: "Repository Memories",
4159
+ title: "Repository Memories",
4160
+ description: "All active memory entries for a specific repository",
4161
+ mimeType: "application/json",
4162
+ annotations: { audience: ["assistant"], priority: 0.85 }
4163
+ },
4164
+ {
4165
+ uriTemplate: "repository://{name}/memories?search={search}&type={type}&tag={tag}",
4166
+ name: "Filtered Repository Memories",
4167
+ title: "Filtered Repository Memories",
4168
+ description: "Filter or search memories within a repository by keyword, type, or tag",
4169
+ mimeType: "application/json",
4170
+ annotations: { audience: ["assistant"], priority: 0.8 }
4171
+ },
4172
+ {
4173
+ uriTemplate: "memory://{id}",
4174
+ name: "Memory Detail",
4175
+ title: "Memory Detail",
4176
+ description: "Full content and statistics for a specific memory UUID",
4177
+ mimeType: "application/json",
4178
+ annotations: { audience: ["assistant"], priority: 0.75 }
4179
+ },
4180
+ // ── Tasks ────────────────────────────────────────────────────────────────
4181
+ {
4182
+ uriTemplate: "repository://{name}/tasks",
4183
+ name: "Repository Tasks",
4184
+ title: "Repository Tasks",
4185
+ description: "All active tasks for a specific repository",
4186
+ mimeType: "application/json",
4187
+ annotations: { audience: ["assistant"], priority: 0.9 }
4279
4188
  },
4280
- outputSchema: {
4281
- type: "object",
4282
- properties: {
4283
- schema: { type: "string", enum: ["task-list"] },
4284
- tasks: {
4285
- type: "object",
4286
- properties: {
4287
- columns: {
4288
- type: "array",
4289
- items: { type: "string" },
4290
- description: "Column names in order: id, task_code, title, status, priority, updated_at, comments_count"
4291
- },
4292
- rows: {
4293
- type: "array",
4294
- items: { type: "array" },
4295
- description: "Each row: [id, task_code, title, status, priority, updated_at, comments_count]. Use task-detail to fetch full task."
4296
- }
4297
- },
4298
- required: ["columns", "rows"]
4299
- },
4300
- count: { type: "number" },
4301
- offset: { type: "number" }
4302
- },
4303
- required: ["schema", "tasks", "count"]
4304
- }
4305
- },
4306
- {
4307
- name: "handoff-create",
4308
- title: "Handoff Create",
4309
- description: "Create a handoff record when work needs context transfer between agents. Optionally link it to a task by task_id or task_code, and put machine-readable details in context.",
4310
- annotations: {
4311
- readOnlyHint: false,
4312
- idempotentHint: false,
4313
- destructiveHint: false,
4314
- openWorldHint: false
4189
+ {
4190
+ uriTemplate: "repository://{name}/tasks?status={status}&priority={priority}",
4191
+ name: "Filtered Repository Tasks",
4192
+ title: "Filtered Repository Tasks",
4193
+ description: "Filter tasks within a repository by status or priority level",
4194
+ mimeType: "application/json",
4195
+ annotations: { audience: ["assistant"], priority: 0.85 }
4315
4196
  },
4316
- inputSchema: {
4317
- type: "object",
4318
- properties: {
4319
- repo: { type: "string", description: "Repository name" },
4320
- from_agent: { type: "string", description: "Agent creating the handoff" },
4321
- to_agent: { type: "string", description: "Optional target agent" },
4322
- task_id: { type: "string", format: "uuid", description: "Optional task id to associate" },
4323
- task_code: { type: "string", description: "Optional task code to associate" },
4324
- summary: { type: "string", minLength: 1, description: "Concise human-readable transfer summary" },
4325
- context: {
4326
- type: "object",
4327
- description: "Structured handoff context, such as changed files, blockers, verification status, and next steps"
4328
- },
4329
- expires_at: { type: "string", description: "Optional expiration timestamp" },
4330
- structured: { type: "boolean", default: false }
4331
- },
4332
- required: ["repo", "from_agent", "summary"]
4197
+ {
4198
+ uriTemplate: "task://{id}",
4199
+ name: "Task Detail",
4200
+ title: "Task Detail",
4201
+ description: "Full content and comments for a specific task UUID",
4202
+ mimeType: "application/json",
4203
+ annotations: { audience: ["assistant"], priority: 0.8 }
4333
4204
  },
4334
- outputSchema: {
4335
- type: "object",
4336
- properties: {
4337
- id: { type: "string" },
4338
- repo: { type: "string" },
4339
- from_agent: { type: "string" },
4340
- to_agent: { type: "string", nullable: true },
4341
- task_id: { type: "string", nullable: true },
4342
- summary: { type: "string" },
4343
- context: { type: "object" },
4344
- status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4345
- created_at: { type: "string" },
4346
- updated_at: { type: "string" },
4347
- expires_at: { type: "string", nullable: true }
4348
- },
4349
- required: ["id", "repo", "from_agent", "summary", "context", "status", "created_at", "updated_at"]
4350
- }
4351
- },
4352
- {
4353
- name: "handoff-list",
4354
- title: "Handoff List",
4355
- description: "Navigation layer for handoff queues. List repository handoffs with optional status and agent filters, then inspect selected rows before acting.",
4356
- annotations: {
4357
- readOnlyHint: true,
4358
- idempotentHint: true,
4359
- openWorldHint: false
4205
+ // ── Repository extras ────────────────────────────────────────────────────
4206
+ {
4207
+ uriTemplate: "repository://{name}/summary",
4208
+ name: "Repository Summary",
4209
+ title: "Repository Summary",
4210
+ description: "High-level architectural summary for a repository",
4211
+ mimeType: "text/plain",
4212
+ annotations: { audience: ["assistant"], priority: 0.95 }
4360
4213
  },
4361
- inputSchema: {
4362
- type: "object",
4363
- properties: {
4364
- repo: { type: "string", description: "Repository name" },
4365
- status: { type: "string", enum: ["pending", "accepted", "rejected", "expired"] },
4366
- from_agent: { type: "string" },
4367
- to_agent: { type: "string" },
4368
- limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4369
- offset: { type: "number", minimum: 0, default: 0 },
4370
- structured: { type: "boolean", default: false }
4371
- },
4372
- required: ["repo"]
4214
+ {
4215
+ uriTemplate: "repository://{name}/actions",
4216
+ name: "Repository Actions",
4217
+ title: "Repository Actions",
4218
+ description: "Audit log of agent tool actions scoped to a repository",
4219
+ mimeType: "application/json",
4220
+ annotations: { audience: ["assistant"], priority: 0.6 }
4373
4221
  },
4374
- outputSchema: {
4375
- type: "object",
4376
- properties: {
4377
- schema: { type: "string", enum: ["handoff-list"] },
4378
- handoffs: {
4379
- type: "object",
4380
- properties: {
4381
- columns: {
4382
- type: "array",
4383
- items: { type: "string" },
4384
- description: "Column names: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4385
- },
4386
- rows: {
4387
- type: "array",
4388
- items: { type: "array" },
4389
- description: "Each row: [id, from_agent, to_agent, task_id, status, created_at, summary]"
4222
+ // ── Action detail ────────────────────────────────────────────────────────
4223
+ {
4224
+ uriTemplate: "action://{id}",
4225
+ name: "Action Detail",
4226
+ title: "Action Detail",
4227
+ description: "Full details of a specific audit log entry by integer ID",
4228
+ mimeType: "application/json",
4229
+ annotations: { audience: ["assistant"], priority: 0.55 }
4230
+ }
4231
+ ];
4232
+ return paginateEntries("resourceTemplates", templates, params);
4233
+ }
4234
+ function completeResourceArgument(resourceUri, argumentName, argumentValue, _contextArguments, dataSources) {
4235
+ 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") {
4236
+ if (argumentName === "name") {
4237
+ return rankCompletionValues(dataSources.repos, argumentValue);
4238
+ }
4239
+ }
4240
+ if (resourceUri === "repository://{name}/memories?search={search}&type={type}&tag={tag}") {
4241
+ if (argumentName === "tag") {
4242
+ return rankCompletionValues(dataSources.tags, argumentValue);
4243
+ }
4244
+ }
4245
+ throw invalidCompletionParams(`Unknown resource template or argument: ${resourceUri} (${argumentName})`);
4246
+ }
4247
+ function readResource(uri, db, session) {
4248
+ logger.info("[Tool] resource.read", { uri });
4249
+ if (uri === "repository://index") {
4250
+ const repos = db.system.listRepoNavigation();
4251
+ const payload = JSON.stringify(repos, null, 2);
4252
+ return {
4253
+ contents: [
4254
+ {
4255
+ uri,
4256
+ mimeType: "application/json",
4257
+ text: payload,
4258
+ size: Buffer.byteLength(payload, "utf8"),
4259
+ annotations: {
4260
+ audience: ["assistant"],
4261
+ priority: 1,
4262
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4263
+ }
4264
+ }
4265
+ ]
4266
+ };
4267
+ }
4268
+ if (uri === "session://roots") {
4269
+ const payload = JSON.stringify({ roots: session?.roots ?? [] }, null, 2);
4270
+ return {
4271
+ contents: [
4272
+ {
4273
+ uri,
4274
+ mimeType: "application/json",
4275
+ text: payload,
4276
+ size: Buffer.byteLength(payload, "utf8"),
4277
+ annotations: {
4278
+ audience: ["assistant"],
4279
+ priority: 0.95,
4280
+ lastModified: (/* @__PURE__ */ new Date()).toISOString()
4281
+ }
4282
+ }
4283
+ ]
4284
+ };
4285
+ }
4286
+ const memoryIdMatch = uri.match(/^memory:\/\/([0-9a-f-]{36})$/i);
4287
+ if (memoryIdMatch) {
4288
+ const id = memoryIdMatch[1];
4289
+ const entry = db.memories.getByIdWithStats(id);
4290
+ if (!entry) throw resourceNotFound(`Memory with ID ${id} not found.`, uri);
4291
+ const payload = JSON.stringify(entry, null, 2);
4292
+ return {
4293
+ contents: [
4294
+ {
4295
+ uri,
4296
+ mimeType: "application/json",
4297
+ text: payload,
4298
+ size: Buffer.byteLength(payload, "utf8"),
4299
+ annotations: {
4300
+ audience: ["assistant"],
4301
+ priority: 0.75,
4302
+ lastModified: entry.updated_at || entry.created_at
4303
+ }
4304
+ }
4305
+ ]
4306
+ };
4307
+ }
4308
+ const taskIdMatch = uri.match(/^task:\/\/([0-9a-f-]{36})$/i);
4309
+ if (taskIdMatch) {
4310
+ const id = taskIdMatch[1];
4311
+ const task = db.tasks.getTaskById(id);
4312
+ if (!task) throw resourceNotFound(`Task with ID ${id} not found.`, uri);
4313
+ const payload = JSON.stringify(task, null, 2);
4314
+ return {
4315
+ contents: [
4316
+ {
4317
+ uri,
4318
+ mimeType: "application/json",
4319
+ text: payload,
4320
+ size: Buffer.byteLength(payload, "utf8"),
4321
+ annotations: {
4322
+ audience: ["assistant"],
4323
+ priority: 0.8,
4324
+ lastModified: task.updated_at || task.created_at
4325
+ }
4326
+ }
4327
+ ]
4328
+ };
4329
+ }
4330
+ const repoBase = parseRepoUri(uri);
4331
+ if (repoBase) {
4332
+ const { name, path: repoPath, query } = repoBase;
4333
+ if (repoPath === "summary") {
4334
+ const summary = db.summaries.getSummary(name);
4335
+ const text = summary?.summary || `No summary available for repository: ${name}`;
4336
+ return {
4337
+ contents: [
4338
+ {
4339
+ uri,
4340
+ mimeType: "text/plain",
4341
+ text,
4342
+ size: Buffer.byteLength(text, "utf8"),
4343
+ annotations: {
4344
+ audience: ["assistant"],
4345
+ priority: 0.95,
4346
+ lastModified: summary?.updated_at || (/* @__PURE__ */ new Date()).toISOString()
4390
4347
  }
4391
- },
4392
- required: ["columns", "rows"]
4393
- },
4394
- count: { type: "number" },
4395
- offset: { type: "number" }
4396
- },
4397
- required: ["schema", "handoffs", "count", "offset"]
4348
+ }
4349
+ ]
4350
+ };
4398
4351
  }
4399
- },
4400
- {
4401
- name: "task-claim",
4402
- title: "Task Claim",
4403
- 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.",
4404
- annotations: {
4405
- readOnlyHint: false,
4406
- idempotentHint: false,
4407
- destructiveHint: false,
4408
- openWorldHint: false
4409
- },
4410
- inputSchema: {
4411
- type: "object",
4412
- properties: {
4413
- repo: { type: "string", description: "Repository name" },
4414
- task_id: { type: "string", format: "uuid", description: "Task id to claim. Optional if task_code is provided." },
4415
- task_code: { type: "string", description: "Task code to claim. Optional if task_id is provided." },
4416
- agent: { type: "string", description: "Claiming agent name" },
4417
- role: { type: "string", description: "Claiming agent role" },
4418
- metadata: { type: "object", description: "Optional claim metadata" },
4419
- structured: { type: "boolean", default: false }
4420
- },
4421
- required: ["repo", "agent"]
4422
- },
4423
- outputSchema: {
4424
- type: "object",
4425
- properties: {
4426
- id: { type: "string" },
4427
- repo: { type: "string" },
4428
- task_id: { type: "string" },
4429
- task_code: { type: "string", nullable: true },
4430
- agent: { type: "string" },
4431
- role: { type: "string" },
4432
- claimed_at: { type: "string" },
4433
- released_at: { type: "string", nullable: true },
4434
- metadata: { type: "object" }
4435
- },
4436
- required: ["id", "repo", "task_id", "agent", "role", "claimed_at", "metadata"]
4352
+ if (repoPath === "memories") {
4353
+ const search = query.get("search") || "";
4354
+ const type = query.get("type");
4355
+ const tag = query.get("tag");
4356
+ const result = db.memories.listMemoriesForDashboard({
4357
+ repo: name,
4358
+ type: type || void 0,
4359
+ tag: tag || void 0,
4360
+ search: search || void 0,
4361
+ limit: 50
4362
+ });
4363
+ const entries = result.items;
4364
+ const payload = JSON.stringify(entries, null, 2);
4365
+ return {
4366
+ contents: [
4367
+ {
4368
+ uri,
4369
+ mimeType: "application/json",
4370
+ text: payload,
4371
+ size: Buffer.byteLength(payload, "utf8"),
4372
+ annotations: {
4373
+ audience: ["assistant"],
4374
+ priority: 0.85,
4375
+ lastModified: deriveLastModifiedFromCollection(
4376
+ entries.map((e) => e.updated_at || e.created_at)
4377
+ )
4378
+ }
4379
+ }
4380
+ ]
4381
+ };
4437
4382
  }
4438
- },
4439
- {
4440
- name: "standard-store",
4441
- title: "Standard Store",
4442
- description: "Store one atomic coding standard. Use for durable implementation rules with explicit context, stack/language filters, and repo/global scope.",
4443
- annotations: {
4444
- readOnlyHint: false,
4445
- idempotentHint: false,
4446
- destructiveHint: false,
4447
- openWorldHint: false
4448
- },
4449
- inputSchema: {
4450
- type: "object",
4451
- properties: {
4452
- name: { type: "string", minLength: 3, maxLength: 255, description: "Human-readable standard name" },
4453
- content: { type: "string", minLength: 10, description: "One atomic, actionable standard written as concise Markdown" },
4454
- context: { type: "string", description: "Context or category (e.g., 'error-handling', 'security')" },
4455
- version: { type: "string", description: "Version of the standard (e.g., '1.0.0')" },
4456
- language: { type: "string", description: "Programming language (e.g., 'typescript', 'python')" },
4457
- stack: {
4458
- type: "array",
4459
- items: { type: "string" },
4460
- description: "Technology stack (e.g., ['react', 'nextjs'])"
4461
- },
4462
- repo: { type: "string", description: "Repository name for repo-specific standards. Omit only for global standards." },
4463
- is_global: { type: "boolean", description: "Whether standard applies globally or repo-specific" },
4464
- tags: {
4465
- type: "array",
4466
- items: { type: "string" },
4467
- description: "Tags for categorization"
4468
- },
4469
- metadata: {
4470
- type: "object",
4471
- description: "Additional metadata"
4472
- },
4473
- agent: { type: "string", description: "Agent creating the standard" },
4474
- model: { type: "string", description: "AI model used" },
4475
- structured: { type: "boolean", default: false }
4476
- },
4477
- required: ["name", "content"]
4478
- },
4479
- outputSchema: {
4480
- type: "object",
4481
- properties: {
4482
- success: { type: "boolean" },
4483
- standard: {
4484
- type: "object",
4485
- properties: {
4486
- id: { type: "string" },
4487
- title: { type: "string" },
4488
- content: { type: "string" },
4489
- context: { type: "string" },
4490
- version: { type: "string" },
4491
- language: { type: "string", nullable: true },
4492
- stack: { type: "array", items: { type: "string" } },
4493
- is_global: { type: "boolean" },
4494
- repo: { type: "string", nullable: true },
4495
- tags: { type: "array", items: { type: "string" } },
4496
- metadata: { type: "object" },
4497
- created_at: { type: "string" },
4498
- updated_at: { type: "string" },
4499
- agent: { type: "string" },
4500
- model: { type: "string" }
4501
- },
4502
- required: [
4503
- "id",
4504
- "title",
4505
- "content",
4506
- "context",
4507
- "version",
4508
- "stack",
4509
- "is_global",
4510
- "tags",
4511
- "metadata",
4512
- "created_at",
4513
- "updated_at",
4514
- "agent",
4515
- "model"
4516
- ]
4517
- },
4518
- message: { type: "string" }
4519
- },
4520
- required: ["success", "standard", "message"]
4383
+ if (repoPath === "tasks") {
4384
+ const status = query.get("status");
4385
+ const priority = query.get("priority");
4386
+ let tasks;
4387
+ if (status && status !== "all") {
4388
+ const statuses = status.split(",").map((s) => s.trim());
4389
+ tasks = db.tasks.getTasksByMultipleStatuses(name, statuses);
4390
+ } else {
4391
+ tasks = db.tasks.getTasksByMultipleStatuses(name, ["backlog", "pending", "in_progress", "blocked"]);
4392
+ }
4393
+ if (priority) {
4394
+ const p = Number(priority);
4395
+ if (!isNaN(p)) {
4396
+ tasks = tasks.filter((t) => t.priority === p);
4397
+ }
4398
+ }
4399
+ const payload = JSON.stringify(tasks, null, 2);
4400
+ return {
4401
+ contents: [
4402
+ {
4403
+ uri,
4404
+ mimeType: "application/json",
4405
+ text: payload,
4406
+ size: Buffer.byteLength(payload, "utf8"),
4407
+ annotations: {
4408
+ audience: ["assistant"],
4409
+ priority: 0.9,
4410
+ lastModified: deriveLastModifiedFromCollection(tasks.map((t) => t.updated_at))
4411
+ }
4412
+ }
4413
+ ]
4414
+ };
4521
4415
  }
4522
- },
4523
- {
4524
- name: "standard-search",
4525
- title: "Standard Search",
4526
- description: "Navigation and lookup layer for coding standards. Query by text, stack, language, version, repo, and global scope before applying or creating standards.",
4527
- annotations: {
4528
- readOnlyHint: true,
4529
- idempotentHint: true,
4530
- openWorldHint: false
4531
- },
4532
- inputSchema: {
4533
- type: "object",
4534
- properties: {
4535
- query: { type: "string", description: "Search query (optional, searches title/content)" },
4536
- stack: {
4537
- type: "array",
4538
- items: { type: "string" },
4539
- description: "Technology stack to filter by (e.g., ['react', 'nextjs'])"
4540
- },
4541
- language: { type: "string", description: "Programming language filter" },
4542
- version: { type: "string", description: "Version filter" },
4543
- repo: { type: "string", description: "Repository filter (optional)" },
4544
- is_global: { type: "boolean", description: "Filter by global/repo-specific" },
4545
- limit: { type: "number", minimum: 1, maximum: 100, default: 20 },
4546
- offset: { type: "number", minimum: 0, default: 0 },
4547
- structured: { type: "boolean", default: false }
4548
- },
4549
- required: []
4550
- },
4551
- outputSchema: {
4552
- type: "object",
4553
- properties: {
4554
- success: { type: "boolean" },
4555
- standards: {
4556
- type: "array",
4557
- items: {
4558
- type: "object",
4559
- properties: {
4560
- id: { type: "string" },
4561
- title: { type: "string" },
4562
- content: { type: "string" },
4563
- context: { type: "string" },
4564
- version: { type: "string" },
4565
- language: { type: "string", nullable: true },
4566
- stack: { type: "array", items: { type: "string" } },
4567
- is_global: { type: "boolean" },
4568
- repo: { type: "string", nullable: true },
4569
- tags: { type: "array", items: { type: "string" } },
4570
- metadata: { type: "object" },
4571
- created_at: { type: "string" },
4572
- updated_at: { type: "string" },
4573
- agent: { type: "string" },
4574
- model: { type: "string" }
4416
+ if (repoPath === "actions") {
4417
+ const actions = db.actions.getRecentActions(name, 100);
4418
+ const payload = JSON.stringify(actions, null, 2);
4419
+ return {
4420
+ contents: [
4421
+ {
4422
+ uri,
4423
+ mimeType: "application/json",
4424
+ text: payload,
4425
+ size: Buffer.byteLength(payload, "utf8"),
4426
+ annotations: {
4427
+ audience: ["assistant"],
4428
+ priority: 0.6,
4429
+ lastModified: deriveLastModifiedFromCollection(actions.map((a) => a.created_at))
4575
4430
  }
4576
- },
4577
- description: "Matching coding standards"
4578
- },
4579
- count: { type: "number", description: "Number of results returned" },
4580
- message: { type: "string" }
4581
- },
4582
- required: ["success", "standards", "count", "message"]
4431
+ }
4432
+ ]
4433
+ };
4583
4434
  }
4584
4435
  }
4585
- ];
4436
+ const actionIdMatch = uri.match(/^action:\/\/(\d+)$/);
4437
+ if (actionIdMatch) {
4438
+ const id = Number(actionIdMatch[1]);
4439
+ const action = db.actions.getActionById(id);
4440
+ if (!action) throw resourceNotFound(`Action with ID ${id} not found.`, uri);
4441
+ const payload = JSON.stringify(action, null, 2);
4442
+ return {
4443
+ contents: [
4444
+ {
4445
+ uri,
4446
+ mimeType: "application/json",
4447
+ text: payload,
4448
+ size: Buffer.byteLength(payload, "utf8"),
4449
+ annotations: {
4450
+ audience: ["assistant"],
4451
+ priority: 0.55,
4452
+ lastModified: action.created_at
4453
+ }
4454
+ }
4455
+ ]
4456
+ };
4457
+ }
4458
+ throw resourceNotFound(`Unknown resource URI: ${uri}`, uri);
4459
+ }
4460
+ function parseRepoUri(uri) {
4461
+ const prefix = "repository://";
4462
+ if (!uri.startsWith(prefix)) return null;
4463
+ const rest = uri.slice(prefix.length);
4464
+ const queryStart = rest.indexOf("?");
4465
+ const withoutQuery = queryStart === -1 ? rest : rest.slice(0, queryStart);
4466
+ const queryString = queryStart === -1 ? "" : rest.slice(queryStart + 1);
4467
+ const slashIdx = withoutQuery.indexOf("/");
4468
+ if (slashIdx === -1) return null;
4469
+ const name = withoutQuery.slice(0, slashIdx);
4470
+ const path6 = withoutQuery.slice(slashIdx + 1);
4471
+ if (!name || !path6) return null;
4472
+ return { name, path: path6, query: new URLSearchParams(queryString) };
4473
+ }
4474
+ function paginateEntries(key, entries, params) {
4475
+ const limit = normalizeLimit(params?.limit);
4476
+ const offset = decodeCursor(params?.cursor);
4477
+ const sliced = entries.slice(offset, offset + limit);
4478
+ const nextOffset = offset + sliced.length;
4479
+ return {
4480
+ [key]: sliced,
4481
+ nextCursor: nextOffset < entries.length ? encodeCursor(nextOffset) : void 0
4482
+ };
4483
+ }
4484
+ function normalizeLimit(limit) {
4485
+ if (typeof limit !== "number" || !Number.isFinite(limit)) {
4486
+ return DEFAULT_PAGE_SIZE;
4487
+ }
4488
+ return Math.min(MAX_PAGE_SIZE, Math.max(1, Math.trunc(limit)));
4489
+ }
4490
+ function deriveLastModifiedFromCollection(values) {
4491
+ const normalized = values.filter((value) => typeof value === "string" && value.length > 0);
4492
+ return normalized.sort().at(-1) ?? (/* @__PURE__ */ new Date()).toISOString();
4493
+ }
4494
+ function resourceNotFound(message, uri) {
4495
+ const error = new Error(message);
4496
+ error.code = -32002;
4497
+ error.data = { uri };
4498
+ return error;
4499
+ }
4500
+ function invalidCompletionParams(message) {
4501
+ const error = new Error(message);
4502
+ error.code = -32602;
4503
+ return error;
4504
+ }
4505
+
4506
+ // src/mcp/prompts/loader.ts
4507
+ import fs4 from "fs";
4508
+ import path5 from "path";
4509
+ import { fileURLToPath as fileURLToPath3 } from "url";
4510
+ import matter from "gray-matter";
4511
+ var __filename = fileURLToPath3(import.meta.url);
4512
+ var __dirname2 = path5.dirname(__filename);
4513
+ function findPromptDir() {
4514
+ const candidates = [
4515
+ // Production if chunked into dist/
4516
+ "./prompts",
4517
+ // Production if inlined into dist/mcp/
4518
+ "../prompts",
4519
+ // Dev: /src/mcp/prompts/definitions (next to loader.ts)
4520
+ "./definitions"
4521
+ ].map((relPath) => path5.resolve(__dirname2, relPath));
4522
+ for (const dir of candidates) {
4523
+ if (fs4.existsSync(dir)) {
4524
+ const files = fs4.readdirSync(dir);
4525
+ if (files.some((f) => f.endsWith(".md"))) {
4526
+ return dir;
4527
+ }
4528
+ }
4529
+ }
4530
+ return path5.resolve(__dirname2, "./definitions");
4531
+ }
4532
+ var PROMPT_DIR = findPromptDir();
4533
+ function listPromptFiles() {
4534
+ if (!fs4.existsSync(PROMPT_DIR)) return [];
4535
+ return fs4.readdirSync(PROMPT_DIR).filter((file) => file.endsWith(".md")).map((file) => file.replace(/\.md$/, "")).sort();
4536
+ }
4537
+ function loadPromptFromMarkdown(name) {
4538
+ const filePath = path5.join(PROMPT_DIR, `${name}.md`);
4539
+ if (!fs4.existsSync(filePath)) {
4540
+ throw new Error(`Prompt file not found: ${filePath}`);
4541
+ }
4542
+ const fileContent = fs4.readFileSync(filePath, "utf-8");
4543
+ const { data, content } = matter(fileContent);
4544
+ return {
4545
+ name: data.name || name,
4546
+ description: data.description || "",
4547
+ arguments: data.arguments || [],
4548
+ agent: data.agent,
4549
+ content: content.trim()
4550
+ };
4551
+ }
4552
+
4553
+ // src/mcp/prompts/registry.ts
4554
+ function createPromptDefinition(loaded) {
4555
+ return {
4556
+ name: loaded.name,
4557
+ description: loaded.description,
4558
+ arguments: loaded.arguments,
4559
+ agent: loaded.agent,
4560
+ messages: [
4561
+ {
4562
+ role: "user",
4563
+ content: {
4564
+ type: "text",
4565
+ text: loaded.content
4566
+ }
4567
+ }
4568
+ ]
4569
+ };
4570
+ }
4571
+ var PROMPTS = {};
4572
+ var promptFiles = listPromptFiles();
4573
+ for (const name of promptFiles) {
4574
+ try {
4575
+ PROMPTS[name] = createPromptDefinition(loadPromptFromMarkdown(name));
4576
+ } catch (e) {
4577
+ logger.warn(`Failed to load prompt ${name}: ${e}`);
4578
+ }
4579
+ }
4580
+ async function listPrompts(db, session, params) {
4581
+ const allPrompts = Object.values(PROMPTS).map((p) => ({
4582
+ name: p.name,
4583
+ description: p.description,
4584
+ arguments: p.arguments,
4585
+ metadata: p.agent ? { agent: p.agent } : void 0
4586
+ }));
4587
+ const rawLimit = typeof params?.limit === "number" && Number.isInteger(params?.limit) ? params.limit : 25;
4588
+ const limit = Math.max(1, Math.min(100, Math.trunc(rawLimit)));
4589
+ const offset = decodeCursor(params?.cursor);
4590
+ const sliced = allPrompts.slice(offset, offset + limit);
4591
+ const nextOffset = offset + sliced.length;
4592
+ return {
4593
+ prompts: sliced,
4594
+ nextCursor: nextOffset < allPrompts.length ? encodeCursor(nextOffset) : void 0
4595
+ };
4596
+ }
4597
+ async function getPrompt(name, args = {}, db, session) {
4598
+ const prompt = PROMPTS[name];
4599
+ if (!prompt) {
4600
+ throw new Error(`Prompt not found: ${name}`);
4601
+ }
4602
+ const inferredRepo = inferRepoFromSession(session);
4603
+ const messages = prompt.messages.map((m) => {
4604
+ let text = m.content.text;
4605
+ for (const [key, value] of Object.entries(args)) {
4606
+ text = text.replace(new RegExp(`\\{{${key}\\}}`, "g"), value);
4607
+ }
4608
+ text = text.replace(/{{current_repo}}/g, inferredRepo || "unknown-repo");
4609
+ return {
4610
+ ...m,
4611
+ content: {
4612
+ ...m.content,
4613
+ text
4614
+ }
4615
+ };
4616
+ });
4617
+ return {
4618
+ description: prompt.description,
4619
+ messages,
4620
+ metadata: prompt.agent ? { agent: prompt.agent } : void 0
4621
+ };
4622
+ }
4623
+ async function completePromptArgument(name, argName, value, contextArguments, dataSources) {
4624
+ void name;
4625
+ void contextArguments;
4626
+ if (argName === "task_id") {
4627
+ const values = dataSources.tasks.map((t) => t.id);
4628
+ return rankCompletionValues(values, value);
4629
+ }
4630
+ return [];
4631
+ }
4586
4632
 
4587
4633
  export {
4634
+ MCP_PROTOCOL_VERSION,
4635
+ CAPABILITIES,
4588
4636
  logger,
4589
4637
  setLogLevel,
4590
4638
  getLogLevel,
4591
4639
  addLogSink,
4592
4640
  LOG_LEVEL_VALUES,
4593
4641
  createFileSink,
4594
- encodeCursor,
4595
- decodeCursor,
4596
- listResources,
4597
- listResourceTemplates,
4598
- completeResourceArgument,
4599
- readResource,
4600
- createSessionContext,
4601
- updateSessionFromInitialize,
4602
- updateSessionRoots,
4603
- extractRootsFromResult,
4604
- getFilesystemRoots,
4605
- isPathWithinRoots,
4606
- findContainingRoot,
4607
- inferRepoFromSession,
4608
- PROMPTS,
4609
- listPrompts,
4610
- getPrompt,
4611
- completePromptArgument,
4612
4642
  normalizeRepo,
4643
+ SQLiteStore,
4613
4644
  MemoryStoreSchema,
4614
4645
  MemoryUpdateSchema,
4615
4646
  MemorySearchSchema,
@@ -4626,12 +4657,28 @@ export {
4626
4657
  MemoryDetailSchema,
4627
4658
  TaskGetSchema,
4628
4659
  HandoffCreateSchema,
4660
+ HandoffUpdateSchema,
4629
4661
  HandoffListSchema,
4630
4662
  TaskClaimSchema,
4631
4663
  StandardStoreSchema,
4632
4664
  StandardSearchSchema,
4633
4665
  TOOL_DEFINITIONS,
4634
- SQLiteStore,
4635
- MCP_PROTOCOL_VERSION,
4636
- CAPABILITIES
4666
+ encodeCursor,
4667
+ decodeCursor,
4668
+ listResources,
4669
+ listResourceTemplates,
4670
+ completeResourceArgument,
4671
+ readResource,
4672
+ createSessionContext,
4673
+ updateSessionFromInitialize,
4674
+ updateSessionRoots,
4675
+ extractRootsFromResult,
4676
+ getFilesystemRoots,
4677
+ isPathWithinRoots,
4678
+ findContainingRoot,
4679
+ inferRepoFromSession,
4680
+ PROMPTS,
4681
+ listPrompts,
4682
+ getPrompt,
4683
+ completePromptArgument
4637
4684
  };