jjhub 0.1.15 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/jjhub.mjs +31859 -23711
- package/package.json +1 -1
- package/skills/jjhub-cli/SKILL.md +329 -26
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jjhub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "jjhub — GitHub CLI with Jujutsu-native superpowers: stable change IDs, stacks, platform-wide undo, and conflicts that wait, layered over plain GitHub repos by a JJHub server.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"jjhub": "./dist/cli/jjhub.mjs"
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jjhub-cli
|
|
3
|
-
description: Drive a running JJHub instance from the command line with the `jjhub` CLI — create and land changes, manage stacks and bookmarks, resolve conflicts, and undo/redo via the operation log. Use when scripting or automating repository operations against a JJHub server
|
|
3
|
+
description: Drive a running JJHub instance (a Jujutsu-native change/stack/operation overlay on GitHub) from the command line with the `jjhub` CLI, or as an agent over MCP, ACP, A2A, or WebMCP — create and land changes, manage stacks and bookmarks, resolve conflicts, coordinate local jj workspaces, and undo/redo via the operation log. Use when scripting or automating repository operations against a JJHub server, or wiring an agent client (Claude Code, Zed, a Claude/ChatGPT connector, an A2A peer) up to one.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
License: MIT (see the public mirror erisera-code/jjhub-skills, LICENSE).
|
|
7
|
+
|
|
8
|
+
# jjhub — the JJHub CLI and agent surfaces
|
|
7
9
|
|
|
8
10
|
`jjhub` is a GitHub CLI (`gh`)-like wrapper with Jujutsu-native superpowers. It is a thin
|
|
9
11
|
client over JJHub's HTTP API: every command reads or mutates state on a running JJHub
|
|
@@ -11,21 +13,47 @@ server, the same server backing the web UI. Repo context is inferred from the cu
|
|
|
11
13
|
directory's git `origin` remote (override with `--repo`), and any top-level command that
|
|
12
14
|
isn't one of `jjhub`'s own nouns is passed straight through to the real `gh` binary.
|
|
13
15
|
|
|
16
|
+
The CLI is one of five interchangeable surfaces over the same server state and the same
|
|
17
|
+
op log — pick whichever fits the client:
|
|
18
|
+
|
|
19
|
+
| Surface | What it is | Entry point |
|
|
20
|
+
|---|---|---|
|
|
21
|
+
| CLI | `jjhub`, a `gh`-alike | `npx jjhub@latest <command>`, or `jjhub` once installed |
|
|
22
|
+
| MCP | 133 structured tools | remote: `<server>/mcp` (OAuth 2.1, RFC 9728 auto-discovery); local: `npx jjhub@latest mcp serve` (stdio) or `--http <port>` |
|
|
23
|
+
| ACP | Agent Client Protocol v1, slash-command style | `npx jjhub@latest acp serve` (stdio) — Zed's agent panel and friends |
|
|
24
|
+
| A2A | Agent2Agent v1.0, one skill per registry command | agent card `<server>/.well-known/agent-card.json`; JSON-RPC `POST <server>/a2a`; REST `<server>/a2a/v1` |
|
|
25
|
+
| WebMCP | 133 tools, progressively loaded | in-browser only, from an open JJHub tab — no separate connection |
|
|
26
|
+
|
|
27
|
+
This file documents the CLI in depth (it is the most complete reference for every verb,
|
|
28
|
+
since ACP/A2A commands are the same verbs under the same names) and points at each other
|
|
29
|
+
surface's own guide for its protocol-specific mechanics. See "Install this skill" below
|
|
30
|
+
for how to wire any of these into an agent client.
|
|
31
|
+
|
|
14
32
|
## Setup
|
|
15
33
|
|
|
16
34
|
See the [top-level README](../../README.md) for full project setup (`npm install`,
|
|
17
35
|
tests, requirements). This section covers just the CLI.
|
|
18
36
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
37
|
+
0. **No install needed**: `npx jjhub@latest <command>` fetches and runs the CLI on demand.
|
|
38
|
+
`npm i -g jjhub` gives a standalone `jjhub` binary if you'd rather not re-fetch every
|
|
39
|
+
invocation.
|
|
40
|
+
1. **Start the JJHub server** (if not already running, and you're not pointing at a
|
|
41
|
+
deployment): `npm start` from the repo root. Defaults to `http://localhost:3000`; set
|
|
42
|
+
`PORT` to change it, `JJHUB_DB` to point at a different SQLite file.
|
|
22
43
|
2. **Point the CLI at it**: `export JJHUB_URL=http://localhost:3000` (this is the default,
|
|
23
|
-
so only needed if the server is elsewhere).
|
|
24
|
-
`
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
44
|
+
so only needed if the server is elsewhere). Against a real deployment, sign in first —
|
|
45
|
+
`jjhub auth login --url https://<your-jjhub>` (GitHub device flow) saves a bearer token
|
|
46
|
+
to `~/.config/jjhub/config.json`, and every subsequent command picks it up with no env
|
|
47
|
+
vars needed. For a local dev server started with `JJHUB_API_KEY` set, instead
|
|
48
|
+
`export JJHUB_API_KEY=<the same key>` — every request otherwise gets `401 unauthorized`.
|
|
49
|
+
Precedence when both a token and a key are present: `JJHUB_TOKEN` (bearer, e.g. from
|
|
50
|
+
`auth login`) beats `JJHUB_API_KEY` (operator key, sent as `x-api-key`) beats the saved
|
|
51
|
+
config file. Exporting a login token as `JJHUB_API_KEY` does NOT work — the server
|
|
52
|
+
rejects it (401). `JJHUB_CONFIG_DIR` overrides `~/.config/jjhub` for the CLI and every
|
|
53
|
+
agent surface below, if you need an isolated config (a scratch credential, a test
|
|
54
|
+
harness, a second identity).
|
|
55
|
+
3. **Invoke commands**: `npx jjhub@latest <command> [args]` or `jjhub <command>` once
|
|
56
|
+
installed; from inside this repo in development, `npm run jjhub -- <command> [args]`.
|
|
29
57
|
4. **Initialize the overlay** for a repo before using any other command:
|
|
30
58
|
`jjhub repo init [owner/name]` (infers `owner/name` from the cwd's git origin if omitted).
|
|
31
59
|
|
|
@@ -44,6 +72,9 @@ two interchangeable clients over the same state, not separate systems.
|
|
|
44
72
|
```
|
|
45
73
|
repo init [owner/name] [--github-token pat]
|
|
46
74
|
create the JJHub overlay for this repo
|
|
75
|
+
repo create owner/name --public|--private [--description text] [--default-branch name]
|
|
76
|
+
create a NEW repo on GitHub (via the GitHub App) then
|
|
77
|
+
overlay it — --public/--private is required, no default
|
|
47
78
|
repo list list repositories you can access
|
|
48
79
|
(permissions come live from GitHub)
|
|
49
80
|
repo archive | unarchive retire/reactivate (archived = read-only, sync skipped)
|
|
@@ -51,7 +82,11 @@ repo delete hard-delete incl. op log (ops credential
|
|
|
51
82
|
|
|
52
83
|
user list list known users (identity is GitHub-federated)
|
|
53
84
|
|
|
54
|
-
change new <title> [--parent CHG-x] [--stack id]
|
|
85
|
+
change new <title> [--parent CHG-x] [--stack id] [--route same-repo|unprojected|fork:<owner/name>]
|
|
86
|
+
--route (issue #277): same-repo is the default; unprojected
|
|
87
|
+
creates the change with no GitHub branch/PR ever; fork:<owner/name>
|
|
88
|
+
is recorded but refused at projection time until real fork-PR
|
|
89
|
+
projection ships
|
|
55
90
|
change list
|
|
56
91
|
change show <changeId>
|
|
57
92
|
change describe <changeId> [--title t] [--description d]
|
|
@@ -59,49 +94,134 @@ change amend <changeId>
|
|
|
59
94
|
change squash <changeId>
|
|
60
95
|
change abandon <changeId>
|
|
61
96
|
change restack <changeId> rebase a needs-restack change onto its rewritten ancestor
|
|
62
|
-
change
|
|
97
|
+
change request-review <changeId> move a draft change into review and mark its PR ready for review
|
|
98
|
+
change return-to-draft <changeId> return an in-review change and its PR to draft
|
|
99
|
+
change context <changeId> compact orientation snapshot: status, diff stat, landability,
|
|
100
|
+
checks, conflicts, review health, and derived nextActions in one call
|
|
101
|
+
change commit <changeId> [<file...>] [-m msg] [--delete <path>]... [--expect-head <sha>]
|
|
63
102
|
commit working-tree files onto the change's branch;
|
|
64
|
-
--delete removes a path in the same commit (repeatable)
|
|
103
|
+
--delete removes a path in the same commit (repeatable);
|
|
104
|
+
--expect-head refuses (409) if the change moved since <sha>
|
|
105
|
+
change propose <title> [<file...>] [--parent CHG-x] [--stack id] [-m msg]
|
|
106
|
+
[--delete <path>]... [--expect-base <sha>]
|
|
107
|
+
issue #374: create + commit in one call, printing the diff
|
|
108
|
+
summary and PR link; --expect-base checks --parent's current
|
|
109
|
+
commit; on partial failure the error names the created change
|
|
110
|
+
id (still listed/abandon-able)
|
|
65
111
|
change diff <changeId> per-file diffs of what the change contains
|
|
66
112
|
change files <changeId> list locally-stored files (contentMode "local" repos only)
|
|
67
|
-
change land <changeId> [--force]
|
|
68
|
-
|
|
113
|
+
change land <changeId> [--force] [--expect-head <sha>]
|
|
114
|
+
waits for real CI triggered by its own ready flip,
|
|
115
|
+
re-syncs a behind branch, then merges; --force bypasses;
|
|
116
|
+
--expect-head refuses (409) if the change moved since <sha>
|
|
117
|
+
change enqueue <changeId> add a review-ready pull request to GitHub's merge queue;
|
|
118
|
+
GitHub owns the merge from here, JJHub marks the change
|
|
119
|
+
landed once GitHub reports it merged
|
|
120
|
+
change dequeue <changeId> withdraw a JJHub-queued pull request back to explicit review
|
|
121
|
+
change queue-status <changeId> print the merge-queue receipt (position/state/merge-group
|
|
122
|
+
sha/checks/removed reason) plus whether GitHub auto-merge
|
|
123
|
+
is armed on the pull request
|
|
69
124
|
change split <changeId> <newTitle>
|
|
70
125
|
change checks <changeId> list GitHub check runs for a change
|
|
71
126
|
change actions <changeId> show bounded GitHub Actions workflow and job evidence
|
|
72
127
|
change actions rerun <changeId> <runId>
|
|
73
128
|
ask GitHub to re-run a workflow run shown by "change actions"
|
|
129
|
+
change actions cancel <changeId> <runId>
|
|
130
|
+
ask GitHub to cancel a workflow run shown by "change actions"
|
|
74
131
|
change deployments <changeId> show bounded GitHub deployment evidence
|
|
75
132
|
change security <changeId> show bounded GitHub code-scanning evidence
|
|
76
133
|
change issues <changeId> show linked GitHub issue context for a change
|
|
77
134
|
change reviewers <changeId> reviewer-routing audit trail: every request/remove that
|
|
78
135
|
actually succeeded against GitHub, who did it, and when
|
|
136
|
+
change recommend-reviewers <changeId> advisory-only reviewer suggestions from CODEOWNERS, prior
|
|
137
|
+
review history, and recent file authorship — never requests
|
|
138
|
+
a reviewer; use "change reviewers" to see actual requests
|
|
139
|
+
change review-health <changeId> live GitHub review-collaboration summary: state, requested
|
|
140
|
+
reviewers, stale/dismissed approvals, unresolved threads,
|
|
141
|
+
CODEOWNERS evidence, and GitHub's effective merge requirement
|
|
142
|
+
change landability <changeId> show the effective GitHub landability policy report — every
|
|
143
|
+
gate's state and resolution, plus the overall verdict
|
|
79
144
|
change projects <changeId> show bounded GitHub Projects v2 evidence for linked issues
|
|
145
|
+
change handoff <changeId> durable task-handoff log (issue #373): findings, validation
|
|
146
|
+
evidence, outstanding work, or a verified commit SHA another
|
|
147
|
+
agent (or the same one later) can pick up without reconstructing
|
|
148
|
+
a chat transcript; works against a landed or abandoned change too
|
|
149
|
+
change handoff <changeId> --add <kind> -m <body>
|
|
150
|
+
append one entry (never updates/deletes an existing one);
|
|
151
|
+
kind: note | status | question | decision | blocker | plan
|
|
152
|
+
change deps <changeId> list a change's declared read-only external dependencies
|
|
153
|
+
(issue #277); with no flag, lists current state
|
|
154
|
+
change deps <changeId> --set kind:owner/repo#ref [--set ...]
|
|
155
|
+
replace-all declaration of external dependency edges
|
|
156
|
+
(kind is issue|pull_request|release|bookmark); never itself
|
|
157
|
+
reads GitHub and never gates landing (advisory only)
|
|
158
|
+
change deps <changeId> --refresh best-effort live GitHub read of every declared dependency's
|
|
159
|
+
current state (open/closed, released/draft, exists/missing)
|
|
160
|
+
change evolution <changeId> auditable logical-change timeline (issue #275): kind, related
|
|
161
|
+
change ids, before/after evidence, remote recovery limits
|
|
162
|
+
change recover <changeId> --keep left|right|both
|
|
163
|
+
resolve a recorded divergence (issue #275) between two live
|
|
164
|
+
changeIds that both claim the same underlying jj change id;
|
|
165
|
+
left/right abandons the other side, both marks it resolved
|
|
166
|
+
without abandoning either; refuses (409) if either is landed
|
|
80
167
|
|
|
81
168
|
stack create <title> [--id x] [--changes CHG-1,CHG-2]
|
|
82
169
|
stack list
|
|
83
170
|
stack land <stackId> [--force] same check-run gate as change land
|
|
171
|
+
stack enqueue <stackId> queue a LINEAR stack root->tip via change enqueue's own path;
|
|
172
|
+
refuses (409) before any GitHub call if the stack isn't linear
|
|
173
|
+
(a member with more than one parent, an unprojected member, or
|
|
174
|
+
a base that doesn't chain onto the previous member); prints each
|
|
175
|
+
member's own outcome (queued/skipped/failed) rather than an
|
|
176
|
+
all-or-nothing result
|
|
84
177
|
stack delete <stackId> disband — members become loose changes (undoable)
|
|
85
178
|
stack reorder <stackId> <changeId,changeId,...>
|
|
86
179
|
stack checks <stackId> every member's GitHub check runs, one batched round trip
|
|
87
180
|
stack issues <stackId> linked issue and external-blocker summary for a stack
|
|
181
|
+
stack review-health <stackId> per-member review-health summary, one batched round trip
|
|
182
|
+
stack landability <stackId> per-member effective GitHub landability policy report plus
|
|
183
|
+
the overall verdict, one batched round trip; a member blocked
|
|
184
|
+
behind an earlier, not-yet-landable member carries its own
|
|
185
|
+
"stack-order" requirement
|
|
186
|
+
|
|
187
|
+
project rules list list this repo's Projects v2 lifecycle->field mapping rules
|
|
188
|
+
project rules set --project <projectNodeId> --field <fieldNodeId> --option <optionId>
|
|
189
|
+
--when <draft|in_review|queued|landed|abandoned|blocked> [--id <ruleId>] [--disabled]
|
|
190
|
+
project rules delete <ruleId>
|
|
191
|
+
project preview <changeId> preview what "project apply" would do right now, without writing
|
|
192
|
+
project apply <changeId> evaluate and write matching rules now; automatic dispatch already
|
|
193
|
+
runs this on land
|
|
88
194
|
|
|
89
195
|
bookmark list
|
|
90
196
|
bookmark delete <name> [--force] --force required for the default (trunk) bookmark
|
|
91
197
|
bookmark set <name> <tracked|view> [--target CHG-x]
|
|
92
198
|
|
|
199
|
+
workspace list live local jj workspace agents reporting into this repo
|
|
200
|
+
(see "jjhub daemon"), with their last-seen topology observation
|
|
201
|
+
workspace status agents, which is primary, and any active lease holder
|
|
202
|
+
workspace primary <workspaceId> mark a workspace as this repository's primary
|
|
203
|
+
workspace route [<workspaceId>|clear] show/set/clear the suggested local-write target — a SUGGESTION
|
|
204
|
+
only, never applied implicitly
|
|
205
|
+
workspace connect [aliasOrPath] start a local daemon for a workspace (= "daemon start")
|
|
206
|
+
workspace disconnect [aliasOrPath] stop a local daemon for a workspace (= "daemon stop")
|
|
207
|
+
|
|
93
208
|
view save <name> <revsetExpression> save a named jj revset expression for later reuse (storage
|
|
94
209
|
only — evaluate it with "jjhub revset")
|
|
95
210
|
view list list saved views in this repo
|
|
96
211
|
view delete <name>
|
|
97
212
|
|
|
98
213
|
op log "@" marks the current head operation
|
|
214
|
+
op diff <fromOpId> <toOpId> structural diff between two operations' immutable snapshots
|
|
215
|
+
(which changes/stacks/bookmarks/conflicts were created,
|
|
216
|
+
modified, or removed); works for adjacent or far-apart ops
|
|
99
217
|
undo [--force] refused when the head op is a "land" (real merge can't be unwound); --force reverts local state anyway
|
|
100
218
|
redo
|
|
101
219
|
|
|
102
|
-
conflict list
|
|
220
|
+
conflict list ACTIONS column: the states each conflict can still take;
|
|
221
|
+
"historical" = its change already landed, nothing to resolve
|
|
103
222
|
conflict resolve <conflictId> <state>
|
|
104
223
|
state: unresolved | left | right | edited | preserve_unresolved
|
|
224
|
+
("superseded" is set by JJHub itself when the change lands; it cannot be chosen)
|
|
105
225
|
|
|
106
226
|
sync push overlay state to GitHub
|
|
107
227
|
|
|
@@ -115,13 +235,30 @@ auth sessions list your live sessions; auth revoke <id>
|
|
|
115
235
|
auth logout forget the saved connection/token
|
|
116
236
|
auth status who the saved credential authenticates as
|
|
117
237
|
|
|
118
|
-
|
|
238
|
+
ai models --provider <p> [--api-key <key>] [--base-url <url>]
|
|
239
|
+
live model list for one AI provider (issue #472); the key
|
|
240
|
+
is used for this one request only and never saved. Without
|
|
241
|
+
--api-key, falls back to your already-saved AI connection's
|
|
242
|
+
key for that same provider, if any. Always succeeds — a
|
|
243
|
+
live-lookup failure shows source: static_fallback with a
|
|
244
|
+
detail message rather than an error, since a free-text model
|
|
245
|
+
id always works when configuring AI, listed or not
|
|
246
|
+
ai status where AI is coming from for you: your own saved connection,
|
|
247
|
+
the server operator's default, or not configured at all
|
|
248
|
+
|
|
249
|
+
mcp serve MCP server over stdio (133 tools) for agent clients
|
|
119
250
|
mcp serve --http <port> the same tools over Streamable HTTP (POST /)
|
|
120
|
-
acp serve Agent Client Protocol agent over stdio (Zed's agent
|
|
121
|
-
panel, ...) —
|
|
251
|
+
acp serve Agent Client Protocol v1 agent over stdio (Zed's agent
|
|
252
|
+
panel, ...) — modes (read-only/ask/auto), a working-
|
|
253
|
+
repository + dry-run config option, persistent sessions,
|
|
254
|
+
plans and diffs — every operation as a slash command
|
|
122
255
|
agents-md print an AGENTS.md section teaching coding agents to
|
|
123
256
|
prefer jjhub over raw git/gh (jjhub agents-md >> AGENTS.md)
|
|
124
257
|
|
|
258
|
+
telemetry flows [--since <ISO8601>] [--surface webmcp|cli|browser|mcp|acp|a2a|api] [--limit N]
|
|
259
|
+
ongoing telemetry for the read -> patch -> verify -> land -> disconnect -> recover flow
|
|
260
|
+
across every client surface — per-step p50/p95 duration and error rate
|
|
261
|
+
|
|
125
262
|
daemon <localRepoPath> [--once] [--interval 5] [--engine auto|subprocess|isomorphic] [--two-way]
|
|
126
263
|
sync a local jj working copy into JJHub (inbound; --two-way mirrors JJHub edits back)
|
|
127
264
|
daemon start [alias|localRepoPath] [--interval 5] [--engine ...] [--two-way]
|
|
@@ -134,6 +271,12 @@ topology <localRepoPath> [--engine subprocess]
|
|
|
134
271
|
revset <localRepoPath> <expression> [--engine subprocess]
|
|
135
272
|
evaluate the installed jj binary's revset in exactly the named workspace;
|
|
136
273
|
this is local, read-only discovery rather than a server-side approximation
|
|
274
|
+
revset <expression> --workspace <workspaceId> [--repo owner/name] [--limit N]
|
|
275
|
+
REMOTE form (issue #274): relays the query through the JJHub server to
|
|
276
|
+
that exact workspace's own "jjhub daemon" and polls for the result — use
|
|
277
|
+
when you're not on the machine with the workspace checked out. Slower
|
|
278
|
+
(bound by the daemon's sync interval) and needs that daemon healthy;
|
|
279
|
+
same result shape/error taxonomy as the local form above
|
|
137
280
|
|
|
138
281
|
content preview <localRepoPath> <split|absorb|squash|duplicate|restore>
|
|
139
282
|
--source <jj-change-id> [--destination <jj-change-id>] [--path <relative-path>]...
|
|
@@ -142,6 +285,14 @@ content apply <localRepoPath> <split|absorb|squash|duplicate|restore>
|
|
|
142
285
|
local-only jj content curation; checked against jj's immutable policy and
|
|
143
286
|
never implemented as a browser patch transform; records a durable receipt
|
|
144
287
|
on the JJHub server (best-effort — see "content receipts" below)
|
|
288
|
+
content preview|apply --workspace <workspaceId> <split|absorb|squash|duplicate|restore>
|
|
289
|
+
--op <json> | (--source <jj-change-id> [--destination <jj-change-id>]
|
|
290
|
+
[--path <relative-path>]... [--title <title>]) [--expected-operation <id>]
|
|
291
|
+
REMOTE form (issue #276): relays the preview/apply through the JJHub server to
|
|
292
|
+
that exact workspace's own "jjhub daemon" and polls for the result — use
|
|
293
|
+
when you're not on the machine with the workspace checked out. jj's own
|
|
294
|
+
immutable-commits check and JJHub's landed-change guard both apply; a
|
|
295
|
+
successful apply also appends a durable content_op_receipts entry
|
|
145
296
|
content receipts [--repo owner/name|repoId]
|
|
146
297
|
list this repo's content-op receipt trail: every content apply invocation,
|
|
147
298
|
successful or rejected, with its kind, revisions, and outcome
|
|
@@ -150,10 +301,65 @@ content receipts [--repo owner/name|repoId]
|
|
|
150
301
|
Anything else (`pr`, `issue`, ...) is passed through to `gh` — e.g. `jjhub pr view 7`. `jjhub auth`
|
|
151
302
|
is jjhub's own command (it manages the *JJHub server* connection); use `gh auth` for GitHub auth.
|
|
152
303
|
|
|
304
|
+
### The other agent surfaces: ACP and A2A
|
|
305
|
+
|
|
306
|
+
`jjhub acp serve` and the server's A2A endpoint are not CLI subcommands (A2A has no CLI
|
|
307
|
+
entry point at all — it's always part of the running server), but they run the exact same
|
|
308
|
+
verbs as the table above, through a shared command registry
|
|
309
|
+
(`src/agents/commands.ts`, 139 commands: every operation, the hand-written extras `whoami`,
|
|
310
|
+
`land_change`, `land_stack`, `get_land_job`, `delete_bookmark`, `sync_github`,
|
|
311
|
+
`raise_conflict`, plus registry-only composites `stack_status`, `get_conflict`,
|
|
312
|
+
`get_landability`, `land_when_ready`, `watch_change`, `watch_stack`, `get_skill`, `ask`) — so
|
|
313
|
+
anything documented above as a CLI command is also an ACP slash command and an A2A skill
|
|
314
|
+
under the same snake_case name.
|
|
315
|
+
|
|
316
|
+
**`ask` / `ask_jjhub` (issue #586)** — free text mapped onto ONE command from this same
|
|
317
|
+
registry (deterministic `/command args` parse first, else the caller's own AI connection,
|
|
318
|
+
resolved SERVER-SIDE via `POST /ai/interpret-command` so the process calling it needs no local
|
|
319
|
+
AI env of its own): `jjhub ask "<free text>"`, the A2A skill `ask`, and the MCP tool
|
|
320
|
+
`ask_jjhub` all wrap the exact same interpretation. Prefer it when a caller (an editor agent
|
|
321
|
+
speaking MCP, in particular) only has free text to work with and can't first call
|
|
322
|
+
`list_repositories`/`list_changes`/etc. itself to build a structured call — e.g. an end user
|
|
323
|
+
typed "land CHG-3 in the payments repo" into a chat and the agent should map that onto
|
|
324
|
+
`land_change` without hand-rolling its own NL parsing. Prefer the DIRECT command/tool instead
|
|
325
|
+
whenever the caller already knows exactly which verb and arguments it wants — `ask` adds an
|
|
326
|
+
extra round trip (interpret, then execute) and, for a command needing confirmation (e.g.
|
|
327
|
+
force-land), never bypasses it: it returns the resolved invocation unexecuted and says to call
|
|
328
|
+
the command directly, EXCEPT `ask_jjhub` itself over MCP, which carries the identical
|
|
329
|
+
SEP-2322/elicitation confirmation seam `land_change`/`land_stack`/`delete_bookmark` use, so a
|
|
330
|
+
force-land asked for in free text still asks before it runs. Without an AI provider configured
|
|
331
|
+
for the caller, only a direct `/command args` prompt resolves; the response says so
|
|
332
|
+
(`interpreted: false`, a `reason`) rather than failing.
|
|
333
|
+
|
|
334
|
+
- **ACP** (`jjhub acp serve`, stdio, Agent Client Protocol v1): sessions have modes
|
|
335
|
+
(`read-only` refuses mutations; `ask`, the default, confirms force-land/trunk-deletion/
|
|
336
|
+
force-undo through `session/request_permission` or `elicitation/create`; `auto`
|
|
337
|
+
auto-approves everything else), a `repository` config option so `/get_change CHG-3` works
|
|
338
|
+
without repeating the repository id, a `dry_run` config option, and persistent sessions
|
|
339
|
+
(`session/load`/`list`/`resume`/`close`/`delete`, stored in
|
|
340
|
+
`~/.config/jjhub/acp-sessions.json` — `JJHUB_ACP_SESSIONS_PATH` overrides the file,
|
|
341
|
+
`JJHUB_CONFIG_DIR` moves the whole config dir). A dev server with no `JJHUB_API_KEY` needs
|
|
342
|
+
`JJHUB_ACP_ALLOW_ANONYMOUS=1` on the agent process. Full Zed setup and everything the
|
|
343
|
+
session exposes: [`docs/guides/COMMON-TASKS.md`](../../docs/guides/COMMON-TASKS.md#drive-jjhub-from-an-editor-agent-panel-acp-eg-zed).
|
|
344
|
+
- **A2A** (spec v1.0 only — the v0.3 compatibility layer was removed; no v0.3 clients):
|
|
345
|
+
agent card at `/.well-known/agent-card.json` (optionally signed via
|
|
346
|
+
`JJHUB_A2A_CARD_SIGNING_JWK`, verifiable against `/.well-known/a2a-jwks.json`), JSON-RPC at
|
|
347
|
+
`POST /a2a`, REST at `/a2a/v1` — one task store shared by both bindings, so a task started
|
|
348
|
+
on one can be fetched/listed/cancelled on the other. Call a command as a text part
|
|
349
|
+
(`/list_changes <repositoryId> status=in_review`) or a data part
|
|
350
|
+
(`{"command": "commit_files", "args": {...}}`); confirmable mutations pause the task in
|
|
351
|
+
`input-required` (a `confirm` message resumes it), plans stream as `working` updates, and
|
|
352
|
+
`CancelTask` can interrupt an in-flight skill at its next safe checkpoint. Full detail and
|
|
353
|
+
the `npm run a2a:exercise` / `npm run acp:exercise` client harnesses (including the shared
|
|
354
|
+
`--mutate --stress N --land` scratch-repository stress scenario):
|
|
355
|
+
[`docs/guides/COMMON-TASKS.md`](../../docs/guides/COMMON-TASKS.md#drive-jjhub-agent-to-agent-a2a).
|
|
356
|
+
|
|
153
357
|
### Server connection: flags, env vars, or a config file
|
|
154
358
|
|
|
155
|
-
Precedence is `--
|
|
156
|
-
(
|
|
359
|
+
Precedence is `--url` > `JJHUB_URL` > `~/.config/jjhub/config.json` for the server, and
|
|
360
|
+
`JJHUB_TOKEN` (bearer token from `jjhub auth login`) > `JJHUB_API_KEY` (operator key, sent as
|
|
361
|
+
`x-api-key`) > the saved config (token, then apiKey) for the credential; the config file is
|
|
362
|
+
written by `jjhub auth login`. Run `jjhub auth login --url http://localhost:3000 --api-key <key>`
|
|
157
363
|
once and every subsequent command in that shell (any shell, any day) picks it up with no env
|
|
158
364
|
vars needed. `jjhub auth status` confirms what's currently in effect.
|
|
159
365
|
|
|
@@ -185,7 +391,14 @@ admin verification (optional mode)" section.
|
|
|
185
391
|
For exact, always-current syntax straight from the CLI binary, run `jjhub help` — this file is
|
|
186
392
|
an expanded, example-rich companion to that output, not a replacement for it.
|
|
187
393
|
|
|
188
|
-
|
|
394
|
+
## Install this skill
|
|
395
|
+
|
|
396
|
+
Three independent ways to pick this skill up — use whichever fits the agent you're
|
|
397
|
+
configuring. `GET /.well-known/skills/` on any running JJHub server (including the live
|
|
398
|
+
instance) returns all three as an `installation` object, e.g.
|
|
399
|
+
`curl https://jjhub.erisera.com/.well-known/skills/ | jq .installation`.
|
|
400
|
+
|
|
401
|
+
### (a) Teach a repo's coding agents to prefer jjhub (AGENTS.md)
|
|
189
402
|
|
|
190
403
|
[`agents-snippet.md`](./agents-snippet.md) is a short, imperative AGENTS.md
|
|
191
404
|
section — "version control in this repo goes through JJHub; prefer `jjhub
|
|
@@ -195,11 +408,82 @@ AGENTS.md so any AGENTS.md-aware coding agent (Claude Code, Copilot, Cursor,
|
|
|
195
408
|
Codex, ...) picks up the workflow automatically:
|
|
196
409
|
|
|
197
410
|
```bash
|
|
198
|
-
jjhub agents-md >> AGENTS.md
|
|
199
|
-
|
|
200
|
-
|
|
411
|
+
npx jjhub@latest agents-md >> AGENTS.md
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### (b) Fetch this skill directly (`skills.sh`, or a plain curl/`.well-known` fetch)
|
|
415
|
+
|
|
416
|
+
The skill content itself (this file plus `agents-snippet.md`) is mirrored, read-only, to the
|
|
417
|
+
public repo [`erisera-code/jjhub-skills`](https://github.com/erisera-code/jjhub-skills) on
|
|
418
|
+
every change (`.github/workflows/publish-skills.yml`, built by
|
|
419
|
+
`scripts/build-skills-dist.ts` — `npm run build:skills` reproduces it locally). Install from
|
|
420
|
+
there with the [skills.sh](https://skills.sh) installer:
|
|
421
|
+
|
|
422
|
+
```bash
|
|
423
|
+
npx skills add erisera-code/jjhub-skills
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
Or mirror it by hand into a local `skills/` directory — the same shape either the live
|
|
427
|
+
server's `GET /.well-known/skills/` or the mirror's own `index.json` returns:
|
|
428
|
+
|
|
429
|
+
```json
|
|
430
|
+
{
|
|
431
|
+
"skills": [
|
|
432
|
+
{ "name": "jjhub-cli", "url": "/.well-known/skills/jjhub-cli/SKILL.md", "agentsSnippet": "/.well-known/skills/jjhub-cli/agents-snippet.md" }
|
|
433
|
+
],
|
|
434
|
+
"installation": {
|
|
435
|
+
"skillsCli": "npx skills add erisera-code/jjhub-skills",
|
|
436
|
+
"claudeCodeMarketplace": "/plugin marketplace add erisera-code/jjhub-skills",
|
|
437
|
+
"agentsMd": "npx jjhub@latest agents-md >> AGENTS.md",
|
|
438
|
+
"wellKnown": "https://jjhub.erisera.com/.well-known/skills/",
|
|
439
|
+
"repository": "https://github.com/erisera-code/jjhub-skills"
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
```bash
|
|
445
|
+
mkdir -p skills/jjhub-cli
|
|
446
|
+
curl -s https://jjhub.erisera.com/.well-known/skills/jjhub-cli/SKILL.md -o skills/jjhub-cli/SKILL.md
|
|
447
|
+
curl -s https://jjhub.erisera.com/.well-known/skills/jjhub-cli/agents-snippet.md -o skills/jjhub-cli/agents-snippet.md
|
|
448
|
+
```
|
|
449
|
+
|
|
450
|
+
`erisera-code/jjhub-skills` also carries a `.claude-plugin/marketplace.json`, so Claude Code
|
|
451
|
+
can add it directly as a plugin marketplace:
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
/plugin marketplace add erisera-code/jjhub-skills
|
|
455
|
+
/plugin install jjhub-cli@jjhub-skills
|
|
201
456
|
```
|
|
202
457
|
|
|
458
|
+
### (c) Wire up an MCP/ACP client, or an A2A peer
|
|
459
|
+
|
|
460
|
+
- **Claude Code** (MCP): `claude mcp add jjhub --http https://jjhub.erisera.com/mcp`
|
|
461
|
+
against a deployment (OAuth 2.1 discovered automatically via RFC 9728 — the CLI walks you
|
|
462
|
+
through sign-in), or point at a local stdio server:
|
|
463
|
+
```json
|
|
464
|
+
{ "mcpServers": { "jjhub": { "command": "npx", "args": ["jjhub@latest", "mcp", "serve"] } } }
|
|
465
|
+
```
|
|
466
|
+
- **Zed** (ACP), `~/.config/zed/settings.json`:
|
|
467
|
+
```json
|
|
468
|
+
{
|
|
469
|
+
"agent_servers": {
|
|
470
|
+
"jjhub": {
|
|
471
|
+
"command": "npx",
|
|
472
|
+
"args": ["jjhub@latest", "acp", "serve"],
|
|
473
|
+
"env": { "JJHUB_URL": "https://jjhub.erisera.com", "JJHUB_TOKEN": "<token from jjhub auth login>" }
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
```
|
|
478
|
+
(Running `jjhub auth login` on the same machine first means the `env` block can be
|
|
479
|
+
omitted entirely — the agent picks up the saved credential from `~/.config/jjhub`.)
|
|
480
|
+
- **A Claude.ai or ChatGPT connector** (remote MCP): add a custom connector pointing at
|
|
481
|
+
`https://<your-jjhub>/mcp` — both discover the OAuth 2.1 flow automatically (RFC 9728
|
|
482
|
+
protected-resource metadata); no manual token entry.
|
|
483
|
+
- **An A2A peer**: fetch `https://<your-jjhub>/.well-known/agent-card.json` for the skill
|
|
484
|
+
list and the two binding URLs (JSON-RPC `/a2a`, REST `/a2a/v1`); see "The other agent
|
|
485
|
+
surfaces: ACP and A2A" above for the auth model and confirmation flow.
|
|
486
|
+
|
|
203
487
|
## Example workflows
|
|
204
488
|
|
|
205
489
|
### Create and land a change
|
|
@@ -214,6 +498,25 @@ npm run jjhub -- stack land "Fix bundle"
|
|
|
214
498
|
|
|
215
499
|
A single change not part of a stack lands directly: `npm run jjhub -- change land CHG-1`.
|
|
216
500
|
|
|
501
|
+
### Use GitHub's merge queue instead of a direct land
|
|
502
|
+
|
|
503
|
+
When the target branch requires it, add a review-ready change to GitHub's
|
|
504
|
+
merge queue rather than merging it directly — GitHub owns the merge from
|
|
505
|
+
there, and JJHub only marks the change landed once GitHub reports it merged:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
npm run jjhub -- change request-review CHG-1
|
|
509
|
+
npm run jjhub -- change enqueue CHG-1
|
|
510
|
+
npm run jjhub -- change queue-status CHG-1 # position/state/merge-group evidence,
|
|
511
|
+
# removal reason if GitHub removed it,
|
|
512
|
+
# and whether GitHub auto-merge is armed
|
|
513
|
+
npm run jjhub -- change dequeue CHG-1 # withdraw back to explicit review
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
A change JJHub knows is queued cannot be rewritten (`describe`/`amend`/
|
|
517
|
+
`restack`/...) or landed directly until it is dequeued — `change land`
|
|
518
|
+
refuses with a message naming the queue.
|
|
519
|
+
|
|
217
520
|
### Sync a local jj working copy into JJHub
|
|
218
521
|
|
|
219
522
|
```bash
|