diffcop 0.0.0-stage → 0.1.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.
Files changed (34) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +141 -2
  3. package/package.json +66 -4
  4. package/src/agent/claude-code/claude-code-agent.ts +174 -0
  5. package/src/agent/explored.ts +93 -0
  6. package/src/agent/reviewer-agent.ts +51 -0
  7. package/src/cli.ts +21 -0
  8. package/src/commands/review.ts +220 -0
  9. package/src/context/git/git-repository.ts +74 -0
  10. package/src/context/github/pull-request-context.ts +81 -0
  11. package/src/context/github/pull-request-data.ts +80 -0
  12. package/src/context/github/pull-request-fetcher.ts +149 -0
  13. package/src/context/repository.ts +16 -0
  14. package/src/context/review-context-collector.ts +106 -0
  15. package/src/github/github-client.ts +23 -0
  16. package/src/github/unwrap-response.ts +15 -0
  17. package/src/prompt/github/pull-request-formatter.ts +96 -0
  18. package/src/prompt/github/sanitize-content.ts +108 -0
  19. package/src/prompt/review-prompt-builder.ts +206 -0
  20. package/src/reporter/console/console-reporter.ts +72 -0
  21. package/src/reporter/console/json-reporter.ts +37 -0
  22. package/src/reporter/github/github-reporter.ts +93 -0
  23. package/src/reporter/github/review-comment-formatter.ts +144 -0
  24. package/src/reporter/github/sticky-comment.ts +181 -0
  25. package/src/reporter/progress.ts +12 -0
  26. package/src/reporter/review-layout.ts +359 -0
  27. package/src/reporter/review-reporter.ts +19 -0
  28. package/src/reporter/stats.ts +41 -0
  29. package/src/review/change-size-limit.ts +92 -0
  30. package/src/review/code-reviewer.ts +120 -0
  31. package/src/review/findings.ts +125 -0
  32. package/src/util/errors.ts +7 -0
  33. package/src/util/json.ts +7 -0
  34. package/tsconfig.json +34 -0
