@f5xc-salesdemos/xcsh 18.83.0 → 18.83.2

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.83.0",
4
+ "version": "18.83.2",
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.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",
52
+ "@f5xc-salesdemos/xcsh-stats": "18.83.2",
53
+ "@f5xc-salesdemos/pi-agent-core": "18.83.2",
54
+ "@f5xc-salesdemos/pi-ai": "18.83.2",
55
+ "@f5xc-salesdemos/pi-natives": "18.83.2",
56
+ "@f5xc-salesdemos/pi-tui": "18.83.2",
57
+ "@f5xc-salesdemos/pi-utils": "18.83.2",
58
58
  "@sinclair/typebox": "^0.34",
59
59
  "@xterm/headless": "^6.0",
60
60
  "ajv": "^8.18",
package/src/cursor.ts CHANGED
@@ -42,7 +42,6 @@ function createToolResultMessage(
42
42
  function buildToolErrorResult(message: string): AgentToolResult<unknown> {
43
43
  return {
44
44
  content: [{ type: "text", text: message }],
45
- details: {},
46
45
  };
47
46
  }
48
47
 
@@ -311,7 +310,6 @@ export class CursorExecHandlers implements ICursorExecHandlers {
311
310
  toolCallId,
312
311
  toolName,
313
312
  content: [{ type: "text", text: message }],
314
- details: {},
315
313
  isError: true,
316
314
  timestamp: Date.now(),
317
315
  };
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "18.83.0",
21
- "commit": "c9b2bcc493b2c4f0f8e6bd7b85985b4d7adb7862",
22
- "shortCommit": "c9b2bcc",
20
+ "version": "18.83.2",
21
+ "commit": "eacd7c8423fea2f500040cc43d684efdbeb2c8b1",
22
+ "shortCommit": "eacd7c8",
23
23
  "branch": "main",
24
- "tag": "v18.83.0",
25
- "commitDate": "2026-05-27T01:03:54Z",
26
- "buildDate": "2026-05-27T01:31:46.285Z",
24
+ "tag": "v18.83.2",
25
+ "commitDate": "2026-05-27T04:27:56Z",
26
+ "buildDate": "2026-05-27T04:46:58.363Z",
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/c9b2bcc493b2c4f0f8e6bd7b85985b4d7adb7862",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.83.0"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/eacd7c8423fea2f500040cc43d684efdbeb2c8b1",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.83.2"
33
33
  };
