feat(dsh-plugin-browserskill): publish the BSK skill through ctx.skills

The plugin exposed only tools; the BrowserSkill agent skill never reached
the harness's <available_skills> catalog. Register it as an embedded runtime
skill (progressive disclosure: catalog entry resident, body on skill-tool
invocation) via the official ctx.skills seam — silent no-op when the
composition lacks it.

Content is build-time assembled so there is one source of truth:
skill/prelude.md (committed, dsh-specific: tool<->CLI map, owned-session
semantics, plugin-only overrides) + the canonical repo-root skill/SKILL.md
verbatim (the same file crates/bsk-cli/build.rs mirrors; frontmatter
stripped, name/description supplied by the registration). Generated into
src/skill-content.generated.ts (gitignored) — registration and every
pre-step snapshot are pure in-memory reads: no disk, no process, no daemon.

Scripts: build:skill chained into build/test/typecheck (same pattern as
build:css); biome excludes the generated artifacts. Tests: +3 (catalog
content, in-memory weight, silent degradation). 116/116 green.
This commit is contained in:
BB-fat
2026-08-14 16:17:54 +00:00
parent a4e668542d
commit 4d1dcd122f
9 changed files with 234 additions and 4 deletions
@@ -17,6 +17,7 @@ import { registerObservationRoutes } from "./observation-http";
import { KeyedExecutor } from "./queue";
import { type BskRunner, createBskRunner } from "./runner";
import { SessionRegistry } from "./sessions";
import { registerBskSkill } from "./skill";
import { type PluginConfig, registerTools } from "./tools";
export const name = "dsh-plugin-browserskill";
@@ -80,6 +81,10 @@ export function apply(
});
registerTools({ ctx, runner, registry, config: resolved, observation, queue });
// Progressive disclosure of the BSK agent skill (catalog entry resident,
// body on demand) through the official skill seam; silent no-op when the
// composition lacks it.
const unregisterSkill = registerBskSkill(ctx);
// Route registration rides ctx.inject: the webServer service may be provided
// AFTER this plugin loads, and in headless compositions it never appears (the
// callback simply never runs, leaving the rest of the plugin unaffected).
@@ -110,6 +115,7 @@ export function apply(
// are swallowed so one stale handle cannot abort the rest.
ctx.effect(() => {
return () => {
unregisterSkill();
removeRoutes();
observation.dispose();
runner.killAll();