diff --git a/.changeset/thirty-jeans-retire.md b/.changeset/thirty-jeans-retire.md new file mode 100644 index 0000000..21d2396 --- /dev/null +++ b/.changeset/thirty-jeans-retire.md @@ -0,0 +1,7 @@ +--- +"@bomb.sh/tools": minor +--- + +Adds skills for coding agents. Eight skills cover the full `bsh` toolchain — lifecycle, lint, build, test, format, dev, init, and migration for onboarding an existing project. + +If you use an AI coding agent, run `pnpm add -D @bomb.sh/tools` then have your agent run `pnpm dlx @tanstack/intent@latest install` to load project-specific skills for `@bomb.sh/tools`. diff --git a/.github/workflows/check-skills.yml b/.github/workflows/check-skills.yml new file mode 100644 index 0000000..d1bc862 --- /dev/null +++ b/.github/workflows/check-skills.yml @@ -0,0 +1,143 @@ +# check-skills.yml — Drop this into your library repo's .github/workflows/ +# +# Checks for stale intent skills after a release and opens a review PR +# if any skills need attention. The PR body includes a prompt you can +# paste into Claude Code, Cursor, or any coding agent to update them. +# +# Triggers: new release published, or manual workflow_dispatch. +# +# Template variables (replaced by `intent setup`): +# @bomb.sh/tools — e.g. @tanstack/query or my-workspace workspace + +name: Check Skills + +on: + release: + types: [published] + workflow_dispatch: {} + +permissions: + contents: write + pull-requests: write + +jobs: + check: + name: Check for stale skills + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install intent + run: npm install -g @tanstack/intent + + - name: Check staleness + id: stale + run: | + OUTPUT=$(intent stale --json 2>&1) || true + echo "$OUTPUT" + + # Check if any skills need review + NEEDS_REVIEW=$(echo "$OUTPUT" | node -e " + const input = require('fs').readFileSync('/dev/stdin','utf8'); + try { + const reports = JSON.parse(input); + const stale = reports.flatMap(r => + r.skills.filter(s => s.needsReview).map(s => ({ library: r.library, skill: s.name, reasons: s.reasons })) + ); + if (stale.length > 0) { + console.log(JSON.stringify(stale)); + } + } catch {} + ") + + if [ -z "$NEEDS_REVIEW" ]; then + echo "has_stale=false" >> "$GITHUB_OUTPUT" + else + echo "has_stale=true" >> "$GITHUB_OUTPUT" + # Escape for multiline GH output + EOF=$(dd if=/dev/urandom bs=15 count=1 status=none | base64) + echo "stale_json<<$EOF" >> "$GITHUB_OUTPUT" + echo "$NEEDS_REVIEW" >> "$GITHUB_OUTPUT" + echo "$EOF" >> "$GITHUB_OUTPUT" + fi + + - name: Build summary + if: steps.stale.outputs.has_stale == 'true' + id: summary + run: | + node -e " + const stale = JSON.parse(process.env.STALE_JSON); + const lines = stale.map(s => + '- **' + s.skill + '** (' + s.library + '): ' + s.reasons.join(', ') + ); + const summary = lines.join('\n'); + + const prompt = [ + 'Review and update the following stale intent skills for @bomb.sh/tools:', + '', + ...stale.map(s => '- ' + s.skill + ': ' + s.reasons.join(', ')), + '', + 'For each stale skill:', + '1. Read the current SKILL.md file', + '2. Check what changed in the library since the skill was last updated', + '3. Update the skill content to reflect current APIs and behavior', + '4. Run \`npx @tanstack/intent validate\` to verify the updated skill', + ].join('\n'); + + // Write outputs + const fs = require('fs'); + const env = fs.readFileSync(process.env.GITHUB_OUTPUT, 'utf8'); + const eof = require('crypto').randomBytes(15).toString('base64'); + fs.appendFileSync(process.env.GITHUB_OUTPUT, + 'summary<<' + eof + '\n' + summary + '\n' + eof + '\n' + + 'prompt<<' + eof + '\n' + prompt + '\n' + eof + '\n' + ); + " + env: + STALE_JSON: ${{ steps.stale.outputs.stale_json }} + + - name: Open review PR + if: steps.stale.outputs.has_stale == 'true' + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ github.event.release.tag_name || 'manual' }}" + BRANCH="skills/review-${VERSION}" + + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git checkout -b "$BRANCH" + git commit --allow-empty -m "chore: review stale skills for ${VERSION}" + git push origin "$BRANCH" + + gh pr create \ + --title "Review stale skills (${VERSION})" \ + --body "$(cat <<'PREOF' + ## Stale Skills Detected + + The following skills may need updates after the latest release: + + ${{ steps.summary.outputs.summary }} + + --- + + ### Update Prompt + + Paste this into your coding agent (Claude Code, Cursor, etc.): + + ~~~ + ${{ steps.summary.outputs.prompt }} + ~~~ + + PREOF + )" \ + --head "$BRANCH" \ + --base main diff --git a/.github/workflows/notify-intent.yml b/.github/workflows/notify-intent.yml new file mode 100644 index 0000000..bd9a4b0 --- /dev/null +++ b/.github/workflows/notify-intent.yml @@ -0,0 +1,51 @@ +# notify-intent.yml — Drop this into your library repo's .github/workflows/ +# +# Fires a repository_dispatch event whenever docs or source files change +# on merge to main. This triggers the skill staleness check workflow. +# +# Requirements: +# - A fine-grained PAT with contents:write on this repository stored +# as the INTENT_NOTIFY_TOKEN repository secret. +# +# Template variables (replaced by `intent setup`): +# @bomb.sh/tools — e.g. @tanstack/query or my-workspace workspace +# docs/** — e.g. docs/** +# src/** — e.g. packages/query-core/src/** + +name: Trigger Skill Review + +on: + push: + branches: [main] + paths: + - "docs/**" + - "src/**" + +jobs: + notify: + name: Trigger Skill Review + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Collect changed files + id: changes + run: | + FILES=$(git diff --name-only HEAD~1 HEAD | jq -R -s -c 'split("\n") | map(select(length > 0))') + echo "files=$FILES" >> "$GITHUB_OUTPUT" + + - name: Dispatch to intent repo + uses: peter-evans/repository-dispatch@v3 + with: + token: ${{ secrets.INTENT_NOTIFY_TOKEN }} + repository: ${{ github.repository }} + event-type: skill-check + client-payload: | + { + "package": "@bomb.sh/tools", + "sha": "${{ github.sha }}", + "changed_files": ${{ steps.changes.outputs.files }} + } diff --git a/.github/workflows/validate-skills.yml b/.github/workflows/validate-skills.yml new file mode 100644 index 0000000..8c62379 --- /dev/null +++ b/.github/workflows/validate-skills.yml @@ -0,0 +1,52 @@ +# validate-skills.yml — Drop this into your library repo's .github/workflows/ +# +# Validates skill files on PRs that touch the skills/ directory. +# Ensures frontmatter is correct, names match paths, and files stay under +# the 500-line limit. + +name: Validate Skills + +on: + pull_request: + paths: + - "skills/**" + - "**/skills/**" + +jobs: + validate: + name: Validate skill files + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install intent CLI + run: npm install -g @tanstack/intent + + - name: Find and validate skills + run: | + # Find all directories containing SKILL.md files + SKILLS_DIR="" + if [ -d "skills" ]; then + SKILLS_DIR="skills" + elif [ -d "packages" ]; then + # Monorepo — find skills/ under packages + for dir in packages/*/skills; do + if [ -d "$dir" ]; then + echo "Validating $dir..." + intent validate "$dir" + fi + done + exit 0 + fi + + if [ -n "$SKILLS_DIR" ]; then + intent validate "$SKILLS_DIR" + else + echo "No skills/ directory found — skipping validation." + fi diff --git a/README.md b/README.md index db29b90..9edf6a9 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,7 @@ If you'd like to use this package for your own projects, please consider forking - `bsh format` command, using [`oxfmt`](https://oxc.rs/docs/guide/usage/formatter) - `bsh lint` command, using [`oxlint`](https://oxc.rs/docs/guide/usage/linter), [`publint`](https://publint.dev/), [`knip`](https://knip.dev), [`tsgo`](https://npmx.dev/@typescript/native-preview) - shared `tsconfig.json` file + +## Agent Skills + +If you use an AI coding agent, run `pnpm add -D @bomb.sh/tools` then have your agent run `pnpm dlx @tanstack/intent@latest install` to load project-specific skills for `@bomb.sh/tools`. diff --git a/package.json b/package.json index 559f5b1..0c7d74b 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,9 @@ "keywords": [ "bombshell", "cli", - "internal" + "internal", + "skills", + "tanstack-intent" ], "homepage": "https://bomb.sh", "license": "MIT", @@ -21,11 +23,22 @@ "bin": { "bsh": "dist/bin.mjs" }, + "files": [ + "dist", + "skills", + "!skills/_artifacts", + ".claude-plugin", + "tsconfig.json", + "package.json", + "oxfmtrc.json", + "oxlintrc.json" + ], "type": "module", "exports": { ".": { "import": "./dist/bin.mjs" }, + "./skills/*": "./skills/*", "./test-utils": { "types": "./dist/test-utils.d.mts", "import": "./dist/test-utils.mjs" @@ -61,6 +74,7 @@ }, "devDependencies": { "@changesets/cli": "^2.28.1", + "@tanstack/intent": "^0.0.23", "@types/node": "^22.13.14" }, "volta": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 77c2475..1d738f6 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -40,11 +40,14 @@ importers: version: 0.21.0-beta.2(@typescript/native-preview@7.0.0-dev.20260307.1)(oxc-resolver@11.19.1)(publint@0.3.18)(typescript@5.9.3) vitest: specifier: ^4.0.18 - version: 4.0.18(@types/node@22.13.14)(jiti@2.6.1) + version: 4.0.18(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3) devDependencies: '@changesets/cli': specifier: ^2.28.1 version: 2.28.1 + '@tanstack/intent': + specifier: ^0.0.23 + version: 0.0.23 '@types/node': specifier: ^22.13.14 version: 22.13.14 @@ -895,6 +898,10 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + '@tanstack/intent@0.0.23': + resolution: {integrity: sha512-q5e0sh5e+xBOR5Z7eoZTBcXtakgTwucm2m0bNUkj7h4UagSgIDmPRYbtC7B81AF4FB5rW2OP1zgl7Jd9EH4qEw==} + hasBin: true + '@tybys/wasm-util@0.10.1': resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} @@ -1662,6 +1669,11 @@ packages: engines: {node: '>=8'} hasBin: true + yaml@2.8.3: + resolution: {integrity: sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==} + engines: {node: '>= 14.6'} + hasBin: true + zod@4.3.6: resolution: {integrity: sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==} @@ -2295,6 +2307,11 @@ snapshots: '@standard-schema/spec@1.1.0': {} + '@tanstack/intent@0.0.23': + dependencies: + cac: 6.7.14 + yaml: 2.8.3 + '@tybys/wasm-util@0.10.1': dependencies: tslib: 2.8.1 @@ -2357,13 +2374,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.0.3 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1))': + '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3))': dependencies: '@vitest/spy': 4.0.18 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@22.13.14)(jiti@2.6.1) + vite: 7.3.1(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3) '@vitest/pretty-format@4.0.18': dependencies: @@ -2990,7 +3007,7 @@ snapshots: dependencies: rolldown: 1.0.0-rc.5 - vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1): + vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3): dependencies: esbuild: 0.27.3 fdir: 6.5.0(picomatch@4.0.3) @@ -3002,11 +3019,12 @@ snapshots: '@types/node': 22.13.14 fsevents: 2.3.3 jiti: 2.6.1 + yaml: 2.8.3 - vitest@4.0.18(@types/node@22.13.14)(jiti@2.6.1): + vitest@4.0.18(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3): dependencies: '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1)) + '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3)) '@vitest/pretty-format': 4.0.18 '@vitest/runner': 4.0.18 '@vitest/snapshot': 4.0.18 @@ -3023,7 +3041,7 @@ snapshots: tinyexec: 1.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@22.13.14)(jiti@2.6.1) + vite: 7.3.1(@types/node@22.13.14)(jiti@2.6.1)(yaml@2.8.3) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 22.13.14 @@ -3051,4 +3069,6 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + yaml@2.8.3: {} + zod@4.3.6: {} diff --git a/skills/_artifacts/domain_map.yaml b/skills/_artifacts/domain_map.yaml new file mode 100644 index 0000000..d3659db --- /dev/null +++ b/skills/_artifacts/domain_map.yaml @@ -0,0 +1,542 @@ +# domain_map.yaml +# Generated by skill-domain-discovery +# Library: @bomb.sh/tools +# Version: 0.2.8 +# Date: 2026-03-25 +# Status: reviewed + +library: + name: '@bomb.sh/tools' + version: '0.2.8' + repository: 'https://github.com/bombshell-dev/tools' + description: 'Unified internal CLI (bsh) that standardizes dev workflows across all Bombshell ecosystem projects by orchestrating opinionated configurations of industry-standard tools.' + primary_framework: 'framework-agnostic' + +domains: + - name: 'development workflow' + slug: 'workflow' + description: 'The end-to-end development lifecycle — from project creation through TDD loop to production readiness' + + - name: 'code quality' + slug: 'quality' + description: 'Linting, formatting, and opinionated conventions that keep code coherent across repositories' + + - name: 'compilation' + slug: 'compilation' + description: 'Building TypeScript to ESM and running TypeScript natively during development' + + - name: 'testing' + slug: 'testing' + description: 'Writing and running tests with colocated conventions and filesystem fixture utilities' + + - name: 'project setup' + slug: 'setup' + description: 'Creating new projects and migrating existing ones to the bsh toolchain' + +skills: + - name: 'Development Lifecycle' + slug: 'lifecycle' + domain: 'workflow' + description: 'The TDD-driven development workflow — command ordering, monorepo vs single-package detection, and the red/green/refactor loop' + type: 'lifecycle' + covers: + - 'TDD workflow (test-implement-lint-format-build-test)' + - 'pnpm run