diff --git a/.gitignore b/.gitignore index 6f5b91f..7f12626 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,6 @@ htmlcov/ *.egg-info/ dist/ build/ + +# AI tools +.claude diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..863dea1 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,4 @@ +# Claude instructions + +@ai-resources/CLAUDE.md + diff --git a/ai-resources/.gitignore b/ai-resources/.gitignore new file mode 100644 index 0000000..4c5f206 --- /dev/null +++ b/ai-resources/.gitignore @@ -0,0 +1 @@ +.claude/ diff --git a/ai-resources/AGENTS.md b/ai-resources/AGENTS.md new file mode 100644 index 0000000..40d6631 --- /dev/null +++ b/ai-resources/AGENTS.md @@ -0,0 +1,3 @@ +# Codex instructions + +@CLAUDE.md diff --git a/ai-resources/CLAUDE.md b/ai-resources/CLAUDE.md new file mode 100644 index 0000000..bbf81d1 --- /dev/null +++ b/ai-resources/CLAUDE.md @@ -0,0 +1,11 @@ +# Claude instructions + +## All rules + +@rules/all.md + +## All skills + +@skills/fix-vulns/SKILL.md +@skills/link-skills/SKILL.md +@skills/md2pdf/SKILL.md diff --git a/ai-resources/README.md b/ai-resources/README.md new file mode 100644 index 0000000..ce3efe3 --- /dev/null +++ b/ai-resources/README.md @@ -0,0 +1,68 @@ +# ai-resources + +A centralised collection of AI coding resources (rules and skills), shareable across multiple repos via `git subtree`. + +**Rules** live in [`rules`](./rules/) as focused markdown files. + +**Skills** live in [`skills`](./skills/). + +> **Other AI tools** (Cursor, Copilot, Windsurf, etc.) use different mechanisms to load rules. Refer to their documentation for how to reference external markdown files. + +## Using these resources in your repo + +### Add this repo as a subtree + +Run this once in your target repo, from the repo root: + +```sh +git checkout -b chore/add-ai-resources +git subtree add --prefix ai-resources https://github.com/mcalthrop/ai-resources main --squash +``` + +`git subtree add` creates a commit automatically, so create a branch first to keep the change reviewable via a PR. + +### Import all rules and skills + +To import all rules and skills, add a single line to your `CLAUDE.md` or `AGENTS.md`: + +```md +@ai-resources/CLAUDE.md +``` + +### Set up skills as slash commands + +Skills become available as slash commands (e.g. `/md2pdf`) once they are symlinked into the consuming repo's `.claude/skills/` directory. + +If your `CLAUDE.md` imports `@ai-resources/CLAUDE.md`, Claude already knows how to set up the symlinks. Just tell it: + +> link skills + +Claude will create the symlinks automatically via the bundled `link-skills` skill. + +### Import specific rules + +To import only the rules you need, import them to your `CLAUDE.md` or `AGENTS.md`: + +```md +@ai-resources/rules/general.md +@ai-resources/rules/pull-requests.md +@ai-resources/rules/conventional-commits.md +@ai-resources/rules/conventional-branch-names.md +``` + +The tool reads the instructions file automatically, so the imported rules are always active. + +### Update the resources + +When the resources in this repo change, your repo won't automatically see them. + +So to pick them up, create a branch and pull in the latest changes: + +```sh +git checkout -b chore/update-ai-resources +git subtree pull --prefix ai-resources https://github.com/mcalthrop/ai-resources main --squash +``` + +Like `git subtree add`, this creates a commit automatically. + +If you are importing individual files, check whether any rules were added, removed, or renamed and update your instructions file accordingly. diff --git a/ai-resources/rules/all.md b/ai-resources/rules/all.md new file mode 100644 index 0000000..e34570e --- /dev/null +++ b/ai-resources/rules/all.md @@ -0,0 +1,8 @@ +# All Rules + +@general.md +@zsh-node-commands.md +@code-organisation.md +@pull-requests.md +@conventional-commits.md +@conventional-branch-names.md diff --git a/ai-resources/rules/code-organisation.md b/ai-resources/rules/code-organisation.md new file mode 100644 index 0000000..88d944f --- /dev/null +++ b/ai-resources/rules/code-organisation.md @@ -0,0 +1,4 @@ +# Code Organisation + +- Each function should live in its own file. +- Test files live adjacent to the source file they test, mirroring the name (e.g. `fetchRecipes.ts` → `fetchRecipes.test.ts`). diff --git a/ai-resources/rules/conventional-branch-names.md b/ai-resources/rules/conventional-branch-names.md new file mode 100644 index 0000000..06281cf --- /dev/null +++ b/ai-resources/rules/conventional-branch-names.md @@ -0,0 +1,45 @@ +# Conventional Branch Names + +Always name branches following the [Conventional Branch specification](https://conventional-branch.github.io/). + +## Format + +```txt +/ +``` + +Use the same types as Conventional Commits. The short description should be lowercase, hyphen-separated, and concise. + +## Types + +| Type | Use when | +| ------ | ---------- | +| `feat` | Implementing a new feature | +| `fix` | Fixing a bug | +| `docs` | Documentation-only changes | +| `style` | Formatting or style changes | +| `refactor` | Code restructuring without behaviour change | +| `test` | Adding or updating tests | +| `chore` | Build, dependency, or tooling updates | +| `ci` | CI/CD pipeline changes | +| `perf` | Performance improvements | +| `revert` | Reverting a previous change | + +## Rules + +- Use only lowercase letters, numbers, and hyphens. +- Keep the description short — ideally 2–5 words. +- Do not include ticket/issue numbers in the branch name unless the project convention requires it. +- Never use `main` or `master` as a base name. + +## Examples + +```txt +feat/user-authentication +fix/null-pointer-on-login +docs/update-api-reference +chore/upgrade-dependencies +refactor/extract-payment-service +test/add-checkout-unit-tests +ci/add-deploy-workflow +``` diff --git a/ai-resources/rules/conventional-commits.md b/ai-resources/rules/conventional-commits.md new file mode 100644 index 0000000..3866200 --- /dev/null +++ b/ai-resources/rules/conventional-commits.md @@ -0,0 +1,50 @@ +# Conventional Commits + +Always write commit messages following the [Conventional Commits specification](https://www.conventionalcommits.org/en/v1.0.0/). + +## Format + +```txt +[optional scope]: + +[optional body] + +[optional footer(s)] +``` + +## Types + +| Type | Use when | +| ------ | ---------- | +| `feat` | A new feature | +| `fix` | A bug fix | +| `docs` | Documentation changes only | +| `style` | Formatting, missing semicolons, etc. — no logic change | +| `refactor` | Code change that neither fixes a bug nor adds a feature | +| `test` | Adding or correcting tests | +| `chore` | Build process, dependency, or tooling changes | +| `ci` | CI/CD configuration changes | +| `perf` | Performance improvements | +| `revert` | Reverts a previous commit | + +## Rules + +- The description must be lowercase and not end with a period. +- Use the imperative mood in the description (e.g. "add feature" not "added feature"). +- Keep the description under 72 characters. +- Add a scope in parentheses after the type when it helps clarify the area of change, e.g. `feat(auth): add OAuth2 support`. +- Mark breaking changes with `!` after the type/scope, e.g. `feat!: remove deprecated endpoint`, and include a `BREAKING CHANGE:` footer. + +## Examples + +```txt +feat(auth): add OAuth2 login support +fix: prevent crash when config file is missing +docs: update installation instructions +chore(deps): upgrade vitest to v2 +refactor(api): extract response normalisation to helper +test(checkout): add edge case for empty cart +feat!: remove legacy REST endpoint + +BREAKING CHANGE: the /v1/users endpoint has been removed; use /v2/users instead +``` diff --git a/ai-resources/rules/general.md b/ai-resources/rules/general.md new file mode 100644 index 0000000..4e0bd13 --- /dev/null +++ b/ai-resources/rules/general.md @@ -0,0 +1,7 @@ +# General Rules + +- Do what has been asked; nothing more, nothing less. +- NEVER create files unless they're absolutely necessary for achieving your goal. +- ALWAYS prefer editing an existing file to creating a new one. +- NEVER proactively create documentation files (*.md) or README files. Only create documentation files if explicitly requested. +- ALWAYS use British English. diff --git a/ai-resources/rules/pull-requests.md b/ai-resources/rules/pull-requests.md new file mode 100644 index 0000000..4ddda8a --- /dev/null +++ b/ai-resources/rules/pull-requests.md @@ -0,0 +1,36 @@ +# Pull Requests + +## Creating a PR + +- ALWAYS create pull requests in draft mode using the `--draft` flag with `gh pr create`. +- ALWAYS show a link to the created pull request. + +## After a PR is Merged + +Run the following steps after a pull request has been merged: + +1. Fetch and prune remote-tracking branches: + + ```sh + git fetch --all --prune --prune-tags + ``` + +2. Switch to the main branch: + + ```sh + git checkout main + ``` + +3. Delete the local branch for the merged PR: + + ```sh + git branch -D + ``` + +4. Pull the latest changes from main: + + ```sh + git pull origin main + ``` + +5. Install packages using the appropriate package manager for the repo (e.g. `pnpm install`, `npm install`, `yarn install`, `pip install -r requirements.txt`). diff --git a/ai-resources/rules/zsh-bash-commands.md b/ai-resources/rules/zsh-bash-commands.md new file mode 100644 index 0000000..29b61e4 --- /dev/null +++ b/ai-resources/rules/zsh-bash-commands.md @@ -0,0 +1,12 @@ +# Running Commands with the `bash` shell + +When running commands in the `bash` shell: + +```sh +zsh -i -c "..." +``` + +Reasons: + +- `-i`: Forces shell to be interactive. Ensures that the `.zshrc` file is loaded. +- `-c`: Takes the first argument as a command to execute. diff --git a/ai-resources/skills/fix-vulns/SKILL.md b/ai-resources/skills/fix-vulns/SKILL.md new file mode 100644 index 0000000..d7bd481 --- /dev/null +++ b/ai-resources/skills/fix-vulns/SKILL.md @@ -0,0 +1,54 @@ +--- +name: fix-vulns +description: Check for vulnerabilities with pnpm audit and raise a PR to fix them. Use when the user asks to "fix vulnerabilities", "fix vulns", "audit dependencies", or "fix security issues". +disable-model-invocation: true +allowed-tools: Bash, Read, Edit +--- + +# fix-vulns + +Check for `pnpm` dependency vulnerabilities and raise a PR that addresses all of them. + +## Instructions + +1. Run the audit: + +```bash +zsh -i -c "pnpm audit" +``` + +2. If no vulnerabilities are found, report that and stop. + +3. If vulnerabilities are found, fetch the latest `main` and create a branch: + +```bash +git fetch origin main +git checkout -b fix/security-vulnerabilities origin/main +``` + +4. Attempt to fix all vulnerabilities automatically: + +```bash +zsh -i -c "pnpm audit --fix" +``` + +5. If any vulnerabilities remain after `--fix`, resolve them by manually updating the affected packages to the minimum safe version identified in the audit output: + +```bash +zsh -i -c "pnpm update " +``` + +6. Re-run the audit to confirm all vulnerabilities are resolved: + +```bash +zsh -i -c "pnpm audit" +``` + +7. Commit the changes: + +```bash +git add pnpm-lock.yaml package.json +git commit -m "fix(deps): resolve pnpm audit vulnerabilities" +``` + +8. Push the branch and raise a draft PR. The PR body must list every vulnerability that was fixed, including package name, severity, and the resolution applied. diff --git a/ai-resources/skills/link-skills/SKILL.md b/ai-resources/skills/link-skills/SKILL.md new file mode 100644 index 0000000..d241864 --- /dev/null +++ b/ai-resources/skills/link-skills/SKILL.md @@ -0,0 +1,26 @@ +--- +name: link-skills +description: Link all skills from the ai-resources git subtree into .claude/skills/. Use when the user asks to "link skills", "set up skills from ai-resources", or "link to all skills in the ai-resources subtree". +disable-model-invocation: true +allowed-tools: Bash +--- + +# link-skills + +Create symlinks in `.claude/skills/` for every skill in the `ai-resources` subtree, making them available as slash commands. + +## Instructions + +1. Confirm that an `ai-resources/skills/` directory exists in the current working directory. If not, abort and tell the user to add the `ai-resources` subtree first. +2. Create `.claude/skills/` in the current working directory if it does not already exist. +3. For each subdirectory in `ai-resources/skills/`, create a relative symlink: + +```bash +for skill_dir in ai-resources/skills/*/; do + skill_name=$(basename "$skill_dir") + mkdir -p .claude/skills + ln -sfn "../../ai-resources/skills/$skill_name" ".claude/skills/$skill_name" +done +``` + +4. Report which symlinks were created. diff --git a/ai-resources/skills/md2pdf/SKILL.md b/ai-resources/skills/md2pdf/SKILL.md new file mode 100644 index 0000000..cc3de4a --- /dev/null +++ b/ai-resources/skills/md2pdf/SKILL.md @@ -0,0 +1,38 @@ +--- +name: md2pdf +description: Convert a Markdown file to PDF. Use when the user asks to convert a .md file to PDF, generate a PDF from Markdown, or run md-to-pdf. +argument-hint: +disable-model-invocation: true +allowed-tools: Bash +--- + +# md2pdf + +Convert a Markdown file to PDF using `npx md-to-pdf` with the config bundled in this skill directory. + +## Instructions + +The user invoked this with: $ARGUMENTS + +1. Resolve the input file path from `$ARGUMENTS`. If a relative path is given, resolve it relative to the current working directory. +2. Ensure Chrome is available for Puppeteer: + +```bash +zsh -i -c "npx puppeteer browsers install chrome" +``` + +3. Determine the absolute path to this skill's directory (where this SKILL.md lives) — it contains `md-to-pdf.config.js`. +4. Run the conversion: + +```bash +zsh -i -c "npx md-to-pdf --config-file '/md-to-pdf.config.js' " +``` + +5. Report the path of the generated PDF (same directory as the input file, with a `.pdf` extension). + +## Notes + +- The config adds a footer with a left-aligned `Generated: `, a centre-aligned filename, and a right-aligned `Page X of Y`. +- Bottom margin is increased to prevent the footer overlapping content. +- On Mac Silicon with an x64 Node install, a "Degraded performance warning" about Rosetta may appear — this is harmless. +- The `zsh -i -c "..."` wrapper ensures the correct Node version from `.nvmrc` is active. diff --git a/ai-resources/skills/md2pdf/md-to-pdf.config.js b/ai-resources/skills/md2pdf/md-to-pdf.config.js new file mode 100644 index 0000000..a26f38f --- /dev/null +++ b/ai-resources/skills/md2pdf/md-to-pdf.config.js @@ -0,0 +1,60 @@ +// Shared configuration for md-to-pdf in this workspace. +// Provides a footer with a left-aligned generation timestamp +// and right-aligned "Page X of Y", plus extra bottom margin. + +const path = require('node:path'); +const generatedAt = new Date().toLocaleString(); +const inputFilename = path.basename(process.argv[process.argv.length - 1]); + +module.exports = { + stylesheet_encoding: 'utf-8', + css: ` + blockquote { + font-family: CommitMono, "Andale Mono", Menlo, Monaco, "SF Mono", "Courier New", ui-monospace, monospace; + font-style: normal; + background-color: #f6f8fa; + } + `, + pdf_options: { + // Top/right/bottom/left margins. Increase bottom so the footer + // does not overlap the page content. + margin: '20mm 20mm 30mm 20mm', + printBackground: true, + footerTemplate: ` + + + `, + }, +};