@f5xc-salesdemos/xcsh 19.7.0 → 19.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/src/extensibility/plugins/marketplace/prerequisites.ts +50 -0
- package/src/extensibility/plugins/marketplace/types.ts +2 -0
- package/src/internal-urls/build-info.generated.ts +8 -8
- package/src/modes/components/plugins/state-manager.ts +10 -0
- package/src/modes/components/plugins/types.ts +3 -1
- package/src/slash-commands/builtin-registry.ts +76 -1
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@f5xc-salesdemos/xcsh",
|
|
4
|
-
"version": "19.
|
|
4
|
+
"version": "19.8.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",
|
|
@@ -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": "19.
|
|
54
|
-
"@f5xc-salesdemos/pi-agent-core": "19.
|
|
55
|
-
"@f5xc-salesdemos/pi-ai": "19.
|
|
56
|
-
"@f5xc-salesdemos/pi-natives": "19.
|
|
57
|
-
"@f5xc-salesdemos/pi-tui": "19.
|
|
58
|
-
"@f5xc-salesdemos/pi-utils": "19.
|
|
53
|
+
"@f5xc-salesdemos/xcsh-stats": "19.8.0",
|
|
54
|
+
"@f5xc-salesdemos/pi-agent-core": "19.8.0",
|
|
55
|
+
"@f5xc-salesdemos/pi-ai": "19.8.0",
|
|
56
|
+
"@f5xc-salesdemos/pi-natives": "19.8.0",
|
|
57
|
+
"@f5xc-salesdemos/pi-tui": "19.8.0",
|
|
58
|
+
"@f5xc-salesdemos/pi-utils": "19.8.0",
|
|
59
59
|
"@sinclair/typebox": "^0.34",
|
|
60
60
|
"@xterm/headless": "^6.0",
|
|
61
61
|
"ajv": "^8.20",
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { MarketplacePluginEntry } from "./types";
|
|
2
|
+
|
|
3
|
+
const cache = new Map<string, boolean>();
|
|
4
|
+
|
|
5
|
+
export async function checkPrerequisite(detectCmd: string): Promise<boolean> {
|
|
6
|
+
const cached = cache.get(detectCmd);
|
|
7
|
+
if (cached !== undefined) return cached;
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const [cmd, ...args] = detectCmd.split(/\s+/);
|
|
11
|
+
const proc = Bun.spawn([cmd!, ...args], {
|
|
12
|
+
stdout: "ignore",
|
|
13
|
+
stderr: "ignore",
|
|
14
|
+
});
|
|
15
|
+
const exitCode = await proc.exited;
|
|
16
|
+
const available = exitCode === 0;
|
|
17
|
+
cache.set(detectCmd, available);
|
|
18
|
+
return available;
|
|
19
|
+
} catch {
|
|
20
|
+
cache.set(detectCmd, false);
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function checkAllPrerequisites(
|
|
26
|
+
plugins: MarketplacePluginEntry[],
|
|
27
|
+
): Promise<Map<string, { available: boolean; missing: string[] }>> {
|
|
28
|
+
const results = new Map<string, { available: boolean; missing: string[] }>();
|
|
29
|
+
|
|
30
|
+
for (const plugin of plugins) {
|
|
31
|
+
if (!plugin.prerequisites || plugin.prerequisites.length === 0) {
|
|
32
|
+
results.set(plugin.name, { available: true, missing: [] });
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const missing: string[] = [];
|
|
37
|
+
for (const prereq of plugin.prerequisites) {
|
|
38
|
+
const ok = await checkPrerequisite(prereq.detectCmd);
|
|
39
|
+
if (!ok) missing.push(prereq.tool);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
results.set(plugin.name, { available: missing.length === 0, missing });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return results;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function clearPrerequisiteCache(): void {
|
|
49
|
+
cache.clear();
|
|
50
|
+
}
|
|
@@ -89,6 +89,8 @@ export interface MarketplacePluginEntry {
|
|
|
89
89
|
tags?: string[];
|
|
90
90
|
strict?: boolean;
|
|
91
91
|
defaultEnabled?: boolean;
|
|
92
|
+
recommended?: boolean;
|
|
93
|
+
prerequisites?: Array<{ tool: string; installCmd: string; detectCmd: string }>;
|
|
92
94
|
commands?: string | string[];
|
|
93
95
|
agents?: string | string[];
|
|
94
96
|
hooks?: string | Record<string, unknown>;
|
|
@@ -17,17 +17,17 @@ export interface BuildInfo {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const BUILD_INFO: BuildInfo = {
|
|
20
|
-
"version": "19.
|
|
21
|
-
"commit": "
|
|
22
|
-
"shortCommit": "
|
|
20
|
+
"version": "19.8.0",
|
|
21
|
+
"commit": "ea6c3e825dbbdf4e3e471bbe3a22dc503e00f30e",
|
|
22
|
+
"shortCommit": "ea6c3e8",
|
|
23
23
|
"branch": "main",
|
|
24
|
-
"tag": "v19.
|
|
25
|
-
"commitDate": "2026-06-
|
|
26
|
-
"buildDate": "2026-06-
|
|
24
|
+
"tag": "v19.8.0",
|
|
25
|
+
"commitDate": "2026-06-05T00:33:42Z",
|
|
26
|
+
"buildDate": "2026-06-05T00:57:39.846Z",
|
|
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/v19.
|
|
31
|
+
"commitUrl": "https://github.com/f5xc-salesdemos/xcsh/commit/ea6c3e825dbbdf4e3e471bbe3a22dc503e00f30e",
|
|
32
|
+
"releaseUrl": "https://github.com/f5xc-salesdemos/xcsh/releases/tag/v19.8.0"
|
|
33
33
|
};
|
|
@@ -62,6 +62,8 @@ function catalogToDashboard(entry: MarketplacePluginEntry, marketplace: string):
|
|
|
62
62
|
installed: false,
|
|
63
63
|
enabled: false,
|
|
64
64
|
hasUpdate: false,
|
|
65
|
+
recommended: entry.recommended,
|
|
66
|
+
prerequisites: entry.prerequisites,
|
|
65
67
|
};
|
|
66
68
|
}
|
|
67
69
|
|
|
@@ -98,6 +100,8 @@ export async function loadAllPlugins(mgr: MarketplaceManager, npmMgr: PluginMana
|
|
|
98
100
|
const existing = plugins.find(p => p.id === pluginId);
|
|
99
101
|
if (existing) {
|
|
100
102
|
existing.displayName = existing.displayName || entry.displayName;
|
|
103
|
+
existing.recommended = existing.recommended || entry.recommended;
|
|
104
|
+
existing.prerequisites = existing.prerequisites || entry.prerequisites;
|
|
101
105
|
existing.description = existing.description || entry.description;
|
|
102
106
|
existing.category = existing.category || entry.category;
|
|
103
107
|
existing.tags = existing.tags || entry.tags;
|
|
@@ -126,10 +130,14 @@ export async function loadAllPlugins(mgr: MarketplaceManager, npmMgr: PluginMana
|
|
|
126
130
|
export function buildTabs(plugins: DashboardPlugin[]): PluginTab[] {
|
|
127
131
|
const tabs: PluginTab[] = [];
|
|
128
132
|
const installedCount = plugins.filter(p => p.installed).length;
|
|
133
|
+
const recommendedCount = plugins.filter(p => !p.installed && p.recommended).length;
|
|
129
134
|
const discoverCount = plugins.filter(p => !p.installed).length;
|
|
130
135
|
const updatesCount = plugins.filter(p => p.hasUpdate).length;
|
|
131
136
|
|
|
132
137
|
tabs.push({ id: "installed", label: "Installed", count: installedCount });
|
|
138
|
+
if (recommendedCount > 0) {
|
|
139
|
+
tabs.push({ id: "recommended", label: "Recommended", count: recommendedCount });
|
|
140
|
+
}
|
|
133
141
|
if (discoverCount > 0) {
|
|
134
142
|
tabs.push({ id: "discover", label: "Discover", count: discoverCount });
|
|
135
143
|
}
|
|
@@ -143,6 +151,8 @@ export function filterByTab(plugins: DashboardPlugin[], tabId: PluginTabId): Das
|
|
|
143
151
|
switch (tabId) {
|
|
144
152
|
case "installed":
|
|
145
153
|
return plugins.filter(p => p.installed);
|
|
154
|
+
case "recommended":
|
|
155
|
+
return plugins.filter(p => !p.installed && p.recommended);
|
|
146
156
|
case "discover":
|
|
147
157
|
return plugins.filter(p => !p.installed);
|
|
148
158
|
case "updates":
|
|
@@ -18,9 +18,11 @@ export interface DashboardPlugin {
|
|
|
18
18
|
shadowedBy?: "project";
|
|
19
19
|
hasUpdate: boolean;
|
|
20
20
|
updateVersion?: string;
|
|
21
|
+
recommended?: boolean;
|
|
22
|
+
prerequisites?: Array<{ tool: string; installCmd: string; detectCmd: string }>;
|
|
21
23
|
}
|
|
22
24
|
|
|
23
|
-
export type PluginTabId = "installed" | "discover" | "updates";
|
|
25
|
+
export type PluginTabId = "installed" | "recommended" | "discover" | "updates";
|
|
24
26
|
|
|
25
27
|
export interface PluginTab {
|
|
26
28
|
id: PluginTabId;
|
|
@@ -875,6 +875,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
875
875
|
{ name: "discover", description: "Browse available plugins", usage: "[marketplace]" },
|
|
876
876
|
{ name: "list", description: "List all installed plugins" },
|
|
877
877
|
{ name: "validate", description: "Validate marketplace or plugin manifest", usage: "[path]" },
|
|
878
|
+
{ name: "setup", description: "Guided setup for recommended plugins" },
|
|
878
879
|
{ name: "help", description: "Show usage guide" },
|
|
879
880
|
],
|
|
880
881
|
allowArgs: true,
|
|
@@ -969,7 +970,6 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
969
970
|
}
|
|
970
971
|
break;
|
|
971
972
|
}
|
|
972
|
-
case "list":
|
|
973
973
|
default: {
|
|
974
974
|
const marketplaces = await mgr.listMarketplaces();
|
|
975
975
|
if (marketplaces.length === 0) {
|
|
@@ -1162,6 +1162,80 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1162
1162
|
}
|
|
1163
1163
|
break;
|
|
1164
1164
|
}
|
|
1165
|
+
// ── Setup (guided recommended plugin install) ──
|
|
1166
|
+
case "setup": {
|
|
1167
|
+
const { checkPrerequisite } = await import("../extensibility/plugins/marketplace/prerequisites");
|
|
1168
|
+
const allPlugins = await mgr.listAvailablePlugins();
|
|
1169
|
+
const recommended = allPlugins.filter(p => p.recommended);
|
|
1170
|
+
if (recommended.length === 0) {
|
|
1171
|
+
runtime.ctx.showStatus("No recommended plugins found in configured marketplaces");
|
|
1172
|
+
break;
|
|
1173
|
+
}
|
|
1174
|
+
|
|
1175
|
+
const installed = await mgr.listInstalledPlugins();
|
|
1176
|
+
const installedIds = new Set(installed.map(p => p.id));
|
|
1177
|
+
const toInstall = recommended.filter(
|
|
1178
|
+
p => !Array.from(installedIds).some(id => id.startsWith(`${p.name}@`)),
|
|
1179
|
+
);
|
|
1180
|
+
|
|
1181
|
+
if (toInstall.length === 0) {
|
|
1182
|
+
runtime.ctx.showStatus("All recommended plugins are already installed");
|
|
1183
|
+
break;
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
const lines: string[] = ["Recommended plugins setup:\n"];
|
|
1187
|
+
let installedCount = 0;
|
|
1188
|
+
let skippedCount = 0;
|
|
1189
|
+
const skippedReasons: string[] = [];
|
|
1190
|
+
|
|
1191
|
+
for (const plugin of toInstall) {
|
|
1192
|
+
if (plugin.prerequisites && plugin.prerequisites.length > 0) {
|
|
1193
|
+
const missing: string[] = [];
|
|
1194
|
+
for (const prereq of plugin.prerequisites) {
|
|
1195
|
+
const ok = await checkPrerequisite(prereq.detectCmd);
|
|
1196
|
+
if (!ok) missing.push(`${prereq.tool} (${prereq.installCmd})`);
|
|
1197
|
+
}
|
|
1198
|
+
if (missing.length > 0) {
|
|
1199
|
+
lines.push(` ⊘ ${plugin.displayName || plugin.name} — missing: ${missing.join(", ")}`);
|
|
1200
|
+
skippedCount++;
|
|
1201
|
+
skippedReasons.push(...missing);
|
|
1202
|
+
continue;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const marketplaces = await mgr.listMarketplaces();
|
|
1207
|
+
let installed = false;
|
|
1208
|
+
for (const mkt of marketplaces) {
|
|
1209
|
+
const available = await mgr.listAvailablePlugins(mkt.name);
|
|
1210
|
+
if (available.some(a => a.name === plugin.name)) {
|
|
1211
|
+
try {
|
|
1212
|
+
await mgr.installPlugin(plugin.name, mkt.name);
|
|
1213
|
+
lines.push(` + ${plugin.displayName || plugin.name} — installed`);
|
|
1214
|
+
installedCount++;
|
|
1215
|
+
installed = true;
|
|
1216
|
+
} catch (err) {
|
|
1217
|
+
lines.push(
|
|
1218
|
+
` ! ${plugin.displayName || plugin.name} — ${err instanceof Error ? err.message : String(err)}`,
|
|
1219
|
+
);
|
|
1220
|
+
skippedCount++;
|
|
1221
|
+
}
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
if (!installed && skippedCount === 0) {
|
|
1226
|
+
lines.push(` ? ${plugin.displayName || plugin.name} — not found in any marketplace`);
|
|
1227
|
+
skippedCount++;
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
lines.push("");
|
|
1232
|
+
lines.push(`Installed ${installedCount}/${toInstall.length} recommended plugin(s)`);
|
|
1233
|
+
if (skippedCount > 0) {
|
|
1234
|
+
lines.push(`${skippedCount} skipped — install missing tools and run /plugin setup again`);
|
|
1235
|
+
}
|
|
1236
|
+
runtime.ctx.showStatus(lines.join("\n"));
|
|
1237
|
+
break;
|
|
1238
|
+
}
|
|
1165
1239
|
// ── Help ──
|
|
1166
1240
|
case "help": {
|
|
1167
1241
|
runtime.ctx.showStatus(
|
|
@@ -1180,6 +1254,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<BuiltinSlashCommandSpec> = [
|
|
|
1180
1254
|
" /plugin upgrade [name@marketplace] Upgrade plugin(s)",
|
|
1181
1255
|
" /plugin list List installed plugins",
|
|
1182
1256
|
" /plugin validate [path] Validate marketplace or plugin",
|
|
1257
|
+
" /plugin setup Guided setup for recommended plugins",
|
|
1183
1258
|
"",
|
|
1184
1259
|
"Quick start:",
|
|
1185
1260
|
" /plugin marketplace add f5xc-salesdemos/marketplace",
|