diffprism 0.34.0 → 0.35.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/dist/bin.js +126 -224
- package/dist/chunk-DHCVZGHE.js +501 -0
- package/dist/chunk-JSBRDJBE.js +30 -0
- package/dist/{chunk-NJKYNMAQ.js → chunk-LUUR6LNP.js} +317 -1802
- package/dist/chunk-QGWYCEJN.js +448 -0
- package/dist/mcp-server.js +75 -280
- package/dist/src-AMCPIYDZ.js +19 -0
- package/dist/src-JMPTSU3P.js +27 -0
- package/package.json +1 -1
- package/ui-dist/assets/index-BfqEajZq.js +325 -0
- package/ui-dist/assets/{index-RCLz30rX.css → index-vG-fI3wH.css} +1 -1
- package/ui-dist/index.html +2 -2
- package/ui-dist/assets/index-1PUjTjRT.js +0 -324
package/dist/bin.js
CHANGED
|
@@ -1,19 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
createGitHubClient,
|
|
4
|
+
ensureServer,
|
|
4
5
|
fetchPullRequest,
|
|
5
6
|
fetchPullRequestDiff,
|
|
6
7
|
isServerAlive,
|
|
7
8
|
normalizePr,
|
|
8
9
|
parsePrRef,
|
|
9
10
|
readServerFile,
|
|
10
|
-
readWatchFile,
|
|
11
11
|
resolveGitHubToken,
|
|
12
12
|
startGlobalServer,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
submitGitHubReview,
|
|
14
|
+
submitReviewToServer
|
|
15
|
+
} from "./chunk-LUUR6LNP.js";
|
|
16
|
+
import "./chunk-QGWYCEJN.js";
|
|
17
|
+
import "./chunk-DHCVZGHE.js";
|
|
18
|
+
import "./chunk-JSBRDJBE.js";
|
|
17
19
|
|
|
18
20
|
// cli/src/index.ts
|
|
19
21
|
import { Command } from "commander";
|
|
@@ -31,11 +33,11 @@ async function review(ref, flags) {
|
|
|
31
33
|
diffRef = "working-copy";
|
|
32
34
|
}
|
|
33
35
|
try {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
+
const serverInfo = await ensureServer({ dev: flags.dev });
|
|
37
|
+
const { result } = await submitReviewToServer(serverInfo, diffRef, {
|
|
36
38
|
title: flags.title,
|
|
37
39
|
cwd: process.cwd(),
|
|
38
|
-
|
|
40
|
+
diffRef
|
|
39
41
|
});
|
|
40
42
|
console.log(JSON.stringify(result, null, 2));
|
|
41
43
|
process.exit(0);
|
|
@@ -62,46 +64,19 @@ async function reviewPr(pr, flags) {
|
|
|
62
64
|
console.log("PR has no changes to review.");
|
|
63
65
|
return;
|
|
64
66
|
}
|
|
65
|
-
const { payload, diffSet
|
|
67
|
+
const { payload, diffSet } = normalizePr(rawDiff, prMetadata, {
|
|
66
68
|
title: flags.title,
|
|
67
69
|
reasoning: flags.reasoning
|
|
68
70
|
});
|
|
69
71
|
console.log(
|
|
70
72
|
`${diffSet.files.length} files, +${diffSet.files.reduce((s, f) => s + f.additions, 0)} -${diffSet.files.reduce((s, f) => s + f.deletions, 0)}`
|
|
71
73
|
);
|
|
72
|
-
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
method: "POST",
|
|
79
|
-
headers: { "Content-Type": "application/json" },
|
|
80
|
-
body: JSON.stringify({
|
|
81
|
-
payload,
|
|
82
|
-
projectPath: `github:${owner}/${repo}`,
|
|
83
|
-
diffRef: `PR #${number}`
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
);
|
|
87
|
-
if (!createResponse.ok) {
|
|
88
|
-
throw new Error(`Global server returned ${createResponse.status}`);
|
|
89
|
-
}
|
|
90
|
-
const { sessionId } = await createResponse.json();
|
|
91
|
-
console.log(`Review session created: ${sessionId}`);
|
|
92
|
-
console.log("Waiting for review submission...");
|
|
93
|
-
result = await pollForResult(serverInfo.httpPort, sessionId);
|
|
94
|
-
} else {
|
|
95
|
-
result = await startReview({
|
|
96
|
-
diffRef: `PR #${number}`,
|
|
97
|
-
title: metadata.title,
|
|
98
|
-
description: metadata.description,
|
|
99
|
-
reasoning: metadata.reasoning,
|
|
100
|
-
cwd: process.cwd(),
|
|
101
|
-
dev: flags.dev,
|
|
102
|
-
injectedPayload: payload
|
|
103
|
-
});
|
|
104
|
-
}
|
|
74
|
+
const serverInfo = await ensureServer({ dev: flags.dev });
|
|
75
|
+
const { result } = await submitReviewToServer(serverInfo, `PR #${number}`, {
|
|
76
|
+
injectedPayload: payload,
|
|
77
|
+
projectPath: `github:${owner}/${repo}`,
|
|
78
|
+
diffRef: `PR #${number}`
|
|
79
|
+
});
|
|
105
80
|
console.log(JSON.stringify(result, null, 2));
|
|
106
81
|
if (flags.postToGithub || result.decision !== "dismissed" && await promptPostToGithub()) {
|
|
107
82
|
console.log("Posting review to GitHub...");
|
|
@@ -117,24 +92,6 @@ async function reviewPr(pr, flags) {
|
|
|
117
92
|
process.exit(1);
|
|
118
93
|
}
|
|
119
94
|
}
|
|
120
|
-
async function pollForResult(httpPort, sessionId) {
|
|
121
|
-
const pollIntervalMs = 2e3;
|
|
122
|
-
const maxWaitMs = 600 * 1e3;
|
|
123
|
-
const start2 = Date.now();
|
|
124
|
-
while (Date.now() - start2 < maxWaitMs) {
|
|
125
|
-
const response = await fetch(
|
|
126
|
-
`http://localhost:${httpPort}/api/reviews/${sessionId}/result`
|
|
127
|
-
);
|
|
128
|
-
if (response.ok) {
|
|
129
|
-
const data = await response.json();
|
|
130
|
-
if (data.result) {
|
|
131
|
-
return data.result;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
135
|
-
}
|
|
136
|
-
throw new Error("Review timed out waiting for submission.");
|
|
137
|
-
}
|
|
138
95
|
async function promptPostToGithub() {
|
|
139
96
|
if (!process.stdin.isTTY) {
|
|
140
97
|
return false;
|
|
@@ -169,130 +126,30 @@ name: review
|
|
|
169
126
|
description: Open current code changes in DiffPrism's browser-based review UI for human review.
|
|
170
127
|
---
|
|
171
128
|
|
|
172
|
-
# DiffPrism Review
|
|
173
|
-
|
|
174
|
-
When the user invokes \`/review\`, open the current code changes in DiffPrism for browser-based human review.
|
|
175
|
-
|
|
176
|
-
## Steps
|
|
177
|
-
|
|
178
|
-
### 1. Check for Watch Mode
|
|
179
|
-
|
|
180
|
-
Before opening a new review, check if \`diffprism watch\` is already running. Look for \`.diffprism/watch.json\` at the git root. If it exists and the process is alive:
|
|
181
|
-
|
|
182
|
-
- **Do NOT call \`open_review\`** (the browser is already open with live-updating diffs)
|
|
183
|
-
- Instead, call \`mcp__diffprism__update_review_context\` to push your reasoning to the existing watch session
|
|
184
|
-
- Then **immediately** call \`mcp__diffprism__get_review_result\` with \`wait: true\` to block until the developer submits their review
|
|
185
|
-
- Tell the user: "DiffPrism watch is running \u2014 pushed reasoning to the live review. Waiting for your feedback..."
|
|
186
|
-
- When the result comes back, handle it per step 5 below
|
|
187
|
-
- Skip steps 2-4
|
|
188
|
-
|
|
189
|
-
### 2. Load Configuration
|
|
190
|
-
|
|
191
|
-
Look for \`diffprism.config.json\` at the project root. If it exists, read it for preferences. If it doesn't exist, use defaults silently \u2014 do not prompt or create the file.
|
|
192
|
-
|
|
193
|
-
\`\`\`json
|
|
194
|
-
{
|
|
195
|
-
"defaultDiffScope": "staged | unstaged | working-copy",
|
|
196
|
-
"includeReasoning": true | false
|
|
197
|
-
}
|
|
198
|
-
\`\`\`
|
|
199
|
-
|
|
200
|
-
**Defaults** (when fields are missing or file doesn't exist):
|
|
201
|
-
- \`defaultDiffScope\`: \`"working-copy"\`
|
|
202
|
-
- \`includeReasoning\`: \`true\`
|
|
203
|
-
|
|
204
|
-
### 3. Open the Review
|
|
205
|
-
|
|
206
|
-
Call \`mcp__diffprism__open_review\` with:
|
|
207
|
-
|
|
208
|
-
- \`diff_ref\`: Use the \`defaultDiffScope\` from config. If the user specified a scope in their message (e.g., "/review staged"), use that instead.
|
|
209
|
-
- \`title\`: A short summary of the changes (generate from git status or the user's message).
|
|
210
|
-
- \`description\`: A brief description of what changed and why.
|
|
211
|
-
- \`reasoning\`: If \`includeReasoning\` is \`true\`, include your reasoning about the implementation decisions.
|
|
129
|
+
# DiffPrism Review
|
|
212
130
|
|
|
213
|
-
|
|
131
|
+
When the user invokes \`/review\`, call \`mcp__diffprism__open_review\` with:
|
|
214
132
|
|
|
215
|
-
|
|
133
|
+
- \`diff_ref\`: \`"working-copy"\` (or what the user specified, e.g. \`"staged"\`)
|
|
134
|
+
- \`title\`: Brief summary of the changes
|
|
135
|
+
- \`reasoning\`: Your reasoning about the implementation decisions
|
|
216
136
|
|
|
217
|
-
|
|
218
|
-
- **\`approved_with_comments\`** \u2014 Note the comments, address any actionable feedback.
|
|
219
|
-
- **\`changes_requested\`** \u2014 Read the comments carefully, make the requested changes, and offer to open another review.
|
|
137
|
+
The tool blocks until the human submits their review. Handle the result:
|
|
220
138
|
|
|
221
|
-
|
|
139
|
+
- **\`approved\`** \u2014 Proceed with the task.
|
|
140
|
+
- **\`changes_requested\`** \u2014 Read comments, make fixes, offer to re-review.
|
|
141
|
+
- If \`postReviewAction\` is \`"commit"\` \u2014 commit the changes.
|
|
142
|
+
- If \`postReviewAction\` is \`"commit_and_pr"\` \u2014 commit and open a PR.
|
|
222
143
|
|
|
223
|
-
|
|
144
|
+
## Headless Tools
|
|
224
145
|
|
|
225
|
-
-
|
|
226
|
-
-
|
|
146
|
+
- \`mcp__diffprism__analyze_diff\` \u2014 Returns analysis JSON (patterns, complexity, test gaps) without opening a browser. Use proactively to self-check before requesting review.
|
|
147
|
+
- \`mcp__diffprism__get_diff\` \u2014 Returns structured diff JSON.
|
|
227
148
|
|
|
228
|
-
|
|
149
|
+
## Rules
|
|
229
150
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
If the \`mcp__diffprism__open_review\` tool is not available:
|
|
233
|
-
- Tell the user: "The DiffPrism MCP server isn't configured. Run \`npx diffprism setup\` to set it up, then restart Claude Code."
|
|
234
|
-
|
|
235
|
-
## Global Server Mode
|
|
236
|
-
|
|
237
|
-
When a global DiffPrism server is running (\`diffprism server\`), the MCP tools automatically detect it and route reviews there instead of opening a new browser tab each time. The review appears in the server's multi-session UI at the existing browser tab.
|
|
238
|
-
|
|
239
|
-
This is transparent \u2014 the same \`open_review\`, \`update_review_context\`, and \`get_review_result\` tools work the same way. No changes to the workflow are needed.
|
|
240
|
-
|
|
241
|
-
## Watch Mode: Waiting for Review Feedback
|
|
242
|
-
|
|
243
|
-
When \`diffprism watch\` is active (detected via \`.diffprism/watch.json\`), the developer can submit reviews at any time in the browser.
|
|
244
|
-
|
|
245
|
-
**After pushing context to a watch session**, call \`mcp__diffprism__get_review_result\` with \`wait: true\` to block until the developer submits their review. This polls the result file every 2 seconds and returns as soon as feedback is available (up to 5 minutes by default).
|
|
246
|
-
|
|
247
|
-
Use this pattern:
|
|
248
|
-
1. Push context via \`update_review_context\`
|
|
249
|
-
2. Call \`get_review_result\` with \`wait: true\` \u2014 this blocks until the developer submits
|
|
250
|
-
3. Handle the result (approved, changes_requested, etc.)
|
|
251
|
-
4. If changes were requested, make fixes, push updated context, and call \`get_review_result\` with \`wait: true\` again
|
|
252
|
-
|
|
253
|
-
You can also check for feedback without blocking by calling \`get_review_result\` without \`wait\` at natural breakpoints (between tasks, before committing, etc.).
|
|
254
|
-
|
|
255
|
-
## Self-Review: Headless Analysis Tools
|
|
256
|
-
|
|
257
|
-
DiffPrism provides two headless tools that return analysis data as JSON without opening a browser. Use these to check your own work before requesting human review.
|
|
258
|
-
|
|
259
|
-
### Available Headless Tools
|
|
260
|
-
|
|
261
|
-
- **\`mcp__diffprism__get_diff\`** \u2014 Returns a structured \`DiffSet\` (files, hunks, additions/deletions) for a given diff ref. Use this to inspect exactly what changed.
|
|
262
|
-
- **\`mcp__diffprism__analyze_diff\`** \u2014 Returns a \`ReviewBriefing\` with summary, file triage, impact detection, complexity scores, test coverage gaps, and pattern flags (security issues, TODOs, console.logs left in, etc.).
|
|
263
|
-
|
|
264
|
-
Both accept a \`diff_ref\` parameter: \`"staged"\`, \`"unstaged"\`, \`"working-copy"\`, or a git range like \`"HEAD~3..HEAD"\`.
|
|
265
|
-
|
|
266
|
-
### Self-Review Loop
|
|
267
|
-
|
|
268
|
-
When you've finished writing code and before requesting human review, use this pattern:
|
|
269
|
-
|
|
270
|
-
1. **Analyze your changes:** Call \`mcp__diffprism__analyze_diff\` with \`diff_ref: "working-copy"\`
|
|
271
|
-
2. **Check the briefing for issues:**
|
|
272
|
-
- \`patterns\` \u2014 Look for console.logs, TODOs, security flags, disabled tests
|
|
273
|
-
- \`testCoverage\` \u2014 Check if changed source files have corresponding test changes
|
|
274
|
-
- \`complexity\` \u2014 Review high-complexity scores
|
|
275
|
-
- \`impact.newDependencies\` \u2014 Verify any new deps are intentional
|
|
276
|
-
- \`impact.breakingChanges\` \u2014 Confirm breaking changes are expected
|
|
277
|
-
3. **Fix any issues found** \u2014 Remove debug statements, add missing tests, address security flags
|
|
278
|
-
4. **Re-analyze** \u2014 Run \`analyze_diff\` again to confirm the issues are resolved
|
|
279
|
-
5. **Open for human review** \u2014 Once clean, use \`/review\` or \`open_review\` for final human sign-off
|
|
280
|
-
|
|
281
|
-
This loop catches common issues (leftover console.logs, missing tests, security anti-patterns) before the human reviewer sees them, making reviews faster and more focused.
|
|
282
|
-
|
|
283
|
-
### When to Use Headless Tools
|
|
284
|
-
|
|
285
|
-
- **After completing a coding task** \u2014 Self-check before requesting review
|
|
286
|
-
- **During implementation** \u2014 Periodically check for patterns and issues as you work
|
|
287
|
-
- **Before committing** \u2014 Quick sanity check on what's about to be committed
|
|
288
|
-
- **Do NOT use these as a replacement for human review** \u2014 They complement, not replace, \`/review\`
|
|
289
|
-
|
|
290
|
-
## Behavior Rules
|
|
291
|
-
|
|
292
|
-
- **IMPORTANT: Do NOT open reviews automatically.** Only open a review when the user explicitly invokes \`/review\` or directly asks for a review.
|
|
293
|
-
- Do NOT open reviews before commits, after code changes, or at any other time unless the user requests it.
|
|
294
|
-
- Headless tools (\`get_diff\`, \`analyze_diff\`) can be used proactively during development without explicit user request \u2014 they don't open a browser or interrupt the user.
|
|
295
|
-
- Power users can create \`diffprism.config.json\` manually to customize defaults (diff scope, reasoning).
|
|
151
|
+
- Only open a review when the user explicitly asks (\`/review\` or "review my changes").
|
|
152
|
+
- Headless tools can be used proactively without user request.
|
|
296
153
|
`;
|
|
297
154
|
|
|
298
155
|
// cli/src/commands/setup.ts
|
|
@@ -808,7 +665,7 @@ async function start(ref, flags) {
|
|
|
808
665
|
});
|
|
809
666
|
const hasChanges = outcome.created.length > 0 || outcome.updated.length > 0;
|
|
810
667
|
if (hasChanges) {
|
|
811
|
-
console.log("
|
|
668
|
+
console.log("DiffPrism configured for Claude Code.");
|
|
812
669
|
}
|
|
813
670
|
let diffRef;
|
|
814
671
|
if (flags.staged) {
|
|
@@ -820,30 +677,23 @@ async function start(ref, flags) {
|
|
|
820
677
|
} else {
|
|
821
678
|
diffRef = "working-copy";
|
|
822
679
|
}
|
|
823
|
-
const pollInterval = flags.interval ? parseInt(flags.interval, 10) : 1e3;
|
|
824
680
|
try {
|
|
825
|
-
const
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
dev: flags.dev,
|
|
830
|
-
pollInterval
|
|
831
|
-
});
|
|
832
|
-
console.log("Use /review in Claude Code to send changes for review.");
|
|
681
|
+
const serverInfo = await ensureServer({ dev: flags.dev });
|
|
682
|
+
console.log(
|
|
683
|
+
`DiffPrism server at http://localhost:${serverInfo.httpPort}`
|
|
684
|
+
);
|
|
833
685
|
if (hasChanges) {
|
|
834
686
|
console.log(
|
|
835
687
|
"If this is your first time, restart Claude Code first to load the MCP server."
|
|
836
688
|
);
|
|
837
689
|
}
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
};
|
|
843
|
-
process.on("SIGINT", shutdown);
|
|
844
|
-
process.on("SIGTERM", shutdown);
|
|
845
|
-
await new Promise(() => {
|
|
690
|
+
const { result } = await submitReviewToServer(serverInfo, diffRef, {
|
|
691
|
+
title: flags.title,
|
|
692
|
+
cwd: process.cwd(),
|
|
693
|
+
diffRef
|
|
846
694
|
});
|
|
695
|
+
console.log(JSON.stringify(result, null, 2));
|
|
696
|
+
process.exit(0);
|
|
847
697
|
} catch (err) {
|
|
848
698
|
const message = err instanceof Error ? err.message : String(err);
|
|
849
699
|
console.error(`Error: ${message}`);
|
|
@@ -863,24 +713,19 @@ async function watch(ref, flags) {
|
|
|
863
713
|
} else {
|
|
864
714
|
diffRef = "working-copy";
|
|
865
715
|
}
|
|
866
|
-
const pollInterval = flags.interval ? parseInt(flags.interval, 10) : 1e3;
|
|
867
716
|
try {
|
|
868
|
-
const
|
|
869
|
-
|
|
717
|
+
const serverInfo = await ensureServer({ dev: flags.dev });
|
|
718
|
+
console.log(
|
|
719
|
+
`DiffPrism server at http://localhost:${serverInfo.httpPort}`
|
|
720
|
+
);
|
|
721
|
+
console.log("Submitting review session...");
|
|
722
|
+
const { result } = await submitReviewToServer(serverInfo, diffRef, {
|
|
870
723
|
title: flags.title,
|
|
871
724
|
cwd: process.cwd(),
|
|
872
|
-
|
|
873
|
-
pollInterval
|
|
874
|
-
});
|
|
875
|
-
const shutdown = async () => {
|
|
876
|
-
console.log("\nStopping watch...");
|
|
877
|
-
await handle.stop();
|
|
878
|
-
process.exit(0);
|
|
879
|
-
};
|
|
880
|
-
process.on("SIGINT", shutdown);
|
|
881
|
-
process.on("SIGTERM", shutdown);
|
|
882
|
-
await new Promise(() => {
|
|
725
|
+
diffRef
|
|
883
726
|
});
|
|
727
|
+
console.log(JSON.stringify(result, null, 2));
|
|
728
|
+
process.exit(0);
|
|
884
729
|
} catch (err) {
|
|
885
730
|
const message = err instanceof Error ? err.message : String(err);
|
|
886
731
|
console.error(`Error: ${message}`);
|
|
@@ -891,15 +736,15 @@ async function watch(ref, flags) {
|
|
|
891
736
|
// cli/src/commands/notify-stop.ts
|
|
892
737
|
async function notifyStop() {
|
|
893
738
|
try {
|
|
894
|
-
const
|
|
895
|
-
if (!
|
|
739
|
+
const serverInfo = await isServerAlive();
|
|
740
|
+
if (!serverInfo) {
|
|
896
741
|
process.exit(0);
|
|
897
742
|
return;
|
|
898
743
|
}
|
|
899
744
|
const controller = new AbortController();
|
|
900
745
|
const timeout = setTimeout(() => controller.abort(), 2e3);
|
|
901
746
|
try {
|
|
902
|
-
await fetch(`http://localhost:${
|
|
747
|
+
await fetch(`http://localhost:${serverInfo.httpPort}/api/refresh`, {
|
|
903
748
|
method: "POST",
|
|
904
749
|
signal: controller.signal
|
|
905
750
|
});
|
|
@@ -912,18 +757,33 @@ async function notifyStop() {
|
|
|
912
757
|
}
|
|
913
758
|
|
|
914
759
|
// cli/src/commands/server.ts
|
|
760
|
+
import { spawn } from "child_process";
|
|
761
|
+
import fs3 from "fs";
|
|
762
|
+
import path3 from "path";
|
|
763
|
+
import os3 from "os";
|
|
915
764
|
async function server(flags) {
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
console.log(`DiffPrism server is already running on port ${existing.httpPort} (PID ${existing.pid})`);
|
|
919
|
-
console.log(`Use 'diffprism server stop' to stop it first.`);
|
|
920
|
-
process.exit(1);
|
|
765
|
+
if (flags.background) {
|
|
766
|
+
await spawnDaemon(flags);
|
|
921
767
|
return;
|
|
922
768
|
}
|
|
769
|
+
const isDaemon = !!flags._daemon;
|
|
770
|
+
if (!isDaemon) {
|
|
771
|
+
const existing = await isServerAlive();
|
|
772
|
+
if (existing) {
|
|
773
|
+
console.log(`DiffPrism server is already running on port ${existing.httpPort} (PID ${existing.pid})`);
|
|
774
|
+
console.log(`Use 'diffprism server stop' to stop it first.`);
|
|
775
|
+
process.exit(1);
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
923
779
|
if (!isGlobalSetupDone()) {
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
780
|
+
if (!isDaemon) {
|
|
781
|
+
console.log("Running global setup...\n");
|
|
782
|
+
}
|
|
783
|
+
await setup({ global: true, quiet: isDaemon });
|
|
784
|
+
if (!isDaemon) {
|
|
785
|
+
console.log("");
|
|
786
|
+
}
|
|
927
787
|
}
|
|
928
788
|
const httpPort = flags.port ? parseInt(flags.port, 10) : void 0;
|
|
929
789
|
const wsPort = flags.wsPort ? parseInt(flags.wsPort, 10) : void 0;
|
|
@@ -931,10 +791,14 @@ async function server(flags) {
|
|
|
931
791
|
const handle = await startGlobalServer({
|
|
932
792
|
httpPort,
|
|
933
793
|
wsPort,
|
|
934
|
-
dev: flags.dev
|
|
794
|
+
dev: flags.dev,
|
|
795
|
+
silent: isDaemon,
|
|
796
|
+
openBrowser: !isDaemon
|
|
935
797
|
});
|
|
936
798
|
const shutdown = async () => {
|
|
937
|
-
|
|
799
|
+
if (!isDaemon) {
|
|
800
|
+
console.log("\nStopping server...");
|
|
801
|
+
}
|
|
938
802
|
await handle.stop();
|
|
939
803
|
process.exit(0);
|
|
940
804
|
};
|
|
@@ -944,10 +808,48 @@ async function server(flags) {
|
|
|
944
808
|
});
|
|
945
809
|
} catch (err) {
|
|
946
810
|
const message = err instanceof Error ? err.message : String(err);
|
|
947
|
-
|
|
811
|
+
if (!isDaemon) {
|
|
812
|
+
console.error(`Error starting server: ${message}`);
|
|
813
|
+
}
|
|
948
814
|
process.exit(1);
|
|
949
815
|
}
|
|
950
816
|
}
|
|
817
|
+
async function spawnDaemon(flags) {
|
|
818
|
+
const existing = await isServerAlive();
|
|
819
|
+
if (existing) {
|
|
820
|
+
console.log(`DiffPrism server is already running on port ${existing.httpPort} (PID ${existing.pid})`);
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
const args = process.argv.slice(1).filter((a) => a !== "--background");
|
|
824
|
+
args.push("--_daemon");
|
|
825
|
+
const logDir = path3.join(os3.homedir(), ".diffprism");
|
|
826
|
+
if (!fs3.existsSync(logDir)) {
|
|
827
|
+
fs3.mkdirSync(logDir, { recursive: true });
|
|
828
|
+
}
|
|
829
|
+
const logPath = path3.join(logDir, "server.log");
|
|
830
|
+
const logFd = fs3.openSync(logPath, "a");
|
|
831
|
+
const child = spawn(process.execPath, args, {
|
|
832
|
+
detached: true,
|
|
833
|
+
stdio: ["ignore", logFd, logFd],
|
|
834
|
+
env: { ...process.env }
|
|
835
|
+
});
|
|
836
|
+
child.unref();
|
|
837
|
+
fs3.closeSync(logFd);
|
|
838
|
+
console.log("Starting DiffPrism server in background...");
|
|
839
|
+
const startTime = Date.now();
|
|
840
|
+
const timeoutMs = 15e3;
|
|
841
|
+
while (Date.now() - startTime < timeoutMs) {
|
|
842
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
843
|
+
const info = await isServerAlive();
|
|
844
|
+
if (info) {
|
|
845
|
+
console.log(`DiffPrism server started (PID ${info.pid}, port ${info.httpPort})`);
|
|
846
|
+
console.log(`Logs: ${logPath}`);
|
|
847
|
+
return;
|
|
848
|
+
}
|
|
849
|
+
}
|
|
850
|
+
console.error("Timed out waiting for server to start. Check logs at:", logPath);
|
|
851
|
+
process.exit(1);
|
|
852
|
+
}
|
|
951
853
|
async function serverStatus() {
|
|
952
854
|
const info = await isServerAlive();
|
|
953
855
|
if (!info) {
|
|
@@ -1001,7 +903,7 @@ async function serverStop() {
|
|
|
1001
903
|
|
|
1002
904
|
// cli/src/index.ts
|
|
1003
905
|
var program = new Command();
|
|
1004
|
-
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.
|
|
906
|
+
program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.35.0" : "0.0.0-dev");
|
|
1005
907
|
program.command("review [ref]").description("Open a browser-based diff review").option("--staged", "Review staged changes").option("--unstaged", "Review unstaged changes").option("-t, --title <title>", "Review title").option("--dev", "Use Vite dev server with HMR instead of static files").action(review);
|
|
1006
908
|
program.command("review-pr <pr>").description("Review a GitHub pull request in DiffPrism").option("-t, --title <title>", "Override review title").option("--reasoning <text>", "Agent reasoning about the PR").option("--dev", "Use Vite dev server with HMR instead of static files").option("--post-to-github", "Automatically post review back to GitHub without prompting").action(reviewPr);
|
|
1007
909
|
program.command("start [ref]").description("Set up DiffPrism and start watching for changes").option("--staged", "Watch staged changes").option("--unstaged", "Watch unstaged changes").option("-t, --title <title>", "Review title").option("--interval <ms>", "Poll interval in milliseconds (default: 1000)").option("--dev", "Use Vite dev server with HMR instead of static files").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action(start);
|
|
@@ -1014,7 +916,7 @@ program.command("setup").description("Configure DiffPrism for Claude Code integr
|
|
|
1014
916
|
program.command("teardown").description("Remove DiffPrism configuration from the current project").option("--global", "Remove global configuration (skill + permissions at ~/.claude/)").option("-q, --quiet", "Suppress output").action((flags) => {
|
|
1015
917
|
teardown(flags);
|
|
1016
918
|
});
|
|
1017
|
-
var serverCmd = program.command("server").description("Start the global DiffPrism server for multi-session reviews").option("-p, --port <port>", "HTTP API port (default: 24680)").option("--ws-port <port>", "WebSocket port (default: 24681)").option("--dev", "Use Vite dev server with HMR instead of static files").action(server);
|
|
919
|
+
var serverCmd = program.command("server").description("Start the global DiffPrism server for multi-session reviews").option("-p, --port <port>", "HTTP API port (default: 24680)").option("--ws-port <port>", "WebSocket port (default: 24681)").option("--dev", "Use Vite dev server with HMR instead of static files").option("--background", "Start server as a background daemon").option("--_daemon", "Internal: run as spawned daemon (do not use directly)").action(server);
|
|
1018
920
|
serverCmd.command("status").description("Check if the global server is running and list active sessions").action(serverStatus);
|
|
1019
921
|
serverCmd.command("stop").description("Stop the running global server").action(serverStop);
|
|
1020
922
|
program.parse();
|