@thegitai/cli 1.0.0-preview.44 → 1.0.0-preview.45

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 CHANGED
@@ -78,12 +78,18 @@ The agent can pause its current turn to ask up to four related questions in one
78
78
  form. Use **↑ / ↓**, the displayed option number, or **Enter** to choose, and
79
79
  **← / →** to move between questions. **Something else / add details** is the
80
80
  numbered final option; focus it and type, paste, or press **Enter** to add a
81
- single-line note. On single-select questions it is mutually exclusive with the
82
- listed choices, and **↑ / ↓** leaves its editor when the note is empty.
81
+ single-line note; **Enter** commits it and moves on the way any other choice
82
+ does — to the next question, or to **Submit answers** on the last one — without
83
+ submitting the form. On single-select questions it is mutually exclusive with
84
+ the listed choices, and **↑ / ↓** leaves its editor when the note is empty.
83
85
  Multi-select questions toggle choices. The last question has an explicit
84
86
  **Submit answers** row. **Esc** closes an open note first and otherwise cancels
85
87
  the form; **Ctrl+C** cancels the whole turn.
86
88
 
89
+ Only the option you are on shows its description, so the whole form — every
90
+ option, the note row, **Submit answers**, and the key hints — stays on screen
91
+ without scrolling. Move between options to read the others.
92
+
87
93
  Answers return to the same turn, and completed question/answer records remain
88
94
  readable when the session is resumed. Default, Bypass all permissions, and Plan
89
95
  modes all support the form; Bypass all permissions does not choose answers for
@@ -103,6 +103,18 @@ const HELP_MARKDOWN = [
103
103
  ? ['- On Mac laptops without dedicated keys, **Home/End** are `Fn+←` / `Fn+→`.']
104
104
  : []),
105
105
  '',
106
+ '## Questions',
107
+ '',
108
+ '- The agent can pause a turn to ask up to four questions in one form.',
109
+ ' **↑/↓** moves, the option number or **Enter** chooses, **←/→** moves',
110
+ ' between questions, and the last question has a **Submit answers** row.',
111
+ '- Only the option you are on shows its description, so the whole form fits',
112
+ ' on screen. Move to an option to read what it means.',
113
+ '- **Something else / add details** is the numbered final option: focus it',
114
+ ' and type. **Enter** commits the note and moves to the next question, or to',
115
+ ' Submit on the last one — it never submits the form for you.',
116
+ '- **Esc** closes an open note first, and otherwise cancels the form.',
117
+ '',
106
118
  '## Chat commands',
107
119
  '',
108
120
  '- `/help` — show this help',
@@ -44,6 +44,7 @@ export function footerNotificationHint(status) {
44
44
  }
45
45
  const FOOTER_CYCLE_HINT = ' (shift+tab to cycle)';
46
46
  const OVERLAY_PANEL_MAX_WIDTH = 86;
47
+ const USER_INPUT_PANEL_MAX_WIDTH = 120;
47
48
  const OVERLAY_PANEL_MARGIN_LINES = 2;
48
49
  const OVERLAY_PANEL_MARGIN_MIN_ROWS = 30;
49
50
  const OVERLAY_BORDER_COLOR = 'yellow';
@@ -817,8 +818,8 @@ function overlayPanelContentBudget(height) {
817
818
  const margins = overlayPanelMarginLineCount(height) * 2;
818
819
  return Math.max(1, Math.floor(height) - margins - 2);
819
820
  }
