@xynogen/pix-hunk 0.1.0 → 0.1.2
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/package.json +3 -2
- package/src/index.test.ts +0 -413
- package/src/lib.test.ts +0 -144
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@xynogen/pix-hunk",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Pi tool — drive live Hunk diff reviews with ordered bulk operations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
},
|
|
13
13
|
"files": [
|
|
14
14
|
"src",
|
|
15
|
+
"!src/**/*.test.*",
|
|
15
16
|
"README.md",
|
|
16
17
|
"LICENSE"
|
|
17
18
|
],
|
|
@@ -43,7 +44,7 @@
|
|
|
43
44
|
"access": "public"
|
|
44
45
|
},
|
|
45
46
|
"dependencies": {
|
|
46
|
-
"@xynogen/pix-pretty": "^1.
|
|
47
|
+
"@xynogen/pix-pretty": "^1.25.0",
|
|
47
48
|
"@xynogen/pix-runtime": "^0.8.1",
|
|
48
49
|
"typebox": "^1.1.38"
|
|
49
50
|
},
|
package/src/index.test.ts
DELETED
|
@@ -1,413 +0,0 @@
|
|
|
1
|
-
import { expect, test } from "bun:test";
|
|
2
|
-
import registerHunk, { type HunkRunner } from "./index.ts";
|
|
3
|
-
|
|
4
|
-
type RenderTheme = {
|
|
5
|
-
fg: (color: string, text: string) => string;
|
|
6
|
-
bold: (text: string) => string;
|
|
7
|
-
};
|
|
8
|
-
|
|
9
|
-
type RenderContext = {
|
|
10
|
-
expanded: boolean;
|
|
11
|
-
isError: boolean;
|
|
12
|
-
state: Record<string, unknown>;
|
|
13
|
-
invalidate: () => void;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
type ToolDef = {
|
|
17
|
-
renderShell?: "self";
|
|
18
|
-
renderCall: (
|
|
19
|
-
args: { ops: Array<Record<string, unknown>> },
|
|
20
|
-
theme: RenderTheme,
|
|
21
|
-
context: RenderContext,
|
|
22
|
-
) => { render: (width: number) => string[] };
|
|
23
|
-
renderResult: (
|
|
24
|
-
result: { content: Array<{ type: "text"; text: string }>; details: unknown },
|
|
25
|
-
options: { expanded: boolean; isPartial: boolean },
|
|
26
|
-
theme: RenderTheme,
|
|
27
|
-
context: RenderContext,
|
|
28
|
-
) => { render: (width: number) => string[] };
|
|
29
|
-
execute: (
|
|
30
|
-
id: string,
|
|
31
|
-
params: { ops: Array<Record<string, unknown>>; maxCharacters?: number },
|
|
32
|
-
signal: AbortSignal | undefined,
|
|
33
|
-
onUpdate: undefined,
|
|
34
|
-
ctx: { cwd: string },
|
|
35
|
-
) => Promise<{ content: Array<{ type: "text"; text: string }>; details: unknown }>;
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
const taggedTheme: RenderTheme = {
|
|
39
|
-
fg: (color, text) => `[${color}]${text}[/${color}]`,
|
|
40
|
-
bold: (text) => `[bold]${text}[/bold]`,
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const renderContext = (expanded = false, collapsed = false): RenderContext => ({
|
|
44
|
-
expanded,
|
|
45
|
-
isError: false,
|
|
46
|
-
state: collapsed ? { collapsed: true } : {},
|
|
47
|
-
invalidate: () => {},
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
function rendered(component: { render: (width: number) => string[] }): string {
|
|
51
|
-
return component.render(240).join("\n");
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function capture(runner: HunkRunner): ToolDef {
|
|
55
|
-
let tool: ToolDef | undefined;
|
|
56
|
-
const pi = {
|
|
57
|
-
registerTool(def: ToolDef) {
|
|
58
|
-
tool = def;
|
|
59
|
-
},
|
|
60
|
-
} as never;
|
|
61
|
-
registerHunk(pi, runner);
|
|
62
|
-
if (!tool) throw new Error("hunk tool not registered");
|
|
63
|
-
return tool;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
test("renders a compact colored summary and hides internal ids from expanded UI", async () => {
|
|
67
|
-
let call = 0;
|
|
68
|
-
const runner: HunkRunner = async () => {
|
|
69
|
-
call++;
|
|
70
|
-
if (call === 1) {
|
|
71
|
-
return {
|
|
72
|
-
stdout: JSON.stringify({
|
|
73
|
-
result: { filePath: "src/a.ts", hunkIndex: 0, selectedHunk: { newRange: [10, 20] } },
|
|
74
|
-
}),
|
|
75
|
-
stderr: "",
|
|
76
|
-
code: 0,
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
return {
|
|
80
|
-
stdout: JSON.stringify({
|
|
81
|
-
result: {
|
|
82
|
-
commentId: "mcp:3681add-very-long-internal-id",
|
|
83
|
-
filePath: "src/a.ts",
|
|
84
|
-
hunkIndex: 0,
|
|
85
|
-
side: "new",
|
|
86
|
-
line: 13,
|
|
87
|
-
},
|
|
88
|
-
}),
|
|
89
|
-
stderr: "",
|
|
90
|
-
code: 0,
|
|
91
|
-
};
|
|
92
|
-
};
|
|
93
|
-
const tool = capture(runner);
|
|
94
|
-
const args = {
|
|
95
|
-
ops: [
|
|
96
|
-
{ action: "navigate", file: "src/a.ts", hunk: 1 },
|
|
97
|
-
{ action: "comment", file: "src/a.ts", newLine: 13, summary: "Long body hidden" },
|
|
98
|
-
],
|
|
99
|
-
};
|
|
100
|
-
const result = await tool.execute("render", args, undefined, undefined, { cwd: "/repo" });
|
|
101
|
-
|
|
102
|
-
expect(tool.renderShell).toBe("self");
|
|
103
|
-
expect(rendered(tool.renderCall(args, taggedTheme, renderContext()))).toContain(
|
|
104
|
-
"[toolTitle][bold]hunk[/bold][/toolTitle] [dim]navigate +1[/dim]",
|
|
105
|
-
);
|
|
106
|
-
const compactContext = renderContext(false, true);
|
|
107
|
-
expect(rendered(tool.renderCall(args, taggedTheme, compactContext))).toBe("");
|
|
108
|
-
const compact = rendered(
|
|
109
|
-
tool.renderResult(result, { expanded: false, isPartial: false }, taggedTheme, compactContext),
|
|
110
|
-
);
|
|
111
|
-
expect(compact).toContain("[toolTitle][bold]hunk[/bold][/toolTitle]");
|
|
112
|
-
expect(compact).toContain("[dim]navigate +1[/dim]");
|
|
113
|
-
expect(compact).toContain("[muted]2 ops · 1 comment[/muted]");
|
|
114
|
-
expect(compact).not.toContain("mcp:3681add");
|
|
115
|
-
expect(compact).not.toContain("─");
|
|
116
|
-
|
|
117
|
-
const expanded = rendered(
|
|
118
|
-
tool.renderResult(
|
|
119
|
-
result,
|
|
120
|
-
{ expanded: true, isPartial: false },
|
|
121
|
-
taggedTheme,
|
|
122
|
-
renderContext(true, true),
|
|
123
|
-
),
|
|
124
|
-
);
|
|
125
|
-
expect(expanded).toContain("[success]────────────────────────────────");
|
|
126
|
-
expect(expanded).toContain("[dim]src/a.ts:h1[/dim] [muted]· 1 comment[/muted]");
|
|
127
|
-
expect(expanded).not.toContain("[text]navigate");
|
|
128
|
-
expect(expanded).not.toContain("[text]comment");
|
|
129
|
-
expect(expanded).not.toContain("[toolTitle]navigate");
|
|
130
|
-
expect(expanded).not.toContain("[toolTitle]comment");
|
|
131
|
-
expect(expanded).not.toContain("src/a.ts:new:13");
|
|
132
|
-
expect(expanded).not.toContain("mcp:3681add");
|
|
133
|
-
expect(expanded).not.toContain("Long body hidden");
|
|
134
|
-
|
|
135
|
-
const partial = rendered(
|
|
136
|
-
tool.renderResult(
|
|
137
|
-
result,
|
|
138
|
-
{ expanded: true, isPartial: true },
|
|
139
|
-
taggedTheme,
|
|
140
|
-
renderContext(true, true),
|
|
141
|
-
),
|
|
142
|
-
);
|
|
143
|
-
expect(partial).not.toContain("─");
|
|
144
|
-
|
|
145
|
-
const failed = await capture(async () => ({
|
|
146
|
-
stdout: "",
|
|
147
|
-
stderr: "session gone",
|
|
148
|
-
code: 1,
|
|
149
|
-
})).execute("render-error", { ops: [{ action: "list" }] }, undefined, undefined, {
|
|
150
|
-
cwd: "/repo",
|
|
151
|
-
});
|
|
152
|
-
const failedExpanded = rendered(
|
|
153
|
-
tool.renderResult(
|
|
154
|
-
failed,
|
|
155
|
-
{ expanded: true, isPartial: false },
|
|
156
|
-
taggedTheme,
|
|
157
|
-
renderContext(true, true),
|
|
158
|
-
),
|
|
159
|
-
);
|
|
160
|
-
expect(failedExpanded).toContain("[error]────────────────────────────────");
|
|
161
|
-
expect(failedExpanded).toContain("[error]list: session gone[/error]");
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
test("executes bulk operations in order and returns every parsed result", async () => {
|
|
165
|
-
const calls: string[][] = [];
|
|
166
|
-
const runner: HunkRunner = async (args) => {
|
|
167
|
-
calls.push(args);
|
|
168
|
-
return calls.length === 1
|
|
169
|
-
? { stdout: '{"sessions":[]}', stderr: "", code: 0 }
|
|
170
|
-
: { stdout: "", stderr: "No active Hunk sessions", code: 1 };
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
const result = await capture(runner).execute(
|
|
174
|
-
"t1",
|
|
175
|
-
{ ops: [{ action: "list" }, { action: "review", repo: "/work" }] },
|
|
176
|
-
undefined,
|
|
177
|
-
undefined,
|
|
178
|
-
{ cwd: "/cwd" },
|
|
179
|
-
);
|
|
180
|
-
|
|
181
|
-
expect(calls).toEqual([
|
|
182
|
-
["session", "list", "--json"],
|
|
183
|
-
["session", "review", "--repo", "/work", "--json"],
|
|
184
|
-
]);
|
|
185
|
-
expect(result.content[0]?.text).toBe("list sessions=0\nreview error: No active Hunk sessions");
|
|
186
|
-
expect(result.details).toEqual({
|
|
187
|
-
_type: "hunkResult",
|
|
188
|
-
outcome: "error",
|
|
189
|
-
results: [
|
|
190
|
-
{ action: "list", ok: true, data: { sessions: [] } },
|
|
191
|
-
{ action: "review", ok: false, error: "No active Hunk sessions" },
|
|
192
|
-
],
|
|
193
|
-
});
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test("summarizes review JSON as files and hunks instead of dumping it", async () => {
|
|
197
|
-
const runner: HunkRunner = async () => ({
|
|
198
|
-
stdout: JSON.stringify({
|
|
199
|
-
review: {
|
|
200
|
-
sessionId: "s1",
|
|
201
|
-
title: "Working tree",
|
|
202
|
-
selectedFile: { path: "src/a.ts" },
|
|
203
|
-
files: [
|
|
204
|
-
{
|
|
205
|
-
path: "src/a.ts",
|
|
206
|
-
additions: 5,
|
|
207
|
-
deletions: 2,
|
|
208
|
-
patch: "@@ -10,2 +10,3 @@\n-old\n+new",
|
|
209
|
-
hunks: [
|
|
210
|
-
{
|
|
211
|
-
index: 0,
|
|
212
|
-
header: "@@ -10,2 +10,3 @@",
|
|
213
|
-
oldRange: [10, 11],
|
|
214
|
-
newRange: [10, 12],
|
|
215
|
-
},
|
|
216
|
-
],
|
|
217
|
-
},
|
|
218
|
-
],
|
|
219
|
-
},
|
|
220
|
-
}),
|
|
221
|
-
stderr: "",
|
|
222
|
-
code: 0,
|
|
223
|
-
});
|
|
224
|
-
const result = await capture(runner).execute(
|
|
225
|
-
"t-review",
|
|
226
|
-
{ ops: [{ action: "review" }] },
|
|
227
|
-
undefined,
|
|
228
|
-
undefined,
|
|
229
|
-
{ cwd: "/repo" },
|
|
230
|
-
);
|
|
231
|
-
|
|
232
|
-
expect(result.content[0]?.text).toBe(
|
|
233
|
-
"review s1 Working tree\n" +
|
|
234
|
-
" selected src/a.ts\n" +
|
|
235
|
-
" src/a.ts +5 -2\n" +
|
|
236
|
-
" h1 old=10-11 new=10-12\n" +
|
|
237
|
-
" patch\n" +
|
|
238
|
-
" @@ -10,2 +10,3 @@\n" +
|
|
239
|
-
" -old\n" +
|
|
240
|
-
" +new",
|
|
241
|
-
);
|
|
242
|
-
expect(result.content[0]?.text).not.toContain("sessionId");
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("returns exact comment ids and locations for follow-up actions", async () => {
|
|
246
|
-
let call = 0;
|
|
247
|
-
const runner: HunkRunner = async () => {
|
|
248
|
-
call++;
|
|
249
|
-
if (call === 1) {
|
|
250
|
-
return {
|
|
251
|
-
stdout: JSON.stringify({
|
|
252
|
-
result: {
|
|
253
|
-
commentId: "c17",
|
|
254
|
-
filePath: "src/a.ts",
|
|
255
|
-
hunkIndex: 0,
|
|
256
|
-
side: "new",
|
|
257
|
-
line: 12,
|
|
258
|
-
},
|
|
259
|
-
}),
|
|
260
|
-
stderr: "",
|
|
261
|
-
code: 0,
|
|
262
|
-
};
|
|
263
|
-
}
|
|
264
|
-
if (call === 2) {
|
|
265
|
-
return {
|
|
266
|
-
stdout: JSON.stringify({
|
|
267
|
-
comments: [
|
|
268
|
-
{
|
|
269
|
-
noteId: "note-81bc",
|
|
270
|
-
filePath: "src/a.ts",
|
|
271
|
-
hunkIndex: 0,
|
|
272
|
-
newRange: [13, 13],
|
|
273
|
-
author: "xynogen",
|
|
274
|
-
body: "Can this be simpler?",
|
|
275
|
-
},
|
|
276
|
-
],
|
|
277
|
-
}),
|
|
278
|
-
stderr: "",
|
|
279
|
-
code: 0,
|
|
280
|
-
};
|
|
281
|
-
}
|
|
282
|
-
return {
|
|
283
|
-
stdout: JSON.stringify({
|
|
284
|
-
result: { commentId: "c17", removed: true, remainingCommentCount: 1 },
|
|
285
|
-
}),
|
|
286
|
-
stderr: "",
|
|
287
|
-
code: 0,
|
|
288
|
-
};
|
|
289
|
-
};
|
|
290
|
-
const result = await capture(runner).execute(
|
|
291
|
-
"t-comments",
|
|
292
|
-
{
|
|
293
|
-
ops: [
|
|
294
|
-
{ action: "comment", file: "src/a.ts", newLine: 12, summary: "Fix this" },
|
|
295
|
-
{ action: "comment_list", type: "user" },
|
|
296
|
-
{ action: "comment_rm", commentId: "c17" },
|
|
297
|
-
],
|
|
298
|
-
},
|
|
299
|
-
undefined,
|
|
300
|
-
undefined,
|
|
301
|
-
{ cwd: "/repo" },
|
|
302
|
-
);
|
|
303
|
-
|
|
304
|
-
expect(result.content[0]?.text).toBe(
|
|
305
|
-
"comment c17 src/a.ts:new:12 h1\n" +
|
|
306
|
-
"comment_list\n" +
|
|
307
|
-
"note-81bc src/a.ts:new:13 h1 @xynogen: Can this be simpler?\n" +
|
|
308
|
-
"comment_rm c17 remaining=1",
|
|
309
|
-
);
|
|
310
|
-
});
|
|
311
|
-
|
|
312
|
-
test("budgets each operation so a large patch cannot hide later results", async () => {
|
|
313
|
-
let call = 0;
|
|
314
|
-
const runner: HunkRunner = async () => {
|
|
315
|
-
call++;
|
|
316
|
-
return call === 1
|
|
317
|
-
? {
|
|
318
|
-
stdout: JSON.stringify({
|
|
319
|
-
review: {
|
|
320
|
-
title: "Large review",
|
|
321
|
-
files: [
|
|
322
|
-
{
|
|
323
|
-
path: "src/large.ts",
|
|
324
|
-
additions: 1,
|
|
325
|
-
deletions: 0,
|
|
326
|
-
hunks: [],
|
|
327
|
-
patch: `+${"x".repeat(60_000)}`,
|
|
328
|
-
},
|
|
329
|
-
],
|
|
330
|
-
},
|
|
331
|
-
}),
|
|
332
|
-
stderr: "",
|
|
333
|
-
code: 0,
|
|
334
|
-
}
|
|
335
|
-
: {
|
|
336
|
-
stdout: JSON.stringify({
|
|
337
|
-
result: {
|
|
338
|
-
commentId: "c-final",
|
|
339
|
-
filePath: "src/final.ts",
|
|
340
|
-
hunkIndex: 2,
|
|
341
|
-
side: "new",
|
|
342
|
-
line: 44,
|
|
343
|
-
},
|
|
344
|
-
}),
|
|
345
|
-
stderr: "",
|
|
346
|
-
code: 0,
|
|
347
|
-
};
|
|
348
|
-
};
|
|
349
|
-
const result = await capture(runner).execute(
|
|
350
|
-
"t2",
|
|
351
|
-
{
|
|
352
|
-
ops: [
|
|
353
|
-
{ action: "review", includePatch: true },
|
|
354
|
-
{ action: "comment", file: "src/final.ts", newLine: 44, summary: "Fix" },
|
|
355
|
-
],
|
|
356
|
-
},
|
|
357
|
-
undefined,
|
|
358
|
-
undefined,
|
|
359
|
-
{ cwd: "/repo" },
|
|
360
|
-
);
|
|
361
|
-
|
|
362
|
-
expect(result.content[0]?.text.length).toBeLessThanOrEqual(10_000);
|
|
363
|
-
expect(result.content[0]?.text).toContain("review Large review");
|
|
364
|
-
expect(result.content[0]?.text).toContain("[truncated; full result in tool details]");
|
|
365
|
-
expect(result.content[0]?.text).toContain("comment c-final src/final.ts:new:44 h3");
|
|
366
|
-
expect(JSON.stringify(result.details).length).toBeGreaterThan(60_000);
|
|
367
|
-
});
|
|
368
|
-
|
|
369
|
-
test("allows a bounded model-output override", async () => {
|
|
370
|
-
const huge = JSON.stringify({
|
|
371
|
-
review: {
|
|
372
|
-
title: "Large review",
|
|
373
|
-
files: [
|
|
374
|
-
{
|
|
375
|
-
path: "src/large.ts",
|
|
376
|
-
additions: 1,
|
|
377
|
-
deletions: 0,
|
|
378
|
-
hunks: [],
|
|
379
|
-
patch: `+${"x".repeat(30_000)}`,
|
|
380
|
-
},
|
|
381
|
-
],
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
const runner: HunkRunner = async () => ({ stdout: huge, stderr: "", code: 0 });
|
|
385
|
-
const result = await capture(runner).execute(
|
|
386
|
-
"t-limit",
|
|
387
|
-
{ ops: [{ action: "review", includePatch: true }], maxCharacters: 20_000 },
|
|
388
|
-
undefined,
|
|
389
|
-
undefined,
|
|
390
|
-
{ cwd: "/repo" },
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
expect(result.content[0]?.text.length).toBeGreaterThan(10_500);
|
|
394
|
-
expect(result.content[0]?.text.length).toBeLessThanOrEqual(20_000);
|
|
395
|
-
expect(result.content[0]?.text).toContain("[truncated; full result in tool details]");
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
test("reports a missing Hunk executable clearly", async () => {
|
|
399
|
-
const runner: HunkRunner = async () => ({
|
|
400
|
-
stdout: "",
|
|
401
|
-
stderr: "spawn hunk ENOENT",
|
|
402
|
-
code: 1,
|
|
403
|
-
});
|
|
404
|
-
const result = await capture(runner).execute(
|
|
405
|
-
"t3",
|
|
406
|
-
{ ops: [{ action: "list" }] },
|
|
407
|
-
undefined,
|
|
408
|
-
undefined,
|
|
409
|
-
{ cwd: "/repo" },
|
|
410
|
-
);
|
|
411
|
-
|
|
412
|
-
expect(result.content[0]?.text).toContain("Hunk CLI not found");
|
|
413
|
-
});
|
package/src/lib.test.ts
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { buildHunkArgs, type HunkOp } from "./lib.ts";
|
|
3
|
-
|
|
4
|
-
describe("buildHunkArgs", () => {
|
|
5
|
-
test("lists sessions as JSON", () => {
|
|
6
|
-
expect(buildHunkArgs({ action: "list" }, "/repo")).toEqual(["session", "list", "--json"]);
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
test("builds documented argv for every review operation", () => {
|
|
10
|
-
const cases: Array<[HunkOp, string[]]> = [
|
|
11
|
-
[{ action: "get" as const }, ["session", "get", "--repo", "/repo", "--json"]],
|
|
12
|
-
[{ action: "context" as const, sessionId: "s1" }, ["session", "context", "s1", "--json"]],
|
|
13
|
-
[
|
|
14
|
-
{ action: "review" as const, includePatch: true },
|
|
15
|
-
["session", "review", "--repo", "/repo", "--include-patch", "--json"],
|
|
16
|
-
],
|
|
17
|
-
[
|
|
18
|
-
{ action: "navigate" as const, file: "src/a.ts", hunk: 2 },
|
|
19
|
-
["session", "navigate", "--repo", "/repo", "--file", "src/a.ts", "--hunk", "2", "--json"],
|
|
20
|
-
],
|
|
21
|
-
[
|
|
22
|
-
{ action: "navigate" as const, nextComment: true },
|
|
23
|
-
["session", "navigate", "--repo", "/repo", "--next-comment", "--json"],
|
|
24
|
-
],
|
|
25
|
-
[
|
|
26
|
-
{
|
|
27
|
-
action: "comment" as const,
|
|
28
|
-
file: "src/a.ts",
|
|
29
|
-
newLine: 42,
|
|
30
|
-
summary: "Fix this",
|
|
31
|
-
rationale: "It breaks",
|
|
32
|
-
focus: true,
|
|
33
|
-
},
|
|
34
|
-
[
|
|
35
|
-
"session",
|
|
36
|
-
"comment",
|
|
37
|
-
"add",
|
|
38
|
-
"--repo",
|
|
39
|
-
"/repo",
|
|
40
|
-
"--file",
|
|
41
|
-
"src/a.ts",
|
|
42
|
-
"--new-line",
|
|
43
|
-
"42",
|
|
44
|
-
"--summary",
|
|
45
|
-
"Fix this",
|
|
46
|
-
"--rationale",
|
|
47
|
-
"It breaks",
|
|
48
|
-
"--focus",
|
|
49
|
-
"--json",
|
|
50
|
-
],
|
|
51
|
-
],
|
|
52
|
-
[
|
|
53
|
-
{ action: "comment_list" as const, file: "src/a.ts", type: "user" as const },
|
|
54
|
-
[
|
|
55
|
-
"session",
|
|
56
|
-
"comment",
|
|
57
|
-
"list",
|
|
58
|
-
"--repo",
|
|
59
|
-
"/repo",
|
|
60
|
-
"--file",
|
|
61
|
-
"src/a.ts",
|
|
62
|
-
"--type",
|
|
63
|
-
"user",
|
|
64
|
-
"--json",
|
|
65
|
-
],
|
|
66
|
-
],
|
|
67
|
-
[
|
|
68
|
-
{ action: "comment_rm" as const, commentId: "c1" },
|
|
69
|
-
["session", "comment", "rm", "--repo", "/repo", "c1", "--json"],
|
|
70
|
-
],
|
|
71
|
-
[
|
|
72
|
-
{
|
|
73
|
-
action: "highlight" as const,
|
|
74
|
-
file: "src/a.ts",
|
|
75
|
-
oldLine: 9,
|
|
76
|
-
start: 1,
|
|
77
|
-
end: 4,
|
|
78
|
-
tone: "warning" as const,
|
|
79
|
-
},
|
|
80
|
-
[
|
|
81
|
-
"session",
|
|
82
|
-
"highlight",
|
|
83
|
-
"add",
|
|
84
|
-
"--repo",
|
|
85
|
-
"/repo",
|
|
86
|
-
"--file",
|
|
87
|
-
"src/a.ts",
|
|
88
|
-
"--old-line",
|
|
89
|
-
"9",
|
|
90
|
-
"--start",
|
|
91
|
-
"1",
|
|
92
|
-
"--end",
|
|
93
|
-
"4",
|
|
94
|
-
"--tone",
|
|
95
|
-
"warning",
|
|
96
|
-
"--json",
|
|
97
|
-
],
|
|
98
|
-
],
|
|
99
|
-
[
|
|
100
|
-
{ action: "highlight_clear" as const, file: "src/a.ts" },
|
|
101
|
-
["session", "highlight", "clear", "--repo", "/repo", "--file", "src/a.ts", "--json"],
|
|
102
|
-
],
|
|
103
|
-
[
|
|
104
|
-
{ action: "reload" as const, reloadArgs: ["diff", "main...HEAD", "--", "src"] },
|
|
105
|
-
[
|
|
106
|
-
"session",
|
|
107
|
-
"reload",
|
|
108
|
-
"--repo",
|
|
109
|
-
"/repo",
|
|
110
|
-
"--json",
|
|
111
|
-
"--",
|
|
112
|
-
"diff",
|
|
113
|
-
"main...HEAD",
|
|
114
|
-
"--",
|
|
115
|
-
"src",
|
|
116
|
-
],
|
|
117
|
-
],
|
|
118
|
-
];
|
|
119
|
-
|
|
120
|
-
for (const [op, expected] of cases) {
|
|
121
|
-
expect(buildHunkArgs(op, "/repo")).toEqual(expected);
|
|
122
|
-
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
test("rejects invalid action fields before execution", () => {
|
|
126
|
-
const invalid = [
|
|
127
|
-
[{ action: "navigate" as const, file: "a.ts" }, "navigation target"],
|
|
128
|
-
[
|
|
129
|
-
{ action: "navigate" as const, nextComment: true, prevComment: true },
|
|
130
|
-
"one comment direction",
|
|
131
|
-
],
|
|
132
|
-
[{ action: "comment" as const, file: "a.ts", newLine: 1 }, "summary"],
|
|
133
|
-
[
|
|
134
|
-
{ action: "highlight" as const, file: "a.ts", newLine: 1, start: 4, end: 4 },
|
|
135
|
-
"greater than start",
|
|
136
|
-
],
|
|
137
|
-
[{ action: "comment_rm" as const }, "commentId"],
|
|
138
|
-
] as const;
|
|
139
|
-
|
|
140
|
-
for (const [op, message] of invalid) {
|
|
141
|
-
expect(() => buildHunkArgs(op, "/repo")).toThrow(message);
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
});
|