@vibescope/mcp-server 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +1 -1
  2. package/dist/api-client.d.ts +120 -2
  3. package/dist/api-client.js +51 -5
  4. package/dist/handlers/bodies-of-work.js +84 -50
  5. package/dist/handlers/cost.js +62 -54
  6. package/dist/handlers/decisions.js +29 -16
  7. package/dist/handlers/deployment.js +114 -107
  8. package/dist/handlers/discovery.d.ts +3 -0
  9. package/dist/handlers/discovery.js +55 -657
  10. package/dist/handlers/fallback.js +42 -28
  11. package/dist/handlers/file-checkouts.d.ts +18 -0
  12. package/dist/handlers/file-checkouts.js +101 -0
  13. package/dist/handlers/findings.d.ts +14 -1
  14. package/dist/handlers/findings.js +104 -28
  15. package/dist/handlers/git-issues.js +36 -32
  16. package/dist/handlers/ideas.js +44 -26
  17. package/dist/handlers/index.d.ts +2 -0
  18. package/dist/handlers/index.js +6 -0
  19. package/dist/handlers/milestones.js +34 -27
  20. package/dist/handlers/organizations.js +86 -78
  21. package/dist/handlers/progress.js +22 -11
  22. package/dist/handlers/project.js +62 -22
  23. package/dist/handlers/requests.js +15 -11
  24. package/dist/handlers/roles.d.ts +18 -0
  25. package/dist/handlers/roles.js +130 -0
  26. package/dist/handlers/session.js +52 -15
  27. package/dist/handlers/sprints.js +78 -65
  28. package/dist/handlers/tasks.js +135 -74
  29. package/dist/handlers/tool-docs.d.ts +4 -3
  30. package/dist/handlers/tool-docs.js +252 -5
  31. package/dist/handlers/validation.js +30 -14
  32. package/dist/index.js +25 -7
  33. package/dist/tools.js +417 -4
  34. package/package.json +1 -1
  35. package/src/api-client.ts +161 -8
  36. package/src/handlers/__test-setup__.ts +12 -0
  37. package/src/handlers/bodies-of-work.ts +127 -111
  38. package/src/handlers/cost.test.ts +34 -44
  39. package/src/handlers/cost.ts +77 -92
  40. package/src/handlers/decisions.test.ts +3 -2
  41. package/src/handlers/decisions.ts +32 -27
  42. package/src/handlers/deployment.ts +144 -190
  43. package/src/handlers/discovery.test.ts +4 -5
  44. package/src/handlers/discovery.ts +60 -746
  45. package/src/handlers/fallback.test.ts +78 -0
  46. package/src/handlers/fallback.ts +51 -38
  47. package/src/handlers/file-checkouts.test.ts +477 -0
  48. package/src/handlers/file-checkouts.ts +127 -0
  49. package/src/handlers/findings.test.ts +274 -2
  50. package/src/handlers/findings.ts +123 -57
  51. package/src/handlers/git-issues.ts +40 -80
  52. package/src/handlers/ideas.ts +56 -54
  53. package/src/handlers/index.ts +6 -0
  54. package/src/handlers/milestones.test.ts +1 -1
  55. package/src/handlers/milestones.ts +47 -45
  56. package/src/handlers/organizations.ts +104 -129
  57. package/src/handlers/progress.ts +24 -22
  58. package/src/handlers/project.ts +89 -57
  59. package/src/handlers/requests.ts +18 -14
  60. package/src/handlers/roles.test.ts +303 -0
  61. package/src/handlers/roles.ts +208 -0
  62. package/src/handlers/session.test.ts +37 -2
  63. package/src/handlers/session.ts +64 -21
  64. package/src/handlers/sprints.ts +114 -134
  65. package/src/handlers/tasks.test.ts +61 -0
  66. package/src/handlers/tasks.ts +170 -139
  67. package/src/handlers/tool-docs.ts +1024 -0
  68. package/src/handlers/validation.test.ts +53 -1
  69. package/src/handlers/validation.ts +32 -21
  70. package/src/index.ts +25 -7
  71. package/src/tools.ts +417 -4
  72. package/dist/config/tool-categories.d.ts +0 -31
  73. package/dist/config/tool-categories.js +0 -253
  74. package/dist/knowledge.d.ts +0 -6
  75. package/dist/knowledge.js +0 -218
  76. package/src/knowledge.ts +0 -230
