feat(cc-config): list a plugin's skills, agents and commands by name

A plugin card showed "7 skills, 5 agents" and nothing more — the counts
were the whole story, so finding out WHICH skills a plugin ships meant
opening its install directory by hand. The contribution chips are now
buttons: clicking one expands a list naming each artifact with its
description, one surface open at a time. Commands render with their
slash. Hooks stay a plain chip — a plugin's hooks are matcher/command
pairs declared inline in plugin.json, not named artifacts.

`contributes.items` carries the four surfaces server-side; the existing
counts keep their type and are now derived from those lists, so a chip
reading "3 commands" can never sit beside a list holding two.

That also fixes a real blind spot. Each surface was read only from the
conventional install subdirectory, but plugin.json may declare its own
paths — this repo's own `ccam` lists three files under
plugins/ccam/commands/ and keeps commands/ empty, so it reported zero
commands. Both sources are now read and de-duplicated. A declared path
resolving outside the plugin's install directory is dropped: a manifest
is third-party data and must not turn the dashboard into an arbitrary
file reader.

Also pins the Refresh button's width: both labels share one grid cell so
the button is always as wide as "Refreshing…" / "Đang làm mới…" and no
longer resizes the toolbar when the state flips. Measuring the longest
real string beats a min-width that would be wrong in the next language.

Verified: npm run test:server (1370 tests, +6 for the new discovery),
npm run test:client (457, +3 for the expand behaviour), tsc --noEmit,
prettier and check-headers clean. Docs synced: docs/API.md, openapi.yaml
(new CcConfigPluginItem schema), ARCHITECTURE.md, docs/PLUGINS.md.
This commit is contained in:
2026-09-04 11:21:12 +07:00
parent 965c62c5cf
commit 677cdf007b
10 changed files with 540 additions and 49 deletions
+20
View File
@@ -2288,6 +2288,18 @@ export interface CcMdItem {
preview: string;
}
/** One named artifact a plugin contributes — a skill, subagent, command or
* output style. `name` is the frontmatter name Claude Code addresses it by
* (`/ccam:ship-feature`, `subagent_type: qc-local`), falling back to the
* filename when the file declares none. */
export interface CcPluginItem {
name: string;
/** Frontmatter `description`; null when the file declares none. */
description: string | null;
/** Absolute path of the backing `.md` file. */
file: string;
}
/** Counts of what a plugin contributes to Claude Code, plus its manifest
* metadata, embedded in {@link CcPlugin}. */
export interface CcPluginContributions {
@@ -2296,6 +2308,14 @@ export interface CcPluginContributions {
commands: number;
outputStyles: number;
hooks: number;
/** The same surfaces by name, so a card can list WHICH artifacts a plugin
* ships rather than only how many. Counts above are these lists' lengths. */
items: {
skills: CcPluginItem[];
agents: CcPluginItem[];
commands: CcPluginItem[];
outputStyles: CcPluginItem[];
};
/** Parsed `plugin.json` fields; null if the plugin has no manifest. */
pluginJson: {
name?: string;