@vibescope/mcp-server 0.2.3 → 0.2.4

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.
Files changed (79) hide show
  1. package/CHANGELOG.md +84 -0
  2. package/README.md +45 -2
  3. package/dist/api-client.d.ts +276 -8
  4. package/dist/api-client.js +123 -8
  5. package/dist/handlers/blockers.d.ts +11 -0
  6. package/dist/handlers/blockers.js +37 -2
  7. package/dist/handlers/bodies-of-work.d.ts +2 -0
  8. package/dist/handlers/bodies-of-work.js +30 -1
  9. package/dist/handlers/connectors.js +2 -2
  10. package/dist/handlers/decisions.d.ts +11 -0
  11. package/dist/handlers/decisions.js +37 -2
  12. package/dist/handlers/deployment.d.ts +6 -0
  13. package/dist/handlers/deployment.js +33 -5
  14. package/dist/handlers/discovery.js +27 -11
  15. package/dist/handlers/fallback.js +12 -6
  16. package/dist/handlers/file-checkouts.d.ts +1 -0
  17. package/dist/handlers/file-checkouts.js +17 -2
  18. package/dist/handlers/findings.d.ts +5 -0
  19. package/dist/handlers/findings.js +19 -2
  20. package/dist/handlers/git-issues.js +4 -2
  21. package/dist/handlers/ideas.d.ts +5 -0
  22. package/dist/handlers/ideas.js +19 -2
  23. package/dist/handlers/progress.js +2 -2
  24. package/dist/handlers/project.d.ts +1 -0
  25. package/dist/handlers/project.js +35 -2
  26. package/dist/handlers/requests.js +6 -3
  27. package/dist/handlers/roles.js +13 -2
  28. package/dist/handlers/session.d.ts +12 -0
  29. package/dist/handlers/session.js +288 -25
  30. package/dist/handlers/sprints.d.ts +2 -0
  31. package/dist/handlers/sprints.js +30 -1
  32. package/dist/handlers/tasks.d.ts +25 -2
  33. package/dist/handlers/tasks.js +228 -35
  34. package/dist/handlers/tool-docs.js +72 -5
  35. package/dist/templates/agent-guidelines.d.ts +18 -0
  36. package/dist/templates/agent-guidelines.js +207 -0
  37. package/dist/tools.js +478 -125
  38. package/dist/utils.d.ts +5 -2
  39. package/dist/utils.js +90 -51
  40. package/package.json +51 -46
  41. package/scripts/version-bump.ts +203 -0
  42. package/src/api-client.ts +371 -12
  43. package/src/handlers/__test-setup__.ts +5 -0
  44. package/src/handlers/blockers.test.ts +76 -0
  45. package/src/handlers/blockers.ts +56 -2
  46. package/src/handlers/bodies-of-work.ts +59 -1
  47. package/src/handlers/connectors.ts +2 -2
  48. package/src/handlers/decisions.test.ts +71 -2
  49. package/src/handlers/decisions.ts +56 -2
  50. package/src/handlers/deployment.test.ts +81 -0
  51. package/src/handlers/deployment.ts +38 -5
  52. package/src/handlers/discovery.ts +27 -11
  53. package/src/handlers/fallback.test.ts +11 -10
  54. package/src/handlers/fallback.ts +14 -8
  55. package/src/handlers/file-checkouts.test.ts +83 -3
  56. package/src/handlers/file-checkouts.ts +22 -2
  57. package/src/handlers/findings.test.ts +2 -2
  58. package/src/handlers/findings.ts +38 -2
  59. package/src/handlers/git-issues.test.ts +2 -2
  60. package/src/handlers/git-issues.ts +4 -2
  61. package/src/handlers/ideas.test.ts +1 -1
  62. package/src/handlers/ideas.ts +34 -2
  63. package/src/handlers/progress.ts +2 -2
  64. package/src/handlers/project.ts +47 -2
  65. package/src/handlers/requests.test.ts +38 -7
  66. package/src/handlers/requests.ts +6 -3
  67. package/src/handlers/roles.test.ts +1 -1
  68. package/src/handlers/roles.ts +20 -2
  69. package/src/handlers/session.test.ts +303 -4
  70. package/src/handlers/session.ts +348 -35
  71. package/src/handlers/sprints.ts +61 -1
  72. package/src/handlers/tasks.test.ts +0 -73
  73. package/src/handlers/tasks.ts +269 -40
  74. package/src/handlers/tool-docs.ts +77 -5
  75. package/src/handlers/types.test.ts +259 -0
  76. package/src/templates/agent-guidelines.ts +210 -0
  77. package/src/tools.ts +479 -125
  78. package/src/utils.test.ts +7 -5
  79. package/src/utils.ts +95 -51