package/src/api-client.ts CHANGED
@@ -47,7 +47,8 @@ export class VibescopeApiClient {
47
47
  return {
48
48
  ok: false,
49
49
  status: response.status,
50
- error: data.error || `HTTP ${response.status}`
50
+ error: data.error || `HTTP ${response.status}`,
51
+ data // Include full response data for additional error context
51
52
  };
52
53
  }
53
54
 
@@ -137,13 +138,16 @@ export class VibescopeApiClient {
137
138
  return this.request('POST', '/api/mcp/sessions/start', params);
138
139
  }
139
140
 
140
- async heartbeat(sessionId: string): Promise<ApiResponse<{
141
+ async heartbeat(sessionId: string, options?: {
142
+ current_worktree_path?: string | null;
143
+ }): Promise<ApiResponse<{
141
144
  success: boolean;
142
145
  session_id: string;
143
146
  timestamp: string;
144
147
  }>> {
145
148
  return this.request('POST', '/api/mcp/sessions/heartbeat', {
146
- session_id: sessionId
149
+ session_id: sessionId,
150
+ ...options
147
151
  });
148
152
  }
149
153
 
@@ -303,6 +307,7 @@ export class VibescopeApiClient {
303
307
  estimated_minutes?: number;
304
308
  blocking?: boolean;
305
309
  session_id?: string;
310
+ task_type?: string;
306
311
  }): Promise<ApiResponse<{
307
312
  success: boolean;
308
313
  task_id: string;
@@ -381,6 +386,20 @@ export class VibescopeApiClient {
381
386
  }): Promise<ApiResponse<{
382
387
  success: boolean;
383
388
  task_id: string;
389
+ git_workflow?: {
390
+ workflow: string;
391
+ base_branch: string;
392
+ suggested_branch: string;
393
+ worktree_required: boolean;
394
+ };
395
+ worktree_setup?: {
396
+ message: string;
397
+ commands: string[];
398
+ worktree_path: string;
399
+ branch_name: string;
400
+ cleanup_command: string;
401
+ };
402
+ next_step?: string;
384
403
  }>> {
385
404
  return this.request('PATCH', `/api/mcp/tasks/${taskId}`, updates);
386
405
  }
@@ -405,6 +424,7 @@ export class VibescopeApiClient {
405
424
  deployment?: string;
406
425
  };
407
426
  next_action: string;
427
+ warnings?: string[];
408
428
  }>> {
409
429
  return this.request('POST', `/api/mcp/tasks/${taskId}/complete`, params);
410
430
  }
