@proletariat/cli 0.3.24 → 0.3.25

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 (75) hide show
  1. package/dist/commands/action/create.js +3 -3
  2. package/dist/commands/action/update.js +3 -3
  3. package/dist/commands/epic/activate.js +9 -17
  4. package/dist/commands/epic/archive.js +13 -24
  5. package/dist/commands/epic/create.js +7 -6
  6. package/dist/commands/epic/move.js +28 -47
  7. package/dist/commands/epic/progress.js +10 -14
  8. package/dist/commands/epic/project.js +42 -59
  9. package/dist/commands/epic/reorder.js +25 -30
  10. package/dist/commands/epic/spec.d.ts +1 -0
  11. package/dist/commands/epic/spec.js +39 -40
  12. package/dist/commands/epic/ticket.d.ts +2 -0
  13. package/dist/commands/epic/ticket.js +63 -37
  14. package/dist/commands/feedback/index.d.ts +10 -0
  15. package/dist/commands/feedback/index.js +60 -0
  16. package/dist/commands/feedback/list.d.ts +12 -0
  17. package/dist/commands/feedback/list.js +126 -0
  18. package/dist/commands/feedback/submit.d.ts +16 -0
  19. package/dist/commands/feedback/submit.js +220 -0
  20. package/dist/commands/feedback/view.d.ts +15 -0
  21. package/dist/commands/feedback/view.js +109 -0
  22. package/dist/commands/gh/index.js +4 -0
  23. package/dist/commands/repo/create.d.ts +38 -0
  24. package/dist/commands/repo/create.js +283 -0
  25. package/dist/commands/repo/index.js +7 -0
  26. package/dist/commands/roadmap/add-project.js +9 -22
  27. package/dist/commands/roadmap/create.d.ts +0 -1
  28. package/dist/commands/roadmap/create.js +46 -40
  29. package/dist/commands/roadmap/delete.js +10 -24
  30. package/dist/commands/roadmap/generate.d.ts +1 -0
  31. package/dist/commands/roadmap/generate.js +21 -22
  32. package/dist/commands/roadmap/remove-project.js +14 -34
  33. package/dist/commands/roadmap/reorder.js +19 -26
  34. package/dist/commands/roadmap/update.js +27 -26
  35. package/dist/commands/roadmap/view.js +5 -12
  36. package/dist/commands/session/attach.d.ts +1 -8
  37. package/dist/commands/session/attach.js +93 -59
  38. package/dist/commands/session/list.d.ts +0 -8
  39. package/dist/commands/session/list.js +130 -81
  40. package/dist/commands/spec/create.js +1 -1
  41. package/dist/commands/spec/edit.js +63 -33
  42. package/dist/commands/support/book.d.ts +10 -0
  43. package/dist/commands/support/book.js +54 -0
  44. package/dist/commands/support/discord.d.ts +10 -0
  45. package/dist/commands/support/discord.js +54 -0
  46. package/dist/commands/support/docs.d.ts +10 -0
  47. package/dist/commands/support/docs.js +54 -0
  48. package/dist/commands/support/index.d.ts +19 -0
  49. package/dist/commands/support/index.js +81 -0
  50. package/dist/commands/support/issues.d.ts +11 -0
  51. package/dist/commands/support/issues.js +77 -0
  52. package/dist/commands/support/logs.d.ts +18 -0
  53. package/dist/commands/support/logs.js +247 -0
  54. package/dist/commands/ticket/create.js +21 -13
  55. package/dist/commands/ticket/edit.js +44 -13
  56. package/dist/commands/ticket/move.d.ts +7 -0
  57. package/dist/commands/ticket/move.js +132 -0
  58. package/dist/commands/work/spawn.d.ts +1 -0
  59. package/dist/commands/work/spawn.js +71 -7
  60. package/dist/commands/work/start.js +6 -0
  61. package/dist/lib/execution/runners.js +21 -17
  62. package/dist/lib/execution/session-utils.d.ts +60 -0
  63. package/dist/lib/execution/session-utils.js +162 -0
  64. package/dist/lib/execution/spawner.d.ts +2 -0
  65. package/dist/lib/execution/spawner.js +42 -0
  66. package/dist/lib/flags/resolver.d.ts +2 -2
  67. package/dist/lib/flags/resolver.js +15 -0
  68. package/dist/lib/init/index.js +18 -0
  69. package/dist/lib/multiline-input.d.ts +63 -0
  70. package/dist/lib/multiline-input.js +360 -0
  71. package/dist/lib/prompt-json.d.ts +5 -5
  72. package/dist/lib/repos/git.d.ts +7 -0
  73. package/dist/lib/repos/git.js +20 -0
  74. package/oclif.manifest.json +2206 -1607
  75. 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
+ }
@@ -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) */
@@ -267,7 +267,7 @@ export declare function normalizeChoices(choices: Array<string | {
267
267
  * @param defaultValue - Optional default value
268
268
  * @returns PromptConfig object
269
269
  */
270
- export declare function buildPromptConfig(type: 'list' | 'checkbox' | 'confirm' | 'input' | 'editor', name: string, message: string, choices?: Array<string | {
270
+ export declare function buildPromptConfig(type: 'list' | 'checkbox' | 'confirm' | 'input' | 'editor' | 'multiline', name: string, message: string, choices?: Array<string | {
271
271
  name: string;
272
272
  value: string;
273
273
  disabled?: boolean | string;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Check if a git repository has a GitHub remote configured.
3
+ *
4
+ * @param cwd - Directory to check (defaults to process.cwd())
5
+ * @returns true if the repository has a GitHub remote
6
+ */
7
+ export declare function hasGitHubRemote(cwd?: string): boolean;
@@ -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
+ }