11 KiB
Integration Guide
Instructions for adding the jj + Claude Code workflow to an existing or new project. Written for agentic devs (Claude, Codex, etc.) to follow.
Workflow Layers
This template ships two independent layers; adopt them together (recommended) or separately.
- Session-commit tooling —
cmd/,scripts/,.claude/settings.json,.claude/commands/. Thejj-commitddaemon + hook scripts +/repo-cleanupslash command give every Claude Code session its own isolated commit stack via debounced per-session commits. Documented in the "Building the Daemon", "New Project", and "Existing Project" sections below. - Spec-driven workflow —
AGENTS.md,CLAUDE.md,dev/,.claude/skills/. Portable cross-agent conventions: YYWWD-prefixed plan directories, the/create-prd→/create-design→/generate-tasks→/process-task-listchain, peer-reviewer CLI skills (codex,copilot,gemini,kiro), multi-agent workspace guidance, optional upstream-leak pre-push guard. Documented in the "Spec-Driven Workflow" section at the bottom of this file.
The layers are orthogonal. You can adopt only the session-commit tooling (existing users pre-dating the workflow layer), only the spec-driven workflow (projects that manage commits manually), or both.
Prerequisites
- jj, jq, Claude Code
- Go 1.22+ (optional, for the commit daemon)
Building the Daemon (Optional)
The commit daemon provides debounced commits during editing. Without it, the hook falls back to committing at session end only.
cd /path/to/jj-template
go install ./cmd/jj-commitd/
# Binary installed to $GOPATH/bin/jj-commitd (ensure it's in PATH)
The hook script auto-starts the daemon on session-start if the binary is found.
New Project
# 1. Create your project and init jj
mkdir my-project && cd my-project
jj git init
# 2. Copy the template files
cp -r /path/to/jj-template/scripts ./scripts
cp -r /path/to/jj-template/.claude ./.claude
# Note: settings.json is team-shared (committed to repo)
chmod +x scripts/jj-hook.sh scripts/test-jj-hooks.sh
# 3. Commit the tooling
jj commit -m "chore: add jj + claude code workflow"
jj bookmark set main -r '@-'
# 4. Verify
scripts/test-jj-hooks.sh
Existing Project
Step 1: Add Scripts
Copy scripts/jj-hook.sh, scripts/_lib.sh, and scripts/test-jj-hooks.sh into your project's scripts/ directory (create it if needed).
mkdir -p scripts
cp /path/to/jj-template/scripts/_lib.sh scripts/
cp /path/to/jj-template/scripts/jj-hook.sh scripts/
cp /path/to/jj-template/scripts/test-jj-hooks.sh scripts/
chmod +x scripts/jj-hook.sh scripts/test-jj-hooks.sh
If your project already has a scripts/_lib.sh, merge in the REPO_ROOT line:
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
That's the only variable jj-hook.sh requires from _lib.sh.
Step 2: Add Claude Code Hooks
Copy .claude/settings.json into your project, or merge the hooks into your existing settings:
{
"hooks": {
"SessionStart": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "./scripts/jj-hook.sh session-start",
"timeout": 15
}
]
}
],
"SessionEnd": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "./scripts/jj-hook.sh session-end",
"timeout": 15
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write|NotebookEdit",
"hooks": [
{
"type": "command",
"command": "./scripts/jj-hook.sh post-edit",
"timeout": 10
}
]
}
]
}
}
Note: settings.json is committed to the repo and shared across the team. Use .claude/settings.local.json for personal overrides (it's gitignored by default).
Step 3: Add Repo Cleanup Command (Optional)
Copy .claude/commands/repo-cleanup.md to get the /repo-cleanup slash command:
mkdir -p .claude/commands
cp /path/to/jj-template/.claude/commands/repo-cleanup.md .claude/commands/
Customize the theme categories in that file to match your project structure.
Step 4: Verify
# Run the test suite
scripts/test-jj-hooks.sh
# Start a Claude session and check status
./scripts/jj-hook.sh status
# View daemon logs (if daemon is running)
tail -f /tmp/jj-commitd-$(printf '%s' "$PWD" | shasum -a 256 | cut -c1-12).log
Converting a Git Repo to jj
If your project uses git and you want to adopt jj:
cd my-project
jj git init --colocate # colocate keeps .git alongside .jj
jj bookmark set main -r '@-'
Colocated mode lets you use both git and jj commands. The hook script works with both colocated and standalone jj repos.
Customization
Different Script Location
If your scripts live somewhere other than scripts/, update the paths in .claude/settings.json:
"command": "./tools/jj-hook.sh session-start"
And update the _lib.sh source line in jj-hook.sh:
source "$(dirname "${BASH_SOURCE[0]}")/_lib.sh"
Existing _lib.sh
The hook only needs REPO_ROOT from _lib.sh. If your _lib.sh already sets REPO_ROOT, no changes needed — just ensure the double-source guard pattern:
[[ -n "${_LIB_LOADED:-}" ]] && return 0
_LIB_LOADED=1
Commit Message Format
The default format is wip(claude:<session_id>): <file summary>. To change it, edit the jj commit -m lines in jj-hook.sh (search for wip(claude:).
Main Branch Name
The squash-wip command assumes a main bookmark. If you use a different name (e.g., trunk, master), update the jj log -r 'main' references in jj-hook.sh.
Tuning
Environment variables (set in your shell profile or .claude/settings.local.json hook commands):
| Variable | Default | Description |
|---|---|---|
JJ_HOOK_DEBOUNCE_SEC |
3 |
Seconds to wait before batching edits into a commit (max 30) |
JJ_HOOK_STALE_MIN |
30 |
Minutes before an idle session is reaped by the daemon |
Example in hook command:
"command": "JJ_HOOK_DEBOUNCE_SEC=5 ./scripts/jj-hook.sh post-edit"
Spec-Driven Workflow
The second template layer: portable cross-agent conventions that sit on top of whatever commit infrastructure you use. Works with or without the jj-commitd daemon above; works with or without jj (though the skills document jj-first commands).
Step 1: Copy the portable files
From the template root into your project:
cp /path/to/template-jj/AGENTS.md .
cp /path/to/template-jj/CLAUDE.md .
cp -r /path/to/template-jj/dev .
mkdir -p .claude && cp -r /path/to/template-jj/.claude/skills .claude/
chmod +x dev/tools/install-hooks.sh dev/tools/pre-push-guard.sh
The template's CLAUDE.md contains a pointer to AGENTS.md, Claude-specific sub-agent guidance, and a "Template-maintenance" section with jj-commitd internals. The first two travel cleanly; drop the "Template-maintenance" section once copied — those notes apply to the template's own repo, not your project.
Step 2: Replace placeholder tokens
The portable files use placeholder tokens so you can slot your project's specifics in. Find them:
rg -n '<[A-Z_]+>' AGENTS.md CLAUDE.md dev/ .claude/skills/
Replace each across the files (find-and-replace, a scripted sed, or ad-hoc as team members encounter them):
| Token | Example values |
|---|---|
<PROJECT_NAME> |
my-app, acme-core |
<PRIMARY_LANGUAGE> |
Zig, Rust, Python, Go, TypeScript |
<TEST_COMMAND> |
pytest, npm test, go test ./..., cargo test |
<SOURCE_ROOT> |
src/, lib/, pkg/ |
<UPSTREAM_BLOCKED_PATTERN> |
upstream-org/repo (for forks), or leave unset for greenfield projects |
The <YYWWD> and <feature-slug> tokens inside plan-directory paths are runtime-generated by the skills — leave those as-is.
Step 3: Install the optional upstream-leak pre-push guard
If this repo is a fork and dev/ artifacts should never land on the upstream:
export UPSTREAM_BLOCKED_PATTERN='upstream-org/upstream-repo' # add to .envrc or shell profile
./dev/tools/install-hooks.sh
The guard at .git/hooks/pre-push (symlinked to dev/tools/pre-push-guard.sh) enumerates every path touched by commits in the push range and refuses pushes touching dev/ paths to any remote whose URL contains the pattern. Greenfield projects with no upstream-leak concern can skip this step — the guard is a no-op when UPSTREAM_BLOCKED_PATTERN is unset.
Step 4: Choose a parallel-agent convention
If multiple agents will run concurrently in this repo:
- Preferred:
jj workspace addper agent. Each agent gets its own working directory sharing the same.jj/object store, so branch switches in one don't flip another's tree. Seedev/README.md→ "Primary isolation: jj workspaces". - Fallback:
$AGENT_ROLE/$AGENT_BOOKMARKenv vars. Each agent session exports its intended role and bookmark; the skills'§0 Pre-flightstep checks for mismatches before any write. Seedev/README.md→ "Env var conventions".
Solo projects can skip this — the convention matters only when two or more agents share a repo.
Step 5: Verify
# Confirm the eight skills are discoverable by Claude Code:
ls .claude/skills/
# Should list: codex copilot create-design create-prd gemini generate-tasks kiro process-task-list
# Confirm dev/ layout:
ls dev/
# Should list: QUICKSTART.md README.md notes plans research tools
# If you installed the upstream guard, verify the symlink:
ls -la .git/hooks/pre-push
# Should show a symlink to ../../dev/tools/pre-push-guard.sh
# Claude Code should now recognize /create-prd, /create-design, /generate-tasks, /process-task-list,
# and the per-tool skills (codex, copilot, gemini, kiro) as slash commands.
Step 6: Drive your first feature
From a Claude Code session, type /create-prd and describe the feature. The skill chain will walk you through PRD → (optional design) → tasks → implementation, with codex peer-reviews between stages. See dev/QUICKSTART.md for the short version.
Mixing with other agent tools
The .claude/skills/ pack is readable documentation even for tools that don't natively load skills:
- GitHub Copilot CLI reads
AGENTS.mdautomatically. The skills are referenced as docs it can fetch via@.claude/skills/codex/SKILL.mdetc. - Codex CLI, Gemini CLI, Kiro CLI don't auto-load skill packs but respect
@-file references in prompts. - Cursor / other IDEs typically read
AGENTS.mdor.cursorrules; point those atAGENTS.md.
If you use multiple agents, per-tool session logs (codex-sessions.md, copilot-sessions.md, gemini-sessions.md, kiro-sessions.md) keep the review lineage unambiguous for each feature.