@deimoscloud/coreai 0.1.0

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 (216) hide show
  1. package/.prettierrc +9 -0
  2. package/AGENT_SPEC.md +347 -0
  3. package/ARCHITECTURE.md +547 -0
  4. package/DRAFT_PRD.md +1440 -0
  5. package/IMPLEMENTATION_PLAN.md +256 -0
  6. package/PRODUCT.md +473 -0
  7. package/README.md +303 -0
  8. package/WORKFLOWS.md +295 -0
  9. package/agents/_templates/ic-engineer.md +185 -0
  10. package/agents/_templates/reviewer.md +182 -0
  11. package/agents/backend-engineer.yaml +72 -0
  12. package/agents/devops-engineer.yaml +72 -0
  13. package/agents/engineering-manager.yaml +70 -0
  14. package/agents/examples/android-engineer.md +302 -0
  15. package/agents/examples/backend-engineer.md +320 -0
  16. package/agents/examples/devops-engineer.md +742 -0
  17. package/agents/examples/engineering-manager.md +469 -0
  18. package/agents/examples/frontend-engineer.md +58 -0
  19. package/agents/examples/product-manager.md +315 -0
  20. package/agents/examples/qa-engineer.md +371 -0
  21. package/agents/examples/security-engineer.md +525 -0
  22. package/agents/examples/solutions-architect.md +351 -0
  23. package/agents/examples/wearos-engineer.md +359 -0
  24. package/agents/frontend-engineer.yaml +72 -0
  25. package/commands/core/check-inbox.md +34 -0
  26. package/commands/core/delegate.md +30 -0
  27. package/commands/core/git-commit.md +144 -0
  28. package/commands/core/pr-create.md +193 -0
  29. package/commands/core/review.md +56 -0
  30. package/commands/core/sprint-status.md +65 -0
  31. package/commands/optional/docs-update.md +200 -0
  32. package/commands/optional/jira-create.md +200 -0
  33. package/commands/optional/jira-transition.md +184 -0
  34. package/commands/optional/worktree-cleanup.md +167 -0
  35. package/commands/optional/worktree-setup.md +110 -0
  36. package/dist/cli/index.js +4037 -0
  37. package/dist/cli/index.js.map +1 -0
  38. package/dist/index.d.ts +2978 -0
  39. package/dist/index.js +3867 -0
  40. package/dist/index.js.map +1 -0
  41. package/eslint.config.js +29 -0
  42. package/jest.config.js +22 -0
  43. package/knowledge-library/README.md +118 -0
  44. package/knowledge-library/android-engineer/context/current.txt +42 -0
  45. package/knowledge-library/android-engineer/control/decisions.txt +9 -0
  46. package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
  47. package/knowledge-library/android-engineer/control/objectives.txt +26 -0
  48. package/knowledge-library/android-engineer/history/.gitkeep +0 -0
  49. package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
  50. package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
  51. package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
  52. package/knowledge-library/architecture.txt +61 -0
  53. package/knowledge-library/backend-engineer/context/current.txt +42 -0
  54. package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
  55. package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
  56. package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
  57. package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
  58. package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
  59. package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
  60. package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
  61. package/knowledge-library/context.txt +52 -0
  62. package/knowledge-library/devops-engineer/context/current.txt +42 -0
  63. package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
  64. package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
  65. package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
  66. package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
  67. package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
  68. package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
  69. package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
  70. package/knowledge-library/engineering-manager/context/current.txt +40 -0
  71. package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
  72. package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
  73. package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
  74. package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
  75. package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
  76. package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
  77. package/knowledge-library/prd.txt +81 -0
  78. package/knowledge-library/product-manager/context/current.txt +42 -0
  79. package/knowledge-library/product-manager/control/decisions.txt +9 -0
  80. package/knowledge-library/product-manager/control/dependencies.txt +19 -0
  81. package/knowledge-library/product-manager/control/objectives.txt +26 -0
  82. package/knowledge-library/product-manager/history/.gitkeep +0 -0
  83. package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
  84. package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
  85. package/knowledge-library/product-manager/tech/.gitkeep +0 -0
  86. package/knowledge-library/qa-engineer/context/current.txt +42 -0
  87. package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
  88. package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
  89. package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
  90. package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
  91. package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
  92. package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
  93. package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
  94. package/knowledge-library/security-engineer/context/current.txt +42 -0
  95. package/knowledge-library/security-engineer/control/decisions.txt +9 -0
  96. package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
  97. package/knowledge-library/security-engineer/control/objectives.txt +26 -0
  98. package/knowledge-library/security-engineer/history/.gitkeep +0 -0
  99. package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
  100. package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
  101. package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
  102. package/knowledge-library/solutions-architect/context/current.txt +42 -0
  103. package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
  104. package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
  105. package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
  106. package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
  107. package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
  108. package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
  109. package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
  110. package/knowledge-library/wearos-engineer/context/current.txt +42 -0
  111. package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
  112. package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
  113. package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
  114. package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
  115. package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
  116. package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
  117. package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
  118. package/package.json +66 -0
  119. package/schemas/agent.schema.json +171 -0
  120. package/schemas/coreai.config.schema.json +257 -0
  121. package/scripts/add-agent.sh +323 -0
  122. package/scripts/install.sh +354 -0
  123. package/src/adapters/factory.test.ts +386 -0
  124. package/src/adapters/factory.ts +305 -0
  125. package/src/adapters/index.ts +113 -0
  126. package/src/adapters/interfaces.ts +268 -0
  127. package/src/adapters/mcp/client.test.ts +130 -0
  128. package/src/adapters/mcp/client.ts +451 -0
  129. package/src/adapters/mcp/discovery.test.ts +315 -0
  130. package/src/adapters/mcp/discovery.ts +340 -0
  131. package/src/adapters/mcp/index.ts +66 -0
  132. package/src/adapters/mcp/mapper.test.ts +218 -0
  133. package/src/adapters/mcp/mapper.ts +536 -0
  134. package/src/adapters/mcp/registry.test.ts +433 -0
  135. package/src/adapters/mcp/registry.ts +550 -0
  136. package/src/adapters/mcp/types.ts +258 -0
  137. package/src/adapters/native/filesystem.test.ts +350 -0
  138. package/src/adapters/native/filesystem.ts +393 -0
  139. package/src/adapters/native/github.test.ts +173 -0
  140. package/src/adapters/native/github.ts +627 -0
  141. package/src/adapters/native/index.ts +22 -0
  142. package/src/adapters/native/selector.test.ts +224 -0
  143. package/src/adapters/native/selector.ts +150 -0
  144. package/src/adapters/types.ts +270 -0
  145. package/src/agents/compiler.test.ts +399 -0
  146. package/src/agents/compiler.ts +359 -0
  147. package/src/agents/index.ts +36 -0
  148. package/src/agents/loader.test.ts +319 -0
  149. package/src/agents/loader.ts +143 -0
  150. package/src/agents/resolver.test.ts +282 -0
  151. package/src/agents/resolver.ts +262 -0
  152. package/src/agents/types.ts +87 -0
  153. package/src/cache/index.ts +38 -0
  154. package/src/cache/interfaces.ts +283 -0
  155. package/src/cache/manager.test.ts +266 -0
  156. package/src/cache/manager.ts +388 -0
  157. package/src/cache/provider.test.ts +485 -0
  158. package/src/cache/provider.ts +745 -0
  159. package/src/cache/types.test.ts +192 -0
  160. package/src/cache/types.ts +313 -0
  161. package/src/cli/commands/build.test.ts +248 -0
  162. package/src/cli/commands/build.ts +244 -0
  163. package/src/cli/commands/cache.test.ts +221 -0
  164. package/src/cli/commands/cache.ts +229 -0
  165. package/src/cli/commands/index.ts +63 -0
  166. package/src/cli/commands/init.test.ts +173 -0
  167. package/src/cli/commands/init.ts +296 -0
  168. package/src/cli/commands/skills.test.ts +272 -0
  169. package/src/cli/commands/skills.ts +348 -0
  170. package/src/cli/commands/status.test.ts +392 -0
  171. package/src/cli/commands/status.ts +332 -0
  172. package/src/cli/commands/sync.test.ts +213 -0
  173. package/src/cli/commands/sync.ts +251 -0
  174. package/src/cli/commands/validate.test.ts +216 -0
  175. package/src/cli/commands/validate.ts +340 -0
  176. package/src/cli/index.test.ts +190 -0
  177. package/src/cli/index.ts +493 -0
  178. package/src/commands/context.test.ts +163 -0
  179. package/src/commands/context.ts +111 -0
  180. package/src/commands/index.ts +56 -0
  181. package/src/commands/loader.test.ts +273 -0
  182. package/src/commands/loader.ts +355 -0
  183. package/src/commands/registry.test.ts +384 -0
  184. package/src/commands/registry.ts +248 -0
  185. package/src/commands/runner.test.ts +297 -0
  186. package/src/commands/runner.ts +222 -0
  187. package/src/commands/types.ts +361 -0
  188. package/src/config/index.ts +19 -0
  189. package/src/config/loader.test.ts +262 -0
  190. package/src/config/loader.ts +188 -0
  191. package/src/config/types.ts +154 -0
  192. package/src/context/index.ts +14 -0
  193. package/src/context/loader.test.ts +334 -0
  194. package/src/context/loader.ts +357 -0
  195. package/src/index.test.ts +13 -0
  196. package/src/index.ts +244 -0
  197. package/src/knowledge-library/index.ts +44 -0
  198. package/src/knowledge-library/manager.test.ts +536 -0
  199. package/src/knowledge-library/manager.ts +804 -0
  200. package/src/knowledge-library/types.ts +432 -0
  201. package/src/skills/generator.test.ts +602 -0
  202. package/src/skills/generator.ts +491 -0
  203. package/src/skills/index.ts +27 -0
  204. package/src/skills/templates.ts +520 -0
  205. package/src/skills/types.ts +251 -0
  206. package/templates/completion-report.md +72 -0
  207. package/templates/feedback.md +56 -0
  208. package/templates/project-files/CLAUDE.md.template +109 -0
  209. package/templates/project-files/coreai.json.example +47 -0
  210. package/templates/project-files/mcp.json.template +20 -0
  211. package/templates/review-complete.md +64 -0
  212. package/templates/review-request.md +67 -0
  213. package/templates/task-assignment.md +51 -0
  214. package/tsconfig.build.json +4 -0
  215. package/tsconfig.json +26 -0
  216. package/tsup.config.ts +23 -0
