@f5xc-salesdemos/xcsh 18.47.0 → 18.48.1
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/autoresearch/tools/init-experiment.ts +2 -2
- package/src/autoresearch/tools/log-experiment.ts +2 -2
- package/src/autoresearch/tools/run-experiment.ts +1 -1
- package/src/config/settings-schema.ts +55 -5
- package/src/extensibility/skills.ts +2 -1
- package/src/internal-urls/build-info.generated.ts +9 -9
- package/src/modes/components/welcome-checks.ts +97 -0
- package/src/modes/interactive-mode.ts +23 -11
- package/src/prompts/system/system-prompt.md +13 -0
- package/src/sdk.ts +24 -0
- package/src/system-prompt.ts +7 -0
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.48.1",
|
|
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.48.1",
|
|
52
|
+
"@f5xc-salesdemos/pi-agent-core": "18.48.1",
|
|
53
|
+
"@f5xc-salesdemos/pi-ai": "18.48.1",
|
|
54
|
+
"@f5xc-salesdemos/pi-natives": "18.48.1",
|
|
55
|
+
"@f5xc-salesdemos/pi-tui": "18.48.1",
|
|
56
|
+
"@f5xc-salesdemos/pi-utils": "18.48.1",
|
|
57
57
|
"@sinclair/typebox": "^0.34",
|
|
58
58
|
"@xterm/headless": "^6.0",
|
|
59
59
|
"ajv": "^8.18",
|
|
@@ -385,8 +385,8 @@ export function createInitExperimentTool(
|
|
|
385
385
|
};
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
-
function renderInitCall(name: string, theme: Theme, width?: number): string {
|
|
389
|
-
return `${theme.fg("toolTitle", theme.bold("init_experiment"))} ${theme.fg("contentAccent", truncateToWidth(replaceTabs(name), Math.max(20, (width ?? 100) - 20)))}`;
|
|
388
|
+
function renderInitCall(name: string | undefined, theme: Theme, width?: number): string {
|
|
389
|
+
return `${theme.fg("toolTitle", theme.bold("init_experiment"))} ${theme.fg("contentAccent", truncateToWidth(replaceTabs(name ?? ""), Math.max(20, (width ?? 100) - 20)))}`;
|
|
390
390
|
}
|
|
391
391
|
|
|
392
392
|
function collectLoggedRunNumbers(results: ExperimentState["results"]): Set<number> {
|
|
@@ -362,7 +362,7 @@ export function createLogExperimentTool(
|
|
|
362
362
|
const color = args.status === "keep" ? "success" : args.status === "discard" ? "warning" : "error";
|
|
363
363
|
return {
|
|
364
364
|
render(width: number): string[] {
|
|
365
|
-
const description = truncateToWidth(replaceTabs(args.description), Math.max(20, width - 30));
|
|
365
|
+
const description = truncateToWidth(replaceTabs(args.description ?? ""), Math.max(20, width - 30));
|
|
366
366
|
return [
|
|
367
367
|
`${theme.fg("toolTitle", theme.bold("log_experiment"))} ${theme.fg(color, args.status)} ${theme.fg("muted", description)}`,
|
|
368
368
|
];
|
|
@@ -773,7 +773,7 @@ function truncateAsiValue(value: ASIData[string]): string {
|
|
|
773
773
|
function renderSummary(details: LogDetails, theme: Theme, width?: number): string {
|
|
774
774
|
const { experiment, state } = details;
|
|
775
775
|
const color = experiment.status === "keep" ? "success" : experiment.status === "discard" ? "warning" : "error";
|
|
776
|
-
let summary = `${theme.fg(color, experiment.status.toUpperCase())} ${theme.fg("muted", truncateToWidth(replaceTabs(experiment.description), Math.max(20, (width ?? 100) - 30)))}`;
|
|
776
|
+
let summary = `${theme.fg(color, experiment.status.toUpperCase())} ${theme.fg("muted", truncateToWidth(replaceTabs(experiment.description ?? ""), Math.max(20, (width ?? 100) - 30)))}`;
|
|
777
777
|
summary += ` ${theme.fg("contentAccent", `${state.metricName}=${formatNum(experiment.metric, state.metricUnit)}`)}`;
|
|
778
778
|
if (state.bestMetric !== null) {
|
|
779
779
|
summary += ` ${theme.fg("dim", `baseline ${formatNum(state.bestMetric, state.metricUnit)}`)}`;
|
|
@@ -383,7 +383,7 @@ export function createRunExperimentTool(
|
|
|
383
383
|
renderCall(args, _options, theme): Component {
|
|
384
384
|
return {
|
|
385
385
|
render(width: number): string[] {
|
|
386
|
-
const commandPreview = truncateToWidth(replaceTabs(args.command), Math.max(20, width - 20));
|
|
386
|
+
const commandPreview = truncateToWidth(replaceTabs(args.command ?? ""), Math.max(20, width - 20));
|
|
387
387
|
return [`${theme.fg("toolTitle", theme.bold("run_experiment"))} ${theme.fg("muted", commandPreview)}`];
|
|
388
388
|
},
|
|
389
389
|
invalidate() {},
|
|
@@ -1622,15 +1622,65 @@ export const SETTINGS_SCHEMA = {
|
|
|
1622
1622
|
ui: { tab: "tasks", label: "Skill Commands", description: "Register skills as /skill:name commands" },
|
|
1623
1623
|
},
|
|
1624
1624
|
|
|
1625
|
-
"skills.enableCodexUser": {
|
|
1625
|
+
"skills.enableCodexUser": {
|
|
1626
|
+
type: "boolean",
|
|
1627
|
+
default: false,
|
|
1628
|
+
ui: {
|
|
1629
|
+
tab: "tasks",
|
|
1630
|
+
label: "Codex User Skills",
|
|
1631
|
+
description: "Load skills from Codex configuration",
|
|
1632
|
+
},
|
|
1633
|
+
},
|
|
1626
1634
|
|
|
1627
|
-
"skills.enableClaudeUser": {
|
|
1635
|
+
"skills.enableClaudeUser": {
|
|
1636
|
+
type: "boolean",
|
|
1637
|
+
default: false,
|
|
1638
|
+
ui: {
|
|
1639
|
+
tab: "tasks",
|
|
1640
|
+
label: "Claude User Skills",
|
|
1641
|
+
description: "Load skills from ~/.claude/skills/",
|
|
1642
|
+
},
|
|
1643
|
+
},
|
|
1628
1644
|
|
|
1629
|
-
"skills.enableClaudeProject": {
|
|
1645
|
+
"skills.enableClaudeProject": {
|
|
1646
|
+
type: "boolean",
|
|
1647
|
+
default: false,
|
|
1648
|
+
ui: {
|
|
1649
|
+
tab: "tasks",
|
|
1650
|
+
label: "Claude Project Skills",
|
|
1651
|
+
description: "Load skills from .claude/skills/",
|
|
1652
|
+
},
|
|
1653
|
+
},
|
|
1630
1654
|
|
|
1631
|
-
"skills.
|
|
1655
|
+
"skills.enableClaudePlugins": {
|
|
1656
|
+
type: "boolean",
|
|
1657
|
+
default: false,
|
|
1658
|
+
ui: {
|
|
1659
|
+
tab: "tasks",
|
|
1660
|
+
label: "Claude Marketplace Skills",
|
|
1661
|
+
description: "Load skills from Claude Code marketplace plugins (~/.claude/plugins/cache/)",
|
|
1662
|
+
},
|
|
1663
|
+
},
|
|
1664
|
+
|
|
1665
|
+
"skills.enablePiUser": {
|
|
1666
|
+
type: "boolean",
|
|
1667
|
+
default: true,
|
|
1668
|
+
ui: {
|
|
1669
|
+
tab: "tasks",
|
|
1670
|
+
label: "xcsh User Skills",
|
|
1671
|
+
description: "Load user-level skills from ~/.xcsh/agent/skills/",
|
|
1672
|
+
},
|
|
1673
|
+
},
|
|
1632
1674
|
|
|
1633
|
-
"skills.enablePiProject": {
|
|
1675
|
+
"skills.enablePiProject": {
|
|
1676
|
+
type: "boolean",
|
|
1677
|
+
default: true,
|
|
1678
|
+
ui: {
|
|
1679
|
+
tab: "tasks",
|
|
1680
|
+
label: "xcsh Project Skills",
|
|
1681
|
+
description: "Load project-level skills from .xcsh/skills/",
|
|
1682
|
+
},
|
|
1683
|
+
},
|
|
1634
1684
|
|
|
1635
1685
|
"skills.customDirectories": { type: "array", default: [] as string[] },
|
|
1636
1686
|
|
|
@@ -82,6 +82,7 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
82
82
|
enableCodexUser = true,
|
|
83
83
|
enableClaudeUser = true,
|
|
84
84
|
enableClaudeProject = true,
|
|
85
|
+
enableClaudePlugins = false,
|
|
85
86
|
enablePiUser = true,
|
|
86
87
|
enablePiProject = true,
|
|
87
88
|
customDirectories = [],
|
|
@@ -105,7 +106,7 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
105
106
|
if (provider === "claude" && level === "project") return enableClaudeProject;
|
|
106
107
|
if (provider === "native" && level === "user") return enablePiUser;
|
|
107
108
|
if (provider === "native" && level === "project") return enablePiProject;
|
|
108
|
-
|
|
109
|
+
if (provider === "claude-plugins") return enableClaudePlugins;
|
|
109
110
|
return anyBuiltInSkillSourceEnabled;
|
|
110
111
|
}
|
|
111
112
|
|
|
@@ -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.48.1",
|
|
21
|
+
"commit": "158e9c98808fdbaeac9b8c2e0623768abffa800e",
|
|
22
|
+
"shortCommit": "158e9c9",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v18.
|
|
25
|
-
"commitDate": "2026-05-
|
|
26
|
-
"buildDate": "2026-05-
|
|
27
|
-
"dirty":
|
|
24
|
+
"tag": "v18.48.1",
|
|
25
|
+
"commitDate": "2026-05-06T15:22:57Z",
|
|
26
|
+
"buildDate": "2026-05-06T15:49:54.715Z",
|
|
27
|
+
"dirty": true,
|
|
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/158e9c98808fdbaeac9b8c2e0623768abffa800e",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v18.48.1"
|
|
33
33
|
};
|
|
@@ -2,6 +2,7 @@ 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 { $which, logger } from "@f5xc-salesdemos/pi-utils";
|
|
4
4
|
import { $ } from "bun";
|
|
5
|
+
import { loadProfile } from "../../internal-urls/user-profile";
|
|
5
6
|
import { type AuthStatus, ContextService } from "../../services/f5xc-context";
|
|
6
7
|
import { deriveTenantFromUrl } from "../../services/f5xc-env";
|
|
7
8
|
import type { AuthStorage } from "../../session/auth-storage";
|
|
@@ -426,3 +427,99 @@ export function mapAzureStatus(status: WelcomeAzureStatus | undefined): ServiceS
|
|
|
426
427
|
return { name: "Azure", state: "unauthenticated", hint: "run: az login --use-device-code" };
|
|
427
428
|
}
|
|
428
429
|
}
|
|
430
|
+
|
|
431
|
+
export type AwsCheckState = "connected" | "auth_error";
|
|
432
|
+
|
|
433
|
+
export interface WelcomeAwsStatus {
|
|
434
|
+
state: AwsCheckState;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
export async function checkAwsStatus(): Promise<WelcomeAwsStatus | undefined> {
|
|
438
|
+
try {
|
|
439
|
+
if (!$which("aws")) return undefined;
|
|
440
|
+
const result = await $`aws sts get-caller-identity --output json`.quiet().nothrow();
|
|
441
|
+
return { state: result.exitCode === 0 ? "connected" : "auth_error" };
|
|
442
|
+
} catch (err) {
|
|
443
|
+
logger.warn("AWS startup check failed", { error: String(err) });
|
|
444
|
+
return { state: "auth_error" };
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
export function mapAwsStatus(status: WelcomeAwsStatus | undefined): ServiceStatus {
|
|
449
|
+
if (!status) return { name: "AWS", state: "unavailable", hint: "not installed" };
|
|
450
|
+
switch (status.state) {
|
|
451
|
+
case "connected":
|
|
452
|
+
return { name: "AWS", state: "connected" };
|
|
453
|
+
case "auth_error":
|
|
454
|
+
return { name: "AWS", state: "unauthenticated", hint: "run: aws configure" };
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
export type GcloudCheckState = "connected" | "auth_error";
|
|
459
|
+
|
|
460
|
+
export interface WelcomeGcloudStatus {
|
|
461
|
+
state: GcloudCheckState;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export async function checkGcloudStatus(): Promise<WelcomeGcloudStatus | undefined> {
|
|
465
|
+
try {
|
|
466
|
+
if (!$which("gcloud")) return undefined;
|
|
467
|
+
const result = await $`gcloud auth list --format=value(account)`.quiet().nothrow();
|
|
468
|
+
const hasAccount = result.text().trim().length > 0;
|
|
469
|
+
return { state: hasAccount ? "connected" : "auth_error" };
|
|
470
|
+
} catch (err) {
|
|
471
|
+
logger.warn("Google Cloud startup check failed", { error: String(err) });
|
|
472
|
+
return { state: "auth_error" };
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export function mapGcloudStatus(status: WelcomeGcloudStatus | undefined): ServiceStatus {
|
|
477
|
+
if (!status) return { name: "Google Cloud", state: "unavailable", hint: "not installed" };
|
|
478
|
+
switch (status.state) {
|
|
479
|
+
case "connected":
|
|
480
|
+
return { name: "Google Cloud", state: "connected" };
|
|
481
|
+
case "auth_error":
|
|
482
|
+
return { name: "Google Cloud", state: "unauthenticated", hint: "run: gcloud auth login" };
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export type ProfileCheckState = "current" | "stale" | "missing";
|
|
487
|
+
|
|
488
|
+
export interface WelcomeProfileStatus {
|
|
489
|
+
state: ProfileCheckState;
|
|
490
|
+
name?: string;
|
|
491
|
+
updatedAt?: string;
|
|
492
|
+
staleDays?: number;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
const PROFILE_STALE_HOURS = 24;
|
|
496
|
+
|
|
497
|
+
/** Check user profile freshness. Returns undefined only on unexpected error. */
|
|
498
|
+
export async function checkProfileStatus(): Promise<WelcomeProfileStatus | undefined> {
|
|
499
|
+
try {
|
|
500
|
+
const profile = await loadProfile();
|
|
501
|
+
if (!profile.givenName && !profile.familyName) {
|
|
502
|
+
return { state: "missing" };
|
|
503
|
+
}
|
|
504
|
+
const name = [profile.givenName, profile.familyName].filter(Boolean).join(" ");
|
|
505
|
+
const updatedAt = profile.updatedAt;
|
|
506
|
+
|
|
507
|
+
if (!updatedAt) {
|
|
508
|
+
return { state: "stale", name };
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
const ageHours = (Date.now() - new Date(updatedAt).getTime()) / (1000 * 60 * 60);
|
|
512
|
+
if (ageHours > PROFILE_STALE_HOURS) {
|
|
513
|
+
return {
|
|
514
|
+
state: "stale",
|
|
515
|
+
name,
|
|
516
|
+
updatedAt,
|
|
517
|
+
staleDays: Math.floor(ageHours / 24),
|
|
518
|
+
};
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return { state: "current", name, updatedAt };
|
|
522
|
+
} catch {
|
|
523
|
+
return { state: "missing" };
|
|
524
|
+
}
|
|
525
|
+
}
|
|
@@ -28,6 +28,7 @@ import type {
|
|
|
28
28
|
import type { CompactOptions } from "../extensibility/extensions/types";
|
|
29
29
|
import { BUILTIN_SLASH_COMMANDS, loadSlashCommands } from "../extensibility/slash-commands";
|
|
30
30
|
import { resolveLocalUrlToPath } from "../internal-urls";
|
|
31
|
+
import { seedProfile } from "../internal-urls/user-profile";
|
|
31
32
|
import { renameApprovedPlanFile } from "../plan-mode/approved-plan";
|
|
32
33
|
import planModeApprovedPrompt from "../prompts/system/plan-mode-approved.md" with { type: "text" };
|
|
33
34
|
import type { AgentSession, AgentSessionEvent } from "../session/agent-session";
|
|
@@ -51,12 +52,16 @@ import { StatusLineComponent } from "./components/status-line";
|
|
|
51
52
|
import type { ToolExecutionHandle } from "./components/tool-execution";
|
|
52
53
|
import { type UpdateStatus, WelcomeComponent } from "./components/welcome";
|
|
53
54
|
import {
|
|
55
|
+
checkAwsStatus,
|
|
54
56
|
checkAzureStatus,
|
|
57
|
+
checkGcloudStatus,
|
|
55
58
|
checkGitHubStatus,
|
|
56
59
|
checkGitLabStatus,
|
|
57
60
|
checkSalesforceStatus,
|
|
61
|
+
mapAwsStatus,
|
|
58
62
|
mapAzureStatus,
|
|
59
63
|
mapContextStatus,
|
|
64
|
+
mapGcloudStatus,
|
|
60
65
|
mapGitHubStatus,
|
|
61
66
|
mapGitLabStatus,
|
|
62
67
|
mapSalesforceStatus,
|
|
@@ -318,17 +323,22 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
318
323
|
getProjectDir(),
|
|
319
324
|
);
|
|
320
325
|
|
|
321
|
-
// Run blocking welcome screen status checks
|
|
322
|
-
const [welcomeResult, gitlabStatus, salesforceStatus, githubStatus, azureStatus] =
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
326
|
+
// Run blocking welcome screen status checks in parallel
|
|
327
|
+
const [welcomeResult, gitlabStatus, salesforceStatus, githubStatus, azureStatus, awsStatus, gcloudStatus] =
|
|
328
|
+
await Promise.all([
|
|
329
|
+
logger.time("InteractiveMode.init:welcomeChecks", () =>
|
|
330
|
+
runWelcomeChecks(this.session.model, this.session.modelRegistry.authStorage),
|
|
331
|
+
),
|
|
332
|
+
checkGitLabStatus(getProjectDir()).catch(() => undefined),
|
|
333
|
+
checkSalesforceStatus(getProjectDir()).catch(() => undefined),
|
|
334
|
+
checkGitHubStatus().catch(() => undefined),
|
|
335
|
+
checkAzureStatus().catch(() => undefined),
|
|
336
|
+
checkAwsStatus().catch(() => undefined),
|
|
337
|
+
checkGcloudStatus().catch(() => undefined),
|
|
338
|
+
]);
|
|
339
|
+
|
|
340
|
+
// Refresh user profile in background — fire and forget
|
|
341
|
+
seedProfile().catch(err => logger.warn("Background profile refresh failed", { error: String(err) }));
|
|
332
342
|
const startupQuiet = settings.get("startup.quiet");
|
|
333
343
|
this.#welcomeComponent = undefined;
|
|
334
344
|
|
|
@@ -348,6 +358,8 @@ export class InteractiveMode implements InteractiveModeContext {
|
|
|
348
358
|
mapGitHubStatus(githubStatus),
|
|
349
359
|
mapSalesforceStatus(salesforceStatus),
|
|
350
360
|
mapAzureStatus(azureStatus),
|
|
361
|
+
mapAwsStatus(awsStatus),
|
|
362
|
+
mapGcloudStatus(gcloudStatus),
|
|
351
363
|
]
|
|
352
364
|
: [];
|
|
353
365
|
this.#welcomeComponent = new WelcomeComponent(
|
|
@@ -149,6 +149,17 @@ Use these values when constructing API payloads and resource names.
|
|
|
149
149
|
Available F5 XC documentation topics: {{knowledgeTopics}}.
|
|
150
150
|
{{/if}}
|
|
151
151
|
{{/if}}
|
|
152
|
+
{{#if userProfile}}
|
|
153
|
+
## Primary Human
|
|
154
|
+
|
|
155
|
+
{{userProfile.name}} ({{userProfile.role}}, {{userProfile.org}}).
|
|
156
|
+
Full profile at `xcsh://user`. **MUST** read when:
|
|
157
|
+
- Addressing the user by name or drafting communications from/to them
|
|
158
|
+
- A tool call needs personal identifiers (Salesforce user ID, GitHub username, email, phone)
|
|
159
|
+
- User asks about themselves ("my email", "who is my manager", "where am I from")
|
|
160
|
+
- Answering relationship/identity questions ("who is your human", "who do you work with")
|
|
161
|
+
**SHOULD NOT** read for routine technical work, code changes, or product questions.
|
|
162
|
+
{{/if}}
|
|
152
163
|
|
|
153
164
|
{{#if contextFiles.length}}
|
|
154
165
|
<context>
|
|
@@ -194,6 +205,8 @@ Most tools resolve custom protocol URLs to internal resources (not web URLs):
|
|
|
194
205
|
- `xcsh://about` — Identity, version, build fingerprint, architecture, self-improvement. **MUST** read for any question about xcsh before exploring `~/.xcsh/`.
|
|
195
206
|
This document contains the authoritative repository URL, issues URL, and source location.
|
|
196
207
|
For identity questions (source code, repo, version, who built this) — answer from `xcsh://about` alone. Do not call external GitHub tools.
|
|
208
|
+
- `xcsh://user` — Primary human user profile (identity, employment, contact, demographics). Read when personal identity context is needed. Do not read proactively on every turn.
|
|
209
|
+
- `xcsh://user?seed=true` — Refresh profile from Salesforce, GitHub, and system sources.
|
|
197
210
|
- `xcsh://api-spec/` — F5 XC API specifications (schema introspection, field types, validation).
|
|
198
211
|
- `xcsh://api-catalog/` — F5 XC API operations catalog (CRUD execution).
|
|
199
212
|
|
package/src/sdk.ts
CHANGED
|
@@ -72,6 +72,7 @@ import {
|
|
|
72
72
|
RuleProtocolHandler,
|
|
73
73
|
SkillProtocolHandler,
|
|
74
74
|
} from "./internal-urls";
|
|
75
|
+
import { loadProfile } from "./internal-urls/user-profile";
|
|
75
76
|
import { disposeAllKernelSessions, disposeKernelSessionsByOwner } from "./ipy/executor";
|
|
76
77
|
import { LSP_STARTUP_EVENT_CHANNEL, type LspStartupEvent } from "./lsp/startup-events";
|
|
77
78
|
import { discoverAndLoadMCPTools, type MCPManager, type MCPToolsLoadResult } from "./mcp";
|
|
@@ -1450,6 +1451,27 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1450
1451
|
}
|
|
1451
1452
|
appendPrompt = parts.join("\n\n");
|
|
1452
1453
|
}
|
|
1454
|
+
// Load compact user profile for system prompt hint
|
|
1455
|
+
let userProfile: { name: string; role: string; org: string } | undefined;
|
|
1456
|
+
try {
|
|
1457
|
+
const _profile = await loadProfile();
|
|
1458
|
+
if (_profile.givenName || _profile.familyName) {
|
|
1459
|
+
const _name = [_profile.givenName, _profile.familyName].filter(Boolean).join(" ");
|
|
1460
|
+
if (_name) {
|
|
1461
|
+
userProfile = {
|
|
1462
|
+
name: _name,
|
|
1463
|
+
role: _profile.jobTitle ?? "",
|
|
1464
|
+
org:
|
|
1465
|
+
typeof _profile.worksFor === "string"
|
|
1466
|
+
? _profile.worksFor
|
|
1467
|
+
: ((_profile.worksFor as { name?: string } | undefined)?.name ?? ""),
|
|
1468
|
+
};
|
|
1469
|
+
}
|
|
1470
|
+
}
|
|
1471
|
+
} catch {
|
|
1472
|
+
// No profile — hint block omitted
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1453
1475
|
const defaultPrompt = await buildSystemPromptInternal({
|
|
1454
1476
|
cwd,
|
|
1455
1477
|
skills,
|
|
@@ -1467,6 +1489,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1467
1489
|
eagerTasks,
|
|
1468
1490
|
secretsEnabled,
|
|
1469
1491
|
context: contextForPrompt,
|
|
1492
|
+
userProfile,
|
|
1470
1493
|
knowledgeTopics,
|
|
1471
1494
|
contextSkillDirs,
|
|
1472
1495
|
contextIncludeSkills,
|
|
@@ -1495,6 +1518,7 @@ export async function createAgentSession(options: CreateAgentSessionOptions = {}
|
|
|
1495
1518
|
eagerTasks,
|
|
1496
1519
|
secretsEnabled,
|
|
1497
1520
|
context: contextForPrompt,
|
|
1521
|
+
userProfile,
|
|
1498
1522
|
knowledgeTopics,
|
|
1499
1523
|
contextSkillDirs,
|
|
1500
1524
|
contextIncludeSkills,
|
package/src/system-prompt.ts
CHANGED
|
@@ -459,6 +459,12 @@ export interface BuildSystemPromptOptions {
|
|
|
459
459
|
credentialSource: string;
|
|
460
460
|
authStatus: string;
|
|
461
461
|
};
|
|
462
|
+
/** Compact user profile hint injected into Workspace section. Omit when no profile exists. */
|
|
463
|
+
userProfile?: {
|
|
464
|
+
name: string;
|
|
465
|
+
role: string;
|
|
466
|
+
org: string;
|
|
467
|
+
};
|
|
462
468
|
knowledgeTopics?: string;
|
|
463
469
|
contextSkillDirs?: string[];
|
|
464
470
|
contextIncludeSkills?: string[];
|
|
@@ -650,6 +656,7 @@ export async function buildSystemPrompt(options: BuildSystemPromptOptions = {}):
|
|
|
650
656
|
eagerTasks,
|
|
651
657
|
secretsEnabled,
|
|
652
658
|
context,
|
|
659
|
+
userProfile: options.userProfile,
|
|
653
660
|
knowledgeTopics: options.knowledgeTopics,
|
|
654
661
|
};
|
|
655
662
|
let rendered = prompt.render(resolvedCustomPrompt ? customSystemPromptTemplate : systemPromptTemplate, data);
|