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/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.