jjhub 0.2.0 → 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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jjhub",
3
- "version": "0.2.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 instead of the web UI.
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
- # jjhub — the JJHub CLI
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
- 1. **Start the JJHub server** (if not already running): `npm start` from the repo root.
20
- Defaults to `http://localhost:3000`; set `PORT` to change it, `JJHUB_DB` to point at a
21
- different SQLite file.
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). If the server was started with
24
- `JJHUB_API_KEY` set, also `export JJHUB_API_KEY=<the same key>` — every request will
25
- otherwise get a `401 unauthorized`. (`jjhub auth login` persists these to a config file
26
- instead of needing them exported every shell — see "Server connection" below.)
27
- 3. **Invoke commands**: `npm run jjhub -- <command> [args]` in development, or `jjhub <command>`
28
- directly if installed as a binary (`bin/jjhub.mjs`).
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)
@@ -63,6 +94,10 @@ change amend <changeId>
63
94
  change squash <changeId>
64
95
  change abandon <changeId>
65
96
  change restack <changeId> rebase a needs-restack change onto its rewritten ancestor
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
66
101
  change commit <changeId> [<file...>] [-m msg] [--delete <path>]... [--expect-head <sha>]
67
102
  commit working-tree files onto the change's branch;
68
103
  --delete removes a path in the same commit (repeatable);
@@ -161,6 +196,15 @@ bookmark list
161
196
  bookmark delete <name> [--force] --force required for the default (trunk) bookmark
162
197
  bookmark set <name> <tracked|view> [--target CHG-x]
163
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
+
164
208
  view save <name> <revsetExpression> save a named jj revset expression for later reuse (storage
165
209
  only — evaluate it with "jjhub revset")
166
210
  view list list saved views in this repo
@@ -173,9 +217,11 @@ op diff <fromOpId> <toOpId> structural diff between two operations' i
173
217
  undo [--force] refused when the head op is a "land" (real merge can't be unwound); --force reverts local state anyway
174
218
  redo
175
219
 
176
- conflict list
220
+ conflict list ACTIONS column: the states each conflict can still take;
221
+ "historical" = its change already landed, nothing to resolve
177
222
  conflict resolve <conflictId> <state>
178
223
  state: unresolved | left | right | edited | preserve_unresolved
224
+ ("superseded" is set by JJHub itself when the change lands; it cannot be chosen)
179
225
 
180
226
  sync push overlay state to GitHub
181
227
 
@@ -200,13 +246,19 @@ ai models --provider <p> [--api-key <key>] [--base-url <url>]
200
246
  ai status where AI is coming from for you: your own saved connection,
201
247
  the server operator's default, or not configured at all
202
248
 
203
- mcp serve MCP server over stdio (125 tools) for agent clients
249
+ mcp serve MCP server over stdio (133 tools) for agent clients
204
250
  mcp serve --http <port> the same tools over Streamable HTTP (POST /)
205
- acp serve Agent Client Protocol agent over stdio (Zed's agent
206
- panel, ...) — every operation as a slash command
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
207
255
  agents-md print an AGENTS.md section teaching coding agents to
208
256
  prefer jjhub over raw git/gh (jjhub agents-md >> AGENTS.md)
209
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
+
210
262
  daemon <localRepoPath> [--once] [--interval 5] [--engine auto|subprocess|isomorphic] [--two-way]
211
263
  sync a local jj working copy into JJHub (inbound; --two-way mirrors JJHub edits back)
212
264
  daemon start [alias|localRepoPath] [--interval 5] [--engine ...] [--two-way]
@@ -249,10 +301,65 @@ content receipts [--repo owner/name|repoId]
249
301
  Anything else (`pr`, `issue`, ...) is passed through to `gh` — e.g. `jjhub pr view 7`. `jjhub auth`
250
302
  is jjhub's own command (it manages the *JJHub server* connection); use `gh auth` for GitHub auth.
251
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
+
252
357
  ### Server connection: flags, env vars, or a config file
253
358
 
254
- Precedence is `--flag` > env var (`JJHUB_URL`/`JJHUB_API_KEY`) > `~/.config/jjhub/config.json`
255
- (written by `jjhub auth login`). Run `jjhub auth login --url http://localhost:3000 --api-key <key>`
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>`
256
363
  once and every subsequent command in that shell (any shell, any day) picks it up with no env
257
364
  vars needed. `jjhub auth status` confirms what's currently in effect.
258
365
 
@@ -284,7 +391,14 @@ admin verification (optional mode)" section.
284
391
  For exact, always-current syntax straight from the CLI binary, run `jjhub help` — this file is
285
392
  an expanded, example-rich companion to that output, not a replacement for it.
286
393
 
287
- ### Install into your repo (teach YOUR coding agents to prefer jjhub)
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)
288
402
 
289
403
  [`agents-snippet.md`](./agents-snippet.md) is a short, imperative AGENTS.md
290
404
  section — "version control in this repo goes through JJHub; prefer `jjhub
@@ -294,11 +408,82 @@ AGENTS.md so any AGENTS.md-aware coding agent (Claude Code, Copilot, Cursor,
294
408
  Codex, ...) picks up the workflow automatically:
295
409
 
296
410
  ```bash
297
- jjhub agents-md >> AGENTS.md # from the CLI
298
- # or fetch it from a running server:
299
- curl https://<your-jjhub>/.well-known/skills/jjhub-cli/agents-snippet.md >> AGENTS.md
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
300
456
  ```
301
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
+
302
487
  ## Example workflows
303
488
 
304
489
  ### Create and land a change