@f5xc-salesdemos/xcsh 18.16.0 → 18.18.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 (35) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/package.json +7 -7
  3. package/src/config/settings-schema.ts +2 -2
  4. package/src/internal-urls/build-info-runtime.ts +18 -18
  5. package/src/internal-urls/build-info.generated.ts +8 -8
  6. package/src/internal-urls/xcsh-protocol.ts +7 -7
  7. package/src/main.ts +5 -5
  8. package/src/modes/components/status-line/presets.ts +8 -8
  9. package/src/modes/components/status-line/segments.ts +6 -6
  10. package/src/modes/components/status-line-segment-editor.ts +1 -1
  11. package/src/modes/components/welcome-checks.ts +25 -25
  12. package/src/modes/components/welcome.ts +18 -18
  13. package/src/modes/interactive-mode.ts +3 -3
  14. package/src/modes/theme/defaults/xcsh-dark.json +2 -2
  15. package/src/modes/theme/defaults/xcsh-light.json +2 -2
  16. package/src/modes/theme/theme-schema.json +6 -6
  17. package/src/modes/theme/theme.ts +6 -6
  18. package/src/prompts/system/custom-system-prompt.md +5 -4
  19. package/src/prompts/system/system-prompt.md +5 -4
  20. package/src/sdk.ts +73 -73
  21. package/src/services/f5xc-context-command.ts +610 -0
  22. package/src/services/f5xc-context-display.ts +15 -0
  23. package/src/services/{f5xc-profile-indicators.ts → f5xc-context-indicators.ts} +1 -1
  24. package/src/services/f5xc-context-segment.ts +23 -0
  25. package/src/services/f5xc-context.ts +1115 -0
  26. package/src/services/f5xc-env.ts +3 -3
  27. package/src/services/f5xc-table.ts +2 -2
  28. package/src/session/agent-session.ts +1 -1
  29. package/src/session/session-manager.ts +26 -26
  30. package/src/slash-commands/builtin-registry.ts +143 -24
  31. package/src/system-prompt.ts +4 -4
  32. package/src/services/f5xc-profile-command.ts +0 -429
  33. package/src/services/f5xc-profile-display.ts +0 -15
  34. package/src/services/f5xc-profile-segment.ts +0 -23
  35. package/src/services/f5xc-profile.ts +0 -728
