@f5xc-salesdemos/xcsh 18.91.3 → 18.91.4
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/scripts/generate-branding-index.ts +1 -0
- package/scripts/generate-terraform-index.ts +1 -0
- package/src/capability/context-file.ts +1 -1
- package/src/capability/types.ts +2 -2
- package/src/config/settings-schema.ts +5 -5
- package/src/config/settings.ts +1 -1
- package/src/config.ts +4 -5
- package/src/discovery/claude-plugins.ts +4 -4
- package/src/discovery/claude.ts +15 -15
- package/src/discovery/helpers.ts +12 -67
- package/src/extensibility/plugins/marketplace/fetcher.ts +2 -2
- package/src/extensibility/plugins/marketplace/manager.ts +4 -4
- package/src/extensibility/plugins/marketplace/source-resolver.ts +1 -1
- package/src/internal-urls/branding-index.generated.ts +42 -53
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/internal-urls/terraform-index.generated.ts +1380 -1906
- package/src/lsp/config.ts +4 -4
- package/src/main.ts +0 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +0 -1
- package/src/sdk.ts +2 -2
- package/src/slash-commands/builtin-registry.ts +0 -3
- package/src/task/commands.ts +1 -1
- package/src/task/discovery.ts +4 -4
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "18.91.
|
|
4
|
+
"version": "18.91.4",
|
|
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,12 +50,12 @@
|
|
|
50
50
|
"dependencies": {
|
|
51
51
|
"@agentclientprotocol/sdk": "0.16.1",
|
|
52
52
|
"@mozilla/readability": "^0.6",
|
|
53
|
-
"@f5xc-salesdemos/xcsh-stats": "18.91.
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "18.91.
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "18.91.
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "18.91.
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "18.91.
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "18.91.
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "18.91.4",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "18.91.4",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "18.91.4",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "18.91.4",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "18.91.4",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "18.91.4",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.18",
|
|
@@ -75,4 +75,5 @@ function generateTypeScript(config: BrandingConfig): string {
|
|
|
75
75
|
const config = await loadBrandingYaml();
|
|
76
76
|
const output = generateTypeScript(config);
|
|
77
77
|
await fs.writeFile(OUTPUT_FILE, output, "utf-8");
|
|
78
|
+
await Bun.$`bunx biome format --write ${OUTPUT_FILE}`.quiet();
|
|
78
79
|
console.log(`Generated ${OUTPUT_FILE}`);
|
|
@@ -53,4 +53,5 @@ function generateTypeScript(data: unknown): string {
|
|
|
53
53
|
const data = await loadTerraformIndex();
|
|
54
54
|
const output = generateTypeScript(data);
|
|
55
55
|
await fs.writeFile(OUTPUT_FILE, output, "utf-8");
|
|
56
|
+
await Bun.$`bunx biome format --write ${OUTPUT_FILE}`.quiet();
|
|
56
57
|
console.log(`Generated ${OUTPUT_FILE}`);
|
|
@@ -31,7 +31,7 @@ export const contextFileCapability = defineCapability<ContextFile>({
|
|
|
31
31
|
// Deduplicate by scope: one user-level file, and one project-level file per directory depth.
|
|
32
32
|
// Within each depth level, higher-priority providers shadow lower-priority ones.
|
|
33
33
|
// This supports monorepo hierarchies where AGENTS.md exists at multiple ancestor levels.
|
|
34
|
-
// Clamp depth >= 0: files inside config subdirectories of an ancestor (e.g. .
|
|
34
|
+
// Clamp depth >= 0: files inside config subdirectories of an ancestor (e.g. .xcsh/, .github/)
|
|
35
35
|
// are same-scope as the ancestor itself.
|
|
36
36
|
key: file => (file.level === "user" ? "user" : `project:${Math.max(0, file.depth ?? 0)}`),
|
|
37
37
|
toExtensionId: file => `context-file:${file.level}:${path.basename(file.path)}`,
|
package/src/capability/types.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Core types for the capability-based config discovery system.
|
|
3
3
|
*
|
|
4
4
|
* This architecture inverts control: instead of callers knowing about paths like
|
|
5
|
-
* `.
|
|
5
|
+
* `.xcsh`, `.codex`, `.gemini`, they simply ask for `load("mcps")` and get back
|
|
6
6
|
* a unified array of MCP servers.
|
|
7
7
|
*/
|
|
8
8
|
|
|
@@ -37,7 +37,7 @@ export interface Provider<T> {
|
|
|
37
37
|
/** Human-readable name for UI display (e.g., "Claude Code", "OpenAI Codex") */
|
|
38
38
|
displayName: string;
|
|
39
39
|
|
|
40
|
-
/** Short description for settings UI (e.g., "Load config from ~/.
|
|
40
|
+
/** Short description for settings UI (e.g., "Load config from ~/.xcsh/ and .xcsh/") */
|
|
41
41
|
description: string;
|
|
42
42
|
|
|
43
43
|
/**
|
|
@@ -1645,7 +1645,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1645
1645
|
ui: {
|
|
1646
1646
|
tab: "tasks",
|
|
1647
1647
|
label: "Claude User Skills",
|
|
1648
|
-
description: "Load skills from ~/.
|
|
1648
|
+
description: "Load skills from ~/.xcsh/agent/skills/ (legacy Claude Code compatibility)",
|
|
1649
1649
|
},
|
|
1650
1650
|
},
|
|
1651
1651
|
|
|
@@ -1655,7 +1655,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1655
1655
|
ui: {
|
|
1656
1656
|
tab: "tasks",
|
|
1657
1657
|
label: "Claude Project Skills",
|
|
1658
|
-
description: "Load skills from .
|
|
1658
|
+
description: "Load skills from .xcsh/skills/ (legacy Claude Code compatibility)",
|
|
1659
1659
|
},
|
|
1660
1660
|
},
|
|
1661
1661
|
|
|
@@ -1665,7 +1665,7 @@ export const SETTINGS_SCHEMA = {
|
|
|
1665
1665
|
ui: {
|
|
1666
1666
|
tab: "tasks",
|
|
1667
1667
|
label: "Claude Marketplace Skills",
|
|
1668
|
-
description: "Load skills from
|
|
1668
|
+
description: "Load skills from marketplace plugins (~/.xcsh/plugins/cache/)",
|
|
1669
1669
|
},
|
|
1670
1670
|
},
|
|
1671
1671
|
|
|
@@ -1699,13 +1699,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
1699
1699
|
"commands.enableClaudeUser": {
|
|
1700
1700
|
type: "boolean",
|
|
1701
1701
|
default: true,
|
|
1702
|
-
ui: { tab: "tasks", label: "Claude User Commands", description: "Load commands from ~/.
|
|
1702
|
+
ui: { tab: "tasks", label: "Claude User Commands", description: "Load commands from ~/.xcsh/agent/commands/" },
|
|
1703
1703
|
},
|
|
1704
1704
|
|
|
1705
1705
|
"commands.enableClaudeProject": {
|
|
1706
1706
|
type: "boolean",
|
|
1707
1707
|
default: true,
|
|
1708
|
-
ui: { tab: "tasks", label: "Claude Project Commands", description: "Load commands from .
|
|
1708
|
+
ui: { tab: "tasks", label: "Claude Project Commands", description: "Load commands from .xcsh/commands/" },
|
|
1709
1709
|
},
|
|
1710
1710
|
|
|
1711
1711
|
// ────────────────────────────────────────────────────────────────────────
|
package/src/config/settings.ts
CHANGED
|
@@ -116,7 +116,7 @@ export class Settings {
|
|
|
116
116
|
|
|
117
117
|
/** Global settings from config.yml */
|
|
118
118
|
#global: RawSettings = {};
|
|
119
|
-
/** Project settings from .
|
|
119
|
+
/** Project settings from .xcsh/settings.yml etc */
|
|
120
120
|
#project: RawSettings = {};
|
|
121
121
|
/** Runtime overrides (not persisted) */
|
|
122
122
|
#overrides: RawSettings = {};
|
package/src/config.ts
CHANGED
|
@@ -17,7 +17,6 @@ import { expandTilde } from "./tools/path-utils";
|
|
|
17
17
|
|
|
18
18
|
const priorityList = [
|
|
19
19
|
{ dir: CONFIG_DIR_NAME, globalAgentDir: getConfigAgentDirName },
|
|
20
|
-
{ dir: ".claude" },
|
|
21
20
|
{ dir: ".codex" },
|
|
22
21
|
{ dir: ".gemini" },
|
|
23
22
|
];
|
|
@@ -254,8 +253,8 @@ export class ConfigFile<T> implements IConfigFile<T> {
|
|
|
254
253
|
|
|
255
254
|
/**
|
|
256
255
|
* Config directory bases in priority order (highest first).
|
|
257
|
-
* User-level: ~/.xcsh/agent, ~/.
|
|
258
|
-
* Project-level: .xcsh, .
|
|
256
|
+
* User-level: ~/.xcsh/agent, ~/.codex, ~/.gemini
|
|
257
|
+
* Project-level: .xcsh, .codex, .gemini
|
|
259
258
|
*/
|
|
260
259
|
const USER_CONFIG_BASES = priorityList.map(({ dir, globalAgentDir }) => ({
|
|
261
260
|
base: () => path.join(os.homedir(), globalAgentDir ? globalAgentDir() : dir),
|
|
@@ -269,7 +268,7 @@ const PROJECT_CONFIG_BASES = priorityList.map(({ dir }) => ({
|
|
|
269
268
|
|
|
270
269
|
export interface ConfigDirEntry {
|
|
271
270
|
path: string;
|
|
272
|
-
source: string; // e.g., ".xcsh", ".
|
|
271
|
+
source: string; // e.g., ".xcsh", ".codex"
|
|
273
272
|
level: "user" | "project";
|
|
274
273
|
}
|
|
275
274
|
|
|
@@ -384,7 +383,7 @@ export function findConfigFileWithMeta(
|
|
|
384
383
|
|
|
385
384
|
/**
|
|
386
385
|
* Find all nearest config directories by walking up from cwd.
|
|
387
|
-
* Returns one entry per config base (.xcsh, .
|
|
386
|
+
* Returns one entry per config base (.xcsh, .codex, .gemini) - the nearest one found.
|
|
388
387
|
* Results are in priority order (highest first).
|
|
389
388
|
*/
|
|
390
389
|
export function findAllNearestProjectConfigDirs(subpath: string, cwd: string = getProjectDir()): ConfigDirEntry[] {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Code Marketplace Plugin Provider
|
|
3
3
|
*
|
|
4
|
-
* Loads configuration from ~/.
|
|
5
|
-
* Priority: 70 (below claude.ts at 80, so user overrides in .
|
|
4
|
+
* Loads configuration from ~/.xcsh/plugins/cache/ based on installed_plugins.json registry.
|
|
5
|
+
* Priority: 70 (below claude.ts at 80, so user overrides in .xcsh/ take precedence)
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
8
8
|
import { logger } from "@f5xc-salesdemos/pi-utils";
|
|
@@ -26,7 +26,7 @@ import { substitutePluginRoot } from "./substitute-plugin-root";
|
|
|
26
26
|
|
|
27
27
|
const PROVIDER_ID = "claude-plugins";
|
|
28
28
|
const DISPLAY_NAME = "Claude Code Marketplace";
|
|
29
|
-
const PRIORITY = 70; // Below claude.ts (80) so user .
|
|
29
|
+
const PRIORITY = 70; // Below claude.ts (80) so user .xcsh/ overrides win
|
|
30
30
|
|
|
31
31
|
// =============================================================================
|
|
32
32
|
// Skills
|
|
@@ -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 Claude Code marketplace plugins (~/.
|
|
261
|
+
description: "Load skills from Claude Code marketplace plugins (~/.xcsh/plugins/cache/)",
|
|
262
262
|
priority: PRIORITY,
|
|
263
263
|
load: loadSkills,
|
|
264
264
|
});
|
package/src/discovery/claude.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude Code Provider
|
|
3
3
|
*
|
|
4
|
-
* Loads configuration from .
|
|
4
|
+
* Loads configuration from .xcsh directories.
|
|
5
5
|
* Priority: 80 (tool-specific, below builtin but above shared standards)
|
|
6
6
|
*/
|
|
7
7
|
import * as path from "node:path";
|
|
@@ -31,17 +31,17 @@ import {
|
|
|
31
31
|
const PROVIDER_ID = "claude";
|
|
32
32
|
const DISPLAY_NAME = "Claude Code";
|
|
33
33
|
const PRIORITY = 80;
|
|
34
|
-
const CONFIG_DIR = ".
|
|
34
|
+
const CONFIG_DIR = ".xcsh";
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Get user-level .
|
|
37
|
+
* Get user-level .xcsh path.
|
|
38
38
|
*/
|
|
39
39
|
function getUserClaude(ctx: LoadContext): string {
|
|
40
40
|
return path.join(ctx.home, CONFIG_DIR);
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
* Get project-level .
|
|
44
|
+
* Get project-level .xcsh path (cwd only).
|
|
45
45
|
*/
|
|
46
46
|
function getProjectClaude(ctx: LoadContext): string {
|
|
47
47
|
return path.join(ctx.cwd, CONFIG_DIR);
|
|
@@ -60,7 +60,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
60
60
|
const warnings: string[] = [];
|
|
61
61
|
|
|
62
62
|
const userBase = getUserClaude(ctx);
|
|
63
|
-
const userClaudeJson = path.join(ctx.home, ".
|
|
63
|
+
const userClaudeJson = path.join(ctx.home, ".xcsh.json");
|
|
64
64
|
const userMcpJson = path.join(userBase, "mcp.json");
|
|
65
65
|
|
|
66
66
|
const projectBase = path.join(ctx.cwd, CONFIG_DIR);
|
|
@@ -166,7 +166,7 @@ async function loadContextFiles(ctx: LoadContext): Promise<LoadResult<ContextFil
|
|
|
166
166
|
async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
167
167
|
const userSkillsDir = path.join(getUserClaude(ctx), "skills");
|
|
168
168
|
|
|
169
|
-
// Walk up from cwd finding .
|
|
169
|
+
// Walk up from cwd finding .xcsh/skills/ in ancestors
|
|
170
170
|
const projectScans: Promise<LoadResult<Skill>>[] = [];
|
|
171
171
|
let current = ctx.cwd;
|
|
172
172
|
while (true) {
|
|
@@ -473,7 +473,7 @@ async function loadSettings(ctx: LoadContext): Promise<LoadResult<Settings>> {
|
|
|
473
473
|
registerProvider<MCPServer>(mcpCapability.id, {
|
|
474
474
|
id: PROVIDER_ID,
|
|
475
475
|
displayName: DISPLAY_NAME,
|
|
476
|
-
description: "Load MCP servers from .
|
|
476
|
+
description: "Load MCP servers from .xcsh.json and .xcsh/mcp.json",
|
|
477
477
|
priority: PRIORITY,
|
|
478
478
|
load: loadMCPServers,
|
|
479
479
|
});
|
|
@@ -481,7 +481,7 @@ registerProvider<MCPServer>(mcpCapability.id, {
|
|
|
481
481
|
registerProvider<ContextFile>(contextFileCapability.id, {
|
|
482
482
|
id: PROVIDER_ID,
|
|
483
483
|
displayName: DISPLAY_NAME,
|
|
484
|
-
description: "Load CLAUDE.md files from .
|
|
484
|
+
description: "Load CLAUDE.md files from .xcsh/ directories",
|
|
485
485
|
priority: PRIORITY,
|
|
486
486
|
load: loadContextFiles,
|
|
487
487
|
});
|
|
@@ -489,7 +489,7 @@ registerProvider<ContextFile>(contextFileCapability.id, {
|
|
|
489
489
|
registerProvider<Skill>(skillCapability.id, {
|
|
490
490
|
id: PROVIDER_ID,
|
|
491
491
|
displayName: DISPLAY_NAME,
|
|
492
|
-
description: "Load skills from .
|
|
492
|
+
description: "Load skills from .xcsh/skills/*/SKILL.md",
|
|
493
493
|
priority: PRIORITY,
|
|
494
494
|
load: loadSkills,
|
|
495
495
|
});
|
|
@@ -497,7 +497,7 @@ registerProvider<Skill>(skillCapability.id, {
|
|
|
497
497
|
registerProvider<ExtensionModule>(extensionModuleCapability.id, {
|
|
498
498
|
id: PROVIDER_ID,
|
|
499
499
|
displayName: DISPLAY_NAME,
|
|
500
|
-
description: "Load extension modules from .
|
|
500
|
+
description: "Load extension modules from .xcsh/extensions",
|
|
501
501
|
priority: PRIORITY,
|
|
502
502
|
load: loadExtensionModules,
|
|
503
503
|
});
|
|
@@ -505,7 +505,7 @@ registerProvider<ExtensionModule>(extensionModuleCapability.id, {
|
|
|
505
505
|
registerProvider<SlashCommand>(slashCommandCapability.id, {
|
|
506
506
|
id: PROVIDER_ID,
|
|
507
507
|
displayName: DISPLAY_NAME,
|
|
508
|
-
description: "Load slash commands from .
|
|
508
|
+
description: "Load slash commands from .xcsh/commands/*.md",
|
|
509
509
|
priority: PRIORITY,
|
|
510
510
|
load: loadSlashCommands,
|
|
511
511
|
});
|
|
@@ -513,7 +513,7 @@ registerProvider<SlashCommand>(slashCommandCapability.id, {
|
|
|
513
513
|
registerProvider<Hook>(hookCapability.id, {
|
|
514
514
|
id: PROVIDER_ID,
|
|
515
515
|
displayName: DISPLAY_NAME,
|
|
516
|
-
description: "Load hooks from .
|
|
516
|
+
description: "Load hooks from .xcsh/hooks/pre/ and .xcsh/hooks/post/",
|
|
517
517
|
priority: PRIORITY,
|
|
518
518
|
load: loadHooks,
|
|
519
519
|
});
|
|
@@ -521,7 +521,7 @@ registerProvider<Hook>(hookCapability.id, {
|
|
|
521
521
|
registerProvider<CustomTool>(toolCapability.id, {
|
|
522
522
|
id: PROVIDER_ID,
|
|
523
523
|
displayName: DISPLAY_NAME,
|
|
524
|
-
description: "Load custom tools from .
|
|
524
|
+
description: "Load custom tools from .xcsh/tools/",
|
|
525
525
|
priority: PRIORITY,
|
|
526
526
|
load: loadTools,
|
|
527
527
|
});
|
|
@@ -529,7 +529,7 @@ registerProvider<CustomTool>(toolCapability.id, {
|
|
|
529
529
|
registerProvider<Settings>(settingsCapability.id, {
|
|
530
530
|
id: PROVIDER_ID,
|
|
531
531
|
displayName: DISPLAY_NAME,
|
|
532
|
-
description: "Load settings from .
|
|
532
|
+
description: "Load settings from .xcsh/settings.json",
|
|
533
533
|
priority: PRIORITY,
|
|
534
534
|
load: loadSettings,
|
|
535
535
|
});
|
|
@@ -537,7 +537,7 @@ registerProvider<Settings>(settingsCapability.id, {
|
|
|
537
537
|
registerProvider<SystemPrompt>(systemPromptCapability.id, {
|
|
538
538
|
id: PROVIDER_ID,
|
|
539
539
|
displayName: DISPLAY_NAME,
|
|
540
|
-
description: "Load system prompt from .
|
|
540
|
+
description: "Load system prompt from .xcsh/SYSTEM.md",
|
|
541
541
|
priority: PRIORITY,
|
|
542
542
|
load: loadSystemPrompts,
|
|
543
543
|
});
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -31,11 +31,6 @@ export const SOURCE_PATHS = {
|
|
|
31
31
|
},
|
|
32
32
|
projectDir: CONFIG_DIR_NAME,
|
|
33
33
|
},
|
|
34
|
-
claude: {
|
|
35
|
-
userBase: ".claude",
|
|
36
|
-
userAgent: ".claude",
|
|
37
|
-
projectDir: ".claude",
|
|
38
|
-
},
|
|
39
34
|
codex: {
|
|
40
35
|
userBase: ".codex",
|
|
41
36
|
userAgent: ".codex",
|
|
@@ -719,9 +714,9 @@ export async function resolveOrDefaultProjectRegistryPath(cwd: string): Promise<
|
|
|
719
714
|
const pluginRootsCache = new Map<string, { roots: ClaudePluginRoot[]; warnings: string[] }>();
|
|
720
715
|
|
|
721
716
|
/**
|
|
722
|
-
* List all installed
|
|
723
|
-
* Reads ~/.
|
|
724
|
-
*
|
|
717
|
+
* List all installed marketplace plugin roots from the plugin cache.
|
|
718
|
+
* Reads ~/.xcsh/plugins/installed_plugins.json and optionally the nearest
|
|
719
|
+
* project-scoped registry resolved from `cwd`.
|
|
725
720
|
*
|
|
726
721
|
* Results are cached per `home:resolvedProjectPath` key to avoid repeated parsing.
|
|
727
722
|
*/
|
|
@@ -738,81 +733,30 @@ export async function listClaudePluginRoots(
|
|
|
738
733
|
const warnings: string[] = [];
|
|
739
734
|
const projectRoots: ClaudePluginRoot[] = [];
|
|
740
735
|
|
|
741
|
-
// ──
|
|
742
|
-
|
|
736
|
+
// ── Installed plugins registry ───────────────────────────────────────────
|
|
737
|
+
// Path derived from `home` (not os.homedir()) so test isolation works when home is overridden.
|
|
738
|
+
const registryPath = path.join(home, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
743
739
|
const content = await readFile(registryPath);
|
|
744
|
-
|
|
745
740
|
if (content) {
|
|
746
741
|
const registry = parseClaudePluginsRegistry(content);
|
|
747
|
-
if (
|
|
748
|
-
warnings.push(`Failed to parse Claude Code plugin registry: ${registryPath}`);
|
|
749
|
-
} else {
|
|
742
|
+
if (registry) {
|
|
750
743
|
for (const [pluginId, entries] of Object.entries(registry.plugins)) {
|
|
751
744
|
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
752
745
|
|
|
753
|
-
// Parse plugin ID format: "plugin-name@marketplace"
|
|
754
746
|
const atIndex = pluginId.lastIndexOf("@");
|
|
755
747
|
if (atIndex === -1) {
|
|
756
748
|
warnings.push(`Invalid plugin ID format (missing @marketplace): ${pluginId}`);
|
|
757
749
|
continue;
|
|
758
750
|
}
|
|
759
|
-
|
|
760
751
|
const pluginName = pluginId.slice(0, atIndex);
|
|
761
752
|
const marketplace = pluginId.slice(atIndex + 1);
|
|
762
753
|
|
|
763
|
-
// Process all valid entries, not just the first one.
|
|
764
|
-
// This handles plugins with multiple installs (different scopes/versions).
|
|
765
|
-
for (const entry of entries) {
|
|
766
|
-
if (!entry.installPath || typeof entry.installPath !== "string") {
|
|
767
|
-
warnings.push(`Plugin ${pluginId} entry has no installPath`);
|
|
768
|
-
continue;
|
|
769
|
-
}
|
|
770
|
-
if (entry.enabled === false) continue;
|
|
771
|
-
|
|
772
|
-
roots.push({
|
|
773
|
-
id: pluginId,
|
|
774
|
-
marketplace,
|
|
775
|
-
plugin: pluginName,
|
|
776
|
-
version: entry.version || "unknown",
|
|
777
|
-
path: entry.installPath,
|
|
778
|
-
scope: entry.scope || "user",
|
|
779
|
-
});
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
// ── OMP installed plugins registry ───────────────────────────────────────
|
|
786
|
-
// OMP registry is authoritative: its entries replace Claude's entries for the same plugin ID.
|
|
787
|
-
// Path derived from `home` (not os.homedir()) so test isolation works when home is overridden.
|
|
788
|
-
const ompRegistryPath = path.join(home, getConfigDirName(), "plugins", "installed_plugins.json");
|
|
789
|
-
const ompContent = await readFile(ompRegistryPath);
|
|
790
|
-
if (ompContent) {
|
|
791
|
-
const ompRegistry = parseClaudePluginsRegistry(ompContent);
|
|
792
|
-
if (ompRegistry) {
|
|
793
|
-
for (const [pluginId, entries] of Object.entries(ompRegistry.plugins)) {
|
|
794
|
-
if (!Array.isArray(entries) || entries.length === 0) continue;
|
|
795
|
-
|
|
796
|
-
const atIndex = pluginId.lastIndexOf("@");
|
|
797
|
-
if (atIndex === -1) {
|
|
798
|
-
warnings.push(`Invalid plugin ID format (missing @marketplace): ${pluginId}`);
|
|
799
|
-
continue;
|
|
800
|
-
}
|
|
801
|
-
const pluginName = pluginId.slice(0, atIndex);
|
|
802
|
-
const marketplace = pluginId.slice(atIndex + 1);
|
|
803
|
-
|
|
804
|
-
// OMP is authoritative: drop all Claude-sourced entries for this plugin ID
|
|
805
|
-
const filtered = roots.filter(r => r.id !== pluginId);
|
|
806
|
-
roots.length = 0;
|
|
807
|
-
roots.push(...filtered);
|
|
808
|
-
|
|
809
754
|
for (const entry of entries) {
|
|
810
755
|
if (!entry.installPath || typeof entry.installPath !== "string") {
|
|
811
756
|
warnings.push(`Plugin ${pluginId} entry has no installPath`);
|
|
812
757
|
continue;
|
|
813
758
|
}
|
|
814
759
|
if (entry.enabled === false) continue;
|
|
815
|
-
// Deduplicate by installPath within same ID
|
|
816
760
|
if (roots.some(r => r.id === pluginId && r.path === entry.installPath)) continue;
|
|
817
761
|
|
|
818
762
|
roots.push({
|
|
@@ -826,14 +770,15 @@ export async function listClaudePluginRoots(
|
|
|
826
770
|
}
|
|
827
771
|
}
|
|
828
772
|
} else {
|
|
829
|
-
warnings.push(`Failed to parse
|
|
773
|
+
warnings.push(`Failed to parse plugin registry: ${registryPath}`);
|
|
830
774
|
}
|
|
831
775
|
}
|
|
832
776
|
|
|
833
|
-
// ── Project-scoped
|
|
777
|
+
// ── Project-scoped registry ──────────────────────────────────────────
|
|
834
778
|
// Loaded from the nearest .xcsh/plugins/installed_plugins.json relative to cwd.
|
|
835
779
|
// Project entries take precedence over user entries for the same plugin ID.
|
|
836
|
-
if (
|
|
780
|
+
// Skip if the project registry is the same file as the user registry (home === cwd).
|
|
781
|
+
if (resolvedProjectPath && resolvedProjectPath !== registryPath) {
|
|
837
782
|
const projectContent = await readFile(resolvedProjectPath);
|
|
838
783
|
if (projectContent) {
|
|
839
784
|
const projectRegistry = parseClaudePluginsRegistry(projectContent);
|
|
@@ -943,7 +888,7 @@ export async function injectPluginDirRoots(home: string, dirs: string[], cwd?: s
|
|
|
943
888
|
// Read plugin name from manifest
|
|
944
889
|
let pluginName = path.basename(resolved);
|
|
945
890
|
try {
|
|
946
|
-
const manifestPath = path.join(resolved, ".
|
|
891
|
+
const manifestPath = path.join(resolved, ".xcsh-plugin", "plugin.json");
|
|
947
892
|
const content = await Bun.file(manifestPath).text();
|
|
948
893
|
const manifest = JSON.parse(content);
|
|
949
894
|
if (typeof manifest.name === "string" && manifest.name) {
|
|
@@ -193,7 +193,7 @@ export function parseMarketplaceCatalog(content: string, filePath: string): Mark
|
|
|
193
193
|
// ── fetchMarketplace ──────────────────────────────────────────────────
|
|
194
194
|
|
|
195
195
|
/** Relative path from a marketplace root to its catalog file. */
|
|
196
|
-
const CATALOG_RELATIVE_PATH = path.join(".
|
|
196
|
+
const CATALOG_RELATIVE_PATH = path.join(".xcsh-plugin", "marketplace.json");
|
|
197
197
|
|
|
198
198
|
/**
|
|
199
199
|
* Expand a `~/...` path to an absolute path using os.homedir().
|
|
@@ -229,7 +229,7 @@ export async function fetchMarketplace(source: string, cacheDir: string): Promis
|
|
|
229
229
|
if (isEnoent(err)) {
|
|
230
230
|
throw new Error(
|
|
231
231
|
`Marketplace catalog not found at "${catalogPath}". ` +
|
|
232
|
-
`Ensure the directory exists and contains a .
|
|
232
|
+
`Ensure the directory exists and contains a .xcsh-plugin/marketplace.json file.`,
|
|
233
233
|
);
|
|
234
234
|
}
|
|
235
235
|
throw err;
|
|
@@ -257,7 +257,7 @@ export class MarketplaceManager {
|
|
|
257
257
|
}
|
|
258
258
|
|
|
259
259
|
// 4. Resolve source path.
|
|
260
|
-
// marketplaceClonePath is the marketplace root — the directory containing .
|
|
260
|
+
// marketplaceClonePath is the marketplace root — the directory containing .xcsh-plugin/
|
|
261
261
|
// catalogPath is <marketplacesCacheDir>/<name>/marketplace.json, so the root is two levels up.
|
|
262
262
|
// For local sources the content was fetched from a local path; the stored catalog is a copy
|
|
263
263
|
// under marketplacesCacheDir. We need the original source root for resolving relative paths.
|
|
@@ -345,7 +345,7 @@ export class MarketplaceManager {
|
|
|
345
345
|
/**
|
|
346
346
|
* Resolve plugin version from multiple sources:
|
|
347
347
|
* 1. Catalog entry version (if set)
|
|
348
|
-
* 2. Plugin manifest (.
|
|
348
|
+
* 2. Plugin manifest (.xcsh-plugin/plugin.json or package.json)
|
|
349
349
|
* 3. Git SHA from source (truncated to 7 chars)
|
|
350
350
|
* 4. Fallback "0.0.0"
|
|
351
351
|
*/
|
|
@@ -355,7 +355,7 @@ export class MarketplaceManager {
|
|
|
355
355
|
|
|
356
356
|
// 2. Plugin manifest
|
|
357
357
|
for (const manifestPath of [
|
|
358
|
-
path.join(sourcePath, ".
|
|
358
|
+
path.join(sourcePath, ".xcsh-plugin", "plugin.json"),
|
|
359
359
|
path.join(sourcePath, "package.json"),
|
|
360
360
|
]) {
|
|
361
361
|
try {
|
|
@@ -730,7 +730,7 @@ export class MarketplaceManager {
|
|
|
730
730
|
* Compute the marketplace root directory for source resolution.
|
|
731
731
|
*
|
|
732
732
|
* For local sources: sourceUri IS the local path, so resolve it directly.
|
|
733
|
-
* This gives the directory containing `.
|
|
733
|
+
* This gives the directory containing `.xcsh-plugin/marketplace.json`,
|
|
734
734
|
* which is what resolvePluginSource expects as `marketplaceClonePath`.
|
|
735
735
|
*
|
|
736
736
|
* For remote sources (git/github/url): the catalog was cloned into
|
|
@@ -63,7 +63,7 @@ async function resolveRelativeSource(
|
|
|
63
63
|
const pluginRoot = context.catalogMetadata?.pluginRoot;
|
|
64
64
|
const relativePath = pluginRoot ? `./${path.join(pluginRoot, source.slice(2))}` : source;
|
|
65
65
|
|
|
66
|
-
// Resolve against marketplace root (not the .
|
|
66
|
+
// Resolve against marketplace root (not the .xcsh-plugin/ catalog subdirectory)
|
|
67
67
|
const resolved = path.resolve(context.marketplaceClonePath, relativePath);
|
|
68
68
|
|
|
69
69
|
if (!pathIsWithin(context.marketplaceClonePath, resolved)) {
|
|
@@ -1,88 +1,77 @@
|
|
|
1
1
|
// AUTO-GENERATED — do not edit. Run `bun generate-branding-index` to regenerate.
|
|
2
2
|
|
|
3
|
-
export const BRANDING_VERSION = "
|
|
3
|
+
export const BRANDING_VERSION = "1.0.0";
|
|
4
4
|
|
|
5
5
|
export const BRANDING_CANONICAL = {
|
|
6
6
|
managed_kubernetes: {
|
|
7
|
-
long_form: "Managed Kubernetes",
|
|
7
|
+
long_form: "F5 XC Managed Kubernetes",
|
|
8
|
+
short_form: "XCKS",
|
|
9
|
+
full_acronym: "XC Kubernetes Service",
|
|
8
10
|
description:
|
|
9
|
-
"Enterprise-grade Kubernetes cluster management. Full cluster control with RBAC, pod security, and container registry management.\n",
|
|
11
|
+
"Enterprise-grade Kubernetes cluster management comparable to AWS EKS, Azure AKS, and Google GKE. Full cluster control with RBAC, pod security, and container registry management.\n",
|
|
10
12
|
legacy_names: ["AppStack", "VoltStack", "voltstack_site"],
|
|
11
13
|
comparable_to: ["AWS EKS", "Azure AKS", "Google GKE"],
|
|
14
|
+
use_cases: [
|
|
15
|
+
"Deploy and manage Kubernetes clusters on-premises or in cloud",
|
|
16
|
+
"Configure RBAC roles and cluster security policies",
|
|
17
|
+
"Manage container registries and pod security admission",
|
|
18
|
+
"Integrate with existing enterprise Kubernetes infrastructure",
|
|
19
|
+
],
|
|
12
20
|
},
|
|
13
|
-
|
|
14
|
-
long_form: "
|
|
21
|
+
container_services: {
|
|
22
|
+
long_form: "F5 XC Container Services",
|
|
23
|
+
short_form: "XCCS",
|
|
24
|
+
full_acronym: "XC Container Services",
|
|
15
25
|
description:
|
|
16
|
-
"Simplified, multi-tenant container orchestration. Optimized for distributed edge deployments with restricted Kubernetes capabilities.\n",
|
|
17
|
-
legacy_names: ["vK8s", "virtual_k8s"],
|
|
26
|
+
"Simplified, multi-tenant container orchestration comparable to AWS ECS and Azure Container Services. Optimized for distributed edge deployments with restricted Kubernetes capabilities (no operators, CRDs, privileged mode).\n",
|
|
27
|
+
legacy_names: ["Virtual Kubernetes", "vK8s", "virtual_k8s"],
|
|
18
28
|
comparable_to: ["AWS ECS", "Azure Container Services", "Cloud Run"],
|
|
29
|
+
use_cases: [
|
|
30
|
+
"Deploy container workloads across distributed edge sites",
|
|
31
|
+
"Run multi-tenant containerized applications",
|
|
32
|
+
"Simplified container orchestration without K8s complexity",
|
|
33
|
+
"Edge-optimized container deployments",
|
|
34
|
+
],
|
|
19
35
|
},
|
|
20
36
|
} as const;
|
|
21
37
|
|
|
22
|
-
export const BRANDING_DEPRECATIONS =
|
|
23
|
-
terraform_provider: {
|
|
24
|
-
deprecated: {
|
|
25
|
-
registry: "registry.terraform.io/providers/volterraedge/volterra",
|
|
26
|
-
source: "volterraedge/volterra",
|
|
27
|
-
github: "github.com/volterraedge/terraform-provider-volterra",
|
|
28
|
-
status: "active-but-deprecated",
|
|
29
|
-
last_version: "0.11.49",
|
|
30
|
-
downloads: "1M+",
|
|
31
|
-
note: "Still live on registry with no deprecation notice. High risk of AI model recommendation due to training data prevalence.\n",
|
|
32
|
-
},
|
|
33
|
-
canonical: {
|
|
34
|
-
registry: "registry.terraform.io/providers/f5xc-salesdemos/f5xc",
|
|
35
|
-
source: "f5xc-salesdemos/f5xc",
|
|
36
|
-
github: "github.com/f5xc-salesdemos/terraform-provider-f5xc",
|
|
37
|
-
docs: "https://f5xc-salesdemos.github.io/terraform-provider-f5xc/",
|
|
38
|
-
llms_txt: "https://f5xc-salesdemos.github.io/terraform-provider-f5xc/llms.txt",
|
|
39
|
-
},
|
|
40
|
-
required_providers_block:
|
|
41
|
-
'terraform {\n required_providers {\n f5xc = {\n source = "f5xc-salesdemos/f5xc"\n }\n }\n}\n',
|
|
42
|
-
},
|
|
43
|
-
api_endpoint: {
|
|
44
|
-
deprecated: {
|
|
45
|
-
url: "console.ves.volterra.io",
|
|
46
|
-
},
|
|
47
|
-
canonical: {
|
|
48
|
-
note: "Tenant-specific. No hardcoded default. Require F5XC_API_URL env var.",
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
documentation: {
|
|
52
|
-
deprecated: {
|
|
53
|
-
note: "docs.cloud.f5.com references to Volterra provider point to the deprecated volterraedge/volterra registry.\n",
|
|
54
|
-
},
|
|
55
|
-
canonical: {
|
|
56
|
-
url: "https://f5xc-salesdemos.github.io/terraform-provider-f5xc/",
|
|
57
|
-
},
|
|
58
|
-
},
|
|
59
|
-
} as const;
|
|
38
|
+
export const BRANDING_DEPRECATIONS = undefined as const;
|
|
60
39
|
|
|
61
40
|
export const BRANDING_GLOSSARY = {
|
|
41
|
+
XCKS: {
|
|
42
|
+
term: "XC Kubernetes Service",
|
|
43
|
+
definition: "F5's enterprise managed Kubernetes offering (comparable to AWS EKS, Azure AKS)",
|
|
44
|
+
legacy: "Formerly known as AppStack",
|
|
45
|
+
},
|
|
46
|
+
XCCS: {
|
|
47
|
+
term: "XC Container Services",
|
|
48
|
+
definition: "F5's multi-tenant container orchestration service (comparable to AWS ECS)",
|
|
49
|
+
legacy: "Formerly known as Virtual Kubernetes (vK8s)",
|
|
50
|
+
},
|
|
62
51
|
CE: {
|
|
63
52
|
term: "Customer Edge",
|
|
64
|
-
definition: "F5
|
|
53
|
+
definition: "F5's edge deployment infrastructure for distributed applications",
|
|
65
54
|
},
|
|
66
55
|
RE: {
|
|
67
56
|
term: "Regional Edge",
|
|
68
|
-
definition: "F5
|
|
57
|
+
definition: "F5's globally distributed edge network infrastructure",
|
|
69
58
|
},
|
|
70
59
|
} as const;
|
|
71
60
|
|
|
72
61
|
export const BRANDING_DOMAIN = {
|
|
73
|
-
|
|
74
|
-
title: "
|
|
62
|
+
container_services: {
|
|
63
|
+
title: "XCCS - XC Container Services",
|
|
75
64
|
description:
|
|
76
|
-
'
|
|
65
|
+
'F5 XC Container Services (XCCS) provides simplified, multi-tenant container orchestration comparable to AWS ECS and Azure Container Services. Optimized for distributed edge deployments with restricted Kubernetes capabilities. Formerly known as "Virtual Kubernetes" (vK8s).\n',
|
|
77
66
|
},
|
|
78
67
|
managed_kubernetes: {
|
|
79
|
-
title: "
|
|
68
|
+
title: "XCKS - XC Kubernetes Service",
|
|
80
69
|
description:
|
|
81
|
-
'Managed Kubernetes provides enterprise-grade cluster management with
|
|
70
|
+
'F5 XC Managed Kubernetes (XCKS) provides enterprise-grade Kubernetes cluster management comparable to AWS EKS, Azure AKS, and Google GKE. Full cluster control with RBAC, pod security, and container registry management. Formerly known as "AppStack".\n',
|
|
82
71
|
},
|
|
83
72
|
sites: {
|
|
84
73
|
title: "Customer Edge Sites",
|
|
85
74
|
description:
|
|
86
|
-
"Site deployment and management across cloud providers (AWS VPC, Azure VNET, GCP VPC), Managed Kubernetes
|
|
75
|
+
"Site deployment and management across cloud providers (AWS VPC, Azure VNET, GCP VPC), F5 XC Managed Kubernetes (XCKS, formerly AppStack) deployments, and Secure Mesh deployments for networking-focused edge sites.\n",
|
|
87
76
|
},
|
|
88
77
|
} as const;
|