local-diff-reviewer 1.0.6 → 1.0.10
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 +21 -0
- package/SKILL.md +1 -0
- package/dist/cli/start.js +182 -26
- package/dist/web/assets/{arc-DfZYUaz4.js → arc-FnFNT5Eh.js} +1 -1
- package/dist/web/assets/{architectureDiagram-3BPJPVTR-Un1C7dX5.js → architectureDiagram-3BPJPVTR-Co5Z_QNw.js} +1 -1
- package/dist/web/assets/{blockDiagram-GPEHLZMM-DoE66LOm.js → blockDiagram-GPEHLZMM-BNsiQyLf.js} +1 -1
- package/dist/web/assets/{c4Diagram-AAUBKEIU-Ch17mKwl.js → c4Diagram-AAUBKEIU-BAZdudVw.js} +1 -1
- package/dist/web/assets/channel-BPbPhJj8.js +1 -0
- package/dist/web/assets/{chunk-2J33WTMH-DsTTzQ06.js → chunk-2J33WTMH-B0vxrf0b.js} +1 -1
- package/dist/web/assets/{chunk-3OPIFGDE-C4BFnYAe.js → chunk-3OPIFGDE-DejuRnfm.js} +1 -1
- package/dist/web/assets/{chunk-4BX2VUAB-CBYOekG6.js → chunk-4BX2VUAB-Bg5xzq_y.js} +1 -1
- package/dist/web/assets/{chunk-55IACEB6-bkX7IfWj.js → chunk-55IACEB6-D0Fci-zx.js} +1 -1
- package/dist/web/assets/{chunk-5ZQYHXKU-DmYX07rl.js → chunk-5ZQYHXKU-BPK5ENee.js} +1 -1
- package/dist/web/assets/{chunk-727SXJPM-myW6yB1O.js → chunk-727SXJPM-7K2UgSc-.js} +1 -1
- package/dist/web/assets/{chunk-AQP2D5EJ-fa_HpDkc.js → chunk-AQP2D5EJ-BWk8xaA0.js} +1 -1
- package/dist/web/assets/{chunk-BSJP7CBP-NpOkPl8K.js → chunk-BSJP7CBP-qQ__7B7h.js} +1 -1
- package/dist/web/assets/{chunk-CSCIHK7Q-jWMYsJiR.js → chunk-CSCIHK7Q-CrFKiZka.js} +1 -1
- package/dist/web/assets/{chunk-FMBD7UC4-BuB89xp8.js → chunk-FMBD7UC4-DvZyaP9P.js} +1 -1
- package/dist/web/assets/{chunk-KSCS5N6A-BvSsBz45.js → chunk-KSCS5N6A-BbcBNuLx.js} +1 -1
- package/dist/web/assets/{chunk-L5ZTLDWV-Cslvwh3t.js → chunk-L5ZTLDWV-D9Jpxw4I.js} +1 -1
- package/dist/web/assets/{chunk-LZXEDZCA-lDGN1tUz.js → chunk-LZXEDZCA-C0KF6zNF.js} +2 -2
- package/dist/web/assets/{chunk-ND2GUHAM-DGtWurFf.js → chunk-ND2GUHAM-B4UYa5t-.js} +1 -1
- package/dist/web/assets/{chunk-NZK2D7GU-BhN9qm2L.js → chunk-NZK2D7GU-DwIixx-H.js} +1 -1
- package/dist/web/assets/{chunk-O5CBEL6O-DPNhEfdl.js → chunk-O5CBEL6O-B6AfXN2g.js} +1 -1
- package/dist/web/assets/chunk-QZHKN3VN-erTNME5J.js +1 -0
- package/dist/web/assets/chunk-WU5MYG2G-CbnTR5Pg.js +1 -0
- package/dist/web/assets/{chunk-XPW4576I-BRb6pBFA.js → chunk-XPW4576I-D15NjZ7Z.js} +1 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-CuafptIX.js +1 -0
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-BIT1gXn5.js +1 -0
- package/dist/web/assets/{cose-bilkent-S5V4N54A-DP7aCqjn.js → cose-bilkent-S5V4N54A-BNwgjIv3.js} +1 -1
- package/dist/web/assets/{dagre-BM42HDAG-DW8OoOF9.js → dagre-BM42HDAG-CpwwUyr_.js} +1 -1
- package/dist/web/assets/{diagram-2AECGRRQ-Dhr52Lcq.js → diagram-2AECGRRQ-Bw8x1vsX.js} +1 -1
- package/dist/web/assets/{diagram-5GNKFQAL-00RS5q-p.js → diagram-5GNKFQAL-BB2GXRx9.js} +1 -1
- package/dist/web/assets/{diagram-KO2AKTUF-CV1YsijI.js → diagram-KO2AKTUF-BgaaH0hW.js} +1 -1
- package/dist/web/assets/{diagram-LMA3HP47-Cu6hlhhL.js → diagram-LMA3HP47-BisC4uiR.js} +1 -1
- package/dist/web/assets/{diagram-OG6HWLK6-f8Aglkk5.js → diagram-OG6HWLK6-BDj48Uho.js} +1 -1
- package/dist/web/assets/{dist-BbvzfnQH.js → dist-D2UD_g6s.js} +1 -1
- package/dist/web/assets/{erDiagram-TEJ5UH35-BOpwL6Hq.js → erDiagram-TEJ5UH35-cBF6TfGh.js} +1 -1
- package/dist/web/assets/{flowDiagram-I6XJVG4X-JpfaqfMd.js → flowDiagram-I6XJVG4X-CYc4faT9.js} +1 -1
- package/dist/web/assets/{ganttDiagram-6RSMTGT7-_07N56My.js → ganttDiagram-6RSMTGT7-CvZHLnnj.js} +1 -1
- package/dist/web/assets/{gitGraphDiagram-PVQCEYII-CW2hqk_8.js → gitGraphDiagram-PVQCEYII-B9EFahM9.js} +1 -1
- package/dist/web/assets/index-1vvZG57w.css +1 -0
- package/dist/web/assets/{index-CxFV1WHQ.js → index-wy40yeNf.js} +24 -24
- package/dist/web/assets/{infoDiagram-5YYISTIA-B35H3DgZ.js → infoDiagram-5YYISTIA-OwyPUHlr.js} +1 -1
- package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-DxVWMRSB.js → ishikawaDiagram-YF4QCWOH-DWNJFp-G.js} +1 -1
- package/dist/web/assets/{journeyDiagram-JHISSGLW-C__VorSx.js → journeyDiagram-JHISSGLW-Fx_1G-6i.js} +1 -1
- package/dist/web/assets/{kanban-definition-UN3LZRKU-BKSCqg5P.js → kanban-definition-UN3LZRKU-Dq4CooCl.js} +1 -1
- package/dist/web/assets/{line-SFaTyCgO.js → line-HQcA7ihX.js} +1 -1
- package/dist/web/assets/{linear-CCDSib_A.js → linear-BZeIaDEq.js} +1 -1
- package/dist/web/assets/{mermaid-parser.core-B4q5D_X_.js → mermaid-parser.core-DJsgXM22.js} +1 -1
- package/dist/web/assets/{mermaid.core-DiPBDfaO.js → mermaid.core-CkAku_bb.js} +3 -3
- package/dist/web/assets/{mindmap-definition-RKZ34NQL-BJZedUBK.js → mindmap-definition-RKZ34NQL-BWRzwBxY.js} +1 -1
- package/dist/web/assets/{pieDiagram-4H26LBE5-DtkdCUA9.js → pieDiagram-4H26LBE5-woyVH6aA.js} +1 -1
- package/dist/web/assets/{quadrantDiagram-W4KKPZXB-Cks8DI1_.js → quadrantDiagram-W4KKPZXB-DmMwbj7N.js} +1 -1
- package/dist/web/assets/{requirementDiagram-4Y6WPE33-DIJqz1Ht.js → requirementDiagram-4Y6WPE33-1vUYL3sj.js} +1 -1
- package/dist/web/assets/{sankeyDiagram-5OEKKPKP-waqn6y15.js → sankeyDiagram-5OEKKPKP-BiRxghpn.js} +1 -1
- package/dist/web/assets/{sequenceDiagram-3UESZ5HK-Cyzxtc0o.js → sequenceDiagram-3UESZ5HK-C-SgacOL.js} +1 -1
- package/dist/web/assets/{src-DboPYYAM.js → src-DMlkBZbY.js} +1 -1
- package/dist/web/assets/{stateDiagram-AJRCARHV-BIfp7CSY.js → stateDiagram-AJRCARHV-Ci958Ibo.js} +1 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-BcqZc5a0.js +1 -0
- package/dist/web/assets/{timeline-definition-PNZ67QCA-5e-cw7ns.js → timeline-definition-PNZ67QCA-CQzVyH6s.js} +1 -1
- package/dist/web/assets/{vennDiagram-CIIHVFJN-H4TiL4fv.js → vennDiagram-CIIHVFJN-DjtlRdrY.js} +1 -1
- package/dist/web/assets/{wardleyDiagram-YWT4CUSO-C9je1Qmh.js → wardleyDiagram-YWT4CUSO-Braalnw5.js} +1 -1
- package/dist/web/assets/{xychartDiagram-2RQKCTM6-CbnF4TLC.js → xychartDiagram-2RQKCTM6-YjueRiNp.js} +1 -1
- package/dist/web/index.html +2 -2
- package/docs/images/diff-review-intro.html +767 -0
- package/docs/images/diff-review-intro.png +0 -0
- package/docs/images/image.png +0 -0
- package/package.json +1 -1
- package/dist/web/assets/channel-tS6_tEZH.js +0 -1
- package/dist/web/assets/chunk-QZHKN3VN-MdvGD9q1.js +0 -1
- package/dist/web/assets/chunk-WU5MYG2G-Cot-8ei2.js +0 -1
- package/dist/web/assets/classDiagram-4FO5ZUOK-Ba9dn90z.js +0 -1
- package/dist/web/assets/classDiagram-v2-Q7XG4LA2-AkPgLlFm.js +0 -1
- package/dist/web/assets/index-CND5NAGY.css +0 -1
- package/dist/web/assets/stateDiagram-v2-BHNVJYJU-CBvj4HDj.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 stop
|
|
45
46
|
local-diff-reviewer --repo /path/to/project
|
|
46
47
|
```
|
|
47
48
|
|
|
@@ -51,6 +52,15 @@ local-diff-reviewer --repo /path/to/project
|
|
|
51
52
|
- `staged`:审查已经 `git add`、但还没有提交的改动。
|
|
52
53
|
- `revision`:审查两个 revision 之间的差异,例如 `local-diff-reviewer HEAD~1 HEAD` 会比较 `HEAD~1..HEAD`。
|
|
53
54
|
|
|
55
|
+
停止当前项目已启动的 review 进程:
|
|
56
|
+
|
|
57
|
+
- `stop`:按当前 Git 仓库范围清理该仓库启动过的 review 运行进程(包含其 API 端口进程)。
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
local-diff-reviewer stop
|
|
61
|
+
local-diff-reviewer --repo /path/to/project stop
|
|
62
|
+
```
|
|
63
|
+
|
|
54
64
|
如果命令不是在目标项目目录里启动,可以用 `--repo <path>` 显式指定要审查的 Git 仓库:
|
|
55
65
|
|
|
56
66
|
```bash
|
|
@@ -65,6 +75,8 @@ local-diff-reviewer --repo /path/to/project staged
|
|
|
65
75
|
项目 B /diff-review -> http://127.0.0.1:<空闲端口> -> 项目 B diff
|
|
66
76
|
```
|
|
67
77
|
|
|
78
|
+
页面打开后,代码 diff 和 Markdown 预览会固定为本次启动时的快照;工作区继续变动不会改写已打开页面里的代码内容。评论线程会在页面可见时持续同步。若需要审查最新工作区内容,请再次执行 `local-diff-reviewer` 或 `/diff-review` 打开新的 review 会话。
|
|
79
|
+
|
|
68
80
|
注意:本地开发的 `--dev` 模式仍使用 Vite dev server,端口和 API proxy 是固定的;多项目并行审查请使用默认的构建页面模式。
|
|
69
81
|
|
|
70
82
|
## Skill 使用方式
|
|
@@ -75,6 +87,7 @@ local-diff-reviewer --repo /path/to/project staged
|
|
|
75
87
|
/diff-review
|
|
76
88
|
/diff-review staged
|
|
77
89
|
/diff-review HEAD~1 HEAD
|
|
90
|
+
/diff-review stop
|
|
78
91
|
```
|
|
79
92
|
|
|
80
93
|
安装 skill:
|
|
@@ -137,12 +150,20 @@ npm run release major
|
|
|
137
150
|
|
|
138
151
|
该命令会按顺序执行:
|
|
139
152
|
|
|
153
|
+
- 发布前检查 npmjs 认证与连通性(`npm whoami --registry=https://registry.npmjs.org/` + `npm ping --registry=https://registry.npmjs.org/`)
|
|
140
154
|
- `npm run release:check`
|
|
141
155
|
- `npm version <patch|minor|major>`(默认 `patch`)
|
|
142
156
|
- `npm publish`
|
|
143
157
|
- `git push`
|
|
144
158
|
- `git push --tags`
|
|
145
159
|
|
|
160
|
+
若 npmjs 认证缺失或过期,发布会在 preflight 阶段提前失败,并提示执行:
|
|
161
|
+
|
|
162
|
+
```bash
|
|
163
|
+
npm login --registry=https://registry.npmjs.org/
|
|
164
|
+
npm whoami --registry=https://registry.npmjs.org/
|
|
165
|
+
```
|
|
166
|
+
|
|
146
167
|
只有在 `npm publish` 成功后,才会自动推送提交和标签到 GitHub。
|
|
147
168
|
|
|
148
169
|
## 本地开发
|
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 stop`: stop all review runtimes created for the current workspace repository.
|
|
18
19
|
|
|
19
20
|
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:
|
|
20
21
|
|
package/dist/cli/start.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// src/cli/start.ts
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { existsSync as existsSync2 } from "node:fs";
|
|
6
|
-
import { dirname as
|
|
6
|
+
import { basename as basename3, dirname as dirname3, join as join5, resolve as resolve2 } from "node:path";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
|
|
9
9
|
// src/server/storage.ts
|
|
@@ -510,10 +510,96 @@ function binaryAddedDiff(path) {
|
|
|
510
510
|
return [`diff --git a/${path} b/${path}`, "new file mode 100644", "index 0000000..0000000", `Binary files /dev/null and b/${path} differ`].join("\n");
|
|
511
511
|
}
|
|
512
512
|
|
|
513
|
+
// src/cli/runtime-registry.ts
|
|
514
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
515
|
+
import { access, mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
|
|
516
|
+
import { homedir as homedir2, platform as platform2 } from "node:os";
|
|
517
|
+
import { basename as basename2, dirname as dirname2, join as join3 } from "node:path";
|
|
518
|
+
async function recordRuntime(entry) {
|
|
519
|
+
const path = runtimePath(entry.repoRoot);
|
|
520
|
+
const store = await readRuntime(path);
|
|
521
|
+
const aliveEntries = store.entries.filter((item) => isPidAlive(item.pid));
|
|
522
|
+
const deduped = aliveEntries.filter((item) => item.pid !== entry.pid);
|
|
523
|
+
deduped.push(entry);
|
|
524
|
+
await writeRuntime(path, { entries: deduped });
|
|
525
|
+
}
|
|
526
|
+
async function stopRecordedRuntimes(repoRoot) {
|
|
527
|
+
const path = runtimePath(repoRoot);
|
|
528
|
+
const store = await readRuntime(path);
|
|
529
|
+
const stopped = [];
|
|
530
|
+
const stale = [];
|
|
531
|
+
for (const entry of store.entries) {
|
|
532
|
+
const parentAlive = isPidAlive(entry.pid);
|
|
533
|
+
const viteAlive = typeof entry.vitePid === "number" && isPidAlive(entry.vitePid);
|
|
534
|
+
if (!parentAlive && !viteAlive) {
|
|
535
|
+
stale.push(entry);
|
|
536
|
+
continue;
|
|
537
|
+
}
|
|
538
|
+
try {
|
|
539
|
+
if (parentAlive) process.kill(entry.pid, "SIGTERM");
|
|
540
|
+
if (viteAlive) process.kill(entry.vitePid, "SIGTERM");
|
|
541
|
+
stopped.push(entry);
|
|
542
|
+
} catch {
|
|
543
|
+
stale.push(entry);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
await writeRuntime(path, { entries: [] });
|
|
547
|
+
return { stopped, stale };
|
|
548
|
+
}
|
|
549
|
+
function runtimePath(repoRoot) {
|
|
550
|
+
const repoName = basename2(repoRoot) || "repo";
|
|
551
|
+
const repoHash = createHash3("sha256").update(repoRoot).digest("hex").slice(0, 12);
|
|
552
|
+
return join3(runtimeDir(), `${repoName}-${repoHash}.runtime.json`);
|
|
553
|
+
}
|
|
554
|
+
function runtimeDir() {
|
|
555
|
+
if (platform2() === "win32") {
|
|
556
|
+
return join3(process.env.LOCALAPPDATA ?? join3(homedir2(), "AppData", "Local"), "diff-review", "runtime");
|
|
557
|
+
}
|
|
558
|
+
return join3(homedir2(), ".local", "diff-review", "runtime");
|
|
559
|
+
}
|
|
560
|
+
async function readRuntime(path) {
|
|
561
|
+
try {
|
|
562
|
+
const text = await readFile3(path, "utf8");
|
|
563
|
+
const parsed = JSON.parse(text);
|
|
564
|
+
if (!Array.isArray(parsed.entries)) return { entries: [] };
|
|
565
|
+
return { entries: parsed.entries.filter(isRuntimeEntry) };
|
|
566
|
+
} catch {
|
|
567
|
+
return { entries: [] };
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
async function writeRuntime(path, store) {
|
|
571
|
+
await mkdir2(dirname2(path), { recursive: true });
|
|
572
|
+
await writeFile2(path, `${JSON.stringify(store, null, 2)}
|
|
573
|
+
`, "utf8");
|
|
574
|
+
}
|
|
575
|
+
function isRuntimeEntry(value) {
|
|
576
|
+
if (!value || typeof value !== "object") return false;
|
|
577
|
+
const entry = value;
|
|
578
|
+
return typeof entry.pid === "number" && (typeof entry.vitePid === "undefined" || typeof entry.vitePid === "number") && typeof entry.repoRoot === "string" && typeof entry.repoName === "string" && typeof entry.startedAt === "string" && typeof entry.apiPort === "number" && typeof entry.usesVite === "boolean";
|
|
579
|
+
}
|
|
580
|
+
function isPidAlive(pid) {
|
|
581
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
582
|
+
try {
|
|
583
|
+
process.kill(pid, 0);
|
|
584
|
+
return true;
|
|
585
|
+
} catch {
|
|
586
|
+
return false;
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
async function hasRuntimeRecord(repoRoot) {
|
|
590
|
+
try {
|
|
591
|
+
await access(runtimePath(repoRoot));
|
|
592
|
+
return true;
|
|
593
|
+
} catch {
|
|
594
|
+
return false;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
513
598
|
// src/server/index.ts
|
|
514
599
|
import express from "express";
|
|
515
600
|
import { existsSync } from "node:fs";
|
|
516
|
-
import {
|
|
601
|
+
import { createServer } from "node:http";
|
|
602
|
+
import { join as join4, normalize as normalize2, resolve, sep } from "node:path";
|
|
517
603
|
|
|
518
604
|
// src/core/markdown-source-map.ts
|
|
519
605
|
import GithubSlugger from "github-slugger";
|
|
@@ -621,6 +707,7 @@ function getAnchorLine(thread) {
|
|
|
621
707
|
|
|
622
708
|
// src/server/index.ts
|
|
623
709
|
async function startServer(state, port = 4966) {
|
|
710
|
+
const markdownPreviews = await buildMarkdownPreviewCache(state);
|
|
624
711
|
const app = express();
|
|
625
712
|
app.use(express.json({ limit: "2mb" }));
|
|
626
713
|
app.get("/api/session", (_req, res) => {
|
|
@@ -629,21 +716,22 @@ async function startServer(state, port = 4966) {
|
|
|
629
716
|
app.get("/api/diff", (_req, res) => {
|
|
630
717
|
res.json({ files: state.diffFiles });
|
|
631
718
|
});
|
|
719
|
+
app.get("/api/review-state", async (_req, res, next) => {
|
|
720
|
+
try {
|
|
721
|
+
const comments = await readComments(state.session.repoRoot);
|
|
722
|
+
res.json({ session: state.session, files: state.diffFiles, threads: comments.threads });
|
|
723
|
+
} catch (error) {
|
|
724
|
+
next(error);
|
|
725
|
+
}
|
|
726
|
+
});
|
|
632
727
|
app.get("/api/markdown-preview", async (req, res, next) => {
|
|
633
728
|
try {
|
|
634
729
|
const filePath = String(req.query.path ?? "");
|
|
635
|
-
const
|
|
636
|
-
if (!
|
|
730
|
+
const preview = markdownPreviews.get(filePath);
|
|
731
|
+
if (!preview) {
|
|
637
732
|
res.status(404).json({ error: "Markdown file not found in diff" });
|
|
638
733
|
return;
|
|
639
734
|
}
|
|
640
|
-
const { content, deleted } = await readFileForPreview(file, state.session.mode, state.session.repoRoot);
|
|
641
|
-
const preview = {
|
|
642
|
-
filePath: file.path,
|
|
643
|
-
content,
|
|
644
|
-
deleted,
|
|
645
|
-
blocks: buildMarkdownBlocks(content)
|
|
646
|
-
};
|
|
647
735
|
res.json(preview);
|
|
648
736
|
} catch (error) {
|
|
649
737
|
next(error);
|
|
@@ -852,23 +940,36 @@ async function startServer(state, port = 4966) {
|
|
|
852
940
|
next(error);
|
|
853
941
|
}
|
|
854
942
|
});
|
|
855
|
-
const webDist = state.webDist ??
|
|
943
|
+
const webDist = state.webDist ?? join4(process.cwd(), "dist", "web");
|
|
856
944
|
if (existsSync(webDist)) {
|
|
857
945
|
app.use(express.static(webDist));
|
|
858
|
-
app.get(/.*/, (_req, res) => res.sendFile(
|
|
946
|
+
app.get(/.*/, (_req, res) => res.sendFile(join4(webDist, "index.html")));
|
|
859
947
|
}
|
|
860
948
|
app.use((error, _req, res, _next) => {
|
|
861
949
|
res.status(500).json({ error: error.message });
|
|
862
950
|
});
|
|
863
951
|
return listen(app, port);
|
|
864
952
|
}
|
|
953
|
+
async function buildMarkdownPreviewCache(state) {
|
|
954
|
+
const previews = /* @__PURE__ */ new Map();
|
|
955
|
+
await Promise.all(
|
|
956
|
+
state.diffFiles.filter((file) => file.isMarkdown).map(async (file) => {
|
|
957
|
+
const { content, deleted } = await readFileForPreview(file, state.session.mode, state.session.repoRoot);
|
|
958
|
+
const preview = {
|
|
959
|
+
filePath: file.path,
|
|
960
|
+
content,
|
|
961
|
+
deleted,
|
|
962
|
+
blocks: buildMarkdownBlocks(content)
|
|
963
|
+
};
|
|
964
|
+
previews.set(file.path, preview);
|
|
965
|
+
previews.set(file.oldPath, preview);
|
|
966
|
+
})
|
|
967
|
+
);
|
|
968
|
+
return previews;
|
|
969
|
+
}
|
|
865
970
|
function listen(app, port) {
|
|
866
971
|
return new Promise((resolve3, reject) => {
|
|
867
|
-
const server = app
|
|
868
|
-
const address = server.address();
|
|
869
|
-
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
870
|
-
resolve3(`http://127.0.0.1:${actualPort}`);
|
|
871
|
-
});
|
|
972
|
+
const server = createServer(app);
|
|
872
973
|
server.once("error", (error) => {
|
|
873
974
|
if (error.code === "EADDRINUSE" && port !== 0) {
|
|
874
975
|
listen(app, 0).then(resolve3, reject);
|
|
@@ -876,6 +977,11 @@ function listen(app, port) {
|
|
|
876
977
|
}
|
|
877
978
|
reject(error);
|
|
878
979
|
});
|
|
980
|
+
server.listen(port, "127.0.0.1", () => {
|
|
981
|
+
const address = server.address();
|
|
982
|
+
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
983
|
+
resolve3(`http://127.0.0.1:${actualPort}`);
|
|
984
|
+
});
|
|
879
985
|
});
|
|
880
986
|
}
|
|
881
987
|
function selectPromptThreads(threads, scope) {
|
|
@@ -887,31 +993,47 @@ function selectPromptThreads(threads, scope) {
|
|
|
887
993
|
}
|
|
888
994
|
|
|
889
995
|
// src/cli/start.ts
|
|
890
|
-
var packageRoot = resolve2(
|
|
891
|
-
var builtWebDist =
|
|
996
|
+
var packageRoot = resolve2(dirname3(fileURLToPath(import.meta.url)), "../..");
|
|
997
|
+
var builtWebDist = join5(packageRoot, "dist", "web");
|
|
892
998
|
async function main() {
|
|
893
|
-
const { dev, repo, reviewArgs, comments } = parseCliOptions(process.argv.slice(2));
|
|
999
|
+
const { command, dev, repo, reviewArgs, comments } = parseCliOptions(process.argv.slice(2));
|
|
1000
|
+
if (command === "stop") {
|
|
1001
|
+
await stopCommand(repo);
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
894
1004
|
const mode = parseReviewMode(reviewArgs);
|
|
895
1005
|
const repoRoot = await getRepoRoot(repo ?? process.cwd());
|
|
896
1006
|
const diff = await getDiff(mode, repoRoot);
|
|
897
1007
|
const diffFiles = parseUnifiedDiff(diff);
|
|
898
1008
|
const session = {
|
|
899
1009
|
id: crypto.randomUUID(),
|
|
1010
|
+
repoName: basename3(repoRoot),
|
|
900
1011
|
repoRoot,
|
|
901
1012
|
mode,
|
|
902
1013
|
diffHash: diffHash(diff),
|
|
903
1014
|
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
904
1015
|
};
|
|
905
1016
|
const importResult = await importAgentComments(repoRoot, diffFiles, comments);
|
|
906
|
-
const hasBuiltWeb = existsSync2(
|
|
1017
|
+
const hasBuiltWeb = existsSync2(join5(builtWebDist, "index.html"));
|
|
907
1018
|
const apiUrl = await startServer({ session, diffFiles, webDist: hasBuiltWeb ? builtWebDist : void 0 });
|
|
908
1019
|
const useVite = dev || !hasBuiltWeb;
|
|
909
1020
|
const uiUrl = useVite ? "http://127.0.0.1:5173" : apiUrl;
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1021
|
+
const vitePid = useVite ? startVite() : void 0;
|
|
1022
|
+
await recordRuntime({
|
|
1023
|
+
pid: process.pid,
|
|
1024
|
+
vitePid,
|
|
1025
|
+
repoRoot,
|
|
1026
|
+
repoName: session.repoName,
|
|
1027
|
+
startedAt: session.createdAt,
|
|
1028
|
+
apiPort: parsePort(apiUrl),
|
|
1029
|
+
usesVite: useVite
|
|
1030
|
+
});
|
|
913
1031
|
openBrowser(uiUrl);
|
|
914
1032
|
console.log(`Diff Review is running: ${uiUrl}`);
|
|
1033
|
+
console.log(`Repo: ${session.repoName} (${repoRoot})`);
|
|
1034
|
+
if (!useVite && uiUrl !== "http://127.0.0.1:4966") {
|
|
1035
|
+
console.log(`Default port 4966 is busy; using ${uiUrl}`);
|
|
1036
|
+
}
|
|
915
1037
|
console.log(`Mode: ${modeLabel(mode)}`);
|
|
916
1038
|
console.log(`Files: ${diffFiles.length}`);
|
|
917
1039
|
if (comments.length > 0) {
|
|
@@ -922,6 +1044,7 @@ async function main() {
|
|
|
922
1044
|
}
|
|
923
1045
|
}
|
|
924
1046
|
function parseCliOptions(args) {
|
|
1047
|
+
let command = "review";
|
|
925
1048
|
const reviewArgs = [];
|
|
926
1049
|
const comments = [];
|
|
927
1050
|
let repo;
|
|
@@ -932,6 +1055,10 @@ function parseCliOptions(args) {
|
|
|
932
1055
|
dev = true;
|
|
933
1056
|
continue;
|
|
934
1057
|
}
|
|
1058
|
+
if (arg === "stop") {
|
|
1059
|
+
command = "stop";
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
935
1062
|
if (arg === "--repo") {
|
|
936
1063
|
const value = args[index + 1];
|
|
937
1064
|
if (!value) throw new Error("--repo requires a path value");
|
|
@@ -960,7 +1087,35 @@ function parseCliOptions(args) {
|
|
|
960
1087
|
}
|
|
961
1088
|
reviewArgs.push(arg);
|
|
962
1089
|
}
|
|
963
|
-
return { dev, repo, reviewArgs, comments };
|
|
1090
|
+
return { command, dev, repo, reviewArgs, comments };
|
|
1091
|
+
}
|
|
1092
|
+
async function stopCommand(repo) {
|
|
1093
|
+
const repoRoot = await getRepoRoot(repo ?? process.cwd());
|
|
1094
|
+
const hasRecord = await hasRuntimeRecord(repoRoot);
|
|
1095
|
+
const { stopped, stale } = await stopRecordedRuntimes(repoRoot);
|
|
1096
|
+
const total = stopped.length + stale.length;
|
|
1097
|
+
if (total === 0) {
|
|
1098
|
+
if (hasRecord) {
|
|
1099
|
+
console.log("No running review process found for this repo.");
|
|
1100
|
+
return;
|
|
1101
|
+
}
|
|
1102
|
+
console.log("No review runtime record found for this repo.");
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
console.log(`Stopped review runtimes: ${stopped.length}`);
|
|
1106
|
+
for (const entry of stopped) {
|
|
1107
|
+
console.log(
|
|
1108
|
+
`- pid=${entry.pid} vitePid=${entry.vitePid ?? "-"} apiPort=${entry.apiPort} vite=${entry.usesVite ? "yes" : "no"} startedAt=${entry.startedAt}`
|
|
1109
|
+
);
|
|
1110
|
+
}
|
|
1111
|
+
if (stale.length > 0) {
|
|
1112
|
+
console.log(`Skipped stale records: ${stale.length}`);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
function parsePort(url) {
|
|
1116
|
+
const parsed = new URL(url);
|
|
1117
|
+
const port = parsed.port ? Number(parsed.port) : 80;
|
|
1118
|
+
return Number.isNaN(port) ? 0 : port;
|
|
964
1119
|
}
|
|
965
1120
|
function modeLabel(mode) {
|
|
966
1121
|
if (mode.kind === "revision") return `${mode.base}..${mode.target}`;
|
|
@@ -975,6 +1130,7 @@ function startVite() {
|
|
|
975
1130
|
});
|
|
976
1131
|
process.on("SIGINT", () => child.kill("SIGINT"));
|
|
977
1132
|
process.on("SIGTERM", () => child.kill("SIGTERM"));
|
|
1133
|
+
return child.pid;
|
|
978
1134
|
}
|
|
979
1135
|
function openBrowser(url) {
|
|
980
1136
|
const child = process.platform === "darwin" ? spawn("open", [url], { stdio: "ignore", detached: true }) : process.platform === "win32" ? spawn("cmd", ["/c", "start", "", url], {
|
|
@@ -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-D2UD_g6s.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};
|