@claudehubcc/cli
v1.2.0
Published
ClaudeHub CLI - Sync skills, workflows, and sub-agents like git
Maintainers
Readme
@claudehubcc/cli
ClaudeHub CLI -- sync skills, workflows, and sub-agents like git.
Installation
npm install -g @claudehubcc/cli
# or
yarn global add @claudehubcc/cli
# or
pnpm add -g @claudehubcc/cliRequires Node.js 18 or higher. Verify with:
ch --versionQuick Start
# 1. Log in
ch login
# 2. Initialize your project (run once per repo)
ch init
# 3. Pull all skills, workflows, and sub-agents
ch pull
# 4. Edit locally, then push
ch pushAuthentication
Credentials are stored globally in ~/.claudehub/config.json.
ch login # prompt for API key, store globally
ch logout # clear credentials
ch logout -y # skip confirmation
ch whoami # show current user + active project workspaceGenerate an API key from Settings > API Keys in your ClaudeHub dashboard.
Init
Run once in your project root to create .claudehub/config.json.
ch init # interactive: select workspace, categories, ignore patterns
ch init --force # overwrite existing config (also resets lock.json)Prompts:
- Workspace -- which workspace to sync with
- Categories -- limit which skills/workflows to sync (leave empty = all)
- Ignore patterns -- glob patterns to skip, e.g.
draft-*,wip-*
Tip: Commit both
.claudehub/config.jsonand.claudehub/lock.jsonto git. The lockfile tracks synced state (likepackage-lock.json) and enables conflict detection for your whole team.
Workspaces
Workspace selection is project-scoped and written to .claudehub/config.json.
ch workspace list # list all workspaces (* = current)
ch workspace use <name-or-id> # set workspace for this project
ch workspace current # show active workspace
ch ws list # alias: wsPull & Push
Skills and workflows live in .claude/skills/. Sub-agents are single .md
files under .claude/agents/.
| Type | Location | Detection |
|------|----------|-----------|
| Skill | .claude/skills/<name>/ | folder with SKILL.md |
| Workflow | .claude/skills/<name>/ | folder with SKILL.md + workflow-graph.yaml |
| Sub-agent | .claude/agents/<name>.md | single .md file with YAML frontmatter |
Pull
Download skills, workflows, and sub-agents from the active workspace.
ch pull # pull all (filtered by categories + ignore)
ch pull <name> # pull one item
ch pull <name> -f # force overwrite local changesOutput:
+ [skill] code-review (new)
* [skill] summarize (v2 -> v3)
= [workflow] deploy-workflow (unchanged)
+ [agent] triage-bot (new)
Summary: 2 new, 1 updated, 1 unchanged, 0 conflictsPush
Upload local skills, workflows, and sub-agents to the active workspace.
ch push # push all changed items
ch push <name> # push one item
ch push <name> -f # force push on conflictOutput:
* [skill] code-review (v3 -> v4)
= [skill] summarize (unchanged)
+ [agent] triage-bot (v1)
Summary: 1 new, 1 updated, 1 unchanged, 0 conflictsConflict detection
Version-based: if the remote version is newer than what is recorded in .claudehub/lock.json and local files have been modified, the CLI warns you instead of silently overwriting.
! [skill] code-review (conflict: local + remote both changed; remote v5 > lock v3; use -f to overwrite)Use -f to force through a conflict.
Creating new skills and workflows
When pushing a new skill or workflow folder, SKILL.md must include YAML
frontmatter:
---
name: my-skill
description: What this skill does
---
# My Skill
...A folder is treated as a workflow (not a plain skill) when it also contains
a workflow-graph.yaml. Each node in the graph must declare a
data.content_file -- a path, relative to the workflow folder, to the file that
holds that node's prompt or script. Those referenced files must be bundled in
the folder so they are uploaded together. A minimal example:
# .claude/skills/deploy-workflow/workflow-graph.yaml
nodes:
- id: node-1
type: prompt
data:
content_file: nodes/node-1.md # required: path relative to this folder
- id: node-2
type: prompt
data:
content_file: nodes/node-2.md
edges:
- source: node-1
target: node-2.claude/skills/deploy-workflow/
├── SKILL.md
├── workflow-graph.yaml
└── nodes/
├── node-1.md
└── node-2.mdA node missing
data.content_file, or one whosecontent_filepoints at a file not present in the folder, is rejected.ch pushvalidates this locally before uploading and reports the offending file.
Creating new sub-agents
Sub-agents are single .md files at .claude/agents/<name>.md. The file
must include YAML frontmatter (name, description, and optional tools,
model, etc. -- the standard Claude Code sub-agent format):
---
name: triage-bot
description: Classifies new GitHub issues by area
tools: [Read, Grep, Glob]
model: claude-sonnet-4-6
---
You are a triage bot. ...Status
Show local vs remote sync state before pulling or pushing -- similar to git status.
ch statusExample output:
Workspace: my-workspace (6839a1f2c4e8b3d2a1f0e9c8)
Categories: programming, code-tools
Ignore: draft-*
Modified locally (run "ch push"):
* [skill] code-review (v3)
Remote ahead (run "ch pull"):
v [skill] summarize (lock v2 -> remote v4)
New locally (run "ch push"):
+ [agent] triage-bot
Ignored:
. [skill] draft-experiment
17 unchanged, 1 ignored (not shown).Diff
Show a line-level diff between a local item and its remote version.
ch diff <name>Example output:
Diff: [skill] code-review (remote -> local; + added locally, - removed locally)
~ SKILL.md (modified)
+ ## New section
+ Added this paragraph locally.If a sub-agent and a skill share a name, ch diff <name> resolves the
skill/workflow first. Resolve the sub-agent explicitly by ensuring no
folder of the same name exists under .claude/skills/.
Configuration files
Global -- ~/.claudehub/config.json
Stores credentials only. Written by ch login.
{
"apiKey": "sk-ch-...",
"user": {
"id": "...",
"email": "[email protected]",
"name": "Your Name"
}
}Project -- .claudehub/config.json
Workspace and sync settings for this project. Written by ch init. Commit to git.
{
"workspace": {
"id": "6839a1f2c4e8b3d2a1f0e9c8",
"name": "my-workspace"
},
"categories": ["programming", "code-tools"],
"ignore": ["draft-*", "wip-*"]
}categories: [] means all categories are included (no filter).
Lockfile -- .claudehub/lock.json
Tracks the last synced state of each item. Commit to git (like package-lock.json).
Keys are namespaced by type (skill:, workflow:, agent:) so the same
name can be used across types without collision.
{
"skill:code-review": {
"id": "abc123",
"version": 3,
"type": "skill",
"hash": "a3f9..."
},
"workflow:deploy-workflow": {
"id": "def456",
"version": 2,
"type": "workflow",
"hash": "b7c2..."
},
"agent:triage-bot": {
"id": "ghi789",
"version": 1,
"type": "agent",
"hash": "c4d1..."
}
}Migrating from a pre-
agentlockfile? Older lockfiles used plain names (no type prefix) as keys. Those entries are now ignored on read with a one-time warning. Runch pull -forch push -fonce to rebuild the lockfile in the new format.
Working tree layout
<project-root>/
├── .claudehub/
│ ├── config.json # workspace, categories, ignore
│ └── lock.json # synced state (commit to git)
└── .claude/
├── skills/
│ ├── code-review/ # skill
│ │ └── SKILL.md
│ └── deploy-workflow/ # workflow (has workflow-graph.yaml)
│ ├── SKILL.md
│ ├── workflow-graph.yaml # each node needs data.content_file
│ └── nodes/
│ ├── node-1.md # referenced by content_file
│ └── node-2.md
└── agents/
└── triage-bot.md # sub-agent (single .md file)Command reference
| Command | Description |
|---------|-------------|
| ch login | Authenticate with an API key |
| ch logout [-y] | Clear stored credentials |
| ch whoami | Show current user and active project workspace |
| ch init [--force] | Initialize project: workspace, categories, ignore |
| ch workspace list | List all workspaces (alias: ws list) |
| ch workspace use [name] | Set the active workspace for this project |
| ch workspace current | Show the active workspace |
| ch pull [name] [-f] | Download all (or one) skills, workflows, and sub-agents |
| ch push [name] [-f] | Upload all (or one) skills, workflows, and sub-agents |
| ch status | Show local vs remote sync state |
| ch diff <name> | Show line-level diff for one item vs remote |
License
MIT
