From 1efc035ac3d21215914aee92abacbf58e97624b2 Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 16:58:57 +0100 Subject: [PATCH 1/4] docs: update README with interactive mode, multi-target, scopes --- README.md | 41 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index c1a821f..57e740a 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ Any CLI that implements the skillflag convention can be used like this: ```bash # list skills the tool can export --skill list -# show a single skill’s metadata +# show a single skill's metadata --skill show # install into Codex user skills --skill export | npx skillflag install --agent codex @@ -64,6 +64,27 @@ Any CLI that implements the skillflag convention can be used like this: --skill export | npx skillflag install --agent claude --scope repo ``` +### Interactive mode + +When `--agent` or `--scope` is omitted and a TTY is available, `skill-install` launches an interactive wizard: + +```bash +# pipe a skill and let the wizard guide you + --skill export | npx skillflag install +``` + +The wizard lets you pick agents and scopes with arrow keys and space to select, then confirms before installing. This works even when stdin is piped. + +### Multi-target install + +You can install to multiple agents and scopes in one command: + +```bash + --skill export | npx skillflag install --agent codex --agent claude --scope repo --scope user +``` + +This installs to all combinations of the specified agents × scopes. + ## Add skillflag to your CLI 1. Add the library and ship your skill directory in the package. @@ -84,6 +105,24 @@ await maybeHandleSkillflag(process.argv, { See the full guide in [docs/INTEGRATION.md](docs/INTEGRATION.md). +## Supported agents + +codex, claude, portable, vscode, copilot, amp, goose, opencode, factory, cursor + +## Supported scopes + +| Scope | Description | +| ------ | ------------------------------------------------------------- | +| `repo` | Install into the current repository (e.g. `.codex/skills/`) | +| `user` | Install into the user's home config (e.g. `~/.codex/skills/`) | +| `cwd` | Install relative to the current working directory | + +## Help + +```bash +skill-install --help +``` + ## Bundled skill This repo ships a single bundled skill at `skills/skillflag/` that documents both `skillflag` and `skill-install`. From 30bb3fed3d73edb5f6507c81d620ba5a6ffaef16 Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 17:10:11 +0100 Subject: [PATCH 2/4] feat: restrict --agent and --scope to single values, update spec and docs --- README.md | 7 +- docs/SKILLFLAG_SPEC.md | 4 +- src/install/cli.ts | 58 +++++--- src/skillflag.ts | 8 +- test/integration/skill-install-cli.test.ts | 164 +++++++++++++-------- test/integration/skillflag.test.ts | 86 ++++++----- 6 files changed, 199 insertions(+), 128 deletions(-) diff --git a/README.md b/README.md index 57e740a..f85d995 100644 --- a/README.md +++ b/README.md @@ -77,13 +77,14 @@ The wizard lets you pick agents and scopes with arrow keys and space to select, ### Multi-target install -You can install to multiple agents and scopes in one command: +`--agent` and `--scope` flags accept a single value each. +To install to multiple agents/scopes in one run, use the interactive wizard: ```bash - --skill export | npx skillflag install --agent codex --agent claude --scope repo --scope user + --skill export | npx skillflag install ``` -This installs to all combinations of the specified agents × scopes. +In the wizard, select multiple entries with space, then confirm the matrix install. ## Add skillflag to your CLI diff --git a/docs/SKILLFLAG_SPEC.md b/docs/SKILLFLAG_SPEC.md index 952e662..610f40d 100644 --- a/docs/SKILLFLAG_SPEC.md +++ b/docs/SKILLFLAG_SPEC.md @@ -384,7 +384,9 @@ skill-install [PATH ...] [--legacy] # only where a legacy target exists (vscode/copilot -> .claude/skills) ``` -Implementations **MAY** accept repeated and/or comma-separated `--agent` and `--scope` flags, and apply an install matrix across selected sources and targets. +The reference implementation accepts only one value for `--agent` and one value for `--scope`. +Repeated flags and comma-separated values are rejected. +Multi-target installs remain available through the interactive wizard (multi-select). ### 2.2 Required flags diff --git a/src/install/cli.ts b/src/install/cli.ts index c56e482..e9c7135 100644 --- a/src/install/cli.ts +++ b/src/install/cli.ts @@ -135,16 +135,16 @@ const scopeList = SCOPES.join(", "); const usageLines = [ "Usage:", - " skill-install [PATH ...] [--agent [,...]] [--scope [,...]] [--force]", + " skill-install [PATH ...] [--agent ] [--scope ] [--force]", "", "Input:", " PATH ... Skill directory path(s) containing SKILL.md.", " stdin tar stream If PATH is omitted, reads a tar bundle from stdin.", "", "Options:", - " --agent Target agent(s); repeat or use comma-separated values.", + " --agent Target agent (single value).", ` Supported agents: ${agentList}`, - " --scope Target scope(s); repeat or use comma-separated values.", + " --scope Target scope (single value).", ` Supported scopes: ${scopeList}`, " --force Overwrite destination if it already exists.", " -h, --help Show this help.", @@ -152,6 +152,8 @@ const usageLines = [ "Behavior:", " If --agent or --scope is missing and an interactive TTY is available,", " the installer launches a wizard to collect missing values.", + " CLI flags accept only one --agent and one --scope.", + " Use the wizard to select multiple agents/scopes.", ]; const usageText = usageLines.join("\n"); @@ -192,51 +194,61 @@ const scopeDescriptions: Record = { cwd: "Install relative to the current working directory.", }; -function parseScopeValues(value: string | undefined): string[] { +function parseScopeValue(value: string | undefined): string { if (!value || value.startsWith("-")) { throw new InstallError("Missing value for --scope."); } - const scopes = value - .split(",") - .map((item) => item.trim()) - .filter((item) => item.length > 0); - if (scopes.length === 0) { + const scope = value.trim(); + if (scope.length === 0) { throw new InstallError("Missing value for --scope."); } - return scopes; + if (scope.includes(",")) { + throw new InstallError( + "Only one value is allowed for --scope. Comma-separated values are not supported.", + ); + } + return scope; } -function parseAgentValues(value: string | undefined): string[] { +function parseAgentValue(value: string | undefined): string { if (!value || value.startsWith("-")) { throw new InstallError("Missing value for --agent."); } - const agents = value - .split(",") - .map((item) => item.trim()) - .filter((item) => item.length > 0); - if (agents.length === 0) { + const agent = value.trim(); + if (agent.length === 0) { throw new InstallError("Missing value for --agent."); } - return agents; + if (agent.includes(",")) { + throw new InstallError( + "Only one value is allowed for --agent. Comma-separated values are not supported.", + ); + } + return agent; } function parseArgs(args: string[]): ParsedArgs { const rest = [...args]; const inputPaths: string[] = []; - const agents: string[] = []; - const scopes: string[] = []; + let agentValue: string | undefined; + let scopeValue: string | undefined; let force = false; let help = false; for (let i = 0; i < rest.length; i += 1) { const arg = rest[i]; if (arg === "--agent") { - agents.push(...parseAgentValues(rest[i + 1])); + if (agentValue !== undefined) { + throw new InstallError("Only one --agent flag is allowed."); + } + agentValue = parseAgentValue(rest[i + 1]); i += 1; continue; } if (arg === "--scope") { - scopes.push(...parseScopeValues(rest[i + 1])); + if (scopeValue !== undefined) { + throw new InstallError("Only one --scope flag is allowed."); + } + scopeValue = parseScopeValue(rest[i + 1]); i += 1; continue; } @@ -256,8 +268,8 @@ function parseArgs(args: string[]): ParsedArgs { return { inputPaths, - agents: uniqueValues(agents), - scopes: uniqueValues(scopes), + agents: agentValue ? [agentValue] : [], + scopes: scopeValue ? [scopeValue] : [], force, help, }; diff --git a/src/skillflag.ts b/src/skillflag.ts index dfccd65..13b52a3 100644 --- a/src/skillflag.ts +++ b/src/skillflag.ts @@ -48,7 +48,7 @@ export type SkillflagPromptApi = { const usageLines = [ "Usage:", - " --skill install [ ...] [--agent [,...]] [--agent [,...]] [--scope [,...]] [--scope [,...]] [--force]", + " --skill install [ ...] [--agent ] [--scope ] [--force]", " --skill list [--json]", " --skill export ", " --skill show ", @@ -75,9 +75,9 @@ export const SKILLFLAG_HELP_TEXT = [ "Export a skill bundle:", " tool --skill export ", "", - "Install a skill bundle into one or more agents:", - " tool --skill install [ ...]", - " tool --skill export | skill-install --agent [--agent ] --scope [--scope ]", + "Install a skill bundle:", + " tool --skill install [ ...] [--agent ] [--scope ]", + " tool --skill export | skill-install --agent --scope ", "", "For full details, read docs/SKILLFLAG_SPEC.md.", ].join("\n"); diff --git a/test/integration/skill-install-cli.test.ts b/test/integration/skill-install-cli.test.ts index 8221442..1060721 100644 --- a/test/integration/skill-install-cli.test.ts +++ b/test/integration/skill-install-cli.test.ts @@ -401,17 +401,12 @@ test("runInstallCli keeps non-interactive install behavior with flags", async (t assert.match(installedContent, /name: cli-skill/); }); -test("runInstallCli installs multiple skills across repeated scopes", async (t) => { +test("runInstallCli installs multiple skills with a single agent/scope target", async (t) => { const repo = await makeTempDir("skill-install-cli-multi-repo-"); - const codexHome = await makeTempDir("skill-install-cli-multi-codex-home-"); const skillOne = await makeTempDir("skill-install-cli-skill-one-"); const skillTwo = await makeTempDir("skill-install-cli-skill-two-"); - const previousCodexHome = process.env.CODEX_HOME; - process.env.CODEX_HOME = codexHome.dir; t.after(async () => { - process.env.CODEX_HOME = previousCodexHome; await repo.cleanup(); - await codexHome.cleanup(); await skillOne.cleanup(); await skillTwo.cleanup(); }); @@ -440,8 +435,6 @@ test("runInstallCli installs multiple skills across repeated scopes", async (t) "codex", "--scope", "repo", - "--scope", - "user", ], { stdin: Readable.from([]), @@ -456,20 +449,13 @@ test("runInstallCli installs multiple skills across repeated scopes", async (t) await fs.access(path.join(repo.dir, ".codex/skills/cli-skill-one/SKILL.md")); await fs.access(path.join(repo.dir, ".codex/skills/cli-skill-two/SKILL.md")); - await fs.access(path.join(codexHome.dir, "skills/cli-skill-one/SKILL.md")); - await fs.access(path.join(codexHome.dir, "skills/cli-skill-two/SKILL.md")); }); -test("runInstallCli accepts comma-separated scopes", async (t) => { - const repo = await makeTempDir("skill-install-cli-comma-repo-"); - const codexHome = await makeTempDir("skill-install-cli-comma-codex-home-"); - const skill = await makeTempDir("skill-install-cli-comma-skill-"); - const previousCodexHome = process.env.CODEX_HOME; - process.env.CODEX_HOME = codexHome.dir; +test("runInstallCli rejects repeated --scope flags", async (t) => { + const repo = await makeTempDir("skill-install-cli-repeat-scope-repo-"); + const skill = await makeTempDir("skill-install-cli-repeat-scope-skill-"); t.after(async () => { - process.env.CODEX_HOME = previousCodexHome; await repo.cleanup(); - await codexHome.cleanup(); await skill.cleanup(); }); @@ -490,7 +476,9 @@ test("runInstallCli accepts comma-separated scopes", async (t) => { "--agent", "codex", "--scope", - "repo,user", + "repo", + "--scope", + "user", ], { stdin: Readable.from([]), @@ -499,30 +487,18 @@ test("runInstallCli accepts comma-separated scopes", async (t) => { }, ); - assert.equal(exitCode, 0); - assert.match(stderr.text(), /Installed cli-skill-comma to/); - - await fs.access( - path.join(repo.dir, ".codex/skills/cli-skill-comma/SKILL.md"), + assert.equal(exitCode, 1); + assert.match(stderr.text(), /Only one --scope flag is allowed/); + await assert.rejects( + fs.access(path.join(repo.dir, ".codex/skills/cli-skill-comma/SKILL.md")), ); - await fs.access(path.join(codexHome.dir, "skills/cli-skill-comma/SKILL.md")); }); -test("runInstallCli supports repeated --agent and installs matrix combinations", async (t) => { - const repo = await makeTempDir("skill-install-cli-agents-repo-"); - const codexHome = await makeTempDir("skill-install-cli-agents-codex-home-"); - const home = await makeTempDir("skill-install-cli-agents-home-"); - const skill = await makeTempDir("skill-install-cli-agents-skill-"); - const previousCodexHome = process.env.CODEX_HOME; - const previousHome = process.env.HOME; - process.env.CODEX_HOME = codexHome.dir; - process.env.HOME = home.dir; +test("runInstallCli rejects comma-separated --scope values", async (t) => { + const repo = await makeTempDir("skill-install-cli-comma-repo-"); + const skill = await makeTempDir("skill-install-cli-comma-skill-"); t.after(async () => { - process.env.CODEX_HOME = previousCodexHome; - process.env.HOME = previousHome; await repo.cleanup(); - await codexHome.cleanup(); - await home.cleanup(); await skill.cleanup(); }); @@ -531,7 +507,7 @@ test("runInstallCli supports repeated --agent and installs matrix combinations", await writeFile( skill.dir, "SKILL.md", - "---\nname: cli-skill-agents\ndescription: CLI multi-agent test skill\n---\n", + "---\nname: cli-skill-comma\ndescription: CLI comma scope skill\n---\n", ); const stderr = createCapture(); @@ -542,12 +518,8 @@ test("runInstallCli supports repeated --agent and installs matrix combinations", skill.dir, "--agent", "codex", - "--agent", - "claude", - "--scope", - "repo", "--scope", - "user", + "repo,user", ], { stdin: Readable.from([]), @@ -556,28 +528,59 @@ test("runInstallCli supports repeated --agent and installs matrix combinations", }, ); - assert.equal(exitCode, 0); - const installLines = stderr - .text() - .split("\n") - .filter((line) => line.startsWith("Installed ")); - assert.equal(installLines.length, 4); + assert.equal(exitCode, 1); + assert.match(stderr.text(), /Only one value is allowed for --scope/); + await assert.rejects( + fs.access(path.join(repo.dir, ".codex/skills/cli-skill-comma/SKILL.md")), + ); +}); - await fs.access( - path.join(repo.dir, ".codex/skills/cli-skill-agents/SKILL.md"), +test("runInstallCli rejects comma-separated --agent values", async (t) => { + const repo = await makeTempDir("skill-install-cli-comma-agent-repo-"); + const skill = await makeTempDir("skill-install-cli-comma-agent-skill-"); + t.after(async () => { + await repo.cleanup(); + await skill.cleanup(); + }); + + initGit(repo.dir); + + await writeFile( + skill.dir, + "SKILL.md", + "---\nname: cli-skill-comma-agent\ndescription: CLI comma agent skill\n---\n", ); - await fs.access( - path.join(repo.dir, ".claude/skills/cli-skill-agents/SKILL.md"), + + const stderr = createCapture(); + const exitCode = await runInstallCli( + [ + "node", + "skill-install", + skill.dir, + "--agent", + "codex,claude", + "--scope", + "repo", + ], + { + stdin: Readable.from([]), + stderr: stderr.stream, + cwd: repo.dir, + }, ); - await fs.access(path.join(codexHome.dir, "skills/cli-skill-agents/SKILL.md")); - await fs.access( - path.join(home.dir, ".claude/skills/cli-skill-agents/SKILL.md"), + + assert.equal(exitCode, 1); + assert.match(stderr.text(), /Only one value is allowed for --agent/); + await assert.rejects( + fs.access( + path.join(repo.dir, ".codex/skills/cli-skill-comma-agent/SKILL.md"), + ), ); }); -test("runInstallCli fails preflight when selected agents/scopes collide on destination", async (t) => { - const repo = await makeTempDir("skill-install-cli-shared-dest-repo-"); - const skill = await makeTempDir("skill-install-cli-shared-dest-skill-"); +test("runInstallCli rejects repeated --agent flags", async (t) => { + const repo = await makeTempDir("skill-install-cli-agents-repo-"); + const skill = await makeTempDir("skill-install-cli-agents-skill-"); t.after(async () => { await repo.cleanup(); await skill.cleanup(); @@ -588,7 +591,7 @@ test("runInstallCli fails preflight when selected agents/scopes collide on desti await writeFile( skill.dir, "SKILL.md", - "---\nname: cli-skill-shared\ndescription: CLI shared destination test skill\n---\n", + "---\nname: cli-skill-agents\ndescription: CLI multi-agent test skill\n---\n", ); const stderr = createCapture(); @@ -598,9 +601,9 @@ test("runInstallCli fails preflight when selected agents/scopes collide on desti "skill-install", skill.dir, "--agent", - "portable", + "codex", "--agent", - "amp", + "claude", "--scope", "repo", ], @@ -611,6 +614,41 @@ test("runInstallCli fails preflight when selected agents/scopes collide on desti }, ); + assert.equal(exitCode, 1); + assert.match(stderr.text(), /Only one --agent flag is allowed/); + await assert.rejects( + fs.access(path.join(repo.dir, ".codex/skills/cli-skill-agents/SKILL.md")), + ); +}); + +test("runInstallCli fails preflight when selected agents/scopes collide on destination", async (t) => { + const repo = await makeTempDir("skill-install-cli-shared-dest-repo-"); + const skill = await makeTempDir("skill-install-cli-shared-dest-skill-"); + t.after(async () => { + await repo.cleanup(); + await skill.cleanup(); + }); + + initGit(repo.dir); + + await writeFile( + skill.dir, + "SKILL.md", + "---\nname: cli-skill-shared\ndescription: CLI shared destination test skill\n---\n", + ); + + const stderr = createCapture(); + const prompt = createPromptStub({ + multiselectResponses: [["portable", "amp"], ["repo"]], + confirmResponses: [false], + }); + const exitCode = await runInstallCli(["node", "skill-install", skill.dir], { + stdin: createTtyStdin(), + stderr: stderr.stream, + cwd: repo.dir, + promptApi: prompt.promptApi, + }); + assert.equal(exitCode, 1); assert.match(stderr.text(), /Install destination collisions detected/); assert.match(stderr.text(), /\.agents\/skills\/cli-skill-shared/); diff --git a/test/integration/skillflag.test.ts b/test/integration/skillflag.test.ts index a79cb5f..fc9be20 100644 --- a/test/integration/skillflag.test.ts +++ b/test/integration/skillflag.test.ts @@ -384,15 +384,10 @@ test("--skill install delegates to installer with exported tar input", async (t) assert.match(installedContent, /name: alpha/); }); -test("--skill install supports multiple ids and multiple scopes", async (t) => { +test("--skill install supports multiple ids with a single scope", async (t) => { const repo = await makeTempDir("skillflag-install-multi-repo-"); - const codexHome = await makeTempDir("skillflag-install-multi-codex-home-"); - const previousCodexHome = process.env.CODEX_HOME; - process.env.CODEX_HOME = codexHome.dir; t.after(async () => { - process.env.CODEX_HOME = previousCodexHome; await repo.cleanup(); - await codexHome.cleanup(); }); initGit(repo.dir); @@ -412,8 +407,6 @@ test("--skill install supports multiple ids and multiple scopes", async (t) => { "codex", "--scope", "repo", - "--scope", - "user", ], { skillsRoot: fixturesRoot, @@ -431,24 +424,12 @@ test("--skill install supports multiple ids and multiple scopes", async (t) => { await fs.access(path.join(repo.dir, ".codex/skills/alpha/SKILL.md")); await fs.access(path.join(repo.dir, ".codex/skills/beta/SKILL.md")); - await fs.access(path.join(codexHome.dir, "skills/alpha/SKILL.md")); - await fs.access(path.join(codexHome.dir, "skills/beta/SKILL.md")); }); -test("--skill install supports multiple agents and multiple scopes", async (t) => { - const repo = await makeTempDir("skillflag-install-agents-repo-"); - const codexHome = await makeTempDir("skillflag-install-agents-codex-home-"); - const home = await makeTempDir("skillflag-install-agents-home-"); - const previousCodexHome = process.env.CODEX_HOME; - const previousHome = process.env.HOME; - process.env.CODEX_HOME = codexHome.dir; - process.env.HOME = home.dir; +test("--skill install rejects repeated --scope flags", async (t) => { + const repo = await makeTempDir("skillflag-install-repeat-scope-repo-"); t.after(async () => { - process.env.CODEX_HOME = previousCodexHome; - process.env.HOME = previousHome; await repo.cleanup(); - await codexHome.cleanup(); - await home.cleanup(); }); initGit(repo.dir); @@ -465,8 +446,6 @@ test("--skill install supports multiple agents and multiple scopes", async (t) = "alpha", "--agent", "codex", - "--agent", - "claude", "--scope", "repo", "--scope", @@ -482,17 +461,56 @@ test("--skill install supports multiple agents and multiple scopes", async (t) = }, ); - assert.equal(exitCode, 0); - const installLines = stderr - .text() - .split("\n") - .filter((line) => line.startsWith("Installed ")); - assert.equal(installLines.length, 4); + assert.equal(exitCode, 1); + assert.match(stderr.text(), /Only one --scope flag is allowed/); + await assert.rejects( + fs.access(path.join(repo.dir, ".codex/skills/alpha/SKILL.md")), + ); +}); - await fs.access(path.join(repo.dir, ".codex/skills/alpha/SKILL.md")); - await fs.access(path.join(repo.dir, ".claude/skills/alpha/SKILL.md")); - await fs.access(path.join(codexHome.dir, "skills/alpha/SKILL.md")); - await fs.access(path.join(home.dir, ".claude/skills/alpha/SKILL.md")); +test("--skill install rejects repeated --agent flags", async (t) => { + const repo = await makeTempDir("skillflag-install-agents-repo-"); + t.after(async () => { + await repo.cleanup(); + }); + + initGit(repo.dir); + + const stdout = createCapture(); + const stderr = createCapture(); + + const exitCode = await handleSkillflag( + [ + "node", + "cli", + "--skill", + "install", + "alpha", + "--agent", + "codex", + "--agent", + "claude", + "--scope", + "repo", + ], + { + skillsRoot: fixturesRoot, + stdin: Readable.from([]), + stdout: stdout.stream, + stderr: stderr.stream, + cwd: repo.dir, + includeBundledSkill: false, + }, + ); + + assert.equal(exitCode, 1); + assert.match(stderr.text(), /Only one --agent flag is allowed/); + await assert.rejects( + fs.access(path.join(repo.dir, ".codex/skills/alpha/SKILL.md")), + ); + await assert.rejects( + fs.access(path.join(repo.dir, ".claude/skills/alpha/SKILL.md")), + ); }); test("--skill install without id in non-interactive mode requires explicit ids when multiple skills exist", async () => { From 3d61f9e681d014e4928080bc81905aabb51bb9e1 Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 17:15:03 +0100 Subject: [PATCH 3/4] docs: add quick setup prompt for agents --- README.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/README.md b/README.md index f85d995..ab737e2 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,37 @@ Installed skill philips-hue to ~/.claude/skills/philips-hue Even better, once this convention becomes commonplace, agents will by default do all these before they even run the tool, so when you ask it to "install hue-cli", it will know to run `--skill list` the same way a human would run `--help` after downloading a program, and install the necessary skills themselves without being asked to. +## Quick setup — tell your agent about skillflag + +Copy the block below and paste it into your coding agent (Claude Code, Codex, OpenClaw, etc.). It will teach the agent how to discover and install skills from any CLI that implements the skillflag convention. + +```text +When you install or set up a CLI tool, check if it supports the skillflag +convention for bundling agent skills. Here's how: + +1. Check if the tool exposes skills: + --skill list + +2. If it does, install the relevant skill for your agent: + --skill export | npx skillflag install --agent --scope + + Supported agents: codex, claude, portable, vscode, copilot, amp, goose, + opencode, factory, cursor + Supported scopes: repo, user, cwd + +3. Or use the interactive wizard (omit --agent/--scope to get prompted): + --skill export | npx skillflag install + +4. To preview a skill before installing: + --skill show + +5. For the full skillflag reference: + https://raw.githubusercontent.com/osolmaz/skillflag/main/skills/skillflag/SKILL.md + +Think of `--skill list` like `--help` — check it whenever you encounter a new +CLI tool. If it has skills, install them so you have the instructions you need. +``` + ## Install (optional) ```bash From e79a0f30f11fb9371723c66b0cf6d205730c7bc3 Mon Sep 17 00:00:00 2001 From: Bob Date: Fri, 20 Feb 2026 17:18:36 +0100 Subject: [PATCH 4/4] docs: rewrite quick setup as add-to-your-CLI prompt --- README.md | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/README.md b/README.md index ab737e2..cfac99a 100644 --- a/README.md +++ b/README.md @@ -39,35 +39,39 @@ Installed skill philips-hue to ~/.claude/skills/philips-hue Even better, once this convention becomes commonplace, agents will by default do all these before they even run the tool, so when you ask it to "install hue-cli", it will know to run `--skill list` the same way a human would run `--help` after downloading a program, and install the necessary skills themselves without being asked to. -## Quick setup — tell your agent about skillflag +## Quick setup — add skillflag to your CLI -Copy the block below and paste it into your coding agent (Claude Code, Codex, OpenClaw, etc.). It will teach the agent how to discover and install skills from any CLI that implements the skillflag convention. +Copy the prompt below and paste it into your coding agent. It will add skillflag support to your project. + +Currently TypeScript/Node only. [Open an issue](https://github.com/osolmaz/skillflag/issues) if you'd like support for another language. ```text -When you install or set up a CLI tool, check if it supports the skillflag -convention for bundling agent skills. Here's how: +Add skillflag to this project so the CLI can bundle and expose agent skills. -1. Check if the tool exposes skills: - --skill list +1. Install the skillflag library: + npm install skillflag -2. If it does, install the relevant skill for your agent: - --skill export | npx skillflag install --agent --scope +2. Create a skill directory at skills//SKILL.md with a YAML + frontmatter (name, description) and markdown instructions for the agent. - Supported agents: codex, claude, portable, vscode, copilot, amp, goose, - opencode, factory, cursor - Supported scopes: repo, user, cwd +3. In the CLI entrypoint, intercept --skill and delegate to skillflag: -3. Or use the interactive wizard (omit --agent/--scope to get prompted): - --skill export | npx skillflag install + import { findSkillsRoot, maybeHandleSkillflag } from "skillflag"; -4. To preview a skill before installing: + await maybeHandleSkillflag(process.argv, { + skillsRoot: findSkillsRoot(import.meta.url), + }); + +4. Verify it works: + --skill list --skill show + --skill export | npx skillflag install -5. For the full skillflag reference: - https://raw.githubusercontent.com/osolmaz/skillflag/main/skills/skillflag/SKILL.md +5. For the full integration guide: + https://raw.githubusercontent.com/osolmaz/skillflag/main/docs/INTEGRATION.md -Think of `--skill list` like `--help` — check it whenever you encounter a new -CLI tool. If it has skills, install them so you have the instructions you need. +6. For the skillflag specification: + https://raw.githubusercontent.com/osolmaz/skillflag/main/docs/SKILLFLAG_SPEC.md ``` ## Install (optional)