@vibescope/mcp-server 0.2.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 (65) hide show
  1. package/dist/api-client.d.ts +64 -1
  2. package/dist/api-client.js +34 -3
  3. package/dist/handlers/bodies-of-work.js +82 -49
  4. package/dist/handlers/cost.js +62 -54
  5. package/dist/handlers/decisions.js +29 -16
  6. package/dist/handlers/deployment.js +112 -106
  7. package/dist/handlers/discovery.js +35 -5
  8. package/dist/handlers/fallback.js +24 -19
  9. package/dist/handlers/file-checkouts.d.ts +18 -0
  10. package/dist/handlers/file-checkouts.js +101 -0
  11. package/dist/handlers/findings.d.ts +6 -0
  12. package/dist/handlers/findings.js +85 -30
  13. package/dist/handlers/git-issues.js +36 -32
  14. package/dist/handlers/ideas.js +44 -26
  15. package/dist/handlers/index.d.ts +2 -0
  16. package/dist/handlers/index.js +6 -0
  17. package/dist/handlers/milestones.js +34 -27
  18. package/dist/handlers/organizations.js +86 -78
  19. package/dist/handlers/progress.js +22 -11
  20. package/dist/handlers/project.js +62 -22
  21. package/dist/handlers/requests.js +15 -11
  22. package/dist/handlers/roles.d.ts +18 -0
  23. package/dist/handlers/roles.js +130 -0
  24. package/dist/handlers/session.js +30 -8
  25. package/dist/handlers/sprints.js +76 -64
  26. package/dist/handlers/tasks.js +113 -73
  27. package/dist/handlers/validation.js +18 -14
  28. package/dist/tools.js +387 -0
  29. package/package.json +1 -1
  30. package/src/api-client.ts +89 -6
  31. package/src/handlers/__test-setup__.ts +7 -0
  32. package/src/handlers/bodies-of-work.ts +101 -101
  33. package/src/handlers/cost.test.ts +34 -44
  34. package/src/handlers/cost.ts +77 -92
  35. package/src/handlers/decisions.test.ts +3 -2
  36. package/src/handlers/decisions.ts +32 -27
  37. package/src/handlers/deployment.ts +142 -190
  38. package/src/handlers/discovery.test.ts +4 -5
  39. package/src/handlers/discovery.ts +37 -6
  40. package/src/handlers/fallback.ts +31 -29
  41. package/src/handlers/file-checkouts.test.ts +477 -0
  42. package/src/handlers/file-checkouts.ts +127 -0
  43. package/src/handlers/findings.test.ts +145 -0
  44. package/src/handlers/findings.ts +101 -64
  45. package/src/handlers/git-issues.ts +40 -80
  46. package/src/handlers/ideas.ts +56 -54
  47. package/src/handlers/index.ts +6 -0
  48. package/src/handlers/milestones.test.ts +1 -1
  49. package/src/handlers/milestones.ts +47 -45
  50. package/src/handlers/organizations.ts +104 -129
  51. package/src/handlers/progress.ts +24 -22
  52. package/src/handlers/project.ts +89 -57
  53. package/src/handlers/requests.ts +18 -14
  54. package/src/handlers/roles.test.ts +303 -0
  55. package/src/handlers/roles.ts +208 -0
  56. package/src/handlers/session.ts +39 -17
  57. package/src/handlers/sprints.ts +96 -129
  58. package/src/handlers/tasks.ts +144 -138
  59. package/src/handlers/validation.test.ts +1 -1
  60. package/src/handlers/validation.ts +20 -22
  61. package/src/tools.ts +387 -0
  62. package/dist/config/tool-categories.d.ts +0 -31
  63. package/dist/config/tool-categories.js +0 -253
  64. package/dist/knowledge.d.ts +0 -6
  65. package/dist/knowledge.js +0 -218
@@ -5,6 +5,7 @@ import {
5
5
  getFindingsStats,
6
6
  updateFinding,
7
7
  deleteFinding,
8
+ queryKnowledgeBase,
8
9
  } from './findings.js';
9
10
  import { ValidationError } from '../validators.js';
10
11
  import { createMockContext } from './__test-utils__.js';
@@ -472,3 +473,147 @@ describe('getFindingsStats', () => {
472
473
  ).rejects.toThrow('Query failed');
473
474
  });
474
475
  });
