From 4cf156b7b0c0d2453d08ffcdb257067bac88d2a6 Mon Sep 17 00:00:00 2001 From: Eli Fine Date: Thu, 19 Mar 2026 00:03:55 +0000 Subject: [PATCH] test --- .claude/.beads/.gitignore | 60 +++++ .claude/.beads/config.yaml | 44 ++++ .claude/.beads/metadata.json | 8 + .claude/commands/add-command.md | 167 ++++++++++++ .claude/commands/commit.md | 57 +++++ .claude/commands/create-adr.md | 240 ++++++++++++++++++ .claude/commands/create-issues.md | 195 ++++++++++++++ .claude/commands/gap.md | 45 ++++ .claude/commands/green.md | 110 ++++++++ .claude/commands/issue.md | 162 ++++++++++++ .claude/commands/polish.md | 185 ++++++++++++++ .claude/commands/red.md | 111 ++++++++ .claude/commands/refactor.md | 127 +++++++++ .claude/commands/research.md | 96 +++++++ .claude/commands/simplify.md | 84 ++++++ .claude/commands/spike.md | 103 ++++++++ .claude/commands/summarize.md | 62 +++++ .claude/commands/tdd-review.md | 110 ++++++++ .claude/commands/tdd.md | 102 ++++++++ .claude/helpers/merge-claude-settings.sh | 98 +++++++ .claude/package-lock.json | 24 ++ .claude/package.json | 6 + .claude/settings/basics.jsonc | 5 + .claude/settings/permissions/bash.jsonc | 120 +++++++++ .claude/settings/permissions/read.jsonc | 9 + .claude/settings/permissions/write.jsonc | 8 + .coderabbit.yaml | 2 +- .copier-answers.yml | 4 +- .coveragerc | 30 +++ .devcontainer/devcontainer.json | 11 +- .devcontainer/docker-compose.yml | 17 ++ .devcontainer/install-ci-tooling.py | 6 +- .devcontainer/on-create-command.sh | 10 + .devcontainer/post-start-command.sh | 8 + .github/actions/install_deps/action.yml | 4 +- .../build-docker-image.yaml | 11 +- .github/workflows/ci.yaml | 46 +++- .github/workflows/pre-commit.yaml | 2 +- .gitignore | 10 + .pre-commit-config.yaml | 13 +- AGENTS.md | 121 +++++++++ CLAUDE.md | 1 + copier.yml | 8 +- extensions/context.py | 46 ++-- pyproject.toml | 4 +- src/copier_tasks/__init__.py | 0 src/copier_tasks/remove_precommit_hooks.py | 105 ++++++++ template/.claude/.beads/.gitignore | 60 +++++ template/.claude/.beads/config.yaml | 44 ++++ template/.claude/.beads/metadata.json | 8 + template/.claude/commands/add-command.md | 167 ++++++++++++ template/.claude/commands/commit.md | 57 +++++ template/.claude/commands/create-adr.md | 240 ++++++++++++++++++ template/.claude/commands/create-issues.md | 195 ++++++++++++++ template/.claude/commands/gap.md | 45 ++++ template/.claude/commands/green.md | 110 ++++++++ template/.claude/commands/issue.md | 162 ++++++++++++ template/.claude/commands/polish.md | 185 ++++++++++++++ template/.claude/commands/red.md | 111 ++++++++ template/.claude/commands/refactor.md | 127 +++++++++ template/.claude/commands/research.md | 96 +++++++ template/.claude/commands/simplify.md | 84 ++++++ template/.claude/commands/spike.md | 103 ++++++++ template/.claude/commands/summarize.md | 62 +++++ template/.claude/commands/tdd-review.md | 110 ++++++++ template/.claude/commands/tdd.md | 102 ++++++++ .../.claude/helpers/merge-claude-settings.sh | 98 +++++++ template/.claude/package-lock.json | 24 ++ template/.claude/package.json | 6 + template/.claude/settings/basics.jsonc | 5 + .../.claude/settings/permissions/bash.jsonc | 120 +++++++++ .../.claude/settings/permissions/read.jsonc | 9 + .../.claude/settings/permissions/write.jsonc | 8 + template/.coderabbit.yaml | 2 +- .../.devcontainer/devcontainer.json.jinja | 10 +- .../.devcontainer/docker-compose.yml.jinja | 20 +- .../.devcontainer/on-create-command.sh.jinja | 12 +- .../.devcontainer/post-start-command.sh.jinja | 10 +- .../.github/actions/ecr-auth/action.yml.jinja | 23 ++ .../.github/actions/install_deps/action.yml | 4 +- template/.github/workflows/pre-commit.yaml | 2 +- template/.gitignore | 10 + template/.pre-commit-config.yaml | 13 +- template/AGENTS.md | 121 +++++++++ template/CLAUDE.md | 1 + tests/unit/copier_tasks/__init__.py | 0 .../test_remove_precommit_hooks.py | 90 +++++++ uv.lock | 50 ++-- 88 files changed, 5411 insertions(+), 92 deletions(-) create mode 100644 .claude/.beads/.gitignore create mode 100644 .claude/.beads/config.yaml create mode 100644 .claude/.beads/metadata.json create mode 100644 .claude/commands/add-command.md create mode 100644 .claude/commands/commit.md create mode 100644 .claude/commands/create-adr.md create mode 100644 .claude/commands/create-issues.md create mode 100644 .claude/commands/gap.md create mode 100644 .claude/commands/green.md create mode 100644 .claude/commands/issue.md create mode 100644 .claude/commands/polish.md create mode 100644 .claude/commands/red.md create mode 100644 .claude/commands/refactor.md create mode 100644 .claude/commands/research.md create mode 100644 .claude/commands/simplify.md create mode 100644 .claude/commands/spike.md create mode 100644 .claude/commands/summarize.md create mode 100644 .claude/commands/tdd-review.md create mode 100644 .claude/commands/tdd.md create mode 100644 .claude/helpers/merge-claude-settings.sh create mode 100644 .claude/package-lock.json create mode 100644 .claude/package.json create mode 100644 .claude/settings/basics.jsonc create mode 100644 .claude/settings/permissions/bash.jsonc create mode 100644 .claude/settings/permissions/read.jsonc create mode 100644 .claude/settings/permissions/write.jsonc create mode 100644 .coveragerc create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 src/copier_tasks/__init__.py create mode 100644 src/copier_tasks/remove_precommit_hooks.py create mode 100644 template/.claude/.beads/.gitignore create mode 100644 template/.claude/.beads/config.yaml create mode 100644 template/.claude/.beads/metadata.json create mode 100644 template/.claude/commands/add-command.md create mode 100644 template/.claude/commands/commit.md create mode 100644 template/.claude/commands/create-adr.md create mode 100644 template/.claude/commands/create-issues.md create mode 100644 template/.claude/commands/gap.md create mode 100644 template/.claude/commands/green.md create mode 100644 template/.claude/commands/issue.md create mode 100644 template/.claude/commands/polish.md create mode 100644 template/.claude/commands/red.md create mode 100644 template/.claude/commands/refactor.md create mode 100644 template/.claude/commands/research.md create mode 100644 template/.claude/commands/simplify.md create mode 100644 template/.claude/commands/spike.md create mode 100644 template/.claude/commands/summarize.md create mode 100644 template/.claude/commands/tdd-review.md create mode 100644 template/.claude/commands/tdd.md create mode 100644 template/.claude/helpers/merge-claude-settings.sh create mode 100644 template/.claude/package-lock.json create mode 100644 template/.claude/package.json create mode 100644 template/.claude/settings/basics.jsonc create mode 100644 template/.claude/settings/permissions/bash.jsonc create mode 100644 template/.claude/settings/permissions/read.jsonc create mode 100644 template/.claude/settings/permissions/write.jsonc create mode 100644 template/.github/actions/ecr-auth/action.yml.jinja create mode 100644 template/AGENTS.md create mode 100644 template/CLAUDE.md create mode 100644 tests/unit/copier_tasks/__init__.py create mode 100644 tests/unit/copier_tasks/test_remove_precommit_hooks.py diff --git a/.claude/.beads/.gitignore b/.claude/.beads/.gitignore new file mode 100644 index 00000000..bb8b4ce8 --- /dev/null +++ b/.claude/.beads/.gitignore @@ -0,0 +1,60 @@ +# Dolt database (managed by Dolt, not git) +dolt/ +dolt-access.lock + +# Runtime files +bd.sock +bd.sock.startlock +sync-state.json +last-touched + +# Local version tracking (prevents upgrade notification spam after git ops) +.local_version + +# Worktree redirect file (contains relative path to main repo's .beads/) +# Must not be committed as paths would be wrong in other clones +redirect + +# Sync state (local-only, per-machine) +# These files are machine-specific and should not be shared across clones +.sync.lock +.jsonl.lock +sync_base.jsonl +export-state/ + +# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned) +ephemeral.sqlite3 +ephemeral.sqlite3-journal +ephemeral.sqlite3-wal +ephemeral.sqlite3-shm + +# Legacy files (from pre-Dolt versions) +*.db +*.db?* +*.db-journal +*.db-wal +*.db-shm +db.sqlite +bd.db +daemon.lock +daemon.log +daemon-*.log.gz +daemon.pid +beads.base.jsonl +beads.base.meta.json +beads.left.jsonl +beads.left.meta.json +beads.right.jsonl +beads.right.meta.json + +# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here. +# They would override fork protection in .git/info/exclude, allowing +# contributors to accidentally commit upstream issue databases. +# The JSONL files (issues.jsonl, interactions.jsonl) and config files +# are tracked by git by default since no pattern above ignores them. + +# at the moment, we're just using beads for local development, so don't commit any jsonl +interactions.jsonl +issues.jsonl +backup/ +issues-dump.jsonl diff --git a/.claude/.beads/config.yaml b/.claude/.beads/config.yaml new file mode 100644 index 00000000..25090ef6 --- /dev/null +++ b/.claude/.beads/config.yaml @@ -0,0 +1,44 @@ +# Beads Configuration File +# This file configures default behavior for all bd commands in this repository +# All settings can also be set via environment variables (BD_* prefix) +# or overridden with command-line flags + +# Issue prefix for this repository (used by bd init) +# If not set, bd init will auto-detect from directory name +# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. +# issue-prefix: "" + +# Use no-db mode: load from JSONL, write back after each command +# When true, bd will use .beads/issues.jsonl as the source of truth +# instead of the Dolt database +# no-db: false + +# Enable JSON output by default +# json: false + +# Default actor for audit trails (overridden by BD_ACTOR or --actor) +# actor: "" + +# Export events (audit trail) to .beads/events.jsonl on each flush/sync +# When enabled, new events are appended incrementally using a high-water mark. +# Use 'bd export --events' to trigger manually regardless of this setting. +# events-export: false + +# Multi-repo configuration (experimental - bd-307) +# Allows hydrating from multiple repositories and routing writes to the correct JSONL +# repos: +# primary: "." # Primary repo (where this database lives) +# additional: # Additional repos to hydrate from (read-only) +# - ~/beads-planning # Personal planning repo +# - ~/work-planning # Work planning repo + +# Integration settings (access with 'bd config get/set') +# These are stored in the database, not in this file: +# - jira.url +# - jira.project +# - linear.url +# - linear.api-key +# - github.org +# - github.repo +no-git-ops: true +backup.enabled: false # in v0.57 the backup seems to automatically add stage the files into git, bypassing the .gitignore I tried to set up, so disabling for now (also we don't really need backup anyway if we're not pushing into git, but this would be nicer long term than having to run the export command manually all the time) diff --git a/.claude/.beads/metadata.json b/.claude/.beads/metadata.json new file mode 100644 index 00000000..a4be941e --- /dev/null +++ b/.claude/.beads/metadata.json @@ -0,0 +1,8 @@ +{ + "database": "dolt", + "jsonl_export": "issues.jsonl", + "backend": "dolt", + "dolt_mode": "server", + "dolt_server_host": "beads-dolt", + "dolt_database": "beads_work" +} diff --git a/.claude/commands/add-command.md b/.claude/commands/add-command.md new file mode 100644 index 00000000..a19105a3 --- /dev/null +++ b/.claude/commands/add-command.md @@ -0,0 +1,167 @@ +--- +description: Guide for creating new slash commands +argument-hint: +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +# Slash Command Creator Guide + +## How This Command Works + +The `/add-command` command shows this guide for creating new slash commands. It includes: + +- Command structure and syntax +- Common patterns and examples +- Security restrictions and limitations +- Frontmatter options + +**Note for AI**: When creating commands, you CAN use bash tools like `Bash(mkdir:*)`, `Bash(ls:*)`, `Bash(git status:*)` in the `allowed-tools` frontmatter of NEW commands - but ONLY for operations within the current project directory. This command itself doesn't need bash tools since it's just documentation. + +## Command Locations + +- **Personal**: `~/.claude/commands/` (available across all projects) +- **Project**: `.claude/commands/` (shared with team, shows "(project)") + +## Basic Structure + +```markdown +--- +allowed-tools: Read, Glob, Grep, Bash(git status:*), Task +description: Brief description of what this command does +argument-hint: [required-arg] [optional-arg] +--- + +# Command Title + +Your command instructions here. + +**User arguments:** + +Add-command: $ARGUMENTS + +**End of user arguments** + +File reference: @path/to/file.js + +Bash command output: (exclamation)git status(backticks) +``` + +## ⚠️ Security Restrictions + +**Bash Commands (exclamation prefix)**: Limited to current working directory only. + +- ✅ Works: `! + backtick + git status + backtick` (in project dir) +- ❌ Blocked: `! + backtick + ls /outside/project + backtick` (outside project) +- ❌ Blocked: `! + backtick + pwd + backtick` (if referencing dirs outside project) + +**File References (`@` prefix)**: No directory restrictions. + +- ✅ Works: `@/path/to/system/file.md` +- ✅ Works: `@../other-project/file.js` + +## Common Patterns + +### Simple Command + +```bash +echo "Review this code for bugs and suggest fixes" > ~/.claude/commands/review.md +``` + +### Command with Arguments + +**Note for AI**: The example below uses a fullwidth dollar sign ($, U+FF04) to prevent interpolation in this documentation. When creating actual commands, use the regular `$` character. + +```markdown +Fix issue $ARGUMENTS following our coding standards +``` + +### Command with File References + +```markdown +Compare @src/old.js with @src/new.js and explain differences +``` + +### Command with Bash Output (Project Directory Only) + +```markdown +--- +allowed-tools: Bash(git status:*), Bash(git branch:*), Bash(git log:*) +--- +Current status: (!)git status(`) +Current branch: (!)git branch --show-current(`) +Recent commits: (!)git log --oneline -5(`) + +Create commit for these changes. +``` + +**Note**: Only works with commands in the current project directory. + +### Namespaced Command + +**Note for AI**: The example below uses a fullwidth dollar sign ($, U+FF04) to prevent interpolation in this documentation. When creating actual commands, use the regular `$` character. + +```bash +mkdir -p ~/.claude/commands/ai +echo "Ask GPT-5 about: $ARGUMENTS" > ~/.claude/commands/ai/gpt5.md +# Creates: /ai:gpt5 +``` + +## Frontmatter Options + +- `allowed-tools`: Tools this command can use + - **Important**: Intrusive tools like `Write`, `Edit`, `NotebookEdit` should NEVER be allowed in commands unless the user explicitly requests them. These tools modify files and should only be used when the command's purpose is to make changes. + - ✅ Safe for most commands: `Read`, `Glob`, `Grep`, `Bash(git status:*)`, `Task`, `AskUserQuestion` +- `description`: Brief description (shows in /help) +- `argument-hint`: Help text for arguments +- `model`: Specific model to use + +## Best Practices + +### Safe Commands (No Security Issues) + +```markdown +# System prompt editor (file reference only) +(@)path/to/system/prompt.md + +Edit your system prompt above. +``` + +### Project-Specific Commands (Bash OK) + +```markdown +--- +allowed-tools: Bash(git status:*), Bash(npm list:*) +--- +Current git status: (!)git status(`) +Package info: (!)npm list --depth=0(`) + +Review project state and suggest next steps. +``` + +### Cross-Directory File Access (Use @ not !) + +```markdown +# Compare config files +Compare (@)path/to/system.md with (@)project/config.md + +Show differences and suggest improvements. +``` + +## Usage + +After creating: `/ [arguments]` + +Example: `/review` or `/ai:gpt5 "explain this code"` diff --git a/.claude/commands/commit.md b/.claude/commands/commit.md new file mode 100644 index 00000000..b11c370e --- /dev/null +++ b/.claude/commands/commit.md @@ -0,0 +1,57 @@ +--- +description: Create a git commit following project standards +argument-hint: [optional-commit-description] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Create a git commit following project standards + +**User arguments:** + +Commit: $ARGUMENTS + +**End of user arguments** + +## Commit Message Rules + +Follows [Conventional Commits](https://www.conventionalcommits.org/) standard. + +1. **Format**: `type(#issue): description` + - Use `#123` for local repo issues + - Use `owner/repo#123` for cross-repo issues + - Common types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore` + +2. **AI Credits**: **NEVER include AI credits in commit messages** + - No "Generated with Claude Code" + - No "Co-Authored-By: Claude" or "Co-Authored-By: Happy" + - Focus on the actual changes made, not conversation history + +3. **Content**: Write clear, concise commit messages describing what changed and why + +## Process + +1. Run `git status` and `git diff` to review changes +2. Run `git log --oneline -5` to see recent commit style +3. Stage relevant files with `git add` +4. Create commit with descriptive message +5. Verify with `git status` + +## Example + +```bash +git add +git commit -m "feat(#123): add validation to user input form" +``` diff --git a/.claude/commands/create-adr.md b/.claude/commands/create-adr.md new file mode 100644 index 00000000..307ed7ef --- /dev/null +++ b/.claude/commands/create-adr.md @@ -0,0 +1,240 @@ +--- +description: Create a new Architecture Decision Record (ADR) +argument-hint: +--- + +# Create ADR: Architecture Decision Record Creator + +Create a new ADR to document an architectural decision. ADRs capture the "why" behind technical choices, helping future developers understand constraints and tradeoffs. + +> ADRs were introduced by Michael Nygard in 2011. The core structure (Context, Decision, Consequences) remains the standard. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +**User arguments:** + +Create-adr: $ARGUMENTS + +**End of user arguments** + +(If no input provided, ask user for the architectural decision topic) + +## Process + +### Step 1: Detect Existing ADR Setup + +Check for existing ADR directory and structure: + +```bash +# Check common ADR directories (in order of preference) +for dir in doc/adr docs/adr decisions doc/architecture/decisions; do + if [ -d "$dir" ]; then + echo "Found: $dir" + ls "$dir"/*.md 2>/dev/null + break + fi +done +``` + +**If ADRs exist:** Read the first few ADRs (especially 0001 if present) to understand: + +- The template/structure this project uses +- Any project-specific sections or frontmatter +- Naming conventions and style + +Adapt the new ADR to match the existing pattern. + +**If no ADR directory exists:** Run the initialization flow (Step 1b). + +### Step 1b: Initialize ADR Practice (First-Time Setup) + +When no existing ADRs are found, help the user set up their ADR practice. + +Ask the user (use AskUserQuestion): + +**Directory location:** + +- doc/adr (Recommended - conventional location) +- docs/adr +- decisions +- doc/architecture/decisions + +**Template style:** + +- Minimal (Nygard's original: Context, Decision, Consequences) +- Standard (Minimal + Status, Date, References) +- With scope (Standard + applies_to patterns, code examples) + +**Create a foundational ADR-0001?** + +- Yes - document "We will use ADRs to record architectural decisions" +- No - proceed directly to creating the requested ADR + +If creating ADR-0001, generate it with: + +- Context: Why the team needs to document decisions +- Decision: Adopt ADRs following [chosen template style] +- Consequences: Better knowledge transfer, slight overhead per decision + +### Step 2: Determine ADR Number + +Calculate next number from existing ADRs: + +1. Extract highest existing number +2. Increment by 1 +3. Format as 4-digit zero-padded (e.g., `0001`, `0012`) + +### Step 3: Discovery Questions + +Gather context through conversation (use AskUserQuestion for structured choices): + +**Context & Problem** + +- What forces are at play? (technological, social, project constraints) +- What problem, pattern, or situation prompted this decision? +- What triggered the need to decide now? (bug, confusion, inconsistency, new requirement) +- Are there related PRs, issues, or prior discussions to reference? + +**The Decision** + +- What are we deciding to do (or not do)? +- What alternatives were considered? +- Why was this approach chosen over alternatives? + +**Consequences** + +- What becomes easier or more consistent with this decision? +- What becomes harder, more constrained, or riskier? +- What tradeoffs are we explicitly accepting? + +**Scope** + +- Which parts of the codebase does this apply to? +- Are there exceptions or areas where this doesn't apply? + +### Step 4: Generate ADR File + +Create `{adr_directory}/NNNN-title-slug.md`: + +- Convert title to kebab-case slug (lowercase, hyphens, no special chars) +- Use today's date for the `date` field +- Default status to `accepted` (most ADRs are written after the decision is made) + +**ADR Template:** + +```markdown +--- +status: accepted +date: YYYY-MM-DD +applies_to: + - "**/*.ts" + - "**/*.tsx" +--- + +# N. Title + +## Context + +[Forces at play - technological, social, project constraints. +What problem prompted this? Value-neutral description of the situation.] + +## Decision + +We will [decision statement in active voice]. + +[If the decision involves code patterns, include concrete examples:] + +**Forbidden pattern:** +\`\`\`typescript +// ❌ BAD - [explanation] +[example of what NOT to do] +\`\`\` + +**Required pattern:** +\`\`\`typescript +// ✅ GOOD - [explanation] +[example of what TO do] +\`\`\` + +## Consequences + +**Positive:** +- [What becomes easier] +- [What becomes more consistent] + +**Negative:** +- [What becomes harder] +- [What constraints we accept] + +**Neutral:** +- [Other impacts worth noting] + +## References + +- [Related PRs, issues, or documentation] +``` + +### Step 5: Refine applies_to Scope + +Help user define which files this decision applies to using glob patterns: + +- All TypeScript files: **/*.ts +- All React component files: **/*.tsx +- Only files in components directory: src/components/** +- Exclude test files (prefix with !): !**/*.test.ts +- Exclude type definition files: !**/*.d.ts +- Specific package only: packages/api/** + +If the decision applies broadly, use **/* (all files). + +**Note**: `applies_to` is recommended for the "With scope" template. Linters and AI assistants use these patterns to determine which files to check against this ADR. + +### Step 6: Confirm and Write + +Show the complete ADR content and ask user to confirm before writing. + +After creation, suggest: + +- Review the ADR for completeness +- Commit with `/commit` + +## Tips for Good ADRs + +1. **Focus on the "why"** - The decision itself may be obvious; the reasoning often isn't +2. **Keep it concise** - 1-2 pages maximum; should be readable in 5 minutes +3. **Use active voice** - "We will use X" not "X will be used" +4. **Include concrete examples** - Code examples make abstract decisions tangible +5. **Document tradeoffs honestly** - Every decision has costs; be explicit about them +6. **Link to context** - Reference PRs, issues, or discussions where the decision was made +7. **Be specific about scope** - Use `applies_to` patterns to clarify affected code + +## Status Values + +| Status | When to Use | +|--------|-------------| +| `proposed` | Under discussion, not yet agreed | +| `accepted` | Agreed upon and should be followed | +| `deprecated` | No longer relevant (context changed) | +| `superseded` | Replaced by another ADR (link to it) | + +To supersede an existing ADR: + +1. Create new ADR with the updated decision +2. Update old ADR's status to `superseded by ADR-NNNN` + +## Integration with Other Commands + +- After creating: Commit with `/commit` +- If decision needs discussion: Create issue with `/create-issues` diff --git a/.claude/commands/create-issues.md b/.claude/commands/create-issues.md new file mode 100644 index 00000000..76d7cd2a --- /dev/null +++ b/.claude/commands/create-issues.md @@ -0,0 +1,195 @@ +--- +description: Create implementation plan from feature/requirement with PRD-style discovery and TDD acceptance criteria +argument-hint: <feature/requirement description or GitHub issue URL/number> +--- + +# Create Issues: PRD-Informed Task Planning for TDD + +Create structured implementation plan that bridges product thinking (PRD) with test-driven development. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +**User arguments:** + +Create-issues: $ARGUMENTS + +**End of user arguments** + +(If no input provided, check conversation context or run `bd ready` to see existing work) + +## Input Processing + +The input can be one of: + +1. **GitHub Issue URL** (e.g., `https://github.com/owner/repo/issues/123`) +2. **GitHub Issue Number** (e.g., `#123` or `123`) +3. **Feature Description** (e.g., "Add user authentication") +4. **Empty** - use conversation context + +### GitHub Issue Integration + +If input looks like a GitHub issue: + +**Step 1: Extract Issue Number** + +- From URL: extract owner/repo/number +- From number: try to infer repo from git remote +- From branch name: check patterns like `issue-123`, `123-feature`, `feature/123` + +**Step 2: Fetch Issue** + +Use the GitHub CLI to fetch issue details: + +```bash +gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state +``` + +If the `gh` CLI is not installed or authenticated, show: + +``` +GitHub CLI not available or not authenticated! +Run: gh auth login +``` + +**Step 3: Use Issue as Discovery Input** + +- Title → Feature name +- Description → Problem statement and context +- Labels → Type/priority hints +- Comments → Additional requirements and discussion +- Linked issues → Dependencies + +Extract from GitHub issue: + +- Problem statement and context +- Acceptance criteria (if present) +- Technical notes (if present) +- Related issues/dependencies + +## Process + +## Discovery Phase + +Understand the requirement by asking (use AskUserQuestion if needed): + +**Problem Statement** + +- What problem does this solve? +- Who experiences this problem? +- What's the current pain point? + +**Desired Outcome** + +- What should happen after this is built? +- How will users interact with it? +- What does success look like? + +**Scope & Constraints** + +- What's in scope vs. out of scope? +- Any technical constraints? +- Dependencies on other systems/features? + +**Context Check** + +- Search codebase for related features/modules +- Check for existing test files that might be relevant + +### Create Beads Issues + +For each task, create a bd issue with: + +```bash +bd create "Task title" \ + --type [feature|bug|task|chore] \ + --priority [1-3] \ + --description "Context and what needs to be built" \ + --design "Technical approach, architecture notes" \ + --acceptance "Given-When-Then acceptance criteria" +``` + +**Issue Structure Best Practices:** + +**Title**: Action-oriented, specific + +- ✅ "Add JWT token validation middleware" +- ❌ "Authentication stuff" + +**Description**: Provide context + +- Why this task exists +- How it fits into the larger feature +- Links to related issues/docs + +**Design**: Technical approach + +- Key interfaces/types needed +- Algorithm or approach +- Libraries or patterns to use +- Known gotchas or considerations + +**Acceptance Criteria**: Test-ready scenarios + +- Given-When-Then format +- Concrete, verifiable conditions +- Cover main case + edge cases +- Map 1:1 to future tests + +**Dependencies**: Link related issues + +```bash +bd dep add ISSUE-123 ISSUE-456 --type blocks +``` + +### Validation + +After creating issues, verify: + +- ✅ Each issue has clear acceptance criteria +- ✅ Dependencies are mapped (use `bd dep add`) +- ✅ Issues are ordered by implementation sequence +- ✅ First few issues are ready to start (`bd ready` shows them) +- ✅ Each issue is small enough for TDD (if too big, break down more) + +## Key Principles + +**From PRD World:** + +- Start with user problems, not solutions +- Define success criteria upfront +- Understand constraints and scope + +**From TDD World:** + +- Make acceptance criteria test-ready +- Break work into small, testable pieces +- Each task should map to test(s) + +### Beads Integration + +Use Beads MCP to: + +- Track work with `bd ready` to find next task +- Create issues with `bd create "description"` +- Track dependencies with `bd dep add` + +See <https://github.com/steveyegge/beads> for more information. + +## Integration with Other Commands + +- **Before /create-issues**: Use `/spike` if you need technical exploration first +- **After /create-issues**: Use `/red` to start TDD on first task +- **During work**: Use `bd update` to add notes/findings back to issues +- **When stuck**: Check `bd show ISSUE-ID` to review acceptance criteria diff --git a/.claude/commands/gap.md b/.claude/commands/gap.md new file mode 100644 index 00000000..e8abaeec --- /dev/null +++ b/.claude/commands/gap.md @@ -0,0 +1,45 @@ +--- +description: Analyze conversation context for unaddressed items and gaps +argument-hint: [optional additional info] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Analyze the current conversation context and identify things that have not yet been addressed. Look for: + +1. **Incomplete implementations** - Code that was started but not finished +2. **Unused variables/results** - Values that were captured but never used +3. **Missing tests** - Functionality without test coverage +4. **Open issues** - Beads issues that are still open or in progress + +5. **User requests** - Things the user asked for that weren't fully completed +6. **TODO comments** - Any TODOs mentioned in conversation +7. **Error handling gaps** - Missing error cases or edge cases +8. **Documentation gaps** - Undocumented APIs or features +9. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase + +Present findings as a prioritized list with: + +- What the gap is +- Why it matters +- Suggested next action + +If there are no gaps, confirm that everything discussed has been addressed. + +**User arguments:** + +Gap: $ARGUMENTS + +**End of user arguments** diff --git a/.claude/commands/green.md b/.claude/commands/green.md new file mode 100644 index 00000000..bc1b159b --- /dev/null +++ b/.claude/commands/green.md @@ -0,0 +1,110 @@ +--- +description: Execute TDD Green Phase - write minimal implementation to pass the failing test +argument-hint: <implementation description> +--- + +**User arguments:** + +Green: $ARGUMENTS + +**End of user arguments** + +GREEN PHASE! Apply the below to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +### Post-Green Verification + +Once the test passes, run the coverage tool scoped to **only the files you edited** and check for uncovered lines: + +- Any uncovered lines in files you edited are over-implementation — **delete them** +- Do not scope to the full test suite; focus only on what changed + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. diff --git a/.claude/commands/issue.md b/.claude/commands/issue.md new file mode 100644 index 00000000..fc39502a --- /dev/null +++ b/.claude/commands/issue.md @@ -0,0 +1,162 @@ +--- +description: Analyze GitHub issue and create TDD implementation plan +argument-hint: [optional-issue-number] +--- + +Analyze GitHub issue and create TDD implementation plan. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Process: + +1. Get Issue Number + +**User arguments:** + +Issue: $ARGUMENTS + +**End of user arguments** + +- Check if argument is an issue number +- Otherwise try branch name patterns: issue-123, 123-feature, feature/123, fix/123 +- If not found: ask user + +1. Fetch Issue + +Use the GitHub CLI to fetch issue details: + +```bash +gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state +``` + +If the `gh` CLI is not installed or authenticated, show: + +``` +GitHub CLI not available or not authenticated! +Run: gh auth login +``` + +1. Analyze and Plan + +Summarize the issue and requirements, then: + +## Discovery Phase + +Understand the requirement by asking (use AskUserQuestion if needed): + +**Problem Statement** + +- What problem does this solve? +- Who experiences this problem? +- What's the current pain point? + +**Desired Outcome** + +- What should happen after this is built? +- How will users interact with it? +- What does success look like? + +**Scope & Constraints** + +- What's in scope vs. out of scope? +- Any technical constraints? +- Dependencies on other systems/features? + +**Context Check** + +- Search codebase for related features/modules +- Check for existing test files that might be relevant + +### Beads Integration + +Use Beads MCP to: + +- Track work with `bd ready` to find next task +- Create issues with `bd create "description"` +- Track dependencies with `bd dep add` + +See <https://github.com/steveyegge/beads> for more information. + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. diff --git a/.claude/commands/polish.md b/.claude/commands/polish.md new file mode 100644 index 00000000..b42ef856 --- /dev/null +++ b/.claude/commands/polish.md @@ -0,0 +1,185 @@ +--- +description: Review and address issues in existing code - fix problems or justify skipping +argument-hint: [branch, PR#, file, or area to polish] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +# Polish + +Take another pass at existing work to address issues. Unlike `/code-review` which only identifies problems, `/polish` resolves each finding by either: + +1. **Fixing** - Implement the improvement +2. **Skipping with justification** - Document why the issue can be deferred or ignored + +## Phase 0: Determine Scope + +Parse the argument to determine what to polish: + +| Input | Action | +|-------|--------| +| No argument | Detect divergence point, review uncommitted + committed changes | +| Branch name | Changes from that branch to HEAD | +| PR number (e.g., `123`) | Fetch PR diff from GitHub | +| PR URL (e.g., `github.com/.../pull/123`) | Extract PR number and fetch diff | +| File/path | Focus on specific file(s) | + +Use the GitHub CLI to fetch PR details: + +```bash +gh pr view [PR_NUMBER] --json title,body,state,mergeable,headRefName,baseRefName +gh pr diff [PR_NUMBER] +``` + +If the `gh` CLI is not installed or authenticated, show: + +``` +GitHub CLI not available or not authenticated! +Run: gh auth login +``` + +**For local branches:** + +1. Get current branch: `git rev-parse --abbrev-ref HEAD` +2. Detect divergence point (same logic as `/code-review`) +3. Collect changed files from diff and uncommitted changes + +## Phase 1: Identify Issues + +Categorize files based on these patterns: + +| Category | File Patterns | +|----------|---------------| +| Frontend/UI | `*.tsx`, `*.jsx`, `components/`, `pages/`, `views/`, `*.vue` | +| Frontend/Styling | `*.css`, `*.scss`, `*.less`, `styles/`, `*.tailwind*`, `*.styled.*` | +| Backend/API | `routes/`, `api/`, `controllers/`, `services/`, `*.controller.*`, `*.service.*`, `*.resolver.*` | +| Backend/Data | `migrations/`, `models/`, `prisma/`, `schema.*`, `*.model.*`, `*.entity.*` | +| Tooling/Config | `scripts/`, `*.config.*`, `package.json`, `tsconfig.*`, `vite.*`, `webpack.*`, `eslint.*` | +| CI/CD | `.github/`, `.gitlab-ci.*`, `Dockerfile`, `docker-compose.*`, `*.yml` in CI paths | +| Tests | `*.test.*`, `*.spec.*`, `__tests__/`, `__mocks__/`, `*.stories.*` | +| Docs | `*.md`, `docs/`, `README*`, `CHANGELOG*` | + +For each category, identify issues at these severity levels: + +- **blocker** - Must fix before merge +- **risky** - Should fix or have strong justification +- **nit** - Nice to have, easily skippable + +## Phase 2: Address Each Issue + +For each identified issue, present it and then take action: + +### Format + +``` +### [file:line] [severity] Title + +**Issue:** Description of the problem + +**Action taken:** +- [ ] Fixed: [what was done] +- [ ] Skipped: [justification] +``` + +### Decision Guidelines + +**Fix when:** + +- Security vulnerability +- Correctness bug +- Missing error handling that could crash +- Breaking API changes without migration +- Tests that don't actually test anything + +**Skip with justification when:** + +- Stylistic preference with no functional impact +- Optimization for unlikely hot paths +- Refactoring that would expand scope significantly +- Issue exists in code outside the change scope +- Technical debt documented for future sprint + +### Fixing Issues + +When fixing: + +1. Make the minimal change to address the issue +2. Ensure tests still pass (run them if needed) +3. Don't expand scope beyond the identified issue + +### Watch for Brittle Tests + +When refactoring implementation, watch for **Peeping Tom** tests that: + +- Test private methods or internal state directly +- Assert on implementation details rather than behavior +- Break on any refactoring even when behavior is preserved + +If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. + +### Skipping Issues + +Valid skip justifications: + +- "Out of scope - exists in unchanged code" +- "Performance optimization unnecessary - called N times per request" +- "Tracked for future work - see issue #X" +- "Intentional design decision - [reason]" +- "Would require significant refactoring - defer to dedicated PR" + +Invalid skip justifications: + +- "Too hard to fix" +- "It works fine" +- "No time" + +## Phase 3: Cross-Cutting Check + +After addressing individual issues: + +1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase + +Additional cross-cutting checks: + +- Did fixes introduce new inconsistencies? +- Are skip justifications consistent with each other? +- Any patterns in what was skipped that suggest a bigger issue? + +## Phase 4: Summary + +``` +## Polish Summary + +### Fixed +- [list of fixes applied] + +### Skipped (with justification) +- [issue]: [justification] + +### Tests +- [ ] All tests passing +- [ ] No new warnings introduced + +### Remaining Work +- [any follow-up items identified] +``` + +--- + +**User arguments:** + +Polish: $ARGUMENTS + +**End of user arguments** diff --git a/.claude/commands/red.md b/.claude/commands/red.md new file mode 100644 index 00000000..e6120877 --- /dev/null +++ b/.claude/commands/red.md @@ -0,0 +1,111 @@ +--- +description: Execute TDD Red Phase - write ONE failing test +argument-hint: [optional additional info] +--- + +**User arguments:** + +Red: $ARGUMENTS + +**End of user arguments** + +RED PHASE! Apply the below to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. + +### Test Structure (AAA Pattern) + +Structure each test with clear phases: + +- **Arrange**: Set up test data and preconditions (keep minimal) +- **Act**: Execute the single action being tested +- **Assert**: Verify the expected outcome with specific assertions diff --git a/.claude/commands/refactor.md b/.claude/commands/refactor.md new file mode 100644 index 00000000..15119e16 --- /dev/null +++ b/.claude/commands/refactor.md @@ -0,0 +1,127 @@ +--- +description: Execute TDD Refactor Phase - improve code structure while keeping tests green +argument-hint: <refactoring description> +--- + +**User arguments:** + +Refactor: $ARGUMENTS + +**End of user arguments** + +Apply this document (specifically the Refactor phase) to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. + +## Code Complexity Signals + +Look for these refactoring opportunities: + +- [ ] Nesting > 3 levels deep +- [ ] Functions > 20 lines +- [ ] Duplicate code blocks +- [ ] Abstractions with single implementation +- [ ] "Just in case" parameters or config +- [ ] Magic values without names +- [ ] Dead/unused code + +### Watch for Brittle Tests + +When refactoring implementation, watch for **Peeping Tom** tests that: + +- Test private methods or internal state directly +- Assert on implementation details rather than behavior +- Break on any refactoring even when behavior is preserved + +If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. + +1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase diff --git a/.claude/commands/research.md b/.claude/commands/research.md new file mode 100644 index 00000000..0a41c22f --- /dev/null +++ b/.claude/commands/research.md @@ -0,0 +1,96 @@ +--- +description: Research a problem in parallel via web docs, web search, codebase exploration, and deep ultrathink +argument-hint: <research topic or question> +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +**User arguments:** + +Research: $ARGUMENTS + +**End of user arguments** + +Research the following problem or question thoroughly, like a senior developer would. + +## Step 1: Launch Parallel Research Agents + +Use the Task tool to spawn these subagents **in parallel** (all in a single message): + +1. **Web Documentation Agent** (subagent_type: general-purpose) + - Search official documentation for the topic + - Find best practices and recommended patterns + - Locate relevant GitHub issues or discussions + +2. **Web Search Agent** (subagent_type: general-purpose) + - Perform broad web searches for solutions and discussions + - Find Stack Overflow answers, blog posts, and tutorials + - Note common pitfalls and gotchas + +3. **Codebase Explorer Agent** (subagent_type: Explore) + - Search the codebase for related patterns + - Find existing solutions to similar problems + - Identify relevant files, functions, or components + +## Step 2: Library Documentation (Optional) + +If the research involves specific frameworks or libraries: + +- Use Context7 MCP tools (mcp__context7__resolve-library-id, then get-library-docs) +- Get up-to-date API references and code examples +- If Context7 is unavailable, note this in findings so user knows library docs were harder to obtain + +## Step 3: Deep Analysis + +With all gathered context, perform extended reasoning (ultrathink) to: + +- Analyze the problem from first principles +- Consider edge cases and trade-offs +- Synthesize insights across all sources +- Identify conflicts between sources + +## Step 4: Present Findings + +Present a structured summary to the user: + +### Problem Statement + +Describe the problem and why it matters. + +### Key Findings + +Summarize the most relevant solutions and approaches. + +### Codebase Patterns + +Document how the current codebase handles similar cases. + +### Recommended Approach + +Provide your recommendation based on all research. + +### Conflicts + +Highlight where sources disagree and provide assessment of which is more reliable. + +### Sources + +List all source links with brief descriptions. This section is required. + +## Research Guidelines + +- Prioritize official documentation over blog posts +- Prefer solutions that match existing codebase patterns +- Note major.minor versions for libraries/frameworks (patch versions only if critical) +- Flag conflicting information across sources +- Write concise, actionable content +- Use active voice throughout +- **Do not create output files** - present findings directly in conversation unless user explicitly requests a file diff --git a/.claude/commands/simplify.md b/.claude/commands/simplify.md new file mode 100644 index 00000000..55242a98 --- /dev/null +++ b/.claude/commands/simplify.md @@ -0,0 +1,84 @@ +--- +description: Reduce code complexity while keeping tests green +argument-hint: [file, function, or area to simplify] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +**User arguments:** + +Simplify: $ARGUMENTS + +**End of user arguments** + +(If there was no info above, fallback to the context of the conversation) + +Reduce complexity while keeping tests green. + +## Core Principles + +**YAGNI** - Don't build until actually needed. Delete "just in case" code. + +**KISS** - Simplest solution that works. Clever is the enemy of clear. + +**Rule of Three** - Don't abstract until 3rd occurrence. "Prefer duplication over wrong abstraction" (Sandi Metz). + +## When NOT to Simplify + +- Essential domain complexity (regulations, business rules) +- Performance-critical optimized code +- Concurrency/thread-safety requirements +- Security-sensitive explicit checks + +## Prerequisites + +Tests must be green. If failing, use `/green` first. + +## Code Complexity Signals + +Look for these refactoring opportunities: + +- [ ] Nesting > 3 levels deep +- [ ] Functions > 20 lines +- [ ] Duplicate code blocks +- [ ] Abstractions with single implementation +- [ ] "Just in case" parameters or config +- [ ] Magic values without names +- [ ] Dead/unused code + +## Techniques + +| Pattern | Before | After | +|---------|--------|-------| +| Guard clause | Nested `if/else` | Early `return` | +| Named condition | Complex boolean | `const isValid = ...` | +| Extract constant | `if (x > 3)` | `if (x > MAX_RETRIES)` | +| Flatten callback | `.then().then()` | `async/await` | + +**Also apply:** Consolidate duplicates, inline unnecessary abstractions, delete dead code. + +## Validate + +1. Tests still green +2. Code reads more clearly +3. No behavioral changes + +**Simplify** removes complexity locally. **Refactor** improves architecture broadly. Use `/refactor` if changes require structural reorganization. + +### Watch for Brittle Tests + +When refactoring implementation, watch for **Peeping Tom** tests that: + +- Test private methods or internal state directly +- Assert on implementation details rather than behavior +- Break on any refactoring even when behavior is preserved + +If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. diff --git a/.claude/commands/spike.md b/.claude/commands/spike.md new file mode 100644 index 00000000..72a8fdb5 --- /dev/null +++ b/.claude/commands/spike.md @@ -0,0 +1,103 @@ +--- +description: Execute TDD Spike Phase - exploratory coding to understand problem space before TDD +argument-hint: <exploration description> +--- + +**User arguments:** + +Spike: $ARGUMENTS + +**End of user arguments** + +SPIKE PHASE! Apply the below to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. diff --git a/.claude/commands/summarize.md b/.claude/commands/summarize.md new file mode 100644 index 00000000..d568f342 --- /dev/null +++ b/.claude/commands/summarize.md @@ -0,0 +1,62 @@ +--- +description: Summarize conversation progress and next steps +argument-hint: [optional additional info] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Create a concise summary of the current conversation suitable for transferring context to a new conversation. + +**User arguments:** + +Summarize: $ARGUMENTS + +**End of user arguments** + +## Summary Structure + +Provide a summary with these sections: + +### What We Did + +- Key accomplishments and changes made +- Important decisions or discoveries +- Files created, modified, or analyzed + +### What We're Doing Next + +- Immediate next steps +- Pending tasks or work in progress +- Goals or objectives to continue + +### Blockers & User Input Needed + +- Any issues requiring user intervention +- Decisions that need to be made +- Missing information or clarifications needed + +## Output Format + +Keep the summary concise and actionable - suitable for pasting into a new conversation to quickly restore context without needing the full conversation history. + +## Beads Integration + +If Beads MCP is available, check for task tracking status and ask if the user wants to: + +1. Review current task status +2. Update task states based on conversation progress +3. Include Beads context in the summary + +Use AskUserQuestion to confirm Beads integration preferences. diff --git a/.claude/commands/tdd-review.md b/.claude/commands/tdd-review.md new file mode 100644 index 00000000..b3612fb8 --- /dev/null +++ b/.claude/commands/tdd-review.md @@ -0,0 +1,110 @@ +--- +description: Review test suite quality against FIRST principles and TDD anti-patterns +argument-hint: [optional test file or directory path] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +# Test Quality Review + +Analyze test files against FIRST principles and TDD best practices. + +## Phase 1: Scope + +| Input | Action | +|-------|--------| +| No argument | Find all test files in project | +| File path | Analyze specific test file | +| Directory | Analyze tests in directory | + +Detect test files using common patterns: `*.test.*`, `*.spec.*`, `*.stories.*`, `__tests__/**` + +Also check for framework-specific patterns based on the project's languages and tools (e.g., `*_test.go`, `*_test.py`, `Test*.java`, `*.feature` for BDD). + +## Phase 2: Analysis + +For each test file, check against these criteria: + +### Quality Criteria + +#### FIRST Principles + +| Principle | What to Check | +|-----------|---------------| +| **Fast** | Tests complete quickly, no I/O, no network calls, no sleep()/setTimeout delays | +| **Independent** | No shared mutable state, no execution order dependencies between tests | +| **Repeatable** | No Date.now(), no Math.random() without seeding, no external service dependencies | +| **Self-validating** | Meaningful assertions that verify behavior, no manual verification needed | + +#### TDD Anti-patterns + +| Anti-pattern | Detection Signals | +|--------------|-------------------| +| **The Liar** | `expect(true).toBe(true)`, empty test bodies, tests with no assertions | +| **Excessive Setup** | >20 lines of arrange code, >5 mocks, deep nested object construction | +| **The One** | >5 assertions testing unrelated behaviors in a single test | +| **The Peeping Tom** | Testing private methods, asserting on internal state, tests that break on any refactor | +| **The Slow Poke** | Real database/network calls, file I/O, hard-coded timeouts | + +#### Test Structure (AAA Pattern) + +- **Arrange**: Clear setup with minimal fixtures +- **Act**: Single action being tested +- **Assert**: Specific, behavior-focused assertions + +## Phase 3: Report + +Output a structured report: + +``` +## Test Quality Report + +### Summary +- Files analyzed: N +- Tests found: N +- Issues found: N (X blockers, Y warnings) + +### By File + +#### path/to/file.test.ts + +| Line | Issue | Severity | Description | +|------|-------|----------|-------------| +| 15 | The Liar | blocker | Test has no assertions | +| 42 | Slow Poke | warning | Uses setTimeout(500) | + +### Recommendations +- [ ] Fix blockers before merge +- [ ] Consider refactoring tests with excessive setup +``` + +### Severity Levels + +- **blocker**: Must fix - test provides false confidence (The Liar, no assertions) +- **warning**: Should fix - test quality issue (Slow Poke, Excessive Setup) +- **info**: Consider - style or structure suggestion (AAA pattern) + +--- + +**User arguments:** + +TDD-review: $ARGUMENTS + +**End of user arguments** diff --git a/.claude/commands/tdd.md b/.claude/commands/tdd.md new file mode 100644 index 00000000..7cb2a554 --- /dev/null +++ b/.claude/commands/tdd.md @@ -0,0 +1,102 @@ +--- +description: Remind agent about TDD approach and continue conversation +argument-hint: [optional-response-to-last-message] +--- + +# TDD Reminder + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. + +## Continue Conversation + +**User arguments:** + +TDD: $ARGUMENTS + +**End of user arguments** + +Please continue with the user input above, applying TDD approach. diff --git a/.claude/helpers/merge-claude-settings.sh b/.claude/helpers/merge-claude-settings.sh new file mode 100644 index 00000000..8343e15d --- /dev/null +++ b/.claude/helpers/merge-claude-settings.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# +# Based on https://github.com/PaulRBerg/dot-claude/blob/main/helpers/merge_settings.sh +# +# Merges multiple JSONC settings files from the settings/ directory into a single +# settings.json file. This script handles: +# - Parsing JSONC files (JSON with comments) using json5 +# - Collecting and deduplicating permission arrays across all files +# - Merging non-permission top-level keys from all files +# +# Usage: merge_settings.sh + +set -euo pipefail + +# ---------------------------------------------------------------------------- # +# CONFIGURATION # +# ---------------------------------------------------------------------------- # + +# Navigate to the .claude directory relative to the folder this script is in +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +cd "$script_dir/../" + +# ---------------------------------------------------------------------------- # +# 1. DISCOVER FILES # +# ---------------------------------------------------------------------------- # + +# Find all .json and .jsonc files in settings/ directory (excluding settings.json) +# Files are sorted alphabetically to ensure consistent merge order +settings_files=$(find settings/ -type f \( -name '*.json' -o -name '*.jsonc' \) ! -name 'settings.json' | sort) + +if [ -z "$settings_files" ]; then + echo "No settings files found in settings/ directory" + exit 0 +fi + +# ---------------------------------------------------------------------------- # +# 2. PARSE JSONC FILES # +# ---------------------------------------------------------------------------- # + +# Parse all JSONC files to valid JSON using json5 in a single Node.js process +# The json5 tool allows comments and trailing commas in JSON files +# Using a single Node.js process is much faster than calling npx per file +# If a file fails to parse, fall back to empty object +parsed_json=$(npx -y -p json5 node -e " +const fs = require('fs'); +const JSON5 = require('json5'); +process.argv.slice(1).forEach(file => { + try { + console.log(JSON.stringify(JSON5.parse(fs.readFileSync(file, 'utf8')))); + } catch { + console.log('{}'); + } +}); +" $settings_files) + +# ---------------------------------------------------------------------------- # +# 3. MERGE WITH JQ # +# ---------------------------------------------------------------------------- # + +# Merge all parsed JSON files using jq +# The merge strategy is: +# 1. Collect all permission arrays from all files and deduplicate +# 2. Merge all other top-level keys (later values override earlier ones) +# 3. Exclude the $schema field from the final output +merged_json=$(echo "$parsed_json" | jq -s ' + # First, build the permissions object by collecting arrays from all files + { + permissions: { + # Collect additionalDirectories from all files, flatten, and deduplicate + additionalDirectories: ([.[].permissions.additionalDirectories // [] | .[] ] | unique), + + # Collect allow patterns from all files, flatten, and deduplicate + allow: ([.[].permissions.allow // [] | .[] ] | unique), + + # Collect ask patterns from all files, flatten, and deduplicate + ask: ([.[].permissions.ask // [] | .[] ] | unique), + + # Collect deny patterns from all files, flatten, and deduplicate + deny: ([.[].permissions.deny // [] | .[] ] | unique) + } + } * + # Then merge all non-permissions top-level keys from all files + # Later files override earlier files for conflicting keys + (reduce .[] as $item ({}; . * ($item | del(.permissions)))) + # Remove the $schema field from the final output + | del(."$schema") + # Sort all object keys alphabetically + | walk(if type == "object" then to_entries | sort_by(.key) | from_entries else . end) +') + +# ---------------------------------------------------------------------------- # +# 4. WRITE OUTPUT # +# ---------------------------------------------------------------------------- # + +# Write the merged JSON to settings.json +echo "$merged_json" > settings.json + +echo "✓ Merged settings.json from JSONC files" diff --git a/.claude/package-lock.json b/.claude/package-lock.json new file mode 100644 index 00000000..860fabfc --- /dev/null +++ b/.claude/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": ".claude", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "json5": "2.2.3" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/.claude/package.json b/.claude/package.json new file mode 100644 index 00000000..f79a49bc --- /dev/null +++ b/.claude/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "json5": "2.2.3" + } +} diff --git a/.claude/settings/basics.jsonc b/.claude/settings/basics.jsonc new file mode 100644 index 00000000..a7b12fae --- /dev/null +++ b/.claude/settings/basics.jsonc @@ -0,0 +1,5 @@ +{ + // Basic configuration for Claude Code + // General settings, environment variables, and UI customization + "$schema": "https://json.schemastore.org/claude-code-settings.json" +} diff --git a/.claude/settings/permissions/bash.jsonc b/.claude/settings/permissions/bash.jsonc new file mode 100644 index 00000000..92caa8a2 --- /dev/null +++ b/.claude/settings/permissions/bash.jsonc @@ -0,0 +1,120 @@ +{ + // Bash command permissions - allowed and denied shell operations + // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": [ + // AI interactions and tooling + "Bash(bd *)", + // Cloud Infrastructure + "Bash(aws sts get*)", + // Code Quality & Formatting + "Bash(pre-commit run *)", + // Core Shell Commands + "Bash(COMMAND *)", // used in slash commands + "Bash(kill *)", + "Bash(ps *)", + "Bash(pwd *)", + "Bash(time *)", + "Bash(timeout *)", + "Bash(which *)", + "Bash(xargs *)", + // Custom Scripts + "Bash(~/.agents/helpers/*)", + "Bash(~/.claude/helpers/*)", + "Bash(~/.claude/hooks/*)", + "Bash(~/.agents/skills/*)", + "Bash(~/.claude/skills/*)", + // File System + "Bash(bat *)", + "Bash(chmod *)", + "Bash(cp *)", + "Bash(eza *)", + "Bash(fd *)", + "Bash(find *)", + "Bash(fzf *)", + "Bash(ls *)", + "Bash(mkdir *)", + "Bash(stat *)", + "Bash(tar *)", + "Bash(touch *)", + "Bash(tree *)", + // Git & Version Control + "Bash(git diff *)", + "Bash(git status *)", + "Bash(git log *)", + "Bash(git rev-parse *)", + "Bash(git branch *)", + // Misc + "Bash(amp *)", + "Bash(atuin *)", + "Bash(bc *)", + "Bash(chezmoi *)", + "Bash(diff *)", + "Bash(jq *)", + "Bash(just *)", + "Bash(lsof *)", + "Bash(test *)", + "Bash(zk *)", + // Node.js + "Bash(pnpm test-unit *)", + "Bash(pnpm test-e2e *)", + // Python + "Bash(uv run pytest *)", + // Text Processing + "Bash(awk *)", + "Bash(cat *)", + "Bash(cut *)", + "Bash(echo *)", + "Bash(grep *)", + "Bash(head *)", + "Bash(printf *)", + "Bash(sed *)", + "Bash(sort *)", + "Bash(tail *)", + // Search + "Bash(rg *)", + ], + "ask": [ + "Bash(gh *)", // let's hold off before we let it use the github CLI in any free running allow mode...I don't want it somehow approving PRs with the user's credentials + "Bash(aws *)", // let's hold off before we let it use AWS CLI in any free running allow mode. We need to be very sure we don't have any access to staging or production credentials in our dev environment (...which we shouldn't...but we need to double check that or consider any other safeguards first) + "Bash(curl *)", + "Bash(ln *)", + "WebFetch", + ], + "deny": [ + // Exceptions to generally allowed AI tooling + "Bash(bd init*)", // we need to control the init process, don't let AI do that in the background + // Destructive File Operations + "Bash(chmod -R *)", + "Bash(chown -R *)", + "Bash(rm -rf / *)", + "Bash(rm -rf ~ *)", + // Dangerous Disk Operations + "Bash(> /dev/sda*)", + "Bash(> /etc/*)", + "Bash(dd *)", + "Bash(mkfs *)", + // Process Management + "Bash(kill -9 *)", + "Bash(killall *)", + // Git & Version Control + "Bash(git reset --hard *)", + "Bash(git push -f *)", + "Bash(git push --force*)", + // Node.js + "Bash(npm publish *)", + // System Administration + "Bash(doas *)", + "Bash(passwd *)", + "Bash(su *)", + "Bash(sudo *)", + "Bash(systemctl *)", + "Bash(userdel *)", + "Bash(usermod *)", + // System Control + "Bash(reboot *)", + "Bash(shutdown *)", + ], + }, +} diff --git a/.claude/settings/permissions/read.jsonc b/.claude/settings/permissions/read.jsonc new file mode 100644 index 00000000..7916e1e8 --- /dev/null +++ b/.claude/settings/permissions/read.jsonc @@ -0,0 +1,9 @@ +{ + // Read permissions for specific files + // NOTE: to add reading directories, use the additional-dirs.jsonc file + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": ["Read(~/.zshrc)"], + "deny": ["Read(~/Drive/Secrets/**)", "Read(~/.ssh/**)", "Read(**/.env*)", "Read(~/.aws/**)"], + }, +} diff --git a/.claude/settings/permissions/write.jsonc b/.claude/settings/permissions/write.jsonc new file mode 100644 index 00000000..f7a11443 --- /dev/null +++ b/.claude/settings/permissions/write.jsonc @@ -0,0 +1,8 @@ +{ + // Write permissions for specific files + // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": ["Write(/tmp/**)"] + } +} diff --git a/.coderabbit.yaml b/.coderabbit.yaml index 9904260d..1d2e81ac 100644 --- a/.coderabbit.yaml +++ b/.coderabbit.yaml @@ -6,7 +6,7 @@ reviews: - path: "**/vendor_files/**" instructions: "These files came from a vendor and we're not allowed to change them. Refer to it if you need to understand how the main code interacts with it, but do not make comments about it." - path: "**/*.py" - instructions: "Do not express concerns about assert statements being removed by using the -O python flag; we never use that flag. Do not express concerns about ruff rules; a pre-commit hook already runs a ruff check. Do not warn about unnecessary super().init() calls; pyright prefers those to be present." + instructions: "Check the `ruff.toml` and `ruff-test.toml` for linting rules we've explicitly disabled and don't suggest changes to please conventions we've disabled. Do not express concerns about ruff rules; a pre-commit hook already runs a ruff check. Do not warn about unnecessary super().__init__() calls; pyright prefers those to be present. Do not warn about missing type hints; a pre-commit hook already checks for that." tools: eslint: # when the code contains typescript, eslint will be run by pre-commit, and coderabbit often generates false positives enabled: false diff --git a/.copier-answers.yml b/.copier-answers.yml index af1dc531..b79585d7 100644 --- a/.copier-answers.yml +++ b/.copier-answers.yml @@ -1,8 +1,8 @@ # Changes here will be overwritten by Copier -_commit: v0.0.97 +_commit: v0.0.104 _src_path: gh:LabAutomationAndScreening/copier-base-template.git description: Copier template for creating Python libraries and executables -install_claude_cli: false +install_claude_cli: true python_ci_versions: - 3.12.7 - 3.13.9 diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 00000000..5f9051a8 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,30 @@ +[run] +branch = True +omit = + # omit anything supplied by vendors + */vendor_files/* + # omit any auto-generated API client code + */generated/open_api/* + # omit CircuitPython firmware + */firmware/code.py + */firmware/boot.py + +[report] +# Regexes for lines to exclude from consideration +exclude_also = + # Don't complain if tests don't hit defensive assertion code: + raise NotImplementedError + raise AssertionError + + # Don't complain about code specifically to help with pyright import resolution + if TYPE_CHECKING: + + # Don't complain if non-runnable code isn't run: + if __name__ == .__main__.: + + # Abstract methods inherently can't be hit + @abstractmethod + +fail_under = 100 +[html] +directory = coverage-report-pytest diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 011961f8..5b3fb147 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -8,7 +8,9 @@ "version": "3.12.7", "installTools": false, "optimize": true - } + }, + // https://github.com/anthropics/devcontainer-features/blob/main/src/claude-code/devcontainer-feature.json + "ghcr.io/anthropics/devcontainer-features/claude-code:1.0.5": {} }, "customizations": { "vscode": { @@ -16,11 +18,12 @@ "extensions": [ // basic tooling // "eamodio.gitlens@15.5.1", - "coderabbit.coderabbit-vscode@0.17.0", + "coderabbit.coderabbit-vscode@0.18.3", "ms-vscode.live-server@0.5.2025051301", "MS-vsliveshare.vsliveshare@1.0.5905", "github.copilot@1.388.0", - "github.copilot-chat@0.38.2026022001", + "github.copilot-chat@0.38.2026022704", + "anthropic.claude-code@2.1.74", // Python "ms-python.python@2026.2.2026021801", @@ -58,5 +61,5 @@ "initializeCommand": "sh .devcontainer/initialize-command.sh", "onCreateCommand": "sh .devcontainer/on-create-command.sh", "postStartCommand": "sh .devcontainer/post-start-command.sh" - // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): 91027588 # spellchecker:disable-line + // Devcontainer context hash (do not manually edit this, it's managed by a pre-commit hook): 0f1c7f94 # spellchecker:disable-line } diff --git a/.devcontainer/docker-compose.yml b/.devcontainer/docker-compose.yml index 5f5bc615..10de4c5a 100644 --- a/.devcontainer/docker-compose.yml +++ b/.devcontainer/docker-compose.yml @@ -17,7 +17,24 @@ services: environment: - AWS_PROFILE=localstack - AWS_DEFAULT_REGION=us-east-1 + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - BEADS_DIR=/workspaces/copier-python-package-template/.claude/.beads + - BEADS_DOLT_SERVER_HOST=beads-dolt # this is on the docker compose network. We don't want to publish the server port because then the local machine would have a bunch of conflicts with multiple devcontainers running + - BEADS_DOLT_SERVER_PORT=3306 + - BEADS_DOLT_SERVER_DATABASE=beads_work + - BEADS_DOLT_SERVER=beads-dolt # for some weird reason, unless we specify both of these envvars it doesn't seem to reliably work + - BEADS_DOLT_PORT=3306 + + + beads-dolt: + image: dolthub/dolt-sql-server:1.83.0 # no explicit reason for this version, just pinning for best practice + volumes: + - beads_dolt_data:/var/lib/dolt + environment: + - DOLT_ROOT_HOST=% + - DOLT_DATABASE=beads_work volumes: + beads_dolt_data: {} python_venv: {} diff --git a/.devcontainer/install-ci-tooling.py b/.devcontainer/install-ci-tooling.py index a2e1bbec..6f90796d 100644 --- a/.devcontainer/install-ci-tooling.py +++ b/.devcontainer/install-ci-tooling.py @@ -7,9 +7,9 @@ import tempfile from pathlib import Path -UV_VERSION = "0.10.4" -PNPM_VERSION = "10.30.0" -COPIER_VERSION = "==9.11.3" +UV_VERSION = "0.10.10" +PNPM_VERSION = "10.32.1" +COPIER_VERSION = "==9.14.0" COPIER_TEMPLATE_EXTENSIONS_VERSION = "==0.3.3" PRE_COMMIT_VERSION = "4.5.1" GITHUB_WINDOWS_RUNNER_BIN_PATH = r"C:\Users\runneradmin\.local\bin" diff --git a/.devcontainer/on-create-command.sh b/.devcontainer/on-create-command.sh index 2d1023a4..b3a4c386 100644 --- a/.devcontainer/on-create-command.sh +++ b/.devcontainer/on-create-command.sh @@ -6,6 +6,16 @@ set -ex git config --global --add safe.directory /workspaces/copier-python-package-template sh .devcontainer/on-create-command-boilerplate.sh +# install json5 for merging claude settings. TODO: consider if we can install json5 globally...or somehow eliminate this dependency +script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +repo_root="$(CDPATH= cd -- "$script_dir/.." && pwd)" +mkdir -p "$repo_root/.claude" +chmod -R ug+rwX "$repo_root/.claude" +chgrp -R 0 "$repo_root/.claude" || true +npm --prefix "$repo_root/.claude" ci + +# Install beads for use in Claude planning +npm install -g @beads/bd@0.57.0 # no specific reason for this version, just pinning for best practice pre-commit install --install-hooks diff --git a/.devcontainer/post-start-command.sh b/.devcontainer/post-start-command.sh index d1ac1f00..4d178b2d 100644 --- a/.devcontainer/post-start-command.sh +++ b/.devcontainer/post-start-command.sh @@ -4,3 +4,11 @@ set -ex # For some reason the directory is not setup correctly and causes build of devcontainer to fail since # it doesn't have access to the workspace directory. This can normally be done in post-start-command git config --global --add safe.directory /workspaces/copier-python-package-template +pre-commit run merge-claude-settings -a +if ! bd ready; then + echo "It's likely the Dolt server has not yet been initialized to support beads, running that now" # TODO: figure out a better way to match this specific scenario than just a non-zero exit code...but beads still seems like in high flux right now so not sure what to tie it to + # the 'stealth' flag is just the only way I could figure out how to stop it from modifying AGENTS.md...if there's another way to avoid that, then fine. Even without the stealth flag though, files inside the .claude/beads directory get modified, so restoring them at the end to what was set in git...these shouldn't really need to change regularly + # trying to set 'prefix' to nothing doesn't seem to work (it just acts like the prefix flag wasn't there), so just setting to 'work' as an arbitrary name + # for some reason, the envvar for the server host isn't being picked up normally, so just passing it explicitly here + rm -rf .claude/.beads && bd init --server-host="$BEADS_DOLT_SERVER_HOST" --database="$BEADS_DOLT_SERVER_DATABASE" --skip-hooks --stealth --prefix=work </dev/null && git -c core.hooksPath=/dev/null restore --source=HEAD --staged --worktree .claude/.beads +fi diff --git a/.github/actions/install_deps/action.yml b/.github/actions/install_deps/action.yml index b212d095..0cedd63a 100644 --- a/.github/actions/install_deps/action.yml +++ b/.github/actions/install_deps/action.yml @@ -64,7 +64,7 @@ runs: - name: Setup node if: ${{ inputs.node-version != 'notUsing' }} - uses: actions/setup-node@v6.2.0 + uses: actions/setup-node@v6.3.0 with: node-version: ${{ inputs.node-version }} @@ -75,7 +75,7 @@ runs: - name: OIDC Auth for CodeArtifact if: ${{ inputs.code-artifact-auth-role-name != 'no-code-artifact' }} - uses: aws-actions/configure-aws-credentials@v5.1.1 + uses: aws-actions/configure-aws-credentials@v6.0.0 with: role-to-assume: arn:aws:iam::${{ inputs.code-artifact-auth-role-account-id }}:role/${{ inputs.code-artifact-auth-role-name }} aws-region: ${{ inputs.code-artifact-auth-region }} diff --git a/.github/reusable_workflows/build-docker-image.yaml b/.github/reusable_workflows/build-docker-image.yaml index ba003d74..cfbdc543 100644 --- a/.github/reusable_workflows/build-docker-image.yaml +++ b/.github/reusable_workflows/build-docker-image.yaml @@ -72,9 +72,9 @@ jobs: - name: OIDC Auth for ECR if: ${{ inputs.push-role-name != 'no-push' }} - uses: aws-actions/configure-aws-credentials@v5.1.1 + uses: ./.github/actions/ecr-auth with: - role-to-assume: arn:aws:iam::${{ steps.parse_ecr_url.outputs.aws_account_id }}:role/${{ inputs.push-role-name }} + role-arn: arn:aws:iam::${{ steps.parse_ecr_url.outputs.aws_account_id }}:role/${{ inputs.push-role-name }} aws-region: ${{ steps.parse_ecr_url.outputs.aws_region }} - name: Calculate hash of files in build context @@ -111,11 +111,6 @@ jobs: echo "status=notfound" >> $GITHUB_OUTPUT fi - - name: Login to Amazon ECR - if: ${{ inputs.push-role-name != 'no-push' && (steps.check-if-exists.outputs.status == 'notfound' || inputs.save-as-artifact ) }} - id: login-ecr - uses: aws-actions/amazon-ecr-login@v2.0.1 - - name: Pull existing image to package as artifact if: ${{ inputs.save-as-artifact && steps.check-if-exists.outputs.status == 'found' }} run: | @@ -156,7 +151,7 @@ jobs: - name: Upload Docker Image Artifact if: ${{ inputs.save-as-artifact }} - uses: actions/upload-artifact@v6.0.0 + uses: actions/upload-artifact@v7.0.0 with: name: ${{ steps.calculate-build-context-hash.outputs.image_name_no_slashes }} path: ${{ steps.calculate-build-context-hash.outputs.image_name_no_slashes }}.tar diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index f3c9c0a4..d7e9fb92 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,6 +29,40 @@ jobs: with: python-version: 3.12.7 + unit-test: + needs: + - pre-commit + strategy: + matrix: + os: + - "ubuntu-24.04" + python-version: + + - 3.12.7 + + - 3.13.9 + + runs-on: ${{ matrix.os }} + timeout-minutes: 8 + env: + UV_PYTHON: ${{ matrix.python-version }} + + steps: + - name: Checkout code + uses: actions/checkout@v6.0.2 + with: + persist-credentials: false + + - name: Install tooling + uses: ./.github/actions/install_deps + with: + python-version: ${{ matrix.python-version }} + + - name: Run Unit Tests + # TODO: figure out what to do about tests of copier task scripts that don't actually get detected by code coverage + run: uv run pytest tests/unit --durations=5 --no-cov + + lint-matrix: needs: - pre-commit @@ -81,6 +115,7 @@ jobs: rm -rf !(new-template) rm -rf .github # apparently this folder doesn't get removed with the previous command for some reason rm -rf .devcontainer # apparently this folder doesn't get removed with the previous command for some reason + rm -rf .claude # apparently this folder doesn't get removed with the previous command for some reason ls -la - name: Move the instantiated template into the repo root run: | @@ -113,7 +148,7 @@ jobs: timeout-minutes: 8 # this is the amount of time this action will wait to attempt to acquire the mutex lock before failing, e.g. if other jobs are queued up in front of it - name: Cache Pre-commit hooks - uses: actions/cache@v5.0.2 + uses: actions/cache@v5.0.3 env: cache-name: cache-pre-commit-hooks with: @@ -144,8 +179,10 @@ jobs: runs-on: ubuntu-24.04 timeout-minutes: 2 needs: - - lint-matrix - get-values + - pre-commit + - unit-test + - lint-matrix permissions: statuses: write # needed for updating status on Dependabot PRs if: always() @@ -154,7 +191,10 @@ jobs: run: | success_pattern="^(skipped|success)$" # these are the possibilities: https://docs.github.com/en/actions/reference/workflows-and-actions/contexts#needs-context - if [[ ! "${{ needs.lint-matrix.result }}" =~ $success_pattern ]]; then + if [[ ! "${{ needs.get-values.result }}" =~ $success_pattern ]] || + [[ ! "${{ needs.pre-commit.result }}" =~ $success_pattern ]] || + [[ ! "${{ needs.unit-test.result }}" =~ $success_pattern ]] || + [[ ! "${{ needs.lint-matrix.result }}" =~ $success_pattern ]]; then echo "❌ One or more jobs did not finish with skipped or success" exit 1 fi diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml index 90f6ceae..ffeb9485 100644 --- a/.github/workflows/pre-commit.yaml +++ b/.github/workflows/pre-commit.yaml @@ -59,7 +59,7 @@ jobs: timeout-minutes: 8 # this is the amount of time this action will wait to attempt to acquire the mutex lock before failing, e.g. if other jobs are queued up in front of it - name: Cache Pre-commit hooks - uses: actions/cache@v5.0.2 + uses: actions/cache@v5.0.3 env: cache-name: cache-pre-commit-hooks with: diff --git a/.gitignore b/.gitignore index efe2fb8c..cab6c195 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,16 @@ .ruff_cache/ .pipx_cache/ + +# Claude +.claude/settings.local.json +## we manage the settings JSON by merging the JSONC files in the settings/ directory +.claude/settings.json +# Dolt database files (used by beads for Claude) +.dolt/ +*.db + + # Vendor build/make dkms.conf diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5b009afa..6ea6a13e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -minimum_pre_commit_version: 4.2.0 +minimum_pre_commit_version: 4.3.0 # run `pre-commit autoupdate --freeze` to update all hooks default_install_hook_types: [pre-commit, post-checkout] repos: @@ -314,10 +314,19 @@ repos: # print the number of files as a sanity-check verbose: true - # Devcontainer context --- this makes Github's "prebuild codespaces" feature work more intelligently for the "Configuration Change" trigger + # Updating repo config/tooling files - repo: local hooks: + - id: merge-claude-settings + # Keep Claude's settings.json synced with the JSON5 files. It's only for local development, so don't run it in CI + name: merge Claude settings + entry: bash -c '[[ "${GITHUB_ACTIONS:-}" == "true" || "${CI:-}" == "true" ]] && exit 0; bash .claude/helpers/merge-claude-settings.sh' + files: ^\.claude/settings/.*\.(json|jsonc)$ + pass_filenames: false + language: system + - id: compute-devcontainer-context-hash + # Devcontainer context --- this makes Github's "prebuild codespaces" feature work more intelligently for the "Configuration Change" trigger name: compute devcontainer context hash entry: bash -c "python3 .github/workflows/hash_git_files.py . --for-devcontainer-config-update" files: (.*.lock)|(.*pnpm-lock.yaml)|(.*hash_git_files.py)|(.devcontainer/.*)|(\.pre-commit-config.yaml) diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..2f443460 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,121 @@ +## Code Style +- Comments should be used very rarely. Code should generally express its intent. +- Never write a one-line docstring — either the name is sufficient or the behavior warrants a full explanation. +- Don't sort or remove imports manually — pre-commit handles it. +- Always include type hints for pyright in Python +- Respect the pyright rule reportUnusedCallResult; assign unneeded return values to `_` +- Prefer keyword-only parameters (unless a very clear single-argument function): use `*` in Python signatures and destructured options objects in TypeScript. +- When disabling a linting rule with an inline directive, provide a comment at the end of the line (or on the line above for tools that don't allow extra text after an inline directive) describing the reasoning for disabling the rule. + +## Testing +- Always run tests with an explicit path (e.g. uv run pytest tests/unit) — test runners discover all types by default. +- Test coverage requirements are usually at 100%, so when running a subset of tests, always disable test coverage to avoid the test run failing for insufficient coverage. +- Avoid magic values in comparisons in tests in all languages (like ruff rule PLR2004 specifies) +- Prefer using random values in tests rather than arbitrary ones (e.g. the faker library, uuids, random.randint) when possible. For enums, pick randomly rather than hardcoding one value. +- Avoid loops in tests — assert each item explicitly so failures pinpoint the exact element. When verifying a condition across all items in a collection, collect the violations into a list and assert it's empty (e.g., assert [x for x in items if bad_condition(x)] == []). +- Key `data-testid` selectors off unique IDs (e.g. UUIDs), not human-readable names which may collide or change. + +### Python Testing +- When using `mocker.spy` on a class-level method (including inherited ones), the spy records the unbound call, so assertions need `ANY` as the first argument to match self: `spy.assert_called_once_with(ANY, expected_arg)` +- Before writing new mock/spy helpers, check the `tests/unit/` folder for pre-built helpers in files like `fixtures.py` or `*mocks.py` +- When a test needs a fixture only for its side effects (not its return value), use `@pytest.mark.usefixtures(fixture_name.__name__)` instead of adding an unused parameter with a noqa comment +- Use `__name__` instead of string literals when referencing functions/methods (e.g., `mocker.patch.object(MyClass, MyClass.method.__name__)`, `pytest.mark.usefixtures(my_fixture.__name__)`). This enables IDE refactoring tools to catch renames. +- **Never hand-write VCR cassette YAML files.** Cassettes must be recorded from real HTTP interactions by running the test once with `--record-mode=once` against a live external service: `uv run pytest --record-mode=once <test path> --no-cov`. The default mode is `none` — a missing cassette will cause an error, which is expected until recorded. +- **Never hand-edit syrupy snapshot files.** Snapshots are auto-generated — to create or update them, run `uv run pytest --snapshot-update <test path> --no-cov`. A missing snapshot causes the test to fail, which is expected until you run with `--snapshot-update`. When a snapshot mismatch occurs, fix the code if the change was unintentional; run `--snapshot-update` if it was intentional. +- **Never hand-write or hand-edit pytest-reserial `.jsonl` recording files.** Recordings must be captured from real serial port traffic by running the test with `--record` while the device is connected: `uv run pytest --record <test path> --no-cov`. The default mode replays recordings — a missing recording causes an error, which is expected until recorded against a live device. + + +## Tooling +- Always use `uv run python` instead of `python3` or `python` when running Python commands. +- Prefer dedicated shell tools over `python3`/`python` for simple one-off tasks: use `jq` for JSON parsing, standard shell builtins for string manipulation, etc. Only reach for `python3` when no simpler tool covers the need. +- Check .devcontainer/devcontainer.json for tooling versions (Python, Node, etc.) when reasoning about version-specific stdlib or tooling behavior. +- For frontend work, run commands via `pnpm` scripts from `frontend/package.json` — never invoke tools directly (not pnpm exec <tool>, npx <tool>, etc.). ✅ pnpm test-unit ❌ pnpm vitest ... or npx vitest ... +- When running terminal commands, execute exactly one command per tool call. Do not chain commands with &&, ||, ;, or & — this prohibition has no exceptions, even for `cd && ...` patterns. Use absolute paths instead of `cd` to avoid needing to chain. Pipes (|) are allowed for output transformation (e.g., head, tail, grep). If two sequential commands are needed, run them in separate tool calls. Chained commands break the permission allow-list matcher and cause unnecessary permission prompts +- Never use backslash line continuations in shell commands — always write the full command on a single line. Backslashes break the permission allow-list matcher. + +<!-- BEGIN BEADS INTEGRATION --> +## Issue Tracking with bd (beads) + +**IMPORTANT**: This project uses **bd (beads)** for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods. + +### Why bd? + +- Dependency-aware: Track blockers and relationships between issues +- Git-friendly: Auto-syncs to JSONL for version control +- Agent-optimized: JSON output, ready work detection, discovered-from links +- Prevents duplicate tracking systems and confusion + +### Quick Start + +**Check for ready work:** + +```bash +bd ready --json +``` + +**Create new issues:** + +```bash +bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json +bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --json +``` + +**Claim and update:** + +```bash +bd update bd-42 --status in_progress --json +bd update bd-42 --priority 1 --json +``` + +**Complete work:** + +```bash +bd close bd-42 --reason "Completed" --json +``` + +**Creating human readable file:** +After every CRUD command on an issue, export it: + +```bash +bd export -o .claude/.beads/issues-dump.jsonl +``` + +### Issue Types + +- `bug` - Something broken +- `feature` - New functionality +- `task` - Work item (tests, docs, refactoring) +- `epic` - Large feature with subtasks +- `chore` - Maintenance (dependencies, tooling) + +### Priorities + +- `0` - Critical (security, data loss, broken builds) +- `1` - High (major features, important bugs) +- `2` - Medium (default, nice-to-have) +- `3` - Low (polish, optimization) +- `4` - Backlog (future ideas) + +### Workflow for AI Agents + +1. **Check ready work**: `bd ready` shows unblocked issues +2. **Claim your task**: `bd update <id> --status in_progress` +3. **Work on it**: Implement, test, document +4. **Discover new work?** Create linked issue: + - `bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>` +5. **Complete**: `bd close <id> --reason "Done"` + + +### Important Rules + +- ✅ Use bd for ALL task tracking +- ✅ Always use `--json` flag for programmatic use +- ✅ Link discovered work with `discovered-from` dependencies +- ✅ Check `bd ready` before asking "what should I work on?" +- ❌ Do NOT create markdown TODO lists +- ❌ Do NOT use external issue trackers +- ❌ Do NOT duplicate tracking systems + +For more details, see README.md and docs/QUICKSTART.md. + +<!-- END BEADS INTEGRATION --> diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/copier.yml b/copier.yml index 49f6ebb9..a0ef7e84 100644 --- a/copier.yml +++ b/copier.yml @@ -143,7 +143,13 @@ use_codecov: help: Upload code coverage results to CodeCov? default: no + _tasks: + - command: | + python3 '{{ _copier_conf.src_path }}/src/copier_tasks/remove_precommit_hooks.py' \ + --hook-id-regex '^\s*-\s+id:\s+merge-claude-settings\s*$' \ + --target-file .pre-commit-config.yaml + when: "{{ not install_claude_cli }}" - command: | if [ -f ruff.toml ]; then echo "Updating ruff target-version from python_version..." @@ -155,7 +161,7 @@ _tasks: fi # Additional Settings -_min_copier_version: "9.4" +_min_copier_version: "9.7" _subdirectory: template diff --git a/extensions/context.py b/extensions/context.py index d2654c2d..6e2bb1b5 100644 --- a/extensions/context.py +++ b/extensions/context.py @@ -10,21 +10,21 @@ class ContextUpdater(ContextHook): @override def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: - context["uv_version"] = "0.10.4" - context["pnpm_version"] = "10.30.0" + context["uv_version"] = "0.10.10" + context["pnpm_version"] = "10.32.1" context["pre_commit_version"] = "4.5.1" context["pyright_version"] = ">=1.1.408" context["pytest_version"] = ">=9.0.2" context["pytest_randomly_version"] = ">=4.0.1" context["pytest_cov_version"] = ">=7.0.0" - context["ty_version"] = ">=0.0.17" - context["copier_version"] = "==9.11.3" + context["ty_version"] = ">=0.0.23" + context["copier_version"] = "==9.14.0" context["copier_template_extensions_version"] = "==0.3.3" context["sphinx_version"] = "9.0.4" - context["pulumi_version"] = ">=3.223.0" - context["pulumi_aws_version"] = ">=7.20.0" - context["pulumi_aws_native_version"] = ">=1.54.0" - context["pulumi_command_version"] = ">=1.1.3" + context["pulumi_version"] = ">=3.226.0" + context["pulumi_aws_version"] = ">=7.23.0" + context["pulumi_aws_native_version"] = ">=1.57.0" + context["pulumi_command_version"] = ">=1.2.1" context["pulumi_github_version"] = ">=6.12.1" context["pulumi_okta_version"] = ">=6.2.3" context["boto3_version"] = ">=1.42.53" @@ -33,9 +33,9 @@ def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["pyinstaller_version"] = ">=6.19.0" context["setuptools_version"] = "80.7.1" context["strawberry_graphql_version"] = ">=0.298.0" - context["fastapi_version"] = ">=0.129.0" + context["fastapi_version"] = ">=0.135.1" context["fastapi_offline_version"] = ">=1.7.4" - context["uvicorn_version"] = ">=0.41.0" + context["uvicorn_version"] = ">=0.42.0" context["lab_auto_pulumi_version"] = ">=0.1.18" context["ariadne_codegen_version"] = ">=0.17.0" context["pytest_mock_version"] = ">=3.15.1" @@ -43,7 +43,7 @@ def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["syrupy_version"] = ">=5.1.0" context["structlog_version"] = ">=25.5.0" context["httpx_version"] = ">=0.28.1" - context["python_kiota_bundle_version"] = ">=1.9.8" + context["python_kiota_bundle_version"] = ">=1.9.10" context["vcrpy_version"] = ">=8.1.1" context["pytest_recording_version"] = ">=0.13.4" context["pytest_asyncio_version"] = ">=1.3.0" @@ -51,14 +51,14 @@ def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["python_faker_version"] = ">=40.4.0" context["default_node_version"] = "24.11.1" - context["nuxt_ui_version"] = "^4.4.0" - context["nuxt_version"] = "^4.3.1" + context["nuxt_ui_version"] = "^4.5.1" + context["nuxt_version"] = "~4.3.1" context["nuxt_icon_version"] = "^2.2.1" context["typescript_version"] = "^5.9.3" context["playwright_version"] = "^1.58.2" - context["vue_version"] = "^3.5.28" + context["vue_version"] = "^3.5.30" context["vue_tsc_version"] = "^3.2.4" - context["vue_devtools_api_version"] = "^8.0.0" + context["vue_devtools_api_version"] = "^8.1.0" context["vue_router_version"] = "^5.0.3" context["dotenv_cli_version"] = "^11.0.0" context["faker_version"] = "^10.3.0" @@ -79,21 +79,21 @@ def hook(self, context: dict[Any, Any]) -> dict[Any, Any]: context["vue_test_utils_version"] = "^2.4.6" context["nuxt_test_utils_version"] = "3.19.1" context["vue_eslint_parser_version"] = "^10.4.0" - context["happy_dom_version"] = "^20.6.3" - context["node_kiota_bundle_version"] = "1.0.0-preview.99" + context["happy_dom_version"] = "^20.8.4" + context["node_kiota_bundle_version"] = "1.0.0-preview.100" context["gha_checkout"] = "v6.0.2" context["gha_setup_python"] = "v6.2.0" - context["gha_cache"] = "v5.0.2" - context["gha_upload_artifact"] = "v6.0.0" - context["gha_download_artifact"] = "v7.0.0" + context["gha_cache"] = "v5.0.3" + context["gha_upload_artifact"] = "v7.0.0" + context["gha_download_artifact"] = "v8.0.0" context["gha_github_script"] = "v7.0.1" context["gha_setup_buildx"] = "v3.11.1" context["buildx_version"] = "v0.27.0" context["gha_docker_build_push"] = "v6.18.0" - context["gha_configure_aws_credentials"] = "v5.1.1" - context["gha_amazon_ecr_login"] = "v2.0.1" - context["gha_setup_node"] = "v6.2.0" + context["gha_configure_aws_credentials"] = "v6.0.0" + context["gha_amazon_ecr_login"] = "v2.0.2" + context["gha_setup_node"] = "v6.3.0" context["gha_action_gh_release"] = "v2.2.1" context["gha_mutex"] = "1ebad517141198e08d47cf72f3c0975316620a65 # v1.0.0-alpha.10" context["gha_pypi_publish"] = "v1.13.0" diff --git a/pyproject.toml b/pyproject.toml index f6c895e8..5fd41a98 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,7 +13,7 @@ dependencies = [ "pytest-cov>=7.0.0", "pytest-randomly>=4.0.1", "pyright[nodejs]>=1.1.408", - "ty>=0.0.17", - "copier==9.11.3", + "ty>=0.0.23", + "copier==9.14.0", "copier-template-extensions==0.3.3" ] diff --git a/src/copier_tasks/__init__.py b/src/copier_tasks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/copier_tasks/remove_precommit_hooks.py b/src/copier_tasks/remove_precommit_hooks.py new file mode 100644 index 00000000..9b0f0c25 --- /dev/null +++ b/src/copier_tasks/remove_precommit_hooks.py @@ -0,0 +1,105 @@ +import argparse +import re +from pathlib import Path + +# ruff: noqa: T201 # the hooks need to print to stdout to show anything + +HOOK_ID_LINE = re.compile(r"^-\s+id:\s") + + +def _is_matching_hook_block(block_lines: list[str], hook_id_pattern: re.Pattern[str]) -> bool: + return bool(hook_id_pattern.search(block_lines[0])) if block_lines else False + + +def remove_hook_blocks(config_path: Path, hook_id_pattern: re.Pattern[str]) -> int: + text = config_path.read_text(encoding="utf-8") + lines = text.splitlines() + + output_lines: list[str] = [] + index = 0 + removed_count = 0 + + while index < len(lines): + line = lines[index] + stripped = line.lstrip() + indentation = len(line) - len(stripped) + + if HOOK_ID_LINE.match(stripped): + block_start = index + block_end = index + 1 + + while block_end < len(lines): + next_line = lines[block_end] + next_stripped = next_line.lstrip() + next_indentation = len(next_line) - len(next_stripped) + if HOOK_ID_LINE.match(next_stripped) and next_indentation == indentation: + break + if next_stripped and next_indentation < indentation: + break + block_end += 1 + + block_lines = lines[block_start:block_end] + if _is_matching_hook_block(block_lines, hook_id_pattern): + removed_count += 1 + index = block_end + continue + + output_lines.extend(block_lines) + index = block_end + continue + + output_lines.append(line) + index += 1 + + if removed_count == 0: + return 0 + + trailing_newline = "\n" if text.endswith("\n") else "" + _ = config_path.write_text("\n".join(output_lines) + trailing_newline, encoding="utf-8") + return removed_count + + +def _parse_args() -> argparse.Namespace: + parser = argparse.ArgumentParser( + description="Remove matching hook blocks from a pre-commit config file.", + ) + _ = parser.add_argument( + "--hook-id-regex", + required=True, + dest="hook_id_regex", + help="Regex to match a hook id line (e.g. '^\\s*-\\s+id:\\s+.*graphql[_-]lambda').", + ) + _ = parser.add_argument( + "--target-file", + default=".pre-commit-config.yaml", + dest="target_file", + help="Path to the pre-commit config file (default: .pre-commit-config.yaml).", + ) + return parser.parse_args() + + +def main() -> int: + args = _parse_args() + target_file = Path(args.target_file) + + try: + hook_id_pattern = re.compile(args.hook_id_regex) + except re.error as error: + print(f"Invalid regex pattern for hook_id_regex: {error}") + return 2 + + if not target_file.exists(): + print(f"{target_file} not found; skipping hook removal.") + return 1 + + removed_count = remove_hook_blocks(target_file, hook_id_pattern) + if removed_count > 0: + print(f"Removed {removed_count} matching hook(s) from {target_file}.") + else: + print(f"No matching hooks found in {target_file}; no changes made.") + + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/template/.claude/.beads/.gitignore b/template/.claude/.beads/.gitignore new file mode 100644 index 00000000..bb8b4ce8 --- /dev/null +++ b/template/.claude/.beads/.gitignore @@ -0,0 +1,60 @@ +# Dolt database (managed by Dolt, not git) +dolt/ +dolt-access.lock + +# Runtime files +bd.sock +bd.sock.startlock +sync-state.json +last-touched + +# Local version tracking (prevents upgrade notification spam after git ops) +.local_version + +# Worktree redirect file (contains relative path to main repo's .beads/) +# Must not be committed as paths would be wrong in other clones +redirect + +# Sync state (local-only, per-machine) +# These files are machine-specific and should not be shared across clones +.sync.lock +.jsonl.lock +sync_base.jsonl +export-state/ + +# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned) +ephemeral.sqlite3 +ephemeral.sqlite3-journal +ephemeral.sqlite3-wal +ephemeral.sqlite3-shm + +# Legacy files (from pre-Dolt versions) +*.db +*.db?* +*.db-journal +*.db-wal +*.db-shm +db.sqlite +bd.db +daemon.lock +daemon.log +daemon-*.log.gz +daemon.pid +beads.base.jsonl +beads.base.meta.json +beads.left.jsonl +beads.left.meta.json +beads.right.jsonl +beads.right.meta.json + +# NOTE: Do NOT add negation patterns (e.g., !issues.jsonl) here. +# They would override fork protection in .git/info/exclude, allowing +# contributors to accidentally commit upstream issue databases. +# The JSONL files (issues.jsonl, interactions.jsonl) and config files +# are tracked by git by default since no pattern above ignores them. + +# at the moment, we're just using beads for local development, so don't commit any jsonl +interactions.jsonl +issues.jsonl +backup/ +issues-dump.jsonl diff --git a/template/.claude/.beads/config.yaml b/template/.claude/.beads/config.yaml new file mode 100644 index 00000000..25090ef6 --- /dev/null +++ b/template/.claude/.beads/config.yaml @@ -0,0 +1,44 @@ +# Beads Configuration File +# This file configures default behavior for all bd commands in this repository +# All settings can also be set via environment variables (BD_* prefix) +# or overridden with command-line flags + +# Issue prefix for this repository (used by bd init) +# If not set, bd init will auto-detect from directory name +# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc. +# issue-prefix: "" + +# Use no-db mode: load from JSONL, write back after each command +# When true, bd will use .beads/issues.jsonl as the source of truth +# instead of the Dolt database +# no-db: false + +# Enable JSON output by default +# json: false + +# Default actor for audit trails (overridden by BD_ACTOR or --actor) +# actor: "" + +# Export events (audit trail) to .beads/events.jsonl on each flush/sync +# When enabled, new events are appended incrementally using a high-water mark. +# Use 'bd export --events' to trigger manually regardless of this setting. +# events-export: false + +# Multi-repo configuration (experimental - bd-307) +# Allows hydrating from multiple repositories and routing writes to the correct JSONL +# repos: +# primary: "." # Primary repo (where this database lives) +# additional: # Additional repos to hydrate from (read-only) +# - ~/beads-planning # Personal planning repo +# - ~/work-planning # Work planning repo + +# Integration settings (access with 'bd config get/set') +# These are stored in the database, not in this file: +# - jira.url +# - jira.project +# - linear.url +# - linear.api-key +# - github.org +# - github.repo +no-git-ops: true +backup.enabled: false # in v0.57 the backup seems to automatically add stage the files into git, bypassing the .gitignore I tried to set up, so disabling for now (also we don't really need backup anyway if we're not pushing into git, but this would be nicer long term than having to run the export command manually all the time) diff --git a/template/.claude/.beads/metadata.json b/template/.claude/.beads/metadata.json new file mode 100644 index 00000000..a4be941e --- /dev/null +++ b/template/.claude/.beads/metadata.json @@ -0,0 +1,8 @@ +{ + "database": "dolt", + "jsonl_export": "issues.jsonl", + "backend": "dolt", + "dolt_mode": "server", + "dolt_server_host": "beads-dolt", + "dolt_database": "beads_work" +} diff --git a/template/.claude/commands/add-command.md b/template/.claude/commands/add-command.md new file mode 100644 index 00000000..a19105a3 --- /dev/null +++ b/template/.claude/commands/add-command.md @@ -0,0 +1,167 @@ +--- +description: Guide for creating new slash commands +argument-hint: <command-name> <description> +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +# Slash Command Creator Guide + +## How This Command Works + +The `/add-command` command shows this guide for creating new slash commands. It includes: + +- Command structure and syntax +- Common patterns and examples +- Security restrictions and limitations +- Frontmatter options + +**Note for AI**: When creating commands, you CAN use bash tools like `Bash(mkdir:*)`, `Bash(ls:*)`, `Bash(git status:*)` in the `allowed-tools` frontmatter of NEW commands - but ONLY for operations within the current project directory. This command itself doesn't need bash tools since it's just documentation. + +## Command Locations + +- **Personal**: `~/.claude/commands/` (available across all projects) +- **Project**: `.claude/commands/` (shared with team, shows "(project)") + +## Basic Structure + +```markdown +--- +allowed-tools: Read, Glob, Grep, Bash(git status:*), Task +description: Brief description of what this command does +argument-hint: [required-arg] [optional-arg] +--- + +# Command Title + +Your command instructions here. + +**User arguments:** + +Add-command: $ARGUMENTS + +**End of user arguments** + +File reference: @path/to/file.js + +Bash command output: (exclamation)git status(backticks) +``` + +## ⚠️ Security Restrictions + +**Bash Commands (exclamation prefix)**: Limited to current working directory only. + +- ✅ Works: `! + backtick + git status + backtick` (in project dir) +- ❌ Blocked: `! + backtick + ls /outside/project + backtick` (outside project) +- ❌ Blocked: `! + backtick + pwd + backtick` (if referencing dirs outside project) + +**File References (`@` prefix)**: No directory restrictions. + +- ✅ Works: `@/path/to/system/file.md` +- ✅ Works: `@../other-project/file.js` + +## Common Patterns + +### Simple Command + +```bash +echo "Review this code for bugs and suggest fixes" > ~/.claude/commands/review.md +``` + +### Command with Arguments + +**Note for AI**: The example below uses a fullwidth dollar sign ($, U+FF04) to prevent interpolation in this documentation. When creating actual commands, use the regular `$` character. + +```markdown +Fix issue $ARGUMENTS following our coding standards +``` + +### Command with File References + +```markdown +Compare @src/old.js with @src/new.js and explain differences +``` + +### Command with Bash Output (Project Directory Only) + +```markdown +--- +allowed-tools: Bash(git status:*), Bash(git branch:*), Bash(git log:*) +--- +Current status: (!)git status(`) +Current branch: (!)git branch --show-current(`) +Recent commits: (!)git log --oneline -5(`) + +Create commit for these changes. +``` + +**Note**: Only works with commands in the current project directory. + +### Namespaced Command + +**Note for AI**: The example below uses a fullwidth dollar sign ($, U+FF04) to prevent interpolation in this documentation. When creating actual commands, use the regular `$` character. + +```bash +mkdir -p ~/.claude/commands/ai +echo "Ask GPT-5 about: $ARGUMENTS" > ~/.claude/commands/ai/gpt5.md +# Creates: /ai:gpt5 +``` + +## Frontmatter Options + +- `allowed-tools`: Tools this command can use + - **Important**: Intrusive tools like `Write`, `Edit`, `NotebookEdit` should NEVER be allowed in commands unless the user explicitly requests them. These tools modify files and should only be used when the command's purpose is to make changes. + - ✅ Safe for most commands: `Read`, `Glob`, `Grep`, `Bash(git status:*)`, `Task`, `AskUserQuestion` +- `description`: Brief description (shows in /help) +- `argument-hint`: Help text for arguments +- `model`: Specific model to use + +## Best Practices + +### Safe Commands (No Security Issues) + +```markdown +# System prompt editor (file reference only) +(@)path/to/system/prompt.md + +Edit your system prompt above. +``` + +### Project-Specific Commands (Bash OK) + +```markdown +--- +allowed-tools: Bash(git status:*), Bash(npm list:*) +--- +Current git status: (!)git status(`) +Package info: (!)npm list --depth=0(`) + +Review project state and suggest next steps. +``` + +### Cross-Directory File Access (Use @ not !) + +```markdown +# Compare config files +Compare (@)path/to/system.md with (@)project/config.md + +Show differences and suggest improvements. +``` + +## Usage + +After creating: `/<command-name> [arguments]` + +Example: `/review` or `/ai:gpt5 "explain this code"` diff --git a/template/.claude/commands/commit.md b/template/.claude/commands/commit.md new file mode 100644 index 00000000..b11c370e --- /dev/null +++ b/template/.claude/commands/commit.md @@ -0,0 +1,57 @@ +--- +description: Create a git commit following project standards +argument-hint: [optional-commit-description] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Create a git commit following project standards + +**User arguments:** + +Commit: $ARGUMENTS + +**End of user arguments** + +## Commit Message Rules + +Follows [Conventional Commits](https://www.conventionalcommits.org/) standard. + +1. **Format**: `type(#issue): description` + - Use `#123` for local repo issues + - Use `owner/repo#123` for cross-repo issues + - Common types: `feat`, `fix`, `docs`, `refactor`, `test`, `chore` + +2. **AI Credits**: **NEVER include AI credits in commit messages** + - No "Generated with Claude Code" + - No "Co-Authored-By: Claude" or "Co-Authored-By: Happy" + - Focus on the actual changes made, not conversation history + +3. **Content**: Write clear, concise commit messages describing what changed and why + +## Process + +1. Run `git status` and `git diff` to review changes +2. Run `git log --oneline -5` to see recent commit style +3. Stage relevant files with `git add` +4. Create commit with descriptive message +5. Verify with `git status` + +## Example + +```bash +git add <files> +git commit -m "feat(#123): add validation to user input form" +``` diff --git a/template/.claude/commands/create-adr.md b/template/.claude/commands/create-adr.md new file mode 100644 index 00000000..307ed7ef --- /dev/null +++ b/template/.claude/commands/create-adr.md @@ -0,0 +1,240 @@ +--- +description: Create a new Architecture Decision Record (ADR) +argument-hint: <title or topic of the architectural decision> +--- + +# Create ADR: Architecture Decision Record Creator + +Create a new ADR to document an architectural decision. ADRs capture the "why" behind technical choices, helping future developers understand constraints and tradeoffs. + +> ADRs were introduced by Michael Nygard in 2011. The core structure (Context, Decision, Consequences) remains the standard. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +**User arguments:** + +Create-adr: $ARGUMENTS + +**End of user arguments** + +(If no input provided, ask user for the architectural decision topic) + +## Process + +### Step 1: Detect Existing ADR Setup + +Check for existing ADR directory and structure: + +```bash +# Check common ADR directories (in order of preference) +for dir in doc/adr docs/adr decisions doc/architecture/decisions; do + if [ -d "$dir" ]; then + echo "Found: $dir" + ls "$dir"/*.md 2>/dev/null + break + fi +done +``` + +**If ADRs exist:** Read the first few ADRs (especially 0001 if present) to understand: + +- The template/structure this project uses +- Any project-specific sections or frontmatter +- Naming conventions and style + +Adapt the new ADR to match the existing pattern. + +**If no ADR directory exists:** Run the initialization flow (Step 1b). + +### Step 1b: Initialize ADR Practice (First-Time Setup) + +When no existing ADRs are found, help the user set up their ADR practice. + +Ask the user (use AskUserQuestion): + +**Directory location:** + +- doc/adr (Recommended - conventional location) +- docs/adr +- decisions +- doc/architecture/decisions + +**Template style:** + +- Minimal (Nygard's original: Context, Decision, Consequences) +- Standard (Minimal + Status, Date, References) +- With scope (Standard + applies_to patterns, code examples) + +**Create a foundational ADR-0001?** + +- Yes - document "We will use ADRs to record architectural decisions" +- No - proceed directly to creating the requested ADR + +If creating ADR-0001, generate it with: + +- Context: Why the team needs to document decisions +- Decision: Adopt ADRs following [chosen template style] +- Consequences: Better knowledge transfer, slight overhead per decision + +### Step 2: Determine ADR Number + +Calculate next number from existing ADRs: + +1. Extract highest existing number +2. Increment by 1 +3. Format as 4-digit zero-padded (e.g., `0001`, `0012`) + +### Step 3: Discovery Questions + +Gather context through conversation (use AskUserQuestion for structured choices): + +**Context & Problem** + +- What forces are at play? (technological, social, project constraints) +- What problem, pattern, or situation prompted this decision? +- What triggered the need to decide now? (bug, confusion, inconsistency, new requirement) +- Are there related PRs, issues, or prior discussions to reference? + +**The Decision** + +- What are we deciding to do (or not do)? +- What alternatives were considered? +- Why was this approach chosen over alternatives? + +**Consequences** + +- What becomes easier or more consistent with this decision? +- What becomes harder, more constrained, or riskier? +- What tradeoffs are we explicitly accepting? + +**Scope** + +- Which parts of the codebase does this apply to? +- Are there exceptions or areas where this doesn't apply? + +### Step 4: Generate ADR File + +Create `{adr_directory}/NNNN-title-slug.md`: + +- Convert title to kebab-case slug (lowercase, hyphens, no special chars) +- Use today's date for the `date` field +- Default status to `accepted` (most ADRs are written after the decision is made) + +**ADR Template:** + +```markdown +--- +status: accepted +date: YYYY-MM-DD +applies_to: + - "**/*.ts" + - "**/*.tsx" +--- + +# N. Title + +## Context + +[Forces at play - technological, social, project constraints. +What problem prompted this? Value-neutral description of the situation.] + +## Decision + +We will [decision statement in active voice]. + +[If the decision involves code patterns, include concrete examples:] + +**Forbidden pattern:** +\`\`\`typescript +// ❌ BAD - [explanation] +[example of what NOT to do] +\`\`\` + +**Required pattern:** +\`\`\`typescript +// ✅ GOOD - [explanation] +[example of what TO do] +\`\`\` + +## Consequences + +**Positive:** +- [What becomes easier] +- [What becomes more consistent] + +**Negative:** +- [What becomes harder] +- [What constraints we accept] + +**Neutral:** +- [Other impacts worth noting] + +## References + +- [Related PRs, issues, or documentation] +``` + +### Step 5: Refine applies_to Scope + +Help user define which files this decision applies to using glob patterns: + +- All TypeScript files: **/*.ts +- All React component files: **/*.tsx +- Only files in components directory: src/components/** +- Exclude test files (prefix with !): !**/*.test.ts +- Exclude type definition files: !**/*.d.ts +- Specific package only: packages/api/** + +If the decision applies broadly, use **/* (all files). + +**Note**: `applies_to` is recommended for the "With scope" template. Linters and AI assistants use these patterns to determine which files to check against this ADR. + +### Step 6: Confirm and Write + +Show the complete ADR content and ask user to confirm before writing. + +After creation, suggest: + +- Review the ADR for completeness +- Commit with `/commit` + +## Tips for Good ADRs + +1. **Focus on the "why"** - The decision itself may be obvious; the reasoning often isn't +2. **Keep it concise** - 1-2 pages maximum; should be readable in 5 minutes +3. **Use active voice** - "We will use X" not "X will be used" +4. **Include concrete examples** - Code examples make abstract decisions tangible +5. **Document tradeoffs honestly** - Every decision has costs; be explicit about them +6. **Link to context** - Reference PRs, issues, or discussions where the decision was made +7. **Be specific about scope** - Use `applies_to` patterns to clarify affected code + +## Status Values + +| Status | When to Use | +|--------|-------------| +| `proposed` | Under discussion, not yet agreed | +| `accepted` | Agreed upon and should be followed | +| `deprecated` | No longer relevant (context changed) | +| `superseded` | Replaced by another ADR (link to it) | + +To supersede an existing ADR: + +1. Create new ADR with the updated decision +2. Update old ADR's status to `superseded by ADR-NNNN` + +## Integration with Other Commands + +- After creating: Commit with `/commit` +- If decision needs discussion: Create issue with `/create-issues` diff --git a/template/.claude/commands/create-issues.md b/template/.claude/commands/create-issues.md new file mode 100644 index 00000000..76d7cd2a --- /dev/null +++ b/template/.claude/commands/create-issues.md @@ -0,0 +1,195 @@ +--- +description: Create implementation plan from feature/requirement with PRD-style discovery and TDD acceptance criteria +argument-hint: <feature/requirement description or GitHub issue URL/number> +--- + +# Create Issues: PRD-Informed Task Planning for TDD + +Create structured implementation plan that bridges product thinking (PRD) with test-driven development. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +**User arguments:** + +Create-issues: $ARGUMENTS + +**End of user arguments** + +(If no input provided, check conversation context or run `bd ready` to see existing work) + +## Input Processing + +The input can be one of: + +1. **GitHub Issue URL** (e.g., `https://github.com/owner/repo/issues/123`) +2. **GitHub Issue Number** (e.g., `#123` or `123`) +3. **Feature Description** (e.g., "Add user authentication") +4. **Empty** - use conversation context + +### GitHub Issue Integration + +If input looks like a GitHub issue: + +**Step 1: Extract Issue Number** + +- From URL: extract owner/repo/number +- From number: try to infer repo from git remote +- From branch name: check patterns like `issue-123`, `123-feature`, `feature/123` + +**Step 2: Fetch Issue** + +Use the GitHub CLI to fetch issue details: + +```bash +gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state +``` + +If the `gh` CLI is not installed or authenticated, show: + +``` +GitHub CLI not available or not authenticated! +Run: gh auth login +``` + +**Step 3: Use Issue as Discovery Input** + +- Title → Feature name +- Description → Problem statement and context +- Labels → Type/priority hints +- Comments → Additional requirements and discussion +- Linked issues → Dependencies + +Extract from GitHub issue: + +- Problem statement and context +- Acceptance criteria (if present) +- Technical notes (if present) +- Related issues/dependencies + +## Process + +## Discovery Phase + +Understand the requirement by asking (use AskUserQuestion if needed): + +**Problem Statement** + +- What problem does this solve? +- Who experiences this problem? +- What's the current pain point? + +**Desired Outcome** + +- What should happen after this is built? +- How will users interact with it? +- What does success look like? + +**Scope & Constraints** + +- What's in scope vs. out of scope? +- Any technical constraints? +- Dependencies on other systems/features? + +**Context Check** + +- Search codebase for related features/modules +- Check for existing test files that might be relevant + +### Create Beads Issues + +For each task, create a bd issue with: + +```bash +bd create "Task title" \ + --type [feature|bug|task|chore] \ + --priority [1-3] \ + --description "Context and what needs to be built" \ + --design "Technical approach, architecture notes" \ + --acceptance "Given-When-Then acceptance criteria" +``` + +**Issue Structure Best Practices:** + +**Title**: Action-oriented, specific + +- ✅ "Add JWT token validation middleware" +- ❌ "Authentication stuff" + +**Description**: Provide context + +- Why this task exists +- How it fits into the larger feature +- Links to related issues/docs + +**Design**: Technical approach + +- Key interfaces/types needed +- Algorithm or approach +- Libraries or patterns to use +- Known gotchas or considerations + +**Acceptance Criteria**: Test-ready scenarios + +- Given-When-Then format +- Concrete, verifiable conditions +- Cover main case + edge cases +- Map 1:1 to future tests + +**Dependencies**: Link related issues + +```bash +bd dep add ISSUE-123 ISSUE-456 --type blocks +``` + +### Validation + +After creating issues, verify: + +- ✅ Each issue has clear acceptance criteria +- ✅ Dependencies are mapped (use `bd dep add`) +- ✅ Issues are ordered by implementation sequence +- ✅ First few issues are ready to start (`bd ready` shows them) +- ✅ Each issue is small enough for TDD (if too big, break down more) + +## Key Principles + +**From PRD World:** + +- Start with user problems, not solutions +- Define success criteria upfront +- Understand constraints and scope + +**From TDD World:** + +- Make acceptance criteria test-ready +- Break work into small, testable pieces +- Each task should map to test(s) + +### Beads Integration + +Use Beads MCP to: + +- Track work with `bd ready` to find next task +- Create issues with `bd create "description"` +- Track dependencies with `bd dep add` + +See <https://github.com/steveyegge/beads> for more information. + +## Integration with Other Commands + +- **Before /create-issues**: Use `/spike` if you need technical exploration first +- **After /create-issues**: Use `/red` to start TDD on first task +- **During work**: Use `bd update` to add notes/findings back to issues +- **When stuck**: Check `bd show ISSUE-ID` to review acceptance criteria diff --git a/template/.claude/commands/gap.md b/template/.claude/commands/gap.md new file mode 100644 index 00000000..e8abaeec --- /dev/null +++ b/template/.claude/commands/gap.md @@ -0,0 +1,45 @@ +--- +description: Analyze conversation context for unaddressed items and gaps +argument-hint: [optional additional info] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Analyze the current conversation context and identify things that have not yet been addressed. Look for: + +1. **Incomplete implementations** - Code that was started but not finished +2. **Unused variables/results** - Values that were captured but never used +3. **Missing tests** - Functionality without test coverage +4. **Open issues** - Beads issues that are still open or in progress + +5. **User requests** - Things the user asked for that weren't fully completed +6. **TODO comments** - Any TODOs mentioned in conversation +7. **Error handling gaps** - Missing error cases or edge cases +8. **Documentation gaps** - Undocumented APIs or features +9. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase + +Present findings as a prioritized list with: + +- What the gap is +- Why it matters +- Suggested next action + +If there are no gaps, confirm that everything discussed has been addressed. + +**User arguments:** + +Gap: $ARGUMENTS + +**End of user arguments** diff --git a/template/.claude/commands/green.md b/template/.claude/commands/green.md new file mode 100644 index 00000000..bc1b159b --- /dev/null +++ b/template/.claude/commands/green.md @@ -0,0 +1,110 @@ +--- +description: Execute TDD Green Phase - write minimal implementation to pass the failing test +argument-hint: <implementation description> +--- + +**User arguments:** + +Green: $ARGUMENTS + +**End of user arguments** + +GREEN PHASE! Apply the below to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +### Post-Green Verification + +Once the test passes, run the coverage tool scoped to **only the files you edited** and check for uncovered lines: + +- Any uncovered lines in files you edited are over-implementation — **delete them** +- Do not scope to the full test suite; focus only on what changed + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. diff --git a/template/.claude/commands/issue.md b/template/.claude/commands/issue.md new file mode 100644 index 00000000..fc39502a --- /dev/null +++ b/template/.claude/commands/issue.md @@ -0,0 +1,162 @@ +--- +description: Analyze GitHub issue and create TDD implementation plan +argument-hint: [optional-issue-number] +--- + +Analyze GitHub issue and create TDD implementation plan. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Process: + +1. Get Issue Number + +**User arguments:** + +Issue: $ARGUMENTS + +**End of user arguments** + +- Check if argument is an issue number +- Otherwise try branch name patterns: issue-123, 123-feature, feature/123, fix/123 +- If not found: ask user + +1. Fetch Issue + +Use the GitHub CLI to fetch issue details: + +```bash +gh issue view [ISSUE_NUMBER] --json title,body,labels,comments,state +``` + +If the `gh` CLI is not installed or authenticated, show: + +``` +GitHub CLI not available or not authenticated! +Run: gh auth login +``` + +1. Analyze and Plan + +Summarize the issue and requirements, then: + +## Discovery Phase + +Understand the requirement by asking (use AskUserQuestion if needed): + +**Problem Statement** + +- What problem does this solve? +- Who experiences this problem? +- What's the current pain point? + +**Desired Outcome** + +- What should happen after this is built? +- How will users interact with it? +- What does success look like? + +**Scope & Constraints** + +- What's in scope vs. out of scope? +- Any technical constraints? +- Dependencies on other systems/features? + +**Context Check** + +- Search codebase for related features/modules +- Check for existing test files that might be relevant + +### Beads Integration + +Use Beads MCP to: + +- Track work with `bd ready` to find next task +- Create issues with `bd create "description"` +- Track dependencies with `bd dep add` + +See <https://github.com/steveyegge/beads> for more information. + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. diff --git a/template/.claude/commands/polish.md b/template/.claude/commands/polish.md new file mode 100644 index 00000000..b42ef856 --- /dev/null +++ b/template/.claude/commands/polish.md @@ -0,0 +1,185 @@ +--- +description: Review and address issues in existing code - fix problems or justify skipping +argument-hint: [branch, PR#, file, or area to polish] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +# Polish + +Take another pass at existing work to address issues. Unlike `/code-review` which only identifies problems, `/polish` resolves each finding by either: + +1. **Fixing** - Implement the improvement +2. **Skipping with justification** - Document why the issue can be deferred or ignored + +## Phase 0: Determine Scope + +Parse the argument to determine what to polish: + +| Input | Action | +|-------|--------| +| No argument | Detect divergence point, review uncommitted + committed changes | +| Branch name | Changes from that branch to HEAD | +| PR number (e.g., `123`) | Fetch PR diff from GitHub | +| PR URL (e.g., `github.com/.../pull/123`) | Extract PR number and fetch diff | +| File/path | Focus on specific file(s) | + +Use the GitHub CLI to fetch PR details: + +```bash +gh pr view [PR_NUMBER] --json title,body,state,mergeable,headRefName,baseRefName +gh pr diff [PR_NUMBER] +``` + +If the `gh` CLI is not installed or authenticated, show: + +``` +GitHub CLI not available or not authenticated! +Run: gh auth login +``` + +**For local branches:** + +1. Get current branch: `git rev-parse --abbrev-ref HEAD` +2. Detect divergence point (same logic as `/code-review`) +3. Collect changed files from diff and uncommitted changes + +## Phase 1: Identify Issues + +Categorize files based on these patterns: + +| Category | File Patterns | +|----------|---------------| +| Frontend/UI | `*.tsx`, `*.jsx`, `components/`, `pages/`, `views/`, `*.vue` | +| Frontend/Styling | `*.css`, `*.scss`, `*.less`, `styles/`, `*.tailwind*`, `*.styled.*` | +| Backend/API | `routes/`, `api/`, `controllers/`, `services/`, `*.controller.*`, `*.service.*`, `*.resolver.*` | +| Backend/Data | `migrations/`, `models/`, `prisma/`, `schema.*`, `*.model.*`, `*.entity.*` | +| Tooling/Config | `scripts/`, `*.config.*`, `package.json`, `tsconfig.*`, `vite.*`, `webpack.*`, `eslint.*` | +| CI/CD | `.github/`, `.gitlab-ci.*`, `Dockerfile`, `docker-compose.*`, `*.yml` in CI paths | +| Tests | `*.test.*`, `*.spec.*`, `__tests__/`, `__mocks__/`, `*.stories.*` | +| Docs | `*.md`, `docs/`, `README*`, `CHANGELOG*` | + +For each category, identify issues at these severity levels: + +- **blocker** - Must fix before merge +- **risky** - Should fix or have strong justification +- **nit** - Nice to have, easily skippable + +## Phase 2: Address Each Issue + +For each identified issue, present it and then take action: + +### Format + +``` +### [file:line] [severity] Title + +**Issue:** Description of the problem + +**Action taken:** +- [ ] Fixed: [what was done] +- [ ] Skipped: [justification] +``` + +### Decision Guidelines + +**Fix when:** + +- Security vulnerability +- Correctness bug +- Missing error handling that could crash +- Breaking API changes without migration +- Tests that don't actually test anything + +**Skip with justification when:** + +- Stylistic preference with no functional impact +- Optimization for unlikely hot paths +- Refactoring that would expand scope significantly +- Issue exists in code outside the change scope +- Technical debt documented for future sprint + +### Fixing Issues + +When fixing: + +1. Make the minimal change to address the issue +2. Ensure tests still pass (run them if needed) +3. Don't expand scope beyond the identified issue + +### Watch for Brittle Tests + +When refactoring implementation, watch for **Peeping Tom** tests that: + +- Test private methods or internal state directly +- Assert on implementation details rather than behavior +- Break on any refactoring even when behavior is preserved + +If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. + +### Skipping Issues + +Valid skip justifications: + +- "Out of scope - exists in unchanged code" +- "Performance optimization unnecessary - called N times per request" +- "Tracked for future work - see issue #X" +- "Intentional design decision - [reason]" +- "Would require significant refactoring - defer to dedicated PR" + +Invalid skip justifications: + +- "Too hard to fix" +- "It works fine" +- "No time" + +## Phase 3: Cross-Cutting Check + +After addressing individual issues: + +1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase + +Additional cross-cutting checks: + +- Did fixes introduce new inconsistencies? +- Are skip justifications consistent with each other? +- Any patterns in what was skipped that suggest a bigger issue? + +## Phase 4: Summary + +``` +## Polish Summary + +### Fixed +- [list of fixes applied] + +### Skipped (with justification) +- [issue]: [justification] + +### Tests +- [ ] All tests passing +- [ ] No new warnings introduced + +### Remaining Work +- [any follow-up items identified] +``` + +--- + +**User arguments:** + +Polish: $ARGUMENTS + +**End of user arguments** diff --git a/template/.claude/commands/red.md b/template/.claude/commands/red.md new file mode 100644 index 00000000..e6120877 --- /dev/null +++ b/template/.claude/commands/red.md @@ -0,0 +1,111 @@ +--- +description: Execute TDD Red Phase - write ONE failing test +argument-hint: [optional additional info] +--- + +**User arguments:** + +Red: $ARGUMENTS + +**End of user arguments** + +RED PHASE! Apply the below to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. + +### Test Structure (AAA Pattern) + +Structure each test with clear phases: + +- **Arrange**: Set up test data and preconditions (keep minimal) +- **Act**: Execute the single action being tested +- **Assert**: Verify the expected outcome with specific assertions diff --git a/template/.claude/commands/refactor.md b/template/.claude/commands/refactor.md new file mode 100644 index 00000000..15119e16 --- /dev/null +++ b/template/.claude/commands/refactor.md @@ -0,0 +1,127 @@ +--- +description: Execute TDD Refactor Phase - improve code structure while keeping tests green +argument-hint: <refactoring description> +--- + +**User arguments:** + +Refactor: $ARGUMENTS + +**End of user arguments** + +Apply this document (specifically the Refactor phase) to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. + +## Code Complexity Signals + +Look for these refactoring opportunities: + +- [ ] Nesting > 3 levels deep +- [ ] Functions > 20 lines +- [ ] Duplicate code blocks +- [ ] Abstractions with single implementation +- [ ] "Just in case" parameters or config +- [ ] Magic values without names +- [ ] Dead/unused code + +### Watch for Brittle Tests + +When refactoring implementation, watch for **Peeping Tom** tests that: + +- Test private methods or internal state directly +- Assert on implementation details rather than behavior +- Break on any refactoring even when behavior is preserved + +If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. + +1. **Consistency check** - Look for inconsistent patterns, naming conventions, or structure across the codebase diff --git a/template/.claude/commands/research.md b/template/.claude/commands/research.md new file mode 100644 index 00000000..0a41c22f --- /dev/null +++ b/template/.claude/commands/research.md @@ -0,0 +1,96 @@ +--- +description: Research a problem in parallel via web docs, web search, codebase exploration, and deep ultrathink +argument-hint: <research topic or question> +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +**User arguments:** + +Research: $ARGUMENTS + +**End of user arguments** + +Research the following problem or question thoroughly, like a senior developer would. + +## Step 1: Launch Parallel Research Agents + +Use the Task tool to spawn these subagents **in parallel** (all in a single message): + +1. **Web Documentation Agent** (subagent_type: general-purpose) + - Search official documentation for the topic + - Find best practices and recommended patterns + - Locate relevant GitHub issues or discussions + +2. **Web Search Agent** (subagent_type: general-purpose) + - Perform broad web searches for solutions and discussions + - Find Stack Overflow answers, blog posts, and tutorials + - Note common pitfalls and gotchas + +3. **Codebase Explorer Agent** (subagent_type: Explore) + - Search the codebase for related patterns + - Find existing solutions to similar problems + - Identify relevant files, functions, or components + +## Step 2: Library Documentation (Optional) + +If the research involves specific frameworks or libraries: + +- Use Context7 MCP tools (mcp__context7__resolve-library-id, then get-library-docs) +- Get up-to-date API references and code examples +- If Context7 is unavailable, note this in findings so user knows library docs were harder to obtain + +## Step 3: Deep Analysis + +With all gathered context, perform extended reasoning (ultrathink) to: + +- Analyze the problem from first principles +- Consider edge cases and trade-offs +- Synthesize insights across all sources +- Identify conflicts between sources + +## Step 4: Present Findings + +Present a structured summary to the user: + +### Problem Statement + +Describe the problem and why it matters. + +### Key Findings + +Summarize the most relevant solutions and approaches. + +### Codebase Patterns + +Document how the current codebase handles similar cases. + +### Recommended Approach + +Provide your recommendation based on all research. + +### Conflicts + +Highlight where sources disagree and provide assessment of which is more reliable. + +### Sources + +List all source links with brief descriptions. This section is required. + +## Research Guidelines + +- Prioritize official documentation over blog posts +- Prefer solutions that match existing codebase patterns +- Note major.minor versions for libraries/frameworks (patch versions only if critical) +- Flag conflicting information across sources +- Write concise, actionable content +- Use active voice throughout +- **Do not create output files** - present findings directly in conversation unless user explicitly requests a file diff --git a/template/.claude/commands/simplify.md b/template/.claude/commands/simplify.md new file mode 100644 index 00000000..55242a98 --- /dev/null +++ b/template/.claude/commands/simplify.md @@ -0,0 +1,84 @@ +--- +description: Reduce code complexity while keeping tests green +argument-hint: [file, function, or area to simplify] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +**User arguments:** + +Simplify: $ARGUMENTS + +**End of user arguments** + +(If there was no info above, fallback to the context of the conversation) + +Reduce complexity while keeping tests green. + +## Core Principles + +**YAGNI** - Don't build until actually needed. Delete "just in case" code. + +**KISS** - Simplest solution that works. Clever is the enemy of clear. + +**Rule of Three** - Don't abstract until 3rd occurrence. "Prefer duplication over wrong abstraction" (Sandi Metz). + +## When NOT to Simplify + +- Essential domain complexity (regulations, business rules) +- Performance-critical optimized code +- Concurrency/thread-safety requirements +- Security-sensitive explicit checks + +## Prerequisites + +Tests must be green. If failing, use `/green` first. + +## Code Complexity Signals + +Look for these refactoring opportunities: + +- [ ] Nesting > 3 levels deep +- [ ] Functions > 20 lines +- [ ] Duplicate code blocks +- [ ] Abstractions with single implementation +- [ ] "Just in case" parameters or config +- [ ] Magic values without names +- [ ] Dead/unused code + +## Techniques + +| Pattern | Before | After | +|---------|--------|-------| +| Guard clause | Nested `if/else` | Early `return` | +| Named condition | Complex boolean | `const isValid = ...` | +| Extract constant | `if (x > 3)` | `if (x > MAX_RETRIES)` | +| Flatten callback | `.then().then()` | `async/await` | + +**Also apply:** Consolidate duplicates, inline unnecessary abstractions, delete dead code. + +## Validate + +1. Tests still green +2. Code reads more clearly +3. No behavioral changes + +**Simplify** removes complexity locally. **Refactor** improves architecture broadly. Use `/refactor` if changes require structural reorganization. + +### Watch for Brittle Tests + +When refactoring implementation, watch for **Peeping Tom** tests that: + +- Test private methods or internal state directly +- Assert on implementation details rather than behavior +- Break on any refactoring even when behavior is preserved + +If tests fail after a pure refactoring (no behavior change), consider whether the tests are testing implementation rather than behavior. diff --git a/template/.claude/commands/spike.md b/template/.claude/commands/spike.md new file mode 100644 index 00000000..72a8fdb5 --- /dev/null +++ b/template/.claude/commands/spike.md @@ -0,0 +1,103 @@ +--- +description: Execute TDD Spike Phase - exploratory coding to understand problem space before TDD +argument-hint: <exploration description> +--- + +**User arguments:** + +Spike: $ARGUMENTS + +**End of user arguments** + +SPIKE PHASE! Apply the below to the user input above. + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. diff --git a/template/.claude/commands/summarize.md b/template/.claude/commands/summarize.md new file mode 100644 index 00000000..d568f342 --- /dev/null +++ b/template/.claude/commands/summarize.md @@ -0,0 +1,62 @@ +--- +description: Summarize conversation progress and next steps +argument-hint: [optional additional info] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +Create a concise summary of the current conversation suitable for transferring context to a new conversation. + +**User arguments:** + +Summarize: $ARGUMENTS + +**End of user arguments** + +## Summary Structure + +Provide a summary with these sections: + +### What We Did + +- Key accomplishments and changes made +- Important decisions or discoveries +- Files created, modified, or analyzed + +### What We're Doing Next + +- Immediate next steps +- Pending tasks or work in progress +- Goals or objectives to continue + +### Blockers & User Input Needed + +- Any issues requiring user intervention +- Decisions that need to be made +- Missing information or clarifications needed + +## Output Format + +Keep the summary concise and actionable - suitable for pasting into a new conversation to quickly restore context without needing the full conversation history. + +## Beads Integration + +If Beads MCP is available, check for task tracking status and ask if the user wants to: + +1. Review current task status +2. Update task states based on conversation progress +3. Include Beads context in the summary + +Use AskUserQuestion to confirm Beads integration preferences. diff --git a/template/.claude/commands/tdd-review.md b/template/.claude/commands/tdd-review.md new file mode 100644 index 00000000..b3612fb8 --- /dev/null +++ b/template/.claude/commands/tdd-review.md @@ -0,0 +1,110 @@ +--- +description: Review test suite quality against FIRST principles and TDD anti-patterns +argument-hint: [optional test file or directory path] +--- + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +(If there was no info above, fallback to: + +1. Context of the conversation, if there's an immediate thing +2. `bd ready` to see what to work on next and start from there) + +# Test Quality Review + +Analyze test files against FIRST principles and TDD best practices. + +## Phase 1: Scope + +| Input | Action | +|-------|--------| +| No argument | Find all test files in project | +| File path | Analyze specific test file | +| Directory | Analyze tests in directory | + +Detect test files using common patterns: `*.test.*`, `*.spec.*`, `*.stories.*`, `__tests__/**` + +Also check for framework-specific patterns based on the project's languages and tools (e.g., `*_test.go`, `*_test.py`, `Test*.java`, `*.feature` for BDD). + +## Phase 2: Analysis + +For each test file, check against these criteria: + +### Quality Criteria + +#### FIRST Principles + +| Principle | What to Check | +|-----------|---------------| +| **Fast** | Tests complete quickly, no I/O, no network calls, no sleep()/setTimeout delays | +| **Independent** | No shared mutable state, no execution order dependencies between tests | +| **Repeatable** | No Date.now(), no Math.random() without seeding, no external service dependencies | +| **Self-validating** | Meaningful assertions that verify behavior, no manual verification needed | + +#### TDD Anti-patterns + +| Anti-pattern | Detection Signals | +|--------------|-------------------| +| **The Liar** | `expect(true).toBe(true)`, empty test bodies, tests with no assertions | +| **Excessive Setup** | >20 lines of arrange code, >5 mocks, deep nested object construction | +| **The One** | >5 assertions testing unrelated behaviors in a single test | +| **The Peeping Tom** | Testing private methods, asserting on internal state, tests that break on any refactor | +| **The Slow Poke** | Real database/network calls, file I/O, hard-coded timeouts | + +#### Test Structure (AAA Pattern) + +- **Arrange**: Clear setup with minimal fixtures +- **Act**: Single action being tested +- **Assert**: Specific, behavior-focused assertions + +## Phase 3: Report + +Output a structured report: + +``` +## Test Quality Report + +### Summary +- Files analyzed: N +- Tests found: N +- Issues found: N (X blockers, Y warnings) + +### By File + +#### path/to/file.test.ts + +| Line | Issue | Severity | Description | +|------|-------|----------|-------------| +| 15 | The Liar | blocker | Test has no assertions | +| 42 | Slow Poke | warning | Uses setTimeout(500) | + +### Recommendations +- [ ] Fix blockers before merge +- [ ] Consider refactoring tests with excessive setup +``` + +### Severity Levels + +- **blocker**: Must fix - test provides false confidence (The Liar, no assertions) +- **warning**: Should fix - test quality issue (Slow Poke, Excessive Setup) +- **info**: Consider - style or structure suggestion (AAA pattern) + +--- + +**User arguments:** + +TDD-review: $ARGUMENTS + +**End of user arguments** diff --git a/template/.claude/commands/tdd.md b/template/.claude/commands/tdd.md new file mode 100644 index 00000000..7cb2a554 --- /dev/null +++ b/template/.claude/commands/tdd.md @@ -0,0 +1,102 @@ +--- +description: Remind agent about TDD approach and continue conversation +argument-hint: [optional-response-to-last-message] +--- + +# TDD Reminder + +## General Guidelines + +### Output Style + +- **Never explicitly mention TDD** in code, comments, commits, PRs, or issues +- Write natural, descriptive code without meta-commentary about the development process +- The code should speak for itself - TDD is the process, not the product + +Beads is available for task tracking. Use `mcp__beads__*` tools to manage issues (the user interacts via `bd` commands). + +## Plan File Restriction + +**NEVER create, read, or update plan.md files.** Claude Code's internal planning files are disabled for this project. Use other methods to track implementation progress (e.g., comments, todo lists, or external tools). + +## TDD Fundamentals + +### The TDD Cycle + +The foundation of TDD is the Red-Green-Refactor cycle: + +1. **Red Phase**: Write ONE failing test that describes desired behavior + + - The test must fail for the RIGHT reason (not syntax/import errors) + - Only one test at a time - this is critical for TDD discipline + - Exception: For browser-level tests or expensive setup (e.g., Storybook `*.stories.tsx`), group multiple assertions within a single test block to avoid redundant setup - but only when adding assertions to an existing interaction flow. If new user interactions are required, still create a new test. Split files by category if they exceed ~1000 lines. + - **Adding a single test to a test file is ALWAYS allowed** - no prior test output needed + - Starting TDD for a new feature is always valid, even if test output shows unrelated work + - For DOM-based tests, use `data-testid` attributes to select elements rather than CSS classes, tag names, or text content + - Avoid hard-coded timeouts both in form of sleep() or timeout: 5000 etc; use proper async patterns (`waitFor`, `findBy*`, event-based sync) instead and rely on global test configs for timeout settings + +2. **Green Phase**: Write MINIMAL code to make the test pass + + - Implement only what's needed for the current failing test + - No anticipatory coding or extra features + - Address the specific failure message + +3. **Refactor Phase**: Improve code structure while keeping tests green + - Only allowed when relevant tests are passing + - Requires proof that tests have been run and are green + - Applies to BOTH implementation and test code + - No refactoring with failing tests - fix them first + +### Core Violations + +1. **Multiple Test Addition** + + - Adding more than one new test at once + - Exception: Initial test file setup or extracting shared test utilities + +2. **Over-Implementation** + + - Code that exceeds what's needed to pass the current failing test + - Adding untested features, methods, or error handling + - Implementing multiple methods when test only requires one + +3. **Premature Implementation** + - Adding implementation before a test exists and fails properly + - Adding implementation without running the test first + - Refactoring when tests haven't been run or are failing + +### Critical Principle: Incremental Development + +Each step in TDD should address ONE specific issue: + +- Test fails "not defined" → Create empty stub/class only +- Test fails "not a function" → Add method stub only +- Test fails with assertion → Implement minimal logic only + +### Optional Pre-Phase: Spike Phase + +In rare cases where the problem space, interface, or expected behavior is unclear, a **Spike Phase** may be used **before the Red Phase**. +This phase is **not part of the regular TDD workflow** and must only be applied under exceptional circumstances. + +- The goal of a Spike is **exploration and learning**, not implementation. +- The code written during a Spike is **disposable** and **must not** be merged or reused directly. +- Once sufficient understanding is achieved, all spike code is discarded, and normal TDD resumes starting from the **Red Phase**. +- A Spike is justified only when it is impossible to define a meaningful failing test due to technical uncertainty or unknown system behavior. + +### General Information + +- Sometimes the test output shows as no tests have been run when a new test is failing due to a missing import or constructor. In such cases, allow the agent to create simple stubs. Ask them if they forgot to create a stub if they are stuck. +- It is never allowed to introduce new logic without evidence of relevant failing tests. However, stubs and simple implementation to make imports and test infrastructure work is fine. +- In the refactor phase, it is perfectly fine to refactor both test and implementation code. That said, completely new functionality is not allowed. Types, clean up, abstractions, and helpers are allowed as long as they do not introduce new behavior. +- Adding types, interfaces, or a constant in order to replace magic values is perfectly fine during refactoring. +- Provide the agent with helpful directions so that they do not get stuck when blocking them. + +## Continue Conversation + +**User arguments:** + +TDD: $ARGUMENTS + +**End of user arguments** + +Please continue with the user input above, applying TDD approach. diff --git a/template/.claude/helpers/merge-claude-settings.sh b/template/.claude/helpers/merge-claude-settings.sh new file mode 100644 index 00000000..8343e15d --- /dev/null +++ b/template/.claude/helpers/merge-claude-settings.sh @@ -0,0 +1,98 @@ +#!/usr/bin/env bash +# +# Based on https://github.com/PaulRBerg/dot-claude/blob/main/helpers/merge_settings.sh +# +# Merges multiple JSONC settings files from the settings/ directory into a single +# settings.json file. This script handles: +# - Parsing JSONC files (JSON with comments) using json5 +# - Collecting and deduplicating permission arrays across all files +# - Merging non-permission top-level keys from all files +# +# Usage: merge_settings.sh + +set -euo pipefail + +# ---------------------------------------------------------------------------- # +# CONFIGURATION # +# ---------------------------------------------------------------------------- # + +# Navigate to the .claude directory relative to the folder this script is in +script_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +cd "$script_dir/../" + +# ---------------------------------------------------------------------------- # +# 1. DISCOVER FILES # +# ---------------------------------------------------------------------------- # + +# Find all .json and .jsonc files in settings/ directory (excluding settings.json) +# Files are sorted alphabetically to ensure consistent merge order +settings_files=$(find settings/ -type f \( -name '*.json' -o -name '*.jsonc' \) ! -name 'settings.json' | sort) + +if [ -z "$settings_files" ]; then + echo "No settings files found in settings/ directory" + exit 0 +fi + +# ---------------------------------------------------------------------------- # +# 2. PARSE JSONC FILES # +# ---------------------------------------------------------------------------- # + +# Parse all JSONC files to valid JSON using json5 in a single Node.js process +# The json5 tool allows comments and trailing commas in JSON files +# Using a single Node.js process is much faster than calling npx per file +# If a file fails to parse, fall back to empty object +parsed_json=$(npx -y -p json5 node -e " +const fs = require('fs'); +const JSON5 = require('json5'); +process.argv.slice(1).forEach(file => { + try { + console.log(JSON.stringify(JSON5.parse(fs.readFileSync(file, 'utf8')))); + } catch { + console.log('{}'); + } +}); +" $settings_files) + +# ---------------------------------------------------------------------------- # +# 3. MERGE WITH JQ # +# ---------------------------------------------------------------------------- # + +# Merge all parsed JSON files using jq +# The merge strategy is: +# 1. Collect all permission arrays from all files and deduplicate +# 2. Merge all other top-level keys (later values override earlier ones) +# 3. Exclude the $schema field from the final output +merged_json=$(echo "$parsed_json" | jq -s ' + # First, build the permissions object by collecting arrays from all files + { + permissions: { + # Collect additionalDirectories from all files, flatten, and deduplicate + additionalDirectories: ([.[].permissions.additionalDirectories // [] | .[] ] | unique), + + # Collect allow patterns from all files, flatten, and deduplicate + allow: ([.[].permissions.allow // [] | .[] ] | unique), + + # Collect ask patterns from all files, flatten, and deduplicate + ask: ([.[].permissions.ask // [] | .[] ] | unique), + + # Collect deny patterns from all files, flatten, and deduplicate + deny: ([.[].permissions.deny // [] | .[] ] | unique) + } + } * + # Then merge all non-permissions top-level keys from all files + # Later files override earlier files for conflicting keys + (reduce .[] as $item ({}; . * ($item | del(.permissions)))) + # Remove the $schema field from the final output + | del(."$schema") + # Sort all object keys alphabetically + | walk(if type == "object" then to_entries | sort_by(.key) | from_entries else . end) +') + +# ---------------------------------------------------------------------------- # +# 4. WRITE OUTPUT # +# ---------------------------------------------------------------------------- # + +# Write the merged JSON to settings.json +echo "$merged_json" > settings.json + +echo "✓ Merged settings.json from JSONC files" diff --git a/template/.claude/package-lock.json b/template/.claude/package-lock.json new file mode 100644 index 00000000..860fabfc --- /dev/null +++ b/template/.claude/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": ".claude", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "json5": "2.2.3" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/template/.claude/package.json b/template/.claude/package.json new file mode 100644 index 00000000..f79a49bc --- /dev/null +++ b/template/.claude/package.json @@ -0,0 +1,6 @@ +{ + "private": true, + "dependencies": { + "json5": "2.2.3" + } +} diff --git a/template/.claude/settings/basics.jsonc b/template/.claude/settings/basics.jsonc new file mode 100644 index 00000000..a7b12fae --- /dev/null +++ b/template/.claude/settings/basics.jsonc @@ -0,0 +1,5 @@ +{ + // Basic configuration for Claude Code + // General settings, environment variables, and UI customization + "$schema": "https://json.schemastore.org/claude-code-settings.json" +} diff --git a/template/.claude/settings/permissions/bash.jsonc b/template/.claude/settings/permissions/bash.jsonc new file mode 100644 index 00000000..92caa8a2 --- /dev/null +++ b/template/.claude/settings/permissions/bash.jsonc @@ -0,0 +1,120 @@ +{ + // Bash command permissions - allowed and denied shell operations + // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": [ + // AI interactions and tooling + "Bash(bd *)", + // Cloud Infrastructure + "Bash(aws sts get*)", + // Code Quality & Formatting + "Bash(pre-commit run *)", + // Core Shell Commands + "Bash(COMMAND *)", // used in slash commands + "Bash(kill *)", + "Bash(ps *)", + "Bash(pwd *)", + "Bash(time *)", + "Bash(timeout *)", + "Bash(which *)", + "Bash(xargs *)", + // Custom Scripts + "Bash(~/.agents/helpers/*)", + "Bash(~/.claude/helpers/*)", + "Bash(~/.claude/hooks/*)", + "Bash(~/.agents/skills/*)", + "Bash(~/.claude/skills/*)", + // File System + "Bash(bat *)", + "Bash(chmod *)", + "Bash(cp *)", + "Bash(eza *)", + "Bash(fd *)", + "Bash(find *)", + "Bash(fzf *)", + "Bash(ls *)", + "Bash(mkdir *)", + "Bash(stat *)", + "Bash(tar *)", + "Bash(touch *)", + "Bash(tree *)", + // Git & Version Control + "Bash(git diff *)", + "Bash(git status *)", + "Bash(git log *)", + "Bash(git rev-parse *)", + "Bash(git branch *)", + // Misc + "Bash(amp *)", + "Bash(atuin *)", + "Bash(bc *)", + "Bash(chezmoi *)", + "Bash(diff *)", + "Bash(jq *)", + "Bash(just *)", + "Bash(lsof *)", + "Bash(test *)", + "Bash(zk *)", + // Node.js + "Bash(pnpm test-unit *)", + "Bash(pnpm test-e2e *)", + // Python + "Bash(uv run pytest *)", + // Text Processing + "Bash(awk *)", + "Bash(cat *)", + "Bash(cut *)", + "Bash(echo *)", + "Bash(grep *)", + "Bash(head *)", + "Bash(printf *)", + "Bash(sed *)", + "Bash(sort *)", + "Bash(tail *)", + // Search + "Bash(rg *)", + ], + "ask": [ + "Bash(gh *)", // let's hold off before we let it use the github CLI in any free running allow mode...I don't want it somehow approving PRs with the user's credentials + "Bash(aws *)", // let's hold off before we let it use AWS CLI in any free running allow mode. We need to be very sure we don't have any access to staging or production credentials in our dev environment (...which we shouldn't...but we need to double check that or consider any other safeguards first) + "Bash(curl *)", + "Bash(ln *)", + "WebFetch", + ], + "deny": [ + // Exceptions to generally allowed AI tooling + "Bash(bd init*)", // we need to control the init process, don't let AI do that in the background + // Destructive File Operations + "Bash(chmod -R *)", + "Bash(chown -R *)", + "Bash(rm -rf / *)", + "Bash(rm -rf ~ *)", + // Dangerous Disk Operations + "Bash(> /dev/sda*)", + "Bash(> /etc/*)", + "Bash(dd *)", + "Bash(mkfs *)", + // Process Management + "Bash(kill -9 *)", + "Bash(killall *)", + // Git & Version Control + "Bash(git reset --hard *)", + "Bash(git push -f *)", + "Bash(git push --force*)", + // Node.js + "Bash(npm publish *)", + // System Administration + "Bash(doas *)", + "Bash(passwd *)", + "Bash(su *)", + "Bash(sudo *)", + "Bash(systemctl *)", + "Bash(userdel *)", + "Bash(usermod *)", + // System Control + "Bash(reboot *)", + "Bash(shutdown *)", + ], + }, +} diff --git a/template/.claude/settings/permissions/read.jsonc b/template/.claude/settings/permissions/read.jsonc new file mode 100644 index 00000000..7916e1e8 --- /dev/null +++ b/template/.claude/settings/permissions/read.jsonc @@ -0,0 +1,9 @@ +{ + // Read permissions for specific files + // NOTE: to add reading directories, use the additional-dirs.jsonc file + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": ["Read(~/.zshrc)"], + "deny": ["Read(~/Drive/Secrets/**)", "Read(~/.ssh/**)", "Read(**/.env*)", "Read(~/.aws/**)"], + }, +} diff --git a/template/.claude/settings/permissions/write.jsonc b/template/.claude/settings/permissions/write.jsonc new file mode 100644 index 00000000..f7a11443 --- /dev/null +++ b/template/.claude/settings/permissions/write.jsonc @@ -0,0 +1,8 @@ +{ + // Write permissions for specific files + // This should only ever be running in a devcontainer, so pretty lenient permissions are allowed + "$schema": "https://json.schemastore.org/claude-code-settings.json", + "permissions": { + "allow": ["Write(/tmp/**)"] + } +} diff --git a/template/.coderabbit.yaml b/template/.coderabbit.yaml index 9904260d..1d2e81ac 100644 --- a/template/.coderabbit.yaml +++ b/template/.coderabbit.yaml @@ -6,7 +6,7 @@ reviews: - path: "**/vendor_files/**" instructions: "These files came from a vendor and we're not allowed to change them. Refer to it if you need to understand how the main code interacts with it, but do not make comments about it." - path: "**/*.py" - instructions: "Do not express concerns about assert statements being removed by using the -O python flag; we never use that flag. Do not express concerns about ruff rules; a pre-commit hook already runs a ruff check. Do not warn about unnecessary super().init() calls; pyright prefers those to be present." + instructions: "Check the `ruff.toml` and `ruff-test.toml` for linting rules we've explicitly disabled and don't suggest changes to please conventions we've disabled. Do not express concerns about ruff rules; a pre-commit hook already runs a ruff check. Do not warn about unnecessary super().__init__() calls; pyright prefers those to be present. Do not warn about missing type hints; a pre-commit hook already checks for that." tools: eslint: # when the code contains typescript, eslint will be run by pre-commit, and coderabbit often generates false positives enabled: false diff --git a/template/.devcontainer/devcontainer.json.jinja b/template/.devcontainer/devcontainer.json.jinja index 042810a6..d044844e 100644 --- a/template/.devcontainer/devcontainer.json.jinja +++ b/template/.devcontainer/devcontainer.json.jinja @@ -12,7 +12,7 @@ "ghcr.io/devcontainers/features/aws-cli:1.1.2": { // https://github.com/devcontainers/features/blob/main/src/aws-cli/devcontainer-feature.json // view latest version https://raw.githubusercontent.com/aws/aws-cli/v2/CHANGELOG.rst - "version": "2.32.6", + "version": "2.32.6" },{% endraw %}{% endif %}{% raw %} "ghcr.io/devcontainers/features/python:1.8.0": { // https://github.com/devcontainers/features/blob/main/src/python/devcontainer-feature.json @@ -36,12 +36,12 @@ "-AmazonWebServices.aws-toolkit-vscode", // the AWS CLI feature installs this automatically, but it's causing problems in VS Code{% endraw %}{% endif %}{% raw %} // basic tooling // "eamodio.gitlens@15.5.1", - "coderabbit.coderabbit-vscode@0.17.0", + "coderabbit.coderabbit-vscode@0.18.3", "ms-vscode.live-server@0.5.2025051301", "MS-vsliveshare.vsliveshare@1.0.5905", "github.copilot@1.388.0", - "github.copilot-chat@0.38.2026022001",{% endraw %}{% if install_claude_cli %}{% raw %} - "anthropic.claude-code@2.1.49",{% endraw %}{% endif %}{% raw %} + "github.copilot-chat@0.38.2026022704",{% endraw %}{% if install_claude_cli %}{% raw %} + "anthropic.claude-code@2.1.74",{% endraw %}{% endif %}{% raw %} // Python "ms-python.python@2026.2.2026021801", @@ -50,7 +50,7 @@ "charliermarsh.ruff@2026.36.0", {% endraw %}{% if is_child_of_copier_base_template is not defined and template_uses_vuejs is defined and template_uses_vuejs is sameas(true) %}{% raw %} // VueJS - "vue.volar@3.2.1", + "vue.volar@3.2.5", "vitest.explorer@1.36.0", {% endraw %}{% endif %}{% raw %}{% endraw %}{% if is_child_of_copier_base_template is not defined and template_uses_javascript is defined and template_uses_javascript is sameas(true) %}{% raw %} // All javascript diff --git a/template/.devcontainer/docker-compose.yml.jinja b/template/.devcontainer/docker-compose.yml.jinja index 17d94259..449cfd5b 100644 --- a/template/.devcontainer/docker-compose.yml.jinja +++ b/template/.devcontainer/docker-compose.yml.jinja @@ -17,8 +17,24 @@ environment: - AWS_PROFILE=localstack - AWS_DEFAULT_REGION={% endraw %}{{ aws_region_for_stack if (aws_region_for_stack is defined and aws_region_for_stack != "") else "us-east-1" }}{% raw %}{% endraw %}{% if install_claude_cli %}{% raw %} - - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}{% endraw %}{% endif %}{% raw %} + - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY} + - BEADS_DIR=/workspaces/{% endraw %}{{ repo_name }}{% raw %}/.claude/.beads + - BEADS_DOLT_SERVER_HOST=beads-dolt # this is on the docker compose network. We don't want to publish the server port because then the local machine would have a bunch of conflicts with multiple devcontainers running + - BEADS_DOLT_SERVER_PORT=3306 + - BEADS_DOLT_SERVER_DATABASE=beads_work + - BEADS_DOLT_SERVER=beads-dolt # for some weird reason, unless we specify both of these envvars it doesn't seem to reliably work + - BEADS_DOLT_PORT=3306 -volumes: + beads-dolt: + image: dolthub/dolt-sql-server:1.83.0 # no explicit reason for this version, just pinning for best practice + volumes: + - beads_dolt_data:/var/lib/dolt + environment: + - DOLT_ROOT_HOST=% + - DOLT_DATABASE=beads_work{% endraw %}{% endif %}{% raw %} + + +volumes:{% endraw %}{% if install_claude_cli %}{% raw %} + beads_dolt_data: {}{% endraw %}{% endif %}{% raw %} python_venv: {}{% endraw %} diff --git a/template/.devcontainer/on-create-command.sh.jinja b/template/.devcontainer/on-create-command.sh.jinja index 77cdae5b..19933278 100644 --- a/template/.devcontainer/on-create-command.sh.jinja +++ b/template/.devcontainer/on-create-command.sh.jinja @@ -5,7 +5,17 @@ set -ex # it doesn't have access to the workspace directory. This can normally be done in post-start-command git config --global --add safe.directory /workspaces/{% endraw %}{{ repo_name }}{% raw %} -sh .devcontainer/on-create-command-boilerplate.sh +sh .devcontainer/on-create-command-boilerplate.sh{% endraw %}{% if install_claude_cli %}{% raw %} +# install json5 for merging claude settings. TODO: consider if we can install json5 globally...or somehow eliminate this dependency +script_dir="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)" +repo_root="$(CDPATH= cd -- "$script_dir/.." && pwd)" +mkdir -p "$repo_root/.claude" +chmod -R ug+rwX "$repo_root/.claude" +chgrp -R 0 "$repo_root/.claude" || true +npm --prefix "$repo_root/.claude" ci + +# Install beads for use in Claude planning +npm install -g @beads/bd@0.57.0 # no specific reason for this version, just pinning for best practice{% endraw %}{% endif %}{% raw %} pre-commit install --install-hooks{% endraw %}{% if python_package_registry is not defined or python_package_registry == "PyPI" %} diff --git a/template/.devcontainer/post-start-command.sh.jinja b/template/.devcontainer/post-start-command.sh.jinja index a8ded17e..1329f4f6 100644 --- a/template/.devcontainer/post-start-command.sh.jinja +++ b/template/.devcontainer/post-start-command.sh.jinja @@ -3,5 +3,13 @@ set -ex # For some reason the directory is not setup correctly and causes build of devcontainer to fail since # it doesn't have access to the workspace directory. This can normally be done in post-start-command -git config --global --add safe.directory /workspaces/{% endraw %}{{ repo_name }}{% if python_package_registry is defined and python_package_registry != "PyPI" %}{% raw %} +git config --global --add safe.directory /workspaces/{% endraw %}{{ repo_name }}{% if install_claude_cli %}{% raw %} +pre-commit run merge-claude-settings -a +if ! bd ready; then + echo "It's likely the Dolt server has not yet been initialized to support beads, running that now" # TODO: figure out a better way to match this specific scenario than just a non-zero exit code...but beads still seems like in high flux right now so not sure what to tie it to + # the 'stealth' flag is just the only way I could figure out how to stop it from modifying AGENTS.md...if there's another way to avoid that, then fine. Even without the stealth flag though, files inside the .claude/beads directory get modified, so restoring them at the end to what was set in git...these shouldn't really need to change regularly + # trying to set 'prefix' to nothing doesn't seem to work (it just acts like the prefix flag wasn't there), so just setting to 'work' as an arbitrary name + # for some reason, the envvar for the server host isn't being picked up normally, so just passing it explicitly here + rm -rf .claude/.beads && bd init --server-host="$BEADS_DOLT_SERVER_HOST" --database="$BEADS_DOLT_SERVER_DATABASE" --skip-hooks --stealth --prefix=work </dev/null && git -c core.hooksPath=/dev/null restore --source=HEAD --staged --worktree .claude/.beads +fi{% endraw %}{% endif %}{% if python_package_registry is defined and python_package_registry != "PyPI" %}{% raw %} echo "!!! In order to install dependencies, you must authenticate into the private registry, so run this script to complete the process: source .devcontainer/manual-setup-deps.sh"{% endraw %}{% endif %} diff --git a/template/.github/actions/ecr-auth/action.yml.jinja b/template/.github/actions/ecr-auth/action.yml.jinja new file mode 100644 index 00000000..82faf894 --- /dev/null +++ b/template/.github/actions/ecr-auth/action.yml.jinja @@ -0,0 +1,23 @@ +{% raw %}name: AWS ECR Auth +description: 'Use OIDC to Authenticate and login to an AWS ECR.' + +inputs: + aws-region: + description: AWS region to use for ECR authentication + required: false + default: '{% endraw %}{{ aws_org_home_region }}{% raw %}' + role-arn: + description: AWS IAM Role ARN to assume for ECR authentication + required: false + default: 'arn:aws:iam::{% endraw %}{{ aws_central_infrastructure_account_id }}{% raw %}:role/{% endraw %}{{ core_infra_base_access_profile_name }}{% raw %}' + +runs: + using: composite + steps: + - name: OIDC Auth for ECR + uses: aws-actions/configure-aws-credentials@{% endraw %}{{ gha_configure_aws_credentials }}{% raw %} + with: + role-to-assume: ${{ inputs.role-arn }} + aws-region: ${{ inputs.aws-region }} + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@{% endraw %}{{ gha_amazon_ecr_login }} diff --git a/template/.github/actions/install_deps/action.yml b/template/.github/actions/install_deps/action.yml index b212d095..0cedd63a 100644 --- a/template/.github/actions/install_deps/action.yml +++ b/template/.github/actions/install_deps/action.yml @@ -64,7 +64,7 @@ runs: - name: Setup node if: ${{ inputs.node-version != 'notUsing' }} - uses: actions/setup-node@v6.2.0 + uses: actions/setup-node@v6.3.0 with: node-version: ${{ inputs.node-version }} @@ -75,7 +75,7 @@ runs: - name: OIDC Auth for CodeArtifact if: ${{ inputs.code-artifact-auth-role-name != 'no-code-artifact' }} - uses: aws-actions/configure-aws-credentials@v5.1.1 + uses: aws-actions/configure-aws-credentials@v6.0.0 with: role-to-assume: arn:aws:iam::${{ inputs.code-artifact-auth-role-account-id }}:role/${{ inputs.code-artifact-auth-role-name }} aws-region: ${{ inputs.code-artifact-auth-region }} diff --git a/template/.github/workflows/pre-commit.yaml b/template/.github/workflows/pre-commit.yaml index 90f6ceae..ffeb9485 100644 --- a/template/.github/workflows/pre-commit.yaml +++ b/template/.github/workflows/pre-commit.yaml @@ -59,7 +59,7 @@ jobs: timeout-minutes: 8 # this is the amount of time this action will wait to attempt to acquire the mutex lock before failing, e.g. if other jobs are queued up in front of it - name: Cache Pre-commit hooks - uses: actions/cache@v5.0.2 + uses: actions/cache@v5.0.3 env: cache-name: cache-pre-commit-hooks with: diff --git a/template/.gitignore b/template/.gitignore index efe2fb8c..cab6c195 100644 --- a/template/.gitignore +++ b/template/.gitignore @@ -14,6 +14,16 @@ .ruff_cache/ .pipx_cache/ + +# Claude +.claude/settings.local.json +## we manage the settings JSON by merging the JSONC files in the settings/ directory +.claude/settings.json +# Dolt database files (used by beads for Claude) +.dolt/ +*.db + + # Vendor build/make dkms.conf diff --git a/template/.pre-commit-config.yaml b/template/.pre-commit-config.yaml index 5b009afa..6ea6a13e 100644 --- a/template/.pre-commit-config.yaml +++ b/template/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -minimum_pre_commit_version: 4.2.0 +minimum_pre_commit_version: 4.3.0 # run `pre-commit autoupdate --freeze` to update all hooks default_install_hook_types: [pre-commit, post-checkout] repos: @@ -314,10 +314,19 @@ repos: # print the number of files as a sanity-check verbose: true - # Devcontainer context --- this makes Github's "prebuild codespaces" feature work more intelligently for the "Configuration Change" trigger + # Updating repo config/tooling files - repo: local hooks: + - id: merge-claude-settings + # Keep Claude's settings.json synced with the JSON5 files. It's only for local development, so don't run it in CI + name: merge Claude settings + entry: bash -c '[[ "${GITHUB_ACTIONS:-}" == "true" || "${CI:-}" == "true" ]] && exit 0; bash .claude/helpers/merge-claude-settings.sh' + files: ^\.claude/settings/.*\.(json|jsonc)$ + pass_filenames: false + language: system + - id: compute-devcontainer-context-hash + # Devcontainer context --- this makes Github's "prebuild codespaces" feature work more intelligently for the "Configuration Change" trigger name: compute devcontainer context hash entry: bash -c "python3 .github/workflows/hash_git_files.py . --for-devcontainer-config-update" files: (.*.lock)|(.*pnpm-lock.yaml)|(.*hash_git_files.py)|(.devcontainer/.*)|(\.pre-commit-config.yaml) diff --git a/template/AGENTS.md b/template/AGENTS.md new file mode 100644 index 00000000..2f443460 --- /dev/null +++ b/template/AGENTS.md @@ -0,0 +1,121 @@ +## Code Style +- Comments should be used very rarely. Code should generally express its intent. +- Never write a one-line docstring — either the name is sufficient or the behavior warrants a full explanation. +- Don't sort or remove imports manually — pre-commit handles it. +- Always include type hints for pyright in Python +- Respect the pyright rule reportUnusedCallResult; assign unneeded return values to `_` +- Prefer keyword-only parameters (unless a very clear single-argument function): use `*` in Python signatures and destructured options objects in TypeScript. +- When disabling a linting rule with an inline directive, provide a comment at the end of the line (or on the line above for tools that don't allow extra text after an inline directive) describing the reasoning for disabling the rule. + +## Testing +- Always run tests with an explicit path (e.g. uv run pytest tests/unit) — test runners discover all types by default. +- Test coverage requirements are usually at 100%, so when running a subset of tests, always disable test coverage to avoid the test run failing for insufficient coverage. +- Avoid magic values in comparisons in tests in all languages (like ruff rule PLR2004 specifies) +- Prefer using random values in tests rather than arbitrary ones (e.g. the faker library, uuids, random.randint) when possible. For enums, pick randomly rather than hardcoding one value. +- Avoid loops in tests — assert each item explicitly so failures pinpoint the exact element. When verifying a condition across all items in a collection, collect the violations into a list and assert it's empty (e.g., assert [x for x in items if bad_condition(x)] == []). +- Key `data-testid` selectors off unique IDs (e.g. UUIDs), not human-readable names which may collide or change. + +### Python Testing +- When using `mocker.spy` on a class-level method (including inherited ones), the spy records the unbound call, so assertions need `ANY` as the first argument to match self: `spy.assert_called_once_with(ANY, expected_arg)` +- Before writing new mock/spy helpers, check the `tests/unit/` folder for pre-built helpers in files like `fixtures.py` or `*mocks.py` +- When a test needs a fixture only for its side effects (not its return value), use `@pytest.mark.usefixtures(fixture_name.__name__)` instead of adding an unused parameter with a noqa comment +- Use `__name__` instead of string literals when referencing functions/methods (e.g., `mocker.patch.object(MyClass, MyClass.method.__name__)`, `pytest.mark.usefixtures(my_fixture.__name__)`). This enables IDE refactoring tools to catch renames. +- **Never hand-write VCR cassette YAML files.** Cassettes must be recorded from real HTTP interactions by running the test once with `--record-mode=once` against a live external service: `uv run pytest --record-mode=once <test path> --no-cov`. The default mode is `none` — a missing cassette will cause an error, which is expected until recorded. +- **Never hand-edit syrupy snapshot files.** Snapshots are auto-generated — to create or update them, run `uv run pytest --snapshot-update <test path> --no-cov`. A missing snapshot causes the test to fail, which is expected until you run with `--snapshot-update`. When a snapshot mismatch occurs, fix the code if the change was unintentional; run `--snapshot-update` if it was intentional. +- **Never hand-write or hand-edit pytest-reserial `.jsonl` recording files.** Recordings must be captured from real serial port traffic by running the test with `--record` while the device is connected: `uv run pytest --record <test path> --no-cov`. The default mode replays recordings — a missing recording causes an error, which is expected until recorded against a live device. + + +## Tooling +- Always use `uv run python` instead of `python3` or `python` when running Python commands. +- Prefer dedicated shell tools over `python3`/`python` for simple one-off tasks: use `jq` for JSON parsing, standard shell builtins for string manipulation, etc. Only reach for `python3` when no simpler tool covers the need. +- Check .devcontainer/devcontainer.json for tooling versions (Python, Node, etc.) when reasoning about version-specific stdlib or tooling behavior. +- For frontend work, run commands via `pnpm` scripts from `frontend/package.json` — never invoke tools directly (not pnpm exec <tool>, npx <tool>, etc.). ✅ pnpm test-unit ❌ pnpm vitest ... or npx vitest ... +- When running terminal commands, execute exactly one command per tool call. Do not chain commands with &&, ||, ;, or & — this prohibition has no exceptions, even for `cd && ...` patterns. Use absolute paths instead of `cd` to avoid needing to chain. Pipes (|) are allowed for output transformation (e.g., head, tail, grep). If two sequential commands are needed, run them in separate tool calls. Chained commands break the permission allow-list matcher and cause unnecessary permission prompts +- Never use backslash line continuations in shell commands — always write the full command on a single line. Backslashes break the permission allow-list matcher. + +<!-- BEGIN BEADS INTEGRATION --> +## Issue Tracking with bd (beads) + +**IMPORTANT**: This project uses **bd (beads)** for ALL issue tracking. Do NOT use markdown TODOs, task lists, or other tracking methods. + +### Why bd? + +- Dependency-aware: Track blockers and relationships between issues +- Git-friendly: Auto-syncs to JSONL for version control +- Agent-optimized: JSON output, ready work detection, discovered-from links +- Prevents duplicate tracking systems and confusion + +### Quick Start + +**Check for ready work:** + +```bash +bd ready --json +``` + +**Create new issues:** + +```bash +bd create "Issue title" --description="Detailed context" -t bug|feature|task -p 0-4 --json +bd create "Issue title" --description="What this issue is about" -p 1 --deps discovered-from:bd-123 --json +``` + +**Claim and update:** + +```bash +bd update bd-42 --status in_progress --json +bd update bd-42 --priority 1 --json +``` + +**Complete work:** + +```bash +bd close bd-42 --reason "Completed" --json +``` + +**Creating human readable file:** +After every CRUD command on an issue, export it: + +```bash +bd export -o .claude/.beads/issues-dump.jsonl +``` + +### Issue Types + +- `bug` - Something broken +- `feature` - New functionality +- `task` - Work item (tests, docs, refactoring) +- `epic` - Large feature with subtasks +- `chore` - Maintenance (dependencies, tooling) + +### Priorities + +- `0` - Critical (security, data loss, broken builds) +- `1` - High (major features, important bugs) +- `2` - Medium (default, nice-to-have) +- `3` - Low (polish, optimization) +- `4` - Backlog (future ideas) + +### Workflow for AI Agents + +1. **Check ready work**: `bd ready` shows unblocked issues +2. **Claim your task**: `bd update <id> --status in_progress` +3. **Work on it**: Implement, test, document +4. **Discover new work?** Create linked issue: + - `bd create "Found bug" --description="Details about what was found" -p 1 --deps discovered-from:<parent-id>` +5. **Complete**: `bd close <id> --reason "Done"` + + +### Important Rules + +- ✅ Use bd for ALL task tracking +- ✅ Always use `--json` flag for programmatic use +- ✅ Link discovered work with `discovered-from` dependencies +- ✅ Check `bd ready` before asking "what should I work on?" +- ❌ Do NOT create markdown TODO lists +- ❌ Do NOT use external issue trackers +- ❌ Do NOT duplicate tracking systems + +For more details, see README.md and docs/QUICKSTART.md. + +<!-- END BEADS INTEGRATION --> diff --git a/template/CLAUDE.md b/template/CLAUDE.md new file mode 100644 index 00000000..43c994c2 --- /dev/null +++ b/template/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/tests/unit/copier_tasks/__init__.py b/tests/unit/copier_tasks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/unit/copier_tasks/test_remove_precommit_hooks.py b/tests/unit/copier_tasks/test_remove_precommit_hooks.py new file mode 100644 index 00000000..9292b39a --- /dev/null +++ b/tests/unit/copier_tasks/test_remove_precommit_hooks.py @@ -0,0 +1,90 @@ +import re +import shutil +import subprocess +import sys +from pathlib import Path + +_EXIT_CODE_INVALID_REGEX = 2 +_PROJECT_ROOT = Path(__file__).resolve().parents[3] +_SCRIPT_PATH = _PROJECT_ROOT / "src" / "copier_tasks" / "remove_precommit_hooks.py" + + +class TestRemovePrecommitHooksViaSubprocess: + def _run_script(self, *, hook_id_regex: str, target_file: Path) -> subprocess.CompletedProcess[str]: + return subprocess.run( # noqa: S603 # this is our own script + [ + sys.executable, + str(_SCRIPT_PATH), + "--hook-id-regex", + hook_id_regex, + "--target-file", + str(target_file), + ], + check=False, + capture_output=True, + text=True, + ) + + def test_When_run_with_matching_hook__Then_hook_removed(self, tmp_path: Path) -> None: + source_config = _PROJECT_ROOT / ".pre-commit-config.yaml" + config_path = tmp_path / ".pre-commit-config.yaml" + _ = shutil.copyfile(source_config, config_path) + original = config_path.read_text(encoding="utf-8") + assert "id: check-json5" in original + assert "id: trailing-whitespace" in original + + result = self._run_script(hook_id_regex=r"^\s*-\s+id:\s+check-json5\s*$", target_file=config_path) + + assert result.returncode == 0 + assert "Removed 1 matching hook" in result.stdout + + updated = config_path.read_text(encoding="utf-8") + assert "id: check-json5" not in updated + assert "id: trailing-whitespace" in updated + + def test_When_run_with_no_matching_hook__Then_file_unchanged(self, tmp_path: Path) -> None: + source_config = _PROJECT_ROOT / ".pre-commit-config.yaml" + config_path = tmp_path / ".pre-commit-config.yaml" + _ = shutil.copyfile(source_config, config_path) + original = config_path.read_text(encoding="utf-8") + + result = self._run_script(hook_id_regex=r"^\s*-\s+id:\s+nonexistent-hook-xyz\s*$", target_file=config_path) + + assert result.returncode == 0 + assert "No matching hooks found" in result.stdout + assert config_path.read_text(encoding="utf-8") == original + + def test_When_target_file_does_not_exist__Then_exits_with_error(self, tmp_path: Path) -> None: + nonexistent_path = tmp_path / "does-not-exist.yaml" + + result = self._run_script(hook_id_regex=r"^\s*-\s+id:\s+some-hook\s*$", target_file=nonexistent_path) + + assert result.returncode != 0 + + def test_When_invalid_regex_provided__Then_exits_with_code_2(self, tmp_path: Path) -> None: + config_path = tmp_path / ".pre-commit-config.yaml" + _ = config_path.write_text("repos: []\n", encoding="utf-8") + + result = self._run_script(hook_id_regex="[invalid-regex", target_file=config_path) + + assert result.returncode == _EXIT_CODE_INVALID_REGEX + assert "Invalid regex pattern" in result.stdout + + def test_When_multiple_hooks_match__Then_all_removed_and_count_reported(self, tmp_path: Path) -> None: + source_config = _PROJECT_ROOT / ".pre-commit-config.yaml" + config_path = tmp_path / ".pre-commit-config.yaml" + _ = shutil.copyfile(source_config, config_path) + + hook_id_regex = r"^\s*-\s+id:\s+check-" + original = config_path.read_text(encoding="utf-8") + expected_removed = sum(1 for line in original.splitlines() if re.match(hook_id_regex, line)) + assert expected_removed > 1 + + result = self._run_script(hook_id_regex=hook_id_regex, target_file=config_path) + + assert result.returncode == 0 + assert f"Removed {expected_removed} matching hook(s)" in result.stdout + + updated = config_path.read_text(encoding="utf-8") + assert "id: check-" not in updated + assert "id: trailing-whitespace" in updated diff --git a/uv.lock b/uv.lock index e5f5c7dc..10ee99e8 100644 --- a/uv.lock +++ b/uv.lock @@ -22,7 +22,7 @@ wheels = [ [[package]] name = "copier" -version = "9.11.3" +version = "9.14.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "colorama" }, @@ -39,9 +39,9 @@ dependencies = [ { name = "pyyaml" }, { name = "questionary" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/be/35bb44c0c7c278bd9144f5934aa10a2d532cedea4e16494c6552aa7132e1/copier-9.11.3.tar.gz", hash = "sha256:f4da98c7f3dd2243480433541b3b4d9daa788bce13b7b6d43c0c6d84bd50e889", size = 610458, upload-time = "2026-01-23T17:19:11.561Z" } +sdist = { url = "https://files.pythonhosted.org/packages/cb/35/42b9e1c2b4adab0ebb788eae1f1800fa5f481ff5552a6e58c3d953dd11c0/copier-9.14.0.tar.gz", hash = "sha256:4d1b6a19538a5d170f913afb7682fe745c74b35c84085890809cb8d8d4d8fe7a", size = 618593, upload-time = "2026-03-13T15:55:30.796Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/01/74922a1c552137c05a41fee0c61153753dddc9117d19c7c5902c146c25ab/copier-9.11.3-py3-none-any.whl", hash = "sha256:ab4bc7e2944edc030b4c14ec84fffd6bf810b9b8fd56938e8ccbab1b169ea6ca", size = 56905, upload-time = "2026-01-23T17:19:09.999Z" }, + { url = "https://files.pythonhosted.org/packages/df/91/4a151c94320458895049a3e23b7b2cfc08953c60b14892de837e8eb51d0a/copier-9.14.0-py3-none-any.whl", hash = "sha256:e12a18cfef22e67254e5229f0b4bdab85e1e3e82926e448226be0b70d0f4de53", size = 59425, upload-time = "2026-03-13T15:55:29.273Z" }, ] [[package]] @@ -60,13 +60,13 @@ dependencies = [ [package.metadata] requires-dist = [ - { name = "copier", specifier = "==9.11.3" }, + { name = "copier", specifier = "==9.14.0" }, { name = "copier-template-extensions", specifier = "==0.3.3" }, { name = "pyright", extras = ["nodejs"], specifier = ">=1.1.408" }, { name = "pytest", specifier = ">=9.0.2" }, { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "pytest-randomly", specifier = ">=4.0.1" }, - { name = "ty", specifier = ">=0.0.17" }, + { name = "ty", specifier = ">=0.0.23" }, ] [[package]] @@ -497,26 +497,26 @@ wheels = [ [[package]] name = "ty" -version = "0.0.18" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/74/15/9682700d8d60fdca7afa4febc83a2354b29cdcd56e66e19c92b521db3b39/ty-0.0.18.tar.gz", hash = "sha256:04ab7c3db5dcbcdac6ce62e48940d3a0124f377c05499d3f3e004e264ae94b83", size = 5214774, upload-time = "2026-02-20T21:51:31.173Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/ae/d8/920460d4c22ea68fcdeb0b2fb53ea2aeb9c6d7875bde9278d84f2ac767b6/ty-0.0.18-py3-none-linux_armv6l.whl", hash = "sha256:4e5e91b0a79857316ef893c5068afc4b9872f9d257627d9bc8ac4d2715750d88", size = 10280825, upload-time = "2026-02-20T21:51:25.03Z" }, - { url = "https://files.pythonhosted.org/packages/83/56/62587de582d3d20d78fcdddd0594a73822ac5a399a12ef512085eb7a4de6/ty-0.0.18-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:ee0e578b3f8416e2d5416da9553b78fd33857868aa1384cb7fefeceee5ff102d", size = 10118324, upload-time = "2026-02-20T21:51:22.27Z" }, - { url = "https://files.pythonhosted.org/packages/2f/2d/dbdace8d432a0755a7417f659bfd5b8a4261938ecbdfd7b42f4c454f5aa9/ty-0.0.18-py3-none-macosx_11_0_arm64.whl", hash = "sha256:3f7a0487d36b939546a91d141f7fc3dbea32fab4982f618d5b04dc9d5b6da21e", size = 9605861, upload-time = "2026-02-20T21:51:16.066Z" }, - { url = "https://files.pythonhosted.org/packages/6b/d9/de11c0280f778d5fc571393aada7fe9b8bc1dd6a738f2e2c45702b8b3150/ty-0.0.18-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d5e2fa8d45f57ca487a470e4bf66319c09b561150e98ae2a6b1a97ef04c1a4eb", size = 10092701, upload-time = "2026-02-20T21:51:26.862Z" }, - { url = "https://files.pythonhosted.org/packages/0f/94/068d4d591d791041732171e7b63c37a54494b2e7d28e88d2167eaa9ad875/ty-0.0.18-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d75652e9e937f7044b1aca16091193e7ef11dac1c7ec952b7fb8292b7ba1f5f2", size = 10109203, upload-time = "2026-02-20T21:51:11.59Z" }, - { url = "https://files.pythonhosted.org/packages/34/e4/526a4aa56dc0ca2569aaa16880a1ab105c3b416dd70e87e25a05688999f3/ty-0.0.18-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:563c868edceb8f6ddd5e91113c17d3676b028f0ed380bdb3829b06d9beb90e58", size = 10614200, upload-time = "2026-02-20T21:51:20.298Z" }, - { url = "https://files.pythonhosted.org/packages/fd/3d/b68ab20a34122a395880922587fbfc3adf090d22e0fb546d4d20fe8c2621/ty-0.0.18-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:502e2a1f948bec563a0454fc25b074bf5cf041744adba8794d024277e151d3b0", size = 11153232, upload-time = "2026-02-20T21:51:14.121Z" }, - { url = "https://files.pythonhosted.org/packages/68/ea/678243c042343fcda7e6af36036c18676c355878dcdcd517639586d2cf9e/ty-0.0.18-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cc881dea97021a3aa29134a476937fd8054775c4177d01b94db27fcfb7aab65b", size = 10832934, upload-time = "2026-02-20T21:51:32.92Z" }, - { url = "https://files.pythonhosted.org/packages/d8/bd/7f8d647cef8b7b346c0163230a37e903c7461c7248574840b977045c77df/ty-0.0.18-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:421fcc3bc64cab56f48edb863c7c1c43649ec4d78ff71a1acb5366ad723b6021", size = 10700888, upload-time = "2026-02-20T21:51:09.673Z" }, - { url = "https://files.pythonhosted.org/packages/6e/06/cb3620dc48c5d335ba7876edfef636b2f4498eff4a262ff90033b9e88408/ty-0.0.18-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:0fe5038a7136a0e638a2fb1ad06e3d3c4045314c6ba165c9c303b9aeb4623d6c", size = 10078965, upload-time = "2026-02-20T21:51:07.678Z" }, - { url = "https://files.pythonhosted.org/packages/60/27/c77a5a84533fa3b685d592de7b4b108eb1f38851c40fac4e79cc56ec7350/ty-0.0.18-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d123600a52372677613a719bbb780adeb9b68f47fb5f25acb09171de390e0035", size = 10134659, upload-time = "2026-02-20T21:51:18.311Z" }, - { url = "https://files.pythonhosted.org/packages/43/6e/60af6b88c73469e628ba5253a296da6984e0aa746206f3034c31f1a04ed1/ty-0.0.18-py3-none-musllinux_1_2_i686.whl", hash = "sha256:bb4bc11d32a1bf96a829bf6b9696545a30a196ac77bbc07cc8d3dfee35e03723", size = 10297494, upload-time = "2026-02-20T21:51:39.631Z" }, - { url = "https://files.pythonhosted.org/packages/33/90/612dc0b68224c723faed6adac2bd3f930a750685db76dfe17e6b9e534a83/ty-0.0.18-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:dda2efbf374ba4cd704053d04e32f2f784e85c2ddc2400006b0f96f5f7e4b667", size = 10791944, upload-time = "2026-02-20T21:51:37.13Z" }, - { url = "https://files.pythonhosted.org/packages/0d/da/f4ada0fd08a9e4138fe3fd2bcd3797753593f423f19b1634a814b9b2a401/ty-0.0.18-py3-none-win32.whl", hash = "sha256:c5768607c94977dacddc2f459ace6a11a408a0f57888dd59abb62d28d4fee4f7", size = 9677964, upload-time = "2026-02-20T21:51:42.039Z" }, - { url = "https://files.pythonhosted.org/packages/5e/fa/090ed9746e5c59fc26d8f5f96dc8441825171f1f47752f1778dad690b08b/ty-0.0.18-py3-none-win_amd64.whl", hash = "sha256:b78d0fa1103d36fc2fce92f2092adace52a74654ab7884d54cdaec8eb5016a4d", size = 10636576, upload-time = "2026-02-20T21:51:29.159Z" }, - { url = "https://files.pythonhosted.org/packages/92/4f/5dd60904c8105cda4d0be34d3a446c180933c76b84ae0742e58f02133713/ty-0.0.18-py3-none-win_arm64.whl", hash = "sha256:01770c3c82137c6b216aa3251478f0b197e181054ee92243772de553d3586398", size = 10095449, upload-time = "2026-02-20T21:51:34.914Z" }, +version = "0.0.23" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/75/ba/d3c998ff4cf6b5d75b39356db55fe1b7caceecc522b9586174e6a5dee6f7/ty-0.0.23.tar.gz", hash = "sha256:5fb05db58f202af366f80ef70f806e48f5237807fe424ec787c9f289e3f3a4ef", size = 5341461, upload-time = "2026-03-13T12:34:23.125Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f4/21/aab32603dfdfacd4819e52fa8c6074e7bd578218a5142729452fc6a62db6/ty-0.0.23-py3-none-linux_armv6l.whl", hash = "sha256:e810eef1a5f1cfc0731a58af8d2f334906a96835829767aed00026f1334a8dd7", size = 10329096, upload-time = "2026-03-13T12:34:09.432Z" }, + { url = "https://files.pythonhosted.org/packages/9f/a9/dd3287a82dce3df546ec560296208d4905dcf06346b6e18c2f3c63523bd1/ty-0.0.23-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e43d36bd89a151ddcad01acaeff7dcc507cb73ff164c1878d2d11549d39a061c", size = 10156631, upload-time = "2026-03-13T12:34:53.122Z" }, + { url = "https://files.pythonhosted.org/packages/0f/01/3f25909b02fac29bb0a62b2251f8d62e65d697781ffa4cf6b47a4c075c85/ty-0.0.23-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bd6a340969577b4645f231572c4e46012acba2d10d4c0c6570fe1ab74e76ae00", size = 9653211, upload-time = "2026-03-13T12:34:15.049Z" }, + { url = "https://files.pythonhosted.org/packages/d5/60/bfc0479572a6f4b90501c869635faf8d84c8c68ffc5dd87d04f049affabc/ty-0.0.23-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341441783e626eeb7b1ec2160432956aed5734932ab2d1c26f94d0c98b229937", size = 10156143, upload-time = "2026-03-13T12:34:34.468Z" }, + { url = "https://files.pythonhosted.org/packages/3a/81/8a93e923535a340f54bea20ff196f6b2787782b2f2f399bd191c4bc132d6/ty-0.0.23-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:8ce1dc66c26d4167e2c78d12fa870ef5a7ec9cc344d2baaa6243297cfa88bd52", size = 10136632, upload-time = "2026-03-13T12:34:28.832Z" }, + { url = "https://files.pythonhosted.org/packages/da/cb/2ac81c850c58acc9f976814404d28389c9c1c939676e32287b9cff61381e/ty-0.0.23-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bae1e7a294bf8528836f7617dc5c360ea2dddb63789fc9471ae6753534adca05", size = 10655025, upload-time = "2026-03-13T12:34:37.105Z" }, + { url = "https://files.pythonhosted.org/packages/b5/9b/bac771774c198c318ae699fc013d8cd99ed9caf993f661fba11238759244/ty-0.0.23-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d2b162768764d9dc177c83fb497a51532bb67cbebe57b8fa0f2668436bf53f3c", size = 11230107, upload-time = "2026-03-13T12:34:20.751Z" }, + { url = "https://files.pythonhosted.org/packages/14/09/7644fb0e297265e18243f878aca343593323b9bb19ed5278dcbc63781be0/ty-0.0.23-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d28384e48ca03b34e4e2beee0e230c39bbfb68994bb44927fec61ef3642900da", size = 10934177, upload-time = "2026-03-13T12:34:17.904Z" }, + { url = "https://files.pythonhosted.org/packages/18/14/69a25a0cad493fb6a947302471b579a03516a3b00e7bece77fdc6b4afb9b/ty-0.0.23-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:559d9a299df793cb7a7902caed5eda8a720ff69164c31c979673e928f02251ee", size = 10752487, upload-time = "2026-03-13T12:34:31.785Z" }, + { url = "https://files.pythonhosted.org/packages/9d/2a/42fc3cbccf95af0a62308ebed67e084798ab7a85ef073c9986ef18032743/ty-0.0.23-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:32a7b8a14a98e1d20a9d8d2af23637ed7efdb297ac1fa2450b8e465d05b94482", size = 10133007, upload-time = "2026-03-13T12:34:42.838Z" }, + { url = "https://files.pythonhosted.org/packages/e1/69/307833f1b52fa3670e0a1d496e43ef7df556ecde838192d3fcb9b35e360d/ty-0.0.23-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:6f803b9b9cca87af793467973b9abdd4b83e6b96d9b5e749d662cff7ead70b6d", size = 10169698, upload-time = "2026-03-13T12:34:12.351Z" }, + { url = "https://files.pythonhosted.org/packages/89/ae/5dd379ec22d0b1cba410d7af31c366fcedff191d5b867145913a64889f66/ty-0.0.23-py3-none-musllinux_1_2_i686.whl", hash = "sha256:4a0bf086ec8e2197b7ea7ebfcf4be36cb6a52b235f8be61647ef1b2d99d6ffd3", size = 10346080, upload-time = "2026-03-13T12:34:40.012Z" }, + { url = "https://files.pythonhosted.org/packages/98/c7/dfc83203d37998620bba9c4873a080c8850a784a8a46f56f8163c5b4e320/ty-0.0.23-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:252539c3fcd7aeb9b8d5c14e2040682c3e1d7ff640906d63fd2c4ce35865a4ba", size = 10848162, upload-time = "2026-03-13T12:34:45.421Z" }, + { url = "https://files.pythonhosted.org/packages/89/08/05481511cfbcc1fd834b6c67aaae090cb609a079189ddf2032139ccfc490/ty-0.0.23-py3-none-win32.whl", hash = "sha256:51b591d19eef23bbc3807aef77d38fa1f003c354e1da908aa80ea2dca0993f77", size = 9748283, upload-time = "2026-03-13T12:34:50.607Z" }, + { url = "https://files.pythonhosted.org/packages/31/2e/eaed4ff5c85e857a02415084c394e02c30476b65e158eec1938fdaa9a205/ty-0.0.23-py3-none-win_amd64.whl", hash = "sha256:1e137e955f05c501cfbb81dd2190c8fb7d01ec037c7e287024129c722a83c9ad", size = 10698355, upload-time = "2026-03-13T12:34:26.134Z" }, + { url = "https://files.pythonhosted.org/packages/91/29/b32cb7b4c7d56b9ed50117f8ad6e45834aec293e4cb14749daab4e9236d5/ty-0.0.23-py3-none-win_arm64.whl", hash = "sha256:a0399bd13fd2cd6683fd0a2d59b9355155d46546d8203e152c556ddbdeb20842", size = 10155890, upload-time = "2026-03-13T12:34:48.082Z" }, ] [[package]]