Skip to content

Commit 898d14d

Browse files
committed
chore: add CI, pre-commit hooks, git rules, and take-issue skill
- GitHub Actions CI on push to main (fmt, clippy, build, test, coverage) - hk pre-commit hooks (cargo fmt + clippy) - CLAUDE.md: git rules, commit message conventions, code style - take-issue claude skill for structured issue workflow - Remove unused release task and cargo-edit dependency
1 parent 40a5e67 commit 898d14d

5 files changed

Lines changed: 236 additions & 115 deletions

File tree

.claude/skills/take-issue.md

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
---
2+
name: take-issue
3+
description: Take a GitHub issue and implement it following the project workflow
4+
user_invocable: true
5+
arguments:
6+
- name: issue
7+
description: "GitHub issue number to implement"
8+
required: true
9+
---
10+
11+
# Take Issue Workflow
12+
13+
You are implementing a GitHub issue for the bombfork/internet-exploder project. Follow this process exactly.
14+
15+
## Phase 1: Read the issue
16+
17+
Fetch the issue details:
18+
```bash
19+
gh issue view {{ issue }} --repo bombfork/internet-exploder
20+
```
21+
22+
## Phase 2: Check for open children
23+
24+
Check if this issue has open sub-issues:
25+
```bash
26+
gh api graphql -f query='query {
27+
repository(owner: "bombfork", name: "internet-exploder") {
28+
issue(number: {{ issue }}) {
29+
subIssues(first: 50, filter: {states: [OPEN]}) {
30+
nodes { number title }
31+
}
32+
}
33+
}
34+
}' --jq '.data.repository.issue.subIssues.nodes'
35+
```
36+
37+
**If there are open children: STOP.** Tell the user this issue has open sub-issues that must be completed first, and list them. Do not proceed.
38+
39+
## Phase 3: Check parent and siblings
40+
41+
Only if there are NO open children, continue.
42+
43+
Get the parent issue:
44+
```bash
45+
gh api graphql -f query='query {
46+
repository(owner: "bombfork", name: "internet-exploder") {
47+
issue(number: {{ issue }}) {
48+
parentIssue { number title }
49+
}
50+
}
51+
}' --jq '.data.repository.issue.parentIssue'
52+
```
53+
54+
If there is a parent, read it and its children (siblings of our issue):
55+
```bash
56+
# Read the parent issue
57+
gh issue view <PARENT_NUMBER> --repo bombfork/internet-exploder
58+
59+
# Get all sibling issues with their state
60+
gh api graphql -f query='query {
61+
repository(owner: "bombfork", name: "internet-exploder") {
62+
issue(number: <PARENT_NUMBER>) {
63+
subIssues(first: 50) {
64+
nodes { number title state }
65+
}
66+
}
67+
}
68+
}' --jq '.data.repository.issue.subIssues.nodes'
69+
```
70+
71+
Assess whether issue #{{ issue }} is the right one to implement next, considering:
72+
- Dependencies between sibling issues (some must be done before others)
73+
- Whether prerequisite siblings are already closed
74+
- The implementation order described in the parent issue
75+
76+
**If there is a better candidate**: tell the user which issue should be implemented first and why. Ask whether they want to stop or keep going with #{{ issue }} anyway. If they want to stop, end here.
77+
78+
## Phase 4: Read comments and implement
79+
80+
Read any comments on the issue:
81+
```bash
82+
gh api repos/bombfork/internet-exploder/issues/{{ issue }}/comments --jq '.[].body'
83+
```
84+
85+
Now implement the issue:
86+
- Work on the `main` branch directly (no feature branches)
87+
- Create one or more commits as you go
88+
- Each commit message follows conventional commit format referencing the issue: `feat(scope): description #{{ issue }}`
89+
- Never bypass pre-commit hooks
90+
- Never force push
91+
- Run `mise run check` to verify everything passes before considering the issue done
92+
93+
Work through all the implementation items and acceptance criteria listed in the issue.
94+
95+
## Phase 5: Review and confirm
96+
97+
When all acceptance criteria are met:
98+
99+
1. Review all non-pushed commits:
100+
```bash
101+
git log origin/main..HEAD --oneline
102+
git diff origin/main..HEAD --stat
103+
```
104+
105+
2. Show the user a summary of what was implemented and the commits made.
106+
107+
3. Ask the user if they are satisfied with the implementation.
108+
109+
4. If satisfied, propose to push and close the issue.
110+
111+
## Phase 6: Push, close, and prepare next
112+
113+
Only if the user agreed to push and close:
114+
115+
1. Identify the probable next sibling issue to implement:
116+
- Look at the open siblings from Phase 3
117+
- Consider the implementation order
118+
- Pick the next one whose prerequisites are now met
119+
120+
2. Assess if the current implementation impacts the definition of the next issue:
121+
- Did you discover something during implementation that changes what the next issue should do?
122+
- Did you make an architectural decision that the next issue should know about?
123+
- Did you add/change APIs that the next issue depends on?
124+
125+
If so, add a comment to the next issue with the relevant information:
126+
```bash
127+
gh issue comment <NEXT_ISSUE> --repo bombfork/internet-exploder --body "..."
128+
```
129+
130+
3. Push the code:
131+
```bash
132+
git push origin main
133+
```
134+
135+
4. Close the issue:
136+
```bash
137+
gh issue close {{ issue }} --repo bombfork/internet-exploder
138+
```
139+
140+
5. Tell the user the issue is closed and suggest the next issue to take.

