opencode-sdlc-plugin 1.0.0-alpha.0 → 1.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/config/presets/event-modeling.json +13 -2
- package/config/presets/minimal.json +29 -16
- package/config/presets/standard.json +13 -2
- package/dist/cli/index.js +1117 -1524
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +507 -0
- package/dist/index.js.map +1 -1
- package/dist/plugin/index.js +507 -0
- package/dist/plugin/index.js.map +1 -1
- package/package.json +2 -1
- package/config/presets/copilot-only.json +0 -76
- package/config/presets/enterprise.json +0 -79
- package/config/presets/solo-quick.json +0 -70
- package/config/presets/strict-tdd.json +0 -79
package/dist/index.d.ts
CHANGED
|
@@ -81,12 +81,12 @@ interface SubagentModelsConfig {
|
|
|
81
81
|
* Options passed to the install command
|
|
82
82
|
*/
|
|
83
83
|
interface InstallOptions {
|
|
84
|
-
preset: string;
|
|
85
84
|
yes: boolean;
|
|
86
85
|
advanced: boolean;
|
|
87
86
|
global: boolean;
|
|
88
87
|
local: boolean;
|
|
89
88
|
reconfigure?: boolean;
|
|
89
|
+
listProfiles?: boolean;
|
|
90
90
|
}
|
|
91
91
|
/**
|
|
92
92
|
* Authentication method for a provider
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { tmpdir, homedir, platform } from 'os';
|
|
2
2
|
import { existsSync, readFileSync, appendFileSync, mkdirSync, writeFileSync, statSync, truncateSync } from 'fs';
|
|
3
3
|
import { join, dirname } from 'path';
|
|
4
|
+
import { execSync } from 'child_process';
|
|
5
|
+
import { Octokit } from '@octokit/rest';
|
|
4
6
|
import { minimatch } from 'minimatch';
|
|
5
7
|
import { z } from 'zod';
|
|
6
8
|
import { readFile, mkdir, writeFile, readdir } from 'fs/promises';
|
|
@@ -173,14 +175,496 @@ function handleSessionError(event, tracker) {
|
|
|
173
175
|
});
|
|
174
176
|
}
|
|
175
177
|
}
|
|
178
|
+
function resolveGitHubToken(explicitToken) {
|
|
179
|
+
if (explicitToken) {
|
|
180
|
+
return explicitToken;
|
|
181
|
+
}
|
|
182
|
+
const envToken = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
|
|
183
|
+
if (envToken) {
|
|
184
|
+
return envToken;
|
|
185
|
+
}
|
|
186
|
+
try {
|
|
187
|
+
const ghToken = execSync("gh auth token", {
|
|
188
|
+
encoding: "utf-8",
|
|
189
|
+
timeout: 5e3,
|
|
190
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
191
|
+
}).trim();
|
|
192
|
+
if (ghToken) {
|
|
193
|
+
return ghToken;
|
|
194
|
+
}
|
|
195
|
+
} catch {
|
|
196
|
+
}
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
function isGitHubAuthenticated(token) {
|
|
200
|
+
return resolveGitHubToken(token) !== null;
|
|
201
|
+
}
|
|
202
|
+
var GitHubClient = class {
|
|
203
|
+
octokit;
|
|
204
|
+
owner;
|
|
205
|
+
repo;
|
|
206
|
+
constructor(options = {}) {
|
|
207
|
+
const token = resolveGitHubToken(options.token);
|
|
208
|
+
if (!token) {
|
|
209
|
+
throw new Error(
|
|
210
|
+
"GitHub authentication not found. Set GITHUB_TOKEN environment variable or run 'gh auth login'."
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
this.octokit = new Octokit({ auth: token });
|
|
214
|
+
this.owner = options.owner;
|
|
215
|
+
this.repo = options.repo;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Set the default owner/repo for operations
|
|
219
|
+
*/
|
|
220
|
+
setRepo(owner, repo) {
|
|
221
|
+
this.owner = owner;
|
|
222
|
+
this.repo = repo;
|
|
223
|
+
}
|
|
224
|
+
getRepoParams(owner, repo) {
|
|
225
|
+
const o = owner || this.owner;
|
|
226
|
+
const r = repo || this.repo;
|
|
227
|
+
if (!o || !r) {
|
|
228
|
+
throw new Error("Repository owner and name are required");
|
|
229
|
+
}
|
|
230
|
+
return { owner: o, repo: r };
|
|
231
|
+
}
|
|
232
|
+
// ==========================================================================
|
|
233
|
+
// Issues
|
|
234
|
+
// ==========================================================================
|
|
235
|
+
/**
|
|
236
|
+
* Get a single issue by number
|
|
237
|
+
*/
|
|
238
|
+
async getIssue(issueNumber, owner, repo) {
|
|
239
|
+
const params = this.getRepoParams(owner, repo);
|
|
240
|
+
const { data } = await this.octokit.issues.get({
|
|
241
|
+
...params,
|
|
242
|
+
issue_number: issueNumber
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
number: data.number,
|
|
246
|
+
title: data.title,
|
|
247
|
+
body: data.body ?? null,
|
|
248
|
+
url: data.html_url,
|
|
249
|
+
state: data.state,
|
|
250
|
+
labels: (data.labels || []).map(
|
|
251
|
+
(label) => typeof label === "string" ? { name: label } : { name: label.name || "" }
|
|
252
|
+
)
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* List issues in a repository
|
|
257
|
+
*/
|
|
258
|
+
async listIssues(options = {}, owner, repo) {
|
|
259
|
+
const params = this.getRepoParams(owner, repo);
|
|
260
|
+
const { data } = await this.octokit.issues.listForRepo({
|
|
261
|
+
...params,
|
|
262
|
+
state: options.state || "open",
|
|
263
|
+
labels: options.labels?.join(","),
|
|
264
|
+
per_page: options.limit || 30
|
|
265
|
+
});
|
|
266
|
+
return data.filter((issue) => !issue.pull_request).map((issue) => ({
|
|
267
|
+
number: issue.number,
|
|
268
|
+
title: issue.title,
|
|
269
|
+
url: issue.html_url,
|
|
270
|
+
state: issue.state
|
|
271
|
+
}));
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Update an issue's body
|
|
275
|
+
*/
|
|
276
|
+
async updateIssueBody(issueNumber, body, owner, repo) {
|
|
277
|
+
const params = this.getRepoParams(owner, repo);
|
|
278
|
+
await this.octokit.issues.update({
|
|
279
|
+
...params,
|
|
280
|
+
issue_number: issueNumber,
|
|
281
|
+
body
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
/**
|
|
285
|
+
* Add labels to an issue
|
|
286
|
+
*/
|
|
287
|
+
async addLabels(issueNumber, labels, owner, repo) {
|
|
288
|
+
const params = this.getRepoParams(owner, repo);
|
|
289
|
+
await this.octokit.issues.addLabels({
|
|
290
|
+
...params,
|
|
291
|
+
issue_number: issueNumber,
|
|
292
|
+
labels
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
// ==========================================================================
|
|
296
|
+
// Pull Requests
|
|
297
|
+
// ==========================================================================
|
|
298
|
+
/**
|
|
299
|
+
* Create a pull request
|
|
300
|
+
*/
|
|
301
|
+
async createPullRequest(options, owner, repo) {
|
|
302
|
+
const params = this.getRepoParams(owner, repo);
|
|
303
|
+
const { data } = await this.octokit.pulls.create({
|
|
304
|
+
...params,
|
|
305
|
+
title: options.title,
|
|
306
|
+
body: options.body,
|
|
307
|
+
head: options.head,
|
|
308
|
+
base: options.base || "main",
|
|
309
|
+
draft: options.draft
|
|
310
|
+
});
|
|
311
|
+
return {
|
|
312
|
+
number: data.number,
|
|
313
|
+
url: data.html_url
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Get pull request details
|
|
318
|
+
*/
|
|
319
|
+
async getPullRequest(prNumber, owner, repo) {
|
|
320
|
+
const params = this.getRepoParams(owner, repo);
|
|
321
|
+
const { data } = await this.octokit.pulls.get({
|
|
322
|
+
...params,
|
|
323
|
+
pull_number: prNumber
|
|
324
|
+
});
|
|
325
|
+
return {
|
|
326
|
+
number: data.number,
|
|
327
|
+
title: data.title,
|
|
328
|
+
body: data.body,
|
|
329
|
+
url: data.html_url,
|
|
330
|
+
state: data.merged ? "merged" : data.state,
|
|
331
|
+
mergeable: data.mergeable,
|
|
332
|
+
mergeableState: data.mergeable_state,
|
|
333
|
+
draft: data.draft || false
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* Get pull request status including checks and reviews
|
|
338
|
+
*/
|
|
339
|
+
async getPullRequestStatus(prNumber, owner, repo) {
|
|
340
|
+
const params = this.getRepoParams(owner, repo);
|
|
341
|
+
const pr = await this.getPullRequest(prNumber, owner, repo);
|
|
342
|
+
const { data: prData } = await this.octokit.pulls.get({
|
|
343
|
+
...params,
|
|
344
|
+
pull_number: prNumber
|
|
345
|
+
});
|
|
346
|
+
let checks = [];
|
|
347
|
+
try {
|
|
348
|
+
const { data: checksData } = await this.octokit.checks.listForRef({
|
|
349
|
+
...params,
|
|
350
|
+
ref: prData.head.sha
|
|
351
|
+
});
|
|
352
|
+
checks = checksData.check_runs.map((check) => ({
|
|
353
|
+
name: check.name,
|
|
354
|
+
status: check.status === "completed" ? check.conclusion === "success" ? "pass" : check.conclusion === "skipped" ? "skipped" : "fail" : "pending",
|
|
355
|
+
conclusion: check.conclusion
|
|
356
|
+
}));
|
|
357
|
+
} catch {
|
|
358
|
+
}
|
|
359
|
+
const { data: reviewsData } = await this.octokit.pulls.listReviews({
|
|
360
|
+
...params,
|
|
361
|
+
pull_number: prNumber
|
|
362
|
+
});
|
|
363
|
+
const reviews = reviewsData.map((review) => ({
|
|
364
|
+
reviewer: review.user?.login || "unknown",
|
|
365
|
+
state: review.state
|
|
366
|
+
}));
|
|
367
|
+
return {
|
|
368
|
+
number: pr.number,
|
|
369
|
+
url: pr.url,
|
|
370
|
+
state: pr.state,
|
|
371
|
+
mergeable: pr.mergeable,
|
|
372
|
+
checks,
|
|
373
|
+
reviews
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
/**
|
|
377
|
+
* Get pull request diff
|
|
378
|
+
*/
|
|
379
|
+
async getPullRequestDiff(prNumber, owner, repo) {
|
|
380
|
+
const params = this.getRepoParams(owner, repo);
|
|
381
|
+
const { data } = await this.octokit.pulls.get({
|
|
382
|
+
...params,
|
|
383
|
+
pull_number: prNumber,
|
|
384
|
+
mediaType: { format: "diff" }
|
|
385
|
+
});
|
|
386
|
+
return data;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Merge a pull request
|
|
390
|
+
*/
|
|
391
|
+
async mergePullRequest(options, owner, repo) {
|
|
392
|
+
const params = this.getRepoParams(owner, repo);
|
|
393
|
+
const { data } = await this.octokit.pulls.merge({
|
|
394
|
+
...params,
|
|
395
|
+
pull_number: options.prNumber,
|
|
396
|
+
merge_method: options.mergeMethod || "squash",
|
|
397
|
+
commit_title: options.commitTitle,
|
|
398
|
+
commit_message: options.commitMessage
|
|
399
|
+
});
|
|
400
|
+
return {
|
|
401
|
+
sha: data.sha,
|
|
402
|
+
merged: data.merged
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
// ==========================================================================
|
|
406
|
+
// Repositories
|
|
407
|
+
// ==========================================================================
|
|
408
|
+
/**
|
|
409
|
+
* Create a new repository
|
|
410
|
+
*/
|
|
411
|
+
async createRepository(options) {
|
|
412
|
+
const { data } = await this.octokit.repos.createForAuthenticatedUser({
|
|
413
|
+
name: options.name,
|
|
414
|
+
description: options.description,
|
|
415
|
+
private: options.private ?? false,
|
|
416
|
+
auto_init: options.autoInit ?? false
|
|
417
|
+
});
|
|
418
|
+
return {
|
|
419
|
+
id: data.id,
|
|
420
|
+
name: data.name,
|
|
421
|
+
fullName: data.full_name,
|
|
422
|
+
url: data.html_url,
|
|
423
|
+
private: data.private,
|
|
424
|
+
defaultBranch: data.default_branch || "main"
|
|
425
|
+
};
|
|
426
|
+
}
|
|
427
|
+
/**
|
|
428
|
+
* Get repository details
|
|
429
|
+
*/
|
|
430
|
+
async getRepository(owner, repo) {
|
|
431
|
+
const params = this.getRepoParams(owner, repo);
|
|
432
|
+
const { data } = await this.octokit.repos.get(params);
|
|
433
|
+
return {
|
|
434
|
+
id: data.id,
|
|
435
|
+
name: data.name,
|
|
436
|
+
fullName: data.full_name,
|
|
437
|
+
url: data.html_url,
|
|
438
|
+
private: data.private,
|
|
439
|
+
defaultBranch: data.default_branch || "main"
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
/**
|
|
443
|
+
* List repositories for the authenticated user
|
|
444
|
+
*/
|
|
445
|
+
async listUserRepos(options = {}) {
|
|
446
|
+
const { data } = await this.octokit.repos.listForAuthenticatedUser({
|
|
447
|
+
type: options.type || "owner",
|
|
448
|
+
per_page: options.limit || 30,
|
|
449
|
+
sort: "updated"
|
|
450
|
+
});
|
|
451
|
+
return data.map((repo) => ({
|
|
452
|
+
id: repo.id,
|
|
453
|
+
name: repo.name,
|
|
454
|
+
fullName: repo.full_name,
|
|
455
|
+
url: repo.html_url,
|
|
456
|
+
private: repo.private,
|
|
457
|
+
defaultBranch: repo.default_branch || "main"
|
|
458
|
+
}));
|
|
459
|
+
}
|
|
460
|
+
// ==========================================================================
|
|
461
|
+
// Projects (GraphQL required for Projects V2)
|
|
462
|
+
// ==========================================================================
|
|
463
|
+
/**
|
|
464
|
+
* List projects for the authenticated user
|
|
465
|
+
* Note: Projects V2 requires GraphQL API
|
|
466
|
+
*/
|
|
467
|
+
async listUserProjects(limit = 20) {
|
|
468
|
+
const query = `
|
|
469
|
+
query($first: Int!) {
|
|
470
|
+
viewer {
|
|
471
|
+
projectsV2(first: $first) {
|
|
472
|
+
nodes {
|
|
473
|
+
id
|
|
474
|
+
number
|
|
475
|
+
title
|
|
476
|
+
url
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
`;
|
|
482
|
+
const result = await this.octokit.graphql(query, { first: limit });
|
|
483
|
+
return result.viewer.projectsV2.nodes;
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* Get project by number for a user
|
|
487
|
+
*/
|
|
488
|
+
async getUserProject(userLogin, projectNumber) {
|
|
489
|
+
const query = `
|
|
490
|
+
query($login: String!, $number: Int!) {
|
|
491
|
+
user(login: $login) {
|
|
492
|
+
projectV2(number: $number) {
|
|
493
|
+
id
|
|
494
|
+
number
|
|
495
|
+
title
|
|
496
|
+
url
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
`;
|
|
501
|
+
try {
|
|
502
|
+
const result = await this.octokit.graphql(query, { login: userLogin, number: projectNumber });
|
|
503
|
+
return result.user.projectV2;
|
|
504
|
+
} catch {
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* Link a repository to a project
|
|
510
|
+
* Note: This creates a linked repository in the project
|
|
511
|
+
*/
|
|
512
|
+
async linkRepoToProject(projectId, repoOwner, repoName) {
|
|
513
|
+
const { data: repoData } = await this.octokit.repos.get({
|
|
514
|
+
owner: repoOwner,
|
|
515
|
+
repo: repoName
|
|
516
|
+
});
|
|
517
|
+
const mutation = `
|
|
518
|
+
mutation($projectId: ID!, $repositoryId: ID!) {
|
|
519
|
+
linkProjectV2ToRepository(input: { projectId: $projectId, repositoryId: $repositoryId }) {
|
|
520
|
+
repository {
|
|
521
|
+
id
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
`;
|
|
526
|
+
await this.octokit.graphql(mutation, {
|
|
527
|
+
projectId,
|
|
528
|
+
repositoryId: repoData.node_id
|
|
529
|
+
});
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Add an issue to a project
|
|
533
|
+
*/
|
|
534
|
+
async addIssueToProject(projectId, issueNodeId) {
|
|
535
|
+
const mutation = `
|
|
536
|
+
mutation($projectId: ID!, $contentId: ID!) {
|
|
537
|
+
addProjectV2ItemById(input: { projectId: $projectId, contentId: $contentId }) {
|
|
538
|
+
item {
|
|
539
|
+
id
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
`;
|
|
544
|
+
const result = await this.octokit.graphql(mutation, {
|
|
545
|
+
projectId,
|
|
546
|
+
contentId: issueNodeId
|
|
547
|
+
});
|
|
548
|
+
return result.addProjectV2ItemById.item.id;
|
|
549
|
+
}
|
|
550
|
+
// ==========================================================================
|
|
551
|
+
// Branch Protection / Rulesets
|
|
552
|
+
// ==========================================================================
|
|
553
|
+
/**
|
|
554
|
+
* Create a branch ruleset with common protection rules
|
|
555
|
+
*/
|
|
556
|
+
async createBranchRuleset(options, owner, repo) {
|
|
557
|
+
const params = this.getRepoParams(owner, repo);
|
|
558
|
+
const rules = [];
|
|
559
|
+
if (options.requirePullRequest) {
|
|
560
|
+
rules.push({
|
|
561
|
+
type: "pull_request",
|
|
562
|
+
parameters: {
|
|
563
|
+
dismiss_stale_reviews_on_push: options.dismissStaleReviews ?? false,
|
|
564
|
+
require_code_owner_review: options.requireCodeOwnerReview ?? false,
|
|
565
|
+
require_last_push_approval: false,
|
|
566
|
+
required_approving_review_count: options.requiredApprovals ?? 1,
|
|
567
|
+
required_review_thread_resolution: false
|
|
568
|
+
}
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
if (options.requireSignedCommits) {
|
|
572
|
+
rules.push({ type: "required_signatures" });
|
|
573
|
+
}
|
|
574
|
+
if (options.requireLinearHistory) {
|
|
575
|
+
rules.push({ type: "required_linear_history" });
|
|
576
|
+
}
|
|
577
|
+
if (options.preventDeletion) {
|
|
578
|
+
rules.push({ type: "deletion" });
|
|
579
|
+
}
|
|
580
|
+
if (options.preventForcePush) {
|
|
581
|
+
rules.push({ type: "non_fast_forward" });
|
|
582
|
+
}
|
|
583
|
+
const { data } = await this.octokit.repos.createRepoRuleset({
|
|
584
|
+
...params,
|
|
585
|
+
name: options.name,
|
|
586
|
+
enforcement: options.enforcement,
|
|
587
|
+
conditions: {
|
|
588
|
+
ref_name: {
|
|
589
|
+
include: options.targetBranches,
|
|
590
|
+
exclude: []
|
|
591
|
+
}
|
|
592
|
+
},
|
|
593
|
+
rules
|
|
594
|
+
});
|
|
595
|
+
return { id: data.id };
|
|
596
|
+
}
|
|
597
|
+
// ==========================================================================
|
|
598
|
+
// Utility Methods
|
|
599
|
+
// ==========================================================================
|
|
600
|
+
/**
|
|
601
|
+
* Get the authenticated user's login
|
|
602
|
+
*/
|
|
603
|
+
async getAuthenticatedUser() {
|
|
604
|
+
const { data } = await this.octokit.users.getAuthenticated();
|
|
605
|
+
return { login: data.login, id: data.id };
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Check if a repository exists
|
|
609
|
+
*/
|
|
610
|
+
async repoExists(owner, repo) {
|
|
611
|
+
try {
|
|
612
|
+
await this.octokit.repos.get({ owner, repo });
|
|
613
|
+
return true;
|
|
614
|
+
} catch {
|
|
615
|
+
return false;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
};
|
|
619
|
+
function createGitHubClientIfAuthenticated(options = {}) {
|
|
620
|
+
if (!isGitHubAuthenticated(options.token)) {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
try {
|
|
624
|
+
return new GitHubClient(options);
|
|
625
|
+
} catch {
|
|
626
|
+
return null;
|
|
627
|
+
}
|
|
628
|
+
}
|
|
176
629
|
|
|
177
630
|
// src/plugin/utils/github-issues.ts
|
|
178
631
|
var log3 = createPluginLogger("github-issues");
|
|
632
|
+
var cachedClient = null;
|
|
633
|
+
var cachedClientRepo = null;
|
|
634
|
+
function getClient(config) {
|
|
635
|
+
if (!config.github?.owner || !config.github?.repo) return null;
|
|
636
|
+
const repoKey = `${config.github.owner}/${config.github.repo}`;
|
|
637
|
+
if (cachedClient && cachedClientRepo === repoKey) {
|
|
638
|
+
return cachedClient;
|
|
639
|
+
}
|
|
640
|
+
cachedClient = createGitHubClientIfAuthenticated({
|
|
641
|
+
owner: config.github.owner,
|
|
642
|
+
repo: config.github.repo
|
|
643
|
+
});
|
|
644
|
+
cachedClientRepo = cachedClient ? repoKey : null;
|
|
645
|
+
return cachedClient;
|
|
646
|
+
}
|
|
179
647
|
function getRepo(config) {
|
|
180
648
|
if (!config.github?.owner || !config.github?.repo) return null;
|
|
181
649
|
return { owner: config.github.owner, repo: config.github.repo };
|
|
182
650
|
}
|
|
183
651
|
async function fetchIssue(ctx, config, issueNumber) {
|
|
652
|
+
const client = getClient(config);
|
|
653
|
+
if (client) {
|
|
654
|
+
try {
|
|
655
|
+
const issue = await client.getIssue(issueNumber);
|
|
656
|
+
return {
|
|
657
|
+
number: issue.number,
|
|
658
|
+
title: issue.title,
|
|
659
|
+
body: issue.body,
|
|
660
|
+
url: issue.url,
|
|
661
|
+
state: issue.state,
|
|
662
|
+
labels: issue.labels
|
|
663
|
+
};
|
|
664
|
+
} catch (error) {
|
|
665
|
+
log3.warn("Failed to fetch issue via Octokit", { issueNumber, error: String(error) });
|
|
666
|
+
}
|
|
667
|
+
}
|
|
184
668
|
const repo = getRepo(config);
|
|
185
669
|
if (!repo) return null;
|
|
186
670
|
try {
|
|
@@ -194,6 +678,20 @@ async function fetchIssue(ctx, config, issueNumber) {
|
|
|
194
678
|
}
|
|
195
679
|
}
|
|
196
680
|
async function listIssues(ctx, config, status, limit = 20) {
|
|
681
|
+
const client = getClient(config);
|
|
682
|
+
if (client && !status) {
|
|
683
|
+
try {
|
|
684
|
+
const issues = await client.listIssues({ limit });
|
|
685
|
+
return issues.map((issue) => ({
|
|
686
|
+
number: issue.number,
|
|
687
|
+
title: issue.title,
|
|
688
|
+
url: issue.url,
|
|
689
|
+
state: issue.state
|
|
690
|
+
}));
|
|
691
|
+
} catch (error) {
|
|
692
|
+
log3.warn("Failed to list issues via Octokit", { error: String(error) });
|
|
693
|
+
}
|
|
694
|
+
}
|
|
197
695
|
const repo = getRepo(config);
|
|
198
696
|
if (!repo) return [];
|
|
199
697
|
try {
|
|
@@ -219,6 +717,15 @@ async function moveIssueToStatus(ctx, config, issueNumber, status) {
|
|
|
219
717
|
}
|
|
220
718
|
}
|
|
221
719
|
async function updateIssueBody(ctx, config, issueNumber, body) {
|
|
720
|
+
const client = getClient(config);
|
|
721
|
+
if (client) {
|
|
722
|
+
try {
|
|
723
|
+
await client.updateIssueBody(issueNumber, body);
|
|
724
|
+
return true;
|
|
725
|
+
} catch (error) {
|
|
726
|
+
log3.warn("Failed to update issue body via Octokit", { issueNumber, error: String(error) });
|
|
727
|
+
}
|
|
728
|
+
}
|
|
222
729
|
const repo = getRepo(config);
|
|
223
730
|
if (!repo) return false;
|
|
224
731
|
try {
|