476
+
477
+ // ============================================================================
478
+ // queryKnowledgeBase Tests
479
+ // ============================================================================
480
+
481
+ describe('queryKnowledgeBase', () => {
482
+ beforeEach(() => vi.clearAllMocks());
483
+
484
+ it('should throw error for missing project_id', async () => {
485
+ const ctx = createMockContext();
486
+
487
+ await expect(queryKnowledgeBase({}, ctx)).rejects.toThrow(ValidationError);
488
+ });
489
+
490
+ it('should throw error for invalid project_id UUID', async () => {
491
+ const ctx = createMockContext();
492
+
493
+ await expect(
494
+ queryKnowledgeBase({ project_id: 'invalid' }, ctx)
495
+ ).rejects.toThrow(ValidationError);
496
+ });
497
+
498
+ it('should query with default parameters', async () => {
499
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
500
+ ok: true,
501
+ data: {
502
+ findings: [],
503
+ decisions: [],
504
+ completed_tasks: [],
505
+ resolved_blockers: [],
506
+ },
507
+ });
508
+ const ctx = createMockContext();
509
+
510
+ const result = await queryKnowledgeBase({ project_id: VALID_UUID }, ctx);
511
+
512
+ expect(result.result).toMatchObject({
513
+ findings: [],
514
+ decisions: [],
515
+ });
516
+ expect(mockApiClient.queryKnowledgeBase).toHaveBeenCalledWith(
517
+ VALID_UUID,
518
+ expect.objectContaining({
519
+ scope: 'summary',
520
+ limit: 5,
521
+ })
522
+ );
523
+ });
524
+
525
+ it('should pass scope parameter', async () => {
526
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
527
+ ok: true,
528
+ data: { findings: [] },
529
+ });
530
+ const ctx = createMockContext();
531
+
532
+ await queryKnowledgeBase({ project_id: VALID_UUID, scope: 'detailed' }, ctx);
533
+
534
+ expect(mockApiClient.queryKnowledgeBase).toHaveBeenCalledWith(
535
+ VALID_UUID,
536
+ expect.objectContaining({ scope: 'detailed' })
537
+ );
538
+ });
539
+
540
+ it('should pass categories filter', async () => {
541
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
542
+ ok: true,
543
+ data: { findings: [], decisions: [] },
544
+ });
545
+ const ctx = createMockContext();
546
+
547
+ await queryKnowledgeBase({
548
+ project_id: VALID_UUID,
549
+ categories: ['findings', 'decisions']
550
+ }, ctx);
551
+
552
+ expect(mockApiClient.queryKnowledgeBase).toHaveBeenCalledWith(
553
+ VALID_UUID,
554
+ expect.objectContaining({
555
+ categories: ['findings', 'decisions']
556
+ })
557
+ );
558
+ });
559
+
560
+ it('should cap limit at 20', async () => {
561
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
562
+ ok: true,
563
+ data: { findings: [] },
564
+ });
565
+ const ctx = createMockContext();
566
+
567
+ await queryKnowledgeBase({ project_id: VALID_UUID, limit: 100 }, ctx);
568
+
569
+ expect(mockApiClient.queryKnowledgeBase).toHaveBeenCalledWith(
570
+ VALID_UUID,
571
+ expect.objectContaining({ limit: 20 })
572
+ );
573
+ });
574
+
575
+ it('should enforce minimum limit of 1', async () => {
576
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
577
+ ok: true,
578
+ data: { findings: [] },
579
+ });
580
+ const ctx = createMockContext();
581
+
582
+ await queryKnowledgeBase({ project_id: VALID_UUID, limit: -5 }, ctx);
583
+
584
+ expect(mockApiClient.queryKnowledgeBase).toHaveBeenCalledWith(
585
+ VALID_UUID,
586
+ expect.objectContaining({ limit: 1 })
587
+ );
588
+ });
589
+
590
+ it('should pass search_query', async () => {
591
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
592
+ ok: true,
593
+ data: { findings: [] },
594
+ });
595
+ const ctx = createMockContext();
596
+
597
+ await queryKnowledgeBase({
598
+ project_id: VALID_UUID,
599
+ search_query: 'security'
600
+ }, ctx);
601
+
602
+ expect(mockApiClient.queryKnowledgeBase).toHaveBeenCalledWith(
603
+ VALID_UUID,
604
+ expect.objectContaining({ search_query: 'security' })
605
+ );
606
+ });
607
+
608
+ it('should throw error when API call fails', async () => {
609
+ mockApiClient.queryKnowledgeBase.mockResolvedValue({
610
+ ok: false,
611
+ error: 'Query failed',
612
+ });
613
+ const ctx = createMockContext();
614
+
615
+ await expect(
616
+ queryKnowledgeBase({ project_id: VALID_UUID }, ctx)
617
+ ).rejects.toThrow('Query failed');
618
+ });
619
+ });
@@ -10,36 +10,76 @@
10
10
  */
