@f5xc-salesdemos/xcsh 17.4.2 → 17.4.3

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": "17.4.2",
4
+ "version": "17.4.3",
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",
@@ -46,12 +46,12 @@
46
46
  "dependencies": {
47
47
  "@agentclientprotocol/sdk": "0.16.1",
48
48
  "@mozilla/readability": "^0.6",
49
- "@f5xc-salesdemos/xcsh-stats": "17.4.2",
50
- "@f5xc-salesdemos/pi-agent-core": "17.4.2",
51
- "@f5xc-salesdemos/pi-ai": "17.4.2",
52
- "@f5xc-salesdemos/pi-natives": "17.4.2",
53
- "@f5xc-salesdemos/pi-tui": "17.4.2",
54
- "@f5xc-salesdemos/pi-utils": "17.4.2",
49
+ "@f5xc-salesdemos/xcsh-stats": "17.4.3",
50
+ "@f5xc-salesdemos/pi-agent-core": "17.4.3",
51
+ "@f5xc-salesdemos/pi-ai": "17.4.3",
52
+ "@f5xc-salesdemos/pi-natives": "17.4.3",
53
+ "@f5xc-salesdemos/pi-tui": "17.4.3",
54
+ "@f5xc-salesdemos/pi-utils": "17.4.3",
55
55
  "@sinclair/typebox": "^0.34",
56
56
  "@xterm/headless": "^6.0",
57
57
  "ajv": "^8.18",
@@ -2,7 +2,7 @@ import * as os from "node:os";
2
2
  import * as path from "node:path";
3
3
  import { ThinkingLevel } from "@f5xc-salesdemos/pi-agent-core";
4
4
  import { TERMINAL } from "@f5xc-salesdemos/pi-tui";
5
- import { formatDuration, formatNumber, getProjectDir, relativePathWithinRoot } from "@f5xc-salesdemos/pi-utils";
5
+ import { formatDuration, formatNumber, relativePathWithinRoot } from "@f5xc-salesdemos/pi-utils";
6
6
  import { theme } from "../../../modes/theme/theme";
7
7
  import { shortenPath } from "../../../tools/render-utils";
8
8
  import { getSessionAccentAnsi, getSessionAccentHex } from "../../../utils/session-color";
@@ -115,7 +115,7 @@ const pathSegment: StatusLineSegment = {
115
115
  render(ctx) {
116
116
  const opts = ctx.options.path ?? {};
117
117
 
118
- let pwd = getProjectDir();
118
+ let pwd = ctx.cwd;
119
119
 
120
120
  if (opts.stripWorkPrefix !== false) {
121
121
  pwd = stripDisplayRoot(pwd);
@@ -19,6 +19,7 @@ export type RGB = readonly [number, number, number];
19
19
  export interface SegmentContext {
20
20
  session: AgentSession;
21
21
  width: number;
22
+ cwd: string;
22
23
  options: StatusLineSegmentOptions;
23
24
  planMode: {
24
25
  enabled: boolean;
@@ -1,13 +1,14 @@
1
1
  import * as fs from "node:fs";
2
2
  import type { AssistantMessage } from "@f5xc-salesdemos/pi-ai";
3
3
  import { type Component, truncateToWidth, visibleWidth } from "@f5xc-salesdemos/pi-tui";
4
- import { formatCount, getProjectDir } from "@f5xc-salesdemos/pi-utils";
4
+ import { formatCount, getShellPwd } from "@f5xc-salesdemos/pi-utils";
5
5
  import { $ } from "bun";
6
6
  import { settings } from "../../config/settings";
7
7
  import type { StatusLinePreset, StatusLineSegmentId, StatusLineSeparatorStyle } from "../../config/settings-schema";
8
8
  import { theme } from "../../modes/theme/theme";
9
9
  import type { AgentSession } from "../../session/agent-session";
10
10
  import { calculatePromptTokens } from "../../session/compaction/compaction";
11
+ import type { EventBus } from "../../utils/event-bus";
11
12
  import * as git from "../../utils/git";
12
13
  import { queryGitStatus } from "../../utils/gitstatus";
13
14
  import { sanitizeStatusText } from "../shared";
@@ -52,6 +53,7 @@ export class StatusLineComponent implements Component {
52
53
  #cachedBranch: string | null | undefined = undefined;
53
54
  #cachedBranchRepoId: string | null | undefined = undefined;
54
55
  #gitWatcher: fs.FSWatcher | null = null;
56
+ #cwdUnsubscribe: (() => void) | null = null;
55
57
  #onBranchChange: (() => void) | null = null;
56
58
  #onStatusChanged: (() => void) | null = null;
57
59
  #autoCompactEnabled: boolean = true;
@@ -130,13 +132,22 @@ export class StatusLineComponent implements Component {
130
132
  this.#setupGitWatcher();
131
133
  }
132
134
 
135
+ watchCwd(eventBus: EventBus): void {
136
+ this.#cwdUnsubscribe?.();
137
+ this.#cwdUnsubscribe = eventBus.on("cwd:changed", () => {
138
+ this.#invalidateGitCaches();
139
+ this.#setupGitWatcher();
140
+ this.#onStatusChanged?.();
141
+ });
142
+ }
143
+
133
144
  #setupGitWatcher(): void {
134
145
  if (this.#gitWatcher) {
135
146
  this.#gitWatcher.close();
136
147
  this.#gitWatcher = null;
137
148
  }
138
149
 
139
- const gitHeadPath = git.repo.resolveSync(getProjectDir())?.headPath ?? null;
150
+ const gitHeadPath = git.repo.resolveSync(getShellPwd())?.headPath ?? null;
140
151
  if (!gitHeadPath) return;
141
152
 
142
153
  try {
@@ -156,6 +167,10 @@ export class StatusLineComponent implements Component {
156
167
  this.#gitWatcher.close();
157
168
  this.#gitWatcher = null;
158
169
  }
170
+ if (this.#cwdUnsubscribe) {
171
+ this.#cwdUnsubscribe();
172
+ this.#cwdUnsubscribe = null;
173
+ }
159
174
  }
160
175
 
161
176
  invalidate(): void {
@@ -168,7 +183,7 @@ export class StatusLineComponent implements Component {
168
183
  this.#cachedPrContext = undefined;
169
184
  }
170
185
  #getCurrentBranch(): string | null {
171
- const head = git.head.resolveSync(getProjectDir());
186
+ const head = git.head.resolveSync(getShellPwd());
172
187
  const gitHeadPath = head?.headPath ?? null;
173
188
  if (this.#cachedBranch !== undefined && this.#cachedBranchRepoId === gitHeadPath) {
174
189
  return this.#cachedBranch;
@@ -189,7 +204,7 @@ export class StatusLineComponent implements Component {
189
204
  if (this.#defaultBranch === undefined) {
190
205
  this.#defaultBranch = "main";
191
206
  (async () => {
192
- const resolved = await git.branch.default(getProjectDir());
207
+ const resolved = await git.branch.default(getShellPwd());
193
208
  if (resolved) {
194
209
  this.#defaultBranch = resolved;
195
210
  if (this.#onBranchChange) {
@@ -220,7 +235,7 @@ export class StatusLineComponent implements Component {
220
235
  (async () => {
221
236
  try {
222
237
  // Prefer gitstatusd daemon (10x faster than git CLI)
223
- const gsResult = await queryGitStatus(getProjectDir());
238
+ const gsResult = await queryGitStatus(getShellPwd());
224
239
  if (gsResult) {
225
240
  this.#cachedGitStatus = {
226
241
  staged: gsResult.staged,
@@ -234,7 +249,7 @@ export class StatusLineComponent implements Component {
234
249
  };
235
250
  } else {
236
251
  // Fallback to git CLI
237
- const summary = await git.status.summary(getProjectDir());
252
+ const summary = await git.status.summary(getShellPwd());
238
253
  this.#cachedGitStatus = summary
239
254
  ? { ...summary, conflicted: 0, ahead: 0, behind: 0, stashes: 0, action: "" }
240
255
  : null;
@@ -284,7 +299,7 @@ export class StatusLineComponent implements Component {
284
299
  };
285
300
  try {
286
301
  // Requires `gh repo set-default` to be configured; fails gracefully if not
287
- const result = await $`gh pr view --json number,url`.quiet().nothrow();
302
+ const result = await $`gh pr view --json number,url`.cwd(getShellPwd()).quiet().nothrow();
288
303
  if (result.exitCode !== 0) {
289
304
  setCachedPr(null);
290
305
  return;
@@ -368,6 +383,7 @@ export class StatusLineComponent implements Component {
368
383
  return {
369
384
  session: this.session,
370
385
  width,
386
+ cwd: getShellPwd(),
371
387
  options: this.#resolveSettings().segmentOptions ?? {},
372
388
  planMode: this.#planModeStatus,
373
389
  usageStats,
@@ -408,6 +408,10 @@ export class InteractiveMode implements InteractiveModeContext {
408
408
  this.ui.requestRender();
409
409
  });
410
410
 
411
+ if (this.#eventBus) {
412
+ this.statusLine.watchCwd(this.#eventBus);
413
+ }
414
+
411
415
  // Initial top border update
412
416
  this.updateEditorTopBorder();
413
417
  }