ralph-review 0.1.4 → 0.1.5
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 +5 -1
- package/package.json +2 -2
- package/src/commands/init.ts +17 -10
- package/src/lib/html/dashboard/script.ts +54 -3
- package/src/lib/html/dashboard/styles.ts +5 -0
- package/src/lib/html/dashboard/view-model.ts +39 -5
- package/src/lib/html/log/page.ts +60 -1
- package/src/lib/html/log/styles.ts +6 -0
- package/src/lib/prompts/fixer.ts +6 -0
- package/src/lib/tui/components/Header.tsx +8 -5
- package/src/lib/types/fix.ts +36 -0
- package/src/lib/types/index.ts +2 -0
- package/src/lib/types/review.ts +2 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
[](https://github.com/kenryu42/ralph-review)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Orchestrating coding agents for code review, verification and fixing via the ralph loop.
|
|
9
9
|
|
|
10
10
|
---
|
|
11
11
|
|
|
@@ -131,6 +131,10 @@ Treats review findings as untrusted input — verifies every claim against actua
|
|
|
131
131
|
## Installation
|
|
132
132
|
|
|
133
133
|
```bash
|
|
134
|
+
# Homebrew
|
|
135
|
+
brew install kenryu42/tap/ralph-review
|
|
136
|
+
|
|
137
|
+
# npm
|
|
134
138
|
npm install -g ralph-review
|
|
135
139
|
```
|
|
136
140
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-review",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.1.5",
|
|
4
|
+
"description": "Orchestrating coding agents for code review, verification and fixing via the ralph loop.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"author": {
|
package/src/commands/init.ts
CHANGED
|
@@ -754,8 +754,8 @@ export async function buildAutoInitInput(
|
|
|
754
754
|
iterationTimeoutMinutes,
|
|
755
755
|
defaultReviewType: "uncommitted",
|
|
756
756
|
runSimplifierByDefault: false,
|
|
757
|
-
runWatchByDefault:
|
|
758
|
-
soundNotificationsEnabled:
|
|
757
|
+
runWatchByDefault: true,
|
|
758
|
+
soundNotificationsEnabled: true,
|
|
759
759
|
},
|
|
760
760
|
skippedAgents,
|
|
761
761
|
};
|
|
@@ -1043,14 +1043,21 @@ export async function runInitWithRuntime(
|
|
|
1043
1043
|
}
|
|
1044
1044
|
|
|
1045
1045
|
const resolvedInput = requireInitInput(runtime, input);
|
|
1046
|
-
const inputWithPreferences: InitInput =
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1046
|
+
const inputWithPreferences: InitInput =
|
|
1047
|
+
setupMode === "auto"
|
|
1048
|
+
? {
|
|
1049
|
+
...resolvedInput,
|
|
1050
|
+
runWatchByDefault: true,
|
|
1051
|
+
soundNotificationsEnabled: true,
|
|
1052
|
+
}
|
|
1053
|
+
: {
|
|
1054
|
+
...resolvedInput,
|
|
1055
|
+
runWatchByDefault: await promptForRunWatch(runtime, resolvedInput.runWatchByDefault),
|
|
1056
|
+
soundNotificationsEnabled: await promptForSoundNotifications(
|
|
1057
|
+
runtime,
|
|
1058
|
+
resolvedInput.soundNotificationsEnabled
|
|
1059
|
+
),
|
|
1060
|
+
};
|
|
1054
1061
|
|
|
1055
1062
|
const config = buildConfig(inputWithPreferences);
|
|
1056
1063
|
runtime.prompt.log.info(`Proposed configuration:\n${formatConfigDisplay(config)}`);
|
|
@@ -163,6 +163,48 @@ const DASHBOARD_SCRIPT_TEMPLATE = `
|
|
|
163
163
|
const sortByPriority = (fixes) =>
|
|
164
164
|
[...fixes].sort((a, b) => getPriorityRank(a.priority) - getPriorityRank(b.priority));
|
|
165
165
|
|
|
166
|
+
const formatFixRangeHunk = (lineStart, lineEnd) => {
|
|
167
|
+
const count = lineEnd - lineStart + 1;
|
|
168
|
+
if (count <= 1) {
|
|
169
|
+
return \`@@ -\${lineStart} +\${lineStart} @@\`;
|
|
170
|
+
}
|
|
171
|
+
return \`@@ -\${lineStart},\${count} +\${lineStart},\${count} @@\`;
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
const normalizeFixCodeLocation = (fix) => {
|
|
175
|
+
const vmLocation = fix?.codeLocation;
|
|
176
|
+
if (
|
|
177
|
+
vmLocation &&
|
|
178
|
+
typeof vmLocation.absoluteFilePath === "string" &&
|
|
179
|
+
Number.isInteger(vmLocation.lineStart) &&
|
|
180
|
+
Number.isInteger(vmLocation.lineEnd) &&
|
|
181
|
+
vmLocation.lineStart > 0 &&
|
|
182
|
+
vmLocation.lineEnd >= vmLocation.lineStart
|
|
183
|
+
) {
|
|
184
|
+
return vmLocation;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const rawLocation = fix?.code_location;
|
|
188
|
+
const start = rawLocation?.line_range?.start;
|
|
189
|
+
const end = rawLocation?.line_range?.end;
|
|
190
|
+
if (
|
|
191
|
+
rawLocation &&
|
|
192
|
+
typeof rawLocation.absolute_file_path === "string" &&
|
|
193
|
+
Number.isInteger(start) &&
|
|
194
|
+
Number.isInteger(end) &&
|
|
195
|
+
start > 0 &&
|
|
196
|
+
end >= start
|
|
197
|
+
) {
|
|
198
|
+
return {
|
|
199
|
+
absoluteFilePath: rawLocation.absolute_file_path,
|
|
200
|
+
lineStart: start,
|
|
201
|
+
lineEnd: end,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return null;
|
|
206
|
+
};
|
|
207
|
+
|
|
166
208
|
const extractFixes = (entries) => {
|
|
167
209
|
const fixes = [];
|
|
168
210
|
const skipped = [];
|
|
@@ -298,15 +340,24 @@ const DASHBOARD_SCRIPT_TEMPLATE = `
|
|
|
298
340
|
<div class="panel-title">Fixes Applied</div>
|
|
299
341
|
\${fixes.length
|
|
300
342
|
? \`<ul class="fix-list">\${fixes
|
|
301
|
-
.map((fix) =>
|
|
343
|
+
.map((fix) => {
|
|
344
|
+
const location = normalizeFixCodeLocation(fix);
|
|
345
|
+
const filePath = fix.file || location?.absoluteFilePath || "";
|
|
346
|
+
const range = location
|
|
347
|
+
? \`<div class="fix-range mono">\${escapeHtml(formatFixRangeHunk(location.lineStart, location.lineEnd))}</div>\`
|
|
348
|
+
: "";
|
|
349
|
+
|
|
350
|
+
return \`
|
|
302
351
|
<li class="fix-item">
|
|
303
352
|
<div class="fix-pill \${getPriorityPillClass(fix.priority)}">\${escapeHtml(fix.priority)}</div>
|
|
304
353
|
<div>
|
|
305
354
|
<div class="fix-title">\${escapeHtml(fix.title)}</div>
|
|
306
|
-
<div class="fix-meta muted">\${escapeHtml(
|
|
355
|
+
<div class="fix-meta muted">\${escapeHtml(filePath)}</div>
|
|
356
|
+
\${range}
|
|
307
357
|
</div>
|
|
308
358
|
</li>
|
|
309
|
-
|
|
359
|
+
\`;
|
|
360
|
+
})
|
|
310
361
|
.join("")}</ul>\`
|
|
311
362
|
: '<div class="muted">No fixes recorded for this session.</div>'}
|
|
312
363
|
</div>
|
|
@@ -359,6 +359,11 @@ export const DASHBOARD_CSS = `
|
|
|
359
359
|
.fix-pill-default { background: var(--accent); }
|
|
360
360
|
.fix-title, .skip-title { font-weight: 600; font-size: 13px; }
|
|
361
361
|
.fix-meta, .skip-reason { font-size: 11px; margin-top: 4px; }
|
|
362
|
+
.fix-range {
|
|
363
|
+
margin-top: 6px;
|
|
364
|
+
color: rgba(237, 242, 255, 0.84);
|
|
365
|
+
font-size: 11px;
|
|
366
|
+
}
|
|
362
367
|
.muted { color: var(--muted); }
|
|
363
368
|
.empty { padding: 24px; text-align: center; color: var(--muted); border: 1px dashed var(--border); border-radius: 16px; background: rgba(9, 14, 23, 0.6); }
|
|
364
369
|
.empty.tiny { padding: 12px; font-size: 12px; }
|
|
@@ -17,6 +17,11 @@ interface FixViewModel {
|
|
|
17
17
|
priority: FixEntry["priority"];
|
|
18
18
|
title: string;
|
|
19
19
|
file: string;
|
|
20
|
+
codeLocation?: {
|
|
21
|
+
absoluteFilePath: string;
|
|
22
|
+
lineStart: number;
|
|
23
|
+
lineEnd: number;
|
|
24
|
+
};
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
interface SkippedViewModel {
|
|
@@ -99,16 +104,45 @@ function formatRoleDisplay(name: string, model: string, reasoning: string): stri
|
|
|
99
104
|
return `${name} (${details.join(", ")})`;
|
|
100
105
|
}
|
|
101
106
|
|
|
107
|
+
function toCodeLocationViewModel(fix: FixEntry): FixViewModel["codeLocation"] {
|
|
108
|
+
const location = fix.code_location;
|
|
109
|
+
if (!location) {
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const lineStart = location.line_range?.start;
|
|
114
|
+
const lineEnd = location.line_range?.end;
|
|
115
|
+
if (
|
|
116
|
+
typeof location.absolute_file_path !== "string" ||
|
|
117
|
+
!Number.isInteger(lineStart) ||
|
|
118
|
+
!Number.isInteger(lineEnd) ||
|
|
119
|
+
lineStart < 1 ||
|
|
120
|
+
lineEnd < lineStart
|
|
121
|
+
) {
|
|
122
|
+
return undefined;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
absoluteFilePath: location.absolute_file_path,
|
|
127
|
+
lineStart,
|
|
128
|
+
lineEnd,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
102
132
|
function buildSessionViewModel(session: SessionStats): SessionViewModel {
|
|
103
133
|
const { fixes, skipped } = extractFixes(session.entries ?? []);
|
|
104
134
|
const priorities = new Set(fixes.map((fix) => fix.priority));
|
|
105
135
|
const sortedFixes = [...fixes]
|
|
106
136
|
.sort((a, b) => getPriorityRank(a.priority) - getPriorityRank(b.priority))
|
|
107
|
-
.map((fix) =>
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
137
|
+
.map((fix) => {
|
|
138
|
+
const codeLocation = toCodeLocationViewModel(fix);
|
|
139
|
+
return {
|
|
140
|
+
priority: fix.priority,
|
|
141
|
+
title: fix.title,
|
|
142
|
+
file: fix.file ?? "",
|
|
143
|
+
...(codeLocation ? { codeLocation } : {}),
|
|
144
|
+
};
|
|
145
|
+
});
|
|
112
146
|
|
|
113
147
|
const sortedSkipped = [...skipped]
|
|
114
148
|
.sort((a, b) => getPriorityRank(a.priority) - getPriorityRank(b.priority))
|
package/src/lib/html/log/page.ts
CHANGED
|
@@ -3,8 +3,10 @@ import { getPriorityPillClass } from "@/lib/html/priority";
|
|
|
3
3
|
import { escapeHtml, formatDate, formatDuration } from "@/lib/html/shared";
|
|
4
4
|
import type {
|
|
5
5
|
AgentSettings,
|
|
6
|
+
CodeLocation,
|
|
6
7
|
FixEntry,
|
|
7
8
|
IterationEntry,
|
|
9
|
+
LineRange,
|
|
8
10
|
LogEntry,
|
|
9
11
|
SkippedEntry,
|
|
10
12
|
SystemEntry,
|
|
@@ -20,8 +22,64 @@ function isCodeSimplified(systemEntry: SystemEntry | undefined): boolean {
|
|
|
20
22
|
return Boolean(systemEntry.codeSimplifier || systemEntry.reviewOptions?.simplifier);
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
function isValidLineRange(lineRange: LineRange | undefined): lineRange is LineRange {
|
|
26
|
+
if (!lineRange) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
Number.isInteger(lineRange.start) &&
|
|
32
|
+
Number.isInteger(lineRange.end) &&
|
|
33
|
+
lineRange.start > 0 &&
|
|
34
|
+
lineRange.end >= lineRange.start
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isValidCodeLocation(location: CodeLocation | null | undefined): location is CodeLocation {
|
|
39
|
+
if (!location || typeof location.absolute_file_path !== "string") {
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return isValidLineRange(location.line_range);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function getFixDisplayFile(fix: FixEntry): string {
|
|
47
|
+
if (fix.file) {
|
|
48
|
+
return fix.file;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const location = fix.code_location;
|
|
52
|
+
if (isValidCodeLocation(location)) {
|
|
53
|
+
return location.absolute_file_path;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return "";
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function formatFixRangeHunk(start: number, end: number): string {
|
|
60
|
+
const count = end - start + 1;
|
|
61
|
+
if (count <= 1) {
|
|
62
|
+
return `@@ -${start} +${start} @@`;
|
|
63
|
+
}
|
|
64
|
+
return `@@ -${start},${count} +${start},${count} @@`;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function renderFixRange(fix: FixEntry): string {
|
|
68
|
+
const location = fix.code_location;
|
|
69
|
+
if (!isValidCodeLocation(location)) {
|
|
70
|
+
return "";
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const lineStart = location.line_range.start;
|
|
74
|
+
const lineEnd = location.line_range.end;
|
|
75
|
+
|
|
76
|
+
return `<div class="fix-range mono">${escapeHtml(formatFixRangeHunk(lineStart, lineEnd))}</div>`;
|
|
77
|
+
}
|
|
78
|
+
|
|
23
79
|
function renderFixEntry(fix: FixEntry): string {
|
|
24
|
-
const
|
|
80
|
+
const filePath = getFixDisplayFile(fix);
|
|
81
|
+
const file = filePath ? `<span class="muted">${escapeHtml(filePath)}</span>` : "";
|
|
82
|
+
const range = renderFixRange(fix);
|
|
25
83
|
const pillClass = getPriorityPillClass(fix.priority);
|
|
26
84
|
return `
|
|
27
85
|
<li class="fix-item">
|
|
@@ -29,6 +87,7 @@ function renderFixEntry(fix: FixEntry): string {
|
|
|
29
87
|
<div>
|
|
30
88
|
<div class="fix-title">${escapeHtml(fix.title)}</div>
|
|
31
89
|
<div class="fix-meta">${file}</div>
|
|
90
|
+
${range}
|
|
32
91
|
</div>
|
|
33
92
|
</li>
|
|
34
93
|
`;
|
|
@@ -84,9 +84,15 @@ export const LOG_CSS = `
|
|
|
84
84
|
.fix-pill-default { background: var(--accent); }
|
|
85
85
|
.fix-title { font-weight: 600; }
|
|
86
86
|
.fix-meta { font-size: 12px; margin-top: 4px; }
|
|
87
|
+
.fix-range {
|
|
88
|
+
margin-top: 6px;
|
|
89
|
+
color: rgba(237, 242, 255, 0.85);
|
|
90
|
+
font-size: 12px;
|
|
91
|
+
}
|
|
87
92
|
.skip-title { font-weight: 600; }
|
|
88
93
|
.skip-reason { font-size: 12px; }
|
|
89
94
|
.muted { color: var(--muted); }
|
|
95
|
+
.mono { font-family: "Space Grotesk", monospace; }
|
|
90
96
|
.callout {
|
|
91
97
|
margin-top: 12px;
|
|
92
98
|
padding: 12px 16px;
|
package/src/lib/prompts/fixer.ts
CHANGED
|
@@ -36,6 +36,7 @@ ${reviewOutput}
|
|
|
36
36
|
- Prefer one aggregate command; otherwise run available lint -> typecheck -> test -> build.
|
|
37
37
|
- Treat warnings as blocking.
|
|
38
38
|
- Iterate fix + rerun until clean.
|
|
39
|
+
6) For each APPLY item, include the applied code location when available (absolute path + line range).
|
|
39
40
|
|
|
40
41
|
## Special rule: tracking-status claims
|
|
41
42
|
Claims like "file is untracked/not committed/missing from git" are SKIP in this pre-commit workflow.
|
|
@@ -69,6 +70,10 @@ ${FIX_SUMMARY_START_TOKEN}
|
|
|
69
70
|
"title": "<one-line title>",
|
|
70
71
|
"priority": "<P0 | P1 | P2 | P3>",
|
|
71
72
|
"file": "<path or null>",
|
|
73
|
+
"code_location": {
|
|
74
|
+
"absolute_file_path": "<absolute path>",
|
|
75
|
+
"line_range": {"start": <int>, "end": <int>}
|
|
76
|
+
},
|
|
72
77
|
"claim": "<issue claim>",
|
|
73
78
|
"evidence": "<file:line / behavior>",
|
|
74
79
|
"fix": "<what changed>"
|
|
@@ -95,6 +100,7 @@ JSON rules:
|
|
|
95
100
|
- fixes MUST be []
|
|
96
101
|
- skipped MUST contain only SKIP items
|
|
97
102
|
- Include all APPLY items in fixes.
|
|
103
|
+
- For each APPLY item, include code_location when available; otherwise set code_location to null or omit it.
|
|
98
104
|
- Include all SKIP items in skipped with required reason prefix.
|
|
99
105
|
- Use [] when empty.
|
|
100
106
|
- Priority must be exactly P0/P1/P2/P3.
|
|
@@ -80,18 +80,21 @@ export function Header({ branch, elapsed, session, projectPath, config }: Header
|
|
|
80
80
|
flexShrink={0}
|
|
81
81
|
>
|
|
82
82
|
<box flexDirection="row">
|
|
83
|
-
<box flexDirection="column" width={
|
|
83
|
+
<box flexDirection="column" width={20}>
|
|
84
84
|
<text>
|
|
85
|
-
<span fg={TUI_COLORS.brand.
|
|
85
|
+
<span fg={TUI_COLORS.brand.title}>{" ██████╗ ██████╗ "}</span>
|
|
86
86
|
</text>
|
|
87
87
|
<text>
|
|
88
|
-
<span fg={TUI_COLORS.brand.
|
|
88
|
+
<span fg={TUI_COLORS.brand.title}>{" ██╔══██╗██╔══██╗"}</span>
|
|
89
89
|
</text>
|
|
90
90
|
<text>
|
|
91
|
-
<span fg={TUI_COLORS.brand.
|
|
91
|
+
<span fg={TUI_COLORS.brand.title}>{" ██████╔╝██████╔╝"}</span>
|
|
92
92
|
</text>
|
|
93
93
|
<text>
|
|
94
|
-
<span fg={TUI_COLORS.brand.
|
|
94
|
+
<span fg={TUI_COLORS.brand.title}>{" ██║ ██║██║ ██║"}</span>
|
|
95
|
+
</text>
|
|
96
|
+
<text>
|
|
97
|
+
<span fg={TUI_COLORS.brand.title}>{" ╚═╝ ╚═╝╚═╝ ╚═╝"}</span>
|
|
95
98
|
</text>
|
|
96
99
|
</box>
|
|
97
100
|
|
package/src/lib/types/fix.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import type { FixDecision, Priority } from "./domain";
|
|
2
2
|
import { VALID_FIX_DECISIONS, VALID_PRIORITIES } from "./domain";
|
|
3
|
+
import type { CodeLocation } from "./review";
|
|
3
4
|
|
|
4
5
|
export interface FixEntry {
|
|
5
6
|
id: number;
|
|
6
7
|
title: string;
|
|
7
8
|
priority: Priority;
|
|
8
9
|
file?: string | null;
|
|
10
|
+
code_location?: CodeLocation | null;
|
|
9
11
|
claim: string;
|
|
10
12
|
evidence: string;
|
|
11
13
|
fix: string;
|
|
@@ -25,6 +27,37 @@ export interface FixSummary {
|
|
|
25
27
|
skipped: SkippedEntry[];
|
|
26
28
|
}
|
|
27
29
|
|
|
30
|
+
function isLineRange(value: unknown): value is CodeLocation["line_range"] {
|
|
31
|
+
if (typeof value !== "object" || value === null) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const obj = value as Record<string, unknown>;
|
|
36
|
+
|
|
37
|
+
return (
|
|
38
|
+
typeof obj.start === "number" &&
|
|
39
|
+
Number.isInteger(obj.start) &&
|
|
40
|
+
obj.start > 0 &&
|
|
41
|
+
typeof obj.end === "number" &&
|
|
42
|
+
Number.isInteger(obj.end) &&
|
|
43
|
+
obj.end >= obj.start
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isCodeLocation(value: unknown): value is CodeLocation {
|
|
48
|
+
if (typeof value !== "object" || value === null) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const obj = value as Record<string, unknown>;
|
|
53
|
+
|
|
54
|
+
if (typeof obj.absolute_file_path !== "string") {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return isLineRange(obj.line_range);
|
|
59
|
+
}
|
|
60
|
+
|
|
28
61
|
function isFixEntry(value: unknown): value is FixEntry {
|
|
29
62
|
if (typeof value !== "object" || value === null) {
|
|
30
63
|
return false;
|
|
@@ -38,6 +71,9 @@ function isFixEntry(value: unknown): value is FixEntry {
|
|
|
38
71
|
typeof obj.priority === "string" &&
|
|
39
72
|
VALID_PRIORITIES.includes(obj.priority as Priority) &&
|
|
40
73
|
(obj.file === undefined || obj.file === null || typeof obj.file === "string") &&
|
|
74
|
+
(obj.code_location === undefined ||
|
|
75
|
+
obj.code_location === null ||
|
|
76
|
+
isCodeLocation(obj.code_location)) &&
|
|
41
77
|
typeof obj.claim === "string" &&
|
|
42
78
|
typeof obj.evidence === "string" &&
|
|
43
79
|
typeof obj.fix === "string"
|
package/src/lib/types/index.ts
CHANGED
package/src/lib/types/review.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { OverallCorrectness } from "./domain";
|
|
2
2
|
import { VALID_OVERALL_CORRECTNESS } from "./domain";
|
|
3
3
|
|
|
4
|
-
interface LineRange {
|
|
4
|
+
export interface LineRange {
|
|
5
5
|
start: number;
|
|
6
6
|
end: number;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
interface CodeLocation {
|
|
9
|
+
export interface CodeLocation {
|
|
10
10
|
absolute_file_path: string;
|
|
11
11
|
line_range: LineRange;
|
|
12
12
|
}
|