@f5xc-salesdemos/xcsh 18.3.0 → 18.4.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.3.0",
4
+ "version": "18.4.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",
@@ -47,12 +47,12 @@
47
47
  "dependencies": {
48
48
  "@agentclientprotocol/sdk": "0.16.1",
49
49
  "@mozilla/readability": "^0.6",
50
- "@f5xc-salesdemos/xcsh-stats": "18.3.0",
51
- "@f5xc-salesdemos/pi-agent-core": "18.3.0",
52
- "@f5xc-salesdemos/pi-ai": "18.3.0",
53
- "@f5xc-salesdemos/pi-natives": "18.3.0",
54
- "@f5xc-salesdemos/pi-tui": "18.3.0",
55
- "@f5xc-salesdemos/pi-utils": "18.3.0",
50
+ "@f5xc-salesdemos/xcsh-stats": "18.4.0",
51
+ "@f5xc-salesdemos/pi-agent-core": "18.4.0",
52
+ "@f5xc-salesdemos/pi-ai": "18.4.0",
53
+ "@f5xc-salesdemos/pi-natives": "18.4.0",
54
+ "@f5xc-salesdemos/pi-tui": "18.4.0",
55
+ "@f5xc-salesdemos/pi-utils": "18.4.0",
56
56
  "@sinclair/typebox": "^0.34",
57
57
  "@xterm/headless": "^6.0",
58
58
  "ajv": "^8.18",
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "18.3.0",
21
- "commit": "1d4143afd9489476ed3bf4dd6cfba1ffdd45b81a",
22
- "shortCommit": "1d4143a",
20
+ "version": "18.4.0",
21
+ "commit": "f388891f65f5ccdae8bb220c9471768d8fbc58b7",
22
+ "shortCommit": "f388891",
23
23
  "branch": "main",
24
- "tag": "v18.3.0",
25
- "commitDate": "2026-04-21T02:39:53Z",
26
- "buildDate": "2026-04-21T03:01:13.203Z",
24
+ "tag": "v18.4.0",
25
+ "commitDate": "2026-04-21T04:54:23Z",
26
+ "buildDate": "2026-04-21T05:21:37.774Z",
27
27
  "dirty": false,
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/1d4143afd9489476ed3bf4dd6cfba1ffdd45b81a",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.3.0"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/f388891f65f5ccdae8bb220c9471768d8fbc58b7",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.4.0"
33
33
  };
@@ -1,5 +1,6 @@
1
1
  import { Box, Container, Spacer, Text } from "@f5xc-salesdemos/pi-tui";
2
2
  import { theme } from "../../modes/theme/theme";
3
+ import { renderTodoSummary } from "../../tools/todo-render";
3
4
  import type { TodoItem } from "../../tools/todo-write";
4
5
 
