@meistrari/remy-cli 1.17.0 → 1.17.1

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
@@ -38,7 +38,7 @@ The dashboard is the starting point for all interactive work. It refreshes the v
38
38
 
39
39
  Press `n` in the dashboard. The new-session flow asks what you want done, selects a GitHub installation when needed, suggests repositories based on your request, and lets you review those suggestions. Remy analyzes the request for existing work: a Remy session ID or number, a PR URL or number (`PR 267`, `#267`, or `owner/repo#267`), or an exact or approximate branch name. It verifies matches against the selected repositories and offers up to five branches per repository. In the branch step, use arrows to move, Space to select, and Enter to select the current option and review your choices. Each repository can continue one branch or start a new session branch. A lookup failure stays on a retry screen; press `r` to retry, `n` to explicitly choose new branches, or Esc to edit the request. Before creating the session, choose the agent model and reasoning effort. The wizard starts with `gpt-6-astra` and `medium` reasoning by default. Remy then opens the session and streams its activity.
40
40
 
41
- Use `Tab` while writing the request to complete a local file or directory path. Remy attaches selected paths when you confirm the request. Each file may be at most 20 MiB. A directory may contain at most 20 MiB across its regular files before compression; Remy uploads it as a temporary `<directory>.tar.gz` archive with the selected directory preserved as its root, so the remote agent can extract and use its files. Remy rejects oversized attachments with guidance to choose a smaller file or directory, and rejects directory attachments that contain symbolic links or other non-regular entries. On macOS, `Ctrl+V` adds a PNG from the system clipboard to the request; Remy attaches it on confirmation. Remy uploads attachments before creating the session and shows their filenames beneath your message.
41
+ Use `Tab` while writing the request to complete a local file or directory path. Remy attaches selected paths when you confirm the request. You can also paste an absolute path directly; quote paths containing spaces (for example, `"/Users/me/Desktop/Screen Shot.png"`) or use shell-style backslash escapes. Autocomplete selection is not required. Each file may be at most 20 MiB. A directory may contain at most 20 MiB across its regular files before compression; Remy uploads it as a temporary `<directory>.tar.gz` archive with the selected directory preserved as its root, so the remote agent can extract and use its files. Remy rejects oversized attachments with guidance to choose a smaller file or directory, and rejects directory attachments that contain symbolic links or other non-regular entries. On macOS, `Ctrl+V` saves a clipboard image as a temporary PNG and adds its path to the request; Remy attaches it on confirmation. Remy uploads attachments before creating the session and shows their filenames beneath your message.
42
42
 
43
43
  ### Resume or follow up
44
44
 
package/dist/remy.js CHANGED
@@ -39362,13 +39362,35 @@ var directoryArchiveMediaType = "application/gzip";
39362
39362
  var directoryArchiveSuffix = ".tar.gz";
39363
39363
  var maxPortableFilenameBytes = 255;
39364
39364
  var attachmentSizeLimitLabel = `${fileUploadMaxBytes / (1024 * 1024)} MiB`;
