Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,5 +74,5 @@ jobs:
git config user.email "github-actions[bot]@users.noreply.github.com"
git add Formula/td.rb
git diff --cached --quiet && echo "No changes" && exit 0
git commit -m "td: bump to ${{ steps.version.outputs.version }}"
git commit -m "chore: bump homebrew formula to ${{ steps.version.outputs.version }}"
git push
5 changes: 4 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,17 @@ go test ./... # Test all
```bash
# Commit changes with proper message
git add .
git commit -m "feat: description of changes
git commit -m "feat: describe the change (td-<id>)

Details here

🤖 Generated with Claude Code

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>"

# Release or automation-only commits without a td task may omit the suffix:
git commit -m "chore: update release metadata"

# Create version tag (bump from current version, e.g., v0.2.0 → v0.3.0)
git tag -a v0.3.0 -m "Release v0.3.0: description"

Expand Down
8 changes: 5 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ help:
@printf "%s\n" \
"Targets:" \
" make fmt # gofmt -w ." \
" make install-hooks # install git pre-commit hook" \
" make install-hooks # install git pre-commit + commit-msg hooks" \
" make test # go test ./..." \
" make install # build and install with version from git" \
" make tag VERSION=vX.Y.Z # create annotated git tag (requires clean tree)" \
Expand Down Expand Up @@ -52,6 +52,8 @@ release: tag
git push origin "$(VERSION)"

install-hooks:
@echo "Installing git pre-commit hook..."
@echo "Installing git hooks..."
@mkdir -p .git/hooks
@ln -sf ../../scripts/pre-commit.sh .git/hooks/pre-commit
@echo "Done. Hook installed at .git/hooks/pre-commit"
@ln -sf ../../scripts/commit-msg.sh .git/hooks/commit-msg
@echo "Done. Hooks installed at .git/hooks/pre-commit and .git/hooks/commit-msg"
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,14 @@ make install-dev
# Format code
make fmt

# Install git pre-commit hook (gofmt, go vet, go build on staged files)
# Install git hooks:
# pre-commit runs gofmt/go vet/go build checks
# commit-msg normalizes subjects to type: summary [(td-<id>)]
make install-hooks
```

Regular development commits should use `type: summary (td-<id>)`, for example `feat: add query validation (td-a1b2)`. Automation or release-maintenance commits that are not tied to a td task should use `type: summary`, for example `chore: bump homebrew formula to v0.2.0`.

## Tests & Quality Checks

```bash
Expand Down
6 changes: 3 additions & 3 deletions docs/guides/releasing-new-version.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ Add entry at the top of `CHANGELOG.md`:
Commit the changelog:
```bash
git add CHANGELOG.md
git commit -m "docs: Update changelog for vX.Y.Z"
git commit -m "docs: update changelog for vX.Y.Z"
```

### 3. Verify Tests Pass
Expand All @@ -73,7 +73,7 @@ git tag -a vX.Y.Z -m "Release vX.Y.Z: brief description"
git push origin vX.Y.Z
```

Pushing the tag triggers `.github/workflows/release.yml`, which runs GoReleaser to build binaries, create the GitHub release, and update the Homebrew tap.
Pushing the tag triggers `.github/workflows/release.yml`, which runs GoReleaser to build binaries, create the GitHub release, and update the Homebrew tap. Release automation uses the same canonical subject shape as the rest of the repo: `type: summary`. For normal development work, keep using `type: summary (td-<id>)`.

### 5. Verify

Expand Down Expand Up @@ -137,7 +137,7 @@ go test ./...
# Update changelog
# (Edit CHANGELOG.md, add entry at top)
git add CHANGELOG.md
git commit -m "docs: Update changelog for vX.Y.Z"
git commit -m "docs: update changelog for vX.Y.Z"

# Push commits, then tag (tag push triggers automated release)
git push origin main
Expand Down
113 changes: 113 additions & 0 deletions scripts/commit-msg.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#!/usr/bin/env bash
# commit-msg hook for td
# Install: make install-hooks (or: ln -sf ../../scripts/commit-msg.sh .git/hooks/commit-msg)
set -euo pipefail

MESSAGE_FILE="${1:-}"
ALLOWED_TYPES_REGEX='^(feat|fix|chore|docs|refactor|test|ci|build|perf|style|revert)$'

