local-diff-reviewer 1.0.12 → 2.0.1
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.md +9 -2
- package/SKILL.md +3 -1
- package/dist/cli/start.js +107 -18
- package/dist/web/assets/{arc-OCjfoMPh.js → arc-BTM4ghNf.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-y4fhLiSc.js → architectureDiagram-3BPJPVTR-DyOV0Jtr.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-DrL5r8-f.js → blockDiagram-GPEHLZMM-CxJbhmIT.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-CzG_hLoH.js → c4Diagram-AAUBKEIU-BJQFA8CQ.js} +1 -1
- package/dist/web/assets/channel-zJdz6BA1.js +1 -0
- package/dist/web/assets/{chunk-2J33WTMH-L39hdgnC.js → chunk-2J33WTMH-C5OtbA_r.js} +1 -1
- package/dist/web/assets/{chunk-3OPIFGDE-CpY_ka2n.js → chunk-3OPIFGDE-B9KuazSf.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-gzClsL04.js → chunk-4BX2VUAB-B8LRminS.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-BROVtiW_.js → chunk-55IACEB6-SkDhErOo.js} +1 -1
- package/dist/web/assets/{chunk-5ZQYHXKU-wADBWiiX.js → chunk-5ZQYHXKU-Bb82bV79.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-Cx8AIeT_.js → chunk-727SXJPM-Dl1e3M-k.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-Q1eF5436.js → chunk-AQP2D5EJ-ds7_Qz_Y.js} +1 -1
- package/dist/web/assets/{chunk-BSJP7CBP-B67bDJwE.js → chunk-BSJP7CBP-Ddngm4Zu.js} +1 -1
- package/dist/web/assets/{chunk-CSCIHK7Q-Ba_j-dfv.js → chunk-CSCIHK7Q-zBbkk0kR.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-B-HXgSfN.js → chunk-FMBD7UC4-C9gGg2hs.js} +1 -1
- package/dist/web/assets/{chunk-KSCS5N6A-CPrC_9mM.js → chunk-KSCS5N6A-CYRCkspU.js} +1 -1
- package/dist/web/assets/{chunk-L5ZTLDWV-CNe0fKtD.js → chunk-L5ZTLDWV-at0vaITZ.js} +1 -1
- package/dist/web/assets/{chunk-LZXEDZCA-CAzYXU6L.js → chunk-LZXEDZCA-CB2tlbwU.js} +2 -2
- package/dist/web/assets/{chunk-ND2GUHAM-YmWlIsKn.js → chunk-ND2GUHAM-SF9ziKuY.js} +1 -1
- package/dist/web/assets/{chunk-NZK2D7GU-CU0gc_zr.js → chunk-NZK2D7GU-cDloGQoC.js} +1 -1
- package/dist/web/assets/{chunk-O5CBEL6O-CR7dgaQM.js → chunk-O5CBEL6O-BKhNShTj.js} +1 -1
- package/dist/web/assets/chunk-QZHKN3VN-DOXAr87O.js +1 -0
- package/dist/web/assets/chunk-WU5MYG2G-HE1Sw8W0.js +1 -0
- package/dist/web/assets/{chunk-XPW4576I-BfxCRfec.js → chunk-XPW4576I-C5N-bWNh.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-DhMmSdQh.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-C-ONPelI.js +1 -0
- package/dist/web/assets/{cose-bilkent-S5V4N54A-J1-9yTTS.js → cose-bilkent-S5V4N54A-CVt4gjy_.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-DoHJPmyX.js → dagre-BM42HDAG-DsFFRdXb.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-DDAxGqxO.js → diagram-2AECGRRQ-DU-SS203.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-BQiTS6Tb.js → diagram-5GNKFQAL-Caxxw6ez.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-DztfYHXu.js → diagram-KO2AKTUF-C86fE-G2.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-BJWqCLig.js → diagram-LMA3HP47-CbblsEig.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-BbwBkO4f.js → diagram-OG6HWLK6-VVwPWCNJ.js} +1 -1
- package/dist/web/assets/{dist-D5MQe1Ta.js → dist-DP09l7dv.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-DpPAyWZV.js → erDiagram-TEJ5UH35-DybhgsWs.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-CQCWAP3M.js → flowDiagram-I6XJVG4X-L175L0f8.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-B7I0DB1q.js → ganttDiagram-6RSMTGT7-Bnn1hKvs.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-CE0nohFp.js → gitGraphDiagram-PVQCEYII-C0ZBrF0f.js} +1 -1
- package/dist/web/assets/{index-B9P6HSSC.js → index-B4AzQzzw.js} +2 -2
- package/dist/web/assets/{infoDiagram-5YYISTIA-B2lIluH-.js → infoDiagram-5YYISTIA-BjE1edtT.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-DyvD6JWp.js → ishikawaDiagram-YF4QCWOH-CA7-Z3nV.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-BJYfDKFF.js → journeyDiagram-JHISSGLW-7yRPAxui.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-BXqSoXXP.js → kanban-definition-UN3LZRKU-CxJbCLrw.js} +1 -1
- package/dist/web/assets/{line-BmLyb-mA.js → line-BZChgTw_.js} +1 -1
- package/dist/web/assets/{linear-B52eGRZ2.js → linear-Ch3uwdzh.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-CkyRpzRz.js → mermaid-parser.core-BwmBog8o.js} +1 -1
- package/dist/web/assets/{mermaid.core-DPMvJDqM.js → mermaid.core-DbfGaZdz.js} +3 -3
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-DjunarmZ.js → mindmap-definition-RKZ34NQL-DY-t2KPo.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-OiYtQIhx.js → pieDiagram-4H26LBE5-BkM6-CcI.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-OTF9Evau.js → quadrantDiagram-W4KKPZXB-Bo2ow9Oq.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-D0Gz33bz.js → requirementDiagram-4Y6WPE33-BpPkW4oY.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-heyjUSye.js → sankeyDiagram-5OEKKPKP-Clh6BTES.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-DJj9hz0k.js → sequenceDiagram-3UESZ5HK-Cj1hGXAx.js} +1 -1
- package/dist/web/assets/{src-4Mn9jGq1.js → src-Bm_zkrgy.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-DJCo8On8.js → stateDiagram-AJRCARHV-D34qh_xR.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-zAS4zz8v.js +1 -0
- package/dist/web/assets/{timeline-definition-PNZ67QCA-B1T8HMSL.js → timeline-definition-PNZ67QCA-C14XIn0F.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-C5JfzLwP.js → vennDiagram-CIIHVFJN-CmRloXuF.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-BirwAuT4.js → wardleyDiagram-YWT4CUSO-BJfba87H.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-ObjzRvhr.js → xychartDiagram-2RQKCTM6-DnEMDZBA.js} +1 -1
- package/dist/web/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/assets/channel-t7kGKHh6.js +0 -1
- package/dist/web/assets/chunk-QZHKN3VN-CHjwSPSc.js +0 -1
- package/dist/web/assets/chunk-WU5MYG2G-C3BQUJws.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-D7vaF2j3.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-CjBR4fNu.js +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-C3hGAxX_.js +0 -1
package/README.md
CHANGED
|
@@ -42,6 +42,7 @@ npx skills add Mone-Lee/diff-review
|
|
|
42
42
|
local-diff-reviewer
|
|
43
43
|
local-diff-reviewer staged
|
|
44
44
|
local-diff-reviewer HEAD~1 HEAD
|
|
45
|
+
local-diff-reviewer --new-session
|
|
45
46
|
local-diff-reviewer stop
|
|
46
47
|
local-diff-reviewer --repo /path/to/project
|
|
47
48
|
```
|
|
@@ -68,14 +69,19 @@ local-diff-reviewer --repo /path/to/project
|
|
|
68
69
|
local-diff-reviewer --repo /path/to/project staged
|
|
69
70
|
```
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
同一项目里再次执行 `local-diff-reviewer` 或 `/diff-review` 时,默认会复用仍在运行的 review 页面和端口,并将该页面刷新为最新 diff。使用 `--new-session` 可以保留已有快照,再打开一个独立页面。不同项目的页面始终分别绑定各自的项目,不会被最后一次启动覆盖。首次启动默认优先使用 `127.0.0.1:4966`;独立页面或其他项目遇到端口占用时会自动选择空闲端口。
|
|
72
73
|
|
|
73
74
|
```text
|
|
74
75
|
项目 A /diff-review -> http://127.0.0.1:4966 -> 项目 A diff
|
|
76
|
+
项目 A /diff-review -> http://127.0.0.1:4966 -> 刷新为项目 A 最新 diff
|
|
77
|
+
项目 A /diff-review --new-session -> http://127.0.0.1:<空闲端口> -> 项目 A 独立快照
|
|
75
78
|
项目 B /diff-review -> http://127.0.0.1:<空闲端口> -> 项目 B diff
|
|
76
79
|
```
|
|
77
80
|
|
|
78
|
-
页面打开后,代码 diff 和 Markdown
|
|
81
|
+
页面打开后,代码 diff 和 Markdown 预览会固定为当前审查快照;工作区继续变动不会自行改写页面内容。再次执行 `local-diff-reviewer` 或 `/diff-review` 会让默认页面自动同步到新快照;使用 `--new-session` 打开的独立页面继续保留旧快照。评论线程与其创建时的 diff 快照绑定:旧线程会在评论侧栏中保留并标记为历史快照,但不会因另一份快照中恰好有相同行号而贴到错误代码上。
|
|
82
|
+
|
|
83
|
+
评论、快照、代码行之间的绑定关系及页面更新判断详见
|
|
84
|
+
[`docs/comment-snapshot-binding.md`](docs/comment-snapshot-binding.md)。
|
|
79
85
|
|
|
80
86
|
注意:本地开发的 `--dev` 模式仍使用 Vite dev server,端口和 API proxy 是固定的;多项目并行审查请使用默认的构建页面模式。
|
|
81
87
|
|
|
@@ -87,6 +93,7 @@ local-diff-reviewer --repo /path/to/project staged
|
|
|
87
93
|
/diff-review
|
|
88
94
|
/diff-review staged
|
|
89
95
|
/diff-review HEAD~1 HEAD
|
|
96
|
+
/diff-review --new-session
|
|
90
97
|
/diff-review stop
|
|
91
98
|
```
|
|
92
99
|
|
package/SKILL.md
CHANGED
|
@@ -15,6 +15,7 @@ Use this skill when the user asks for `/diff-review`, wants to inspect current w
|
|
|
15
15
|
- `/diff-review working`: review current working tree diff.
|
|
16
16
|
- `/diff-review staged`: review staged diff.
|
|
17
17
|
- `/diff-review <base> <target>`: review diff between two Git revisions.
|
|
18
|
+
- `/diff-review --new-session`: preserve the current snapshot and open a separate review session.
|
|
18
19
|
- `/diff-review stop`: stop all review runtimes created for the current workspace repository.
|
|
19
20
|
|
|
20
21
|
Do not ask the user to run a shell CLI manually. Determine the target workspace/repository from the user's active environment context, then run the package command with that repository as the command working directory:
|
|
@@ -33,7 +34,7 @@ npx --yes local-diff-reviewer [args...] \
|
|
|
33
34
|
--comment '{"type":"reply","threadId":"existing-thread-id","body":"Answer the existing thread as the agent."}'
|
|
34
35
|
```
|
|
35
36
|
|
|
36
|
-
After
|
|
37
|
+
After a newly started script prints a local URL, open it in the Codex browser when available. If the script reports `Diff Review refreshed`, do not open another page: the already open review page updates automatically. If browser automation is not available, report the URL.
|
|
37
38
|
|
|
38
39
|
When the user gives you copied prompt text containing `[thread:<id>]`, treat it as an existing review thread. After you answer or make code/doc changes for that thread, append a concise agent reply to the same thread with `--comment '{"type":"reply","threadId":"<id>","body":"..."}'` the next time you launch or refresh the viewer. If the viewer is already running and you can reach the API, post the same reply to `/api/threads/<id>/comments` with `author: "agent"`. Do this for every handled thread unless the user explicitly asks you not to write back. The reply should say what changed or why no change was made, not repeat the full diff.
|
|
39
40
|
|
|
@@ -46,6 +47,7 @@ When the user gives you copied prompt text containing `[thread:<id>]`, treat it
|
|
|
46
47
|
- AI prompt copy output includes `[thread:<id>]`, file path, line number or Markdown source line, and comment body.
|
|
47
48
|
- Agent findings can be preloaded as comments with `--comment`.
|
|
48
49
|
- A thread can contain multiple comments. New findings for the same anchor are appended to the existing thread, and repeated agent comments with identical bodies in the same thread are skipped.
|
|
50
|
+
- Comments are associated with the diff snapshot where they were created. Refreshed workbenches retain older threads in the comment rail as history, without attaching old line comments to changed content.
|
|
49
51
|
|
|
50
52
|
## Comment Arguments
|
|
51
53
|
|
package/dist/cli/start.js
CHANGED
|
@@ -42,7 +42,23 @@ function getMergedThreadStatus(threads) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/server/storage.ts
|
|
45
|
-
async function readComments(repoRoot) {
|
|
45
|
+
async function readComments(repoRoot, diffHash2) {
|
|
46
|
+
const store = await readCommentStore(repoRoot);
|
|
47
|
+
if (!diffHash2) return store;
|
|
48
|
+
return { threads: store.threads.filter((thread) => thread.diffHash === diffHash2) };
|
|
49
|
+
}
|
|
50
|
+
async function attachLegacyComments(repoRoot, diffHash2) {
|
|
51
|
+
const store = await readCommentStore(repoRoot);
|
|
52
|
+
let changed = false;
|
|
53
|
+
for (const thread of store.threads) {
|
|
54
|
+
if (!thread.diffHash) {
|
|
55
|
+
thread.diffHash = diffHash2;
|
|
56
|
+
changed = true;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (changed) await writeComments(repoRoot, store);
|
|
60
|
+
}
|
|
61
|
+
async function readCommentStore(repoRoot) {
|
|
46
62
|
const path = commentsPath(repoRoot);
|
|
47
63
|
try {
|
|
48
64
|
return normalizeStore(JSON.parse(await readFile(path, "utf8")));
|
|
@@ -77,7 +93,7 @@ async function readLegacyComments(repoRoot) {
|
|
|
77
93
|
function normalizeStore(store) {
|
|
78
94
|
const groups = /* @__PURE__ */ new Map();
|
|
79
95
|
for (const thread of store.threads) {
|
|
80
|
-
const key = anchorKey(thread.anchor)
|
|
96
|
+
const key = `${thread.diffHash ?? "legacy"}:${anchorKey(thread.anchor)}`;
|
|
81
97
|
groups.set(key, [...groups.get(key) ?? [], thread]);
|
|
82
98
|
}
|
|
83
99
|
return {
|
|
@@ -105,7 +121,7 @@ function earliestTimestamp(values) {
|
|
|
105
121
|
}
|
|
106
122
|
|
|
107
123
|
// src/core/comment-import.ts
|
|
108
|
-
async function importAgentComments(repoRoot, diffFiles, rawComments) {
|
|
124
|
+
async function importAgentComments(repoRoot, diffHash2, diffFiles, rawComments) {
|
|
109
125
|
const result = { imported: 0, skipped: [] };
|
|
110
126
|
if (rawComments.length === 0) return result;
|
|
111
127
|
const store = await readComments(repoRoot);
|
|
@@ -119,9 +135,9 @@ async function importAgentComments(repoRoot, diffFiles, rawComments) {
|
|
|
119
135
|
}
|
|
120
136
|
continue;
|
|
121
137
|
}
|
|
122
|
-
const thread = buildAgentThread(parsed, diffFiles, result, label);
|
|
138
|
+
const thread = buildAgentThread(parsed, diffHash2, diffFiles, result, label);
|
|
123
139
|
if (!thread) continue;
|
|
124
|
-
const existingThread = store.threads.find((item) => sameAnchor(item.anchor, thread.anchor));
|
|
140
|
+
const existingThread = store.threads.find((item) => item.diffHash === diffHash2 && sameAnchor(item.anchor, thread.anchor));
|
|
125
141
|
if (hasDuplicateAgentComment(store.threads, thread)) {
|
|
126
142
|
result.skipped.push(`${label}: duplicate agent comment skipped`);
|
|
127
143
|
continue;
|
|
@@ -197,7 +213,7 @@ function appendAgentReply(threads, comment, result, label) {
|
|
|
197
213
|
thread.updatedAt = now;
|
|
198
214
|
return true;
|
|
199
215
|
}
|
|
200
|
-
function buildAgentThread(comment, diffFiles, result, label) {
|
|
216
|
+
function buildAgentThread(comment, diffHash2, diffFiles, result, label) {
|
|
201
217
|
const file = diffFiles.find((item) => item.path === comment.filePath || item.oldPath === comment.filePath || item.newPath === comment.filePath);
|
|
202
218
|
if (!file) {
|
|
203
219
|
result.skipped.push(`${label}: ${comment.filePath} is not present in the current diff`);
|
|
@@ -210,6 +226,7 @@ function buildAgentThread(comment, diffFiles, result, label) {
|
|
|
210
226
|
id: crypto.randomUUID(),
|
|
211
227
|
filePath: anchor.filePath,
|
|
212
228
|
anchor,
|
|
229
|
+
diffHash: diffHash2,
|
|
213
230
|
status: "replied",
|
|
214
231
|
comments: [
|
|
215
232
|
{
|
|
@@ -256,7 +273,7 @@ function buildAnchor(file, comment, result, label) {
|
|
|
256
273
|
function hasDuplicateAgentComment(threads, nextThread) {
|
|
257
274
|
const nextComment = nextThread.comments[0]?.body.trim();
|
|
258
275
|
return threads.some((thread) => {
|
|
259
|
-
return thread.comments.some((comment) => comment.author === "agent" && comment.body.trim() === nextComment) && thread.filePath === nextThread.filePath && sameAnchor(thread.anchor, nextThread.anchor);
|
|
276
|
+
return thread.comments.some((comment) => comment.author === "agent" && comment.body.trim() === nextComment) && thread.diffHash === nextThread.diffHash && thread.filePath === nextThread.filePath && sameAnchor(thread.anchor, nextThread.anchor);
|
|
260
277
|
});
|
|
261
278
|
}
|
|
262
279
|
function diffLineExists(file, side, lineNumber) {
|
|
@@ -523,6 +540,10 @@ async function recordRuntime(entry) {
|
|
|
523
540
|
deduped.push(entry);
|
|
524
541
|
await writeRuntime(path, { entries: deduped });
|
|
525
542
|
}
|
|
543
|
+
async function getLiveRuntimes(repoRoot) {
|
|
544
|
+
const store = await readRuntime(runtimePath(repoRoot));
|
|
545
|
+
return store.entries.filter((entry) => isPidAlive(entry.pid)).sort((left, right) => right.startedAt.localeCompare(left.startedAt));
|
|
546
|
+
}
|
|
526
547
|
async function stopRecordedRuntimes(repoRoot) {
|
|
527
548
|
const path = runtimePath(repoRoot);
|
|
528
549
|
const store = await readRuntime(path);
|
|
@@ -601,6 +622,9 @@ import { existsSync } from "node:fs";
|
|
|
601
622
|
import { createServer } from "node:http";
|
|
602
623
|
import { join as join4, normalize as normalize2, resolve, sep } from "node:path";
|
|
603
624
|
|
|
625
|
+
// src/shared/types.ts
|
|
626
|
+
var REVIEW_REFRESH_PROTOCOL = 1;
|
|
627
|
+
|
|
604
628
|
// src/core/markdown-source-map.ts
|
|
605
629
|
import GithubSlugger from "github-slugger";
|
|
606
630
|
function buildMarkdownBlocks(content) {
|
|
@@ -707,7 +731,7 @@ function getAnchorLine(thread) {
|
|
|
707
731
|
|
|
708
732
|
// src/server/index.ts
|
|
709
733
|
async function startServer(state, port = 4966) {
|
|
710
|
-
|
|
734
|
+
let markdownPreviews = await buildMarkdownPreviewCache(state);
|
|
711
735
|
const app = express();
|
|
712
736
|
app.use(express.json({ limit: "2mb" }));
|
|
713
737
|
app.get("/api/session", (_req, res) => {
|
|
@@ -716,6 +740,9 @@ async function startServer(state, port = 4966) {
|
|
|
716
740
|
app.get("/api/diff", (_req, res) => {
|
|
717
741
|
res.json({ files: state.diffFiles });
|
|
718
742
|
});
|
|
743
|
+
app.get("/api/capabilities", (_req, res) => {
|
|
744
|
+
res.json({ reviewRefreshProtocol: REVIEW_REFRESH_PROTOCOL });
|
|
745
|
+
});
|
|
719
746
|
app.get("/api/review-state", async (_req, res, next) => {
|
|
720
747
|
try {
|
|
721
748
|
const comments = await readComments(state.session.repoRoot);
|
|
@@ -724,6 +751,26 @@ async function startServer(state, port = 4966) {
|
|
|
724
751
|
next(error);
|
|
725
752
|
}
|
|
726
753
|
});
|
|
754
|
+
app.post("/api/review-state", async (req, res, next) => {
|
|
755
|
+
try {
|
|
756
|
+
const nextState = req.body;
|
|
757
|
+
if (!nextState.session || nextState.session.repoRoot !== state.session.repoRoot || !Array.isArray(nextState.diffFiles)) {
|
|
758
|
+
res.status(400).json({ error: "Review state must target the running repository" });
|
|
759
|
+
return;
|
|
760
|
+
}
|
|
761
|
+
const nextReviewState = {
|
|
762
|
+
session: nextState.session,
|
|
763
|
+
diffFiles: nextState.diffFiles,
|
|
764
|
+
webDist: state.webDist
|
|
765
|
+
};
|
|
766
|
+
markdownPreviews = await buildMarkdownPreviewCache(nextReviewState);
|
|
767
|
+
state.session = nextReviewState.session;
|
|
768
|
+
state.diffFiles = nextReviewState.diffFiles;
|
|
769
|
+
res.json({ session: state.session, files: state.diffFiles });
|
|
770
|
+
} catch (error) {
|
|
771
|
+
next(error);
|
|
772
|
+
}
|
|
773
|
+
});
|
|
727
774
|
app.get("/api/markdown-preview", async (req, res, next) => {
|
|
728
775
|
try {
|
|
729
776
|
const filePath = String(req.query.path ?? "");
|
|
@@ -770,7 +817,7 @@ async function startServer(state, port = 4966) {
|
|
|
770
817
|
return;
|
|
771
818
|
}
|
|
772
819
|
const store = await readComments(state.session.repoRoot);
|
|
773
|
-
const existingThread = store.threads.find((thread2) => sameAnchor(thread2.anchor, body.anchor));
|
|
820
|
+
const existingThread = store.threads.find((thread2) => thread2.diffHash === state.session.diffHash && sameAnchor(thread2.anchor, body.anchor));
|
|
774
821
|
const comment = {
|
|
775
822
|
id: crypto.randomUUID(),
|
|
776
823
|
body: commentBody,
|
|
@@ -790,6 +837,7 @@ async function startServer(state, port = 4966) {
|
|
|
790
837
|
id: crypto.randomUUID(),
|
|
791
838
|
filePath: body.filePath,
|
|
792
839
|
anchor: body.anchor,
|
|
840
|
+
diffHash: state.session.diffHash,
|
|
793
841
|
status: "submit",
|
|
794
842
|
comments: [comment],
|
|
795
843
|
createdAt: now,
|
|
@@ -996,7 +1044,7 @@ function selectPromptThreads(threads, scope) {
|
|
|
996
1044
|
var packageRoot = resolve2(dirname3(fileURLToPath(import.meta.url)), "../..");
|
|
997
1045
|
var builtWebDist = join5(packageRoot, "dist", "web");
|
|
998
1046
|
async function main() {
|
|
999
|
-
const { command, dev, repo, reviewArgs, comments } = parseCliOptions(process.argv.slice(2));
|
|
1047
|
+
const { command, dev, newSession, repo, reviewArgs, comments } = parseCliOptions(process.argv.slice(2));
|
|
1000
1048
|
if (command === "stop") {
|
|
1001
1049
|
await stopCommand(repo);
|
|
1002
1050
|
return;
|
|
@@ -1013,7 +1061,20 @@ async function main() {
|
|
|
1013
1061
|
diffHash: diffHash(diff),
|
|
1014
1062
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1015
1063
|
};
|
|
1016
|
-
|
|
1064
|
+
await attachLegacyComments(repoRoot, session.diffHash);
|
|
1065
|
+
const importResult = await importAgentComments(repoRoot, session.diffHash, diffFiles, comments);
|
|
1066
|
+
if (!newSession && !dev) {
|
|
1067
|
+
const reusedUrl = await refreshRunningReview(session, diffFiles);
|
|
1068
|
+
if (reusedUrl) {
|
|
1069
|
+
console.log(`Diff Review refreshed: ${reusedUrl}`);
|
|
1070
|
+
console.log(`Repo: ${session.repoName} (${repoRoot})`);
|
|
1071
|
+
console.log(`Mode: ${modeLabel(mode)}`);
|
|
1072
|
+
console.log(`Files: ${diffFiles.length}`);
|
|
1073
|
+
console.log("The existing review page will update automatically.");
|
|
1074
|
+
logImportResult(comments, importResult);
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1017
1078
|
const hasBuiltWeb = existsSync2(join5(builtWebDist, "index.html"));
|
|
1018
1079
|
const apiUrl = await startServer({ session, diffFiles, webDist: hasBuiltWeb ? builtWebDist : void 0 });
|
|
1019
1080
|
const useVite = dev || !hasBuiltWeb;
|
|
@@ -1036,12 +1097,7 @@ async function main() {
|
|
|
1036
1097
|
}
|
|
1037
1098
|
console.log(`Mode: ${modeLabel(mode)}`);
|
|
1038
1099
|
console.log(`Files: ${diffFiles.length}`);
|
|
1039
|
-
|
|
1040
|
-
console.log(`Agent comments imported: ${importResult.imported}`);
|
|
1041
|
-
for (const skipped of importResult.skipped) {
|
|
1042
|
-
console.warn(`Skipped ${skipped}`);
|
|
1043
|
-
}
|
|
1044
|
-
}
|
|
1100
|
+
logImportResult(comments, importResult);
|
|
1045
1101
|
}
|
|
1046
1102
|
function parseCliOptions(args) {
|
|
1047
1103
|
let command = "review";
|
|
@@ -1049,12 +1105,17 @@ function parseCliOptions(args) {
|
|
|
1049
1105
|
const comments = [];
|
|
1050
1106
|
let repo;
|
|
1051
1107
|
let dev = false;
|
|
1108
|
+
let newSession = false;
|
|
1052
1109
|
for (let index = 0; index < args.length; index += 1) {
|
|
1053
1110
|
const arg = args[index];
|
|
1054
1111
|
if (arg === "--dev") {
|
|
1055
1112
|
dev = true;
|
|
1056
1113
|
continue;
|
|
1057
1114
|
}
|
|
1115
|
+
if (arg === "--new-session") {
|
|
1116
|
+
newSession = true;
|
|
1117
|
+
continue;
|
|
1118
|
+
}
|
|
1058
1119
|
if (arg === "stop") {
|
|
1059
1120
|
command = "stop";
|
|
1060
1121
|
continue;
|
|
@@ -1087,7 +1148,35 @@ function parseCliOptions(args) {
|
|
|
1087
1148
|
}
|
|
1088
1149
|
reviewArgs.push(arg);
|
|
1089
1150
|
}
|
|
1090
|
-
return { command, dev, repo, reviewArgs, comments };
|
|
1151
|
+
return { command, dev, newSession, repo, reviewArgs, comments };
|
|
1152
|
+
}
|
|
1153
|
+
async function refreshRunningReview(session, diffFiles) {
|
|
1154
|
+
const runtimes = await getLiveRuntimes(session.repoRoot);
|
|
1155
|
+
for (const runtime of runtimes) {
|
|
1156
|
+
const apiUrl = `http://127.0.0.1:${runtime.apiPort}`;
|
|
1157
|
+
try {
|
|
1158
|
+
const capabilityResponse = await fetch(`${apiUrl}/api/capabilities`);
|
|
1159
|
+
if (!capabilityResponse.ok) continue;
|
|
1160
|
+
const capabilities = await capabilityResponse.json();
|
|
1161
|
+
if (capabilities.reviewRefreshProtocol !== REVIEW_REFRESH_PROTOCOL) continue;
|
|
1162
|
+
const response = await fetch(`${apiUrl}/api/review-state`, {
|
|
1163
|
+
method: "POST",
|
|
1164
|
+
headers: { "Content-Type": "application/json" },
|
|
1165
|
+
body: JSON.stringify({ session, diffFiles })
|
|
1166
|
+
});
|
|
1167
|
+
if (!response.ok) continue;
|
|
1168
|
+
return runtime.usesVite ? "http://127.0.0.1:5173" : apiUrl;
|
|
1169
|
+
} catch {
|
|
1170
|
+
}
|
|
1171
|
+
}
|
|
1172
|
+
return void 0;
|
|
1173
|
+
}
|
|
1174
|
+
function logImportResult(comments, result) {
|
|
1175
|
+
if (comments.length === 0) return;
|
|
1176
|
+
console.log(`Agent comments imported: ${result.imported}`);
|
|
1177
|
+
for (const skipped of result.skipped) {
|
|
1178
|
+
console.warn(`Skipped ${skipped}`);
|
|
1179
|
+
}
|
|
1091
1180
|
}
|
|
1092
1181
|
async function stopCommand(repo) {
|
|
1093
1182
|
const repoRoot = await getRepoRoot(repo ?? process.cwd());
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{n as e,t}from"./path-BJQEcSo7.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-
|
|
1
|
+
import{n as e,t}from"./path-BJQEcSo7.js";import{a as n,c as r,d as i,f as a,i as o,l as s,m as c,n as l,o as u,p as d,r as f,u as p}from"./dist-DP09l7dv.js";function m(e){return e.innerRadius}function h(e){return e.outerRadius}function g(e){return e.startAngle}function _(e){return e.endAngle}function v(e){return e&&e.padAngle}function y(e,t,n,r,i,a,o,s){var c=n-e,l=r-t,u=o-i,d=s-a,f=d*c-u*l;if(!(f*f<1e-12))return f=(u*(t-a)-d*(e-i))/f,[e+f*c,t+f*l]}function b(e,t,n,r,i,a,o){var c=e-n,l=t-r,u=(o?a:-a)/d(c*c+l*l),f=u*l,p=-u*c,m=e+f,h=t+p,g=n+f,_=r+p,v=(m+g)/2,y=(h+_)/2,b=g-m,x=_-h,S=b*b+x*x,C=i-a,w=m*_-g*h,T=(x<0?-1:1)*d(s(0,C*C*S-w*w)),E=(w*x-b*T)/S,D=(-w*b-x*T)/S,O=(w*x+b*T)/S,k=(-w*b+x*T)/S,A=E-v,j=D-y,M=O-v,N=k-y;return A*A+j*j>M*M+N*N&&(E=O,D=k),{cx:E,cy:D,x01:-f,y01:-p,x11:E*(i/C-1),y11:D*(i/C-1)}}function x(){var s=m,x=h,S=e(0),C=null,w=g,T=_,E=v,D=null,O=t(k);function k(){var e,t,m=+s.apply(this,arguments),h=+x.apply(this,arguments),g=w.apply(this,arguments)-r,_=T.apply(this,arguments)-r,v=l(_-g),k=_>g;if(D||=e=O(),h<m&&(t=h,h=m,m=t),!(h>1e-12))D.moveTo(0,0);else if(v>c-1e-12)D.moveTo(h*u(g),h*a(g)),D.arc(0,0,h,g,_,!k),m>1e-12&&(D.moveTo(m*u(_),m*a(_)),D.arc(0,0,m,_,g,k));else{var A=g,j=_,M=g,N=_,P=v,F=v,I=E.apply(this,arguments)/2,L=I>1e-12&&(C?+C.apply(this,arguments):d(m*m+h*h)),R=p(l(h-m)/2,+S.apply(this,arguments)),z=R,B=R,V,H;if(L>1e-12){var U=o(L/m*a(I)),W=o(L/h*a(I));(P-=U*2)>1e-12?(U*=k?1:-1,M+=U,N-=U):(P=0,M=N=(g+_)/2),(F-=W*2)>1e-12?(W*=k?1:-1,A+=W,j-=W):(F=0,A=j=(g+_)/2)}var G=h*u(A),K=h*a(A),q=m*u(N),J=m*a(N);if(R>1e-12){var Y=h*u(j),X=h*a(j),Z=m*u(M),Q=m*a(M),$;if(v<i)if($=y(G,K,Z,Q,Y,X,q,J)){var ee=G-$[0],te=K-$[1],ne=Y-$[0],re=X-$[1],ie=1/a(f((ee*ne+te*re)/(d(ee*ee+te*te)*d(ne*ne+re*re)))/2),ae=d($[0]*$[0]+$[1]*$[1]);z=p(R,(m-ae)/(ie-1)),B=p(R,(h-ae)/(ie+1))}else z=B=0}F>1e-12?B>1e-12?(V=b(Z,Q,G,K,h,B,k),H=b(Y,X,q,J,h,B,k),D.moveTo(V.cx+V.x01,V.cy+V.y01),B<R?D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,B,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,h,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),!k),D.arc(H.cx,H.cy,B,n(H.y11,H.x11),n(H.y01,H.x01),!k))):(D.moveTo(G,K),D.arc(0,0,h,A,j,!k)):D.moveTo(G,K),!(m>1e-12)||!(P>1e-12)?D.lineTo(q,J):z>1e-12?(V=b(q,J,Y,X,m,-z,k),H=b(G,K,Z,Q,m,-z,k),D.lineTo(V.cx+V.x01,V.cy+V.y01),z<R?D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(H.y01,H.x01),!k):(D.arc(V.cx,V.cy,z,n(V.y01,V.x01),n(V.y11,V.x11),!k),D.arc(0,0,m,n(V.cy+V.y11,V.cx+V.x11),n(H.cy+H.y11,H.cx+H.x11),k),D.arc(H.cx,H.cy,z,n(H.y11,H.x11),n(H.y01,H.x01),!k))):D.arc(0,0,m,N,M,k)}if(D.closePath(),e)return D=null,e+``||null}return k.centroid=function(){var e=(+s.apply(this,arguments)+ +x.apply(this,arguments))/2,t=(+w.apply(this,arguments)+ +T.apply(this,arguments))/2-i/2;return[u(t)*e,a(t)*e]},k.innerRadius=function(t){return arguments.length?(s=typeof t==`function`?t:e(+t),k):s},k.outerRadius=function(t){return arguments.length?(x=typeof t==`function`?t:e(+t),k):x},k.cornerRadius=function(t){return arguments.length?(S=typeof t==`function`?t:e(+t),k):S},k.padRadius=function(t){return arguments.length?(C=t==null?null:typeof t==`function`?t:e(+t),k):C},k.startAngle=function(t){return arguments.length?(w=typeof t==`function`?t:e(+t),k):w},k.endAngle=function(t){return arguments.length?(T=typeof t==`function`?t:e(+t),k):T},k.padAngle=function(t){return arguments.length?(E=typeof t==`function`?t:e(+t),k):E},k.context=function(e){return arguments.length?(D=e??null,k):D},k}export{x as t};
|