@crowley/rag-mcp 1.1.0 → 1.2.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.
- package/dist/__tests__/tool-middleware.test.d.ts +1 -0
- package/dist/__tests__/tool-middleware.test.js +83 -0
- package/dist/__tests__/tools/memory.test.d.ts +1 -0
- package/dist/__tests__/tools/memory.test.js +127 -0
- package/dist/tools/advanced.js +6 -6
- package/dist/tools/agents.js +1 -1
- package/dist/tools/analytics.js +1 -1
- package/dist/tools/architecture.js +3 -3
- package/dist/tools/ask.js +1 -1
- package/dist/tools/clustering.js +6 -6
- package/dist/tools/confluence.js +2 -2
- package/dist/tools/indexing.js +3 -3
- package/dist/tools/memory.js +80 -23
- package/dist/tools/pm.js +3 -3
- package/dist/tools/search.js +15 -15
- package/dist/tools/session.js +1 -1
- package/dist/tools/suggestions.js +1 -1
- package/package.json +6 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { summarizeInput, countResults, formatToolError, TRACKING_EXCLUDE, SESSION_TOOLS, TOOL_TIMEOUTS, } from '../tool-middleware.js';
|
|
3
|
+
describe('Tool Middleware', () => {
|
|
4
|
+
beforeEach(() => {
|
|
5
|
+
vi.resetAllMocks();
|
|
6
|
+
});
|
|
7
|
+
describe('summarizeInput', () => {
|
|
8
|
+
it('extracts query field', () => {
|
|
9
|
+
expect(summarizeInput('search', { query: 'find auth code' })).toBe('find auth code');
|
|
10
|
+
});
|
|
11
|
+
it('extracts question field', () => {
|
|
12
|
+
expect(summarizeInput('ask', { question: 'what is auth?' })).toBe('what is auth?');
|
|
13
|
+
});
|
|
14
|
+
it('extracts content field as fallback', () => {
|
|
15
|
+
expect(summarizeInput('remember', { content: 'important note' })).toBe('important note');
|
|
16
|
+
});
|
|
17
|
+
it('extracts file path as fallback', () => {
|
|
18
|
+
expect(summarizeInput('explain', { filePath: 'src/auth.ts' })).toBe('src/auth.ts');
|
|
19
|
+
});
|
|
20
|
+
it('truncates long strings to 200 chars', () => {
|
|
21
|
+
const long = 'a'.repeat(300);
|
|
22
|
+
expect(summarizeInput('search', { query: long }).length).toBeLessThanOrEqual(200);
|
|
23
|
+
});
|
|
24
|
+
it('returns tool name when no useful field', () => {
|
|
25
|
+
expect(summarizeInput('get_stats', {})).toBe('get_stats');
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
describe('countResults', () => {
|
|
29
|
+
it('returns 0 for "No results" messages', () => {
|
|
30
|
+
expect(countResults('No results found.')).toBe(0);
|
|
31
|
+
});
|
|
32
|
+
it('returns 0 for "not found" messages', () => {
|
|
33
|
+
expect(countResults('Memory not found')).toBe(0);
|
|
34
|
+
});
|
|
35
|
+
it('counts numbered items', () => {
|
|
36
|
+
const text = '1. First\n2. Second\n3. Third';
|
|
37
|
+
expect(countResults(text)).toBe(3);
|
|
38
|
+
});
|
|
39
|
+
it('counts bullet items', () => {
|
|
40
|
+
const text = '- item1\n- item2';
|
|
41
|
+
expect(countResults(text)).toBe(2);
|
|
42
|
+
});
|
|
43
|
+
it('returns 1 for generic content', () => {
|
|
44
|
+
expect(countResults('Some response text')).toBe(1);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
describe('formatToolError', () => {
|
|
48
|
+
const ctx = {
|
|
49
|
+
api: { defaults: { baseURL: 'http://localhost:3100' } },
|
|
50
|
+
};
|
|
51
|
+
it('formats ECONNREFUSED error', () => {
|
|
52
|
+
const err = { code: 'ECONNREFUSED' };
|
|
53
|
+
const result = formatToolError(err, ctx);
|
|
54
|
+
expect(result).toContain('Cannot connect');
|
|
55
|
+
expect(result).toContain('localhost:3100');
|
|
56
|
+
});
|
|
57
|
+
it('formats API error with status', () => {
|
|
58
|
+
const err = { response: { status: 404, data: { error: 'not found' } } };
|
|
59
|
+
const result = formatToolError(err, ctx);
|
|
60
|
+
expect(result).toContain('404');
|
|
61
|
+
});
|
|
62
|
+
it('formats generic error message', () => {
|
|
63
|
+
const err = { message: 'Something broke' };
|
|
64
|
+
const result = formatToolError(err, ctx);
|
|
65
|
+
expect(result).toContain('Something broke');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
describe('constants', () => {
|
|
69
|
+
it('TRACKING_EXCLUDE contains meta tools', () => {
|
|
70
|
+
expect(TRACKING_EXCLUDE.has('get_tool_analytics')).toBe(true);
|
|
71
|
+
expect(TRACKING_EXCLUDE.has('get_quality_metrics')).toBe(true);
|
|
72
|
+
});
|
|
73
|
+
it('SESSION_TOOLS contains session management', () => {
|
|
74
|
+
expect(SESSION_TOOLS.has('start_session')).toBe(true);
|
|
75
|
+
expect(SESSION_TOOLS.has('end_session')).toBe(true);
|
|
76
|
+
});
|
|
77
|
+
it('TOOL_TIMEOUTS has correct tiers', () => {
|
|
78
|
+
expect(TOOL_TIMEOUTS['index_codebase']).toBe(120_000);
|
|
79
|
+
expect(TOOL_TIMEOUTS['search_codebase']).toBe(15_000);
|
|
80
|
+
expect(TOOL_TIMEOUTS['recall']).toBe(10_000);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
2
|
+
import { createMemoryTools } from '../../tools/memory.js';
|
|
3
|
+
function createMockCtx() {
|
|
4
|
+
return {
|
|
5
|
+
api: {
|
|
6
|
+
post: vi.fn(),
|
|
7
|
+
get: vi.fn(),
|
|
8
|
+
delete: vi.fn(),
|
|
9
|
+
patch: vi.fn(),
|
|
10
|
+
defaults: { baseURL: 'http://localhost:3100' },
|
|
11
|
+
},
|
|
12
|
+
projectName: 'testproject',
|
|
13
|
+
projectPath: '/tmp/testproject',
|
|
14
|
+
collectionPrefix: 'testproject',
|
|
15
|
+
enrichmentEnabled: false,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
describe('Memory Tools', () => {
|
|
19
|
+
let tools;
|
|
20
|
+
let ctx;
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
vi.resetAllMocks();
|
|
23
|
+
tools = createMemoryTools('testproject');
|
|
24
|
+
ctx = createMockCtx();
|
|
25
|
+
});
|
|
26
|
+
function findTool(name) {
|
|
27
|
+
return tools.find((t) => t.name === name);
|
|
28
|
+
}
|
|
29
|
+
describe('remember', () => {
|
|
30
|
+
it('stores memory and returns formatted result', async () => {
|
|
31
|
+
const mem = { id: 'mem-1', type: 'note', content: 'test note', createdAt: new Date().toISOString() };
|
|
32
|
+
ctx.api.post.mockResolvedValue({ data: { memory: mem } });
|
|
33
|
+
const result = await findTool('remember').handler({ content: 'test note', type: 'note', tags: ['tag1'] }, ctx);
|
|
34
|
+
expect(ctx.api.post).toHaveBeenCalledWith('/api/memory', expect.objectContaining({
|
|
35
|
+
projectName: 'testproject',
|
|
36
|
+
content: 'test note',
|
|
37
|
+
type: 'note',
|
|
38
|
+
}));
|
|
39
|
+
expect(result).toContain('Memory stored');
|
|
40
|
+
expect(result).toContain('mem-1');
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
describe('recall', () => {
|
|
44
|
+
it('returns formatted results', async () => {
|
|
45
|
+
ctx.api.post.mockResolvedValue({
|
|
46
|
+
data: {
|
|
47
|
+
results: [
|
|
48
|
+
{ memory: { type: 'insight', content: 'found it', createdAt: new Date().toISOString(), tags: [] }, score: 0.85 },
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
});
|
|
52
|
+
const result = await findTool('recall').handler({ query: 'find something', limit: 5 }, ctx);
|
|
53
|
+
expect(result).toContain('Recalled Memories');
|
|
54
|
+
});
|
|
55
|
+
it('returns empty message when no results', async () => {
|
|
56
|
+
ctx.api.post.mockResolvedValue({ data: { results: [] } });
|
|
57
|
+
const result = await findTool('recall').handler({ query: 'nothing' }, ctx);
|
|
58
|
+
expect(result).toContain('No memories found');
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
describe('forget', () => {
|
|
62
|
+
it('deletes by memoryId', async () => {
|
|
63
|
+
ctx.api.delete.mockResolvedValue({ data: { success: true } });
|
|
64
|
+
const result = await findTool('forget').handler({ memoryId: 'mem-1' }, ctx);
|
|
65
|
+
expect(ctx.api.delete).toHaveBeenCalledWith(expect.stringContaining('/api/memory/mem-1'));
|
|
66
|
+
expect(result).toContain('deleted');
|
|
67
|
+
});
|
|
68
|
+
it('deletes by type', async () => {
|
|
69
|
+
ctx.api.delete.mockResolvedValue({ data: {} });
|
|
70
|
+
const result = await findTool('forget').handler({ type: 'note' }, ctx);
|
|
71
|
+
expect(ctx.api.delete).toHaveBeenCalledWith(expect.stringContaining('/api/memory/type/note'));
|
|
72
|
+
expect(result).toContain('note');
|
|
73
|
+
});
|
|
74
|
+
it('deletes by olderThanDays', async () => {
|
|
75
|
+
ctx.api.post.mockResolvedValue({ data: { deleted: 10 } });
|
|
76
|
+
const result = await findTool('forget').handler({ olderThanDays: 30 }, ctx);
|
|
77
|
+
expect(ctx.api.post).toHaveBeenCalledWith('/api/memory/forget-older', expect.objectContaining({
|
|
78
|
+
olderThanDays: 30,
|
|
79
|
+
}));
|
|
80
|
+
expect(result).toContain('10');
|
|
81
|
+
});
|
|
82
|
+
it('returns message when nothing specified', async () => {
|
|
83
|
+
const result = await findTool('forget').handler({}, ctx);
|
|
84
|
+
expect(result).toContain('specify');
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
describe('promote_memory', () => {
|
|
88
|
+
it('promotes and returns formatted result', async () => {
|
|
89
|
+
const mem = { id: 'mem-1', type: 'insight', content: 'promoted' };
|
|
90
|
+
ctx.api.post.mockResolvedValue({ data: { memory: mem } });
|
|
91
|
+
const result = await findTool('promote_memory').handler({ memoryId: 'mem-1', reason: 'human_validated' }, ctx);
|
|
92
|
+
expect(result).toContain('promoted to durable');
|
|
93
|
+
expect(result).toContain('mem-1');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
describe('memory_maintenance', () => {
|
|
97
|
+
it('formats maintenance results', async () => {
|
|
98
|
+
ctx.api.post.mockResolvedValue({
|
|
99
|
+
data: {
|
|
100
|
+
quarantine_cleanup: { rejected: ['q-1', 'q-2'], errors: [] },
|
|
101
|
+
feedback_maintenance: { promoted: ['f-1'], pruned: [], errors: [] },
|
|
102
|
+
},
|
|
103
|
+
});
|
|
104
|
+
const result = await findTool('memory_maintenance').handler({}, ctx);
|
|
105
|
+
expect(result).toContain('Maintenance Results');
|
|
106
|
+
expect(result).toContain('Quarantine Cleanup');
|
|
107
|
+
expect(result).toContain('Feedback Maintenance');
|
|
108
|
+
});
|
|
109
|
+
});
|
|
110
|
+
describe('batch_remember', () => {
|
|
111
|
+
it('stores multiple memories', async () => {
|
|
112
|
+
ctx.api.post.mockResolvedValue({
|
|
113
|
+
data: {
|
|
114
|
+
savedCount: 2,
|
|
115
|
+
memories: [
|
|
116
|
+
{ id: 'b-1', type: 'note', content: 'first' },
|
|
117
|
+
{ id: 'b-2', type: 'insight', content: 'second' },
|
|
118
|
+
],
|
|
119
|
+
errors: [],
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
const result = await findTool('batch_remember').handler({ items: [{ content: 'first' }, { content: 'second', type: 'insight' }] }, ctx);
|
|
123
|
+
expect(result).toContain('Saved');
|
|
124
|
+
expect(result).toContain('2');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
});
|
package/dist/tools/advanced.js
CHANGED
|
@@ -15,9 +15,9 @@ export function createAdvancedTools(projectName) {
|
|
|
15
15
|
description: `Consolidate duplicate memories for ${projectName}. Finds similar memories and merges them using LLM to reduce clutter.`,
|
|
16
16
|
schema: z.object({
|
|
17
17
|
type: z.string().optional().describe("Filter by memory type (decision, insight, context, todo, conversation, note, or all). Default: all"),
|
|
18
|
-
threshold: z.number().optional().describe("Similarity threshold for merging (0.5-1.0, default: 0.9). Lower = more aggressive merging."),
|
|
18
|
+
threshold: z.coerce.number().optional().describe("Similarity threshold for merging (0.5-1.0, default: 0.9). Lower = more aggressive merging."),
|
|
19
19
|
dryRun: z.boolean().optional().describe("If true, preview merge candidates without making changes (default: true)."),
|
|
20
|
-
limit: z.number().optional().describe("Max clusters to process (default: 50)."),
|
|
20
|
+
limit: z.coerce.number().optional().describe("Max clusters to process (default: 50)."),
|
|
21
21
|
}),
|
|
22
22
|
annotations: TOOL_ANNOTATIONS["merge_memories"],
|
|
23
23
|
handler: async (args, ctx) => {
|
|
@@ -65,7 +65,7 @@ export function createAdvancedTools(projectName) {
|
|
|
65
65
|
currentFile: z.string().describe("Path of the file being edited"),
|
|
66
66
|
currentCode: z.string().describe("Current code snippet or file content"),
|
|
67
67
|
language: z.string().optional().describe("Programming language filter (optional)"),
|
|
68
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
68
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
69
69
|
}),
|
|
70
70
|
annotations: TOOL_ANNOTATIONS["get_completion_context"],
|
|
71
71
|
handler: async (args, ctx) => {
|
|
@@ -108,7 +108,7 @@ export function createAdvancedTools(projectName) {
|
|
|
108
108
|
currentFile: z.string().describe("Path of the file being edited"),
|
|
109
109
|
currentCode: z.string().describe("Current code content"),
|
|
110
110
|
language: z.string().optional().describe("Programming language filter (optional)"),
|
|
111
|
-
limit: z.number().optional().describe("Max suggestions (default: 10)"),
|
|
111
|
+
limit: z.coerce.number().optional().describe("Max suggestions (default: 10)"),
|
|
112
112
|
}),
|
|
113
113
|
annotations: TOOL_ANNOTATIONS["get_import_suggestions"],
|
|
114
114
|
handler: async (args, ctx) => {
|
|
@@ -147,7 +147,7 @@ export function createAdvancedTools(projectName) {
|
|
|
147
147
|
typeName: z.string().optional().describe("Name of the type/interface/class to look up"),
|
|
148
148
|
code: z.string().optional().describe("Code containing types to look up (alternative to typeName)"),
|
|
149
149
|
currentFile: z.string().optional().describe("Current file to exclude from results"),
|
|
150
|
-
limit: z.number().optional().describe("Max results per category (default: 5)"),
|
|
150
|
+
limit: z.coerce.number().optional().describe("Max results per category (default: 5)"),
|
|
151
151
|
}),
|
|
152
152
|
annotations: TOOL_ANNOTATIONS["get_type_context"],
|
|
153
153
|
handler: async (args, ctx) => {
|
|
@@ -187,7 +187,7 @@ export function createAdvancedTools(projectName) {
|
|
|
187
187
|
name: "get_behavior_patterns",
|
|
188
188
|
description: `Analyze user workflow patterns for ${projectName}. Shows peak hours, tool preferences, common sequences, and session statistics.`,
|
|
189
189
|
schema: z.object({
|
|
190
|
-
days: z.number().optional().describe("Number of days to analyze (default: 7)"),
|
|
190
|
+
days: z.coerce.number().optional().describe("Number of days to analyze (default: 7)"),
|
|
191
191
|
sessionId: z.string().optional().describe("Filter to a specific session (optional)"),
|
|
192
192
|
}),
|
|
193
193
|
annotations: TOOL_ANNOTATIONS["get_behavior_patterns"],
|
package/dist/tools/agents.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createAgentTools(projectName) {
|
|
|
15
15
|
type: z.enum(["research", "review", "documentation", "refactor", "test"]).describe("Agent type: research, review, documentation, refactor, or test"),
|
|
16
16
|
task: z.string().describe("The task for the agent to perform"),
|
|
17
17
|
context: z.string().optional().describe("Optional additional context (code, requirements, etc.)"),
|
|
18
|
-
maxIterations: z.number().optional().describe("Maximum ReAct iterations (default: varies by agent type)"),
|
|
18
|
+
maxIterations: z.coerce.number().optional().describe("Maximum ReAct iterations (default: varies by agent type)"),
|
|
19
19
|
}),
|
|
20
20
|
annotations: TOOL_ANNOTATIONS["run_agent"],
|
|
21
21
|
handler: async (args, ctx) => {
|
package/dist/tools/analytics.js
CHANGED
|
@@ -154,7 +154,7 @@ export function createAnalyticsTools(projectName) {
|
|
|
154
154
|
description: `Enable scalar quantization on a ${projectName} collection to reduce memory usage.`,
|
|
155
155
|
schema: z.object({
|
|
156
156
|
collectionName: z.string().describe("Collection name to enable quantization on"),
|
|
157
|
-
quantile: z.number().optional().describe("Quantile for quantization (0-1, default: 0.99)"),
|
|
157
|
+
quantile: z.coerce.number().optional().describe("Quantile for quantization (0-1, default: 0.99)"),
|
|
158
158
|
}),
|
|
159
159
|
annotations: TOOL_ANNOTATIONS["enable_quantization"],
|
|
160
160
|
handler: async (args, ctx) => {
|
|
@@ -56,7 +56,7 @@ ${alternatives ? `## Alternatives Considered\n${alternatives}` : ""}`;
|
|
|
56
56
|
schema: z.object({
|
|
57
57
|
query: z.string().optional().describe("Search query (optional - returns all if empty)"),
|
|
58
58
|
status: z.enum(["proposed", "accepted", "deprecated", "superseded", "all"]).optional().describe("Filter by status"),
|
|
59
|
-
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
59
|
+
limit: z.coerce.number().optional().describe("Max results (default: 10)"),
|
|
60
60
|
}),
|
|
61
61
|
annotations: TOOL_ANNOTATIONS["get_adrs"],
|
|
62
62
|
handler: async (args, ctx) => {
|
|
@@ -138,7 +138,7 @@ ${appliesTo ? `## Applies To\n${appliesTo}` : ""}`;
|
|
|
138
138
|
schema: z.object({
|
|
139
139
|
query: z.string().optional().describe("Search for patterns by name or description"),
|
|
140
140
|
appliesTo: z.string().optional().describe("Filter by what patterns apply to (e.g., 'api', 'module')"),
|
|
141
|
-
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
141
|
+
limit: z.coerce.number().optional().describe("Max results (default: 10)"),
|
|
142
142
|
}),
|
|
143
143
|
annotations: TOOL_ANNOTATIONS["get_patterns"],
|
|
144
144
|
handler: async (args, ctx) => {
|
|
@@ -437,7 +437,7 @@ ${relatedAdr ? `## Related ADR\n${relatedAdr}` : ""}`;
|
|
|
437
437
|
description: `List technical debt items for ${projectName}.`,
|
|
438
438
|
schema: z.object({
|
|
439
439
|
impact: z.enum(["low", "medium", "high", "critical", "all"]).optional().describe("Filter by impact"),
|
|
440
|
-
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
440
|
+
limit: z.coerce.number().optional().describe("Max results (default: 10)"),
|
|
441
441
|
}),
|
|
442
442
|
annotations: TOOL_ANNOTATIONS["get_tech_debt"],
|
|
443
443
|
handler: async (args, ctx) => {
|
package/dist/tools/ask.js
CHANGED
|
@@ -99,7 +99,7 @@ export function createAskTools(projectName) {
|
|
|
99
99
|
conversation: z.string().describe("The conversation text to analyze"),
|
|
100
100
|
context: z.string().optional().describe("Additional context about the conversation"),
|
|
101
101
|
autoSave: z.boolean().optional().describe("Automatically save extracted learnings (default: false)"),
|
|
102
|
-
minConfidence: z.number().optional().describe("Minimum confidence threshold for learnings (0-1, default: 0.7)"),
|
|
102
|
+
minConfidence: z.coerce.number().optional().describe("Minimum confidence threshold for learnings (0-1, default: 0.7)"),
|
|
103
103
|
}),
|
|
104
104
|
annotations: TOOL_ANNOTATIONS["analyze_conversation"],
|
|
105
105
|
handler: async (args, ctx) => {
|
package/dist/tools/clustering.js
CHANGED
|
@@ -15,8 +15,8 @@ export function createClusteringTools(projectName) {
|
|
|
15
15
|
description: `Cluster code in the ${projectName} codebase by similarity. Groups related files around seed points.`,
|
|
16
16
|
schema: z.object({
|
|
17
17
|
seedIds: z.array(z.string()).describe("Seed point IDs to cluster around"),
|
|
18
|
-
limit: z.number().optional().describe("Max results per cluster (default: 5)"),
|
|
19
|
-
threshold: z.number().optional().describe("Minimum similarity threshold (0-1, default: 0.7)"),
|
|
18
|
+
limit: z.coerce.number().optional().describe("Max results per cluster (default: 5)"),
|
|
19
|
+
threshold: z.coerce.number().optional().describe("Minimum similarity threshold (0-1, default: 0.7)"),
|
|
20
20
|
}),
|
|
21
21
|
annotations: TOOL_ANNOTATIONS["cluster_code"],
|
|
22
22
|
handler: async (args, ctx) => {
|
|
@@ -52,8 +52,8 @@ export function createClusteringTools(projectName) {
|
|
|
52
52
|
description: `Find duplicate or near-duplicate code in ${projectName}. Groups similar files by content.`,
|
|
53
53
|
schema: z.object({
|
|
54
54
|
collection: z.string().optional().describe("Collection to search (default: codebase)"),
|
|
55
|
-
limit: z.number().optional().describe("Max duplicate groups to return (default: 10)"),
|
|
56
|
-
threshold: z.number().optional().describe("Minimum similarity threshold (0-1, default: 0.9)"),
|
|
55
|
+
limit: z.coerce.number().optional().describe("Max duplicate groups to return (default: 10)"),
|
|
56
|
+
threshold: z.coerce.number().optional().describe("Minimum similarity threshold (0-1, default: 0.9)"),
|
|
57
57
|
}),
|
|
58
58
|
annotations: TOOL_ANNOTATIONS["find_duplicates"],
|
|
59
59
|
handler: async (args, ctx) => {
|
|
@@ -97,7 +97,7 @@ export function createClusteringTools(projectName) {
|
|
|
97
97
|
schema: z.object({
|
|
98
98
|
positiveIds: z.array(z.string()).describe("IDs of vectors to find similar code to"),
|
|
99
99
|
negativeIds: z.array(z.string()).optional().describe("IDs of vectors to avoid (dissimilar)"),
|
|
100
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
100
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
101
101
|
}),
|
|
102
102
|
annotations: TOOL_ANNOTATIONS["recommend_similar"],
|
|
103
103
|
handler: async (args, ctx) => {
|
|
@@ -129,7 +129,7 @@ export function createClusteringTools(projectName) {
|
|
|
129
129
|
text: z.string().describe("Text to extract learnings from"),
|
|
130
130
|
context: z.string().optional().describe("Additional context about the text"),
|
|
131
131
|
autoSave: z.boolean().optional().describe("Automatically save extracted learnings (default: false)"),
|
|
132
|
-
minConfidence: z.number().optional().describe("Minimum confidence threshold (0-1, default: 0.7)"),
|
|
132
|
+
minConfidence: z.coerce.number().optional().describe("Minimum confidence threshold (0-1, default: 0.7)"),
|
|
133
133
|
}),
|
|
134
134
|
annotations: TOOL_ANNOTATIONS["extract_learnings"],
|
|
135
135
|
handler: async (args, ctx) => {
|
package/dist/tools/confluence.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createConfluenceTools(projectName) {
|
|
|
15
15
|
description: `Search indexed Confluence documentation for ${projectName}. Returns relevant pages with content snippets.`,
|
|
16
16
|
schema: z.object({
|
|
17
17
|
query: z.string().describe("Search query for Confluence content"),
|
|
18
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
18
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
19
19
|
spaceKey: z.string().optional().describe("Filter by Confluence space key"),
|
|
20
20
|
}),
|
|
21
21
|
annotations: TOOL_ANNOTATIONS["search_confluence"],
|
|
@@ -46,7 +46,7 @@ export function createConfluenceTools(projectName) {
|
|
|
46
46
|
schema: z.object({
|
|
47
47
|
spaceKeys: z.array(z.string()).optional().describe("Specific space keys to index (indexes all accessible if empty)"),
|
|
48
48
|
labels: z.array(z.string()).optional().describe("Filter pages by labels"),
|
|
49
|
-
maxPages: z.number().optional().describe("Maximum pages to index (default: 500)"),
|
|
49
|
+
maxPages: z.coerce.number().optional().describe("Maximum pages to index (default: 500)"),
|
|
50
50
|
force: z.boolean().optional().describe("Force re-index even if already indexed"),
|
|
51
51
|
}),
|
|
52
52
|
annotations: TOOL_ANNOTATIONS["index_confluence"],
|
package/dist/tools/indexing.js
CHANGED
|
@@ -138,10 +138,10 @@ export function createIndexingTools(projectName) {
|
|
|
138
138
|
schema: z.object({}),
|
|
139
139
|
outputSchema: z.object({
|
|
140
140
|
status: z.string(),
|
|
141
|
-
totalFiles: z.number().optional(),
|
|
142
|
-
indexedFiles: z.number().optional(),
|
|
141
|
+
totalFiles: z.coerce.number().optional(),
|
|
142
|
+
indexedFiles: z.coerce.number().optional(),
|
|
143
143
|
lastUpdated: z.string().optional(),
|
|
144
|
-
vectorCount: z.number().optional(),
|
|
144
|
+
vectorCount: z.coerce.number().optional(),
|
|
145
145
|
cached: z.boolean(),
|
|
146
146
|
}),
|
|
147
147
|
annotations: TOOL_ANNOTATIONS["get_index_status"],
|
package/dist/tools/memory.js
CHANGED
|
@@ -62,7 +62,7 @@ export function createMemoryTools(projectName) {
|
|
|
62
62
|
schema: z.object({
|
|
63
63
|
query: z.string().describe("What to recall (semantic search)"),
|
|
64
64
|
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note", "all"]).optional().describe("Filter by memory type (default: all)"),
|
|
65
|
-
limit: z.number().optional().describe("Max memories to retrieve (default: 5)"),
|
|
65
|
+
limit: z.coerce.number().optional().describe("Max memories to retrieve (default: 5)"),
|
|
66
66
|
}),
|
|
67
67
|
annotations: TOOL_ANNOTATIONS["recall"],
|
|
68
68
|
handler: async (args, ctx) => {
|
|
@@ -89,8 +89,8 @@ export function createMemoryTools(projectName) {
|
|
|
89
89
|
schema: z.object({
|
|
90
90
|
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note", "all"]).optional().describe("Filter by type"),
|
|
91
91
|
tag: z.string().optional().describe("Filter by tag"),
|
|
92
|
-
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
93
|
-
offset: z.number().optional().describe("Pagination offset (default: 0)"),
|
|
92
|
+
limit: z.coerce.number().optional().describe("Max results (default: 10)"),
|
|
93
|
+
offset: z.coerce.number().optional().describe("Pagination offset (default: 0)"),
|
|
94
94
|
}),
|
|
95
95
|
annotations: TOOL_ANNOTATIONS["list_memories"],
|
|
96
96
|
handler: async (args, ctx) => {
|
|
@@ -129,12 +129,13 @@ export function createMemoryTools(projectName) {
|
|
|
129
129
|
schema: z.object({
|
|
130
130
|
memoryId: z.string().optional().describe("Specific memory ID to delete"),
|
|
131
131
|
type: z.enum(["decision", "insight", "context", "todo", "conversation", "note"]).optional().describe("Delete all memories of this type"),
|
|
132
|
-
olderThanDays: z.number().optional().describe("Delete memories older than N days"),
|
|
132
|
+
olderThanDays: z.coerce.number().optional().describe("Delete memories older than N days"),
|
|
133
133
|
}),
|
|
134
134
|
annotations: TOOL_ANNOTATIONS["forget"],
|
|
135
135
|
handler: async (args, ctx) => {
|
|
136
136
|
const memoryId = args.memoryId;
|
|
137
137
|
const type = args.type;
|
|
138
|
+
const olderThanDays = args.olderThanDays;
|
|
138
139
|
if (memoryId) {
|
|
139
140
|
const response = await ctx.api.delete(`/api/memory/${memoryId}?projectName=${ctx.projectName}`);
|
|
140
141
|
return response.data.success
|
|
@@ -145,7 +146,14 @@ export function createMemoryTools(projectName) {
|
|
|
145
146
|
await ctx.api.delete(`/api/memory/type/${type}?projectName=${ctx.projectName}`);
|
|
146
147
|
return `\u{1F5D1}\uFE0F Deleted all memories of type: ${type}`;
|
|
147
148
|
}
|
|
148
|
-
|
|
149
|
+
if (olderThanDays) {
|
|
150
|
+
const response = await ctx.api.post("/api/memory/forget-older", {
|
|
151
|
+
projectName: ctx.projectName,
|
|
152
|
+
olderThanDays,
|
|
153
|
+
});
|
|
154
|
+
return `\u{1F5D1}\uFE0F Deleted ${response.data.deleted} memories older than ${olderThanDays} days`;
|
|
155
|
+
}
|
|
156
|
+
return "Please specify memoryId, type, or olderThanDays to delete.";
|
|
149
157
|
},
|
|
150
158
|
},
|
|
151
159
|
{
|
|
@@ -191,6 +199,7 @@ export function createMemoryTools(projectName) {
|
|
|
191
199
|
handler: async (args, ctx) => {
|
|
192
200
|
const items = args.items;
|
|
193
201
|
const response = await ctx.api.post("/api/memory/batch", {
|
|
202
|
+
projectName: ctx.projectName,
|
|
194
203
|
items,
|
|
195
204
|
});
|
|
196
205
|
const { savedCount, errors, memories } = response.data;
|
|
@@ -238,8 +247,8 @@ export function createMemoryTools(projectName) {
|
|
|
238
247
|
name: "review_memories",
|
|
239
248
|
description: `Get auto-extracted memories pending review in ${projectName}. Shows unvalidated learnings that need human confirmation.`,
|
|
240
249
|
schema: z.object({
|
|
241
|
-
limit: z.number().optional().describe("Max memories to return (default: 20)"),
|
|
242
|
-
offset: z.number().optional().describe("Pagination offset (default: 0)"),
|
|
250
|
+
limit: z.coerce.number().optional().describe("Max memories to return (default: 20)"),
|
|
251
|
+
offset: z.coerce.number().optional().describe("Pagination offset (default: 0)"),
|
|
243
252
|
}),
|
|
244
253
|
annotations: TOOL_ANNOTATIONS["review_memories"],
|
|
245
254
|
handler: async (args, ctx) => {
|
|
@@ -345,33 +354,81 @@ export function createMemoryTools(projectName) {
|
|
|
345
354
|
},
|
|
346
355
|
{
|
|
347
356
|
name: "memory_maintenance",
|
|
348
|
-
description: `Run
|
|
349
|
-
schema: z.object({
|
|
357
|
+
description: `Run memory maintenance for ${projectName}: quarantine cleanup (expire old auto-memories), feedback-driven promote/prune, and compaction (merge similar durable memories).`,
|
|
358
|
+
schema: z.object({
|
|
359
|
+
operations: z.object({
|
|
360
|
+
quarantine_cleanup: z.boolean().optional().describe("Remove expired quarantine memories (default: true)"),
|
|
361
|
+
feedback_maintenance: z.boolean().optional().describe("Auto-promote/prune by feedback (default: true)"),
|
|
362
|
+
compaction: z.boolean().optional().describe("Merge similar durable memories (default: false)"),
|
|
363
|
+
compaction_dry_run: z.boolean().optional().describe("Preview compaction without changes (default: true)"),
|
|
364
|
+
}).optional().describe("Which operations to run (default: quarantine_cleanup + feedback_maintenance)"),
|
|
365
|
+
}),
|
|
350
366
|
annotations: TOOL_ANNOTATIONS["memory_maintenance"],
|
|
351
|
-
handler: async (
|
|
367
|
+
handler: async (args, ctx) => {
|
|
368
|
+
const operations = args.operations;
|
|
352
369
|
const response = await ctx.api.post("/api/memory/maintenance", {
|
|
353
370
|
projectName: ctx.projectName,
|
|
371
|
+
operations,
|
|
354
372
|
});
|
|
355
|
-
const
|
|
373
|
+
const data = response.data;
|
|
356
374
|
let result = `# \u{1F9F9} Memory Maintenance Results\n\n`;
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
375
|
+
// Quarantine cleanup section
|
|
376
|
+
if (data.quarantine_cleanup) {
|
|
377
|
+
const qc = data.quarantine_cleanup;
|
|
378
|
+
result += `## Quarantine Cleanup\n`;
|
|
379
|
+
if (qc.rejected.length > 0) {
|
|
380
|
+
result += `**Expired** (${qc.rejected.length}): removed from quarantine\n`;
|
|
381
|
+
qc.rejected.slice(0, 10).forEach((id) => { result += ` \u{1F5D1}\u{FE0F} ${id}\n`; });
|
|
382
|
+
if (qc.rejected.length > 10)
|
|
383
|
+
result += ` ... and ${qc.rejected.length - 10} more\n`;
|
|
384
|
+
}
|
|
385
|
+
else {
|
|
386
|
+
result += `No expired quarantine memories.\n`;
|
|
387
|
+
}
|
|
388
|
+
if (qc.errors.length > 0) {
|
|
389
|
+
qc.errors.forEach((e) => { result += ` \u26A0\u{FE0F} ${e}\n`; });
|
|
390
|
+
}
|
|
360
391
|
result += `\n`;
|
|
361
392
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
393
|
+
// Feedback maintenance section
|
|
394
|
+
if (data.feedback_maintenance) {
|
|
395
|
+
const fm = data.feedback_maintenance;
|
|
396
|
+
result += `## Feedback Maintenance\n`;
|
|
397
|
+
if (fm.promoted.length > 0) {
|
|
398
|
+
result += `**Promoted** (${fm.promoted.length}): moved to durable\n`;
|
|
399
|
+
fm.promoted.forEach((id) => { result += ` \u2705 ${id}\n`; });
|
|
400
|
+
}
|
|
401
|
+
if (fm.pruned.length > 0) {
|
|
402
|
+
result += `**Pruned** (${fm.pruned.length}): removed\n`;
|
|
403
|
+
fm.pruned.forEach((id) => { result += ` \u{1F5D1}\u{FE0F} ${id}\n`; });
|
|
404
|
+
}
|
|
405
|
+
if (fm.promoted.length === 0 && fm.pruned.length === 0) {
|
|
406
|
+
result += `No feedback-based actions needed.\n`;
|
|
407
|
+
}
|
|
408
|
+
if (fm.errors.length > 0) {
|
|
409
|
+
fm.errors.forEach((e) => { result += ` \u26A0\u{FE0F} ${e}\n`; });
|
|
410
|
+
}
|
|
365
411
|
result += `\n`;
|
|
366
412
|
}
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
413
|
+
// Compaction section
|
|
414
|
+
if (data.compaction) {
|
|
415
|
+
const cp = data.compaction;
|
|
416
|
+
result += `## Compaction${cp.dryRun ? ' (dry run)' : ''}\n`;
|
|
417
|
+
if (cp.clusters.length > 0) {
|
|
418
|
+
result += `**${cp.totalClusters} cluster(s)** of similar memories found\n\n`;
|
|
419
|
+
cp.clusters.slice(0, 5).forEach((c, i) => {
|
|
420
|
+
result += `${i + 1}. ${c.originalIds.length} memories → ${truncate(c.mergedContent, 120)}\n`;
|
|
421
|
+
if (c.mergedId)
|
|
422
|
+
result += ` Merged ID: \`${c.mergedId}\`\n`;
|
|
423
|
+
});
|
|
424
|
+
if (cp.clusters.length > 5)
|
|
425
|
+
result += `... and ${cp.clusters.length - 5} more clusters\n`;
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
result += `No similar memory clusters found.\n`;
|
|
429
|
+
}
|
|
370
430
|
result += `\n`;
|
|
371
431
|
}
|
|
372
|
-
if (promoted.length === 0 && pruned.length === 0) {
|
|
373
|
-
result += `No memories needed maintenance. All feedback thresholds are below auto-action levels.\n`;
|
|
374
|
-
}
|
|
375
432
|
return result;
|
|
376
433
|
},
|
|
377
434
|
},
|
package/dist/tools/pm.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createPmTools(projectName) {
|
|
|
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
17
|
query: z.string().describe("Search query for requirements (e.g., 'video inspection flow', 'payment integration')"),
|
|
18
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
18
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
19
19
|
}),
|
|
20
20
|
annotations: TOOL_ANNOTATIONS["search_requirements"],
|
|
21
21
|
handler: async (args, ctx) => {
|
|
@@ -236,8 +236,8 @@ export function createPmTools(projectName) {
|
|
|
236
236
|
description: `List all documented requirements/features for ${projectName} from Confluence. Groups by category or status.`,
|
|
237
237
|
schema: z.object({
|
|
238
238
|
category: z.string().optional().describe("Filter by category (optional)"),
|
|
239
|
-
limit: z.number().optional().describe("Max results (default: 20)"),
|
|
240
|
-
offset: z.number().optional().describe("Pagination offset (default: 0)"),
|
|
239
|
+
limit: z.coerce.number().optional().describe("Max results (default: 20)"),
|
|
240
|
+
offset: z.coerce.number().optional().describe("Pagination offset (default: 0)"),
|
|
241
241
|
}),
|
|
242
242
|
annotations: TOOL_ANNOTATIONS["list_requirements"],
|
|
243
243
|
handler: async (args, ctx) => {
|
package/dist/tools/search.js
CHANGED
|
@@ -15,7 +15,7 @@ export function createSearchTools(projectName) {
|
|
|
15
15
|
description: `Search the ${projectName} codebase. Returns file locations, symbols, and graph connections. Use Read tool to view the actual code at returned locations.`,
|
|
16
16
|
schema: z.object({
|
|
17
17
|
query: z.string().describe("Search query for finding code"),
|
|
18
|
-
limit: z.number().optional().describe("Max results to return (default: 5)"),
|
|
18
|
+
limit: z.coerce.number().optional().describe("Max results to return (default: 5)"),
|
|
19
19
|
language: z.string().optional().describe("Filter by language (typescript, python, vue, etc.)"),
|
|
20
20
|
path: z.string().optional().describe("Filter by path pattern (e.g., 'src/modules/*')"),
|
|
21
21
|
layer: z.string().optional().describe("Filter by architectural layer (api, service, util, model, middleware, test, parser, types, config, other)"),
|
|
@@ -43,7 +43,7 @@ export function createSearchTools(projectName) {
|
|
|
43
43
|
description: "Find code similar to a given snippet.",
|
|
44
44
|
schema: z.object({
|
|
45
45
|
code: z.string().describe("Code snippet to find similar code for"),
|
|
46
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
46
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
47
47
|
}),
|
|
48
48
|
annotations: TOOL_ANNOTATIONS["search_similar"],
|
|
49
49
|
handler: async (args, ctx) => {
|
|
@@ -66,7 +66,7 @@ export function createSearchTools(projectName) {
|
|
|
66
66
|
schema: z.object({
|
|
67
67
|
query: z.string().describe("Search query"),
|
|
68
68
|
groupBy: z.string().optional().describe("Field to group by (default: 'file')"),
|
|
69
|
-
limit: z.number().optional().describe("Max groups to return (default: 10)"),
|
|
69
|
+
limit: z.coerce.number().optional().describe("Max groups to return (default: 10)"),
|
|
70
70
|
language: z.string().optional().describe("Filter by language"),
|
|
71
71
|
layer: z.string().optional().describe("Filter by architectural layer (api, service, util, etc.)"),
|
|
72
72
|
service: z.string().optional().describe("Filter by service/class name"),
|
|
@@ -97,8 +97,8 @@ export function createSearchTools(projectName) {
|
|
|
97
97
|
description: `Hybrid search combining keyword matching and semantic similarity for ${projectName}. Returns file locations with symbols and connections. Use Read tool to view code.`,
|
|
98
98
|
schema: z.object({
|
|
99
99
|
query: z.string().describe("Search query"),
|
|
100
|
-
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
101
|
-
semanticWeight: z.number().optional().describe("Weight for semantic vs keyword (0-1, default: 0.7)"),
|
|
100
|
+
limit: z.coerce.number().optional().describe("Max results (default: 10)"),
|
|
101
|
+
semanticWeight: z.coerce.number().optional().describe("Weight for semantic vs keyword (0-1, default: 0.7)"),
|
|
102
102
|
language: z.string().optional().describe("Filter by language"),
|
|
103
103
|
layer: z.string().optional().describe("Filter by architectural layer (api, service, util, etc.)"),
|
|
104
104
|
service: z.string().optional().describe("Filter by service/class name"),
|
|
@@ -128,7 +128,7 @@ export function createSearchTools(projectName) {
|
|
|
128
128
|
description: `Search documentation in the ${projectName} project.`,
|
|
129
129
|
schema: z.object({
|
|
130
130
|
query: z.string().describe("Search query"),
|
|
131
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
131
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
132
132
|
}),
|
|
133
133
|
annotations: TOOL_ANNOTATIONS["search_docs"],
|
|
134
134
|
handler: async (args, ctx) => {
|
|
@@ -154,11 +154,11 @@ export function createSearchTools(projectName) {
|
|
|
154
154
|
schema: z.object({}),
|
|
155
155
|
outputSchema: z.object({
|
|
156
156
|
projectName: z.string(),
|
|
157
|
-
totalFiles: z.number(),
|
|
158
|
-
totalLines: z.number().optional(),
|
|
159
|
-
vectorCount: z.number(),
|
|
157
|
+
totalFiles: z.coerce.number(),
|
|
158
|
+
totalLines: z.coerce.number().optional(),
|
|
159
|
+
vectorCount: z.coerce.number(),
|
|
160
160
|
lastIndexed: z.string().optional(),
|
|
161
|
-
languages: z.record(z.string(), z.number()).optional(),
|
|
161
|
+
languages: z.record(z.string(), z.coerce.number()).optional(),
|
|
162
162
|
}),
|
|
163
163
|
annotations: TOOL_ANNOTATIONS["get_project_stats"],
|
|
164
164
|
handler: async (_args, ctx) => {
|
|
@@ -194,15 +194,15 @@ export function createSearchTools(projectName) {
|
|
|
194
194
|
schema: z.object({
|
|
195
195
|
symbol: z.string().describe("Symbol name to find (function, class, type, etc.)"),
|
|
196
196
|
kind: z.string().optional().describe("Filter by kind: function, class, interface, type, enum, const"),
|
|
197
|
-
limit: z.number().optional().describe("Max results (default: 10)"),
|
|
197
|
+
limit: z.coerce.number().optional().describe("Max results (default: 10)"),
|
|
198
198
|
}),
|
|
199
199
|
outputSchema: z.object({
|
|
200
200
|
symbols: z.array(z.object({
|
|
201
201
|
kind: z.string(),
|
|
202
202
|
name: z.string(),
|
|
203
203
|
file: z.string(),
|
|
204
|
-
startLine: z.number(),
|
|
205
|
-
endLine: z.number(),
|
|
204
|
+
startLine: z.coerce.number(),
|
|
205
|
+
endLine: z.coerce.number(),
|
|
206
206
|
signature: z.string(),
|
|
207
207
|
exported: z.boolean(),
|
|
208
208
|
})),
|
|
@@ -246,8 +246,8 @@ export function createSearchTools(projectName) {
|
|
|
246
246
|
description: `Search ${projectName} codebase with graph expansion. Returns file locations plus connected files via import/call relationships. Use Read tool to view code.`,
|
|
247
247
|
schema: z.object({
|
|
248
248
|
query: z.string().describe("Search query"),
|
|
249
|
-
limit: z.number().optional().describe("Max direct results (default: 5)"),
|
|
250
|
-
expandHops: z.number().optional().describe("Number of graph hops to expand (default: 1)"),
|
|
249
|
+
limit: z.coerce.number().optional().describe("Max direct results (default: 5)"),
|
|
250
|
+
expandHops: z.coerce.number().optional().describe("Number of graph hops to expand (default: 1)"),
|
|
251
251
|
}),
|
|
252
252
|
annotations: TOOL_ANNOTATIONS["search_graph"],
|
|
253
253
|
handler: async (args, ctx) => {
|
package/dist/tools/session.js
CHANGED
|
@@ -102,7 +102,7 @@ export function createSessionTools(projectName, sharedCtx) {
|
|
|
102
102
|
name: "analyze_usage_patterns",
|
|
103
103
|
description: `Analyze tool usage patterns for ${projectName}. Shows common workflows, detected patterns, and recommendations for improving productivity.`,
|
|
104
104
|
schema: z.object({
|
|
105
|
-
days: z.number().optional().describe("Number of days to analyze (default: 7)."),
|
|
105
|
+
days: z.coerce.number().optional().describe("Number of days to analyze (default: 7)."),
|
|
106
106
|
}),
|
|
107
107
|
annotations: TOOL_ANNOTATIONS["analyze_usage_patterns"],
|
|
108
108
|
handler: async (args, ctx) => {
|
|
@@ -290,7 +290,7 @@ export function createSuggestionTools(projectName) {
|
|
|
290
290
|
schema: z.object({
|
|
291
291
|
file: z.string().optional().describe("File path to find related code for"),
|
|
292
292
|
code: z.string().optional().describe("Code snippet to find related code for"),
|
|
293
|
-
limit: z.number().optional().describe("Max results (default: 5)"),
|
|
293
|
+
limit: z.coerce.number().optional().describe("Max results (default: 5)"),
|
|
294
294
|
}),
|
|
295
295
|
annotations: TOOL_ANNOTATIONS["suggest_related_code"],
|
|
296
296
|
handler: async (args, ctx) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crowley/rag-mcp",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"description": "Universal RAG MCP Server for any project",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
"build": "tsc",
|
|
15
15
|
"dev": "tsc -w",
|
|
16
16
|
"start": "node dist/index.js",
|
|
17
|
-
"prepublishOnly": "npm run build"
|
|
17
|
+
"prepublishOnly": "npm run build",
|
|
18
|
+
"test": "vitest run --passWithNoTests",
|
|
19
|
+
"test:watch": "vitest"
|
|
18
20
|
},
|
|
19
21
|
"keywords": [
|
|
20
22
|
"mcp",
|
|
@@ -38,6 +40,7 @@
|
|
|
38
40
|
},
|
|
39
41
|
"devDependencies": {
|
|
40
42
|
"@types/node": "^20.10.0",
|
|
41
|
-
"typescript": "^5.3.0"
|
|
43
|
+
"typescript": "^5.3.0",
|
|
44
|
+
"vitest": "^4.0.18"
|
|
42
45
|
}
|
|
43
46
|
}
|