@f5xc-salesdemos/xcsh 18.82.0 → 18.83.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5xc-salesdemos/xcsh",
4
- "version": "18.82.0",
4
+ "version": "18.83.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5xc-salesdemos/xcsh",
7
7
  "author": "Can Boluk",
@@ -49,12 +49,12 @@
49
49
  "dependencies": {
50
50
  "@agentclientprotocol/sdk": "0.16.1",
51
51
  "@mozilla/readability": "^0.6",
52
- "@f5xc-salesdemos/xcsh-stats": "18.82.0",
53
- "@f5xc-salesdemos/pi-agent-core": "18.82.0",
54
- "@f5xc-salesdemos/pi-ai": "18.82.0",
55
- "@f5xc-salesdemos/pi-natives": "18.82.0",
56
- "@f5xc-salesdemos/pi-tui": "18.82.0",
57
- "@f5xc-salesdemos/pi-utils": "18.82.0",
52
+ "@f5xc-salesdemos/xcsh-stats": "18.83.0",
53
+ "@f5xc-salesdemos/pi-agent-core": "18.83.0",
54
+ "@f5xc-salesdemos/pi-ai": "18.83.0",
55
+ "@f5xc-salesdemos/pi-natives": "18.83.0",
56
+ "@f5xc-salesdemos/pi-tui": "18.83.0",
57
+ "@f5xc-salesdemos/pi-utils": "18.83.0",
58
58
  "@sinclair/typebox": "^0.34",
59
59
  "@xterm/headless": "^6.0",
60
60
  "ajv": "^8.18",
@@ -391,6 +391,17 @@ export const SETTINGS_SCHEMA = {
391
391
  ui: { tab: "appearance", label: "Block Images", description: "Prevent images from being sent to LLM providers" },
392
392
  },
393
393
 
394
+ "images.pasteDir": {
395
+ type: "string",
396
+ default: ".",
397
+ ui: {
398
+ tab: "tools",
399
+ label: "Clipboard Image Directory",
400
+ description:
401
+ "Directory to save clipboard-pasted images. Defaults to current working directory. Supports ~. Examples: ., ~/Screenshots, ./assets",
402
+ },
403
+ },
404
+
394
405
  "tui.maxInlineImageColumns": {
395
406
  type: "number",
396
407
  default: 100,
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "18.82.0",
21
- "commit": "25dbb133a016e072790449ae4338a3b963723ca4",
22
- "shortCommit": "25dbb13",
20
+ "version": "18.83.0",
21
+ "commit": "c9b2bcc493b2c4f0f8e6bd7b85985b4d7adb7862",
22
+ "shortCommit": "c9b2bcc",
23
23
  "branch": "main",
24
- "tag": "v18.82.0",
25
- "commitDate": "2026-05-25T23:16:56Z",
26
- "buildDate": "2026-05-25T23:41:19.829Z",
24
+ "tag": "v18.83.0",
25
+ "commitDate": "2026-05-27T01:03:54Z",
26
+ "buildDate": "2026-05-27T01:31:46.285Z",
27
27
  "dirty": true,
28
28
  "prNumber": "",
29
29
  "repoUrl": "https://github.com/f5xc-salesdemos/xcsh",
30
30
  "repoSlug": "f5xc-salesdemos/xcsh",
31
- "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/25dbb133a016e072790449ae4338a3b963723ca4",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.82.0"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/c9b2bcc493b2c4f0f8e6bd7b85985b4d7adb7862",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.83.0"
33
33
  };
@@ -1,4 +1,5 @@
1
1
  import * as fs from "node:fs/promises";
2
+ import * as path from "node:path";
2
3
  import { type AgentMessage, ThinkingLevel } from "@f5xc-salesdemos/pi-agent-core";
3
4
  import { sanitizeText } from "@f5xc-salesdemos/pi-natives";
4
5
  import { type AutocompleteProvider, ChordDispatcher, type SlashCommand } from "@f5xc-salesdemos/pi-tui";
@@ -11,6 +12,7 @@ import type { InteractiveModeContext } from "../../modes/types";
11
12
  import type { AgentSessionEvent } from "../../session/agent-session";
12
13
  import { SKILL_PROMPT_MESSAGE_TYPE, type SkillPromptDetails } from "../../session/messages";
13
14
  import { executeBuiltinSlashCommand } from "../../slash-commands/builtin-registry";
15
+ import { resolveToCwd } from "../../tools/path-utils";
14
16
  import { copyToClipboard, readImageFromClipboard } from "../../utils/clipboard";
15
17
  import { getEditorCommand, openInEditor } from "../../utils/external-editor";
16
18
  import { ensureSupportedImageInput } from "../../utils/image-loading";
@@ -25,6 +27,15 @@ function isExpandable(obj: unknown): obj is Expandable {
25
27
  return typeof obj === "object" && obj !== null && "setExpanded" in obj && typeof obj.setExpanded === "function";
26
28
  }
27
29
 
30
+ async function fileExists(filePath: string): Promise<boolean> {
31
+ try {
32
+ await fs.access(filePath);
33
+ return true;
34
+ } catch {
35
+ return false;
36
+ }
37
+ }
38
+
28
39
  export class InputController {
29
40
  #dispatcher: ChordDispatcher | null = null;
30
41
 
@@ -599,9 +610,17 @@ export class InputController {
599
610
  data: imageData.data,
600
611
  mimeType: imageData.mimeType,
601
612
  });
602
- // Insert placeholder at cursor like Claude does
613
+
603
614
  const imageNum = this.ctx.pendingImages.length;
604
- const placeholder = `[Image #${imageNum}]`;
615
+ let placeholder = `[Image #${imageNum}]`;
616
+
617
+ const cwd = this.ctx.sessionManager.getCwd();
618
+ const savedPath = await this.saveImageToDisk(imageData.data, cwd);
619
+ if (savedPath) {
620
+ const relativePath = path.relative(cwd, savedPath);
621
+ placeholder = `[Image #${imageNum}] (./${relativePath})`;
622
+ }
623
+
605
624
  this.ctx.editor.insertText(`${placeholder} `);
606
625
  this.ctx.ui.requestRender();
607
626
  return true;
@@ -615,6 +634,34 @@ export class InputController {
615
634
  }
616
635
  }
617
636
 
637
+ private async saveImageToDisk(base64Data: string, cwd: string): Promise<string | null> {
638
+ try {
639
+ const pasteDir = settings.get("images.pasteDir") ?? ".";
640
+ const resolvedDir = resolveToCwd(pasteDir, cwd);
641
+ await fs.mkdir(resolvedDir, { recursive: true });
642
+
643
+ const now = new Date();
644
+ const pad = (n: number) => String(n).padStart(2, "0");
645
+ const timestamp = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
646
+
647
+ let filename = `clipboard-${timestamp}.png`;
648
+ let filePath = path.join(resolvedDir, filename);
649
+
650
+ let counter = 2;
651
+ while (await fileExists(filePath)) {
652
+ filename = `clipboard-${timestamp}-${counter}.png`;
653
+ filePath = path.join(resolvedDir, filename);
654
+ counter++;
655
+ }
656
+
657
+ const buffer = Buffer.from(base64Data, "base64");
658
+ await fs.writeFile(filePath, buffer);
659
+ return filePath;
660
+ } catch {
661
+ return null;
662
+ }
663
+ }
664
+
618
665
  createAutocompleteProvider(commands: SlashCommand[], basePath: string): AutocompleteProvider {
619
666
  return createPromptActionAutocompleteProvider({
620
667
  commands,