mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
* feat(web): feature-flagged rich composer for inline session @ mentions Custom segmented contenteditable (not TipTap) inserts caret-local session atoms from the existing @ picker and serializes to markdown links on send. Textarea path remains default until flag parity dogfood. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): rich composer mention boundary + #1215 refs Treat U+FFFC mirror atoms as word boundaries so @ after a session token still opens autocomplete. Point comments at Fixes #1215. Co-authored-by: Cursor <cursoragent@cursor.com> * test(web): peer-stack e2e for rich composer session @ mentions (#1215) Smoke: flag on, @ picker inserts inline session atom chip (not prose dump). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): preserve newlines in rich composer Enter-newline mode Chromium splits contenteditable on Enter into block divs; serialize those as \\n and insert <br> when parent leaves Enter unhandled (Shift+Enter / enter-inserts-newline). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): show @ badge when rich composer mentions flag is on Dogfood was invisible: flag-off looks like a normal textarea, and flag-on had no chrome. Surface a small @ badge when enabled. * fix(web): rich session composer on by default (not a user setting) The plan dual-path was an engineering kill-switch, not an opt-in. Default to the segmented composer; only richMentions=0 disables. Drop the flag badge and record a peer-stack motion proof covering chips + baseline UX. * fix(web): make rich composer Shift+Enter create a visible newline Trailing <br>+empty text node was a silent no-op at EOL. Use insertLineBreak (ZWSP pad fallback), assert real \\n in peer e2e. * feat(web): hover tooltips on rich composer session chips Show full title, status, short id, and path on chip hover via a portal bubble fed by live useSessions lookup (drafts fall back to title + id). * fix(web): dismiss rich composer chip tooltips on mouse leave contenteditable pointerout/relatedTarget was flaky so tips stuck after leaving the chip. Hit-test on pointermove, clear on prose/input/leave. * fix(web): address cold-review Blocker/Majors on rich composer Exclude peer e2e from default Playwright; force plain-text paste; restore newline hard-stop in findActiveWord; fix root-anchored selection mapping and nested-block serialize; cover with unit tests. * chore: drop accidental .cursor files from rich-composer tip * fix(web): close remaining cold-review gaps on rich composer Drop absolute peer e2e tooling imports, prove chip→markdown send, and harden paste/EOL/focus/tooltip/Enter edges before Meta rematerialize. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: absorb soup playwright.config union for clean remat Keep fork peer-stack timeouts/annotated-video wiring and add testIgnore for e2e/peer so the next driver rematerialize does not conflict. Co-authored-by: Cursor <cursoragent@cursor.com> * revert: drop fork playwright tooling from upstreamable tip Peer-stack annotated-video + HAPI_PEER wiring stay on fork main / soup. Product tip only needs testIgnore for e2e/peer (see docs/tooling/peer-stack.md). Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): fix rich composer Shift+Enter double newline and paste space Prefer manual newline+pad over execCommand insertLineBreak, and stop applying autocomplete trailing-space on paste/drop paths. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): pad EOL Shift+Enter after Range.insertNode split insertNode always leaves an empty text sibling, so !nextSibling never saw EOL; detect meaningful trailing content and cover with jsdom tests. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): drop custom onDrop from rich composer Intercepting drop without caretRangeFromPoint landed text at EOF or no-oped in-editor moves. Native CE drop is enough for #1215; paste still forces plain text. Co-authored-by: Cursor <cursoragent@cursor.com> * feat(web): sidebar-parity tooltips on rich composer session chips Reuse SessionRowSummary (flavor, thinking/attention, schedule, todos, relative ago, path) for chip hover so the tip matches the session list. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: keep peer-stack e2e off the upstreamable tip Peer specs and playwright.peer.config stay on fork main per docs/tooling/peer-stack.md; default config still testIgnore's e2e/peer. Co-authored-by: Cursor <cursoragent@cursor.com> * feat: cite sessions with UUID wire + inspect_peer for agent/overseer Rich composer chips already serialize to [title](/sessions/<id>); flush before send so the agent prompt never gets title-only chip text. Add inspect_peer (MCP + hapi inspect-peer) as the read twin of ping_peer so that same id is immediately usable for overseer/agent peer lookup, with system-prompt glue from citations to inspect/ping. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli): gate inspect_peer behind permission approval Cross-session history reads need the same prompt path as ping_peer: keep inspect_peer off Claude --allowedTools and treat it as sensitive in ACP/OpenCode read-only mode so prompt injection cannot silently enumerate peer transcripts. Co-authored-by: Cursor <cursoragent@cursor.com> * chore: clarify playwright peer testIgnore is upstream-safe Co-authored-by: Cursor <cursoragent@cursor.com> * fix(web): keep session UUIDs on rich composer copy/cut/paste Copy/cut write wire markdown so chips do not collapse to @title-only clipboard text; paste reparses session links back into atoms. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com>
183 lines
7.8 KiB
Markdown
183 lines
7.8 KiB
Markdown
# hapi CLI
|
|
|
|
Run Claude Code, Codex, Cursor Agent, Grok Build, or OpenCode sessions from your terminal and control them remotely through the hapi hub.
|
|
|
|
## What it does
|
|
|
|
- Starts Claude Code sessions and registers them with hapi-hub.
|
|
- Starts Codex mode for OpenAI-based sessions.
|
|
- Starts Cursor Agent mode for Cursor CLI sessions.
|
|
- Starts Grok Build locally or via ACP for remote sessions.
|
|
- Starts OpenCode mode via ACP and its plugin hook system.
|
|
- Provides an MCP stdio bridge for external tools.
|
|
- Manages a background runner for long-running sessions.
|
|
- Includes diagnostics and auth helpers.
|
|
|
|
## Typical flow
|
|
|
|
1. Start the hub and set env vars (see ../hub/README.md).
|
|
2. Set the same CLI_API_TOKEN on this machine or run `hapi auth login`.
|
|
3. Run `hapi` to start a session.
|
|
4. Use the web app or Telegram Mini App to monitor and control.
|
|
|
|
## Commands
|
|
|
|
### Session commands
|
|
|
|
- `hapi` - Start a Claude Code session (passes through Claude CLI flags). See `src/index.ts`.
|
|
- `hapi codex` - Start Codex mode. See `src/codex/runCodex.ts`.
|
|
- `hapi codex resume <sessionId>` - Resume existing Codex session.
|
|
- `hapi cursor` - Start Cursor Agent mode. See `src/cursor/runCursor.ts`.
|
|
Supports `hapi cursor resume <chatId>`, `hapi cursor --continue`, `--mode plan|ask`, `--yolo`, `--model`.
|
|
Local and remote modes supported; remote uses `agent -p` with stream-json.
|
|
- `hapi grok` - Start Grok Build mode. See `src/grok/runGrok.ts`.
|
|
- `hapi opencode` - Start OpenCode mode via ACP. See `src/opencode/runOpencode.ts`.
|
|
Note: OpenCode supports local and remote modes; local mode streams via OpenCode plugins.
|
|
- `hapi resume [sessionId]` - List resumable sessions for this machine or resume one locally.
|
|
- `hapi ping-peer <session-id-prefix> <message>` - Resume (if needed) and message another session. Prefer this or MCP `ping_peer` over reinventing JWT+curl. Also `--message-file` / `--list`.
|
|
- `hapi inspect-peer <session-id-or-prefix>` - Read-only peer metadata + recent message text (no resume). Prefer this or MCP `inspect_peer` when a user cites `[title](/sessions/<id>)`. Optional `--limit`.
|
|
|
|
### Resume a remote session locally
|
|
|
|
```bash
|
|
hapi resume
|
|
hapi resume <session-id>
|
|
```
|
|
|
|
`hapi resume` lists resumable sessions for the current machine. `hapi resume <session-id>` hands off an active remote session and opens the same HAPI session in the local terminal.
|
|
|
|
### Authentication
|
|
|
|
- `hapi auth status` - Show authentication configuration and token source.
|
|
- `hapi auth login` - Interactively enter and save CLI_API_TOKEN.
|
|
- `hapi auth logout` - Clear saved credentials.
|
|
|
|
See `src/commands/auth.ts`.
|
|
|
|
### Runner management
|
|
|
|
- `hapi runner start` - Start runner as detached process.
|
|
- `hapi runner stop` - Stop runner gracefully.
|
|
- `hapi runner status` - Show runner diagnostics.
|
|
- `hapi runner list` - List active sessions managed by runner.
|
|
- `hapi runner stop-session <sessionId>` - Terminate specific session.
|
|
- `hapi runner logs` - Print path to latest runner log file.
|
|
|
|
Both `start` and `start-sync` accept repeatable `--workspace-root <path>` (or `--workspace-root=<path>`). When set:
|
|
|
|
- The web `/browse` page surfaces scoped file trees rooted at those paths.
|
|
- The runner refuses `list-directory` and `spawn-session` requests for paths outside the configured roots.
|
|
- `~` and `~/foo` are expanded.
|
|
|
|
Omitting the flag keeps the legacy behavior: no scoping, no `/browse` feature.
|
|
|
|
See `src/runner/run.ts`.
|
|
|
|
### Diagnostics
|
|
|
|
- `hapi doctor` - Show full diagnostics (version, runner status, logs, processes).
|
|
- `hapi doctor clean` - Kill runaway HAPI processes.
|
|
|
|
See `src/ui/doctor.ts`.
|
|
|
|
### Other
|
|
|
|
- `hapi mcp` - Start MCP stdio bridge. See `src/codex/happyMcpStdioBridge.ts`.
|
|
- `hapi hub` - Start the bundled hub (single binary workflow).
|
|
- `hapi server` - Alias for `hapi hub`.
|
|
|
|
## Configuration
|
|
|
|
See `src/configuration.ts` for all options.
|
|
|
|
### Required
|
|
|
|
- `CLI_API_TOKEN` - Shared secret; must match the hub. Can be set via env or `~/.hapi/settings.json` (env wins).
|
|
- `HAPI_API_URL` - Hub base URL (default: http://localhost:3006).
|
|
|
|
### Optional
|
|
|
|
- `HAPI_HOME` - Config/data directory (default: ~/.hapi).
|
|
- `HAPI_EXPERIMENTAL` - Enable experimental features (true/1/yes).
|
|
- `HAPI_EXTRA_HEADERS_JSON` - JSON object of extra headers to send on CLI → hub requests, e.g. `{"Cookie":"CF_Authorization=..."}`. Can also be set as the `extraHeaders` object in `~/.hapi/settings.json` (environment variable wins).
|
|
- `HAPI_CLAUDE_PATH` - Path to a specific `claude` executable.
|
|
- `HAPI_HTTP_MCP_URL` - Default MCP target for `hapi mcp`.
|
|
|
|
### Runner
|
|
|
|
- `HAPI_RUNNER_HEARTBEAT_INTERVAL` - Heartbeat interval in ms (default: 60000).
|
|
- `HAPI_RUNNER_HTTP_TIMEOUT` - HTTP timeout for runner control in ms (default: 10000).
|
|
|
|
### Worktree (set by runner)
|
|
|
|
- `HAPI_WORKTREE_BASE_PATH` - Base repository path.
|
|
- `HAPI_WORKTREE_BRANCH` - Current branch name.
|
|
- `HAPI_WORKTREE_NAME` - Worktree name.
|
|
- `HAPI_WORKTREE_PATH` - Full worktree path.
|
|
- `HAPI_WORKTREE_CREATED_AT` - Creation timestamp (ms).
|
|
|
|
### Set for the wrapped agent
|
|
|
|
- `HAPI_SESSION_ID` - The hub session id for the current run, exported into the wrapped agent/CLI child environment at spawn for every flavor (claude / codex / cursor / gemini / opencode / kimi / grok / pi), both runner-spawned and locally started sessions. Agents can read it to self-target "this chat" over the hub REST API or shell helpers without listing `/api/sessions`. Prefer the MCP `display_image` tool for inline media when it is available; use `HAPI_SESSION_ID` for hub REST / shell tooling where MCP is not. To **read** another session, prefer MCP `inspect_peer` or `hapi inspect-peer`. To **message** another session, prefer MCP `ping_peer` or `hapi ping-peer` — do not reinvent JWT+curl. User citations look like `[title](/sessions/<id>)`; pass that `<id>` as `sessionIdPrefix`.
|
|
|
|
Lazy Codex (terminal) sessions export the id only after the hub row is materialized, which happens when the MCP bridge starts — before the agent process is spawned — so path-only self-targeting does not race a missing hub row.
|
|
|
|
Example (shell fallback when MCP is unavailable) — path-only, self-targets the current session:
|
|
|
|
```bash
|
|
bun scripts/tooling/hapi-display-image.mjs /absolute/path/to/image.png "optional title"
|
|
```
|
|
|
|
Explicit other session (prefix or full uuid) still works; that path may list sessions.
|
|
|
|
## Storage
|
|
|
|
Data is stored in `~/.hapi/` (or `$HAPI_HOME`):
|
|
|
|
- `settings.json` - User settings (machineId, token, onboarding flag). See `src/persistence.ts`.
|
|
- `runner.state.json` - Runner state (pid, port, version, heartbeat).
|
|
- `logs/` - Log files.
|
|
|
|
## Requirements
|
|
|
|
- Claude CLI installed and logged in (`claude` on PATH).
|
|
- Cursor Agent CLI installed (`agent` on PATH) for `hapi cursor`. Install: `curl https://cursor.com/install -fsS | bash` (macOS/Linux), `irm 'https://cursor.com/install?win32=true' | iex` (Windows).
|
|
- Grok Build CLI installed (`grok` on PATH) for `hapi grok`. Authenticate with `grok login --device-auth` on headless runner machines, or set `XAI_API_KEY`.
|
|
- OpenCode CLI installed (`opencode` on PATH).
|
|
- Bun for building from source.
|
|
|
|
## Build from source
|
|
|
|
From the repo root:
|
|
|
|
```bash
|
|
bun install
|
|
bun run build:cli
|
|
bun run build:cli:exe
|
|
```
|
|
|
|
For an all-in-one binary that also embeds the web app:
|
|
|
|
```bash
|
|
bun run build:single-exe
|
|
```
|
|
|
|
## Source structure
|
|
|
|
- `src/api/` - Bot communication (Socket.IO + REST).
|
|
- `src/claude/` - Claude Code integration.
|
|
- `src/codex/` - Codex mode integration.
|
|
- `src/cursor/` - Cursor Agent integration.
|
|
- `src/grok/` - Grok Build native TUI + ACP integration.
|
|
- `src/agent/` - Shared support for ACP-compatible agents.
|
|
- `src/opencode/` - OpenCode ACP + hook integration.
|
|
- `src/runner/` - Background service.
|
|
- `src/commands/` - CLI command handlers.
|
|
- `src/ui/` - User interface and diagnostics.
|
|
- `src/modules/` - Tool implementations (ripgrep, difftastic, git).
|
|
|
|
## Related docs
|
|
|
|
- `../hub/README.md`
|
|
- `../web/README.md`
|