11
11
 
12
12
  import type { Handler, HandlerRegistry } from './types.js';
13
- import { validateRequired, validateUUID } from '../validators.js';
13
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
14
14
  import { getApiClient } from '../api-client.js';
15
15
 
16
- type FindingCategory = 'performance' | 'security' | 'code_quality' | 'accessibility' | 'documentation' | 'architecture' | 'testing' | 'other';
17
- type FindingSeverity = 'info' | 'low' | 'medium' | 'high' | 'critical';
18
- type FindingStatus = 'open' | 'addressed' | 'dismissed' | 'wontfix';
16
+ const VALID_FINDING_CATEGORIES = ['performance', 'security', 'code_quality', 'accessibility', 'documentation', 'architecture', 'testing', 'other'] as const;
17
+ const VALID_FINDING_SEVERITIES = ['info', 'low', 'medium', 'high', 'critical'] as const;
18
+ const VALID_FINDING_STATUSES = ['open', 'addressed', 'dismissed', 'wontfix'] as const;
19
+
20
+ type FindingCategory = typeof VALID_FINDING_CATEGORIES[number];
21
+ type FindingSeverity = typeof VALID_FINDING_SEVERITIES[number];
22
+ type FindingStatus = typeof VALID_FINDING_STATUSES[number];
23
+
24
+ // Argument schemas for type-safe parsing
25
+ const addFindingSchema = {
26
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
27
+ title: { type: 'string' as const, required: true as const },
28
+ description: { type: 'string' as const },
29
+ category: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
30
+ severity: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
31
+ file_path: { type: 'string' as const },
32
+ line_number: { type: 'number' as const },
33
+ related_task_id: { type: 'string' as const, validate: uuidValidator },
34
+ };
35
+
36
+ const getFindingsSchema = {
37
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
38
+ category: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_CATEGORIES) },
39
+ severity: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
40
+ status: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_STATUSES) },
41
+ limit: { type: 'number' as const, default: 50 },
42
+ offset: { type: 'number' as const, default: 0 },
43
+ search_query: { type: 'string' as const },
44
+ summary_only: { type: 'boolean' as const, default: false },
45
+ };
46
+
47
+ const getFindingsStatsSchema = {
48
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
49
+ };
50
+
51
+ const updateFindingSchema = {
52
+ finding_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
53
+ title: { type: 'string' as const },
54
+ description: { type: 'string' as const },
55
+ severity: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_SEVERITIES) },
56
+ status: { type: 'string' as const, validate: createEnumValidator(VALID_FINDING_STATUSES) },
57
+ resolution_note: { type: 'string' as const },
58
+ };
59
+
60
+ const deleteFindingSchema = {
61
+ finding_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
62
+ };
63
+
64
+ const VALID_SCOPES = ['summary', 'detailed'] as const;
65
+
66
+ const queryKnowledgeBaseSchema = {
67
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
68
+ scope: { type: 'string' as const, default: 'summary', validate: createEnumValidator(VALID_SCOPES) },
69
+ categories: { type: 'array' as const },
70
+ limit: { type: 'number' as const, default: 5 },
71
+ search_query: { type: 'string' as const },
72
+ };
19
73
 
