@f5xc-salesdemos/xcsh 19.28.2 → 19.29.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": "19.
|
|
4
|
+
"version": "19.29.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",
|
|
@@ -50,12 +50,12 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
52
52
|
"@mozilla/readability": "^0.6",
|
|
53
|
-
"@f5xc-salesdemos/xcsh-stats": "19.
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "19.
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "19.
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "19.
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "19.
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "19.
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "19.29.0",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "19.29.0",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "19.29.0",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "19.29.0",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "19.29.0",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "19.29.0",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.20",
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "19.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.29.0",
|
|
21
|
+
"commit": "7fa5eedd8b420a253446f200bd667bb2e09f7320",
|
|
22
|
+
"shortCommit": "7fa5eed",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v19.29.0",
|
|
25
|
+
"commitDate": "2026-06-12T16:42:15Z",
|
|
26
|
+
"buildDate": "2026-06-12T17:15:16.199Z",
|
|
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/v19.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/7fa5eedd8b420a253446f200bd667bb2e09f7320",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.29.0"
|
|
33
33
|
};
|
|
@@ -666,3 +666,12 @@ export function addSection(
|
|
|
666
666
|
sections.push({ label: titled, lines });
|
|
667
667
|
}
|
|
668
668
|
}
|
|
669
|
+
|
|
670
|
+
export function humanizeResourceType(raw: string): string {
|
|
671
|
+
return raw
|
|
672
|
+
.replace(/^http_/, "")
|
|
673
|
+
.replace(/^tcp_/, "TCP ")
|
|
674
|
+
.replace(/_/g, " ")
|
|
675
|
+
.replace(/([a-z])(balancer|checker)/gi, "$1 $2")
|
|
676
|
+
.replace(/s$/, "");
|
|
677
|
+
}
|
|
@@ -8,6 +8,7 @@ import { CachedOutputBlock, F5_TOOL_BORDER_COLOR, renderStatusLine } from "../tu
|
|
|
8
8
|
import {
|
|
9
9
|
formatErrorMessage,
|
|
10
10
|
formatTimestamp,
|
|
11
|
+
humanizeResourceType,
|
|
11
12
|
addSection as pushSection,
|
|
12
13
|
replaceTabs,
|
|
13
14
|
stripEmpty,
|
|
@@ -91,7 +92,15 @@ function splitResultContent(textContent: string, isError: boolean): { json?: str
|
|
|
91
92
|
|
|
92
93
|
if (!isError) {
|
|
93
94
|
const pretty = tryPrettyJson(body);
|
|
94
|
-
|
|
95
|
+
if (pretty) return { json: pretty, raw: body };
|
|
96
|
+
// Body may contain "compactJSON\n\nAI guidance text" — split and discard guidance for display
|
|
97
|
+
const split = body.indexOf("\n\n");
|
|
98
|
+
if (split >= 0) {
|
|
99
|
+
const jsonPart = body.slice(0, split);
|
|
100
|
+
const prettyPart = tryPrettyJson(jsonPart);
|
|
101
|
+
if (prettyPart) return { json: prettyPart, raw: body };
|
|
102
|
+
}
|
|
103
|
+
return { raw: body };
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
// Error: body is "compactJSON\n\nguidanceText"
|
|
@@ -263,15 +272,10 @@ export const xcshApiToolRenderer = {
|
|
|
263
272
|
const emptyList = Array.isArray(parsed?.items) && (parsed!.items as unknown[]).length === 0;
|
|
264
273
|
|
|
265
274
|
if ((emptyBody || emptyList) && !guidance) {
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const rn = resourceName ? ` \u2018${resourceName}\u2019` : "";
|
|
269
|
-
if (!emptyList && status >= 200 && status < 300) {
|
|
270
|
-
if (method === "DELETE") successMessage = `Resource${rn} deleted successfully.`;
|
|
271
|
-
else if (method === "POST") successMessage = `Resource${rn} created successfully.`;
|
|
272
|
-
else if (method === "PUT" || method === "PATCH") successMessage = `Resource${rn} updated successfully.`;
|
|
275
|
+
if (emptyList) {
|
|
276
|
+
addSection("Response", [uiTheme.fg("dim", "No items found.")]);
|
|
273
277
|
}
|
|
274
|
-
|
|
278
|
+
// Empty mutation bodies ({}) are handled by the Result section below \u2014 no Response section needed
|
|
275
279
|
} else if (json && parsed) {
|
|
276
280
|
// Branch 1: List response with named items — compact summary
|
|
277
281
|
const items = parsed.items;
|
|
@@ -343,15 +347,40 @@ export const xcshApiToolRenderer = {
|
|
|
343
347
|
);
|
|
344
348
|
}
|
|
345
349
|
|
|
346
|
-
// Section
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
const
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
if (
|
|
353
|
-
|
|
354
|
-
|
|
350
|
+
// Section: Result — human-readable glyph status at the bottom of the panel
|
|
351
|
+
{
|
|
352
|
+
const resultLines: string[] = [];
|
|
353
|
+
const successIcon = uiTheme.styledSymbol("status.success", "success");
|
|
354
|
+
const errorIcon = uiTheme.styledSymbol("status.error", "error");
|
|
355
|
+
|
|
356
|
+
if (isError) {
|
|
357
|
+
const apiMessage =
|
|
358
|
+
parsed && typeof parsed.message === "string" ? stripProtobufPrefix(parsed.message) : undefined;
|
|
359
|
+
const errorLabel = apiMessage ?? (status > 0 ? `Request failed (${status})` : "Request failed");
|
|
360
|
+
resultLines.push(`${errorIcon} ${uiTheme.fg("error", errorLabel)}`);
|
|
361
|
+
if (guidance) resultLines.push(` ${uiTheme.fg("warning", `Try: ${guidance}`)}`);
|
|
362
|
+
} else if (details?.mutationVerb && details?.resourceLabel) {
|
|
363
|
+
resultLines.push(
|
|
364
|
+
`${successIcon} ${uiTheme.fg("success", `${details.mutationVerb} ${details.resourceLabel}`)}`,
|
|
365
|
+
);
|
|
366
|
+
} else if (method === "GET" && parsed) {
|
|
367
|
+
const items = parsed.items;
|
|
368
|
+
if (Array.isArray(items)) {
|
|
369
|
+
const rawType = pathParts.at(-1) ?? "items";
|
|
370
|
+
const typeLabel = humanizeResourceType(rawType);
|
|
371
|
+
const plural = items.length === 1 ? typeLabel : `${typeLabel}s`;
|
|
372
|
+
resultLines.push(`${successIcon} ${uiTheme.fg("success", `${items.length} ${plural}`)}`);
|
|
373
|
+
} else {
|
|
374
|
+
const rawType = pathParts.at(-2) ?? "";
|
|
375
|
+
const name = resourceName ?? "";
|
|
376
|
+
const label = rawType && name ? `${humanizeResourceType(rawType)} '${name}'` : displayPath;
|
|
377
|
+
resultLines.push(`${successIcon} ${uiTheme.fg("success", `Loaded ${label}`)}`);
|
|
378
|
+
}
|
|
379
|
+
} else if (status >= 200 && status < 300) {
|
|
380
|
+
resultLines.push(`${successIcon} ${uiTheme.fg("success", "OK")}`);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (resultLines.length > 0) addSection("Result", resultLines);
|
|
355
384
|
}
|
|
356
385
|
|
|
357
386
|
// --- Render with CachedOutputBlock ---
|
package/src/tools/xcsh-api.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { type Static, Type } from "@sinclair/typebox";
|
|
|
5
5
|
import xcshApiDescription from "../prompts/tools/xcsh-api.md" with { type: "text" };
|
|
6
6
|
import { type ContextEnv, createContextEnv } from "../services/context-env";
|
|
7
7
|
import type { ToolSession } from ".";
|
|
8
|
+
import { humanizeResourceType } from "./render-utils";
|
|
8
9
|
|
|
9
10
|
// Namespace filtering driven by x-f5xc-namespace-profile from enriched API specs.
|
|
10
11
|
|
|
@@ -108,6 +109,10 @@ export interface XcshApiToolDetails {
|
|
|
108
109
|
batchTotalItems?: number;
|
|
109
110
|
/** The resolved JSON body string sent to the API (after $F5XC_* expansion). */
|
|
110
111
|
resolvedPayload?: string;
|
|
112
|
+
/** Human-readable verb for mutation results. */
|
|
113
|
+
mutationVerb?: "Created" | "Updated" | "Deleted";
|
|
114
|
+
/** Human-readable resource label, e.g. "load balancer 'rm-headers'". */
|
|
115
|
+
resourceLabel?: string;
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
type XcshApiResult = AgentToolResult<XcshApiToolDetails> & { isError?: boolean };
|
|
@@ -817,13 +822,6 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
817
822
|
// Label changes help the model echo type names in its response (Finding 13).
|
|
818
823
|
// INTERACTION EFFECT: this change + TCP protocol label are synergistic (Finding 31).
|
|
819
824
|
const pathSegs = resolvedPath.split("/").filter(Boolean);
|
|
820
|
-
const humanizeResourceType = (raw: string): string =>
|
|
821
|
-
raw
|
|
822
|
-
.replace(/^http_/, "")
|
|
823
|
-
.replace(/^tcp_/, "TCP ")
|
|
824
|
-
.replace(/_/g, " ")
|
|
825
|
-
.replace(/([a-z])(balancer|checker)/gi, "$1 $2")
|
|
826
|
-
.replace(/s$/, "");
|
|
827
825
|
let resourceLabel: string;
|
|
828
826
|
if (params.method === "POST") {
|
|
829
827
|
const rawType = pathSegs.at(-1) ?? "";
|
|
@@ -835,6 +833,9 @@ export class XcshApiTool implements AgentTool<typeof xcshApiSchema, XcshApiToolD
|
|
|
835
833
|
const rawType = pathSegs.at(-2) ?? "";
|
|
836
834
|
resourceLabel = name && rawType ? `${humanizeResourceType(rawType)} ${name}` : resolvedPath;
|
|
837
835
|
}
|
|
836
|
+
detail.mutationVerb = verb as "Created" | "Updated" | "Deleted";
|
|
837
|
+
detail.resourceLabel = resourceLabel;
|
|
838
|
+
|
|
838
839
|
// POST returns the full resource; PUT/DELETE return {}.
|
|
839
840
|
// Only claim response contains the resource for POST to avoid misleading the model.
|
|
840
841
|
const resourceHint =
|