| name | jj-opencode |
|---|---|
| description | JJ VCS integration - describe intent before every edit |
| alwaysApply | true |
Blocks edits until you describe your intent. Every logical unit of work gets its own commit.
jj describe -m "Add input validation" ← declare intent, unlocks editing
[edit files] ← edits go to this commit
[session ends] ← auto-commits, locks editing
jj describe -m "Add tests" ← declare next intent, unlocks
[edit files]
[session ends] ← auto-commits, locks editing
jj new runs automatically when the session goes idle. No manual commit needed.
If something goes wrong: jj undo reverts the last operation.
Until jj describe -m "description" is run:
write,editlsp_rename,lsp_code_action_resolveast_grep_replace
| Task | Command |
|---|---|
| Start work | jj describe -m "what you're about to do" |
| Finish work | (automatic on session idle) |
| Check status | jj st |
| View history | jj log |
| Undo | jj undo |
| Abandon current work | jj abandon @ |
| Push to remote | jj_push tool (or specify bookmark: jj_push bookmark="feature") |
Safely pushes changes to a bookmark (defaults to main).
jj_push ← push to main
jj_push bookmark="feature" ← push to feature branch (user must specify)
The tool auto-detects what to push:
- If
@has changes → pushes@ - If
@is empty (common after session idle) → pushes@- - If both empty → searches for unpushed commits, requires confirmation
Flow:
- Shows preview with commits and files to push
- Requires user confirmation
- Moves bookmark → pushes → verifies clean working copy
Important: Only specify a bookmark if the user explicitly requested it.
If a subagent tries to edit without a description, it will be blocked and instructed to return to the parent agent. Only the primary agent should manage JJ workflow (describe, new, push).
- Guaranteed separation —
jj newruns automatically, re-engaging the gate - Never lose work — every edit is in a described commit
- Clear history —
jj logshows what happened step by step - No WIP commits — every commit has meaning
When session goes idle, the plugin auto-detects whether to chain or branch:
| Parent (@-) State | Action | Result |
|---|---|---|
| Has changes | jj new @- |
Child of last work (linear chain) |
| Empty | jj new |
Sibling from main |
Linear chain (default when continuing work):
main@origin
│
◉ "Add validation"
│
◉ "Add tests" ← child of previous
│
@ (current)
Siblings (when @- was empty or starting fresh):
main@origin
├── ◉ "Add validation" (message 1)
├── ◉ "Add tests" (message 2) ← siblings
└── @ (current)
If you have siblings that represent one logical unit of work, squash before pushing.
Run jj log before push. If you see siblings from same parent that represent one logical unit of work:
@ (empty)
│
│ ◉ "Fix typo in validation"
├─╯
│ ◉ "Add tests for validation"
├─╯
│ ◉ "Add input validation"
├─╯
◆ main@origin
These three commits are related work — offer to squash.
# Create merge commit from siblings
jj new sibling1 sibling2 sibling3 -m "Add input validation with tests"
# Squash the merge into single commit (now a child of main)
jj squashResult:
@ (empty)
│
◉ "Add input validation with tests" ← combined work
│
◆ main@origin
| See This | Do This |
|---|---|
| Multiple siblings, related work | Offer squash before push |
| Multiple siblings, unrelated work | Push as-is (or separate bookmarks) |
| Linear chain (parent→child→...) | Push as-is |
Prompt: "I see N related commits. Want me to squash them into one before pushing?"
Git thinking (WRONG):
Push parent commit → Push child commit → Push next...
JJ thinking (CORRECT):
Move bookmark to tip → Push once (all ancestors included automatically)
@ (commit C - docs)
│
◉ (commit B - feature)
│
◆ main@origin (commit A)
WRONG: Push B first, then push C ← Creates immutability issues
RIGHT: Point main at C, push once ← B and C both pushed
- Ancestors are automatic — pushing a bookmark pushes ALL commits between
bookmark@originand the new target - Commits become immutable after push — you can't squash/rewrite them
- No sequential pushing needed — one push, all commits
| Don't | Do Instead |
|---|---|
| Push commits one at a time | Push bookmark once (includes all ancestors) |
| Squash after pushing | Squash BEFORE pushing (commits are immutable after) |
| Think "I need to push each commit" | Think "move bookmark to tip, push bookmark" |
| Git | JJ |
|---|---|
git status |
jj st |
git log |
jj log |
git diff |
jj diff |
git add && git commit |
Not needed |
git push |
jj git push -b main |
Just call jj_push — it auto-detects the right commit and shows a preview.
Wait for explicit "yes" before calling with confirmed: true.