@skedulo/knowledge-hub-agent
v0.1.0
Published
Knowledge Hub agent: corpus reader and linter, guide draft flow, quality gates and Hub publish
Maintainers
Keywords
Readme
Knowledge Hub agent
@skedulo/knowledge-hub-agent is the knowledge-hub CLI behind the /knowledge-hub-* Claude Code commands
(skills/, shipped as the knowledge-hub plugin). It
reads the se-knowledge-corpus repo, checks and writes guides, runs the quality gates and publishes guides to the
Knowledge Hub tenant. Operators use the commands, not this package directly; see the
repo README.
Install
npm install -g @skedulo/knowledge-hub-agent && knowledge-hub setupThe package installs three commands:
knowledge-hub, the CLI.knowledge-hub-agent, the same CLI, sonpx -y @skedulo/knowledge-hub-agent <command>works.kh, the launcher the skills call. It runsnpx -y --package=@skedulo/knowledge-hub-agent@latest knowledge-hub, so the skills always use the newest release. It findsnpxin the usual Node locations whenPATHlacks it. The plugin ships the same script in itsbin/, and Claude Code puts that after your ownPATH, so it does not matter whichkhruns.
knowledge-hub setup [--no-plugin] signs you in to sked and gh where needed, then sets up Claude Code:
- Plugin. It runs
claude plugin marketplace add Skedulo/se-knowledge-hub#main, then setsextraKnownMarketplaces.se-knowledge-hub.autoUpdate: trueinsettings.json. Claude'sadddrops that setting each time it runs, so setup sets it afterwards. Then it runsclaude plugin install knowledge-hub@se-knowledge-hub.- An entry for
se-knowledge-hubwith no ref followsdev, and Claude then refuses#main, so setup first writesref: maininto it. - It removes the command copies an earlier setup made. It finds them by a
.knowledge-hub-setupfile in each folder, so a copy you made by hand is reported, never deleted.
- An entry for
- Copies. Setup uses copies when the
claudeCLI is missing, when GitHub access fails, or with--no-plugin. It copies the skills into~/.claude/skills/knowledge-hub-*.- Every later run of a newer
knowledge-hub(for example throughkh) updates those copies. A run from an older version never downgrades them. - The update prints one line on stderr only.
- Every later run of a newer
Setup then fetches the newest agent into npm's cache and runs preflight --hub. It writes Claude Code's files under
$CLAUDE_CONFIG_DIR (default ~/.claude) and nothing else; your logins stay in sked's and gh's own files. Run it
again any time.
From the repo:
git clone https://github.com/Skedulo/se-knowledge-hub.git && cd se-knowledge-hub
yarn --cwd packages/agent install --frozen-lockfile
yarn --cwd packages/agent build
node packages/agent/dist/cli.js <command>To install that build, run npm pack in packages/agent. Its prepack copies the root skills/ and bin/kh into
assets/, then builds. The package holds dist/ (without comments), assets/, bin/*.sh, bin/*.py, this README
and the corpus contract. Then run npm install -g ./skedulo-knowledge-hub-agent-<version>.tgz. To make kh run that
tarball, set KNOWLEDGE_HUB_AGENT_SPEC=<path to the .tgz>.
Release
The plugin's version in .claude-plugin/plugin.json must equal this package's version (a test checks). Installed
plugins only update when that string changes.
- Bump both versions in a PR to
dev. - Merge
devintomainwith a merge commit, never a squash. The marketplace followsmain, so plugin users get the new skills at their next session. - From
main, inpackages/agent, runnpm publish --tag next. Try it withKNOWLEDGE_HUB_AGENT_SPEC=@skedulo/knowledge-hub-agent@next, then runnpm dist-tag add @skedulo/knowledge-hub-agent@<version> latestright away.
Between steps 2 and 3, and in sessions opened before an update, new skills can meet the old CLI and old skills the new one. The CLI refuses unknown flags, so a release adds flags and commands and never renames or removes one the previous release's skills use.
Prerequisites
- macOS for the media and mobile commands. They use Spotlight (
mdls),screencapture, Apple Vision and Xcode.lint,gates,draftand the Hub commands are plain Node. - Node.js 20 or newer (24 recommended).
- git, and the GitHub CLI logged in (
gh auth login).corpus-syncclones the corpus withgh, and a livepublishasks origin formainwithgit ls-remote. - bash, for
preflightandcorpus-sync. - For the Hub commands (
publish,unpublish,preview-link,guide-health): the Skedulo CLI (npm install -g @skedulo/cli) logged in to the Hub tenant withsked tenant login web -a knowledge-hub -t knowledge-hub -o. Add-e testwhen the team is not found in production. A service token inSKEDULO_API_TOKENandSKEDULO_API_SERVERworks instead. - Optional, for media:
ffmpegandffprobe(brew install ffmpeg) formedia-infoand video frames.- The Python at
~/.knowledge-hub/ocr-venv, with Pillow formedia-sheetand soft cover bars, and pyobjc for text recognition (mobile --platform mirror,mobile cover). Set it up withpython3 -m venv ~/.knowledge-hub/ocr-venv && ~/.knowledge-hub/ocr-venv/bin/pip install pyobjc-framework-Vision pyobjc-framework-Quartz pillow.
- Optional, for mobile:
- Android:
adb(brew install android-platform-tools). - iPhone Simulator: Xcode (
xcrun simctl) andidb(brew install facebook/fb/idb-companion && pip3 install fb-idb). - A real iPhone: iPhone Mirroring.
- Android:
knowledge-hub preflight [--hub] checks the tools and the sked login, and prints the fix for each problem.
Commands
knowledge-hub --help | -h | help [<command>] # usage on stdout, exit 0 (no command at all: exit 2)
knowledge-hub --version | -v
knowledge-hub <command> --help # one command's usage, exit 0
knowledge-hub setup [--no-plugin]
knowledge-hub preflight [--hub]
knowledge-hub corpus-sync [main|dev]
knowledge-hub draft pending <corpus>
knowledge-hub draft review <corpus>/customers/<slug> [--base <ref>] [--markdown]
knowledge-hub draft plan <corpus>/customers/<slug>
knowledge-hub draft write <corpus>/customers/<slug> <draft.json>
knowledge-hub draft done <corpus>/customers/<slug> <plan.json> <area>...
knowledge-hub media-info <file> [--frames <dir>]
knowledge-hub media-sheet <spec.json>
knowledge-hub guide-outline <corpus>/customers/<slug>
knowledge-hub mobile labels [--headers <word>]... [--redact-text <text>]...
knowledge-hub mobile tap "<label>"
knowledge-hub mobile shot <out.png> [--focus <label>]... [--redact-text <text>]... [--headers <word>]...
knowledge-hub mobile cover <in.png> <out.png> [--focus <label>]... [--redact-text <text>]... [--headers <word>]...
# every mobile command: [--platform android|ios|mirror] [--serial <serial>] [--udid <udid>]
knowledge-hub lint <corpus>/customers/<slug> [--allow-dangling]
knowledge-hub gates <corpus>/customers/<slug> [--markdown] [--golden <file.json>]
knowledge-hub publish <corpus>/customers/<slug> [--alias <sked-alias>] [--draft] [--dry-run]
[--overwrite <guide_id>]... [--skip <guide_id>]... [--kind update|revise|media|publish] [--reason "<why>"]
[--pr https://github.com/<owner>/<repo>/pull/<n>]
knowledge-hub unpublish <slug> (--guide "<title>"... | --all) --reason "<why>" [--alias <sked-alias>] [--dry-run]
knowledge-hub preview-link <slug> [--alias <sked-alias>] [--days <n>]
knowledge-hub guide-health (<slug> | --all) [--alias <sked-alias>] [--dry-run]| Command | What it does |
| --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| setup | Installs the /knowledge-hub-* commands in Claude Code (the plugin with auto-update on, or copies) and signs you in to sked and gh; see Install. |
| preflight | Checks git, Node.js, gh and its login (with --hub, also sked and the sked login for $SKED_ALIAS, default knowledge-hub). Prints ready or one fix per problem. With the plugin installed, it also warns when its updates are off, it follows a branch other than main, or old copies duplicate it. |
| corpus-sync | Clones the corpus or fast-forwards it on main (default) or dev, and prints its path. Refuses a clone with local changes or unpushed commits. |
| draft | pending: customers with new material (JSON). review: source checks (--base also checks what a PR changed). plan: what to redraft (save it for done). write: writes one guide from draft.json. done: clears drafted areas. |
| media-info | What a screenshot or video says about itself (JSON). --frames saves three stills from a video. |
| media-sheet | One contact sheet (PNG) of up to 12 screenshots from { "output", "title", "shots": [{ "image", "title", "note"?, "replaces"? }] }. |
| guide-outline | Each guide's sections with their bold UI labels and media (JSON). |
| mobile | Reads, taps and screenshots an Android phone, the iPhone Simulator or iPhone Mirroring, with personal data covered. cover covers any screenshot. It never taps a control that changes data. |
| lint | Checks sources and guides against the corpus contract, and flags email addresses and phone numbers in guide text. --allow-dangling makes citations of removed sources warnings. |
| gates | The quality gates (evidence, coverage, thin content, golden questions) as JSON, or markdown. |
| publish | Publishes a customer's guides to the Hub as immutable ProjectGuide versions, live or with --draft. |
| unpublish | Takes live guides off the Hub page now, without a PR. Each gets a withdrawn version; nothing is deleted. |
| preview-link | Prints { "token", "expiresAt" } for the draft preview link #/<slug>?draft=<token>. Reuses the token until a day before it expires. |
| guide-health | Rebuilds the customer's Guide rows and Project counts. It writes only what changed and never deletes. |
draft.json is { guide_id | new: { area, name }, category, title, sources, body_file }.
publish --dry-run marks each guide as one of these:
create,update,skiporconflict.withdrawn: taken down and unchanged, so it stays down.returns: taken down, but its text changed, so it comes back. The takedownreasonis shown.orphan: live in the Hub but gone from the corpus. A publish leaves orphans live; take them down withunpublish.
Before publishing, a run syncs the media under guides/media on corpus main or dev (as pushed) to the Project's
attachments.
After a publish or unpublish writes its status, the run's SyncRun also gets:
Kind: from--kind. The default ispublish, orupdatewith--draft.update,reviseandmedianeed--draft.Reason.Conflicts: the titles left as conflicts.CorpusCommit: the pushed corpus commit.- The approval PR from
--pr.
The customer's Guide rows and the Project's guide counts and last run are refreshed too. Neither step can fail the
run; anything that could not be written is listed under warnings. guide-health is the fix for a "Guide health not
refreshed" warning.
Strict flags
The Hub commands stop with exit 2 before they read a login, run git or call the Hub when they get any of these:
- a flag they don't take, such as
--draftsor--dryrun - a flag with no value, or whose value starts with
-- - a single-value flag given twice
- an extra argument
They print one plain line, such as unknown option --drafts, then the usage on stderr. --help or -h prints the
usage on stdout and exits 0. Dry runs (publish --dry-run, unpublish --dry-run, guide-health --dry-run) print
"dryRun": true in their JSON.
Live-publish guard
A live publish (no --draft, no --dry-run) runs only from the approved main. Before any Hub call, two checks must
pass:
- The corpus clone's HEAD must be the commit that origin has on
main. This is checked withgit ls-remote origin refs/heads/main, which also works in shallow clones. customers/<slug>/guidesmust have no uncommitted or untracked changes.
Otherwise it exits 2 with one line, for example:
Live publishing only runs from the approved main branch; the guides folder is on 9f8e7d6, main is a1b2c3d. Run knowledge-hub corpus-sync main and try again.When git cannot reach origin, it prints could not check the approved main branch: <reason>. knowledge-hub corpus-sync
main puts the clone on main. Draft publishes and dry runs work on any branch.
Login
The Hub commands pick their login in this order:
- The sked login for
--alias <sked-alias>. An explicit alias always wins. SKEDULO_API_TOKEN+SKEDULO_API_SERVER.- The sked login for
$SKED_ALIAS.
There is no default alias, because a default is how someone writes to the wrong tenant. With no login, the command prints this line and exits 2:
No Skedulo login: pass --alias <sked-alias> (e.g. --alias knowledge-hub) or set SKEDULO_API_TOKEN and SKEDULO_API_SERVER.An expired or missing sked login names the sked tenant login web command to run. The token is read locally and never
printed. Only preflight --hub assumes the alias knowledge-hub when SKED_ALIAS is not set.
Each Hub request gives up after 60 s. These failures each print one line:
could not reach <host>: <cause>when the Hub cannot be reached.the Hub at <host> did not answer within 60 son a timeout.the Skedulo login was rejected (HTTP 401); sign in again with sked tenant login webwhen the login is refused (HTTP 401 or 403). What the Hub said is added in brackets, so a missing permission can be told apart from an old login.
Environment
| Variable | Used by | Meaning |
| --------------------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------- |
| SKED_ALIAS | Hub commands, preflight --hub | sked alias to log in with when --alias is not given and no service token is set |
| SKED_TEAM | Hub commands, preflight | team named in the login hint (default knowledge-hub) |
| SKED_ENV | Hub commands, preflight | environment named in the login hint (-e <env>), e.g. test |
| SKED_CONFIG_PATH | Hub commands, preflight --hub | sked's login file (default $XDG_CONFIG_HOME/sked/config.json, else ~/.config/sked/config.json) |
| XDG_CONFIG_HOME | Hub commands, preflight --hub | where sked's config lives when SKED_CONFIG_PATH is not set |
| SKEDULO_API_TOKEN | Hub commands | service-account token, used with SKEDULO_API_SERVER when no --alias is given |
| SKEDULO_API_SERVER | Hub commands | API server for that token, e.g. https://api.skedulo.com |
| PUBLISH_TRIGGERED_BY | publish, unpublish | who ran it, kept on the SyncRun (default: the sked login's username, else ci) |
| KNOWLEDGE_HUB_CORPUS | corpus-sync | where the corpus clone lives (default ~/.knowledge-hub/corpus) |
| KNOWLEDGE_HUB_CORPUS_REPO | corpus-sync | the corpus repo to clone (default Skedulo/se-knowledge-corpus) |
| KNOWLEDGE_HUB_OCR_PYTHON | mobile, media-sheet | Python with Pillow and Apple Vision (default ~/.knowledge-hub/ocr-venv/bin/python) |
| KNOWLEDGE_HUB_AGENT_SPEC | kh | the package kh runs (default @skedulo/knowledge-hub-agent@latest); @next or a tarball path to try a release |
| KNOWLEDGE_HUB_MARKETPLACE | setup | the marketplace to install from (default Skedulo/se-knowledge-hub#main); a branch, for testing |
| CLAUDE_CONFIG_DIR | setup, preflight, every run | Claude Code's config folder (default ~/.claude) |
Exit codes
| Code | Meaning |
| ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 0 | Done. Also for --help, help and --version. |
| 1 | The work failed: a lint or gate problem, the corpus needs attention, a Hub failure, an unknown customer or title, a device or tool not ready (mobile, media-sheet), or an unexpected error (printed as one line). For preflight, a tool every command needs is missing. For corpus-sync, local changes, unpushed commits or no such branch. |
| 2 | Bad usage or environment: no command, an unknown command or flag, a missing value, an extra argument, a path outside customers/, a missing file or folder (media-info, guide-outline, publish), no Skedulo login, a live publish that is not on the approved main, or bash not installed. For preflight, only the Skedulo side is missing. |
| 3 | publish only: published, but some guides were left because a newer manual edit exists in the Hub (see conflicts). |
Layout
| Path | What it holds |
| -------------- | ----------------------------------------------------------------------------------------------------------------- |
| src/cli.ts | The knowledge-hub entry point: routing, help, version |
| src/corpus/ | Front-matter schemas, corpus reader, dirty marks, lint |
| src/draft/ | draft: pending, review, plan, write, done |
| src/gates/ | Quality gates: evidence, coverage, thin content, golden questions |
| src/media/ | media-info, media-sheet, guide-outline |
| src/mobile/ | mobile: Android, iPhone Simulator and iPhone Mirroring capture, personal-data cover |
| src/publish/ | publish, unpublish, preview-link, guide-health; GraphQL and Files clients, sked login, live-publish guard |
| bin/ | corpus-sync.sh, preflight.sh, and the Python helpers for media and text recognition |
Tests run from the repo root: yarn test, or npx jest packages/agent for this package only.
The corpus format is in docs/corpus-contract.md. How the pieces fit is in
docs/current-flow.md.
