@vibescope/mcp-server 0.2.1 → 0.2.3
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 +63 -38
- package/dist/api-client.d.ts +187 -0
- package/dist/api-client.js +53 -1
- package/dist/handlers/blockers.js +9 -8
- package/dist/handlers/bodies-of-work.js +14 -14
- 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 +54 -0
- package/dist/handlers/decisions.js +3 -3
- package/dist/handlers/deployment.js +35 -19
- package/dist/handlers/discovery.d.ts +7 -0
- package/dist/handlers/discovery.js +61 -2
- package/dist/handlers/fallback.js +5 -4
- package/dist/handlers/file-checkouts.d.ts +2 -0
- package/dist/handlers/file-checkouts.js +38 -6
- package/dist/handlers/findings.js +13 -12
- package/dist/handlers/git-issues.js +4 -4
- package/dist/handlers/ideas.js +5 -5
- package/dist/handlers/index.d.ts +1 -0
- package/dist/handlers/index.js +3 -0
- package/dist/handlers/milestones.js +5 -5
- package/dist/handlers/organizations.js +13 -13
- package/dist/handlers/progress.js +2 -2
- package/dist/handlers/project.js +6 -6
- package/dist/handlers/requests.js +3 -3
- package/dist/handlers/session.js +28 -9
- package/dist/handlers/sprints.js +17 -17
- package/dist/handlers/tasks.d.ts +2 -0
- package/dist/handlers/tasks.js +78 -20
- package/dist/handlers/types.d.ts +64 -2
- package/dist/handlers/types.js +48 -1
- package/dist/handlers/validation.js +3 -3
- 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 +298 -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 +723 -0
- package/src/api-client.ts +236 -1
- package/src/handlers/__test-setup__.ts +9 -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 +14 -14
- package/src/handlers/connectors.test.ts +834 -0
- package/src/handlers/connectors.ts +229 -0
- package/src/handlers/cost.ts +66 -0
- package/src/handlers/decisions.test.ts +34 -25
- package/src/handlers/decisions.ts +3 -3
- package/src/handlers/deployment.ts +39 -19
- package/src/handlers/discovery.ts +61 -2
- package/src/handlers/fallback.test.ts +26 -22
- package/src/handlers/fallback.ts +5 -4
- package/src/handlers/file-checkouts.test.ts +242 -49
- package/src/handlers/file-checkouts.ts +44 -6
- package/src/handlers/findings.test.ts +38 -24
- package/src/handlers/findings.ts +13 -12
- package/src/handlers/git-issues.test.ts +51 -43
- package/src/handlers/git-issues.ts +4 -4
- package/src/handlers/ideas.test.ts +28 -23
- package/src/handlers/ideas.ts +5 -5
- package/src/handlers/index.ts +3 -0
- package/src/handlers/milestones.test.ts +33 -28
- package/src/handlers/milestones.ts +5 -5
- package/src/handlers/organizations.test.ts +104 -83
- package/src/handlers/organizations.ts +13 -13
- package/src/handlers/progress.test.ts +20 -14
- package/src/handlers/progress.ts +2 -2
- package/src/handlers/project.test.ts +34 -27
- package/src/handlers/project.ts +6 -6
- package/src/handlers/requests.test.ts +27 -18
- package/src/handlers/requests.ts +3 -3
- package/src/handlers/session.test.ts +47 -0
- package/src/handlers/session.ts +26 -9
- package/src/handlers/sprints.test.ts +71 -50
- package/src/handlers/sprints.ts +17 -17
- package/src/handlers/tasks.test.ts +77 -15
- package/src/handlers/tasks.ts +90 -21
- 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 +3 -3
- 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 +298 -9
- package/src/utils.test.ts +2 -2
- package/src/utils.ts +17 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Connectors Handlers
|
|
3
|
+
*
|
|
4
|
+
* Handles external integration management:
|
|
5
|
+
* - get_connectors
|
|
6
|
+
* - get_connector
|
|
7
|
+
* - add_connector
|
|
8
|
+
* - update_connector
|
|
9
|
+
* - delete_connector
|
|
10
|
+
* - test_connector
|
|
11
|
+
* - get_connector_events
|
|
12
|
+
*/
|
|
13
|
+
import { success, error } from './types.js';
|
|
14
|
+
import { parseArgs, uuidValidator, createEnumValidator, } from '../validators.js';
|
|
15
|
+
import { getApiClient } from '../api-client.js';
|
|
16
|
+
// Valid connector types
|
|
17
|
+
const VALID_CONNECTOR_TYPES = ['webhook', 'slack', 'discord', 'github', 'custom'];
|
|
18
|
+
// Valid connector statuses
|
|
19
|
+
const VALID_CONNECTOR_STATUSES = ['active', 'disabled'];
|
|
20
|
+
// Valid event statuses
|
|
21
|
+
const VALID_EVENT_STATUSES = ['pending', 'sent', 'failed', 'retrying'];
|
|
22
|
+
// Argument schemas for type-safe parsing
|
|
23
|
+
const getConnectorsSchema = {
|
|
24
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
25
|
+
type: { type: 'string', validate: createEnumValidator(VALID_CONNECTOR_TYPES) },
|
|
26
|
+
status: { type: 'string', validate: createEnumValidator(VALID_CONNECTOR_STATUSES) },
|
|
27
|
+
limit: { type: 'number', default: 50 },
|
|
28
|
+
offset: { type: 'number', default: 0 },
|
|
29
|
+
};
|
|
30
|
+
const getConnectorSchema = {
|
|
31
|
+
connector_id: { type: 'string', required: true, validate: uuidValidator },
|
|
32
|
+
};
|
|
33
|
+
const addConnectorSchema = {
|
|
34
|
+
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
35
|
+
name: { type: 'string', required: true },
|
|
36
|
+
type: { type: 'string', required: true, validate: createEnumValidator(VALID_CONNECTOR_TYPES) },
|
|
37
|
+
description: { type: 'string' },
|
|
38
|
+
config: { type: 'object' },
|
|
39
|
+
events: { type: 'object' },
|
|
40
|
+
};
|
|
41
|
+
const updateConnectorSchema = {
|
|
42
|
+
connector_id: { type: 'string', required: true, validate: uuidValidator },
|
|
43
|
+
name: { type: 'string' },
|
|
44
|
+
description: { type: 'string' },
|
|
45
|
+
config: { type: 'object' },
|
|
46
|
+
events: { type: 'object' },
|
|
47
|
+
status: { type: 'string', validate: createEnumValidator(VALID_CONNECTOR_STATUSES) },
|
|
48
|
+
};
|
|
49
|
+
const deleteConnectorSchema = {
|
|
50
|
+
connector_id: { type: 'string', required: true, validate: uuidValidator },
|
|
51
|
+
};
|
|
52
|
+
const testConnectorSchema = {
|
|
53
|
+
connector_id: { type: 'string', required: true, validate: uuidValidator },
|
|
54
|
+
};
|
|
55
|
+
const getConnectorEventsSchema = {
|
|
56
|
+
connector_id: { type: 'string', validate: uuidValidator },
|
|
57
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
58
|
+
status: { type: 'string', validate: createEnumValidator(VALID_EVENT_STATUSES) },
|
|
59
|
+
limit: { type: 'number', default: 50 },
|
|
60
|
+
offset: { type: 'number', default: 0 },
|
|
61
|
+
};
|
|
62
|
+
/**
|
|
63
|
+
* Get all connectors for a project
|
|
64
|
+
*/
|
|
65
|
+
export const getConnectors = async (args, _ctx) => {
|
|
66
|
+
const { project_id, type, status, limit, offset } = parseArgs(args, getConnectorsSchema);
|
|
67
|
+
const apiClient = getApiClient();
|
|
68
|
+
const response = await apiClient.getConnectors(project_id, {
|
|
69
|
+
type,
|
|
70
|
+
status,
|
|
71
|
+
limit,
|
|
72
|
+
offset
|
|
73
|
+
});
|
|
74
|
+
if (!response.ok) {
|
|
75
|
+
return error(response.error || 'Failed to fetch connectors');
|
|
76
|
+
}
|
|
77
|
+
return success(response.data);
|
|
78
|
+
};
|
|
79
|
+
/**
|
|
80
|
+
* Get a single connector with full details
|
|
81
|
+
*/
|
|
82
|
+
export const getConnector = async (args, _ctx) => {
|
|
83
|
+
const { connector_id } = parseArgs(args, getConnectorSchema);
|
|
84
|
+
const apiClient = getApiClient();
|
|
85
|
+
const response = await apiClient.getConnector(connector_id);
|
|
86
|
+
if (!response.ok) {
|
|
87
|
+
return error(response.error || 'Failed to fetch connector');
|
|
88
|
+
}
|
|
89
|
+
return success(response.data);
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Add a new connector
|
|
93
|
+
*/
|
|
94
|
+
export const addConnector = async (args, _ctx) => {
|
|
95
|
+
const { project_id, name, type, description, config, events } = parseArgs(args, addConnectorSchema);
|
|
96
|
+
const apiClient = getApiClient();
|
|
97
|
+
const response = await apiClient.addConnector(project_id, {
|
|
98
|
+
name,
|
|
99
|
+
type,
|
|
100
|
+
description,
|
|
101
|
+
config: config,
|
|
102
|
+
events: events
|
|
103
|
+
});
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
return error(response.error || 'Failed to create connector');
|
|
106
|
+
}
|
|
107
|
+
return success(response.data);
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Update a connector
|
|
111
|
+
*/
|
|
112
|
+
export const updateConnector = async (args, _ctx) => {
|
|
113
|
+
const { connector_id, name, description, config, events, status } = parseArgs(args, updateConnectorSchema);
|
|
114
|
+
const apiClient = getApiClient();
|
|
115
|
+
const response = await apiClient.updateConnector(connector_id, {
|
|
116
|
+
name,
|
|
117
|
+
description,
|
|
118
|
+
config: config,
|
|
119
|
+
events: events,
|
|
120
|
+
status
|
|
121
|
+
});
|
|
122
|
+
if (!response.ok) {
|
|
123
|
+
return error(response.error || 'Failed to update connector');
|
|
124
|
+
}
|
|
125
|
+
return success(response.data);
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Delete a connector
|
|
129
|
+
*/
|
|
130
|
+
export const deleteConnector = async (args, _ctx) => {
|
|
131
|
+
const { connector_id } = parseArgs(args, deleteConnectorSchema);
|
|
132
|
+
const apiClient = getApiClient();
|
|
133
|
+
const response = await apiClient.deleteConnector(connector_id);
|
|
134
|
+
if (!response.ok) {
|
|
135
|
+
return error(response.error || 'Failed to delete connector');
|
|
136
|
+
}
|
|
137
|
+
return success(response.data);
|
|
138
|
+
};
|
|
139
|
+
/**
|
|
140
|
+
* Test a connector by sending a test event
|
|
141
|
+
*/
|
|
142
|
+
export const testConnector = async (args, _ctx) => {
|
|
143
|
+
const { connector_id } = parseArgs(args, testConnectorSchema);
|
|
144
|
+
const apiClient = getApiClient();
|
|
145
|
+
const response = await apiClient.testConnector(connector_id);
|
|
146
|
+
if (!response.ok) {
|
|
147
|
+
return error(response.error || 'Failed to test connector');
|
|
148
|
+
}
|
|
149
|
+
return success(response.data);
|
|
150
|
+
};
|
|
151
|
+
/**
|
|
152
|
+
* Get connector event history
|
|
153
|
+
*/
|
|
154
|
+
export const getConnectorEvents = async (args, _ctx) => {
|
|
155
|
+
const { connector_id, project_id, status, limit, offset } = parseArgs(args, getConnectorEventsSchema);
|
|
156
|
+
if (!connector_id && !project_id) {
|
|
157
|
+
return error('Either connector_id or project_id is required');
|
|
158
|
+
}
|
|
159
|
+
const apiClient = getApiClient();
|
|
160
|
+
const response = await apiClient.getConnectorEvents({
|
|
161
|
+
connector_id,
|
|
162
|
+
project_id,
|
|
163
|
+
status,
|
|
164
|
+
limit,
|
|
165
|
+
offset
|
|
166
|
+
});
|
|
167
|
+
if (!response.ok) {
|
|
168
|
+
return error(response.error || 'Failed to fetch connector events');
|
|
169
|
+
}
|
|
170
|
+
return success(response.data);
|
|
171
|
+
};
|
|
172
|
+
/**
|
|
173
|
+
* Connectors handlers registry
|
|
174
|
+
*/
|
|
175
|
+
export const connectorHandlers = {
|
|
176
|
+
get_connectors: getConnectors,
|
|
177
|
+
get_connector: getConnector,
|
|
178
|
+
add_connector: addConnector,
|
|
179
|
+
update_connector: updateConnector,
|
|
180
|
+
delete_connector: deleteConnector,
|
|
181
|
+
test_connector: testConnector,
|
|
182
|
+
get_connector_events: getConnectorEvents,
|
|
183
|
+
};
|
package/dist/handlers/cost.d.ts
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* - update_cost_alert
|
|
9
9
|
* - delete_cost_alert
|
|
10
10
|
* - get_task_costs
|
|
11
|
+
* - get_body_of_work_costs
|
|
12
|
+
* - get_sprint_costs
|
|
11
13
|
*/
|
|
12
14
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
13
15
|
/**
|
|
@@ -34,6 +36,14 @@ export declare const deleteCostAlert: Handler;
|
|
|
34
36
|
* Get task costs for a project
|
|
35
37
|
*/
|
|
36
38
|
export declare const getTaskCosts: Handler;
|
|
39
|
+
/**
|
|
40
|
+
* Get body of work costs with phase breakdown
|
|
41
|
+
*/
|
|
42
|
+
export declare const getBodyOfWorkCosts: Handler;
|
|
43
|
+
/**
|
|
44
|
+
* Get sprint costs with velocity metrics
|
|
45
|
+
*/
|
|
46
|
+
export declare const getSprintCosts: Handler;
|
|
37
47
|
/**
|
|
38
48
|
* Cost handlers registry
|
|
39
49
|
*/
|
package/dist/handlers/cost.js
CHANGED
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* - update_cost_alert
|
|
9
9
|
* - delete_cost_alert
|
|
10
10
|
* - get_task_costs
|
|
11
|
+
* - get_body_of_work_costs
|
|
12
|
+
* - get_sprint_costs
|
|
11
13
|
*/
|
|
12
14
|
import { parseArgs, uuidValidator, createEnumValidator, ValidationError } from '../validators.js';
|
|
13
15
|
import { getApiClient } from '../api-client.js';
|
|
@@ -42,6 +44,14 @@ const getTaskCostsSchema = {
|
|
|
42
44
|
project_id: { type: 'string', required: true, validate: uuidValidator },
|
|
43
45
|
limit: { type: 'number', default: 20 },
|
|
44
46
|
};
|
|
47
|
+
const getBodyOfWorkCostsSchema = {
|
|
48
|
+
body_of_work_id: { type: 'string', validate: uuidValidator },
|
|
49
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
50
|
+
};
|
|
51
|
+
const getSprintCostsSchema = {
|
|
52
|
+
sprint_id: { type: 'string', validate: uuidValidator },
|
|
53
|
+
project_id: { type: 'string', validate: uuidValidator },
|
|
54
|
+
};
|
|
45
55
|
// Custom validator for positive numbers
|
|
46
56
|
function validatePositiveNumber(value, fieldName) {
|
|
47
57
|
if (value !== undefined && value <= 0) {
|
|
@@ -164,6 +174,48 @@ export const getTaskCosts = async (args, _ctx) => {
|
|
|
164
174
|
}
|
|
165
175
|
return { result: response.data };
|
|
166
176
|
};
|
|
177
|
+
/**
|
|
178
|
+
* Get body of work costs with phase breakdown
|
|
179
|
+
*/
|
|
180
|
+
export const getBodyOfWorkCosts = async (args, _ctx) => {
|
|
181
|
+
const { body_of_work_id, project_id } = parseArgs(args, getBodyOfWorkCostsSchema);
|
|
182
|
+
if (!body_of_work_id && !project_id) {
|
|
183
|
+
return {
|
|
184
|
+
result: { error: 'Either body_of_work_id or project_id is required' },
|
|
185
|
+
isError: true,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
const apiClient = getApiClient();
|
|
189
|
+
const response = await apiClient.getBodyOfWorkCosts({ body_of_work_id, project_id });
|
|
190
|
+
if (!response.ok) {
|
|
191
|
+
return {
|
|
192
|
+
result: { error: response.error || 'Failed to get body of work costs' },
|
|
193
|
+
isError: true,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return { result: response.data };
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* Get sprint costs with velocity metrics
|
|
200
|
+
*/
|
|
201
|
+
export const getSprintCosts = async (args, _ctx) => {
|
|
202
|
+
const { sprint_id, project_id } = parseArgs(args, getSprintCostsSchema);
|
|
203
|
+
if (!sprint_id && !project_id) {
|
|
204
|
+
return {
|
|
205
|
+
result: { error: 'Either sprint_id or project_id is required' },
|
|
206
|
+
isError: true,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const apiClient = getApiClient();
|
|
210
|
+
const response = await apiClient.getSprintCosts({ sprint_id, project_id });
|
|
211
|
+
if (!response.ok) {
|
|
212
|
+
return {
|
|
213
|
+
result: { error: response.error || 'Failed to get sprint costs' },
|
|
214
|
+
isError: true,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return { result: response.data };
|
|
218
|
+
};
|
|
167
219
|
/**
|
|
168
220
|
* Cost handlers registry
|
|
169
221
|
*/
|
|
@@ -174,4 +226,6 @@ export const costHandlers = {
|
|
|
174
226
|
update_cost_alert: updateCostAlert,
|
|
175
227
|
delete_cost_alert: deleteCostAlert,
|
|
176
228
|
get_task_costs: getTaskCosts,
|
|
229
|
+
get_body_of_work_costs: getBodyOfWorkCosts,
|
|
230
|
+
get_sprint_costs: getSprintCosts,
|
|
177
231
|
};
|
|
@@ -38,7 +38,7 @@ export const logDecision = async (args, ctx) => {
|
|
|
38
38
|
alternatives_considered: alternatives_considered
|
|
39
39
|
}, session.currentSessionId || undefined);
|
|
40
40
|
if (!response.ok) {
|
|
41
|
-
|
|
41
|
+
return { result: { error: response.error || 'Failed to log decision' }, isError: true };
|
|
42
42
|
}
|
|
43
43
|
return { result: { success: true, title, decision_id: response.data?.decision_id } };
|
|
44
44
|
};
|
|
@@ -51,7 +51,7 @@ export const getDecisions = async (args, _ctx) => {
|
|
|
51
51
|
search_query
|
|
52
52
|
});
|
|
53
53
|
if (!response.ok) {
|
|
54
|
-
|
|
54
|
+
return { result: { error: response.error || 'Failed to fetch decisions' }, isError: true };
|
|
55
55
|
}
|
|
56
56
|
return {
|
|
57
57
|
result: {
|
|
@@ -64,7 +64,7 @@ export const deleteDecision = async (args, _ctx) => {
|
|
|
64
64
|
const apiClient = getApiClient();
|
|
65
65
|
const response = await apiClient.deleteDecision(decision_id);
|
|
66
66
|
if (!response.ok) {
|
|
67
|
-
|
|
67
|
+
return { result: { error: response.error || 'Failed to delete decision' }, isError: true };
|
|
68
68
|
}
|
|
69
69
|
return { result: { success: true } };
|
|
70
70
|
};
|
|
@@ -20,7 +20,7 @@ const VERSION_BUMPS = ['patch', 'minor', 'major'];
|
|
|
20
20
|
const REQUIREMENT_TYPES = ['migration', 'env_var', 'config', 'manual', 'breaking_change', 'agent_task'];
|
|
21
21
|
const REQUIREMENT_STAGES = ['preparation', 'deployment', 'verification'];
|
|
22
22
|
const REQUIREMENT_STATUSES = ['pending', 'completed', 'converted_to_task', 'all'];
|
|
23
|
-
const SCHEDULE_TYPES = ['once', 'daily', 'weekly', 'monthly'];
|
|
23
|
+
const SCHEDULE_TYPES = ['once', 'hourly', 'daily', 'weekly', 'monthly'];
|
|
24
24
|
// ============================================================================
|
|
25
25
|
// Argument Schemas
|
|
26
26
|
// ============================================================================
|
|
@@ -80,6 +80,7 @@ const scheduleDeploymentSchema = {
|
|
|
80
80
|
schedule_type: { type: 'string', default: 'once', validate: createEnumValidator(SCHEDULE_TYPES) },
|
|
81
81
|
scheduled_at: { type: 'string', required: true },
|
|
82
82
|
auto_trigger: { type: 'boolean', default: true },
|
|
83
|
+
hours_interval: { type: 'number', default: 1 },
|
|
83
84
|
notes: { type: 'string' },
|
|
84
85
|
git_ref: { type: 'string' },
|
|
85
86
|
};
|
|
@@ -94,6 +95,7 @@ const updateScheduledDeploymentSchema = {
|
|
|
94
95
|
schedule_type: { type: 'string', validate: createEnumValidator(SCHEDULE_TYPES) },
|
|
95
96
|
scheduled_at: { type: 'string' },
|
|
96
97
|
auto_trigger: { type: 'boolean' },
|
|
98
|
+
hours_interval: { type: 'number' },
|
|
97
99
|
enabled: { type: 'boolean' },
|
|
98
100
|
notes: { type: 'string' },
|
|
99
101
|
git_ref: { type: 'string' },
|
|
@@ -118,7 +120,7 @@ export const requestDeployment = async (args, ctx) => {
|
|
|
118
120
|
git_ref
|
|
119
121
|
});
|
|
120
122
|
if (!response.ok) {
|
|
121
|
-
|
|
123
|
+
return { result: { error: response.error || 'Failed to request deployment' }, isError: true };
|
|
122
124
|
}
|
|
123
125
|
return { result: response.data };
|
|
124
126
|
};
|
|
@@ -128,7 +130,7 @@ export const claimDeploymentValidation = async (args, ctx) => {
|
|
|
128
130
|
const apiClient = getApiClient();
|
|
129
131
|
const response = await apiClient.claimDeploymentValidation(project_id, session.currentSessionId || undefined);
|
|
130
132
|
if (!response.ok) {
|
|
131
|
-
|
|
133
|
+
return { result: { error: response.error || 'Failed to claim deployment validation' }, isError: true };
|
|
132
134
|
}
|
|
133
135
|
return { result: response.data };
|
|
134
136
|
};
|
|
@@ -142,7 +144,7 @@ export const reportValidation = async (args, ctx) => {
|
|
|
142
144
|
error_message
|
|
143
145
|
});
|
|
144
146
|
if (!response.ok) {
|
|
145
|
-
|
|
147
|
+
return { result: { error: response.error || 'Failed to report validation' }, isError: true };
|
|
146
148
|
}
|
|
147
149
|
return { result: response.data };
|
|
148
150
|
};
|
|
@@ -151,7 +153,7 @@ export const checkDeploymentStatus = async (args, ctx) => {
|
|
|
151
153
|
const apiClient = getApiClient();
|
|
152
154
|
const response = await apiClient.checkDeploymentStatus(project_id);
|
|
153
155
|
if (!response.ok) {
|
|
154
|
-
|
|
156
|
+
return { result: { error: response.error || 'Failed to check deployment status' }, isError: true };
|
|
155
157
|
}
|
|
156
158
|
return { result: response.data };
|
|
157
159
|
};
|
|
@@ -161,7 +163,7 @@ export const startDeployment = async (args, ctx) => {
|
|
|
161
163
|
const apiClient = getApiClient();
|
|
162
164
|
const response = await apiClient.startDeployment(project_id, session.currentSessionId || undefined);
|
|
163
165
|
if (!response.ok) {
|
|
164
|
-
|
|
166
|
+
return { result: { error: response.error || 'Failed to start deployment' }, isError: true };
|
|
165
167
|
}
|
|
166
168
|
return { result: response.data };
|
|
167
169
|
};
|
|
@@ -174,7 +176,7 @@ export const completeDeployment = async (args, ctx) => {
|
|
|
174
176
|
summary
|
|
175
177
|
});
|
|
176
178
|
if (!response.ok) {
|
|
177
|
-
|
|
179
|
+
return { result: { error: response.error || 'Failed to complete deployment' }, isError: true };
|
|
178
180
|
}
|
|
179
181
|
return { result: response.data };
|
|
180
182
|
};
|
|
@@ -183,7 +185,7 @@ export const cancelDeployment = async (args, ctx) => {
|
|
|
183
185
|
const apiClient = getApiClient();
|
|
184
186
|
const response = await apiClient.cancelDeployment(project_id, reason);
|
|
185
187
|
if (!response.ok) {
|
|
186
|
-
|
|
188
|
+
return { result: { error: response.error || 'Failed to cancel deployment' }, isError: true };
|
|
187
189
|
}
|
|
188
190
|
return { result: response.data };
|
|
189
191
|
};
|
|
@@ -200,7 +202,7 @@ export const addDeploymentRequirement = async (args, ctx) => {
|
|
|
200
202
|
recurring
|
|
201
203
|
});
|
|
202
204
|
if (!response.ok) {
|
|
203
|
-
|
|
205
|
+
return { result: { error: response.error || 'Failed to add deployment requirement' }, isError: true };
|
|
204
206
|
}
|
|
205
207
|
return { result: response.data };
|
|
206
208
|
};
|
|
@@ -209,7 +211,7 @@ export const completeDeploymentRequirement = async (args, ctx) => {
|
|
|
209
211
|
const apiClient = getApiClient();
|
|
210
212
|
const response = await apiClient.completeDeploymentRequirement(requirement_id);
|
|
211
213
|
if (!response.ok) {
|
|
212
|
-
|
|
214
|
+
return { result: { error: response.error || 'Failed to complete deployment requirement' }, isError: true };
|
|
213
215
|
}
|
|
214
216
|
return { result: response.data };
|
|
215
217
|
};
|
|
@@ -221,7 +223,7 @@ export const getDeploymentRequirements = async (args, ctx) => {
|
|
|
221
223
|
stage: stage
|
|
222
224
|
});
|
|
223
225
|
if (!response.ok) {
|
|
224
|
-
|
|
226
|
+
return { result: { error: response.error || 'Failed to get deployment requirements' }, isError: true };
|
|
225
227
|
}
|
|
226
228
|
return { result: response.data };
|
|
227
229
|
};
|
|
@@ -229,7 +231,7 @@ export const getDeploymentRequirements = async (args, ctx) => {
|
|
|
229
231
|
// Scheduled Deployments
|
|
230
232
|
// ============================================================================
|
|
231
233
|
export const scheduleDeployment = async (args, ctx) => {
|
|
232
|
-
const { project_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, notes, git_ref, } = parseArgs(args, scheduleDeploymentSchema);
|
|
234
|
+
const { project_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, hours_interval, notes, git_ref, } = parseArgs(args, scheduleDeploymentSchema);
|
|
233
235
|
// Parse and validate scheduled_at
|
|
234
236
|
const scheduledDate = new Date(scheduled_at);
|
|
235
237
|
if (isNaN(scheduledDate.getTime())) {
|
|
@@ -243,6 +245,13 @@ export const scheduleDeployment = async (args, ctx) => {
|
|
|
243
245
|
field: 'scheduled_at',
|
|
244
246
|
});
|
|
245
247
|
}
|
|
248
|
+
// Validate hours_interval for hourly schedule type (default is 1)
|
|
249
|
+
const hoursInterval = hours_interval ?? 1;
|
|
250
|
+
if (schedule_type === 'hourly' && (hoursInterval < 1 || hoursInterval > 24)) {
|
|
251
|
+
throw new ValidationError('hours_interval must be between 1 and 24', {
|
|
252
|
+
field: 'hours_interval',
|
|
253
|
+
});
|
|
254
|
+
}
|
|
246
255
|
const apiClient = getApiClient();
|
|
247
256
|
const response = await apiClient.scheduleDeployment(project_id, {
|
|
248
257
|
environment: environment,
|
|
@@ -250,11 +259,12 @@ export const scheduleDeployment = async (args, ctx) => {
|
|
|
250
259
|
schedule_type: schedule_type,
|
|
251
260
|
scheduled_at: scheduledDate.toISOString(),
|
|
252
261
|
auto_trigger,
|
|
262
|
+
hours_interval: hoursInterval,
|
|
253
263
|
notes,
|
|
254
264
|
git_ref
|
|
255
265
|
});
|
|
256
266
|
if (!response.ok) {
|
|
257
|
-
|
|
267
|
+
return { result: { error: response.error || 'Failed to schedule deployment' }, isError: true };
|
|
258
268
|
}
|
|
259
269
|
return { result: response.data };
|
|
260
270
|
};
|
|
@@ -263,12 +273,12 @@ export const getScheduledDeployments = async (args, ctx) => {
|
|
|
263
273
|
const apiClient = getApiClient();
|
|
264
274
|
const response = await apiClient.getScheduledDeployments(project_id, include_disabled);
|
|
265
275
|
if (!response.ok) {
|
|
266
|
-
|
|
276
|
+
return { result: { error: response.error || 'Failed to get scheduled deployments' }, isError: true };
|
|
267
277
|
}
|
|
268
278
|
return { result: response.data };
|
|
269
279
|
};
|
|
270
280
|
export const updateScheduledDeployment = async (args, ctx) => {
|
|
271
|
-
const { schedule_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, enabled, notes, git_ref, } = parseArgs(args, updateScheduledDeploymentSchema);
|
|
281
|
+
const { schedule_id, environment, version_bump, schedule_type, scheduled_at, auto_trigger, hours_interval, enabled, notes, git_ref, } = parseArgs(args, updateScheduledDeploymentSchema);
|
|
272
282
|
const updates = {};
|
|
273
283
|
if (environment !== undefined)
|
|
274
284
|
updates.environment = environment;
|
|
@@ -285,6 +295,12 @@ export const updateScheduledDeployment = async (args, ctx) => {
|
|
|
285
295
|
}
|
|
286
296
|
if (auto_trigger !== undefined)
|
|
287
297
|
updates.auto_trigger = auto_trigger;
|
|
298
|
+
if (hours_interval !== undefined) {
|
|
299
|
+
if (hours_interval < 1 || hours_interval > 24) {
|
|
300
|
+
throw new ValidationError('hours_interval must be between 1 and 24');
|
|
301
|
+
}
|
|
302
|
+
updates.hours_interval = hours_interval;
|
|
303
|
+
}
|
|
288
304
|
if (enabled !== undefined)
|
|
289
305
|
updates.enabled = enabled;
|
|
290
306
|
if (notes !== undefined)
|
|
@@ -297,7 +313,7 @@ export const updateScheduledDeployment = async (args, ctx) => {
|
|
|
297
313
|
const apiClient = getApiClient();
|
|
298
314
|
const response = await apiClient.updateScheduledDeployment(schedule_id, updates);
|
|
299
315
|
if (!response.ok) {
|
|
300
|
-
|
|
316
|
+
return { result: { error: response.error || 'Failed to update scheduled deployment' }, isError: true };
|
|
301
317
|
}
|
|
302
318
|
return { result: response.data };
|
|
303
319
|
};
|
|
@@ -306,7 +322,7 @@ export const deleteScheduledDeployment = async (args, ctx) => {
|
|
|
306
322
|
const apiClient = getApiClient();
|
|
307
323
|
const response = await apiClient.deleteScheduledDeployment(schedule_id);
|
|
308
324
|
if (!response.ok) {
|
|
309
|
-
|
|
325
|
+
return { result: { error: response.error || 'Failed to delete scheduled deployment' }, isError: true };
|
|
310
326
|
}
|
|
311
327
|
return { result: response.data };
|
|
312
328
|
};
|
|
@@ -316,7 +332,7 @@ export const triggerScheduledDeployment = async (args, ctx) => {
|
|
|
316
332
|
const apiClient = getApiClient();
|
|
317
333
|
const response = await apiClient.triggerScheduledDeployment(schedule_id, session.currentSessionId || undefined);
|
|
318
334
|
if (!response.ok) {
|
|
319
|
-
|
|
335
|
+
return { result: { error: response.error || 'Failed to trigger scheduled deployment' }, isError: true };
|
|
320
336
|
}
|
|
321
337
|
return { result: response.data };
|
|
322
338
|
};
|
|
@@ -325,7 +341,7 @@ export const checkDueDeployments = async (args, ctx) => {
|
|
|
325
341
|
const apiClient = getApiClient();
|
|
326
342
|
const response = await apiClient.checkDueDeployments(project_id);
|
|
327
343
|
if (!response.ok) {
|
|
328
|
-
|
|
344
|
+
return { result: { error: response.error || 'Failed to check due deployments' }, isError: true };
|
|
329
345
|
}
|
|
330
346
|
return { result: response.data };
|
|
331
347
|
};
|
|
@@ -9,6 +9,13 @@
|
|
|
9
9
|
* This saves ~8,000 tokens per schema load.
|
|
10
10
|
*/
|
|
11
11
|
import type { Handler, HandlerRegistry } from './types.js';
|
|
12
|
+
export declare const TOOL_CATEGORIES: Record<string, {
|
|
13
|
+
description: string;
|
|
14
|
+
tools: Array<{
|
|
15
|
+
name: string;
|
|
16
|
+
brief: string;
|
|
17
|
+
}>;
|
|
18
|
+
}>;
|
|
12
19
|
export declare const discoverTools: Handler;
|
|
13
20
|
export declare const getToolInfo: Handler;
|
|
14
21
|
/**
|
|
@@ -30,8 +30,8 @@ async function getToolDocs() {
|
|
|
30
30
|
toolInfoCache = TOOL_INFO;
|
|
31
31
|
return toolInfoCache;
|
|
32
32
|
}
|
|
33
|
-
// Tool categories with brief descriptions
|
|
34
|
-
const TOOL_CATEGORIES = {
|
|
33
|
+
// Tool categories with brief descriptions (exported for documentation generation)
|
|
34
|
+
export const TOOL_CATEGORIES = {
|
|
35
35
|
session: {
|
|
36
36
|
description: 'Session lifecycle and monitoring',
|
|
37
37
|
tools: [
|
|
@@ -116,6 +116,7 @@ const TOOL_CATEGORIES = {
|
|
|
116
116
|
tools: [
|
|
117
117
|
{ name: 'add_finding', brief: 'Record audit finding' },
|
|
118
118
|
{ name: 'get_findings', brief: 'List findings' },
|
|
119
|
+
{ name: 'get_findings_stats', brief: 'Get findings statistics' },
|
|
119
120
|
{ name: 'update_finding', brief: 'Update finding status' },
|
|
120
121
|
{ name: 'delete_finding', brief: 'Remove finding' },
|
|
121
122
|
],
|
|
@@ -169,6 +170,10 @@ const TOOL_CATEGORIES = {
|
|
|
169
170
|
{ name: 'add_task_to_body_of_work', brief: 'Add task to group' },
|
|
170
171
|
{ name: 'remove_task_from_body_of_work', brief: 'Remove from group' },
|
|
171
172
|
{ name: 'activate_body_of_work', brief: 'Activate for work' },
|
|
173
|
+
{ name: 'add_task_dependency', brief: 'Add task dependency' },
|
|
174
|
+
{ name: 'remove_task_dependency', brief: 'Remove task dependency' },
|
|
175
|
+
{ name: 'get_task_dependencies', brief: 'List task dependencies' },
|
|
176
|
+
{ name: 'get_next_body_of_work_task', brief: 'Get next available task' },
|
|
172
177
|
],
|
|
173
178
|
},
|
|
174
179
|
sprints: {
|
|
@@ -222,6 +227,8 @@ const TOOL_CATEGORIES = {
|
|
|
222
227
|
{ name: 'update_cost_alert', brief: 'Update alert config' },
|
|
223
228
|
{ name: 'delete_cost_alert', brief: 'Remove alert' },
|
|
224
229
|
{ name: 'get_task_costs', brief: 'Cost per task' },
|
|
230
|
+
{ name: 'get_body_of_work_costs', brief: 'Cost per body of work' },
|
|
231
|
+
{ name: 'get_sprint_costs', brief: 'Cost per sprint' },
|
|
225
232
|
],
|
|
226
233
|
},
|
|
227
234
|
git_issues: {
|
|
@@ -239,6 +246,58 @@ const TOOL_CATEGORIES = {
|
|
|
239
246
|
{ name: 'query_knowledge_base', brief: 'Aggregated project knowledge in one call' },
|
|
240
247
|
],
|
|
241
248
|
},
|
|
249
|
+
discovery: {
|
|
250
|
+
description: 'Tool discovery and documentation',
|
|
251
|
+
tools: [
|
|
252
|
+
{ name: 'discover_tools', brief: 'List tools by category' },
|
|
253
|
+
{ name: 'get_tool_info', brief: 'Get detailed tool docs' },
|
|
254
|
+
],
|
|
255
|
+
},
|
|
256
|
+
subtasks: {
|
|
257
|
+
description: 'Break tasks into smaller pieces',
|
|
258
|
+
tools: [
|
|
259
|
+
{ name: 'add_subtask', brief: 'Add subtask to task' },
|
|
260
|
+
{ name: 'get_subtasks', brief: 'List task subtasks' },
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
worktrees: {
|
|
264
|
+
description: 'Git worktree management',
|
|
265
|
+
tools: [
|
|
266
|
+
{ name: 'get_stale_worktrees', brief: 'Find orphaned worktrees' },
|
|
267
|
+
{ name: 'clear_worktree_path', brief: 'Clear worktree from task' },
|
|
268
|
+
],
|
|
269
|
+
},
|
|
270
|
+
roles: {
|
|
271
|
+
description: 'Agent role management',
|
|
272
|
+
tools: [
|
|
273
|
+
{ name: 'get_role_settings', brief: 'Get project role settings' },
|
|
274
|
+
{ name: 'update_role_settings', brief: 'Configure role behavior' },
|
|
275
|
+
{ name: 'set_session_role', brief: 'Set session role' },
|
|
276
|
+
{ name: 'get_agents_by_role', brief: 'List agents by role' },
|
|
277
|
+
],
|
|
278
|
+
},
|
|
279
|
+
file_locks: {
|
|
280
|
+
description: 'File checkout/locking for multi-agent',
|
|
281
|
+
tools: [
|
|
282
|
+
{ name: 'checkout_file', brief: 'Lock file for editing' },
|
|
283
|
+
{ name: 'checkin_file', brief: 'Release file lock' },
|
|
284
|
+
{ name: 'get_file_checkouts', brief: 'List file locks' },
|
|
285
|
+
{ name: 'abandon_checkout', brief: 'Force-release lock' },
|
|
286
|
+
{ name: 'is_file_available', brief: 'Check if file is free' },
|
|
287
|
+
],
|
|
288
|
+
},
|
|
289
|
+
connectors: {
|
|
290
|
+
description: 'External integration connectors',
|
|
291
|
+
tools: [
|
|
292
|
+
{ name: 'get_connectors', brief: 'List project connectors' },
|
|
293
|
+
{ name: 'get_connector', brief: 'Get connector details' },
|
|
294
|
+
{ name: 'add_connector', brief: 'Create new connector' },
|
|
295
|
+
{ name: 'update_connector', brief: 'Update connector config' },
|
|
296
|
+
{ name: 'delete_connector', brief: 'Remove connector' },
|
|
297
|
+
{ name: 'test_connector', brief: 'Send test event' },
|
|
298
|
+
{ name: 'get_connector_events', brief: 'Event history' },
|
|
299
|
+
],
|
|
300
|
+
},
|
|
242
301
|
};
|
|
243
302
|
export const discoverTools = async (args) => {
|
|
244
303
|
const { category } = parseArgs(args, discoverToolsSchema);
|
|
@@ -23,6 +23,7 @@ const VALID_ACTIVITIES = [
|
|
|
23
23
|
'documentation_review',
|
|
24
24
|
'dependency_audit',
|
|
25
25
|
'validate_completed_tasks',
|
|
26
|
+
'worktree_cleanup',
|
|
26
27
|
];
|
|
27
28
|
// Argument schemas for type-safe parsing
|
|
28
29
|
const startFallbackActivitySchema = {
|
|
@@ -47,7 +48,7 @@ export const startFallbackActivity = async (args, ctx) => {
|
|
|
47
48
|
const apiClient = getApiClient();
|
|
48
49
|
const response = await apiClient.startFallbackActivity(project_id, activity, session.currentSessionId || undefined);
|
|
49
50
|
if (!response.ok) {
|
|
50
|
-
|
|
51
|
+
return { result: { error: response.error || 'Failed to start fallback activity' }, isError: true };
|
|
51
52
|
}
|
|
52
53
|
// Get the activity details for the response
|
|
53
54
|
const activityInfo = FALLBACK_ACTIVITIES.find((a) => a.activity === activity);
|
|
@@ -77,7 +78,7 @@ export const stopFallbackActivity = async (args, ctx) => {
|
|
|
77
78
|
const apiClient = getApiClient();
|
|
78
79
|
const response = await apiClient.stopFallbackActivity(project_id, summary, session.currentSessionId || undefined);
|
|
79
80
|
if (!response.ok) {
|
|
80
|
-
|
|
81
|
+
return { result: { error: response.error || 'Failed to stop fallback activity' }, isError: true };
|
|
81
82
|
}
|
|
82
83
|
return {
|
|
83
84
|
result: {
|
|
@@ -96,7 +97,7 @@ export const getActivityHistory = async (args, _ctx) => {
|
|
|
96
97
|
limit
|
|
97
98
|
});
|
|
98
99
|
if (!response.ok) {
|
|
99
|
-
|
|
100
|
+
return { result: { error: response.error || 'Failed to get activity history' }, isError: true };
|
|
100
101
|
}
|
|
101
102
|
return {
|
|
102
103
|
result: {
|
|
@@ -114,7 +115,7 @@ export const getActivitySchedules = async (args, _ctx) => {
|
|
|
114
115
|
project_id
|
|
115
116
|
});
|
|
116
117
|
if (!response.ok) {
|
|
117
|
-
|
|
118
|
+
return { result: { error: response.error || 'Failed to get activity schedules' }, isError: true };
|
|
118
119
|
}
|
|
119
120
|
return {
|
|
120
121
|
result: {
|