@vibescope/mcp-server 0.2.0 → 0.2.2
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/README.md +60 -7
- package/dist/api-client.d.ts +251 -1
- package/dist/api-client.js +82 -3
- package/dist/handlers/blockers.js +9 -8
- package/dist/handlers/bodies-of-work.js +96 -63
- package/dist/handlers/connectors.d.ts +45 -0
- package/dist/handlers/connectors.js +183 -0
- package/dist/handlers/cost.d.ts +10 -0
- package/dist/handlers/cost.js +112 -50
- package/dist/handlers/decisions.js +32 -19
- package/dist/handlers/deployment.js +144 -122
- package/dist/handlers/discovery.d.ts +7 -0
- package/dist/handlers/discovery.js +96 -7
- package/dist/handlers/fallback.js +29 -23
- package/dist/handlers/file-checkouts.d.ts +20 -0
- package/dist/handlers/file-checkouts.js +133 -0
- package/dist/handlers/findings.d.ts +6 -0
- package/dist/handlers/findings.js +96 -40
- package/dist/handlers/git-issues.js +40 -36
- package/dist/handlers/ideas.js +49 -31
- package/dist/handlers/index.d.ts +3 -0
- package/dist/handlers/index.js +9 -0
- package/dist/handlers/milestones.js +39 -32
- package/dist/handlers/organizations.js +99 -91
- package/dist/handlers/progress.js +24 -13
- package/dist/handlers/project.js +68 -28
- package/dist/handlers/requests.js +18 -14
- package/dist/handlers/roles.d.ts +18 -0
- package/dist/handlers/roles.js +130 -0
- package/dist/handlers/session.js +58 -17
- package/dist/handlers/sprints.js +93 -81
- package/dist/handlers/tasks.d.ts +2 -0
- package/dist/handlers/tasks.js +189 -91
- package/dist/handlers/types.d.ts +64 -2
- package/dist/handlers/types.js +48 -1
- package/dist/handlers/validation.js +21 -17
- package/dist/index.js +7 -2716
- package/dist/token-tracking.d.ts +74 -0
- package/dist/token-tracking.js +122 -0
- package/dist/tools.js +685 -9
- package/dist/utils.d.ts +5 -0
- package/dist/utils.js +17 -0
- package/docs/TOOLS.md +2053 -0
- package/package.json +4 -1
- package/scripts/generate-docs.ts +212 -0
- package/src/api-client.test.ts +718 -0
- package/src/api-client.ts +320 -6
- package/src/handlers/__test-setup__.ts +16 -0
- package/src/handlers/blockers.test.ts +31 -19
- package/src/handlers/blockers.ts +9 -8
- package/src/handlers/bodies-of-work.test.ts +55 -32
- package/src/handlers/bodies-of-work.ts +115 -115
- package/src/handlers/connectors.test.ts +834 -0
- package/src/handlers/connectors.ts +229 -0
- package/src/handlers/cost.test.ts +34 -44
- package/src/handlers/cost.ts +136 -85
- package/src/handlers/decisions.test.ts +37 -27
- package/src/handlers/decisions.ts +35 -30
- package/src/handlers/deployment.ts +180 -208
- package/src/handlers/discovery.test.ts +4 -5
- package/src/handlers/discovery.ts +98 -8
- package/src/handlers/fallback.test.ts +26 -22
- package/src/handlers/fallback.ts +36 -33
- package/src/handlers/file-checkouts.test.ts +670 -0
- package/src/handlers/file-checkouts.ts +165 -0
- package/src/handlers/findings.test.ts +178 -19
- package/src/handlers/findings.ts +112 -74
- package/src/handlers/git-issues.test.ts +51 -43
- package/src/handlers/git-issues.ts +44 -84
- package/src/handlers/ideas.test.ts +28 -23
- package/src/handlers/ideas.ts +61 -59
- package/src/handlers/index.ts +9 -0
- package/src/handlers/milestones.test.ts +33 -28
- package/src/handlers/milestones.ts +52 -50
- package/src/handlers/organizations.test.ts +104 -83
- package/src/handlers/organizations.ts +117 -142
- package/src/handlers/progress.test.ts +20 -14
- package/src/handlers/progress.ts +26 -24
- package/src/handlers/project.test.ts +34 -27
- package/src/handlers/project.ts +95 -63
- package/src/handlers/requests.test.ts +27 -18
- package/src/handlers/requests.ts +21 -17
- package/src/handlers/roles.test.ts +303 -0
- package/src/handlers/roles.ts +208 -0
- package/src/handlers/session.test.ts +47 -0
- package/src/handlers/session.ts +71 -26
- package/src/handlers/sprints.test.ts +71 -50
- package/src/handlers/sprints.ts +113 -146
- package/src/handlers/tasks.test.ts +77 -15
- package/src/handlers/tasks.ts +231 -156
- package/src/handlers/tool-categories.test.ts +66 -0
- package/src/handlers/types.ts +81 -2
- package/src/handlers/validation.test.ts +78 -45
- package/src/handlers/validation.ts +23 -25
- package/src/index.ts +12 -2732
- package/src/token-tracking.test.ts +453 -0
- package/src/token-tracking.ts +164 -0
- package/src/tools.ts +685 -9
- package/src/utils.test.ts +2 -2
- package/src/utils.ts +17 -0
- package/dist/config/tool-categories.d.ts +0 -31
- package/dist/config/tool-categories.js +0 -253
- package/dist/knowledge.d.ts +0 -6
- package/dist/knowledge.js +0 -218
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*
|
|
10
10
|
* MIGRATED: Uses Vibescope API client instead of direct Supabase
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
12
|
+
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
13
13
|
import { FALLBACK_ACTIVITIES } from '../utils.js';
|
|
14
14
|
import { getApiClient } from '../api-client.js';
|
|
15
15
|
const VALID_ACTIVITIES = [
|
|
@@ -23,20 +23,32 @@ const VALID_ACTIVITIES = [
|
|
|
23
23
|
'documentation_review',
|
|
24
24
|
'dependency_audit',
|
|
25
25
|
'validate_completed_tasks',
|
|
26
|
+
'worktree_cleanup',
|
|
26
27
|
];
|
|
28
|
+
// Argument schemas for type-safe parsing
|
|
29
|
+
const startFallbackActivitySchema = {
|
|
30
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
31
|
+
activity: { type: 'string', required: true, validate: createEnumValidator(VALID_ACTIVITIES) },
|
|
32
|
+
};
|
|
33
|
+
const stopFallbackActivitySchema = {
|
|
34
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
35
|
+
summary: { type: 'string' },
|
|
36
|
+
};
|
|
37
|
+
const getActivityHistorySchema = {
|
|
38
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
39
|
+
activity_type: { type: 'string' },
|
|
40
|
+
limit: { type: 'number', default: 50 },
|
|
41
|
+
};
|
|
42
|
+
const getActivitySchedulesSchema = {
|
|
43
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
44
|
+
};
|
|
27
45
|
export const startFallbackActivity = async (args, ctx) => {
|
|
28
|
-
const { project_id, activity } = args;
|
|
29
|
-
validateRequired(project_id, 'project_id');
|
|
30
|
-
validateUUID(project_id, 'project_id');
|
|
31
|
-
validateRequired(activity, 'activity');
|
|
32
|
-
if (!VALID_ACTIVITIES.includes(activity)) {
|
|
33
|
-
throw new Error(`Invalid activity. Must be one of: ${VALID_ACTIVITIES.join(', ')}`);
|
|
34
|
-
}
|
|
46
|
+
const { project_id, activity } = parseArgs(args, startFallbackActivitySchema);
|
|
35
47
|
const { session } = ctx;
|
|
36
48
|
const apiClient = getApiClient();
|
|
37
49
|
const response = await apiClient.startFallbackActivity(project_id, activity, session.currentSessionId || undefined);
|
|
38
50
|
if (!response.ok) {
|
|
39
|
-
|
|
51
|
+
return { result: { error: response.error || 'Failed to start fallback activity' }, isError: true };
|
|
40
52
|
}
|
|
41
53
|
// Get the activity details for the response
|
|
42
54
|
const activityInfo = FALLBACK_ACTIVITIES.find((a) => a.activity === activity);
|
|
@@ -61,14 +73,12 @@ export const startFallbackActivity = async (args, ctx) => {
|
|
|
61
73
|
return { result };
|
|
62
74
|
};
|
|
63
75
|
export const stopFallbackActivity = async (args, ctx) => {
|
|
64
|
-
const { project_id, summary } = args;
|
|
65
|
-
validateRequired(project_id, 'project_id');
|
|
66
|
-
validateUUID(project_id, 'project_id');
|
|
76
|
+
const { project_id, summary } = parseArgs(args, stopFallbackActivitySchema);
|
|
67
77
|
const { session } = ctx;
|
|
68
78
|
const apiClient = getApiClient();
|
|
69
79
|
const response = await apiClient.stopFallbackActivity(project_id, summary, session.currentSessionId || undefined);
|
|
70
80
|
if (!response.ok) {
|
|
71
|
-
|
|
81
|
+
return { result: { error: response.error || 'Failed to stop fallback activity' }, isError: true };
|
|
72
82
|
}
|
|
73
83
|
return {
|
|
74
84
|
result: {
|
|
@@ -77,10 +87,8 @@ export const stopFallbackActivity = async (args, ctx) => {
|
|
|
77
87
|
},
|
|
78
88
|
};
|
|
79
89
|
};
|
|
80
|
-
export const getActivityHistory = async (args,
|
|
81
|
-
const { project_id, activity_type, limit
|
|
82
|
-
validateRequired(project_id, 'project_id');
|
|
83
|
-
validateUUID(project_id, 'project_id');
|
|
90
|
+
export const getActivityHistory = async (args, _ctx) => {
|
|
91
|
+
const { project_id, activity_type, limit } = parseArgs(args, getActivityHistorySchema);
|
|
84
92
|
const apiClient = getApiClient();
|
|
85
93
|
// Use proxy for get_activity_history operation
|
|
86
94
|
const response = await apiClient.proxy('get_activity_history', {
|
|
@@ -89,7 +97,7 @@ export const getActivityHistory = async (args, ctx) => {
|
|
|
89
97
|
limit
|
|
90
98
|
});
|
|
91
99
|
if (!response.ok) {
|
|
92
|
-
|
|
100
|
+
return { result: { error: response.error || 'Failed to get activity history' }, isError: true };
|
|
93
101
|
}
|
|
94
102
|
return {
|
|
95
103
|
result: {
|
|
@@ -99,17 +107,15 @@ export const getActivityHistory = async (args, ctx) => {
|
|
|
99
107
|
},
|
|
100
108
|
};
|
|
101
109
|
};
|
|
102
|
-
export const getActivitySchedules = async (args,
|
|
103
|
-
const { project_id } = args;
|
|
104
|
-
validateRequired(project_id, 'project_id');
|
|
105
|
-
validateUUID(project_id, 'project_id');
|
|
110
|
+
export const getActivitySchedules = async (args, _ctx) => {
|
|
111
|
+
const { project_id } = parseArgs(args, getActivitySchedulesSchema);
|
|
106
112
|
const apiClient = getApiClient();
|
|
107
113
|
// Use proxy for get_activity_schedules operation
|
|
108
114
|
const response = await apiClient.proxy('get_activity_schedules', {
|
|
109
115
|
project_id
|
|
110
116
|
});
|
|
111
117
|
if (!response.ok) {
|
|
112
|
-
|
|
118
|
+
return { result: { error: response.error || 'Failed to get activity schedules' }, isError: true };
|
|
113
119
|
}
|
|
114
120
|
return {
|
|
115
121
|
result: {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Checkouts Handlers
|
|
3
|
+
*
|
|
4
|
+
* Handles file checkout/checkin for multi-agent coordination:
|
|
5
|
+
* - checkout_file: Check out a file before editing
|
|
6
|
+
* - checkin_file: Check in a file after editing
|
|
7
|
+
* - get_file_checkouts: Get active checkouts for a project
|
|
8
|
+
* - abandon_checkout: Force release a checkout
|
|
9
|
+
* - is_file_available: Check if a file is available for checkout
|
|
10
|
+
*/
|
|
11
|
+
import type { Handler, HandlerRegistry } from './types.js';
|
|
12
|
+
export declare const checkoutFile: Handler;
|
|
13
|
+
export declare const checkinFile: Handler;
|
|
14
|
+
export declare const getFileCheckouts: Handler;
|
|
15
|
+
export declare const abandonCheckout: Handler;
|
|
16
|
+
export declare const isFileAvailable: Handler;
|
|
17
|
+
/**
|
|
18
|
+
* File Checkouts handlers registry
|
|
19
|
+
*/
|
|
20
|
+
export declare const fileCheckoutHandlers: HandlerRegistry;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Checkouts Handlers
|
|
3
|
+
*
|
|
4
|
+
* Handles file checkout/checkin for multi-agent coordination:
|
|
5
|
+
* - checkout_file: Check out a file before editing
|
|
6
|
+
* - checkin_file: Check in a file after editing
|
|
7
|
+
* - get_file_checkouts: Get active checkouts for a project
|
|
8
|
+
* - abandon_checkout: Force release a checkout
|
|
9
|
+
* - is_file_available: Check if a file is available for checkout
|
|
10
|
+
*/
|
|
11
|
+
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
12
|
+
import { getApiClient } from '../api-client.js';
|
|
13
|
+
const VALID_CHECKOUT_STATUSES = ['checked_out', 'checked_in', 'abandoned'];
|
|
14
|
+
// Argument schemas for type-safe parsing
|
|
15
|
+
const checkoutFileSchema = {
|
|
16
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
17
|
+
file_path: { type: 'string', required: true },
|
|
18
|
+
reason: { type: 'string' },
|
|
19
|
+
};
|
|
20
|
+
const checkinFileSchema = {
|
|
21
|
+
checkout_id: { type: 'string', validate: uuidValidator },
|
|
22
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
23
|
+
file_path: { type: 'string' },
|
|
24
|
+
summary: { type: 'string' },
|
|
25
|
+
};
|
|
26
|
+
const getFileCheckoutsSchema = {
|
|
27
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
28
|
+
status: { type: 'string', validate: createEnumValidator(VALID_CHECKOUT_STATUSES) },
|
|
29
|
+
file_path: { type: 'string' },
|
|
30
|
+
limit: { type: 'number', default: 50 },
|
|
31
|
+
};
|
|
32
|
+
const abandonCheckoutSchema = {
|
|
33
|
+
checkout_id: { type: 'string', validate: uuidValidator },
|
|
34
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
35
|
+
file_path: { type: 'string' },
|
|
36
|
+
};
|
|
37
|
+
const isFileAvailableSchema = {
|
|
38
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
39
|
+
file_path: { type: 'string', required: true },
|
|
40
|
+
};
|
|
41
|
+
export const checkoutFile = async (args, ctx) => {
|
|
42
|
+
const { project_id, file_path, reason } = parseArgs(args, checkoutFileSchema);
|
|
43
|
+
const apiClient = getApiClient();
|
|
44
|
+
const response = await apiClient.checkoutFile(project_id, file_path, reason, ctx.session.currentSessionId || undefined);
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
return { result: { error: response.error || 'Failed to checkout file' }, isError: true };
|
|
47
|
+
}
|
|
48
|
+
return { result: response.data };
|
|
49
|
+
};
|
|
50
|
+
export const checkinFile = async (args, ctx) => {
|
|
51
|
+
const { checkout_id, project_id, file_path, summary } = parseArgs(args, checkinFileSchema);
|
|
52
|
+
// Validate that either checkout_id or both project_id and file_path are provided
|
|
53
|
+
if (!checkout_id && (!project_id || !file_path)) {
|
|
54
|
+
return { result: { error: 'Either checkout_id or both project_id and file_path are required' }, isError: true };
|
|
55
|
+
}
|
|
56
|
+
const apiClient = getApiClient();
|
|
57
|
+
const response = await apiClient.checkinFile({
|
|
58
|
+
checkout_id,
|
|
59
|
+
project_id,
|
|
60
|
+
file_path,
|
|
61
|
+
summary
|
|
62
|
+
}, ctx.session.currentSessionId || undefined);
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
return { result: { error: response.error || 'Failed to checkin file' }, isError: true };
|
|
65
|
+
}
|
|
66
|
+
return { result: response.data };
|
|
67
|
+
};
|
|
68
|
+
export const getFileCheckouts = async (args, _ctx) => {
|
|
69
|
+
const { project_id, status, file_path, limit } = parseArgs(args, getFileCheckoutsSchema);
|
|
70
|
+
const apiClient = getApiClient();
|
|
71
|
+
const response = await apiClient.getFileCheckouts(project_id, {
|
|
72
|
+
status,
|
|
73
|
+
file_path,
|
|
74
|
+
limit
|
|
75
|
+
});
|
|
76
|
+
if (!response.ok) {
|
|
77
|
+
return { result: { error: response.error || 'Failed to get file checkouts' }, isError: true };
|
|
78
|
+
}
|
|
79
|
+
return { result: response.data };
|
|
80
|
+
};
|
|
81
|
+
export const abandonCheckout = async (args, _ctx) => {
|
|
82
|
+
const { checkout_id, project_id, file_path } = parseArgs(args, abandonCheckoutSchema);
|
|
83
|
+
// Validate that either checkout_id or both project_id and file_path are provided
|
|
84
|
+
if (!checkout_id && (!project_id || !file_path)) {
|
|
85
|
+
return { result: { error: 'Either checkout_id or both project_id and file_path are required' }, isError: true };
|
|
86
|
+
}
|
|
87
|
+
const apiClient = getApiClient();
|
|
88
|
+
const response = await apiClient.abandonCheckout({
|
|
89
|
+
checkout_id,
|
|
90
|
+
project_id,
|
|
91
|
+
file_path
|
|
92
|
+
});
|
|
93
|
+
if (!response.ok) {
|
|
94
|
+
return { result: { error: response.error || 'Failed to abandon checkout' }, isError: true };
|
|
95
|
+
}
|
|
96
|
+
return { result: response.data };
|
|
97
|
+
};
|
|
98
|
+
export const isFileAvailable = async (args, _ctx) => {
|
|
99
|
+
const { project_id, file_path } = parseArgs(args, isFileAvailableSchema);
|
|
100
|
+
const apiClient = getApiClient();
|
|
101
|
+
const response = await apiClient.getFileCheckouts(project_id, {
|
|
102
|
+
status: 'checked_out',
|
|
103
|
+
file_path,
|
|
104
|
+
limit: 1
|
|
105
|
+
});
|
|
106
|
+
if (!response.ok) {
|
|
107
|
+
return { result: { error: response.error || 'Failed to check file availability' }, isError: true };
|
|
108
|
+
}
|
|
109
|
+
const checkouts = response.data?.checkouts || [];
|
|
110
|
+
const activeCheckout = checkouts.length > 0 ? checkouts[0] : null;
|
|
111
|
+
return {
|
|
112
|
+
result: {
|
|
113
|
+
available: !activeCheckout,
|
|
114
|
+
file_path,
|
|
115
|
+
checked_out_by: activeCheckout ? {
|
|
116
|
+
checkout_id: activeCheckout.id,
|
|
117
|
+
checked_out_by: activeCheckout.checked_out_by,
|
|
118
|
+
checked_out_at: activeCheckout.checked_out_at,
|
|
119
|
+
reason: activeCheckout.checkout_reason
|
|
120
|
+
} : null
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* File Checkouts handlers registry
|
|
126
|
+
*/
|
|
127
|
+
export const fileCheckoutHandlers = {
|
|
128
|
+
checkout_file: checkoutFile,
|
|
129
|
+
checkin_file: checkinFile,
|
|
130
|
+
get_file_checkouts: getFileCheckouts,
|
|
131
|
+
abandon_checkout: abandonCheckout,
|
|
132
|
+
is_file_available: isFileAvailable,
|
|
133
|
+
};
|
|
@@ -19,6 +19,12 @@ export declare const getFindings: Handler;
|
|
|
19
19
|
export declare const getFindingsStats: Handler;
|
|
20
20
|
export declare const updateFinding: Handler;
|
|
21
21
|
export declare const deleteFinding: Handler;
|
|
22
|
+
/**
|
|
23
|
+
* Query aggregated project knowledge in a single call.
|
|
24
|
+
* Returns findings, Q&A, decisions, completed tasks, and resolved blockers.
|
|
25
|
+
* Use this instead of multiple separate tool calls to reduce token usage.
|
|
26
|
+
*/
|
|
27
|
+
export declare const queryKnowledgeBase: Handler;
|
|
22
28
|
/**
|
|
23
29
|
* Findings handlers registry
|
|
24
30
|
*/
|
|
@@ -8,92 +8,147 @@
|
|
|
8
8
|
* - update_finding
|
|
9
9
|
* - delete_finding
|
|
10
10
|
*/
|
|
11
|
-
import {
|
|
11
|
+
import { success, error } from './types.js';
|
|
12
|
+
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
12
13
|
import { getApiClient } from '../api-client.js';
|
|
14
|
+
const VALID_FINDING_CATEGORIES = ['performance', 'security', 'code_quality', 'accessibility', 'documentation', 'architecture', 'testing', 'other'];
|
|
15
|
+
const VALID_FINDING_SEVERITIES = ['info', 'low', 'medium', 'high', 'critical'];
|
|
16
|
+
const VALID_FINDING_STATUSES = ['open', 'addressed', 'dismissed', 'wontfix'];
|
|
17
|
+
// Argument schemas for type-safe parsing
|
|
18
|
+
const addFindingSchema = {
|
|
19
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
20
|
+
title: { type: 'string', required: true },
|
|
21
|
+
description: { type: 'string' },
|
|
22
|
+
category: { type: 'string', validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
|
|
23
|
+
severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
|
|
24
|
+
file_path: { type: 'string' },
|
|
25
|
+
line_number: { type: 'number' },
|
|
26
|
+
related_task_id: { type: 'string', validate: uuidValidator },
|
|
27
|
+
};
|
|
28
|
+
const getFindingsSchema = {
|
|
29
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
30
|
+
category: { type: 'string', validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
|
|
31
|
+
severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
|
|
32
|
+
status: { type: 'string', validate: createEnumValidator(VALID_FINDING_STATUSES) },
|
|
33
|
+
limit: { type: 'number', default: 50 },
|
|
34
|
+
offset: { type: 'number', default: 0 },
|
|
35
|
+
search_query: { type: 'string' },
|
|
36
|
+
summary_only: { type: 'boolean', default: false },
|
|
37
|
+
};
|
|
38
|
+
const getFindingsStatsSchema = {
|
|
39
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
40
|
+
};
|
|
41
|
+
const updateFindingSchema = {
|
|
42
|
+
finding_id: { type: 'string', required: true, validate: uuidValidator },
|
|
43
|
+
title: { type: 'string' },
|
|
44
|
+
description: { type: 'string' },
|
|
45
|
+
severity: { type: 'string', validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
|
|
46
|
+
status: { type: 'string', validate: createEnumValidator(VALID_FINDING_STATUSES) },
|
|
47
|
+
resolution_note: { type: 'string' },
|
|
48
|
+
};
|
|
49
|
+
const deleteFindingSchema = {
|
|
50
|
+
finding_id: { type: 'string', required: true, validate: uuidValidator },
|
|
51
|
+
};
|
|
52
|
+
const VALID_SCOPES = ['summary', 'detailed'];
|
|
53
|
+
const queryKnowledgeBaseSchema = {
|
|
54
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
55
|
+
scope: { type: 'string', default: 'summary', validate: createEnumValidator(VALID_SCOPES) },
|
|
56
|
+
categories: { type: 'array' },
|
|
57
|
+
limit: { type: 'number', default: 5 },
|
|
58
|
+
search_query: { type: 'string' },
|
|
59
|
+
};
|
|
13
60
|
export const addFinding = async (args, ctx) => {
|
|
14
|
-
const { project_id,
|
|
15
|
-
validateRequired(project_id, 'project_id');
|
|
16
|
-
validateUUID(project_id, 'project_id');
|
|
17
|
-
validateRequired(title, 'title');
|
|
18
|
-
if (related_task_id)
|
|
19
|
-
validateUUID(related_task_id, 'related_task_id');
|
|
61
|
+
const { project_id, title, description, category, severity, file_path, line_number, related_task_id } = parseArgs(args, addFindingSchema);
|
|
20
62
|
const apiClient = getApiClient();
|
|
21
63
|
const response = await apiClient.addFinding(project_id, {
|
|
22
64
|
title,
|
|
23
65
|
description,
|
|
24
|
-
category,
|
|
25
|
-
severity,
|
|
66
|
+
category: category,
|
|
67
|
+
severity: severity,
|
|
26
68
|
file_path,
|
|
27
69
|
line_number,
|
|
28
70
|
related_task_id
|
|
29
71
|
}, ctx.session.currentSessionId || undefined);
|
|
30
72
|
if (!response.ok) {
|
|
31
|
-
|
|
73
|
+
return error(response.error || 'Failed to add finding');
|
|
32
74
|
}
|
|
33
|
-
return
|
|
75
|
+
return success(response.data);
|
|
34
76
|
};
|
|
35
|
-
export const getFindings = async (args,
|
|
36
|
-
const { project_id, category, severity, status, limit
|
|
37
|
-
validateRequired(project_id, 'project_id');
|
|
38
|
-
validateUUID(project_id, 'project_id');
|
|
77
|
+
export const getFindings = async (args, _ctx) => {
|
|
78
|
+
const { project_id, category, severity, status, limit, offset, search_query, summary_only } = parseArgs(args, getFindingsSchema);
|
|
39
79
|
const apiClient = getApiClient();
|
|
40
80
|
const response = await apiClient.getFindings(project_id, {
|
|
41
|
-
category,
|
|
42
|
-
severity,
|
|
43
|
-
status,
|
|
81
|
+
category: category,
|
|
82
|
+
severity: severity,
|
|
83
|
+
status: status,
|
|
44
84
|
limit,
|
|
45
85
|
offset,
|
|
46
86
|
search_query,
|
|
47
87
|
summary_only
|
|
48
88
|
});
|
|
49
89
|
if (!response.ok) {
|
|
50
|
-
|
|
90
|
+
return error(response.error || 'Failed to get findings');
|
|
51
91
|
}
|
|
52
|
-
return
|
|
92
|
+
return success(response.data);
|
|
53
93
|
};
|
|
54
94
|
/**
|
|
55
95
|
* Get aggregate statistics about findings for a project.
|
|
56
96
|
* Returns counts by category, severity, and status without the actual finding data.
|
|
57
97
|
* This is much more token-efficient than get_findings for understanding the overall state.
|
|
58
98
|
*/
|
|
59
|
-
export const getFindingsStats = async (args,
|
|
60
|
-
const { project_id } = args;
|
|
61
|
-
validateRequired(project_id, 'project_id');
|
|
62
|
-
validateUUID(project_id, 'project_id');
|
|
99
|
+
export const getFindingsStats = async (args, _ctx) => {
|
|
100
|
+
const { project_id } = parseArgs(args, getFindingsStatsSchema);
|
|
63
101
|
const apiClient = getApiClient();
|
|
64
102
|
const response = await apiClient.getFindingsStats(project_id);
|
|
65
103
|
if (!response.ok) {
|
|
66
|
-
|
|
104
|
+
return error(response.error || 'Failed to get findings stats');
|
|
67
105
|
}
|
|
68
|
-
return
|
|
106
|
+
return success(response.data);
|
|
69
107
|
};
|
|
70
|
-
export const updateFinding = async (args,
|
|
71
|
-
const { finding_id,
|
|
72
|
-
validateRequired(finding_id, 'finding_id');
|
|
73
|
-
validateUUID(finding_id, 'finding_id');
|
|
108
|
+
export const updateFinding = async (args, _ctx) => {
|
|
109
|
+
const { finding_id, title, description, severity, status, resolution_note } = parseArgs(args, updateFindingSchema);
|
|
74
110
|
const apiClient = getApiClient();
|
|
75
111
|
const response = await apiClient.updateFinding(finding_id, {
|
|
76
112
|
title,
|
|
77
113
|
description,
|
|
78
|
-
severity,
|
|
79
|
-
status,
|
|
114
|
+
severity: severity,
|
|
115
|
+
status: status,
|
|
80
116
|
resolution_note
|
|
81
117
|
});
|
|
82
118
|
if (!response.ok) {
|
|
83
|
-
|
|
119
|
+
return error(response.error || 'Failed to update finding');
|
|
84
120
|
}
|
|
85
|
-
return
|
|
121
|
+
return success(response.data);
|
|
86
122
|
};
|
|
87
|
-
export const deleteFinding = async (args,
|
|
88
|
-
const { finding_id } = args;
|
|
89
|
-
validateRequired(finding_id, 'finding_id');
|
|
90
|
-
validateUUID(finding_id, 'finding_id');
|
|
123
|
+
export const deleteFinding = async (args, _ctx) => {
|
|
124
|
+
const { finding_id } = parseArgs(args, deleteFindingSchema);
|
|
91
125
|
const apiClient = getApiClient();
|
|
92
126
|
const response = await apiClient.deleteFinding(finding_id);
|
|
93
127
|
if (!response.ok) {
|
|
94
|
-
|
|
128
|
+
return error(response.error || 'Failed to delete finding');
|
|
129
|
+
}
|
|
130
|
+
return success(response.data);
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Query aggregated project knowledge in a single call.
|
|
134
|
+
* Returns findings, Q&A, decisions, completed tasks, and resolved blockers.
|
|
135
|
+
* Use this instead of multiple separate tool calls to reduce token usage.
|
|
136
|
+
*/
|
|
137
|
+
export const queryKnowledgeBase = async (args, _ctx) => {
|
|
138
|
+
const { project_id, scope, categories, limit, search_query } = parseArgs(args, queryKnowledgeBaseSchema);
|
|
139
|
+
// Validate limit range
|
|
140
|
+
const effectiveLimit = Math.min(Math.max(1, limit ?? 5), 20);
|
|
141
|
+
const apiClient = getApiClient();
|
|
142
|
+
const response = await apiClient.queryKnowledgeBase(project_id, {
|
|
143
|
+
scope: scope,
|
|
144
|
+
categories: categories,
|
|
145
|
+
limit: effectiveLimit,
|
|
146
|
+
search_query
|
|
147
|
+
});
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
return error(response.error || 'Failed to query knowledge base');
|
|
95
150
|
}
|
|
96
|
-
return
|
|
151
|
+
return success(response.data);
|
|
97
152
|
};
|
|
98
153
|
/**
|
|
99
154
|
* Findings handlers registry
|
|
@@ -104,4 +159,5 @@ export const findingHandlers = {
|
|
|
104
159
|
get_findings_stats: getFindingsStats,
|
|
105
160
|
update_finding: updateFinding,
|
|
106
161
|
delete_finding: deleteFinding,
|
|
162
|
+
query_knowledge_base: queryKnowledgeBase,
|
|
107
163
|
};
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - get_git_issues: List git issues for a project
|
|
8
8
|
* - delete_git_issue: Remove a git issue
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
10
|
+
import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
|
|
11
11
|
import { getApiClient } from '../api-client.js';
|
|
12
12
|
const VALID_GIT_ISSUE_TYPES = [
|
|
13
13
|
'merge_conflict',
|
|
@@ -17,77 +17,81 @@ const VALID_GIT_ISSUE_TYPES = [
|
|
|
17
17
|
'pr_not_mergeable',
|
|
18
18
|
];
|
|
19
19
|
const VALID_GIT_ISSUE_STATUSES = ['open', 'resolved'];
|
|
20
|
+
// Argument schemas for type-safe parsing
|
|
21
|
+
const addGitIssueSchema = {
|
|
22
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
23
|
+
issue_type: { type: 'string', required: true, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
|
|
24
|
+
branch: { type: 'string', required: true },
|
|
25
|
+
target_branch: { type: 'string' },
|
|
26
|
+
pr_url: { type: 'string' },
|
|
27
|
+
conflicting_files: { type: 'array' },
|
|
28
|
+
error_message: { type: 'string' },
|
|
29
|
+
task_id: { type: 'string', validate: uuidValidator },
|
|
30
|
+
};
|
|
31
|
+
const resolveGitIssueSchema = {
|
|
32
|
+
git_issue_id: { type: 'string', required: true, validate: uuidValidator },
|
|
33
|
+
resolution_note: { type: 'string' },
|
|
34
|
+
auto_resolved: { type: 'boolean' },
|
|
35
|
+
};
|
|
36
|
+
const getGitIssuesSchema = {
|
|
37
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
38
|
+
status: { type: 'string', default: 'open', validate: createEnumValidator(VALID_GIT_ISSUE_STATUSES) },
|
|
39
|
+
issue_type: { type: 'string', validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
|
|
40
|
+
branch: { type: 'string' },
|
|
41
|
+
limit: { type: 'number', default: 50 },
|
|
42
|
+
};
|
|
43
|
+
const deleteGitIssueSchema = {
|
|
44
|
+
git_issue_id: { type: 'string', required: true, validate: uuidValidator },
|
|
45
|
+
};
|
|
20
46
|
export const addGitIssue = async (args, ctx) => {
|
|
21
|
-
const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id
|
|
22
|
-
validateRequired(project_id, 'project_id');
|
|
23
|
-
validateUUID(project_id, 'project_id');
|
|
24
|
-
validateRequired(issue_type, 'issue_type');
|
|
25
|
-
validateRequired(branch, 'branch');
|
|
26
|
-
if (!VALID_GIT_ISSUE_TYPES.includes(issue_type)) {
|
|
27
|
-
throw new Error(`Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`);
|
|
28
|
-
}
|
|
29
|
-
if (task_id) {
|
|
30
|
-
validateUUID(task_id, 'task_id');
|
|
31
|
-
}
|
|
47
|
+
const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id } = parseArgs(args, addGitIssueSchema);
|
|
32
48
|
const apiClient = getApiClient();
|
|
33
49
|
const response = await apiClient.addGitIssue(project_id, {
|
|
34
50
|
issue_type: issue_type,
|
|
35
51
|
branch,
|
|
36
52
|
target_branch,
|
|
37
53
|
pr_url,
|
|
38
|
-
conflicting_files,
|
|
54
|
+
conflicting_files: conflicting_files,
|
|
39
55
|
error_message,
|
|
40
56
|
task_id
|
|
41
57
|
}, ctx.session.currentSessionId || undefined);
|
|
42
58
|
if (!response.ok) {
|
|
43
|
-
|
|
59
|
+
return { result: { error: response.error || 'Failed to add git issue' }, isError: true };
|
|
44
60
|
}
|
|
45
61
|
return { result: response.data };
|
|
46
62
|
};
|
|
47
63
|
export const resolveGitIssue = async (args, ctx) => {
|
|
48
|
-
const { git_issue_id, resolution_note, auto_resolved } = args;
|
|
49
|
-
validateRequired(git_issue_id, 'git_issue_id');
|
|
50
|
-
validateUUID(git_issue_id, 'git_issue_id');
|
|
64
|
+
const { git_issue_id, resolution_note, auto_resolved } = parseArgs(args, resolveGitIssueSchema);
|
|
51
65
|
const apiClient = getApiClient();
|
|
52
66
|
const response = await apiClient.resolveGitIssue(git_issue_id, {
|
|
53
67
|
resolution_note,
|
|
54
68
|
auto_resolved
|
|
55
69
|
}, ctx.session.currentSessionId || undefined);
|
|
56
70
|
if (!response.ok) {
|
|
57
|
-
|
|
71
|
+
return { result: { error: response.error || 'Failed to resolve git issue' }, isError: true };
|
|
58
72
|
}
|
|
59
73
|
return { result: response.data };
|
|
60
74
|
};
|
|
61
|
-
export const getGitIssues = async (args,
|
|
62
|
-
const { project_id, status
|
|
63
|
-
validateRequired(project_id, 'project_id');
|
|
64
|
-
validateUUID(project_id, 'project_id');
|
|
65
|
-
if (status && !VALID_GIT_ISSUE_STATUSES.includes(status)) {
|
|
66
|
-
throw new Error(`Invalid status. Valid statuses: ${VALID_GIT_ISSUE_STATUSES.join(', ')}`);
|
|
67
|
-
}
|
|
68
|
-
if (issue_type && !VALID_GIT_ISSUE_TYPES.includes(issue_type)) {
|
|
69
|
-
throw new Error(`Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`);
|
|
70
|
-
}
|
|
75
|
+
export const getGitIssues = async (args, _ctx) => {
|
|
76
|
+
const { project_id, status, issue_type, branch, limit } = parseArgs(args, getGitIssuesSchema);
|
|
71
77
|
const apiClient = getApiClient();
|
|
72
78
|
const response = await apiClient.getGitIssues(project_id, {
|
|
73
|
-
status,
|
|
74
|
-
issue_type,
|
|
79
|
+
status: status,
|
|
80
|
+
issue_type: issue_type,
|
|
75
81
|
branch,
|
|
76
82
|
limit
|
|
77
83
|
});
|
|
78
84
|
if (!response.ok) {
|
|
79
|
-
|
|
85
|
+
return { result: { error: response.error || 'Failed to fetch git issues' }, isError: true };
|
|
80
86
|
}
|
|
81
87
|
return { result: response.data };
|
|
82
88
|
};
|
|
83
|
-
export const deleteGitIssue = async (args,
|
|
84
|
-
const { git_issue_id } = args;
|
|
85
|
-
validateRequired(git_issue_id, 'git_issue_id');
|
|
86
|
-
validateUUID(git_issue_id, 'git_issue_id');
|
|
89
|
+
export const deleteGitIssue = async (args, _ctx) => {
|
|
90
|
+
const { git_issue_id } = parseArgs(args, deleteGitIssueSchema);
|
|
87
91
|
const apiClient = getApiClient();
|
|
88
92
|
const response = await apiClient.deleteGitIssue(git_issue_id);
|
|
89
93
|
if (!response.ok) {
|
|
90
|
-
|
|
94
|
+
return { result: { error: response.error || 'Failed to delete git issue' }, isError: true };
|
|
91
95
|
}
|
|
92
96
|
return { result: response.data };
|
|
93
97
|
};
|