@f5-sales-demo/xcsh 19.95.4 → 19.97.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.
Files changed (36) hide show
  1. package/package.json +8 -8
  2. package/src/browser/capabilities.generated.ts +7 -3
  3. package/src/browser/capabilities.json +6 -2
  4. package/src/browser/chat-conformance.json +140 -1
  5. package/src/browser/chat-handler.ts +24 -1
  6. package/src/browser/chat-protocol.ts +28 -0
  7. package/src/browser/host-profiles.ts +5 -2
  8. package/src/cli/args.ts +233 -131
  9. package/src/cli/flag-spec.ts +219 -0
  10. package/src/cli/office-cli.ts +26 -3
  11. package/src/commands/launch.ts +3 -105
  12. package/src/config/settings-schema.ts +9 -2
  13. package/src/extensibility/extensions/bundled/sandbox-guard.ts +0 -0
  14. package/src/extensibility/skills.ts +27 -7
  15. package/src/extensibility/slash-commands.ts +20 -0
  16. package/src/internal-urls/build-info-runtime.ts +21 -2
  17. package/src/internal-urls/build-info.generated.ts +8 -8
  18. package/src/internal-urls/plugin-resolve.test.ts +61 -0
  19. package/src/internal-urls/plugin-resolve.ts +63 -9
  20. package/src/internal-urls/xcsh-protocol.active-model.test.ts +53 -0
  21. package/src/internal-urls/xcsh-protocol.ts +10 -1
  22. package/src/main.ts +50 -23
  23. package/src/modes/print-mode.ts +21 -6
  24. package/src/modes/rpc/rpc-client.ts +10 -0
  25. package/src/modes/rpc/rpc-mode.ts +8 -0
  26. package/src/modes/rpc/rpc-types.ts +9 -0
  27. package/src/prompts/internal-urls/active-model.md +32 -0
  28. package/src/sandbox/command-operands.ts +280 -0
  29. package/src/sandbox/enforce.ts +48 -4
  30. package/src/sandbox/session-policy.ts +0 -0
  31. package/src/sdk.ts +16 -0
  32. package/src/session/active-model.ts +86 -0
  33. package/src/session/agent-session.ts +29 -2
  34. package/src/tools/bash-skill-urls.ts +114 -61
  35. package/src/tools/bash.ts +24 -21
  36. package/src/tools/shell-lex.ts +652 -0
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5-sales-demo/xcsh",
4
- "version": "19.95.4",
4
+ "version": "19.97.0",
5
5
  "description": "Coding agent CLI with read, bash, edit, write tools and session management",
6
6
  "homepage": "https://github.com/f5-sales-demo/xcsh",
7
7
  "author": "Can Boluk",
