ai-git-tools 2.0.39 → 2.0.40

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.
@@ -394,4 +394,88 @@ export class GitHubAPI {
394
394
  log.error('無法添加團隊 reviewers,請手動操作');
395
395
  }
396
396
  }
397
+
398
+ // ─────────────────────────────────────────────────────────────
399
+ // Issue 評論功能(供 ai autodev 使用)
400
+ // ─────────────────────────────────────────────────────────────
401
+
402
+ /**
403
+ * 在 Issue 上發佈評論
404
+ * @param {string} owner
405
+ * @param {string} repo
406
+ * @param {number} issueNumber
407
+ * @param {string} body - Markdown 內容
408
+ * @returns {{ id: number, url: string }}
409
+ */
410
+ postCommentOnIssue(owner, repo, issueNumber, body) {
411
+ const tmpFile = `/tmp/ai-autodev-comment-${Date.now()}.json`;
412
+ try {
413
+ const payload = JSON.stringify({ body });
414
+ writeFileSync(tmpFile, payload, 'utf-8');
415
+ const result = execSync(
416
+ `gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --input "${tmpFile}"`,
417
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
418
+ );
419
+ const data = JSON.parse(result);
420
+ return { id: data.id, url: data.html_url };
421
+ } finally {
422
+ this.safeUnlink(tmpFile);
423
+ }
424
+ }
425
+
426
+ /**
427
+ * 更新已存在的 Issue 評論
428
+ * @param {string} owner
429
+ * @param {string} repo
430
+ * @param {number} commentId
431
+ * @param {string} body
432
+ * @returns {{ id: number, url: string }}
433
+ */
434
+ editIssueComment(owner, repo, commentId, body) {
435
+ const tmpFile = `/tmp/ai-autodev-edit-${Date.now()}.json`;
436
+ try {
437
+ const payload = JSON.stringify({ body });
438
+ writeFileSync(tmpFile, payload, 'utf-8');
439
+ const result = execSync(
440
+ `gh api repos/${owner}/${repo}/issues/comments/${commentId} --input "${tmpFile}" -X PATCH`,
441
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
442
+ );
443
+ const data = JSON.parse(result);
444
+ return { id: data.id, url: data.html_url };
445
+ } finally {
446
+ this.safeUnlink(tmpFile);
447
+ }
448
+ }
449
+
450
+ /**
451
+ * 尋找 Issue 上含有特定 marker 的評論(用於重複執行時更新而非重建)
452
+ * @param {string} owner
453
+ * @param {string} repo
454
+ * @param {number} issueNumber
455
+ * @param {string} marker - 識別字串
456
+ * @returns {{ id: number }|null}
457
+ */
458
+ findCommentWithMarker(owner, repo, issueNumber, marker) {
459
+ try {
460
+ const result = execSync(
461
+ `gh api repos/${owner}/${repo}/issues/${issueNumber}/comments --jq '.[] | {id, body}'`,
462
+ { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }
463
+ );
464
+ // 每行一個 JSON 物件
465
+ const lines = result.trim().split('\n').filter(Boolean);
466
+ for (const line of lines) {
467
+ try {
468
+ const obj = JSON.parse(line);
469
+ if (obj.body && obj.body.includes(marker)) {
470
+ return { id: obj.id };
471
+ }
472
+ } catch (_) {
473
+ // 忽略解析錯誤
474
+ }
475
+ }
476
+ return null;
477
+ } catch (_) {
478
+ return null;
479
+ }
480
+ }
397
481
  }