820
- function buildOverlayPanel(rows, width, color, height = Number.POSITIVE_INFINITY) {
821
- const panelWidth = Math.max(24, Math.min(width, OVERLAY_PANEL_MAX_WIDTH));
821
+ function buildOverlayPanel(rows, width, color, height = Number.POSITIVE_INFINITY, maxWidth = OVERLAY_PANEL_MAX_WIDTH) {
822
+ const panelWidth = Math.max(24, Math.min(width, maxWidth));
822
823
  const innerWidth = Math.max(1, panelWidth - 4);
823
824
  const margin = Array.from({
824
825
  length: overlayPanelMarginLineCount(height),
@@ -1104,6 +1105,9 @@ export function buildWorkingClockLine(state, elapsedSeconds) {
1104
1105
  export function approvalPanelInnerWidth(width) {
1105
1106
  return Math.max(1, Math.max(24, Math.min(width, OVERLAY_PANEL_MAX_WIDTH)) - 4);
1106
1107
  }
1108
+ export function userInputPanelInnerWidth(width) {
1109
+ return Math.max(1, Math.max(24, Math.min(width, USER_INPUT_PANEL_MAX_WIDTH)) - 4);
1110
+ }
1107
1111
  export function approvalPaddingEnabled(height) {
1108
1112
  return height >= APPROVAL_PADDING_MIN_ROWS;
1109
1113
  }
@@ -1212,7 +1216,7 @@ function buildOverlayLines(state, width, height, nowMs) {
1212
1216
  if (height < 3) {
1213
1217
  return [];
1214
1218
  }
1215
- return buildOverlayPanel(buildUserInputOverlayLines(state.userInputPrompt, innerWidth, overlayPanelContentBudget(height)), width, USER_INPUT_BORDER_COLOR, height);
1219
+ return buildOverlayPanel(buildUserInputOverlayLines(state.userInputPrompt, userInputPanelInnerWidth(width), overlayPanelContentBudget(height)), width, USER_INPUT_BORDER_COLOR, height, USER_INPUT_PANEL_MAX_WIDTH);
1216
1220
  }
1217
1221
  if (state.approvalPrompt) {
1218
1222
  const prompt = state.approvalPrompt;
@@ -1337,7 +1341,7 @@ export function userInputViewportForFrame(state, cols, rows, spinnerFrame, elaps
1337
1341
  const liveRows = buildLiveLines(state, contentWidth, spinnerFrame, elapsedSeconds, nowMs).length;
1338
1342
  const overlayHeight = Math.max(0, rows - liveRows - composerFooterLines(state, contentWidth).length);
1339
1343
  return {
1340
- width: approvalPanelInnerWidth(contentWidth),
1344
+ width: userInputPanelInnerWidth(contentWidth),
1341
1345
  maxRows: overlayPanelContentBudget(overlayHeight),
1342
1346
  };
1343
1347
  }
@@ -139,9 +139,19 @@ function editOpenNote(state, event) {
139
139
  const question = currentQuestion(state);
140
140
  const current = state.customText[question.id] ?? '';
141
141
  const cursor = Math.max(0, Math.min(state.noteCursor, current.length));
142
- if (event.escape || event.returnKey) {
142
+ if (event.escape) {
143
143
  return { ...state, noteOpen: false };
144
144
  }
145
+ if (event.returnKey) {
146
+ const closed = { ...state, noteOpen: false };
147
+ if (question.multiSelect || !current.trim()) {
148
+ return closed;
149
+ }
150
+ const index = questionIndex(closed);
151
+ return index < closed.questions.length - 1
152
+ ? setQuestion(closed, index + 1)
153
+ : setCursor(closed, question, SUBMIT_CURSOR);
154
+ }
145
155
  if (event.tab)
146
156
  return state;
147
157
  if ((event.upArrow || event.downArrow) && !current.trim()) {
@@ -433,8 +443,10 @@ function buildUserInputContent(state, width) {
433
443
  }), ...(recommendation
434
444
  ? [span(recommendation, { color: 'green' })]
435
445
  : [])));
436
- for (const description of wrapText(option.description, Math.max(8, width - 5))) {
437
- lines.push(plainLine(` ${description}`, { color: 'gray' }));
446
+ if (focused) {
447
+ for (const description of wrapText(option.description, Math.max(8, width - 5))) {
448
+ lines.push(plainLine(` ${description}`, { color: 'gray' }));
449
+ }
438
450
  }
439
451
  if (state.contentScrollAnchor === option.id) {
440
452
  anchorRange = { start: blockStart, end: lines.length - 1 };
@@ -490,23 +502,38 @@ function buildUserInputContent(state, width) {
490
502
  if (state.validationMessage) {
491
503
  lines.push(plainLine(state.validationMessage, { color: 'yellow' }));
492
504
  }
493
- lines.push(plainLine(state.noteOpen
494
- ? 'Type a single-line note • ↑/↓ moves when empty • Enter saves • Esc closes • PgUp/PgDn scroll'
505
+ const footer = plainLine(state.noteOpen
506
+ ? 'Type a single-line note • ↑/↓ moves when empty • Enter saves • Esc closes'
495
507
  : question.multiSelect
496
- ? `↑/↓ move • 1–${otherNumber} choose • Enter toggles/edits • ←/→ questions • PgUp/PgDn scroll • Esc cancels`
497
- : `↑/↓ move • 1–${otherNumber} choose • Enter selects/edits • ←/→ questions • PgUp/PgDn scroll • Esc cancels`, { color: 'gray' }));
508
+ ? `↑/↓ move • 1–${otherNumber} choose • Enter toggles/edits • ←/→ questions • Esc cancels`
509
+ : `↑/↓ move • 1–${otherNumber} choose • Enter selects/edits • ←/→ questions • Esc cancels`, { color: 'gray' });
498
510
  return {
499
511
  anchorRange,
512
+ footer: clipLineToWidth(footer, width),
500
513
  lines: lines.map((row) => clipLineToWidth(row, width)),
501
514
  };
502
515
  }
503
516
  function buildUserInputWindow(state, width, maxRows) {
504
517
  const content = buildUserInputContent(state, width);
505
- if (!Number.isFinite(maxRows) || content.lines.length <= maxRows) {
506
- return { lines: content.lines, maxOffset: 0, offset: 0 };
518
+ if (!Number.isFinite(maxRows)) {
519
+ return {
520
+ lines: [...content.lines, content.footer],
521
+ maxOffset: 0,
522
+ offset: 0,
523
+ };
507
524
  }
508
525
  const rowBudget = Math.max(1, Math.floor(maxRows));
509
- const visibleRowBudget = rowBudget === 1 ? 1 : rowBudget - 1;
526
+ if (rowBudget === 1) {
527
+ return { lines: [content.footer], maxOffset: 0, offset: 0 };
528
+ }
529
+ if (content.lines.length <= rowBudget - 1) {
530
+ return {
531
+ lines: [...content.lines, content.footer],
532
+ maxOffset: 0,
533
+ offset: 0,
534
+ };
535
+ }
536
+ const visibleRowBudget = Math.max(1, rowBudget - 2);
510
537
  const maxOffset = Math.max(0, content.lines.length - visibleRowBudget);
511
538
  let offset = Math.max(0, Math.min(state.contentScrollOffset, maxOffset));
512
539
  const range = content.anchorRange;
@@ -527,13 +554,11 @@ function buildUserInputWindow(state, width, maxRows) {
527
554
  offset = Math.max(0, Math.min(offset, maxOffset));
528
555
  }
529
556
  const visible = content.lines.slice(offset, offset + visibleRowBudget);
530
- if (rowBudget === 1) {
531
- return { lines: visible, maxOffset, offset };
532
- }
533
557
  return {
534
558
  lines: [
535
559
  ...visible,
536
560
  plainLine(fitText(`Rows ${offset + 1}–${offset + visible.length} of ${content.lines.length} · PgUp/PgDn scroll`, width), { color: 'gray' }),
561
+ content.footer,
537
562
  ],
538
563
  maxOffset,
539
564
  offset,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegitai/cli",
3
- "version": "1.0.0-preview.44",
3
+ "version": "1.0.0-preview.45",
4
4
  "description": "TheGitAI is an agentic AI coding tool for your terminal. It reads and searches your repository, writes and edits files, runs your tests, and verifies the change before handing it back.",
5
5
  "keywords": [
6
6
  "agentic-ai",
@@ -44,11 +44,11 @@
44
44
  "@lydell/node-pty-linux-x64": "1.1.0",
45
45
  "@lydell/node-pty-win32-arm64": "1.1.0",
46
46
  "@lydell/node-pty-win32-x64": "1.1.0",
47
- "@thegitai/tui-darwin-arm64": "1.0.0-preview.44",
48
- "@thegitai/tui-darwin-x64": "1.0.0-preview.44",
49
- "@thegitai/tui-linux-arm64": "1.0.0-preview.44",
50
- "@thegitai/tui-linux-x64": "1.0.0-preview.44",
51
- "@thegitai/tui-win32-x64": "1.0.0-preview.44",
47
+ "@thegitai/tui-darwin-arm64": "1.0.0-preview.45",
48
+ "@thegitai/tui-darwin-x64": "1.0.0-preview.45",
49
+ "@thegitai/tui-linux-arm64": "1.0.0-preview.45",
50
+ "@thegitai/tui-linux-x64": "1.0.0-preview.45",
51
+ "@thegitai/tui-win32-x64": "1.0.0-preview.45",
52
52
  "@vscode/ripgrep": "1.18.0"
53
53
  },
54
54
  "repository": {