Galaxy Blackhole CLI
blackhole — the terminal half of Galaxy Blackhole: a coding agent you can run in any
repository, plus the launcher for the Galaxy Blackhole web GUI. Published identity is
@galaxy-stack/blackhole-cli; the binary is blackhole.
blackhole chat and blackhole web run on the Galaxy Blackhole harness
(blackhole-tauri): the session, the model, the tools and the web GUI are the same app the
desktop shell loads. The one-shot run, flow, skills and memory commands still use
@galaxy-stack/ai-coder-core; the terminal surface itself is React 19 + Ink 7.
The harness ships separately from this package: blackhole harness install downloads it into
~/.galaxy/harness/blackhole-harness. The resolver then reads, in order, GBH_TAURI_ROOT,
$GBH_HOME/harness/blackhole-harness, ~/.galaxy/harness/blackhole-harness (and any --dir
the installer recorded), a sibling blackhole-harness checkout, and finally the developer path
~/Desktop/Project/galaxy/galaxy-blackhole/blackhole-harness. Booting needs Node 22.6+ — the
floor for stripping TypeScript types — and nothing else: the launcher is a Node program, so
blackhole web runs it with Node instead of asking for Bun. The ladder is GBH_NODE → newest
nvm install → node on PATH → /usr/local/bin/node → /opt/homebrew/bin/node; when none
reaches 22.6 the command names every binary it refused and how to install one. Bun is only a
build tool of the desktop checkout (it bundles the client plugins), never a boot dependency.
Install
npm i -g @galaxy-stack/blackhole-cli@latest
blackhole harness install # install the harness from npm into ~/.galaxy/harness
blackhole doctor # check the API key and the connection
blackhole web # open the Galaxy Blackhole web GUI
blackhole chat # start working
Node.js >= 22.13 is required.
A global install carries the CLI, not the harness. Run blackhole harness install once; if you
forget, chat and web notice the missing harness, print the two ways out, and in a terminal
offer Cài harness ngay? [Y/n] — answering yes installs it and the command you typed continues
on its own.
The harness
The harness (blackhole-harness) is the app chat and web boot. It is a separate project, so
this package installs it on demand instead of bundling it:
| command | what it does |
|---|---|
blackhole harness install | Installs the harness into ~/.galaxy/harness/blackhole-harness — the npm package first, then git, then ZIP — and verifies the host runtime (--force, --dir, --from, --no-deps) |
blackhole harness status | Prints the harness in use, where it came from, its version, node/bun, which node_modules holds the kernel (apps/host or the hoisted one), whether the host runtime (@deepseek-ai/dsh/lib/bin.js) is there, and nguồn: npm @galaxy-blackhole/[email protected] for an npm install |
blackhole harness remove | Deletes ~/.galaxy/harness after asking (--yes skips the question) |
install fetches the published harness from npm first: npm i --omit=dev --prefix <tmp> @galaxy-blackhole/blackhole-harness@latest is one install that already carries the kernel and its
dependencies — measured 12/11/2026: 684 packages, 16 direct dependencies — and the installer moves
that tree to the checkout root, where the kernel sits hoisted at node_modules/@deepseek-ai/dsh
(no apps/host/node_modules, and no second npm install). GBH_HARNESS_PACKAGE renames the
package and GBH_HARNESS_SPEC pins a version. When npm is missing or fails, it falls back to
git clone --depth 1 (git on PATH; Git for Windows counts) and then to the branch archive from
GitHub, unpacked with a built-in ZIP reader — no git, no unzip, no tar. Those rungs install
what the launcher needs: bun install at the checkout root and in apps/host when Bun is
available, or npm inside apps/host when only npm is — npm ci --omit=dev when the checkout
carries a package-lock.json, npm install --omit=dev otherwise, and as the fallback when
ci refuses a lockfile it cannot honour. Every step is printed before it runs, and a step that
fails prints its own last 15 lines. --force downloads again, --dir <path> puts the checkout
somewhere else (the location is recorded so the next chat finds it), --from <url|npm> takes a
.zip URL, another git repository, or the word npm to force the package, and --no-deps
stops after the download (skipping npm, which always installs the package's dependencies).
install is finished only when the checkout can boot: it checks for the runtime the launcher
starts, @deepseek-ai/dsh/lib/bin.js, in both layouts — apps/host/node_modules/… for a
repository checkout, node_modules/… for the npm package — and when that file is missing it
reports the failure with exit code 1 instead of saying "xong".
chat and web make the same check before spawning anything, so a half-installed tree produces
one sentence (what is missing, and blackhole harness install --force) instead of a path from
inside the launcher.
status always says which rung of the ladder answered:
| source | path |
|---|---|
GBH_TAURI_ROOT | the checkout named by that variable |
$GBH_HOME/harness/blackhole-harness | a harness pinned to one harness home |
~/.galaxy/harness/blackhole-harness | the default install, or wherever --dir recorded |
sibling của package | a blackhole-harness checkout next to this package |
dev checkout | ~/Desktop/Project/galaxy/galaxy-blackhole/blackhole-harness |
When none of them answers, the CLI prints the two ways out — install it, or point
GBH_TAURI_ROOT at a checkout — with a command for macOS/Linux and for both Windows shells:
Chưa có harness blackhole-harness. Chạy: blackhole harness install (hoặc đặt GBH_TAURI_ROOT tới bản checkout)
macOS/Linux (bash/zsh): export GBH_TAURI_ROOT="$HOME/Desktop/Project/galaxy/galaxy-blackhole/blackhole-harness"
Windows (PowerShell): $env:GBH_TAURI_ROOT = "$HOME\Desktop\Project\galaxy\galaxy-blackhole\blackhole-harness"
Windows (cmd): set GBH_TAURI_ROOT=%USERPROFILE%\Desktop\Project\galaxy\galaxy-blackhole\blackhole-harness
In a terminal chat and web ask Cài harness ngay? [Y/n] before anything else; a script (no
TTY) gets the text above and exit code 1, because nothing would answer a prompt there.
Commands
blackhole chat Terminal UI on the Galaxy Blackhole harness (default)
blackhole chat --once "<text>" One harness turn to stdout: prompt, answer, log
blackhole harness install Download the harness into ~/.galaxy/harness (--force, --dir, --from, --no-deps)
blackhole harness status Which harness is used, its source, version, node/bun, dependencies
blackhole harness remove Delete ~/.galaxy/harness (asks first; --yes skips the question)
blackhole migrate [--from <dir>] [--to <dir>] [--dry-run]
Copy sessions/storages/attachments from an older home
blackhole web [options] Open the Galaxy Blackhole web GUI
blackhole doctor Check the API key and the connection
blackhole run "task" [--json] One task; --json writes a single final result
blackhole flow <file.json> Run sequential prompts in one session and log each step
blackhole skills [show <id>] Discover or inspect skills
blackhole skills search <query> Search a configured skill index
blackhole skills install <id> Install a skill (--skill-version <range>)
blackhole skills update Update installed skills
blackhole skills remove <id> Remove an installed skill
blackhole mcp Connect configured servers and list tools
blackhole mcp login <server> Browser login for an HTTP server with auth=oauth
blackhole mcp logout <server> Forget stored OAuth tokens for a server
blackhole memory search <query> Recall confirmed workspace notes
blackhole memory list List notes, including candidates
blackhole memory consolidate Prune superseded revisions and orphan vectors
blackhole memory remember <key> <text> Store a user-confirmed note
blackhole memory confirm <key> Confirm the current candidate
blackhole memory history <key> Show revisions and provenance
blackhole memory forget <key> Delete all revisions of a key
blackhole sessions List conversation sessions
blackhole logs [n] Show the last n runtime log entries (default 20)
blackhole setup Ask for the Galaxy API key (--reset to enter a new one)
blackhole config Check the shared credential document (config doctor)
blackhole config migrate Consolidate provider keys into ~/.galaxy/credentials.yaml
blackhole config doctor Check the shared credential document
blackhole config path Print where credentials and backups live
blackhole update Update blackhole to the newest version on npm
Every command takes --workspace <dir>; chat, run and flow also accept --model,
--base-url, --profile coding|assistant|research, --session <id>, --thinking <level>,
--approval full|balanced, --state-dir <outside-workspace>, --agent-config <json>,
--no-galaxy-mcp, --skills-dir, --skills-index and the embedding flags. blackhole chat --continue resumes the newest session of this workspace.
API key
One key, one place: ~/.galaxy/credentials.yaml. Enter it once with blackhole setup, or in
the web GUI's dialog, or in the VS Code extension — all three read the same document, so a key
entered anywhere works everywhere. Setting GBH_GALAXY_API_KEY in the environment also works
if you would rather not store it on disk.
blackhole doctor prints the key masked, says which source answered, and then checks that the
model actually replies:
| exit | meaning | what to do |
|---|---|---|
| 0 | key, model and network are fine | the failure is elsewhere (for example the web GUI) |
| 1 | no key in any store | run blackhole setup, or enter it in the web dialog |
| 2 | the provider is unreachable | a proxy or firewall is in the way; no key can help |
| 3 | the key was refused (401/403) | enter it again |
| 4 | another answer | the body is printed |
blackhole config path prints exactly which files are in play. There is no second settings
file: ~/.galaxy/config.json was retired, and nothing reads, writes or compares it any more.
A base URL or model id is a per-run flag (--base-url, --model) or a harness setting, never
a stored mirror of the key.
The web GUI
blackhole web boots the Galaxy Blackhole harness (blackhole-harness, the same app the
desktop shell loads) on 127.0.0.1 and opens the authenticated URL it prints. The page is the
GBH interface, in Vietnamese, with the Galaxy Blackhole title in the tab and the sidebar.
The port is fixed at 55906, so the URL is the same on every boot and can be bookmarked:
| you want | how |
|---|---|
| the default | blackhole web → http://127.0.0.1:55906/?token=… |
| another fixed port | --port 56000 (wins over everything) |
| a default for your shell | GBH_WEB_PORT=56000 blackhole web (wins over 55906, loses to --port) |
| a random free port | --port 0 — the OS picks, and the printed URL says which |
A busy 55906 is an error, not a fallback: the launcher prints cổng 55906 đang bận — dùng --port <cổng khác>, hoặc --port 0 để lấy cổng ngẫu nhiên. and stops, because a URL nobody
printed is worse than a clear failure. The desktop app owns 55907 and the shared harness host
keeps 5439, so CLI, app and shared host can run side by side. --dry-run prints the exact
command first.
The GUI starts even before you have a key: the page loads and its dialog asks for one, and a launcher problem is reported as a warning rather than a failure to start.
blackhole doctor checks the model side; blackhole web doctor reports on the harness
launcher (checkout, Node 22.6+, credentials, the apps/host runtime) itself.
Windows
Windows 10/11 (cmd, PowerShell, Git Bash/MINGW64) is supported for the paths this package owns:
harness install|status|remove, chat and web.
What already works, and why:
- No unzip tool needed.
harness installreads the GitHub.zipwith a built-in extractor (central directory + zlib), so a machine without git,unziportarstill installs. - npm is never spawned as a bare name, and never through a shell. Windows npm is a
.cmdshim thatspawnrefuses to run without one, and a node installed by the MSI can leave the shim off PATH.src/npm-cli.tsresolves npm instead, in this order:GBH_NPM(explicit path);npmon PATH;npm.cmdnext tonode.exe;- node running npm's own JS entry —
node <nodeDir>\node_modules\npm\bin\npm-cli.js(Windows installer, nvm-windows) ornode <nodeDir>\..\lib\node_modules\npm\bin\npm-cli.js(nvm on macOS/Linux, Homebrew); /opt/homebrew/bin/npm,/usr/local/bin/npm(macOS/Linux). A.cmdshim is unwrapped to thenpm-cli.jsbehind it and started withshell: false; only a shim whose package is gone is wrapped incmd.exe /d /s /cwith every argument quoted, so a path such asC:\Users\Nguyen Van A\...never depends on how cmd strips quotes. Bun is an.exeand is spawned directly.
- A machine with no npm is told so, in words it can act on. The message lists every path
that was tried and ends with the manual command (
npm i -g @galaxy-stack/blackhole-cli@latest);spawn npm ENOENTis never printed.blackhole updatewarns when that npm is older than 7, the first version with--omit=dev. harness installproves it is installable. After the dependency step it checksapps/host\node_modules\@deepseek-ai\dsh\lib\bin.js; a missing file is a failure (exit 1, with the last 15 lines of npm output andblackhole harness install --force) instead of a "xong" that only fails later, at boot.harness install --no-depsskips the dependency step on purpose and exits 0 with that stated.- Node and npm are one requirement. Install Node 22.6+ from nodejs.org, which includes npm;
without npm
harness installcan download the checkout but cannot make it boot. ~is never literal. Every path isos.homedir()+path.join, so~/.galaxy/harnessbecomesC:\Users\<you>\.galaxy\harnesson Windows.- No shell for the harness.
chat/webspawnnode.exe <checkout>\apps\cli\src\main.ts, and stopping it useskill("SIGTERM")(TerminateProcess on Windows). - No
/bin/shand no:-split PATH. PATH lookups usewhereon Windows; the journey panel runs its commands throughcmd.exe /d /s /cinstead of/bin/sh -c; the browser opener usescmd /c startwith^escaping. - Archive symlinks are recreated when Windows allows it (Developer Mode or an elevated shell)
and skipped with a warning otherwise; the harness repository has one such link under
tools/features, which the launcher does not need to boot. - No POSIX symlink in the boot path. Nothing the CLI creates on the way to
chat/webis a symbolic link.
Known gaps (documented, not fixed here):
- The harness repository's own
bun installruns a postinstall that creates a scope of symlinks (node_modules/@galaxy-blackhole/*). On Windows that needs Developer Mode or an elevated shell; without Bun,harness installonly guaranteesapps/host/node_modules(via npm), which is the host runtime but not the launcher's own workspace tooling. Without npm the install cannot produce that runtime at all, and says so instead of pretending the download was enough. - Building the desktop shell (
apps/desktop+ Tauri) and installing thebhlauncher are outside this package and untested on Windows. - Interactive
chathas only been exercised on macOS/Linux; the TUI itself is Ink, which supports Windows, but its key handling there has not been verified.
Thinking levels
Reasoning effort belongs to the system and the model, not to the CLI: a provider takes the
think field as a boolean or as levels such as low/medium/high/max, and different
systems expose different sets. For run and flow the CLI resolves the list per model and
validates the choice:
Mặc địnhsends nothinkfield at all, so the model keeps its own default.- A probe that reports no thinking hides the control; a model that requires thinking (for
example
kimi-k2.7-code) dropsTắt; a model whose capability is unverified keeps the levels and reports a warning. --thinkingacceptsauto|default|off|on|minimal|low|medium|high|xhigh|max, validated against the resolved list; an unsupported choice fails and names what that model does offer.
The harness owns the model catalogue and the effort list; blackhole chat shows the model facts
the session reports. The retired settings file is not read, so there is nowhere to declare a
per-model list any more — --thinking still validates against the resolved list for
run/flow.
Inside the harness TUI: Ctrl+O toggles tool details and Ctrl+L shows or hides the thinking
transcript. Model and effort pickers are not wired into the harness TUI yet (see the
limitations below).
Terminal UI
The TUI uses the terminal's alternate screen, with a pinned header, a bottom input and a scrollable center. While a run is active, an animated label reports Connecting, Thinking, Responding, Running tools or Validating, next to a live tool line.
| Key | Action |
|---|---|
| Enter | Send the request to the harness session |
| Esc | Cancel the active turn (the harness receives session.cancel) |
| Ctrl+C | Exit and restore the terminal |
| PageUp / PageDown | Scroll the transcript |
| Ctrl+O | Expand/collapse tool details |
| Ctrl+L | Show/hide reasoning text |
| ← / →, Home / End, Ctrl+A / Ctrl+E | Edit the input without moving the transcript |
| Delete / Backspace | Delete the previous grapheme (including Vietnamese accents and emoji) |
| Ctrl+U / Ctrl+W | Delete input before the cursor / previous word |
The terminal shows the harness session, the workspace, the host log path and the model facts
the harness reports. --harness-profile <name> selects a harness profile; the app's default is
blackhole. (--profile coding|assistant|research still selects the legacy agent profile for
run/flow.)
Harness TUI v1 — what is not wired yet
The move to the harness is complete for one real turn (the prompt, the model call, tool rows, the answer and the transcript log), and these are the honest gaps:
- Approvals: the harness session runs with its own approval policy. The TUI does not yet
render or answer
approval/requestframes, so a tool that needs an explicit answer will wait. Read-only questions and the built-in tools run normally. - Model and effort pickers:
/modeland/effortare not offered; the session uses the harness default.--modelis recorded in the transcript and the meta line only. - Session resume:
--continueand--sessionstill belong to the legacy runtime; a harness UI session starts fresh, and every turn is persisted by the harness in~/.galaxy/blackhole. /skills,/compact,/plan,/diffand/updateremain available through the legacyrun/flowpaths, not in the harness TUI.
Providers
The default provider is auto — Galaxy Blackhole resolves it to its own model,
deepseek-v4.1-flash, local or cloud; you never pick a vendor. The harness profile holds the
model route; blackhole chat prints the model the session reports. The key always comes from
~/.galaxy/credentials.yaml or the environment, and run/flow still accept --base-url and
--model per run. The retired settings file is not read or written.
MCP
Explicit host configuration is read from ~/.galaxy/agent.json or --agent-config:
{
"mcpServers": [
{ "name": "project", "transport": "stdio", "command": "my-mcp-server", "args": [], "timeoutMs": 30000 },
{ "name": "remote", "transport": "http", "url": "http://127.0.0.1:8080/mcp" }
]
}
Stdio commands execute only when a configured server is connected, and tool names are
namespaced. Remote HTTP servers can use browser OAuth: set "auth": "oauth" on the connection
and run blackhole mcp login <server>. The CLI also ships the first-party orbit and nebula
servers and uses them by default; skip them with --no-galaxy-mcp.
Skills and memory
Skills are discovered in ~/.agents/skills/<name>/SKILL.md and
<workspace>/.agents/skills/<name>/SKILL.md. Host state defaults to ~/.galaxy/agent/ and must
be outside the selected workspace; override with --state-dir.
Memory uses SQLite WAL + FTS5 with revision checks, provenance and supersession. Explicit CLI
notes are confirmed; model-written notes stay candidates until blackhole memory confirm.
Runtime logs
Every command writes JSON lines to <state-dir>/logs/cli-<date>.ndjson (0600, rotated at 5 MB,
keeping recent segments) and blackhole logs [n] reads them back. Session history resumes with
--session or --continue; simultaneous writers to the same session are rejected.