.github/workflows/ci.yml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [main]
6+
7+
env:
8+
CARGO_TERM_COLOR: always
9+
10+
jobs:
11+
check:
12+
runs-on: ubuntu-latest
13+
steps:
14+
- uses: actions/checkout@v4
15+
- name: Prepare cache key
16+
id: cache-key
17+
run: |
18+
HASH=$(sha256sum Cargo.lock | cut -d' ' -f1)
19+
echo "cargo-lock-hash=$HASH" >> "$GITHUB_OUTPUT"
20+
- uses: jdx/mise-action@v2
21+
- uses: actions/cache@v4
22+
with:
23+
path: |
24+
~/.cargo
25+
target
26+
key: v0-linux-${{ runner.arch }}-${{ steps.cache-key.outputs.cargo-lock-hash }}
27+
- run: mise run check
28+
29+
coverage:
30+
needs: check
31+
runs-on: ubuntu-latest
32+
steps:
33+
- uses: actions/checkout@v4
34+
- name: Prepare cache key
35+
id: cache-key
36+
run: |
37+
HASH=$(sha256sum Cargo.lock | cut -d' ' -f1)
38+
echo "cargo-lock-hash=$HASH" >> "$GITHUB_OUTPUT"
39+
- uses: jdx/mise-action@v2
40+
- uses: actions/cache@v4
41+
with:
42+
path: |
43+
~/.cargo
44+
target
45+
key: v0-linux-${{ runner.arch }}-${{ steps.cache-key.outputs.cargo-lock-hash }}
46+
- run: cargo install cargo-llvm-cov
47+
- run: mise run coverage:check

CLAUDE.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,35 @@ mise run check # All of the above
9292
mise run run # Launch the browser
9393
```
9494

95+
## Git Rules
96+
97+
These rules have NO exceptions:
98+
99+
- **Never bypass pre-commit hooks** (`--no-verify` is forbidden)
100+
- **Never force push** (`--force`, `--force-with-lease` are forbidden)
101+
- **Only amend commits that have not been pushed** — once pushed, create a new commit instead
102+
103+
## Commit Messages
104+
105+
Use short conventional commits referencing the GitHub issue:
106+
107+
```
108+
feat(ie-dom): add tree traversal iterators #4
109+
fix(ie-net): handle redirect loop edge case #5
110+
test(ie-shell): CLI parsing unit tests #6
111+
refactor(ie-css): split style.rs into modules #22
112+
chore: update dependencies
113+
```
114+
115+
Format: `type(scope): short description #issue`
116+
117+
Types: `feat`, `fix`, `test`, `refactor`, `chore`, `docs`
118+
119+
## Code Style
120+
121+
- Keep comments short — one line when possible, no prose
122+
- No comments for self-evident code
123+
95124
## Key Design Decisions
96125

97126
- **Latest standards only**: no quirks mode, no legacy HTML elements, no vendor-prefixed CSS

hk.pkl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
amends "package://github.com/jdx/hk/releases/download/v1.36.0/hk@1.36.0#/Config.pkl"
2+
import "package://github.com/jdx/hk/releases/download/v1.36.0/hk@1.36.0#/Builtins.pkl"
3+
4+
local linters = new Mapping<String, Step> {
5+
["cargo-fmt"] = Builtins.cargo_fmt
6+
["cargo-clippy"] = (Builtins.cargo_clippy) {
7+
check = "cargo clippy --workspace -- -D warnings"
8+
}
9+
}
10+
11+
hooks {
12+
["pre-commit"] {
13+
stash = "git"
14+
steps = linters
15+
}
16+
["fix"] {
17+
fix = true
18+
steps = linters
19+
}
20+
}

mise.toml

Lines changed: 0 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
[tools]
22
rust = "stable"
33
"cargo:cargo-binstall" = "latest"
4-
"cargo:cargo-edit" = "latest"
54
"cargo:cargo-llvm-cov" = "latest"
65
hk = "latest"
76
pkl = "latest"
@@ -49,117 +48,3 @@ depends = ["fmt:check", "lint:check", "build", "test"]
4948
[tasks.run]
5049
description = "Run the browser"
5150
run = "cargo run -p ie-shell"
52-
53-
[tasks.release]
54-
description = "Prepare a release: bump version, create PR, wait for merge, tag & push"
55-
usage = '''
56-
flag "--major" help="Bump the major version"
57-
flag "--minor" help="Bump the minor version"
58-
flag "--patch" help="Bump the patch version"
59-
'''
60-
run = '''
61-
#!/usr/bin/env bash
62-
set -euo pipefail
63-
64-
# --- Validate flags ---
65-
count=0
66-
[[ "${usage_major:-false}" == "true" ]] && count=$((count + 1))
67-
[[ "${usage_minor:-false}" == "true" ]] && count=$((count + 1))
68-
[[ "${usage_patch:-false}" == "true" ]] && count=$((count + 1))
69-
70-
if [[ "$count" -ne 1 ]]; then
71-
echo "Error: exactly one of --major, --minor, or --patch must be provided" >&2
72-
exit 1
73-
fi
74-
75-
# --- Ensure we are on main and up to date ---
76-
current_branch=$(git branch --show-current)
77-
if [[ "$current_branch" != "main" ]]; then
78-
echo "Error: must be on the main branch (currently on '$current_branch')" >&2
79-
exit 1
80-
fi
81-
82-
git fetch origin main
83-
local_sha=$(git rev-parse HEAD)
84-
remote_sha=$(git rev-parse origin/main)
85-
if [[ "$local_sha" != "$remote_sha" ]]; then
86-
echo "Error: local main is not up to date with origin/main" >&2
87-
echo " local: $local_sha" >&2
88-
echo " remote: $remote_sha" >&2
89-
exit 1
90-
fi
91-
92-
# --- Compute new version ---
93-
current_version=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')
94-
IFS='.' read -r major minor patch <<< "$current_version"
95-
96-
if [[ "${usage_major:-false}" == "true" ]]; then
97-
major=$((major + 1)); minor=0; patch=0
98-
elif [[ "${usage_minor:-false}" == "true" ]]; then
99-
minor=$((minor + 1)); patch=0
100-
else
101-
patch=$((patch + 1))
102-
fi
103-
104-
new_version="${major}.${minor}.${patch}"
105-
echo "Bumping version: $current_version -> $new_version"
106-
107-
# --- Create release branch ---
108-
branch="prepare-release-${new_version}"
109-
git checkout -b "$branch"
110-
111-
# --- Update version in Cargo.toml ---
112-
sed -i "0,/^version = \"${current_version}\"/s//version = \"${new_version}\"/" Cargo.toml
113-
114-
# --- Update Cargo.lock ---
115-
cargo generate-lockfile
116-
117-
# --- Commit ---
118-
git add Cargo.toml Cargo.lock
119-
git commit -m "chore: bump version to ${new_version}"
120-
121-
# --- Push and create PR ---
122-
git push -u origin "$branch"
123-
pr_url=$(gh pr create \
124-
--title "chore: release v${new_version}" \
125-
--body "Bump version to ${new_version}." \
126-
--base main)
127-
128-
echo "PR created: $pr_url"
129-
130-
# --- Enable auto-merge ---
131-
gh pr merge --auto --squash "$pr_url"
132-
133-
# --- Open PR in browser ---
134-
xdg-open "$pr_url"
135-
136-
# --- Wait for PR to be merged ---
137-
echo "Waiting for PR to be merged..."
138-
while true; do
139-
state=$(gh pr view "$pr_url" --json state --jq '.state')
140-
if [[ "$state" == "MERGED" ]]; then
141-
echo "PR merged!"
142-
break
143-
fi
144-
sleep 10
145-
done
146-
147-
# --- Switch back to main and update ---
148-
git checkout main
149-
git pull origin main
150-
151-
# --- Tag and push ---
152-
prev_tag=$(git tag --sort=-v:refname | head -n1)
153-
if [[ -n "$prev_tag" ]]; then
154-
tag_message=$(git log "${prev_tag}..HEAD" --pretty=format:"- %s (%h)" --no-merges)
155-
else
156-
tag_message=$(git log --pretty=format:"- %s (%h)" --no-merges)
157-
fi
158-
git tag -a "v${new_version}" -m "$tag_message"
159-
git push origin "v${new_version}"
160-
161-
# --- Cleanup local branch ---
162-
git branch -d "$branch"
163-
164-
echo "Release v${new_version} complete!"
165-
'''

0 commit comments

Comments
 (0)