diff --git a/.bumpy/skill-distribution.md b/.bumpy/skill-distribution.md new file mode 100644 index 0000000..39e7b2a --- /dev/null +++ b/.bumpy/skill-distribution.md @@ -0,0 +1,9 @@ +--- +'@varlock/bumpy': patch +--- + +Streamline agent skill distribution and remove the `bumpy ai` command. + +The canonical `add-change` skill now lives at the repo root (`skills/`) as a single source of truth and is synced into the package on `prepack` (gitignored copy), so it ships version-pinned in the npm tarball and via the Claude Code plugin (`claude plugin install @varlock/bumpy`). + +The `bumpy ai setup` command has been removed. Its file-copying targets (`opencode`, `cursor`, `codex`) duplicated the skill into tool-specific directories that drifted from the canonical copy — and had silently been broken in the published package — while the `claude` target was a thin wrapper around `claude plugin install`. Install the skill via the Claude Code plugin, or reference the bundled `SKILL.md` directly from `node_modules/@varlock/bumpy/skills/add-change/SKILL.md`. diff --git a/README.md b/README.md index 7ea08b1..9804d58 100644 --- a/README.md +++ b/README.md @@ -102,16 +102,31 @@ bumpy publish # pack and publish, create git tags, push tags, and create GitHu ## AI Integration -Bumpy ships with an AI skill that teaches LLMs how to create bump files. +Bumpy ships with an [agent skill](https://github.com/dmno-dev/bumpy/blob/main/skills/add-change/SKILL.md) that teaches LLMs how to create bump files. It teaches the AI to examine git changes, identify affected packages, choose bump levels, and create bump files with `bumpy add` — and to keep existing bump files up to date as work continues on a branch, updating packages, bump levels, and summaries to reflect the final state of changes. -```bash -bumpy ai setup --target claude # installs Claude Code plugin -bumpy ai setup --target opencode # creates OpenCode command file -bumpy ai setup --target cursor # creates Cursor rule file -bumpy ai setup --target codex # creates Codex instruction file -``` +### Installing the skill + +Pick whichever fits your setup: + +- **[`skills`](https://github.com/vercel-labs/skills) (any agent — recommended)** — works with Claude Code, Cursor, OpenCode, Codex, and others: + + ```bash + npx skills add dmno-dev/bumpy # update with: npx skills update + ``` + +- **GitHub CLI** (v2.90+): + + ```bash + gh skill install dmno-dev/bumpy add-change # update with: gh skill update add-change + ``` + +- **Claude Code plugin:** + + ```bash + claude plugin install @varlock/bumpy + ``` -The skill teaches the AI to examine git changes, identify affected packages, choose bump levels, and create bump files with `bumpy add`. It also instructs the AI to keep existing bump files up to date as work continues on a branch - updating packages, bump levels, and summaries to reflect the final state of changes. +- **npm-bundled** — the skill ships inside the published package, so once `@varlock/bumpy` is installed it lives at `node_modules/@varlock/bumpy/skills/add-change/SKILL.md`, version-pinned to your installed bumpy. Point any tool that consumes a `SKILL.md` path at it directly. ## Documentation diff --git a/docs/cli.md b/docs/cli.md index 7a6045d..7b79741 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -263,20 +263,3 @@ Interactive guide to set up `BUMPY_GH_TOKEN` for CI. Walks through creating a fi ```bash bumpy ci setup ``` - -## `bumpy ai setup` - -Install an AI skill for creating bump files in supported coding tools. - -```bash -bumpy ai setup --target claude -bumpy ai setup --target opencode -bumpy ai setup --target cursor -bumpy ai setup --target codex -``` - -| Flag | Description | -| ----------------- | ------------------------------------------ | -| `--target ` | `claude`, `opencode`, `cursor`, or `codex` | - -For Claude Code, this runs `claude plugin install @varlock/bumpy` under the hood. For other targets, it copies a command/rule file into your project. diff --git a/packages/bumpy/.gitignore b/packages/bumpy/.gitignore new file mode 100644 index 0000000..6a50a20 --- /dev/null +++ b/packages/bumpy/.gitignore @@ -0,0 +1,2 @@ +dist +skills diff --git a/packages/bumpy/package.json b/packages/bumpy/package.json index cd6bfce..08a7325 100644 --- a/packages/bumpy/package.json +++ b/packages/bumpy/package.json @@ -37,7 +37,8 @@ }, "scripts": { "build": "tsdown", - "prepack": "cp ../../README.md .", + "prepack": "bun run scripts/sync-skill.ts && cp ../../README.md .", + "sync-skill": "bun run scripts/sync-skill.ts", "check": "bun run tsc --noEmit", "test": "bun test" }, diff --git a/packages/bumpy/scripts/sync-skill.ts b/packages/bumpy/scripts/sync-skill.ts new file mode 100644 index 0000000..be2b8d6 --- /dev/null +++ b/packages/bumpy/scripts/sync-skill.ts @@ -0,0 +1,28 @@ +/** + * Copies the canonical agent skill from the repo root (`skills/`) into this + * package so it ships inside the published npm tarball. The skill then travels + * with the installed bumpy version, letting agents discover version-pinned + * guidance straight from `node_modules` (no separate install / registry). + * + * The canonical copy lives at the repo root so a single source of truth feeds + * both the npm bundle and any future discovery endpoints. This package copy is + * a generated artifact (gitignored) and is regenerated on every `prepack` / + * publish (run `bun run sync-skill` to refresh it manually). It is also what + * the Claude Code plugin manifest (`.claude-plugin/plugin.json`) ships to + * agents. + */ +import { existsSync, rmSync, cpSync } from 'node:fs'; +import { resolve } from 'node:path'; + +const PKG_DIR = resolve(import.meta.dir, '..'); +const SRC = resolve(PKG_DIR, '../../skills'); +const DEST = resolve(PKG_DIR, 'skills'); + +if (!existsSync(SRC)) { + throw new Error(`[sync-skill] canonical skills dir not found at ${SRC}`); +} + +rmSync(DEST, { recursive: true, force: true }); +cpSync(SRC, DEST, { recursive: true }); + +console.log(`[sync-skill] copied ${SRC} -> ${DEST}`); diff --git a/packages/bumpy/src/cli.ts b/packages/bumpy/src/cli.ts index 4b55c24..698ea81 100644 --- a/packages/bumpy/src/cli.ts +++ b/packages/bumpy/src/cli.ts @@ -162,23 +162,6 @@ async function main() { break; } - case 'ai': { - const rootDir = await findRoot(); - const subcommand = args[1]; - const aiFlags = parseFlags(args.slice(2)); - - if (subcommand === 'setup') { - const { aiSetupCommand } = await import('./commands/ai.ts'); - await aiSetupCommand(rootDir, { - target: aiFlags.target as string | undefined, - }); - } else { - log.error(`Unknown ai subcommand: ${subcommand}. Use "ai setup".`); - process.exit(1); - } - break; - } - case '--version': case '-v': console.log(`bumpy ${__BUMPY_VERSION__}`); @@ -228,7 +211,6 @@ function printHelp() { ci plan Report what ci release would do (JSON + GitHub Actions outputs) ci release Release — create version PR or auto-publish ci setup Set up a token for triggering CI on version PRs - ai setup Install AI skill for creating bump files Add options: --packages Package bumps (e.g., "pkg-a:minor,pkg-b:patch") @@ -271,9 +253,6 @@ function printHelp() { --tag npm dist-tag for auto-publish --branch Branch name for version PR (default: bumpy/version-packages) - AI setup options: - --target Target AI tool: claude, opencode, cursor, codex - ${colorize('https://bumpy.varlock.dev', 'dim')} `); } diff --git a/packages/bumpy/src/commands/ai.ts b/packages/bumpy/src/commands/ai.ts deleted file mode 100644 index 5edc68c..0000000 --- a/packages/bumpy/src/commands/ai.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { resolve, dirname } from 'node:path'; -import { readFile } from 'node:fs/promises'; -import { fileURLToPath } from 'node:url'; -import { execSync } from 'node:child_process'; -import { ensureDir, exists, writeText } from '../utils/fs.ts'; -import { log } from '../utils/logger.ts'; - -const SUPPORTED_TARGETS = ['claude', 'opencode', 'cursor', 'codex'] as const; -type AiTarget = (typeof SUPPORTED_TARGETS)[number]; - -interface AiSetupOptions { - target?: string; -} - -export async function aiSetupCommand(rootDir: string, opts: AiSetupOptions): Promise { - const target = opts.target as AiTarget | undefined; - - if (!target) { - log.error(`Please specify a target: bumpy ai setup --target <${SUPPORTED_TARGETS.join('|')}>`); - process.exit(1); - } - - if (!SUPPORTED_TARGETS.includes(target)) { - log.error(`Unknown target: "${target}". Supported: ${SUPPORTED_TARGETS.join(', ')}`); - process.exit(1); - } - - if (target === 'claude') { - setupClaude(); - return; - } - - // Read the prompt template bundled with bumpy - const promptContent = await loadPromptTemplate(); - - switch (target) { - case 'opencode': - await setupOpenCode(rootDir, promptContent); - break; - case 'cursor': - await setupCursor(rootDir, promptContent); - break; - case 'codex': - await setupCodex(rootDir, promptContent); - break; - } -} - -async function loadPromptTemplate(): Promise { - // The prompt file is the SKILL.md bundled with the plugin - const thisDir = dirname(fileURLToPath(import.meta.url)); - const promptPath = resolve(thisDir, '../../skills/add-change/SKILL.md'); - const content = await readFile(promptPath, 'utf-8'); - // Strip the YAML frontmatter (skill-specific metadata) - return content.replace(/^---\n[\s\S]*?\n---\n\n?/, ''); -} - -/** Install as a Claude Code plugin */ -function setupClaude(): void { - log.step('Installing Claude Code plugin...'); - try { - execSync('claude plugin install @varlock/bumpy', { stdio: 'inherit' }); - log.success('Installed Claude Code plugin'); - log.dim(' Usage: /bumpy:add-change in Claude Code'); - } catch { - log.error('Failed to install Claude Code plugin. Make sure `claude` is installed and available in your PATH.'); - process.exit(1); - } -} - -/** Install as an OpenCode custom command */ -async function setupOpenCode(rootDir: string, promptContent: string): Promise { - const commandsDir = resolve(rootDir, '.opencode', 'commands'); - const targetPath = resolve(commandsDir, 'add-bumpy-change.md'); - - await ensureDir(commandsDir); - - if (await exists(targetPath)) { - log.warn('.opencode/commands/add-bumpy-change.md already exists — overwriting'); - } - - // OpenCode commands use frontmatter with description - const openCodeContent = `--- -description: Create a bumpy bump file to track package version bumps ---- - -${promptContent}`; - - await writeText(targetPath, openCodeContent); - - log.success('Installed OpenCode command'); - log.dim(' Created .opencode/commands/add-bumpy-change.md'); - log.dim(' Usage: type /add-bumpy-change in OpenCode'); -} - -/** Install as a Cursor rule */ -async function setupCursor(rootDir: string, promptContent: string): Promise { - const rulesDir = resolve(rootDir, '.cursor', 'rules'); - const targetPath = resolve(rulesDir, 'add-bumpy-change.mdc'); - - await ensureDir(rulesDir); - - if (await exists(targetPath)) { - log.warn('.cursor/rules/add-bumpy-change.mdc already exists — overwriting'); - } - - // Cursor rules use .mdc format with frontmatter - const cursorContent = `--- -description: Create a bumpy bump file to track package version bumps -globs: -alwaysApply: false ---- - -${promptContent}`; - - await writeText(targetPath, cursorContent); - - log.success('Installed Cursor rule'); - log.dim(' Created .cursor/rules/add-bumpy-change.mdc'); - log.dim(' The rule will be suggested when relevant, or you can reference it manually'); -} - -/** Install as a Codex instruction */ -async function setupCodex(rootDir: string, promptContent: string): Promise { - const targetPath = resolve(rootDir, '.codex', 'add-bumpy-change.md'); - - await ensureDir(resolve(rootDir, '.codex')); - - if (await exists(targetPath)) { - log.warn('.codex/add-bumpy-change.md already exists — overwriting'); - } - - await writeText(targetPath, promptContent); - - log.success('Installed Codex instruction'); - log.dim(' Created .codex/add-bumpy-change.md'); - log.dim(' Reference this file in your AGENTS.md or pass it as context'); -} diff --git a/packages/bumpy/skills/add-change/SKILL.md b/skills/add-change/SKILL.md similarity index 100% rename from packages/bumpy/skills/add-change/SKILL.md rename to skills/add-change/SKILL.md