39365
+ var clipboardImageScript = `
39366
+ ObjC.import('AppKit');
39367
+ const pasteboard = $.NSPasteboard.generalPasteboard;
39368
+ let png = pasteboard.dataForType($.NSPasteboardTypePNG);
39369
+ if (!png || png.isNil()) {
39370
+ const tiff = pasteboard.dataForType($.NSPasteboardTypeTIFF);
39371
+ if (!tiff || tiff.isNil()) throw new Error('Clipboard does not contain an image.');
39372
+ const bitmap = $.NSBitmapImageRep.imageRepWithData(tiff);
39373
+ if (!bitmap || bitmap.isNil()) throw new Error('Clipboard image could not be decoded.');
39374
+ png = bitmap.representationUsingTypeProperties($.NSPNGFileType, $.NSDictionary.dictionary);
39375
+ }
39376
+ if (!png || png.isNil()) throw new Error('Clipboard image could not be converted to PNG.');
39377
+ if (png.length > ${fileUploadMaxBytes}) throw new Error('Clipboard image is larger than ${attachmentSizeLimitLabel}.');
39378
+ ObjC.unwrap(png.base64EncodedStringWithOptions(0));
39379
+ `;
39365
39380
  async function readClipboardImage() {
39366
39381
  if (process.platform !== "darwin")
39367
39382
  throw new Error("Clipboard image paste is supported on macOS only.");
39368
- const clipboard = Bun.spawn(["pbpaste", "-Prefer", "png"], { stdout: "pipe", stderr: "ignore" });
39369
- const bytes = new Uint8Array(await new Response(clipboard.stdout).arrayBuffer());
39370
- if (await clipboard.exited !== 0 || !isPng(bytes))
39371
- throw new Error("Clipboard does not contain an image.");
39383
+ const clipboard = Bun.spawn(["/usr/bin/osascript", "-l", "JavaScript", "-e", clipboardImageScript], { stdout: "pipe", stderr: "pipe" });
39384
+ const [encoded, error93, exitCode] = await Promise.all([
39385
+ new Response(clipboard.stdout).text(),
39386
+ new Response(clipboard.stderr).text(),
39387
+ clipboard.exited
39388
+ ]);
39389
+ if (exitCode !== 0)
39390
+ throw new Error(`Could not read clipboard image: ${error93.trim() || "macOS clipboard reader failed."}`);
39391
+ const bytes = new Uint8Array(Buffer2.from(encoded.trim(), "base64"));
39392
+ if (!isPng(bytes))
39393
+ throw new Error("Clipboard image could not be decoded as PNG.");
39372
39394
  return { bytes, mediaType: "image/png" };
39373
39395
  }
39374
39396
  async function writePastedImage({
@@ -39558,8 +39580,11 @@ function promptFilePaths({ text, cwd, selectedPaths }) {
39558
39580
  const paths = new Map;
39559
39581
  for (const selectedPath of selectedPaths)
39560
39582
  paths.set(isAbsolute2(selectedPath) ? selectedPath : resolve2(cwd, selectedPath), true);
39561
- for (const rawToken of text.split(/\s+/)) {
39562
- const token = rawToken.replace(/^[([{'"`]+/, "").replace(/[),.;:!?\]}"`]+$/, "");
39583
+ const tokens = text.match(/[([{]*@?(?:"[^"]*"|'[^']*'|`[^`]*`)|(?:\\.|[^\s\\])+/g) ?? [];
39584
+ for (const quotedToken of tokens) {
39585
+ const wrappedToken = quotedToken.replace(/^[([{]+/, "").replace(/[),.;:!?\]}]+$/, "");
39586
+ const quotedPath = /^(@?)(["'`])([\s\S]*)\2$/.exec(wrappedToken);
39587
+ const token = quotedPath ? `${quotedPath[1]}${quotedPath[3]}` : wrappedToken.replace(/\\(.)/g, "$1");
39563
39588
  const pathText = token.startsWith("@") ? token.slice(1) : token;
39564
39589
  if (!pathText)
39565
39590
  continue;
@@ -39608,7 +39633,7 @@ var compactMarkRows = 9;
39608
39633
  var compactMinWidth = 48;
39609
39634
  var compactMinHeight = 20;
39610
39635
  var markBrightnessGain = 4.2;
39611
- var remyCliVersion = "1.17.0";
39636
+ var remyCliVersion = "1.17.1";
39612
39637
  async function showRemySplash({
39613
39638
  createRenderer = createRemyRenderer,
39614
39639
  durationMs = splashDurationMs,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meistrari/remy-cli",
3
- "version": "1.17.0",
3
+ "version": "1.17.1",
4
4
  "description": "Remy, the Coding Agent terminal client.",
5
5
  "type": "module",
6
6
  "bin": {