local-diff-reviewer 1.0.11 → 2.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.md +9 -2
- package/SKILL.md +3 -1
- package/dist/cli/start.js +97 -18
- package/dist/web/assets/{arc-BOpgS1XG.js → arc-BTM4ghNf.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-C7QsBMbs.js → architectureDiagram-3BPJPVTR-DyOV0Jtr.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-QaQaVyDL.js → blockDiagram-GPEHLZMM-CxJbhmIT.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-CufBBkVk.js → c4Diagram-AAUBKEIU-BJQFA8CQ.js} +1 -1
- package/dist/web/assets/channel-zJdz6BA1.js +1 -0
- package/dist/web/assets/{chunk-2J33WTMH-Cj_oaVb4.js → chunk-2J33WTMH-C5OtbA_r.js} +1 -1
- package/dist/web/assets/{chunk-3OPIFGDE-DmJyquPX.js → chunk-3OPIFGDE-B9KuazSf.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-CwaF4zJY.js → chunk-4BX2VUAB-B8LRminS.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-DqeKiemL.js → chunk-55IACEB6-SkDhErOo.js} +1 -1
- package/dist/web/assets/{chunk-5ZQYHXKU-DNp0ZNVB.js → chunk-5ZQYHXKU-Bb82bV79.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-BKKrSCOF.js → chunk-727SXJPM-Dl1e3M-k.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-BCNSIQmO.js → chunk-AQP2D5EJ-ds7_Qz_Y.js} +1 -1
- package/dist/web/assets/{chunk-BSJP7CBP-Ce9WIScW.js → chunk-BSJP7CBP-Ddngm4Zu.js} +1 -1
- package/dist/web/assets/{chunk-CSCIHK7Q-Cj2N3A_X.js → chunk-CSCIHK7Q-zBbkk0kR.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-BADdZIdh.js → chunk-FMBD7UC4-C9gGg2hs.js} +1 -1
- package/dist/web/assets/{chunk-KSCS5N6A-BeYyq8hv.js → chunk-KSCS5N6A-CYRCkspU.js} +1 -1
- package/dist/web/assets/{chunk-L5ZTLDWV-vyV2WecR.js → chunk-L5ZTLDWV-at0vaITZ.js} +1 -1
- package/dist/web/assets/{chunk-LZXEDZCA-DFAA6FIb.js → chunk-LZXEDZCA-CB2tlbwU.js} +2 -2
- package/dist/web/assets/{chunk-ND2GUHAM-BvZh95FB.js → chunk-ND2GUHAM-SF9ziKuY.js} +1 -1
- package/dist/web/assets/{chunk-NZK2D7GU-Cd_AyI2R.js → chunk-NZK2D7GU-cDloGQoC.js} +1 -1
- package/dist/web/assets/{chunk-O5CBEL6O-BWTFeGAA.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-BoQ5PHbI.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-DEOEnRu1.js → cose-bilkent-S5V4N54A-CVt4gjy_.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-3EFNtJfA.js → dagre-BM42HDAG-DsFFRdXb.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-Dl6nD1tR.js → diagram-2AECGRRQ-DU-SS203.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-C2ZqLXAR.js → diagram-5GNKFQAL-Caxxw6ez.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-CLemszwR.js → diagram-KO2AKTUF-C86fE-G2.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-WJtVAgyf.js → diagram-LMA3HP47-CbblsEig.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-DLcglDzh.js → diagram-OG6HWLK6-VVwPWCNJ.js} +1 -1
- package/dist/web/assets/{dist-BD8BTEle.js → dist-DP09l7dv.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-DB0o5lHM.js → erDiagram-TEJ5UH35-DybhgsWs.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-DGNyzO3n.js → flowDiagram-I6XJVG4X-L175L0f8.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-DFcI8gjU.js → ganttDiagram-6RSMTGT7-Bnn1hKvs.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-q30GMhck.js → gitGraphDiagram-PVQCEYII-C0ZBrF0f.js} +1 -1
- package/dist/web/assets/{index-mFrEtl6e.js → index-B4AzQzzw.js} +24 -24
- package/dist/web/assets/index-uLsBVssy.css +1 -0
- package/dist/web/assets/{infoDiagram-5YYISTIA-Bn7G90QN.js → infoDiagram-5YYISTIA-BjE1edtT.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-BV3Sotps.js → ishikawaDiagram-YF4QCWOH-CA7-Z3nV.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-BtSXI5Am.js → journeyDiagram-JHISSGLW-7yRPAxui.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-B8tZB50Y.js → kanban-definition-UN3LZRKU-CxJbCLrw.js} +1 -1
- package/dist/web/assets/{line-ig55n5ES.js → line-BZChgTw_.js} +1 -1
- package/dist/web/assets/{linear-qGjeU4AP.js → linear-Ch3uwdzh.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-DxxQqVaE.js → mermaid-parser.core-BwmBog8o.js} +1 -1
- package/dist/web/assets/{mermaid.core-BBkLU4xy.js → mermaid.core-DbfGaZdz.js} +3 -3
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-D61BQDKI.js → mindmap-definition-RKZ34NQL-DY-t2KPo.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-GrXCe3Mv.js → pieDiagram-4H26LBE5-BkM6-CcI.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-Bmj1Zhyf.js → quadrantDiagram-W4KKPZXB-Bo2ow9Oq.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-C4Wt9tYO.js → requirementDiagram-4Y6WPE33-BpPkW4oY.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-BpEXiP3R.js → sankeyDiagram-5OEKKPKP-Clh6BTES.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-BY0QnrHB.js → sequenceDiagram-3UESZ5HK-Cj1hGXAx.js} +1 -1
- package/dist/web/assets/{src-DKCcYPqn.js → src-Bm_zkrgy.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-BNe3FO3i.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-BlWgTk_0.js → timeline-definition-PNZ67QCA-C14XIn0F.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-Dpfbo-af.js → vennDiagram-CIIHVFJN-CmRloXuF.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-BKnl9Arx.js → wardleyDiagram-YWT4CUSO-BJfba87H.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-C5VJlVCG.js → xychartDiagram-2RQKCTM6-DnEMDZBA.js} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/dist/web/assets/channel-BPEgeY7j.js +0 -1
- package/dist/web/assets/chunk-QZHKN3VN-BM3ywZyg.js +0 -1
- package/dist/web/assets/chunk-WU5MYG2G-DwPzycK6.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-B-Ye1HF8.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-DnIZKORc.js +0 -1
- package/dist/web/assets/index-1vvZG57w.css +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-DMtybq8y.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);
|
|
@@ -707,7 +728,7 @@ function getAnchorLine(thread) {
|
|
|
707
728
|
|
|
708
729
|
// src/server/index.ts
|
|
709
730
|
async function startServer(state, port = 4966) {
|
|
710
|
-
|
|
731
|
+
let markdownPreviews = await buildMarkdownPreviewCache(state);
|
|
711
732
|
const app = express();
|
|
712
733
|
app.use(express.json({ limit: "2mb" }));
|
|
713
734
|
app.get("/api/session", (_req, res) => {
|
|
@@ -724,6 +745,26 @@ async function startServer(state, port = 4966) {
|
|
|
724
745
|
next(error);
|
|
725
746
|
}
|
|
726
747
|
});
|
|
748
|
+
app.post("/api/review-state", async (req, res, next) => {
|
|
749
|
+
try {
|
|
750
|
+
const nextState = req.body;
|
|
751
|
+
if (!nextState.session || nextState.session.repoRoot !== state.session.repoRoot || !Array.isArray(nextState.diffFiles)) {
|
|
752
|
+
res.status(400).json({ error: "Review state must target the running repository" });
|
|
753
|
+
return;
|
|
754
|
+
}
|
|
755
|
+
const nextReviewState = {
|
|
756
|
+
session: nextState.session,
|
|
757
|
+
diffFiles: nextState.diffFiles,
|
|
758
|
+
webDist: state.webDist
|
|
759
|
+
};
|
|
760
|
+
markdownPreviews = await buildMarkdownPreviewCache(nextReviewState);
|
|
761
|
+
state.session = nextReviewState.session;
|
|
762
|
+
state.diffFiles = nextReviewState.diffFiles;
|
|
763
|
+
res.json({ session: state.session, files: state.diffFiles });
|
|
764
|
+
} catch (error) {
|
|
765
|
+
next(error);
|
|
766
|
+
}
|
|
767
|
+
});
|
|
727
768
|
app.get("/api/markdown-preview", async (req, res, next) => {
|
|
728
769
|
try {
|
|
729
770
|
const filePath = String(req.query.path ?? "");
|
|
@@ -770,7 +811,7 @@ async function startServer(state, port = 4966) {
|
|
|
770
811
|
return;
|
|
771
812
|
}
|
|
772
813
|
const store = await readComments(state.session.repoRoot);
|
|
773
|
-
const existingThread = store.threads.find((thread2) => sameAnchor(thread2.anchor, body.anchor));
|
|
814
|
+
const existingThread = store.threads.find((thread2) => thread2.diffHash === state.session.diffHash && sameAnchor(thread2.anchor, body.anchor));
|
|
774
815
|
const comment = {
|
|
775
816
|
id: crypto.randomUUID(),
|
|
776
817
|
body: commentBody,
|
|
@@ -790,6 +831,7 @@ async function startServer(state, port = 4966) {
|
|
|
790
831
|
id: crypto.randomUUID(),
|
|
791
832
|
filePath: body.filePath,
|
|
792
833
|
anchor: body.anchor,
|
|
834
|
+
diffHash: state.session.diffHash,
|
|
793
835
|
status: "submit",
|
|
794
836
|
comments: [comment],
|
|
795
837
|
createdAt: now,
|
|
@@ -996,7 +1038,7 @@ function selectPromptThreads(threads, scope) {
|
|
|
996
1038
|
var packageRoot = resolve2(dirname3(fileURLToPath(import.meta.url)), "../..");
|
|
997
1039
|
var builtWebDist = join5(packageRoot, "dist", "web");
|
|
998
1040
|
async function main() {
|
|
999
|
-
const { command, dev, repo, reviewArgs, comments } = parseCliOptions(process.argv.slice(2));
|
|
1041
|
+
const { command, dev, newSession, repo, reviewArgs, comments } = parseCliOptions(process.argv.slice(2));
|
|
1000
1042
|
if (command === "stop") {
|
|
1001
1043
|
await stopCommand(repo);
|
|
1002
1044
|
return;
|
|
@@ -1013,7 +1055,20 @@ async function main() {
|
|
|
1013
1055
|
diffHash: diffHash(diff),
|
|
1014
1056
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1015
1057
|
};
|
|
1016
|
-
|
|
1058
|
+
await attachLegacyComments(repoRoot, session.diffHash);
|
|
1059
|
+
const importResult = await importAgentComments(repoRoot, session.diffHash, diffFiles, comments);
|
|
1060
|
+
if (!newSession && !dev) {
|
|
1061
|
+
const reusedUrl = await refreshRunningReview(session, diffFiles);
|
|
1062
|
+
if (reusedUrl) {
|
|
1063
|
+
console.log(`Diff Review refreshed: ${reusedUrl}`);
|
|
1064
|
+
console.log(`Repo: ${session.repoName} (${repoRoot})`);
|
|
1065
|
+
console.log(`Mode: ${modeLabel(mode)}`);
|
|
1066
|
+
console.log(`Files: ${diffFiles.length}`);
|
|
1067
|
+
console.log("The existing review page will update automatically.");
|
|
1068
|
+
logImportResult(comments, importResult);
|
|
1069
|
+
return;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1017
1072
|
const hasBuiltWeb = existsSync2(join5(builtWebDist, "index.html"));
|
|
1018
1073
|
const apiUrl = await startServer({ session, diffFiles, webDist: hasBuiltWeb ? builtWebDist : void 0 });
|
|
1019
1074
|
const useVite = dev || !hasBuiltWeb;
|
|
@@ -1036,12 +1091,7 @@ async function main() {
|
|
|
1036
1091
|
}
|
|
1037
1092
|
console.log(`Mode: ${modeLabel(mode)}`);
|
|
1038
1093
|
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
|
-
}
|
|
1094
|
+
logImportResult(comments, importResult);
|
|
1045
1095
|
}
|
|
1046
1096
|
function parseCliOptions(args) {
|
|
1047
1097
|
let command = "review";
|
|
@@ -1049,12 +1099,17 @@ function parseCliOptions(args) {
|
|
|
1049
1099
|
const comments = [];
|
|
1050
1100
|
let repo;
|
|
1051
1101
|
let dev = false;
|
|
1102
|
+
let newSession = false;
|
|
1052
1103
|
for (let index = 0; index < args.length; index += 1) {
|
|
1053
1104
|
const arg = args[index];
|
|
1054
1105
|
if (arg === "--dev") {
|
|
1055
1106
|
dev = true;
|
|
1056
1107
|
continue;
|
|
1057
1108
|
}
|
|
1109
|
+
if (arg === "--new-session") {
|
|
1110
|
+
newSession = true;
|
|
1111
|
+
continue;
|
|
1112
|
+
}
|
|
1058
1113
|
if (arg === "stop") {
|
|
1059
1114
|
command = "stop";
|
|
1060
1115
|
continue;
|
|
@@ -1087,7 +1142,31 @@ function parseCliOptions(args) {
|
|
|
1087
1142
|
}
|
|
1088
1143
|
reviewArgs.push(arg);
|
|
1089
1144
|
}
|
|
1090
|
-
return { command, dev, repo, reviewArgs, comments };
|
|
1145
|
+
return { command, dev, newSession, repo, reviewArgs, comments };
|
|
1146
|
+
}
|
|
1147
|
+
async function refreshRunningReview(session, diffFiles) {
|
|
1148
|
+
const runtimes = await getLiveRuntimes(session.repoRoot);
|
|
1149
|
+
for (const runtime of runtimes) {
|
|
1150
|
+
const apiUrl = `http://127.0.0.1:${runtime.apiPort}`;
|
|
1151
|
+
try {
|
|
1152
|
+
const response = await fetch(`${apiUrl}/api/review-state`, {
|
|
1153
|
+
method: "POST",
|
|
1154
|
+
headers: { "Content-Type": "application/json" },
|
|
1155
|
+
body: JSON.stringify({ session, diffFiles })
|
|
1156
|
+
});
|
|
1157
|
+
if (!response.ok) continue;
|
|
1158
|
+
return runtime.usesVite ? "http://127.0.0.1:5173" : apiUrl;
|
|
1159
|
+
} catch {
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
return void 0;
|
|
1163
|
+
}
|
|
1164
|
+
function logImportResult(comments, result) {
|
|
1165
|
+
if (comments.length === 0) return;
|
|
1166
|
+
console.log(`Agent comments imported: ${result.imported}`);
|
|
1167
|
+
for (const skipped of result.skipped) {
|
|
1168
|
+
console.warn(`Skipped ${skipped}`);
|
|
1169
|
+
}
|
|
1091
1170
|
}
|
|
1092
1171
|
async function stopCommand(repo) {
|
|
1093
1172
|
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};
|