@@ -57,13 +57,13 @@
57
57
  "dependencies": {
58
58
  "@agentclientprotocol/sdk": "1.3.0",
59
59
  "@mozilla/readability": "^0.6",
60
- "@f5-sales-demo/xcsh-stats": "19.95.4",
61
- "@f5-sales-demo/pi-agent-core": "19.95.4",
62
- "@f5-sales-demo/pi-ai": "19.95.4",
63
- "@f5-sales-demo/pi-natives": "19.95.4",
64
- "@f5-sales-demo/pi-resource-management": "19.95.4",
65
- "@f5-sales-demo/pi-tui": "19.95.4",
66
- "@f5-sales-demo/pi-utils": "19.95.4",
60
+ "@f5-sales-demo/xcsh-stats": "19.97.0",
61
+ "@f5-sales-demo/pi-agent-core": "19.97.0",
62
+ "@f5-sales-demo/pi-ai": "19.97.0",
63
+ "@f5-sales-demo/pi-natives": "19.97.0",
64
+ "@f5-sales-demo/pi-resource-management": "19.97.0",
65
+ "@f5-sales-demo/pi-tui": "19.97.0",
66
+ "@f5-sales-demo/pi-utils": "19.97.0",
67
67
  "@sinclair/typebox": "^0.34",
68
68
  "@xterm/headless": "^6.0",
69
69
  "ajv": "^8.20",
@@ -26,7 +26,7 @@ export interface ExtensionCapabilities {
26
26
 
27
27
  export const EXTENSION_CAPABILITIES: ExtensionCapabilities = {
28
28
  "version": "0.1.0",
29
- "contractVersion": "1.11.0",
29
+ "contractVersion": "1.12.0",
30
30
  "multiPortDiscovery": true,
31
31
  "protocol": "tool_request/result",
32
32
  "tools": [
@@ -858,7 +858,11 @@ export const EXTENSION_CAPABILITIES: ExtensionCapabilities = {
858
858
  "host_tool_cancel",
859
859
  "configure",
860
860
  "configure_ack",
861
- "configure_error"
861
+ "configure_error",
862
+ "list_skills",
863
+ "skills",
864
+ "list_commands",
865
+ "commands"
862
866
  ],
863
867
  "description": "User ↔ xcsh chat over the bridge. The extension side panel sends chat_request (with mode and page-context snapshot); xcsh streams chat_delta tokens then a terminal chat_done (with reference links) or chat_error. Chat ids are prefixed \"c-\". Tool calls during a turn use the normal tool_request flow. chat_stop halts a streaming response. chat_tool_notice is emitted by the EXTENSION (the service worker) to the panel as a best-effort UI signal when a tool runs during a turn — it is NOT sent by xcsh; xcsh must not produce it to avoid double-rendering in the panel.",
864
868
  "promptHints": {
@@ -878,6 +882,6 @@ export const EXTENSION_CAPABILITIES: ExtensionCapabilities = {
878
882
  }
879
883
  };
880
884
 
881
- export const EXTENSION_CONTRACT_VERSION = "1.11.0";
885
+ export const EXTENSION_CONTRACT_VERSION = "1.12.0";
882
886
 
883
887
  export const EXTENSION_TOOL_NAMES: readonly string[] = ["ping","capabilities","reload","debug_exec","detach","set_bridge_port","navigate","login","scroll_to","resize_window","tabs_list","tabs_create","tabs_close","click","click_element","click_xy","type_text","form_input","key_press","select_option","label_select","file_upload","read_ax","get_page_text","query_dom","find","wait_for","assert_text","screenshot","read_console","read_network","diag_suspension","diag_bridges","diag_activation","diag_ttft","capture_login_flow","wait_for_api_response","get_page_context","javascript_tool","browser_batch","set_explain_mode","annotate"];
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": "0.1.0",
3
- "contractVersion": "1.11.0",
3
+ "contractVersion": "1.12.0",
4
4
  "multiPortDiscovery": true,
5
5
  "protocol": "tool_request/result",
6
6
  "tools": [
@@ -751,7 +751,11 @@
751
751
  "host_tool_cancel",
752
752
  "configure",
753
753
  "configure_ack",
754
- "configure_error"
754
+ "configure_error",
755
+ "list_skills",
756
+ "skills",
757
+ "list_commands",
758
+ "commands"
755
759
  ],
756
760
  "description": "User ↔ xcsh chat over the bridge. The extension side panel sends chat_request (with mode and page-context snapshot); xcsh streams chat_delta tokens then a terminal chat_done (with reference links) or chat_error. Chat ids are prefixed \"c-\". Tool calls during a turn use the normal tool_request flow. chat_stop halts a streaming response. chat_tool_notice is emitted by the EXTENSION (the service worker) to the panel as a best-effort UI signal when a tool runs during a turn — it is NOT sent by xcsh; xcsh must not produce it to avoid double-rendering in the panel.",
757
761
  "promptHints": {
@@ -1,5 +1,5 @@
1
1
  {
2
- "contractVersion": "1.11.0",
2
+ "contractVersion": "1.12.0",
3
3
  "schemas": {
4
4
  "chat_request": {
5
5
  "type": "object",
@@ -599,6 +599,68 @@
599
599
  "type": "boolean"
600
600
  }
601
601
  }
602
+ },
603
+ "list_skills": {
604
+ "type": "object",
605
+ "required": ["type"],
606
+ "properties": {
607
+ "type": {
608
+ "const": "list_skills",
609
+ "type": "string"
610
+ }
611
+ }
612
+ },
613
+ "skills": {
614
+ "type": "object",
615
+ "required": ["type", "skills"],
616
+ "properties": {
617
+ "type": {
618
+ "const": "skills",
619
+ "type": "string"
620
+ },
621
+ "skills": {
622
+ "type": "array",
623
+ "items": {
624
+ "type": "object",
625
+ "required": ["name", "description"],
626
+ "properties": {
627
+ "name": { "type": "string", "minLength": 1 },
628
+ "description": { "type": "string" }
629
+ }
630
+ }
631
+ }
632
+ }
633
+ },
634
+ "list_commands": {
635
+ "type": "object",
636
+ "required": ["type"],
637
+ "properties": {
638
+ "type": {
639
+ "const": "list_commands",
640
+ "type": "string"
641
+ }
642
+ }
643
+ },
644
+ "commands": {
645
+ "type": "object",
646
+ "required": ["type", "commands"],
647
+ "properties": {
648
+ "type": {
649
+ "const": "commands",
650
+ "type": "string"
651
+ },
652
+ "commands": {
653
+ "type": "array",
654
+ "items": {
655
+ "type": "object",
656
+ "required": ["name", "description"],
657
+ "properties": {
658
+ "name": { "type": "string", "minLength": 1 },
659
+ "description": { "type": "string" }
660
+ }
661
+ }
662
+ }
663
+ }
602
664
  }
603
665
  },
604
666
  "examples": {
@@ -814,6 +876,38 @@
814
876
  "type": "host_tool_cancel",
815
877
  "id": "7295551234567891",
816
878
  "targetId": "7295551234567890"
879
+ },
880
+ "list_skills": {
881
+ "type": "list_skills"
882
+ },
883
+ "skills": {
884
+ "type": "skills",
885
+ "skills": [
886
+ {
887
+ "name": "meddpicc:deal-review",
888
+ "description": "Facilitate a structured weekly MEDDPICC deal review"
889
+ }
890
+ ]
891
+ },
892
+ "skills_empty": {
893
+ "type": "skills",
894
+ "skills": []
895
+ },
896
+ "list_commands": {
897
+ "type": "list_commands"
898
+ },
899
+ "commands": {
900
+ "type": "commands",
901
+ "commands": [
902
+ {
903
+ "name": "meddpicc:qualify-deal",
904
+ "description": "Score and qualify a deal using the MEDDPICC framework"
905
+ }
906
+ ]
907
+ },
908
+ "commands_empty": {
909
+ "type": "commands",
910
+ "commands": []
817
911
  }
818
912
  },
819
913
  "invalid": [
@@ -1014,6 +1108,51 @@
1014
1108
  "type": "configure",
1015
1109
  "token": ""
1016
1110
  }
1111
+ },
1112
+ {
1113
+ "schema": "commands",
1114
+ "why": "commands must be an array, not a name-keyed map",
1115
+ "value": {
1116
+ "type": "commands",
1117
+ "commands": {
1118
+ "meddpicc:qualify-deal": "Score and qualify a deal"
1119
+ }
1120
+ }
1121
+ },
1122
+ {
1123
+ "schema": "commands",
1124
+ "why": "an entry with no description has nothing to render in the menu",
1125
+ "value": {
1126
+ "type": "commands",
1127
+ "commands": [
1128
+ {
1129
+ "name": "meddpicc:qualify-deal"
1130
+ }
1131
+ ]
1132
+ }
1133
+ },
1134
+ {
1135
+ "schema": "commands",
1136
+ "why": "an entry with an empty name cannot be sent back as a prompt",
1137
+ "value": {
1138
+ "type": "commands",
1139
+ "commands": [
1140
+ {
1141
+ "name": "",
1142
+ "description": "Score and qualify a deal"
1143
+ }
1144
+ ]
1145
+ }
1146
+ },
1147
+ {
1148
+ "schema": "skills",
1149
+ "why": "skills must be an array, not a name-keyed map",
1150
+ "value": {
1151
+ "type": "skills",
1152
+ "skills": {
1153
+ "meddpicc:coach": "MEDDPICC coaching"
1154
+ }
1155
+ }
1017
1156
  }
1018
1157
  ]
1019
1158
  }