@@ -0,0 +1,198 @@
1
+ /** TUI renderer for lightweight action tools — checkpoint, rewind, cancel_job, poll. */
2
+ import type { Component } from "@f5xc-salesdemos/pi-tui";
3
+ import { Text } from "@f5xc-salesdemos/pi-tui";
4
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
5
+ import type { Theme, ThemeColor } from "../modes/theme/theme";
6
+ import { CachedOutputBlock, F5_TOOL_BORDER_COLOR, renderStatusLine } from "../tui";
7
+ import type { CancelJobToolDetails } from "./cancel-job";
8
+ import type { CheckpointToolDetails, RewindToolDetails } from "./checkpoint";
9
+ import type { PollToolDetails } from "./poll-tool";
10
+ import { addSection, formatErrorMessage, replaceTabs } from "./render-utils";
11
+
12
+ type ActionDetails = CheckpointToolDetails | RewindToolDetails | CancelJobToolDetails | PollToolDetails;
13
+
14
+ type ActionRenderArgs = {
15
+ goal?: string;
16
+ report?: string;
17
+ job_id?: string;
18
+ jobs?: string[];
19
+ };
20
+
21
+ const CANCEL_STATUS_COLORS: Record<string, ThemeColor> = {
22
+ cancelled: "success",
23
+ not_found: "warning",
24
+ already_completed: "dim",
25
+ };
26
+
27
+ const POLL_STATUS_COLORS: Record<string, ThemeColor> = {
28
+ completed: "success",
29
+ failed: "error",
30
+ running: "chromeAccent",
31
+ cancelled: "warning",
32
+ };
33
+
34
+ function formatDuration(ms: number): string {
35
+ if (ms < 1_000) return `${ms}ms`;
36
+ const seconds = Math.floor(ms / 1_000);
37
+ if (seconds < 60) return `${seconds}s`;
38
+ const minutes = Math.floor(seconds / 60);
39
+ const remainingSeconds = seconds % 60;
40
+ return `${minutes}m${remainingSeconds}s`;
41
+ }
42
+
43
+ function isCheckpointDetails(d: ActionDetails): d is CheckpointToolDetails {
44
+ return "goal" in d && "startedAt" in d;
45
+ }
46
+
47
+ function isRewindDetails(d: ActionDetails): d is RewindToolDetails {
48
+ return "report" in d && "rewound" in d;
49
+ }
50
+
51
+ function isCancelJobDetails(d: ActionDetails): d is CancelJobToolDetails {
52
+ return "status" in d && "jobId" in d;
53
+ }
54
+
55
+ function isPollDetails(d: ActionDetails): d is PollToolDetails {
56
+ return "jobs" in d && Array.isArray((d as PollToolDetails).jobs);
57
+ }
58
+
59
+ function buildPollJobTable(details: PollToolDetails, uiTheme: Theme): string[] {
60
+ const jobs = details.jobs;
61
+ if (jobs.length === 0) return [uiTheme.fg("dim", " No jobs.")];
62
+
63
+ return jobs.map(job => {
64
+ const statusColor = POLL_STATUS_COLORS[job.status] ?? "muted";
65
+ const status = uiTheme.fg(statusColor, job.status.padEnd(10));
66
+ const id = uiTheme.fg("toolOutput", job.id);
67
+ const typeBadge = uiTheme.fg("dim", `[${job.type}]`);
68
+ const label = uiTheme.fg("muted", job.label.length > 50 ? `${job.label.slice(0, 47)}…` : job.label);
69
+ const duration = uiTheme.fg("dim", formatDuration(job.durationMs));
70
+ return ` ${status} ${id} ${typeBadge} ${label} ${duration}`;
71
+ });
72
+ }
73
+
74
+ export const actionRenderer = {
75
+ renderCall(args: ActionRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
76
+ let title: string;
77
+ let description: string | undefined;
78
+
79
+ if (args.goal !== undefined) {
80
+ title = "Checkpoint";
81
+ description = uiTheme.fg("muted", args.goal.length > 60 ? `${args.goal.slice(0, 57)}…` : args.goal);
82
+ } else if (args.report !== undefined) {
83
+ title = "Rewind";
84
+ } else if (args.job_id !== undefined) {
85
+ title = "Cancel Job";
86
+ description = uiTheme.fg("muted", args.job_id);
87
+ } else if (args.jobs !== undefined) {
88
+ title = "Poll";
89
+ description =
90
+ args.jobs.length > 0
91
+ ? uiTheme.fg("muted", `${args.jobs.length} job${args.jobs.length !== 1 ? "s" : ""}`)
92
+ : undefined;
93
+ } else {
94
+ title = "Poll";
95
+ }
96
+
97
+ const text = renderStatusLine({ icon: "pending", title, description }, uiTheme);
98
+ return new Text(text, 0, 0);
99
+ },
100
+
101
+ renderResult(
102
+ result: { content: Array<{ type: string; text?: string }>; details?: ActionDetails; isError?: boolean },
103
+ options: RenderResultOptions,
104
+ uiTheme: Theme,
105
+ _args?: ActionRenderArgs,
106
+ ): Component {
107
+ const details = result.details;
108
+ const isError = result.isError === true;
109
+
110
+ if (isError || !details) {
111
+ const errorText = result.content?.find(c => c.type === "text")?.text;
112
+ return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
113
+ }
114
+
115
+ const sections: Array<{ label?: string; lines: string[] }> = [];
116
+ const meta: string[] = [];
117
+ let title: string;
118
+ let badgeLabel: string;
119
+ let badgeColor: ThemeColor;
120
+
121
+ if (isCheckpointDetails(details)) {
122
+ title = "Checkpoint";
123
+ badgeLabel = "created";
124
+ badgeColor = "chromeAccent";
125
+ addSection(sections, "Goal", [uiTheme.fg("toolOutput", ` ${details.goal}`)], uiTheme);
126
+ } else if (isRewindDetails(details)) {
127
+ title = "Rewind";
128
+ badgeLabel = details.rewound ? "rewound" : "no-op";
129
+ badgeColor = details.rewound ? "warning" : "dim";
130
+ addSection(
131
+ sections,
132
+ "Report",
133
+ details.report.split("\n").map(line => replaceTabs(uiTheme.fg("toolOutput", ` ${line}`))),
134
+ uiTheme,
135
+ );
136
+ } else if (isCancelJobDetails(details)) {
137
+ title = "Cancel Job";
138
+ badgeLabel = details.status;
139
+ badgeColor = CANCEL_STATUS_COLORS[details.status] ?? "muted";
140
+ meta.push(uiTheme.fg("dim", details.jobId));
141
+ const text = result.content?.find(c => c.type === "text")?.text ?? "";
142
+ addSection(sections, "Result", [uiTheme.fg("toolOutput", ` ${text}`)], uiTheme);
143
+ } else if (isPollDetails(details)) {
144
+ title = "Poll";
145
+ const completed = details.jobs.filter(j => j.status !== "running");
146
+ const running = details.jobs.filter(j => j.status === "running");
147
+ badgeLabel = `${details.jobs.length} job${details.jobs.length !== 1 ? "s" : ""}`;
148
+ badgeColor = running.length > 0 ? "chromeAccent" : "success";
149
+ if (completed.length > 0) meta.push(uiTheme.fg("success", `${completed.length} done`));
150
+ if (running.length > 0) meta.push(uiTheme.fg("chromeAccent", `${running.length} running`));
151
+ addSection(sections, "Jobs", buildPollJobTable(details, uiTheme), uiTheme);
152
+ for (const job of completed) {
153
+ if (job.resultText) {
154
+ const outputLines = job.resultText.split("\n").map(line => replaceTabs(uiTheme.fg("toolOutput", line)));
155
+ addSection(sections, `Output: ${job.id}`, outputLines, uiTheme, 30);
156
+ }
157
+ if (job.errorText) {
158
+ addSection(sections, `Error: ${job.id}`, [uiTheme.fg("error", job.errorText)], uiTheme);
159
+ }
160
+ }
161
+ } else {
162
+ title = "Action";
163
+ badgeLabel = "done";
164
+ badgeColor = "muted";
165
+ const text = result.content?.find(c => c.type === "text")?.text ?? "";
166
+ addSection(
167
+ sections,
168
+ "Result",
169
+ text.split("\n").map(line => replaceTabs(uiTheme.fg("toolOutput", line))),
170
+ uiTheme,
171
+ );
172
+ }
173
+
174
+ const header = renderStatusLine(
175
+ {
176
+ title,
177
+ titleColor: "muted",
178
+ badge: { label: badgeLabel, color: badgeColor },
179
+ meta: meta.length > 0 ? meta : undefined,
180
+ },
181
+ uiTheme,
182
+ );
183
+
184
+ const outputBlock = new CachedOutputBlock();
185
+ return {
186
+ render(width: number): string[] {
187
+ const state = options.isPartial ? "pending" : "success";
188
+ return outputBlock.render({ header, state, sections, width, borderColor: F5_TOOL_BORDER_COLOR }, uiTheme);
189
+ },
190
+ invalidate() {
191
+ outputBlock.invalidate();
192
+ },
193
+ };
194
+ },
195
+
196
+ mergeCallAndResult: true,
197
+ inline: true,
198
+ };
@@ -0,0 +1,129 @@
1
+ /** TUI renderer for the browser (puppeteer) tool — lightweight action-aware display. */
2
+ import type { Component } from "@f5xc-salesdemos/pi-tui";
3
+ import { Text } from "@f5xc-salesdemos/pi-tui";
4
+ import type { RenderResultOptions } from "../extensibility/custom-tools/types";
5
+ import type { Theme, ThemeColor } from "../modes/theme/theme";
6
+ import { CachedOutputBlock, F5_TOOL_BORDER_COLOR, renderStatusLine } from "../tui";
7
+ import type { BrowserToolDetails } from "./browser";
8
+ import { addSection, formatErrorMessage, replaceTabs, shortenPath } from "./render-utils";
9
+
10
+ const TOOL_TITLE = "Browser";
11
+ const MAX_CONTENT_LINES = 30;
12
+
13
+ type BrowserRenderArgs = {
14
+ action?: string;
15
+ url?: string;
16
+ selector?: string;
17
+ text?: string;
18
+ };
19
+
20
+ const ACTION_COLORS: Partial<Record<string, ThemeColor>> = {
21
+ open: "muted",
22
+ goto: "muted",
23
+ close: "dim",
24
+ click: "chromeAccent",
25
+ click_id: "chromeAccent",
26
+ type: "chromeAccent",
27
+ type_id: "chromeAccent",
28
+ fill: "chromeAccent",
29
+ fill_id: "chromeAccent",
30
+ press: "chromeAccent",
31
+ scroll: "muted",
32
+ drag: "chromeAccent",
33
+ wait_for_selector: "dim",
34
+ evaluate: "contentAccent",
35
+ get_text: "contentAccent",
36
+ get_html: "contentAccent",
37
+ get_attribute: "contentAccent",
38
+ extract_readable: "contentAccent",
39
+ screenshot: "warning",
40
+ observe: "accent",
41
+ };
42
+
43
+ export const browserRenderer = {
44
+ renderCall(args: BrowserRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
45
+ const action = args.action ?? "browse";
46
+ const description = args.url
47
+ ? uiTheme.fg("muted", args.url)
48
+ : args.selector
49
+ ? uiTheme.fg("dim", args.selector)
50
+ : undefined;
51
+ const badgeColor = ACTION_COLORS[action] ?? "muted";
52
+ const text = renderStatusLine(
53
+ { icon: "pending", title: TOOL_TITLE, badge: { label: action, color: badgeColor }, description },
54
+ uiTheme,
55
+ );
56
+ return new Text(text, 0, 0);
57
+ },
58
+
59
+ renderResult(
60
+ result: { content: Array<{ type: string; text?: string }>; details?: BrowserToolDetails; isError?: boolean },
61
+ options: RenderResultOptions,
62
+ uiTheme: Theme,
63
+ args?: BrowserRenderArgs,
64
+ ): Component {
65
+ const details = result.details;
66
+ const isError = result.isError === true;
67
+
68
+ if (isError && !details) {
69
+ const errorText = result.content?.find(c => c.type === "text")?.text;
70
+ return new Text(formatErrorMessage(errorText, uiTheme), 0, 0);
71
+ }
72
+
73
+ const action = details?.action ?? args?.action ?? "browse";
74
+ const badgeColor = ACTION_COLORS[action] ?? "muted";
75
+ const sections: Array<{ label?: string; lines: string[] }> = [];
76
+ const meta: string[] = [];
77
+
78
+ if (isError) {
79
+ const errorText = result.content?.find(c => c.type === "text")?.text ?? "Unknown error";
80
+ addSection(sections, "Error", [uiTheme.fg("error", errorText)], uiTheme);
81
+ } else {
82
+ if (details?.url)
83
+ meta.push(uiTheme.fg("dim", details.url.length > 50 ? `${details.url.slice(0, 47)}…` : details.url));
84
+
85
+ if (details?.screenshotPath) {
86
+ addSection(
87
+ sections,
88
+ "Screenshot",
89
+ [uiTheme.fg("toolOutput", ` ${shortenPath(details.screenshotPath)}`)],
90
+ uiTheme,
91
+ );
92
+ }
93
+
94
+ if (details?.viewport) {
95
+ meta.push(uiTheme.fg("dim", `${details.viewport.width}×${details.viewport.height}`));
96
+ }
97
+
98
+ const textContent = result.content?.find(c => c.type === "text")?.text;
99
+ if (textContent) {
100
+ const contentLines = textContent.split("\n").map(line => replaceTabs(uiTheme.fg("toolOutput", line)));
101
+ addSection(sections, "Result", contentLines, uiTheme, MAX_CONTENT_LINES);
102
+ }
103
+ }
104
+
105
+ const header = renderStatusLine(
106
+ {
107
+ title: TOOL_TITLE,
108
+ titleColor: "contentAccent",
109
+ badge: { label: action, color: isError ? "error" : badgeColor },
110
+ meta: meta.length > 0 ? meta : undefined,
111
+ },
112
+ uiTheme,
113
+ );
114
+
115
+ const outputBlock = new CachedOutputBlock();
116
+ return {
117
+ render(width: number): string[] {
118
+ const state = options.isPartial ? "pending" : isError ? "error" : "success";
119
+ return outputBlock.render({ header, state, sections, width, borderColor: F5_TOOL_BORDER_COLOR }, uiTheme);
120
+ },
121
+ invalidate() {
122
+ outputBlock.invalidate();
123
+ },
124
+ };
125
+ },
126
+
127
+ mergeCallAndResult: true,
128
+ inline: true,
129
+ };