@f5xc-salesdemos/xcsh 18.91.4 → 19.0.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/examples/extensions/plan-mode.ts +1 -1
- package/package.json +8 -8
- package/scripts/generate-branding-index.ts +3 -1
- package/src/capability/types.ts +2 -2
- package/src/config/settings-schema.ts +17 -17
- package/src/discovery/claude-plugins.ts +15 -15
- package/src/discovery/claude.ts +3 -3
- package/src/discovery/helpers.ts +27 -27
- package/src/discovery/plugin-dir-roots.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +2 -2
- package/src/extensibility/plugins/marketplace/types.ts +5 -5
- package/src/extensibility/skills.ts +7 -7
- package/src/extensibility/slash-commands.ts +1 -1
- package/src/internal-urls/branding-index.generated.ts +1 -1
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/internal-urls/terraform-resolve.ts +26 -41
- package/src/main.ts +2 -2
- package/src/mcp/loader.ts +1 -1
- package/src/mcp/tool-bridge.ts +2 -2
- package/src/modes/components/gutter-block.ts +1 -1
- package/src/modes/components/plugins/index.ts +5 -0
- package/src/modes/components/plugins/plugin-dashboard.ts +449 -0
- package/src/modes/components/plugins/plugin-inspector-pane.ts +84 -0
- package/src/modes/components/plugins/plugin-list-pane.ts +104 -0
- package/src/modes/components/plugins/state-manager.ts +211 -0
- package/src/modes/components/plugins/types.ts +42 -0
- package/src/modes/controllers/command-controller.ts +2 -2
- package/src/modes/controllers/selector-controller.ts +17 -2
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/types.ts +1 -0
- package/src/slash-commands/builtin-registry.ts +26 -10
- package/src/task/discovery.ts +3 -3
- package/src/tools/json-tree.ts +1 -1
- package/src/utils/shell-snapshot.ts +1 -1
- package/src/web/search/providers/anthropic.ts +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Plan Mode Extension
|
|
3
3
|
*
|
|
4
|
-
* Provides a
|
|
4
|
+
* Provides a xcsh-style "plan mode" for safe code exploration.
|
|
5
5
|
* When enabled, the agent can only use read-only tools and cannot modify files.
|
|
6
6
|
*
|
|
7
7
|
* Features:
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "19.0.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",
|
|
@@ -50,15 +50,15 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
52
52
|
"@mozilla/readability": "^0.6",
|
|
53
|
-
"@f5xc-salesdemos/xcsh-stats": "
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "19.0.1",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "19.0.1",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "19.0.1",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "19.0.1",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "19.0.1",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "19.0.1",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
|
-
"ajv": "^8.
|
|
61
|
+
"ajv": "^8.20",
|
|
62
62
|
"chalk": "^5.6",
|
|
63
63
|
"diff": "^8.0",
|
|
64
64
|
"fflate": "0.8.2",
|
|
@@ -61,7 +61,9 @@ function generateTypeScript(config: BrandingConfig): string {
|
|
|
61
61
|
"",
|
|
62
62
|
`export const BRANDING_CANONICAL = ${JSON.stringify(config.canonical, null, 2)} as const;`,
|
|
63
63
|
"",
|
|
64
|
-
|
|
64
|
+
config.deprecations
|
|
65
|
+
? `export const BRANDING_DEPRECATIONS = ${JSON.stringify(config.deprecations, null, 2)} as const;`
|
|
66
|
+
: "export const BRANDING_DEPRECATIONS = null;",
|
|
65
67
|
"",
|
|
66
68
|
`export const BRANDING_GLOSSARY = ${JSON.stringify(config.glossary, null, 2)} as const;`,
|
|
67
69
|
"",
|
package/src/capability/types.ts
CHANGED
|
@@ -31,10 +31,10 @@ export interface LoadResult<T> {
|
|
|
31
31
|
* A provider that can load items for a capability.
|
|
32
32
|
*/
|
|
33
33
|
export interface Provider<T> {
|
|
34
|
-
/** Unique provider ID (e.g., "
|
|
34
|
+
/** Unique provider ID (e.g., "xcsh", "mcp-json", "agents-md") */
|
|
35
35
|
id: string;
|
|
36
36
|
|
|
37
|
-
/** Human-readable name for UI display (e.g., "
|
|
37
|
+
/** Human-readable name for UI display (e.g., "xcsh", "OpenAI Codex") */
|
|
38
38
|
displayName: string;
|
|
39
39
|
|
|
40
40
|
/** Short description for settings UI (e.g., "Load config from ~/.xcsh/ and .xcsh/") */
|
|
@@ -227,8 +227,8 @@ export const SETTINGS_SCHEMA = {
|
|
|
227
227
|
disabledProviders: {
|
|
228
228
|
type: "array",
|
|
229
229
|
default: [
|
|
230
|
-
"
|
|
231
|
-
"
|
|
230
|
+
"xcsh",
|
|
231
|
+
"xcsh-plugins",
|
|
232
232
|
"codex",
|
|
233
233
|
"agents",
|
|
234
234
|
"agents-md",
|
|
@@ -1639,32 +1639,32 @@ export const SETTINGS_SCHEMA = {
|
|
|
1639
1639
|
},
|
|
1640
1640
|
},
|
|
1641
1641
|
|
|
1642
|
-
"skills.
|
|
1642
|
+
"skills.enableXcshUser": {
|
|
1643
1643
|
type: "boolean",
|
|
1644
1644
|
default: false,
|
|
1645
1645
|
ui: {
|
|
1646
1646
|
tab: "tasks",
|
|
1647
|
-
label: "
|
|
1648
|
-
description: "Load skills from ~/.xcsh/agent/skills/
|
|
1647
|
+
label: "xcsh User Skills",
|
|
1648
|
+
description: "Load skills from ~/.xcsh/agent/skills/",
|
|
1649
1649
|
},
|
|
1650
1650
|
},
|
|
1651
1651
|
|
|
1652
|
-
"skills.
|
|
1652
|
+
"skills.enableXcshProject": {
|
|
1653
1653
|
type: "boolean",
|
|
1654
1654
|
default: false,
|
|
1655
1655
|
ui: {
|
|
1656
1656
|
tab: "tasks",
|
|
1657
|
-
label: "
|
|
1658
|
-
description: "Load skills from .xcsh/skills/
|
|
1657
|
+
label: "xcsh Project Skills",
|
|
1658
|
+
description: "Load skills from .xcsh/skills/",
|
|
1659
1659
|
},
|
|
1660
1660
|
},
|
|
1661
1661
|
|
|
1662
|
-
"skills.
|
|
1662
|
+
"skills.enableXcshPlugins": {
|
|
1663
1663
|
type: "boolean",
|
|
1664
1664
|
default: false,
|
|
1665
1665
|
ui: {
|
|
1666
1666
|
tab: "tasks",
|
|
1667
|
-
label: "
|
|
1667
|
+
label: "xcsh Marketplace Skills",
|
|
1668
1668
|
description: "Load skills from marketplace plugins (~/.xcsh/plugins/cache/)",
|
|
1669
1669
|
},
|
|
1670
1670
|
},
|
|
@@ -1696,16 +1696,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
1696
1696
|
"skills.includeSkills": { type: "array", default: [] as string[] },
|
|
1697
1697
|
|
|
1698
1698
|
// Commands
|
|
1699
|
-
"commands.
|
|
1699
|
+
"commands.enableXcshUser": {
|
|
1700
1700
|
type: "boolean",
|
|
1701
1701
|
default: true,
|
|
1702
|
-
ui: { tab: "tasks", label: "
|
|
1702
|
+
ui: { tab: "tasks", label: "xcsh User Commands", description: "Load commands from ~/.xcsh/agent/commands/" },
|
|
1703
1703
|
},
|
|
1704
1704
|
|
|
1705
|
-
"commands.
|
|
1705
|
+
"commands.enableXcshProject": {
|
|
1706
1706
|
type: "boolean",
|
|
1707
1707
|
default: true,
|
|
1708
|
-
ui: { tab: "tasks", label: "
|
|
1708
|
+
ui: { tab: "tasks", label: "xcsh Project Commands", description: "Load commands from .xcsh/commands/" },
|
|
1709
1709
|
},
|
|
1710
1710
|
|
|
1711
1711
|
// ────────────────────────────────────────────────────────────────────────
|
|
@@ -2011,9 +2011,9 @@ export interface SkillsSettings {
|
|
|
2011
2011
|
enabled?: boolean;
|
|
2012
2012
|
enableSkillCommands?: boolean;
|
|
2013
2013
|
enableCodexUser?: boolean;
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2014
|
+
enableXcshUser?: boolean;
|
|
2015
|
+
enableXcshProject?: boolean;
|
|
2016
|
+
enableXcshPlugins?: boolean;
|
|
2017
2017
|
enablePiUser?: boolean;
|
|
2018
2018
|
enablePiProject?: boolean;
|
|
2019
2019
|
customDirectories?: string[];
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* xcsh Marketplace Plugin Provider
|
|
3
3
|
*
|
|
4
4
|
* Loads configuration from ~/.xcsh/plugins/cache/ based on installed_plugins.json registry.
|
|
5
5
|
* Priority: 70 (below claude.ts at 80, so user overrides in .xcsh/ take precedence)
|
|
@@ -15,17 +15,17 @@ import { type SlashCommand, slashCommandCapability } from "../capability/slash-c
|
|
|
15
15
|
import { type CustomTool, toolCapability } from "../capability/tool";
|
|
16
16
|
import type { LoadContext, LoadResult } from "../capability/types";
|
|
17
17
|
import {
|
|
18
|
-
type ClaudePluginRoot,
|
|
19
18
|
createSourceMeta,
|
|
20
|
-
|
|
19
|
+
listXcshPluginRoots,
|
|
21
20
|
loadFilesFromDir,
|
|
22
21
|
scanSkillsFromDir,
|
|
22
|
+
type XcshPluginRoot,
|
|
23
23
|
} from "./helpers";
|
|
24
24
|
|
|
25
25
|
import { substitutePluginRoot } from "./substitute-plugin-root";
|
|
26
26
|
|
|
27
|
-
const PROVIDER_ID = "
|
|
28
|
-
const DISPLAY_NAME = "
|
|
27
|
+
const PROVIDER_ID = "xcsh-plugins";
|
|
28
|
+
const DISPLAY_NAME = "xcsh Marketplace";
|
|
29
29
|
const PRIORITY = 70; // Below claude.ts (80) so user .xcsh/ overrides win
|
|
30
30
|
|
|
31
31
|
// =============================================================================
|
|
@@ -36,7 +36,7 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
36
36
|
const items: Skill[] = [];
|
|
37
37
|
const warnings: string[] = [];
|
|
38
38
|
|
|
39
|
-
const { roots, warnings: rootWarnings } = await
|
|
39
|
+
const { roots, warnings: rootWarnings } = await listXcshPluginRoots(ctx.home, ctx.cwd);
|
|
40
40
|
warnings.push(...rootWarnings);
|
|
41
41
|
|
|
42
42
|
const results = await Promise.all(
|
|
@@ -70,7 +70,7 @@ async function loadSlashCommands(ctx: LoadContext): Promise<LoadResult<SlashComm
|
|
|
70
70
|
const items: SlashCommand[] = [];
|
|
71
71
|
const warnings: string[] = [];
|
|
72
72
|
|
|
73
|
-
const { roots, warnings: rootWarnings } = await
|
|
73
|
+
const { roots, warnings: rootWarnings } = await listXcshPluginRoots(ctx.home, ctx.cwd);
|
|
74
74
|
warnings.push(...rootWarnings);
|
|
75
75
|
|
|
76
76
|
const results = await Promise.all(
|
|
@@ -108,12 +108,12 @@ async function loadHooks(ctx: LoadContext): Promise<LoadResult<Hook>> {
|
|
|
108
108
|
const items: Hook[] = [];
|
|
109
109
|
const warnings: string[] = [];
|
|
110
110
|
|
|
111
|
-
const { roots, warnings: rootWarnings } = await
|
|
111
|
+
const { roots, warnings: rootWarnings } = await listXcshPluginRoots(ctx.home, ctx.cwd);
|
|
112
112
|
warnings.push(...rootWarnings);
|
|
113
113
|
|
|
114
114
|
const hookTypes = ["pre", "post"] as const;
|
|
115
115
|
|
|
116
|
-
const loadTasks: { root:
|
|
116
|
+
const loadTasks: { root: XcshPluginRoot; hookType: "pre" | "post" }[] = [];
|
|
117
117
|
for (const root of roots) {
|
|
118
118
|
for (const hookType of hookTypes) {
|
|
119
119
|
loadTasks.push({ root, hookType });
|
|
@@ -155,7 +155,7 @@ async function loadTools(ctx: LoadContext): Promise<LoadResult<CustomTool>> {
|
|
|
155
155
|
const items: CustomTool[] = [];
|
|
156
156
|
const warnings: string[] = [];
|
|
157
157
|
|
|
158
|
-
const { roots, warnings: rootWarnings } = await
|
|
158
|
+
const { roots, warnings: rootWarnings } = await listXcshPluginRoots(ctx.home, ctx.cwd);
|
|
159
159
|
warnings.push(...rootWarnings);
|
|
160
160
|
|
|
161
161
|
const results = await Promise.all(
|
|
@@ -192,7 +192,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
192
192
|
const items: MCPServer[] = [];
|
|
193
193
|
const warnings: string[] = [];
|
|
194
194
|
|
|
195
|
-
const { roots, warnings: rootWarnings } = await
|
|
195
|
+
const { roots, warnings: rootWarnings } = await listXcshPluginRoots(ctx.home, ctx.cwd);
|
|
196
196
|
warnings.push(...rootWarnings);
|
|
197
197
|
|
|
198
198
|
for (const root of roots) {
|
|
@@ -258,7 +258,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
258
258
|
registerProvider<Skill>(skillCapability.id, {
|
|
259
259
|
id: PROVIDER_ID,
|
|
260
260
|
displayName: DISPLAY_NAME,
|
|
261
|
-
description: "Load skills from
|
|
261
|
+
description: "Load skills from xcsh marketplace plugins (~/.xcsh/plugins/cache/)",
|
|
262
262
|
priority: PRIORITY,
|
|
263
263
|
load: loadSkills,
|
|
264
264
|
});
|
|
@@ -266,7 +266,7 @@ registerProvider<Skill>(skillCapability.id, {
|
|
|
266
266
|
registerProvider<SlashCommand>(slashCommandCapability.id, {
|
|
267
267
|
id: PROVIDER_ID,
|
|
268
268
|
displayName: DISPLAY_NAME,
|
|
269
|
-
description: "Load slash commands from
|
|
269
|
+
description: "Load slash commands from xcsh marketplace plugins",
|
|
270
270
|
priority: PRIORITY,
|
|
271
271
|
load: loadSlashCommands,
|
|
272
272
|
});
|
|
@@ -274,7 +274,7 @@ registerProvider<SlashCommand>(slashCommandCapability.id, {
|
|
|
274
274
|
registerProvider<Hook>(hookCapability.id, {
|
|
275
275
|
id: PROVIDER_ID,
|
|
276
276
|
displayName: DISPLAY_NAME,
|
|
277
|
-
description: "Load hooks from
|
|
277
|
+
description: "Load hooks from xcsh marketplace plugins",
|
|
278
278
|
priority: PRIORITY,
|
|
279
279
|
load: loadHooks,
|
|
280
280
|
});
|
|
@@ -282,7 +282,7 @@ registerProvider<Hook>(hookCapability.id, {
|
|
|
282
282
|
registerProvider<CustomTool>(toolCapability.id, {
|
|
283
283
|
id: PROVIDER_ID,
|
|
284
284
|
displayName: DISPLAY_NAME,
|
|
285
|
-
description: "Load custom tools from
|
|
285
|
+
description: "Load custom tools from xcsh marketplace plugins",
|
|
286
286
|
priority: PRIORITY,
|
|
287
287
|
load: loadTools,
|
|
288
288
|
});
|
package/src/discovery/claude.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* xcsh Provider
|
|
3
3
|
*
|
|
4
4
|
* Loads configuration from .xcsh directories.
|
|
5
5
|
* Priority: 80 (tool-specific, below builtin but above shared standards)
|
|
@@ -28,8 +28,8 @@ import {
|
|
|
28
28
|
scanSkillsFromDir,
|
|
29
29
|
} from "./helpers";
|
|
30
30
|
|
|
31
|
-
const PROVIDER_ID = "
|
|
32
|
-
const DISPLAY_NAME = "
|
|
31
|
+
const PROVIDER_ID = "xcsh";
|
|
32
|
+
const DISPLAY_NAME = "xcsh";
|
|
33
33
|
const PRIORITY = 80;
|
|
34
34
|
const CONFIG_DIR = ".xcsh";
|
|
35
35
|
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -583,13 +583,13 @@ export function getExtensionNameFromPath(extensionPath: string): string {
|
|
|
583
583
|
}
|
|
584
584
|
|
|
585
585
|
// =============================================================================
|
|
586
|
-
//
|
|
586
|
+
// xcsh Plugin Cache Helpers
|
|
587
587
|
// =============================================================================
|
|
588
588
|
|
|
589
589
|
/**
|
|
590
|
-
* Entry for an installed
|
|
590
|
+
* Entry for an installed xcsh plugin.
|
|
591
591
|
*/
|
|
592
|
-
export interface
|
|
592
|
+
export interface XcshPluginEntry {
|
|
593
593
|
scope: "user" | "project";
|
|
594
594
|
installPath: string;
|
|
595
595
|
version: string;
|
|
@@ -600,17 +600,17 @@ export interface ClaudePluginEntry {
|
|
|
600
600
|
}
|
|
601
601
|
|
|
602
602
|
/**
|
|
603
|
-
*
|
|
603
|
+
* xcsh installed_plugins.json registry format.
|
|
604
604
|
*/
|
|
605
|
-
export interface
|
|
605
|
+
export interface XcshPluginsRegistry {
|
|
606
606
|
version: number;
|
|
607
|
-
plugins: Record<string,
|
|
607
|
+
plugins: Record<string, XcshPluginEntry[]>;
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
/**
|
|
611
611
|
* Resolved plugin root for loading.
|
|
612
612
|
*/
|
|
613
|
-
export interface
|
|
613
|
+
export interface XcshPluginRoot {
|
|
614
614
|
/** Plugin ID (e.g., "simpleclaude-core@simpleclaude") */
|
|
615
615
|
id: string;
|
|
616
616
|
/** Marketplace name */
|
|
@@ -626,10 +626,10 @@ export interface ClaudePluginRoot {
|
|
|
626
626
|
}
|
|
627
627
|
|
|
628
628
|
/**
|
|
629
|
-
* Parse
|
|
629
|
+
* Parse xcsh installed_plugins.json content.
|
|
630
630
|
*/
|
|
631
|
-
export function
|
|
632
|
-
const data = tryParseJson<
|
|
631
|
+
export function parseXcshPluginsRegistry(content: string): XcshPluginsRegistry | null {
|
|
632
|
+
const data = tryParseJson<XcshPluginsRegistry>(content);
|
|
633
633
|
if (!data || typeof data !== "object") return null;
|
|
634
634
|
if (
|
|
635
635
|
typeof data.version !== "number" ||
|
|
@@ -711,7 +711,7 @@ export async function resolveOrDefaultProjectRegistryPath(cwd: string): Promise<
|
|
|
711
711
|
return path.join(cwd, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
-
const pluginRootsCache = new Map<string, { roots:
|
|
714
|
+
const pluginRootsCache = new Map<string, { roots: XcshPluginRoot[]; warnings: string[] }>();
|
|
715
715
|
|
|
716
716
|
/**
|
|
717
717
|
* List all installed marketplace plugin roots from the plugin cache.
|
|
@@ -720,25 +720,25 @@ const pluginRootsCache = new Map<string, { roots: ClaudePluginRoot[]; warnings:
|
|
|
720
720
|
*
|
|
721
721
|
* Results are cached per `home:resolvedProjectPath` key to avoid repeated parsing.
|
|
722
722
|
*/
|
|
723
|
-
export async function
|
|
723
|
+
export async function listXcshPluginRoots(
|
|
724
724
|
home: string,
|
|
725
725
|
cwd?: string,
|
|
726
|
-
): Promise<{ roots:
|
|
726
|
+
): Promise<{ roots: XcshPluginRoot[]; warnings: string[] }> {
|
|
727
727
|
const resolvedProjectPath = cwd ? await resolveActiveProjectRegistryPath(cwd) : null;
|
|
728
728
|
const cacheKey = `${home}:${resolvedProjectPath ?? ""}`;
|
|
729
729
|
const cached = pluginRootsCache.get(cacheKey);
|
|
730
730
|
if (cached) return cached;
|
|
731
731
|
|
|
732
|
-
const roots:
|
|
732
|
+
const roots: XcshPluginRoot[] = [];
|
|
733
733
|
const warnings: string[] = [];
|
|
734
|
-
const projectRoots:
|
|
734
|
+
const projectRoots: XcshPluginRoot[] = [];
|
|
735
735
|
|
|
736
736
|
// ── Installed plugins registry ───────────────────────────────────────────
|
|
737
737
|
// Path derived from `home` (not os.homedir()) so test isolation works when home is overridden.
|
|
738
738
|
const registryPath = path.join(home, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
739
739
|
const content = await readFile(registryPath);
|
|
740
740
|
if (content) {
|
|
741
|
-
const registry =
|
|
741
|
+
const registry = parseXcshPluginsRegistry(content);
|
|
742
742
|
if (registry) {
|
|
743
743
|
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
744
744
|
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
@@ -781,7 +781,7 @@ export async function listClaudePluginRoots(
|
|
|
781
781
|
if (resolvedProjectPath && resolvedProjectPath !== registryPath) {
|
|
782
782
|
const projectContent = await readFile(resolvedProjectPath);
|
|
783
783
|
if (projectContent) {
|
|
784
|
-
const projectRegistry =
|
|
784
|
+
const projectRegistry = parseXcshPluginsRegistry(projectContent);
|
|
785
785
|
if (projectRegistry) {
|
|
786
786
|
for (const [pluginId, entries] of Object.entries(projectRegistry.plugins)) {
|
|
787
787
|
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
@@ -838,7 +838,7 @@ export async function listClaudePluginRoots(
|
|
|
838
838
|
/**
|
|
839
839
|
* Clear the plugin roots cache (useful for testing or when plugins change).
|
|
840
840
|
*/
|
|
841
|
-
export function
|
|
841
|
+
export function clearXcshPluginRootsCache(): void {
|
|
842
842
|
pluginRootsCache.clear();
|
|
843
843
|
preloadedPluginRoots = [...injectedPluginDirRoots];
|
|
844
844
|
// Re-warm preloaded roots asynchronously so sync LSP config reads stay valid
|
|
@@ -851,8 +851,8 @@ export function clearClaudePluginRootsCache(): void {
|
|
|
851
851
|
// Populated at startup by preloadPluginRoots(). Read synchronously by
|
|
852
852
|
// getPreloadedPluginRoots(). Safe degradation: empty array if not warmed.
|
|
853
853
|
|
|
854
|
-
let preloadedPluginRoots:
|
|
855
|
-
let injectedPluginDirRoots:
|
|
854
|
+
let preloadedPluginRoots: XcshPluginRoot[] = [];
|
|
855
|
+
let injectedPluginDirRoots: XcshPluginRoot[] = [];
|
|
856
856
|
let lastPreloadHome: string | undefined;
|
|
857
857
|
|
|
858
858
|
/**
|
|
@@ -862,7 +862,7 @@ let lastPreloadHome: string | undefined;
|
|
|
862
862
|
*/
|
|
863
863
|
export async function preloadPluginRoots(home: string, cwd?: string): Promise<void> {
|
|
864
864
|
lastPreloadHome = home;
|
|
865
|
-
const { roots } = await
|
|
865
|
+
const { roots } = await listXcshPluginRoots(home, cwd);
|
|
866
866
|
preloadedPluginRoots = roots;
|
|
867
867
|
}
|
|
868
868
|
|
|
@@ -870,7 +870,7 @@ export async function preloadPluginRoots(home: string, cwd?: string): Promise<vo
|
|
|
870
870
|
* Get pre-loaded plugin roots synchronously.
|
|
871
871
|
* Returns empty array if preloadPluginRoots() hasn't been called.
|
|
872
872
|
*/
|
|
873
|
-
export function getPreloadedPluginRoots(): readonly
|
|
873
|
+
export function getPreloadedPluginRoots(): readonly XcshPluginRoot[] {
|
|
874
874
|
return preloadedPluginRoots;
|
|
875
875
|
}
|
|
876
876
|
|
|
@@ -878,11 +878,11 @@ export function getPreloadedPluginRoots(): readonly ClaudePluginRoot[] {
|
|
|
878
878
|
|
|
879
879
|
/**
|
|
880
880
|
* Inject synthetic plugin roots from --plugin-dir paths.
|
|
881
|
-
* These are prepended to the cache with highest precedence (before OMP/
|
|
882
|
-
* Must be called before any
|
|
881
|
+
* These are prepended to the cache with highest precedence (before OMP/xcsh entries).
|
|
882
|
+
* Must be called before any listXcshPluginRoots() access.
|
|
883
883
|
*/
|
|
884
884
|
export async function injectPluginDirRoots(home: string, dirs: string[], cwd?: string): Promise<void> {
|
|
885
|
-
const injected:
|
|
885
|
+
const injected: XcshPluginRoot[] = [];
|
|
886
886
|
for (const dir of dirs) {
|
|
887
887
|
const resolved = path.resolve(dir);
|
|
888
888
|
// Read plugin name from manifest
|
|
@@ -901,13 +901,13 @@ export async function injectPluginDirRoots(home: string, dirs: string[], cwd?: s
|
|
|
901
901
|
injected.push(buildPluginDirRoot(resolved, pluginName));
|
|
902
902
|
}
|
|
903
903
|
|
|
904
|
-
// Set injected roots BEFORE populating cache so
|
|
904
|
+
// Set injected roots BEFORE populating cache so listXcshPluginRoots merges them.
|
|
905
905
|
injectedPluginDirRoots = injected;
|
|
906
906
|
lastPreloadHome = home; // ensure cache-clear re-warm fires even when injectPluginDirRoots was the startup path
|
|
907
907
|
// Clear any stale cache entries (populated before injected roots were set).
|
|
908
908
|
pluginRootsCache.clear();
|
|
909
909
|
// Rebuild — cache miss triggers fresh load that includes both user+project registries
|
|
910
910
|
// and prepends injectedPluginDirRoots at highest precedence.
|
|
911
|
-
const { roots } = await
|
|
911
|
+
const { roots } = await listXcshPluginRoots(home, cwd);
|
|
912
912
|
preloadedPluginRoots = roots;
|
|
913
913
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
|
|
3
|
-
/** Synthetic plugin root for a --plugin-dir path. Shape-compatible with
|
|
3
|
+
/** Synthetic plugin root for a --plugin-dir path. Shape-compatible with XcshPluginRoot. */
|
|
4
4
|
export interface PluginDirRoot {
|
|
5
5
|
id: string;
|
|
6
6
|
marketplace: string;
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Constructor takes explicit paths for testability (same pattern as registry.ts).
|
|
5
5
|
* The `clearPluginRootsCache` dependency is injected so callers can provide
|
|
6
|
-
* the real `
|
|
6
|
+
* the real `clearXcshPluginRootsCache` while tests supply a counter stub.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import * as fs from "node:fs/promises";
|
|
@@ -51,7 +51,7 @@ export interface MarketplaceManagerOptions {
|
|
|
51
51
|
projectInstalledRegistryPath?: string;
|
|
52
52
|
marketplacesCacheDir: string;
|
|
53
53
|
pluginsCacheDir: string;
|
|
54
|
-
/** Injected for testing; production callers pass
|
|
54
|
+
/** Injected for testing; production callers pass clearXcshPluginRootsCache.
|
|
55
55
|
* Receives any additional file paths that should also be invalidated from the fs cache.
|
|
56
56
|
*/
|
|
57
57
|
clearPluginRootsCache?: (extraPaths?: readonly string[]) => void;
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Two registries:
|
|
5
5
|
* - MarketplacesRegistry: which marketplace catalogs the user has added (config)
|
|
6
|
-
* - InstalledPluginsRegistry: which plugins are installed (data
|
|
6
|
+
* - InstalledPluginsRegistry: which plugins are installed (data)
|
|
7
7
|
*
|
|
8
|
-
* The installed registry MUST pass `
|
|
8
|
+
* The installed registry MUST pass `parseXcshPluginsRegistry()` validation —
|
|
9
9
|
* it uses `version: 2` (numeric) and `plugins: Record<string, ...[]>`.
|
|
10
10
|
*/
|
|
11
11
|
|
|
@@ -151,11 +151,11 @@ export interface MarketplaceRegistryEntry {
|
|
|
151
151
|
}
|
|
152
152
|
|
|
153
153
|
// ── Installed plugins registry ───────────────────────────────────────
|
|
154
|
-
// MUST match
|
|
154
|
+
// MUST match XcshPluginsRegistry shape for parseXcshPluginsRegistry()
|
|
155
155
|
// compatibility: `version: number`, `plugins: Record<string, entry[]>`.
|
|
156
156
|
|
|
157
157
|
export interface InstalledPluginsRegistry {
|
|
158
|
-
/** MUST be 2 —
|
|
158
|
+
/** MUST be 2 — parseXcshPluginsRegistry rejects non-numeric version. */
|
|
159
159
|
version: 2;
|
|
160
160
|
plugins: Record<string, InstalledPluginEntry[]>;
|
|
161
161
|
}
|
|
@@ -171,7 +171,7 @@ export interface InstalledPluginEntry {
|
|
|
171
171
|
lastUpdated: string;
|
|
172
172
|
/** For git-sourced plugins. */
|
|
173
173
|
gitCommitSha?: string;
|
|
174
|
-
/** OMP extension —
|
|
174
|
+
/** OMP extension — CLI/UI concern only in v1. */
|
|
175
175
|
enabled?: boolean;
|
|
176
176
|
}
|
|
177
177
|
|
|
@@ -80,9 +80,9 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
80
80
|
cwd = getProjectDir(),
|
|
81
81
|
enabled = true,
|
|
82
82
|
enableCodexUser = true,
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
83
|
+
enableXcshUser = true,
|
|
84
|
+
enableXcshProject = true,
|
|
85
|
+
enableXcshPlugins = false,
|
|
86
86
|
enablePiUser = true,
|
|
87
87
|
enablePiProject = true,
|
|
88
88
|
customDirectories = [],
|
|
@@ -97,16 +97,16 @@ export async function loadSkills(options: LoadSkillsOptions = {}): Promise<LoadS
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
const anyBuiltInSkillSourceEnabled =
|
|
100
|
-
enableCodexUser ||
|
|
100
|
+
enableCodexUser || enableXcshUser || enableXcshProject || enablePiUser || enablePiProject;
|
|
101
101
|
// Helper to check if a source is enabled
|
|
102
102
|
function isSourceEnabled(source: SourceMeta): boolean {
|
|
103
103
|
const { provider, level } = source;
|
|
104
104
|
if (provider === "codex" && level === "user") return enableCodexUser;
|
|
105
|
-
if (provider === "
|
|
106
|
-
if (provider === "
|
|
105
|
+
if (provider === "xcsh" && level === "user") return enableXcshUser;
|
|
106
|
+
if (provider === "xcsh" && level === "project") return enableXcshProject;
|
|
107
107
|
if (provider === "native" && level === "user") return enablePiUser;
|
|
108
108
|
if (provider === "native" && level === "project") return enablePiProject;
|
|
109
|
-
if (provider === "
|
|
109
|
+
if (provider === "xcsh-plugins") return enableXcshPlugins;
|
|
110
110
|
return anyBuiltInSkillSourceEnabled;
|
|
111
111
|
}
|
|
112
112
|
|
|
@@ -142,7 +142,7 @@ export interface FileSlashCommand {
|
|
|
142
142
|
name: string;
|
|
143
143
|
description: string;
|
|
144
144
|
content: string;
|
|
145
|
-
source: string; // e.g., "via
|
|
145
|
+
source: string; // e.g., "via xcsh (User)"
|
|
146
146
|
/** Source metadata for display */
|
|
147
147
|
_source?: { providerName: string; level: "user" | "project" | "native" };
|
|
148
148
|
}
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.0.1",
|
|
21
|
+
"commit": "5d8fde93b95cde89c2b5c596fd2ff7bb3f841aab",
|
|
22
|
+
"shortCommit": "5d8fde9",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v19.0.1",
|
|
25
|
+
"commitDate": "2026-06-03T03:34:26Z",
|
|
26
|
+
"buildDate": "2026-06-03T03:59:58.131Z",
|
|
27
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/
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/5d8fde93b95cde89c2b5c596fd2ff7bb3f841aab",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.0.1"
|
|
33
33
|
};
|