hubble-cli 4.0.0__tar.gz → 4.1.0__tar.gz
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.
- hubble_cli-4.1.0/PKG-INFO +463 -0
- hubble_cli-4.1.0/README.md +433 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/__init__.py +1 -1
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/agent.py +252 -36
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/banner.py +54 -15
- hubble_cli-4.1.0/hubble/github.py +204 -0
- hubble_cli-4.1.0/hubble/hooks.py +114 -0
- hubble_cli-4.1.0/hubble/images.py +100 -0
- hubble_cli-4.1.0/hubble/keystore.py +82 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/main.py +80 -13
- hubble_cli-4.1.0/hubble/mcp.py +648 -0
- hubble_cli-4.1.0/hubble/mcp_oauth.py +277 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/models.py +26 -3
- hubble_cli-4.1.0/hubble/plugins.py +160 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/provider.py +4 -1
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/providers.py +44 -10
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/repl.py +416 -24
- hubble_cli-4.1.0/hubble/sandbox.py +129 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/scanner.py +94 -7
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/session.py +3 -2
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/settings.py +18 -3
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/skills.py +3 -1
- hubble_cli-4.1.0/hubble/subagents.py +80 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/tools.py +33 -6
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/ui.py +52 -0
- hubble_cli-4.1.0/hubble/update.py +98 -0
- hubble_cli-4.1.0/hubble/worktree.py +145 -0
- hubble_cli-4.1.0/hubble_cli.egg-info/PKG-INFO +463 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble_cli.egg-info/SOURCES.txt +26 -1
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble_cli.egg-info/requires.txt +1 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/pyproject.toml +1 -0
- hubble_cli-4.1.0/tests/test_agents_plugins.py +145 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/tests/test_core.py +10 -0
- hubble_cli-4.1.0/tests/test_github.py +108 -0
- hubble_cli-4.1.0/tests/test_hook_events.py +92 -0
- hubble_cli-4.1.0/tests/test_hooks.py +86 -0
- hubble_cli-4.1.0/tests/test_images.py +79 -0
- hubble_cli-4.1.0/tests/test_keystore.py +70 -0
- hubble_cli-4.1.0/tests/test_mcp.py +128 -0
- hubble_cli-4.1.0/tests/test_mcp_remote.py +137 -0
- hubble_cli-4.1.0/tests/test_sandbox.py +102 -0
- hubble_cli-4.1.0/tests/test_scanner.py +183 -0
- hubble_cli-4.1.0/tests/test_stream_json.py +40 -0
- hubble_cli-4.1.0/tests/test_task_tool.py +240 -0
- hubble_cli-4.1.0/tests/test_update.py +72 -0
- hubble_cli-4.1.0/tests/test_worktree.py +102 -0
- hubble_cli-4.0.0/PKG-INFO +0 -275
- hubble_cli-4.0.0/README.md +0 -246
- hubble_cli-4.0.0/hubble_cli.egg-info/PKG-INFO +0 -275
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/LICENSE +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/__main__.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/board.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/onboarding.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/permissions.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/prompts.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/spinner.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble/web.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble_cli.egg-info/dependency_links.txt +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble_cli.egg-info/entry_points.txt +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/hubble_cli.egg-info/top_level.txt +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/setup.cfg +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/tests/test_skills.py +0 -0
- {hubble_cli-4.0.0 → hubble_cli-4.1.0}/tests/test_tools.py +0 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: hubble-cli
|
|
3
|
+
Version: 4.1.0
|
|
4
|
+
Summary: Hubble: agentic coding CLI for AIHub and other OpenAI-compatible APIs
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/Hamdayrabby/hubble-cli
|
|
7
|
+
Project-URL: Repository, https://github.com/Hamdayrabby/hubble-cli
|
|
8
|
+
Project-URL: Changelog, https://github.com/Hamdayrabby/hubble-cli/blob/main/CHANGELOG.md
|
|
9
|
+
Keywords: cli,agent,coding-assistant,llm,ai
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Environment :: Console
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Operating System :: OS Independent
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Software Development
|
|
19
|
+
Classifier: Topic :: Utilities
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: httpx>=0.25.0
|
|
24
|
+
Requires-Dist: rich>=13.0
|
|
25
|
+
Requires-Dist: prompt_toolkit>=3.0.40
|
|
26
|
+
Requires-Dist: keyring>=24
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
# Hubble
|
|
32
|
+
|
|
33
|
+
An agentic coding CLI in the style of Claude Code, Antigravity/Gemini CLI and Codex CLI. It works directly
|
|
34
|
+
in your repository: it searches, reads and edits files and runs commands through native function calling,
|
|
35
|
+
and you approve each action. It talks to any OpenAI-compatible API — the AIHub gateway by default, or
|
|
36
|
+
OpenAI, Groq, OpenRouter, Mistral, a local Ollama server, or others via `/provider add`.
|
|
37
|
+
|
|
38
|
+
## Install
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pipx install hubble-cli
|
|
42
|
+
```
|
|
43
|
+
(or `pip install --user hubble-cli`; the latest unreleased code: `pipx install git+https://github.com/Hamdayrabby/hubble-cli.git`)
|
|
44
|
+
|
|
45
|
+
Upgrade with `pipx upgrade hubble-cli` (or `pip install -U hubble-cli`). Hubble tells you when a new
|
|
46
|
+
version is out — it checks PyPI at most once a day; set `"update_check": false` to turn that off.
|
|
47
|
+
|
|
48
|
+
Then just run it from any project:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
cd /path/to/your/project
|
|
52
|
+
hubble
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
With no API key configured anywhere, the first run walks you through adding one — no manual `.env` editing
|
|
56
|
+
required. To set one up yourself instead, put it in `.env` in your project or in `~/.hubble/.env`:
|
|
57
|
+
```
|
|
58
|
+
HUBBLE_API_KEY=your_key_here
|
|
59
|
+
HUBBLE_BASE_URL=https://aihub.071129.xyz/v1 # or any other OpenAI-compatible base URL
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
**From a local clone**, for development: `pip install -e .` from the repo root installs the `hubble`
|
|
63
|
+
command (`aihub` also works, kept as an alias) against your working copy — edits take effect immediately.
|
|
64
|
+
Without installing at all: `python code_cli.py [args]` from the repo root (add `--cwd <project>` to work
|
|
65
|
+
elsewhere). The original single-file prototype is still there as `python chat_cli.py`.
|
|
66
|
+
|
|
67
|
+
## Usage
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
hubble # interactive REPL
|
|
71
|
+
hubble "explain the architecture" # REPL with a first prompt
|
|
72
|
+
hubble -c # continue the latest session in this folder
|
|
73
|
+
hubble -r # choose a session to resume
|
|
74
|
+
hubble -p "fix the failing test" --permission-mode accept-edits --allow "shell(pytest*)"
|
|
75
|
+
git diff | hubble -p "review this diff" --output-format json
|
|
76
|
+
hubble -p "run the tests" --output-format stream-json # one JSON event per line, as it happens
|
|
77
|
+
hubble -p "what is wrong in this UI?" --image screenshot.png
|
|
78
|
+
hubble -w refactor-auth # work in an isolated git worktree on branch hubble/refactor-auth
|
|
79
|
+
hubble -m nvidia/nemotron-3-super-120b-a12b --persona architect
|
|
80
|
+
hubble --test codestral-latest # check that a model responds
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
`stream-json` lines have a `type`: `init`, `text` (streamed delta), `assistant` (one model call, with its
|
|
84
|
+
tool calls and usage), `tool_use`, `tool_result`, `todos`, `notice`, `subagent_start`, `subagent_end`, and
|
|
85
|
+
always last `result` (the same fields as `--output-format json`).
|
|
86
|
+
|
|
87
|
+
## Tools the model can use
|
|
88
|
+
|
|
89
|
+
| Tool | What it does |
|
|
90
|
+
|---|---|
|
|
91
|
+
| `read_file` | Read with line numbers, `offset`/`limit` for large files |
|
|
92
|
+
| `edit_file` | Exact string replace; must be unique unless `replace_all`; keeps CRLF |
|
|
93
|
+
| `write_file` | Create or overwrite a file (existing files must be read first) |
|
|
94
|
+
| `shell` | Run a command in the workspace root (PowerShell on Windows), with timeout and closed stdin |
|
|
95
|
+
| `grep` | Regex search (ripgrep if installed, otherwise Python) |
|
|
96
|
+
| `glob` | Find files by pattern, newest first |
|
|
97
|
+
| `list_dir` | List a directory |
|
|
98
|
+
| `todo_write` | Task list for multi-step work, shown in the terminal |
|
|
99
|
+
| `task` | Sub-agent for one self-contained piece of work: `read_only` (default) for research, returns a report; `edit` for a delegated implementation task, with its own file/shell tools — its edits and commands still ask for approval the same way yours would. Several `read_only` tasks in one turn run in parallel; an `edit` task always runs on its own. Can target a different model per task, a [custom agent](#custom-agents) (`agent`), and `isolation: "worktree"` to do its edits in a fresh git worktree on its own branch. |
|
|
100
|
+
| `web_search` | Web search. DuckDuckGo by default (no key); set `BRAVE_API_KEY` or `TAVILY_API_KEY` to use those instead |
|
|
101
|
+
| `web_fetch` | Fetch a URL as readable text, page by page (`offset`). Asks once per domain; refuses local and private addresses |
|
|
102
|
+
| `mcp__<server>__<tool>` | Tools from any MCP server you've configured, plus `list_resources`/`read_resource` for servers that offer resources (see [MCP servers](#mcp-servers) below) |
|
|
103
|
+
|
|
104
|
+
Safety:
|
|
105
|
+
- **Workspace confinement:** paths outside the workspace are refused. Add others with `additional_dirs`.
|
|
106
|
+
- **Secret files are blocked:** `.env`, `*.pem`, `id_rsa` and similar are never read, searched or attached (`allow_secret_files` turns this off).
|
|
107
|
+
- **Edits need a fresh read:** a file must be read before it is edited or overwritten, and read again if it changed on disk since.
|
|
108
|
+
- **Undo:** every change is snapshotted, so `/undo` can revert it.
|
|
109
|
+
|
|
110
|
+
### Sandboxed shell execution
|
|
111
|
+
|
|
112
|
+
On **macOS and Linux, shell commands run in an OS sandbox by default** (`"shell_sandbox": "auto"`), like
|
|
113
|
+
Codex: a command can read anything but can only write inside the workspace and temp directories (and any
|
|
114
|
+
`sandbox_writable` paths you add). macOS uses the built-in `sandbox-exec` (Seatbelt); Linux uses
|
|
115
|
+
[bubblewrap](https://github.com/containers/bubblewrap) (`sudo apt install bubblewrap`). If a command
|
|
116
|
+
genuinely needs to write elsewhere — a global install, a config file in your home folder — the model
|
|
117
|
+
retries it with `unsandboxed: true`, and **you are always asked first**, even in accept-edits mode or with a
|
|
118
|
+
matching allow rule (only `yolo` skips that).
|
|
119
|
+
|
|
120
|
+
**On Windows there is no built-in equivalent**, so commands run directly on your machine with your own
|
|
121
|
+
permissions, and approval prompts are the protection — unless you use the Docker sandbox below.
|
|
122
|
+
|
|
123
|
+
`/sandbox` shows the current state; `/sandbox auto|native|docker|off` changes it.
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"shell_sandbox": "auto",
|
|
128
|
+
"sandbox_network": true,
|
|
129
|
+
"sandbox_writable": ["~/.cache/pip", "~/.npm"]
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Docker sandbox** (any OS, needs [Docker Desktop](https://www.docker.com/products/docker-desktop/)): shell
|
|
134
|
+
commands run inside an isolated, disposable container instead of on your machine:
|
|
135
|
+
|
|
136
|
+
```
|
|
137
|
+
/sandbox docker
|
|
138
|
+
```
|
|
139
|
+
or in `~/.hubble/settings.json` / `.hubble/settings.json`:
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"shell_sandbox": "docker",
|
|
143
|
+
"sandbox_image": "python:3.12-slim",
|
|
144
|
+
"sandbox_memory": "1g",
|
|
145
|
+
"sandbox_cpus": "2",
|
|
146
|
+
"sandbox_network": true
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
Only the project folder is mounted in (as `/workspace`); nothing else on your machine is reachable from
|
|
150
|
+
inside it. Memory and CPU are capped, and the container is removed after every command. Set
|
|
151
|
+
`sandbox_image` to whatever your project needs (e.g. `node:20` for a JS project); set `sandbox_network` to
|
|
152
|
+
`false` to also block network access from inside the sandbox, if your workflow doesn't need `pip`/`npm`
|
|
153
|
+
install-style commands. Like `permission_mode`, `shell_sandbox`, `sandbox_network` and `sandbox_writable` only take effect from
|
|
154
|
+
a project's own `.hubble/settings.json` once you've trusted that folder — an untrusted, freshly cloned
|
|
155
|
+
project can't quietly turn sandboxing off or re-enable network access on your behalf.
|
|
156
|
+
|
|
157
|
+
### MCP servers
|
|
158
|
+
|
|
159
|
+
Connect any [MCP](https://modelcontextprotocol.io/) server — local (stdio) or remote (Streamable HTTP, or
|
|
160
|
+
the older HTTP+SSE transport) — and its tools become available to the model, namespaced as
|
|
161
|
+
`mcp__<server>__<tool>`:
|
|
162
|
+
|
|
163
|
+
```json
|
|
164
|
+
{
|
|
165
|
+
"mcp_servers": {
|
|
166
|
+
"filesystem": {"command": ["npx", "-y", "@modelcontextprotocol/server-filesystem", "/path/to/share"]},
|
|
167
|
+
"git": {"command": ["uvx", "mcp-server-git"]},
|
|
168
|
+
"linear": {"url": "https://mcp.linear.app/mcp"},
|
|
169
|
+
"internal": {"url": "https://mcp.example.com/sse", "transport": "sse",
|
|
170
|
+
"headers": {"Authorization": "Bearer ${INTERNAL_MCP_TOKEN}"}}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
```
|
|
174
|
+
- `${VAR}` in `url`, `headers` and `env` is read from the environment, so tokens stay out of the file.
|
|
175
|
+
- `transport` is `auto` by default: Streamable HTTP, falling back to HTTP+SSE for older servers.
|
|
176
|
+
- **OAuth:** a remote server that answers 401 shows as "needs login". Run `/mcp login <server>`: Hubble
|
|
177
|
+
discovers the authorization server, registers itself (or uses `"oauth": {"client_id": "..."}`), opens
|
|
178
|
+
your browser, and keeps the token in your OS credential store, refreshing it automatically.
|
|
179
|
+
`/mcp logout <server>` forgets it.
|
|
180
|
+
- **Resources** a server offers are readable by the model through `list_resources`/`read_resource`.
|
|
181
|
+
- **Prompts** a server offers become slash commands: `/mcp__<server>__<prompt> key=value ...`.
|
|
182
|
+
|
|
183
|
+
`/mcp` lists servers, their tools and prompts, and which need a login. A server that fails to start or
|
|
184
|
+
handshake is skipped with a warning, not a crash. Like the sandbox settings, `mcp_servers` only takes effect
|
|
185
|
+
from a project's own `.hubble/settings.json` once you've trusted that folder — a cloned repo can't have you
|
|
186
|
+
unknowingly launch arbitrary processes.
|
|
187
|
+
|
|
188
|
+
### Hooks
|
|
189
|
+
|
|
190
|
+
Hooks are shell commands that run at fixed points in the agent loop, independent of the model's own
|
|
191
|
+
choices — for enforcing project policy, logging, or linting. Configure them in `settings.json`:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"hooks": {
|
|
196
|
+
"PreToolUse": [{"matcher": "shell", "command": "python .hubble/check_command.py"}],
|
|
197
|
+
"PostToolUse": [{"matcher": "edit_file", "command": "python .hubble/lint_changed_file.py"}],
|
|
198
|
+
"UserPromptSubmit": [{"command": "python .hubble/inject_context.py"}],
|
|
199
|
+
"Stop": [{"command": "python .hubble/require_tests_ran.py"}]
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
Each hook receives a JSON payload on stdin — `{"event": "PreToolUse", "tool": "edit_file", "args": {...}}`
|
|
204
|
+
for tool events, `{"event": "UserPromptSubmit", "prompt": "..."}`, or `{"event": "Stop", "final_text": "..."}`
|
|
205
|
+
— and reads whatever it needs (e.g. `args["path"]`) from there, not from shell variables. It controls what
|
|
206
|
+
happens next through its exit code and stdout:
|
|
207
|
+
- **Exit non-zero** → blocks the action; stderr becomes the reason shown to the model.
|
|
208
|
+
- **Print `{"decision": "block", "reason": "..."}`** → same, from an exit-0 process.
|
|
209
|
+
- **Print `{"additionalContext": "..."}`** → the action proceeds, and the text is appended (to the prompt
|
|
210
|
+
for `UserPromptSubmit`, to the tool's result for `PostToolUse`).
|
|
211
|
+
- **`Stop` hooks** can refuse to let the turn end (`"decision": "block"`) — the reason is fed back as if it
|
|
212
|
+
were a new instruction, so the model keeps going (e.g. "you haven't run the tests yet").
|
|
213
|
+
- Anything else on stdout is just logged, not acted on.
|
|
214
|
+
|
|
215
|
+
`matcher` filters by tool name (glob, e.g. `"shell"` or `"mcp__*"`); omit it to run on every event of that
|
|
216
|
+
type. Hooks run on the host, not inside the sandbox, and — like `mcp_servers` — only take effect once
|
|
217
|
+
you've trusted the project.
|
|
218
|
+
|
|
219
|
+
All events:
|
|
220
|
+
|
|
221
|
+
| Event | When | Can |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| `SessionStart` | Hubble starts or resumes (`source`: startup / resume) | `additionalContext` is added to the system prompt for the session |
|
|
224
|
+
| `UserPromptSubmit` | You send a message | block it, or add context |
|
|
225
|
+
| `PreToolUse` | Before a tool runs | block it |
|
|
226
|
+
| `PostToolUse` | After a tool runs | add context to its result |
|
|
227
|
+
| `Notification` | Hubble is waiting for your approval | observe (e.g. desktop notification) |
|
|
228
|
+
| `PreCompact` | Before history is compacted (`trigger`: auto / manual) | block compaction |
|
|
229
|
+
| `SubagentStop` | A `task` sub-agent finished | add context to its report |
|
|
230
|
+
| `Stop` | The turn is about to end | block = keep going with `reason` as the next instruction |
|
|
231
|
+
| `SessionEnd` | Hubble exits | observe |
|
|
232
|
+
|
|
233
|
+
## Custom agents
|
|
234
|
+
|
|
235
|
+
Named specialists the model can delegate to with the `task` tool. One Markdown file each, in
|
|
236
|
+
`.hubble/agents/` (project) or `~/.hubble/agents/` (user):
|
|
237
|
+
|
|
238
|
+
```markdown
|
|
239
|
+
---
|
|
240
|
+
name: test-writer
|
|
241
|
+
description: Writes focused pytest tests for a module. Use after adding or changing a feature.
|
|
242
|
+
tools: read_file, grep, glob, write_file, edit_file, shell # optional: narrows its toolset
|
|
243
|
+
model: codestral-latest # optional: its own model
|
|
244
|
+
capability: edit # read_only (default) or edit
|
|
245
|
+
---
|
|
246
|
+
You write small, fast pytest tests. Cover the edge cases first. Run the tests before reporting.
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
Only the name and description are shown to the main model, so many agents cost little. A `read_only`
|
|
250
|
+
agent never gets edit tools, whatever its `tools` line says. `/agents` lists them; `/agents new <name>`
|
|
251
|
+
creates a template.
|
|
252
|
+
|
|
253
|
+
## Plugins
|
|
254
|
+
|
|
255
|
+
A plugin is one folder that bundles commands, skills, agents, hooks and MCP servers, so a team can share a
|
|
256
|
+
whole setup:
|
|
257
|
+
|
|
258
|
+
```
|
|
259
|
+
my-plugin/
|
|
260
|
+
plugin.json {"name": "my-plugin", "version": "1.0.0", "description": "...",
|
|
261
|
+
"hooks": {...}, "mcp_servers": {...}} # same shape as in settings.json
|
|
262
|
+
commands/*.md agents/*.md skills/<name>/SKILL.md
|
|
263
|
+
```
|
|
264
|
+
`${PLUGIN_DIR}` in a hook or MCP command points at the plugin's folder, so it can ship its own scripts.
|
|
265
|
+
|
|
266
|
+
`/plugin install <folder or git URL> [--project]`, `/plugin list`, `/plugin disable|enable <name>`,
|
|
267
|
+
`/plugin remove <name>`. Hooks and MCP servers from a *project* plugin only load once you trust the folder.
|
|
268
|
+
|
|
269
|
+
## Images
|
|
270
|
+
|
|
271
|
+
Attach screenshots or diagrams for a vision-capable model: mention `@shot.png` in a message, press
|
|
272
|
+
**Alt+V** to paste an image from the clipboard (shows as `[Image #1]`), or pass `--image file.png` with
|
|
273
|
+
`-p`. PNG, JPEG, GIF, WebP and BMP up to 8 MB. Models without vision support usually answer with an error.
|
|
274
|
+
|
|
275
|
+
## Git worktrees
|
|
276
|
+
|
|
277
|
+
Work on something risky, or several things at once, without touching your main checkout:
|
|
278
|
+
|
|
279
|
+
- `hubble -w <name>` or `/worktree new <name>` creates `.hubble/worktrees/<name>` on branch `hubble/<name>`
|
|
280
|
+
and moves every tool there. `/worktree exit` goes back; `/worktree switch <name>`, `/worktree list`,
|
|
281
|
+
`/worktree remove <name> [--force] [--delete-branch]`.
|
|
282
|
+
- The folder is added to `.git/info/exclude`, so it never shows up in `git status`.
|
|
283
|
+
- A `task` with `isolation: "worktree"` makes an edit sub-agent work in a fresh worktree. Its file edits
|
|
284
|
+
there need no approval (they can't touch your checkout); its shell commands still ask. When it finishes,
|
|
285
|
+
its changes are committed on their own branch and the main model is told how to review and
|
|
286
|
+
cherry-pick them.
|
|
287
|
+
|
|
288
|
+
## GitHub integration
|
|
289
|
+
|
|
290
|
+
`/install-github` writes `.github/workflows/hubble.yml`. After you add a `HUBBLE_API_KEY` repository
|
|
291
|
+
secret (and optionally `HUBBLE_MODEL` / `HUBBLE_BASE_URL` variables):
|
|
292
|
+
- every pull request from the same repository gets a review comment;
|
|
293
|
+
- `@hubble <request>` in an issue, or a PR comment, gets an answer. On a pull request Hubble can make the
|
|
294
|
+
requested change, and the workflow pushes it to the PR branch.
|
|
295
|
+
|
|
296
|
+
Only the repository's owners, members and collaborators can trigger it, since a mention runs an agent with
|
|
297
|
+
your API key and push access; comment text is never pasted into a shell script. Under the hood this is
|
|
298
|
+
`hubble -p --github`, which reads the Actions event and posts the reply with `GITHUB_TOKEN`.
|
|
299
|
+
|
|
300
|
+
## Permission modes
|
|
301
|
+
|
|
302
|
+
Cycle with **Shift+Tab** or set with `/mode` or `--permission-mode`:
|
|
303
|
+
|
|
304
|
+
| Mode | Behaviour |
|
|
305
|
+
|---|---|
|
|
306
|
+
| `default` | Ask before every edit and shell command. The prompt shows a diff or the command. |
|
|
307
|
+
| `accept-edits` | Edits are auto-approved. Shell commands still ask. |
|
|
308
|
+
| `plan` | Read-only. The model explores and proposes a plan. |
|
|
309
|
+
| `yolo` | Everything is auto-approved except deny rules. |
|
|
310
|
+
|
|
311
|
+
At an approval prompt:
|
|
312
|
+
- `y` allows the action once.
|
|
313
|
+
- `a` allows it for the rest of the session: all edits, or commands with the same prefix, e.g. `pytest*`.
|
|
314
|
+
- `n` denies it. You can add feedback, which is sent to the model.
|
|
315
|
+
- Ctrl+C stops the turn.
|
|
316
|
+
|
|
317
|
+
Allow rules never apply to chained commands (`&&`, `;`, `|`, redirects), so `shell(git status*)` does not approve `git status && rm -rf x`.
|
|
318
|
+
|
|
319
|
+
## In-session commands
|
|
320
|
+
|
|
321
|
+
| Input | Action |
|
|
322
|
+
|---|---|
|
|
323
|
+
| `/help` | All commands and shortcuts |
|
|
324
|
+
| `/model [name\|#]`, `/models [filter]` | Switch or list models (latencies from `python test_models.py`) |
|
|
325
|
+
| `/mode [mode]` | Permission mode |
|
|
326
|
+
| `/persona [code\|debug\|review\|architect\|chat]` | System persona |
|
|
327
|
+
| `/clear` | New conversation |
|
|
328
|
+
| `/compact [focus]` | Summarize history to free context (automatic at 80%) |
|
|
329
|
+
| `/resume [#\|id]` | Resume a saved session |
|
|
330
|
+
| `/undo` | Revert files changed in the last turn that edited files |
|
|
331
|
+
| `/diff` | Show `git diff` |
|
|
332
|
+
| `/init` | Generate `HUBBLE.md` project instructions |
|
|
333
|
+
| `/memory` | Show loaded memory files |
|
|
334
|
+
| `/add <file>`, `/drop <file>`, `/files` | Pin files into the system prompt |
|
|
335
|
+
| `/todos`, `/cost`, `/context`, `/config`, `/test [model]`, `/temp [t]`, `/export [file]` | Info and utilities |
|
|
336
|
+
| `/sandbox`, `/mcp`, `/hooks` | Sandbox mode, MCP servers (`login`/`logout`), configured hooks |
|
|
337
|
+
| `/agents`, `/plugin`, `/skills` | Custom agents, plugins, skills |
|
|
338
|
+
| `/worktree` | Git worktrees: `new`, `switch`, `exit`, `remove`, `list` |
|
|
339
|
+
| `/install-github` | Set up the GitHub Action |
|
|
340
|
+
| `@path` | Attach a file, directory listing or image to your message; Tab completes paths |
|
|
341
|
+
| Alt+V | Paste an image from the clipboard |
|
|
342
|
+
| `!cmd` | Run a shell command yourself |
|
|
343
|
+
| `#note` | Append a note to `./HUBBLE.md` |
|
|
344
|
+
| Esc+Enter / Ctrl+J | New line |
|
|
345
|
+
| Ctrl+C / Ctrl+D | Cancel turn / quit |
|
|
346
|
+
|
|
347
|
+
**Custom commands:** `.hubble/commands/<name>.md` (project) or `~/.hubble/commands/<name>.md` (user) becomes
|
|
348
|
+
`/<name>`. `$ARGUMENTS` is replaced by the text after the command.
|
|
349
|
+
|
|
350
|
+
## Providers (extra base URLs and API keys)
|
|
351
|
+
|
|
352
|
+
Any OpenAI-compatible API can be added next to the built-in AIHub gateway: OpenRouter, Groq, OpenAI, Mistral,
|
|
353
|
+
Gemini's OpenAI endpoint, a local Ollama server, and others.
|
|
354
|
+
|
|
355
|
+
1. Type `/provider add`, or open `/provider` and choose **+ Add a provider**.
|
|
356
|
+
2. Pick a known provider or **Custom URL...**, then paste the API key. The key is hidden as you type.
|
|
357
|
+
3. The CLI checks the URL and key by listing the provider's models. If the check fails, it tells you why: wrong key, wrong URL, or can't connect.
|
|
358
|
+
4. Optionally, the CLI checks which models actually respond, in the background. Progress shows in the bottom bar.
|
|
359
|
+
5. Choose whether to switch to the new provider now. If you do, a model picker for that provider opens.
|
|
360
|
+
|
|
361
|
+
After that:
|
|
362
|
+
- `/model` lists models from every provider and switches provider automatically. The choice is saved as your default.
|
|
363
|
+
- `/provider` switches provider, `/provider list` shows all of them, and `/provider remove <name>` deletes one.
|
|
364
|
+
- `hubble --provider <name>` picks a provider for one run.
|
|
365
|
+
- On first start with no API key at all, the same setup runs instead of an error.
|
|
366
|
+
|
|
367
|
+
Extra providers are stored in `~/.hubble/providers.json`. Their API keys go to your OS credential store
|
|
368
|
+
(Windows Credential Manager, macOS Keychain, or Secret Service on Linux); only where no credential store
|
|
369
|
+
exists (headless Linux, containers) are they written into that file in plain text. `/provider secure` moves
|
|
370
|
+
keys saved by older versions into the credential store.
|
|
371
|
+
Their model lists are stored in `~/.hubble/models/<name>.json`. When a model is rate limited or down, Hubble retries that request with a fallback that the current provider actually has, in this order: the fallback you picked with `/fallback`, then `fallback_model` if the provider has it, then the provider's fastest verified model, then `fallback_model` on the built-in hubble provider. Sub-agents use the same route. `/fallback off` disables it for a provider.
|
|
372
|
+
|
|
373
|
+
## Project memory
|
|
374
|
+
|
|
375
|
+
On startup the CLI loads `~/.hubble/HUBBLE.md`, then one of `HUBBLE.md`, `AGENTS.md`, `CLAUDE.md` or `GEMINI.md`
|
|
376
|
+
from each directory between the git root and the workspace, into the system prompt.
|
|
377
|
+
|
|
378
|
+
## Configuration
|
|
379
|
+
|
|
380
|
+
Settings merge in order (later wins):
|
|
381
|
+
1. Built-in defaults
|
|
382
|
+
2. `~/.hubble/settings.json`
|
|
383
|
+
3. `.hubble/settings.json`
|
|
384
|
+
4. `.hubble/settings.local.json`
|
|
385
|
+
5. CLI flags
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"model": "codestral-latest",
|
|
390
|
+
"max_tokens": 8192,
|
|
391
|
+
"context_window": 128000,
|
|
392
|
+
"permission_mode": "default",
|
|
393
|
+
"shell": "auto",
|
|
394
|
+
"additional_dirs": [],
|
|
395
|
+
"permissions": {
|
|
396
|
+
"allow": ["shell(pytest*)", "shell(git status*)", "shell(git diff*)"],
|
|
397
|
+
"deny": ["shell(git push*)", "edit_file(migrations/*)"]
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
- Rule syntax is `tool` or `tool(glob)`. The glob is matched against the path or the command.
|
|
403
|
+
- `bash`, `edit`, `write` and `read` work as aliases for the tool names.
|
|
404
|
+
- Deny rules win over allow rules.
|
|
405
|
+
- `shell` can be `auto` (pwsh, then Windows PowerShell), `cmd` or `bash`.
|
|
406
|
+
|
|
407
|
+
Project settings files (`.hubble/*.json`) come from the repository, so they are treated as untrusted:
|
|
408
|
+
- They can never set `base_url` or `api_key`.
|
|
409
|
+
- `permission_mode`, `allow_secret_files`, `additional_dirs`, `shell` and allow rules apply only after you trust the folder. The CLI asks once and remembers the answer in `~/.hubble/trusted_folders.json`.
|
|
410
|
+
- Deny rules always apply.
|
|
411
|
+
|
|
412
|
+
Credentials come from `HUBBLE_API_KEY` / `HUBBLE_BASE_URL` (and optionally `HUBBLE_MODEL`) in the
|
|
413
|
+
environment, `.env` in your project, or `~/.hubble/.env`. Only `HUBBLE_*` keys (and the older `AIHUB_*`
|
|
414
|
+
names) are read from those files; nothing else in a `.env` is touched.
|
|
415
|
+
|
|
416
|
+
Sessions are saved as JSONL in `~/.hubble/projects/<project>/`. Input history is in `~/.hubble/history`.
|
|
417
|
+
|
|
418
|
+
## Layout
|
|
419
|
+
|
|
420
|
+
```
|
|
421
|
+
hubble/
|
|
422
|
+
main.py CLI flags, headless -p mode, resume
|
|
423
|
+
repl.py prompt_toolkit REPL, slash commands, @mentions
|
|
424
|
+
ui.py rich rendering: streamed markdown, diffs, approval prompts
|
|
425
|
+
agent.py agent loop, compaction, task sub-agent
|
|
426
|
+
provider.py OpenAI-compatible SSE client, retries, tool-call assembly
|
|
427
|
+
tools.py workspace tools, checkpoints
|
|
428
|
+
sandbox.py OS-native command sandbox (Seatbelt / bubblewrap)
|
|
429
|
+
mcp.py MCP client: stdio, Streamable HTTP, SSE; mcp_oauth.py: OAuth login
|
|
430
|
+
hooks.py hook runner; subagents.py: custom agents; plugins.py: plugins
|
|
431
|
+
worktree.py git worktrees; github.py: GitHub Actions integration
|
|
432
|
+
images.py image attachments and clipboard paste; keystore.py: OS credential store
|
|
433
|
+
scanner.py background model availability checks
|
|
434
|
+
permissions.py modes and allow/deny rules
|
|
435
|
+
session.py JSONL transcripts
|
|
436
|
+
prompts.py system prompt, personas, memory files
|
|
437
|
+
settings.py layered settings and .env loading
|
|
438
|
+
models.py model registry (shared with config.py)
|
|
439
|
+
tests/ pytest suite (python -m pytest -q)
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
## Models on AIHub
|
|
443
|
+
|
|
444
|
+
Hubble checks which models actually respond in the background: automatically every time it starts (for
|
|
445
|
+
every provider you have configured), and on demand with `/models refresh`. `/models` shows the results and
|
|
446
|
+
how long ago they were checked. Set `"model_refresh_hours"` in `~/.hubble/settings.json` to a positive number
|
|
447
|
+
to only recheck once the list is that many hours old instead of on every start, or `null` to turn the
|
|
448
|
+
automatic check off entirely (`/models refresh` still works). The check is one tiny request per model, so it
|
|
449
|
+
costs a little on paid APIs.
|
|
450
|
+
|
|
451
|
+
Running `python test_models.py` does the same scan for the built-in provider from the command line.
|
|
452
|
+
The last scan found 50 working models out of 295. Models that were only rate limited or timed out during a
|
|
453
|
+
check show as "unknown", not "unavailable", and one that worked last time stays listed.
|
|
454
|
+
|
|
455
|
+
| Category | Models |
|
|
456
|
+
|---|---|
|
|
457
|
+
| Coding (default) | `codestral-latest`, `codestral-2508`, `mistral-code-latest`, `mistral-code-fim-latest` |
|
|
458
|
+
| Reasoning | `nvidia/nemotron-3-super-120b-a12b`, `intern-s2-preview`, `intern-s1-mini`, `intern-s1` |
|
|
459
|
+
| Fast chat | `ministral-14b-latest`, `open-mistral-nemo`, `ministral-8b-latest`, `ministral-3b-latest` |
|
|
460
|
+
| Vision | `meta/llama-3.2-11b-vision-instruct`, `internvl3.5-latest`, `internvl-latest` |
|
|
461
|
+
|
|
462
|
+
Native tool calling was verified on `codestral-latest`, `mistral-code-latest`, `ministral-14b-latest` and
|
|
463
|
+
`nvidia/nemotron-3-super-120b-a12b`.
|