@@ -0,0 +1,627 @@
1
+ /**
2
+ * Native GitHub Adapter
3
+ *
4
+ * Implements GitProviderAdapter using the GitHub CLI (gh).
5
+ * Requires the gh CLI to be installed and authenticated.
6
+ */
7
+
8
+ import { execFile } from 'child_process';
9
+ import { promisify } from 'util';
10
+ import type { GitProviderAdapter } from '../interfaces.js';
11
+ import type {
12
+ AdapterInfo,
13
+ PullRequest,
14
+ PullRequestQuery,
15
+ CreatePullRequestData,
16
+ Review,
17
+ CreateReviewData,
18
+ PullRequestStatus,
19
+ ReviewDecision,
20
+ } from '../types.js';
21
+ import { AdapterError } from '../types.js';
22
+
23
+ const execFileAsync = promisify(execFile);
24
+
25
+ /**
26
+ * Options for creating a GitHub adapter
27
+ */
28
+ export interface GitHubAdapterOptions {
29
+ /**
30
+ * Repository in owner/repo format
31
+ */
32
+ repository: string;
33
+
34
+ /**
35
+ * Path to gh CLI (default: 'gh')
36
+ */
37
+ ghPath?: string;
38
+ }
39
+
40
+ /**
41
+ * Native GitHub adapter using gh CLI
42
+ */
43
+ export class GitHubAdapter implements GitProviderAdapter {
44
+ private repository: string;
45
+ private ghPath: string;
46
+ private connected = false;
47
+ private owner: string;
48
+ private repo: string;
49
+
50
+ constructor(options: GitHubAdapterOptions) {
51
+ this.repository = options.repository;
52
+ this.ghPath = options.ghPath ?? 'gh';
53
+
54
+ const [owner, repo] = this.repository.split('/');
55
+ if (!owner || !repo) {
56
+ throw new AdapterError(
57
+ `Invalid repository format: ${this.repository}. Expected owner/repo`,
58
+ 'invalid_config',
59
+ this.getInfo()
60
+ );
61
+ }
62
+ this.owner = owner;
63
+ this.repo = repo;
64
+ }
65
+
66
+ /**
67
+ * Get adapter info
68
+ */
69
+ getInfo(): AdapterInfo {
70
+ return {
71
+ type: 'git',
72
+ provider: 'github',
73
+ implementation: 'native',
74
+ connected: this.connected,
75
+ };
76
+ }
77
+
78
+ /**
79
+ * Check if connected
80
+ */
81
+ isConnected(): boolean {
82
+ return this.connected;
83
+ }
84
+
85
+ /**
86
+ * Connect (verify gh CLI is available and authenticated)
87
+ */
88
+ async connect(): Promise<void> {
89
+ try {
90
+ // Check gh is installed and authenticated
91
+ await this.gh(['auth', 'status']);
92
+
93
+ // Verify we can access the repository
94
+ await this.gh(['repo', 'view', this.repository, '--json', 'name']);
95
+
96
+ this.connected = true;
97
+ } catch (error) {
98
+ throw new AdapterError(
99
+ `Failed to connect to GitHub: ${error instanceof Error ? error.message : String(error)}`,
100
+ 'connection_failed',
101
+ this.getInfo(),
102
+ error instanceof Error ? error : undefined
103
+ );
104
+ }
105
+ }
106
+
107
+ /**
108
+ * Disconnect
109
+ */
110
+ async disconnect(): Promise<void> {
111
+ this.connected = false;
112
+ }
113
+
114
+ /**
115
+ * Get a pull request by number
116
+ */
117
+ async getPullRequest(idOrNumber: string | number): Promise<PullRequest> {
118
+ this.ensureConnected();
119
+
120
+ try {
121
+ const result = await this.gh([
122
+ 'pr',
123
+ 'view',
124
+ String(idOrNumber),
125
+ '-R',
126
+ this.repository,
127
+ '--json',
128
+ 'number,title,body,state,author,headRefName,baseRefName,createdAt,updatedAt,mergedAt,url,reviewRequests',
129
+ ]);
130
+
131
+ const data = JSON.parse(result);
132
+ return this.mapPullRequest(data);
133
+ } catch (error) {
134
+ if (this.isNotFoundError(error)) {
135
+ throw new AdapterError(
136
+ `Pull request #${idOrNumber} not found`,
137
+ 'not_found',
138
+ this.getInfo()
139
+ );
140
+ }
141
+ throw this.wrapError(error, `Failed to get pull request #${idOrNumber}`);
142
+ }
143
+ }
144
+
145
+ /**
146
+ * List pull requests
147
+ */
148
+ async listPullRequests(query?: PullRequestQuery): Promise<PullRequest[]> {
149
+ this.ensureConnected();
150
+
151
+ try {
152
+ const args = [
153
+ 'pr',
154
+ 'list',
155
+ '-R',
156
+ this.repository,
157
+ '--json',
158
+ 'number,title,body,state,author,headRefName,baseRefName,createdAt,updatedAt,url',
159
+ ];
160
+
161
+ // Map status to gh CLI state
162
+ if (query?.status) {
163
+ const states = Array.isArray(query.status) ? query.status : [query.status];
164
+ const ghStates = states
165
+ .map((s) => this.mapStatusToGh(s))
166
+ .filter((s): s is string => s !== undefined);
167
+ if (ghStates.length > 0 && ghStates[0]) {
168
+ args.push('--state', ghStates[0]);
169
+ }
170
+ }
171
+
172
+ if (query?.author) {
173
+ args.push('--author', query.author);
174
+ }
175
+
176
+ if (query?.limit) {
177
+ args.push('--limit', String(query.limit));
178
+ }
179
+
180
+ const result = await this.gh(args);
181
+ const data = JSON.parse(result) as unknown[];
182
+
183
+ return data.map((pr) => this.mapPullRequest(pr as Record<string, unknown>));
184
+ } catch (error) {
185
+ throw this.wrapError(error, 'Failed to list pull requests');
186
+ }
187
+ }
188
+
189
+ /**
190
+ * Create a pull request
191
+ */
192
+ async createPullRequest(data: CreatePullRequestData): Promise<PullRequest> {
193
+ this.ensureConnected();
194
+
195
+ try {
196
+ const args = [
197
+ 'pr',
198
+ 'create',
199
+ '-R',
200
+ this.repository,
201
+ '--head',
202
+ data.source_branch,
203
+ '--base',
204
+ data.target_branch,
205
+ '--title',
206
+ data.title,
207
+ ];
208
+
209
+ if (data.description) {
210
+ args.push('--body', data.description);
211
+ }
212
+
213
+ if (data.draft) {
214
+ args.push('--draft');
215
+ }
216
+
217
+ if (data.reviewers && data.reviewers.length > 0) {
218
+ args.push('--reviewer', data.reviewers.join(','));
219
+ }
220
+
221
+ // Create and get the PR details
222
+ const createResult = await this.gh(args);
223
+
224
+ // Parse the URL from the output to get the PR number
225
+ const urlMatch = createResult.match(/https:\/\/github\.com\/[^/]+\/[^/]+\/pull\/(\d+)/);
226
+ if (urlMatch?.[1]) {
227
+ return this.getPullRequest(parseInt(urlMatch[1], 10));
228
+ }
229
+
230
+ // Fallback: list to find the just-created PR
231
+ const prs = await this.listPullRequests({ limit: 1 });
232
+ const firstPr = prs[0];
233
+ if (firstPr) {
234
+ return firstPr;
235
+ }
236
+
237
+ throw new Error('Failed to retrieve created pull request');
238
+ } catch (error) {
239
+ throw this.wrapError(error, 'Failed to create pull request');
240
+ }
241
+ }
242
+
243
+ /**
244
+ * Update a pull request
245
+ */
246
+ async updatePullRequest(
247
+ idOrNumber: string | number,
248
+ data: Partial<CreatePullRequestData>
249
+ ): Promise<PullRequest> {
250
+ this.ensureConnected();
251
+
252
+ try {
253
+ const args = ['pr', 'edit', String(idOrNumber), '-R', this.repository];
254
+
255
+ if (data.title) {
256
+ args.push('--title', data.title);
257
+ }
258
+
259
+ if (data.description !== undefined) {
260
+ args.push('--body', data.description);
261
+ }
262
+
263
+ if (data.target_branch) {
264
+ args.push('--base', data.target_branch);
265
+ }
266
+
267
+ if (data.reviewers && data.reviewers.length > 0) {
268
+ args.push('--add-reviewer', data.reviewers.join(','));
269
+ }
270
+
271
+ await this.gh(args);
272
+
273
+ return this.getPullRequest(idOrNumber);
274
+ } catch (error) {
275
+ throw this.wrapError(error, `Failed to update pull request #${idOrNumber}`);
276
+ }
277
+ }
278
+
279
+ /**
280
+ * Merge a pull request
281
+ */
282
+ async mergePullRequest(
283
+ idOrNumber: string | number,
284
+ options?: { method?: 'merge' | 'squash' | 'rebase'; message?: string }
285
+ ): Promise<void> {
286
+ this.ensureConnected();
287
+
288
+ try {
289
+ const args = ['pr', 'merge', String(idOrNumber), '-R', this.repository];
290
+
291
+ const method = options?.method ?? 'merge';
292
+ args.push(`--${method}`);
293
+
294
+ if (options?.message) {
295
+ args.push('--body', options.message);
296
+ }
297
+
298
+ // Auto-confirm the merge
299
+ args.push('--delete-branch=false');
300
+
301
+ await this.gh(args);
302
+ } catch (error) {
303
+ throw this.wrapError(error, `Failed to merge pull request #${idOrNumber}`);
304
+ }
305
+ }
306
+
307
+ /**
308
+ * Close a pull request
309
+ */
310
+ async closePullRequest(idOrNumber: string | number): Promise<void> {
311
+ this.ensureConnected();
312
+
313
+ try {
314
+ await this.gh(['pr', 'close', String(idOrNumber), '-R', this.repository]);
315
+ } catch (error) {
316
+ throw this.wrapError(error, `Failed to close pull request #${idOrNumber}`);
317
+ }
318
+ }
319
+
320
+ /**
321
+ * Add a review to a pull request
322
+ */
323
+ async addReview(idOrNumber: string | number, review: CreateReviewData): Promise<Review> {
324
+ this.ensureConnected();
325
+
326
+ try {
327
+ const args = ['pr', 'review', String(idOrNumber), '-R', this.repository];
328
+
329
+ // Map decision to gh CLI flag
330
+ switch (review.decision) {
331
+ case 'approve':
332
+ args.push('--approve');
333
+ break;
334
+ case 'request_changes':
335
+ args.push('--request-changes');
336
+ break;
337
+ case 'comment':
338
+ args.push('--comment');
339
+ break;
340
+ }
341
+
342
+ if (review.body) {
343
+ args.push('--body', review.body);
344
+ }
345
+
346
+ await this.gh(args);
347
+
348
+ // Return the review data (gh doesn't return review ID)
349
+ const reviewResult: Review = {
350
+ id: `review-${Date.now()}`,
351
+ pull_request_id: String(idOrNumber),
352
+ author: 'current-user',
353
+ decision: review.decision,
354
+ body: review.body ?? '',
355
+ created_at: new Date().toISOString(),
356
+ };
357
+ return reviewResult;
358
+ } catch (error) {
359
+ throw this.wrapError(error, `Failed to add review to pull request #${idOrNumber}`);
360
+ }
361
+ }
362
+
363
+ /**
364
+ * Get reviews for a pull request
365
+ */
366
+ async getReviews(idOrNumber: string | number): Promise<Review[]> {
367
+ this.ensureConnected();
368
+
369
+ try {
370
+ const result = await this.gh([
371
+ 'pr',
372
+ 'view',
373
+ String(idOrNumber),
374
+ '-R',
375
+ this.repository,
376
+ '--json',
377
+ 'reviews',
378
+ ]);
379
+
380
+ const data = JSON.parse(result);
381
+ const reviews = data.reviews || [];
382
+
383
+ return reviews.map((r: Record<string, unknown>) => ({
384
+ id: String(r.id || `review-${Date.now()}`),
385
+ pull_request_id: String(idOrNumber),
386
+ author: (r.author as { login?: string })?.login || 'unknown',
387
+ decision: this.mapGhStateToDecision(String(r.state || 'COMMENTED')),
388
+ body: String(r.body || ''),
389
+ created_at: String(r.submittedAt || new Date().toISOString()),
390
+ }));
391
+ } catch (error) {
392
+ throw this.wrapError(error, `Failed to get reviews for pull request #${idOrNumber}`);
393
+ }
394
+ }
395
+
396
+ /**
397
+ * Request reviewers for a pull request
398
+ */
399
+ async requestReviewers(idOrNumber: string | number, reviewers: string[]): Promise<void> {
400
+ this.ensureConnected();
401
+
402
+ try {
403
+ await this.gh([
404
+ 'pr',
405
+ 'edit',
406
+ String(idOrNumber),
407
+ '-R',
408
+ this.repository,
409
+ '--add-reviewer',
410
+ reviewers.join(','),
411
+ ]);
412
+ } catch (error) {
413
+ throw this.wrapError(error, `Failed to request reviewers for pull request #${idOrNumber}`);
414
+ }
415
+ }
416
+
417
+ /**
418
+ * Add a comment to a pull request
419
+ */
420
+ async addPullRequestComment(idOrNumber: string | number, comment: string): Promise<void> {
421
+ this.ensureConnected();
422
+
423
+ try {
424
+ await this.gh([
425
+ 'pr',
426
+ 'comment',
427
+ String(idOrNumber),
428
+ '-R',
429
+ this.repository,
430
+ '--body',
431
+ comment,
432
+ ]);
433
+ } catch (error) {
434
+ throw this.wrapError(error, `Failed to add comment to pull request #${idOrNumber}`);
435
+ }
436
+ }
437
+
438
+ /**
439
+ * Get the diff for a pull request
440
+ */
441
+ async getPullRequestDiff(idOrNumber: string | number): Promise<string> {
442
+ this.ensureConnected();
443
+
444
+ try {
445
+ return await this.gh(['pr', 'diff', String(idOrNumber), '-R', this.repository]);
446
+ } catch (error) {
447
+ throw this.wrapError(error, `Failed to get diff for pull request #${idOrNumber}`);
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Get files changed in a pull request
453
+ */
454
+ async getPullRequestFiles(idOrNumber: string | number): Promise<string[]> {
455
+ this.ensureConnected();
456
+
457
+ try {
458
+ const result = await this.gh([
459
+ 'pr',
460
+ 'view',
461
+ String(idOrNumber),
462
+ '-R',
463
+ this.repository,
464
+ '--json',
465
+ 'files',
466
+ ]);
467
+
468
+ const data = JSON.parse(result);
469
+ const files = data.files || [];
470
+
471
+ return files.map((f: { path?: string }) => f.path || '');
472
+ } catch (error) {
473
+ throw this.wrapError(error, `Failed to get files for pull request #${idOrNumber}`);
474
+ }
475
+ }
476
+
477
+ /**
478
+ * Execute gh CLI command
479
+ */
480
+ private async gh(args: string[]): Promise<string> {
481
+ try {
482
+ const { stdout } = await execFileAsync(this.ghPath, args, {
483
+ maxBuffer: 10 * 1024 * 1024, // 10MB
484
+ });
485
+ return stdout;
486
+ } catch (error) {
487
+ const execError = error as { stderr?: string; message?: string };
488
+ throw new Error(execError.stderr || execError.message || 'Unknown gh error');
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Ensure adapter is connected
494
+ */
495
+ private ensureConnected(): void {
496
+ if (!this.connected) {
497
+ throw new AdapterError(
498
+ 'GitHub adapter is not connected. Call connect() first.',
499
+ 'not_connected',
500
+ this.getInfo()
501
+ );
502
+ }
503
+ }
504
+
505
+ /**
506
+ * Map gh PR data to PullRequest type
507
+ */
508
+ private mapPullRequest(data: Record<string, unknown>): PullRequest {
509
+ const pr: PullRequest = {
510
+ id: String(data.number),
511
+ number: Number(data.number),
512
+ title: String(data.title || ''),
513
+ source_branch: String(data.headRefName || ''),
514
+ target_branch: String(data.baseRefName || ''),
515
+ status: this.mapGhStateToPrStatus(String(data.state || 'OPEN')),
516
+ };
517
+
518
+ if (data.body) {
519
+ pr.description = String(data.body);
520
+ }
521
+
522
+ if (data.author && typeof data.author === 'object') {
523
+ const login = (data.author as { login?: string }).login;
524
+ if (login) {
525
+ pr.author = login;
526
+ }
527
+ }
528
+
529
+ if (data.createdAt) {
530
+ pr.created_at = String(data.createdAt);
531
+ }
532
+
533
+ if (data.updatedAt) {
534
+ pr.updated_at = String(data.updatedAt);
535
+ }
536
+
537
+ if (data.mergedAt) {
538
+ pr.merged_at = String(data.mergedAt);
539
+ }
540
+
541
+ if (data.url) {
542
+ pr.url = String(data.url);
543
+ }
544
+
545
+ if (data.reviewRequests && Array.isArray(data.reviewRequests)) {
546
+ pr.reviewers = data.reviewRequests
547
+ .map((r: { login?: string }) => r.login || '')
548
+ .filter(Boolean);
549
+ }
550
+
551
+ return pr;
552
+ }
553
+
554
+ /**
555
+ * Map gh state to PullRequestStatus
556
+ */
557
+ private mapGhStateToPrStatus(state: string): PullRequestStatus {
558
+ switch (state.toUpperCase()) {
559
+ case 'OPEN':
560
+ return 'open';
561
+ case 'CLOSED':
562
+ return 'closed';
563
+ case 'MERGED':
564
+ return 'merged';
565
+ default:
566
+ return 'open';
567
+ }
568
+ }
569
+
570
+ /**
571
+ * Map PullRequestStatus to gh CLI state
572
+ */
573
+ private mapStatusToGh(status: PullRequestStatus): string | undefined {
574
+ switch (status) {
575
+ case 'open':
576
+ return 'open';
577
+ case 'closed':
578
+ return 'closed';
579
+ case 'merged':
580
+ return 'merged';
581
+ default:
582
+ return undefined;
583
+ }
584
+ }
585
+
586
+ /**
587
+ * Map gh review state to ReviewDecision
588
+ */
589
+ private mapGhStateToDecision(state: string): ReviewDecision {
590
+ switch (state.toUpperCase()) {
591
+ case 'APPROVED':
592
+ return 'approve';
593
+ case 'CHANGES_REQUESTED':
594
+ return 'request_changes';
595
+ case 'COMMENTED':
596
+ default:
597
+ return 'comment';
598
+ }
599
+ }
600
+
601
+ /**
602
+ * Check if error is a not found error
603
+ */
604
+ private isNotFoundError(error: unknown): boolean {
605
+ const message = error instanceof Error ? error.message : String(error);
606
+ return message.includes('Could not resolve') || message.includes('not found');
607
+ }
608
+
609
+ /**
610
+ * Wrap error in AdapterError
611
+ */
612
+ private wrapError(error: unknown, message: string): AdapterError {
613
+ return new AdapterError(
614
+ `${message}: ${error instanceof Error ? error.message : String(error)}`,
615
+ 'operation_failed',
616
+ this.getInfo(),
617
+ error instanceof Error ? error : undefined
618
+ );
619
+ }
620
+ }
621
+
622
+ /**
623
+ * Create a GitHub adapter
624
+ */
625
+ export function createGitHubAdapter(options: GitHubAdapterOptions): GitHubAdapter {
626
+ return new GitHubAdapter(options);
627
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Native Adapters
3
+ *
4
+ * Native implementations of adapter interfaces that don't require MCP servers.
5
+ * These serve as fallbacks when MCP servers are unavailable.
6
+ */
7
+
8
+ // Filesystem adapter
9
+ export type { FilesystemAdapterOptions } from './filesystem.js';
10
+ export { FilesystemAdapter, createFilesystemAdapter } from './filesystem.js';
11
+
12
+ // GitHub adapter
13
+ export type { GitHubAdapterOptions } from './github.js';
14
+ export { GitHubAdapter, createGitHubAdapter } from './github.js';
15
+
16
+ // Selector
17
+ export type { NativeAdapterSelectorOptions, NativeAdapterAvailability } from './selector.js';
18
+ export {
19
+ registerNativeAdapters,
20
+ checkNativeAdapterAvailability,
21
+ parseGitHubRemote,
22
+ } from './selector.js';