@vibescope/mcp-server 0.0.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 (170) hide show
  1. package/README.md +98 -0
  2. package/dist/cli.d.ts +34 -0
  3. package/dist/cli.js +356 -0
  4. package/dist/cli.test.d.ts +1 -0
  5. package/dist/cli.test.js +367 -0
  6. package/dist/handlers/__test-utils__.d.ts +72 -0
  7. package/dist/handlers/__test-utils__.js +176 -0
  8. package/dist/handlers/blockers.d.ts +18 -0
  9. package/dist/handlers/blockers.js +81 -0
  10. package/dist/handlers/bodies-of-work.d.ts +34 -0
  11. package/dist/handlers/bodies-of-work.js +614 -0
  12. package/dist/handlers/checkouts.d.ts +37 -0
  13. package/dist/handlers/checkouts.js +377 -0
  14. package/dist/handlers/cost.d.ts +39 -0
  15. package/dist/handlers/cost.js +247 -0
  16. package/dist/handlers/decisions.d.ts +16 -0
  17. package/dist/handlers/decisions.js +64 -0
  18. package/dist/handlers/deployment.d.ts +36 -0
  19. package/dist/handlers/deployment.js +1062 -0
  20. package/dist/handlers/discovery.d.ts +14 -0
  21. package/dist/handlers/discovery.js +870 -0
  22. package/dist/handlers/fallback.d.ts +18 -0
  23. package/dist/handlers/fallback.js +216 -0
  24. package/dist/handlers/findings.d.ts +18 -0
  25. package/dist/handlers/findings.js +110 -0
  26. package/dist/handlers/git-issues.d.ts +22 -0
  27. package/dist/handlers/git-issues.js +247 -0
  28. package/dist/handlers/ideas.d.ts +19 -0
  29. package/dist/handlers/ideas.js +188 -0
  30. package/dist/handlers/index.d.ts +29 -0
  31. package/dist/handlers/index.js +65 -0
  32. package/dist/handlers/knowledge-query.d.ts +22 -0
  33. package/dist/handlers/knowledge-query.js +253 -0
  34. package/dist/handlers/knowledge.d.ts +12 -0
  35. package/dist/handlers/knowledge.js +108 -0
  36. package/dist/handlers/milestones.d.ts +20 -0
  37. package/dist/handlers/milestones.js +179 -0
  38. package/dist/handlers/organizations.d.ts +36 -0
  39. package/dist/handlers/organizations.js +428 -0
  40. package/dist/handlers/progress.d.ts +14 -0
  41. package/dist/handlers/progress.js +149 -0
  42. package/dist/handlers/project.d.ts +20 -0
  43. package/dist/handlers/project.js +278 -0
  44. package/dist/handlers/requests.d.ts +16 -0
  45. package/dist/handlers/requests.js +131 -0
  46. package/dist/handlers/roles.d.ts +30 -0
  47. package/dist/handlers/roles.js +281 -0
  48. package/dist/handlers/session.d.ts +20 -0
  49. package/dist/handlers/session.js +791 -0
  50. package/dist/handlers/tasks.d.ts +52 -0
  51. package/dist/handlers/tasks.js +1111 -0
  52. package/dist/handlers/tasks.test.d.ts +1 -0
  53. package/dist/handlers/tasks.test.js +431 -0
  54. package/dist/handlers/types.d.ts +94 -0
  55. package/dist/handlers/types.js +1 -0
  56. package/dist/handlers/validation.d.ts +16 -0
  57. package/dist/handlers/validation.js +188 -0
  58. package/dist/index.d.ts +2 -0
  59. package/dist/index.js +2707 -0
  60. package/dist/knowledge.d.ts +6 -0
  61. package/dist/knowledge.js +121 -0
  62. package/dist/tools.d.ts +2 -0
  63. package/dist/tools.js +2498 -0
  64. package/dist/utils.d.ts +149 -0
  65. package/dist/utils.js +317 -0
  66. package/dist/utils.test.d.ts +1 -0
  67. package/dist/utils.test.js +532 -0
  68. package/dist/validators.d.ts +35 -0
  69. package/dist/validators.js +111 -0
  70. package/dist/validators.test.d.ts +1 -0
  71. package/dist/validators.test.js +176 -0
  72. package/package.json +44 -0
  73. package/src/cli.test.ts +442 -0
  74. package/src/cli.ts +439 -0
  75. package/src/handlers/__test-utils__.ts +217 -0
  76. package/src/handlers/blockers.test.ts +390 -0
  77. package/src/handlers/blockers.ts +110 -0
  78. package/src/handlers/bodies-of-work.test.ts +1276 -0
  79. package/src/handlers/bodies-of-work.ts +783 -0
  80. package/src/handlers/cost.test.ts +436 -0
  81. package/src/handlers/cost.ts +322 -0
  82. package/src/handlers/decisions.test.ts +401 -0
  83. package/src/handlers/decisions.ts +86 -0
  84. package/src/handlers/deployment.test.ts +516 -0
  85. package/src/handlers/deployment.ts +1289 -0
  86. package/src/handlers/discovery.test.ts +254 -0
  87. package/src/handlers/discovery.ts +969 -0
  88. package/src/handlers/fallback.test.ts +687 -0
  89. package/src/handlers/fallback.ts +260 -0
  90. package/src/handlers/findings.test.ts +565 -0
  91. package/src/handlers/findings.ts +153 -0
  92. package/src/handlers/ideas.test.ts +753 -0
  93. package/src/handlers/ideas.ts +247 -0
  94. package/src/handlers/index.ts +69 -0
  95. package/src/handlers/milestones.test.ts +584 -0
  96. package/src/handlers/milestones.ts +217 -0
  97. package/src/handlers/organizations.test.ts +997 -0
  98. package/src/handlers/organizations.ts +550 -0
  99. package/src/handlers/progress.test.ts +369 -0
  100. package/src/handlers/progress.ts +188 -0
  101. package/src/handlers/project.test.ts +562 -0
  102. package/src/handlers/project.ts +352 -0
  103. package/src/handlers/requests.test.ts +531 -0
  104. package/src/handlers/requests.ts +150 -0
  105. package/src/handlers/session.test.ts +459 -0
  106. package/src/handlers/session.ts +912 -0
  107. package/src/handlers/tasks.test.ts +602 -0
  108. package/src/handlers/tasks.ts +1393 -0
  109. package/src/handlers/types.ts +88 -0
  110. package/src/handlers/validation.test.ts +880 -0
  111. package/src/handlers/validation.ts +223 -0
  112. package/src/index.ts +3205 -0
  113. package/src/knowledge.ts +132 -0
  114. package/src/tmpclaude-0078-cwd +1 -0
  115. package/src/tmpclaude-0ee1-cwd +1 -0
  116. package/src/tmpclaude-2dd5-cwd +1 -0
  117. package/src/tmpclaude-344c-cwd +1 -0
  118. package/src/tmpclaude-3860-cwd +1 -0
  119. package/src/tmpclaude-4b63-cwd +1 -0
  120. package/src/tmpclaude-5c73-cwd +1 -0
  121. package/src/tmpclaude-5ee3-cwd +1 -0
  122. package/src/tmpclaude-6795-cwd +1 -0
  123. package/src/tmpclaude-709e-cwd +1 -0
  124. package/src/tmpclaude-9839-cwd +1 -0
  125. package/src/tmpclaude-d829-cwd +1 -0
  126. package/src/tmpclaude-e072-cwd +1 -0
  127. package/src/tmpclaude-f6ee-cwd +1 -0
  128. package/src/utils.test.ts +681 -0
  129. package/src/utils.ts +375 -0
  130. package/src/validators.test.ts +223 -0
  131. package/src/validators.ts +122 -0
  132. package/tmpclaude-0439-cwd +1 -0
  133. package/tmpclaude-132f-cwd +1 -0
  134. package/tmpclaude-15bb-cwd +1 -0
  135. package/tmpclaude-165a-cwd +1 -0
  136. package/tmpclaude-1ba9-cwd +1 -0
  137. package/tmpclaude-21a3-cwd +1 -0
  138. package/tmpclaude-2a38-cwd +1 -0
  139. package/tmpclaude-2adf-cwd +1 -0
  140. package/tmpclaude-2f56-cwd +1 -0
  141. package/tmpclaude-3626-cwd +1 -0
  142. package/tmpclaude-3727-cwd +1 -0
  143. package/tmpclaude-40bc-cwd +1 -0
  144. package/tmpclaude-436f-cwd +1 -0
  145. package/tmpclaude-4783-cwd +1 -0
  146. package/tmpclaude-4b6d-cwd +1 -0
  147. package/tmpclaude-4ba4-cwd +1 -0
  148. package/tmpclaude-51e6-cwd +1 -0
  149. package/tmpclaude-5ecf-cwd +1 -0
  150. package/tmpclaude-6f97-cwd +1 -0
  151. package/tmpclaude-7fb2-cwd +1 -0
  152. package/tmpclaude-825c-cwd +1 -0
  153. package/tmpclaude-8baf-cwd +1 -0
  154. package/tmpclaude-8d9f-cwd +1 -0
  155. package/tmpclaude-975c-cwd +1 -0
  156. package/tmpclaude-9983-cwd +1 -0
  157. package/tmpclaude-a045-cwd +1 -0
  158. package/tmpclaude-ac4a-cwd +1 -0
  159. package/tmpclaude-b593-cwd +1 -0
  160. package/tmpclaude-b891-cwd +1 -0
  161. package/tmpclaude-c032-cwd +1 -0
  162. package/tmpclaude-cf43-cwd +1 -0
  163. package/tmpclaude-d040-cwd +1 -0
  164. package/tmpclaude-dcdd-cwd +1 -0
  165. package/tmpclaude-dcee-cwd +1 -0
  166. package/tmpclaude-e16b-cwd +1 -0
  167. package/tmpclaude-ecd2-cwd +1 -0
  168. package/tmpclaude-f48d-cwd +1 -0
  169. package/tsconfig.json +16 -0
  170. package/vitest.config.ts +13 -0
