* docs/building.md — how to build jj-commitd and planctl from source, requirements, `go install` recipes, reproducible-build flags (`-trimpath -ldflags="-buildid="` per planctl PRD R7.3), test + benchmark commands, cross-compile, CI matrix link. * docs/planctl.md — full command reference beyond --help: plan-dir discovery cases A-D, all 9 classification codes with severity and meaning, tag-grammar spec (including paren-aware annotations + the tag-unclosed recovery semantics), the five EARS regexes and the bold-prefix exemption, JSON schema, worked examples for pre-commit / CI / multi-plan / JSON consumers. * docs/jj-commitd.md — architecture diagram, socket event protocol (session-start, post-edit, session-end, shutdown, status) with field tables, all env vars (REPO_ROOT, JJ_HOOK_DEBOUNCE_SEC, JJ_HOOK_STALE_MIN, JJ_HOOK_AUTO_SQUASH), baked-in timing constants, socket / log paths, fallback mode, troubleshooting. * README.md — expanded the jj-commitd bullets and linked to each reference from a new "Documentation" section; kept top-level summaries terse.
147 lines
7.5 KiB
Markdown
147 lines
7.5 KiB
Markdown
# jj-template
|
|
|
|
Reusable [jj](https://martinvonz.github.io/jj/) + [Claude Code](https://docs.anthropic.com/en/docs/claude-code) workflow template. Provides automatic per-session snapshot management so every Claude Code session's work is committed and isolated — even with concurrent agents.
|
|
|
|
## What It Does
|
|
|
|
- **Session start**: snapshots any pre-existing uncommitted work before the agent starts editing
|
|
- **Post-edit**: tracks every file Claude edits (via Edit/Write/NotebookEdit tools) per-session
|
|
- **Session end**: commits tracked files with a descriptive `wip(claude:<session>):` message, then commits any untracked leftovers
|
|
- **Concurrent safety**: multiple agent sessions on the same repo get isolated, file-scoped commits
|
|
- **Bookmarks**: each session's commit gets a `wip/claude-{session}` bookmark for easy reference
|
|
- **Base tracking**: records the starting revision per session (visible in `status`)
|
|
- **Orphan reaping**: dead sessions (crashed CLI) are cleaned up automatically via PID liveness checks and 30m timeout (configurable via `JJ_HOOK_STALE_MIN`)
|
|
- **Legacy cleanup**: daemon sweeps orphaned `/tmp/jj-claude-*-files` from pre-daemon hook versions on startup
|
|
- **Conflict detection**: warns when multiple sessions edit the same file
|
|
- **Squash**: `squash-wip` consolidates accumulated wip commits into one for cleaner history
|
|
- **Auto-squash**: set `JJ_HOOK_AUTO_SQUASH=1` to squash automatically at session end
|
|
- **Repo cleanup**: `/repo-cleanup` Claude command organizes wip commits into themed conventional commits
|
|
|
|
## Commit Daemon (`jj-commitd`)
|
|
|
|
A Go daemon that batches file edits into debounced commits via a Unix socket.
|
|
|
|
- **How it works**: `session-start` launches the daemon; `post-edit` sends file paths over a socket; a debounce timer (default 3s) batches edits into a single `jj commit`; `session-end` flushes pending commits and shuts down.
|
|
- **Install**: `go install forgejo.zerova.net/sid/template-jj/cmd/jj-commitd@latest`.
|
|
- **Config**: `JJ_HOOK_DEBOUNCE_SEC` (default `3`, max `30`), `JJ_HOOK_STALE_MIN` (default `30`), `JJ_HOOK_AUTO_SQUASH=1` to squash at session end.
|
|
- **Session inventory**: on `session-start`, returns other active sessions and their tracked files.
|
|
- **Logging**: daemon log at `/tmp/jj-commitd-<repo_id>.log`.
|
|
- **Fallback**: without the binary on `PATH`, the hook falls back to file-tracking-only mode that commits at session end.
|
|
- **Full reference**: [`docs/jj-commitd.md`](docs/jj-commitd.md).
|
|
|
|
## planctl
|
|
|
|
Lint tool for spec-driven plan directories (`dev/plans/<YYWWD>-<feature-slug>/`). Validates traceability tags, cross-references between `prd.md` / `design.md` / `tasks.md`, file-presence at close-out, and EARS-keyword conformance on PRD acceptance criteria. Read-only by design — v1 ships `planctl lint` and nothing else.
|
|
|
|
- **Install**: `go install forgejo.zerova.net/sid/template-jj/cmd/planctl@latest`.
|
|
- **Usage**: `planctl lint [plan-dir]` — pass a path, or invoke from anywhere inside a plan dir or a repo containing `dev/plans/`.
|
|
- **Output**: one line per diagnostic in the form `<path>:<line>: [<severity>] <code>: <message>`; a clean plan prints one summary line. `--format=json` emits JSONL for tooling.
|
|
- **Flags**: `--strict` (warnings → exit 1), `--no-ears` (skip EARS check), `--format={text,json}`, `--color={auto,always,never}` (no-op in v1).
|
|
- **Classification codes**: `tag-syntax`, `tag-unclosed`, `orphan-requirement`, `orphan-design`, `uncovered-requirement`, `uncovered-design`, `missing-prd`, `missing-closeout-file`, `ears-violation`. These are grep-stable public API.
|
|
- **Full reference**: [`docs/planctl.md`](docs/planctl.md).
|
|
- **Spec**: [`dev/plans/26172-planctl/`](dev/plans/26172-planctl/) — PRD, design, task list, and codex-review log.
|
|
|
|
## Documentation
|
|
|
|
- [`docs/building.md`](docs/building.md) — build both binaries from source, reproducible builds, cross-compile, CI matrix.
|
|
- [`docs/planctl.md`](docs/planctl.md) — full `planctl` command reference.
|
|
- [`docs/jj-commitd.md`](docs/jj-commitd.md) — full `jj-commitd` daemon reference.
|
|
- [`INTEGRATION.md`](INTEGRATION.md) — integrate this template into an existing or new project.
|
|
- [`AGENTS.md`](AGENTS.md) — cross-agent working conventions.
|
|
|
|
## Prerequisites
|
|
|
|
- [jj](https://martinvonz.github.io/jj/latest/install/) (Jujutsu VCS)
|
|
- [jq](https://jqlang.github.io/jq/) (JSON processor)
|
|
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI
|
|
- [Go](https://go.dev/dl/) 1.22+ (optional, for building the commit daemon)
|
|
|
|
## Quick Start
|
|
|
|
See [INTEGRATION.md](INTEGRATION.md) for step-by-step instructions to add this to an existing or new project.
|
|
|
|
## Files
|
|
|
|
```
|
|
cmd/jj-commitd/
|
|
main.go # Commit daemon (Go)
|
|
scripts/
|
|
_lib.sh # Shared utilities (REPO_ROOT resolution)
|
|
jj-hook.sh # Main hook script (all 6 actions)
|
|
test-jj-hooks.sh # 26-test integration suite
|
|
.claude/
|
|
settings.json # Hook configuration for Claude Code (team-shared)
|
|
commands/
|
|
repo-cleanup.md # /repo-cleanup slash command
|
|
```
|
|
|
|
## Manual Commands
|
|
|
|
```bash
|
|
# Check active sessions for this repo
|
|
./scripts/jj-hook.sh status
|
|
|
|
# Squash all wip commits between main and @ into one
|
|
./scripts/jj-hook.sh squash-wip
|
|
|
|
# Force-reap all non-alive sessions
|
|
./scripts/jj-hook.sh reap
|
|
```
|
|
|
|
## How It Works
|
|
|
|
### Tracking Files
|
|
|
|
Each active session gets a tracking file at `/tmp/jj-claude-{REPO_ID}-{SESSION_ID}-files`. The `REPO_ID` is a `cksum` of the repo root path, preventing cross-repo contamination. Companion files record the parent process (`-pid`) and starting revision (`-base`) for liveness checking and cleanup context.
|
|
|
|
### Commit Flow
|
|
|
|
With daemon (default when `jj-commitd` binary is available):
|
|
|
|
```
|
|
session-start → snapshot pre-existing work
|
|
→ launch jj-commitd daemon (or connect to existing)
|
|
→ record base revision, reap stale sessions
|
|
|
|
post-edit (per file) → send file path to daemon via Unix socket
|
|
→ daemon resets debounce timer (3s default)
|
|
→ on timer expiry: commit all batched files
|
|
|
|
session-end → flush pending commits (daemon commits immediately)
|
|
→ set wip/claude-{session} bookmark
|
|
→ commit untracked leftovers (if sole session)
|
|
→ auto-squash (if JJ_HOOK_AUTO_SQUASH=1)
|
|
→ clean up tracking + PID + base files
|
|
```
|
|
|
|
Without daemon (fallback):
|
|
|
|
```
|
|
session-start → snapshot pre-existing work
|
|
→ record base revision, initialize tracking file
|
|
→ reap stale/orphaned sessions
|
|
|
|
post-edit (per file) → append relative path to tracking file (dedup)
|
|
|
|
session-end → commit tracked files (file-scoped)
|
|
→ set wip/claude-{session} bookmark
|
|
→ commit untracked leftovers (if sole session)
|
|
→ auto-squash (if JJ_HOOK_AUTO_SQUASH=1)
|
|
→ clean up tracking + PID + base files
|
|
```
|
|
|
|
### Concurrency
|
|
|
|
When multiple Claude sessions edit the same repo simultaneously:
|
|
- Each session tracks its own files independently
|
|
- Session A ending only commits files A touched
|
|
- Untracked changes (Bash-created files) are only committed when the last session ends
|
|
- If both sessions touch the same file, the first to end commits it; the second skips it (no diff remaining)
|
|
|
|
## Tests
|
|
|
|
```bash
|
|
scripts/test-jj-hooks.sh
|
|
```
|
|
|
|
Runs 34 integration tests in temporary jj repos. Requires `jj` and `jq`. Portable across macOS and Linux.
|