diffprism 0.10.2 → 0.11.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 CHANGED
@@ -20,45 +20,20 @@ DiffPrism gives you a visual review step for AI-written code — stage your chan
20
20
 
21
21
  ### Use with Claude Code (recommended)
22
22
 
23
- **1. Add the MCP server** — create `.mcp.json` in your project root:
23
+ Run this from your project root:
24
24
 
25
- ```json
26
- {
27
- "mcpServers": {
28
- "diffprism": {
29
- "command": "npx",
30
- "args": ["diffprism", "serve"]
31
- }
32
- }
33
- }
34
- ```
35
-
36
- **2. Auto-approve the tool** (optional) — add to `.claude/settings.json` so Claude can open reviews without prompting:
37
-
38
- ```json
39
- {
40
- "permissions": {
41
- "allow": [
42
- "mcp__diffprism__open_review"
43
- ]
44
- }
45
- }
25
+ ```bash
26
+ npx diffprism setup
46
27
  ```
47
28
 
48
- **3. Tell Claude to use it** — add to your project's `CLAUDE.md`:
49
-
50
- ```markdown
51
- ## Code Review
52
-
53
- Before committing changes, use the diffprism MCP tool to open a review:
54
- - Call `open_review` with the appropriate `diff_ref` (e.g. `"staged"`, `"HEAD~1..HEAD"`)
55
- - Include a `title` and `description` summarizing the changes
56
- - Wait for the user's review decision before proceeding
57
- ```
29
+ This single command configures everything:
30
+ - Creates `.mcp.json` with the DiffPrism MCP server
31
+ - Creates `.claude/settings.json` with auto-approve permissions
32
+ - Installs a `/review` skill so you can type `/review` in Claude Code at any time
58
33
 
59
- That's it. Claude will now open a browser-based review before committing. You review the diff, leave comments, and the result goes back to Claude as structured JSON.
34
+ After running, restart Claude Code. The first time you use `/review`, Claude will ask your preferences and save them to `diffprism.config.json`.
60
35
 
61
- See the [full setup guide](docs/claude-setup.md) for Claude Desktop config, troubleshooting, and advanced options.
36
+ See the [full setup guide](docs/claude-setup.md) for manual configuration, Claude Desktop config, troubleshooting, and advanced options.
62
37
 
63
38
  ### Use from the CLI
64
39
 
package/dist/bin.js CHANGED
@@ -40,9 +40,222 @@ async function serve() {
40
40
  await startMcpServer();
41
41
  }
42
42
 
43
+ // cli/src/commands/setup.ts
44
+ import fs from "fs";
45
+ import path from "path";
46
+ import os from "os";
47
+
48
+ // cli/src/templates/skill.ts
49
+ var skillContent = `---
50
+ name: review
51
+ description: Open current code changes in DiffPrism's browser-based review UI for human review.
52
+ ---
53
+
54
+ # DiffPrism Review Skill
55
+
56
+ When the user invokes \`/review\`, open the current code changes in DiffPrism for browser-based human review.
57
+
58
+ ## Steps
59
+
60
+ ### 1. Check for Configuration
61
+
62
+ Look for \`diffprism.config.json\` at the project root. If it exists, read it for preferences:
63
+
64
+ \`\`\`json
65
+ {
66
+ "reviewTrigger": "ask | before_commit | always",
67
+ "defaultDiffScope": "staged | unstaged | all",
68
+ "includeReasoning": true | false
69
+ }
70
+ \`\`\`
71
+
72
+ **Defaults** (when fields are missing or file doesn't exist):
73
+ - \`reviewTrigger\`: \`"ask"\`
74
+ - \`defaultDiffScope\`: \`"all"\`
75
+ - \`includeReasoning\`: \`true\`
76
+
77
+ ### 2. First-Run Onboarding
78
+
79
+ If \`diffprism.config.json\` does **not** exist, ask the user these questions before proceeding:
80
+
81
+ 1. **"When should I open DiffPrism reviews?"**
82
+ - \`"ask"\` \u2014 Only when you explicitly ask (default)
83
+ - \`"before_commit"\` \u2014 Automatically before every commit
84
+ - \`"always"\` \u2014 After every code change
85
+
86
+ 2. **"What should the default diff scope be?"**
87
+ - \`"all"\` \u2014 All changes, staged and unstaged (default)
88
+ - \`"staged"\` \u2014 Only staged changes
89
+ - \`"unstaged"\` \u2014 Only unstaged changes
90
+
91
+ 3. **"Should I include my reasoning about the changes in reviews?"**
92
+ - Yes (default)
93
+ - No
94
+
95
+ After collecting answers, create \`diffprism.config.json\` at the project root with the user's choices. Then proceed to open the review.
96
+
97
+ ### 3. Open the Review
98
+
99
+ Call \`mcp__diffprism__open_review\` with:
100
+
101
+ - \`diff_ref\`: Use the \`defaultDiffScope\` from config. If the user specified a scope in their message (e.g., "/review staged"), use that instead.
102
+ - \`title\`: A short summary of the changes (generate from git status or the user's message).
103
+ - \`description\`: A brief description of what changed and why.
104
+ - \`reasoning\`: If \`includeReasoning\` is \`true\`, include your reasoning about the implementation decisions.
105
+
106
+ ### 4. Handle the Result
107
+
108
+ The tool blocks until the user submits their review in the browser. When it returns:
109
+
110
+ - **\`approved\`** \u2014 Acknowledge and proceed with whatever task was in progress.
111
+ - **\`approved_with_comments\`** \u2014 Note the comments, address any actionable feedback.
112
+ - **\`changes_requested\`** \u2014 Read the comments carefully, make the requested changes, and offer to open another review.
113
+
114
+ ### 5. Error Handling
115
+
116
+ If the \`mcp__diffprism__open_review\` tool is not available:
117
+ - Tell the user: "The DiffPrism MCP server isn't configured. Run \`npx diffprism setup\` to set it up, then restart Claude Code."
118
+
119
+ ## Behavior Rules
120
+
121
+ - When invoked via \`/review\`, always open a review regardless of the \`reviewTrigger\` setting.
122
+ - The \`reviewTrigger\` setting only applies to automatic review behavior during other workflows:
123
+ - \`"ask"\` \u2014 Never auto-review; only review when the user asks.
124
+ - \`"before_commit"\` \u2014 Open a review before creating any git commit.
125
+ - \`"always"\` \u2014 Open a review after any code change.
126
+ - To re-run onboarding, the user can delete \`diffprism.config.json\` and invoke \`/review\` again.
127
+ `;
128
+
129
+ // cli/src/commands/setup.ts
130
+ function findGitRoot(from) {
131
+ let dir = path.resolve(from);
132
+ while (true) {
133
+ if (fs.existsSync(path.join(dir, ".git"))) {
134
+ return dir;
135
+ }
136
+ const parent = path.dirname(dir);
137
+ if (parent === dir) return null;
138
+ dir = parent;
139
+ }
140
+ }
141
+ function readJsonFile(filePath) {
142
+ try {
143
+ const raw = fs.readFileSync(filePath, "utf-8");
144
+ return JSON.parse(raw);
145
+ } catch {
146
+ return {};
147
+ }
148
+ }
149
+ function writeJsonFile(filePath, data) {
150
+ const dir = path.dirname(filePath);
151
+ if (!fs.existsSync(dir)) {
152
+ fs.mkdirSync(dir, { recursive: true });
153
+ }
154
+ fs.writeFileSync(filePath, JSON.stringify(data, null, 2) + "\n");
155
+ }
156
+ function setupMcpJson(gitRoot, force) {
157
+ const filePath = path.join(gitRoot, ".mcp.json");
158
+ const existing = readJsonFile(filePath);
159
+ const servers = existing.mcpServers ?? {};
160
+ if (servers.diffprism && !force) {
161
+ return { action: "skipped", filePath };
162
+ }
163
+ servers.diffprism = {
164
+ command: "npx",
165
+ args: ["diffprism@latest", "serve"]
166
+ };
167
+ const action = fs.existsSync(filePath) ? "updated" : "created";
168
+ writeJsonFile(filePath, { ...existing, mcpServers: servers });
169
+ return { action, filePath };
170
+ }
171
+ function setupClaudeSettings(gitRoot, force) {
172
+ const filePath = path.join(gitRoot, ".claude", "settings.json");
173
+ const existing = readJsonFile(filePath);
174
+ const permissions = existing.permissions ?? {};
175
+ const allow = permissions.allow ?? [];
176
+ const toolName = "mcp__diffprism__open_review";
177
+ if (allow.includes(toolName) && !force) {
178
+ return { action: "skipped", filePath };
179
+ }
180
+ if (!allow.includes(toolName)) {
181
+ allow.push(toolName);
182
+ }
183
+ permissions.allow = allow;
184
+ const action = fs.existsSync(filePath) ? "updated" : "created";
185
+ writeJsonFile(filePath, { ...existing, permissions });
186
+ return { action, filePath };
187
+ }
188
+ function setupSkill(gitRoot, global, force) {
189
+ const skillDir = global ? path.join(os.homedir(), ".claude", "skills", "review") : path.join(gitRoot, ".claude", "skills", "review");
190
+ const filePath = path.join(skillDir, "SKILL.md");
191
+ if (fs.existsSync(filePath)) {
192
+ const existingContent = fs.readFileSync(filePath, "utf-8");
193
+ if (existingContent === skillContent) {
194
+ return { action: "skipped", filePath };
195
+ }
196
+ if (!force) {
197
+ console.log(
198
+ ` Warning: ${filePath} exists with different content. Use --force to overwrite.`
199
+ );
200
+ return { action: "skipped", filePath };
201
+ }
202
+ }
203
+ if (!fs.existsSync(skillDir)) {
204
+ fs.mkdirSync(skillDir, { recursive: true });
205
+ }
206
+ const action = fs.existsSync(filePath) ? "updated" : "created";
207
+ fs.writeFileSync(filePath, skillContent);
208
+ return { action, filePath };
209
+ }
210
+ async function setup(flags) {
211
+ const gitRoot = findGitRoot(process.cwd());
212
+ if (!gitRoot) {
213
+ console.error(
214
+ "Error: Not in a git repository. Run this command from inside a git project."
215
+ );
216
+ process.exit(1);
217
+ return;
218
+ }
219
+ const force = flags.force ?? false;
220
+ const global = flags.global ?? false;
221
+ console.log("Setting up DiffPrism for Claude Code...\n");
222
+ const result = { created: [], updated: [], skipped: [] };
223
+ const mcp = setupMcpJson(gitRoot, force);
224
+ result[mcp.action === "skipped" ? "skipped" : mcp.action === "created" ? "created" : "updated"].push(mcp.filePath);
225
+ const settings = setupClaudeSettings(gitRoot, force);
226
+ result[settings.action === "skipped" ? "skipped" : settings.action === "created" ? "created" : "updated"].push(settings.filePath);
227
+ const skill = setupSkill(gitRoot, global, force);
228
+ result[skill.action === "skipped" ? "skipped" : skill.action === "created" ? "created" : "updated"].push(skill.filePath);
229
+ if (result.created.length > 0) {
230
+ console.log("Created:");
231
+ for (const f of result.created) {
232
+ console.log(` + ${path.relative(gitRoot, f)}`);
233
+ }
234
+ }
235
+ if (result.updated.length > 0) {
236
+ console.log("Updated:");
237
+ for (const f of result.updated) {
238
+ console.log(` ~ ${path.relative(gitRoot, f)}`);
239
+ }
240
+ }
241
+ if (result.skipped.length > 0) {
242
+ console.log("Skipped (already configured):");
243
+ for (const f of result.skipped) {
244
+ console.log(` - ${path.relative(gitRoot, f)}`);
245
+ }
246
+ }
247
+ console.log(
248
+ "\nYou can now use /review in Claude Code to open a DiffPrism review."
249
+ );
250
+ console.log(
251
+ "If Claude Code is running, restart it to pick up the new configuration."
252
+ );
253
+ }
254
+
43
255
  // cli/src/index.ts
44
256
  var program = new Command();
45
- program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.10.2" : "0.0.0-dev");
257
+ program.name("diffprism").description("Local-first code review tool for agent-generated changes").version(true ? "0.11.1" : "0.0.0-dev");
46
258
  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);
47
259
  program.command("serve").description("Start the MCP server for Claude Code integration").action(serve);
260
+ program.command("setup").description("Configure DiffPrism for Claude Code integration").option("--global", "Install skill globally (~/.claude/skills/)").option("--force", "Overwrite existing configuration files").action(setup);
48
261
  program.parse();
@@ -11,7 +11,7 @@ import { z } from "zod";
11
11
  async function startMcpServer() {
12
12
  const server = new McpServer({
13
13
  name: "diffprism",
14
- version: true ? "0.10.2" : "0.0.0-dev"
14
+ version: true ? "0.11.1" : "0.0.0-dev"
15
15
  });
16
16
  server.tool(
17
17
  "open_review",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "diffprism",
3
- "version": "0.10.2",
3
+ "version": "0.11.1",
4
4
  "type": "module",
5
5
  "description": "Local-first code review tool for agent-generated code changes",
6
6
  "bin": {