@@ -521,7 +541,11 @@ export class VibescopeApiClient {
521
541
  // ============================================================================
522
542
  // Decision endpoints
523
543
  // ============================================================================
524
- async getDecisions(projectId: string): Promise<ApiResponse<{
544
+ async getDecisions(projectId: string, options?: {
545
+ limit?: number;
546
+ offset?: number;
547
+ search_query?: string;
548
+ }): Promise<ApiResponse<{
525
549
  decisions: Array<{
526
550
  id: string;
527
551
  title: string;
@@ -531,7 +555,7 @@ export class VibescopeApiClient {
531
555
  created_at: string;
532
556
  }>;
533
557
  }>> {
534
- return this.proxy('get_decisions', { project_id: projectId });
558
+ return this.proxy('get_decisions', { project_id: projectId, ...options });
535
559
  }
536
560
 
537
561
  async logDecision(projectId: string, params: {
@@ -642,6 +666,9 @@ export class VibescopeApiClient {
642
666
  severity?: string;
643
667
  status?: string;
644
668
  limit?: number;
669
+ offset?: number;
670
+ search_query?: string;
671
+ summary_only?: boolean;
645
672
  }): Promise<ApiResponse<{
646
673
  findings: Array<{
647
674
  id: string;
@@ -655,6 +682,8 @@ export class VibescopeApiClient {
655
682
  resolution_note?: string;
656
683
  created_at: string;
657
684
  }>;
685
+ total_count?: number;
686
+ has_more?: boolean;
658
687
  }>> {
659
688
  return this.proxy('get_findings', {
660
689
  project_id: projectId,
@@ -662,6 +691,17 @@ export class VibescopeApiClient {
662
691
  });
663
692
  }
664
693
 
694
+ async getFindingsStats(projectId: string): Promise<ApiResponse<{
695
+ total: number;
696
+ by_status: Record<string, number>;
697
+ by_severity: Record<string, number>;
698
+ by_category: Record<string, number>;
699
+ }>> {
700
+ return this.proxy('get_findings_stats', {
701
+ project_id: projectId
702
+ });
703
+ }
704
+
665
705
  async addFinding(projectId: string, params: {
666
706
  title: string;
667
707
  description?: string;
@@ -839,11 +879,13 @@ export class VibescopeApiClient {
839
879
  async validateTask(taskId: string, params: {
840
880
  approved: boolean;
841
881
  validation_notes?: string;
882
+ skip_pr_check?: boolean;
842
883
  }, sessionId?: string): Promise<ApiResponse<{
843
884
  success: boolean;
844
885
  approved: boolean;
845
886
  task_id: string;
846
887
  message?: string;
888
+ workflow?: string;
847
889
  }>> {
848
890
  return this.proxy('validate_task', {
849
891
  task_id: taskId,
@@ -858,6 +900,21 @@ export class VibescopeApiClient {
858
900
  success: boolean;
859
901
  activity: string;
860
902
  message: string;
903
+ git_workflow?: {
904
+ workflow: string;
905
+ base_branch: string;
906
+ worktree_recommended: boolean;
907
+ note: string;
908
+ };
909
+ worktree_setup?: {
910
+ message: string;
911
+ commands: string[];
912
+ worktree_path: string;
913
+ branch_name: string;
914
+ cleanup_command: string;
915
+ report_worktree: string;
916
+ };
917
+ next_step?: string;
861
918
  }>> {
862
919
  return this.proxy('start_fallback_activity', {
863
920
  project_id: projectId,
@@ -954,6 +1011,8 @@ export class VibescopeApiClient {
954
1011
  async getActivityFeed(projectId: string, params?: {
955
1012
  limit?: number;
956
1013
  since?: string;
1014
+ types?: string[];
1015
+ created_by?: string;
957
1016
  }): Promise<ApiResponse<{
958
1017
  activities: Array<{
959
1018
  type: string;
@@ -1088,14 +1147,17 @@ export class VibescopeApiClient {
1088
1147
  // Knowledge base endpoint
1089
1148
  // ============================================================================
1090
1149
  async queryKnowledgeBase(projectId: string, params?: {
1150
+ scope?: 'summary' | 'detailed';
1091
1151
  categories?: string[];
1092
1152
  limit?: number;
1093
1153
  search_query?: string;
1094
1154
  }): Promise<ApiResponse<{
1095
- findings?: Array<{ id: string; title: string; category: string; severity: string }>;
1096
- decisions?: Array<{ id: string; title: string; description: string }>;
1097
- completed_tasks?: Array<{ id: string; title: string; completed_at: string }>;
1155
+ findings?: Array<{ id: string; title: string; category: string; severity: string; description?: string }>;
1156
+ decisions?: Array<{ id: string; title: string; description: string; rationale?: string }>;
1157
+ completed_tasks?: Array<{ id: string; title: string; completed_at: string; summary?: string }>;
1098
1158
  resolved_blockers?: Array<{ id: string; description: string; resolution_note?: string }>;
1159
+ progress?: Array<{ id: string; summary: string; details?: string }>;
1160
+ qa?: Array<{ id: string; question: string; answer: string }>;
1099
1161
  }>> {
1100
1162
  return this.proxy('query_knowledge_base', {
1101
1163
  project_id: projectId,
@@ -1591,6 +1653,7 @@ export class VibescopeApiClient {
1591
1653
  file_path?: string;
1592
1654
  stage?: string;
1593
1655
  blocking?: boolean;
1656
+ recurring?: boolean;
1594
1657
  }): Promise<ApiResponse<{
1595
1658
  success: boolean;
1596
1659
  requirement_id: string;
@@ -1730,6 +1793,96 @@ export class VibescopeApiClient {
1730
1793
  readme_content: readmeContent
1731
1794
  });
1732
1795
  }
1796
+
1797
+ // ============================================================================
1798
+ // Help Topics (database-backed)
1799
+ // ============================================================================
1800
+ async getHelpTopic(slug: string): Promise<ApiResponse<{
1801
+ slug: string;
1802
+ title: string;
1803
+ content: string;
1804
+ } | null>> {
1805
+ return this.proxy('get_help_topic', { slug });
1806
+ }
1807
+
1808
+ async getHelpTopics(): Promise<ApiResponse<Array<{
1809
+ slug: string;
1810
+ title: string;
1811
+ }>>> {
1812
+ return this.proxy('get_help_topics', {});
1813
+ }
1814
+
1815
+ // ============================================================================
1816
+ // File Checkout endpoints (multi-agent coordination)
1817
+ // ============================================================================
1818
+ async checkoutFile(projectId: string, filePath: string, reason?: string, sessionId?: string): Promise<ApiResponse<{
1819
+ success: boolean;
1820
+ checkout_id: string;
1821
+ file_path: string;
1822
+ already_checked_out?: boolean;
1823
+ message: string;
1824
+ }>> {
1825
+ return this.proxy('checkout_file', {
1826
+ project_id: projectId,
1827
+ file_path: filePath,
1828
+ reason
1829
+ }, sessionId ? {
1830
+ session_id: sessionId,
1831
+ persona: null,
1832
+ instance_id: ''
1833
+ } : undefined);
1834
+ }
1835
+
1836
+ async checkinFile(params: {
1837
+ checkout_id?: string;
1838
+ project_id?: string;
1839
+ file_path?: string;
1840
+ summary?: string;
1841
+ }, sessionId?: string): Promise<ApiResponse<{
1842
+ success: boolean;
1843
+ checkout_id: string;
1844
+ message: string;
1845
+ }>> {
1846
+ return this.proxy('checkin_file', params, sessionId ? {
1847
+ session_id: sessionId,
1848
+ persona: null,
1849
+ instance_id: ''
1850
+ } : undefined);
1851
+ }
1852
+
1853
+ async getFileCheckouts(projectId: string, options?: {
1854
+ status?: string;
1855
+ file_path?: string;
1856
+ limit?: number;
1857
+ }): Promise<ApiResponse<{
1858
+ checkouts: Array<{
1859
+ id: string;
1860
+ file_path: string;
1861
+ status: string;
1862
+ checked_out_at: string;
1863
+ checkout_reason?: string;
1864
+ checked_in_at?: string;
1865
+ checkin_summary?: string;
1866
+ checked_out_by?: string;
1867
+ }>;
1868
+ }>> {
1869
+ return this.proxy('get_file_checkouts', {
1870
+ project_id: projectId,
1871
+ ...options
1872
+ });
1873
+ }
1874
+
1875
+ async abandonCheckout(params: {
1876
+ checkout_id?: string;
1877
+ project_id?: string;
1878
+ file_path?: string;
1879
+ }): Promise<ApiResponse<{
1880
+ success: boolean;
1881
+ checkout_id: string;
1882
+ message: string;
1883
+ }>> {
1884
+ return this.proxy('abandon_checkout', params);
1885
+ }
1733
1886
  }
1734
1887
 
1735
1888
  // Singleton instance
@@ -77,8 +77,10 @@ export const mockApiClient = {
77
77
  // Findings
78
78
  addFinding: vi.fn(),
79
79
  getFindings: vi.fn(),
80
+ getFindingsStats: vi.fn(),
80
81
  updateFinding: vi.fn(),
81
82
  deleteFinding: vi.fn(),
83
+ queryKnowledgeBase: vi.fn(),
82
84
 
83
85
  // Requests
84
86
  getPendingRequests: vi.fn(),
@@ -164,6 +166,16 @@ export const mockApiClient = {
164
166
  claimValidation: vi.fn(),
165
167
  validateTask: vi.fn(),
166
168
 
169
+ // Help Topics
170
+ getHelpTopic: vi.fn(),
171
+ getHelpTopics: vi.fn(),
172
+
173
+ // File Checkouts
174
+ checkoutFile: vi.fn(),
175
+ checkinFile: vi.fn(),
176
+ getFileCheckouts: vi.fn(),
177
+ abandonCheckout: vi.fn(),
178
+
167
179
  // Proxy (generic)
168
180
  proxy: vi.fn(),
169
181
  };
@@ -19,14 +19,96 @@
19
19
  */
20
20
 
21
21
  import type { Handler, HandlerRegistry } from './types.js';
22
- import { validateRequired, validateUUID } from '../validators.js';
22
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
23
23
  import { getApiClient } from '../api-client.js';
24
24
 
25
- type BodyOfWorkStatus = 'draft' | 'active' | 'completed' | 'cancelled';
26
- type TaskPhase = 'pre' | 'core' | 'post';
27
- type DeployEnvironment = 'development' | 'staging' | 'production';
28
- type VersionBump = 'patch' | 'minor' | 'major';
29
- type DeployTrigger = 'all_completed' | 'all_completed_validated';
25
+ const BODY_OF_WORK_STATUSES = ['draft', 'active', 'completed', 'cancelled'] as const;
26
+ const TASK_PHASES = ['pre', 'core', 'post'] as const;
27
+ const DEPLOY_ENVIRONMENTS = ['development', 'staging', 'production'] as const;
28
+ const VERSION_BUMPS = ['patch', 'minor', 'major'] as const;
29
+ const DEPLOY_TRIGGERS = ['all_completed', 'all_completed_validated'] as const;
30
+
31
+ type BodyOfWorkStatus = typeof BODY_OF_WORK_STATUSES[number];
32
+ type TaskPhase = typeof TASK_PHASES[number];
33
+ type DeployEnvironment = typeof DEPLOY_ENVIRONMENTS[number];
34
+ type VersionBump = typeof VERSION_BUMPS[number];
35
+ type DeployTrigger = typeof DEPLOY_TRIGGERS[number];
36
+
37
+ // ============================================================================
38
+ // Argument Schemas
39
+ // ============================================================================
40
+
41
+ const createBodyOfWorkSchema = {
42
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
43
+ title: { type: 'string' as const, required: true as const },
44
+ description: { type: 'string' as const },
45
+ auto_deploy_on_completion: { type: 'boolean' as const },
46
+ deploy_environment: { type: 'string' as const, validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
47
+ deploy_version_bump: { type: 'string' as const, validate: createEnumValidator(VERSION_BUMPS) },
48
+ deploy_trigger: { type: 'string' as const, validate: createEnumValidator(DEPLOY_TRIGGERS) },
49
+ };
50
+
51
+ const updateBodyOfWorkSchema = {
52
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
53
+ title: { type: 'string' as const },
54
+ description: { type: 'string' as const },
55
+ auto_deploy_on_completion: { type: 'boolean' as const },
56
+ deploy_environment: { type: 'string' as const, validate: createEnumValidator(DEPLOY_ENVIRONMENTS) },
57
+ deploy_version_bump: { type: 'string' as const, validate: createEnumValidator(VERSION_BUMPS) },
58
+ deploy_trigger: { type: 'string' as const, validate: createEnumValidator(DEPLOY_TRIGGERS) },
59
+ };
60
+
61
+ const getBodyOfWorkSchema = {
62
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
63
+ summary_only: { type: 'boolean' as const, default: false },
64
+ };
65
+
66
+ const getBodiesOfWorkSchema = {
67
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
68
+ status: { type: 'string' as const, validate: createEnumValidator(BODY_OF_WORK_STATUSES) },
69
+ limit: { type: 'number' as const, default: 50 },
70
+ offset: { type: 'number' as const, default: 0 },
71
+ search_query: { type: 'string' as const },
72
+ };
73
+
74
+ const deleteBodyOfWorkSchema = {
75
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
76
+ };
77
+
78
+ const addTaskToBodyOfWorkSchema = {
79
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
80
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
81
+ phase: { type: 'string' as const, validate: createEnumValidator(TASK_PHASES) },
82
+ order_index: { type: 'number' as const },
83
+ };
84
+
85
+ const removeTaskFromBodyOfWorkSchema = {
86
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
87
+ };
88
+
89
+ const activateBodyOfWorkSchema = {
90
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
91
+ };
92
+
93
+ const addTaskDependencySchema = {
94
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
95
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
96
+ depends_on_task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
97
+ };
98
+
99
+ const removeTaskDependencySchema = {
100
+ task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
101
+ depends_on_task_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
102
+ };
103
+
104
+ const getTaskDependenciesSchema = {
105
+ body_of_work_id: { type: 'string' as const, validate: uuidValidator },
106
+ task_id: { type: 'string' as const, validate: uuidValidator },
107
+ };
108
+
109
+ const getNextBodyOfWorkTaskSchema = {
110
+ body_of_work_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
111
+ };
30
112
 
31
113
  export const createBodyOfWork: Handler = async (args, ctx) => {
32
114
  const {
@@ -37,19 +119,7 @@ export const createBodyOfWork: Handler = async (args, ctx) => {
37
119
  deploy_environment,
38
120
  deploy_version_bump,
39
121
  deploy_trigger,
40
- } = args as {
41
- project_id: string;
42
- title: string;
43
- description?: string;
44
- auto_deploy_on_completion?: boolean;
45
- deploy_environment?: DeployEnvironment;
46
- deploy_version_bump?: VersionBump;
47
- deploy_trigger?: DeployTrigger;
48
- };
49
-
50
- validateRequired(project_id, 'project_id');
51
- validateUUID(project_id, 'project_id');
52
- validateRequired(title, 'title');
122
+ } = parseArgs(args, createBodyOfWorkSchema);
53
123
 
54
124
  const { session } = ctx;
55
125
  const apiClient = getApiClient();
@@ -95,18 +165,7 @@ export const updateBodyOfWork: Handler = async (args, ctx) => {
95
165
  deploy_environment,
96
166
  deploy_version_bump,
97
167
  deploy_trigger,
98
- } = args as {
99
- body_of_work_id: string;
100
- title?: string;
101
- description?: string;
102
- auto_deploy_on_completion?: boolean;
103
- deploy_environment?: DeployEnvironment;
104
- deploy_version_bump?: VersionBump;
105
- deploy_trigger?: DeployTrigger;
106
- };
107
-
108
- validateRequired(body_of_work_id, 'body_of_work_id');
109
- validateUUID(body_of_work_id, 'body_of_work_id');
168
+ } = parseArgs(args, updateBodyOfWorkSchema);
110
169
 
111
170
  // Check if any updates provided
112
171
  if (title === undefined && description === undefined && auto_deploy_on_completion === undefined &&
@@ -134,24 +193,37 @@ export const updateBodyOfWork: Handler = async (args, ctx) => {
134
193
  };
135
194
 
136
195
  export const getBodyOfWork: Handler = async (args, ctx) => {
137
- const { body_of_work_id } = args as { body_of_work_id: string };
138
-
139
- validateRequired(body_of_work_id, 'body_of_work_id');
140
- validateUUID(body_of_work_id, 'body_of_work_id');
196
+ const { body_of_work_id, summary_only } = parseArgs(args, getBodyOfWorkSchema);
141
197
 
142
198
  const apiClient = getApiClient();
143
199
 
200
+ // Response type varies based on summary_only
144
201
  const response = await apiClient.proxy<{
145
- id: string;
146
- title: string;
147
- description?: string;
148
- status: string;
149
- progress_percentage: number;
150
- pre_tasks: unknown[];
151
- core_tasks: unknown[];
152
- post_tasks: unknown[];
153
- total_tasks: number;
154
- }>('get_body_of_work', { body_of_work_id });
202
+ body_of_work: {
203
+ id: string;
204
+ title: string;
205
+ description?: string;
206
+ status: string;
207
+ progress_percentage: number;
208
+ };
209
+ // Full response includes tasks grouped by phase
210
+ tasks?: {
211
+ pre: unknown[];
212
+ core: unknown[];
213
+ post: unknown[];
214
+ };
215
+ // Summary response includes counts and next task
216
+ task_counts?: {
217
+ pre: { total: number; completed: number };
218
+ core: { total: number; completed: number };
219
+ post: { total: number; completed: number };
220
+ total: number;
221
+ completed: number;
222
+ in_progress: number;
223
+ };
224
+ current_task?: { id: string; title: string } | null;
225
+ next_task?: { id: string; title: string; priority: number } | null;
226
+ }>('get_body_of_work', { body_of_work_id, summary_only });
155
227
 
156
228
  if (!response.ok) {
157
229
  throw new Error(`Failed to get body of work: ${response.error}`);
@@ -161,16 +233,7 @@ export const getBodyOfWork: Handler = async (args, ctx) => {
161
233
  };
162
234
 
163
235
  export const getBodiesOfWork: Handler = async (args, ctx) => {
164
- const { project_id, status, limit = 50, offset = 0, search_query } = args as {
165
- project_id: string;
166
- status?: BodyOfWorkStatus;
167
- limit?: number;
168
- offset?: number;
169
- search_query?: string;
170
- };
171
-
172
- validateRequired(project_id, 'project_id');
173
- validateUUID(project_id, 'project_id');
236
+ const { project_id, status, limit, offset, search_query } = parseArgs(args, getBodiesOfWorkSchema);
174
237
 
175
238
  const apiClient = getApiClient();
176
239
 
@@ -192,7 +255,7 @@ export const getBodiesOfWork: Handler = async (args, ctx) => {
192
255
  }>('get_bodies_of_work', {
193
256
  project_id,
194
257
  status,
195
- limit: Math.min(limit, 100),
258
+ limit: Math.min(limit ?? 50, 100),
196
259
  offset,
197
260
  search_query
198
261
  });
@@ -205,10 +268,7 @@ export const getBodiesOfWork: Handler = async (args, ctx) => {
205
268
  };
206
269
 
207
270
  export const deleteBodyOfWork: Handler = async (args, ctx) => {
208
- const { body_of_work_id } = args as { body_of_work_id: string };
209
-
210
- validateRequired(body_of_work_id, 'body_of_work_id');
211
- validateUUID(body_of_work_id, 'body_of_work_id');
271
+ const { body_of_work_id } = parseArgs(args, deleteBodyOfWorkSchema);
212
272
 
213
273
  const apiClient = getApiClient();
214
274
 
@@ -224,17 +284,7 @@ export const deleteBodyOfWork: Handler = async (args, ctx) => {
224
284
  };
225
285
 
226
286
  export const addTaskToBodyOfWork: Handler = async (args, ctx) => {
227
- const { body_of_work_id, task_id, phase, order_index } = args as {
228
- body_of_work_id: string;
229
- task_id: string;
230
- phase?: TaskPhase;
231
- order_index?: number;
232
- };
233
-
234
- validateRequired(body_of_work_id, 'body_of_work_id');
235
- validateUUID(body_of_work_id, 'body_of_work_id');
236
- validateRequired(task_id, 'task_id');
237
- validateUUID(task_id, 'task_id');
287
+ const { body_of_work_id, task_id, phase, order_index } = parseArgs(args, addTaskToBodyOfWorkSchema);
238
288
 
239
289
  const apiClient = getApiClient();
240
290
 
@@ -259,10 +309,7 @@ export const addTaskToBodyOfWork: Handler = async (args, ctx) => {
259
309
  };
260
310
 
261
311
  export const removeTaskFromBodyOfWork: Handler = async (args, ctx) => {
262
- const { task_id } = args as { task_id: string };
263
-
264
- validateRequired(task_id, 'task_id');
265
- validateUUID(task_id, 'task_id');
312
+ const { task_id } = parseArgs(args, removeTaskFromBodyOfWorkSchema);
266
313
 
267
314
  const apiClient = getApiClient();
268
315
 
@@ -280,10 +327,7 @@ export const removeTaskFromBodyOfWork: Handler = async (args, ctx) => {
280
327
  };
281
328
 
282
329
  export const activateBodyOfWork: Handler = async (args, ctx) => {
283
- const { body_of_work_id } = args as { body_of_work_id: string };
284
-
285
- validateRequired(body_of_work_id, 'body_of_work_id');
286
- validateUUID(body_of_work_id, 'body_of_work_id');
330
+ const { body_of_work_id } = parseArgs(args, activateBodyOfWorkSchema);
287
331
 
288
332
  const apiClient = getApiClient();
289
333
 
@@ -303,18 +347,7 @@ export const activateBodyOfWork: Handler = async (args, ctx) => {
303
347
  };
304
348
 
305
349
  export const addTaskDependency: Handler = async (args, ctx) => {
306
- const { body_of_work_id, task_id, depends_on_task_id } = args as {
307
- body_of_work_id: string;
308
- task_id: string;
309
- depends_on_task_id: string;
310
- };
311
-
312
- validateRequired(body_of_work_id, 'body_of_work_id');
313
- validateUUID(body_of_work_id, 'body_of_work_id');
314
- validateRequired(task_id, 'task_id');
315
- validateUUID(task_id, 'task_id');
316
- validateRequired(depends_on_task_id, 'depends_on_task_id');
317
- validateUUID(depends_on_task_id, 'depends_on_task_id');
350
+ const { body_of_work_id, task_id, depends_on_task_id } = parseArgs(args, addTaskDependencySchema);
318
351
 
319
352
  if (task_id === depends_on_task_id) {
320
353
  throw new Error('A task cannot depend on itself');
@@ -342,15 +375,7 @@ export const addTaskDependency: Handler = async (args, ctx) => {
342
375
  };
343
376
 
344
377
  export const removeTaskDependency: Handler = async (args, ctx) => {
345
- const { task_id, depends_on_task_id } = args as {
346
- task_id: string;
347
- depends_on_task_id: string;
348
- };
349
-
350
- validateRequired(task_id, 'task_id');
351
- validateUUID(task_id, 'task_id');
352
- validateRequired(depends_on_task_id, 'depends_on_task_id');
353
- validateUUID(depends_on_task_id, 'depends_on_task_id');
378
+ const { task_id, depends_on_task_id } = parseArgs(args, removeTaskDependencySchema);
354
379
 
355
380
  const apiClient = getApiClient();
356
381
 
@@ -371,18 +396,12 @@ export const removeTaskDependency: Handler = async (args, ctx) => {
371
396
  };
372
397
 
373
398
  export const getTaskDependencies: Handler = async (args, ctx) => {
374
- const { body_of_work_id, task_id } = args as {
375
- body_of_work_id?: string;
376
- task_id?: string;
377
- };
399
+ const { body_of_work_id, task_id } = parseArgs(args, getTaskDependenciesSchema);
378
400
 
379
401
  if (!body_of_work_id && !task_id) {
380
402
  throw new Error('Either body_of_work_id or task_id is required');
381
403
  }
382
404
 
383
- if (body_of_work_id) validateUUID(body_of_work_id, 'body_of_work_id');
384
- if (task_id) validateUUID(task_id, 'task_id');
385
-
386
405
  const apiClient = getApiClient();
387
406
 
388
407
  const response = await apiClient.proxy<{
@@ -405,10 +424,7 @@ export const getTaskDependencies: Handler = async (args, ctx) => {
405
424
  };
406
425
 
407
426
  export const getNextBodyOfWorkTask: Handler = async (args, ctx) => {
408
- const { body_of_work_id } = args as { body_of_work_id: string };
409
-
410
- validateRequired(body_of_work_id, 'body_of_work_id');
411
- validateUUID(body_of_work_id, 'body_of_work_id');
427
+ const { body_of_work_id } = parseArgs(args, getNextBodyOfWorkTaskSchema);
412
428
 
413
429
  const apiClient = getApiClient();
414
430