diff --git a/.claude/skills/create-branch/SKILL.md b/.claude/skills/create-branch/SKILL.md new file mode 100644 index 00000000..192f150d --- /dev/null +++ b/.claude/skills/create-branch/SKILL.md @@ -0,0 +1,48 @@ +--- +name: create-branch +description: Create a feature branch following project naming conventions. Use when starting work on a ticket, after understanding the scope, or when the agent needs to branch off main for new work. +--- + +# Create Branch + +Create a feature branch following the `/` +convention used in this repository. + +## Steps + +### 1. Detect the user's nickname + +Resolve the nickname using the first approach that succeeds: + +1. **Memory** — check if a stored memory already contains the user's nickname. +2. **`make nickname`** — outputs the local part of `git config user.email` (before `@`). +3. **Ask** — if neither works, ask the user. + +Once resolved, save the nickname to memory for future conversations. + +### 2. Derive a descriptive branch name + +Combine the ticket ID (if available) and a short slug describing the change: + +- Use lowercase kebab-case: `fix-auth-timeout`, `add-mcp-tool-list` +- Keep it under 50 characters +- If a YouTrack ticket is known (e.g., `DBA-123`), prefer including it: + `/DBA-123-fix-auth-timeout` + +### 3. Ensure a clean starting point + +- Fetch latest: `git fetch origin` +- If the working tree has uncommitted changes, warn the user and ask whether + to stash or proceed. +- Branch from `main` (or the base branch the user specifies): + `git checkout -b / origin/main` + +### 4. Confirm + +Report the created branch name to the user. + +## Guardrails + +- Never create branches directly on `main` — always branch _from_ main. +- Never silently discard uncommitted changes. +- If the user is already on a feature branch, ask before switching. diff --git a/.claude/skills/create-branch/evals/evals.json b/.claude/skills/create-branch/evals/evals.json new file mode 100644 index 00000000..c71d53ce --- /dev/null +++ b/.claude/skills/create-branch/evals/evals.json @@ -0,0 +1,38 @@ +{ + "skill_name": "create-branch", + "evals": [ + { + "id": 1, + "prompt": "Create a branch for ticket DBA-150 about fixing the status command output.", + "expected_output": "Agent detects nickname from remotes, creates a branch like /DBA-150-fix-status-output from origin/main.", + "assertions": [ + "Agent resolves the nickname from memory or git email", + "Agent fetches from origin before creating the branch", + "Branch name follows `/` pattern", + "Branch is created from origin/main, not from the current HEAD of an unrelated branch", + "Agent reports the created branch name to the user" + ] + }, + { + "id": 2, + "prompt": "I need a new branch for some exploratory work, no ticket yet.", + "expected_output": "Agent creates a branch with a descriptive name without a ticket prefix.", + "assertions": [ + "Agent resolves the nickname from memory or git email", + "Agent asks or derives a descriptive slug for the branch", + "Branch name does NOT include a ticket ID since none was provided", + "Agent does NOT require a ticket to proceed" + ] + }, + { + "id": 3, + "prompt": "Create a branch for DBA-200. I have uncommitted changes in my working tree.", + "expected_output": "Agent warns about uncommitted changes and asks user how to proceed before branching.", + "assertions": [ + "Agent detects or warns about uncommitted changes", + "Agent asks the user whether to stash, commit, or abort before switching branches", + "Agent does NOT silently discard uncommitted changes" + ] + } + ] +} diff --git a/.claude/skills/create-pr/SKILL.md b/.claude/skills/create-pr/SKILL.md new file mode 100644 index 00000000..d6e12896 --- /dev/null +++ b/.claude/skills/create-pr/SKILL.md @@ -0,0 +1,90 @@ +--- +name: create-pr +description: Stage, commit, push, and open a GitHub PR following project conventions. Use when code is ready to ship — after tests pass, code review, and architecture review are done. +--- + +# Create PR + +Stage changes, commit, push, and open a GitHub pull request following +project conventions. + +## Steps + +### 1. Verify preconditions + +- Confirm you are NOT on `main`. If on `main`, run the `create-branch` + skill to create a feature branch before continuing. +- Check `git status` for changes to commit. If nothing to commit, inform + the user and stop. +### 2. Run quality gates + +These are **blocking** — do not commit until both pass. + +1. **`make check`** (ruff + mypy) + - Run `make check`. + - If ruff fails, auto-fix with `uv run ruff check --fix src/databao_cli && uv run ruff format src/databao_cli`, then re-run `make check`. + - If mypy fails after ruff is clean, stop and fix the type errors before continuing. +2. **`make test`** (pytest) + - Run `make test`. + - If tests fail, stop and fix the failures before continuing. +3. **`make test-cov-check`** (coverage threshold) + - Run `make test-cov-check`. + - Warn the user if coverage is below threshold, but do **not** block the PR. + +### 3. Determine the ticket ID + +- Extract from the current branch name if it contains `DBA-`. +- Otherwise ask the user for the ticket ID. + +### 4. Stage and commit + +- Stage relevant files (prefer explicit paths over `git add -A`). +- Commit following the **Commit Messages** section in `CLAUDE.md`. + +### 5. Pause for confirmation + +Present the user with: + +- The branch name and commit(s) that will be pushed. +- A draft PR description following the template below. + +**Wait for explicit user confirmation before proceeding.** + +### 6. Push and create PR + +- Push with `-u` flag: `git push -u origin ` +- Create the PR via `gh pr create` using the template: + +``` +## Summary +<1-3 sentence overview of why this change exists> + +## Changes + +### + +
Files + +- `path/to/file1` +- `path/to/file2` +
+ +### +... + +## Test Plan +- [ ] +``` + +### 7. Report + +Output the PR URL so the user can review it. + +## Guardrails + +- Never push to `main`. +- Never push without explicit user confirmation. +- Never skip the commit prefix when a ticket is known (see `CLAUDE.md`). +- Never use `git add -A` or `git add .` — stage specific files. +- If `gh` CLI is not available, show the push command and PR URL for + manual creation. diff --git a/.claude/skills/create-pr/evals/evals.json b/.claude/skills/create-pr/evals/evals.json new file mode 100644 index 00000000..b54aec02 --- /dev/null +++ b/.claude/skills/create-pr/evals/evals.json @@ -0,0 +1,38 @@ +{ + "skill_name": "create-pr", + "evals": [ + { + "id": 1, + "prompt": "Create a PR for the changes on my current branch. The ticket is DBA-180.", + "expected_output": "Agent stages changes, commits with [DBA-180] prefix, presents a draft PR, pauses for confirmation, then pushes and creates the PR.", + "assertions": [ + "Agent commits with a message prefixed by [DBA-180]", + "Agent presents a draft PR description to the user before pushing", + "Agent waits for explicit user confirmation before pushing", + "Agent pushes with the -u flag", + "PR description follows the Summary / Changes / Test Plan template", + "Agent reports the PR URL after creation" + ] + }, + { + "id": 2, + "prompt": "Ship my changes as a PR. I'm on a feature branch but I don't remember the ticket number.", + "expected_output": "Agent tries to extract the ticket ID from the branch name; if not found, asks the user.", + "assertions": [ + "Agent attempts to extract a ticket ID from the current branch name", + "If no ticket ID is found in the branch name, agent asks the user", + "Agent does NOT push without a commit message prefix when a ticket is expected" + ] + }, + { + "id": 3, + "prompt": "Create a PR. I'm currently on the main branch.", + "expected_output": "Agent refuses to push directly to main and suggests creating a feature branch first.", + "assertions": [ + "Agent detects that the current branch is main", + "Agent does NOT push to main", + "Agent suggests creating a feature branch or switching to one" + ] + } + ] +} diff --git a/.claude/skills/make-yt-issue/SKILL.md b/.claude/skills/make-yt-issue/SKILL.md new file mode 100644 index 00000000..ac479f41 --- /dev/null +++ b/.claude/skills/make-yt-issue/SKILL.md @@ -0,0 +1,52 @@ +--- +name: make-yt-issue +description: Create a YouTrack issue for planned work using MCP tools. Use when no ticket exists for the work, when the user asks to create a ticket, or before starting untracked work. +--- + +# Make YouTrack Issue + +Create a YouTrack issue in the DBA project using the YouTrack MCP tools. + +## Steps + +### 1. Gather issue details + +Ask the user for (or propose from conversation context): + +- **Summary**: concise one-line title +- **Description**: what the work involves and why +- **Type**: Bug, Task, Feature, or other applicable type + +### 2. Show the proposed issue + +Present the summary, description, and type to the user. +**Wait for explicit approval before creating.** + +### 3. Create the issue + +Use the `create_issue` MCP tool to create the issue in the DBA project. + +### 4. Report + +Output the issue ID (e.g., `DBA-123`) so the user can reference it. + +### 5. Optionally transition to Develop + +If the user is starting work immediately, move the issue to **Develop** +state using `update_issue` (set the `State` field). + +## Guardrails + +- Never create an issue without explicit user approval of the summary, + description, and type. +- If the YouTrack MCP server is unavailable, inform the user and refer + them to `DEVELOPMENT.md` for setup instructions. +- Default to the `DBA` project unless the user specifies otherwise. + +## What this skill does NOT do + +- It does not manage issue state transitions beyond the initial Develop + move — ongoing state management (Develop → Review) is handled by the + development workflow in CLAUDE.md. +- It does not search or update existing issues — use `get_issue` and + `update_issue` MCP tools directly for that. diff --git a/.claude/skills/make-yt-issue/evals/evals.json b/.claude/skills/make-yt-issue/evals/evals.json new file mode 100644 index 00000000..244b0b36 --- /dev/null +++ b/.claude/skills/make-yt-issue/evals/evals.json @@ -0,0 +1,35 @@ +{ + "skill_name": "make-yt-issue", + "evals": [ + { + "id": 1, + "prompt": "Create a YouTrack ticket for adding a new 'config' CLI command that lets users manage project settings.", + "expected_output": "Agent proposes an issue with summary, description, and type, waits for approval, then creates it via MCP.", + "assertions": [ + "Agent proposes a summary, description, and type before creating", + "Agent waits for explicit user approval before calling create_issue", + "Agent reports the created issue ID (e.g., DBA-XXX) after creation" + ] + }, + { + "id": 2, + "prompt": "I want to start working on something but there's no ticket yet. Can you make one?", + "expected_output": "Agent asks for details to populate the issue, then proposes and creates after approval.", + "assertions": [ + "Agent asks the user for issue details (summary, description, or type)", + "Agent does NOT create an issue without gathering sufficient information", + "Agent presents the proposed issue for approval before creating" + ] + }, + { + "id": 3, + "prompt": "Create a YouTrack issue and start working on it right away.", + "expected_output": "Agent creates the issue after approval and moves it to Develop state.", + "assertions": [ + "Agent waits for approval before creating the issue", + "After creation, agent moves the issue to Develop state using update_issue", + "Agent reports both the issue ID and the state transition" + ] + } + ] +} diff --git a/.claude/skills/update-pr/SKILL.md b/.claude/skills/update-pr/SKILL.md new file mode 100644 index 00000000..461e9936 --- /dev/null +++ b/.claude/skills/update-pr/SKILL.md @@ -0,0 +1,52 @@ +--- +name: update-pr +description: Stage, commit, and push follow-up changes to an existing feature branch or PR. Use for quick iterations — after addressing review feedback, fixing a bug on the branch, or adding incremental changes that don't need a new PR. +--- + +# Update PR + +Stage changes, commit, and push to the current feature branch. Designed for +fast iterations on an existing branch or open PR. + +## Steps + +### 1. Verify preconditions + +- Confirm you are NOT on `main`. If on `main`, run the `create-branch` + skill to create a feature branch before continuing. +- Check `git status` for changes to commit. If nothing to commit, inform + the user and stop. + +### 2. Run quality gates + +These are **blocking** — do not commit until both pass. + +1. **`make check`** (ruff + mypy) + - Run `make check`. + - If ruff fails, auto-fix with `uv run ruff check --fix src/databao_cli && uv run ruff format src/databao_cli`, then re-run `make check`. + - If mypy fails after ruff is clean, stop and fix the type errors before continuing. +2. **`make test`** (pytest) + - Run `make test`. + - If tests fail, stop and fix the failures before continuing. + +### 3. Stage and commit + +- Stage relevant files (prefer explicit paths over `git add -A`). +- Extract the ticket ID from the branch name if it contains `DBA-`. +- Commit following the **Commit Messages** section in `CLAUDE.md`. + +### 4. Push + +- Push to the tracked remote branch: `git push`. +- If no upstream is set, push with `-u`: `git push -u origin `. + +### 5. Report + +Confirm the push succeeded and show the commit hash. If a PR exists for +the branch, show the PR URL. + +## Guardrails + +- Never push to `main`. +- Never use `git add -A` or `git add .` — stage specific files. +- Never skip the commit prefix when a ticket is known (see `CLAUDE.md`). diff --git a/.claude/skills/update-pr/evals/evals.json b/.claude/skills/update-pr/evals/evals.json new file mode 100644 index 00000000..1b7da078 --- /dev/null +++ b/.claude/skills/update-pr/evals/evals.json @@ -0,0 +1,37 @@ +{ + "skill_name": "update-pr", + "evals": [ + { + "id": 1, + "prompt": "Push my latest changes to the PR. I'm on branch andrei.gasparian/DBA-200-add-logging.", + "expected_output": "Agent runs quality gates, stages changes, commits with [DBA-200] prefix, and pushes to the existing branch.", + "assertions": [ + "Agent runs make check before committing", + "Agent runs make test before committing", + "Agent commits with a message prefixed by [DBA-200]", + "Agent stages specific files, not git add -A", + "Agent pushes to the current branch" + ] + }, + { + "id": 2, + "prompt": "I fixed the review comments, just push it.", + "expected_output": "Agent stages the changes, commits with ticket prefix from branch name, and pushes.", + "assertions": [ + "Agent runs quality gates before committing", + "Agent extracts ticket ID from branch name if present", + "Agent pushes without creating a new PR" + ] + }, + { + "id": 3, + "prompt": "Update the PR with my changes. I'm on main.", + "expected_output": "Agent detects it is on main and runs the create-branch skill before continuing.", + "assertions": [ + "Agent detects that the current branch is main", + "Agent does NOT push to main", + "Agent runs the create-branch skill to create a feature branch" + ] + } + ] +} diff --git a/CLAUDE.md b/CLAUDE.md index dd8f9b65..0d967150 100755 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -94,14 +94,21 @@ style issues; do not manually fix formatting. for setup instructions. - If a ticket is provided, read it with the `get_issue` tool to understand the full scope before writing any code. -- If no ticket exists for the work, propose one (show summary, description, and - type) and wait for explicit user approval before creating it. +- If no ticket exists for the work, use the `make-yt-issue` skill to create one. - When starting work on a ticket, move it to **Develop** state using `update_issue` (set `State` field). - After creating a PR, move the ticket to **Review** state and add the PR URL as a comment. -- Prefix every commit message with the ticket ID: - `[DBA-123] Description of change`. +## Commit Messages + +- Format: `[DBA-XXX] ` (max 72 chars) +- Use imperative mood: "Add feature", not "Added feature" or "Adds feature" +- Lowercase after the prefix: `[DBA-123] fix auth timeout` +- No trailing period +- If a body is needed, add a blank line after the summary: + - Explain *why*, not *what* (the diff shows what) + - Wrap at 72 characters +- If no ticket exists, omit the prefix — don't invent one ## After Completing Work @@ -115,42 +122,9 @@ style issues; do not manually fix formatting. findings before proceeding. 5. **Architecture review** — review architecture quality of the changed code. Fix any high-severity issues before proceeding. -6. **Branch** — create a branch following `/`. - Detect the user's nickname from existing remote branches: - ```bash - git branch -r | sed -nE 's|^ *origin/([^/]+)/.*|\1|p' | grep -vE '^(dependabot|HEAD|revert-)' | sort | uniq -c | sort -rn - ``` -7. **Commit** — stage and commit with `[DBA-123] Description of change`. -8. **Pause** — present the user with a summary of what will be pushed and - the draft PR description. Wait for explicit confirmation before - proceeding. -9. **Push & PR** — push with `-u` flag, create PR using the standard - format (see Pull Request Format section). -10. **Update YouTrack** — move the ticket to **Review** state and add +6. **Branch** — use the `create-branch` skill. +7. **Commit & PR** — use the `create-pr` skill (stages, commits, pauses + for confirmation, pushes, and opens the PR). +8. **Update YouTrack** — move the ticket to **Review** state and add a comment with the PR URL. -11. Never commit directly to `main`. - -## Pull Request Format - -Use the following structure for PR descriptions: - -``` -## Summary -<1–3 sentence overview of why this change exists> - -## Changes - -### - -
Files - -- `path/to/file1` -- `path/to/file2` -
- -### -... - -## Test Plan -- [ ] -``` +9. Never commit directly to `main`. diff --git a/Makefile b/Makefile index 80e030c7..817c55ee 100644 --- a/Makefile +++ b/Makefile @@ -28,6 +28,9 @@ test-cov-check: e2e-test: uv run --group e2e-tests pytest e2e-tests +nickname: + @git config user.email | cut -d@ -f1 + lint-skills: scripts/validate-agent-guidance.sh