@f5xc-salesdemos/xcsh 18.44.0 → 18.46.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.44.0",
4
+ "version": "18.46.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",
@@ -48,12 +48,12 @@
48
48
  "dependencies": {
49
49
  "@agentclientprotocol/sdk": "0.16.1",
50
50
  "@mozilla/readability": "^0.6",
51
- "@f5xc-salesdemos/xcsh-stats": "18.44.0",
52
- "@f5xc-salesdemos/pi-agent-core": "18.44.0",
53
- "@f5xc-salesdemos/pi-ai": "18.44.0",
54
- "@f5xc-salesdemos/pi-natives": "18.44.0",
55
- "@f5xc-salesdemos/pi-tui": "18.44.0",
56
- "@f5xc-salesdemos/pi-utils": "18.44.0",
51
+ "@f5xc-salesdemos/xcsh-stats": "18.46.0",
52
+ "@f5xc-salesdemos/pi-agent-core": "18.46.0",
53
+ "@f5xc-salesdemos/pi-ai": "18.46.0",
54
+ "@f5xc-salesdemos/pi-natives": "18.46.0",
55
+ "@f5xc-salesdemos/pi-tui": "18.46.0",
56
+ "@f5xc-salesdemos/pi-utils": "18.46.0",
57
57
  "@sinclair/typebox": "^0.34",
58
58
  "@xterm/headless": "^6.0",
59
59
  "ajv": "^8.18",
package/src/cli/args.ts CHANGED
@@ -33,6 +33,7 @@ export interface Args {
33
33
  models?: string[];
34
34
  tools?: string[];
35
35
  noTools?: boolean;
36
+ noMcp?: boolean;
36
37
  noLsp?: boolean;
37
38
  noPty?: boolean;
38
39
  hooks?: string[];
@@ -114,6 +115,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
114
115
  result.noLsp = true;
115
116
  } else if (arg === "--no-pty") {
116
117
  result.noPty = true;
118
+ } else if (arg === "--no-mcp") {
119
+ result.noMcp = true;
117
120
  } else if (arg === "--tools" && i + 1 < args.length) {
118
121
  const toolNames = args[++i]
119
122
  .split(",")
@@ -76,6 +76,9 @@ export default class Index extends Command {
76
76
  "no-tools": Flags.boolean({
77
77
  description: "Disable all built-in tools",
78
78
  }),
79
+ "no-mcp": Flags.boolean({
80
+ description: "Disable MCP server discovery and tools",
81
+ }),
79
82
  "no-lsp": Flags.boolean({
80
83
  description: "Disable LSP tools, formatting, and diagnostics",
81
84
  }),
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "18.44.0",
21
- "commit": "f7fdd6b7707603c64fc09e1ad591af368250a1a2",
22
- "shortCommit": "f7fdd6b",
20
+ "version": "18.46.0",
21
+ "commit": "c70ff40053bde7f2b4ba3a16404328dd63afcc17",
22
+ "shortCommit": "c70ff40",
23
23
  "branch": "main",
24
- "tag": "v18.44.0",
25
- "commitDate": "2026-05-06T02:37:32Z",
26
- "buildDate": "2026-05-06T02:56:20.883Z",
24
+ "tag": "v18.46.0",
25
+ "commitDate": "2026-05-06T05:14:35Z",
26
+ "buildDate": "2026-05-06T05:34:34.075Z",
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/f7fdd6b7707603c64fc09e1ad591af368250a1a2",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.44.0"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/c70ff40053bde7f2b4ba3a16404328dd63afcc17",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.46.0"
33
33
  };
package/src/main.ts CHANGED
@@ -56,7 +56,6 @@ import type { AgentSession } from "./session/agent-session";
56
56
  import { resolveResumableSession, type SessionInfo, SessionManager } from "./session/session-manager";
57
57
  import { resolvePromptInput } from "./system-prompt";
58
58
  import type { LspStartupServerInfo } from "./tools";
59
- import { getChangelogPath, getNewEntries, parseChangelog } from "./utils/changelog";
60
59
  import type { EventBus } from "./utils/event-bus";
61
60
 
62
61
  async function checkForNewVersion(currentVersion: string): Promise<string | undefined> {
@@ -149,7 +148,6 @@ const INITIAL_UPDATE_CHECK_TIMEOUT_MS = 500;
149
148
  async function runInteractiveMode(
150
149
  session: AgentSession,
151
150
  version: string,
152
- changelogStatus: { hasNew: boolean; version: string } | undefined,
153
151
  notifs: (InteractiveModeNotify | null)[],
154
152
  versionCheckPromise: Promise<string | undefined>,
155
153
  initialMessages: string[],
@@ -171,7 +169,6 @@ async function runInteractiveMode(
171
169
  const mode = new InteractiveMode(
172
170
  session,
173
171
  version,
174
- changelogStatus,
175
172
  initialUpdateStatus,
176
173
  setExtensionUIContext,
177
174
  lspServers,
@@ -256,31 +253,6 @@ async function promptForkSession(session: SessionInfo): Promise<boolean> {
256
253
  }
257
254
  }
258
255
 
259
- async function getChangelogForDisplay(parsed: Args): Promise<{ hasNew: boolean; version: string } | undefined> {
260
- if (parsed.continue || parsed.resume) {
261
- return undefined;
262
- }
263
-
264
- const lastVersion = settings.get("lastChangelogVersion");
265
- const changelogPath = getChangelogPath();
266
- const entries = await parseChangelog(changelogPath);
267
-
268
- if (!lastVersion) {
269
- if (entries.length > 0) {
270
- settings.set("lastChangelogVersion", VERSION);
271
- return { hasNew: true, version: VERSION };
272
- }
273
- } else {
274
- const newEntries = getNewEntries(entries, lastVersion);
275
- if (newEntries.length > 0) {
276
- settings.set("lastChangelogVersion", VERSION);
277
- return { hasNew: true, version: VERSION };
278
- }
279
- }
280
-
281
- return undefined;
282
- }
283
-
284
256
  async function createSessionManager(parsed: Args, cwd: string): Promise<SessionManager | undefined> {
285
257
  if (parsed.fork) {
286
258
  if (parsed.noSession) {
@@ -556,6 +528,10 @@ async function buildSessionOptions(
556
528
  options.toolNames = parsed.tools;
557
529
  }
558
530
 
531
+ if (parsed.noTools || parsed.noMcp) {
532
+ options.enableMCP = false;
533
+ }
534
+
559
535
  if (parsed.noLsp) {
560
536
  options.enableLsp = false;
561
537
  }
@@ -890,8 +866,6 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
890
866
  await runAcpMode(session, createAcpSession);
891
867
  } else if (isInteractive) {
892
868
  const versionCheckPromise = checkForNewVersion(VERSION).catch(() => undefined);
893
- logger.time("main:getChangelogForDisplay");
894
- const changelogStatus = await getChangelogForDisplay(parsedArgs);
895
869
 
896
870
  const scopedModelsForDisplay = sessionOptions.scopedModels ?? scopedModels;
897
871
  if (scopedModelsForDisplay.length > 0) {
@@ -915,7 +889,6 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
915
889
  await runInteractiveMode(
916
890
  session,
917
891
  VERSION,
918
- changelogStatus,
919
892
  notifs,
920
893
  versionCheckPromise,
921
894
  parsedArgs.messages,
@@ -331,6 +331,23 @@ export async function checkSalesforceStatus(_cwd: string): Promise<WelcomeSalesf
331
331
  }
332
332
  }
333
333
 
334
+ export type GitHubCheckState = "connected" | "auth_error";
335
+
336
+ export interface WelcomeGitHubStatus {
337
+ state: GitHubCheckState;
338
+ }
339
+
340
+ export async function checkGitHubStatus(): Promise<WelcomeGitHubStatus | undefined> {
341
+ try {
342
+ if (!$which("gh")) return undefined;
343
+ const result = await $`gh auth status`.quiet().nothrow();
344
+ return { state: result.exitCode === 0 ? "connected" : "auth_error" };
345
+ } catch (err) {
346
+ logger.warn("GitHub startup check failed", { error: String(err) });
347
+ return { state: "auth_error" };
348
+ }
349
+ }
350
+
334
351
  export type ServiceState = "connected" | "unauthenticated" | "unavailable";
335
352
 
336
353
  export interface ServiceStatus {
@@ -372,3 +389,13 @@ export function mapSalesforceStatus(status: WelcomeSalesforceStatus | undefined)
372
389
  return { name: "Salesforce", state: "unauthenticated", hint: "run: sf org login web" };
373
390
  }
374
391
  }
392
+
393
+ export function mapGitHubStatus(status: WelcomeGitHubStatus | undefined): ServiceStatus {
394
+ if (!status) return { name: "GitHub", state: "unavailable", hint: "not installed" };
395
+ switch (status.state) {
396
+ case "connected":
397
+ return { name: "GitHub", state: "connected" };
398
+ case "auth_error":
399
+ return { name: "GitHub", state: "unauthenticated", hint: "run: gh auth login" };
400
+ }
401
+ }
@@ -2,47 +2,30 @@ import { type Component, padding, truncateToWidth, visibleWidth } from "@f5xc-sa
2
2
  import { APP_NAME } from "@f5xc-salesdemos/pi-utils";
3
3
  import { theme } from "../../modes/theme/theme";
4
4
  import { formatStatusIcon } from "../../services/f5xc-context-indicators";
5
- import type { ModelStatus, WelcomeContextStatus, WelcomeGitLabStatus, WelcomeSalesforceStatus } from "./welcome-checks";
5
+ import type { ModelStatus, ServiceStatus } from "./welcome-checks";
6
6
 
7
7
  export interface UpdateStatus {
8
8
  available: boolean;
9
9
  latestVersion?: string;
10
10
  }
11
11
 
12
- export interface ChangelogStatus {
13
- hasNew: boolean;
14
- version: string;
15
- }
16
-
17
12
  export class WelcomeComponent implements Component {
18
13
  constructor(
19
14
  private readonly version: string,
20
15
  private modelStatus: ModelStatus,
21
- private contextStatus?: WelcomeContextStatus,
16
+ private services: ServiceStatus[] = [],
22
17
  private updateStatus?: UpdateStatus,
23
- private changelogStatus?: ChangelogStatus,
24
- private gitlabStatus?: WelcomeGitLabStatus,
25
- private salesforceStatus?: WelcomeSalesforceStatus,
26
18
  ) {}
27
19
  invalidate(): void {}
28
20
  setModelStatus(status: ModelStatus): void {
29
21
  this.modelStatus = status;
30
22
  }
31
- setContextStatus(status: WelcomeContextStatus | undefined): void {
32
- this.contextStatus = status;
23
+ setServices(services: ServiceStatus[]): void {
24
+ this.services = services;
33
25
  }
34
26
  setUpdateStatus(status: UpdateStatus | undefined): void {
35
27
  this.updateStatus = status;
36
28
  }
37
- setChangelogStatus(status: ChangelogStatus | undefined): void {
38
- this.changelogStatus = status;
39
- }
40
- setGitLabStatus(status: WelcomeGitLabStatus | undefined): void {
41
- this.gitlabStatus = status;
42
- }
43
- setSalesforceStatus(status: WelcomeSalesforceStatus | undefined): void {
44
- this.salesforceStatus = status;
45
- }
46
29
 
47
30
  render(termWidth: number): string[] {
48
31
  const minLeftCol = 48;
@@ -63,7 +46,8 @@ export class WelcomeComponent implements Component {
63
46
  ? preferredLeftCol
64
47
  : Math.max(minLeftCol, dualContentWidth - idealRight);
65
48
  const dualRightCol = Math.max(0, dualContentWidth - dualLeftCol);
66
- const showRightColumn = dualLeftCol >= minLeftCol && dualRightCol >= minRightCol;
49
+ // Only show dual column when both columns have enough room for their content
50
+ const showRightColumn = dualLeftCol >= minLeftCol && dualRightCol >= Math.max(minRightCol, naturalRight);
67
51
  const leftCol = showRightColumn ? dualLeftCol : boxWidth - 2;
68
52
  const rightCol = showRightColumn ? dualRightCol : 0;
69
53
 
@@ -95,7 +79,10 @@ export class WelcomeComponent implements Component {
95
79
  const logoBlockPad = Math.max(0, Math.floor((leftCol - logoMaxWidth) / 2));
96
80
  const logoPadStr = padding(logoBlockPad);
97
81
  const leftLines = [...logoColored.map(l => logoPadStr + l), ""];
98
- const rightLines = this.#buildStatusLines(rightCol);
82
+ const rightLines = this.#buildStatusLines(showRightColumn ? rightCol : leftCol);
83
+ if (!showRightColumn) {
84
+ leftLines.push(...rightLines);
85
+ }
99
86
  const border = (s: string) => theme.fg("borderMuted", s);
100
87
  const hChar = theme.boxRound.horizontal;
101
88
  const h = border(hChar);
@@ -135,20 +122,11 @@ export class WelcomeComponent implements Component {
135
122
 
136
123
  #measureStatusWidth(): number {
137
124
  const lines: string[] = [" Model Provider", ...this.#renderModelStatus()];
138
- if (this.contextStatus) {
139
- lines.push(" F5 XC Context", ...this.#renderContextStatus());
140
- }
141
- if (this.gitlabStatus) {
142
- lines.push(" GitLab", ...this.#renderGitLabStatus());
143
- }
144
- if (this.salesforceStatus) {
145
- lines.push(" Salesforce", ...this.#renderSalesforceStatus());
125
+ for (const svc of this.services) {
126
+ lines.push(this.#renderServiceLine(svc));
146
127
  }
147
128
  if (this.#showUpdateSection()) {
148
- lines.push(" Update Available", ...this.#renderUpdateStatus());
149
- }
150
- if (this.#showChangelogSection()) {
151
- lines.push(" What's New", ...this.#renderChangelogStatus());
129
+ lines.push(this.#renderUpdateLine());
152
130
  }
153
131
  return Math.max(...lines.map(l => visibleWidth(l)));
154
132
  }
@@ -161,41 +139,16 @@ export class WelcomeComponent implements Component {
161
139
  lines.push(` ${theme.bold(theme.fg("contentAccent", "Model Provider"))}`);
162
140
  lines.push(...this.#renderModelStatus());
163
141
  lines.push("");
164
- if (this.contextStatus) {
142
+ if (this.services.length > 0 || this.#showUpdateSection()) {
165
143
  lines.push(separator);
166
- lines.push("");
167
- lines.push(` ${theme.bold(theme.fg("contentAccent", "F5 XC Context"))}`);
168
- lines.push(...this.#renderContextStatus());
169
- lines.push("");
170
- }
171
- if (this.gitlabStatus) {
172
- lines.push(separator);
173
- lines.push("");
174
- lines.push(` ${theme.bold(theme.fg("contentAccent", "GitLab"))}`);
175
- lines.push(...this.#renderGitLabStatus());
176
- lines.push("");
177
- }
178
- if (this.salesforceStatus) {
179
- lines.push(separator);
180
- lines.push("");
181
- lines.push(` ${theme.bold(theme.fg("contentAccent", "Salesforce"))}`);
182
- lines.push(...this.#renderSalesforceStatus());
183
- lines.push("");
184
- }
185
- if (this.#showUpdateSection()) {
186
- lines.push(separator);
187
- lines.push("");
188
- lines.push(` ${theme.bold(theme.fg("contentAccent", "Update Available"))}`);
189
- lines.push(...this.#renderUpdateStatus());
190
- lines.push("");
191
- }
192
- if (this.#showChangelogSection()) {
193
- lines.push(separator);
194
- lines.push("");
195
- lines.push(` ${theme.bold(theme.fg("contentAccent", "What's New"))}`);
196
- lines.push(...this.#renderChangelogStatus());
197
- lines.push("");
144
+ for (const svc of this.services) {
145
+ lines.push(this.#renderServiceLine(svc));
146
+ }
147
+ if (this.#showUpdateSection()) {
148
+ lines.push(this.#renderUpdateLine());
149
+ }
198
150
  }
151
+ lines.push("");
199
152
  return lines;
200
153
  }
201
154
 
@@ -203,25 +156,18 @@ export class WelcomeComponent implements Component {
203
156
  return this.updateStatus?.available === true;
204
157
  }
205
158
 
206
- #showChangelogSection(): boolean {
207
- return this.changelogStatus?.hasNew === true;
159
+ #renderServiceLine(service: ServiceStatus): string {
160
+ if (service.state === "connected") {
161
+ return ` ${formatStatusIcon("connected")} ${theme.fg("muted", service.name)}`;
162
+ }
163
+ const hint = service.hint ?? "";
164
+ return ` ${formatStatusIcon("warning")} ${theme.fg("muted", service.name)} ${theme.fg("dim", hint)}`;
208
165
  }
209
166
 
210
- #renderUpdateStatus(): string[] {
167
+ #renderUpdateLine(): string {
211
168
  const latest = this.updateStatus?.latestVersion;
212
169
  const label = latest ? `v${latest}` : "new version";
213
- return [
214
- ` ${theme.fg("warning", "\u2191")} ${theme.fg("muted", label)}`,
215
- ` ${theme.fg("dim", "Run")} ${theme.fg("contentAccent", "xcsh update")}`,
216
- ];
217
- }
218
-
219
- #renderChangelogStatus(): string[] {
220
- const v = this.changelogStatus?.version ?? this.version;
221
- return [
222
- ` ${theme.fg("success", "\u2605")} ${theme.fg("muted", `v${v}`)}`,
223
- ` ${theme.fg("dim", "Run")} ${theme.fg("contentAccent", "/changelog")}`,
224
- ];
170
+ return ` ${theme.fg("warning", "↑")} ${theme.fg("muted", label)} ${theme.fg("dim", "run: xcsh update")}`;
225
171
  }
226
172
 
227
173
  #renderModelStatus(): string[] {
@@ -243,89 +189,6 @@ export class WelcomeComponent implements Component {
243
189
  }
244
190
  }
245
191
 
246
- #renderContextStatus(): string[] {
247
- if (!this.contextStatus) return [];
248
- const { state, name } = this.contextStatus;
249
- const n = name ?? "(unknown)";
250
- switch (state) {
251
- case "connected":
252
- return [` ${formatStatusIcon("connected")} ${theme.fg("muted", n)}`];
253
- case "auth_error":
254
- return [
255
- ` ${formatStatusIcon("error")} ${theme.fg("muted", n)} ${theme.fg("error", "\u2014 token invalid")}`,
256
- ` ${theme.fg("dim", "Run /context to update")}`,
257
- ];
258
- case "offline":
259
- if (this.contextStatus?.errorClass === "url_not_found") {
260
- return [
261
- ` ${formatStatusIcon("error")} ${theme.fg("muted", n)} ${theme.fg("error", "\u2014 tenant not found")}`,
262
- ` ${theme.fg("dim", "Recreate with /context create or check with /context show")}`,
263
- ];
264
- }
265
- return [
266
- ` ${formatStatusIcon("warning")} ${theme.fg("muted", n)} ${theme.fg("warning", "\u2014 unreachable")}`,
267
- ` ${theme.fg("dim", "Check network, /context")}`,
268
- ];
269
- case "no_context":
270
- return [
271
- ` ${formatStatusIcon("warning")} ${theme.fg("warning", "No context configured")}`,
272
- ` ${theme.fg("dim", "Run /context create <name> <url> <token>")}`,
273
- ];
274
- }
275
- }
276
-
277
- #renderGitLabStatus(): string[] {
278
- if (!this.gitlabStatus) return [];
279
- const { state, project } = this.gitlabStatus;
280
- switch (state) {
281
- case "connected":
282
- return [` ${formatStatusIcon("connected")} ${theme.fg("muted", project ?? "configured")}`];
283
- case "auth_error":
284
- return [
285
- ` ${formatStatusIcon("error")} ${theme.fg("error", "Not authenticated")}`,
286
- ` ${theme.fg("dim", "Run")} ${theme.fg("contentAccent", "glab auth login")}`,
287
- ];
288
- case "not_configured":
289
- return [
290
- ` ${formatStatusIcon("warning")} ${theme.fg("warning", "No project configured")}`,
291
- ` ${theme.fg("dim", "Run glab_setup action save_project project GROUP/REPO")}`,
292
- ];
293
- case "project_inaccessible":
294
- return [
295
- ` ${formatStatusIcon("warning")} ${theme.fg("muted", project ?? "unknown")} ${theme.fg("warning", "\u2014 access denied")}`,
296
- ` ${theme.fg("dim", "Check permissions or run glab_setup with action save_project")}`,
297
- ];
298
- case "not_installed":
299
- return [` ${formatStatusIcon("warning")} ${theme.fg("warning", "glab CLI not installed")}`];
300
- }
301
- }
302
-
303
- #renderSalesforceStatus(): string[] {
304
- if (!this.salesforceStatus) return [];
305
- const { state, username, orgAlias } = this.salesforceStatus;
306
- switch (state) {
307
- case "connected":
308
- return [
309
- ` ${formatStatusIcon("connected")} ${theme.fg("muted", orgAlias ?? "org")}${username ? ` ${theme.fg("dim", `(${username})`)}` : ""}`,
310
- ];
311
- case "not_configured":
312
- return [
313
- ` ${formatStatusIcon("warning")} ${theme.fg("warning", "Authenticated (no default org)")}`,
314
- ` ${theme.fg("dim", "Run")} ${theme.fg("contentAccent", "sf_setup")} ${theme.fg("dim", "with action set_default")}`,
315
- ];
316
- case "auth_error":
317
- return [
318
- ` ${formatStatusIcon("error")} ${theme.fg("error", "Not authenticated")}`,
319
- ` ${theme.fg("dim", "Run")} ${theme.fg("contentAccent", "sf org login web --set-default --alias SFDC")}`,
320
- ];
321
- case "session_expired":
322
- return [
323
- ` ${formatStatusIcon("warning")} ${theme.fg("muted", orgAlias ?? "org")} ${theme.fg("warning", "— session expired")}`,
324
- ` ${theme.fg("dim", "Re-authenticate with")} ${theme.fg("contentAccent", "sf org login web --set-default")}`,
325
- ];
326
- }
327
- }
328
-
329
192
  #f5ColorLine(line: string): string {
330
193
  const red = "\x1b[38;5;160m";
331
194
  const white = "\x1b[1;37m";
@@ -49,8 +49,17 @@ import type { HookSelectorComponent } from "./components/hook-selector";
49
49
  import type { PythonExecutionComponent } from "./components/python-execution";
50
50
  import { StatusLineComponent } from "./components/status-line";
51
51
  import type { ToolExecutionHandle } from "./components/tool-execution";
52
- import { type ChangelogStatus, type UpdateStatus, WelcomeComponent } from "./components/welcome";
53
- import { checkGitLabStatus, checkSalesforceStatus, runWelcomeChecks } from "./components/welcome-checks";
52
+ import { type UpdateStatus, WelcomeComponent } from "./components/welcome";
53
+ import {
54
+ checkGitHubStatus,
55
+ checkGitLabStatus,
56
+ checkSalesforceStatus,
57
+ mapContextStatus,
58
+ mapGitHubStatus,
59
+ mapGitLabStatus,
60
+ mapSalesforceStatus,
61
+ runWelcomeChecks,
62
+ } from "./components/welcome-checks";
54
63
  import { BtwController } from "./controllers/btw-controller";
55
64
  import { CommandController } from "./controllers/command-controller";
56
65
  import { EventController } from "./controllers/event-controller";
@@ -161,7 +170,6 @@ export class InteractiveMode implements InteractiveModeContext {
161
170
  #pendingSlashCommands: SlashCommand[] = [];
162
171
  #cleanupUnsubscribe?: () => void;
163
172
  readonly #version: string;
164
- readonly #changelogStatus: ChangelogStatus | undefined;
165
173
  readonly #initialUpdateStatus: UpdateStatus | undefined;
166
174
  #planModePreviousTools: string[] | undefined;
167
175
  #planModePreviousModelState: { model: Model; thinkingLevel?: ThinkingLevel } | undefined;
@@ -193,7 +201,6 @@ export class InteractiveMode implements InteractiveModeContext {
193
201
  constructor(
194
202
  session: AgentSession,
195
203
  version: string,
196
- changelogStatus: ChangelogStatus | undefined = undefined,
197
204
  initialUpdateStatus: UpdateStatus | undefined = undefined,
198
205
  setToolUIContext: (uiContext: ExtensionUIContext, hasUI: boolean) => void = () => {},
199
206
  lspServers?: import("../tools").LspStartupServerInfo[],
@@ -206,7 +213,6 @@ export class InteractiveMode implements InteractiveModeContext {
206
213
  this.keybindings = KeybindingsManager.inMemory();
207
214
  this.agent = session.agent;
208
215
  this.#version = version;
209
- this.#changelogStatus = changelogStatus;
210
216
  this.#initialUpdateStatus = initialUpdateStatus;
211
217
  this.#toolUiContextSetter = setToolUIContext;
212
218
  this.lspServers = lspServers;
@@ -310,13 +316,14 @@ export class InteractiveMode implements InteractiveModeContext {
310
316
  getProjectDir(),
311
317
  );
312
318
 
313
- // Run blocking welcome screen status checks (model + context + gitlab + salesforce) in parallel
314
- const [welcomeResult, gitlabStatus, salesforceStatus] = await Promise.all([
319
+ // Run blocking welcome screen status checks (model + context + gitlab + github + salesforce) in parallel
320
+ const [welcomeResult, gitlabStatus, salesforceStatus, githubStatus] = await Promise.all([
315
321
  logger.time("InteractiveMode.init:welcomeChecks", () =>
316
322
  runWelcomeChecks(this.session.model, this.session.modelRegistry.authStorage),
317
323
  ),
318
324
  checkGitLabStatus(getProjectDir()).catch(() => undefined),
319
325
  checkSalesforceStatus(getProjectDir()).catch(() => undefined),
326
+ checkGitHubStatus().catch(() => undefined),
320
327
  ]);
321
328
 
322
329
  const startupQuiet = settings.get("startup.quiet");
@@ -329,15 +336,21 @@ export class InteractiveMode implements InteractiveModeContext {
329
336
  }
330
337
 
331
338
  if (!startupQuiet) {
332
- // Welcome box owns all startup notifications (model, context, update, changelog)
339
+ // Welcome box owns all startup notifications (model, services, update)
340
+ const services =
341
+ welcomeResult.model.state === "connected"
342
+ ? [
343
+ mapContextStatus(welcomeResult.context ?? { state: "no_context" }),
344
+ mapGitLabStatus(gitlabStatus),
345
+ mapGitHubStatus(githubStatus),
346
+ mapSalesforceStatus(salesforceStatus),
347
+ ]
348
+ : [];
333
349
  this.#welcomeComponent = new WelcomeComponent(
334
350
  this.#version,
335
351
  welcomeResult.model,
336
- welcomeResult.context,
352
+ services,
337
353
  this.#initialUpdateStatus,
338
- this.#changelogStatus,
339
- gitlabStatus,
340
- salesforceStatus,
341
354
  );
342
355
 
343
356
  // Setup UI layout