1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
# Eval runs load no AGENTS.md, so every expectation below has to be earned by
# SKILL.md and pitfalls.md alone — not by the global "always use jj" rule.

trigger:
  positive:
    - prompt: I want to move my working copy changes into whichever ancestor commit last touched those lines.
      note: absorb, which the description does not name
    - prompt: The description on my last commit is wrong. How do I fix the message without moving @?
      note: describe, named in the description
    - prompt: I have three stacked bookmarks to push to origin. What order should I push them in?
      note: bookmarks and push, both named in the description
    - prompt: jj says my change is (divergent) and half my edits have vanished. What do I do?
      note: divergent changes, which the description does not name
    - prompt: How do I extract just one file out of a commit into its own commit?
      note: split, which the description does not name
  negative:
    - prompt: Add fzf-git as a git submodule, shallow cloned.
      note: version control adjacent, but git tooling with no jj involved
    - prompt: What does `git rebase -i` do, and when would I reach for it?
      note: pure git; the skill's git-differences section makes this tempting
    - prompt: Write a bash script that renames every .jpeg in a directory to .jpg.

adherence:
  - prompt: |
      I finished the change in @ and want to give it a message and start fresh work on top of it.
      Which single command does that, and what does it do to @?
    expect:
      - names `jj commit -m` as the single command
      - says it moves `@` to a new empty commit
      - contrasts it with `jj describe`, which sets a message without moving `@`

  - prompt: |
      I have three stacked bookmarks to push to origin and was going to start with the tip,
      using a bare `jj git push`. Is there a problem with that?
    expect:
      - says pushing the tip first drops the ancestor bookmarks out of the default push revset
      - warns that the next push reports nothing changed and leaves the lower branch unpushed, so the failure reads like success
      - tells me to push bottom-first or to pass `-b <bookmark>` per branch

  - prompt: |
      I ran `jj bookmark create alpha/my-fix -r abcd`. Now finish the job and push it to origin.
    expect:
      - pushes with `jj git push -b alpha/my-fix`
      - does not use `--named`, or says `--named` would error because the name is already local
      - does not reach for `--allow-new`, or says no such flag exists

  - prompt: |
      Ten commands ago `jj log` showed me change `qrstuvwx`. I've squashed twice since then and now
      `jj log -r qrstuvwx` says the revision doesn't exist. Did I mistype it?
    expect:
      - says the ID most likely went stale because the rewrites reassigned it, not that it was mistyped
      - tells me to re-run `jj log` and re-derive the ID rather than searching for a typo

  - prompt: |
      I wrote a fix in a commit on top of `mm` and now I can't push it. How do I get it into the
      merge properly?
    expect:
      - explains that work on top of `mm` cannot be pushed because its diff against trunk contains every parent
      - prescribes moving the commit beneath the merge with a single `jj rebase -r <rev> -A trunk -B mm`
      - warns that rewriting `mm` separately leaves a second near-identical megamerge

  # The cases below come from pitfalls.md, where a model's git-shaped instinct is
  # wrong. Filter to them with `--case pitfall`.

  - prompt: |
      Print the contents of version.rb as it exists in the parent commit, then tell me which version
      string that copy contains.
    note: 'pitfall: `jj file show` ignores a revision passed without -r, and only warns'
    tools: [read, bash]
    setup: |
      jj git init
      printf 'VERSION = "1.0"\n' > version.rb
      jj commit -m 'app: add version'
      printf 'VERSION = "2.0"\n' > version.rb
      jj describe -m 'app: bump version'
    expect:
      - passes `-r` to `jj file show` rather than naming the revision as a positional argument
      - reports that the parent's copy contains 1.0
      - does not report 2.0 as the parent's version

  - prompt: |
      Show me the diff for just lib/a.rb in the parent commit, and nothing else.
    note: 'pitfall: `jj show` takes no path arguments, so `jj diff -r` is the way'
    tools: [read, bash]
    setup: |
      jj git init
      mkdir lib
      printf 'class A\nend\n' > lib/a.rb
      printf 'class B\nend\n' > lib/b.rb
      jj commit -m 'lib: add a and b'
    expect:
      - scopes the diff with `jj diff -r` and the path
      - never passes a path argument to `jj show`
      - the output it reports covers lib/a.rb and not lib/b.rb

  - prompt: |
      Squash @ into its parent, keeping the parent's message.
    note: 'pitfall: squash opens $EDITOR when both commits have descriptions'
    tools: [read, bash]
    setup: |
      jj git init
      printf 'a\n' > a.txt
      jj commit -m 'app: add a'
      printf 'b\n' > b.txt
      jj describe -m 'app: add b'
      # Fail fast instead of hanging a headless run: the pitfall still bites, but a
      # squash that reaches for an editor errors rather than waiting forever.
      # Quote the value, or jj stores a boolean and rejects it as an invalid editor.
      jj config set --repo ui.editor '/bin/false'
    expect:
      - passes `--use-destination-message` or `-u` to `jj squash`
      - the squash succeeds without an editor error
      - "the surviving commit keeps the message `app: add a`"

  - prompt: |
      Commit only the file bin/,z with the message "bin: add ,z". Leave bin/normal uncommitted.
    note: 'pitfall: a comma in a path parses as fileset syntax'
    tools: [read, bash]
    setup: |
      jj git init
      mkdir bin
      printf '#!/bin/sh\n' > 'bin/,z'
      printf '#!/bin/sh\n' > bin/normal
    expect:
      - wraps the path in a double-quoted fileset string literal so the comma is treated literally
      - the commit it creates contains bin/,z
      - bin/normal is still uncommitted in the working copy afterward

  - prompt: |
      Print the revset this repo's `trunk()` alias expands to.
    note: 'pitfall: jj parses the config key as TOML, so the shell must not eat the inner quotes'
    tools: [read, bash]
    setup: |
      jj git init
    expect:
      - single-quotes the whole config key so jj receives the inner double quotes
      - the command succeeds rather than failing with a TOML parse error

  - prompt: |
      In this repo, split lib/a.rb out of @ into its own commit described "lib: extract a", and leave
      the original change ID and description on the commit that still holds lib/b.rb. Then show me
      the resulting log.
    tools: [read, bash]
    setup: |
      jj git init
      mkdir lib
      printf 'class A\nend\n' > lib/a.rb
      printf 'class B\nend\n' > lib/b.rb
      jj describe -m 'lib: add a and b'
    expect:
      - runs `jj split` passing the same revision to both `-r` and `-A`
      - passes `-m` to `jj split` so no editor opens
      - "the final log shows the original description `lib: add a and b` still on the commit holding lib/b.rb"
      - "the new commit described `lib: extract a` contains lib/a.rb"