diff --git a/.agents/skills/checkout-branch/SKILL.md b/.agents/skills/checkout-branch/SKILL.md index 0d0f43f..e6ccc30 100644 --- a/.agents/skills/checkout-branch/SKILL.md +++ b/.agents/skills/checkout-branch/SKILL.md @@ -2,6 +2,7 @@ name: checkout-branch description: 'Create and switch to a new branch. Accepts a branch name or GitHub issue number. Derives branch name from issue title when an issue number is given. USE FOR: starting new work, branching from an issue, creating feature/bugfix branches. DO NOT USE FOR: committing, pushing, or managing PRs.' user-invocable: true +disable-model-invocation: true argument-hint: '' --- @@ -29,39 +30,50 @@ Create a new Git branch and switch to it. Supports two input modes: - `enhancement`, `feature` → `feature/-` - No matching label → `issue/-` - Confirm the derived branch name with the user before proceeding + - After confirmation, record the confirmed name so it can be reused in Step 3. 2. Go to Step 3 ### 2b. Explicit Name Flow -1. Use the provided name exactly -2. Validate it is a valid Git branch name (no spaces, no `..`, no `~^:?*[\`, not `@{`, not `-`) -3. If invalid, suggest a sanitized version and ask the user to confirm -4. Go to Step 3 +1. Store the provided name as the confirmed `branch_name` value. +2. Validate it by running `git check-ref-format --branch "$branch_name"`; exit status 0 means valid, non-zero means invalid. +3. If invalid, suggest a sanitized version and ask the user to confirm. +4. After confirmation, carry the confirmed branch name explicitly into Step 3; do not rely on shell variables persisting across separate tool invocations. +5. Go to Step 3 ### 3. Create and Switch -Run the following steps: +Resolve the GitHub remote before running the workflow: -```bash -# Fetch latest remote refs -git fetch origin - -# Create branch from default branch (usually main or master) -# Determine the default branch first: -git remote show origin | grep "HEAD branch" -``` - -Then create and switch: +1. Inspect configured remotes with `git remote -v`. +2. Select the GitHub remote to use; if none exists, stop and ask for a GitHub remote. If multiple GitHub remotes exist, ask the user which one to use. +3. After the branch name is confirmed and the remote is selected, run the following block as one shell invocation, passing the confirmed branch name and selected remote as positional arguments rather than interpolating either value into shell source. `$1` is the confirmed branch name and `$2` is the selected GitHub remote; do not rely on shell variables persisting across tool invocations. ```bash -git checkout -b origin/ +branch_name="$1" +github_remote="$2" +git remote get-url "$github_remote" +git fetch "$github_remote" +if ! git remote set-head "$github_remote" --auto >/dev/null; then + echo "Could not determine a unique default branch for $github_remote." >&2 + exit 1 +fi +default_ref="$(git symbolic-ref --quiet "refs/remotes/$github_remote/HEAD" || true)" +if [ -z "$default_ref" ]; then + echo "No default branch is available for $github_remote." >&2 + exit 1 +fi +default_branch="${default_ref#refs/remotes/$github_remote/}" +git checkout -b "$branch_name" "$github_remote/$default_branch" ``` -If the branch already exists locally, ask the user whether to: -- **Switch** to the existing branch (`git checkout `) -- **Reset** it to the latest origin (`git checkout -B origin/`) +If the branch already exists locally, ask the user whether to run one of these commands with the confirmed branch name as a quoted argument: +- **Switch**: `git checkout ""` +- **Reset** it to the latest remote: `git checkout -B "" "$github_remote/$default_branch"` - **Choose a different name** +Do not interpolate a branch name directly into shell command text. + ### 4. Verify After switching: diff --git a/.agents/skills/commit/SKILL.md b/.agents/skills/commit/SKILL.md index eb20bc3..37bb970 100644 --- a/.agents/skills/commit/SKILL.md +++ b/.agents/skills/commit/SKILL.md @@ -2,6 +2,7 @@ name: commit description: "**WORKFLOW SKILL** — Stage changes, write clear commit messages, and commit following best practices. USE FOR: committing code changes; writing conventional commit messages; staging files before commits; amending previous commits; creating atomic commits; generating commit messages from diffs. DO NOT USE FOR: pushing to remote (use git push); resolving merge conflicts; branching or rebasing." user-invocable: true +disable-model-invocation: true --- # Commit Workflow @@ -77,12 +78,12 @@ Follow this structure: **Simple change**: ``` -fix(auth): prevent token refresh race condition +fix(auth): Prevent token refresh race condition ``` **Feature with context**: ``` -feat(api): add pagination to user list endpoint +feat(api): Add pagination to user list endpoint Implement offset-based pagination with configurable page size. Default limit is 20 items per page. Includes cursor-based @@ -93,7 +94,7 @@ Closes #234 **Breaking change**: ``` -refactor(config)!: rename environment variables for consistency +refactor(config)!: Rename environment variables for consistency BREAKING CHANGE: DATABASE_URL is now DB_CONNECTION_STRING ``` diff --git a/.agents/skills/create-issue/SKILL.md b/.agents/skills/create-issue/SKILL.md index e5a2a6a..a26a204 100644 --- a/.agents/skills/create-issue/SKILL.md +++ b/.agents/skills/create-issue/SKILL.md @@ -2,6 +2,7 @@ name: create-issue description: 'Create a GitHub issue with proper structure and metadata. Use when the user asks to create an issue, open a ticket, report a bug, or request a feature. Supports labels, assignees, milestones, and templates.' user-invocable: true +disable-model-invocation: true argument-hint: '[optional: issue title or description]' --- @@ -130,11 +131,42 @@ Use **GitHub MCP tools** (`mcp_github_mcp_se_issue_write`) to create the issue: - `assignees`: Array of usernames - `milestone`: Milestone number (if provided) -If MCP tools are unavailable, fall back to **`gh` CLI**: +If MCP tools are unavailable, fall back to **`gh` CLI**. Pass generated values as data, not shell source. In the snippet, initialize the optional metadata arrays and populate them with the values confirmed in Step 4: + +- Start with `labels=()` and append each confirmed label with `labels+=("$label")`. +- Start with `assignees=()` and append each confirmed assignee with `assignees+=("$assignee")`. +- Set `milestone=""` when no milestone is confirmed; otherwise set it to the confirmed milestone value. + ```bash -gh issue create --title "" --body "<body>" --label "<label1>,<label2>" --assignee "<user1>,<user2>" +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +labels=() +assignees=() +milestone="" +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +# Append each confirmed value to labels/assignees and set milestone before building args. +args=(issue create --title "$title" --body-file "$body_file") +for label in "${labels[@]}"; do + args+=(--label "$label") +done +for assignee in "${assignees[@]}"; do + args+=(--assignee "$assignee") +done +if [ -n "$milestone" ]; then + args+=(--milestone "$milestone") +fi + +if ! gh "${args[@]}"; then + echo "Failed to create the GitHub issue." >&2 + exit 1 +fi ``` +Never interpolate the title or body directly into command text; quoted variable expansion and `--body-file` preserve the generated content as data. + ### 6. Report Result - Display the created issue URL - Suggest next steps (e.g., add to project board, link to PR) diff --git a/.agents/skills/create-pr/SKILL.md b/.agents/skills/create-pr/SKILL.md index 7439e65..435c73b 100644 --- a/.agents/skills/create-pr/SKILL.md +++ b/.agents/skills/create-pr/SKILL.md @@ -1,6 +1,6 @@ --- name: create-pr -description: 'Create a GitHub pull request from the current branch. Use when the user asks to create a PR, open a pull request, or submit a PR for review. Assumes branch is pushed. Auto-generates title and description from commit history.' +description: 'Create a GitHub pull request from the current branch. Use when the user asks to create a PR, open a pull request, or submit a PR for review. Branch will be pushed if needed. Auto-generates title and description from commit history.' user-invocable: true argument-hint: 'Optional: base branch name (defaults to main)' --- @@ -61,17 +61,28 @@ Before creating the PR, present: ### 5. Create the Pull Request -Prefer **GitHub MCP tools** (`mcp_github_mcp_se_create_pull_request`) to create the PR. If MCP tools are unavailable or fail, fall back to **`gh` CLI** in the terminal: +Prefer **GitHub MCP tools** (`mcp_github_mcp_se_create_pull_request`) to create the PR. If MCP tools are unavailable or fail, fall back to **`gh` CLI** in the terminal. Before running the snippet, expose the generated values to the shell as `GENERATED_TITLE`, `GENERATED_BODY`, `HEAD_BRANCH`, `BASE_BRANCH`, and `DRAFT` (where `DRAFT` is `true` or `false`). + ```bash -gh pr create --base <base> --head <head> --title "<title>" --body "<body>" +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +head="$HEAD_BRANCH" +base="$BASE_BRANCH" +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +draft_args=() +if [ "${DRAFT:-false}" = "true" ]; then + draft_args+=(--draft) +fi + +gh pr create --base "$base" --head "$head" --title "$title" --body-file "$body_file" "${draft_args[@]}" ``` -Set the following: -- `title` → generated title -- `body` → generated description -- `head` → current branch -- `base` → target branch -- `draft` → based on user preference +Set the shell variables above from the generated title/description, current branch, target branch, and user-selected draft state before invoking the snippet. + +Pass all generated metadata through quoted arguments or files; do not build shell command text from generated values. Preserve `--draft` whenever the user selected Draft. ### 6. Report Result - Display the created PR URL diff --git a/.agents/skills/create-release/SKILL.md b/.agents/skills/create-release/SKILL.md index c058fe6..357875d 100644 --- a/.agents/skills/create-release/SKILL.md +++ b/.agents/skills/create-release/SKILL.md @@ -16,11 +16,15 @@ user-invocable: true ### 1. Analyze Recent Commits -Gather recent commits to determine the appropriate version bump: +Gather recent commits to determine the appropriate version bump. Use the full history when the repository has no tags: ```bash -# Get commits since last tag (or all commits if no tags exist) -git log --oneline --no-merges $(git describe --tags --abbrev=0 2>/dev/null || echo "HEAD")..HEAD +LATEST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)" +if [ -n "$LATEST_TAG" ]; then + git log --oneline --no-merges "$LATEST_TAG..HEAD" +else + git log --oneline --no-merges HEAD +fi ``` **Version determination rules:** @@ -31,10 +35,17 @@ git log --oneline --no-merges $(git describe --tags --abbrev=0 2>/dev/null || ec ### 2. Determine Next Version ```bash -# Get current version from latest tag (strip leading 'v') -LATEST_TAG=$(git describe --tags --abbrev=0 2>/dev/null | sed 's/^v//') -# Default to 0.0.0 if no tags exist -CURRENT_VERSION=${LATEST_TAG:-0.0.0} +LATEST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)" +if [ -n "$LATEST_TAG" ]; then + CURRENT_VERSION="$(printf '%s' "$LATEST_TAG" | sed 's/^v//')" +else + CURRENT_VERSION="$(node -p "require('./package.json').version")" +fi + +if [ -z "$CURRENT_VERSION" ] || [ "$CURRENT_VERSION" = "undefined" ]; then + echo "No release baseline is available; provide the current version explicitly." + exit 1 +fi # Parse version components IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" @@ -43,13 +54,15 @@ IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" # (Check for breaking changes, features, or fixes as described above) ``` +When no tag exists, use `package.json` as the version baseline; do not default to `0.0.0`. If `package.json` cannot be read, require the user to provide the current version explicitly. + ### 3. Generate Changelog -Use the [update-changelog](../update-changelog/SKILL.md) skill to generate the changelog entry for this release. Follow the procedure in that skill to: +Use the [update-changelog](../update-changelog/SKILL.md) skill to generate the changelog entry for this release. Invoke it with `NEW_VERSION` as its version argument and follow its procedure to: 1. Categorize commits into Keep a Changelog sections (Added, Changed, Fixed, etc.) -2. Write the entry to `CHANGELOG.md` -3. Show the diff and get user confirmation before saving +2. Build and show the proposed diff +3. After explicit user confirmation, write the entry to `CHANGELOG.md` ### 4. Create Git Tag @@ -67,18 +80,26 @@ git push origin "v$NEW_VERSION" ### 6. Create GitHub Release -Use the GitHub CLI to create the release: +Use the GitHub CLI to create the release. Extract only the newly generated version section into a temporary notes file: ```bash -# Create release with changelog body +release_notes_file="$(mktemp)" +awk -v section="## [$NEW_VERSION]" ' + $0 == section || index($0, section " -") == 1 { in_section=1; next } + in_section && $0 ~ /^## / { exit } + in_section { print } +' CHANGELOG.md >"$release_notes_file" +test -s "$release_notes_file" + gh release create "v$NEW_VERSION" \ --title "Release v$NEW_VERSION" \ - --notes-file CHANGELOG.tmp + --notes-file "$release_notes_file" -# Clean up temporary file -rm CHANGELOG.tmp +rm -f "$release_notes_file" ``` +The temporary file is created from `CHANGELOG.md` after the changelog confirmation step; never point cleanup at `CHANGELOG.md` itself. + ### 7. Verify Release ```bash diff --git a/.agents/skills/patch-findings/SKILL.md b/.agents/skills/patch-findings/SKILL.md index 492a777..b46fbf8 100644 --- a/.agents/skills/patch-findings/SKILL.md +++ b/.agents/skills/patch-findings/SKILL.md @@ -2,6 +2,7 @@ name: patch-findings description: 'Generate fix patches from pre-identified findings. Use when: generating code fixes from PR review comments, producing patches from scan results, translating described problems into code changes. DO NOT USE FOR: resolving GitHub issues (use resolve-issue); creating PRs (use create-pr); managing branches.' argument-hint: 'describe the finding to patch' +disable-model-invocation: true --- # Patch Findings diff --git a/.agents/skills/resolve-issue/SKILL.md b/.agents/skills/resolve-issue/SKILL.md index 202286e..ae1ec2b 100644 --- a/.agents/skills/resolve-issue/SKILL.md +++ b/.agents/skills/resolve-issue/SKILL.md @@ -2,6 +2,7 @@ name: resolve-issue description: "**WORKFLOW SKILL** — Systematically resolve GitHub issues from assignment through PR creation. USE FOR: working on assigned issues; following structured debugging workflows; implementing feature requests; fixing bugs reported in issues; creating focused PRs linked to issues. DO NOT USE FOR: creating new issues (use create-issue); reviewing PRs (use review-pr); general coding tasks without an issue context." user-invocable: true +disable-model-invocation: true argument-hint: '[issue-number]' --- diff --git a/.agents/skills/review-changes/SKILL.md b/.agents/skills/review-changes/SKILL.md index 87483b7..ab25052 100644 --- a/.agents/skills/review-changes/SKILL.md +++ b/.agents/skills/review-changes/SKILL.md @@ -2,7 +2,7 @@ name: review-changes description: 'Review staged and unstaged changes before committing. Use for: code review, pre-commit checks, validating changes, checking diff quality, linting, test verification, security scan. Trigger phrases: review changes, review my changes, check changes, pre-commit review, review diff.' user-invocable: true -argument-hint: '[optional: file path or branch to review]' +argument-hint: '[optional: file path, branch, --staged, or --unstaged]' --- # Review Changes @@ -17,11 +17,18 @@ Systematic pre-commit code review checklist for staged and unstaged changes. ## Procedure -### 1. Identify Changes -- Run `git diff --staged` to see staged changes -- Run `git diff` to see unstaged changes -- Run `git status` to see untracked files -- Summarize the scope: which files changed, what the changes accomplish +### 1. Identify Changes and Scope + +Honor the optional `argument-hint`: + +- **No argument**: review staged and unstaged changes plus all untracked files. +- **`--staged`**: review only staged changes with `git diff --staged`. +- **`--unstaged`**: review only unstaged changes with `git diff`. +- **Existing path**: review only that path. Use path-limited diffs (`git diff --staged -- "$path"` and `git diff -- "$path"`) and `git status --short -- "$path"`. For an untracked path, read its contents directly before reviewing it; `git diff` does not include untracked content. +- **Existing local branch**: review that branch's changes from its merge base with the current branch. Verify it with `git show-ref --verify --quiet "refs/heads/$branch"`, then use `base="$(git merge-base HEAD "$branch")"` and `git diff "$base..$branch"`. +- **Anything else**: report that the scope argument is neither an existing path nor a local branch and stop rather than silently reviewing a different scope. + +Summarize the selected scope: which files changed and what the changes accomplish. ### 2. Correctness Check For each changed file, verify: diff --git a/.agents/skills/review-pr/SKILL.md b/.agents/skills/review-pr/SKILL.md index efa867f..9c1530c 100644 --- a/.agents/skills/review-pr/SKILL.md +++ b/.agents/skills/review-pr/SKILL.md @@ -13,16 +13,19 @@ Comprehensive PR review that summarizes changes, identifies potential issues, an ### 1. Identify the PR -- If the user provided a reference like `owner/repo#123`, parse it. +- If the user provided a reference like `owner/repo#123`, parse it into a repository (`owner/repo`) and pull request number. - Otherwise, check for an active branch or recent PR in the workspace. -- Use `mcp_github_mcp_se_search_pull_requests` to find the PR if needed. +- Use the GitHub integration configured for the environment to find the PR when available; do not assume a connector-specific MCP tool name. +- When no GitHub integration is available and the reference was left blank, derive the current PR's repository and number before Step 2 with `repo="$(gh repo view --json nameWithOwner -q .nameWithOwner)"` and `number="$(gh pr view --json number -q .number)"`. +- When no GitHub integration is available, use the `gh` CLI fallback described below. ### 2. Gather PR Details -Fetch the PR metadata and content: -- Use `mcp_github_mcp_se_search_pull_requests` with the PR query to get title, description, author, state, and labels. -- Use `mcp_github_mcp_se_list_branches` if branch info is needed. -- Note the base and head branches to understand the diff scope. +Fetch the PR metadata, changed-file list, unified diff, and file contents before reviewing code: + +- Use the configured GitHub integration when available to get the PR metadata, changed-file list, unified diff, and head revision. +- Otherwise, use the `gh` CLI commands in Step 5: `gh pr view "$number" -R "$repo"`, `gh pr diff "$number" -R "$repo"`, and the GitHub API fallback for file contents. +- Note the base and head branches and head revision to understand the diff scope. ### 3. Analyze Changes @@ -80,4 +83,10 @@ One of: ### 5. Optional: Submit the Review -If the user wants, use `mcp_github_mcp_se_pull_request_review_write` to submit the review directly on GitHub with the appropriate event (`APPROVE`, `REQUEST_CHANGES`, or `COMMENT`). +If a configured GitHub integration is available, use it to submit the review with the requested event (`APPROVE`, `REQUEST_CHANGES`, or `COMMENT`). Otherwise use the `gh` CLI fallback below. + +If GitHub MCP tools are unavailable, use `gh` CLI. When the user supplied a reference, parse `owner/repo#123` into `repo="$owner/$repo_name"` and `number="123"`. When the PR was detected from the workspace instead, derive `repo="$(gh repo view --json nameWithOwner -q .nameWithOwner)"` and `number="$(gh pr view --json number -q .number)"`. Then run `gh pr view "$number" -R "$repo" --json title,body,state,labels,baseRefName,headRefName,headRefOid,files` for PR metadata and the changed-file list. +- Use `gh pr diff "$number" -R "$repo"` for the unified diff. +- Extract the head revision with `head_sha="$(gh pr view "$number" -R "$repo" --json headRefOid -q .headRefOid)"` before fetching changed files. +- For each changed file with status other than `removed`, URL-encode each path segment before interpolation (spaces, `#`, `?`, and `%`). For example, set `encoded_path="$(python3 -c 'import sys; from urllib.parse import quote; print("/".join(quote(part, safe="") for part in sys.argv[1].split("/")))' "$path")"` and fetch it at the PR head with `gh api "repos/$repo/contents/$encoded_path?ref=$head_sha" -H "Accept: application/vnd.github.raw+json"`. A removed path is already represented by the diff and should be skipped. For a blob that exceeds the contents API size cap, use the Git Data blobs API with its blob SHA instead. +- For review submission, write the review body to a temporary file and use `gh pr review "$number" -R "$repo" --approve --body-file "$review_file"`, `--request-changes`, or `--comment` as appropriate. Never interpolate review text into shell command source. diff --git a/.agents/skills/scan-features/SKILL.md b/.agents/skills/scan-features/SKILL.md index f7d8f8f..b5ae5a5 100644 --- a/.agents/skills/scan-features/SKILL.md +++ b/.agents/skills/scan-features/SKILL.md @@ -104,6 +104,30 @@ For each prioritized finding (or group of related findings), create a GitHub iss - **Effort estimate**: S / M / L - **Impact**: What improves if this is built +If GitHub MCP tools are unavailable, fall back to `gh` CLI for each issue. Populate the generated title and body in quoted shell variables and append each confirmed label before invoking `gh`. + +```bash +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +labels=() +# Append each confirmed label, for example: labels+=("bug") +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +args=(issue create --title "$title" --body-file "$body_file") +for label in "${labels[@]}"; do + args+=(--label "$label") +done + +if ! gh "${args[@]}"; then + echo "Failed to create the GitHub issue." >&2 + exit 1 +fi +``` + +Never interpolate generated text directly into shell command source. + ### 7. Summary Report After scanning, provide the user with a summary: diff --git a/.agents/skills/scan-issues/SKILL.md b/.agents/skills/scan-issues/SKILL.md index f860f6c..d278737 100644 --- a/.agents/skills/scan-issues/SKILL.md +++ b/.agents/skills/scan-issues/SKILL.md @@ -70,6 +70,30 @@ For each prioritized issue (or group of related issues), create a GitHub issue u - **Suggested fix**: How to resolve the issue - **Impact**: What could happen if left unaddressed +If GitHub MCP tools are unavailable, fall back to `gh` CLI for each issue. Populate the generated title and body in quoted shell variables and append each confirmed label before invoking `gh`. + +```bash +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +labels=() +# Append each confirmed label, for example: labels+=("bug") +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +args=(issue create --title "$title" --body-file "$body_file") +for label in "${labels[@]}"; do + args+=(--label "$label") +done + +if ! gh "${args[@]}"; then + echo "Failed to create the GitHub issue." >&2 + exit 1 +fi +``` + +Never interpolate generated text directly into shell command source. + ### 6. Summary Report After all issues are created, provide the user with a summary: diff --git a/.agents/skills/update-changelog/SKILL.md b/.agents/skills/update-changelog/SKILL.md index e0d7d99..b35ebc6 100644 --- a/.agents/skills/update-changelog/SKILL.md +++ b/.agents/skills/update-changelog/SKILL.md @@ -20,12 +20,13 @@ Generate a [Keep a Changelog](https://keepachangelog.com/) formatted entry from Find the starting point for the changelog entry: -1. Check if a CHANGELOG.md exists. If it does, scan it for the most recent version header to understand the existing format. -2. Find the latest git tag: `git describe --tags --abbrev=0` -3. If the user specifies a starting point (commit, tag, or date), use that instead. -4. Confirm the range with the user: "I'll summarize commits from `<last-tag>` to `HEAD`. Is that correct?" +- Check if a CHANGELOG.md exists. If it does, scan it for the most recent version header to understand the existing format. +- Find the latest git tag with `git describe --tags --abbrev=0`. +- If the user specifies a starting point (commit, tag, or date), set `start` to that value. +- Otherwise, if a tag exists, set `start` to the latest tag. +- Confirm the selected range with the user before categorizing it. -Run: `git log <start>..HEAD --oneline` to preview the commit list before categorizing. +Set `start` to the latest tag or user-specified starting point. If `start` is set, run `git log "$start"..HEAD --oneline`; only when no tag exists and no starting point was given, run `git log --oneline --no-merges HEAD`. ### 3. Categorize Commits @@ -69,11 +70,10 @@ Rules: ### 5. Write or Append to CHANGELOG.md -1. If `CHANGELOG.md` exists: - - Insert the new entry after the `# Changelog` header (or after any "Keep a Changelog" preamble). - - Don't duplicate entries that already exist. -2. If `CHANGELOG.md` does not exist: - - Create it with the standard preamble: +Build the proposed change before modifying the file: + +- If `CHANGELOG.md` exists, construct the new entry in memory and prepare a unified diff against the current file. Insert the entry after the `# Changelog` header (or after any "Keep a Changelog" preamble) and avoid duplicates. +- If `CHANGELOG.md` does not exist, construct the complete file in memory using the standard preamble: ```markdown # Changelog @@ -83,7 +83,8 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/). ``` -3. Show the user the diff of what was written/appended and ask for confirmation before saving. +- Show the proposed diff to the user and request explicit confirmation. +- Only after the user confirms, write or append the entry to `CHANGELOG.md`. Do not modify the file before confirmation. ### 6. Finalize diff --git a/.claude/skills/checkout-branch/SKILL.md b/.claude/skills/checkout-branch/SKILL.md new file mode 100644 index 0000000..e6ccc30 --- /dev/null +++ b/.claude/skills/checkout-branch/SKILL.md @@ -0,0 +1,97 @@ +--- +name: checkout-branch +description: 'Create and switch to a new branch. Accepts a branch name or GitHub issue number. Derives branch name from issue title when an issue number is given. USE FOR: starting new work, branching from an issue, creating feature/bugfix branches. DO NOT USE FOR: committing, pushing, or managing PRs.' +user-invocable: true +disable-model-invocation: true +argument-hint: '<branch-name-or-issue-number>' +--- + +# Checkout Branch + +Create a new Git branch and switch to it. Supports two input modes: + +1. **Issue number** (e.g., `42`) — fetches the issue title and derives a branch name like `feature/42-fix-login-error` +2. **Branch name** (e.g., `feature/my-feature`) — uses the name as-is + +## Procedure + +### 1. Determine Input Type + +- If the argument is a **pure number** → treat as an issue number, go to Step 2a +- Otherwise → treat as an explicit branch name, go to Step 2b + +### 2a. Issue Number Flow + +1. Fetch the issue from the current GitHub repository using the provided issue number + - Extract the issue title + - Derive a slug: lowercase the title, replace spaces with hyphens, strip special characters, truncate to 50 chars + - Choose a prefix based on issue labels (if available): + - `bug`, `bugfix`, `defect` → `bugfix/<number>-<slug>` + - `enhancement`, `feature` → `feature/<number>-<slug>` + - No matching label → `issue/<number>-<slug>` + - Confirm the derived branch name with the user before proceeding + - After confirmation, record the confirmed name so it can be reused in Step 3. +2. Go to Step 3 + +### 2b. Explicit Name Flow + +1. Store the provided name as the confirmed `branch_name` value. +2. Validate it by running `git check-ref-format --branch "$branch_name"`; exit status 0 means valid, non-zero means invalid. +3. If invalid, suggest a sanitized version and ask the user to confirm. +4. After confirmation, carry the confirmed branch name explicitly into Step 3; do not rely on shell variables persisting across separate tool invocations. +5. Go to Step 3 + +### 3. Create and Switch + +Resolve the GitHub remote before running the workflow: + +1. Inspect configured remotes with `git remote -v`. +2. Select the GitHub remote to use; if none exists, stop and ask for a GitHub remote. If multiple GitHub remotes exist, ask the user which one to use. +3. After the branch name is confirmed and the remote is selected, run the following block as one shell invocation, passing the confirmed branch name and selected remote as positional arguments rather than interpolating either value into shell source. `$1` is the confirmed branch name and `$2` is the selected GitHub remote; do not rely on shell variables persisting across tool invocations. + +```bash +branch_name="$1" +github_remote="$2" +git remote get-url "$github_remote" +git fetch "$github_remote" +if ! git remote set-head "$github_remote" --auto >/dev/null; then + echo "Could not determine a unique default branch for $github_remote." >&2 + exit 1 +fi +default_ref="$(git symbolic-ref --quiet "refs/remotes/$github_remote/HEAD" || true)" +if [ -z "$default_ref" ]; then + echo "No default branch is available for $github_remote." >&2 + exit 1 +fi +default_branch="${default_ref#refs/remotes/$github_remote/}" +git checkout -b "$branch_name" "$github_remote/$default_branch" +``` + +If the branch already exists locally, ask the user whether to run one of these commands with the confirmed branch name as a quoted argument: +- **Switch**: `git checkout "<confirmed-branch-name>"` +- **Reset** it to the latest remote: `git checkout -B "<confirmed-branch-name>" "$github_remote/$default_branch"` +- **Choose a different name** + +Do not interpolate a branch name directly into shell command text. + +### 4. Verify + +After switching: + +- Confirm the current branch with `git branch --show-current` +- Show a short summary: branch name, base branch, and tracking status + +## Example Interactions + +| User says | Action | +|-----------|--------| +| `/checkout-branch 42` | Fetch issue #42, derive `feature/42-fix-login`, create & switch | +| `/checkout-branch feature/auth-refactor` | Create & switch to `feature/auth-refactor` | +| `/checkout-branch bugfix/15-memory-leak` | Create & switch to `bugfix/15-memory-leak` | + +## Edge Cases + +- **Branch already exists locally**: Offer switch, reset, or rename. +- **Issue not found**: Report the error and ask the user to provide a branch name manually. +- **Detached HEAD state**: Warn the user and suggest creating a new branch from their current commit. +- **Dirty working tree**: Warn the user that uncommitted changes will be carried over; suggest stashing first if they prefer. diff --git a/.claude/skills/commit/SKILL.md b/.claude/skills/commit/SKILL.md new file mode 100644 index 0000000..37bb970 --- /dev/null +++ b/.claude/skills/commit/SKILL.md @@ -0,0 +1,111 @@ +--- +name: commit +description: "**WORKFLOW SKILL** — Stage changes, write clear commit messages, and commit following best practices. USE FOR: committing code changes; writing conventional commit messages; staging files before commits; amending previous commits; creating atomic commits; generating commit messages from diffs. DO NOT USE FOR: pushing to remote (use git push); resolving merge conflicts; branching or rebasing." +user-invocable: true +disable-model-invocation: true +--- + +# Commit Workflow + +## Overview + +This skill guides a structured commit workflow: analyze changes, stage appropriately, write a clear commit message, and commit. + +## Workflow Steps + +### 1. Analyze Changes + +Review what has changed before committing: + +- **Check status**: Understand which files are modified, added, or deleted +- **Review diffs**: Understand *what* and *why* each change was made +- **Group related changes**: Identify logical units of work that belong together + +### 2. Stage Changes + +Stage files intentionally — avoid `git add .` unless all changes are part of one commit: + +- **Atomic commits**: Each commit should represent one logical change +- **Partial staging**: Use `git add -p` for interactive staging when a file contains multiple unrelated changes +- **Review before staging**: Confirm no debug code, secrets, or unrelated files are included + +### 3. Write Commit Message + +Follow this structure: + +``` +<type>(<scope>): <subject> + +<body> + +<footer> +``` + +**Type** (pick one): +| Type | When to Use | +|------|-------------| +| `feat` | New feature or capability | +| `fix` | Bug fix | +| `docs` | Documentation only | +| `style` | Formatting, missing semicolons, etc. (no logic change) | +| `refactor` | Code restructuring (no feature or fix) | +| `test` | Adding or updating tests | +| `chore` | Build, CI, tooling, dependencies | +| `perf` | Performance improvement | + +**Subject line rules**: +- Use imperative mood: "add" not "added" or "adds" +- No period at the end +- Keep under 72 characters +- Capitalize the first letter + +**Body** (when needed): +- Explain *what* and *why*, not *how* +- Wrap at 72 characters +- Separate from subject with a blank line + +**Footer** (when applicable): +- Reference issues: `Closes #123`, `Fixes #456` +- Note breaking changes: `BREAKING CHANGE: <description>` + +### 4. Commit + +- Review the commit message one more time +- Commit with `git commit` +- Verify with `git log --oneline -1` + +## Common Patterns + +**Simple change**: +``` +fix(auth): Prevent token refresh race condition +``` + +**Feature with context**: +``` +feat(api): Add pagination to user list endpoint + +Implement offset-based pagination with configurable page size. +Default limit is 20 items per page. Includes cursor-based +navigation for large datasets. + +Closes #234 +``` + +**Breaking change**: +``` +refactor(config)!: Rename environment variables for consistency + +BREAKING CHANGE: DATABASE_URL is now DB_CONNECTION_STRING +``` + +## Quality Checklist + +Before finalizing a commit, verify: + +- [ ] Changes are staged intentionally (no unintended files) +- [ ] Commit type accurately describes the change +- [ ] Subject is imperative, concise, and under 72 chars +- [ ] Body explains *why* (if non-obvious) +- [ ] No secrets, credentials, or debug code included +- [ ] Commit represents a single logical change diff --git a/.claude/skills/create-issue/SKILL.md b/.claude/skills/create-issue/SKILL.md new file mode 100644 index 0000000..a26a204 --- /dev/null +++ b/.claude/skills/create-issue/SKILL.md @@ -0,0 +1,198 @@ +--- +name: create-issue +description: 'Create a GitHub issue with proper structure and metadata. Use when the user asks to create an issue, open a ticket, report a bug, or request a feature. Supports labels, assignees, milestones, and templates.' +user-invocable: true +disable-model-invocation: true +argument-hint: '[optional: issue title or description]' +--- + +# Create Issue + +Create a well-structured GitHub issue with appropriate metadata. + +## When to Use +- User asks to "create an issue", "open a ticket", or "report a bug" +- User wants to track a task, bug, or feature request +- After discovering a problem that needs documentation + +## Procedure + +### 1. Determine Repository Context +- Try to infer the repository from `git remote -v` +- If no remote or multiple remotes exist, ask the user for the target `owner/repo` + +### 2. Gather Issue Details + +**Auto-detect issue type** from the title/description using these heuristics: +| Keywords | Type | +|----------|------| +| `fail`, `error`, `crash`, `broken`, `doesn't work`, `bug`, `wrong`, `regression` | Bug | +| `add`, `support`, `implement`, `feature`, `new`, `enhance`, `improve`, `request` | Feature | +| `fix typo`, `update docs`, `refactor`, `clean up`, `rename`, `move`, `migrate` | Task | + +If the type is ambiguous, ask the user to confirm. If confident, proceed without asking. + +**Infer from context** (don't ask if already known): +- **Title**: Use the provided argument or derive from description +- **Description**: Expand on the title with relevant details +- **Labels**: Query existing repository labels and pick matching ones automatically (see Step 3) + +**Only ask if missing** (don't prompt for optional fields the user didn't mention): +- Assignee(s) +- Milestone +- Priority +- Additional context or details + +### 3. Structure the Issue Body + +Use this template based on issue type: + +**Bug Report:** +```markdown +## Description +[Clear description of the bug] + +## Steps to Reproduce +1. [Step 1] +2. [Step 2] +3. [Step 3] + +## Expected Behavior +[What should happen] + +## Actual Behavior +[What actually happens] + +## Environment +- OS: [e.g., Windows 11, macOS 14] +- Browser: [if applicable] +- Version: [e.g., v1.2.3] + +## Additional Context +[Any other relevant information, screenshots, logs] +``` + +**Feature Request:** +```markdown +## Description +[Clear description of the feature] + +## Problem Statement +[What problem does this solve?] + +## Proposed Solution +[How should this work?] + +## Alternatives Considered +[Other approaches considered] + +## Additional Context +[Any mockups, examples, or references] +``` + +**Task:** +```markdown +## Description +[What needs to be done] + +## Acceptance Criteria +- [ ] [Criterion 1] +- [ ] [Criterion 2] +- [ ] [Criterion 3] + +## Dependencies +[Any blockers or related issues] + +## Timeline +[Expected completion, if applicable] +``` + +### 4. Confirm with User +Before creating, present a concise summary: +- Generated title +- Detected type (if inferred) +- Labels to be applied +- Assignees (if any) +- Milestone (if any) + +Ask: **"Create this issue?"** + +> **Suggestions are optional.** Only present labels/assignees/milestones that were explicitly requested or automatically inferred. Don't add extra fields the user didn't ask for. + +### 5. Create the Issue + +Use **GitHub MCP tools** (`mcp_github_mcp_se_issue_write`) to create the issue: +- `method`: "create" +- `owner`: Repository owner +- `repo`: Repository name +- `title`: Generated title +- `body`: Generated description +- `labels`: Array of label names +- `assignees`: Array of usernames +- `milestone`: Milestone number (if provided) + +If MCP tools are unavailable, fall back to **`gh` CLI**. Pass generated values as data, not shell source. In the snippet, initialize the optional metadata arrays and populate them with the values confirmed in Step 4: + +- Start with `labels=()` and append each confirmed label with `labels+=("$label")`. +- Start with `assignees=()` and append each confirmed assignee with `assignees+=("$assignee")`. +- Set `milestone=""` when no milestone is confirmed; otherwise set it to the confirmed milestone value. + +```bash +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +labels=() +assignees=() +milestone="" +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +# Append each confirmed value to labels/assignees and set milestone before building args. +args=(issue create --title "$title" --body-file "$body_file") +for label in "${labels[@]}"; do + args+=(--label "$label") +done +for assignee in "${assignees[@]}"; do + args+=(--assignee "$assignee") +done +if [ -n "$milestone" ]; then + args+=(--milestone "$milestone") +fi + +if ! gh "${args[@]}"; then + echo "Failed to create the GitHub issue." >&2 + exit 1 +fi +``` + +Never interpolate the title or body directly into command text; quoted variable expansion and `--body-file` preserve the generated content as data. + +### 6. Report Result +- Display the created issue URL +- Suggest next steps (e.g., add to project board, link to PR) + +## Example Interactions + +| User says | Action | +|-----------|--------| +| `/create-issue` | Interactive: gather all details step-by-step | +| `/create-issue Login fails with SSO` | Create bug report with provided title | +| `/create-issue Add dark mode support` | Create feature request | +| `/create-issue Fix typo in README` | Create simple task issue | + +## Edge Cases + +- **No repository context**: Fall back to `git remote -v`; ask only if inference fails +- **Missing title**: Prompt for a title; infer type automatically +- **Invalid labels**: Only use existing repository labels — skip labels that don't exist rather than creating new ones +- **Issue already exists**: Search for similar open issues first, suggest linking if found +- **Rate limits**: Handle GitHub API rate limits gracefully, suggest waiting or using CLI + +## Integration with Other Skills + +This skill works well with: +- `checkout-branch`: After creating an issue, **optionally** suggest creating a branch with `/checkout-branch <issue-number>` +- `commit`: **Optionally** mention referencing the issue with `Closes #<number>` in commits +- `create-pr`: **Optionally** suggest linking a PR to close the issue + +> Keep all integration suggestions brief and non-intrusive. Only mention them once, not repeatedly. \ No newline at end of file diff --git a/.claude/skills/create-pr/SKILL.md b/.claude/skills/create-pr/SKILL.md new file mode 100644 index 0000000..435c73b --- /dev/null +++ b/.claude/skills/create-pr/SKILL.md @@ -0,0 +1,105 @@ +--- +name: create-pr +description: 'Create a GitHub pull request from the current branch. Use when the user asks to create a PR, open a pull request, or submit a PR for review. Branch will be pushed if needed. Auto-generates title and description from commit history.' +user-invocable: true +argument-hint: 'Optional: base branch name (defaults to main)' +--- + +# Create Pull Request + +## When to Use +- User asks to "create a PR", "open a pull request", or "submit for review" +- User wants to submit changes for team review +- After completing work on a feature branch + +## Prerequisites +- Current branch must have at least one commit ahead of the base branch +- Branch will be auto-pushed to remote if not already pushed + +## Procedure + +### 1. Determine Branch Context +- Identify the current branch name +- Identify the base branch (default: `main`, or use the argument if provided) +- Verify there are commits ahead of the base branch +- If branch has no upstream or has unpushed commits, push automatically (`git push -u origin <branch>`) + +### 2. Analyze Commits +- Run `git log --oneline base..current` to get all commits on the feature branch +- Group commits by type (feat, fix, refactor, docs, test, chore, etc.) +- Identify the primary change/feature being introduced + +### 3. Generate PR Metadata + +**Title** (max 72 characters): +- Use the first line of the most significant commit, or synthesize from commit grouping +- Follow Conventional Commits format when commits use it: `type(scope): description` + +**Description body** (Markdown): +```markdown +## Summary +[1-2 sentence description of what this PR accomplishes] + +## Changes +- **feat**: [list of feature commits] +- **fix**: [list of bug fix commits] +- **refactor**: [list of refactors] +- [other categories as needed] + +## Testing +[How to test these changes, if evident from commits or code] + +## Related +[Closes #123, Fixes #456 if commit messages reference issues] +``` + +### 4. Ask User for Confirmation +Before creating the PR, present: +- The generated title +- The generated description +- Ask: **Draft or Ready for review?** + +### 5. Create the Pull Request + +Prefer **GitHub MCP tools** (`mcp_github_mcp_se_create_pull_request`) to create the PR. If MCP tools are unavailable or fail, fall back to **`gh` CLI** in the terminal. Before running the snippet, expose the generated values to the shell as `GENERATED_TITLE`, `GENERATED_BODY`, `HEAD_BRANCH`, `BASE_BRANCH`, and `DRAFT` (where `DRAFT` is `true` or `false`). + +```bash +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +head="$HEAD_BRANCH" +base="$BASE_BRANCH" +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +draft_args=() +if [ "${DRAFT:-false}" = "true" ]; then + draft_args+=(--draft) +fi + +gh pr create --base "$base" --head "$head" --title "$title" --body-file "$body_file" "${draft_args[@]}" +``` + +Set the shell variables above from the generated title/description, current branch, target branch, and user-selected draft state before invoking the snippet. + +Pass all generated metadata through quoted arguments or files; do not build shell command text from generated values. Preserve `--draft` whenever the user selected Draft. + +### 6. Report Result +- Display the created PR URL +- Suggest next steps (e.g., request reviewers, add labels) + +## Example Usage + +User: "create a PR" +→ Skill detects current branch `feat/user-auth`, base `main` +→ Analyzes 5 commits, generates title and description +→ Presents to user for review +→ Creates PR when confirmed + +User: "create a PR targeting develop" +→ Uses `develop` as the base branch instead of `main` + +## Notes +- If commits are not conventional format, synthesize description from commit diffs +- For single-commit PRs, use the commit message directly +- Always verify remote tracking branch exists or offer to push diff --git a/.claude/skills/create-release/SKILL.md b/.claude/skills/create-release/SKILL.md new file mode 100644 index 0000000..357875d --- /dev/null +++ b/.claude/skills/create-release/SKILL.md @@ -0,0 +1,146 @@ +--- +name: create-release +description: 'Create GitHub releases with automated versioning and changelog generation. Use for: publishing releases, creating tags, generating release notes, version bumping, semantic versioning.' +user-invocable: true +--- + +# Create GitHub Release + +## When to Use +- Publishing a new version of your software +- Creating a GitHub release with tag and changelog +- Automating semantic versioning based on commit history +- Generating release notes from conventional commits + +## Procedure + +### 1. Analyze Recent Commits + +Gather recent commits to determine the appropriate version bump. Use the full history when the repository has no tags: + +```bash +LATEST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)" +if [ -n "$LATEST_TAG" ]; then + git log --oneline --no-merges "$LATEST_TAG..HEAD" +else + git log --oneline --no-merges HEAD +fi +``` + +**Version determination rules:** +- **Major (X.0.0)**: If any commit contains `BREAKING CHANGE` or starts with `feat!:` or `refactor!:` +- **Minor (x.Y.0)**: If any commit starts with `feat:` +- **Patch (x.y.Z)**: For bug fixes (`fix:`), docs (`docs:`), chores (`chore:`), or other changes + +### 2. Determine Next Version + +```bash +LATEST_TAG="$(git describe --tags --abbrev=0 2>/dev/null || true)" +if [ -n "$LATEST_TAG" ]; then + CURRENT_VERSION="$(printf '%s' "$LATEST_TAG" | sed 's/^v//')" +else + CURRENT_VERSION="$(node -p "require('./package.json').version")" +fi + +if [ -z "$CURRENT_VERSION" ] || [ "$CURRENT_VERSION" = "undefined" ]; then + echo "No release baseline is available; provide the current version explicitly." + exit 1 +fi + +# Parse version components +IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT_VERSION" + +# Determine bump type based on commits +# (Check for breaking changes, features, or fixes as described above) +``` + +When no tag exists, use `package.json` as the version baseline; do not default to `0.0.0`. If `package.json` cannot be read, require the user to provide the current version explicitly. + +### 3. Generate Changelog + +Use the [update-changelog](../update-changelog/SKILL.md) skill to generate the changelog entry for this release. Invoke it with `NEW_VERSION` as its version argument and follow its procedure to: + +1. Categorize commits into Keep a Changelog sections (Added, Changed, Fixed, etc.) +2. Build and show the proposed diff +3. After explicit user confirmation, write the entry to `CHANGELOG.md` + +### 4. Create Git Tag + +```bash +# Create annotated tag +git tag -a "v$NEW_VERSION" -m "Release v$NEW_VERSION" +``` + +### 5. Push Tag to Remote + +```bash +# Push tag to origin +git push origin "v$NEW_VERSION" +``` + +### 6. Create GitHub Release + +Use the GitHub CLI to create the release. Extract only the newly generated version section into a temporary notes file: + +```bash +release_notes_file="$(mktemp)" +awk -v section="## [$NEW_VERSION]" ' + $0 == section || index($0, section " -") == 1 { in_section=1; next } + in_section && $0 ~ /^## / { exit } + in_section { print } +' CHANGELOG.md >"$release_notes_file" +test -s "$release_notes_file" + +gh release create "v$NEW_VERSION" \ + --title "Release v$NEW_VERSION" \ + --notes-file "$release_notes_file" + +rm -f "$release_notes_file" +``` + +The temporary file is created from `CHANGELOG.md` after the changelog confirmation step; never point cleanup at `CHANGELOG.md` itself. + +### 7. Verify Release + +```bash +# Confirm the release was created +gh release view "v$NEW_VERSION" +``` + +## Completion Checklist + +After executing the skill: +- [ ] Version was correctly determined from commit history +- [ ] Changelog includes all relevant commits since last release +- [ ] Git tag was created and pushed to remote +- [ ] GitHub release exists with proper title and notes +- [ ] Release is marked as latest (unless specified otherwise) + +## Error Handling + +- **No commits found**: If no changes since last tag, ask user to confirm if they want to proceed +- **Tag already exists**: Offer to update the existing release or choose a different version +- **Push failed**: Check for remote permissions or network issues +- **GitHub CLI not installed**: Provide installation instructions: `brew install gh` or see https://cli.github.com/ + +## Advanced Options + +For advanced users, the skill can support: +- Pre-release versions (e.g., `v1.0.0-beta.1`) +- Draft releases (not immediately published) +- Custom release notes beyond auto-generated changelog +- Attaching binary assets to the release + +## Example Usage + +``` +/create-release +``` + +The skill will: +1. Analyze commits since last tag +2. Determine next semantic version +3. Generate changelog +4. Create and push git tag +5. Create GitHub release with notes +6. Provide confirmation with release URL diff --git a/.claude/skills/patch-findings/SKILL.md b/.claude/skills/patch-findings/SKILL.md new file mode 100644 index 0000000..b46fbf8 --- /dev/null +++ b/.claude/skills/patch-findings/SKILL.md @@ -0,0 +1,54 @@ +--- +name: patch-findings +description: 'Generate fix patches from pre-identified findings. Use when: generating code fixes from PR review comments, producing patches from scan results, translating described problems into code changes. DO NOT USE FOR: resolving GitHub issues (use resolve-issue); creating PRs (use create-pr); managing branches.' +argument-hint: 'describe the finding to patch' +disable-model-invocation: true +--- + +# Patch Findings + +Generate targeted fix patches from pre-identified findings. This skill handles the code-level fix generation — it does NOT manage issues, branches, or PRs. + +## Scope + +| This skill (patch-findings) | Use resolve-issue instead | +|---|---| +| Fix from a PR review comment | Fix from a GitHub issue | +| Fix from a scan/audit finding | Full issue-to-PR workflow | +| Fix from a described problem | Need issue tracking, branching, PR creation | + +## When to Use + +- A PR review comment identifies a problem that needs a code fix +- A code scan or audit has flagged specific issues +- The user describes a problem and wants a targeted patch + +## Procedure + +### 1. Understand the Finding + +Clarify what needs fixing: +- Read the affected file(s) and surrounding code +- Identify the root cause +- Determine the minimal change needed +- Check for related code that may need the same fix + +### 2. Generate the Patch + +Make the minimal, focused change: +- Fix only what's needed — no drive-by refactors +- Follow existing project conventions and patterns +- Add or update tests if the project has test coverage + +### 3. Validate + +- Run linting and type checks +- Run relevant tests +- Review the diff for correctness and regressions + +### 4. Present + +Show the user: +- What was fixed and why +- The diff for review +- Any follow-up actions (branching, committing, PR creation are outside this skill) diff --git a/.claude/skills/resolve-issue/SKILL.md b/.claude/skills/resolve-issue/SKILL.md new file mode 100644 index 0000000..ae1ec2b --- /dev/null +++ b/.claude/skills/resolve-issue/SKILL.md @@ -0,0 +1,118 @@ +--- +name: resolve-issue +description: "**WORKFLOW SKILL** — Systematically resolve GitHub issues from assignment through PR creation. USE FOR: working on assigned issues; following structured debugging workflows; implementing feature requests; fixing bugs reported in issues; creating focused PRs linked to issues. DO NOT USE FOR: creating new issues (use create-issue); reviewing PRs (use review-pr); general coding tasks without an issue context." +user-invocable: true +disable-model-invocation: true +argument-hint: '[issue-number]' +--- + +# Resolve Issue Workflow + +## Overview + +This skill provides a structured approach to resolving GitHub issues: from understanding the problem through implementation to creating a linked pull request. + +## Core Workflow + +### 1. Understand the Issue + +- **Auto-fetch issue details**: Use `issue_read` to get the issue title, body, labels, assignees, and linked PRs +- **Analyze labels**: Identify issue type from labels (e.g., `bug`, `enhancement`, `feature`) to determine approach and branch naming +- **Clarify scope**: Identify what's requested vs. what's needed +- **Identify constraints**: Check for performance, compatibility, or design requirements +- **Review related discussions**: Use `issue_read` with `get_comments` to gather additional context or decisions + +### 2. Plan the Solution + +- **Break down into tasks**: Identify specific code changes needed +- **Assess complexity**: Determine if this is a simple fix or requires architectural changes +- **Consider edge cases**: Think about error handling and boundary conditions +- **Choose approach**: Decide between multiple possible solutions if applicable + +### 3. Implement Changes + +- **Create a branch**: Auto-detect naming convention from issue labels: + - `bug` labels → `fix/<issue-number>-<short-description>` + - `enhancement`/`feature` labels → `feat/<issue-number>-<short-description>` + - Default → `fix/<issue-number>-<short-description>` +- **Make focused changes**: Keep changes minimal and related to the issue +- **Follow project conventions**: Use existing patterns, coding standards, and architecture +- **Write tests**: Add or update tests to cover the changes + +### 4. Validate and Test + +- **Run existing tests**: Ensure no regressions +- **Test new functionality**: Verify the fix or feature works as expected +- **Check edge cases**: Test boundary conditions and error scenarios +- **Review your changes**: Self-review for quality and completeness + +### 5. Document and Reference + +- **Update documentation**: If behavior changed, update relevant docs +- **Reference the issue**: Use `Closes #123` or `Fixes #123` in commit messages +- **Write clear commit messages**: Follow conventional commit standards + +### 6. Create Pull Request + +Suggest creating a PR when implementation is complete. If the user wants to proceed, reference the **create-pr** skill for best practices. + +- **Confirm with user**: Ask if they want to create the PR now +- **Link to the issue**: Ensure the PR references the issue it resolves (e.g., "Closes #123") +- **Write descriptive PR title and description**: Explain what changed and why +- **Request review**: Assign appropriate reviewers +- **Respond to feedback**: Address review comments promptly + +## Deep-Dive Sections + +### Debugging Complex Issues + +For issues requiring investigation: + +1. **Reproduce the problem**: Create a minimal reproduction case +2. **Add debugging instrumentation**: Log relevant state and parameters +3. **Isolate the cause**: Use binary search or divide-and-conquer approaches +4. **Verify the fix**: Ensure the fix addresses root cause, not just symptoms + +### Feature Implementation + +For feature requests: + +1. **Design the API**: Consider how users will interact with the feature +2. **Plan backward compatibility**: Ensure existing functionality isn't broken +3. **Consider performance**: Evaluate impact on performance and resource usage +4. **Write comprehensive tests**: Cover happy path and error scenarios + +### Refactoring for Issues + +When the issue reveals code quality problems: + +1. **Separate refactoring from fixes**: Keep refactoring changes separate +2. **Ensure behavior preservation**: Refactoring shouldn't change functionality +3. **Update tests if needed**: Tests should still pass after refactoring + +## Quality Checklist + +Before submitting a PR, verify: + +- [ ] Issue is clearly understood and scoped +- [ ] Solution addresses the root cause +- [ ] Changes are minimal and focused +- [ ] Tests cover new or changed functionality +- [ ] No regressions introduced +- [ ] Code follows project conventions +- [ ] Documentation updated if needed +- [ ] Commit messages are clear and reference the issue + +## Example Prompts + +- "Resolve issue #123 by implementing the requested feature" +- "Fix the bug described in issue #456" +- "Work on issue #789 following the resolve-issue workflow" +- "Help me understand and fix issue #101" + +## Related Skills + +- **create-issue**: For creating new issues +- **commit**: For writing proper commit messages +- **create-pr**: For creating pull requests +- **review-pr**: For reviewing pull requests \ No newline at end of file diff --git a/.claude/skills/review-changes/SKILL.md b/.claude/skills/review-changes/SKILL.md new file mode 100644 index 0000000..ab25052 --- /dev/null +++ b/.claude/skills/review-changes/SKILL.md @@ -0,0 +1,72 @@ +--- +name: review-changes +description: 'Review staged and unstaged changes before committing. Use for: code review, pre-commit checks, validating changes, checking diff quality, linting, test verification, security scan. Trigger phrases: review changes, review my changes, check changes, pre-commit review, review diff.' +user-invocable: true +argument-hint: '[optional: file path, branch, --staged, or --unstaged]' +--- + +# Review Changes + +Systematic pre-commit code review checklist for staged and unstaged changes. + +## When to Use +- Before committing code to verify quality +- When asked to review local changes +- To validate a diff before creating a PR +- After making multiple edits to ensure nothing was missed + +## Procedure + +### 1. Identify Changes and Scope + +Honor the optional `argument-hint`: + +- **No argument**: review staged and unstaged changes plus all untracked files. +- **`--staged`**: review only staged changes with `git diff --staged`. +- **`--unstaged`**: review only unstaged changes with `git diff`. +- **Existing path**: review only that path. Use path-limited diffs (`git diff --staged -- "$path"` and `git diff -- "$path"`) and `git status --short -- "$path"`. For an untracked path, read its contents directly before reviewing it; `git diff` does not include untracked content. +- **Existing local branch**: review that branch's changes from its merge base with the current branch. Verify it with `git show-ref --verify --quiet "refs/heads/$branch"`, then use `base="$(git merge-base HEAD "$branch")"` and `git diff "$base..$branch"`. +- **Anything else**: report that the scope argument is neither an existing path nor a local branch and stop rather than silently reviewing a different scope. + +Summarize the selected scope: which files changed and what the changes accomplish. + +### 2. Correctness Check +For each changed file, verify: +- [ ] Logic is correct — no off-by-one errors, null/undefined gaps, or broken control flow +- [ ] Edge cases are handled — empty inputs, error states, boundary conditions +- [ ] Return values and types are consistent with existing patterns +- [ ] No leftover debug code (`console.log`, `print()`, `TODO`, `FIXME`, `HACK`) +- [ ] No accidental deletions or commented-out code blocks + +### 3. Style & Consistency +- [ ] Code follows existing project conventions (naming, formatting, patterns) +- [ ] Imports are organized and unused imports are removed +- [ ] Error messages are descriptive and actionable +- [ ] Function and variable names clearly convey purpose + +### 4. Security Scan +- [ ] No hardcoded secrets, API keys, tokens, or credentials +- [ ] No unsanitized user input used in queries, commands, or HTML rendering +- [ ] Authentication/authorization logic is not weakened +- [ ] Sensitive data is not logged + +### 5. Tests +- [ ] New or changed behavior has corresponding tests +- [ ] Existing tests still pass (run the test suite) +- [ ] Test names and assertions clearly describe expected behavior + +### 6. Documentation +- [ ] Public API changes include updated docstrings/comments +- [ ] Breaking changes are noted +- [ ] README or config changes are included if dependencies or setup changed + +### 7. Final Summary +After completing the checklist, provide a concise summary: +- **Verdict**: Ready to commit / Needs changes / Needs discussion +- **Issues found**: List any problems with severity (blocking / warning / suggestion) +- **Positive notes**: What was done well + +## Example Prompts +- `/review-changes` — review all staged and unstaged changes +- `/review-changes src/api/handler.ts` — review changes in a specific file +- `/review-changes --staged` — review only staged changes diff --git a/.claude/skills/review-pr/SKILL.md b/.claude/skills/review-pr/SKILL.md new file mode 100644 index 0000000..9c1530c --- /dev/null +++ b/.claude/skills/review-pr/SKILL.md @@ -0,0 +1,92 @@ +--- +name: review-pr +description: 'Review a pull request for quality, issues, and improvements. Use when the user asks to review a PR, check a pull request, do a code review, or assess PR quality. Triggers: review pr, pull request review, code review, check pr.' +user-invocable: true +argument-hint: '<owner/repo#number> or leave blank to detect from context' +--- + +# Pull Request Review + +Comprehensive PR review that summarizes changes, identifies potential issues, and suggests improvements. + +## Procedure + +### 1. Identify the PR + +- If the user provided a reference like `owner/repo#123`, parse it into a repository (`owner/repo`) and pull request number. +- Otherwise, check for an active branch or recent PR in the workspace. +- Use the GitHub integration configured for the environment to find the PR when available; do not assume a connector-specific MCP tool name. +- When no GitHub integration is available and the reference was left blank, derive the current PR's repository and number before Step 2 with `repo="$(gh repo view --json nameWithOwner -q .nameWithOwner)"` and `number="$(gh pr view --json number -q .number)"`. +- When no GitHub integration is available, use the `gh` CLI fallback described below. + +### 2. Gather PR Details + +Fetch the PR metadata, changed-file list, unified diff, and file contents before reviewing code: + +- Use the configured GitHub integration when available to get the PR metadata, changed-file list, unified diff, and head revision. +- Otherwise, use the `gh` CLI commands in Step 5: `gh pr view "$number" -R "$repo"`, `gh pr diff "$number" -R "$repo"`, and the GitHub API fallback for file contents. +- Note the base and head branches and head revision to understand the diff scope. + +### 3. Analyze Changes + +Examine the PR systematically: + +**a. Understand the intent** +- Read the PR title and description for what problem it solves. +- Check linked issues if any. + +**b. Review changed files** +- Look at the diff for each changed file. +- Focus on logic changes, not just formatting. + +**c. Evaluate quality across these dimensions:** + +| Dimension | What to check | +|-----------|---------------| +| **Correctness** | Logic errors, off-by-one, null handling, race conditions | +| **Security** | Injection risks, secret leaks, auth bypasses, input validation | +| **Performance** | N+1 queries, unnecessary re-renders, memory leaks, missing indexes | +| **Maintainability** | Naming, duplication, complexity, missing documentation | +| **Testing** | Adequate coverage, edge cases, test quality | +| **API Design** | Consistency, backward compatibility, clear interfaces | + +### 4. Compile the Review + +Present findings in this structure: + +#### Summary +One paragraph explaining what the PR does and whether it achieves its goal. + +#### Strengths +What the PR does well (call out good patterns, thorough tests, clear docs). + +#### Issues Found +Categorized by severity: + +**🔴 Critical** — Must be fixed before merge (bugs, security, data loss) +**🟡 Suggestions** — Should be considered (performance, maintainability) +**🟢 Nitpicks** — Optional improvements (style, naming) + +For each issue: +- **File and location** — Where the issue is +- **Description** — What the problem is +- **Suggestion** — How to fix it (with code example if helpful) + +#### Testing Assessment +Whether the test coverage is adequate and what additional tests might be needed. + +#### Verdict +One of: +- **Approve** — Good to merge +- **Approve with suggestions** — Mergeable but consider the suggestions +- **Request changes** — Critical issues must be resolved first + +### 5. Optional: Submit the Review + +If a configured GitHub integration is available, use it to submit the review with the requested event (`APPROVE`, `REQUEST_CHANGES`, or `COMMENT`). Otherwise use the `gh` CLI fallback below. + +If GitHub MCP tools are unavailable, use `gh` CLI. When the user supplied a reference, parse `owner/repo#123` into `repo="$owner/$repo_name"` and `number="123"`. When the PR was detected from the workspace instead, derive `repo="$(gh repo view --json nameWithOwner -q .nameWithOwner)"` and `number="$(gh pr view --json number -q .number)"`. Then run `gh pr view "$number" -R "$repo" --json title,body,state,labels,baseRefName,headRefName,headRefOid,files` for PR metadata and the changed-file list. +- Use `gh pr diff "$number" -R "$repo"` for the unified diff. +- Extract the head revision with `head_sha="$(gh pr view "$number" -R "$repo" --json headRefOid -q .headRefOid)"` before fetching changed files. +- For each changed file with status other than `removed`, URL-encode each path segment before interpolation (spaces, `#`, `?`, and `%`). For example, set `encoded_path="$(python3 -c 'import sys; from urllib.parse import quote; print("/".join(quote(part, safe="") for part in sys.argv[1].split("/")))' "$path")"` and fetch it at the PR head with `gh api "repos/$repo/contents/$encoded_path?ref=$head_sha" -H "Accept: application/vnd.github.raw+json"`. A removed path is already represented by the diff and should be skipped. For a blob that exceeds the contents API size cap, use the Git Data blobs API with its blob SHA instead. +- For review submission, write the review body to a temporary file and use `gh pr review "$number" -R "$repo" --approve --body-file "$review_file"`, `--request-changes`, or `--comment` as appropriate. Never interpolate review text into shell command source. diff --git a/.claude/skills/scan-features/SKILL.md b/.claude/skills/scan-features/SKILL.md new file mode 100644 index 0000000..b5ae5a5 --- /dev/null +++ b/.claude/skills/scan-features/SKILL.md @@ -0,0 +1,164 @@ +--- +name: scan-features +description: 'Deeply scan and analyze the codebase for potential new features, then open GitHub issues to submit them. USE FOR: discovering missing functionality, finding unimplemented ideas, identifying feature gaps, suggesting enhancements, finding TODOs and stubs that indicate planned work. Triggers: scan features, find feature gaps, suggest new features, discover missing functionality, feature audit, what features are missing.' +argument-hint: '[optional: focus area like api, ui, cli, performance, or all]' +user-invocable: true +--- + +# Scan Features + +## When to Use + +- Discover missing or incomplete features in the codebase +- Identify TODO/FIXME/HACK comments that indicate planned but unimplemented work +- Find stub functions, placeholder implementations, or skeleton code +- Detect patterns where a feature exists in one place but is missing in another +- Spot configuration options, CLI flags, or API endpoints that are referenced but not implemented +- Audit documentation or specs against actual implementation to find gaps +- Suggest enhancements based on common best practices for the tech stack + +## Procedure + +### 1. Understand the Codebase + +- Read `package.json`, `tsconfig.json`, or equivalent project config to understand the tech stack +- Identify the main source directories, entry points, and module structure +- Note the project's purpose and domain from README or docs +- Review any existing specs, RFCs, or design documents + +### 2. Define Scan Scope + +Determine what to scan based on the user's request or codebase context: + +| Focus Area | What to Look For | +|-----------|------------------| +| **api** | Missing endpoints, incomplete request handlers, stub route definitions, referenced but unimplemented API calls | +| **ui** | Placeholder components, commented-out UI sections, missing loading/error states, TODO labels in templates | +| **cli** | Unimplemented commands, missing flags, incomplete argument parsing, help text for features not yet built | +| **performance** | Missing caching layers, absent pagination, no lazy loading, absent rate limiting or throttling | +| **testing** | Untested modules, skipped test files, test stubs with no implementation | +| **all** | Comprehensive scan across all categories above plus the cross-cutting indicators below | + +### 3. Scan for Feature Indicators + +Work through the source files systematically. For each file, look for these signals: + +#### Explicit Indicators (High Confidence) +- **TODO/FIXME/HACK/XXX comments** — Direct evidence of planned work +- **Stub functions** — Functions with empty bodies, `NotImplementedError`, or placeholder returns +- **Commented-out code** — Previously working code that was disabled, suggesting incomplete refactoring +- **`throw new Error("Not implemented")`** — Explicit markers of unbuilt functionality + +#### Pattern-Based Indicators (Medium Confidence) +- **Missing error handling** — Try blocks without catch, unhandled promise rejections +- **Absent validation** — Input handlers without parameter validation +- **Incomplete CRUD** — Some operations implemented (GET, POST) but others missing (PUT, DELETE) +- **Feature parity gaps** — Similar modules where one has a capability the other lacks +- **Configuration without implementation** — Config keys defined but never read or used + +#### Spec-Based Indicators (Requires Docs) +- **Documentation gaps** — README or docs describe features not found in source +- **Unused imports or dependencies** — Packages installed but not used, suggesting planned features +- **Type definitions without implementations** — Interfaces or types defined but never instantiated + +### 4. Analyze and Classify + +For each finding: + +1. **Identify** the feature opportunity (what could be built) +2. **Classify** by confidence level and effort estimate +3. **Assess** the impact (user value, technical debt reduction, completeness) + +| Confidence | Source | Action | +|-----------|--------|--------| +| **High** | Explicit TODOs, stubs, commented-out code | Strong candidate for an issue | +| **Medium** | Pattern gaps, missing CRUD, parity issues | Worth investigating further | +| **Low** | Spec gaps, unused deps, best-practice suggestions | Suggest only if clearly valuable | + +**Effort Estimate**: +- **S** — Small (< 1 day): Fix a stub, add a missing validation, implement a simple handler +- **M** — Medium (1–3 days): Add a new endpoint, implement a missing CRUD operation +- **L** — Large (3+ days): New subsystem, major refactoring, new architectural layer + +### 5. Deduplicate and Prioritize + +- Merge duplicate or overlapping findings +- Group related features that could be bundled into a single issue +- Rank by impact × confidence: + - **Immediate**: High confidence + High impact + - **Planned**: High confidence + Low impact, or Medium confidence + High impact + - **Backlog**: Medium confidence + Low impact + - **Skip**: Low confidence + Low impact + +### 6. Create GitHub Issues + +For each prioritized finding (or group of related findings), create a GitHub issue using the GitHub MCP tools: + +1. **Title**: Use format `[Feature] Short description` (e.g., `[Feature] Add retry logic for external API calls`) +2. **Labels**: Apply appropriate labels based on focus area and effort +3. **Body**: Include: + - **Description**: What feature is missing and why it would be valuable + - **Location**: Exact file path(s) and line number(s) with the indicator + - **Evidence**: The TODO comment, stub function, or pattern gap + - **Proposed solution**: How the feature could be implemented + - **Effort estimate**: S / M / L + - **Impact**: What improves if this is built + +If GitHub MCP tools are unavailable, fall back to `gh` CLI for each issue. Populate the generated title and body in quoted shell variables and append each confirmed label before invoking `gh`. + +```bash +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +labels=() +# Append each confirmed label, for example: labels+=("bug") +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +args=(issue create --title "$title" --body-file "$body_file") +for label in "${labels[@]}"; do + args+=(--label "$label") +done + +if ! gh "${args[@]}"; then + echo "Failed to create the GitHub issue." >&2 + exit 1 +fi +``` + +Never interpolate generated text directly into shell command source. + +### 7. Summary Report + +After scanning, provide the user with a summary: + +``` +## Feature Scan Results + +| Category | High | Medium | Low | Total | +|----------|------|--------|-----|-------| +| API | X | X | X | X | +| UI | X | X | X | X | +| CLI | X | X | X | X | +| Performance | X | X | X | X | +| Testing | X | X | X | X | +| Cross-cutting | X | X | X | X | +| **Total** | **X** | **X** | **X** | **X** | + +### Top Recommendations + +1. **[Feature]** Short description — [Effort: S/M/L] — [Impact: High/Med/Low] + - Location: `path/to/file.ts:42` + - Evidence: `// TODO: implement retry logic` + +Issues created: [list links to created issues] +``` + +## Guidelines + +- **Be specific**: Every finding must reference an exact file, line number, and the specific indicator +- **Be actionable**: Include a proposed solution, not just a vague suggestion +- **Avoid overreach**: Don't invent features the project doesn't need — focus on evidence in the code +- **Respect project scope**: Only suggest features that align with the project's domain and goals +- **Don't overwhelm**: Group minor, related features into a single issue rather than creating many small ones +- **Distinguish tech debt from features**: TODOs about refactoring are tech debt, not features — label them accordingly diff --git a/.claude/skills/scan-issues/SKILL.md b/.claude/skills/scan-issues/SKILL.md new file mode 100644 index 0000000..d278737 --- /dev/null +++ b/.claude/skills/scan-issues/SKILL.md @@ -0,0 +1,121 @@ +--- +name: scan-issues +description: 'Deeply scan and analyze issues in the codebase, then open GitHub issues to submit them. USE FOR: finding code smells, bugs, performance problems, security vulnerabilities, dead code, missing error handling, or other potential issues. Triggers: scan issues, find bugs, code review, detect problems, audit codebase, check for issues.' +argument-hint: '[optional: focus area like security, performance, or all]' +user-invocable: true +--- + +# Scan Issues + +## When to Use + +- Find potential bugs, code smells, or logic errors in the codebase +- Detect performance bottlenecks or inefficient patterns +- Identify security vulnerabilities or unsafe code +- Locate missing error handling, edge cases, or null safety issues +- Find dead code, unused imports, or deprecated patterns +- Audit codebase quality before a release or PR + +## Procedure + +### 1. Understand the Codebase + +- Read `package.json`, `tsconfig.json`, or equivalent project config to understand the tech stack +- Identify the main source directories and entry points +- Note any existing linting, testing, or CI configuration + +### 2. Define Scan Scope + +Determine what to scan based on the user's request or codebase context: + +| Focus Area | What to Look For | +|-----------|------------------| +| **security** | Hardcoded secrets, SQL injection, XSS, insecure dependencies, unsafe eval | +| **performance** | N+1 queries, unnecessary re-renders, memory leaks, large bundle imports | +| **reliability** | Missing null checks, unhandled promises, race conditions, uncaught exceptions | +| **maintainability** | Dead code, duplicated logic, overly complex functions, missing types | +| **all** | Comprehensive scan across all categories above | + +### 3. Scan the Codebase + +Work through the source files systematically. For each file: + +1. **Read** the file content +2. **Analyze** for issues in the target focus area +3. **Classify** each finding by severity and category +4. **Document** the issue with file path, line number, description, and suggested fix + +Use a subagent for large codebases to parallelize scanning across directories. + +### 4. Deduplicate and Prioritize + +- Merge duplicate or overlapping findings +- Rank issues by severity: + - **Critical**: Security vulnerabilities, data loss risks, crashes + - **High**: Bugs that affect functionality, performance regressions + - **Medium**: Code smells, maintainability concerns, missing best practices + - **Low**: Minor style issues, suggestions, nitpicks +- Group related issues that can be addressed together + +### 5. Create GitHub Issues + +For each prioritized issue (or group of related issues), create a GitHub issue using the GitHub MCP tools: + +1. **Title**: Use format `[Category] Short description` (e.g., `[Security] Hardcoded API key in config.ts`) +2. **Labels**: Apply appropriate labels based on severity and category +3. **Body**: Include: + - **Description**: What the issue is and why it matters + - **Location**: Exact file path and line number(s) + - **Code snippet**: The problematic code + - **Suggested fix**: How to resolve the issue + - **Impact**: What could happen if left unaddressed + +If GitHub MCP tools are unavailable, fall back to `gh` CLI for each issue. Populate the generated title and body in quoted shell variables and append each confirmed label before invoking `gh`. + +```bash +title="$GENERATED_TITLE" +body="$GENERATED_BODY" +labels=() +# Append each confirmed label, for example: labels+=("bug") +body_file="$(mktemp)" +trap 'rm -f "$body_file"' EXIT +printf '%s\n' "$body" >"$body_file" + +args=(issue create --title "$title" --body-file "$body_file") +for label in "${labels[@]}"; do + args+=(--label "$label") +done + +if ! gh "${args[@]}"; then + echo "Failed to create the GitHub issue." >&2 + exit 1 +fi +``` + +Never interpolate generated text directly into shell command source. + +### 6. Summary Report + +After all issues are created, provide the user with a summary: + +``` +## Scan Results + +| Category | Critical | High | Medium | Low | Total | +|----------|----------|------|--------|-----|-------| +| Security | X | X | X | X | X | +| Performance | X | X | X | X | X | +| Reliability | X | X | X | X | X | +| Maintainability | X | X | X | X | X | +| **Total** | **X** | **X** | **X** | **X** | **X** | + +Issues created: [list links to created issues] +``` + +## Guidelines + +- **Be specific**: Every issue must reference an exact file and line number +- **Be actionable**: Include a suggested fix, not just a complaint +- **Avoid false positives**: Only report issues you are confident about +- **Respect project conventions**: Don't flag patterns the project intentionally uses +- **Don't overwhelm**: Group minor issues together into a single issue rather than creating many tiny ones diff --git a/.claude/skills/update-changelog/SKILL.md b/.claude/skills/update-changelog/SKILL.md new file mode 100644 index 0000000..b35ebc6 --- /dev/null +++ b/.claude/skills/update-changelog/SKILL.md @@ -0,0 +1,98 @@ +--- +name: update-changelog +description: 'Generate or update a changelog entry from git commit history. Use when: writing changelogs, generating release notes, summarizing changes between versions, creating CHANGELOG.md entries, preparing releases.' +user-invocable: true +argument-hint: '[version] (e.g. 1.2.0, unreleased)' +--- + +# Update Changelog + +Generate a [Keep a Changelog](https://keepachangelog.com/) formatted entry from recent git history. + +## Procedure + +### 1. Determine the Version + +- If the user provided a version argument, use it. +- If no version is provided, ask the user: "What version should this entry be labeled as? (e.g., `1.2.0` or `Unreleased`)" + +### 2. Determine the Commit Range + +Find the starting point for the changelog entry: + +- Check if a CHANGELOG.md exists. If it does, scan it for the most recent version header to understand the existing format. +- Find the latest git tag with `git describe --tags --abbrev=0`. +- If the user specifies a starting point (commit, tag, or date), set `start` to that value. +- Otherwise, if a tag exists, set `start` to the latest tag. +- Confirm the selected range with the user before categorizing it. + +Set `start` to the latest tag or user-specified starting point. If `start` is set, run `git log "$start"..HEAD --oneline`; only when no tag exists and no starting point was given, run `git log --oneline --no-merges HEAD`. + +### 3. Categorize Commits + +Map commits into Keep a Changelog sections using commit message prefixes and content: + +| Section | Conventional Prefix | Fallback Keywords | +|---------|--------------------|--------------------| +| **Added** | `feat`, `add`, `new` | new file, new feature, implement | +| **Changed** | `refactor`, `update`, `change`, `improve`, `perf`, `style`, `build` | update, modify, enhance, migrate, upgrade | +| **Deprecated** | `deprecate` | deprecated, will be removed | +| **Removed** | `remove` | remove, delete, drop | +| **Fixed** | `fix`, `bugfix` | bug, fix, patch, resolve, handle error | +| **Security** | `security`, `cve` | vulnerability, CVE, security fix | + +- Skip commits that are clearly automated: merge commits, version bumps (`bump version`, `chore: release`), CI config only. +- If a commit doesn't fit any section, skip it and note it to the user. + +### 4. Format the Entry + +Output a markdown block like this: + +```markdown +## [Unreleased] + +### Added +- Feature description (`abc1234`) + +### Fixed +- Bug fix description (`def5678`) + +### Changed +- Change description (`ghi9012`) +``` + +Rules: +- Use short commit hashes in backticks at the end of each entry. +- If the commit message is clear and concise, adapt it as the entry description. +- If the commit message is unclear, read the actual diff (`git show <hash> --stat`) to write a better description. +- Avoid internal jargon — write for end users. +- Use the imperative mood ("Add support for…" not "Added support for…" or "Adds…"). + +### 5. Write or Append to CHANGELOG.md + +Build the proposed change before modifying the file: + +- If `CHANGELOG.md` exists, construct the new entry in memory and prepare a unified diff against the current file. Insert the entry after the `# Changelog` header (or after any "Keep a Changelog" preamble) and avoid duplicates. +- If `CHANGELOG.md` does not exist, construct the complete file in memory using the standard preamble: + +```markdown +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/). +``` + +- Show the proposed diff to the user and request explicit confirmation. +- Only after the user confirms, write or append the entry to `CHANGELOG.md`. Do not modify the file before confirmation. + +### 6. Finalize + +- Print a summary: how many commits were categorized, how many were skipped. +- If the version is `Unreleased`, remind the user to update it to a real version before tagging a release. + +## Tips + +- For projects using conventional commits, the categorization is nearly automatic. +- For projects without conventional commits, rely more heavily on `git show` and manual interpretation. +- If the repo has a `package.json` or `Cargo.toml`, you can read the current version from there to suggest a label. diff --git a/.github/workflows/check-skill-sync.yml b/.github/workflows/check-skill-sync.yml new file mode 100644 index 0000000..d36ebaf --- /dev/null +++ b/.github/workflows/check-skill-sync.yml @@ -0,0 +1,46 @@ +name: Check skill mirrors + +on: + pull_request: + paths: + - ".agents/skills/**" + - ".claude/skills/**" + - "scripts/sync-claude-skills.sh" + - ".github/workflows/check-skill-sync.yml" + push: + paths: + - ".agents/skills/**" + - ".claude/skills/**" + - "scripts/sync-claude-skills.sh" + - ".github/workflows/check-skill-sync.yml" + +jobs: + skill-sync: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4 + + - name: Regenerate Claude skill mirror + run: bash scripts/sync-claude-skills.sh + + - name: Check for drift + run: | + set -euo pipefail + status=0 + + if ! git diff --quiet -- .claude/skills; then + echo "Tracked drift detected in .claude/skills:" >&2 + git diff -- .claude/skills + status=1 + fi + + changes="$(git status --porcelain=v1 --untracked-files=all -- .claude/skills)" + if [[ -n "$changes" ]]; then + echo "Untracked drift detected in .claude/skills:" >&2 + printf "%s\n" "$changes" >&2 + status=1 + fi + + exit "$status" diff --git a/AGENTS.md b/AGENTS.md index e64505e..634ffbb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -102,7 +102,7 @@ The `example` provider is a no-op provider useful for testing without an API key ## Skills -The project includes reusable agent skills in `.agents/skills/` for common GitHub workflows: +The project includes reusable agent skills in `.agents/skills/` for common GitHub workflows. `.claude/skills/` is a generated mirror; after changing a source skill, run `bash scripts/sync-claude-skills.sh` and commit the regenerated mirror. | Skill | Description | |---|---| diff --git a/scripts/sync-claude-skills.sh b/scripts/sync-claude-skills.sh new file mode 100644 index 0000000..9f0cfa3 --- /dev/null +++ b/scripts/sync-claude-skills.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -euo pipefail + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +SOURCE_DIR="$ROOT_DIR/.agents/skills" +TARGET_DIR="$ROOT_DIR/.claude/skills" + +# .agents/skills is the canonical source; .claude/skills is the generated mirror. +if [[ ! -d "$SOURCE_DIR" ]] || [[ -z "$(find "$SOURCE_DIR" -maxdepth 2 -name SKILL.md -print -quit)" ]]; then + echo "error: source skill directory not found or contains no SKILL.md files: $SOURCE_DIR" >&2 + exit 1 +fi + +rm -rf "$TARGET_DIR" +mkdir -p "$TARGET_DIR" +cp -R "$SOURCE_DIR/." "$TARGET_DIR/"