@@ -4,6 +4,7 @@ import type { AssistantMessage, ImageContent } from "@f5-sales-demo/pi-ai";
4
4
  import { settings } from "../config/settings";
5
5
  import { DEFAULT_MODEL_ROLE } from "../config/settings-schema";
6
6
  import { toSkillSummaries } from "../extensibility/skills";
7
+ import { expandSlashCommand, toSlashCommandSummaries } from "../extensibility/slash-commands";
7
8
  import {
8
9
  isRpcHostToolResult,
9
10
  isRpcHostToolUpdate,
@@ -28,6 +29,7 @@ import {
28
29
  isChatRequest,
29
30
  isChatStop,
30
31
  isConfigure,
32
+ isListCommands,
31
33
  isListSkills,
32
34
  isSetHostTools,
33
35
  type PageContextSnapshot,
@@ -35,6 +37,7 @@ import {
35
37
  type SetHostToolsAck,
36
38
  type SetHostToolsError,
37
39
  type SkillsList,
40
+ type SlashCommandsList,
38
41
  } from "./chat-protocol";
39
42
  import { CONSOLE_ROUTES } from "./console-routes.generated";
40
43
  import type { BridgeServer } from "./extension-bridge";
@@ -110,6 +113,9 @@ export class ChatHandler {
110
113
  // Skills enumeration (#2311): the pane asks for the loaded skills to populate
111
114
  // the composer's Skills submenu.
112
115
  else if (isListSkills(msg)) this.#handleListSkills();
116
+ // Slash-command enumeration: the pane asks for the session's file-based
117
+ // commands to populate the composer's `/` menu.
118
+ else if (isListCommands(msg)) this.#handleListCommands();
113
119
  else if (isRpcHostToolResult(msg)) this.#hostToolBridge.handleResult(msg as unknown as HostToolResult);
114
120
  else if (isRpcHostToolUpdate(msg)) this.#hostToolBridge.handleUpdate(msg as unknown as HostToolUpdate);
115
121
  });
@@ -179,7 +185,14 @@ export class ChatHandler {
179
185
  // model's read/grep/bash calls pass the gate. Session-scoped, in-memory, deduped.
180
186
  const contextPaths = Array.isArray(req.contextPaths) ? sanitizeContextPaths(req.contextPaths) : [];
181
187
  if (contextPaths.length > 0) grantSandboxPaths(contextPaths);
182
- const prompt = composeChatPrompt(req.text, req.context, req.mode, this.#server.clientHost, contextPaths);
188
+ // Expand a file-based slash command HERE, on the user's raw text, before it is
189
+ // composed. Two reasons it cannot live in `AgentSession.prompt` for this surface:
190
+ // we call `prompt` with `expandPromptTemplates: false`, and `composeChatPrompt`
191
+ // appends the user's text last, so by then the string no longer starts with `/`.
192
+ // A `/name` that matches nothing is returned unchanged, which is what keeps the
193
+ // skill convention (same spelling, handled by the system prompt) working.
194
+ const text = expandSlashCommand(req.text, [...this.#session.slashCommands]);
195
+ const prompt = composeChatPrompt(text, req.context, req.mode, this.#server.clientHost, contextPaths);
183
196
  // Photo/image attachments ride as base64 vision blocks (the model is
184
197
  // vision-capable); text attachments are already folded into req.text upstream.
185
198
  const images: ImageContent[] | undefined = req.images?.map(img => ({
@@ -433,6 +446,16 @@ export class ChatHandler {
433
446
  this.#server.send({ type: "skills", skills: toSkillSummaries(this.#session.skills) } satisfies SkillsList);
434
447
  }
435
448
 
449
+ /** Reply to `list_commands` with the session's file-based slash commands (name +
450
+ * description) so the pane can populate the composer's `/` menu. Pure read — the
451
+ * commands are already discovered; the template bodies never cross the wire. */
452
+ #handleListCommands(): void {
453
+ this.#server.send({
454
+ type: "commands",
455
+ commands: toSlashCommandSummaries(this.#session.slashCommands),
456
+ } satisfies SlashCommandsList);
457
+ }
458
+
436
459
  #sendTerminal(chat: ActiveChat, frame: ChatDone | ChatError): void {
437
460
  if (chat.terminalSent) return;
438
461
  chat.terminalSent = true;
@@ -129,6 +129,30 @@ export interface SkillsList {
129
129
  skills: SkillInfo[];
130
130
  }
131
131
 
132
+ /** Client → engine: enumerate the session's file-based slash commands for the
133
+ * composer's `/` menu. Sent once after the pane connects, beside `list_skills`. */
134
+ export interface ListCommands {
135
+ type: "list_commands";
136
+ }
137
+
138
+ /**
139
+ * One slash command surfaced to the pane's `/` menu.
140
+ *
141
+ * Deliberately NOT the command's `content`: that body is a prompt template which can run
142
+ * to hundreds of lines, and the menu needs a label. It also keeps the plugin author's
143
+ * instructions off the wire, where nothing reads them.
144
+ */
145
+ export interface SlashCommandInfo {
146
+ name: string;
147
+ description: string;
148
+ }
149
+
150
+ /** Engine → client: the session's live slash commands, in load order. */
151
+ export interface SlashCommandsList {
152
+ type: "commands";
153
+ commands: SlashCommandInfo[];
154
+ }
155
+
132
156
  export interface ChatStop {
133
157
  type: "chat_stop";
134
158
  id: string;
@@ -312,6 +336,10 @@ export function isListSkills(msg: Record<string, unknown>): boolean {
312
336
  return msg.type === "list_skills";
313
337
  }
314
338
 
339
+ export function isListCommands(msg: Record<string, unknown>): boolean {
340
+ return msg.type === "list_commands";
341
+ }
342
+
315
343
  export function isPickPath(msg: Record<string, unknown>): boolean {
316
344
  return msg.type === "pick_path" && (msg.mode === "file" || msg.mode === "folder");
317
345
  }
@@ -94,9 +94,11 @@ SAFETY — NEVER DO THESE:
94
94
  const OFFICE_NATIVE_TOOLS_NOTE = `
95
95
  NATIVE TOOLS: Beyond the document host tools, you have xcsh's full local toolset — \`bash\` (run shell commands, including CLIs like \`az\`, \`gh\`, \`terraform\`, \`git\` when installed and authenticated), file tools (\`read\`/\`write\`/\`edit\`), and \`grep\` — plus any skills available in this workspace. Reach for them when the task genuinely needs them (pull live data with a CLI, read a local file the user points you at). Prefer the document host tools for document work. Your file tools and shell are confined to the folder xcsh was launched from.
96
96
 
97
- This pane runs NO MCP servers and NO plugin-provided tools — the tools listed above (plus the document host tools) are everything you have. Do not look for, read, or report on plugin/MCP manifests: to use a cloud or SCM CLI, invoke it directly with \`bash\` (e.g. \`az account show\`, \`gh repo view\`). The user sees your narration, so don't describe missing plugins as failures — just use the CLI.
97
+ This pane runs NO MCP servers and exposes NO plugin-provided TOOLS — the tools listed above (plus the document host tools) are everything you can call. To use a cloud or SCM CLI, invoke it directly with \`bash\` (e.g. \`az account show\`, \`gh repo view\`) rather than hunting for an MCP server that would provide it. The user sees your narration, so don't describe a missing MCP tool as a failure — just use the CLI.
98
98
 
99
- SKILLS: When a message begins with \`/<skill-name>\` naming one of your available skills, treat it as a request to USE that skill read its instructions (open \`skill://<skill-name>\`, or its SKILL.md via \`read\`) and follow them, applying any text after the name as the skill's input.
99
+ Plugin RESOURCES are a different thing and they ARE available to you: an installed plugin's skills, slash commands, schemas, templates and engines. Read them through \`xcsh://plugin/<name>\` (summary), \`xcsh://plugin/<name>/<key>\` (a declared resource such as \`schema\`), or \`xcsh://plugin/<name>/file/<path>\` (any file in the plugin), and run a plugin's engine with \`bash\` (e.g. \`bun xcsh://plugin/<name>/file/engine/cli.ts <command>\`). When a plugin declares a deterministic engine, use it never recompute by hand what the engine computes.
100
+
101
+ SKILLS AND SLASH COMMANDS: A message beginning \`/<name>\` names either a slash command (already expanded for you before it arrived — just follow the instructions you were given) or one of your available skills. For a skill, treat it as a request to USE it — read its instructions (open \`skill://<skill-name>\`, or its SKILL.md via \`read\`) and follow them, applying any text after the name as the skill's input. Names from a plugin are prefixed with the plugin, e.g. \`/meddpicc:deal-review\`.
100
102
 
101
103
  SAFETY:
102
104
  - NEVER kill, stop, inspect, or manage the xcsh \`office serve\` process, its bridge ports, or any xcsh process — that bridge IS you; ending it ends the session.
@@ -122,6 +124,7 @@ TOOLS: Discover the workbook before you answer, then reach for the tool that mat
122
124
  - Use \`read_table\` for structured Excel Tables (it tracks the real extent), \`get_formulas\` to see the formulas behind cells, \`get_cell_metadata\` for cell types/number formats, and \`read_named_range\` to read a defined name.
123
125
  - Use \`sort_filter_table\` to sort or filter a Table by column.
124
126
  - Use \`read_range\`/\`write_range\` for arbitrary cell ranges (bare or sheet-qualified like Sheet2!A1:B10), and \`list_sheets\` when you only need the tab names.
127
+ - Use \`add_sheet\` to build a report on its own tab instead of overwriting the user's data. It is idempotent — an existing tab of that name is reused — so add first, then \`write_range\` block by block, and do not read the sheet back to confirm.
125
128
  ${OFFICE_NATIVE_TOOLS_NOTE}
126
129
 
127
130
  BEHAVIOR: