Skip to content

Commit 515ce0b

Browse files
authored
Add path-scoped .claude/rules (#101)
**What:** Add slash commands (`/implement`, `/address-review`, `/issue`) and extract path-scoped `.claude/rules` for a more structured Claude Code workflow. **Why:** Common tasks like picking up GitHub issues, addressing PR review comments, and drafting issues were manual and repetitive. Testing, config, and version-specific guidance was embedded inline in `CLAUDE.md`, making it verbose. Extracting rules into path-scoped files surfaces the right context only when editing relevant files. **Changes:** - Add `/implement` command: fetches a GitHub issue, creates a branch from latest master (with remote selection), plans the work, executes step-by-step with programmer approval, commits incrementally, and generates a PR summary - Add `/address-review` command: walks through unaddressed PR review comments one by one (fix, reply, or skip), commits fixes, and posts replies with confirmation - Add `/issue` command: drafts a GitHub issue markdown file from user-provided context and saves to `plans/` - Extract testing patterns, config entry conventions, and version-specific SparkSubmit rules into `.claude/rules/` with path-scoped triggers - Scope `gh` CLI permissions in `settings.json` to specific subcommands instead of a blanket `gh *` wildcard - Trim `CLAUDE.md` by replacing inline code examples with references to the new rule files **How to verify:** 1. Run `/implement <issue-number>` and verify it walks through all 5 phases (fetch, branch, plan, execute, summary) 2. Run `/address-review <pr-number>` on a PR with review comments 3. Edit a file under `src/test/` and confirm testing rules context is surfaced 4. Check `.claude/settings.json` for scoped `gh` permissions --------- Signed-off-by: Sudipto Baral <sudiptobaral.me@gmail.com>
1 parent 62b35c9 commit 515ce0b

File tree

10 files changed

+458
-47
lines changed

10 files changed

+458
-47
lines changed

.claude/commands/address-review.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
Address review comments on a GitHub pull request.
2+
3+
The argument is a PR number (e.g. `/address-review 98`). If no number is given, ask the user.
4+
5+
## Phase 1: Fetch and display comments
6+
7+
1. Save the current branch name: `git rev-parse --abbrev-ref HEAD`
8+
2. Checkout the PR branch: `gh pr checkout <number> --detach`
9+
3. Fetch all review comments:
10+
11+
```
12+
gh api repos/{owner}/{repo}/pulls/<number>/comments --paginate
13+
```
14+
15+
4. Group comments by file. For each comment, extract: `id`, `path`, `line` (or `original_line`), `body`, `user.login`, `in_reply_to_id` (to detect threads).
16+
5. Filter out threads that are already resolved or where the last message is from the PR author (likely already addressed).
17+
6. Present a numbered summary to the programmer:
18+
19+
```
20+
PR #<number> — <n> unaddressed review comments
21+
22+
1. <file>:<line> — @<author>: <first 80 chars of comment>
23+
2. <file>:<line> — @<author>: <first 80 chars of comment>
24+
...
25+
```
26+
27+
If there are no unaddressed comments, say so and return to the original branch.
28+
29+
## Phase 2: Walk through each comment
30+
31+
For each comment in order, show:
32+
33+
- The full comment body
34+
- The file path and line number
35+
- The relevant code context (read ~10 lines around the commented line from the actual file)
36+
- The author who left the comment
37+
38+
Then ask the programmer what to do using AskUserQuestion with these options:
39+
40+
- **Fix** — Make a code change to address the comment. After the programmer picks this, analyze the comment and the surrounding code, propose a fix, and apply it with Edit. Show the programmer what changed and confirm before moving to the next comment.
41+
- **Reply** — Draft a reply to the comment. Write a concise, human-sounding response (see tone rules below). Show the exact text to the programmer. They can modify it. Queue the reply for posting in Phase 3.
42+
- **Skip** — Move to the next comment without action.
43+
44+
If the programmer chooses Fix, apply the change and move on. If they choose Reply, queue it. Track all code changes and queued replies separately.
45+
46+
## Phase 3: Commit, push, and post replies
47+
48+
After walking through all comments:
49+
50+
**Code changes:**
51+
If any code fixes were made, run the /commit skill to create a commit with the changes, then push:
52+
53+
```
54+
git push
55+
```
56+
57+
**Replies:**
58+
If there are queued replies, show all of them in a final summary:
59+
60+
```
61+
Queued replies:
62+
63+
1. <file>:<line> — reply to @<author>:
64+
"<reply text>"
65+
66+
2. <file>:<line> — reply to @<author>:
67+
"<reply text>"
68+
```
69+
70+
Ask the programmer for final confirmation before posting. They can modify any reply text at this point.
71+
72+
Post each reply as a separate API call:
73+
74+
```
75+
gh api repos/{owner}/{repo}/pulls/<number>/comments/<comment_id>/replies \
76+
--method POST \
77+
-f body="<reply text>"
78+
```
79+
80+
After all replies are posted, return to the original branch:
81+
82+
```
83+
git checkout <original-branch>
84+
```
85+
86+
## Reply tone and style
87+
88+
Follow these rules strictly when drafting replies:
89+
90+
- Write as a human developer, not an AI. Never mention AI, automation, or tools.
91+
- No markdown headings (#, ##, ###) in replies. Plain text and inline formatting only.
92+
- Keep replies concise — 1-3 sentences. Get to the point.
93+
- Conversational, collaborative tone: "good catch, fixed in the latest push", "I think this is fine because...", "yeah, updated this to use X instead"
94+
- Be direct but not dismissive. If you disagree with a comment, explain briefly why.
95+
- No bullet lists. Keep it flowing as natural text.
96+
- No emojis.
97+
- The programmer gets final say on every reply before it is posted.
98+
99+
## Rules
100+
101+
- Never post anything to GitHub without explicit programmer confirmation
102+
- Never modify code without programmer approval
103+
- Show exact reply text before queuing it
104+
- The programmer can modify any reply at any point
105+
- Always checkout with `--detach` to avoid creating local branches
106+
- After everything is done, return to the original branch
107+
- No emojis in any output
108+
- No markdown headings in any output or posted content
109+
- If an API call fails, show the error and ask the programmer how to proceed
110+
111+
$ARGUMENTS

.claude/commands/docs.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
Update existing project documentation to reflect the current state of the branch.
2+
3+
Steps:
4+
1. Run `git diff master...HEAD --stat` and `git log master..HEAD --oneline` to understand what changed on this branch
5+
2. Read the changed files to understand what was added, removed, or modified
6+
3. Read the current documentation files: `README.md`, `CONTRIBUTING.md`, `CLAUDE.md`
7+
4. Identify documentation that is now outdated or missing based on the branch changes
8+
5. Apply minimal, targeted edits to bring docs in line with the code
9+
10+
What to update:
11+
- **CONTRIBUTING.md** — new/removed slash commands, hooks, plugins, rules, build steps, coding standards
12+
- **README.md** — project description, features list, usage examples, configuration options
13+
- **CLAUDE.md** — project structure, architecture, key classes, common pitfalls, build commands
14+
15+
Principles:
16+
- **Minimal changes only.** Do not rewrite sections that are already accurate. Edit the smallest possible region.
17+
- **Capture important information.** New commands, config keys, classes, architecture changes, and breaking changes must be documented.
18+
- **Keep it concise.** Prefer inline descriptions over new subsections. Use tables and bullet points. Avoid verbose prose.
19+
- **Match existing style.** Follow the formatting, tone, and structure already in each file. Do not add headings, sections, or patterns that don't already exist.
20+
- **One edit per concern.** If a section needs updating, make one focused edit rather than rewriting the whole section.
21+
- **Skip trivial changes.** Internal refactors, renames, or implementation details that don't affect the public interface do not need doc updates.
22+
23+
After editing, report what was updated:
24+
- List each file edited and a one-line description of the change
25+
- If no docs needed updating, say "Documentation is up to date" and stop
26+
27+
Do NOT:
28+
- Create new documentation files
29+
- Add sections or headings that don't already exist
30+
- Rewrite large blocks of text when a small edit suffices
31+
- Document internal implementation details
32+
- Add emojis, badges, or decorative elements
33+
- Update docs for changes that don't affect user-facing behavior

.claude/commands/implement.md

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
Implement a GitHub issue end-to-end: fetch the issue, plan, code with approval, commit incrementally, and generate a PR summary.
2+
3+
The argument is an issue number (e.g. `/implement 42`) or a full GitHub URL (e.g. `/implement https://github.com/armadaproject/armada-spark/issues/42`). If no argument is given, ask the user.
4+
5+
## Phase 1: Fetch issue
6+
7+
1. Parse the argument:
8+
- If it is a full URL like `https://github.com/{owner}/{repo}/issues/{number}`, extract `{owner}/{repo}` and `{number}`.
9+
- If it is just a number, use the current repo (run `gh repo view --json nameWithOwner -q .nameWithOwner` to get it).
10+
2. Fetch issue details:
11+
```
12+
gh issue view <number> --repo <owner/repo> --json title,body,labels,assignees,comments
13+
```
14+
3. Display a summary to the programmer:
15+
- Issue number and title
16+
- Labels (if any)
17+
- Body (truncated to ~40 lines if longer)
18+
- Number of comments and any noteworthy discussion points
19+
4. Ask the programmer to confirm this is the right issue before proceeding. Use AskUserQuestion with options: "Proceed", "Show full issue body", "Cancel".
20+
21+
## Phase 2: Branch setup
22+
23+
1. List all configured remotes: `git remote -v`
24+
2. If there is more than one remote, ask the programmer which remote to use as upstream using AskUserQuestion (list the remote names as options).
25+
If there is only one remote, use it automatically.
26+
3. Fetch and update master from the chosen remote:
27+
```
28+
git fetch <remote>
29+
git checkout master
30+
git pull <remote> master
31+
```
32+
4. Create a new branch from master. The branch name must follow this convention:
33+
- Format: `<type>/<short-slug>` where `<type>` is a Conventional Commits type (`feat`, `fix`, `refactor`, `docs`, `chore`, etc.) and `<short-slug>` is a kebab-case summary derived from the issue title (3-5 words max).
34+
- Examples: `feat/dynamic-allocation-support`, `fix/event-watcher-reconnect`, `refactor/pod-spec-converter`
35+
- Pick the type based on the issue labels and description (e.g. a bug report maps to `fix/`, a feature request to `feat/`).
36+
```
37+
git checkout -b <type>/<short-slug>
38+
```
39+
5. Confirm to the programmer: "Created branch `<branch-name>` from `<remote>/master`."
40+
41+
## Phase 3: Plan
42+
43+
1. Based on the issue description, explore the codebase to understand the relevant code paths. Use subagents to search for files, classes, and patterns referenced in or implied by the issue.
44+
2. Create an implementation plan with numbered steps. Each step should be a logical, committable unit of work. The plan must include:
45+
- A 1-2 sentence summary of the issue
46+
- Numbered steps, where each step has a title, a description of what to change, and the affected file paths
47+
3. Save the plan to `plans/implement-<issue-number>.md` using this format:
48+
```
49+
## Issue #<number>: <title>
50+
51+
<1-2 sentence summary of what the issue asks for>
52+
53+
## Steps
54+
55+
1. <step title>
56+
- <what to change and where>
57+
- Files: <path/to/file.scala>
58+
59+
2. <step title>
60+
- <what to change and where>
61+
- Files: <path/to/file.scala, path/to/other.scala>
62+
```
63+
4. Show the full plan to the programmer for approval.
64+
5. Ask the programmer using AskUserQuestion with options: "Approve plan", "Modify plan", "Cancel".
65+
6. If the programmer wants modifications, iterate on the plan until approved.
66+
67+
## Phase 4: Execute step by step
68+
69+
For each step in the approved plan:
70+
71+
1. Announce the step: "Step <n>/<total>: <step title>"
72+
2. Make the code changes for that step
73+
3. If the step involves logic changes, run tests (`mvn test`) and show the result
74+
4. Show the programmer a brief summary of what changed (key files and the nature of the change)
75+
5. Ask the programmer using AskUserQuestion with options: "Commit this step", "Revise changes", "Skip this step", "Stop here".
76+
6. If the programmer picks "Commit this step", run the /commit skill to commit the changes
77+
7. If the programmer picks "Revise changes", iterate until they are satisfied
78+
8. If the programmer picks "Skip this step", move to the next step without committing
79+
9. If the programmer picks "Stop here", skip all remaining steps and jump to Phase 5
80+
81+
After each commit, briefly confirm the commit was made and move to the next step.
82+
83+
## Phase 5: Summary
84+
85+
1. After all steps are complete (or the programmer stops early):
86+
- Run the /summary skill to generate a PR description based on all commits made during this session
87+
- Show the summary to the programmer
88+
2. Do NOT create a PR or push. Just present the summary for the programmer to use when they are ready.
89+
3. If no commits were made (e.g. the programmer cancelled early), skip the summary and say so.
90+
91+
## Rules
92+
93+
- Never make code changes without programmer approval
94+
- Always show what changed after each step before committing
95+
- The programmer can modify, skip, reorder, or stop steps at any point
96+
- Save the plan file before starting execution so the programmer has a reference
97+
- Each commit should be a logical unit (one step = one commit, unless the step is trivial)
98+
- If the issue is from a different repo (not the current one), fetch it via the full URL but make changes in the current repo
99+
- No emojis in any output
100+
- Do not create a PR or push to remote — only local commits and a summary
101+
- If a step requires running tests, run them and show results before committing
102+
- Use subagents for codebase exploration and code changes; keep the main flow focused on coordination and programmer interaction
103+
104+
$ARGUMENTS

.claude/commands/summary.md

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,40 @@
11
Generate a concise implementation summary for a PR description.
22

33
Steps:
4-
1. Run `git diff master...HEAD --stat` and `git log master..HEAD --oneline` to understand all changes on this branch
5-
2. Read the changed files to understand what was implemented and why
6-
3. Write a summary suitable for a GitHub PR description
4+
1. First, run the `/docs` command to ensure documentation is up to date with the branch changes
5+
2. Run `git diff master...HEAD --stat` and `git log master..HEAD --oneline` to understand all changes on this branch
6+
3. Read the changed files to understand what was implemented and why
7+
4. Get the current branch name: `git rev-parse --abbrev-ref HEAD`
8+
5. Write a summary suitable for a GitHub PR description
9+
6. Save the raw markdown summary (without the wrapping code fence) to `plans/<branch-name>-summary.md`, replacing any `/` in the branch name with `-`
710

811
Summary format rules:
9-
- Start with a one-line "What" statement explaining the change
10-
- Follow with a "Why" section (2-3 sentences max) explaining the motivation
11-
- List the key changes as plain bullet points (no nested bullets)
12-
- If there are new tests, mention what they cover in one line
13-
- End with a "How to verify" section with concrete steps if applicable
12+
- The summary must be GitHub-flavored markdown that renders nicely in a PR description
13+
- Do NOT use any headings (`#`, `##`, `###`, etc.) — structure the summary with bold labels and line breaks instead
14+
- Use `**bold**` for section labels (e.g., `**What:**`, `**Why:**`, `**Changes:**`)
15+
- Use backtick-wrapped inline code for file names, config keys, commands, and identifiers
16+
- Use markdown bullet points (`-`) for listing changes
17+
- Use numbered lists for verification steps
18+
- Use `[text](url)` for hyperlinks when referencing issues or external resources
19+
- Start with `**What:**` — a one-line statement explaining the change
20+
- Follow with `**Why:**` — 2-3 sentences max explaining the motivation
21+
- Include `**Changes:**` — key changes as bullet points (no nested bullets)
22+
- If there are new tests, add `**Tests:**` with one line describing coverage
23+
- End with `**How to verify:**` — concrete steps as a numbered list
1424
- Keep the total summary under 30 lines
15-
- Use plain text with minimal markdown (no tables, no headers larger than ##, no code blocks unless showing a command)
1625
- Do not repeat file paths or class names unnecessarily
1726
- Focus on behavior changes, not implementation details
1827
- Write in present tense, active voice
1928

29+
Output rules:
30+
- Do NOT print the summary to the conversation
31+
- Only save it to the file and tell the user where it was saved: "Summary saved to `plans/<branch-name>-summary.md`"
32+
2033
Do NOT:
21-
- Use heavy markdown formatting (no bold, no tables, no badges)
34+
- Use headings (`#`, `##`, `###`) anywhere in the summary
35+
- Use tables or badges
2236
- List every single file changed
2337
- Include generic boilerplate like "This PR adds..."
2438
- Add emojis
2539
- Over-explain things that are obvious from the diff
26-
27-
Output the summary directly so the user can copy-paste it into the PR description.
40+
- Print the summary content in the conversation output

.claude/rules/config-entries.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
---
2+
paths:
3+
- "**/Config.scala"
4+
---
5+
6+
# Config Entry Rules
7+
8+
All entries use Spark's `ConfigBuilder` API, prefixed `spark.armada.*`, with `UPPER_SNAKE_CASE` constant names.
9+
10+
## Pattern by Type
11+
12+
```scala
13+
// Optional string with file-path validation
14+
val ARMADA_FOO: OptionalConfigEntry[String] =
15+
ConfigBuilder("spark.armada.foo")
16+
.doc("Description of config entry.")
17+
.stringConf
18+
.checkValue(path => path.nonEmpty && isValidFilePath(path),
19+
"Must be a valid local file path, file://, http:// or https:// URL")
20+
.createOptional
21+
22+
// Boolean with default
23+
val ARMADA_BAR_ENABLED: ConfigEntry[Boolean] =
24+
ConfigBuilder("spark.armada.bar.enabled")
25+
.doc("Description.")
26+
.booleanConf
27+
.createWithDefault(false)
28+
29+
// Time duration (parsed from string, stored as Long)
30+
val ARMADA_TIMEOUT: ConfigEntry[Long] =
31+
ConfigBuilder("spark.armada.timeout")
32+
.doc("Description.")
33+
.timeConf(TimeUnit.SECONDS)
34+
.checkValue(_ > 0, "Timeout must be positive.")
35+
.createWithDefaultString("300s")
36+
37+
// Integer with range check
38+
val ARMADA_BATCH_SIZE: ConfigEntry[Int] =
39+
ConfigBuilder("spark.armada.batchSize")
40+
.doc("Description.")
41+
.intConf
42+
.checkValue(_ > 0, "Must be positive")
43+
.createWithDefault(10)
44+
```
45+
46+
## Available Validators
47+
48+
- `isValidFilePath(path)` -- accepts `file://`, `http://`, `https://`, or bare local paths
49+
- `k8sLabelListValidator` / `k8sAnnotationListValidator` -- validates comma-separated `k=v` lists via `K8sValidator`
50+
- Inline predicates: `_ > 0`, `_.nonEmpty`, custom lambdas
51+
52+
## Creation Methods
53+
54+
| Method | Returns | Use when |
55+
|--------|---------|----------|
56+
| `createOptional` | `OptionalConfigEntry[T]` | No sensible default; caller checks `Option` |
57+
| `createWithDefault(v)` | `ConfigEntry[T]` | Typed default value |
58+
| `createWithDefaultString(s)` | `ConfigEntry[T]` | Default needs parsing (e.g., `"300s"` for timeConf) |

0 commit comments

Comments
 (0)