diff --git a/packages/dsh-plugin-browserskill/scripts/build-skill-content.mjs b/packages/dsh-plugin-browserskill/scripts/build-skill-content.mjs index 8348fba..47042f4 100644 --- a/packages/dsh-plugin-browserskill/scripts/build-skill-content.mjs +++ b/packages/dsh-plugin-browserskill/scripts/build-skill-content.mjs @@ -1,8 +1,8 @@ -// Assemble the plugin's skill content: the dsh-specific prelude (committed in -// this package at skill/prelude.md) followed VERBATIM by the canonical CLI -// skill body (repo-root skill/SKILL.md — the same source crates/bsk-cli's -// build.rs mirrors, so the two can never drift apart). The original's YAML -// frontmatter is stripped: name/description are supplied by the registration. +// Assemble the plugin's skill content: a dsh-specific routing prelude, the +// canonical CLI skill body verbatim (repo-root skill/SKILL.md — the same source +// crates/bsk-cli's build.rs mirrors), then a dsh routing reminder postlude. The +// original's YAML frontmatter is stripped: name/description are supplied by the +// registration. // Output: src/skill-content.generated.ts (gitignored, built before tsdown and // before vitest) so the markdown ships inlined in the bundle and registration // is a pure in-memory read — never a per-step disk access. @@ -14,6 +14,7 @@ const here = dirname(fileURLToPath(import.meta.url)); const pkg = join(here, ".."); const prelude = readFileSync(join(pkg, "skill", "prelude.md"), "utf8"); const upstream = readFileSync(join(pkg, "..", "..", "skill", "SKILL.md"), "utf8"); +const postlude = readFileSync(join(pkg, "skill", "postlude.md"), "utf8"); function stripFrontmatter(markdown) { if (!markdown.startsWith("---\n")) return markdown; @@ -23,16 +24,16 @@ function stripFrontmatter(markdown) { const NAME = "browser-skill"; const DESCRIPTION = - "Browser automation against the user's logged-in Chromium through this plugin's browser_* " + + "Browser automation against the user's logged-in Chromium through this plugin's injected browser_* tools; never invoke bsk through bash or shell. " + "tools (bsk engine): visit and read pages, fill forms, click through flows, scrape data, " + "smoke-test UI changes. Use when a task needs a real browser; requires the bsk CLI and the " + "browser-skill extension connected."; -const content = `${prelude.trim()}\n\n${stripFrontmatter(upstream).trim()}\n`; +const content = `${prelude.trim()}\n\n${stripFrontmatter(upstream).trim()}\n\n${postlude.trim()}\n`; const banner = "// GENERATED by scripts/build-skill-content.mjs — do not edit.\n" + - "// Sources: skill/prelude.md (this package) + ../../../skill/SKILL.md (repo root).\n"; + "// Sources: skill/prelude.md + ../../../skill/SKILL.md + skill/postlude.md.\n"; writeFileSync( join(pkg, "src", "skill-content.generated.ts"), diff --git a/packages/dsh-plugin-browserskill/skill/postlude.md b/packages/dsh-plugin-browserskill/skill/postlude.md new file mode 100644 index 0000000..0d4d16c --- /dev/null +++ b/packages/dsh-plugin-browserskill/skill/postlude.md @@ -0,0 +1,10 @@ +--- + +## DSH routing reminder + +The CLI-oriented reference above describes behavior, not the execution mechanism in +DeepSeek Harness. For every browser operation, call the injected `browser_*` model +tool. Do not translate these examples into `bash` commands and do not execute `bsk` +directly; the plugin invokes `bsk` internally. In particular, always begin with +`browser_session_start`, pass its returned session id to subsequent `browser_*` +tools, and finish with `browser_session_stop`. diff --git a/packages/dsh-plugin-browserskill/skill/prelude.md b/packages/dsh-plugin-browserskill/skill/prelude.md index ac1e736..5aa3a3f 100644 --- a/packages/dsh-plugin-browserskill/skill/prelude.md +++ b/packages/dsh-plugin-browserskill/skill/prelude.md @@ -1,9 +1,25 @@ # browser-skill (dsh edition) +## DSH tool-routing requirement (highest priority) + +This skill is running inside DeepSeek Harness, where the plugin has injected +structured `browser_*` model tools. **Always call those injected `browser_*` tools +for browser work. Never use `bash`, `shell`, `exec`, or another tool to invoke the +`bsk` CLI directly.** The CLI command names shown below are only conceptual +mappings for understanding the tools; they are not instructions to run commands. +If a `browser_*` tool is available, using the CLI directly is incorrect because it +bypasses plugin session ownership, observation UI, tool cards, and cleanup. + +Use the injected tool whose name matches the operation, for example: +`browser_session_start`, `browser_navigate`, `browser_observe`, `browser_snapshot`, +`browser_click`, `browser_fill`, `browser_press`, `browser_screenshot`, and +`browser_session_stop`. Pass arguments as the tool schema specifies (not CLI flags). + Drive the user's **real Chromium browser** (logins, cookies, Agent Window isolation) -through this plugin's `browser_*` tools. Each tool spawns `bsk --json` — the -same engine the canonical CLI skill documents — so the workflow, judgment rules, and -safety constraints are identical. +through this plugin's `browser_*` tools. Each injected tool is implemented by the +plugin using the `bsk` engine internally — the model must not invoke that engine +itself. The workflow, judgment rules, and safety constraints remain identical to +the canonical CLI skill. > **Tool availability**: invoking this skill (which you just did) makes the plugin's > `browser_*` tools available for the rest of the session — they are injected on diff --git a/packages/dsh-plugin-browserskill/tests/skill.test.ts b/packages/dsh-plugin-browserskill/tests/skill.test.ts index b36cab2..a3bbcf2 100644 --- a/packages/dsh-plugin-browserskill/tests/skill.test.ts +++ b/packages/dsh-plugin-browserskill/tests/skill.test.ts @@ -26,9 +26,14 @@ describe("registerBskSkill", () => { expect(skill.name).toBe("browser-skill"); expect(typeof skill.description).toBe("string"); expect(String(skill.description)).toContain("browser_*"); - // body = dsh prelude + canonical CLI skill body + // body = dsh prelude + canonical CLI skill body + dsh postlude const content = String(skill.content); expect(content).toContain("browser_observe"); + expect(String(skill.description)).toContain("never invoke bsk through bash or shell"); + expect(content).toContain("Always call those injected `browser_*` tools"); + expect(content).toContain("Never use `bash`, `shell`, `exec`"); + expect(content).toContain("## DSH routing reminder"); + expect(content.trimEnd()).toMatch(/always begin with[\s\S]*`browser_session_start`[\s\S]*`browser_session_stop`\.$/); expect(content).toContain("Owned sessions only"); expect(content).toContain("Mandatory workflow"); expect(content).toContain("Refs invalidate after navigation");