@thegitai/cli 1.0.0-beta.2 → 1.0.0-beta.4

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.
@@ -21,6 +21,14 @@ export function isSupportedImageMimeType(mime) {
21
21
  }
22
22
  function whichSync(cmd) {
23
23
  try {
24
+ if (process.platform === 'win32') {
25
+ execFileSync('where.exe', [cmd], {
26
+ stdio: 'ignore',
27
+ timeout: 2000,
28
+ windowsHide: true,
29
+ });
30
+ return true;
31
+ }
24
32
  execFileSync('which', [cmd], { stdio: 'ignore', timeout: 2000 });
25
33
  return true;
26
34
  }
@@ -93,6 +101,52 @@ function readClipboardLinux() {
93
101
  }
94
102
  throw new ClipboardError('Clipboard contains no image data.', 'NO_IMAGE');
95
103
  }
104
+ const WINDOWS_CLIPBOARD_IMAGE_PS = [
105
+ '[Console]::OutputEncoding = [System.Text.Encoding]::UTF8;',
106
+ '$ErrorActionPreference = "Stop";',
107
+ 'Add-Type -AssemblyName System.Drawing;',
108
+ '$img = Get-Clipboard -Format Image;',
109
+ 'if ($null -eq $img) { exit 2 }',
110
+ '$ms = New-Object System.IO.MemoryStream;',
111
+ '$img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png);',
112
+ '[Convert]::ToBase64String($ms.ToArray())',
113
+ ].join(' ');
114
+ function readClipboardWindows() {
115
+ try {
116
+ const b64 = execFileSync('powershell.exe', ['-NoProfile', '-Command', WINDOWS_CLIPBOARD_IMAGE_PS], {
117
+ encoding: 'utf-8',
118
+ timeout: 5000,
119
+ maxBuffer: MAX_IMAGE_SIZE_BYTES * 2,
120
+ stdio: ['ignore', 'pipe', 'pipe'],
121
+ windowsHide: true,
122
+ }).trim();
123
+ if (!b64) {
124
+ throw new ClipboardError('Clipboard contains no image data.', 'NO_IMAGE');
125
+ }
126
+ const buf = Buffer.from(b64, 'base64');
127
+ if (!buf.length) {
128
+ throw new ClipboardError('Clipboard contains no image data.', 'NO_IMAGE');
129
+ }
130
+ if (buf.length > MAX_IMAGE_SIZE_BYTES) {
131
+ throw new ClipboardError('Clipboard image exceeds 10MB size limit.', 'READ_FAILED');
132
+ }
133
+ return { base64Data: b64, mimeType: 'image/png' };
134
+ }
135
+ catch (err) {
136
+ if (err instanceof ClipboardError)
137
+ throw err;
138
+ if (err?.status === 2) {
139
+ throw new ClipboardError('Clipboard contains no image data. Copy an image first (Win+Shift+S), then press Alt+V.', 'NO_IMAGE');
140
+ }
141
+ if (isMaxBufferError(err)) {
142
+ throw new ClipboardError('Clipboard image exceeds 10MB size limit.', 'READ_FAILED');
143
+ }
144
+ const detail = [err?.message, err?.stderr?.toString?.()?.trim()]
145
+ .filter(Boolean)
146
+ .join(' — ');
147
+ throw new ClipboardError(`Failed to read clipboard image on Windows: ${detail || 'unknown error'}`, 'READ_FAILED');
148
+ }
149
+ }
96
150
  function readClipboardDarwin() {
97
151
  if (whichSync('pngpaste')) {
98
152
  try {
@@ -123,6 +177,8 @@ export function readClipboardImage(platform = process.platform) {
123
177
  return readClipboardLinux();
124
178
  case 'darwin':
125
179
  return readClipboardDarwin();
180
+ case 'win32':
181
+ return readClipboardWindows();
126
182
  default:
127
183
  throw new ClipboardError(`Clipboard image paste is not supported on ${platform}.`, 'NO_TOOL');
128
184
  }
@@ -114,13 +114,13 @@ export function resolveTuiBinaryPath() {
114
114
  export function spawnTuiProcess(options = {}) {
115
115
  const binaryPath = resolveTuiBinaryPath();
116
116
  return spawn(binaryPath, [], {
117
- stdio: ['pipe', 'pipe', 'inherit'],
117
+ stdio: ['pipe', 'inherit', 'pipe'],
118
118
  env: { ...process.env, ...options.env },
119
119
  });
120
120
  }
121
121
  export function createRatatuiBridge() {
122
122
  const child = spawnTuiProcess();
123
- const rl = createInterface({ input: child.stdout });
123
+ const rl = createInterface({ input: child.stderr });
124
124
  let eventHandler = null;
125
125
  let closed = false;
126
126
  rl.on('line', (line) => {
@@ -311,7 +311,11 @@ function composerFooterLines(state) {
311
311
  ? state.queuedMessage
312
312
  ? 'Enter re-queues • ↑ edit queued • Esc cancels queued'
313
313
  : 'Enter queues • Esc / Ctrl+C cancel turn'
314
- : 'Enter sends • Shift+Tab mode • Esc cancel turn • Ctrl+C quits';
314
+ : process.platform === 'win32'
315
+ ? 'Enter sends • Shift+Tab mode • Alt+V image • Esc cancel turn • Ctrl+C quits'
316
+ : process.platform === 'darwin'
317
+ ? 'Enter sends • Shift+Tab mode • Ctrl+V image • Esc cancel turn • Ctrl+C quits'
318
+ : 'Enter sends • Shift+Tab mode • Ctrl+V image • Esc cancel turn • Ctrl+C quits';
315
319
  const agentLabel = agentModeLabel(state.agentMode).padEnd(AGENT_MODE_LABEL_WIDTH);
316
320
  const tokenUsageText = state.tokenUsage || formatClientTokenUsage(null);
317
321
  const footerSpans = [
@@ -1,6 +1,14 @@
1
1
  import { readClipboardImage, readClipboardText } from '../../core/clipboard.js';
2
2
  import { applySlashCommandSuggestion, buildModelPickerOptions, deleteAtCursor, deleteBeforeCursor, getApprovalChoiceForCursor, getInputCommandToken, getNextApprovalCursor, getNextModelPickerIndex, getSlashCommandSuggestions, insertAtCursor, isExactSlashCommandToken, navigatePromptHistory, resolveApprovalChoiceFromInput, shouldRemountLiveFrameForComposerInputChange, } from '../repl.js';
3
3
  import { buildPastePlaceholder, shouldCollapsePaste, } from '../paste-collapse.js';
4
+ function isClipboardImagePasteKey(key) {
5
+ if (process.platform === 'win32') {
6
+ return ((key.ctrl || key.meta) &&
7
+ key.input.toLowerCase() === 'v' &&
8
+ !key.shift);
9
+ }
10
+ return key.ctrl && key.input === 'v' && !key.shift && !key.meta;
11
+ }
4
12
  function shouldShowCommandPalette(state) {
5
13
  if (state.busy ||
6
14
  state.exiting ||
@@ -428,7 +436,7 @@ export function handleShellKeyEvent(store, handlers, event) {
428
436
  });
429
437
  return;
430
438
  }
431
- if (key.ctrl && key.input === 'v') {
439
+ if (isClipboardImagePasteKey(key)) {
432
440
  const current = store.getState();
433
441
  if (current.busy)
434
442
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thegitai/cli",
3
- "version": "1.0.0-beta.2",
3
+ "version": "1.0.0-beta.4",
4
4
  "description": "TheGitAI CLI client (source-visible, proprietary)",
5
5
  "license": "SEE LICENSE IN LICENSE",
6
6
  "homepage": "https://thegit.ai",
@@ -27,10 +27,10 @@
27
27
  "web-tree-sitter": "^0.26.6"
28
28
  },
29
29
  "optionalDependencies": {
30
- "@thegitai/tui-darwin-arm64": "1.0.0-beta.2",
31
- "@thegitai/tui-darwin-x64": "1.0.0-beta.2",
32
- "@thegitai/tui-linux-x64": "1.0.0-beta.2",
33
- "@thegitai/tui-win32-x64": "1.0.0-beta.2"
30
+ "@thegitai/tui-darwin-arm64": "1.0.0-beta.4",
31
+ "@thegitai/tui-darwin-x64": "1.0.0-beta.4",
32
+ "@thegitai/tui-linux-x64": "1.0.0-beta.4",
33
+ "@thegitai/tui-win32-x64": "1.0.0-beta.4"
34
34
  },
35
35
  "publishConfig": {
36
36
  "access": "public"