revspec 0.2.1 → 0.2.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 +1 -1
- package/src/cli/reply.ts +4 -1
- package/src/cli/watch.ts +6 -0
- package/src/protocol/live-events.ts +2 -1
- package/src/tui/app.ts +13 -3
- package/src/tui/comment-input.ts +5 -82
package/package.json
CHANGED
package/src/cli/reply.ts
CHANGED
|
@@ -42,12 +42,15 @@ export function runReply(
|
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
// Clean up shell escaping artifacts (e.g., \! from bash history expansion)
|
|
46
|
+
const cleanText = text.replace(/\\!/g, "!");
|
|
47
|
+
|
|
45
48
|
// Append reply event
|
|
46
49
|
appendEvent(jsonlPath, {
|
|
47
50
|
type: "reply",
|
|
48
51
|
threadId,
|
|
49
52
|
author: "owner",
|
|
50
|
-
text,
|
|
53
|
+
text: cleanText,
|
|
51
54
|
ts: Date.now(),
|
|
52
55
|
});
|
|
53
56
|
}
|
package/src/cli/watch.ts
CHANGED
|
@@ -193,6 +193,12 @@ function processNewEvents(
|
|
|
193
193
|
return { approved: true, output: "", newOffset };
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
// Check for session-end — TUI exited, break the loop
|
|
197
|
+
const hasSessionEnd = events.some((e) => e.type === "session-end");
|
|
198
|
+
if (hasSessionEnd) {
|
|
199
|
+
return { approved: false, output: "Session ended. Reviewer exited revspec.\n", newOffset };
|
|
200
|
+
}
|
|
201
|
+
|
|
196
202
|
// Only return actionable events — comments and replies that need an LLM response.
|
|
197
203
|
// Resolves, unresolves, and deletes are informational — no reply needed.
|
|
198
204
|
const actionableEvents = events.filter(
|
package/src/tui/app.ts
CHANGED
|
@@ -200,6 +200,8 @@ export async function runTui(
|
|
|
200
200
|
|
|
201
201
|
function mergeAndExit(resolve: () => void): void {
|
|
202
202
|
doMerge();
|
|
203
|
+
// Signal to watch process that session has ended
|
|
204
|
+
appendEvent(jsonlPath, { type: "session-end", author: "reviewer", ts: Date.now() });
|
|
203
205
|
liveWatcher.stop();
|
|
204
206
|
renderer.destroy();
|
|
205
207
|
resolve();
|
|
@@ -249,11 +251,13 @@ export async function runTui(
|
|
|
249
251
|
setTimeout(() => { refreshPager(); }, 2000);
|
|
250
252
|
return "stay";
|
|
251
253
|
}
|
|
254
|
+
appendEvent(jsonlPath, { type: "session-end", author: "reviewer", ts: Date.now() });
|
|
252
255
|
liveWatcher.stop();
|
|
253
256
|
return "exit";
|
|
254
257
|
}
|
|
255
258
|
if (cmd === "q!") {
|
|
256
259
|
// Exit without merging
|
|
260
|
+
appendEvent(jsonlPath, { type: "session-end", author: "reviewer", ts: Date.now() });
|
|
257
261
|
liveWatcher.stop();
|
|
258
262
|
return "exit";
|
|
259
263
|
}
|
|
@@ -263,7 +267,7 @@ export async function runTui(
|
|
|
263
267
|
// --- Overlay launchers ---
|
|
264
268
|
|
|
265
269
|
function showCommentInput(): void {
|
|
266
|
-
|
|
270
|
+
let existingThread = state.threadAtLine(state.cursorLine);
|
|
267
271
|
|
|
268
272
|
const overlay = createCommentInput({
|
|
269
273
|
renderer,
|
|
@@ -278,13 +282,19 @@ export async function runTui(
|
|
|
278
282
|
refreshPager();
|
|
279
283
|
// Don't dismiss — overlay stays open, message appended by comment-input
|
|
280
284
|
} else {
|
|
281
|
-
// New comment —
|
|
285
|
+
// New comment — create thread, stay open
|
|
282
286
|
state.addComment(state.cursorLine, text);
|
|
283
287
|
const newThread = state.threadAtLine(state.cursorLine);
|
|
284
288
|
if (newThread) {
|
|
285
289
|
appendEvent(jsonlPath, { type: "comment", threadId: newThread.id, line: state.cursorLine, author: "reviewer", text, ts: Date.now() });
|
|
290
|
+
// Update overlay to reference the new thread
|
|
291
|
+
if (activeOverlay) {
|
|
292
|
+
activeOverlay.threadId = newThread.id;
|
|
293
|
+
activeOverlay.container.title = ` Thread #${newThread.id} (line ${state.cursorLine}) `;
|
|
294
|
+
}
|
|
295
|
+
existingThread = newThread;
|
|
286
296
|
}
|
|
287
|
-
|
|
297
|
+
refreshPager();
|
|
288
298
|
}
|
|
289
299
|
},
|
|
290
300
|
onResolve: () => {
|
package/src/tui/comment-input.ts
CHANGED
|
@@ -28,89 +28,12 @@ export interface CommentInputOverlay {
|
|
|
28
28
|
|
|
29
29
|
export function createCommentInput(opts: CommentInputOptions): CommentInputOverlay {
|
|
30
30
|
const { renderer, line, existingThread, onSubmit, onResolve, onCancel } = opts;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return createNewComment(renderer, line, onSubmit, onCancel);
|
|
35
|
-
}
|
|
36
|
-
return createThreadView(renderer, line, existingThread!, onSubmit, onResolve, onCancel);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// --- New comment: insert-only buffer, Tab submits and closes ---
|
|
40
|
-
function createNewComment(
|
|
41
|
-
renderer: CliRenderer,
|
|
42
|
-
line: number,
|
|
43
|
-
onSubmit: (text: string) => void,
|
|
44
|
-
onCancel: () => void,
|
|
45
|
-
): CommentInputOverlay {
|
|
46
|
-
const container = new BoxRenderable(renderer, {
|
|
47
|
-
position: "absolute",
|
|
48
|
-
top: "30%",
|
|
49
|
-
left: "10%",
|
|
50
|
-
width: "80%",
|
|
51
|
-
height: 10,
|
|
52
|
-
zIndex: 100,
|
|
53
|
-
backgroundColor: theme.base,
|
|
54
|
-
border: true,
|
|
55
|
-
borderStyle: "single",
|
|
56
|
-
borderColor: theme.borderComment,
|
|
57
|
-
title: ` New comment on line ${line} `,
|
|
58
|
-
flexDirection: "column",
|
|
59
|
-
padding: 1,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
const textarea = new TextareaRenderable(renderer, {
|
|
63
|
-
width: "100%",
|
|
64
|
-
flexGrow: 1,
|
|
65
|
-
backgroundColor: theme.surface0,
|
|
66
|
-
textColor: theme.text,
|
|
67
|
-
focusedBackgroundColor: theme.surface0,
|
|
68
|
-
focusedTextColor: theme.text,
|
|
69
|
-
wrapMode: "word",
|
|
70
|
-
placeholder: "Type your comment...",
|
|
71
|
-
placeholderColor: theme.overlay,
|
|
72
|
-
initialValue: "",
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
const hint = new TextRenderable(renderer, {
|
|
76
|
-
content: " [Tab] submit [Esc] cancel",
|
|
77
|
-
width: "100%",
|
|
78
|
-
height: 1,
|
|
79
|
-
fg: theme.hintFg,
|
|
80
|
-
bg: theme.hintBg,
|
|
81
|
-
wrapMode: "none",
|
|
82
|
-
truncate: true,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
container.add(textarea);
|
|
86
|
-
container.add(hint);
|
|
87
|
-
setTimeout(() => { textarea.focus(); renderer.requestRender(); }, 0);
|
|
88
|
-
|
|
89
|
-
let submitted = false;
|
|
90
|
-
const keyHandler = (key: KeyEvent) => {
|
|
91
|
-
if (key.name === "escape") {
|
|
92
|
-
key.preventDefault(); key.stopPropagation(); onCancel(); return;
|
|
93
|
-
}
|
|
94
|
-
if (key.name === "tab") {
|
|
95
|
-
key.preventDefault(); key.stopPropagation();
|
|
96
|
-
if (submitted) return;
|
|
97
|
-
submitted = true;
|
|
98
|
-
const text = textarea.plainText.trim();
|
|
99
|
-
if (text.length > 0) onSubmit(text); else onCancel();
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
renderer.keyInput.on("keypress", keyHandler);
|
|
104
|
-
|
|
105
|
-
return {
|
|
106
|
-
container,
|
|
107
|
-
cleanup() { renderer.keyInput.off("keypress", keyHandler); textarea.destroy(); },
|
|
108
|
-
addMessage() {},
|
|
109
|
-
threadId: null,
|
|
110
|
-
};
|
|
31
|
+
// Always use thread view — even for new comments (empty history, just the input)
|
|
32
|
+
const thread = existingThread ?? { id: "", line, status: "open" as const, messages: [] };
|
|
33
|
+
return createThreadView(renderer, line, thread, onSubmit, onResolve, onCancel);
|
|
111
34
|
}
|
|
112
35
|
|
|
113
|
-
// ---
|
|
36
|
+
// --- Unified thread view: works for both new comments and existing threads ---
|
|
114
37
|
function createThreadView(
|
|
115
38
|
renderer: CliRenderer,
|
|
116
39
|
line: number,
|
|
@@ -130,7 +53,7 @@ function createThreadView(
|
|
|
130
53
|
border: true,
|
|
131
54
|
borderStyle: "single",
|
|
132
55
|
borderColor: theme.borderComment,
|
|
133
|
-
title: ` Thread #${thread.id} (line ${line}) `,
|
|
56
|
+
title: thread.id ? ` Thread #${thread.id} (line ${line}) ` : ` New comment on line ${line} `,
|
|
134
57
|
flexDirection: "column",
|
|
135
58
|
padding: 1,
|
|
136
59
|
});
|