Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/skill-install-frontmatter.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"clerk": patch
---

Fix `clerk skill install` failing with `No valid skills found` on published releases. The bundled skill's frontmatter now parses as strict YAML.
22 changes: 22 additions & 0 deletions packages/cli-core/src/commands/skill/install.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { test, expect, describe } from "bun:test";
import { existsSync } from "node:fs";
import { readFile, stat } from "node:fs/promises";
import { join } from "node:path";
import YAML from "yaml";
import { buildSkillsArgs, renderSkillVersionPlaceholder, withStagedClerkSkill } from "./install.ts";

describe("buildSkillsArgs", () => {
Expand Down Expand Up @@ -136,6 +137,27 @@ describe("withStagedClerkSkill version rendering", () => {
});
});

describe("bundled SKILL.md frontmatter", () => {
// Regression guard: the upstream `skills` CLI parses SKILL.md frontmatter as
// strict YAML and silently drops skills whose frontmatter fails to parse.
// An unquoted `: ` inside the description (e.g. `raw HTTP: it handles`)
// triggers "Nested mappings are not allowed in compact mappings" and makes
// `clerk skill install` fail with "No valid skills found".
test("parses as YAML with name and description strings", async () => {
await withStagedClerkSkill(undefined, async (stageDir) => {
const content = await readFile(join(stageDir, "clerk/SKILL.md"), "utf8");
const frontmatter = content.match(/^---\n([\s\S]*?)\n---/)?.[1];
expect(frontmatter, "SKILL.md must have YAML frontmatter").toBeDefined();

const parsed = YAML.parse(frontmatter!);
expect(typeof parsed.name).toBe("string");
expect(typeof parsed.description).toBe("string");
expect(parsed.name.length).toBeGreaterThan(0);
expect(parsed.description.length).toBeGreaterThan(0);
});
});
});

describe("renderSkillVersionPlaceholder", () => {
test("replaces {{CLI_VERSION}} with the provided version", () => {
const result = renderSkillVersionPlaceholder("Pinned: `bunx clerk@{{CLI_VERSION}}`.", "1.2.3");
Expand Down
8 changes: 7 additions & 1 deletion skills/clerk/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
---
name: clerk
description: Operate the Clerk CLI (`clerk` binary) for authentication, user/org/session management, instance config, env keys, and any Clerk Backend or Platform API call. Use when the user mentions Clerk management tasks, "list clerk users", "create a clerk user", "update organization", "pull clerk config", "clerk env pull", "clerk doctor", "clerk api", or any ad-hoc Clerk API request. Prefer the CLI over raw HTTP: it handles auth, key resolution, app/instance targeting, and formatting automatically.
description: >-
Operate the Clerk CLI (`clerk` binary) for authentication, user/org/session management,
instance config, env keys, and any Clerk Backend or Platform API call. Use when the user
mentions Clerk management tasks, "list clerk users", "create a clerk user", "update
organization", "pull clerk config", "clerk env pull", "clerk doctor", "clerk api", or
any ad-hoc Clerk API request. Prefer the CLI over raw HTTP: it handles auth, key
resolution, app/instance targeting, and formatting automatically.
---

# Clerk CLI
Expand Down
Loading