@@ -0,0 +1,149 @@
1
+ export declare const AGENT_PERSONAS: readonly ["Apex", "Arc", "Atlas", "Blaze", "Bolt", "Byte", "Cipher", "Core", "Dash", "Drift", "Echo", "Edge", "Ember", "Flux", "Forge", "Ghost", "Glitch", "Haze", "Hex", "Ion", "Iris", "Jade", "Jet", "Karma", "Kite", "Lux", "Mako", "Neon", "Nova", "Onyx", "Orbit", "Pixel", "Pulse", "Quest", "Rex", "Rune", "Shade", "Spark", "Storm", "Trace", "Turbo", "Vex", "Volt", "Warp", "Wave", "Zen", "Zero"];
2
+ export type AgentPersona = typeof AGENT_PERSONAS[number];
3
+ export declare const FALLBACK_ACTIVITIES: readonly [{
4
+ readonly activity: "feature_ideation";
5
+ readonly title: "Generate feature ideas";
6
+ readonly description: "Review the codebase and suggest improvements or new features. Use add_idea to record your findings.";
7
+ readonly prompt: "Explore the codebase, identify areas for improvement, and suggest 2-3 actionable feature ideas using add_idea.";
8
+ }, {
9
+ readonly activity: "feature_planning";
10
+ readonly title: "Plan features from ideas";
11
+ readonly description: "Take raw ideas and develop them into planned features with documentation.";
12
+ readonly prompt: "Call get_ideas to find ideas with status \"raw\" or \"exploring\". Pick one and develop it: research requirements, identify implementation steps, write a feature spec. Use update_idea to change status to \"planned\" and add a doc_url if you create documentation.";
13
+ }, {
14
+ readonly activity: "code_review";
15
+ readonly title: "Code standards review";
16
+ readonly description: "Check for inconsistencies, missing types, code smells, or patterns that could be improved.";
17
+ readonly prompt: "Review the codebase for code quality issues. Look for missing types, inconsistent patterns, or technical debt. Log findings with add_idea or add_task.";
18
+ }, {
19
+ readonly activity: "performance_audit";
20
+ readonly title: "Performance audit";
21
+ readonly description: "Identify potential performance bottlenecks or optimization opportunities.";
22
+ readonly prompt: "Analyze the codebase for performance issues: N+1 queries, unnecessary re-renders, expensive operations. Create tasks for fixes with add_task.";
23
+ }, {
24
+ readonly activity: "ux_review";
25
+ readonly title: "UX review";
26
+ readonly description: "Evaluate user flows and identify usability improvements.";
27
+ readonly prompt: "Review the UI components and user flows. Identify confusing interactions, missing feedback, or accessibility issues. Log improvements with add_idea.";
28
+ }, {
29
+ readonly activity: "cost_analysis";
30
+ readonly title: "Cost analysis";
31
+ readonly description: "Review infrastructure and API usage for cost optimization opportunities.";
32
+ readonly prompt: "Analyze the codebase for expensive operations: unnecessary API calls, inefficient queries, large bundle sizes. Suggest cost-saving alternatives with add_idea.";
33
+ }, {
34
+ readonly activity: "security_review";
35
+ readonly title: "Security review";
36
+ readonly description: "Check for common security issues: auth gaps, input validation, data exposure.";
37
+ readonly prompt: "Review the codebase for security concerns: authentication gaps, missing input validation, sensitive data handling. Create high-priority tasks for issues found.";
38
+ }, {
39
+ readonly activity: "test_coverage";
40
+ readonly title: "Test coverage analysis";
41
+ readonly description: "Find untested code paths and suggest test cases.";
42
+ readonly prompt: "Identify areas of the codebase with missing or weak test coverage. Create tasks for writing tests using add_task.";
43
+ }, {
44
+ readonly activity: "documentation_review";
45
+ readonly title: "Documentation review";
46
+ readonly description: "Identify missing or outdated documentation.";
47
+ readonly prompt: "Review existing docs and code comments. Identify gaps, outdated information, or confusing explanations. Log improvements with add_idea.";
48
+ }, {
49
+ readonly activity: "dependency_audit";
50
+ readonly title: "Dependency audit";
51
+ readonly description: "Review dependencies for updates, security issues, or unused packages.";
52
+ readonly prompt: "Check package.json for outdated dependencies, security vulnerabilities, or unused packages. Create tasks for updates with add_task.";
53
+ }, {
54
+ readonly activity: "validate_completed_tasks";
55
+ readonly title: "Validate completed tasks";
56
+ readonly description: "Review tasks completed by other agents to ensure quality.";
57
+ readonly prompt: "Call get_tasks_awaiting_validation to find completed tasks that need review. Validate each one by checking the implementation and running tests if applicable.";
58
+ }];
59
+ export type FallbackActivity = typeof FALLBACK_ACTIVITIES[number];
60
+ /**
61
+ * Get a random fallback activity
62
+ */
63
+ export declare function getRandomFallbackActivity(): FallbackActivity;
64
+ /**
65
+ * Select an available persona from the pool (randomly)
66
+ * @param usedPersonas Set of personas currently in use
67
+ * @param instanceId The instance ID for fallback naming
68
+ * @returns The selected persona name
69
+ */
70
+ export declare function selectPersona(usedPersonas: Set<string>, instanceId: string): string;
71
+ export interface RateLimitResult {
72
+ allowed: boolean;
73
+ remaining: number;
74
+ resetIn: number;
75
+ }
76
+ export declare class RateLimiter {
77
+ private requests;
78
+ private readonly maxRequests;
79
+ private readonly windowMs;
80
+ constructor(maxRequests?: number, windowMs?: number);
81
+ check(key: string): RateLimitResult;
82
+ cleanup(): void;
83
+ getRequestCount(key: string): number | undefined;
84
+ }
85
+ /**
86
+ * Check if a task can be validated by the given session
87
+ * @param task The task to validate
88
+ * @param validatorSessionId The session trying to validate
89
+ * @returns Object with canValidate boolean and reason if not allowed
90
+ */
91
+ export declare function canValidateTask(task: {
92
+ status: string;
93
+ validated_at: string | null;
94
+ working_agent_session_id: string | null;
95
+ }, validatorSessionId: string | null): {
96
+ canValidate: boolean;
97
+ reason?: string;
98
+ };
99
+ /**
100
+ * Check if a task status transition is valid
101
+ * @param currentStatus Current task status
102
+ * @param newStatus Desired new status
103
+ * @returns Object with isValid boolean and reason if not valid
104
+ */
105
+ export declare function isValidStatusTransition(currentStatus: string, newStatus: string): {
106
+ isValid: boolean;
107
+ reason?: string;
108
+ };
109
+ /**
110
+ * Check if a deployment status transition is valid
111
+ * @param currentStatus Current deployment status
112
+ * @param newStatus Desired new status
113
+ * @returns Object with isValid boolean and reason if not valid
114
+ */
115
+ export declare function isValidDeploymentStatusTransition(currentStatus: string, newStatus: string): {
116
+ isValid: boolean;
117
+ reason?: string;
118
+ };
119
+ /**
120
+ * Extract project name from a git URL.
121
+ * Handles various git URL formats:
122
+ * - https://github.com/user/repo -> repo
123
+ * - https://github.com/user/repo.git -> repo
124
+ * - git@github.com:user/repo.git -> repo
125
+ * - https://gitlab.com/user/repo -> repo
126
+ * - ssh://git@github.com/user/repo.git -> repo
127
+ *
128
+ * @param gitUrl The git URL to parse
129
+ * @returns The extracted project name, or 'my-project' if parsing fails
130
+ */
131
+ export declare function extractProjectNameFromGitUrl(gitUrl: string): string;
132
+ /**
133
+ * Normalize a git URL to a consistent HTTPS format.
134
+ * - Removes .git suffix
135
+ * - Converts SSH URLs (git@host:path) to HTTPS format
136
+ *
137
+ * Supports GitHub, GitLab, and Bitbucket.
138
+ *
139
+ * @param gitUrl The git URL to normalize
140
+ * @returns The normalized URL, or the original if no normalization needed
141
+ *
142
+ * @example
143
+ * normalizeGitUrl('git@github.com:user/repo.git')
144
+ * // returns 'https://github.com/user/repo'
145
+ *
146
+ * normalizeGitUrl('https://github.com/user/repo.git')
147
+ * // returns 'https://github.com/user/repo'
148
+ */
149
+ export declare function normalizeGitUrl(gitUrl: string): string;
package/dist/utils.js ADDED
@@ -0,0 +1,317 @@
1
+ // ============================================================================
2
+ // Utilities Module - Extracted from index.ts for testability
3
+ // ============================================================================
4
+ // ============================================================================
5
+ // Agent Persona Pool
6
+ // Short, memorable names for distinguishing agents on the dashboard
7
+ // ============================================================================
8
+ export const AGENT_PERSONAS = [
9
+ 'Apex',
10
+ 'Arc',
11
+ 'Atlas',
12
+ 'Blaze',
13
+ 'Bolt',
14
+ 'Byte',
15
+ 'Cipher',
16
+ 'Core',
17
+ 'Dash',
18
+ 'Drift',
19
+ 'Echo',
20
+ 'Edge',
21
+ 'Ember',
22
+ 'Flux',
23
+ 'Forge',
24
+ 'Ghost',
25
+ 'Glitch',
26
+ 'Haze',
27
+ 'Hex',
28
+ 'Ion',
29
+ 'Iris',
30
+ 'Jade',
31
+ 'Jet',
32
+ 'Karma',
33
+ 'Kite',
34
+ 'Lux',
35
+ 'Mako',
36
+ 'Neon',
37
+ 'Nova',
38
+ 'Onyx',
39
+ 'Orbit',
40
+ 'Pixel',
41
+ 'Pulse',
42
+ 'Quest',
43
+ 'Rex',
44
+ 'Rune',
45
+ 'Shade',
46
+ 'Spark',
47
+ 'Storm',
48
+ 'Trace',
49
+ 'Turbo',
50
+ 'Vex',
51
+ 'Volt',
52
+ 'Warp',
53
+ 'Wave',
54
+ 'Zen',
55
+ 'Zero',
56
+ ];
57
+ // ============================================================================
58
+ // Fallback Activities for Idle Agents
59
+ // Suggested productive activities when no pending tasks exist
60
+ // ============================================================================
61
+ export const FALLBACK_ACTIVITIES = [
62
+ {
63
+ activity: 'feature_ideation',
64
+ title: 'Generate feature ideas',
65
+ description: 'Review the codebase and suggest improvements or new features. Use add_idea to record your findings.',
66
+ prompt: 'Explore the codebase, identify areas for improvement, and suggest 2-3 actionable feature ideas using add_idea.',
67
+ },
68
+ {
69
+ activity: 'feature_planning',
70
+ title: 'Plan features from ideas',
71
+ description: 'Take raw ideas and develop them into planned features with documentation.',
72
+ prompt: 'Call get_ideas to find ideas with status "raw" or "exploring". Pick one and develop it: research requirements, identify implementation steps, write a feature spec. Use update_idea to change status to "planned" and add a doc_url if you create documentation.',
73
+ },
74
+ {
75
+ activity: 'code_review',
76
+ title: 'Code standards review',
77
+ description: 'Check for inconsistencies, missing types, code smells, or patterns that could be improved.',
78
+ prompt: 'Review the codebase for code quality issues. Look for missing types, inconsistent patterns, or technical debt. Log findings with add_idea or add_task.',
79
+ },
80
+ {
81
+ activity: 'performance_audit',
82
+ title: 'Performance audit',
83
+ description: 'Identify potential performance bottlenecks or optimization opportunities.',
84
+ prompt: 'Analyze the codebase for performance issues: N+1 queries, unnecessary re-renders, expensive operations. Create tasks for fixes with add_task.',
85
+ },
86
+ {
87
+ activity: 'ux_review',
88
+ title: 'UX review',
89
+ description: 'Evaluate user flows and identify usability improvements.',
90
+ prompt: 'Review the UI components and user flows. Identify confusing interactions, missing feedback, or accessibility issues. Log improvements with add_idea.',
91
+ },
92
+ {
93
+ activity: 'cost_analysis',
94
+ title: 'Cost analysis',
95
+ description: 'Review infrastructure and API usage for cost optimization opportunities.',
96
+ prompt: 'Analyze the codebase for expensive operations: unnecessary API calls, inefficient queries, large bundle sizes. Suggest cost-saving alternatives with add_idea.',
97
+ },
98
+ {
99
+ activity: 'security_review',
100
+ title: 'Security review',
101
+ description: 'Check for common security issues: auth gaps, input validation, data exposure.',
102
+ prompt: 'Review the codebase for security concerns: authentication gaps, missing input validation, sensitive data handling. Create high-priority tasks for issues found.',
103
+ },
104
+ {
105
+ activity: 'test_coverage',
106
+ title: 'Test coverage analysis',
107
+ description: 'Find untested code paths and suggest test cases.',
108
+ prompt: 'Identify areas of the codebase with missing or weak test coverage. Create tasks for writing tests using add_task.',
109
+ },
110
+ {
111
+ activity: 'documentation_review',
112
+ title: 'Documentation review',
113
+ description: 'Identify missing or outdated documentation.',
114
+ prompt: 'Review existing docs and code comments. Identify gaps, outdated information, or confusing explanations. Log improvements with add_idea.',
115
+ },
116
+ {
117
+ activity: 'dependency_audit',
118
+ title: 'Dependency audit',
119
+ description: 'Review dependencies for updates, security issues, or unused packages.',
120
+ prompt: 'Check package.json for outdated dependencies, security vulnerabilities, or unused packages. Create tasks for updates with add_task.',
121
+ },
122
+ {
123
+ activity: 'validate_completed_tasks',
124
+ title: 'Validate completed tasks',
125
+ description: 'Review tasks completed by other agents to ensure quality.',
126
+ prompt: 'Call get_tasks_awaiting_validation to find completed tasks that need review. Validate each one by checking the implementation and running tests if applicable.',
127
+ },
128
+ ];
129
+ /**
130
+ * Get a random fallback activity
131
+ */
132
+ export function getRandomFallbackActivity() {
133
+ const index = Math.floor(Math.random() * FALLBACK_ACTIVITIES.length);
134
+ return FALLBACK_ACTIVITIES[index];
135
+ }
136
+ /**
137
+ * Select an available persona from the pool (randomly)
138
+ * @param usedPersonas Set of personas currently in use
139
+ * @param instanceId The instance ID for fallback naming
140
+ * @returns The selected persona name
141
+ */
142
+ export function selectPersona(usedPersonas, instanceId) {
143
+ const availablePersonas = AGENT_PERSONAS.filter((p) => !usedPersonas.has(p));
144
+ if (availablePersonas.length === 0) {
145
+ return `Agent-${instanceId.slice(0, 6)}`;
146
+ }
147
+ const randomIndex = Math.floor(Math.random() * availablePersonas.length);
148
+ return availablePersonas[randomIndex];
149
+ }
150
+ export class RateLimiter {
151
+ requests = new Map();
152
+ maxRequests;
153
+ windowMs;
154
+ constructor(maxRequests = 60, windowMs = 60000) {
155
+ this.maxRequests = maxRequests;
156
+ this.windowMs = windowMs;
157
+ }
158
+ check(key) {
159
+ const now = Date.now();
160
+ const record = this.requests.get(key);
161
+ // If no record or window expired, create new record
162
+ if (!record || now >= record.resetAt) {
163
+ this.requests.set(key, { count: 1, resetAt: now + this.windowMs });
164
+ return { allowed: true, remaining: this.maxRequests - 1, resetIn: this.windowMs };
165
+ }
166
+ // Check if limit exceeded
167
+ if (record.count >= this.maxRequests) {
168
+ return { allowed: false, remaining: 0, resetIn: record.resetAt - now };
169
+ }
170
+ // Increment count
171
+ record.count++;
172
+ return { allowed: true, remaining: this.maxRequests - record.count, resetIn: record.resetAt - now };
173
+ }
174
+ // Clean up expired entries periodically
175
+ cleanup() {
176
+ const now = Date.now();
177
+ for (const [key, record] of this.requests.entries()) {
178
+ if (now >= record.resetAt) {
179
+ this.requests.delete(key);
180
+ }
181
+ }
182
+ }
183
+ // Expose for testing
184
+ getRequestCount(key) {
185
+ return this.requests.get(key)?.count;
186
+ }
187
+ }
188
+ // ============================================================================
189
+ // Task Validation Helpers
190
+ // ============================================================================
191
+ /**
192
+ * Check if a task can be validated by the given session
193
+ * @param task The task to validate
194
+ * @param validatorSessionId The session trying to validate
195
+ * @returns Object with canValidate boolean and reason if not allowed
196
+ */
197
+ export function canValidateTask(task, validatorSessionId) {
198
+ if (task.status !== 'completed') {
199
+ return { canValidate: false, reason: 'Can only validate completed tasks' };
200
+ }
201
+ if (task.validated_at) {
202
+ return { canValidate: false, reason: 'Task has already been validated' };
203
+ }
204
+ // Note: Self-validation check would go here if we tracked who completed the task
205
+ // Currently, working_agent_session_id is cleared on completion
206
+ return { canValidate: true };
207
+ }
208
+ /**
209
+ * Check if a task status transition is valid
210
+ * @param currentStatus Current task status
211
+ * @param newStatus Desired new status
212
+ * @returns Object with isValid boolean and reason if not valid
213
+ */
214
+ export function isValidStatusTransition(currentStatus, newStatus) {
215
+ const validTransitions = {
216
+ pending: ['in_progress', 'cancelled'],
217
+ in_progress: ['completed', 'pending', 'cancelled'],
218
+ completed: ['in_progress'], // Can reopen via validation failure
219
+ cancelled: ['pending'], // Can reactivate cancelled tasks
220
+ };
221
+ const allowed = validTransitions[currentStatus];
222
+ if (!allowed) {
223
+ return { isValid: false, reason: `Unknown current status: ${currentStatus}` };
224
+ }
225
+ if (!allowed.includes(newStatus)) {
226
+ return {
227
+ isValid: false,
228
+ reason: `Cannot transition from '${currentStatus}' to '${newStatus}'. Valid transitions: ${allowed.join(', ')}`
229
+ };
230
+ }
231
+ return { isValid: true };
232
+ }
233
+ /**
234
+ * Check if a deployment status transition is valid
235
+ * @param currentStatus Current deployment status
236
+ * @param newStatus Desired new status
237
+ * @returns Object with isValid boolean and reason if not valid
238
+ */
239
+ export function isValidDeploymentStatusTransition(currentStatus, newStatus) {
240
+ const validTransitions = {
241
+ pending: ['validating', 'failed'], // Can claim validation or cancel
242
+ validating: ['ready', 'failed'], // Validation passes or fails
243
+ ready: ['deploying', 'failed'], // Start deployment or cancel
244
+ deploying: ['deployed', 'failed'], // Deployment succeeds or fails
245
+ deployed: [], // Terminal state
246
+ failed: [], // Terminal state (create new deployment to retry)
247
+ };
248
+ const allowed = validTransitions[currentStatus];
249
+ if (!allowed) {
250
+ return { isValid: false, reason: `Unknown current status: ${currentStatus}` };
251
+ }
252
+ if (!allowed.includes(newStatus)) {
253
+ return {
254
+ isValid: false,
255
+ reason: `Cannot transition deployment from '${currentStatus}' to '${newStatus}'. Valid transitions: ${allowed.length ? allowed.join(', ') : 'none (terminal state)'}`
256
+ };
257
+ }
258
+ return { isValid: true };
259
+ }
260
+ // ============================================================================
261
+ // Git URL Parsing
262
+ // ============================================================================
263
+ /**
264
+ * Extract project name from a git URL.
265
+ * Handles various git URL formats:
266
+ * - https://github.com/user/repo -> repo
267
+ * - https://github.com/user/repo.git -> repo
268
+ * - git@github.com:user/repo.git -> repo
269
+ * - https://gitlab.com/user/repo -> repo
270
+ * - ssh://git@github.com/user/repo.git -> repo
271
+ *
272
+ * @param gitUrl The git URL to parse
273
+ * @returns The extracted project name, or 'my-project' if parsing fails
274
+ */
275
+ export function extractProjectNameFromGitUrl(gitUrl) {
276
+ if (!gitUrl) {
277
+ return 'my-project';
278
+ }
279
+ // Match the last path segment, optionally removing .git suffix
280
+ // Works for:
281
+ // - /user/repo (https URLs)
282
+ // - /user/repo.git
283
+ // - :user/repo (ssh URLs like git@github.com:user/repo)
284
+ // - :user/repo.git
285
+ const match = gitUrl.match(/[/:]([^/:]+?)(?:\.git)?$/);
286
+ if (match && match[1]) {
287
+ return match[1];
288
+ }
289
+ return 'my-project';
290
+ }
291
+ /**
292
+ * Normalize a git URL to a consistent HTTPS format.
293
+ * - Removes .git suffix
294
+ * - Converts SSH URLs (git@host:path) to HTTPS format
295
+ *
296
+ * Supports GitHub, GitLab, and Bitbucket.
297
+ *
298
+ * @param gitUrl The git URL to normalize
299
+ * @returns The normalized URL, or the original if no normalization needed
300
+ *
301
+ * @example
302
+ * normalizeGitUrl('git@github.com:user/repo.git')
303
+ * // returns 'https://github.com/user/repo'
304
+ *
305
+ * normalizeGitUrl('https://github.com/user/repo.git')
306
+ * // returns 'https://github.com/user/repo'
307
+ */
308
+ export function normalizeGitUrl(gitUrl) {
309
+ if (!gitUrl) {
310
+ return gitUrl;
311
+ }
312
+ return gitUrl
313
+ .replace(/\.git$/, '')
314
+ .replace(/^git@github\.com:/, 'https://github.com/')
315
+ .replace(/^git@gitlab\.com:/, 'https://gitlab.com/')
316
+ .replace(/^git@bitbucket\.org:/, 'https://bitbucket.org/');
317
+ }
@@ -0,0 +1 @@
1
+ export {};