@proletariat/cli 0.3.24 → 0.3.26
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/dist/commands/action/create.js +3 -3
- package/dist/commands/action/index.js +2 -2
- package/dist/commands/action/update.js +3 -3
- package/dist/commands/agent/auth.js +1 -1
- package/dist/commands/agent/cleanup.js +6 -6
- package/dist/commands/agent/discover.js +1 -1
- package/dist/commands/agent/remove.js +4 -4
- package/dist/commands/autocomplete/setup.d.ts +2 -2
- package/dist/commands/autocomplete/setup.js +5 -5
- package/dist/commands/branch/create.js +31 -30
- package/dist/commands/category/create.js +4 -5
- package/dist/commands/category/delete.js +2 -3
- package/dist/commands/category/rename.js +2 -3
- package/dist/commands/claude.d.ts +2 -8
- package/dist/commands/claude.js +26 -26
- package/dist/commands/commit.d.ts +2 -8
- package/dist/commands/commit.js +4 -26
- package/dist/commands/config/index.d.ts +2 -10
- package/dist/commands/config/index.js +8 -34
- package/dist/commands/docker/index.d.ts +2 -2
- package/dist/commands/docker/index.js +8 -8
- package/dist/commands/epic/activate.js +9 -17
- package/dist/commands/epic/archive.js +13 -24
- package/dist/commands/epic/create.js +7 -6
- package/dist/commands/epic/delete.js +4 -5
- package/dist/commands/epic/move.js +28 -47
- package/dist/commands/epic/progress.js +10 -14
- package/dist/commands/epic/project.js +42 -59
- package/dist/commands/epic/reorder.js +25 -30
- package/dist/commands/epic/spec.d.ts +1 -0
- package/dist/commands/epic/spec.js +39 -40
- package/dist/commands/epic/ticket.d.ts +2 -0
- package/dist/commands/epic/ticket.js +63 -37
- package/dist/commands/feedback/index.d.ts +10 -0
- package/dist/commands/feedback/index.js +60 -0
- package/dist/commands/feedback/list.d.ts +12 -0
- package/dist/commands/feedback/list.js +126 -0
- package/dist/commands/feedback/submit.d.ts +16 -0
- package/dist/commands/feedback/submit.js +220 -0
- package/dist/commands/feedback/view.d.ts +15 -0
- package/dist/commands/feedback/view.js +109 -0
- package/dist/commands/gh/index.js +4 -0
- package/dist/commands/link/index.js +2 -2
- package/dist/commands/pmo/init.d.ts +2 -2
- package/dist/commands/pmo/init.js +7 -7
- package/dist/commands/project/spec.js +6 -6
- package/dist/commands/repo/create.d.ts +38 -0
- package/dist/commands/repo/create.js +283 -0
- package/dist/commands/repo/index.js +7 -0
- package/dist/commands/roadmap/add-project.js +9 -22
- package/dist/commands/roadmap/create.d.ts +0 -1
- package/dist/commands/roadmap/create.js +46 -40
- package/dist/commands/roadmap/delete.js +10 -24
- package/dist/commands/roadmap/generate.d.ts +1 -0
- package/dist/commands/roadmap/generate.js +21 -22
- package/dist/commands/roadmap/remove-project.js +14 -34
- package/dist/commands/roadmap/reorder.js +19 -26
- package/dist/commands/roadmap/update.js +27 -26
- package/dist/commands/roadmap/view.js +5 -12
- package/dist/commands/session/attach.d.ts +1 -8
- package/dist/commands/session/attach.js +93 -59
- package/dist/commands/session/health.d.ts +29 -0
- package/dist/commands/session/health.js +495 -0
- package/dist/commands/session/index.js +4 -0
- package/dist/commands/session/list.d.ts +0 -8
- package/dist/commands/session/list.js +130 -81
- package/dist/commands/spec/create.js +1 -1
- package/dist/commands/spec/edit.js +64 -35
- package/dist/commands/staff/add.d.ts +2 -2
- package/dist/commands/staff/add.js +15 -14
- package/dist/commands/staff/index.js +2 -2
- package/dist/commands/staff/remove.js +4 -4
- package/dist/commands/status/index.js +6 -7
- package/dist/commands/support/book.d.ts +10 -0
- package/dist/commands/support/book.js +54 -0
- package/dist/commands/support/discord.d.ts +10 -0
- package/dist/commands/support/discord.js +54 -0
- package/dist/commands/support/docs.d.ts +10 -0
- package/dist/commands/support/docs.js +54 -0
- package/dist/commands/support/index.d.ts +19 -0
- package/dist/commands/support/index.js +81 -0
- package/dist/commands/support/issues.d.ts +11 -0
- package/dist/commands/support/issues.js +77 -0
- package/dist/commands/support/logs.d.ts +18 -0
- package/dist/commands/support/logs.js +247 -0
- package/dist/commands/template/apply.js +10 -11
- package/dist/commands/template/create.js +18 -17
- package/dist/commands/template/index.d.ts +2 -2
- package/dist/commands/template/index.js +6 -6
- package/dist/commands/template/save.js +8 -7
- package/dist/commands/template/update.js +6 -7
- package/dist/commands/terminal/title.d.ts +2 -26
- package/dist/commands/terminal/title.js +4 -33
- package/dist/commands/theme/index.d.ts +2 -2
- package/dist/commands/theme/index.js +19 -18
- package/dist/commands/theme/set.d.ts +2 -2
- package/dist/commands/theme/set.js +5 -5
- package/dist/commands/ticket/create.js +52 -26
- package/dist/commands/ticket/delete.js +15 -13
- package/dist/commands/ticket/edit.js +59 -20
- package/dist/commands/ticket/epic.js +12 -10
- package/dist/commands/ticket/move.d.ts +7 -0
- package/dist/commands/ticket/move.js +132 -0
- package/dist/commands/ticket/project.js +11 -9
- package/dist/commands/ticket/reassign.js +23 -19
- package/dist/commands/ticket/spec.js +7 -5
- package/dist/commands/ticket/update.js +55 -53
- package/dist/commands/whoami.js +1 -0
- package/dist/commands/work/ready.js +7 -7
- package/dist/commands/work/revise.js +13 -11
- package/dist/commands/work/spawn.d.ts +1 -0
- package/dist/commands/work/spawn.js +225 -64
- package/dist/commands/work/start.d.ts +1 -0
- package/dist/commands/work/start.js +301 -173
- package/dist/hooks/init.js +4 -0
- package/dist/lib/execution/runners.js +21 -17
- package/dist/lib/execution/session-utils.d.ts +60 -0
- package/dist/lib/execution/session-utils.js +162 -0
- package/dist/lib/execution/spawner.d.ts +2 -0
- package/dist/lib/execution/spawner.js +42 -0
- package/dist/lib/flags/resolver.d.ts +2 -2
- package/dist/lib/flags/resolver.js +15 -0
- package/dist/lib/init/index.js +18 -0
- package/dist/lib/multiline-input.d.ts +63 -0
- package/dist/lib/multiline-input.js +360 -0
- package/dist/lib/pr/index.d.ts +4 -0
- package/dist/lib/pr/index.js +32 -14
- package/dist/lib/prompt-command.d.ts +3 -0
- package/dist/lib/prompt-json.d.ts +77 -6
- package/dist/lib/prompt-json.js +46 -0
- package/dist/lib/repos/git.d.ts +7 -0
- package/dist/lib/repos/git.js +20 -0
- package/oclif.manifest.json +2913 -2246
- package/package.json +1 -1
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-line text input utility for CLI.
|
|
3
|
+
*
|
|
4
|
+
* Provides inline TTY text input without opening external editors.
|
|
5
|
+
* Handles paste safely, supports cursor navigation, and provides
|
|
6
|
+
* clear visual feedback.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const text = await multiLineInput({
|
|
11
|
+
* message: 'Enter description:',
|
|
12
|
+
* default: 'Existing content...',
|
|
13
|
+
* });
|
|
14
|
+
* ```
|
|
15
|
+
*/
|
|
16
|
+
import * as readline from 'readline';
|
|
17
|
+
import chalk from 'chalk';
|
|
18
|
+
// ANSI escape codes for terminal control
|
|
19
|
+
const ESC = '\x1b';
|
|
20
|
+
const CSI = `${ESC}[`;
|
|
21
|
+
// Control characters
|
|
22
|
+
const CTRL_C = '\x03';
|
|
23
|
+
const CTRL_D = '\x04';
|
|
24
|
+
const BACKSPACE = '\x7f';
|
|
25
|
+
const DELETE = '\x1b[3~';
|
|
26
|
+
const ENTER = '\r';
|
|
27
|
+
const NEWLINE = '\n';
|
|
28
|
+
// Arrow keys (CSI sequences)
|
|
29
|
+
const ARROW_UP = `${CSI}A`;
|
|
30
|
+
const ARROW_DOWN = `${CSI}B`;
|
|
31
|
+
const ARROW_RIGHT = `${CSI}C`;
|
|
32
|
+
const ARROW_LEFT = `${CSI}D`;
|
|
33
|
+
const HOME = `${CSI}H`;
|
|
34
|
+
const END = `${CSI}F`;
|
|
35
|
+
/**
|
|
36
|
+
* Clear the current line and move cursor to beginning
|
|
37
|
+
*/
|
|
38
|
+
function clearLine() {
|
|
39
|
+
process.stdout.write(`${CSI}2K${CSI}G`);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Move cursor up N lines
|
|
43
|
+
*/
|
|
44
|
+
function moveUp(n) {
|
|
45
|
+
if (n > 0) {
|
|
46
|
+
process.stdout.write(`${CSI}${n}A`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Move cursor down N lines
|
|
51
|
+
*/
|
|
52
|
+
function moveDown(n) {
|
|
53
|
+
if (n > 0) {
|
|
54
|
+
process.stdout.write(`${CSI}${n}B`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Move cursor to specific column
|
|
59
|
+
*/
|
|
60
|
+
function moveToColumn(col) {
|
|
61
|
+
process.stdout.write(`${CSI}${col + 1}G`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Clear from cursor to end of screen
|
|
65
|
+
*/
|
|
66
|
+
function clearToEnd() {
|
|
67
|
+
process.stdout.write(`${CSI}J`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Hide cursor
|
|
71
|
+
*/
|
|
72
|
+
function hideCursor() {
|
|
73
|
+
process.stdout.write(`${CSI}?25l`);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Show cursor
|
|
77
|
+
*/
|
|
78
|
+
function showCursor() {
|
|
79
|
+
process.stdout.write(`${CSI}?25h`);
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Collect multi-line input from the user with an inline TTY editor.
|
|
83
|
+
*
|
|
84
|
+
* Features:
|
|
85
|
+
* - Arrow key navigation
|
|
86
|
+
* - Backspace/delete
|
|
87
|
+
* - Copy-paste handling (escapes special characters)
|
|
88
|
+
* - Ctrl+D to finish, Ctrl+C to cancel
|
|
89
|
+
* - Pre-populated content support
|
|
90
|
+
* - Real-time visual feedback
|
|
91
|
+
*
|
|
92
|
+
* @param options Input options
|
|
93
|
+
* @returns The entered text and cancellation status
|
|
94
|
+
*/
|
|
95
|
+
export async function multiLineInput(options) {
|
|
96
|
+
const { message, default: defaultValue = '', hint = 'Ctrl+D to finish, Ctrl+C to cancel', required = false, validate, } = options;
|
|
97
|
+
// If not a TTY, return the default value
|
|
98
|
+
if (!process.stdin.isTTY) {
|
|
99
|
+
return { value: defaultValue, cancelled: false };
|
|
100
|
+
}
|
|
101
|
+
return new Promise((resolve) => {
|
|
102
|
+
// Initialize state
|
|
103
|
+
const lines = defaultValue.split('\n');
|
|
104
|
+
let cursorLine = lines.length - 1;
|
|
105
|
+
let cursorCol = lines[cursorLine].length;
|
|
106
|
+
let renderedLineCount = 0;
|
|
107
|
+
let inputBuffer = ''; // Buffer for multi-byte sequences
|
|
108
|
+
// Set up raw mode
|
|
109
|
+
const wasRaw = process.stdin.isRaw;
|
|
110
|
+
readline.emitKeypressEvents(process.stdin);
|
|
111
|
+
process.stdin.setRawMode(true);
|
|
112
|
+
process.stdin.resume();
|
|
113
|
+
// Render the input area
|
|
114
|
+
function render() {
|
|
115
|
+
// Move up to the start of our rendered area
|
|
116
|
+
if (renderedLineCount > 0) {
|
|
117
|
+
moveUp(renderedLineCount);
|
|
118
|
+
}
|
|
119
|
+
clearLine();
|
|
120
|
+
// Print message
|
|
121
|
+
process.stdout.write(chalk.cyan(message) + '\n');
|
|
122
|
+
// Print hint
|
|
123
|
+
process.stdout.write(chalk.dim(hint) + '\n');
|
|
124
|
+
// Print border (guard against very small terminal widths)
|
|
125
|
+
const termWidth = process.stdout.columns || 80;
|
|
126
|
+
const borderWidth = Math.max(1, Math.min(termWidth - 4, 76));
|
|
127
|
+
process.stdout.write(chalk.dim('┌' + '─'.repeat(borderWidth) + '┐') + '\n');
|
|
128
|
+
// Print lines with line numbers
|
|
129
|
+
const displayLines = lines.length > 0 ? lines : [''];
|
|
130
|
+
for (let i = 0; i < displayLines.length; i++) {
|
|
131
|
+
clearLine();
|
|
132
|
+
const lineNum = chalk.dim(`${String(i + 1).padStart(2)} │`);
|
|
133
|
+
const lineContent = displayLines[i];
|
|
134
|
+
process.stdout.write(`${lineNum} ${lineContent}\n`);
|
|
135
|
+
}
|
|
136
|
+
// Print bottom border
|
|
137
|
+
process.stdout.write(chalk.dim('└' + '─'.repeat(borderWidth) + '┘') + '\n');
|
|
138
|
+
// Calculate total rendered lines: message + hint + top border + content lines + bottom border
|
|
139
|
+
renderedLineCount = 1 + 1 + 1 + displayLines.length + 1;
|
|
140
|
+
// Position cursor
|
|
141
|
+
// Move up from current position to the correct line
|
|
142
|
+
const linesFromBottom = displayLines.length - cursorLine;
|
|
143
|
+
moveUp(linesFromBottom); // Go to the correct content line (accounting for bottom border)
|
|
144
|
+
// Move to correct column (line number takes 5 chars: "NN │ ")
|
|
145
|
+
moveToColumn(5 + cursorCol);
|
|
146
|
+
}
|
|
147
|
+
// Handle cleanup
|
|
148
|
+
function cleanup() {
|
|
149
|
+
process.stdin.setRawMode(wasRaw || false);
|
|
150
|
+
process.stdin.pause();
|
|
151
|
+
showCursor();
|
|
152
|
+
// Move to end of rendered area
|
|
153
|
+
const displayLines = lines.length > 0 ? lines : [''];
|
|
154
|
+
const linesFromBottom = displayLines.length - cursorLine;
|
|
155
|
+
moveDown(linesFromBottom + 1); // +1 for bottom border
|
|
156
|
+
clearLine();
|
|
157
|
+
}
|
|
158
|
+
// Handle input
|
|
159
|
+
function handleInput(chunk) {
|
|
160
|
+
const str = chunk.toString('utf8');
|
|
161
|
+
// Append to buffer for handling multi-byte sequences
|
|
162
|
+
inputBuffer += str;
|
|
163
|
+
// Process buffer character by character
|
|
164
|
+
while (inputBuffer.length > 0) {
|
|
165
|
+
// Check for escape sequences
|
|
166
|
+
if (inputBuffer.startsWith(ESC)) {
|
|
167
|
+
// Wait for more data if sequence might be incomplete
|
|
168
|
+
if (inputBuffer.length < 3 && inputBuffer !== ESC) {
|
|
169
|
+
// Could be start of a sequence, wait for more
|
|
170
|
+
if (inputBuffer.length === 1) {
|
|
171
|
+
// Just ESC by itself, treat as cancel after timeout
|
|
172
|
+
setTimeout(() => {
|
|
173
|
+
if (inputBuffer === ESC) {
|
|
174
|
+
inputBuffer = '';
|
|
175
|
+
// ESC key pressed - ignore
|
|
176
|
+
}
|
|
177
|
+
}, 50);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
// Handle arrow keys and other sequences
|
|
183
|
+
if (inputBuffer.startsWith(ARROW_UP)) {
|
|
184
|
+
inputBuffer = inputBuffer.slice(3);
|
|
185
|
+
if (cursorLine > 0) {
|
|
186
|
+
cursorLine--;
|
|
187
|
+
cursorCol = Math.min(cursorCol, lines[cursorLine].length);
|
|
188
|
+
}
|
|
189
|
+
render();
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
if (inputBuffer.startsWith(ARROW_DOWN)) {
|
|
193
|
+
inputBuffer = inputBuffer.slice(3);
|
|
194
|
+
if (cursorLine < lines.length - 1) {
|
|
195
|
+
cursorLine++;
|
|
196
|
+
cursorCol = Math.min(cursorCol, lines[cursorLine].length);
|
|
197
|
+
}
|
|
198
|
+
render();
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (inputBuffer.startsWith(ARROW_LEFT)) {
|
|
202
|
+
inputBuffer = inputBuffer.slice(3);
|
|
203
|
+
if (cursorCol > 0) {
|
|
204
|
+
cursorCol--;
|
|
205
|
+
}
|
|
206
|
+
else if (cursorLine > 0) {
|
|
207
|
+
cursorLine--;
|
|
208
|
+
cursorCol = lines[cursorLine].length;
|
|
209
|
+
}
|
|
210
|
+
render();
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (inputBuffer.startsWith(ARROW_RIGHT)) {
|
|
214
|
+
inputBuffer = inputBuffer.slice(3);
|
|
215
|
+
if (cursorCol < lines[cursorLine].length) {
|
|
216
|
+
cursorCol++;
|
|
217
|
+
}
|
|
218
|
+
else if (cursorLine < lines.length - 1) {
|
|
219
|
+
cursorLine++;
|
|
220
|
+
cursorCol = 0;
|
|
221
|
+
}
|
|
222
|
+
render();
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (inputBuffer.startsWith(HOME)) {
|
|
226
|
+
inputBuffer = inputBuffer.slice(3);
|
|
227
|
+
cursorCol = 0;
|
|
228
|
+
render();
|
|
229
|
+
continue;
|
|
230
|
+
}
|
|
231
|
+
if (inputBuffer.startsWith(END)) {
|
|
232
|
+
inputBuffer = inputBuffer.slice(3);
|
|
233
|
+
cursorCol = lines[cursorLine].length;
|
|
234
|
+
render();
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
if (inputBuffer.startsWith(DELETE)) {
|
|
238
|
+
inputBuffer = inputBuffer.slice(4);
|
|
239
|
+
if (cursorCol < lines[cursorLine].length) {
|
|
240
|
+
lines[cursorLine] = lines[cursorLine].slice(0, cursorCol) + lines[cursorLine].slice(cursorCol + 1);
|
|
241
|
+
}
|
|
242
|
+
else if (cursorLine < lines.length - 1) {
|
|
243
|
+
// Join with next line
|
|
244
|
+
lines[cursorLine] += lines[cursorLine + 1];
|
|
245
|
+
lines.splice(cursorLine + 1, 1);
|
|
246
|
+
}
|
|
247
|
+
render();
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
// Unknown escape sequence - skip ESC and continue
|
|
251
|
+
inputBuffer = inputBuffer.slice(1);
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
// Handle control characters
|
|
255
|
+
const char = inputBuffer[0];
|
|
256
|
+
inputBuffer = inputBuffer.slice(1);
|
|
257
|
+
if (char === CTRL_C) {
|
|
258
|
+
cleanup();
|
|
259
|
+
resolve({ value: '', cancelled: true });
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (char === CTRL_D) {
|
|
263
|
+
const text = lines.join('\n').trim();
|
|
264
|
+
// Validate if required
|
|
265
|
+
if (required && text.length === 0) {
|
|
266
|
+
// Show error and continue - must restore raw mode for input to work
|
|
267
|
+
process.stdout.write(chalk.red('Input is required. Please enter some text.') + '\n');
|
|
268
|
+
renderedLineCount = 0;
|
|
269
|
+
render();
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
if (validate) {
|
|
273
|
+
const result = validate(text);
|
|
274
|
+
if (result !== true) {
|
|
275
|
+
// Show error and continue - keep raw mode active
|
|
276
|
+
process.stdout.write(chalk.red(typeof result === 'string' ? result : 'Invalid input') + '\n');
|
|
277
|
+
renderedLineCount = 0;
|
|
278
|
+
render();
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
cleanup();
|
|
283
|
+
resolve({ value: text, cancelled: false });
|
|
284
|
+
return;
|
|
285
|
+
}
|
|
286
|
+
if (char === BACKSPACE || char === '\b') {
|
|
287
|
+
if (cursorCol > 0) {
|
|
288
|
+
lines[cursorLine] = lines[cursorLine].slice(0, cursorCol - 1) + lines[cursorLine].slice(cursorCol);
|
|
289
|
+
cursorCol--;
|
|
290
|
+
}
|
|
291
|
+
else if (cursorLine > 0) {
|
|
292
|
+
// Join with previous line
|
|
293
|
+
cursorCol = lines[cursorLine - 1].length;
|
|
294
|
+
lines[cursorLine - 1] += lines[cursorLine];
|
|
295
|
+
lines.splice(cursorLine, 1);
|
|
296
|
+
cursorLine--;
|
|
297
|
+
}
|
|
298
|
+
render();
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
if (char === ENTER || char === NEWLINE) {
|
|
302
|
+
// Split line at cursor
|
|
303
|
+
const before = lines[cursorLine].slice(0, cursorCol);
|
|
304
|
+
const after = lines[cursorLine].slice(cursorCol);
|
|
305
|
+
lines[cursorLine] = before;
|
|
306
|
+
lines.splice(cursorLine + 1, 0, after);
|
|
307
|
+
cursorLine++;
|
|
308
|
+
cursorCol = 0;
|
|
309
|
+
render();
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
// Handle regular characters (including paste)
|
|
313
|
+
// Filter out non-printable characters except tab
|
|
314
|
+
if (char === '\t' || (char >= ' ' && char <= '~') || char.charCodeAt(0) > 127) {
|
|
315
|
+
// Insert character at cursor position
|
|
316
|
+
lines[cursorLine] = lines[cursorLine].slice(0, cursorCol) + char + lines[cursorLine].slice(cursorCol);
|
|
317
|
+
cursorCol++;
|
|
318
|
+
render();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Initial render
|
|
323
|
+
render();
|
|
324
|
+
// Listen for input
|
|
325
|
+
process.stdin.on('data', handleInput);
|
|
326
|
+
// Cleanup listener on resolve
|
|
327
|
+
const originalResolve = resolve;
|
|
328
|
+
resolve = (result) => {
|
|
329
|
+
process.stdin.removeListener('data', handleInput);
|
|
330
|
+
originalResolve(result);
|
|
331
|
+
};
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Convenience wrapper that returns just the value string.
|
|
336
|
+
* Throws if cancelled.
|
|
337
|
+
*/
|
|
338
|
+
export async function promptMultiLine(options) {
|
|
339
|
+
const result = await multiLineInput(options);
|
|
340
|
+
if (result.cancelled) {
|
|
341
|
+
throw new Error('Input cancelled');
|
|
342
|
+
}
|
|
343
|
+
return result.value;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Integration with FlagResolver - creates a prompt-compatible function
|
|
347
|
+
*/
|
|
348
|
+
export function createMultiLinePrompt(message, defaultValue, hint) {
|
|
349
|
+
return async () => {
|
|
350
|
+
const result = await multiLineInput({
|
|
351
|
+
message,
|
|
352
|
+
default: defaultValue,
|
|
353
|
+
hint,
|
|
354
|
+
});
|
|
355
|
+
if (result.cancelled) {
|
|
356
|
+
throw new Error('Input cancelled');
|
|
357
|
+
}
|
|
358
|
+
return result.value;
|
|
359
|
+
};
|
|
360
|
+
}
|
package/dist/lib/pr/index.d.ts
CHANGED
|
@@ -51,6 +51,8 @@ export declare function isGHTokenInEnv(): boolean;
|
|
|
51
51
|
export declare function getGitHubRepo(cwd?: string): string | null;
|
|
52
52
|
/**
|
|
53
53
|
* Get the default base branch (main or master).
|
|
54
|
+
* Prefers origin/main over local main to avoid stale local branches
|
|
55
|
+
* in agent worktrees where local main is never updated.
|
|
54
56
|
*/
|
|
55
57
|
export declare function getDefaultBaseBranch(cwd?: string): string;
|
|
56
58
|
/**
|
|
@@ -71,6 +73,8 @@ export declare function pushBranch(branch: string, cwd?: string): boolean;
|
|
|
71
73
|
export declare function hasUnpushedCommits(branch: string, cwd?: string): boolean;
|
|
72
74
|
/**
|
|
73
75
|
* Get the commit log between base and head.
|
|
76
|
+
* Prefers origin/${base} over local ${base} to avoid stale local branches
|
|
77
|
+
* in agent worktrees where local main/master is never updated.
|
|
74
78
|
*/
|
|
75
79
|
export declare function getCommitLog(base: string, cwd?: string): string[];
|
|
76
80
|
/**
|
package/dist/lib/pr/index.js
CHANGED
|
@@ -85,29 +85,31 @@ export function getGitHubRepo(cwd) {
|
|
|
85
85
|
}
|
|
86
86
|
/**
|
|
87
87
|
* Get the default base branch (main or master).
|
|
88
|
+
* Prefers origin/main over local main to avoid stale local branches
|
|
89
|
+
* in agent worktrees where local main is never updated.
|
|
88
90
|
*/
|
|
89
91
|
export function getDefaultBaseBranch(cwd) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
// Fall back to 'master'
|
|
92
|
+
// Check origin/main first (most reliable in worktree environments)
|
|
93
|
+
// then local main, then origin/master, then local master
|
|
94
|
+
const candidates = [
|
|
95
|
+
{ ref: 'origin/main', name: 'main' },
|
|
96
|
+
{ ref: 'main', name: 'main' },
|
|
97
|
+
{ ref: 'origin/master', name: 'master' },
|
|
98
|
+
{ ref: 'master', name: 'master' },
|
|
99
|
+
];
|
|
100
|
+
for (const { ref, name } of candidates) {
|
|
100
101
|
try {
|
|
101
|
-
execSync(
|
|
102
|
+
execSync(`git rev-parse --verify ${ref}`, {
|
|
102
103
|
cwd,
|
|
103
104
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
104
105
|
});
|
|
105
|
-
return
|
|
106
|
+
return name;
|
|
106
107
|
}
|
|
107
108
|
catch {
|
|
108
|
-
|
|
109
|
+
// Try next candidate
|
|
109
110
|
}
|
|
110
111
|
}
|
|
112
|
+
return 'main'; // Default to main even if not found
|
|
111
113
|
}
|
|
112
114
|
/**
|
|
113
115
|
* Get the current branch name.
|
|
@@ -173,10 +175,26 @@ export function hasUnpushedCommits(branch, cwd) {
|
|
|
173
175
|
}
|
|
174
176
|
/**
|
|
175
177
|
* Get the commit log between base and head.
|
|
178
|
+
* Prefers origin/${base} over local ${base} to avoid stale local branches
|
|
179
|
+
* in agent worktrees where local main/master is never updated.
|
|
176
180
|
*/
|
|
177
181
|
export function getCommitLog(base, cwd) {
|
|
182
|
+
// Prefer origin/${base} for accurate comparison (local branch may be stale)
|
|
183
|
+
let ref = base;
|
|
184
|
+
if (!base.startsWith('origin/')) {
|
|
185
|
+
try {
|
|
186
|
+
execSync(`git rev-parse --verify origin/${base}`, {
|
|
187
|
+
cwd,
|
|
188
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
189
|
+
});
|
|
190
|
+
ref = `origin/${base}`;
|
|
191
|
+
}
|
|
192
|
+
catch {
|
|
193
|
+
// Fall back to local ref
|
|
194
|
+
}
|
|
195
|
+
}
|
|
178
196
|
try {
|
|
179
|
-
const output = execSync(`git log ${
|
|
197
|
+
const output = execSync(`git log ${ref}..HEAD --oneline`, {
|
|
180
198
|
cwd,
|
|
181
199
|
encoding: 'utf-8',
|
|
182
200
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
@@ -83,6 +83,9 @@ export declare abstract class PromptCommand extends Command {
|
|
|
83
83
|
default?: unknown;
|
|
84
84
|
validate?: (input: unknown) => boolean | string;
|
|
85
85
|
when?: boolean | ((answers: Record<string, unknown>) => boolean);
|
|
86
|
+
filter?: (input: unknown) => unknown;
|
|
87
|
+
pageSize?: number;
|
|
88
|
+
[key: string]: unknown;
|
|
86
89
|
}>, jsonModeConfig?: {
|
|
87
90
|
flags: JsonFlags & Record<string, unknown>;
|
|
88
91
|
commandName: string;
|
|
@@ -46,8 +46,8 @@ export interface PromptChoice {
|
|
|
46
46
|
* Form field configuration for multi-field prompts
|
|
47
47
|
*/
|
|
48
48
|
export interface FormField {
|
|
49
|
-
/** Type of field: input, list, checkbox, confirm, editor */
|
|
50
|
-
type: 'input' | 'list' | 'checkbox' | 'confirm' | 'editor';
|
|
49
|
+
/** Type of field: input, list, checkbox, confirm, editor, multiline */
|
|
50
|
+
type: 'input' | 'list' | 'checkbox' | 'confirm' | 'editor' | 'multiline';
|
|
51
51
|
/** Field name */
|
|
52
52
|
name: string;
|
|
53
53
|
/** User-facing message */
|
|
@@ -61,8 +61,8 @@ export interface FormField {
|
|
|
61
61
|
* Prompt configuration for JSON output
|
|
62
62
|
*/
|
|
63
63
|
export interface PromptConfig {
|
|
64
|
-
/** Type of prompt: list (single select), checkbox (multi select), confirm, input, editor, or form (multi-field) */
|
|
65
|
-
type: 'list' | 'checkbox' | 'confirm' | 'input' | 'editor' | 'form';
|
|
64
|
+
/** Type of prompt: list (single select), checkbox (multi select), confirm, input, editor, multiline (inline multi-line), or form (multi-field) */
|
|
65
|
+
type: 'list' | 'checkbox' | 'confirm' | 'input' | 'editor' | 'multiline' | 'form';
|
|
66
66
|
/** Field name for the prompt answer (not used for form type) */
|
|
67
67
|
name?: string;
|
|
68
68
|
/** User-facing prompt message (not used for form type) */
|
|
@@ -150,10 +150,49 @@ export interface DryRunJsonOutput {
|
|
|
150
150
|
/** Command metadata */
|
|
151
151
|
metadata: OutputMetadata;
|
|
152
152
|
}
|
|
153
|
+
/**
|
|
154
|
+
* JSON output for confirmation needed (two-step execute protocol)
|
|
155
|
+
* Used when all required flags are provided but --yes is not set.
|
|
156
|
+
* Agent should review the plan and re-run with --yes to execute.
|
|
157
|
+
*/
|
|
158
|
+
export interface ConfirmationNeededJsonOutput {
|
|
159
|
+
/** Output type discriminator */
|
|
160
|
+
type: 'confirmation_needed';
|
|
161
|
+
/** Plan of what will happen if confirmed */
|
|
162
|
+
plan: Record<string, unknown>;
|
|
163
|
+
/** The full command to run to confirm and execute */
|
|
164
|
+
confirm_command: string;
|
|
165
|
+
/** Human-readable message */
|
|
166
|
+
message: string;
|
|
167
|
+
/** Command metadata */
|
|
168
|
+
metadata: OutputMetadata;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* JSON output for execution result (after successful spawn/start)
|
|
172
|
+
*/
|
|
173
|
+
export interface ExecutionResultJsonOutput {
|
|
174
|
+
/** Output type discriminator */
|
|
175
|
+
type: 'execution_result';
|
|
176
|
+
/** Execution results */
|
|
177
|
+
result: {
|
|
178
|
+
executions: Array<{
|
|
179
|
+
workId: string;
|
|
180
|
+
ticketId: string;
|
|
181
|
+
agent: string;
|
|
182
|
+
sessionId?: string;
|
|
183
|
+
containerId?: string;
|
|
184
|
+
status: string;
|
|
185
|
+
}>;
|
|
186
|
+
successCount: number;
|
|
187
|
+
failCount: number;
|
|
188
|
+
};
|
|
189
|
+
/** Command metadata */
|
|
190
|
+
metadata: OutputMetadata;
|
|
191
|
+
}
|
|
153
192
|
/**
|
|
154
193
|
* Union type for all JSON output types
|
|
155
194
|
*/
|
|
156
|
-
export type JsonOutput = PromptJsonOutput | SuccessJsonOutput | ErrorJsonOutput | DryRunJsonOutput;
|
|
195
|
+
export type JsonOutput = PromptJsonOutput | SuccessJsonOutput | ErrorJsonOutput | DryRunJsonOutput | ConfirmationNeededJsonOutput | ExecutionResultJsonOutput;
|
|
157
196
|
/**
|
|
158
197
|
* Flags interface for JSON mode detection
|
|
159
198
|
*/
|
|
@@ -267,7 +306,7 @@ export declare function normalizeChoices(choices: Array<string | {
|
|
|
267
306
|
* @param defaultValue - Optional default value
|
|
268
307
|
* @returns PromptConfig object
|
|
269
308
|
*/
|
|
270
|
-
export declare function buildPromptConfig(type: 'list' | 'checkbox' | 'confirm' | 'input' | 'editor', name: string, message: string, choices?: Array<string | {
|
|
309
|
+
export declare function buildPromptConfig(type: 'list' | 'checkbox' | 'confirm' | 'input' | 'editor' | 'multiline', name: string, message: string, choices?: Array<string | {
|
|
271
310
|
name: string;
|
|
272
311
|
value: string;
|
|
273
312
|
disabled?: boolean | string;
|
|
@@ -304,3 +343,35 @@ export declare function outputDryRunErrorsAsJson(errors: Array<{
|
|
|
304
343
|
field: string;
|
|
305
344
|
error: string;
|
|
306
345
|
}>, metadata: OutputMetadata): never;
|
|
346
|
+
/**
|
|
347
|
+
* Output a confirmation needed response as JSON and exit
|
|
348
|
+
*
|
|
349
|
+
* Use this in non-TTY mode when all required flags are provided but --yes is not set.
|
|
350
|
+
* This allows agents to preview what will happen before confirming execution.
|
|
351
|
+
*
|
|
352
|
+
* @param plan - Details of what will happen if confirmed
|
|
353
|
+
* @param confirmCommand - The full command to run with --yes to execute
|
|
354
|
+
* @param message - Human-readable message explaining the confirmation
|
|
355
|
+
* @param metadata - Command metadata
|
|
356
|
+
*/
|
|
357
|
+
export declare function outputConfirmationNeededAsJson(plan: Record<string, unknown>, confirmCommand: string, message: string, metadata: OutputMetadata): never;
|
|
358
|
+
/**
|
|
359
|
+
* Output execution result as JSON (non-exiting version)
|
|
360
|
+
*
|
|
361
|
+
* Use this after execution completes in non-TTY mode to provide structured
|
|
362
|
+
* results. Unlike other output functions, this does NOT exit - caller should
|
|
363
|
+
* handle cleanup and exit.
|
|
364
|
+
*
|
|
365
|
+
* @param executions - Array of execution results
|
|
366
|
+
* @param successCount - Number of successful executions
|
|
367
|
+
* @param failCount - Number of failed executions
|
|
368
|
+
* @param metadata - Command metadata
|
|
369
|
+
*/
|
|
370
|
+
export declare function outputExecutionResultAsJson(executions: Array<{
|
|
371
|
+
workId: string;
|
|
372
|
+
ticketId: string;
|
|
373
|
+
agent: string;
|
|
374
|
+
sessionId?: string;
|
|
375
|
+
containerId?: string;
|
|
376
|
+
status: string;
|
|
377
|
+
}>, successCount: number, failCount: number, metadata: OutputMetadata): void;
|
package/dist/lib/prompt-json.js
CHANGED
|
@@ -280,3 +280,49 @@ export function outputDryRunErrorsAsJson(errors, metadata) {
|
|
|
280
280
|
console.log(JSON.stringify(output, null, 2));
|
|
281
281
|
process.exit(EXIT_ERROR);
|
|
282
282
|
}
|
|
283
|
+
/**
|
|
284
|
+
* Output a confirmation needed response as JSON and exit
|
|
285
|
+
*
|
|
286
|
+
* Use this in non-TTY mode when all required flags are provided but --yes is not set.
|
|
287
|
+
* This allows agents to preview what will happen before confirming execution.
|
|
288
|
+
*
|
|
289
|
+
* @param plan - Details of what will happen if confirmed
|
|
290
|
+
* @param confirmCommand - The full command to run with --yes to execute
|
|
291
|
+
* @param message - Human-readable message explaining the confirmation
|
|
292
|
+
* @param metadata - Command metadata
|
|
293
|
+
*/
|
|
294
|
+
export function outputConfirmationNeededAsJson(plan, confirmCommand, message, metadata) {
|
|
295
|
+
const output = {
|
|
296
|
+
type: 'confirmation_needed',
|
|
297
|
+
plan,
|
|
298
|
+
confirm_command: confirmCommand,
|
|
299
|
+
message,
|
|
300
|
+
metadata,
|
|
301
|
+
};
|
|
302
|
+
console.log(JSON.stringify(output, null, 2));
|
|
303
|
+
process.exit(EXIT_NEEDS_INPUT);
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Output execution result as JSON (non-exiting version)
|
|
307
|
+
*
|
|
308
|
+
* Use this after execution completes in non-TTY mode to provide structured
|
|
309
|
+
* results. Unlike other output functions, this does NOT exit - caller should
|
|
310
|
+
* handle cleanup and exit.
|
|
311
|
+
*
|
|
312
|
+
* @param executions - Array of execution results
|
|
313
|
+
* @param successCount - Number of successful executions
|
|
314
|
+
* @param failCount - Number of failed executions
|
|
315
|
+
* @param metadata - Command metadata
|
|
316
|
+
*/
|
|
317
|
+
export function outputExecutionResultAsJson(executions, successCount, failCount, metadata) {
|
|
318
|
+
const output = {
|
|
319
|
+
type: 'execution_result',
|
|
320
|
+
result: {
|
|
321
|
+
executions,
|
|
322
|
+
successCount,
|
|
323
|
+
failCount,
|
|
324
|
+
},
|
|
325
|
+
metadata,
|
|
326
|
+
};
|
|
327
|
+
console.log(JSON.stringify(output, null, 2));
|
|
328
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { execSync } from 'node:child_process';
|
|
2
|
+
/**
|
|
3
|
+
* Check if a git repository has a GitHub remote configured.
|
|
4
|
+
*
|
|
5
|
+
* @param cwd - Directory to check (defaults to process.cwd())
|
|
6
|
+
* @returns true if the repository has a GitHub remote
|
|
7
|
+
*/
|
|
8
|
+
export function hasGitHubRemote(cwd) {
|
|
9
|
+
try {
|
|
10
|
+
const remoteUrl = execSync('git remote get-url origin', {
|
|
11
|
+
cwd,
|
|
12
|
+
encoding: 'utf-8',
|
|
13
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
14
|
+
}).trim();
|
|
15
|
+
return remoteUrl.includes('github.com');
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|