trim() {
local value="$1"
value="${value#"${value%%[![:space:]]*}"}"
value="${value%"${value##*[![:space:]]}"}"
printf '%s' "$value"
}

normalize_subject() {
local original="$1"
local working prefix raw_type remainder type task_suffix normalized

working="$(trim "$original")"
prefix=""

if [[ "$working" =~ ^((fixup|squash)!\ )(.*)$ ]]; then
prefix="${BASH_REMATCH[1]}"
working="${BASH_REMATCH[3]}"
fi

if [[ "$working" == Merge\ * || "$working" == Revert\ \"* ]]; then
printf '%s%s\n' "$prefix" "$working"
return 0
fi

if [[ "$working" =~ ^([[:alpha:]]+)[[:space:]]*:[[:space:]]*(.+)$ ]]; then
raw_type="${BASH_REMATCH[1]}"
remainder="${BASH_REMATCH[2]}"
elif [[ "$working" =~ ^([[:alpha:]]+)[[:space:]]+(.+)$ ]]; then
raw_type="${BASH_REMATCH[1]}"
remainder="${BASH_REMATCH[2]}"
else
return 1
fi

type="$(printf '%s' "$raw_type" | tr '[:upper:]' '[:lower:]')"
if ! [[ "$type" =~ $ALLOWED_TYPES_REGEX ]]; then
return 1
fi

remainder="$(trim "$remainder")"
task_suffix=""
if [[ "$remainder" =~ ^(.*)[[:space:]]+\(([Tt][Dd]-[[:alnum:]][[:alnum:]-]*)\)$ ]]; then
remainder="$(trim "${BASH_REMATCH[1]}")"
task_suffix="$(printf '%s' "${BASH_REMATCH[2]}" | tr '[:upper:]' '[:lower:]')"
fi

if [[ -z "$remainder" ]]; then
return 1
fi

normalized="${prefix}${type}: ${remainder}"
if [[ -n "$task_suffix" ]]; then
normalized="${normalized} (${task_suffix})"
fi

printf '%s\n' "$normalized"
}

if [[ -z "$MESSAGE_FILE" || ! -f "$MESSAGE_FILE" ]]; then
echo "commit-msg: expected the commit message file path as the first argument." >&2
exit 1
fi

lines=()
while IFS= read -r line || [[ -n "$line" ]]; do
lines+=("$line")
done < "$MESSAGE_FILE"
subject_index=-1
subject_line=""

for i in "${!lines[@]}"; do
line="${lines[$i]}"
trimmed_line="$(trim "$line")"
if [[ -z "$trimmed_line" || "$trimmed_line" == \#* ]]; then
continue
fi
subject_index="$i"
subject_line="$line"
break
done

if [[ "$subject_index" -lt 0 ]]; then
exit 0
fi

if ! normalized_subject="$(normalize_subject "$subject_line")"; then
cat >&2 <<'EOF'
Commit message must use one of these formats:
type: summary (td-<id>) # normal development work
type: summary # automation/release work without a td task

Examples:
feat: add session analytics (td-a1b2)
chore: bump homebrew formula to v0.2.0

Allowed types: feat, fix, chore, docs, refactor, test, ci, build, perf, style, revert
EOF
exit 1
fi

if [[ "$(trim "$subject_line")" != "$normalized_subject" ]]; then
lines[$subject_index]="$normalized_subject"
printf '%s\n' "${lines[@]}" > "$MESSAGE_FILE"
echo "Normalized commit subject to: $normalized_subject"
fi
3 changes: 2 additions & 1 deletion scripts/loop-prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ td review <id>
```

Use `td review`, not `td close` — self-closing is blocked.
For release or automation-only commits without a td task, use `type: <summary>` instead.

## Rules

Expand All @@ -174,4 +175,4 @@ Use `td review`, not `td close` — self-closing is blocked.
- **Don't break sync.** Deterministic IDs, proper event logging, no hard deletes.
- **Session isolation is sacred.** Don't bypass review guards.
- **If stuck, log and skip.** `td log <id> "Blocked: <reason>"` then `td block <id>`.
- **Commit messages reference td.** Format: `feat|fix|chore: <summary> (td-<id>)`
- **Commit messages use one canonical subject style.** Prefer `type: <summary> (td-<id>)`; release/automation commits without a td task may use `type: <summary>`.
2 changes: 1 addition & 1 deletion scripts/pre-commit.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# pre-commit hook for td
# Install: make install-hooks (or: ln -sf ../../scripts/pre-commit.sh .git/hooks/pre-commit)
# Install: make install-hooks (installs both pre-commit and commit-msg hooks)
set -euo pipefail

PASS=0
Expand Down