@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.
|
|
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.
|
|
53
|
-
"@f5xc-salesdemos/pi-agent-core": "18.
|
|
54
|
-
"@f5xc-salesdemos/pi-ai": "18.
|
|
55
|
-
"@f5xc-salesdemos/pi-natives": "18.
|
|
56
|
-
"@f5xc-salesdemos/pi-tui": "18.
|
|
57
|
-
"@f5xc-salesdemos/pi-utils": "18.
|
|
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.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "18.83.0",
|
|
21
|
+
"commit": "c9b2bcc493b2c4f0f8e6bd7b85985b4d7adb7862",
|
|
22
|
+
"shortCommit": "c9b2bcc",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-05-
|
|
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/
|
|
32
|
-
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.
|
|
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
|
-
|
|
613
|
+
|
|
603
614
|
const imageNum = this.ctx.pendingImages.length;
|
|
604
|
-
|
|
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,
|