@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 +7 -7
- package/src/cli/args.ts +3 -0
- package/src/commands/launch.ts +3 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/main.ts +4 -31
- package/src/modes/components/welcome-checks.ts +27 -0
- package/src/modes/components/welcome.ts +29 -166
- package/src/modes/interactive-mode.ts +25 -12
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "18.
|
|
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.
|
|
52
|
-
"@f5xc-salesdemos/pi-agent-core": "18.
|
|
53
|
-
"@f5xc-salesdemos/pi-ai": "18.
|
|
54
|
-
"@f5xc-salesdemos/pi-natives": "18.
|
|
55
|
-
"@f5xc-salesdemos/pi-tui": "18.
|
|
56
|
-
"@f5xc-salesdemos/pi-utils": "18.
|
|
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(",")
|
package/src/commands/launch.ts
CHANGED
|
@@ -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.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "18.46.0",
|
|
21
|
+
"commit": "c70ff40053bde7f2b4ba3a16404328dd63afcc17",
|
|
22
|
+
"shortCommit": "c70ff40",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-05-
|
|
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/
|
|
32
|
-
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.
|
|
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,
|
|
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
|
|
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
|
-
|
|
32
|
-
this.
|
|
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
|
-
|
|
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
|
-
|
|
139
|
-
lines.push(
|
|
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(
|
|
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.
|
|
142
|
+
if (this.services.length > 0 || this.#showUpdateSection()) {
|
|
165
143
|
lines.push(separator);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
-
#
|
|
207
|
-
|
|
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
|
-
#
|
|
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
|
|
53
|
-
import {
|
|
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,
|
|
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
|
-
|
|
352
|
+
services,
|
|
337
353
|
this.#initialUpdateStatus,
|
|
338
|
-
this.#changelogStatus,
|
|
339
|
-
gitlabStatus,
|
|
340
|
-
salesforceStatus,
|
|
341
354
|
);
|
|
342
355
|
|
|
343
356
|
// Setup UI layout
|