jjhub 0.1.1
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/README.md +35 -0
- package/dist/cli/jjh.mjs +95143 -0
- package/package.json +24 -0
- package/skills/jjhub-cli/SKILL.md +209 -0
- package/skills/jjhub-cli/agents-snippet.md +28 -0
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "jjhub",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "jjh — 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
|
+
"bin": {
|
|
6
|
+
"jjh": "./dist/cli/jjh.mjs"
|
|
7
|
+
},
|
|
8
|
+
"type": "module",
|
|
9
|
+
"engines": {
|
|
10
|
+
"node": ">=22.6.0"
|
|
11
|
+
},
|
|
12
|
+
"license": "UNLICENSED",
|
|
13
|
+
"homepage": "https://jjhub.johnhenry.me",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"jujutsu",
|
|
16
|
+
"jj",
|
|
17
|
+
"github",
|
|
18
|
+
"cli",
|
|
19
|
+
"stacked-prs",
|
|
20
|
+
"undo",
|
|
21
|
+
"vcs"
|
|
22
|
+
],
|
|
23
|
+
"author": "John Henry"
|
|
24
|
+
}
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jjhub-cli
|
|
3
|
+
description: Drive a running JJHub instance from the command line with the `jjh` 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.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# jjh — the JJHub CLI
|
|
7
|
+
|
|
8
|
+
`jjh` is a GitHub CLI (`gh`)-like wrapper with Jujutsu-native superpowers. It is a thin
|
|
9
|
+
client over JJHub's HTTP API: every command reads or mutates state on a running JJHub
|
|
10
|
+
server, the same server backing the web UI. Repo context is inferred from the current
|
|
11
|
+
directory's git `origin` remote (override with `--repo`), and any top-level command that
|
|
12
|
+
isn't one of `jjh`'s own nouns is passed straight through to the real `gh` binary.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
See the [top-level README](../../README.md) for full project setup (`npm install`,
|
|
17
|
+
tests, requirements). This section covers just the CLI.
|
|
18
|
+
|
|
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.
|
|
22
|
+
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`. (`jjh 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 jjh -- <command> [args]` in development, or `jjh <command>`
|
|
28
|
+
directly if installed as a binary (`bin/jjh.mjs`).
|
|
29
|
+
4. **Initialize the overlay** for a repo before using any other command:
|
|
30
|
+
`jjh repo init [owner/name]` (infers `owner/name` from the cwd's git origin if omitted).
|
|
31
|
+
|
|
32
|
+
The same JJHub server also backs a web UI (`npm run web:dev` from the repo root, in a
|
|
33
|
+
second terminal — see [`web/README.md`](../../web/README.md)); the CLI and the web UI are
|
|
34
|
+
two interchangeable clients over the same state, not separate systems.
|
|
35
|
+
|
|
36
|
+
## Global flags
|
|
37
|
+
|
|
38
|
+
- `--repo <owner/name|repoId>` — override the inferred repository context.
|
|
39
|
+
- `--json` — emit machine-readable JSON instead of a human-readable table.
|
|
40
|
+
- `--url <baseUrl>` — override the server URL for this invocation (same as `JJHUB_URL`).
|
|
41
|
+
|
|
42
|
+
## Commands
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
repo init [owner/name] [--github-token pat]
|
|
46
|
+
create the JJHub overlay for this repo
|
|
47
|
+
repo list list repositories you can access
|
|
48
|
+
(permissions come live from GitHub)
|
|
49
|
+
repo archive | unarchive retire/reactivate (archived = read-only, sync skipped)
|
|
50
|
+
repo delete hard-delete incl. op log (ops credential only)
|
|
51
|
+
|
|
52
|
+
user list list known users (identity is GitHub-federated)
|
|
53
|
+
|
|
54
|
+
change new <title> [--parent CHG-x] [--stack id]
|
|
55
|
+
change list
|
|
56
|
+
change show <changeId>
|
|
57
|
+
change describe <changeId> [--title t] [--description d]
|
|
58
|
+
change amend <changeId>
|
|
59
|
+
change squash <changeId>
|
|
60
|
+
change abandon <changeId>
|
|
61
|
+
change restack <changeId> rebase a needs-restack change onto its rewritten ancestor
|
|
62
|
+
change commit <changeId> <file...> [-m msg]
|
|
63
|
+
commit working-tree files onto the change's branch
|
|
64
|
+
change diff <changeId> per-file diffs of what the change contains
|
|
65
|
+
change files <changeId> list locally-stored files (contentMode "local" repos only)
|
|
66
|
+
change land <changeId> [--force] waits for real CI triggered by its own ready flip,
|
|
67
|
+
re-syncs a behind branch, then merges; --force bypasses
|
|
68
|
+
change split <changeId> <newTitle>
|
|
69
|
+
change checks <changeId> list GitHub check runs for a change
|
|
70
|
+
|
|
71
|
+
stack create <title> [--id x] [--changes CHG-1,CHG-2]
|
|
72
|
+
stack list
|
|
73
|
+
stack land <stackId> [--force] same check-run gate as change land
|
|
74
|
+
stack delete <stackId> disband — members become loose changes (undoable)
|
|
75
|
+
stack reorder <stackId> <changeId,changeId,...>
|
|
76
|
+
stack checks <stackId> every member's GitHub check runs, one batched round trip
|
|
77
|
+
|
|
78
|
+
bookmark list
|
|
79
|
+
bookmark delete <name> [--force] --force required for the default (trunk) bookmark
|
|
80
|
+
bookmark set <name> <tracked|view> [--target CHG-x]
|
|
81
|
+
|
|
82
|
+
op log "@" marks the current head operation
|
|
83
|
+
undo [--force] refused when the head op is a "land" (real merge can't be unwound); --force reverts local state anyway
|
|
84
|
+
redo
|
|
85
|
+
|
|
86
|
+
conflict list
|
|
87
|
+
conflict resolve <conflictId> <state>
|
|
88
|
+
state: unresolved | left | right | edited | preserve_unresolved
|
|
89
|
+
|
|
90
|
+
sync push overlay state to GitHub
|
|
91
|
+
|
|
92
|
+
auth login [--url <baseUrl>] [--scope read|write] [--repos o/a,o/b]
|
|
93
|
+
sign in with GitHub via the device flow (saves an
|
|
94
|
+
OAuth token to ~/.config/jjhub). --scope read mints
|
|
95
|
+
a read-only token; --repos fences it to specific
|
|
96
|
+
repositories. --api-key <key> instead saves the
|
|
97
|
+
legacy ops shared secret
|
|
98
|
+
auth sessions list your live sessions; auth revoke <id> kills one
|
|
99
|
+
auth logout forget the saved connection/token
|
|
100
|
+
auth status who the saved credential authenticates as
|
|
101
|
+
|
|
102
|
+
mcp serve MCP server over stdio (50 tools) for agent clients
|
|
103
|
+
mcp serve --http <port> the same tools over Streamable HTTP (POST /)
|
|
104
|
+
acp serve Agent Client Protocol agent over stdio (Zed's agent
|
|
105
|
+
panel, ...) — every operation as a slash command
|
|
106
|
+
agents-md print an AGENTS.md section teaching coding agents to
|
|
107
|
+
prefer jjh over raw git/gh (jjh agents-md >> AGENTS.md)
|
|
108
|
+
|
|
109
|
+
daemon <localRepoPath> [--once] [--interval 5] [--engine auto|subprocess|isomorphic] [--two-way]
|
|
110
|
+
sync a local jj working copy into JJHub (inbound; --two-way mirrors JJHub edits back)
|
|
111
|
+
daemon start [alias|localRepoPath] [--interval 5] [--engine ...] [--two-way]
|
|
112
|
+
spawn the same sync as a detached background process
|
|
113
|
+
daemon stop [alias] | daemon status [alias]
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Anything else (`pr`, `issue`, ...) is passed through to `gh` — e.g. `jjh pr view 7`. `jjh auth`
|
|
117
|
+
is jjh's own command (it manages the *JJHub server* connection); use `gh auth` for GitHub auth.
|
|
118
|
+
|
|
119
|
+
### Server connection: flags, env vars, or a config file
|
|
120
|
+
|
|
121
|
+
Precedence is `--flag` > env var (`JJHUB_URL`/`JJHUB_API_KEY`) > `~/.config/jjhub/config.json`
|
|
122
|
+
(written by `jjh auth login`). Run `jjh auth login --url http://localhost:3000 --api-key <key>`
|
|
123
|
+
once and every subsequent command in that shell (any shell, any day) picks it up with no env
|
|
124
|
+
vars needed. `jjh auth status` confirms what's currently in effect.
|
|
125
|
+
|
|
126
|
+
### Identity & permissions (issue #43: GitHub-federated)
|
|
127
|
+
|
|
128
|
+
A JJHub user IS a GitHub account. `jjh auth login` runs an OAuth device flow against the
|
|
129
|
+
JJHub server's own authorization server: open the printed URL, sign in with GitHub, confirm
|
|
130
|
+
the code, and the CLI saves a JJHub-issued OAuth token. Every mutation is attributed to that
|
|
131
|
+
identity (visible in `jjh op log`).
|
|
132
|
+
|
|
133
|
+
What you can do on a repo is asked of GitHub LIVE at request time (cached ~60s):
|
|
134
|
+
GitHub `admin` → owner, `maintain`/`write` → writer, `triage`/`read` → reader. There is no
|
|
135
|
+
internal membership table and nothing to manage in JJHub — manage collaborators on GitHub.
|
|
136
|
+
|
|
137
|
+
The legacy shared secret (`--api-key` / `JJHUB_API_KEY`) still exists as an ops/emergency
|
|
138
|
+
credential: full access, RBAC bypassed, no attribution. Anonymous requests (no server-side
|
|
139
|
+
`JJHUB_API_KEY` configured at all) remain fully open — dev mode, unchanged.
|
|
140
|
+
|
|
141
|
+
### GitHub admin verification (optional, separate mode)
|
|
142
|
+
|
|
143
|
+
This RBAC is entirely JJHub-internal — a JJHub "owner" isn't checked against real GitHub
|
|
144
|
+
permissions at all. If the server has `JJHUB_REQUIRE_GITHUB_VERIFICATION=1` set,
|
|
145
|
+
`jjh repo init` requires `--github-token <pat>` (or falls back to `$GITHUB_TOKEN`): the
|
|
146
|
+
server verifies that token has real `admin` access to the exact `owner/name` being created
|
|
147
|
+
via a live GitHub API call, rejecting creation (403) otherwise. The verified token also
|
|
148
|
+
becomes the new repo's GitHub connection automatically. See the top-level README's "GitHub
|
|
149
|
+
admin verification (optional mode)" section.
|
|
150
|
+
|
|
151
|
+
For exact, always-current syntax straight from the CLI binary, run `jjh help` — this file is
|
|
152
|
+
an expanded, example-rich companion to that output, not a replacement for it.
|
|
153
|
+
|
|
154
|
+
### Install into your repo (teach YOUR coding agents to prefer jjh)
|
|
155
|
+
|
|
156
|
+
[`agents-snippet.md`](./agents-snippet.md) is a short, imperative AGENTS.md
|
|
157
|
+
section — "version control in this repo goes through JJHub; prefer `jjh
|
|
158
|
+
change/stack/undo` over raw git/gh mutations" — the same pattern as GitHub's
|
|
159
|
+
`gh skill install github/gh-stack`. Append it to a tracked repo's own
|
|
160
|
+
AGENTS.md so any AGENTS.md-aware coding agent (Claude Code, Copilot, Cursor,
|
|
161
|
+
Codex, ...) picks up the workflow automatically:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
jjh agents-md >> AGENTS.md # from the CLI
|
|
165
|
+
# or fetch it from a running server:
|
|
166
|
+
curl https://<your-jjhub>/.well-known/skills/jjhub-cli/agents-snippet.md >> AGENTS.md
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Example workflows
|
|
170
|
+
|
|
171
|
+
### Create and land a change
|
|
172
|
+
|
|
173
|
+
```bash
|
|
174
|
+
npm run jjh -- repo init owner/name
|
|
175
|
+
npm run jjh -- change new "Fix: add user validation"
|
|
176
|
+
npm run jjh -- change list # note the CHG-n id
|
|
177
|
+
npm run jjh -- stack create "Fix bundle" --changes CHG-1
|
|
178
|
+
npm run jjh -- stack land "Fix bundle"
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
A single change not part of a stack lands directly: `npm run jjh -- change land CHG-1`.
|
|
182
|
+
|
|
183
|
+
### Sync a local jj working copy into JJHub
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
cd /path/to/local/jj/repo
|
|
187
|
+
jj bookmark track main --remote=origin # REQUIRED once per clone (issue #28):
|
|
188
|
+
# without it, already-landed commits look
|
|
189
|
+
# like new local work and the daemon could
|
|
190
|
+
# try to re-project them
|
|
191
|
+
npm run jjh -- repo init alice/my-project
|
|
192
|
+
jj new -m "Implement feature X" && jj commit
|
|
193
|
+
npm run jjh -- daemon . --once # one-shot sync; omit --once to loop every 5s
|
|
194
|
+
npm run jjh -- op log # verify the sync landed as a new operation
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
### Resolve a conflict
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
npm run jjh -- conflict list # note the conflict id
|
|
201
|
+
npm run jjh -- conflict resolve CONFLICT-abc123 left # or: right | edited | preserve_unresolved
|
|
202
|
+
npm run jjh -- conflict list # confirm it now reads Resolved
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Undo/redo caution
|
|
206
|
+
|
|
207
|
+
`jjh undo` and `jjh redo` move the repository's operation-log head pointer — they affect
|
|
208
|
+
*all* state (changes, stacks, bookmarks, conflicts), not just the last command's target.
|
|
209
|
+
Run `jjh op log` first if you're not sure what the current head operation is before undoing.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Version control in this repo goes through JJHub
|
|
2
|
+
|
|
3
|
+
This repository is tracked by JJHub (a change/stack/operation overlay on
|
|
4
|
+
GitHub). Use the `jjh` CLI for version-control mutations instead of raw
|
|
5
|
+
`git`/`gh` — every `jjh` action is attributed in an operation log and
|
|
6
|
+
reversible with `jjh undo`, and landing is CI-gated.
|
|
7
|
+
|
|
8
|
+
- Start work: `jjh change new "<title>"` (not a git branch).
|
|
9
|
+
- Describe/retitle: `jjh change describe CHG-N --title t --description d`.
|
|
10
|
+
- Amend after edits: `jjh change amend CHG-N` (descendants get flagged and
|
|
11
|
+
restack with one command — no force-push ceremony).
|
|
12
|
+
- Stacked work: `jjh stack create "<title>" --changes CHG-1,CHG-2`;
|
|
13
|
+
reorder with `jjh stack reorder`; land root-to-tip with
|
|
14
|
+
`jjh stack land <stackId>` (all-or-nothing, CI-gated).
|
|
15
|
+
- Ship a single change: `jjh change land CHG-N`. Do NOT merge the PR with
|
|
16
|
+
`gh pr merge` — that bypasses the gate and the op log.
|
|
17
|
+
- Made a mistake: `jjh undo` (works across changes, stacks, bookmarks, and
|
|
18
|
+
conflicts; `jjh op log` shows what would be undone).
|
|
19
|
+
- Conflicts: `jjh conflict list` / `jjh conflict resolve <id> <state>` —
|
|
20
|
+
conflicts are durable objects; you can defer them and keep working.
|
|
21
|
+
- Machine output: append `--json` to any command.
|
|
22
|
+
- Repo context is inferred from the git `origin` remote; `--repo owner/name`
|
|
23
|
+
overrides.
|
|
24
|
+
|
|
25
|
+
Reads are fine with either tool. Any `jjh` command it doesn't recognize
|
|
26
|
+
(`pr`, `issue`, ...) passes through to the real `gh` unchanged. Full command
|
|
27
|
+
reference: run `jjh help`, or see the served skill at
|
|
28
|
+
`/.well-known/skills/jjhub-cli/SKILL.md` on your JJHub server.
|