diff --git a/.changeset/fix-skill-list.md b/.changeset/fix-skill-list.md new file mode 100644 index 000000000..e1da4be5a --- /dev/null +++ b/.changeset/fix-skill-list.md @@ -0,0 +1,5 @@ +--- +"@tailor-platform/sdk": patch +--- + +fix(skills): rename bundled skill directory to `agent-skills/` so `gh skill install tailor-platform/sdk` resolves a single skill. `gh skill` uses a recursive `**/skills/*/SKILL.md` match and was picking up both `skills/tailor-sdk/` (the public mirror) and `packages/sdk/skills/tailor-sdk/` (the npm bundle), erroring with conflicting names. The bundled copy is now under `packages/sdk/agent-skills/` (excluded from gh's match), while the repo-root `skills/` mirror remains the single discovery target for `gh skill install` and `npx skills add `. `npx tailor-sdk skills install` continues to work via the bundled `agent-skills/` path inside the published package. diff --git a/.claude/skills/tailor-sdk b/.claude/skills/tailor-sdk index d82fb04fe..167d322cb 120000 --- a/.claude/skills/tailor-sdk +++ b/.claude/skills/tailor-sdk @@ -1 +1 @@ -../../packages/sdk/skills/tailor-sdk \ No newline at end of file +../../packages/sdk/agent-skills/tailor-sdk \ No newline at end of file diff --git a/.github/workflows/skills-sync-check.yml b/.github/workflows/skills-sync-check.yml index e1ce91ad7..ff20a27a0 100644 --- a/.github/workflows/skills-sync-check.yml +++ b/.github/workflows/skills-sync-check.yml @@ -3,12 +3,12 @@ name: Skills Sync on: pull_request: paths: - - "packages/sdk/skills/**" + - "packages/sdk/agent-skills/**" - ".github/workflows/skills-sync-check.yml" push: branches: [main] paths: - - "packages/sdk/skills/**" + - "packages/sdk/agent-skills/**" permissions: contents: read @@ -28,10 +28,13 @@ jobs: - name: Sync skills to root run: | - # Copy public skills from packages/sdk/skills/ to skills/ - # This enables distribution via `gh skill` and `npx skills` + # Copy public skills from packages/sdk/agent-skills/ to skills/. + # The bundled copy lives under a non-`skills/` directory so that + # `gh skill`'s recursive `**/skills/*/SKILL.md` match resolves to a + # single entry (the repo-root mirror). Root `skills/` is what both + # `gh skill install` and `npx skills add ` actually pick up. - for skill_dir in packages/sdk/skills/*/; do + for skill_dir in packages/sdk/agent-skills/*/; do skill_name=$(basename "$skill_dir") mkdir -p "skills/$skill_name" cp -r "$skill_dir"* "skills/$skill_name/" diff --git a/packages/sdk/README.md b/packages/sdk/README.md index 816587aca..ae1ee7cee 100644 --- a/packages/sdk/README.md +++ b/packages/sdk/README.md @@ -60,7 +60,7 @@ npx tailor-sdk skills install -a codex -y ``` This uses the `skills` CLI under the hood, sourcing the skill from -`node_modules/@tailor-platform/sdk/skills` so the skill version always matches +`node_modules/@tailor-platform/sdk/agent-skills` so the skill version always matches the installed SDK version. Files are copied (not symlinked) so they survive `pnpm install` wiping `node_modules`. diff --git a/packages/sdk/skills/tailor-sdk/SKILL.md b/packages/sdk/agent-skills/tailor-sdk/SKILL.md similarity index 100% rename from packages/sdk/skills/tailor-sdk/SKILL.md rename to packages/sdk/agent-skills/tailor-sdk/SKILL.md diff --git a/packages/sdk/package.json b/packages/sdk/package.json index d5b250810..68913a681 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -18,7 +18,7 @@ "docs", "postinstall.mjs", "README.md", - "skills" + "agent-skills" ], "type": "module", "sideEffects": [ diff --git a/packages/sdk/src/cli/commands/skills/install.test.ts b/packages/sdk/src/cli/commands/skills/install.test.ts index d346cbb6b..2c9145e31 100644 --- a/packages/sdk/src/cli/commands/skills/install.test.ts +++ b/packages/sdk/src/cli/commands/skills/install.test.ts @@ -4,9 +4,9 @@ import { describe, expect, it } from "vitest"; import { installCommand, resolveBundledSkillsDir } from "./install"; describe("resolveBundledSkillsDir", () => { - it("resolves to the SDK package's skills/ directory", async () => { + it("resolves to the SDK package's agent-skills/ directory", async () => { const dir = await resolveBundledSkillsDir(); - expect(dir.endsWith("/skills")).toBe(true); + expect(dir.endsWith("/agent-skills")).toBe(true); expect(existsSync(dir)).toBe(true); expect(statSync(dir).isDirectory()).toBe(true); expect(existsSync(resolve(dir, "tailor-sdk", "SKILL.md"))).toBe(true); diff --git a/packages/sdk/src/cli/commands/skills/install.ts b/packages/sdk/src/cli/commands/skills/install.ts index 8165dd91e..e6be3bec4 100644 --- a/packages/sdk/src/cli/commands/skills/install.ts +++ b/packages/sdk/src/cli/commands/skills/install.ts @@ -7,9 +7,12 @@ import { runSkillsInstaller } from "@/cli/shared/skills-installer"; // Resolve the SDK package root at runtime so the skills directory is found // regardless of how the file is bundled (tsdown inlines non-entry modules). +// The directory is named `agent-skills` (not `skills`) to avoid `gh skill`'s +// `**/skills/*/SKILL.md` recursive match that would otherwise pick up both +// this bundled copy and the repo-root `skills/` and report a conflict. export async function resolveBundledSkillsDir(): Promise { const pkgJsonPath = await resolvePackageJSON(import.meta.url); - return resolve(dirname(pkgJsonPath), "skills"); + return resolve(dirname(pkgJsonPath), "agent-skills"); } const DEFAULT_AGENT = "claude-code"; diff --git a/packages/sdk/src/cli/shared/skills-packaged.test.ts b/packages/sdk/src/cli/shared/skills-packaged.test.ts index 86921a562..7d461847a 100644 --- a/packages/sdk/src/cli/shared/skills-packaged.test.ts +++ b/packages/sdk/src/cli/shared/skills-packaged.test.ts @@ -4,7 +4,7 @@ import { resolve } from "pathe"; import { describe, expect, it } from "vitest"; const sdkRoot = resolve(import.meta.dirname, "..", "..", ".."); -const skillsRoot = resolve(sdkRoot, "skills"); +const skillsRoot = resolve(sdkRoot, "agent-skills"); const NODE_MODULES_REF_PATTERN = /node_modules\/@tailor-platform\/sdk\/([^\s`)]+)/g;