feat: theme hooks/skills/plugins output
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import fs from "node:fs";
|
||||
import fsp from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import chalk from "chalk";
|
||||
import type { Command } from "commander";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
@@ -23,6 +22,7 @@ import { recordHookInstall } from "../hooks/installs.js";
|
||||
import { buildPluginStatusReport } from "../plugins/status.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { renderTable } from "../terminal/table.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
@@ -66,50 +66,40 @@ function buildHooksReport(config: ClawdbotConfig): HookStatusReport {
|
||||
return buildWorkspaceHookStatus(workspaceDir, { config, entries });
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a single hook for display in the list
|
||||
*/
|
||||
function formatHookLine(hook: HookStatusEntry, verbose = false): string {
|
||||
function formatHookStatus(hook: HookStatusEntry): string {
|
||||
if (hook.eligible) return theme.success("✓ ready");
|
||||
if (hook.disabled) return theme.warn("⏸ disabled");
|
||||
return theme.error("✗ missing");
|
||||
}
|
||||
|
||||
function formatHookName(hook: HookStatusEntry): string {
|
||||
const emoji = hook.emoji ?? "🔗";
|
||||
const status = hook.eligible
|
||||
? chalk.green("✓")
|
||||
: hook.disabled
|
||||
? chalk.yellow("disabled")
|
||||
: chalk.red("missing reqs");
|
||||
return `${emoji} ${theme.command(hook.name)}`;
|
||||
}
|
||||
|
||||
const name = hook.eligible ? chalk.white(hook.name) : chalk.gray(hook.name);
|
||||
function formatHookSource(hook: HookStatusEntry): string {
|
||||
if (!hook.managedByPlugin) return hook.source;
|
||||
return `plugin:${hook.pluginId ?? "unknown"}`;
|
||||
}
|
||||
|
||||
const desc = chalk.gray(
|
||||
hook.description.length > 50 ? `${hook.description.slice(0, 47)}...` : hook.description,
|
||||
);
|
||||
const sourceLabel = hook.managedByPlugin
|
||||
? chalk.magenta(`plugin:${hook.pluginId ?? "unknown"}`)
|
||||
: "";
|
||||
|
||||
if (verbose) {
|
||||
const missing: string[] = [];
|
||||
if (hook.missing.bins.length > 0) {
|
||||
missing.push(`bins: ${hook.missing.bins.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.anyBins.length > 0) {
|
||||
missing.push(`anyBins: ${hook.missing.anyBins.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.env.length > 0) {
|
||||
missing.push(`env: ${hook.missing.env.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.config.length > 0) {
|
||||
missing.push(`config: ${hook.missing.config.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.os.length > 0) {
|
||||
missing.push(`os: ${hook.missing.os.join(", ")}`);
|
||||
}
|
||||
const missingStr = missing.length > 0 ? chalk.red(` [${missing.join("; ")}]`) : "";
|
||||
const sourceSuffix = sourceLabel ? ` ${sourceLabel}` : "";
|
||||
return `${emoji} ${name} ${status}${missingStr}\n ${desc}${sourceSuffix}`;
|
||||
function formatHookMissingSummary(hook: HookStatusEntry): string {
|
||||
const missing: string[] = [];
|
||||
if (hook.missing.bins.length > 0) {
|
||||
missing.push(`bins: ${hook.missing.bins.join(", ")}`);
|
||||
}
|
||||
|
||||
const sourceSuffix = sourceLabel ? ` ${sourceLabel}` : "";
|
||||
return `${emoji} ${name} ${status} - ${desc}${sourceSuffix}`;
|
||||
if (hook.missing.anyBins.length > 0) {
|
||||
missing.push(`anyBins: ${hook.missing.anyBins.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.env.length > 0) {
|
||||
missing.push(`env: ${hook.missing.env.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.config.length > 0) {
|
||||
missing.push(`config: ${hook.missing.config.join(", ")}`);
|
||||
}
|
||||
if (hook.missing.os.length > 0) {
|
||||
missing.push(`os: ${hook.missing.os.join(", ")}`);
|
||||
}
|
||||
return missing.join("; ");
|
||||
}
|
||||
|
||||
async function readInstalledPackageVersion(dir: string): Promise<string | undefined> {
|
||||
@@ -157,27 +147,39 @@ export function formatHooksList(report: HookStatusReport, opts: HooksListOptions
|
||||
}
|
||||
|
||||
const eligible = hooks.filter((h) => h.eligible);
|
||||
const notEligible = hooks.filter((h) => !h.eligible);
|
||||
const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
|
||||
const rows = hooks.map((hook) => {
|
||||
const missing = formatHookMissingSummary(hook);
|
||||
return {
|
||||
Status: formatHookStatus(hook),
|
||||
Hook: formatHookName(hook),
|
||||
Description: theme.muted(hook.description),
|
||||
Source: formatHookSource(hook),
|
||||
Missing: missing ? theme.warn(missing) : "",
|
||||
};
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{ key: "Status", header: "Status", minWidth: 10 },
|
||||
{ key: "Hook", header: "Hook", minWidth: 18, flex: true },
|
||||
{ key: "Description", header: "Description", minWidth: 24, flex: true },
|
||||
{ key: "Source", header: "Source", minWidth: 12, flex: true },
|
||||
];
|
||||
if (opts.verbose) {
|
||||
columns.push({ key: "Missing", header: "Missing", minWidth: 18, flex: true });
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(chalk.bold.cyan("Hooks") + chalk.gray(` (${eligible.length}/${hooks.length} ready)`));
|
||||
lines.push("");
|
||||
|
||||
if (eligible.length > 0) {
|
||||
lines.push(chalk.bold.green("Ready:"));
|
||||
for (const hook of eligible) {
|
||||
lines.push(` ${formatHookLine(hook, opts.verbose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (notEligible.length > 0 && !opts.eligible) {
|
||||
if (eligible.length > 0) lines.push("");
|
||||
lines.push(chalk.bold.yellow("Not ready:"));
|
||||
for (const hook of notEligible) {
|
||||
lines.push(` ${formatHookLine(hook, opts.verbose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(
|
||||
`${theme.heading("Hooks")} ${theme.muted(`(${eligible.length}/${hooks.length} ready)`)}`,
|
||||
);
|
||||
lines.push(
|
||||
renderTable({
|
||||
width: tableWidth,
|
||||
columns,
|
||||
rows,
|
||||
}).trimEnd(),
|
||||
);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
@@ -205,33 +207,33 @@ export function formatHookInfo(
|
||||
const lines: string[] = [];
|
||||
const emoji = hook.emoji ?? "🔗";
|
||||
const status = hook.eligible
|
||||
? chalk.green("✓ Ready")
|
||||
? theme.success("✓ Ready")
|
||||
: hook.disabled
|
||||
? chalk.yellow("⏸ Disabled")
|
||||
: chalk.red("✗ Missing requirements");
|
||||
? theme.warn("⏸ Disabled")
|
||||
: theme.error("✗ Missing requirements");
|
||||
|
||||
lines.push(`${emoji} ${chalk.bold.cyan(hook.name)} ${status}`);
|
||||
lines.push(`${emoji} ${theme.heading(hook.name)} ${status}`);
|
||||
lines.push("");
|
||||
lines.push(chalk.white(hook.description));
|
||||
lines.push(hook.description);
|
||||
lines.push("");
|
||||
|
||||
// Details
|
||||
lines.push(chalk.bold("Details:"));
|
||||
lines.push(theme.heading("Details:"));
|
||||
if (hook.managedByPlugin) {
|
||||
lines.push(` Source: ${hook.source} (${hook.pluginId ?? "unknown"})`);
|
||||
lines.push(`${theme.muted(" Source:")} ${hook.source} (${hook.pluginId ?? "unknown"})`);
|
||||
} else {
|
||||
lines.push(` Source: ${hook.source}`);
|
||||
lines.push(`${theme.muted(" Source:")} ${hook.source}`);
|
||||
}
|
||||
lines.push(` Path: ${chalk.gray(hook.filePath)}`);
|
||||
lines.push(` Handler: ${chalk.gray(hook.handlerPath)}`);
|
||||
lines.push(`${theme.muted(" Path:")} ${hook.filePath}`);
|
||||
lines.push(`${theme.muted(" Handler:")} ${hook.handlerPath}`);
|
||||
if (hook.homepage) {
|
||||
lines.push(` Homepage: ${chalk.blue(hook.homepage)}`);
|
||||
lines.push(`${theme.muted(" Homepage:")} ${hook.homepage}`);
|
||||
}
|
||||
if (hook.events.length > 0) {
|
||||
lines.push(` Events: ${hook.events.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Events:")} ${hook.events.join(", ")}`);
|
||||
}
|
||||
if (hook.managedByPlugin) {
|
||||
lines.push(` Managed by plugin; enable/disable via hooks CLI not available.`);
|
||||
lines.push(theme.muted(" Managed by plugin; enable/disable via hooks CLI not available."));
|
||||
}
|
||||
|
||||
// Requirements
|
||||
@@ -244,40 +246,40 @@ export function formatHookInfo(
|
||||
|
||||
if (hasRequirements) {
|
||||
lines.push("");
|
||||
lines.push(chalk.bold("Requirements:"));
|
||||
lines.push(theme.heading("Requirements:"));
|
||||
if (hook.requirements.bins.length > 0) {
|
||||
const binsStatus = hook.requirements.bins.map((bin) => {
|
||||
const missing = hook.missing.bins.includes(bin);
|
||||
return missing ? chalk.red(`✗ ${bin}`) : chalk.green(`✓ ${bin}`);
|
||||
return missing ? theme.error(`✗ ${bin}`) : theme.success(`✓ ${bin}`);
|
||||
});
|
||||
lines.push(` Binaries: ${binsStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Binaries:")} ${binsStatus.join(", ")}`);
|
||||
}
|
||||
if (hook.requirements.anyBins.length > 0) {
|
||||
const anyBinsStatus =
|
||||
hook.missing.anyBins.length > 0
|
||||
? chalk.red(`✗ (any of: ${hook.requirements.anyBins.join(", ")})`)
|
||||
: chalk.green(`✓ (any of: ${hook.requirements.anyBins.join(", ")})`);
|
||||
lines.push(` Any binary: ${anyBinsStatus}`);
|
||||
? theme.error(`✗ (any of: ${hook.requirements.anyBins.join(", ")})`)
|
||||
: theme.success(`✓ (any of: ${hook.requirements.anyBins.join(", ")})`);
|
||||
lines.push(`${theme.muted(" Any binary:")} ${anyBinsStatus}`);
|
||||
}
|
||||
if (hook.requirements.env.length > 0) {
|
||||
const envStatus = hook.requirements.env.map((env) => {
|
||||
const missing = hook.missing.env.includes(env);
|
||||
return missing ? chalk.red(`✗ ${env}`) : chalk.green(`✓ ${env}`);
|
||||
return missing ? theme.error(`✗ ${env}`) : theme.success(`✓ ${env}`);
|
||||
});
|
||||
lines.push(` Environment: ${envStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Environment:")} ${envStatus.join(", ")}`);
|
||||
}
|
||||
if (hook.requirements.config.length > 0) {
|
||||
const configStatus = hook.configChecks.map((check) => {
|
||||
return check.satisfied ? chalk.green(`✓ ${check.path}`) : chalk.red(`✗ ${check.path}`);
|
||||
return check.satisfied ? theme.success(`✓ ${check.path}`) : theme.error(`✗ ${check.path}`);
|
||||
});
|
||||
lines.push(` Config: ${configStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Config:")} ${configStatus.join(", ")}`);
|
||||
}
|
||||
if (hook.requirements.os.length > 0) {
|
||||
const osStatus =
|
||||
hook.missing.os.length > 0
|
||||
? chalk.red(`✗ (${hook.requirements.os.join(", ")})`)
|
||||
: chalk.green(`✓ (${hook.requirements.os.join(", ")})`);
|
||||
lines.push(` OS: ${osStatus}`);
|
||||
? theme.error(`✗ (${hook.requirements.os.join(", ")})`)
|
||||
: theme.success(`✓ (${hook.requirements.os.join(", ")})`);
|
||||
lines.push(`${theme.muted(" OS:")} ${osStatus}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -313,15 +315,15 @@ export function formatHooksCheck(report: HookStatusReport, opts: HooksCheckOptio
|
||||
const notEligible = report.hooks.filter((h) => !h.eligible);
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(chalk.bold.cyan("Hooks Status"));
|
||||
lines.push(theme.heading("Hooks Status"));
|
||||
lines.push("");
|
||||
lines.push(`Total hooks: ${report.hooks.length}`);
|
||||
lines.push(chalk.green(`Ready: ${eligible.length}`));
|
||||
lines.push(chalk.yellow(`Not ready: ${notEligible.length}`));
|
||||
lines.push(`${theme.muted("Total hooks:")} ${report.hooks.length}`);
|
||||
lines.push(`${theme.success("Ready:")} ${eligible.length}`);
|
||||
lines.push(`${theme.warn("Not ready:")} ${notEligible.length}`);
|
||||
|
||||
if (notEligible.length > 0) {
|
||||
lines.push("");
|
||||
lines.push(chalk.bold.yellow("Hooks not ready:"));
|
||||
lines.push(theme.heading("Hooks not ready:"));
|
||||
for (const hook of notEligible) {
|
||||
const reasons = [];
|
||||
if (hook.disabled) reasons.push("disabled");
|
||||
@@ -374,7 +376,9 @@ export async function enableHook(hookName: string): Promise<void> {
|
||||
};
|
||||
|
||||
await writeConfigFile(nextConfig);
|
||||
console.log(`${chalk.green("✓")} Enabled hook: ${hook.emoji ?? "🔗"} ${hookName}`);
|
||||
defaultRuntime.log(
|
||||
`${theme.success("✓")} Enabled hook: ${hook.emoji ?? "🔗"} ${theme.command(hookName)}`,
|
||||
);
|
||||
}
|
||||
|
||||
export async function disableHook(hookName: string): Promise<void> {
|
||||
@@ -408,7 +412,9 @@ export async function disableHook(hookName: string): Promise<void> {
|
||||
};
|
||||
|
||||
await writeConfigFile(nextConfig);
|
||||
console.log(`${chalk.yellow("⏸")} Disabled hook: ${hook.emoji ?? "🔗"} ${hookName}`);
|
||||
defaultRuntime.log(
|
||||
`${theme.warn("⏸")} Disabled hook: ${hook.emoji ?? "🔗"} ${theme.command(hookName)}`,
|
||||
);
|
||||
}
|
||||
|
||||
export function registerHooksCli(program: Command): void {
|
||||
@@ -431,9 +437,11 @@ export function registerHooksCli(program: Command): void {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const report = buildHooksReport(config);
|
||||
console.log(formatHooksList(report, opts));
|
||||
defaultRuntime.log(formatHooksList(report, opts));
|
||||
} catch (err) {
|
||||
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
||||
defaultRuntime.error(
|
||||
`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -446,9 +454,11 @@ export function registerHooksCli(program: Command): void {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const report = buildHooksReport(config);
|
||||
console.log(formatHookInfo(report, name, opts));
|
||||
defaultRuntime.log(formatHookInfo(report, name, opts));
|
||||
} catch (err) {
|
||||
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
||||
defaultRuntime.error(
|
||||
`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -461,9 +471,11 @@ export function registerHooksCli(program: Command): void {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const report = buildHooksReport(config);
|
||||
console.log(formatHooksCheck(report, opts));
|
||||
defaultRuntime.log(formatHooksCheck(report, opts));
|
||||
} catch (err) {
|
||||
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
||||
defaultRuntime.error(
|
||||
`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -475,7 +487,9 @@ export function registerHooksCli(program: Command): void {
|
||||
try {
|
||||
await enableHook(name);
|
||||
} catch (err) {
|
||||
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
||||
defaultRuntime.error(
|
||||
`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -487,7 +501,9 @@ export function registerHooksCli(program: Command): void {
|
||||
try {
|
||||
await disableHook(name);
|
||||
} catch (err) {
|
||||
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
||||
defaultRuntime.error(
|
||||
`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
@@ -570,7 +586,7 @@ export function registerHooksCli(program: Command): void {
|
||||
path: resolved,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
if (!result.ok) {
|
||||
@@ -650,7 +666,7 @@ export function registerHooksCli(program: Command): void {
|
||||
spec: raw,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
if (!result.ok) {
|
||||
@@ -726,15 +742,15 @@ export function registerHooksCli(program: Command): void {
|
||||
for (const hookId of targets) {
|
||||
const record = installs[hookId];
|
||||
if (!record) {
|
||||
defaultRuntime.log(chalk.yellow(`No install record for "${hookId}".`));
|
||||
defaultRuntime.log(theme.warn(`No install record for "${hookId}".`));
|
||||
continue;
|
||||
}
|
||||
if (record.source !== "npm") {
|
||||
defaultRuntime.log(chalk.yellow(`Skipping "${hookId}" (source: ${record.source}).`));
|
||||
defaultRuntime.log(theme.warn(`Skipping "${hookId}" (source: ${record.source}).`));
|
||||
continue;
|
||||
}
|
||||
if (!record.spec) {
|
||||
defaultRuntime.log(chalk.yellow(`Skipping "${hookId}" (missing npm spec).`));
|
||||
defaultRuntime.log(theme.warn(`Skipping "${hookId}" (missing npm spec).`));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -749,11 +765,11 @@ export function registerHooksCli(program: Command): void {
|
||||
expectedHookPackId: hookId,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
if (!probe.ok) {
|
||||
defaultRuntime.log(chalk.red(`Failed to check ${hookId}: ${probe.error}`));
|
||||
defaultRuntime.log(theme.error(`Failed to check ${hookId}: ${probe.error}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -773,11 +789,11 @@ export function registerHooksCli(program: Command): void {
|
||||
expectedHookPackId: hookId,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
if (!result.ok) {
|
||||
defaultRuntime.log(chalk.red(`Failed to update ${hookId}: ${result.error}`));
|
||||
defaultRuntime.log(theme.error(`Failed to update ${hookId}: ${result.error}`));
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -811,9 +827,11 @@ export function registerHooksCli(program: Command): void {
|
||||
try {
|
||||
const config = loadConfig();
|
||||
const report = buildHooksReport(config);
|
||||
console.log(formatHooksList(report, {}));
|
||||
defaultRuntime.log(formatHooksList(report, {}));
|
||||
} catch (err) {
|
||||
console.error(chalk.red("Error:"), err instanceof Error ? err.message : String(err));
|
||||
defaultRuntime.error(
|
||||
`${theme.error("Error:")} ${err instanceof Error ? err.message : String(err)}`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import chalk from "chalk";
|
||||
import type { Command } from "commander";
|
||||
|
||||
import { loadConfig, writeConfigFile } from "../config/config.js";
|
||||
@@ -14,6 +13,7 @@ import { buildPluginStatusReport } from "../plugins/status.js";
|
||||
import { updateNpmInstalledPlugins } from "../plugins/update.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { renderTable } from "../terminal/table.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { resolveUserPath } from "../utils.js";
|
||||
|
||||
@@ -35,19 +35,19 @@ export type PluginUpdateOptions = {
|
||||
function formatPluginLine(plugin: PluginRecord, verbose = false): string {
|
||||
const status =
|
||||
plugin.status === "loaded"
|
||||
? chalk.green("✓")
|
||||
? theme.success("loaded")
|
||||
: plugin.status === "disabled"
|
||||
? chalk.yellow("disabled")
|
||||
: chalk.red("error");
|
||||
const name = plugin.name ? chalk.white(plugin.name) : chalk.white(plugin.id);
|
||||
const idSuffix = plugin.name !== plugin.id ? chalk.gray(` (${plugin.id})`) : "";
|
||||
? theme.warn("disabled")
|
||||
: theme.error("error");
|
||||
const name = theme.command(plugin.name || plugin.id);
|
||||
const idSuffix = plugin.name && plugin.name !== plugin.id ? theme.muted(` (${plugin.id})`) : "";
|
||||
const desc = plugin.description
|
||||
? chalk.gray(
|
||||
? theme.muted(
|
||||
plugin.description.length > 60
|
||||
? `${plugin.description.slice(0, 57)}...`
|
||||
: plugin.description,
|
||||
)
|
||||
: chalk.gray("(no description)");
|
||||
: theme.muted("(no description)");
|
||||
|
||||
if (!verbose) {
|
||||
return `${name}${idSuffix} ${status} - ${desc}`;
|
||||
@@ -55,14 +55,14 @@ function formatPluginLine(plugin: PluginRecord, verbose = false): string {
|
||||
|
||||
const parts = [
|
||||
`${name}${idSuffix} ${status}`,
|
||||
` source: ${chalk.gray(plugin.source)}`,
|
||||
` source: ${theme.muted(plugin.source)}`,
|
||||
` origin: ${plugin.origin}`,
|
||||
];
|
||||
if (plugin.version) parts.push(` version: ${plugin.version}`);
|
||||
if (plugin.providerIds.length > 0) {
|
||||
parts.push(` providers: ${plugin.providerIds.join(", ")}`);
|
||||
}
|
||||
if (plugin.error) parts.push(chalk.red(` error: ${plugin.error}`));
|
||||
if (plugin.error) parts.push(theme.error(` error: ${plugin.error}`));
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ function applySlotSelectionForPlugin(
|
||||
function logSlotWarnings(warnings: string[]) {
|
||||
if (warnings.length === 0) return;
|
||||
for (const warning of warnings) {
|
||||
defaultRuntime.log(chalk.yellow(warning));
|
||||
defaultRuntime.log(theme.warn(warning));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,19 +124,51 @@ export function registerPluginsCli(program: Command) {
|
||||
}
|
||||
|
||||
if (list.length === 0) {
|
||||
defaultRuntime.log("No plugins found.");
|
||||
defaultRuntime.log(theme.muted("No plugins found."));
|
||||
return;
|
||||
}
|
||||
|
||||
const loaded = list.filter((p) => p.status === "loaded").length;
|
||||
defaultRuntime.log(
|
||||
`${theme.heading("Plugins")} ${theme.muted(`(${loaded}/${list.length} loaded)`)}`,
|
||||
);
|
||||
|
||||
if (!opts.verbose) {
|
||||
const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
|
||||
const rows = list.map((plugin) => ({
|
||||
Name: plugin.name || plugin.id,
|
||||
ID: plugin.name && plugin.name !== plugin.id ? plugin.id : "",
|
||||
Status:
|
||||
plugin.status === "loaded"
|
||||
? theme.success("loaded")
|
||||
: plugin.status === "disabled"
|
||||
? theme.warn("disabled")
|
||||
: theme.error("error"),
|
||||
Source: plugin.source,
|
||||
Version: plugin.version ?? "",
|
||||
Description: plugin.description ?? "",
|
||||
}));
|
||||
defaultRuntime.log(
|
||||
renderTable({
|
||||
width: tableWidth,
|
||||
columns: [
|
||||
{ key: "Name", header: "Name", minWidth: 14, flex: true },
|
||||
{ key: "ID", header: "ID", minWidth: 10, flex: true },
|
||||
{ key: "Status", header: "Status", minWidth: 10 },
|
||||
{ key: "Source", header: "Source", minWidth: 10 },
|
||||
{ key: "Version", header: "Version", minWidth: 8 },
|
||||
{ key: "Description", header: "Description", minWidth: 18, flex: true },
|
||||
],
|
||||
rows,
|
||||
}).trimEnd(),
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
const loaded = list.filter((p) => p.status === "loaded").length;
|
||||
lines.push(
|
||||
`${chalk.bold.cyan("Plugins")} ${chalk.gray(`(${loaded}/${list.length} loaded)`)}`,
|
||||
);
|
||||
lines.push("");
|
||||
for (const plugin of list) {
|
||||
lines.push(formatPluginLine(plugin, opts.verbose));
|
||||
if (opts.verbose) lines.push("");
|
||||
lines.push(formatPluginLine(plugin, true));
|
||||
lines.push("");
|
||||
}
|
||||
defaultRuntime.log(lines.join("\n").trim());
|
||||
});
|
||||
@@ -162,43 +194,45 @@ export function registerPluginsCli(program: Command) {
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(chalk.bold.cyan(plugin.name || plugin.id));
|
||||
lines.push(theme.heading(plugin.name || plugin.id));
|
||||
if (plugin.name && plugin.name !== plugin.id) {
|
||||
lines.push(chalk.gray(`id: ${plugin.id}`));
|
||||
lines.push(theme.muted(`id: ${plugin.id}`));
|
||||
}
|
||||
if (plugin.description) lines.push(plugin.description);
|
||||
lines.push("");
|
||||
lines.push(`Status: ${plugin.status}`);
|
||||
lines.push(`Source: ${plugin.source}`);
|
||||
lines.push(`Origin: ${plugin.origin}`);
|
||||
if (plugin.version) lines.push(`Version: ${plugin.version}`);
|
||||
lines.push(`${theme.muted("Status:")} ${plugin.status}`);
|
||||
lines.push(`${theme.muted("Source:")} ${plugin.source}`);
|
||||
lines.push(`${theme.muted("Origin:")} ${plugin.origin}`);
|
||||
if (plugin.version) lines.push(`${theme.muted("Version:")} ${plugin.version}`);
|
||||
if (plugin.toolNames.length > 0) {
|
||||
lines.push(`Tools: ${plugin.toolNames.join(", ")}`);
|
||||
lines.push(`${theme.muted("Tools:")} ${plugin.toolNames.join(", ")}`);
|
||||
}
|
||||
if (plugin.hookNames.length > 0) {
|
||||
lines.push(`Hooks: ${plugin.hookNames.join(", ")}`);
|
||||
lines.push(`${theme.muted("Hooks:")} ${plugin.hookNames.join(", ")}`);
|
||||
}
|
||||
if (plugin.gatewayMethods.length > 0) {
|
||||
lines.push(`Gateway methods: ${plugin.gatewayMethods.join(", ")}`);
|
||||
lines.push(`${theme.muted("Gateway methods:")} ${plugin.gatewayMethods.join(", ")}`);
|
||||
}
|
||||
if (plugin.providerIds.length > 0) {
|
||||
lines.push(`Providers: ${plugin.providerIds.join(", ")}`);
|
||||
lines.push(`${theme.muted("Providers:")} ${plugin.providerIds.join(", ")}`);
|
||||
}
|
||||
if (plugin.cliCommands.length > 0) {
|
||||
lines.push(`CLI commands: ${plugin.cliCommands.join(", ")}`);
|
||||
lines.push(`${theme.muted("CLI commands:")} ${plugin.cliCommands.join(", ")}`);
|
||||
}
|
||||
if (plugin.services.length > 0) {
|
||||
lines.push(`Services: ${plugin.services.join(", ")}`);
|
||||
lines.push(`${theme.muted("Services:")} ${plugin.services.join(", ")}`);
|
||||
}
|
||||
if (plugin.error) lines.push(chalk.red(`Error: ${plugin.error}`));
|
||||
if (plugin.error) lines.push(`${theme.error("Error:")} ${plugin.error}`);
|
||||
if (install) {
|
||||
lines.push("");
|
||||
lines.push(`Install: ${install.source}`);
|
||||
if (install.spec) lines.push(`Spec: ${install.spec}`);
|
||||
if (install.sourcePath) lines.push(`Source path: ${install.sourcePath}`);
|
||||
if (install.installPath) lines.push(`Install path: ${install.installPath}`);
|
||||
if (install.version) lines.push(`Recorded version: ${install.version}`);
|
||||
if (install.installedAt) lines.push(`Installed at: ${install.installedAt}`);
|
||||
lines.push(`${theme.muted("Install:")} ${install.source}`);
|
||||
if (install.spec) lines.push(`${theme.muted("Spec:")} ${install.spec}`);
|
||||
if (install.sourcePath) lines.push(`${theme.muted("Source path:")} ${install.sourcePath}`);
|
||||
if (install.installPath)
|
||||
lines.push(`${theme.muted("Install path:")} ${install.installPath}`);
|
||||
if (install.version) lines.push(`${theme.muted("Recorded version:")} ${install.version}`);
|
||||
if (install.installedAt)
|
||||
lines.push(`${theme.muted("Installed at:")} ${install.installedAt}`);
|
||||
}
|
||||
defaultRuntime.log(lines.join("\n"));
|
||||
});
|
||||
@@ -308,7 +342,7 @@ export function registerPluginsCli(program: Command) {
|
||||
path: resolved,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
if (!result.ok) {
|
||||
@@ -372,7 +406,7 @@ export function registerPluginsCli(program: Command) {
|
||||
spec: raw,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
if (!result.ok) {
|
||||
@@ -430,17 +464,17 @@ export function registerPluginsCli(program: Command) {
|
||||
dryRun: opts.dryRun,
|
||||
logger: {
|
||||
info: (msg) => defaultRuntime.log(msg),
|
||||
warn: (msg) => defaultRuntime.log(chalk.yellow(msg)),
|
||||
warn: (msg) => defaultRuntime.log(theme.warn(msg)),
|
||||
},
|
||||
});
|
||||
|
||||
for (const outcome of result.outcomes) {
|
||||
if (outcome.status === "error") {
|
||||
defaultRuntime.log(chalk.red(outcome.message));
|
||||
defaultRuntime.log(theme.error(outcome.message));
|
||||
continue;
|
||||
}
|
||||
if (outcome.status === "skipped") {
|
||||
defaultRuntime.log(chalk.yellow(outcome.message));
|
||||
defaultRuntime.log(theme.warn(outcome.message));
|
||||
continue;
|
||||
}
|
||||
defaultRuntime.log(outcome.message);
|
||||
@@ -467,14 +501,14 @@ export function registerPluginsCli(program: Command) {
|
||||
|
||||
const lines: string[] = [];
|
||||
if (errors.length > 0) {
|
||||
lines.push(chalk.bold.red("Plugin errors:"));
|
||||
lines.push(theme.error("Plugin errors:"));
|
||||
for (const entry of errors) {
|
||||
lines.push(`- ${entry.id}: ${entry.error ?? "failed to load"} (${entry.source})`);
|
||||
}
|
||||
}
|
||||
if (diags.length > 0) {
|
||||
if (lines.length > 0) lines.push("");
|
||||
lines.push(chalk.bold.yellow("Diagnostics:"));
|
||||
lines.push(theme.warn("Diagnostics:"));
|
||||
for (const diag of diags) {
|
||||
const target = diag.pluginId ? `${diag.pluginId}: ` : "";
|
||||
lines.push(`- ${target}${diag.message}`);
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import chalk from "chalk";
|
||||
import type { Command } from "commander";
|
||||
|
||||
import { loadConfig } from "../config/config.js";
|
||||
@@ -121,7 +120,7 @@ export function registerSecurityCli(program: Command) {
|
||||
lines.push("");
|
||||
lines.push(heading(label));
|
||||
for (const f of list) {
|
||||
lines.push(`${chalk.gray(f.checkId)} ${f.title}`);
|
||||
lines.push(`${theme.muted(f.checkId)} ${f.title}`);
|
||||
lines.push(` ${f.detail}`);
|
||||
if (f.remediation?.trim()) lines.push(` ${muted(`Fix: ${f.remediation.trim()}`)}`);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import chalk from "chalk";
|
||||
import type { Command } from "commander";
|
||||
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
||||
import {
|
||||
@@ -9,6 +8,7 @@ import {
|
||||
import { loadConfig } from "../config/config.js";
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { renderTable } from "../terminal/table.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { formatCliCommand } from "./command-format.js";
|
||||
|
||||
@@ -31,47 +31,36 @@ function appendClawdHubHint(output: string, json?: boolean): string {
|
||||
return `${output}\n\nTip: use \`npx clawdhub\` to search, install, and sync skills.`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format a single skill for display in the list
|
||||
*/
|
||||
function formatSkillLine(skill: SkillStatusEntry, verbose = false): string {
|
||||
function formatSkillStatus(skill: SkillStatusEntry): string {
|
||||
if (skill.eligible) return theme.success("✓ ready");
|
||||
if (skill.disabled) return theme.warn("⏸ disabled");
|
||||
if (skill.blockedByAllowlist) return theme.warn("🚫 blocked");
|
||||
return theme.error("✗ missing");
|
||||
}
|
||||
|
||||
function formatSkillName(skill: SkillStatusEntry): string {
|
||||
const emoji = skill.emoji ?? "📦";
|
||||
const status = skill.eligible
|
||||
? chalk.green("✓")
|
||||
: skill.disabled
|
||||
? chalk.yellow("disabled")
|
||||
: skill.blockedByAllowlist
|
||||
? chalk.yellow("blocked")
|
||||
: chalk.red("missing reqs");
|
||||
return `${emoji} ${theme.command(skill.name)}`;
|
||||
}
|
||||
|
||||
const name = skill.eligible ? chalk.white(skill.name) : chalk.gray(skill.name);
|
||||
|
||||
const desc = chalk.gray(
|
||||
skill.description.length > 50 ? `${skill.description.slice(0, 47)}...` : skill.description,
|
||||
);
|
||||
|
||||
if (verbose) {
|
||||
const missing: string[] = [];
|
||||
if (skill.missing.bins.length > 0) {
|
||||
missing.push(`bins: ${skill.missing.bins.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.anyBins.length > 0) {
|
||||
missing.push(`anyBins: ${skill.missing.anyBins.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.env.length > 0) {
|
||||
missing.push(`env: ${skill.missing.env.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.config.length > 0) {
|
||||
missing.push(`config: ${skill.missing.config.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.os.length > 0) {
|
||||
missing.push(`os: ${skill.missing.os.join(", ")}`);
|
||||
}
|
||||
const missingStr = missing.length > 0 ? chalk.red(` [${missing.join("; ")}]`) : "";
|
||||
return `${emoji} ${name} ${status}${missingStr}\n ${desc}`;
|
||||
function formatSkillMissingSummary(skill: SkillStatusEntry): string {
|
||||
const missing: string[] = [];
|
||||
if (skill.missing.bins.length > 0) {
|
||||
missing.push(`bins: ${skill.missing.bins.join(", ")}`);
|
||||
}
|
||||
|
||||
return `${emoji} ${name} ${status} - ${desc}`;
|
||||
if (skill.missing.anyBins.length > 0) {
|
||||
missing.push(`anyBins: ${skill.missing.anyBins.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.env.length > 0) {
|
||||
missing.push(`env: ${skill.missing.env.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.config.length > 0) {
|
||||
missing.push(`config: ${skill.missing.config.join(", ")}`);
|
||||
}
|
||||
if (skill.missing.os.length > 0) {
|
||||
missing.push(`os: ${skill.missing.os.join(", ")}`);
|
||||
}
|
||||
return missing.join("; ");
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -108,28 +97,39 @@ export function formatSkillsList(report: SkillStatusReport, opts: SkillsListOpti
|
||||
}
|
||||
|
||||
const eligible = skills.filter((s) => s.eligible);
|
||||
const notEligible = skills.filter((s) => !s.eligible);
|
||||
const tableWidth = Math.max(60, (process.stdout.columns ?? 120) - 1);
|
||||
const rows = skills.map((skill) => {
|
||||
const missing = formatSkillMissingSummary(skill);
|
||||
return {
|
||||
Status: formatSkillStatus(skill),
|
||||
Skill: formatSkillName(skill),
|
||||
Description: theme.muted(skill.description),
|
||||
Source: skill.source ?? "",
|
||||
Missing: missing ? theme.warn(missing) : "",
|
||||
};
|
||||
});
|
||||
|
||||
const columns = [
|
||||
{ key: "Status", header: "Status", minWidth: 10 },
|
||||
{ key: "Skill", header: "Skill", minWidth: 18, flex: true },
|
||||
{ key: "Description", header: "Description", minWidth: 24, flex: true },
|
||||
{ key: "Source", header: "Source", minWidth: 10 },
|
||||
];
|
||||
if (opts.verbose) {
|
||||
columns.push({ key: "Missing", header: "Missing", minWidth: 18, flex: true });
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(
|
||||
chalk.bold.cyan("Skills") + chalk.gray(` (${eligible.length}/${skills.length} ready)`),
|
||||
`${theme.heading("Skills")} ${theme.muted(`(${eligible.length}/${skills.length} ready)`)}`,
|
||||
);
|
||||
lines.push(
|
||||
renderTable({
|
||||
width: tableWidth,
|
||||
columns,
|
||||
rows,
|
||||
}).trimEnd(),
|
||||
);
|
||||
lines.push("");
|
||||
|
||||
if (eligible.length > 0) {
|
||||
lines.push(chalk.bold.green("Ready:"));
|
||||
for (const skill of eligible) {
|
||||
lines.push(` ${formatSkillLine(skill, opts.verbose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
if (notEligible.length > 0 && !opts.eligible) {
|
||||
if (eligible.length > 0) lines.push("");
|
||||
lines.push(chalk.bold.yellow("Not ready:"));
|
||||
for (const skill of notEligible) {
|
||||
lines.push(` ${formatSkillLine(skill, opts.verbose)}`);
|
||||
}
|
||||
}
|
||||
|
||||
return appendClawdHubHint(lines.join("\n"), opts.json);
|
||||
}
|
||||
@@ -161,27 +161,27 @@ export function formatSkillInfo(
|
||||
const lines: string[] = [];
|
||||
const emoji = skill.emoji ?? "📦";
|
||||
const status = skill.eligible
|
||||
? chalk.green("✓ Ready")
|
||||
? theme.success("✓ Ready")
|
||||
: skill.disabled
|
||||
? chalk.yellow("⏸ Disabled")
|
||||
? theme.warn("⏸ Disabled")
|
||||
: skill.blockedByAllowlist
|
||||
? chalk.yellow("🚫 Blocked by allowlist")
|
||||
: chalk.red("✗ Missing requirements");
|
||||
? theme.warn("🚫 Blocked by allowlist")
|
||||
: theme.error("✗ Missing requirements");
|
||||
|
||||
lines.push(`${emoji} ${chalk.bold.cyan(skill.name)} ${status}`);
|
||||
lines.push(`${emoji} ${theme.heading(skill.name)} ${status}`);
|
||||
lines.push("");
|
||||
lines.push(chalk.white(skill.description));
|
||||
lines.push(skill.description);
|
||||
lines.push("");
|
||||
|
||||
// Details
|
||||
lines.push(chalk.bold("Details:"));
|
||||
lines.push(` Source: ${skill.source}`);
|
||||
lines.push(` Path: ${chalk.gray(skill.filePath)}`);
|
||||
lines.push(theme.heading("Details:"));
|
||||
lines.push(`${theme.muted(" Source:")} ${skill.source}`);
|
||||
lines.push(`${theme.muted(" Path:")} ${skill.filePath}`);
|
||||
if (skill.homepage) {
|
||||
lines.push(` Homepage: ${chalk.blue(skill.homepage)}`);
|
||||
lines.push(`${theme.muted(" Homepage:")} ${skill.homepage}`);
|
||||
}
|
||||
if (skill.primaryEnv) {
|
||||
lines.push(` Primary env: ${skill.primaryEnv}`);
|
||||
lines.push(`${theme.muted(" Primary env:")} ${skill.primaryEnv}`);
|
||||
}
|
||||
|
||||
// Requirements
|
||||
@@ -194,51 +194,51 @@ export function formatSkillInfo(
|
||||
|
||||
if (hasRequirements) {
|
||||
lines.push("");
|
||||
lines.push(chalk.bold("Requirements:"));
|
||||
lines.push(theme.heading("Requirements:"));
|
||||
if (skill.requirements.bins.length > 0) {
|
||||
const binsStatus = skill.requirements.bins.map((bin) => {
|
||||
const missing = skill.missing.bins.includes(bin);
|
||||
return missing ? chalk.red(`✗ ${bin}`) : chalk.green(`✓ ${bin}`);
|
||||
return missing ? theme.error(`✗ ${bin}`) : theme.success(`✓ ${bin}`);
|
||||
});
|
||||
lines.push(` Binaries: ${binsStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Binaries:")} ${binsStatus.join(", ")}`);
|
||||
}
|
||||
if (skill.requirements.anyBins.length > 0) {
|
||||
const anyBinsMissing = skill.missing.anyBins.length > 0;
|
||||
const anyBinsStatus = skill.requirements.anyBins.map((bin) => {
|
||||
const missing = anyBinsMissing;
|
||||
return missing ? chalk.red(`✗ ${bin}`) : chalk.green(`✓ ${bin}`);
|
||||
return missing ? theme.error(`✗ ${bin}`) : theme.success(`✓ ${bin}`);
|
||||
});
|
||||
lines.push(` Any binaries: ${anyBinsStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Any binaries:")} ${anyBinsStatus.join(", ")}`);
|
||||
}
|
||||
if (skill.requirements.env.length > 0) {
|
||||
const envStatus = skill.requirements.env.map((env) => {
|
||||
const missing = skill.missing.env.includes(env);
|
||||
return missing ? chalk.red(`✗ ${env}`) : chalk.green(`✓ ${env}`);
|
||||
return missing ? theme.error(`✗ ${env}`) : theme.success(`✓ ${env}`);
|
||||
});
|
||||
lines.push(` Environment: ${envStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Environment:")} ${envStatus.join(", ")}`);
|
||||
}
|
||||
if (skill.requirements.config.length > 0) {
|
||||
const configStatus = skill.requirements.config.map((cfg) => {
|
||||
const missing = skill.missing.config.includes(cfg);
|
||||
return missing ? chalk.red(`✗ ${cfg}`) : chalk.green(`✓ ${cfg}`);
|
||||
return missing ? theme.error(`✗ ${cfg}`) : theme.success(`✓ ${cfg}`);
|
||||
});
|
||||
lines.push(` Config: ${configStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" Config:")} ${configStatus.join(", ")}`);
|
||||
}
|
||||
if (skill.requirements.os.length > 0) {
|
||||
const osStatus = skill.requirements.os.map((osName) => {
|
||||
const missing = skill.missing.os.includes(osName);
|
||||
return missing ? chalk.red(`✗ ${osName}`) : chalk.green(`✓ ${osName}`);
|
||||
return missing ? theme.error(`✗ ${osName}`) : theme.success(`✓ ${osName}`);
|
||||
});
|
||||
lines.push(` OS: ${osStatus.join(", ")}`);
|
||||
lines.push(`${theme.muted(" OS:")} ${osStatus.join(", ")}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Install options
|
||||
if (skill.install.length > 0 && !skill.eligible) {
|
||||
lines.push("");
|
||||
lines.push(chalk.bold("Install options:"));
|
||||
lines.push(theme.heading("Install options:"));
|
||||
for (const inst of skill.install) {
|
||||
lines.push(` ${chalk.yellow("→")} ${inst.label}`);
|
||||
lines.push(` ${theme.warn("→")} ${inst.label}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,17 +281,17 @@ export function formatSkillsCheck(report: SkillStatusReport, opts: SkillsCheckOp
|
||||
}
|
||||
|
||||
const lines: string[] = [];
|
||||
lines.push(chalk.bold.cyan("Skills Status Check"));
|
||||
lines.push(theme.heading("Skills Status Check"));
|
||||
lines.push("");
|
||||
lines.push(`Total: ${report.skills.length}`);
|
||||
lines.push(`${chalk.green("✓")} Eligible: ${eligible.length}`);
|
||||
lines.push(`${chalk.yellow("⏸")} Disabled: ${disabled.length}`);
|
||||
lines.push(`${chalk.yellow("🚫")} Blocked by allowlist: ${blocked.length}`);
|
||||
lines.push(`${chalk.red("✗")} Missing requirements: ${missingReqs.length}`);
|
||||
lines.push(`${theme.muted("Total:")} ${report.skills.length}`);
|
||||
lines.push(`${theme.success("✓")} ${theme.muted("Eligible:")} ${eligible.length}`);
|
||||
lines.push(`${theme.warn("⏸")} ${theme.muted("Disabled:")} ${disabled.length}`);
|
||||
lines.push(`${theme.warn("🚫")} ${theme.muted("Blocked by allowlist:")} ${blocked.length}`);
|
||||
lines.push(`${theme.error("✗")} ${theme.muted("Missing requirements:")} ${missingReqs.length}`);
|
||||
|
||||
if (eligible.length > 0) {
|
||||
lines.push("");
|
||||
lines.push(chalk.bold.green("Ready to use:"));
|
||||
lines.push(theme.heading("Ready to use:"));
|
||||
for (const skill of eligible) {
|
||||
const emoji = skill.emoji ?? "📦";
|
||||
lines.push(` ${emoji} ${skill.name}`);
|
||||
@@ -300,7 +300,7 @@ export function formatSkillsCheck(report: SkillStatusReport, opts: SkillsCheckOp
|
||||
|
||||
if (missingReqs.length > 0) {
|
||||
lines.push("");
|
||||
lines.push(chalk.bold.red("Missing requirements:"));
|
||||
lines.push(theme.heading("Missing requirements:"));
|
||||
for (const skill of missingReqs) {
|
||||
const emoji = skill.emoji ?? "📦";
|
||||
const missing: string[] = [];
|
||||
@@ -319,7 +319,7 @@ export function formatSkillsCheck(report: SkillStatusReport, opts: SkillsCheckOp
|
||||
if (skill.missing.os.length > 0) {
|
||||
missing.push(`os: ${skill.missing.os.join(", ")}`);
|
||||
}
|
||||
lines.push(` ${emoji} ${skill.name} ${chalk.gray(`(${missing.join("; ")})`)}`);
|
||||
lines.push(` ${emoji} ${skill.name} ${theme.muted(`(${missing.join("; ")})`)}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +350,7 @@ export function registerSkillsCli(program: Command) {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
console.log(formatSkillsList(report, opts));
|
||||
defaultRuntime.log(formatSkillsList(report, opts));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
@@ -367,7 +367,7 @@ export function registerSkillsCli(program: Command) {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
console.log(formatSkillInfo(report, name, opts));
|
||||
defaultRuntime.log(formatSkillInfo(report, name, opts));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
@@ -383,7 +383,7 @@ export function registerSkillsCli(program: Command) {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
console.log(formatSkillsCheck(report, opts));
|
||||
defaultRuntime.log(formatSkillsCheck(report, opts));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
@@ -396,7 +396,7 @@ export function registerSkillsCli(program: Command) {
|
||||
const config = loadConfig();
|
||||
const workspaceDir = resolveAgentWorkspaceDir(config, resolveDefaultAgentId(config));
|
||||
const report = buildWorkspaceSkillStatus(workspaceDir, { config });
|
||||
console.log(formatSkillsList(report, {}));
|
||||
defaultRuntime.log(formatSkillsList(report, {}));
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
|
||||
Reference in New Issue
Block a user