Add CUPS print server container for Brother HL-4150CDN

This commit is contained in:
juergen committed 2026-10-18 09:16:37 +02:00
commit 6f3162849b
52 files changed
+5168

No files matched your search

+333
View File
@@ -0,0 +1,333 @@
---
name: jujutsu
description: Use this skill for any version control operations (commit, log, diff, push, fetch, bookmark, workspace, rebase, undo, etc.). If a `.jj` directory exists, the repo is jujutsu and git mutations will corrupt it — use `jj` for all mutations; read-only `git log/diff/show/blame/grep` are allowed. Covers co-located repos, multi-agent parallel workspaces, and operation-log recovery. **DO NOT IGNORE**
allowed-tools: Bash(jj *)
license: Apache-2.0
metadata:
author: johnstegeman
version: "1.3"
---
# Jujutsu (jj) Version Control System
This skill helps you work with Jujutsu, a Git-compatible VCS with mutable commits and automatic rebasing.
**Tested with jj v0.41.0** - Commands may differ in other versions.
For token-sensitive inspection commands, prefer the compact commands in `references/TEMPLATES.md` over jj's default output.
The compact commands in `references/TEMPLATES.md` intentionally use `jj --no-pager ...`.
For revsets, filesets, and template syntax, see `references/QUERY_LANGUAGES.md`.
For sibling or unrelated work that should start from `trunk()`, see `references/NONLINEAR.md`.
For common surprises and easy mistakes, see `references/GOTCHAS.md`.
<!--
Attribution and inspiration sources:
- Forked from danverbraganza/jujutsu-skill (https://skills.sh/danverbraganza/jujutsu-skill/jujutsu).
The original scaffolding, references/ structure, and core mental-model wording are from there.
- Enforcement language in "Critical: never use git for mutations" was tightened using the
read-only-git allowlist pattern from knoopx/pi@jujutsu
(https://skills.sh/knoopx/pi/jujutsu).
- "Parallel Workspaces" framing as a tool for multi-agent isolation draws on
onevcat/skills@onevcat-jj (https://skills.sh/onevcat/skills/onevcat-jj),
which is explicitly subtitled "Version Control for Agent Workflows".
- The .workspaces/ project-local directory convention, the .gitignore safety check,
and the directory-selection priority documented in references/WORKSPACES.md
come from edmundmiller/dotfiles using-jj-workspaces
(https://lobehub.com/skills/edmundmiller-dotfiles-using-jj-workspaces).
- The "op log records every mutation; nothing is ever truly lost" framing in the
Recovery section is adapted from trevors/dot-claude@jj-workflow
(https://skills.sh/trevors/dot-claude/jj-workflow).
- Query-language guidance, nonlinear-work patterns, and workspace handoff /
monitoring ideas were adapted from joshuadavidthomas/agent-skills `jj`
(https://github.com/joshuadavidthomas/agent-skills/tree/main/jj) and its
`workspaces.md`
(https://raw.githubusercontent.com/joshuadavidthomas/agent-skills/main/jj/workspaces.md),
while intentionally preserving this skill's local describe-first workflow and
`.workspaces/` / `workspaces/` directory priority.
-->
## Detecting a jj repo
Check whether a directory is part of a jj repo:
```bash
jj root
```
If it returns a path, the repo is jj-managed. If it returns `Error: There is no jj repo in <directory>`, it isn't. The presence of a `.jj/` folder is another giveaway. If the repo is not a jj one, do not further use this skill.
<!-- Enforcement wording (allowed/forbidden split with read-only git allowlist) adapted from knoopx/pi@jujutsu. -->
## Critical: never use git for mutations in a jj repo
In a jj repo (including co-located repos that also have `.git/`):
- **Forbidden** (corrupts jj state): `git commit`, `git add`, `git stash`, `git reset`, `git checkout <branch>`, `git switch`, `git rebase`, `git merge`, `git cherry-pick`, `git push`, `git pull`.
- **Allowed** (read-only): `git log`, `git show`, `git diff`, `git blame`, `git grep`, `git status`. Prefer the `jj` equivalents (`jj log`, `jj show`, `jj diff`, etc.) but read-only git commands won't break anything.
- **Use jj instead**: `jj git push`, `jj git fetch`, `jj edit <change>` (not `git checkout`), `jj rebase`, `jj new <a> <b>` (for merges).
See `references/COLOCATED.md` for the full co-located repo workflow.
## Important: Automated/Agent Environment
When running as an agent or running on behalf of the user:
1. **Always use `-m` flags** to provide commit messages inline rather than relying on editor prompts:
```bash
# Always use -m to avoid editor prompts
jj desc -m "message" # NOT: jj desc
jj squash --into @- -m "message" # NOT: jj squash (which opens editor or prompts for hunks)
```
Editor-based commands will fail in non-interactive environments.
2. **Verify operations with `jj st`** after mutations (`squash`, `abandon`, `rebase`, `restore`) to confirm the operation succeeded.
## Important Principles
1. Each commit should represent one logical change. See [commit messages](references/COMMIT_MSG.md) for details on good commit messages. Before starting work on a new change, make sure the working copy is clean (no changes)
```bash
jj st
```
## Core Concepts
### The Working Copy is a Commit
In jj, your working directory is always a commit (referenced as `@`). Changes are automatically snapshotted when you run any jj command. There is no staging area.
There is no need to run `jj commit`.
### Commits Are Mutable
**CRITICAL**: Unlike git, jj commits can be freely modified. This enables a high-quality commit workflow:
1. Before starting work, run `jj st`. If `@` already has changes, run `jj new` first. If `@` is empty, use it as-is.
2. Describe your intended changes with `jj desc -m "Message"`
3. Make your changes.
4. Do NOT run `jj new` when finished — leave that to the next task's step 1.
You may refine the commit using `jj squash` or `jj absorb` as needed
### Change IDs vs Commit IDs
- **Change ID**: A stable identifier (like `tqpwlqmp`) that persists when a commit is rewritten
- **Commit ID**: A content hash (like `3ccf7581`) that changes when commit content changes
Prefer using Change IDs when referencing commits in commands.
## Essential Workflow
### Starting Work: Describe First, Then Code
**Always create your commit message before writing code:**
```bash
# First, describe what you intend to do
jj desc -m "feat: Add user authentication to login endpoint"
# Then make your changes - they automatically become part of this commit
# ... edit files ...
# Check status
jj st
```
For the full starting-a-change workflow (when to reuse `@` vs `jj new`, anti-patterns, decision logic), see `references/NEW_CHANGE.md`.
For unrelated or sibling work that should branch from `trunk()`, see `references/NONLINEAR.md`.
### Creating Atomic Commits
Each commit should represent ONE logical change.
### Viewing History
```bash
# View recent commits
jj log
# View with patches
jj log -p
# View specific commit
jj show <change-id>
# View diff of working copy
jj diff
```
If you need to run `jj log`, `jj show`, or `jj diff` as an agent, use the matching compact command from `references/TEMPLATES.md` unless the full default output is explicitly needed.
For the revsets, filesets, and template syntax behind those compact commands, see `references/QUERY_LANGUAGES.md`.
### Moving Between Commits
```bash
# Create a new empty commit on top of current
jj new
# Create new commit with message
jj new && jj desc -m "Commit message"
# Edit an existing commit (working copy becomes that commit)
jj edit <change-id>
# Edit the previous commit
jj prev -e
# Edit the next commit
jj next -e
```
## Refining Commits
If you need to refine a commit (squash, abandon, undo, split, etc), see (Refining commits)[references/REFINE_COMMIT.md]
## Working with Bookmarks (Branches)
Bookmarks are jj's equivalent to git branches:
```bash
# Create a bookmark at current commit
jj bookmark create my-feature -r@
# Move bookmark to a different commit
jj bookmark move my-feature --to <change-id>
# List bookmarks
jj bookmark list
# Delete a bookmark
jj bookmark delete my-feature
```
If you need to run `jj bookmark list`, use the matching compact command from `references/TEMPLATES.md`.
<!--
Multi-agent workspace framing inspired by onevcat/skills@onevcat-jj.
The .workspaces/ project-local convention codified in references/WORKSPACES.md
is adapted from edmundmiller/dotfiles using-jj-workspaces.
-->
## Parallel Workspaces
For running multiple agents in parallel against the same repo without working-copy collisions, use `jj workspace`. Each workspace gets its own `@` while sharing the underlying repo store.
```bash
# Quick reference (full convention in references/WORKSPACES.md)
jj workspace add .workspaces/<repo>-<purpose> # Create
jj workspace list # List
jj workspace forget <name> # Untrack (files stay on disk)
```
If you need to run `jj workspace list`, use the matching compact command from `references/TEMPLATES.md`.
See `references/WORKSPACES.md` for the full convention: the `.workspaces/` directory layout, `.gitignore` safety check, naming, environment bootstrapping, agent handoff, monitoring, stale-workspace recovery, and merging results from multiple workspaces back together.
## Working with tags
jujutsu does not yet support tags and pushing them to a remote. If you need to tag (such as for a release), you will need to use git to create and push the tags.
## Git Integration (Co-located Repos)
Always use co-located repos when working with existing git projects. They give you jj locally while keeping the repo remote-compatible with git tooling.
```bash
jj git clone <url> --colocate # Clone a git repo
jj git init --colocate # Adopt an existing git repo
```
See `references/COLOCATED.md` for the complete workflow: the mutation rule (`git` is forbidden for mutations, allowed read-only), the git-command allowlist, common gotchas, and switching modes when absolutely necessary.
See `references/GOTCHAS.md` when the behavior feels surprising and you want the short list of jj-specific footguns before trying a git-style workaround.
### Pushing Changes
For pushing bookmarks to a remote, see `references/PUSH.md`. For named remotes, fork+upstream setups, and other multi-remote workflows, see `references/REMOTES.md`.
## Handling Conflicts
jj treats conflicts as first-class objects — a commit can contain a conflict, and that conflict persists through rebases until resolved. `jj` refuses to push conflicted commits, so they must be resolved before `jj git push`.
```bash
jj st # Reports if @ has unresolved conflicts
jj resolve --list # Lists conflicted paths
jj resolve --tool :ours # Take side #1 non-interactively (agent-safe)
jj resolve --tool :theirs # Take side #2 non-interactively (agent-safe)
```
**Never run bare `jj resolve` (no `--tool`)** — it's interactive and will hang in non-interactive agent environments. Either pass `--tool :ours` / `--tool :theirs`, or edit conflict markers directly in the file and let `jj st` snapshot the resolution.
See `references/CONFLICTS.md` for the full resolution playbook: marker formats (diff vs snapshot), five resolution paths, ancestor-level resolution to avoid re-resolving across descendants, verification (beyond `jj st`), and anti-patterns.
<!-- Recovery framing ("op log records every mutation; nothing is ever lost") adapted from trevors/dot-claude@jj-workflow. -->
## Recovery and the Operation Log
The op log records every mutation. Nothing is ever lost.
```bash
jj undo # Reverse the last operation (primary recovery tool)
jj op log # See all operations
jj op restore <op-id> # Jump back to any past state
jj evolog -r <change> # See how a specific change evolved
```
If you need to run `jj op log` or `jj evolog`, use the matching compact commands from `references/TEMPLATES.md` first. Add `-p` only after you have narrowed the scope.
**Always reach for `jj undo` first** when something looks wrong — it's always correct and safe. See `references/RECOVERY.md` for recovery patterns by scenario (bad squash, accidental abandon, lost work, etc.).
## Preserving Commit Quality
**IMPORTANT**: Because commits are mutable, always refine them:
1. **Review your commit**: `jj show @` or `jj diff`
2. **Is it atomic?** One logical change per commit
3. **Is the message clear?** Use imperative verb phrase in sentence case format with no full stop: "Verb object"
4. **Are there unrelated changes?** Use `jj restore` to move changes out, then create separate commits
5. **Should changes be elsewhere?** Use `jj squash` or `jj absorb`
## Quick Reference
| Action | Command |
|--------|---------|
| Describe commit | `jj desc -m "message"` |
| View status | `jj st` |
| View log | `jj log` |
| View diff | `jj diff` |
| New commit | `jj st` then `jj new` only if `@` has changes, then `jj desc -m "message"` |
| Edit commit | `jj edit <id>` |
| Squash to parent | `jj squash --into @- -m "message"` |
| Auto-distribute | `jj absorb` |
| Abandon commit | `jj abandon <id>` |
| Undo last operation | `jj undo` |
| Restore files | `jj restore [paths]` |
| Create bookmark | `jj bookmark create <name>` |
| Push bookmark | `jj git push -b <name>` |
| Add workspace | `jj workspace add .workspaces/<repo>-<purpose>` |
| List workspaces | `jj workspace list` |
| Forget workspace | `jj workspace forget <name>` |
| See operation log | `jj op log` |
| Jump to past state | `jj op restore <op-id>` |
| See change evolution | `jj evolog -r <id>` |
## Best Practices Summary
1. **Describe first**: Set the commit message before coding
2. **One change per commit**: Keep commits atomic and focused
3. **Use change IDs**: They're stable across rewrites
4. **Refine commits**: Leverage mutability for clean history
5. **Embrace the workflow**: No staging area, no stashing — the op log replaces both
6. **Workspaces for parallel agents**: Isolate concurrent agent work in `.workspaces/<repo>-<purpose>` (see `references/WORKSPACES.md`)
7. **`jj undo` first**: When something looks wrong, undo before fixing manually (see `references/RECOVERY.md`)
@@ -0,0 +1,4 @@
interface:
display_name: "Jujutsu"
short_description: "Help with Jujutsu version control workflows"
default_prompt: "Use $jujutsu to inspect this repo, choose the right jj workflow, and carry the change through safely."
@@ -0,0 +1,81 @@
# Co-located jj + git Repositories
A **co-located** repo has both `.jj/` and `.git/` at the same root. jj manages local operations; git stays in sync underneath so remote-side tooling (CI, GitHub, hooks, IDE plugins, `gh` CLI) continues to work normally.
This is the **strongly recommended** setup for working with an existing git project.
## Why co-located
- **Local: jj's superior model.** Mutable commits, no staging area, the op log as a safety net.
- **Remote: still git.** GitHub sees ordinary commits and refs. Collaborators don't need to know you use jj.
- **Tooling parity.** IDE git integrations, pre-commit hooks, `gh`/`git` CLI for read-only inspection all continue to work.
## Creating a co-located repo
```bash
# From scratch
jj git init --colocate
# From a remote
jj git clone <url> --colocate
# Adopt an existing git repo (run from inside it)
cd existing-git-repo
jj git init --colocate
```
After this, both `.jj/` and `.git/` exist and stay synchronized automatically. **Never run `jj git init` without `--colocate`** on a project you already have git history for — non-colocated init creates a separate git store inside `.jj/repo/store/git` which is harder to work with.
## The mutation rule
> **Never use `git` for mutations in a co-located jj repo.** Read-only `git` is fine.
| git command | Allowed in jj repo? |
|---|---|
| `git log`, `git show`, `git diff`, `git blame`, `git grep` | ✅ Allowed (jj equivalents preferred) |
| `git status` | ✅ Allowed (shows git's view; may differ from `jj st`) |
| `git commit`, `git add`, `git stash`, `git reset` | ❌ Forbidden — corrupts jj state |
| `git checkout <branch>`, `git switch` | ❌ Forbidden — use `jj edit` instead |
| `git rebase`, `git merge`, `git cherry-pick` | ❌ Forbidden — use `jj rebase`, `jj new <a> <b>`, `jj duplicate` |
| `git fetch` | ⚠️ Use `jj git fetch` — it keeps jj in sync |
| `git pull` | ❌ Forbidden — `git pull` does fetch+merge; use `jj git fetch` + `jj rebase` |
| `git push` | ❌ Forbidden — use `jj git push -b <bookmark>` |
The reason: jj snapshots the working copy on every jj command. If git mutates state behind jj's back, jj's next snapshot will be confused about what changed when, and the op log can get out of sync.
## Switching to git mode (rare; usually a smell)
If you genuinely need to use a git workflow for one operation (e.g., an external tool insists), do it carefully:
```bash
# 1. Ensure jj working copy is committed and clean
jj st
# (resolve anything pending — describe, refine, etc.)
# 2. Now run the git operation
git <something>
# 3. Return to jj — jj will re-sync on the next command
jj st # Triggers re-sync
jj edit <change-id> # Resume work
```
**Almost every git operation has a jj equivalent that's safer.** Reach for git mode only as a last resort.
## Common gotchas
- **Git complains about uncommitted changes**: jj's working copy is always a commit; git sees that as "uncommitted." Use `jj st` (not `git status`) to assess state.
- **HEAD detachment**: jj manages `HEAD` per change. `git checkout` will detach HEAD in confusing ways — use `jj edit` instead.
- **`.gitignore` is respected by jj** automatically. `.jjignore` is only needed if you want jj-only ignore patterns the outer git doesn't see.
- **Branches vs bookmarks**: git branches become jj bookmarks on fetch. Bookmarks don't auto-advance like git branches — you must `jj bookmark move` before pushing.
## Pushing from a co-located repo
Use `jj git push`, not `git push`:
```bash
jj bookmark move main --to @ # Move bookmark to your latest change
jj git push -b main # Push to remote
```
See `references/PUSH.md` for the full push workflow.
@@ -0,0 +1,25 @@
# Creating good commit messages
1. Use the following format:
```
<type>: <subject>
<body>
```
The valid <type> values are: fix, feat, chore, docs
Type and subject are required
Body is optional
2. If there is a body, separate it from the subject with a blank line
3. Limit the subject line to 50 characters. This is not a hard limit but a rule of thumb. The subject line should explain the "what" of the logical change
4. Capitalize the subject line
5. Do not end the subject line with a period
6. Use the imperative mood in the subject line
Imperative mood just means “spoken or written as if giving a command or instruction”. A few examples:
* Clean your room
* Close the door
* Take out the trash
7. Wrap the body at 72 characters
8. Use the body to explain what and why vs. how
@@ -0,0 +1,176 @@
# Resolving Conflicts
jj treats conflicts as **first-class objects**: a commit can contain a conflict, and that conflict **persists through rebases** until someone resolves it. This is the headline mental-model difference from git, where a working tree must be conflict-free before committing.
For an agent, this changes the rhythm:
- A `jj rebase` or `jj squash` that produces a conflict **does not fail**. It succeeds; the resulting commit is just marked conflicted.
- You can keep working — even keep rebasing the conflicted commit further — and resolve it later.
- `jj` won't let you push a conflicted commit to a remote, so conflicts must be resolved before `jj git push`.
## Detecting conflicts
```bash
jj st # Shows "There are unresolved conflicts" if any
jj resolve --list # Lists all conflicted paths in @
jj resolve --list -r <change-id> # Lists conflicts in a specific change
jj log # Conflicted changes are shown with a "conflict" marker
```
## Conflict marker formats
jj supports three marker styles (set via `ui.conflict-marker-style`):
- **`diff`** (modern default) — shows base→side as a diff
- **`snapshot`** — full content of each side, like git's `<<<<<<<` style
- **`git`** — legacy git-compatible 3-way
Check your style:
```bash
jj config get ui.conflict-marker-style
```
### Example: `diff` style
```
<<<<<<< Conflict 1 of 1
%%%%%%% Changes from base to side #1
-base content here
+side 1 content here
+++++++ Contents of side #2
side 2 content here
>>>>>>> Conflict 1 of 1 ends
```
- Side #1 = "ours" (the destination — typically your current branch/change)
- Side #2 = "theirs" (the source — typically what's being rebased/merged in)
- The `%%%%%%%` block shows what side #1 changed *from the common base*; side #2's content is shown directly.
### Example: `snapshot` style
```
<<<<<<< Conflict 1 of 1
+++++++ Contents of side #1
side 1 content here
%%%%%%% Contents of base
base content here
+++++++ Contents of side #2
side 2 content here
>>>>>>> Conflict 1 of 1 ends
```
Both styles delimit conflict regions identically. To resolve: replace the entire `<<<<<<< ... >>>>>>>` block with the desired final content.
## Five resolution paths (agent-friendly)
`jj resolve` with no `--tool` launches an interactive merge tool and **will hang in non-interactive agent environments**. Use one of these instead:
### 1. Edit conflict markers directly (most common)
Read the file, replace each `<<<<<<<` block with the resolved content, save. Then snapshot:
```bash
jj st # Triggers snapshot; should report no remaining conflicts
```
This is the canonical agent path. Works for any merge complexity.
### 2. Take one side wholesale: `:ours` / `:theirs`
For conflicts where one side simply wins (e.g., a rebase brings in unrelated formatting changes you don't want):
```bash
jj resolve --tool :ours # Keep side #1; drop side #2's conflicting changes
jj resolve --tool :theirs # Keep side #2; drop side #1's conflicting changes
# Scope to specific paths
jj resolve --tool :ours path/to/file.rs
```
These built-in tools are **non-interactive** and safe to use in agent contexts. They apply to all conflicts in `@` (or the paths given).
### 3. Restore from a specific version: `jj restore --from`
When neither side is right but a *different* revision has the content you want:
```bash
jj restore --from <change-id> path/to/file.txt
```
This pulls the file contents from `<change-id>` into the working copy, replacing conflict markers entirely.
### 4. Abandon a hopelessly conflicted change
If the conflicted change wasn't worth keeping:
```bash
jj abandon <change-id>
```
Descendants are rebased onto the parent. Their own conflicts (if any) remain to be resolved separately.
### 5. Resolve at the ancestor, let jj re-propagate
If a rebase produced the same conflict in many descendants, **don't resolve each descendant individually**. Resolve once at the change where the conflict first appeared, and jj will re-propagate the resolution forward:
```bash
# Find where the conflict first appeared
jj log -r 'conflicts()'
# Edit that change directly
jj edit <ancestor-change-id>
# ... fix conflict markers ...
jj st # Snapshot the resolution
jj edit @- # Move back to where you were
```
Descendants pick up the resolution automatically.
## Verifying a resolution
After editing, **don't trust `jj st` alone**:
```bash
# 1. Status should report no conflicts
jj st
# 2. Grep for residual markers (catches typos that left markers in place)
git grep -E '^(<{7}|>{7}|\\%{7}|\\+{7})' || echo 'no residual conflict markers'
# 3. Run the project's tests/linter — markers may be syntactically valid in some
# languages (e.g. inside strings) and parse-tests catch what eyeballing misses.
```
In a colocated repo, `git grep` is allowed (read-only). In a non-colocated repo, use `grep -rE` or `rg`.
## Pushing conflicted commits is blocked
```bash
jj git push -b main
# Refused: branch main contains conflicts
```
You'll need to resolve before pushing. There's no `--force-push-conflicts` option — this is by design.
## Anti-patterns
- ❌ **`jj resolve` (no `--tool`)** — interactive; will hang the agent. Always pass `--tool :ours` / `--tool :theirs` or edit markers manually.
- ❌ **Resolving descendants individually after a rebase** — wasteful and error-prone; resolve at the ancestor (path 5).
- ❌ **Relying on `jj st` to verify resolution** — also grep for residual markers and run tests.
- ❌ **Trying to `git checkout` to "drop" a conflict** in a colocated repo — corrupts jj state. Use `jj abandon` or resolve.
- ❌ **`jj squash` to "hide" a conflict into another commit** — the conflict propagates; it doesn't disappear.
## Quick reference
| Action | Command |
|---|---|
| List conflicts in `@` | `jj resolve --list` |
| List conflicts everywhere | `jj log -r 'conflicts()'` |
| Resolve manually | Edit file, then `jj st` |
| Take "ours" (side #1) | `jj resolve --tool :ours [paths...]` |
| Take "theirs" (side #2) | `jj resolve --tool :theirs [paths...]` |
| Restore from another rev | `jj restore --from <change-id> <path>` |
| Drop the change | `jj abandon <change-id>` |
| Find conflict's ancestor | `jj log -r 'conflicts() ~ ::conflicts()-'` |
| Check marker style | `jj config get ui.conflict-marker-style` |
@@ -0,0 +1,47 @@
# Gotchas
Short list of jj behaviors that regularly surprise agents.
**Tested with jj 0.41.0**
## 1. Working Copy Is a Commit
`@` is always a commit, not a staging area. `jj new` starts a different change; it does not finish the current one.
## 2. Most jj Commands Snapshot the Working Copy
Running `jj st`, `jj log`, and many other commands can record filesystem changes into the working-copy commit. If you intentionally need a stale view, use `--ignore-working-copy`.
## 3. Change IDs and Commit IDs Are Different
Change IDs are stable across rewrites. Commit IDs change whenever content or metadata changes. Prefer change IDs when handing work to another agent.
## 4. `jj undo` Is the First Recovery Tool
Do not manually reverse a bad `rebase`, `squash`, or `abandon` before trying `jj undo`. Use `references/RECOVERY.md` for deeper recovery.
## 5. Pager Output Is Not a Good Agent Default
Use the compact commands in `references/TEMPLATES.md`. Those commands intentionally use `jj --no-pager ...`.
## 6. Co-located Repos Still Forbid Git Mutations
`git log`, `git show`, `git diff`, `git blame`, `git grep`, and `git status` are fine. `git commit`, `git add`, `git rebase`, `git push`, and similar mutations can corrupt jj state.
## 7. `jj workspace forget` Does Not Delete Files
It only unregisters the workspace. Delete the directory separately if cleanup is intended.
## 8. Stale Workspaces Are Normal in Parallel Work
If another workspace rewrites your base, use `jj workspace update-stale`. Verify the result with `jj st` instead of improvising a manual repair.
## 9. Bare `jj resolve` Is Interactive
For agent-safe conflict handling, use `jj resolve --tool :ours`, `jj resolve --tool :theirs`, or edit conflict markers directly.
## 10. Default Output Is Often Too Large
Prefer compact templates, targeted revsets, and narrow filesets before reaching for full `jj log -p` or `jj show`.
When behavior feels surprising, stop and check `references/NEW_CHANGE.md`, `references/TEMPLATES.md`, `references/WORKSPACES.md`, or `references/RECOVERY.md` before inventing a git-style workaround.
@@ -0,0 +1,49 @@
# Starting a New Change
The jj workflow is: **describe first, then code, then refine**. The working copy is always a commit (`@`), so you're already in one when you start. The question is whether to reuse it or start fresh.
## The opening sequence
```bash
# 1. Check what @ currently looks like
jj st
```
**If `@` is empty** (no changes, no description set, or description is fine to discard):
- Reuse it. Just describe and start.
**If `@` has changes you want to keep** (it's someone else's WIP, or work you haven't finished):
- Start fresh with `jj new` — this creates a new empty commit on top of `@`.
```bash
# 2. Describe before coding (only with -m; never editor-based)
jj desc -m "feat: Add user authentication to login endpoint"
# 3. Make changes — they snapshot to @ automatically on the next jj command
# (no `jj add`, no `jj commit` needed)
# 4. Verify
jj st
```
## Why describe first
Setting the message up front:
- Forces you to articulate the change as one logical unit before writing code (atomic commits)
- Means there's never a phase where `@` says "(no description set)"
- Makes the change navigable in `jj log` while it's still being built
See `references/COMMIT_MSG.md` for the message format conventions.
## After the change is done
**Do NOT run `jj new` to "close" the change.** In jj, the working copy is always a commit — there's no separate commit step. Leave `@` as-is; the *next* task's step 1 (`jj st` → reuse or `jj new`) handles transition.
If refinement is needed (split, squash, absorb, abandon), see `references/REFINE_COMMIT.md`.
## Anti-patterns
- ❌ `jj commit` — doesn't exist; the working copy is already a commit
- ❌ `jj desc` without `-m` — opens an editor; will hang in non-interactive agent environments
- ❌ Running `jj new` at the end of a task — leaves an empty `@` for the next agent to clean up
- ❌ Starting code before describing — you'll forget to describe, or describe vaguely after the fact
@@ -0,0 +1,65 @@
# Nonlinear Work
This skill supports sibling commits and unrelated work. Do not force everything onto the current stack if the work is logically independent.
**Tested with jj 0.41.0**
## When to Start a Sibling from `trunk()`
Use a fresh sibling when:
- `@` already represents one logical change and the new work is unrelated
- the user wants multiple independent lines of work
- you need to compare or merge two alternative implementations
- a quick fix appears while a feature change is already in progress
## Preferred Flow
This skill keeps the local "describe first" rule, even for sibling work:
```bash
jj st
jj new trunk()
jj desc -m "fix: handle stale workspace recovery"
# make changes
jj st
```
Use `trunk()` instead of guessing `main` or `master`. If the new work should build on the current change, use plain `jj new` instead of `jj new trunk()`.
## Child vs Sibling
Use a child (`jj new`) when:
- the new work depends on the current change
- the user wants a stacked sequence
- the current commit is intentionally being split into follow-up commits
Use a sibling from `trunk()` when:
- the work should review, land, or be abandoned independently
- you are branching off to do an unrelated fix, doc change, or experiment
- parallel agents need clearly separated lines of history
## Merge-Shaped Work
If two independently meaningful lines of work should remain visible as a merge, create an explicit merge commit:
```bash
jj new <left-change> <right-change> -m "merge: combine related lines of work"
```
Ask the user before choosing between:
- rebasing one line onto the other
- keeping a visible merge
- pushing each line separately
## Anti-Patterns
- Do not keep piling unrelated work into the current `@` just because it is convenient.
- Do not adopt a `describe -> code -> jj new -> repeat` loop. In this skill, `jj new` starts the next change; it does not close the current one.
- Do not default to `jj new trunk() --no-edit -m ...` here. Keep the local sequence: choose the base first, then run `jj desc -m ...`.
- Do not hardcode `main` or `master` when `trunk()` expresses the intent.
See `references/NEW_CHANGE.md` for the default change-starting workflow and `references/WORKSPACES.md` for multi-workspace parallelism.
@@ -0,0 +1,38 @@
### Pushing Changes
When the user asks you to push changes:
```bash
# Push a specific bookmark to the remote
jj git push -b <bookmark-name>
# Example: push the main bookmark
jj git push -b main
```
**Before pushing, ensure:**
1. Your bookmark points to the correct commit (bookmarks don't auto-advance like git branches)
2. The commits are refined and atomic
3. The user has explicitly requested the push
**IMPORTANT**: Unlike git branches, jj bookmarks do not automatically move when you create new commits. You must manually update them before pushing:
```bash
# Move an existing bookmark to the current commit
jj bookmark move my-feature --to @
# Then push it
jj git push -b my-feature
```
If no bookmark exists for your changes, create one first:
```bash
# Create a bookmark at the current commit
jj bookmark create my-feature
# Then push it
jj git push -b my-feature
```
For named remotes, multi-remote setups (e.g. fork + upstream), tracking remote bookmarks, and one-time configuration for the two canonical multi-remote workflows, see `references/REMOTES.md`.
@@ -0,0 +1,91 @@
# Query Languages
Use jj's query languages when you need to select revisions, paths, or render compact output without falling back to the default verbose UI.
**Tested with jj 0.41.0**
## Revsets
Use revsets to select commits and workspaces.
Common selectors:
- `@`: current working-copy change
- `@-`: parent of the current working-copy change
- `trunk()`: configured mainline; use this instead of guessing `main` vs `master`
- `::@`: all ancestors of `@`
- `trunk()..@`: work reachable from `@` that is not on `trunk()`
- `working_copies()`: all workspace working-copy commits
- `<workspace>@`: another workspace's working-copy commit
- `<change-id>`: a stable change ID such as `wqzktpqm`
Agent patterns:
```bash
# Current work relative to trunk
jj --no-pager log -r 'trunk()..@'
# All active workspace tips
jj --no-pager log -r 'working_copies()'
# One specific workspace tip
jj --no-pager log -r '<workspace>@'
```
Quote revsets that contain punctuation or function calls.
## Filesets
Use filesets when a command should operate on only part of the tree.
Common patterns:
- `path` or `"path"`: cwd-relative path prefix
- `file:"path"`: exact cwd-relative file path
- `glob:"pattern"`: cwd-relative glob
- `root:"path"`: workspace-relative path prefix
- `~x`: everything except `x`
- `x & y`: intersection
- `x | y`: union
- `x ~ y`: subtract `y` from `x`
Examples:
```bash
# Only markdown files in the current directory
jj diff 'glob:"*.md"'
# Everything in src except snapshots
jj diff 'src ~ glob:"src/**/*.snap"'
# Exact file path
jj diff 'file:"README.md"'
```
## Templates
Use templates when default output is too large or too decorative for agent work.
Common building blocks:
- `++`: concatenate output pieces
- `if(cond, a, b)`: conditional formatting
- `description.first_line()`: one-line summary
- `change_id.shortest(8)`: stable short ID
- `bookmarks`: bookmark names for a commit
- `current_working_copy`, `empty`, `conflict`: useful booleans in `jj log`
Example:
```bash
jj --no-pager log --no-graph -n 5 -T 'change_id.shortest(8) ++ " " ++ if(description, description.first_line(), "(no description)") ++ "\n"'
```
Start from `references/TEMPLATES.md` instead of inventing new templates unless you need something substantially different.
## Rules
- Prefer `trunk()` over hardcoding `main` or `master`.
- Quote revsets and filesets in shell commands when punctuation could be interpreted by the shell.
- Prefer compact `jj --no-pager` inspection commands before widening the query.
- If a query fails to parse, check `jj <command> --help`, `jj help -k revsets`, `jj help -k filesets`, or `jj help -k templates`.
@@ -0,0 +1,118 @@
# Recovery and the Operation Log
The **operation log** records every mutation to the repo — every `jj` command that changed state. Nothing is ever truly lost. This is jj's safety net: an agent that makes a mistake (bad squash, accidental abandon, wrong rebase) can always go back.
## The three recovery primitives
### 1. `jj undo` — reverse the last operation
```bash
jj undo
```
The first thing to reach for. Reverses the most recent operation atomically — works for `abandon`, `squash`, `rebase`, `restore`, `describe`, and most other mutations.
**Prefer `jj undo` over manually reversing changes** — it's always correct and safe.
### 2. `jj op log` — see the full operation history
```bash
jj op log # All operations, newest first
jj op log -n 20 # Limit to 20
jj op log -p # Include diffs of what each op changed
```
If you are inspecting first and do not yet need full diffs, start with the compact `jj op log` command from `references/TEMPLATES.md`. Add `-p` only after you have narrowed the relevant op IDs.
Each operation has an `op-id`. Use these to navigate further back than a single `jj undo`.
### 3. `jj op restore` — jump to any past state
```bash
jj op restore <op-id>
```
Restores the entire repo to the state it was in at that op. **This is the nuke-from-orbit recovery** — it can undo many operations at once. Safe because the restore itself becomes a new op in the log (you can `jj undo` it if you change your mind).
## Per-change history: `jj evolog`
The op log shows everything; `jj evolog` shows how a *single change* evolved (was rewritten, squashed into, rebased, etc.):
```bash
jj evolog # Evolution of the current @
jj evolog -r <change-id> # Evolution of a specific change
jj evolog -p # Include patches between versions
```
If you are inspecting first and do not yet need patches, start with the compact `jj evolog` command from `references/TEMPLATES.md`. Add `-p` only after you have narrowed the relevant version.
Useful when you want to recover an earlier *version* of a specific commit (e.g., "what did this commit look like before I squashed into it?").
## Common recovery scenarios
### Accidentally abandoned the wrong change
```bash
jj undo # If it was just now
# or
jj op log # Find the abandon op
jj op restore <op-id> # Restore to before it
```
### A `jj squash` merged work into the wrong parent
```bash
jj undo # Single-step undo
```
If you've done more work since:
```bash
jj op log # Locate the squash op
jj op restore <op-id> # Jump back; ops after that point are dropped
```
### A rebase went sideways
```bash
jj undo # Reverse the rebase entirely
```
### Lost work after a complex sequence
```bash
jj op log -p # Diffs show exactly what each op did
# Find the op id where the work still existed
jj op restore <op-id>
```
### Need a previous version of a commit's content
```bash
jj evolog -r <change-id> -p # Find the version you want
jj restore --from <commit-id-from-evolog> <path> # Restore working-copy content from it
```
### Restored the wrong op-id
```bash
jj undo # The restore is itself a recorded op
```
## Notes for agents
- **Always reach for `jj undo` first** when something looks wrong. Don't try to manually unwind with new commits or `jj abandon` — `undo` is the canonical safe path.
- **The op log isn't pruned aggressively** — even a week of work is fully retrievable.
- **`jj op restore` is recorded** — it's not destructive in the way `git reset --hard` is. You can always undo a restore.
- **There is no stash/pop in jj** because every state is automatically a commit in the op log. Treat `jj undo` / `jj op restore` as the equivalent.
## Quick reference
| Action | Command |
|---|---|
| Undo last operation | `jj undo` |
| See all operations | `jj op log` |
| See ops with diffs | `jj op log -p` |
| Jump to past state | `jj op restore <op-id>` |
| Change evolution | `jj evolog -r <change-id>` |
| Restore file from past version | `jj restore --from <commit-id> <path>` |
@@ -0,0 +1,77 @@
### Squashing Changes
`jj squash` moves changes from one revision to another. The default is `@` → its parent; pass `--into` to target any other change.
```bash
# Default: squash @ into its parent
jj squash
# Squash @ into a specific change (not the parent)
jj squash --into <change-id>
jj squash -t <change-id> # short form
# Squash a non-@ change into its parent
jj squash -r <change-id>
# Full explicit form: any source, any destination
jj squash --from <source-id> --into <destination-id>
# Squash only specific paths (the rest stay in the source)
jj squash path/to/file.txt
jj squash --into <change-id> src/auth/
```
**Combined description prompt**: if both source and destination have non-empty descriptions, `jj squash` opens an editor to pick a combined description — and hangs in agent environments. Always pass `-m` inline when squashing two described commits together:
```bash
jj squash --into <change-id> -m "feat: Combine auth changes"
```
**Empty source**: when squashing leaves the source revision empty (no content vs. its parent), jj abandons it by default. Pass `--keep-emptied` to keep an empty placeholder instead.
**Interactive forms to avoid**: `jj squash -i` / `jj squash --interactive` open a TUI and will hang in agent environments. Use `--from` / `--into` / paths instead.
### Splitting Commits
**Warning**: `jj split` is interactive and will hang in agent environments. To divide a commit, use `jj restore` to move changes out, then create separate commits manually.
### Absorbing Changes
Automatically distribute changes to the commits that last modified those lines:
```bash
# Absorb working copy changes into appropriate ancestor commits
jj absorb
```
### Abandoning Commits
Remove a commit entirely (descendants are rebased to its parent):
```bash
jj abandon <change-id>
```
### Undoing Operations
Reverse the last jj operation:
```bash
jj undo
```
This reverts the repository to its state before the previous command. Useful for recovering from mistakes like accidental `abandon`, `squash`, or `rebase`.
### Restoring Files
Discard changes to specific files or restore files from another revision:
```bash
# Discard all uncommitted changes in working copy (restore from parent)
jj restore
# Discard changes to specific files
jj restore path/to/file.txt
# Restore files from a specific revision
jj restore --from <change-id> path/to/file.txt
@@ -0,0 +1,164 @@
# Named Remotes and Multi-Remote Workflows
For pushing to a single remote (the common case), see `references/PUSH.md`. This file covers everything else: managing named remotes, selecting which remote to push to, tracking remote bookmarks, and the two canonical multi-remote setups.
## Named remotes
When a repo has more than one remote (e.g. `origin` = your fork, `upstream` = the canonical source), you select the target with `--remote <name>`.
```bash
jj git remote list # See all remotes and their URLs
jj git remote add upstream <url> # Add a second remote
jj git remote rename origin fork # Rename a remote
jj git remote set-url origin <new-url> # Change a remote's URL
jj git remote remove old-name # Remove a remote (and forget its bookmarks)
```
**Default remote selection**: when `--remote` is omitted, `jj git push` uses:
1. The `git.push` config setting (if set), or
2. The remote named `origin` (if multiple remotes exist), or
3. The single remote (if only one exists)
There is **no built-in option to push to multiple remotes in one command** — call `jj git push` once per remote.
### Pushing the same bookmark to two remotes
```bash
jj bookmark move my-feature --to @ # Move the bookmark to current change
jj git push -b my-feature --remote fork # Push to your fork
jj git push -b my-feature --remote upstream # Push to canonical (e.g. for a PR target branch)
```
### Setting a default remote
To avoid typing `--remote` repeatedly:
```bash
# Per-repo (writes to .jj/repo/config.toml)
jj config set --repo git.push fork
# Globally (writes to ~/.config/jj/config.toml)
jj config set --user git.push origin
```
### Tracking a remote bookmark
By default, when you `jj git fetch`, remote bookmarks appear as `<name>@<remote>` (e.g. `main@upstream`). To get a local bookmark that follows it:
```bash
jj bookmark track main@upstream # Track upstream/main as a local 'main'
jj bookmark track 'glob:release/*@upstream' # Track all upstream release branches
```
Once tracked, `jj git push --tracked --remote upstream` will push all tracked bookmarks back to that remote.
### Fetching from a specific remote
```bash
jj git fetch --remote upstream # Fetch only from upstream
jj git fetch --all-remotes # Fetch from every configured remote
```
### When a push is rejected
If the remote has moved since your last fetch, `jj git push` refuses (similar to `git push --force-with-lease`). Re-sync first:
```bash
jj git fetch --remote <name> # Pull the remote's current state
# (resolve any bookmark conflicts or rebase if needed)
jj git push -b <bookmark> --remote <name> # Try again
```
### Quick reference
| Action | Command |
|---|---|
| List remotes | `jj git remote list` |
| Add a remote | `jj git remote add <name> <url>` |
| Push to specific remote | `jj git push -b <bookmark> --remote <name>` |
| Push all tracked bookmarks to a remote | `jj git push --tracked --remote <name>` |
| Set default push remote (repo) | `jj config set --repo git.push <name>` |
| Track a remote bookmark | `jj bookmark track <name>@<remote>` |
| Fetch one remote | `jj git fetch --remote <name>` |
| Fetch all remotes | `jj git fetch --all-remotes` |
## Common multi-remote workflows
Two canonical setups documented in [the jj docs](https://docs.jj-vcs.dev/latest/guides/multiple-remotes/). Pick the one that matches your relationship to the remotes; the config is one-time per repo.
**Nomenclature** (jj convention):
- `origin` = a remote you have write access to (typically where you push)
- `upstream` = the more canonical / well-known repo (you may not have push access)
- Trunk is assumed to be `main`, so remote bookmarks are `main@origin` and `main@upstream`
### Scenario 1: GitHub-style fork contributing upstream
`upstream` is the canonical project; `origin` is your fork; you open PRs from `origin` against `upstream`.
Typical actions:
- Fetch from `upstream` to get the latest changes
- Push `main` to `origin` to keep your fork's `main` in sync
- Push `my-feature` to `origin`, then open a PR targeting `upstream`
One-time setup:
```bash
# Fetch from both remotes when you run `jj git fetch`
jj config set --repo git.fetch '["upstream", "origin"]'
# Default push target is your fork
jj config set --repo git.push origin
# Track both remote bookmarks named `main`
jj bookmark track main
# Make upstream's main the trunk (treated as immutable; rebases stop here)
jj config set --repo 'revset-aliases."trunk()"' main@upstream
```
Why these specifically:
- Tracking `main@upstream` keeps your local `main` updated whenever you fetch from upstream
- Tracking `main@origin` means `jj git push` will also update your fork's `main`
- `trunk() = main@upstream` prevents jj from rewriting commits that are already in the canonical repo
### Scenario 2: Independent repository, periodically integrating from upstream
`origin` is a long-lived repository with its own divergent history; `upstream` is the source you occasionally pull changes from but don't contribute back to.
Typical actions:
- Fetch from `origin` to get the latest of your own work
- Push bookmarks to `origin`; merge PRs into `main@origin`
- Periodically fetch from `main@upstream` and merge/rebase/duplicate its changes into `main@origin`
One-time setup:
```bash
# Fetch only from origin (typical case)
jj config set --repo git.fetch '["origin"]'
# Or fetch from both if you want upstream changes visible on every fetch:
# jj config set --repo git.fetch '["upstream", "origin"]'
# Default push target is origin
jj config set --repo git.push origin
# Track only origin's main — do NOT track upstream's main
jj bookmark track main --remote=origin
jj bookmark untrack main --remote=upstream
# Your origin defines the trunk (your main is the canonical line)
jj config set --repo 'revset-aliases."trunk()"' main@origin
```
Why these specifically:
- Tracking only `main@origin` keeps `main@upstream` as a clearly separate reference you can `rebase`/`merge` from intentionally — it won't silently overwrite your local `main`
- `trunk() = main@origin` treats your divergent line (not upstream's) as the immutable history boundary
### Choosing between the scenarios
| Question | Scenario 1 | Scenario 2 |
|---|---|---|
| Do you push features back to upstream as PRs? | Yes | No |
| Should fetching from upstream update your local `main`? | Yes | No (you integrate manually) |
| Is `upstream/main` immutable from your repo's perspective? | Yes | No (you may rewrite when integrating) |
| Is `origin/main` allowed to diverge from `upstream/main`? | Minimally (just for syncing) | Yes (it's the point) |
@@ -0,0 +1,84 @@
# Token-Reducing Templates
Use inline `-T` templates for compact, agent-friendly jj output. Prefer inline `-T` use over editing repo or global jj config so the skill stays explicit and portable.
Assumptions:
- Assume `jj 0.41.0` or later.
- Each template was tested with `jj 0.41.0`.
- If a template fails to parse, treat that as jj version drift and verify with `jj <command> --help` plus `jj help -k templates`.
- Use the commands below as written. They intentionally put `--no-pager` at the top-level `jj` invocation so agent output does not depend on pager configuration.
## `jj log`
Tested with: `jj 0.41.0`
```bash
jj --no-pager log --no-graph -n 20 -T 'change_id.shortest(8) ++ if(bookmarks, " " ++ bookmarks, "") ++ if(current_working_copy, " @", "") ++ if(empty, " (empty)", "") ++ if(conflict, " (conflict)", "") ++ " " ++ if(description, description.first_line(), "(no description)") ++ "\n"'
```
This keeps the change ID, bookmark or `@`, empty/conflict markers, and the first description line. Use `shortest(8)` instead of bare `shortest()` so IDs do not collapse to one character on `jj 0.41.0`.
## `jj show`
Tested with: `jj 0.41.0`
```bash
jj --no-pager show <change-id> --stat -T 'change_id.shortest(8) ++ if(bookmarks, " " ++ bookmarks, "") ++ if(current_working_copy, " @", "") ++ if(empty, " (empty)", "") ++ if(conflict, " (conflict)", "") ++ " " ++ if(description, description.first_line(), "(no description)") ++ "\n"'
```
Prefer `--stat` for a compact file summary. Swap `--stat` for `--no-patch` if you want the commit summary without any file-level diff summary.
## `jj diff`
Tested with: `jj 0.41.0`
```bash
jj --no-pager diff -T 'self.status_char() ++ " " ++ self.display_diff_path() ++ "\n"'
```
Add `-r <rev>` if you want a diff for a specific revision instead of the working copy.
## `jj bookmark list`
Tested with: `jj 0.41.0`
```bash
jj --no-pager bookmark list -T 'if(self.normal_target(), self.name() ++ ": " ++ self.normal_target().change_id().shortest(8) ++ " " ++ if(self.normal_target().description(), self.normal_target().description().first_line(), "(no description)") ++ "\n", self.name() ++ ": (conflicted)\n")'
```
Use this for ordinary bookmark inspection. If you are explicitly debugging conflicted bookmarks and need every old/new target, fall back to jj's default output.
## `jj workspace list`
Tested with: `jj 0.41.0`
```bash
jj --no-pager workspace list -T 'self.name() ++ ": " ++ self.target().change_id().shortest(8) ++ " " ++ if(self.target().description(), self.target().description().first_line(), "(no description)") ++ if(self.target().empty(), " (empty)", "") ++ "\n"'
```
This shows workspace name, working-copy change ID, description, and whether that working copy is empty.
## `jj op log`
Tested with: `jj 0.41.0`
```bash
jj --no-pager op log --no-graph -n 20 -T 'id.short(8) ++ " " ++ time.start().ago() ++ " " ++ description.first_line() ++ "\n"'
```
Use this first when you need recovery context but do not yet need full diffs. Add `-p` only after you have narrowed down the interesting op IDs.
## `jj evolog`
Tested with: `jj 0.41.0`
```bash
jj --no-pager evolog -r <change-id> --no-graph -n 20 -T 'commit.change_id().shortest(8) ++ " " ++ if(commit.description(), commit.description().first_line(), "(no description)") ++ " | " ++ operation.id().short(8) ++ " " ++ operation.description().first_line() ++ "\n"'
```
This keeps the evolving change summary on the left and the rewriting operation on the right.
## Not Templated
`jj st` and `jj status` do not expose a template flag in `jj 0.41.0`, so leave status output untemplated. The same applies to commands like `jj undo`, `jj op restore`, and `jj resolve` where the value is in the side effect, not in custom rendering.
@@ -0,0 +1,209 @@
# Parallel Workspaces
Use `jj workspace` when multiple agents need independent working copies of the same repo. Each workspace gets its own `@` while sharing the same repo store and operation log.
**Tested with jj 0.41.0**
This skill intentionally prefers project-local workspace directories when they are safely ignored. Directory priority for this skill:
1. `.workspaces/`
2. `workspaces/`
3. Repo-local instructions such as `AGENTS.md` or `CLAUDE.md`
4. Ask the user
Do not replace this with a blanket "sibling directories only" rule.
## Before Creating a Workspace
First, make sure project-local workspaces will not leak into Git history:
```bash
grep -E '^\.?workspaces/?$' .gitignore 2>/dev/null
```
If the entry is missing, add it before `jj workspace add` and record that change using the normal workflow from `references/NEW_CHANGE.md`:
```bash
echo ".workspaces/" >> .gitignore
```
Never skip this check. `jj workspace add` creates directories that contain `.jj/` metadata, and the outer Git repo must ignore them.
Use descriptive names so `jj workspace list` and `jj log` stay readable:
```bash
REPO_NAME=$(basename "$(jj workspace root)")
WORKSPACE_PATH=".workspaces/${REPO_NAME}-<purpose>"
```
Examples:
- `.workspaces/myrepo-test-runner`
- `.workspaces/myrepo-reviewer-agent`
- `.workspaces/myrepo-refactor-experiment`
Avoid generic names such as `tmp`, `wip`, or `new`.
## Creating and Bootstrapping
Create the workspace and move into it:
```bash
jj workspace add "$WORKSPACE_PATH"
cd "$WORKSPACE_PATH"
```
After creation:
- the workspace has its own working-copy commit
- it appears in log output as `<workspace-name>@`
- it shares history and the operation log with the main workspace
- snapshots in this workspace affect only this workspace's `@`
If the repo needs environment setup, bootstrap it in the new workspace before handing it to another agent:
| Marker file | Setup command |
| --- | --- |
| `package.json` | `npm install` |
| `Cargo.toml` | `cargo build` |
| `pyproject.toml` | `uv sync` |
| `Gemfile` | `bundle install` |
| `flake.nix` | `nix develop` |
| `go.mod` | `go mod download` |
| `mix.exs` | `mix deps.get` |
## Agent Handoff
When one agent creates a workspace for another, always report:
- the absolute workspace path
- the change ID the agent should edit
- the exact `cd` command
- the exact `jj edit <change-id>` command
- the scope boundaries
Use a handoff like this:
```text
Workspace: /absolute/path/to/.workspaces/myrepo-tests
Change ID: abcdefgh
cd /absolute/path/to/.workspaces/myrepo-tests
jj edit abcdefgh
Rules:
- Always use -m for messages.
- Run jj st after every mutation.
- Do not modify files outside the assigned scope.
```
Absolute paths matter. Agents lose track of relative locations more often than they lose track of change IDs.
## Monitoring Progress
Start from the compact `jj log` command in `references/TEMPLATES.md`, then swap in the revset you need:
- all workspace tips: `-r 'working_copies()'`
- one named workspace: `-r '<workspace>@'`
Examples of the revsets themselves:
```bash
working_copies()
reviewer@
```
See `references/QUERY_LANGUAGES.md` for the revset syntax behind those examples.
## Combining Results
When a workspace finishes its work, bring that change back to the main line and retire the workspace. Do not decide silently; ask the user which strategy they want.
### Strategy 1: Rebase into the Default Workspace
Use this when one developer is orchestrating multiple agents and the result should land as ordinary commits in the default workspace.
```bash
jj rebase -s <workspace-change-id> -d @
jj workspace forget <workspace-name>
rm -rf "$WORKSPACE_PATH"
```
If the repo uses bookmarks for shipping work, move or create the bookmark after the rebase using the normal push workflow from `references/PUSH.md`.
### Strategy 2: Keep an Explicit Merge
Use this when both lines of work are independently meaningful and the merge structure should remain visible:
```bash
jj new <change-from-workspace-a> <change-from-workspace-b> -m "merge: combine parallel work"
```
### Strategy 3: Push a Bookmark and Open a PR
Use this when the work needs human review before landing:
```bash
jj bookmark create my-feature -r <workspace-change-id>
jj git push -b my-feature
```
Decision guide:
| Situation | Default suggestion |
| --- | --- |
| Solo developer running multiple local agents | Strategy 1 |
| Long-lived feature with checkpoints | Strategy 1, then push a bookmark when ready |
| Human review required | Strategy 3 |
| Both lines of work are independently meaningful | Strategy 2 |
If the context is ambiguous, suggest Strategy 1 first and ask whether the user wants the result to land in the default workspace or as a PR.
## Listing and Cleaning Up
```bash
jj workspace list
jj workspace forget <workspace-name>
rm -rf "$WORKSPACE_PATH"
```
If you need to inspect `jj workspace list` output as an agent, use the compact command from `references/TEMPLATES.md`.
`jj workspace forget` only unregisters the workspace. It does not delete files from disk.
## `update-stale`
If another workspace rewrites the commit your workspace had checked out, jj marks that workspace as stale. Use this first:
```bash
jj workspace update-stale
jj st
```
Do not improvise a manual repair before trying `update-stale`. If the old operation is gone, jj can create a recovery commit with the current working-copy contents instead of silently discarding them.
## Conflict Avoidance
| Risk | Recommended mitigation |
| --- | --- |
| Build outputs or generated files | Ignore them or give each workspace separate output paths |
| Shared config files | Assign a single owner for that file or change it serially |
| Lockfiles / dependency updates | Let one task own dependency changes |
| Same source files across workspaces | Redesign the task boundary or serialize the work |
## Critical Rules
1. Never skip the `.gitignore` check for project-local workspace directories.
2. Always follow the directory priority: `.workspaces/` -> `workspaces/` -> repo instructions -> ask.
3. Use descriptive names: `${REPO_NAME}-<purpose>`, not `tmp`.
4. Bootstrap the workspace before handing it to another agent.
5. Report the absolute path, change ID, and next commands when a workspace is ready.
6. Run `jj workspace forget` before deleting the directory.
7. Ask before choosing between rebase, merge, or PR integration.
8. Use `jj workspace update-stale` as the first response to a stale workspace.
## Reference
Convention adapted from [edmundmiller/dotfiles `using-jj-workspaces`](https://lobehub.com/skills/edmundmiller-dotfiles-using-jj-workspaces).
Additional handoff, monitoring, stale-workspace, and conflict-avoidance patterns were adapted from [joshuadavidthomas/agent-skills `jj`](https://github.com/joshuadavidthomas/agent-skills/tree/main/jj) and its [`workspaces.md`](https://raw.githubusercontent.com/joshuadavidthomas/agent-skills/main/jj/workspaces.md), while intentionally preserving this skill's local `.workspaces/` / `workspaces/` directory priority and local change-creation workflow.