claude-code-hub 1.11.0 → 1.13.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/README.md +3 -3
- package/lib/apps.js +125 -25
- package/lib/ext.js +470 -0
- package/lib/hub-config.js +15 -0
- package/lib/install.js +60 -19
- package/package.json +5 -5
- package/packages/claude-hub-sdk/src/client.js +25 -10
- package/plugin/plugins/claude-code-hub/.claude-plugin/plugin.json +1 -1
- package/plugin/plugins/claude-code-hub/skills/hub-builder/SKILL.md +2 -1
- package/plugin/plugins/claude-code-hub/skills/hub-builder/references/extensibility.md +1 -1
- package/plugin/plugins/claude-code-hub/skills/hub-builder/references/extensions.md +56 -0
- package/public/app.js +231 -76
- package/public/index.html +20 -13
- package/server.js +90 -43
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
((root) => {
|
|
5
5
|
'use strict';
|
|
6
6
|
|
|
7
|
+
// Holds {origin, vars}: the hub origin that sent the cached vars. Every config dir's app shares the
|
|
8
|
+
// hub's proxy port, so a page framed by something other than the hub would otherwise paint the hub's last theme.
|
|
7
9
|
const VARS_KEY = 'claude-hub:vars';
|
|
8
10
|
const WELCOME_WAIT_MS = 2000;
|
|
9
11
|
|
|
@@ -51,24 +53,33 @@
|
|
|
51
53
|
return true;
|
|
52
54
|
}
|
|
53
55
|
|
|
54
|
-
function adoptVars(vars) {
|
|
56
|
+
function adoptVars(vars, from) {
|
|
55
57
|
if (!setVars(vars) && vars) return;
|
|
56
58
|
try {
|
|
57
|
-
if (vars) win.localStorage.setItem(VARS_KEY, JSON.stringify(vars));
|
|
59
|
+
if (vars) win.localStorage.setItem(VARS_KEY, JSON.stringify({ origin: from, vars }));
|
|
58
60
|
else win.localStorage.removeItem(VARS_KEY);
|
|
59
61
|
} catch (_) {}
|
|
60
62
|
}
|
|
61
63
|
|
|
62
|
-
function
|
|
64
|
+
function readCache() {
|
|
63
65
|
try {
|
|
64
|
-
const
|
|
65
|
-
return isVars(
|
|
66
|
+
const c = JSON.parse(win.localStorage.getItem(VARS_KEY));
|
|
67
|
+
return c && isVars(c.vars) ? c : null;
|
|
66
68
|
} catch (_) {
|
|
67
69
|
return null;
|
|
68
70
|
}
|
|
69
71
|
}
|
|
70
72
|
|
|
71
|
-
|
|
73
|
+
// The hub is the top window, also for a page framed inside another app's page, such as a cck
|
|
74
|
+
// pane. Firefox does not list the ancestors, so there any origin passes.
|
|
75
|
+
const ancestors = win.location?.ancestorOrigins;
|
|
76
|
+
const topOrigin = ancestors?.length ? ancestors[ancestors.length - 1] : undefined;
|
|
77
|
+
const hubOnTop = (o) => topOrigin === undefined || topOrigin === o;
|
|
78
|
+
|
|
79
|
+
if (framed) {
|
|
80
|
+
const cache = readCache();
|
|
81
|
+
if (cache && hubOnTop(cache.origin)) setVars(cache.vars);
|
|
82
|
+
}
|
|
72
83
|
|
|
73
84
|
function connect({ standalone = {} } = {}) {
|
|
74
85
|
if (hub) return hub;
|
|
@@ -98,8 +109,9 @@
|
|
|
98
109
|
return typeof fn === 'function' ? fn : null;
|
|
99
110
|
};
|
|
100
111
|
|
|
112
|
+
// The top window, not the parent: a page nested inside another app's page talks to the hub itself.
|
|
101
113
|
function post(msg) {
|
|
102
|
-
if (origin) win.
|
|
114
|
+
if (origin) win.top.postMessage(msg, origin);
|
|
103
115
|
}
|
|
104
116
|
|
|
105
117
|
function setStatus(next) {
|
|
@@ -129,7 +141,7 @@
|
|
|
129
141
|
|
|
130
142
|
function themeIn(p) {
|
|
131
143
|
if (!p || typeof p !== 'object') return;
|
|
132
|
-
if (isVars(p.vars)) adoptVars(p.vars);
|
|
144
|
+
if (isVars(p.vars)) adoptVars(p.vars, origin);
|
|
133
145
|
else if (!waiting()) adoptVars(null);
|
|
134
146
|
lastTheme = p;
|
|
135
147
|
applyMode(p);
|
|
@@ -172,7 +184,7 @@
|
|
|
172
184
|
}
|
|
173
185
|
|
|
174
186
|
function onMessage(e) {
|
|
175
|
-
if (!origin || e.source !== win.
|
|
187
|
+
if (!origin || e.source !== win.top || e.origin !== origin) return;
|
|
176
188
|
const m = e.data;
|
|
177
189
|
if (!m || typeof m.type !== 'string') return;
|
|
178
190
|
switch (m.type) {
|
|
@@ -247,7 +259,10 @@
|
|
|
247
259
|
.catch(() => ({}))
|
|
248
260
|
.then((cfg) => {
|
|
249
261
|
try {
|
|
250
|
-
if (framed && cfg?.enabled === true)
|
|
262
|
+
if (framed && cfg?.enabled === true) {
|
|
263
|
+
const hubOrigin = new URL(cfg.url).origin;
|
|
264
|
+
if (hubOnTop(hubOrigin)) origin = hubOrigin;
|
|
265
|
+
}
|
|
251
266
|
} catch (_) {}
|
|
252
267
|
if (!origin) {
|
|
253
268
|
setVars(null);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-hub",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.9",
|
|
4
4
|
"description": "Claude Code Hub integration: a skill to build on and extend the hub, with links to its docs, the extension mechanism and custom themes",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "NikiforovAll"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: hub-builder
|
|
3
|
-
description: Build on and extend Claude Code Hub. Use when the user wants to patch or fork a built-in tool (Kanban, Cost, Marketplace, Memory), write a new app for a hub tab, connect apps through topics and actions, make a custom color theme, configure the hub (flags, config dirs, token, hub keyboard shortcuts), or fix a hub problem.
|
|
3
|
+
description: Build on and extend Claude Code Hub. Use when the user wants to patch or fork a built-in tool (Kanban, Cost, Marketplace, Memory), write a new app for a hub tab, find, install, manage or develop an Extension (`claude-code-hub ext`), connect apps through topics and actions, make a custom color theme, configure the hub (flags, config dirs, token, hub keyboard shortcuts), or fix a hub problem.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Hub builder
|
|
@@ -18,6 +18,7 @@ The files under `references/` ship with the installed hub, so they match its ver
|
|
|
18
18
|
| Case | Page |
|
|
19
19
|
|---|---|
|
|
20
20
|
| Extend the hub: patch or fork a built-in tool, write a new app, configure the `apps` entry, the manifest, the SDK, topics, actions, capabilities | [`references/extensibility.md`](references/extensibility.md). It names the docs page for each part |
|
|
21
|
+
| Find, install, manage or develop an Extension (`claude-code-hub ext`) | [`references/extensions.md`](references/extensions.md) |
|
|
21
22
|
| The wire messages of the hub protocol | `reference/protocol/`, then `reference/architecture/` for processes and ports |
|
|
22
23
|
| Start the hub, open it with its token, install it as an app | `getting-started/` |
|
|
23
24
|
| Flags, environment variables, `config.json`, the files the hub writes | `reference/configuration/` |
|
|
@@ -60,7 +60,7 @@ After a server change, restart the app: `Ctrl+Alt+A`, select it, `Ctrl+R`. After
|
|
|
60
60
|
|
|
61
61
|
## The manifest
|
|
62
62
|
|
|
63
|
-
`hub-app.json` declares how to run the app and what it offers: `manifest: 1`, `id`, `name`, `icon` (a Lucide name), `run.entry` (inside the folder; a path into `node_modules` is fine), `run.defaultPort`, `loading.verbs`, `publishes`, `actions.handles`, `provides`, `keys.keeps`, `plugin` (`{
|
|
63
|
+
`hub-app.json` declares how to run the app and what it offers: `manifest: 1`, `id`, `name`, `icon` (a Lucide name), `run.entry` (inside the folder; a path into `node_modules` is fine), `run.defaultPort`, `loading.verbs`, `publishes`, `actions.handles`, `provides`, `keys.keeps`, `plugin` (`{marketplace, name, install?}` of a Claude Code plugin the app ships; the app launcher shows its state), `requires` (`{hub, node, apps}`), `listing` (the text of its registry page). To share an app, package it as an extension: [`extensions.md`](extensions.md). The hub reads it before the app runs and skips the app, with a log line, when it breaks a rule. Page: `extensibility/reference/manifest/`.
|
|
64
64
|
|
|
65
65
|
The npm packages of the built-in tools do not ship `hub-app.json`. A patch or fork starts from the tool's manifest in its GitHub repo and changes `id`, `name` and `run.entry`. Page for what each built-in declares: `extensibility/reference/built-in/`.
|
|
66
66
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# Extensions
|
|
2
|
+
|
|
3
|
+
An extension is a hub app that the hub does not ship: a folder with a `hub-app.json`, often with a Claude Code plugin. Page: `extensibility/extensions/`.
|
|
4
|
+
|
|
5
|
+
- `ext install <name>` finds the extension in the registry and installs the GitHub source it lists, as below. A folder named `<name>` in the current dir wins.
|
|
6
|
+
- `ext install github:<owner>/<repo>[/<folder>][@<commit or branch>]` clones it into `<hub dir>/extensions/<id>` (needs `git`), installs its npm dependencies with install scripts off, and installs its plugin.
|
|
7
|
+
- `ext install <dir>` makes it a *linked* extension: the hub runs it from that folder in place and copies nothing.
|
|
8
|
+
|
|
9
|
+
`claude-code-hub ext` prints the commands and their options. In a clone of the hub, run `node server.js ext …` from the clone.
|
|
10
|
+
|
|
11
|
+
## Find
|
|
12
|
+
|
|
13
|
+
Fetch the registry index, `https://nikiforovall.blog/claude-code-hub/extensions.json`, and match the user's need against each entry's `tagline`, `description` and `tags`. Done when the user has the `page` link of every match, or knows the registry has none.
|
|
14
|
+
|
|
15
|
+
## Install
|
|
16
|
+
|
|
17
|
+
1. **Trust.** An extension runs as the user with no sandbox, and its plugin can run hooks in every Claude Code session. Tell the user that, name the repository and the plugin, and wait for a yes.
|
|
18
|
+
2. **Run** the entry's `install` command, `claude-code-hub ext install <id>`. A failed step deletes the clone and leaves `config.json` as it was, so fix the cause (see Failures) and run the command again.
|
|
19
|
+
3. **Restart** the hub. The extension starts with it and gets the next tab. `"deferredStart": true` in its `apps` entry makes it start only from the app launcher (`Ctrl+Alt+A`, select it, `Ctrl+S`).
|
|
20
|
+
|
|
21
|
+
Done when `claude-code-hub ext list` shows the id with its source and its plugin `ok`.
|
|
22
|
+
|
|
23
|
+
For an extension outside the registry: step 1, `claude-code-hub ext install github:<owner>/<repo>[/<folder>]`, step 3. For a folder on disk, the user's own included: step 1, `npm install` in the folder when its `package.json` has dependencies, `claude-code-hub ext install <dir>`, step 3.
|
|
24
|
+
|
|
25
|
+
A linked extension lives in its folder: to move it, `ext remove <id>`, move the folder, `ext install` again.
|
|
26
|
+
|
|
27
|
+
## Develop
|
|
28
|
+
|
|
29
|
+
A linked extension is the dev loop: the hub runs the code where the user edits it.
|
|
30
|
+
|
|
31
|
+
| Changed | Then |
|
|
32
|
+
|---|---|
|
|
33
|
+
| App code | Restart the app: `Ctrl+Alt+A`, select it, `Ctrl+R` |
|
|
34
|
+
| Plugin | `claude-code-hub ext update <id>`, then a new Claude Code session. Claude Code runs a copy from its cache, and `update` reinstalls even at the same version |
|
|
35
|
+
| `hub-app.json` | Restart the hub |
|
|
36
|
+
|
|
37
|
+
The registry lists an extension whose manifest has `listing.tagline`. Page: `extensibility/reference/manifest/#listing`.
|
|
38
|
+
|
|
39
|
+
## Manage
|
|
40
|
+
|
|
41
|
+
- **Another config dir.** Claude Code loads plugins per config dir: `ext install <id> --dir <config dir>`. It works for a built-in tool too (`ext install kanban`), and is safe to run again.
|
|
42
|
+
- **Update.** `ext update <id>` moves a GitHub extension to the tip of its branch (a pinned commit stays), installs its dependencies and plugin again, and goes back to the old commit when a check fails. Then `Ctrl+R` on its row and a new Claude Code session.
|
|
43
|
+
- **Remove.** `ext remove <id>` removes the plugin, its marketplace and the `apps` entry, deletes the clone of a GitHub extension, keeps a linked folder, and needs a hub restart.
|
|
44
|
+
- `ext update` and `ext remove` act only on entries that `ext install` added.
|
|
45
|
+
|
|
46
|
+
## Failures
|
|
47
|
+
|
|
48
|
+
- **⚠ on the app's row in the app launcher**, or a plugin state other than `ok` in `ext list` (`missing`, `disabled`, `mismatch`): a click on the ⚠ copies the `ext install <id>` command that fixes it.
|
|
49
|
+
- **`Cannot register the plugin: marketplace "<name>" is registered from <path>`**: another folder already uses that marketplace name. The line ends with a `claude plugin marketplace remove` command; ask the user before running it, because it removes that marketplace's plugins.
|
|
50
|
+
- **`it needs hub <x>` or `it needs Node <x>`**: upgrade the hub (`npm i -g claude-code-hub`) or Node. When it names an app that is off, turn that app on in the `apps` list of `config.json`.
|
|
51
|
+
- **`git is not on PATH`**: install git, or clone the repo by other means and pass the folder.
|
|
52
|
+
- **`git clone failed`**: the repo name is wrong, or the repo is private and git has no credentials for it.
|
|
53
|
+
- **`Only a registry name, a github: source or a local folder installs`**: the source is `npm:` or a URL. Write it as `github:<owner>/<repo>[/<folder>]`.
|
|
54
|
+
- **`Cannot read the registry`**: no network, or the site is down. Install with the entry's `source` from the registry page: `ext install github:…`, plus `@<sha>` for a pinned entry.
|
|
55
|
+
- **`is not an app the hub runs, a folder, or an extension in the registry`**: the name is wrong. Find it in the registry (see Find).
|
|
56
|
+
- **The app is missing from the launcher after a restart**: the hub startup log names each skipped app and why.
|