@kolisachint/hoocode-agent 0.5.6 → 0.5.8
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/CHANGELOG.md +69 -0
- package/dist/{modes/interactive → core}/brand.d.ts +6 -0
- package/dist/core/brand.d.ts.map +1 -0
- package/dist/{modes/interactive → core}/brand.js +6 -0
- package/dist/core/brand.js.map +1 -0
- package/dist/core/extensions/plugins/listing.d.ts +34 -0
- package/dist/core/extensions/plugins/listing.d.ts.map +1 -0
- package/dist/core/extensions/plugins/listing.js +69 -0
- package/dist/core/extensions/plugins/listing.js.map +1 -0
- package/dist/core/extensions/types.d.ts +9 -0
- package/dist/core/extensions/types.d.ts.map +1 -1
- package/dist/core/extensions/types.js.map +1 -1
- package/dist/core/format-list.d.ts +101 -0
- package/dist/core/format-list.d.ts.map +1 -0
- package/dist/core/format-list.js +171 -0
- package/dist/core/format-list.js.map +1 -0
- package/dist/core/skills.d.ts +9 -0
- package/dist/core/skills.d.ts.map +1 -1
- package/dist/core/skills.js +9 -0
- package/dist/core/skills.js.map +1 -1
- package/dist/core/tools/plugins.d.ts.map +1 -1
- package/dist/core/tools/plugins.js +93 -28
- package/dist/core/tools/plugins.js.map +1 -1
- package/dist/extensions/core/marketplace.d.ts.map +1 -1
- package/dist/extensions/core/marketplace.js +61 -13
- package/dist/extensions/core/marketplace.js.map +1 -1
- package/dist/modes/interactive/components/footer.d.ts.map +1 -1
- package/dist/modes/interactive/components/footer.js +10 -3
- package/dist/modes/interactive/components/footer.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +24 -3
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/dist/modes/interactive/resource-display.d.ts +2 -0
- package/dist/modes/interactive/resource-display.d.ts.map +1 -1
- package/dist/modes/interactive/resource-display.js +10 -7
- package/dist/modes/interactive/resource-display.js.map +1 -1
- package/dist/modes/interactive/theme/theme-schema.json +16 -8
- package/dist/modes/interactive/theme/theme.d.ts +6 -2
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/dist/modes/interactive/theme/theme.js +15 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -1
- package/dist/modes/interactive/theme/vox-dark.json +97 -0
- package/dist/modes/interactive/theme/vox-light.json +101 -0
- package/docs/themes.md +65 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
- package/dist/modes/interactive/brand.d.ts.map +0 -1
- package/dist/modes/interactive/brand.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,74 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.8] - 2026-08-12
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- **Capability listings share one layout.** `/plugin list`,
|
|
8
|
+
`/plugin marketplace list`, `SearchPlugins` and `ListPlugins` each built their
|
|
9
|
+
own `name [a, b] — description` line, so the same plugin printed three ways
|
|
10
|
+
and the model's view never matched the user's. They now go through a single
|
|
11
|
+
row formatter (`core/format-list.ts`, `core/extensions/plugins/listing.ts`):
|
|
12
|
+
aligned name column, facts beside it, description wrapped with a hanging
|
|
13
|
+
indent instead of restarting at column 0, and plugins grouped by the
|
|
14
|
+
marketplace that offers them. `/plugin list` marks what is already installed.
|
|
15
|
+
- `SearchPlugins` takes a `limit` (default 10). With no query it previously
|
|
16
|
+
returned every plugin in every registered marketplace — dozens of entries,
|
|
17
|
+
several lines each — into the chat and the context.
|
|
18
|
+
- The five plugin tools define `renderResult`, as every other built-in tool
|
|
19
|
+
already did. Results are bounded to 20 lines with an expand hint rather than
|
|
20
|
+
dumped whole.
|
|
21
|
+
- `ListPlugins` reports the manifest format its description already promised,
|
|
22
|
+
counts `themes` and `providers` as capabilities (a plugin shipping only a
|
|
23
|
+
theme reported none), and carries the plugin description through.
|
|
24
|
+
- `[Agents]` in the startup summary is one line per agent, truncated to the
|
|
25
|
+
terminal, instead of a 200-character summary wrapping into a paragraph.
|
|
26
|
+
- `brand.ts` moved to `core/` so non-interactive surfaces can label capability
|
|
27
|
+
classes; added a `marketplaces` glyph.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Status messages carrying their own colors are no longer wrapped in a blanket
|
|
32
|
+
dim. `theme.fg` closes with `\x1b[39m` (reset, not restore), so the dim died
|
|
33
|
+
at the first inner span and — because the wrapper carries that state across
|
|
34
|
+
newlines — every line after it lost the dim too.
|
|
35
|
+
- `InstallPlugin` no longer erases its own announcement. It notified "installing
|
|
36
|
+
X because Y", then notified the outcome; consecutive info notifications
|
|
37
|
+
coalesce into one chat line, so the second replaced the first and the
|
|
38
|
+
transparency the tool guidelines require never reached the screen.
|
|
39
|
+
- Truncation in the new listings does not emit escape codes. The TUI's
|
|
40
|
+
`truncateToWidth` appends `\x1b[0m` even for plain input — a full reset that
|
|
41
|
+
would both leak into model-facing text and clear any enclosing style mid-line.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
|
|
45
|
+
- `ExtensionUIContext.columns` exposes the terminal width to extensions, so a
|
|
46
|
+
listing can wrap to the actual terminal. Optional, and undefined outside a
|
|
47
|
+
terminal (RPC, print), which is the signal to emit unwrapped text rather than
|
|
48
|
+
wrap to someone else's width.
|
|
49
|
+
|
|
50
|
+
## [0.5.7] - 2026-08-12
|
|
51
|
+
|
|
52
|
+
### Added
|
|
53
|
+
|
|
54
|
+
- **`vox-dark` and `vox-light` themes** — explanatory-journalism styling: one
|
|
55
|
+
loud yellow used sparingly against a newsprint-quiet palette. The yellow is
|
|
56
|
+
reserved for four roles (`accent`, `mdHeading`, `selectedBg`, `borderAccent`)
|
|
57
|
+
and never carries a status, so `warning` is orange rather than the usual
|
|
58
|
+
yellow — a yellow `◐` or context gauge stops reading as a signal when yellow
|
|
59
|
+
is already the accent. Both are AA, not AAA, and are not covered by
|
|
60
|
+
`test/theme-contrast.test.ts`, but they are held to the same standards the
|
|
61
|
+
default `light` theme gained in 0.5.6: every text token clears 4.5:1 on every
|
|
62
|
+
surface it paints, rules and inactive chrome clear 2.8:1, and no two tokens
|
|
63
|
+
that mean different things sit closer than ΔE 11. Rules stay neutral rather
|
|
64
|
+
than saturated — a separator carries no meaning through hue — which is the one
|
|
65
|
+
deliberate departure, documented in `docs/themes.md`.
|
|
66
|
+
- Optional `brandBg` / `brandText` theme tokens, honored only as a pair, paint
|
|
67
|
+
the footer's brand mark as a filled chip instead of accent-colored text.
|
|
68
|
+
Themes that omit them are unchanged. Added for light themes, where a brand
|
|
69
|
+
hue vivid enough to be recognizable is rarely legible as text on a light
|
|
70
|
+
canvas. `vox-light` is the only built-in that sets them.
|
|
71
|
+
|
|
3
72
|
## [0.5.6] - 2026-08-12
|
|
4
73
|
|
|
5
74
|
### Changed
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* Glyphs are chosen to be single terminal cell, widely supported, and distinct
|
|
12
12
|
* from the task-panel's own status/owner glyphs (◐ ◆ ◇ ▸ ⧉ ✓ ✗ ○) so the two
|
|
13
13
|
* vocabularies never read as the same signal.
|
|
14
|
+
*
|
|
15
|
+
* This lives in `core/` rather than `modes/interactive/` because the `/plugin`
|
|
16
|
+
* command extension labels the same capability classes, and extensions must not
|
|
17
|
+
* reach into a mode's internals. The module is pure data — no theme, no
|
|
18
|
+
* rendering — so it is safe for every surface.
|
|
14
19
|
*/
|
|
15
20
|
/** The HooCode mark — a filled hexagon, rendered in the accent colour. */
|
|
16
21
|
export declare const BRAND_MARK = "\u2B22";
|
|
@@ -25,6 +30,7 @@ export declare const CATEGORY_GLYPH: {
|
|
|
25
30
|
readonly agents: "◈";
|
|
26
31
|
readonly mcp: "⧉";
|
|
27
32
|
readonly plugins: "⬡";
|
|
33
|
+
readonly marketplaces: "⊞";
|
|
28
34
|
readonly themes: "◒";
|
|
29
35
|
readonly context: "❯";
|
|
30
36
|
readonly extensions: "⊹";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.d.ts","sourceRoot":"","sources":["../../src/core/brand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,4EAA0E;AAC1E,eAAO,MAAM,UAAU,WAAM,CAAC;AAE9B;;;;GAIG;AACH,eAAO,MAAM,cAAc;;;;;;;;;;CAUjB,CAAC;AAEX,MAAM,MAAM,WAAW,GAAG,MAAM,OAAO,cAAc,CAAC;AAEtD,iEAAiE;AACjE,eAAO,MAAM,WAAW,WAAM,CAAC;AAE/B,yCAAyC;AACzC,eAAO,MAAM,gBAAgB,WAAM,CAAC","sourcesContent":["/**\n * HooCode brand identity — the single source of truth for the product mark and\n * the glyph vocabulary the interactive surfaces share.\n *\n * The brand *colour* is the theme's `accent` token (see theme/dark.json,\n * theme/light.json) so it stays theme-aware and swappable; this module owns the\n * non-colour identity: the mark and the category glyphs used by the footer,\n * the startup resource summary, and anywhere a capability class is labelled.\n * Keeping them here means a future rebrand touches one file, not twenty.\n *\n * Glyphs are chosen to be single terminal cell, widely supported, and distinct\n * from the task-panel's own status/owner glyphs (◐ ◆ ◇ ▸ ⧉ ✓ ✗ ○) so the two\n * vocabularies never read as the same signal.\n *\n * This lives in `core/` rather than `modes/interactive/` because the `/plugin`\n * command extension labels the same capability classes, and extensions must not\n * reach into a mode's internals. The module is pure data — no theme, no\n * rendering — so it is safe for every surface.\n */\n\n/** The HooCode mark — a filled hexagon, rendered in the accent colour. */\nexport const BRAND_MARK = \"⬢\";\n\n/**\n * Glyphs for the capability classes a session can load. Used by the startup\n * \"resources ready\" summary and reusable anywhere a class needs a label. Each is\n * a single cell so counts and columns stay aligned.\n */\nexport const CATEGORY_GLYPH = {\n\tskills: \"✦\",\n\tcommands: \"⌘\",\n\tagents: \"◈\",\n\tmcp: \"⧉\",\n\tplugins: \"⬡\",\n\tmarketplaces: \"⊞\",\n\tthemes: \"◒\",\n\tcontext: \"❯\",\n\textensions: \"⊹\",\n} as const;\n\nexport type CategoryKey = keyof typeof CATEGORY_GLYPH;\n\n/** A soft dot separator used between footer/summary segments. */\nexport const SEGMENT_SEP = \"·\";\n\n/** Fork glyph preceding a git branch. */\nexport const GIT_BRANCH_GLYPH = \"⑂\";\n"]}
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* Glyphs are chosen to be single terminal cell, widely supported, and distinct
|
|
12
12
|
* from the task-panel's own status/owner glyphs (◐ ◆ ◇ ▸ ⧉ ✓ ✗ ○) so the two
|
|
13
13
|
* vocabularies never read as the same signal.
|
|
14
|
+
*
|
|
15
|
+
* This lives in `core/` rather than `modes/interactive/` because the `/plugin`
|
|
16
|
+
* command extension labels the same capability classes, and extensions must not
|
|
17
|
+
* reach into a mode's internals. The module is pure data — no theme, no
|
|
18
|
+
* rendering — so it is safe for every surface.
|
|
14
19
|
*/
|
|
15
20
|
/** The HooCode mark — a filled hexagon, rendered in the accent colour. */
|
|
16
21
|
export const BRAND_MARK = "⬢";
|
|
@@ -25,6 +30,7 @@ export const CATEGORY_GLYPH = {
|
|
|
25
30
|
agents: "◈",
|
|
26
31
|
mcp: "⧉",
|
|
27
32
|
plugins: "⬡",
|
|
33
|
+
marketplaces: "⊞",
|
|
28
34
|
themes: "◒",
|
|
29
35
|
context: "❯",
|
|
30
36
|
extensions: "⊹",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand.js","sourceRoot":"","sources":["../../src/core/brand.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,4EAA0E;AAC1E,MAAM,CAAC,MAAM,UAAU,GAAG,KAAG,CAAC;AAE9B;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAG;IAC7B,MAAM,EAAE,KAAG;IACX,QAAQ,EAAE,KAAG;IACb,MAAM,EAAE,KAAG;IACX,GAAG,EAAE,KAAG;IACR,OAAO,EAAE,KAAG;IACZ,YAAY,EAAE,KAAG;IACjB,MAAM,EAAE,KAAG;IACX,OAAO,EAAE,KAAG;IACZ,UAAU,EAAE,KAAG;CACN,CAAC;AAIX,iEAAiE;AACjE,MAAM,CAAC,MAAM,WAAW,GAAG,IAAG,CAAC;AAE/B,yCAAyC;AACzC,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAG,CAAC","sourcesContent":["/**\n * HooCode brand identity — the single source of truth for the product mark and\n * the glyph vocabulary the interactive surfaces share.\n *\n * The brand *colour* is the theme's `accent` token (see theme/dark.json,\n * theme/light.json) so it stays theme-aware and swappable; this module owns the\n * non-colour identity: the mark and the category glyphs used by the footer,\n * the startup resource summary, and anywhere a capability class is labelled.\n * Keeping them here means a future rebrand touches one file, not twenty.\n *\n * Glyphs are chosen to be single terminal cell, widely supported, and distinct\n * from the task-panel's own status/owner glyphs (◐ ◆ ◇ ▸ ⧉ ✓ ✗ ○) so the two\n * vocabularies never read as the same signal.\n *\n * This lives in `core/` rather than `modes/interactive/` because the `/plugin`\n * command extension labels the same capability classes, and extensions must not\n * reach into a mode's internals. The module is pure data — no theme, no\n * rendering — so it is safe for every surface.\n */\n\n/** The HooCode mark — a filled hexagon, rendered in the accent colour. */\nexport const BRAND_MARK = \"⬢\";\n\n/**\n * Glyphs for the capability classes a session can load. Used by the startup\n * \"resources ready\" summary and reusable anywhere a class needs a label. Each is\n * a single cell so counts and columns stay aligned.\n */\nexport const CATEGORY_GLYPH = {\n\tskills: \"✦\",\n\tcommands: \"⌘\",\n\tagents: \"◈\",\n\tmcp: \"⧉\",\n\tplugins: \"⬡\",\n\tmarketplaces: \"⊞\",\n\tthemes: \"◒\",\n\tcontext: \"❯\",\n\textensions: \"⊹\",\n} as const;\n\nexport type CategoryKey = keyof typeof CATEGORY_GLYPH;\n\n/** A soft dot separator used between footer/summary segments. */\nexport const SEGMENT_SEP = \"·\";\n\n/** Fork glyph preceding a git branch. */\nexport const GIT_BRANCH_GLYPH = \"⑂\";\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row shapes for the plugin listings, shared by every surface that prints one.
|
|
3
|
+
*
|
|
4
|
+
* `/plugin list`, `SearchPlugins` and `ListPlugins` describe the same objects,
|
|
5
|
+
* and each used to build its own `name [a, b] — description` string, so the same
|
|
6
|
+
* plugin printed three ways and the model's view never quite matched the user's.
|
|
7
|
+
* They all go through here now; only the styling and the width differ.
|
|
8
|
+
*/
|
|
9
|
+
import type { ListGroup, ListRow } from "../../format-list.js";
|
|
10
|
+
import type { AvailablePlugin } from "./install.js";
|
|
11
|
+
import type { NormalizedPlugin } from "./manifest.js";
|
|
12
|
+
/** Human-readable source for an available plugin (a URL, a path, an npm spec). */
|
|
13
|
+
export declare function formatPluginSource(source: AvailablePlugin["source"]): string;
|
|
14
|
+
/**
|
|
15
|
+
* Capability classes a plugin actually ships.
|
|
16
|
+
*
|
|
17
|
+
* Covers every capability `NormalizedPlugin` can carry — themes and providers
|
|
18
|
+
* included, which the old `ListPlugins` inline list omitted, so a plugin that
|
|
19
|
+
* shipped only a theme or only a provider reported no capabilities at all.
|
|
20
|
+
*/
|
|
21
|
+
export declare function pluginCapabilities(plugin: NormalizedPlugin): string[];
|
|
22
|
+
/**
|
|
23
|
+
* Available plugins, grouped by the marketplace offering them.
|
|
24
|
+
*
|
|
25
|
+
* Grouping is what makes provenance legible: a flat list cannot answer "where
|
|
26
|
+
* would this come from if I installed it", which is the one question that
|
|
27
|
+
* matters at a trust boundary. Marketplace order is preserved from the caller.
|
|
28
|
+
*/
|
|
29
|
+
export declare function availablePluginGroups(plugins: readonly AvailablePlugin[], options?: {
|
|
30
|
+
installed?: ReadonlySet<string>;
|
|
31
|
+
}): ListGroup[];
|
|
32
|
+
/** Installed plugins as rows: id@version, platforms, format, capabilities. */
|
|
33
|
+
export declare function installedPluginRows(plugins: readonly NormalizedPlugin[]): ListRow[];
|
|
34
|
+
//# sourceMappingURL=listing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listing.d.ts","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/listing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEtD,kFAAkF;AAClF,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,CAAC,QAAQ,CAAC,GAAG,MAAM,CAI5E;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,gBAAgB,GAAG,MAAM,EAAE,CAUrE;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CACpC,OAAO,EAAE,SAAS,eAAe,EAAE,EACnC,OAAO,GAAE;IAAE,SAAS,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAO,GAC/C,SAAS,EAAE,CAab;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,SAAS,gBAAgB,EAAE,GAAG,OAAO,EAAE,CAWnF","sourcesContent":["/**\n * Row shapes for the plugin listings, shared by every surface that prints one.\n *\n * `/plugin list`, `SearchPlugins` and `ListPlugins` describe the same objects,\n * and each used to build its own `name [a, b] — description` string, so the same\n * plugin printed three ways and the model's view never quite matched the user's.\n * They all go through here now; only the styling and the width differ.\n */\n\nimport type { ListGroup, ListRow } from \"../../format-list.js\";\nimport type { AvailablePlugin } from \"./install.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\n\n/** Human-readable source for an available plugin (a URL, a path, an npm spec). */\nexport function formatPluginSource(source: AvailablePlugin[\"source\"]): string {\n\tif (typeof source === \"string\") return source;\n\tif (source.source === \"url\") return source.url;\n\treturn `${source.url}/${source.path}`;\n}\n\n/**\n * Capability classes a plugin actually ships.\n *\n * Covers every capability `NormalizedPlugin` can carry — themes and providers\n * included, which the old `ListPlugins` inline list omitted, so a plugin that\n * shipped only a theme or only a provider reported no capabilities at all.\n */\nexport function pluginCapabilities(plugin: NormalizedPlugin): string[] {\n\treturn [\n\t\tplugin.skillsDir && \"skills\",\n\t\tplugin.commandsDir && \"commands\",\n\t\tplugin.agentsDir && \"agents\",\n\t\tplugin.themesDir && \"themes\",\n\t\tplugin.hooks && \"hooks\",\n\t\tplugin.mcpServers && \"mcp\",\n\t\tplugin.providers?.length && \"providers\",\n\t].filter((value): value is string => typeof value === \"string\");\n}\n\n/**\n * Available plugins, grouped by the marketplace offering them.\n *\n * Grouping is what makes provenance legible: a flat list cannot answer \"where\n * would this come from if I installed it\", which is the one question that\n * matters at a trust boundary. Marketplace order is preserved from the caller.\n */\nexport function availablePluginGroups(\n\tplugins: readonly AvailablePlugin[],\n\toptions: { installed?: ReadonlySet<string> } = {},\n): ListGroup[] {\n\tconst groups = new Map<string, ListRow[]>();\n\tfor (const plugin of plugins) {\n\t\tconst rows = groups.get(plugin.marketplaceName) ?? [];\n\t\trows.push({\n\t\t\tname: plugin.name,\n\t\t\tmarker: options.installed?.has(plugin.name) ? \"✓\" : undefined,\n\t\t\tfacts: [plugin.supportPlatform.join(\"/\"), plugin.sourceKind],\n\t\t\tdetail: plugin.description ?? formatPluginSource(plugin.source),\n\t\t});\n\t\tgroups.set(plugin.marketplaceName, rows);\n\t}\n\treturn Array.from(groups, ([title, rows]) => ({ title, rows }));\n}\n\n/** Installed plugins as rows: id@version, platforms, format, capabilities. */\nexport function installedPluginRows(plugins: readonly NormalizedPlugin[]): ListRow[] {\n\treturn plugins.map((plugin) => {\n\t\tconst capabilities = pluginCapabilities(plugin);\n\t\treturn {\n\t\t\tname: `${plugin.id}${plugin.version ? `@${plugin.version}` : \"\"}`,\n\t\t\t// The manifest format is parenthesised because it is usually the same\n\t\t\t// word as the platform (\"claude/github · claude\" reads as a stutter).\n\t\t\tfacts: [plugin.supportPlatform.join(\"/\"), `(${plugin.format})`, capabilities.join(\", \") || \"no capabilities\"],\n\t\t\tdetail: plugin.description,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Row shapes for the plugin listings, shared by every surface that prints one.
|
|
3
|
+
*
|
|
4
|
+
* `/plugin list`, `SearchPlugins` and `ListPlugins` describe the same objects,
|
|
5
|
+
* and each used to build its own `name [a, b] — description` string, so the same
|
|
6
|
+
* plugin printed three ways and the model's view never quite matched the user's.
|
|
7
|
+
* They all go through here now; only the styling and the width differ.
|
|
8
|
+
*/
|
|
9
|
+
/** Human-readable source for an available plugin (a URL, a path, an npm spec). */
|
|
10
|
+
export function formatPluginSource(source) {
|
|
11
|
+
if (typeof source === "string")
|
|
12
|
+
return source;
|
|
13
|
+
if (source.source === "url")
|
|
14
|
+
return source.url;
|
|
15
|
+
return `${source.url}/${source.path}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Capability classes a plugin actually ships.
|
|
19
|
+
*
|
|
20
|
+
* Covers every capability `NormalizedPlugin` can carry — themes and providers
|
|
21
|
+
* included, which the old `ListPlugins` inline list omitted, so a plugin that
|
|
22
|
+
* shipped only a theme or only a provider reported no capabilities at all.
|
|
23
|
+
*/
|
|
24
|
+
export function pluginCapabilities(plugin) {
|
|
25
|
+
return [
|
|
26
|
+
plugin.skillsDir && "skills",
|
|
27
|
+
plugin.commandsDir && "commands",
|
|
28
|
+
plugin.agentsDir && "agents",
|
|
29
|
+
plugin.themesDir && "themes",
|
|
30
|
+
plugin.hooks && "hooks",
|
|
31
|
+
plugin.mcpServers && "mcp",
|
|
32
|
+
plugin.providers?.length && "providers",
|
|
33
|
+
].filter((value) => typeof value === "string");
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Available plugins, grouped by the marketplace offering them.
|
|
37
|
+
*
|
|
38
|
+
* Grouping is what makes provenance legible: a flat list cannot answer "where
|
|
39
|
+
* would this come from if I installed it", which is the one question that
|
|
40
|
+
* matters at a trust boundary. Marketplace order is preserved from the caller.
|
|
41
|
+
*/
|
|
42
|
+
export function availablePluginGroups(plugins, options = {}) {
|
|
43
|
+
const groups = new Map();
|
|
44
|
+
for (const plugin of plugins) {
|
|
45
|
+
const rows = groups.get(plugin.marketplaceName) ?? [];
|
|
46
|
+
rows.push({
|
|
47
|
+
name: plugin.name,
|
|
48
|
+
marker: options.installed?.has(plugin.name) ? "✓" : undefined,
|
|
49
|
+
facts: [plugin.supportPlatform.join("/"), plugin.sourceKind],
|
|
50
|
+
detail: plugin.description ?? formatPluginSource(plugin.source),
|
|
51
|
+
});
|
|
52
|
+
groups.set(plugin.marketplaceName, rows);
|
|
53
|
+
}
|
|
54
|
+
return Array.from(groups, ([title, rows]) => ({ title, rows }));
|
|
55
|
+
}
|
|
56
|
+
/** Installed plugins as rows: id@version, platforms, format, capabilities. */
|
|
57
|
+
export function installedPluginRows(plugins) {
|
|
58
|
+
return plugins.map((plugin) => {
|
|
59
|
+
const capabilities = pluginCapabilities(plugin);
|
|
60
|
+
return {
|
|
61
|
+
name: `${plugin.id}${plugin.version ? `@${plugin.version}` : ""}`,
|
|
62
|
+
// The manifest format is parenthesised because it is usually the same
|
|
63
|
+
// word as the platform ("claude/github · claude" reads as a stutter).
|
|
64
|
+
facts: [plugin.supportPlatform.join("/"), `(${plugin.format})`, capabilities.join(", ") || "no capabilities"],
|
|
65
|
+
detail: plugin.description,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=listing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"listing.js","sourceRoot":"","sources":["../../../../src/core/extensions/plugins/listing.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,kFAAkF;AAClF,MAAM,UAAU,kBAAkB,CAAC,MAAiC,EAAU;IAC7E,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,IAAI,MAAM,CAAC,MAAM,KAAK,KAAK;QAAE,OAAO,MAAM,CAAC,GAAG,CAAC;IAC/C,OAAO,GAAG,MAAM,CAAC,GAAG,IAAI,MAAM,CAAC,IAAI,EAAE,CAAC;AAAA,CACtC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,MAAwB,EAAY;IACtE,OAAO;QACN,MAAM,CAAC,SAAS,IAAI,QAAQ;QAC5B,MAAM,CAAC,WAAW,IAAI,UAAU;QAChC,MAAM,CAAC,SAAS,IAAI,QAAQ;QAC5B,MAAM,CAAC,SAAS,IAAI,QAAQ;QAC5B,MAAM,CAAC,KAAK,IAAI,OAAO;QACvB,MAAM,CAAC,UAAU,IAAI,KAAK;QAC1B,MAAM,CAAC,SAAS,EAAE,MAAM,IAAI,WAAW;KACvC,CAAC,MAAM,CAAC,CAAC,KAAK,EAAmB,EAAE,CAAC,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC;AAAA,CAChE;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CACpC,OAAmC,EACnC,OAAO,GAAwC,EAAE,EACnC;IACd,MAAM,MAAM,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC5C,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,EAAE,CAAC;QACtD,IAAI,CAAC,IAAI,CAAC;YACT,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,MAAM,EAAE,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAG,CAAC,CAAC,CAAC,SAAS;YAC7D,KAAK,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;YAC5D,MAAM,EAAE,MAAM,CAAC,WAAW,IAAI,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC;SAC/D,CAAC,CAAC;QACH,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,KAAK,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;AAAA,CAChE;AAED,8EAA8E;AAC9E,MAAM,UAAU,mBAAmB,CAAC,OAAoC,EAAa;IACpF,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC;QAC9B,MAAM,YAAY,GAAG,kBAAkB,CAAC,MAAM,CAAC,CAAC;QAChD,OAAO;YACN,IAAI,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE;YACjE,sEAAsE;YACtE,uEAAsE;YACtE,KAAK,EAAE,CAAC,MAAM,CAAC,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,MAAM,CAAC,MAAM,GAAG,EAAE,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC;YAC7G,MAAM,EAAE,MAAM,CAAC,WAAW;SAC1B,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACH","sourcesContent":["/**\n * Row shapes for the plugin listings, shared by every surface that prints one.\n *\n * `/plugin list`, `SearchPlugins` and `ListPlugins` describe the same objects,\n * and each used to build its own `name [a, b] — description` string, so the same\n * plugin printed three ways and the model's view never quite matched the user's.\n * They all go through here now; only the styling and the width differ.\n */\n\nimport type { ListGroup, ListRow } from \"../../format-list.js\";\nimport type { AvailablePlugin } from \"./install.js\";\nimport type { NormalizedPlugin } from \"./manifest.js\";\n\n/** Human-readable source for an available plugin (a URL, a path, an npm spec). */\nexport function formatPluginSource(source: AvailablePlugin[\"source\"]): string {\n\tif (typeof source === \"string\") return source;\n\tif (source.source === \"url\") return source.url;\n\treturn `${source.url}/${source.path}`;\n}\n\n/**\n * Capability classes a plugin actually ships.\n *\n * Covers every capability `NormalizedPlugin` can carry — themes and providers\n * included, which the old `ListPlugins` inline list omitted, so a plugin that\n * shipped only a theme or only a provider reported no capabilities at all.\n */\nexport function pluginCapabilities(plugin: NormalizedPlugin): string[] {\n\treturn [\n\t\tplugin.skillsDir && \"skills\",\n\t\tplugin.commandsDir && \"commands\",\n\t\tplugin.agentsDir && \"agents\",\n\t\tplugin.themesDir && \"themes\",\n\t\tplugin.hooks && \"hooks\",\n\t\tplugin.mcpServers && \"mcp\",\n\t\tplugin.providers?.length && \"providers\",\n\t].filter((value): value is string => typeof value === \"string\");\n}\n\n/**\n * Available plugins, grouped by the marketplace offering them.\n *\n * Grouping is what makes provenance legible: a flat list cannot answer \"where\n * would this come from if I installed it\", which is the one question that\n * matters at a trust boundary. Marketplace order is preserved from the caller.\n */\nexport function availablePluginGroups(\n\tplugins: readonly AvailablePlugin[],\n\toptions: { installed?: ReadonlySet<string> } = {},\n): ListGroup[] {\n\tconst groups = new Map<string, ListRow[]>();\n\tfor (const plugin of plugins) {\n\t\tconst rows = groups.get(plugin.marketplaceName) ?? [];\n\t\trows.push({\n\t\t\tname: plugin.name,\n\t\t\tmarker: options.installed?.has(plugin.name) ? \"✓\" : undefined,\n\t\t\tfacts: [plugin.supportPlatform.join(\"/\"), plugin.sourceKind],\n\t\t\tdetail: plugin.description ?? formatPluginSource(plugin.source),\n\t\t});\n\t\tgroups.set(plugin.marketplaceName, rows);\n\t}\n\treturn Array.from(groups, ([title, rows]) => ({ title, rows }));\n}\n\n/** Installed plugins as rows: id@version, platforms, format, capabilities. */\nexport function installedPluginRows(plugins: readonly NormalizedPlugin[]): ListRow[] {\n\treturn plugins.map((plugin) => {\n\t\tconst capabilities = pluginCapabilities(plugin);\n\t\treturn {\n\t\t\tname: `${plugin.id}${plugin.version ? `@${plugin.version}` : \"\"}`,\n\t\t\t// The manifest format is parenthesised because it is usually the same\n\t\t\t// word as the platform (\"claude/github · claude\" reads as a stutter).\n\t\t\tfacts: [plugin.supportPlatform.join(\"/\"), `(${plugin.format})`, capabilities.join(\", \") || \"no capabilities\"],\n\t\t\tdetail: plugin.description,\n\t\t};\n\t});\n}\n"]}
|
|
@@ -99,6 +99,15 @@ export interface ExtensionUIContext {
|
|
|
99
99
|
askOptions(questions: AskQuestion[], opts?: ExtensionUIDialogOptions): Promise<string[] | undefined>;
|
|
100
100
|
/** Show a notification to the user. */
|
|
101
101
|
notify(message: string, type?: "info" | "warning" | "error"): void;
|
|
102
|
+
/**
|
|
103
|
+
* Terminal width in columns, when the surface has one.
|
|
104
|
+
*
|
|
105
|
+
* Undefined outside a terminal (RPC, print, headless), which is the signal to
|
|
106
|
+
* emit unwrapped text and let the consumer lay it out — a listing pre-wrapped
|
|
107
|
+
* to someone else's terminal is worse than no wrapping at all. Optional so
|
|
108
|
+
* third-party implementations of this interface keep compiling.
|
|
109
|
+
*/
|
|
110
|
+
readonly columns?: number;
|
|
102
111
|
/** Listen to raw terminal input (interactive mode only). Returns an unsubscribe function. */
|
|
103
112
|
onTerminalInput(handler: TerminalInputHandler): () => void;
|
|
104
113
|
/** Set status text in the footer/status bar. Pass undefined to clear. */
|