package/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [18.18.0] - 2026-04-25
6
+
7
+ ### Changed
8
+
9
+ - Renamed F5 XC credential system from "profile" to "context" to align with kubectl conventions. The `/profile` command is now `/context`, all types/classes use `Context*` naming (`ContextService`, `ContextStatus`, `F5XCContext`, etc.), on-disk paths changed from `profiles/` to `contexts/` and `active_profile` to `active_context`, and the status-line segment ID is now `context_f5xc`. ([#302](https://github.com/f5xc-salesdemos/xcsh/issues/302))
10
+
5
11
  ## [18.12.0] - 2026-04-23
6
12
 
7
13
  ### Changed
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@f5xc-salesdemos/xcsh",
4
- "version": "18.16.0",
4
+ "version": "18.18.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.16.0",
51
- "@f5xc-salesdemos/pi-agent-core": "18.16.0",
52
- "@f5xc-salesdemos/pi-ai": "18.16.0",
53
- "@f5xc-salesdemos/pi-natives": "18.16.0",
54
- "@f5xc-salesdemos/pi-tui": "18.16.0",
55
- "@f5xc-salesdemos/pi-utils": "18.16.0",
50
+ "@f5xc-salesdemos/xcsh-stats": "18.18.0",
51
+ "@f5xc-salesdemos/pi-agent-core": "18.18.0",
52
+ "@f5xc-salesdemos/pi-ai": "18.18.0",
53
+ "@f5xc-salesdemos/pi-natives": "18.18.0",
54
+ "@f5xc-salesdemos/pi-tui": "18.18.0",
55
+ "@f5xc-salesdemos/pi-utils": "18.18.0",
56
56
  "@sinclair/typebox": "^0.34",
57
57
  "@xterm/headless": "^6.0",
58
58
  "ajv": "^8.18",
@@ -76,7 +76,7 @@ export type StatusLineSegmentId =
76
76
  | "cache_read"
77
77
  | "cache_write"
78
78
  | "session_name"
79
- | "profile_f5xc"
79
+ | "context_f5xc"
80
80
  | "os_icon";
81
81
 
82
82
  interface UiMetadata {
@@ -1763,7 +1763,7 @@ export const SETTINGS_SCHEMA = {
1763
1763
  // Fork-specific settings (xcsh / f5xc)
1764
1764
  // ────────────────────────────────────────────────────────────────────────
1765
1765
 
1766
- /** Per-session environment variables injected into bash (used by f5xc profile system) */
1766
+ /** Per-session environment variables injected into bash (used by f5xc context system) */
1767
1767
  "bash.environment": { type: "record", default: {} as Record<string, string> },
1768
1768
 
1769
1769
  /** Clear terminal on startup */
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { $ } from "bun";
4
- import type { ProfileStatus } from "../services/f5xc-profile";
4
+ import type { ContextStatus } from "../services/f5xc-context";
5
5
  import { BUILD_INFO, type BuildInfo } from "./build-info.generated";
6
6
 
7
7
  export type BuildInfoSource = "compiled" | "live-git" | "embedded-fallback";
@@ -104,46 +104,46 @@ export function formatRelativeTime(epochMs: number, nowMs: number): string {
104
104
  return `${days} day${days === 1 ? "" : "s"} ago`;
105
105
  }
106
106
 
107
- function renderAuthStatusLine(profile: ProfileStatus, nowMs: number): string {
108
- const base = `**Auth Status:** ${profile.authStatus}`;
109
- if (profile.authLatencyMs === undefined || profile.authCheckedAt === undefined) {
107
+ function renderAuthStatusLine(context: ContextStatus, nowMs: number): string {
108
+ const base = `**Auth Status:** ${context.authStatus}`;
109
+ if (context.authLatencyMs === undefined || context.authCheckedAt === undefined) {
110
110
  return base;
111
111
  }
112
- const checked = formatRelativeTime(profile.authCheckedAt, nowMs);
113
- return `${base} (latency: ${profile.authLatencyMs}ms, checked: ${checked})`;
112
+ const checked = formatRelativeTime(context.authCheckedAt, nowMs);
113
+ return `${base} (latency: ${context.authLatencyMs}ms, checked: ${checked})`;
114
114
  }
115
115
 
116
- function renderPlatformContext(profile: ProfileStatus | null, nowMs: number): string {
117
- // xcsh can be connected via a named profile OR via F5XC_API_URL / F5XC_API_TOKEN env vars.
118
- // In the env-only case, activeProfileName is null but activeProfileTenant (derived from the
116
+ function renderPlatformContext(context: ContextStatus | null, nowMs: number): string {
117
+ // xcsh can be connected via a named context OR via F5XC_API_URL / F5XC_API_TOKEN env vars.
118
+ // In the env-only case, activeContextName is null but activeContextTenant (derived from the
119
119
  // env URL) and credentialSource ("environment") are still populated. Guard on tenant, not
120
120
  // name, so env-backed deployments see the configured state instead of the unconfigured copy.
121
- if (!profile?.isConfigured || !profile.activeProfileTenant) {
121
+ if (!context?.isConfigured || !context.activeContextTenant) {
122
122
  return [
123
123
  "## Current Platform Context",
124
124
  "",
125
- "No F5 XC profile active. Run `/profile create` or `/profile activate` to connect.",
125
+ "No F5 XC context active. Run `/context create` or `/context activate` to connect.",
126
126
  "",
127
127
  ].join("\n");
128
128
  }
129
129
 
130
- const authLine = renderAuthStatusLine(profile, nowMs);
131
- const credentialLine = `**Credential Source:** ${profile.credentialSource}${
132
- profile.credentialSource === "profile" && profile.activeProfileName ? ` (name: ${profile.activeProfileName})` : ""
130
+ const authLine = renderAuthStatusLine(context, nowMs);
131
+ const credentialLine = `**Credential Source:** ${context.credentialSource}${
132
+ context.credentialSource === "context" && context.activeContextName ? ` (name: ${context.activeContextName})` : ""
133
133
  }`;
134
134
 
135
135
  return [
136
136
  "## Current Platform Context",
137
137
  "",
138
- `- **Tenant:** ${profile.activeProfileTenant}`,
139
- `- **Namespace:** ${profile.activeProfileNamespace ?? "default"}`,
138
+ `- **Tenant:** ${context.activeContextTenant}`,
139
+ `- **Namespace:** ${context.activeContextNamespace ?? "default"}`,
140
140
  `- ${authLine}`,
141
141
  `- ${credentialLine}`,
142
142
  "",
143
143
  ].join("\n");
144
144
  }
145
145
 
146
- export function renderAboutDoc(info: RuntimeBuildInfo, profile: ProfileStatus | null): string {
146
+ export function renderAboutDoc(info: RuntimeBuildInfo, context: ContextStatus | null): string {
147
147
  return [
148
148
  "# xcsh — identity and build fingerprint",
149
149
  "",
@@ -163,7 +163,7 @@ export function renderAboutDoc(info: RuntimeBuildInfo, profile: ProfileStatus |
163
163
  `- PR that shipped this version: ${info.prNumber ? `#${info.prNumber}` : "unknown (resolve via gh if needed)"}`,
164
164
  `- Provenance source: \`${info.source}\` (resolved at ${info.resolvedAt})`,
165
165
  "",
166
- renderPlatformContext(profile, Date.now()),
166
+ renderPlatformContext(context, Date.now()),
167
167
  "## Source of truth",
168
168
  "",
169
169
  `- Repository: ${info.repoUrl}`,
@@ -17,17 +17,17 @@ export interface BuildInfo {
17
17
  }
18
18
 
19
19
  export const BUILD_INFO: BuildInfo = {
20
- "version": "18.16.0",
21
- "commit": "3dd57e1d4e386be08789232ae35d2332f473a7df",
22
- "shortCommit": "3dd57e1",
20
+ "version": "18.18.0",
21
+ "commit": "e903ec539e8c8079df8aa50a8917c5ea2aa4dfe0",
22
+ "shortCommit": "e903ec5",
23
23
  "branch": "main",
24
- "tag": "v18.16.0",
25
- "commitDate": "2026-04-24T03:31:16Z",
26
- "buildDate": "2026-04-24T03:59:07.572Z",
24
+ "tag": "v18.18.0",
25
+ "commitDate": "2026-04-25T06:12:23Z",
26
+ "buildDate": "2026-04-25T06:32:00.724Z",
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/3dd57e1d4e386be08789232ae35d2332f473a7df",
32
- "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.16.0"
31
+ "commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/e903ec539e8c8079df8aa50a8917c5ea2aa4dfe0",
32
+ "releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.18.0"
33
33
  };
@@ -10,7 +10,7 @@
10
10
  * - xcsh://about - Identity fingerprint (version, commit, branch, repo)
11
11
  */
12
12
  import * as path from "node:path";
13
- import type { ProfileStatus } from "../services/f5xc-profile";
13
+ import type { ContextStatus } from "../services/f5xc-context";
14
14
  import { getRuntimeBuildInfo, type RuntimeBuildInfo, renderAboutDoc } from "./build-info-runtime";
15
15
  import { EMBEDDED_DOC_FILENAMES, EMBEDDED_DOCS } from "./docs-index.generated";
16
16
  import type { InternalResource, InternalUrl, ProtocolHandler } from "./types";
@@ -21,8 +21,8 @@ const ABOUT_ROUTE = "about";
21
21
  export interface InternalDocsProtocolOptions {
22
22
  /** Override runtime build-info resolution. Primarily for tests. */
23
23
  readonly resolveBuildInfo?: () => Promise<RuntimeBuildInfo>;
24
- /** Sync getter returning the current profile status (or null if unconfigured / unavailable). */
25
- readonly getProfileStatus?: () => ProfileStatus | null;
24
+ /** Sync getter returning the current context status (or null if unconfigured / unavailable). */
25
+ readonly getContextStatus?: () => ContextStatus | null;
26
26
  }
27
27
 
28
28
  /**
@@ -34,11 +34,11 @@ export interface InternalDocsProtocolOptions {
34
34
  export class InternalDocsProtocolHandler implements ProtocolHandler {
35
35
  readonly scheme = "xcsh";
36
36
  readonly #resolveBuildInfo: () => Promise<RuntimeBuildInfo>;
37
- readonly #getProfileStatus: (() => ProfileStatus | null) | undefined;
37
+ readonly #getContextStatus: (() => ContextStatus | null) | undefined;
38
38
 
39
39
  constructor(options: InternalDocsProtocolOptions = {}) {
40
40
  this.#resolveBuildInfo = options.resolveBuildInfo ?? getRuntimeBuildInfo;
41
- this.#getProfileStatus = options.getProfileStatus;
41
+ this.#getContextStatus = options.getContextStatus;
42
42
  }
43
43
 
44
44
  async resolve(url: InternalUrl): Promise<InternalResource> {
@@ -84,8 +84,8 @@ export class InternalDocsProtocolHandler implements ProtocolHandler {
84
84
 
85
85
  if (normalized === ABOUT_ROUTE || normalized === `${ABOUT_ROUTE}.md`) {
86
86
  const info = await this.#resolveBuildInfo();
87
- const profile = this.#getProfileStatus?.() ?? null;
88
- const content = renderAboutDoc(info, profile);
87
+ const context = this.#getContextStatus?.() ?? null;
88
+ const content = renderAboutDoc(info, context);
89
89
  return {
90
90
  url: url.href,
91
91
  content,
package/src/main.ts CHANGED
@@ -638,14 +638,14 @@ export async function runRootCommand(parsed: Args, rawArgs: string[]): Promise<v
638
638
  const cwd = getProjectDir();
639
639
  await logger.time("settings:init", Settings.init, { cwd });
640
640
 
641
- // F5 XC profile loading — optional, never blocks startup.
641
+ // F5 XC context loading — optional, never blocks startup.
642
642
  // NOTE: This runs in the CLI path only. SDK consumers using createAgentSession()
643
- // directly must call ProfileService.init(configDir).loadActive() themselves.
643
+ // directly must call ContextService.init(configDir).loadActive() themselves.
644
644
  try {
645
- const { ProfileService } = await import("./services/f5xc-profile");
645
+ const { ContextService } = await import("./services/f5xc-context");
646
646
  const { getF5XCConfigDir } = await import("@f5xc-salesdemos/pi-utils");
647
- const profileService = ProfileService.init(getF5XCConfigDir());
648
- await profileService.loadActive();
647
+ const contextService = ContextService.init(getF5XCConfigDir());
648
+ await contextService.loadActive();
649
649
  } catch {
650
650
  // F5 XC auth is optional — silently continue if anything fails
651
651
  }
@@ -3,7 +3,7 @@ import type { PresetDef, StatusLinePreset } from "./types";
3
3
  export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
4
4
  default: {
5
5
  leftSegments: ["pi", "model", "plan_mode", "path", "git", "pr", "context_pct", "token_total", "cost"],
6
- rightSegments: ["profile_f5xc"],
6
+ rightSegments: ["context_f5xc"],
7
7
  separator: "powerline",
8
8
  segmentOptions: {
9
9
  model: { showThinkingLevel: true },
@@ -14,7 +14,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
14
14
 
15
15
  minimal: {
16
16
  leftSegments: ["path", "git"],
17
- rightSegments: ["plan_mode", "context_pct", "profile_f5xc"],
17
+ rightSegments: ["plan_mode", "context_pct", "context_f5xc"],
18
18
  separator: "slash",
19
19
  segmentOptions: {
20
20
  path: { abbreviate: true, maxLength: 30 },
@@ -24,7 +24,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
24
24
 
25
25
  compact: {
26
26
  leftSegments: ["model", "plan_mode", "git", "pr"],
27
- rightSegments: ["cost", "context_pct", "profile_f5xc"],
27
+ rightSegments: ["cost", "context_pct", "context_f5xc"],
28
28
  separator: "powerline",
29
29
  segmentOptions: {
30
30
  model: { showThinkingLevel: false },
@@ -43,7 +43,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
43
43
  "context_pct",
44
44
  "time_spent",
45
45
  "time",
46
- "profile_f5xc",
46
+ "context_f5xc",
47
47
  ],
48
48
  separator: "powerline",
49
49
  segmentOptions: {
@@ -68,7 +68,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
68
68
  "context_total",
69
69
  "time_spent",
70
70
  "time",
71
- "profile_f5xc",
71
+ "context_f5xc",
72
72
  ],
73
73
  separator: "powerline",
74
74
  segmentOptions: {
@@ -82,7 +82,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
82
82
  ascii: {
83
83
  // No Nerd Font dependencies
84
84
  leftSegments: ["model", "plan_mode", "path", "git", "pr"],
85
- rightSegments: ["token_total", "cost", "context_pct", "profile_f5xc"],
85
+ rightSegments: ["token_total", "cost", "context_pct", "context_f5xc"],
86
86
  separator: "ascii",
87
87
  segmentOptions: {
88
88
  model: { showThinkingLevel: true },
@@ -93,7 +93,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
93
93
 
94
94
  xcsh: {
95
95
  leftSegments: ["context_pct", "path", "git"],
96
- rightSegments: ["plan_mode", "profile_f5xc"],
96
+ rightSegments: ["plan_mode", "context_f5xc"],
97
97
  separator: "powerline",
98
98
  segmentOptions: {
99
99
  model: { showThinkingLevel: true },
@@ -106,7 +106,7 @@ export const STATUS_LINE_PRESETS: Record<StatusLinePreset, PresetDef> = {
106
106
  custom: {
107
107
  // User-defined - these are just defaults that get overridden
108
108
  leftSegments: ["model", "plan_mode", "path", "git", "pr"],
109
- rightSegments: ["token_total", "cost", "context_pct", "profile_f5xc"],
109
+ rightSegments: ["token_total", "cost", "context_pct", "context_f5xc"],
110
110
  separator: "powerline",
111
111
  segmentOptions: {},
112
112
  },
@@ -440,17 +440,17 @@ export const SEGMENTS: Record<StatusLineSegmentId, StatusLineSegment> = {
440
440
  return { content: `${ansi}${sanitizeStatusText(name)}\x1b[39m`, visible: true };
441
441
  },
442
442
  },
443
- profile_f5xc: {
444
- id: "profile_f5xc",
443
+ context_f5xc: {
444
+ id: "context_f5xc",
445
445
  render() {
446
446
  try {
447
- const { renderF5XCProfileSegment } = require("../../../services/f5xc-profile-segment");
448
- const result = renderF5XCProfileSegment();
447
+ const { renderF5XCContextSegment } = require("../../../services/f5xc-context-segment");
448
+ const result = renderF5XCContextSegment();
449
449
  if (!result.visible) return result;
450
450
  return {
451
451
  ...result,
452
- bg: theme.fgColorAsBg("statusLineProfileF5xcBg"),
453
- fg: theme.getFgAnsi("statusLineProfileF5xcFg"),
452
+ bg: theme.fgColorAsBg("statusLineContextF5xcBg"),
453
+ fg: theme.getFgAnsi("statusLineContextF5xcFg"),
454
454
  };
455
455
  } catch {
456
456
  return { content: "", visible: false };
@@ -38,7 +38,7 @@ const SEGMENT_INFO: Record<StatusLineSegmentId, { label: string; short: string }
38
38
  cache_read: { label: "Cache ↓", short: "cache read" },
39
39
  cache_write: { label: "Cache ↑", short: "cache write" },
40
40
  session_name: { label: "Session Name", short: "session name" },
41
- profile_f5xc: { label: "F5 XC Profile", short: "F5 XC tenant" },
41
+ context_f5xc: { label: "F5 XC Context", short: "F5 XC tenant" },
42
42
  };
43
43
 
44
44
  type Column = "left" | "right" | "disabled";
@@ -1,27 +1,27 @@
1
1
  import type { Model } from "@f5xc-salesdemos/pi-ai";
2
2
  import { validateApiKeyAgainstModelsEndpoint } from "@f5xc-salesdemos/pi-ai/utils/oauth/api-key-validation";
3
3
  import { logger } from "@f5xc-salesdemos/pi-utils";
4
- import { type AuthStatus, ProfileService } from "../../services/f5xc-profile";
4
+ import { type AuthStatus, ContextService } from "../../services/f5xc-context";
5
5
  import type { AuthStorage } from "../../session/auth-storage";
6
6
 
7
7
  // Startup validation budget. These are longer than validateToken's 3000ms default because
8
8
  // the welcome path runs during TLS/DNS cold-start — a single 3s shot races against warm-up
9
- // and falsely reports offline for profiles that reconnect cleanly moments later.
9
+ // and falsely reports offline for contexts that reconnect cleanly moments later.
10
10
  const STARTUP_FIRST_TIMEOUT_MS = 4000;
11
11
  const STARTUP_RETRY_TIMEOUT_MS = 5000;
12
12
  const STARTUP_RETRY_DELAY_MS = 500;
13
13
 
14
- type ProfileValidator = (opts: {
14
+ type ContextValidator = (opts: {
15
15
  timeoutMs: number;
16
16
  }) => Promise<{ status: AuthStatus; latencyMs?: number; errorClass?: "network" | "credential" }>;
17
17
 
18
18
  /**
19
- * Runs the profile validator once with a startup-sized timeout; if the result is `offline`
19
+ * Runs the context validator once with a startup-sized timeout; if the result is `offline`
20
20
  * (the only transient class — auth_error/connected/unknown are definitive), waits briefly
21
21
  * to let DNS/TLS warm up, then tries once more with a longer timeout.
22
22
  */
23
- export async function validateProfileWithStartupRetry(
24
- validate: ProfileValidator,
23
+ export async function validateContextWithStartupRetry(
24
+ validate: ContextValidator,
25
25
  options?: {
26
26
  firstTimeoutMs?: number;
27
27
  retryTimeoutMs?: number;
@@ -49,17 +49,17 @@ export interface ModelStatus {
49
49
  latencyMs?: number;
50
50
  }
51
51
 
52
- export type ProfileCheckState = "no_profile" | "connected" | "auth_error" | "offline";
52
+ export type ContextCheckState = "no_context" | "connected" | "auth_error" | "offline";
53
53
 
54
- export interface WelcomeProfileStatus {
55
- state: ProfileCheckState;
54
+ export interface WelcomeContextStatus {
55
+ state: ContextCheckState;
56
56
  name?: string;
57
57
  latencyMs?: number;
58
58
  }
59
59
 
60
60
  export interface WelcomeCheckResult {
61
61
  model: ModelStatus;
62
- profile?: WelcomeProfileStatus;
62
+ context?: WelcomeContextStatus;
63
63
  }
64
64
 
65
65
  /** Providers that don't store API keys (local inference servers) */
@@ -67,7 +67,7 @@ const KEYLESS_PROVIDERS = new Set(["ollama", "llama.cpp", "lm-studio", "llamafil
67
67
 
68
68
  /**
69
69
  * Run blocking startup checks for the welcome screen.
70
- * Model check always runs. Profile check only runs if model is connected.
70
+ * Model check always runs. Context check only runs if model is connected.
71
71
  */
72
72
  export async function runWelcomeChecks(
73
73
  model: Model | undefined,
@@ -87,9 +87,9 @@ export async function runWelcomeChecks(
87
87
  return { model: modelStatus };
88
88
  }
89
89
 
90
- // Step 3: Profile check (only if model is connected)
91
- const profileStatus = await checkProfileStatus();
92
- return { model: modelStatus, profile: profileStatus };
90
+ // Step 3: Context check (only if model is connected)
91
+ const contextStatus = await checkContextStatus();
92
+ return { model: modelStatus, context: contextStatus };
93
93
  }
94
94
 
95
95
  async function validateModelConnection(model: Model | undefined, authStorage: AuthStorage): Promise<ModelStatus> {
@@ -141,20 +141,20 @@ async function validateModelConnection(model: Model | undefined, authStorage: Au
141
141
  }
142
142
  }
143
143
 
144
- async function checkProfileStatus(): Promise<WelcomeProfileStatus> {
144
+ async function checkContextStatus(): Promise<WelcomeContextStatus> {
145
145
  try {
146
- const profileService = ProfileService.instance;
147
- if (!profileService) {
148
- return { state: "no_profile" };
146
+ const contextService = ContextService.instance;
147
+ if (!contextService) {
148
+ return { state: "no_context" };
149
149
  }
150
150
 
151
- const status = profileService.getStatus();
151
+ const status = contextService.getStatus();
152
152
  if (!status.isConfigured) {
153
- return { state: "no_profile" };
153
+ return { state: "no_context" };
154
154
  }
155
155
 
156
- const name = status.activeProfileName ?? "default";
157
- const result = await validateProfileWithStartupRetry(opts => profileService.validateToken(opts));
156
+ const name = status.activeContextName ?? "default";
157
+ const result = await validateContextWithStartupRetry(opts => contextService.validateToken(opts));
158
158
 
159
159
  switch (result.status) {
160
160
  case "connected":
@@ -164,10 +164,10 @@ async function checkProfileStatus(): Promise<WelcomeProfileStatus> {
164
164
  case "offline":
165
165
  return { state: "offline", name };
166
166
  default:
167
- return { state: "no_profile" };
167
+ return { state: "no_context" };
168
168
  }
169
169
  } catch {
170
- logger.warn("Welcome profile validation failed");
171
- return { state: "no_profile" };
170
+ logger.warn("Welcome context validation failed");
171
+ return { state: "no_context" };
172
172
  }
173
173
  }
@@ -1,8 +1,8 @@
1
1
  import { type Component, padding, truncateToWidth, visibleWidth } from "@f5xc-salesdemos/pi-tui";
2
2
  import { APP_NAME } from "@f5xc-salesdemos/pi-utils";
3
3
  import { theme } from "../../modes/theme/theme";
4
- import { formatStatusIcon } from "../../services/f5xc-profile-indicators";
5
- import type { ModelStatus, WelcomeProfileStatus } from "./welcome-checks";
4
+ import { formatStatusIcon } from "../../services/f5xc-context-indicators";
5
+ import type { ModelStatus, WelcomeContextStatus } from "./welcome-checks";
6
6
 
7
7
  export interface UpdateStatus {
8
8
  available: boolean;
@@ -18,7 +18,7 @@ export class WelcomeComponent implements Component {
18
18
  constructor(
19
19
  private readonly version: string,
20
20
  private modelStatus: ModelStatus,
21
- private profileStatus?: WelcomeProfileStatus,
21
+ private contextStatus?: WelcomeContextStatus,
22
22
  private updateStatus?: UpdateStatus,
23
23
  private changelogStatus?: ChangelogStatus,
24
24
  ) {}
@@ -26,8 +26,8 @@ export class WelcomeComponent implements Component {
26
26
  setModelStatus(status: ModelStatus): void {
27
27
  this.modelStatus = status;
28
28
  }
29
- setProfileStatus(status: WelcomeProfileStatus | undefined): void {
30
- this.profileStatus = status;
29
+ setContextStatus(status: WelcomeContextStatus | undefined): void {
30
+ this.contextStatus = status;
31
31
  }
32
32
  setUpdateStatus(status: UpdateStatus | undefined): void {
33
33
  this.updateStatus = status;
@@ -127,8 +127,8 @@ export class WelcomeComponent implements Component {
127
127
 
128
128
  #measureStatusWidth(): number {
129
129
  const lines: string[] = [" Model Provider", ...this.#renderModelStatus()];
130
- if (this.profileStatus) {
131
- lines.push(" F5 XC Profile", ...this.#renderProfileStatus());
130
+ if (this.contextStatus) {
131
+ lines.push(" F5 XC Context", ...this.#renderContextStatus());
132
132
  }
133
133
  if (this.#showUpdateSection()) {
134
134
  lines.push(" Update Available", ...this.#renderUpdateStatus());
@@ -147,11 +147,11 @@ export class WelcomeComponent implements Component {
147
147
  lines.push(` ${theme.bold(theme.fg("contentAccent", "Model Provider"))}`);
148
148
  lines.push(...this.#renderModelStatus());
149
149
  lines.push("");
150
- if (this.profileStatus) {
150
+ if (this.contextStatus) {
151
151
  lines.push(separator);
152
152
  lines.push("");
153
- lines.push(` ${theme.bold(theme.fg("contentAccent", "F5 XC Profile"))}`);
154
- lines.push(...this.#renderProfileStatus());
153
+ lines.push(` ${theme.bold(theme.fg("contentAccent", "F5 XC Context"))}`);
154
+ lines.push(...this.#renderContextStatus());
155
155
  lines.push("");
156
156
  }
157
157
  if (this.#showUpdateSection()) {
@@ -217,9 +217,9 @@ export class WelcomeComponent implements Component {
217
217
  }
218
218
  }
219
219
 
220
- #renderProfileStatus(): string[] {
221
- if (!this.profileStatus) return [];
222
- const { state, name, latencyMs } = this.profileStatus;
220
+ #renderContextStatus(): string[] {
221
+ if (!this.contextStatus) return [];
222
+ const { state, name, latencyMs } = this.contextStatus;
223
223
  const n = name ?? "default";
224
224
  switch (state) {
225
225
  case "connected":
@@ -229,17 +229,17 @@ export class WelcomeComponent implements Component {
229
229
  case "auth_error":
230
230
  return [
231
231
  ` ${formatStatusIcon("error")} ${theme.fg("muted", n)} ${theme.fg("error", "\u2014 token invalid")}`,
232
- ` ${theme.fg("dim", "Run /profile to update")}`,
232
+ ` ${theme.fg("dim", "Run /context to update")}`,
233
233
  ];
234
234
  case "offline":
235
235
  return [
236
236
  ` ${formatStatusIcon("warning")} ${theme.fg("muted", n)} ${theme.fg("warning", "\u2014 unreachable")}`,
237
- ` ${theme.fg("dim", "Check network, /profile")}`,
237
+ ` ${theme.fg("dim", "Check network, /context")}`,
238
238
  ];
239
- case "no_profile":
239
+ case "no_context":
240
240
  return [
241
- ` ${formatStatusIcon("warning")} ${theme.fg("warning", "No profile configured")}`,
242
- ` ${theme.fg("dim", "Run /profile create <name> <url> <token>")}`,
241
+ ` ${formatStatusIcon("warning")} ${theme.fg("warning", "No context configured")}`,
242
+ ` ${theme.fg("dim", "Run /context create <name> <url> <token>")}`,
243
243
  ];
244
244
  }
245
245
  }
@@ -310,7 +310,7 @@ export class InteractiveMode implements InteractiveModeContext {
310
310
  getProjectDir(),
311
311
  );
312
312
 
313
- // Run blocking welcome screen status checks (model + profile)
313
+ // Run blocking welcome screen status checks (model + context)
314
314
  const welcomeResult = await logger.time("InteractiveMode.init:welcomeChecks", () =>
315
315
  runWelcomeChecks(this.session.model, this.session.modelRegistry.authStorage),
316
316
  );
@@ -324,11 +324,11 @@ export class InteractiveMode implements InteractiveModeContext {
324
324
  }
325
325
 
326
326
  if (!startupQuiet) {
327
- // Welcome box owns all startup notifications (model, profile, update, changelog)
327
+ // Welcome box owns all startup notifications (model, context, update, changelog)
328
328
  this.#welcomeComponent = new WelcomeComponent(
329
329
  this.#version,
330
330
  welcomeResult.model,
331
- welcomeResult.profile,
331
+ welcomeResult.context,
332
332
  this.#initialUpdateStatus,
333
333
  this.#changelogStatus,
334
334
  );
@@ -100,8 +100,8 @@
100
100
  "statusLineGitConflictFg": 7,
101
101
  "statusLinePlanModeBg": 236,
102
102
  "statusLinePlanModeFg": 117,
103
- "statusLineProfileF5xcBg": "f5Red",
104
- "statusLineProfileF5xcFg": 231,
103
+ "statusLineContextF5xcBg": "f5Red",
104
+ "statusLineContextF5xcFg": 231,
105
105
  "pythonMode": "#f0c040",
106
106
  "syntaxControl": "#569CD6"
107
107
  },
@@ -102,8 +102,8 @@
102
102
  "statusLineGitConflictFg": 255,
103
103
  "statusLinePlanModeBg": 223,
104
104
  "statusLinePlanModeFg": 238,
105
- "statusLineProfileF5xcBg": "f5Red",
106
- "statusLineProfileF5xcFg": 231,
105
+ "statusLineContextF5xcBg": "f5Red",
106
+ "statusLineContextF5xcFg": 231,
107
107
  "pythonMode": "warningAmber",
108
108
  "syntaxControl": "f5Red"
109
109
  },
@@ -123,8 +123,8 @@
123
123
  "statusLineGitConflictFg",
124
124
  "statusLinePlanModeBg",
125
125
  "statusLinePlanModeFg",
126
- "statusLineProfileF5xcBg",
127
- "statusLineProfileF5xcFg"
126
+ "statusLineContextF5xcBg",
127
+ "statusLineContextF5xcFg"
128
128
  ],
129
129
  "properties": {
130
130
  "accent": {
@@ -483,13 +483,13 @@
483
483
  "$ref": "#/$defs/colorValue",
484
484
  "description": "256-color palette index for the powerline plan-mode segment foreground. See issue #242 for the two-domain color model."
485
485
  },
486
- "statusLineProfileF5xcBg": {
486
+ "statusLineContextF5xcBg": {
487
487
  "$ref": "#/$defs/colorValue",
488
- "description": "256-color palette index OR color reference for the powerline F5 XC profile segment background. Stays F5 brand red across light and dark themes. See issue #242 for the two-domain color model."
488
+ "description": "256-color palette index OR color reference for the powerline F5 XC context segment background. Stays F5 brand red across light and dark themes. See issue #242 for the two-domain color model."
489
489
  },
490
- "statusLineProfileF5xcFg": {
490
+ "statusLineContextF5xcFg": {
491
491
  "$ref": "#/$defs/colorValue",
492
- "description": "256-color palette index for the powerline F5 XC profile segment foreground. See issue #242 for the two-domain color model."
492
+ "description": "256-color palette index for the powerline F5 XC context segment foreground. See issue #242 for the two-domain color model."
493
493
  }
494
494
  },
495
495
  "additionalProperties": false