difit 3.1.17 → 4.0.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/README.ja.md +30 -3
- package/README.ko.md +30 -3
- package/README.md +30 -3
- package/README.zh.md +30 -3
- package/dist/cli/github.d.ts +65 -0
- package/dist/cli/github.js +296 -0
- package/dist/cli/github.test.d.ts +1 -0
- package/dist/cli/github.test.js +341 -0
- package/dist/cli/index.js +26 -1
- package/dist/cli/index.test.js +206 -4
- package/dist/cli/utils.d.ts +2 -8
- package/dist/cli/utils.js +43 -56
- package/dist/cli/utils.test.js +61 -67
- package/dist/client/assets/{_basePickBy-r8KiD0PT.js → _basePickBy-B9N-f0iT.js} +1 -1
- package/dist/client/assets/{_baseUniq-WYpg9s_f.js → _baseUniq-tbL7nVvN.js} +1 -1
- package/dist/client/assets/{arc-BZWd656X.js → arc-BOY-7mep.js} +1 -1
- package/dist/client/assets/{architectureDiagram-2XIMDMQ5-BiaoV1Oc.js → architectureDiagram-2XIMDMQ5-59AvHaSB.js} +1 -1
- package/dist/client/assets/{blockDiagram-WCTKOSBZ-T1RU4TI6.js → blockDiagram-WCTKOSBZ-DXIlumQk.js} +1 -1
- package/dist/client/assets/{c4Diagram-IC4MRINW-C1aQSMsj.js → c4Diagram-IC4MRINW-BbfZ0uRn.js} +1 -1
- package/dist/client/assets/channel-cZXsTJxA.js +1 -0
- package/dist/client/assets/{chunk-4BX2VUAB-DFcwtPlK.js → chunk-4BX2VUAB-l7rcB2IW.js} +1 -1
- package/dist/client/assets/{chunk-55IACEB6-Bl3vvNDx.js → chunk-55IACEB6-CrZL3qv9.js} +1 -1
- package/dist/client/assets/{chunk-FMBD7UC4-B_2obFwM.js → chunk-FMBD7UC4-CrKv7ndg.js} +1 -1
- package/dist/client/assets/{chunk-JSJVCQXG-BrSq4jyX.js → chunk-JSJVCQXG-DyBDhAEM.js} +1 -1
- package/dist/client/assets/{chunk-KX2RTZJC-18m3UONJ.js → chunk-KX2RTZJC-By5mkZmU.js} +1 -1
- package/dist/client/assets/{chunk-NQ4KR5QH-hFDbMzZU.js → chunk-NQ4KR5QH-C30p9xRx.js} +1 -1
- package/dist/client/assets/{chunk-QZHKN3VN-CyCFXX2j.js → chunk-QZHKN3VN-DVlhR2wU.js} +1 -1
- package/dist/client/assets/{chunk-WL4C6EOR-BDdHa7t1.js → chunk-WL4C6EOR-Cn7a6CO3.js} +1 -1
- package/dist/client/assets/classDiagram-VBA2DB6C-B_coIPEy.js +1 -0
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-B_coIPEy.js +1 -0
- package/dist/client/assets/clone-BjaT2HOk.js +1 -0
- package/dist/client/assets/{cose-bilkent-S5V4N54A-D7t718Sq.js → cose-bilkent-S5V4N54A-LyauIk_9.js} +1 -1
- package/dist/client/assets/{dagre-KLK3FWXG-DJXcjsV8.js → dagre-KLK3FWXG-DRWb2KE3.js} +1 -1
- package/dist/client/assets/{diagram-E7M64L7V-DL8ck_Al.js → diagram-E7M64L7V-ChT6mNWK.js} +1 -1
- package/dist/client/assets/{diagram-IFDJBPK2-NTxUWyD3.js → diagram-IFDJBPK2-CqbTduoP.js} +1 -1
- package/dist/client/assets/{diagram-P4PSJMXO-CGkcnGxk.js → diagram-P4PSJMXO-Bzv5Z3ri.js} +1 -1
- package/dist/client/assets/{erDiagram-INFDFZHY-BqpbHQrZ.js → erDiagram-INFDFZHY-CvXfUZ4L.js} +1 -1
- package/dist/client/assets/{flowDiagram-PKNHOUZH-B-DK3_9I.js → flowDiagram-PKNHOUZH-CxmpNUKq.js} +1 -1
- package/dist/client/assets/{ganttDiagram-A5KZAMGK-BK1C57ll.js → ganttDiagram-A5KZAMGK-9LpZCsg6.js} +1 -1
- package/dist/client/assets/{gitGraphDiagram-K3NZZRJ6-Duxlcz8R.js → gitGraphDiagram-K3NZZRJ6-C6yZOrQJ.js} +1 -1
- package/dist/client/assets/{graph-C7r58m4O.js → graph-bUZ7uHLW.js} +1 -1
- package/dist/client/assets/index-BLNN1bfE.js +98 -0
- package/dist/client/assets/index-VxkpzDXr.css +1 -0
- package/dist/client/assets/{infoDiagram-LFFYTUFH-Bqt-4V9X.js → infoDiagram-LFFYTUFH-Djdy3W21.js} +1 -1
- package/dist/client/assets/{ishikawaDiagram-PHBUUO56-B1ZVSkls.js → ishikawaDiagram-PHBUUO56-oOdwCpeS.js} +1 -1
- package/dist/client/assets/{journeyDiagram-4ABVD52K-LSEcxqrO.js → journeyDiagram-4ABVD52K-DTb_nGAw.js} +1 -1
- package/dist/client/assets/{kanban-definition-K7BYSVSG-CldPadPs.js → kanban-definition-K7BYSVSG-CMtP7pHA.js} +1 -1
- package/dist/client/assets/{layout-NpxIVVkp.js → layout-CXr5MatK.js} +1 -1
- package/dist/client/assets/{linear-JpKpxaS-.js → linear-pOMS9pjV.js} +1 -1
- package/dist/client/assets/{mermaid.core-gANNEmg0.js → mermaid.core-DV5JJ1Ie.js} +4 -4
- package/dist/client/assets/{mindmap-definition-YRQLILUH-ewFI1yc5.js → mindmap-definition-YRQLILUH-DN-sbonc.js} +1 -1
- package/dist/client/assets/{pieDiagram-SKSYHLDU-CWlAr2t8.js → pieDiagram-SKSYHLDU-tAHCkgh1.js} +1 -1
- package/dist/client/assets/{prism-csharp-CxRfePTX.js → prism-csharp-5CQ0RcEE.js} +1 -1
- package/dist/client/assets/{prism-elixir-B0H1PC_E.js → prism-elixir-BSOTyVg2.js} +1 -1
- package/dist/client/assets/{prism-hcl-Csmcce-t.js → prism-hcl-BYvi1mtM.js} +1 -1
- package/dist/client/assets/{prism-java-BRzwomgj.js → prism-java-DMU2FM4X.js} +1 -1
- package/dist/client/assets/{prism-perl-DQMRA6u_.js → prism-perl-CpfvaEQk.js} +1 -1
- package/dist/client/assets/{prism-php-C6fR1C7-.js → prism-php-SC920LoD.js} +1 -1
- package/dist/client/assets/{prism-ruby-CWeh27h1.js → prism-ruby-DZph-YiO.js} +1 -1
- package/dist/client/assets/{prism-solidity-3wCU4ra_.js → prism-solidity-qTLbmiAT.js} +1 -1
- package/dist/client/assets/{quadrantDiagram-337W2JSQ-D76E3PCD.js → quadrantDiagram-337W2JSQ-B0wODmgR.js} +1 -1
- package/dist/client/assets/{requirementDiagram-Z7DCOOCP-C49LvKzR.js → requirementDiagram-Z7DCOOCP-A3aeHC06.js} +1 -1
- package/dist/client/assets/{sankeyDiagram-WA2Y5GQK-DOvEhLMf.js → sankeyDiagram-WA2Y5GQK-BWa6kZhG.js} +1 -1
- package/dist/client/assets/{sequenceDiagram-2WXFIKYE-BR6dsfEq.js → sequenceDiagram-2WXFIKYE-Cx_COX9G.js} +1 -1
- package/dist/client/assets/{stateDiagram-RAJIS63D-CHII26YE.js → stateDiagram-RAJIS63D-BXGnN6rZ.js} +1 -1
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-CMw3xNha.js +1 -0
- package/dist/client/assets/{timeline-definition-YZTLITO2-DhUTiAsW.js → timeline-definition-YZTLITO2-DbqaUm9k.js} +1 -1
- package/dist/client/assets/{treemap-KZPCXAKY-C0Rh3R0y.js → treemap-KZPCXAKY-CfEujPCR.js} +1 -1
- package/dist/client/assets/{vennDiagram-LZ73GAT5-CWt3wBDG.js → vennDiagram-LZ73GAT5-CqJE8CAD.js} +1 -1
- package/dist/client/assets/{xychartDiagram-JWTSCODW-DhwJwxGz.js → xychartDiagram-JWTSCODW-CfdDvzHC.js} +1 -1
- package/dist/client/index.html +2 -2
- package/dist/server/generated-file-check.js +113 -58
- package/dist/server/generated-file-check.test.js +2 -0
- package/dist/server/git-diff.d.ts +1 -0
- package/dist/server/git-diff.js +33 -6
- package/dist/server/git-diff.test.js +45 -0
- package/dist/server/server.d.ts +2 -0
- package/dist/server/server.js +107 -34
- package/dist/server/server.test.js +120 -0
- package/dist/types/diff.d.ts +73 -14
- package/dist/utils/commentFormatting.d.ts +4 -2
- package/dist/utils/commentFormatting.js +57 -19
- package/dist/utils/commentImports.d.ts +9 -0
- package/dist/utils/commentImports.js +264 -0
- package/dist/utils/commentImports.test.d.ts +1 -0
- package/dist/utils/commentImports.test.js +197 -0
- package/package.json +3 -3
- package/dist/client/assets/channel-C081SflL.js +0 -1
- package/dist/client/assets/classDiagram-VBA2DB6C-CD8hB8X7.js +0 -1
- package/dist/client/assets/classDiagram-v2-RAHNMMFH-CD8hB8X7.js +0 -1
- package/dist/client/assets/clone-DL1yO1kL.js +0 -1
- package/dist/client/assets/index-DcsVWNsS.css +0 -1
- package/dist/client/assets/index-Igyd6olF.js +0 -92
- package/dist/client/assets/stateDiagram-v2-FVOUBMTO-DtCFGPiV.js +0 -1
package/README.ja.md
CHANGED
|
@@ -28,9 +28,14 @@ difit # 最新コミットのdiffをWebUIで表示
|
|
|
28
28
|
AIエージェントから使えるようにする
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npx skills add yoshiko-pg/difit #
|
|
31
|
+
npx skills add yoshiko-pg/difit # エージェントにスキル群を追加
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
インストールされる主な skill:
|
|
35
|
+
|
|
36
|
+
- `difit`: コード変更後に difit を使ってユーザーへレビューを依頼する
|
|
37
|
+
- `difit-review`: 特定の diff や PR をレビューし、指摘や解説をコメントとして事前投入した difit を起動する
|
|
38
|
+
|
|
34
39
|
## 🚀 使い方
|
|
35
40
|
|
|
36
41
|
### 基本的な使い方
|
|
@@ -73,6 +78,7 @@ difit --pr https://github.com/owner/repo/pull/123
|
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
`--pr` モードでは、内部で `gh pr diff --patch` を実行してパッチを取得します。
|
|
81
|
+
加えて、PR 上の未解決 inline review thread も起動時コメントとして取り込み、difit 上でそのまま表示します。
|
|
76
82
|
|
|
77
83
|
認証は GitHub CLI が処理します:
|
|
78
84
|
|
|
@@ -86,6 +92,21 @@ Enterprise Server の PR を表示する場合は、GitHub CLI を Enterprise
|
|
|
86
92
|
1. `gh auth login --hostname YOUR-ENTERPRISE-SERVER`
|
|
87
93
|
2. または `GH_HOST=YOUR-ENTERPRISE-SERVER` と `GH_TOKEN` / `GITHUB_TOKEN` を設定
|
|
88
94
|
|
|
95
|
+
### 起動時コメント注入
|
|
96
|
+
|
|
97
|
+
difit の起動時に初期コメントを注入できます。
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
difit --comment '{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"この変更の背景は…"}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`--comment` は複数回指定でき、単一の JSON object と JSON array の両方を受け付けます。対応する type は次の 2 種類です。
|
|
104
|
+
|
|
105
|
+
- `thread`: 指定位置に新しい thread を追加
|
|
106
|
+
- `reply`: 同じ位置にある最新 thread に reply を追加
|
|
107
|
+
|
|
108
|
+
同じコメントが既に存在する場合は import をスキップします。
|
|
109
|
+
|
|
89
110
|
### 標準入力
|
|
90
111
|
|
|
91
112
|
パイプを使用して標準入力経由で統一diff形式を渡すことで、任意のツールからのdiffをdifitで表示できます。
|
|
@@ -120,6 +141,7 @@ git diff --cached | difit -
|
|
|
120
141
|
| `<target>` | HEAD | コミットハッシュ、タグ、HEAD~n、ブランチ、または特別な引数 |
|
|
121
142
|
| `[compare-with]` | - | 比較対象の2番目のコミット(2つの間のdiffを表示) |
|
|
122
143
|
| `--pr <url>` | - | レビューするGitHub PRのURL(例:https://github.com/owner/repo/pull/123) |
|
|
144
|
+
| `--comment <json>` | - | 起動時に初期コメントを注入(複数指定可。JSON object または array を受け付ける) |
|
|
123
145
|
| `--port` | 4966 | 優先ポート。使用中の場合は+1にフォールバック |
|
|
124
146
|
| `--host` | 127.0.0.1 | サーバーをバインドするホストアドレス(外部からアクセスしたい場合は0.0.0.0を指定) |
|
|
125
147
|
| `--no-open` | false | ブラウザを自動的に開かない |
|
|
@@ -155,13 +177,18 @@ src/components/Button.tsx:L42-L48 # この行が自動的に追加されます
|
|
|
155
177
|
|
|
156
178
|
## 🤖 エージェントからの呼び出し
|
|
157
179
|
|
|
158
|
-
difit
|
|
180
|
+
AIエージェントから difit を使うための skill 群を以下でインストールできます。
|
|
159
181
|
|
|
160
182
|
```sh
|
|
161
183
|
npx skills add yoshiko-pg/difit
|
|
162
184
|
```
|
|
163
185
|
|
|
164
|
-
|
|
186
|
+
インストールされる主な skill:
|
|
187
|
+
|
|
188
|
+
- `difit`: コード変更後に difit を使ってユーザーへレビューを依頼する
|
|
189
|
+
- `difit-review`: 特定の diff や PR をレビューし、指摘や解説をコメントとして事前投入した difit を起動する
|
|
190
|
+
|
|
191
|
+
コード編集後や自動レビュー時に、目的に応じた skill で difit サーバーを起動できます。
|
|
165
192
|
|
|
166
193
|
## 🎨 シンタックスハイライト対応言語
|
|
167
194
|
|
package/README.ko.md
CHANGED
|
@@ -28,9 +28,14 @@ difit # WebUI에서 최신 커밋 diff 보기
|
|
|
28
28
|
AI 에이전트에서 사용할 수 있도록 설정
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npx skills add yoshiko-pg/difit # 에이전트에 Skill 추가
|
|
31
|
+
npx skills add yoshiko-pg/difit # 에이전트에 Skill들 추가
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
설치되는 주요 Skill:
|
|
35
|
+
|
|
36
|
+
- `difit`: 코드 변경 후 difit을 통해 사용자에게 리뷰를 요청
|
|
37
|
+
- `difit-review`: 특정 diff 또는 PR을 검토하고, 지적사항이나 설명을 코멘트로 미리 넣은 difit을 실행
|
|
38
|
+
|
|
34
39
|
## 🚀 사용법
|
|
35
40
|
|
|
36
41
|
### 기본 사용법
|
|
@@ -73,6 +78,7 @@ difit --pr https://github.com/owner/repo/pull/123
|
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
`--pr` 모드는 내부적으로 `gh pr diff --patch`를 실행해 패치를 가져옵니다.
|
|
81
|
+
추가로 PR의 unresolved inline review thread도 시작 코멘트로 가져와 difit에서 그대로 표시합니다.
|
|
76
82
|
|
|
77
83
|
인증은 GitHub CLI가 처리합니다:
|
|
78
84
|
|
|
@@ -86,6 +92,21 @@ Enterprise Server PR의 경우 GitHub CLI를 Enterprise 호스트에 인증하
|
|
|
86
92
|
1. `gh auth login --hostname YOUR-ENTERPRISE-SERVER`
|
|
87
93
|
2. 또는 `GH_HOST=YOUR-ENTERPRISE-SERVER`와 `GH_TOKEN`/`GITHUB_TOKEN` 설정
|
|
88
94
|
|
|
95
|
+
### 시작 시 코멘트 주입
|
|
96
|
+
|
|
97
|
+
difit 실행 시 초기 리뷰 코멘트를 주입할 수 있습니다:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
difit --comment '{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"이 변경의 배경은..."}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`--comment` 는 여러 번 지정할 수 있으며, 단일 JSON object 와 JSON array 둘 다 받을 수 있습니다. 지원하는 타입은 다음과 같습니다:
|
|
104
|
+
|
|
105
|
+
- `thread`: 지정한 diff 위치에 새 thread 생성
|
|
106
|
+
- `reply`: 같은 diff 위치에 있는 가장 최신 thread 에 reply 추가
|
|
107
|
+
|
|
108
|
+
같은 코멘트가 이미 있으면 difit 이 import 를 건너뜁니다.
|
|
109
|
+
|
|
89
110
|
### 표준 입력
|
|
90
111
|
|
|
91
112
|
파이프를 사용하여 표준 입력을 통해 통합 diff를 전달하면 모든 도구의 diff를 difit으로 볼 수 있습니다.
|
|
@@ -120,6 +141,7 @@ git diff --cached | difit -
|
|
|
120
141
|
| `<target>` | HEAD | 커밋 해시, 태그, HEAD~n, 브랜치 또는 특수 인수 |
|
|
121
142
|
| `[compare-with]` | - | 비교할 선택적 두 번째 커밋 (둘 사이의 diff 표시) |
|
|
122
143
|
| `--pr <url>` | - | 검토할 GitHub PR URL (예: https://github.com/owner/repo/pull/123) |
|
|
144
|
+
| `--comment <json>` | - | 초기 코멘트 주입 (반복 가능; JSON object 또는 array 허용) |
|
|
123
145
|
| `--port` | 4966 | 선호 포트; 사용 중인 경우 +1로 대체 |
|
|
124
146
|
| `--host` | 127.0.0.1 | 서버를 바인딩할 호스트 주소 (외부 액세스는 0.0.0.0 사용) |
|
|
125
147
|
| `--no-open` | false | 브라우저를 자동으로 열지 않음 |
|
|
@@ -155,13 +177,18 @@ src/components/Button.tsx:L42-L48 # 이 줄은 자동으로 추가됩니다
|
|
|
155
177
|
|
|
156
178
|
## 🤖 에이전트에서 호출
|
|
157
179
|
|
|
158
|
-
difit을
|
|
180
|
+
AI 에이전트에서 difit을 사용하기 위한 Skill들은 아래 명령으로 설치할 수 있습니다:
|
|
159
181
|
|
|
160
182
|
```sh
|
|
161
183
|
npx skills add yoshiko-pg/difit
|
|
162
184
|
```
|
|
163
185
|
|
|
164
|
-
|
|
186
|
+
설치되는 주요 Skill:
|
|
187
|
+
|
|
188
|
+
- `difit`: 코드 변경 후 difit을 통해 사용자에게 리뷰를 요청
|
|
189
|
+
- `difit-review`: 특정 diff 또는 PR을 검토하고, 지적사항이나 설명을 코멘트로 미리 넣은 difit을 실행
|
|
190
|
+
|
|
191
|
+
코드 수정 후나 자동 리뷰 시, 목적에 맞는 Skill로 difit 서버를 실행할 수 있습니다.
|
|
165
192
|
|
|
166
193
|
## 🎨 구문 강조 언어
|
|
167
194
|
|
package/README.md
CHANGED
|
@@ -28,9 +28,14 @@ difit # View the latest commit diff in WebUI
|
|
|
28
28
|
Enable use from AI agents
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npx skills add yoshiko-pg/difit # Add the
|
|
31
|
+
npx skills add yoshiko-pg/difit # Add the Skills to your agent
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
Installed skills include:
|
|
35
|
+
|
|
36
|
+
- `difit`: ask the user for a review through difit after code changes
|
|
37
|
+
- `difit-review`: review a specific diff or PR and launch difit with findings or explanations preloaded as comments
|
|
38
|
+
|
|
34
39
|
## 🚀 Usage
|
|
35
40
|
|
|
36
41
|
### Basic Usage
|
|
@@ -73,6 +78,7 @@ difit --pr https://github.com/owner/repo/pull/123
|
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
`--pr` mode fetches patches by running `gh pr diff --patch` under the hood.
|
|
81
|
+
It also imports unresolved inline review threads from the PR so they appear as startup comments in difit.
|
|
76
82
|
|
|
77
83
|
Authentication is handled by GitHub CLI:
|
|
78
84
|
|
|
@@ -86,6 +92,21 @@ For Enterprise Server PRs, authenticate GitHub CLI against your Enterprise host:
|
|
|
86
92
|
1. `gh auth login --hostname YOUR-ENTERPRISE-SERVER`
|
|
87
93
|
2. Or set `GH_HOST=YOUR-ENTERPRISE-SERVER` with `GH_TOKEN`/`GITHUB_TOKEN`
|
|
88
94
|
|
|
95
|
+
### Initial Comments
|
|
96
|
+
|
|
97
|
+
You can inject initial review comments when launching difit:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
difit --comment '{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"The background for this change is..."}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`--comment` is repeatable and accepts either a single JSON object or a JSON array. Supported types:
|
|
104
|
+
|
|
105
|
+
- `thread`: create a new thread at the specified diff position
|
|
106
|
+
- `reply`: add a reply to the latest existing thread at the same diff position
|
|
107
|
+
|
|
108
|
+
If the same comment already exists, difit skips importing it.
|
|
109
|
+
|
|
89
110
|
### Stdin
|
|
90
111
|
|
|
91
112
|
By using a pipe to pass unified diffs via stdin, you can view diffs from any tool with difit.
|
|
@@ -120,6 +141,7 @@ Stdin mode is selected with intent-first rules:
|
|
|
120
141
|
| `<target>` | HEAD | Commit hash, tag, HEAD~n, branch, or special arguments |
|
|
121
142
|
| `[compare-with]` | - | Optional second commit to compare with (shows diff between the two) |
|
|
122
143
|
| `--pr <url>` | - | GitHub PR URL to review (e.g., https://github.com/owner/repo/pull/123) |
|
|
144
|
+
| `--comment <json>` | - | Inject initial comments (repeatable; accepts a JSON object or array) |
|
|
123
145
|
| `--port` | 4966 | Preferred port; falls back to +1 if occupied |
|
|
124
146
|
| `--host` | 127.0.0.1 | Host address to bind server to (use 0.0.0.0 for external access) |
|
|
125
147
|
| `--no-open` | false | Don't automatically open browser |
|
|
@@ -155,13 +177,18 @@ This section is unnecessary
|
|
|
155
177
|
|
|
156
178
|
## 🤖 Calling from Agents
|
|
157
179
|
|
|
158
|
-
You can install the following
|
|
180
|
+
You can install the following Skills to work with difit from AI agents.
|
|
159
181
|
|
|
160
182
|
```sh
|
|
161
183
|
npx skills add yoshiko-pg/difit
|
|
162
184
|
```
|
|
163
185
|
|
|
164
|
-
|
|
186
|
+
Installed skills include:
|
|
187
|
+
|
|
188
|
+
- `difit`: ask the user for a review through difit after code changes
|
|
189
|
+
- `difit-review`: review a specific diff or PR and launch difit with findings or explanations preloaded as comments
|
|
190
|
+
|
|
191
|
+
After code edits or automated review, the agent can start the difit server with the appropriate skill.
|
|
165
192
|
|
|
166
193
|
## 🎨 Syntax Highlighting Languages
|
|
167
194
|
|
package/README.zh.md
CHANGED
|
@@ -28,9 +28,14 @@ difit # 在 WebUI 中查看最新提交的差异
|
|
|
28
28
|
使其可供 AI 代理使用
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
npx skills add yoshiko-pg/difit # 为代理添加
|
|
31
|
+
npx skills add yoshiko-pg/difit # 为代理添加 Skills
|
|
32
32
|
```
|
|
33
33
|
|
|
34
|
+
安装后包含的主要 Skill:
|
|
35
|
+
|
|
36
|
+
- `difit`:在代码修改后通过 difit 向用户请求审查
|
|
37
|
+
- `difit-review`:审查特定 diff 或 PR,并启动一个已预载评论或说明的 difit
|
|
38
|
+
|
|
34
39
|
## 🚀 使用方法
|
|
35
40
|
|
|
36
41
|
### 基本用法
|
|
@@ -73,6 +78,7 @@ difit --pr https://github.com/owner/repo/pull/123
|
|
|
73
78
|
```
|
|
74
79
|
|
|
75
80
|
`--pr` 模式会在内部执行 `gh pr diff --patch` 来获取补丁。
|
|
81
|
+
同时还会导入 PR 中未解决的 inline review thread,并在 difit 中作为启动评论显示出来。
|
|
76
82
|
|
|
77
83
|
认证由 GitHub CLI 处理:
|
|
78
84
|
|
|
@@ -86,6 +92,21 @@ difit --pr https://github.com/owner/repo/pull/123
|
|
|
86
92
|
1. `gh auth login --hostname YOUR-ENTERPRISE-SERVER`
|
|
87
93
|
2. 或设置 `GH_HOST=YOUR-ENTERPRISE-SERVER`,并配置 `GH_TOKEN` / `GITHUB_TOKEN`
|
|
88
94
|
|
|
95
|
+
### 启动时注入评论
|
|
96
|
+
|
|
97
|
+
你可以在启动 difit 时注入初始审查评论:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
difit --comment '{"type":"thread","filePath":"src/example.ts","position":{"side":"new","line":10},"body":"这次修改的背景是……"}'
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
`--comment` 可重复指定,同时接受单个 JSON object 或 JSON array。支持的类型如下:
|
|
104
|
+
|
|
105
|
+
- `thread`:在指定 diff 位置创建新的 thread
|
|
106
|
+
- `reply`:向相同 diff 位置上最新的现有 thread 添加 reply
|
|
107
|
+
|
|
108
|
+
如果相同评论已经存在,difit 会跳过导入。
|
|
109
|
+
|
|
89
110
|
### 标准输入
|
|
90
111
|
|
|
91
112
|
通过使用管道通过标准输入传递统一差异,您可以使用 difit 查看来自任何工具的差异。
|
|
@@ -120,6 +141,7 @@ git diff --cached | difit -
|
|
|
120
141
|
| `<target>` | HEAD | 提交哈希、标签、HEAD~n、分支或特殊参数 |
|
|
121
142
|
| `[compare-with]` | - | 要比较的可选第二个提交(显示两者之间的差异) |
|
|
122
143
|
| `--pr <url>` | - | 要审查的 GitHub PR URL(例如:https://github.com/owner/repo/pull/123) |
|
|
144
|
+
| `--comment <json>` | - | 注入初始评论(可重复指定;接受 JSON object 或 array) |
|
|
123
145
|
| `--port` | 4966 | 首选端口;如果被占用则回退到 +1 |
|
|
124
146
|
| `--host` | 127.0.0.1 | 绑定服务器的主机地址(使用 0.0.0.0 进行外部访问) |
|
|
125
147
|
| `--no-open` | false | 不自动打开浏览器 |
|
|
@@ -155,13 +177,18 @@ src/components/Button.tsx:L42-L48 # 此行自动添加
|
|
|
155
177
|
|
|
156
178
|
## 🤖 从代理调用
|
|
157
179
|
|
|
158
|
-
|
|
180
|
+
你可以通过以下命令安装这些 Skill,以便从 AI 代理中使用 difit:
|
|
159
181
|
|
|
160
182
|
```sh
|
|
161
183
|
npx skills add yoshiko-pg/difit
|
|
162
184
|
```
|
|
163
185
|
|
|
164
|
-
|
|
186
|
+
安装后包含的主要 Skill:
|
|
187
|
+
|
|
188
|
+
- `difit`:在代码修改后通过 difit 向用户请求审查
|
|
189
|
+
- `difit-review`:审查特定 diff 或 PR,并启动一个已预载评论或说明的 difit
|
|
190
|
+
|
|
191
|
+
在代码修改后或自动审查时,代理可以根据目的使用对应的 Skill 来启动 difit 服务器。
|
|
165
192
|
|
|
166
193
|
## 🎨 语法高亮语言
|
|
167
194
|
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import type { CommentImport } from '../types/diff.js';
|
|
2
|
+
interface PullRequestInfo {
|
|
3
|
+
owner: string;
|
|
4
|
+
repo: string;
|
|
5
|
+
pullNumber: number;
|
|
6
|
+
hostname: string;
|
|
7
|
+
}
|
|
8
|
+
interface GitHubReviewThreadAuthor {
|
|
9
|
+
login?: string | null;
|
|
10
|
+
}
|
|
11
|
+
interface GitHubReviewThreadCommentNode {
|
|
12
|
+
id?: string | null;
|
|
13
|
+
body?: string | null;
|
|
14
|
+
createdAt?: string | null;
|
|
15
|
+
updatedAt?: string | null;
|
|
16
|
+
author?: GitHubReviewThreadAuthor | null;
|
|
17
|
+
}
|
|
18
|
+
interface GitHubReviewThreadNode {
|
|
19
|
+
id?: string | null;
|
|
20
|
+
isResolved?: boolean | null;
|
|
21
|
+
isOutdated?: boolean | null;
|
|
22
|
+
subjectType?: string | null;
|
|
23
|
+
path?: string | null;
|
|
24
|
+
diffSide?: string | null;
|
|
25
|
+
startDiffSide?: string | null;
|
|
26
|
+
line?: number | null;
|
|
27
|
+
startLine?: number | null;
|
|
28
|
+
originalLine?: number | null;
|
|
29
|
+
originalStartLine?: number | null;
|
|
30
|
+
comments?: {
|
|
31
|
+
nodes?: GitHubReviewThreadCommentNode[] | null;
|
|
32
|
+
} | null;
|
|
33
|
+
}
|
|
34
|
+
interface GitHubReviewThreadsPageInfo {
|
|
35
|
+
hasNextPage?: boolean | null;
|
|
36
|
+
endCursor?: string | null;
|
|
37
|
+
}
|
|
38
|
+
interface GitHubReviewThreadsConnection {
|
|
39
|
+
nodes?: GitHubReviewThreadNode[] | null;
|
|
40
|
+
pageInfo?: GitHubReviewThreadsPageInfo | null;
|
|
41
|
+
}
|
|
42
|
+
interface GitHubReviewThreadsGraphqlResponse {
|
|
43
|
+
data?: {
|
|
44
|
+
repository?: {
|
|
45
|
+
pullRequest?: {
|
|
46
|
+
reviewThreads?: GitHubReviewThreadsConnection | null;
|
|
47
|
+
} | null;
|
|
48
|
+
} | null;
|
|
49
|
+
} | null;
|
|
50
|
+
errors?: Array<{
|
|
51
|
+
message?: string | null;
|
|
52
|
+
}> | null;
|
|
53
|
+
}
|
|
54
|
+
interface PrCommentImportsPage {
|
|
55
|
+
commentImports: CommentImport[];
|
|
56
|
+
pageInfo: {
|
|
57
|
+
hasNextPage: boolean;
|
|
58
|
+
endCursor: string | null;
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export declare function parseGitHubPrUrl(url: string): PullRequestInfo | null;
|
|
62
|
+
export declare function getPrPatch(prArg: string): string;
|
|
63
|
+
export declare function parsePrCommentImportsResponse(response: GitHubReviewThreadsGraphqlResponse): PrCommentImportsPage;
|
|
64
|
+
export declare function getPrCommentImports(prArg: string): Promise<CommentImport[]>;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { execFileSync } from 'child_process';
|
|
2
|
+
const PR_REVIEW_THREADS_GRAPHQL_QUERY = `
|
|
3
|
+
query($owner: String!, $repo: String!, $number: Int!, $endCursor: String) {
|
|
4
|
+
repository(owner: $owner, name: $repo) {
|
|
5
|
+
pullRequest(number: $number) {
|
|
6
|
+
reviewThreads(first: 100, after: $endCursor) {
|
|
7
|
+
nodes {
|
|
8
|
+
id
|
|
9
|
+
isResolved
|
|
10
|
+
isOutdated
|
|
11
|
+
subjectType
|
|
12
|
+
path
|
|
13
|
+
diffSide
|
|
14
|
+
startDiffSide
|
|
15
|
+
line
|
|
16
|
+
startLine
|
|
17
|
+
originalLine
|
|
18
|
+
originalStartLine
|
|
19
|
+
comments(first: 100) {
|
|
20
|
+
nodes {
|
|
21
|
+
id
|
|
22
|
+
body
|
|
23
|
+
createdAt
|
|
24
|
+
updatedAt
|
|
25
|
+
author {
|
|
26
|
+
login
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
pageInfo {
|
|
32
|
+
hasNextPage
|
|
33
|
+
endCursor
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
`;
|
|
40
|
+
function isPositiveInteger(value) {
|
|
41
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0;
|
|
42
|
+
}
|
|
43
|
+
function formatGhCommandError(error) {
|
|
44
|
+
const stderr = error.stderr;
|
|
45
|
+
const stderrText = typeof stderr === 'string'
|
|
46
|
+
? stderr.trim()
|
|
47
|
+
: Buffer.isBuffer(stderr)
|
|
48
|
+
? stderr.toString('utf8').trim()
|
|
49
|
+
: '';
|
|
50
|
+
const message = stderrText || (error instanceof Error ? error.message : 'Unknown error while running gh');
|
|
51
|
+
return new Error(`${message}\nTry: gh auth login`);
|
|
52
|
+
}
|
|
53
|
+
function warnPrCommentImport(threadId, message) {
|
|
54
|
+
const threadLabel = threadId ? ` ${threadId}` : '';
|
|
55
|
+
console.warn(`Warning: Skipping PR review thread${threadLabel}: ${message}`);
|
|
56
|
+
}
|
|
57
|
+
function createSingleLinePosition(side, line) {
|
|
58
|
+
return { side, line };
|
|
59
|
+
}
|
|
60
|
+
function createMultiLinePosition(side, start, end) {
|
|
61
|
+
const line = { start, end };
|
|
62
|
+
return { side, line };
|
|
63
|
+
}
|
|
64
|
+
function createRightSidePosition(thread) {
|
|
65
|
+
if (!isPositiveInteger(thread.line)) {
|
|
66
|
+
warnPrCommentImport(thread.id ?? undefined, 'RIGHT thread is missing line.');
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const startLine = thread.startLine !== undefined && thread.startLine !== null ? thread.startLine : null;
|
|
70
|
+
const isSingleLineWithRedundantStart = startLine !== null && startLine === thread.line && thread.startDiffSide == null;
|
|
71
|
+
const hasMultiLineMetadata = thread.startDiffSide !== undefined && thread.startDiffSide !== null
|
|
72
|
+
? true
|
|
73
|
+
: startLine !== null && !isSingleLineWithRedundantStart;
|
|
74
|
+
if (!hasMultiLineMetadata) {
|
|
75
|
+
return createSingleLinePosition('new', thread.line);
|
|
76
|
+
}
|
|
77
|
+
if (thread.startDiffSide !== 'RIGHT') {
|
|
78
|
+
warnPrCommentImport(thread.id ?? undefined, 'RIGHT thread has mismatched startDiffSide.');
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (!isPositiveInteger(startLine) || startLine > thread.line) {
|
|
82
|
+
warnPrCommentImport(thread.id ?? undefined, 'RIGHT thread has an invalid multi-line range.');
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
return createMultiLinePosition('new', startLine, thread.line);
|
|
86
|
+
}
|
|
87
|
+
function createLeftSidePosition(thread) {
|
|
88
|
+
if (!isPositiveInteger(thread.originalLine)) {
|
|
89
|
+
warnPrCommentImport(thread.id ?? undefined, 'LEFT thread is missing originalLine.');
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
const originalStartLine = thread.originalStartLine !== undefined && thread.originalStartLine !== null
|
|
93
|
+
? thread.originalStartLine
|
|
94
|
+
: null;
|
|
95
|
+
const isSingleLineWithRedundantStart = originalStartLine !== null &&
|
|
96
|
+
originalStartLine === thread.originalLine &&
|
|
97
|
+
thread.startDiffSide == null;
|
|
98
|
+
const hasMultiLineMetadata = thread.startDiffSide !== undefined && thread.startDiffSide !== null
|
|
99
|
+
? true
|
|
100
|
+
: originalStartLine !== null && !isSingleLineWithRedundantStart;
|
|
101
|
+
if (!hasMultiLineMetadata) {
|
|
102
|
+
return createSingleLinePosition('old', thread.originalLine);
|
|
103
|
+
}
|
|
104
|
+
if (thread.startDiffSide !== 'LEFT') {
|
|
105
|
+
warnPrCommentImport(thread.id ?? undefined, 'LEFT thread has mismatched startDiffSide.');
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (!isPositiveInteger(originalStartLine) || originalStartLine > thread.originalLine) {
|
|
109
|
+
warnPrCommentImport(thread.id ?? undefined, 'LEFT thread has an invalid multi-line range.');
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return createMultiLinePosition('old', originalStartLine, thread.originalLine);
|
|
113
|
+
}
|
|
114
|
+
function getThreadPosition(thread) {
|
|
115
|
+
if (thread.diffSide === 'RIGHT') {
|
|
116
|
+
return createRightSidePosition(thread);
|
|
117
|
+
}
|
|
118
|
+
if (thread.diffSide === 'LEFT') {
|
|
119
|
+
return createLeftSidePosition(thread);
|
|
120
|
+
}
|
|
121
|
+
warnPrCommentImport(thread.id ?? undefined, 'Unsupported diffSide.');
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
function toSortedComments(thread) {
|
|
125
|
+
return [...(thread.comments?.nodes ?? [])].sort((left, right) => (left.createdAt ?? '').localeCompare(right.createdAt ?? ''));
|
|
126
|
+
}
|
|
127
|
+
function toCommentImportsForThread(thread) {
|
|
128
|
+
if (thread.isResolved || thread.isOutdated || thread.subjectType !== 'LINE') {
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
if (typeof thread.path !== 'string' || thread.path.trim().length === 0) {
|
|
132
|
+
warnPrCommentImport(thread.id ?? undefined, 'Thread is missing path.');
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
const filePath = thread.path;
|
|
136
|
+
const position = getThreadPosition(thread);
|
|
137
|
+
if (!position) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const comments = toSortedComments(thread);
|
|
141
|
+
if (comments.length === 0) {
|
|
142
|
+
warnPrCommentImport(thread.id ?? undefined, 'Thread has no comments.');
|
|
143
|
+
return [];
|
|
144
|
+
}
|
|
145
|
+
const rootComment = comments[0];
|
|
146
|
+
if (!rootComment ||
|
|
147
|
+
typeof rootComment.id !== 'string' ||
|
|
148
|
+
typeof rootComment.body !== 'string' ||
|
|
149
|
+
typeof rootComment.createdAt !== 'string' ||
|
|
150
|
+
typeof rootComment.updatedAt !== 'string') {
|
|
151
|
+
warnPrCommentImport(thread.id ?? undefined, 'Thread has a comment with missing fields.');
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
const commentImports = [
|
|
155
|
+
{
|
|
156
|
+
type: 'thread',
|
|
157
|
+
id: rootComment.id,
|
|
158
|
+
filePath,
|
|
159
|
+
position,
|
|
160
|
+
body: rootComment.body,
|
|
161
|
+
author: rootComment.author?.login ?? undefined,
|
|
162
|
+
createdAt: rootComment.createdAt,
|
|
163
|
+
updatedAt: rootComment.updatedAt,
|
|
164
|
+
},
|
|
165
|
+
];
|
|
166
|
+
for (const comment of comments.slice(1)) {
|
|
167
|
+
if (typeof comment.id !== 'string' ||
|
|
168
|
+
typeof comment.body !== 'string' ||
|
|
169
|
+
typeof comment.createdAt !== 'string' ||
|
|
170
|
+
typeof comment.updatedAt !== 'string') {
|
|
171
|
+
warnPrCommentImport(thread.id ?? undefined, 'Thread has a comment with missing fields.');
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
commentImports.push({
|
|
175
|
+
type: 'reply',
|
|
176
|
+
id: comment.id,
|
|
177
|
+
filePath,
|
|
178
|
+
position,
|
|
179
|
+
body: comment.body,
|
|
180
|
+
author: comment.author?.login ?? undefined,
|
|
181
|
+
createdAt: comment.createdAt,
|
|
182
|
+
updatedAt: comment.updatedAt,
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return commentImports;
|
|
186
|
+
}
|
|
187
|
+
export function parseGitHubPrUrl(url) {
|
|
188
|
+
try {
|
|
189
|
+
const urlObj = new URL(url);
|
|
190
|
+
const pathParts = urlObj.pathname.split('/').filter(Boolean);
|
|
191
|
+
if (pathParts.length < 4 || pathParts[2] !== 'pull') {
|
|
192
|
+
return null;
|
|
193
|
+
}
|
|
194
|
+
const owner = pathParts[0];
|
|
195
|
+
const repo = pathParts[1];
|
|
196
|
+
const pullNumber = parseInt(pathParts[3], 10);
|
|
197
|
+
if (isNaN(pullNumber)) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
return { owner, repo, pullNumber, hostname: urlObj.hostname };
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
export function getPrPatch(prArg) {
|
|
207
|
+
try {
|
|
208
|
+
const patch = execFileSync('gh', ['pr', 'diff', prArg], {
|
|
209
|
+
encoding: 'utf8',
|
|
210
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
211
|
+
});
|
|
212
|
+
if (!patch.trim()) {
|
|
213
|
+
throw new Error('No diff content returned from gh pr diff');
|
|
214
|
+
}
|
|
215
|
+
return patch;
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
throw formatGhCommandError(error);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export function parsePrCommentImportsResponse(response) {
|
|
222
|
+
const errors = response.errors?.map((error) => error.message).filter((message) => message) ?? [];
|
|
223
|
+
if (errors.length > 0) {
|
|
224
|
+
throw new Error(errors.join('\n'));
|
|
225
|
+
}
|
|
226
|
+
const reviewThreads = response.data?.repository?.pullRequest?.reviewThreads;
|
|
227
|
+
if (!reviewThreads) {
|
|
228
|
+
return {
|
|
229
|
+
commentImports: [],
|
|
230
|
+
pageInfo: {
|
|
231
|
+
hasNextPage: false,
|
|
232
|
+
endCursor: null,
|
|
233
|
+
},
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
commentImports: (reviewThreads.nodes ?? []).flatMap((thread) => toCommentImportsForThread(thread)),
|
|
238
|
+
pageInfo: {
|
|
239
|
+
hasNextPage: reviewThreads.pageInfo?.hasNextPage === true,
|
|
240
|
+
endCursor: reviewThreads.pageInfo?.endCursor ?? null,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
export function getPrCommentImports(prArg) {
|
|
245
|
+
const pullRequestInfo = parseGitHubPrUrl(prArg);
|
|
246
|
+
if (!pullRequestInfo) {
|
|
247
|
+
throw new Error('Invalid GitHub PR URL');
|
|
248
|
+
}
|
|
249
|
+
const { owner, repo, pullNumber, hostname } = pullRequestInfo;
|
|
250
|
+
const commentImports = [];
|
|
251
|
+
let endCursor = null;
|
|
252
|
+
while (true) {
|
|
253
|
+
try {
|
|
254
|
+
const args = [
|
|
255
|
+
'api',
|
|
256
|
+
'graphql',
|
|
257
|
+
'--hostname',
|
|
258
|
+
hostname,
|
|
259
|
+
'-f',
|
|
260
|
+
`query=${PR_REVIEW_THREADS_GRAPHQL_QUERY}`,
|
|
261
|
+
'-F',
|
|
262
|
+
`owner=${owner}`,
|
|
263
|
+
'-F',
|
|
264
|
+
`repo=${repo}`,
|
|
265
|
+
'-F',
|
|
266
|
+
`number=${pullNumber}`,
|
|
267
|
+
];
|
|
268
|
+
if (endCursor) {
|
|
269
|
+
args.push('-F', `endCursor=${endCursor}`);
|
|
270
|
+
}
|
|
271
|
+
const stdout = execFileSync('gh', args, {
|
|
272
|
+
encoding: 'utf8',
|
|
273
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
274
|
+
});
|
|
275
|
+
const parsed = JSON.parse(stdout);
|
|
276
|
+
const page = parsePrCommentImportsResponse(parsed);
|
|
277
|
+
commentImports.push(...page.commentImports);
|
|
278
|
+
if (!page.pageInfo.hasNextPage) {
|
|
279
|
+
return Promise.resolve(commentImports);
|
|
280
|
+
}
|
|
281
|
+
if (!page.pageInfo.endCursor) {
|
|
282
|
+
throw new Error('GitHub GraphQL response indicated more pages without endCursor.');
|
|
283
|
+
}
|
|
284
|
+
endCursor = page.pageInfo.endCursor;
|
|
285
|
+
}
|
|
286
|
+
catch (error) {
|
|
287
|
+
if (error instanceof SyntaxError) {
|
|
288
|
+
throw new Error('Invalid JSON returned from gh api graphql');
|
|
289
|
+
}
|
|
290
|
+
if (error instanceof Error && !('stderr' in error)) {
|
|
291
|
+
throw error;
|
|
292
|
+
}
|
|
293
|
+
throw formatGhCommandError(error);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|