@@ -114,6 +114,61 @@ export class VibescopeApiClient {
114
114
  async getTask(taskId) {
115
115
  return this.request('GET', `/api/mcp/tasks/${taskId}`);
116
116
  }
117
+ /**
118
+ * Get a single task by ID with optional subtasks and milestones
119
+ */
120
+ async getTaskById(taskId, params) {
121
+ return this.proxy('get_task', {
122
+ task_id: taskId,
123
+ include_subtasks: params?.include_subtasks,
124
+ include_milestones: params?.include_milestones,
125
+ });
126
+ }
127
+ /**
128
+ * Search tasks by text query with pagination
129
+ */
130
+ async searchTasks(projectId, params) {
131
+ return this.proxy('search_tasks', {
132
+ project_id: projectId,
133
+ query: params.query,
134
+ status: params.status,
135
+ limit: params.limit,
136
+ offset: params.offset,
137
+ });
138
+ }
139
+ /**
140
+ * Get tasks filtered by priority with pagination
141
+ */
142
+ async getTasksByPriority(projectId, params) {
143
+ return this.proxy('get_tasks_by_priority', {
144
+ project_id: projectId,
145
+ priority: params?.priority,
146
+ priority_max: params?.priority_max,
147
+ status: params?.status,
148
+ limit: params?.limit,
149
+ offset: params?.offset,
150
+ });
151
+ }
152
+ /**
153
+ * Get recent tasks (newest or oldest) with pagination
154
+ */
155
+ async getRecentTasks(projectId, params) {
156
+ return this.proxy('get_recent_tasks', {
157
+ project_id: projectId,
158
+ order: params?.order,
159
+ status: params?.status,
160
+ limit: params?.limit,
161
+ offset: params?.offset,
162
+ });
163
+ }
164
+ /**
165
+ * Get task statistics for a project
166
+ */
167
+ async getTaskStats(projectId) {
168
+ return this.proxy('get_task_stats', {
169
+ project_id: projectId,
170
+ });
171
+ }
117
172
  async updateTask(taskId, updates) {
118
173
  return this.request('PATCH', `/api/mcp/tasks/${taskId}`, updates);
119
174
  }
@@ -173,6 +228,11 @@ export class VibescopeApiClient {
173
228
  async deleteBlocker(blockerId) {
174
229
  return this.proxy('delete_blocker', { blocker_id: blockerId });
175
230
  }
231
+ async getBlockersStats(projectId) {
232
+ return this.proxy('get_blockers_stats', {
233
+ project_id: projectId
234
+ });
235
+ }
176
236
  // ============================================================================
177
237
  // Decision endpoints
178
238
  // ============================================================================
@@ -188,6 +248,11 @@ export class VibescopeApiClient {
188
248
  async deleteDecision(decisionId) {
189
249
  return this.proxy('delete_decision', { decision_id: decisionId });
190
250
  }
251
+ async getDecisionsStats(projectId) {
252
+ return this.proxy('get_decisions_stats', {
253
+ project_id: projectId
254
+ });
255
+ }
191
256
  // ============================================================================
192
257
  // Idea endpoints
193
258
  // ============================================================================
@@ -274,8 +339,12 @@ export class VibescopeApiClient {
274
339
  // ============================================================================
275
340
  // Request endpoints
276
341
  // ============================================================================
277
- async getPendingRequests(projectId, sessionId) {
278
- return this.proxy('get_pending_requests', { project_id: projectId }, sessionId ? {
342
+ async getPendingRequests(projectId, sessionId, limit, offset) {
343
+ return this.proxy('get_pending_requests', {
344
+ project_id: projectId,
345
+ ...(limit !== undefined && { limit }),
346
+ ...(offset !== undefined && { offset }),
347
+ }, sessionId ? {
279
348
  session_id: sessionId,
280
349
  persona: null,
281
350
  instance_id: ''
@@ -334,8 +403,11 @@ export class VibescopeApiClient {
334
403
  ...params
335
404
  });
336
405
  }
337
- async getActivitySchedules(projectId) {
338
- return this.proxy('get_activity_schedules', { project_id: projectId });
406
+ async getActivitySchedules(projectId, params) {
407
+ return this.proxy('get_activity_schedules', {
408
+ project_id: projectId,
409
+ ...params
410
+ });
339
411
  }
340
412
  // ============================================================================
341
413
  // Subtask endpoints
@@ -439,6 +511,16 @@ export class VibescopeApiClient {
439
511
  ...params
440
512
  });
441
513
  }
514
+ /**
515
+ * Report actual API token usage for accurate cost tracking.
516
+ * This records token usage to the session and attributes cost to the current task.
517
+ */
518
+ async reportTokenUsage(sessionId, params) {
519
+ return this.proxy('report_token_usage', {
520
+ session_id: sessionId,
521
+ ...params
522
+ });
523
+ }
442
524
  // ============================================================================
443
525
  // Organization endpoints
444
526
  // ============================================================================
@@ -653,6 +735,11 @@ export class VibescopeApiClient {
653
735
  ...params
654
736
  });
655
737
  }
738
+ async getDeploymentRequirementsStats(projectId) {
739
+ return this.proxy('get_deployment_requirements_stats', {
740
+ project_id: projectId
741
+ });
742
+ }
656
743
  async completeDeploymentRequirement(requirementId) {
657
744
  return this.proxy('complete_deployment_requirement', { requirement_id: requirementId });
658
745
  }
@@ -662,10 +749,12 @@ export class VibescopeApiClient {
662
749
  ...params
663
750
  });
664
751
  }
665
- async getScheduledDeployments(projectId, includeDisabled) {
752
+ async getScheduledDeployments(projectId, params) {
666
753
  return this.proxy('get_scheduled_deployments', {
667
754
  project_id: projectId,
668
- include_disabled: includeDisabled
755
+ include_disabled: params?.includeDisabled,
756
+ limit: params?.limit,
757
+ offset: params?.offset
669
758
  });
670
759
  }
671
760
  async updateScheduledDeployment(scheduleId, updates) {
@@ -696,6 +785,9 @@ export class VibescopeApiClient {
696
785
  readme_content: readmeContent
697
786
  });
698
787
  }
788
+ async getProjectSummary(projectId) {
789
+ return this.proxy('get_project_summary', { project_id: projectId });
790
+ }
699
791
  // ============================================================================
700
792
  // Help Topics (database-backed)
701
793
  // ============================================================================
@@ -735,11 +827,21 @@ export class VibescopeApiClient {
735
827
  async abandonCheckout(params) {
736
828
  return this.proxy('abandon_checkout', params);
737
829
  }
830
+ async getFileCheckoutsStats(projectId) {
831
+ return this.proxy('get_file_checkouts_stats', { project_id: projectId });
832
+ }
738
833
  // ============================================================================
739
834
  // Worktree Management
740
835
  // ============================================================================
741
- async getStaleWorktrees(projectId) {
742
- return this.request('GET', `/api/mcp/worktrees/stale?project_id=${projectId}`);
836
+ async getStaleWorktrees(projectId, params) {
837
+ const queryParams = new URLSearchParams({ project_id: projectId });
838
+ if (params?.hostname !== undefined)
839
+ queryParams.set('hostname', params.hostname);
840
+ if (params?.limit !== undefined)
841
+ queryParams.set('limit', String(params.limit));
842
+ if (params?.offset !== undefined)
843
+ queryParams.set('offset', String(params.offset));
844
+ return this.request('GET', `/api/mcp/worktrees/stale?${queryParams.toString()}`);
743
845
  }
744
846
  async clearWorktreePath(taskId) {
745
847
  return this.request('PATCH', `/api/mcp/tasks/${taskId}`, { worktree_path: null });
@@ -777,6 +879,19 @@ export class VibescopeApiClient {
777
879
  async getConnectorEvents(params) {
778
880
  return this.proxy('get_connector_events', params);
779
881
  }
882
+ // ============================================================================
883
+ // Agent onboarding endpoints
884
+ // ============================================================================
885
+ /**
886
+ * Confirm that agent setup is complete for a project.
887
+ * This marks the agent type as onboarded so future sessions don't receive setup instructions.
888
+ */
889
+ async confirmAgentSetup(projectId, agentType) {
890
+ return this.proxy('confirm_agent_setup', {
891
+ project_id: projectId,
892
+ agent_type: agentType
893
+ });
894
+ }
780
895
  }
781
896
  // Singleton instance
782
897
  let apiClient = null;
@@ -10,8 +10,19 @@
10
10
  import type { Handler, HandlerRegistry } from './types.js';
11
11
  export declare const addBlocker: Handler;
12
12
  export declare const resolveBlocker: Handler;
13
+ /**
14
+ * Get a single blocker by ID.
15
+ * More token-efficient than get_blockers when you need details for a specific blocker.
16
+ */
17
+ export declare const getBlocker: Handler;
13
18
  export declare const getBlockers: Handler;
14
19
  export declare const deleteBlocker: Handler;
20
+ /**
21
+ * Get aggregate statistics about blockers for a project.
22
+ * Returns total count and breakdown by status without the actual blocker data.
23
+ * This is much more token-efficient than get_blockers for understanding the overall state.
24
+ */
25
+ export declare const getBlockersStats: Handler;
15
26
  /**
16
27
  * Blockers handlers registry
17
28
  */
@@ -19,16 +19,22 @@ const resolveBlockerSchema = {
19
19
  blocker_id: { type: 'string', required: true, validate: uuidValidator },
20
20
  resolution_note: { type: 'string' },
21
21
  };
22
+ const getBlockerSchema = {
23
+ blocker_id: { type: 'string', required: true, validate: uuidValidator },
24
+ };
22
25
  const getBlockersSchema = {
23
26
  project_id: { type: 'string', required: true, validate: uuidValidator },
24
27
  status: { type: 'string', default: 'open', validate: createEnumValidator(VALID_BLOCKER_STATUSES) },
25
- limit: { type: 'number', default: 50 },
28
+ limit: { type: 'number', default: 10 },
26
29
  offset: { type: 'number', default: 0 },
27
30
  search_query: { type: 'string' },
28
31
  };
29
32
  const deleteBlockerSchema = {
30
33
  blocker_id: { type: 'string', required: true, validate: uuidValidator },
31
34
  };
35
+ const getBlockersStatsSchema = {
36
+ project_id: { type: 'string', required: true, validate: uuidValidator },
37
+ };
32
38
  export const addBlocker = async (args, ctx) => {
33
39
  const { project_id, description } = parseArgs(args, addBlockerSchema);
34
40
  const apiClient = getApiClient();
@@ -47,12 +53,25 @@ export const resolveBlocker = async (args, _ctx) => {
47
53
  }
48
54
  return success(response.data);
49
55
  };
56
+ /**
57
+ * Get a single blocker by ID.
58
+ * More token-efficient than get_blockers when you need details for a specific blocker.
59
+ */
60
+ export const getBlocker = async (args, _ctx) => {
61
+ const { blocker_id } = parseArgs(args, getBlockerSchema);
62
+ const apiClient = getApiClient();
63
+ const response = await apiClient.proxy('get_blocker', { blocker_id });
64
+ if (!response.ok) {
65
+ return error(response.error || 'Failed to get blocker');
66
+ }
67
+ return success(response.data);
68
+ };
50
69
  export const getBlockers = async (args, _ctx) => {
51
70
  const { project_id, status, limit, offset, search_query } = parseArgs(args, getBlockersSchema);
52
71
  const apiClient = getApiClient();
53
72
  const response = await apiClient.getBlockers(project_id, {
54
73
  status,
55
- limit,
74
+ limit: Math.min(limit ?? 10, 200),
56
75
  offset,
57
76
  search_query
58
77
  });
@@ -70,12 +89,28 @@ export const deleteBlocker = async (args, _ctx) => {
70
89
  }
71
90
  return success(response.data);
72
91
  };
92
+ /**
93
+ * Get aggregate statistics about blockers for a project.
94
+ * Returns total count and breakdown by status without the actual blocker data.
95
+ * This is much more token-efficient than get_blockers for understanding the overall state.
96
+ */
97
+ export const getBlockersStats = async (args, _ctx) => {
98
+ const { project_id } = parseArgs(args, getBlockersStatsSchema);
99
+ const apiClient = getApiClient();
100
+ const response = await apiClient.getBlockersStats(project_id);
101
+ if (!response.ok) {
102
+ return error(response.error || 'Failed to get blockers stats');
103
+ }
104
+ return success(response.data);
105
+ };
73
106
  /**
74
107
  * Blockers handlers registry
75
108
  */
76
109
  export const blockerHandlers = {
77
110
  add_blocker: addBlocker,
78
111
  resolve_blocker: resolveBlocker,
112
+ get_blocker: getBlocker,
79
113
  get_blockers: getBlockers,
114
+ get_blockers_stats: getBlockersStats,
80
115
  delete_blocker: deleteBlocker,
81
116
  };
@@ -30,6 +30,8 @@ export declare const addTaskDependency: Handler;
30
30
  export declare const removeTaskDependency: Handler;
31
31
  export declare const getTaskDependencies: Handler;
32
32
  export declare const getNextBodyOfWorkTask: Handler;
33
+ export declare const archiveBodyOfWork: Handler;
34
+ export declare const unarchiveBodyOfWork: Handler;
33
35
  /**
34
36
  * Bodies of Work handlers registry
35
37
  */
@@ -19,7 +19,7 @@
19
19
  */
20
20
  import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
21
21
  import { getApiClient } from '../api-client.js';
22
- const BODY_OF_WORK_STATUSES = ['draft', 'active', 'completed', 'cancelled'];
22
+ const BODY_OF_WORK_STATUSES = ['draft', 'active', 'completed', 'cancelled', 'archived'];
23
23
  const TASK_PHASES = ['pre', 'core', 'post'];
24
24
  const DEPLOY_ENVIRONMENTS = ['development', 'staging', 'production'];
25
25
  const VERSION_BUMPS = ['patch', 'minor', 'major'];
@@ -259,6 +259,33 @@ export const getNextBodyOfWorkTask = async (args, ctx) => {
259
259
  }
260
260
  return { result: response.data };
261
261
  };
262
+ // ============================================================================
263
+ // Archive / Unarchive Handlers
264
+ // ============================================================================
265
+ const archiveBodyOfWorkSchema = {
266
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
267
+ };
268
+ const unarchiveBodyOfWorkSchema = {
269
+ body_of_work_id: { type: 'string', required: true, validate: uuidValidator },
270
+ };
271
+ export const archiveBodyOfWork = async (args, ctx) => {
272
+ const { body_of_work_id } = parseArgs(args, archiveBodyOfWorkSchema);
273
+ const apiClient = getApiClient();
274
+ const response = await apiClient.proxy('archive_body_of_work', { body_of_work_id });
275
+ if (!response.ok) {
276
+ return { result: { error: response.error || 'Failed to archive body of work' }, isError: true };
277
+ }
278
+ return { result: response.data };
279
+ };
280
+ export const unarchiveBodyOfWork = async (args, ctx) => {
281
+ const { body_of_work_id } = parseArgs(args, unarchiveBodyOfWorkSchema);
282
+ const apiClient = getApiClient();
283
+ const response = await apiClient.proxy('unarchive_body_of_work', { body_of_work_id });
284
+ if (!response.ok) {
285
+ return { result: { error: response.error || 'Failed to unarchive body of work' }, isError: true };
286
+ }
287
+ return { result: response.data };
288
+ };
262
289
  /**
263
290
  * Bodies of Work handlers registry
264
291
  */
@@ -275,4 +302,6 @@ export const bodiesOfWorkHandlers = {
275
302
  remove_task_dependency: removeTaskDependency,
276
303
  get_task_dependencies: getTaskDependencies,
277
304
  get_next_body_of_work_task: getNextBodyOfWorkTask,
305
+ archive_body_of_work: archiveBodyOfWork,
306
+ unarchive_body_of_work: unarchiveBodyOfWork,
278
307
  };
@@ -68,7 +68,7 @@ export const getConnectors = async (args, _ctx) => {
68
68
  const response = await apiClient.getConnectors(project_id, {
69
69
  type,
70
70
  status,
71
- limit,
71
+ limit: Math.min(limit ?? 50, 50),
72
72
  offset
73
73
  });
74
74
  if (!response.ok) {
@@ -161,7 +161,7 @@ export const getConnectorEvents = async (args, _ctx) => {
161
161
  connector_id,
162
162
  project_id,
163
163
  status,
164
- limit,
164
+ limit: Math.min(limit ?? 50, 50),
165
165
  offset
166
166
  });
167
167
  if (!response.ok) {
@@ -10,8 +10,19 @@
10
10
  */
11
11
  import type { Handler, HandlerRegistry } from './types.js';
12
12
  export declare const logDecision: Handler;
13
+ /**
14
+ * Get a single decision by ID.
15
+ * More token-efficient than get_decisions when you need details for a specific decision.
16
+ */
17
+ export declare const getDecision: Handler;
13
18
  export declare const getDecisions: Handler;
14
19
  export declare const deleteDecision: Handler;
20
+ /**
21
+ * Get aggregate statistics about decisions for a project.
22
+ * Returns total count without the actual decision data.
23
+ * This is more token-efficient than get_decisions for understanding overall state.
24
+ */
25
+ export declare const getDecisionsStats: Handler;
15
26
  /**
16
27
  * Decisions handlers registry
17
28
  */
@@ -18,15 +18,21 @@ const logDecisionSchema = {
18
18
  rationale: { type: 'string' },
19
19
  alternatives_considered: { type: 'array' },
20
20
  };
21
+ const getDecisionSchema = {
22
+ decision_id: { type: 'string', required: true, validate: uuidValidator },
23
+ };
21
24
  const getDecisionsSchema = {
22
25
  project_id: { type: 'string', required: true, validate: uuidValidator },
23
- limit: { type: 'number', default: 50 },
26
+ limit: { type: 'number', default: 10 },
24
27
  offset: { type: 'number', default: 0 },
25
28
  search_query: { type: 'string' },
26
29
  };
27
30
  const deleteDecisionSchema = {
28
31
  decision_id: { type: 'string', required: true, validate: uuidValidator },
29
32
  };
33
+ const getDecisionsStatsSchema = {
34
+ project_id: { type: 'string', required: true, validate: uuidValidator },
35
+ };
30
36
  export const logDecision = async (args, ctx) => {
31
37
  const { project_id, title, description, rationale, alternatives_considered } = parseArgs(args, logDecisionSchema);
32
38
  const { session } = ctx;
@@ -42,11 +48,24 @@ export const logDecision = async (args, ctx) => {
42
48
  }
43
49
  return { result: { success: true, title, decision_id: response.data?.decision_id } };
44
50
  };
51
+ /**
52
+ * Get a single decision by ID.
53
+ * More token-efficient than get_decisions when you need details for a specific decision.
54
+ */
55
+ export const getDecision = async (args, _ctx) => {
56
+ const { decision_id } = parseArgs(args, getDecisionSchema);
57
+ const apiClient = getApiClient();
58
+ const response = await apiClient.proxy('get_decision', { decision_id });
59
+ if (!response.ok) {
60
+ return { result: { error: response.error || 'Failed to get decision' }, isError: true };
61
+ }
62
+ return { result: response.data };
63
+ };
45
64
  export const getDecisions = async (args, _ctx) => {
46
65
  const { project_id, limit, offset, search_query } = parseArgs(args, getDecisionsSchema);
47
66
  const apiClient = getApiClient();
48
67
  const response = await apiClient.getDecisions(project_id, {
49
- limit,
68
+ limit: Math.min(limit ?? 10, 200),
50
69
  offset,
51
70
  search_query
52
71
  });
@@ -68,11 +87,27 @@ export const deleteDecision = async (args, _ctx) => {
68
87
  }
69
88
  return { result: { success: true } };
70
89
  };
90
+ /**
91
+ * Get aggregate statistics about decisions for a project.
92
+ * Returns total count without the actual decision data.
93
+ * This is more token-efficient than get_decisions for understanding overall state.
94
+ */
95
+ export const getDecisionsStats = async (args, _ctx) => {
96
+ const { project_id } = parseArgs(args, getDecisionsStatsSchema);
97
+ const apiClient = getApiClient();
98
+ const response = await apiClient.getDecisionsStats(project_id);
99
+ if (!response.ok) {
100
+ return { result: { error: response.error || 'Failed to get decisions stats' }, isError: true };
101
+ }
102
+ return { result: response.data };
103
+ };
71
104
  /**
72
105
  * Decisions handlers registry
73
106
  */
74
107
  export const decisionHandlers = {
75
108
  log_decision: logDecision,
109
+ get_decision: getDecision,
76
110
  get_decisions: getDecisions,
111
+ get_decisions_stats: getDecisionsStats,
77
112
  delete_decision: deleteDecision,
78
113
  };
@@ -24,6 +24,12 @@ export declare const cancelDeployment: Handler;
24
24
  export declare const addDeploymentRequirement: Handler;
25
25
  export declare const completeDeploymentRequirement: Handler;
26
26
  export declare const getDeploymentRequirements: Handler;
27
+ /**
28
+ * Get aggregate statistics about deployment requirements for a project.
29
+ * Returns total count and breakdowns by status, stage, and type.
30
+ * More token-efficient than get_deployment_requirements when you just need to understand the overall state.
31
+ */
32
+ export declare const getDeploymentRequirementsStats: Handler;
27
33
  export declare const scheduleDeployment: Handler;
28
34
  export declare const getScheduledDeployments: Handler;
29
35
  export declare const updateScheduledDeployment: Handler;
@@ -63,7 +63,7 @@ const addDeploymentRequirementSchema = {
63
63
  file_path: { type: 'string' },
64
64
  stage: { type: 'string', default: 'preparation', validate: createEnumValidator(REQUIREMENT_STAGES) },
65
65
  blocking: { type: 'boolean', default: false },
66
- recurring: { type: 'boolean', default: false },
66
+ recurring: { type: 'boolean', default: true },
67
67
  };
68
68
  const completeDeploymentRequirementSchema = {
69
69
  requirement_id: { type: 'string', required: true, validate: uuidValidator },
@@ -72,6 +72,11 @@ const getDeploymentRequirementsSchema = {
72
72
  project_id: { type: 'string', required: true, validate: uuidValidator },
73
73
  status: { type: 'string', default: 'pending', validate: createEnumValidator(REQUIREMENT_STATUSES) },
74
74
  stage: { type: 'string', validate: createEnumValidator([...REQUIREMENT_STAGES, 'all']) },
75
+ limit: { type: 'number', default: 50 },
76
+ offset: { type: 'number', default: 0 },
77
+ };
78
+ const getDeploymentRequirementsStatsSchema = {
79
+ project_id: { type: 'string', required: true, validate: uuidValidator },
75
80
  };
76
81
  const scheduleDeploymentSchema = {
77
82
  project_id: { type: 'string', required: true, validate: uuidValidator },
@@ -87,6 +92,8 @@ const scheduleDeploymentSchema = {
87
92
  const getScheduledDeploymentsSchema = {
88
93
  project_id: { type: 'string', required: true, validate: uuidValidator },
89
94
  include_disabled: { type: 'boolean', default: false },
95
+ limit: { type: 'number', default: 50 },
96
+ offset: { type: 'number', default: 0 },
90
97
  };
91
98
  const updateScheduledDeploymentSchema = {
92
99
  schedule_id: { type: 'string', required: true, validate: uuidValidator },
@@ -216,17 +223,33 @@ export const completeDeploymentRequirement = async (args, ctx) => {
216
223
  return { result: response.data };
217
224
  };
218
225
  export const getDeploymentRequirements = async (args, ctx) => {
219
- const { project_id, status, stage } = parseArgs(args, getDeploymentRequirementsSchema);
226
+ const { project_id, status, stage, limit, offset } = parseArgs(args, getDeploymentRequirementsSchema);
220
227
  const apiClient = getApiClient();
221
228
  const response = await apiClient.getDeploymentRequirements(project_id, {
222
229
  status: status,
223
- stage: stage
230
+ stage: stage,
231
+ limit,
232
+ offset
224
233
  });
225
234
  if (!response.ok) {
226
235
  return { result: { error: response.error || 'Failed to get deployment requirements' }, isError: true };
227
236
  }
228
237
  return { result: response.data };
229
238
  };
239
+ /**
240
+ * Get aggregate statistics about deployment requirements for a project.
241
+ * Returns total count and breakdowns by status, stage, and type.
242
+ * More token-efficient than get_deployment_requirements when you just need to understand the overall state.
243
+ */
244
+ export const getDeploymentRequirementsStats = async (args, ctx) => {
245
+ const { project_id } = parseArgs(args, getDeploymentRequirementsStatsSchema);
246
+ const apiClient = getApiClient();
247
+ const response = await apiClient.getDeploymentRequirementsStats(project_id);
248
+ if (!response.ok) {
249
+ return { result: { error: response.error || 'Failed to get deployment requirements stats' }, isError: true };
250
+ }
251
+ return { result: response.data };
252
+ };
230
253
  // ============================================================================
231
254
  // Scheduled Deployments
232
255
  // ============================================================================
@@ -269,9 +292,13 @@ export const scheduleDeployment = async (args, ctx) => {
269
292
  return { result: response.data };
270
293
  };
271
294
  export const getScheduledDeployments = async (args, ctx) => {
272
- const { project_id, include_disabled } = parseArgs(args, getScheduledDeploymentsSchema);
295
+ const { project_id, include_disabled, limit, offset } = parseArgs(args, getScheduledDeploymentsSchema);
273
296
  const apiClient = getApiClient();
274
- const response = await apiClient.getScheduledDeployments(project_id, include_disabled);
297
+ const response = await apiClient.getScheduledDeployments(project_id, {
298
+ includeDisabled: include_disabled,
299
+ limit,
300
+ offset
301
+ });
275
302
  if (!response.ok) {
276
303
  return { result: { error: response.error || 'Failed to get scheduled deployments' }, isError: true };
277
304
  }
@@ -359,6 +386,7 @@ export const deploymentHandlers = {
359
386
  add_deployment_requirement: addDeploymentRequirement,
360
387
  complete_deployment_requirement: completeDeploymentRequirement,
361
388
  get_deployment_requirements: getDeploymentRequirements,
389
+ get_deployment_requirements_stats: getDeploymentRequirementsStats,
362
390
  // Scheduled deployments
363
391
  schedule_deployment: scheduleDeployment,
364
392
  get_scheduled_deployments: getScheduledDeployments,