@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.
- package/.prettierrc +9 -0
- package/AGENT_SPEC.md +347 -0
- package/ARCHITECTURE.md +547 -0
- package/DRAFT_PRD.md +1440 -0
- package/IMPLEMENTATION_PLAN.md +256 -0
- package/PRODUCT.md +473 -0
- package/README.md +303 -0
- package/WORKFLOWS.md +295 -0
- package/agents/_templates/ic-engineer.md +185 -0
- package/agents/_templates/reviewer.md +182 -0
- package/agents/backend-engineer.yaml +72 -0
- package/agents/devops-engineer.yaml +72 -0
- package/agents/engineering-manager.yaml +70 -0
- package/agents/examples/android-engineer.md +302 -0
- package/agents/examples/backend-engineer.md +320 -0
- package/agents/examples/devops-engineer.md +742 -0
- package/agents/examples/engineering-manager.md +469 -0
- package/agents/examples/frontend-engineer.md +58 -0
- package/agents/examples/product-manager.md +315 -0
- package/agents/examples/qa-engineer.md +371 -0
- package/agents/examples/security-engineer.md +525 -0
- package/agents/examples/solutions-architect.md +351 -0
- package/agents/examples/wearos-engineer.md +359 -0
- package/agents/frontend-engineer.yaml +72 -0
- package/commands/core/check-inbox.md +34 -0
- package/commands/core/delegate.md +30 -0
- package/commands/core/git-commit.md +144 -0
- package/commands/core/pr-create.md +193 -0
- package/commands/core/review.md +56 -0
- package/commands/core/sprint-status.md +65 -0
- package/commands/optional/docs-update.md +200 -0
- package/commands/optional/jira-create.md +200 -0
- package/commands/optional/jira-transition.md +184 -0
- package/commands/optional/worktree-cleanup.md +167 -0
- package/commands/optional/worktree-setup.md +110 -0
- package/dist/cli/index.js +4037 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +2978 -0
- package/dist/index.js +3867 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +29 -0
- package/jest.config.js +22 -0
- package/knowledge-library/README.md +118 -0
- package/knowledge-library/android-engineer/context/current.txt +42 -0
- package/knowledge-library/android-engineer/control/decisions.txt +9 -0
- package/knowledge-library/android-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/android-engineer/control/objectives.txt +26 -0
- package/knowledge-library/android-engineer/history/.gitkeep +0 -0
- package/knowledge-library/android-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/android-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/android-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/architecture.txt +61 -0
- package/knowledge-library/backend-engineer/context/current.txt +42 -0
- package/knowledge-library/backend-engineer/control/decisions.txt +9 -0
- package/knowledge-library/backend-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/backend-engineer/control/objectives.txt +26 -0
- package/knowledge-library/backend-engineer/history/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/backend-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/context.txt +52 -0
- package/knowledge-library/devops-engineer/context/current.txt +42 -0
- package/knowledge-library/devops-engineer/control/decisions.txt +9 -0
- package/knowledge-library/devops-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/devops-engineer/control/objectives.txt +26 -0
- package/knowledge-library/devops-engineer/history/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/devops-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/context/current.txt +40 -0
- package/knowledge-library/engineering-manager/control/decisions.txt +9 -0
- package/knowledge-library/engineering-manager/control/objectives.txt +27 -0
- package/knowledge-library/engineering-manager/history/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/engineering-manager/tech/.gitkeep +0 -0
- package/knowledge-library/prd.txt +81 -0
- package/knowledge-library/product-manager/context/current.txt +42 -0
- package/knowledge-library/product-manager/control/decisions.txt +9 -0
- package/knowledge-library/product-manager/control/dependencies.txt +19 -0
- package/knowledge-library/product-manager/control/objectives.txt +26 -0
- package/knowledge-library/product-manager/history/.gitkeep +0 -0
- package/knowledge-library/product-manager/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/product-manager/outbox/.gitkeep +0 -0
- package/knowledge-library/product-manager/tech/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/context/current.txt +42 -0
- package/knowledge-library/qa-engineer/control/decisions.txt +9 -0
- package/knowledge-library/qa-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/qa-engineer/control/objectives.txt +26 -0
- package/knowledge-library/qa-engineer/history/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/qa-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/security-engineer/context/current.txt +42 -0
- package/knowledge-library/security-engineer/control/decisions.txt +9 -0
- package/knowledge-library/security-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/security-engineer/control/objectives.txt +26 -0
- package/knowledge-library/security-engineer/history/.gitkeep +0 -0
- package/knowledge-library/security-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/security-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/security-engineer/tech/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/context/current.txt +42 -0
- package/knowledge-library/solutions-architect/control/decisions.txt +9 -0
- package/knowledge-library/solutions-architect/control/dependencies.txt +19 -0
- package/knowledge-library/solutions-architect/control/objectives.txt +26 -0
- package/knowledge-library/solutions-architect/history/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/outbox/.gitkeep +0 -0
- package/knowledge-library/solutions-architect/tech/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/context/current.txt +42 -0
- package/knowledge-library/wearos-engineer/control/decisions.txt +9 -0
- package/knowledge-library/wearos-engineer/control/dependencies.txt +19 -0
- package/knowledge-library/wearos-engineer/control/objectives.txt +26 -0
- package/knowledge-library/wearos-engineer/history/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/inbox/processed/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/outbox/.gitkeep +0 -0
- package/knowledge-library/wearos-engineer/tech/.gitkeep +0 -0
- package/package.json +66 -0
- package/schemas/agent.schema.json +171 -0
- package/schemas/coreai.config.schema.json +257 -0
- package/scripts/add-agent.sh +323 -0
- package/scripts/install.sh +354 -0
- package/src/adapters/factory.test.ts +386 -0
- package/src/adapters/factory.ts +305 -0
- package/src/adapters/index.ts +113 -0
- package/src/adapters/interfaces.ts +268 -0
- package/src/adapters/mcp/client.test.ts +130 -0
- package/src/adapters/mcp/client.ts +451 -0
- package/src/adapters/mcp/discovery.test.ts +315 -0
- package/src/adapters/mcp/discovery.ts +340 -0
- package/src/adapters/mcp/index.ts +66 -0
- package/src/adapters/mcp/mapper.test.ts +218 -0
- package/src/adapters/mcp/mapper.ts +536 -0
- package/src/adapters/mcp/registry.test.ts +433 -0
- package/src/adapters/mcp/registry.ts +550 -0
- package/src/adapters/mcp/types.ts +258 -0
- package/src/adapters/native/filesystem.test.ts +350 -0
- package/src/adapters/native/filesystem.ts +393 -0
- package/src/adapters/native/github.test.ts +173 -0
- package/src/adapters/native/github.ts +627 -0
- package/src/adapters/native/index.ts +22 -0
- package/src/adapters/native/selector.test.ts +224 -0
- package/src/adapters/native/selector.ts +150 -0
- package/src/adapters/types.ts +270 -0
- package/src/agents/compiler.test.ts +399 -0
- package/src/agents/compiler.ts +359 -0
- package/src/agents/index.ts +36 -0
- package/src/agents/loader.test.ts +319 -0
- package/src/agents/loader.ts +143 -0
- package/src/agents/resolver.test.ts +282 -0
- package/src/agents/resolver.ts +262 -0
- package/src/agents/types.ts +87 -0
- package/src/cache/index.ts +38 -0
- package/src/cache/interfaces.ts +283 -0
- package/src/cache/manager.test.ts +266 -0
- package/src/cache/manager.ts +388 -0
- package/src/cache/provider.test.ts +485 -0
- package/src/cache/provider.ts +745 -0
- package/src/cache/types.test.ts +192 -0
- package/src/cache/types.ts +313 -0
- package/src/cli/commands/build.test.ts +248 -0
- package/src/cli/commands/build.ts +244 -0
- package/src/cli/commands/cache.test.ts +221 -0
- package/src/cli/commands/cache.ts +229 -0
- package/src/cli/commands/index.ts +63 -0
- package/src/cli/commands/init.test.ts +173 -0
- package/src/cli/commands/init.ts +296 -0
- package/src/cli/commands/skills.test.ts +272 -0
- package/src/cli/commands/skills.ts +348 -0
- package/src/cli/commands/status.test.ts +392 -0
- package/src/cli/commands/status.ts +332 -0
- package/src/cli/commands/sync.test.ts +213 -0
- package/src/cli/commands/sync.ts +251 -0
- package/src/cli/commands/validate.test.ts +216 -0
- package/src/cli/commands/validate.ts +340 -0
- package/src/cli/index.test.ts +190 -0
- package/src/cli/index.ts +493 -0
- package/src/commands/context.test.ts +163 -0
- package/src/commands/context.ts +111 -0
- package/src/commands/index.ts +56 -0
- package/src/commands/loader.test.ts +273 -0
- package/src/commands/loader.ts +355 -0
- package/src/commands/registry.test.ts +384 -0
- package/src/commands/registry.ts +248 -0
- package/src/commands/runner.test.ts +297 -0
- package/src/commands/runner.ts +222 -0
- package/src/commands/types.ts +361 -0
- package/src/config/index.ts +19 -0
- package/src/config/loader.test.ts +262 -0
- package/src/config/loader.ts +188 -0
- package/src/config/types.ts +154 -0
- package/src/context/index.ts +14 -0
- package/src/context/loader.test.ts +334 -0
- package/src/context/loader.ts +357 -0
- package/src/index.test.ts +13 -0
- package/src/index.ts +244 -0
- package/src/knowledge-library/index.ts +44 -0
- package/src/knowledge-library/manager.test.ts +536 -0
- package/src/knowledge-library/manager.ts +804 -0
- package/src/knowledge-library/types.ts +432 -0
- package/src/skills/generator.test.ts +602 -0
- package/src/skills/generator.ts +491 -0
- package/src/skills/index.ts +27 -0
- package/src/skills/templates.ts +520 -0
- package/src/skills/types.ts +251 -0
- package/templates/completion-report.md +72 -0
- package/templates/feedback.md +56 -0
- package/templates/project-files/CLAUDE.md.template +109 -0
- package/templates/project-files/coreai.json.example +47 -0
- package/templates/project-files/mcp.json.template +20 -0
- package/templates/review-complete.md +64 -0
- package/templates/review-request.md +67 -0
- package/templates/task-assignment.md +51 -0
- package/tsconfig.build.json +4 -0
- package/tsconfig.json +26 -0
- 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';
|