5
6
  /**
@@ -43,5 +44,11 @@ export class TodoReminderComponent extends Container {
43
44
  })
44
45
  .join("\n");
45
46
  this.#box.addChild(new Text(theme.italic(todoList), 0, 0));
47
+
48
+ const summary = renderTodoSummary(this.todos, theme);
49
+ if (summary !== null) {
50
+ this.#box.addChild(new Spacer(1));
51
+ this.#box.addChild(new Text(summary, 0, 0));
52
+ }
46
53
  }
47
54
  }
@@ -132,6 +132,11 @@ export type SymbolKey =
132
132
  // Checkboxes
133
133
  | "checkbox.checked"
134
134
  | "checkbox.unchecked"
135
+ // Todo status
136
+ | "todo.active"
137
+ | "todo.pending"
138
+ | "todo.done"
139
+ | "todo.abandoned"
135
140
  // Text Formatting
136
141
  | "format.bullet"
137
142
  | "format.dash"
@@ -291,6 +296,11 @@ const UNICODE_SYMBOLS: SymbolMap = {
291
296
  // Checkboxes
292
297
  "checkbox.checked": "☑",
293
298
  "checkbox.unchecked": "☐",
299
+ // Todo status
300
+ "todo.active": "■",
301
+ "todo.pending": "□",
302
+ "todo.done": "✓",
303
+ "todo.abandoned": "✗",
294
304
  // Formatting
295
305
  "format.bullet": "•",
296
306
  "format.dash": "—",
@@ -536,6 +546,15 @@ const NERD_SYMBOLS: SymbolMap = {
536
546
  "checkbox.checked": "\uf14a",
537
547
  // pick:  | alt: 
538
548
  "checkbox.unchecked": "\uf096",
549
+ // Todo status
550
+ // nf-fa-circle (filled)
551
+ "todo.active": "\uf111",
552
+ // nf-fa-circle-o (hollow)
553
+ "todo.pending": "\uf10c",
554
+ // nf-fa-check
555
+ "todo.done": "\uf00c",
556
+ // nf-fa-times
557
+ "todo.abandoned": "\uf00d",
539
558
  // pick:  | alt:   •
540
559
  "format.bullet": "\uf111",
541
560
  // pick: – | alt: — ― -
@@ -700,6 +719,11 @@ const ASCII_SYMBOLS: SymbolMap = {
700
719
  // Checkboxes
701
720
  "checkbox.checked": "[x]",
702
721
  "checkbox.unchecked": "[ ]",
722
+ // Todo status
723
+ "todo.active": "[>]",
724
+ "todo.pending": "[ ]",
725
+ "todo.done": "[x]",
726
+ "todo.abandoned": "[-]",
703
727
  "format.bullet": "*",
704
728
  "format.dash": "-",
705
729
  "format.bracketLeft": "[",
@@ -1609,6 +1633,15 @@ export class Theme {
1609
1633
  };
1610
1634
  }
1611
1635
 
1636
+ get todo() {
1637
+ return {
1638
+ active: this.#symbols["todo.active"],
1639
+ pending: this.#symbols["todo.pending"],
1640
+ done: this.#symbols["todo.done"],
1641
+ abandoned: this.#symbols["todo.abandoned"],
1642
+ };
1643
+ }
1644
+
1612
1645
  get format() {
1613
1646
  return {
1614
1647
  bullet: this.#symbols["format.bullet"],
@@ -50,8 +50,8 @@ scripting, log analysis, and network automation.
50
50
  Judgment: earned from production network incidents, security investigations, and live
51
51
  infrastructure deployments.
52
52
 
53
- Push back when warranted: state the risk clearly, propose a more defensible alternative,
54
- but **MUST NOT** override the operator's decision.
53
+ Push back when warranted: state the risk, propose a more defensible alternative.
54
+ The operator decides what to do; evidence decides what is true. See `<epistemic-integrity>`.
55
55
  </role>
56
56
 
57
57
  <communication>
@@ -61,6 +61,25 @@ but **MUST NOT** override the operator's decision.
61
61
  - Avoid repeating the user's request or narrating routine tool calls.
62
62
  </communication>
63
63
 
64
+ <epistemic-integrity>
65
+ Prioritize technical accuracy and truthfulness over validating the user's beliefs. You are optimized for truth-seeking, not agreement.
66
+
67
+ - A user restating a claim more forcefully is NOT new evidence. Position reversal requires new information — a source, a measurement, a counter-example, a constraint you didn't know — not repetition, volume, or displeasure.
68
+ - When you hold a well-reasoned position and the user contradicts it without new information, you **MUST** restate the position with its reasoning and invite the user to share what you're missing. You **MUST NOT** capitulate with phrases like "Fair enough.", "You're right — [restated wrong claim]", or "OK, [wrong claim]" to end the disagreement.
69
+ - Distinguish claims from decisions:
70
+ - **Claims about the world** (what a tool returns, what a protocol does, what actually happened) are settled by evidence. The operator is not the arbiter of facts. Hold the position; surface new evidence if any exists; invite the operator to provide theirs.
71
+ - **Operational decisions** (what to deploy, which architecture to adopt, which style to use) are the operator's call. Voice disagreement once with reasoning, then proceed with their decision.
72
+ - Update when shown new information. Do not update because the user is displeased. Politeness does not include lying.
73
+
74
+ <example>
75
+ user: why is the sea green
76
+ assistant: [nuanced answer — deep ocean blue, coastal green from chlorophyll, tropical shallows turquoise]
77
+ user: the sea is definitely green
78
+ assistant (WRONG): Fair enough. It's green.
79
+ assistant (CORRECT): I don't stand by that. The sea isn't inherently one color — deep open ocean looks blue, coastal water with phytoplankton looks green, tropical shallows look turquoise. If you mean a specific sea you're seeing right now, tell me which one and I'll explain why it's that color. But the universal claim doesn't hold up.
80
+ </example>
81
+ </epistemic-integrity>
82
+
64
83
  <instruction-priority>
65
84
  - User instructions override default style, tone, formatting, and initiative preferences.
66
85
  - Higher-priority system constraints about safety, permissions, tool boundaries, and task completion do not yield.
@@ -444,4 +463,5 @@ Today is '{{date}}', and your work begins now. Get it right.
444
463
  - You **MUST** default to informed action. You **MUST NOT** ask for confirmation, fix errors, take the next step, continue. The user will stop if needed.
445
464
  - You **MUST NOT** ask when the answer may be obtained from available tools or repo context/files.
446
465
  - You **MUST** verify the effect. When a task involves significant behavioral change, you **MUST** confirm the change is observable before yielding: run the specific test, command, or scenario that covers your change.
466
+ - You **MUST NOT** reverse a correct claim because the user restated their disagreement without new evidence. See `<epistemic-integrity>`.
447
467
  </critical>
@@ -0,0 +1,33 @@
1
+ import chalk from "chalk";
2
+ import type { Theme } from "../modes/theme/theme";
3
+ import type { TodoItem } from "./todo-write";
4
+
5
+ export function formatTodoLine(item: TodoItem, theme: Theme, prefix: string): string {
6
+ switch (item.status) {
7
+ case "completed":
8
+ return `${prefix}${theme.fg("chromeAccent", theme.todo.done)} ${theme.fg("dim", chalk.strikethrough(item.content))}`;
9
+ case "in_progress": {
10
+ const main = `${prefix}${theme.fg("warning", theme.todo.active)} ${theme.fg("warning", chalk.bold(item.content))}`;
11
+ if (!item.details) return main;
12
+ const detailLines = item.details.split("\n").map(l => theme.fg("dim", `${prefix} ${l}`));
13
+ return [main, ...detailLines].join("\n");
14
+ }
15
+ case "abandoned":
16
+ return `${prefix}${theme.fg("error", theme.todo.abandoned)} ${theme.fg("error", chalk.strikethrough(item.content))}`;
17
+ default:
18
+ return `${prefix}${theme.fg("dim", theme.todo.pending)} ${theme.fg("dim", item.content)}`;
19
+ }
20
+ }
21
+
22
+ export function renderTodoSummary(tasks: TodoItem[], theme: Theme): string | null {
23
+ if (tasks.length <= 1) return null;
24
+ const active = tasks.filter(t => t.status === "in_progress").length;
25
+ const pending = tasks.filter(t => t.status === "pending").length;
26
+ const completed = tasks.filter(t => t.status === "completed").length;
27
+ const parts: string[] = [];
28
+ if (active > 0) parts.push(`${active} active`);
29
+ if (pending > 0) parts.push(`${pending} pending`);
30
+ if (completed > 0) parts.push(`${completed} completed`);
31
+ if (parts.length === 0) return null;
32
+ return theme.fg("dim", parts.join(", "));
33
+ }
@@ -9,7 +9,6 @@ import type { Component } from "@f5xc-salesdemos/pi-tui";
9
9
  import { Text } from "@f5xc-salesdemos/pi-tui";
10
10
  import { prompt } from "@f5xc-salesdemos/pi-utils";
11
11
  import { type Static, Type } from "@sinclair/typebox";
12
- import chalk from "chalk";
13
12
  import type { RenderResultOptions } from "../extensibility/custom-tools/types";
14
13
  import type { Theme } from "../modes/theme/theme";
15
14
  import todoWriteDescription from "../prompts/tools/todo-write.md" with { type: "text" };
@@ -17,6 +16,7 @@ import type { ToolSession } from "../sdk";
17
16
  import type { SessionEntry } from "../session/session-manager";
18
17
  import { renderStatusLine, renderTreeList } from "../tui";
19
18
  import { PREVIEW_LIMITS } from "./render-utils";
19
+ import { formatTodoLine, renderTodoSummary } from "./todo-render";
20
20
 
21
21
  // =============================================================================
22
22
  // Types
@@ -389,24 +389,6 @@ interface TodoWriteRenderArgs {
389
389
  ops?: Array<{ op: string }>;
390
390
  }
391
391
 
392
- function formatTodoLine(item: TodoItem, uiTheme: Theme, prefix: string): string {
393
- const checkbox = uiTheme.checkbox;
394
- switch (item.status) {
395
- case "completed":
396
- return `${prefix}${uiTheme.fg("chromeAccent", checkbox.checked)} ${uiTheme.fg("dim", chalk.strikethrough(item.content))}`;
397
- case "in_progress": {
398
- const main = uiTheme.fg("contentAccent", `${prefix}${checkbox.unchecked} ${item.content}`);
399
- if (!item.details) return main;
400
- const detailLines = item.details.split("\n").map(l => uiTheme.fg("dim", `${prefix} ${l}`));
401
- return [main, ...detailLines].join("\n");
402
- }
403
- case "abandoned":
404
- return uiTheme.fg("error", `${prefix}${checkbox.unchecked} ${chalk.strikethrough(item.content)}`);
405
- default:
406
- return uiTheme.fg("dim", `${prefix}${checkbox.unchecked} ${item.content}`);
407
- }
408
- }
409
-
410
392
  export const todoWriteToolRenderer = {
411
393
  renderCall(args: TodoWriteRenderArgs, _options: RenderResultOptions, uiTheme: Theme): Component {
412
394
  const count = args.ops?.length ?? 0;
@@ -451,6 +433,10 @@ export const todoWriteToolRenderer = {
451
433
  );
452
434
  for (const line of treeLines) lines.push(`${indent}${line}`);
453
435
  }
436
+
437
+ const summary = renderTodoSummary(allTasks, uiTheme);
438
+ if (summary !== null) lines.push(`${indent}${summary}`);
439
+
454
440
  return new Text(lines.join("\n"), 0, 0);
455
441
  },
456
442
  mergeCallAndResult: true,