@crowley/rag-mcp 1.5.0 → 1.6.1

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.
@@ -5,7 +5,7 @@
5
5
  * batch_remember, validate_memory, review_memories,
6
6
  * promote_memory, run_quality_gates, memory_maintenance
7
7
  */
8
- import { formatMemoryResults, truncate, paginationFooter, PREVIEW } from "../formatters.js";
8
+ import { formatMemoryResults, truncate, paginationFooter, PREVIEW, } from "../formatters.js";
9
9
  import { z } from "zod";
10
10
  import { TOOL_ANNOTATIONS } from "../annotations.js";
11
11
  const typeEmojis = {
@@ -15,6 +15,7 @@ const typeEmojis = {
15
15
  todo: "\u{1F4CB}",
16
16
  conversation: "\u{1F4AC}",
17
17
  note: "\u{1F4DD}",
18
+ procedure: "\u{1F4D6}",
18
19
  };
19
20
  const statusEmojis = {
20
21
  pending: "\u23F3",
@@ -29,8 +30,22 @@ export function createMemoryTools(projectName) {
29
30
  description: "Store important information in agent memory. Use this to save decisions, insights, context, todos, or important conversations for future reference.",
30
31
  schema: z.object({
31
32
  content: z.string().describe("Information to remember"),
32
- type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Type of memory (default: note)"),
33
- tags: z.array(z.string()).optional().describe("Tags for categorization (e.g., ['feature-x', 'important'])"),
33
+ type: z
34
+ .enum([
35
+ "decision",
36
+ "insight",
37
+ "context",
38
+ "todo",
39
+ "conversation",
40
+ "note",
41
+ "procedure",
42
+ ])
43
+ .optional()
44
+ .describe("Type of memory (default: note)"),
45
+ tags: z
46
+ .array(z.string())
47
+ .optional()
48
+ .describe("Tags for categorization (e.g., ['feature-x', 'important'])"),
34
49
  relatedTo: z.string().optional().describe("Related feature or topic"),
35
50
  }),
36
51
  annotations: TOOL_ANNOTATIONS["remember"],
@@ -61,9 +76,27 @@ export function createMemoryTools(projectName) {
61
76
  description: "Retrieve relevant memories based on context. Searches agent memory for past decisions, insights, and notes related to the query.",
62
77
  schema: z.object({
63
78
  query: z.string().describe("What to recall (semantic search)"),
64
- type: z.enum(["decision", "insight", "context", "todo", "conversation", "note", "procedure", "all"]).optional().describe("Filter by memory type (default: all)"),
65
- limit: z.coerce.number().optional().describe("Max memories to retrieve (default: 5)"),
66
- graphRecall: z.boolean().optional().describe("Enable graph-aware recall with spreading activation (default: false)"),
79
+ type: z
80
+ .enum([
81
+ "decision",
82
+ "insight",
83
+ "context",
84
+ "todo",
85
+ "conversation",
86
+ "note",
87
+ "procedure",
88
+ "all",
89
+ ])
90
+ .optional()
91
+ .describe("Filter by memory type (default: all)"),
92
+ limit: z.coerce
93
+ .number()
94
+ .optional()
95
+ .describe("Max memories to retrieve (default: 5)"),
96
+ graphRecall: z
97
+ .boolean()
98
+ .optional()
99
+ .describe("Enable graph-aware recall with spreading activation (default: false)"),
67
100
  }),
68
101
  annotations: TOOL_ANNOTATIONS["recall"],
69
102
  handler: async (args, ctx) => {
@@ -90,10 +123,27 @@ export function createMemoryTools(projectName) {
90
123
  name: "list_memories",
91
124
  description: "List recent memories or filter by type/tags. Shows what the agent has remembered.",
92
125
  schema: z.object({
93
- type: z.enum(["decision", "insight", "context", "todo", "conversation", "note", "all"]).optional().describe("Filter by type"),
126
+ type: z
127
+ .enum([
128
+ "decision",
129
+ "insight",
130
+ "context",
131
+ "todo",
132
+ "conversation",
133
+ "note",
134
+ "all",
135
+ ])
136
+ .optional()
137
+ .describe("Filter by type"),
94
138
  tag: z.string().optional().describe("Filter by tag"),
95
- limit: z.coerce.number().optional().describe("Max results (default: 10)"),
96
- offset: z.coerce.number().optional().describe("Pagination offset (default: 0)"),
139
+ limit: z.coerce
140
+ .number()
141
+ .optional()
142
+ .describe("Max results (default: 10)"),
143
+ offset: z.coerce
144
+ .number()
145
+ .optional()
146
+ .describe("Pagination offset (default: 0)"),
97
147
  }),
98
148
  annotations: TOOL_ANNOTATIONS["list_memories"],
99
149
  handler: async (args, ctx) => {
@@ -130,9 +180,25 @@ export function createMemoryTools(projectName) {
130
180
  name: "forget",
131
181
  description: "Delete a specific memory by ID or clear memories by type.",
132
182
  schema: z.object({
133
- memoryId: z.string().optional().describe("Specific memory ID to delete"),
134
- type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Delete all memories of this type"),
135
- olderThanDays: z.coerce.number().optional().describe("Delete memories older than N days"),
183
+ memoryId: z
184
+ .string()
185
+ .optional()
186
+ .describe("Specific memory ID to delete"),
187
+ type: z
188
+ .enum([
189
+ "decision",
190
+ "insight",
191
+ "context",
192
+ "todo",
193
+ "conversation",
194
+ "note",
195
+ ])
196
+ .optional()
197
+ .describe("Delete all memories of this type"),
198
+ olderThanDays: z.coerce
199
+ .number()
200
+ .optional()
201
+ .describe("Delete memories older than N days"),
136
202
  }),
137
203
  annotations: TOOL_ANNOTATIONS["forget"],
138
204
  handler: async (args, ctx) => {
@@ -164,7 +230,9 @@ export function createMemoryTools(projectName) {
164
230
  description: "Update status of a todo/task in memory.",
165
231
  schema: z.object({
166
232
  todoId: z.string().describe("Todo memory ID"),
167
- status: z.enum(["pending", "in_progress", "done", "cancelled"]).describe("New status"),
233
+ status: z
234
+ .enum(["pending", "in_progress", "done", "cancelled"])
235
+ .describe("New status"),
168
236
  note: z.string().optional().describe("Optional note about the update"),
169
237
  }),
170
238
  annotations: TOOL_ANNOTATIONS["update_todo"],
@@ -191,12 +259,54 @@ export function createMemoryTools(projectName) {
191
259
  name: "batch_remember",
192
260
  description: `Efficiently store multiple memories at once in ${projectName}. Faster than individual remember calls.`,
193
261
  schema: z.object({
194
- items: z.array(z.object({
262
+ items: z
263
+ .array(z.object({
195
264
  content: z.string().describe("Content to remember"),
196
- type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Memory type (default: note)"),
197
- tags: z.array(z.string()).optional().describe("Tags for categorization"),
198
- relatedTo: z.string().optional().describe("Related feature or topic"),
199
- })).describe("Array of memories to store"),
265
+ type: z
266
+ .enum([
267
+ "decision",
268
+ "insight",
269
+ "context",
270
+ "todo",
271
+ "conversation",
272
+ "note",
273
+ "procedure",
274
+ ])
275
+ .optional()
276
+ .describe("Memory type (default: note)"),
277
+ tags: z
278
+ .array(z.string())
279
+ .optional()
280
+ .describe("Tags for categorization"),
281
+ relatedTo: z
282
+ .string()
283
+ .optional()
284
+ .describe("Related feature or topic"),
285
+ metadata: z
286
+ .record(z.string(), z.unknown())
287
+ .optional()
288
+ .describe("Custom metadata (factEntities, factDateTs, etc.)"),
289
+ factCategory: z
290
+ .enum([
291
+ "personal_info",
292
+ "preference",
293
+ "event",
294
+ "temporal",
295
+ "update",
296
+ "plan",
297
+ ])
298
+ .optional()
299
+ .describe("Structured fact category for temporal retrieval"),
300
+ factEntities: z
301
+ .array(z.string())
302
+ .optional()
303
+ .describe("Named entities: file names, service names, external systems"),
304
+ factDateTs: z
305
+ .number()
306
+ .optional()
307
+ .describe("Unix timestamp (seconds) for temporal filtering"),
308
+ }))
309
+ .describe("Array of memories to store"),
200
310
  }),
201
311
  annotations: TOOL_ANNOTATIONS["batch_remember"],
202
312
  handler: async (args, ctx) => {
@@ -229,7 +339,9 @@ export function createMemoryTools(projectName) {
229
339
  description: `Validate or reject an auto-extracted memory in ${projectName}. Helps improve future extraction accuracy.`,
230
340
  schema: z.object({
231
341
  memoryId: z.string().describe("ID of the memory to validate"),
232
- validated: z.boolean().describe("true to confirm the memory is valuable, false to reject it"),
342
+ validated: z
343
+ .boolean()
344
+ .describe("true to confirm the memory is valuable, false to reject it"),
233
345
  }),
234
346
  annotations: TOOL_ANNOTATIONS["validate_memory"],
235
347
  handler: async (args, ctx) => {
@@ -250,8 +362,14 @@ export function createMemoryTools(projectName) {
250
362
  name: "review_memories",
251
363
  description: `Get auto-extracted memories pending review in ${projectName}. Shows unvalidated learnings that need human confirmation.`,
252
364
  schema: z.object({
253
- limit: z.coerce.number().optional().describe("Max memories to return (default: 20)"),
254
- offset: z.coerce.number().optional().describe("Pagination offset (default: 0)"),
365
+ limit: z.coerce
366
+ .number()
367
+ .optional()
368
+ .describe("Max memories to return (default: 20)"),
369
+ offset: z.coerce
370
+ .number()
371
+ .optional()
372
+ .describe("Pagination offset (default: 0)"),
255
373
  }),
256
374
  annotations: TOOL_ANNOTATIONS["review_memories"],
257
375
  handler: async (args, ctx) => {
@@ -285,10 +403,21 @@ export function createMemoryTools(projectName) {
285
403
  description: `Promote a quarantine memory to durable storage in ${projectName}. Requires a reason for promotion. Optionally runs quality gates before promotion.`,
286
404
  schema: z.object({
287
405
  memoryId: z.string().describe("ID of the memory to promote"),
288
- reason: z.enum(["human_validated", "pr_merged", "tests_passed"]).describe("Reason for promotion"),
289
- evidence: z.string().optional().describe("Optional evidence supporting the promotion"),
290
- runGates: z.boolean().optional().describe("Run quality gates before promotion (default: false)"),
291
- affectedFiles: z.array(z.string()).optional().describe("Files affected by this memory (for quality gate checking)"),
406
+ reason: z
407
+ .enum(["human_validated", "pr_merged", "tests_passed"])
408
+ .describe("Reason for promotion"),
409
+ evidence: z
410
+ .string()
411
+ .optional()
412
+ .describe("Optional evidence supporting the promotion"),
413
+ runGates: z
414
+ .boolean()
415
+ .optional()
416
+ .describe("Run quality gates before promotion (default: false)"),
417
+ affectedFiles: z
418
+ .array(z.string())
419
+ .optional()
420
+ .describe("Files affected by this memory (for quality gate checking)"),
292
421
  }),
293
422
  annotations: TOOL_ANNOTATIONS["promote_memory"],
294
423
  handler: async (args, ctx) => {
@@ -320,8 +449,14 @@ export function createMemoryTools(projectName) {
320
449
  name: "run_quality_gates",
321
450
  description: `Run quality gates (typecheck, tests, blast radius) for ${projectName}.`,
322
451
  schema: z.object({
323
- affectedFiles: z.array(z.string()).optional().describe("Files to check (for related tests and blast radius)"),
324
- skipGates: z.array(z.string()).optional().describe("Gates to skip (typecheck, test, blast_radius)"),
452
+ affectedFiles: z
453
+ .array(z.string())
454
+ .optional()
455
+ .describe("Files to check (for related tests and blast radius)"),
456
+ skipGates: z
457
+ .array(z.string())
458
+ .optional()
459
+ .describe("Gates to skip (typecheck, test, blast_radius)"),
325
460
  }),
326
461
  annotations: TOOL_ANNOTATIONS["run_quality_gates"],
327
462
  handler: async (args, ctx) => {
@@ -359,12 +494,27 @@ export function createMemoryTools(projectName) {
359
494
  name: "memory_maintenance",
360
495
  description: `Run memory maintenance for ${projectName}: quarantine cleanup (expire old auto-memories), feedback-driven promote/prune, and compaction (merge similar durable memories).`,
361
496
  schema: z.object({
362
- operations: z.object({
363
- quarantine_cleanup: z.boolean().optional().describe("Remove expired quarantine memories (default: true)"),
364
- feedback_maintenance: z.boolean().optional().describe("Auto-promote/prune by feedback (default: true)"),
365
- compaction: z.boolean().optional().describe("Merge similar durable memories (default: false)"),
366
- compaction_dry_run: z.boolean().optional().describe("Preview compaction without changes (default: true)"),
367
- }).optional().describe("Which operations to run (default: quarantine_cleanup + feedback_maintenance)"),
497
+ operations: z
498
+ .object({
499
+ quarantine_cleanup: z
500
+ .boolean()
501
+ .optional()
502
+ .describe("Remove expired quarantine memories (default: true)"),
503
+ feedback_maintenance: z
504
+ .boolean()
505
+ .optional()
506
+ .describe("Auto-promote/prune by feedback (default: true)"),
507
+ compaction: z
508
+ .boolean()
509
+ .optional()
510
+ .describe("Merge similar durable memories (default: false)"),
511
+ compaction_dry_run: z
512
+ .boolean()
513
+ .optional()
514
+ .describe("Preview compaction without changes (default: true)"),
515
+ })
516
+ .optional()
517
+ .describe("Which operations to run (default: quarantine_cleanup + feedback_maintenance)"),
368
518
  }),
369
519
  annotations: TOOL_ANNOTATIONS["memory_maintenance"],
370
520
  handler: async (args, ctx) => {
@@ -381,7 +531,9 @@ export function createMemoryTools(projectName) {
381
531
  result += `## Quarantine Cleanup\n`;
382
532
  if (qc.rejected.length > 0) {
383
533
  result += `**Expired** (${qc.rejected.length}): removed from quarantine\n`;
384
- qc.rejected.slice(0, 10).forEach((id) => { result += ` \u{1F5D1}\u{FE0F} ${id}\n`; });
534
+ qc.rejected.slice(0, 10).forEach((id) => {
535
+ result += ` \u{1F5D1}\u{FE0F} ${id}\n`;
536
+ });
385
537
  if (qc.rejected.length > 10)
386
538
  result += ` ... and ${qc.rejected.length - 10} more\n`;
387
539
  }
@@ -389,7 +541,9 @@ export function createMemoryTools(projectName) {
389
541
  result += `No expired quarantine memories.\n`;
390
542
  }
391
543
  if (qc.errors.length > 0) {
392
- qc.errors.forEach((e) => { result += ` \u26A0\u{FE0F} ${e}\n`; });
544
+ qc.errors.forEach((e) => {
545
+ result += ` \u26A0\u{FE0F} ${e}\n`;
546
+ });
393
547
  }
394
548
  result += `\n`;
395
549
  }
@@ -399,24 +553,30 @@ export function createMemoryTools(projectName) {
399
553
  result += `## Feedback Maintenance\n`;
400
554
  if (fm.promoted.length > 0) {
401
555
  result += `**Promoted** (${fm.promoted.length}): moved to durable\n`;
402
- fm.promoted.forEach((id) => { result += ` \u2705 ${id}\n`; });
556
+ fm.promoted.forEach((id) => {
557
+ result += ` \u2705 ${id}\n`;
558
+ });
403
559
  }
404
560
  if (fm.pruned.length > 0) {
405
561
  result += `**Pruned** (${fm.pruned.length}): removed\n`;
406
- fm.pruned.forEach((id) => { result += ` \u{1F5D1}\u{FE0F} ${id}\n`; });
562
+ fm.pruned.forEach((id) => {
563
+ result += ` \u{1F5D1}\u{FE0F} ${id}\n`;
564
+ });
407
565
  }
408
566
  if (fm.promoted.length === 0 && fm.pruned.length === 0) {
409
567
  result += `No feedback-based actions needed.\n`;
410
568
  }
411
569
  if (fm.errors.length > 0) {
412
- fm.errors.forEach((e) => { result += ` \u26A0\u{FE0F} ${e}\n`; });
570
+ fm.errors.forEach((e) => {
571
+ result += ` \u26A0\u{FE0F} ${e}\n`;
572
+ });
413
573
  }
414
574
  result += `\n`;
415
575
  }
416
576
  // Compaction section
417
577
  if (data.compaction) {
418
578
  const cp = data.compaction;
419
- result += `## Compaction${cp.dryRun ? ' (dry run)' : ''}\n`;
579
+ result += `## Compaction${cp.dryRun ? " (dry run)" : ""}\n`;
420
580
  if (cp.clusters.length > 0) {
421
581
  result += `**${cp.totalClusters} cluster(s)** of similar memories found\n\n`;
422
582
  cp.clusters.slice(0, 5).forEach((c, i) => {
package/dist/tools/pm.js CHANGED
@@ -14,8 +14,13 @@ export function createPmTools(projectName) {
14
14
  name: "search_requirements",
15
15
  description: `Search technical requirements and product documentation for ${projectName}. Finds relevant requirements, user stories, and specifications from Confluence.`,
16
16
  schema: z.object({
17
- query: z.string().describe("Search query for requirements (e.g., 'video inspection flow', 'payment integration')"),
18
- limit: z.coerce.number().optional().describe("Max results (default: 5)"),
17
+ query: z
18
+ .string()
19
+ .describe("Search query for requirements (e.g., 'video inspection flow', 'payment integration')"),
20
+ limit: z.coerce
21
+ .number()
22
+ .optional()
23
+ .describe("Max results (default: 5)"),
19
24
  }),
20
25
  annotations: TOOL_ANNOTATIONS["search_requirements"],
21
26
  handler: async (args, ctx) => {
@@ -42,8 +47,13 @@ export function createPmTools(projectName) {
42
47
  name: "analyze_requirements",
43
48
  description: `Analyze technical requirements and compare with existing implementation in ${projectName}. Identifies gaps, missing features, and implementation status.`,
44
49
  schema: z.object({
45
- feature: z.string().describe("Feature or requirement to analyze (e.g., 'video inspection', 'notifications')"),
46
- detailed: z.boolean().optional().describe("Include detailed code references (default: false)"),
50
+ feature: z
51
+ .string()
52
+ .describe("Feature or requirement to analyze (e.g., 'video inspection', 'notifications')"),
53
+ detailed: z
54
+ .boolean()
55
+ .optional()
56
+ .describe("Include detailed code references (default: false)"),
47
57
  }),
48
58
  annotations: TOOL_ANNOTATIONS["analyze_requirements"],
49
59
  handler: async (args, ctx) => {
@@ -107,7 +117,10 @@ export function createPmTools(projectName) {
107
117
  description: `Estimate development effort for a feature based on requirements and codebase analysis. Returns complexity assessment, affected files, and risk factors.`,
108
118
  schema: z.object({
109
119
  feature: z.string().describe("Feature description to estimate"),
110
- includeSubtasks: z.boolean().optional().describe("Break down into subtasks (default: true)"),
120
+ includeSubtasks: z
121
+ .boolean()
122
+ .optional()
123
+ .describe("Break down into subtasks (default: true)"),
111
124
  }),
112
125
  annotations: TOOL_ANNOTATIONS["estimate_feature"],
113
126
  handler: async (args, ctx) => {
@@ -235,13 +248,22 @@ export function createPmTools(projectName) {
235
248
  name: "list_requirements",
236
249
  description: `List all documented requirements/features for ${projectName} from Confluence. Groups by category or status.`,
237
250
  schema: z.object({
238
- category: z.string().optional().describe("Filter by category (optional)"),
239
- limit: z.coerce.number().optional().describe("Max results (default: 20)"),
240
- offset: z.coerce.number().optional().describe("Pagination offset (default: 0)"),
251
+ category: z
252
+ .string()
253
+ .optional()
254
+ .describe("Filter by category (optional)"),
255
+ limit: z.coerce
256
+ .number()
257
+ .optional()
258
+ .describe("Max results (default: 20)"),
259
+ offset: z.coerce
260
+ .number()
261
+ .optional()
262
+ .describe("Pagination offset (default: 0)"),
241
263
  }),
242
264
  annotations: TOOL_ANNOTATIONS["list_requirements"],
243
265
  handler: async (args, ctx) => {
244
- const { category, limit = 20, offset = 0 } = args;
266
+ const { category, limit = 20, offset = 0, } = args;
245
267
  const query = category || "requirements features specifications";
246
268
  const response = await ctx.api.post("/api/search", {
247
269
  collection: `${ctx.collectionPrefix}confluence`,
@@ -274,7 +296,9 @@ export function createPmTools(projectName) {
274
296
  name: "ask_pm",
275
297
  description: `Ask product management questions about ${projectName}. Answers questions about requirements, features, priorities, and project status using both documentation and codebase.`,
276
298
  schema: z.object({
277
- question: z.string().describe("PM question (e.g., 'What features are planned for video inspection?', 'What\\'s the status of notifications?')"),
299
+ question: z
300
+ .string()
301
+ .describe("PM question (e.g., 'What features are planned for video inspection?', 'What\\'s the status of notifications?')"),
278
302
  }),
279
303
  annotations: TOOL_ANNOTATIONS["ask_pm"],
280
304
  handler: async (args, ctx) => {
@@ -335,7 +359,10 @@ export function createPmTools(projectName) {
335
359
  description: `Generate technical specification from requirements. Creates a structured spec document based on Confluence requirements and existing codebase patterns.`,
336
360
  schema: z.object({
337
361
  feature: z.string().describe("Feature to generate spec for"),
338
- format: z.enum(["markdown", "jira", "brief"]).optional().describe("Output format (default: markdown)"),
362
+ format: z
363
+ .enum(["markdown", "jira", "brief"])
364
+ .optional()
365
+ .describe("Output format (default: markdown)"),
339
366
  }),
340
367
  annotations: TOOL_ANNOTATIONS["generate_spec"],
341
368
  handler: async (args, ctx) => {
@@ -12,7 +12,10 @@ export function createQualityTools(projectName) {
12
12
  name: "get_quality_report",
13
13
  description: `Get LLM quality metrics for ${projectName}. Shows JSON parse rates, latency percentiles, thinking trace rates, and alerts.`,
14
14
  schema: z.object({
15
- endpoint: z.string().optional().describe("Filter by specific endpoint (e.g., '/api/ask')"),
15
+ endpoint: z
16
+ .string()
17
+ .optional()
18
+ .describe("Filter by specific endpoint (e.g., '/api/ask')"),
16
19
  }),
17
20
  annotations: TOOL_ANNOTATIONS["get_quality_report"] || {
18
21
  title: "Get Quality Report",
@@ -20,7 +23,9 @@ export function createQualityTools(projectName) {
20
23
  openWorldHint: false,
21
24
  },
22
25
  handler: async (args, ctx) => {
23
- const params = args.endpoint ? `?endpoint=${encodeURIComponent(args.endpoint)}` : '';
26
+ const params = args.endpoint
27
+ ? `?endpoint=${encodeURIComponent(args.endpoint)}`
28
+ : "";
24
29
  const response = await ctx.api.get(`/api/quality/report${params}`);
25
30
  const data = response.data;
26
31
  let result = `## Quality Report\n\n`;
@@ -14,12 +14,18 @@ export function createReviewTools(projectName) {
14
14
  schema: z.object({
15
15
  code: z.string().describe("Code to review"),
16
16
  filePath: z.string().optional().describe("File path for context"),
17
- reviewType: z.enum(["security", "performance", "patterns", "style", "general"]).optional().describe("Type of review focus (default: general)"),
18
- diff: z.string().optional().describe("Git diff to review instead of full code"),
17
+ reviewType: z
18
+ .enum(["security", "performance", "patterns", "style", "general"])
19
+ .optional()
20
+ .describe("Type of review focus (default: general)"),
21
+ diff: z
22
+ .string()
23
+ .optional()
24
+ .describe("Git diff to review instead of full code"),
19
25
  }),
20
26
  annotations: TOOL_ANNOTATIONS["review_code"],
21
27
  handler: async (args, ctx) => {
22
- const { code, filePath, reviewType = "general", diff } = args;
28
+ const { code, filePath, reviewType = "general", diff, } = args;
23
29
  const response = await ctx.api.post("/api/review", {
24
30
  code: code || diff,
25
31
  filePath,
@@ -76,9 +82,18 @@ export function createReviewTools(projectName) {
76
82
  schema: z.object({
77
83
  code: z.string().describe("Code to generate tests for"),
78
84
  filePath: z.string().optional().describe("File path for context"),
79
- framework: z.enum(["jest", "vitest", "pytest", "mocha"]).optional().describe("Test framework to use (default: jest)"),
80
- testType: z.enum(["unit", "integration", "e2e"]).optional().describe("Type of tests to generate (default: unit)"),
81
- coverage: z.enum(["minimal", "standard", "comprehensive"]).optional().describe("Coverage level (default: comprehensive)"),
85
+ framework: z
86
+ .enum(["jest", "vitest", "pytest", "mocha"])
87
+ .optional()
88
+ .describe("Test framework to use (default: jest)"),
89
+ testType: z
90
+ .enum(["unit", "integration", "e2e"])
91
+ .optional()
92
+ .describe("Type of tests to generate (default: unit)"),
93
+ coverage: z
94
+ .enum(["minimal", "standard", "comprehensive"])
95
+ .optional()
96
+ .describe("Coverage level (default: comprehensive)"),
82
97
  }),
83
98
  annotations: TOOL_ANNOTATIONS["generate_tests"],
84
99
  handler: async (args, ctx) => {
@@ -115,7 +130,10 @@ export function createReviewTools(projectName) {
115
130
  description: "Analyze existing tests for coverage and quality.",
116
131
  schema: z.object({
117
132
  testCode: z.string().describe("Test code to analyze"),
118
- sourceCode: z.string().optional().describe("Optional source code being tested"),
133
+ sourceCode: z
134
+ .string()
135
+ .optional()
136
+ .describe("Optional source code being tested"),
119
137
  }),
120
138
  annotations: TOOL_ANNOTATIONS["analyze_tests"],
121
139
  handler: async (args, ctx) => {