FN-9035: prevent static computer commands in shipped skills
Keep bundled computer-use guidance limited to runtime discovery so command syntax cannot drift. - Scan all shipped skill markdown files, including plugin skills - Reject static `fn computer` commands outside the computer-use discovery stub - Assert the discovery stub exposes only its supported JSON probes Files changed: packages/cli/src/commands/__tests__/computer-use-skill.test.ts | 50 ++++++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) Fusion-Task-Id: FN-9035 Fusion-Task-Lineage: 5049fd29-1b3b-4968-b09e-1bb7be9d52ff Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
@@ -1,12 +1,42 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { dirname, join } from "node:path";
|
||||
import { dirname, join, relative } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { parse } from "yaml";
|
||||
import { COMPUTER_COMMAND_SURFACE } from "../computer/contract.js";
|
||||
const skill = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "../../../skill/computer-use/SKILL.md"), "utf8");
|
||||
|
||||
const testDir = dirname(fileURLToPath(import.meta.url));
|
||||
const cliRoot = join(testDir, "../../..");
|
||||
const repoRoot = join(cliRoot, "../..");
|
||||
const computerUseSkillPath = join(cliRoot, "skill/computer-use/SKILL.md");
|
||||
const skillRoots = [join(cliRoot, "skill"), join(repoRoot, "plugins")];
|
||||
|
||||
function findShippedSkillMarkdown(root: string): string[] {
|
||||
if (!existsSync(root)) return [];
|
||||
return readdirSync(root, { withFileTypes: true }).flatMap((entry) => {
|
||||
const path = join(root, entry.name);
|
||||
if (entry.isDirectory()) return findShippedSkillMarkdown(path);
|
||||
return entry.name === "SKILL.md" ? [path] : [];
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* FNXC:ComputerUseSkill 2026-08-13-23:35:
|
||||
* The original stub-only ratchet let sibling shipped skills reintroduce static computer commands and
|
||||
* flags that drift from the installed binary. Discover every bundled SKILL.md so the binary remains
|
||||
* the only version-matched command guide, while preserving the three read-only fallback probes.
|
||||
*/
|
||||
function readShippedSkills(): Array<{ path: string; content: string }> {
|
||||
return skillRoots
|
||||
.flatMap(findShippedSkillMarkdown)
|
||||
.filter((path) => path.includes("/skill/") || /\/plugins\/[^/]+\/src\/skills\//.test(path))
|
||||
.sort()
|
||||
.map((path) => ({ path, content: readFileSync(path, "utf8") }));
|
||||
}
|
||||
|
||||
describe("computer-use shipped skill", () => {
|
||||
it("has parseable frontmatter and keeps its anti-drift stub thin", () => {
|
||||
const skill = readFileSync(computerUseSkillPath, "utf8");
|
||||
const frontmatter = skill.match(/^---\n([\s\S]*?)\n---/)?.[1];
|
||||
expect(parse(frontmatter ?? "")).toMatchObject({ name: "computer-use" });
|
||||
expect(skill).toContain("fn skills get computer-use");
|
||||
@@ -16,5 +46,19 @@ describe("computer-use shipped skill", () => {
|
||||
expect(COMPUTER_COMMAND_SURFACE[name as keyof typeof COMPUTER_COMMAND_SURFACE]).toBeDefined();
|
||||
expect(tail.replace(/`$/, "")).toBe(" --json");
|
||||
}
|
||||
expect([...skill.matchAll(/--[a-z][a-z-]*/g)].map(([flag]) => flag)).toEqual(["--json", "--json", "--json"]);
|
||||
});
|
||||
|
||||
it("keeps computer commands out of every other shipped skill", () => {
|
||||
const skills = readShippedSkills();
|
||||
const skillPaths = skills.map(({ path }) => relative(repoRoot, path));
|
||||
expect(skillPaths).not.toHaveLength(0);
|
||||
expect(skillPaths).toContain("packages/cli/skill/computer-use/SKILL.md");
|
||||
expect(skillPaths).toContain("packages/cli/skill/fusion/SKILL.md");
|
||||
|
||||
for (const { path, content } of skills) {
|
||||
if (path === computerUseSkillPath) continue;
|
||||
expect(content, relative(repoRoot, path)).not.toMatch(/fn computer [a-z-]+/);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user