20
74
  export const addFinding: Handler = async (args, ctx) => {
21
- const { project_id, category, title, description, severity, file_path, line_number, related_task_id } = args as {
22
- project_id: string;
23
- category?: FindingCategory;
24
- title: string;
25
- description?: string;
26
- severity?: FindingSeverity;
27
- file_path?: string;
28
- line_number?: number;
29
- related_task_id?: string;
30
- };
31
-
32
- validateRequired(project_id, 'project_id');
33
- validateUUID(project_id, 'project_id');
34
- validateRequired(title, 'title');
35
- if (related_task_id) validateUUID(related_task_id, 'related_task_id');
75
+ const { project_id, title, description, category, severity, file_path, line_number, related_task_id } = parseArgs(args, addFindingSchema);
36
76
 
37
77
  const apiClient = getApiClient();
38
78
  const response = await apiClient.addFinding(project_id, {
39
79
  title,
40
80
  description,
41
- category,
42
- severity,
81
+ category: category as FindingCategory | undefined,
82
+ severity: severity as FindingSeverity | undefined,
43
83
  file_path,
44
84
  line_number,
45
85
  related_task_id
@@ -52,26 +92,14 @@ export const addFinding: Handler = async (args, ctx) => {
52
92
  return { result: response.data };
53
93
  };
54
94
 
55
- export const getFindings: Handler = async (args, ctx) => {
56
- const { project_id, category, severity, status, limit = 50, offset = 0, search_query, summary_only = false } = args as {
57
- project_id: string;
58
- category?: FindingCategory;
59
- severity?: FindingSeverity;
60
- status?: FindingStatus;
61
- limit?: number;
62
- offset?: number;
63
- search_query?: string;
64
- summary_only?: boolean;
65
- };
66
-
67
- validateRequired(project_id, 'project_id');
68
- validateUUID(project_id, 'project_id');
95
+ export const getFindings: Handler = async (args, _ctx) => {
96
+ const { project_id, category, severity, status, limit, offset, search_query, summary_only } = parseArgs(args, getFindingsSchema);
69
97
 
70
98
  const apiClient = getApiClient();
71
99
  const response = await apiClient.getFindings(project_id, {
72
- category,
73
- severity,
74
- status,
100
+ category: category as FindingCategory | undefined,
101
+ severity: severity as FindingSeverity | undefined,
102
+ status: status as FindingStatus | undefined,
75
103
  limit,
76
104
  offset,
77
105
  search_query,
@@ -90,13 +118,8 @@ export const getFindings: Handler = async (args, ctx) => {
90
118
  * Returns counts by category, severity, and status without the actual finding data.
91
119
  * This is much more token-efficient than get_findings for understanding the overall state.
92
120
  */
93
- export const getFindingsStats: Handler = async (args, ctx) => {
94
- const { project_id } = args as {
95
- project_id: string;
96
- };
97
-
98
- validateRequired(project_id, 'project_id');
99
- validateUUID(project_id, 'project_id');
121
+ export const getFindingsStats: Handler = async (args, _ctx) => {
122
+ const { project_id } = parseArgs(args, getFindingsStatsSchema);
100
123
 
101
124
  const apiClient = getApiClient();
102
125
  const response = await apiClient.getFindingsStats(project_id);
@@ -108,25 +131,15 @@ export const getFindingsStats: Handler = async (args, ctx) => {
108
131
  return { result: response.data };
109
132
  };
110
133
 
111
- export const updateFinding: Handler = async (args, ctx) => {
112
- const { finding_id, status, resolution_note, title, description, severity } = args as {
113
- finding_id: string;
114
- status?: FindingStatus;
115
- resolution_note?: string;
116
- title?: string;
117
- description?: string;
118
- severity?: FindingSeverity;
119
- };
120
-
121
- validateRequired(finding_id, 'finding_id');
122
- validateUUID(finding_id, 'finding_id');
134
+ export const updateFinding: Handler = async (args, _ctx) => {
135
+ const { finding_id, title, description, severity, status, resolution_note } = parseArgs(args, updateFindingSchema);
123
136
 
124
137
  const apiClient = getApiClient();
125
138
  const response = await apiClient.updateFinding(finding_id, {
126
139
  title,
127
140
  description,
128
- severity,
129
- status,
141
+ severity: severity as FindingSeverity | undefined,
142
+ status: status as FindingStatus | undefined,
130
143
  resolution_note
131
144
  });
132
145
 
@@ -137,11 +150,8 @@ export const updateFinding: Handler = async (args, ctx) => {
137
150
  return { result: response.data };
138
151
  };
139
152
 
140
- export const deleteFinding: Handler = async (args, ctx) => {
141
- const { finding_id } = args as { finding_id: string };
142
-
143
- validateRequired(finding_id, 'finding_id');
144
- validateUUID(finding_id, 'finding_id');
153
+ export const deleteFinding: Handler = async (args, _ctx) => {
154
+ const { finding_id } = parseArgs(args, deleteFindingSchema);
145
155
 
146
156
  const apiClient = getApiClient();
147
157
  const response = await apiClient.deleteFinding(finding_id);
@@ -153,6 +163,32 @@ export const deleteFinding: Handler = async (args, ctx) => {
153
163
  return { result: response.data };
154
164
  };
155
165
 
166
+ /**
167
+ * Query aggregated project knowledge in a single call.
168
+ * Returns findings, Q&A, decisions, completed tasks, and resolved blockers.
169
+ * Use this instead of multiple separate tool calls to reduce token usage.
170
+ */
171
+ export const queryKnowledgeBase: Handler = async (args, _ctx) => {
172
+ const { project_id, scope, categories, limit, search_query } = parseArgs(args, queryKnowledgeBaseSchema);
173
+
174
+ // Validate limit range
175
+ const effectiveLimit = Math.min(Math.max(1, limit ?? 5), 20);
176
+
177
+ const apiClient = getApiClient();
178
+ const response = await apiClient.queryKnowledgeBase(project_id, {
179
+ scope: scope as 'summary' | 'detailed' | undefined,
180
+ categories: categories as string[] | undefined,
181
+ limit: effectiveLimit,
182
+ search_query
183
+ });
184
+
185
+ if (!response.ok) {
186
+ throw new Error(response.error || 'Failed to query knowledge base');
187
+ }
188
+
189
+ return { result: response.data };
190
+ };
191
+
156
192
  /**
157
193
  * Findings handlers registry
158
194
  */
@@ -162,4 +198,5 @@ export const findingHandlers: HandlerRegistry = {
162
198
  get_findings_stats: getFindingsStats,
163
199
  update_finding: updateFinding,
164
200
  delete_finding: deleteFinding,
201
+ query_knowledge_base: queryKnowledgeBase,
165
202
  };
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import type { Handler, HandlerRegistry } from './types.js';
12
- import { validateRequired, validateUUID } from '../validators.js';
12
+ import { parseArgs, uuidValidator, createEnumValidator } from '../validators.js';
13
13
  import { getApiClient } from '../api-client.js';
14
14
 
15
15
  const VALID_GIT_ISSUE_TYPES = [
@@ -25,41 +25,38 @@ const VALID_GIT_ISSUE_STATUSES = ['open', 'resolved'] as const;
25
25
  type GitIssueType = (typeof VALID_GIT_ISSUE_TYPES)[number];
26
26
  type GitIssueStatus = (typeof VALID_GIT_ISSUE_STATUSES)[number];
27
27
 
28
- export const addGitIssue: Handler = async (args, ctx) => {
29
- const {
30
- project_id,
31
- issue_type,
32
- branch,
33
- target_branch,
34
- pr_url,
35
- conflicting_files,
36
- error_message,
37
- task_id,
38
- } = args as {
39
- project_id: string;
40
- issue_type: string;
41
- branch: string;
42
- target_branch?: string;
43
- pr_url?: string;
44
- conflicting_files?: string[];
45
- error_message?: string;
46
- task_id?: string;
47
- };
48
-
49
- validateRequired(project_id, 'project_id');
50
- validateUUID(project_id, 'project_id');
51
- validateRequired(issue_type, 'issue_type');
52
- validateRequired(branch, 'branch');
53
-
54
- if (!VALID_GIT_ISSUE_TYPES.includes(issue_type as GitIssueType)) {
55
- throw new Error(
56
- `Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`
57
- );
58
- }
28
+ // Argument schemas for type-safe parsing
29
+ const addGitIssueSchema = {
30
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
31
+ issue_type: { type: 'string' as const, required: true as const, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
32
+ branch: { type: 'string' as const, required: true as const },
33
+ target_branch: { type: 'string' as const },
34
+ pr_url: { type: 'string' as const },
35
+ conflicting_files: { type: 'array' as const },
36
+ error_message: { type: 'string' as const },
37
+ task_id: { type: 'string' as const, validate: uuidValidator },
38
+ };
59
39
 
60
- if (task_id) {
61
- validateUUID(task_id, 'task_id');
62
- }
40
+ const resolveGitIssueSchema = {
41
+ git_issue_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
42
+ resolution_note: { type: 'string' as const },
43
+ auto_resolved: { type: 'boolean' as const },
44
+ };
45
+
46
+ const getGitIssuesSchema = {
47
+ project_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
48
+ status: { type: 'string' as const, default: 'open', validate: createEnumValidator(VALID_GIT_ISSUE_STATUSES) },
49
+ issue_type: { type: 'string' as const, validate: createEnumValidator(VALID_GIT_ISSUE_TYPES) },
50
+ branch: { type: 'string' as const },
51
+ limit: { type: 'number' as const, default: 50 },
52
+ };
53
+
54
+ const deleteGitIssueSchema = {
55
+ git_issue_id: { type: 'string' as const, required: true as const, validate: uuidValidator },
56
+ };
57
+
58
+ export const addGitIssue: Handler = async (args, ctx) => {
59
+ const { project_id, issue_type, branch, target_branch, pr_url, conflicting_files, error_message, task_id } = parseArgs(args, addGitIssueSchema);
63
60
 
64
61
  const apiClient = getApiClient();
65
62
  const response = await apiClient.addGitIssue(project_id, {
@@ -67,7 +64,7 @@ export const addGitIssue: Handler = async (args, ctx) => {
67
64
  branch,
68
65
  target_branch,
69
66
  pr_url,
70
- conflicting_files,
67
+ conflicting_files: conflicting_files as string[] | undefined,
71
68
  error_message,
72
69
  task_id
73
70
  }, ctx.session.currentSessionId || undefined);
@@ -80,14 +77,7 @@ export const addGitIssue: Handler = async (args, ctx) => {
80
77
  };
81
78
 
82
79
  export const resolveGitIssue: Handler = async (args, ctx) => {
83
- const { git_issue_id, resolution_note, auto_resolved } = args as {
84
- git_issue_id: string;
85
- resolution_note?: string;
86
- auto_resolved?: boolean;
87
- };
88
-
89
- validateRequired(git_issue_id, 'git_issue_id');
90
- validateUUID(git_issue_id, 'git_issue_id');
80
+ const { git_issue_id, resolution_note, auto_resolved } = parseArgs(args, resolveGitIssueSchema);
91
81
 
92
82
  const apiClient = getApiClient();
93
83
  const response = await apiClient.resolveGitIssue(git_issue_id, {
@@ -102,40 +92,13 @@ export const resolveGitIssue: Handler = async (args, ctx) => {
102
92
  return { result: response.data };
103
93
  };
104
94
 
105
- export const getGitIssues: Handler = async (args, ctx) => {
106
- const {
107
- project_id,
108
- status = 'open',
109
- issue_type,
110
- branch,
111
- limit = 50,
112
- } = args as {
113
- project_id: string;
114
- status?: string;
115
- issue_type?: string;
116
- branch?: string;
117
- limit?: number;
118
- };
119
-
120
- validateRequired(project_id, 'project_id');
121
- validateUUID(project_id, 'project_id');
122
-
123
- if (status && !VALID_GIT_ISSUE_STATUSES.includes(status as GitIssueStatus)) {
124
- throw new Error(
125
- `Invalid status. Valid statuses: ${VALID_GIT_ISSUE_STATUSES.join(', ')}`
126
- );
127
- }
128
-
129
- if (issue_type && !VALID_GIT_ISSUE_TYPES.includes(issue_type as GitIssueType)) {
130
- throw new Error(
131
- `Invalid issue_type. Valid types: ${VALID_GIT_ISSUE_TYPES.join(', ')}`
132
- );
133
- }
95
+ export const getGitIssues: Handler = async (args, _ctx) => {
96
+ const { project_id, status, issue_type, branch, limit } = parseArgs(args, getGitIssuesSchema);
134
97
 
135
98
  const apiClient = getApiClient();
136
99
  const response = await apiClient.getGitIssues(project_id, {
137
- status,
138
- issue_type,
100
+ status: status as GitIssueStatus | undefined,
101
+ issue_type: issue_type as GitIssueType | undefined,
139
102
  branch,
140
103
  limit
141
104
  });
@@ -147,11 +110,8 @@ export const getGitIssues: Handler = async (args, ctx) => {
147
110
  return { result: response.data };
148
111
  };
149
112
 
150
- export const deleteGitIssue: Handler = async (args, ctx) => {
151
- const { git_issue_id } = args as { git_issue_id: string };
152
-
153
- validateRequired(git_issue_id, 'git_issue_id');
154
- validateUUID(git_issue_id, 'git_issue_id');
113
+ export const deleteGitIssue: Handler = async (args, _ctx) => {
114
+ const { git_issue_id } = parseArgs(args, deleteGitIssueSchema);
155
115
 
156
116
  const apiClient = getApiClient();
157
117
  const response = await apiClient.deleteGitIssue(git_issue_id);