@@ -0,0 +1,125 @@
1
+ import { z } from "zod";
2
+ import type { Explored } from "@/agent/explored";
3
+ import type { RunStats } from "@/agent/reviewer-agent";
4
+
5
+ export const SEVERITIES = ["high", "medium", "low", "nit"] as const;
6
+
7
+ export type Severity = (typeof SEVERITIES)[number];
8
+
9
+ export const SCOPES = ["introduced", "preexisting", "description"] as const;
10
+
11
+ export type Scope = (typeof SCOPES)[number];
12
+
13
+ // Descriptions end up in the JSON schema the model answers to.
14
+ export const findingSchema = z.object({
15
+ file: z.string().describe("Path relative to the repository root"),
16
+ startLine: z.int().positive(),
17
+ endLine: z.int().positive(),
18
+ severity: z
19
+ .enum(SEVERITIES)
20
+ .describe(
21
+ "high, medium or low: a bug, by impact. nit: not a bug, but readability, clarity, naming or a small cleanup on a changed line; never anything a formatter or linter enforces",
22
+ ),
23
+ scope: z
24
+ .enum(SCOPES)
25
+ .describe(
26
+ "introduced: this diff caused it. preexisting: it was already wrong before this diff. description: the PR description claims something the diff doesn't do",
27
+ ),
28
+ title: z.string().describe("One-line summary of the bug or nit"),
29
+ explanation: z
30
+ .string()
31
+ .describe(
32
+ "At most two sentences: which input or state fails and why, citing the code. For a nit, what to change and why",
33
+ ),
34
+ });
35
+
36
+ export type Finding = z.infer<typeof findingSchema>;
37
+
38
+ export const findingsSchema = z.object({
39
+ summary: z
40
+ .string()
41
+ .describe(
42
+ "One sentence on what you checked, e.g. the call paths you traced",
43
+ ),
44
+ findings: z.array(findingSchema),
45
+ });
46
+
47
+ const verdictSchema = z.object({
48
+ id: z.int().describe("The finding's id"),
49
+ verdict: z.enum(["confirmed", "refuted"]),
50
+ reason: z.string(),
51
+ });
52
+
53
+ export type Verdict = z.infer<typeof verdictSchema>;
54
+
55
+ export const verdictsSchema = z.object({
56
+ verdicts: z.array(verdictSchema),
57
+ });
58
+
59
+ export interface DroppedFinding {
60
+ finding: Finding;
61
+ // Why the verifier didn't confirm it.
62
+ reason: string;
63
+ }
64
+
65
+ export interface ReviewResult {
66
+ summary: string;
67
+ findings: Finding[];
68
+ dropped: DroppedFinding[];
69
+ explored: Explored;
70
+ stats: RunStats;
71
+ }
72
+
73
+ // Ids are 1-based positions, the numbering the verifier is shown.
74
+ export function numberFindings(findings: Finding[]) {
75
+ return findings.map((finding, index) => {
76
+ return {
77
+ id: index + 1,
78
+ ...finding,
79
+ };
80
+ });
81
+ }
82
+
83
+ function isConfirmed(verdict: Verdict) {
84
+ return verdict.verdict === "confirmed";
85
+ }
86
+
87
+ function dropReason(verdicts: Verdict[]) {
88
+ if (verdicts.length === 0) {
89
+ return "No verdict returned";
90
+ }
91
+ const reasons = verdicts
92
+ .map((v) => {
93
+ return v.reason.trim();
94
+ })
95
+ .filter(Boolean)
96
+ .join(" / ");
97
+ if (verdicts.some(isConfirmed)) {
98
+ return `Contradictory verdicts: ${reasons}`;
99
+ }
100
+ return reasons || "Refuted without a reason";
101
+ }
102
+
103
+ // A finding needs a confirmation and no refutation, so a skipped or
104
+ // contradictory verdict drops it.
105
+ export function splitByVerdict(findings: Finding[], verdicts: Verdict[]) {
106
+ const confirmed: Finding[] = [];
107
+ const dropped: DroppedFinding[] = [];
108
+ findings.forEach((finding, index) => {
109
+ const own = verdicts.filter((v) => {
110
+ return v.id === index + 1;
111
+ });
112
+ if (own.length > 0 && own.every(isConfirmed)) {
113
+ confirmed.push(finding);
114
+ } else {
115
+ dropped.push({
116
+ finding: finding,
117
+ reason: dropReason(own),
118
+ });
119
+ }
120
+ });
121
+ return {
122
+ confirmed: confirmed,
123
+ dropped: dropped,
124
+ };
125
+ }
@@ -0,0 +1,7 @@
1
+ // Anything can be thrown, so turn a caught value into an Error once, at the catch.
2
+ export function toError(cause: unknown): Error {
3
+ if (cause instanceof Error) {
4
+ return cause;
5
+ }
6
+ return new Error(String(cause), { cause: cause });
7
+ }
@@ -0,0 +1,7 @@
1
+ export type JSONType =
2
+ | string
3
+ | number
4
+ | boolean
5
+ | null
6
+ | JSONType[]
7
+ | { [key: string]: JSONType };
package/tsconfig.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "compilerOptions": {
3
+ // Environment setup & latest features
4
+ "lib": ["ESNext"],
5
+ "target": "ESNext",
6
+ "module": "Preserve",
7
+ "moduleDetection": "force",
8
+ "jsx": "react-jsx",
9
+ "allowJs": true,
10
+ "types": ["bun"],
11
+
12
+ // Bundler mode
13
+ "moduleResolution": "bundler",
14
+ "allowImportingTsExtensions": true,
15
+ "verbatimModuleSyntax": true,
16
+ "noEmit": true,
17
+ "paths": {
18
+ "@/*": ["./src/*"]
19
+ },
20
+
21
+ // Best practices
22
+ "strict": true,
23
+ "skipLibCheck": true,
24
+ "noFallthroughCasesInSwitch": true,
25
+ "noUncheckedIndexedAccess": true,
26
+ "noImplicitOverride": true,
27
+
28
+ // Some stricter flags (disabled by default)
29
+ "noUnusedLocals": false,
30
+ "noUnusedParameters": false,
31
+ "noPropertyAccessFromIndexSignature": false
32
+ },
33
+ "include": ["src", "*.config.ts"]
34
+ }