AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
翻譯準備中
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證Gravity — 桌面工作區,供 AI 程式碼代理使用。
AI 翻譯
Gravity — a desktop workspace for AI coding agents
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Always-on, multi-bot desktop app with Claude Code or Codex CLI as the agent runtime.
A Rust daemon (gravityd) runs on macOS or Windows; the Tauri desktop client attaches
from any machine. See the architecture for the system
design and the control-plane protocol for client APIs.
Native Windows x64 builds and local-daemon setup are described in
Windows build instructions.檢視完整 README ↗Always-on, multi-bot desktop app with Claude Code or Codex CLI as the agent runtime.
A Rust daemon (gravityd) runs on macOS or Windows; the Tauri desktop client attaches
from any machine. See the architecture for the system
design and the control-plane protocol for client APIs.
Native Windows x64 builds and local-daemon setup are described in
Windows build instructions.
Builds and tests require no Linear, PostHog, or Cloudflare account. Running real bots requires your own installation and authentication for the chosen CLI; the test runtime works without it. See public-build configuration for telemetry, releases, and secret scanning.
Choose each bot's CLI in Bot info → Bot runtime. See bot runtimes for setup, configuration and the native Codex terminal.
crates/bus/ shared types, SQLite schema, envelope format
crates/gravityd/ daemon: runtime adapters, durable delivery, scheduler,
MCP bridge, WebSocket control plane
apps/desktop/ Tauri v2 + React + xterm.js client
docs/ architecture, WS protocol v2, public-build configuration
ops/ launchd plist + example gravityd.toml
scripts/ repository checks (file-length rule)
After installing the desktop and marketing dependencies, run pnpm run verify
from the repository root for the full verification suite (macOS and Docker
required). See CONTRIBUTING.md for setup.
# Daemon
cargo build --release # target/release/gravityd
cargo test --workspace # unit + integration (uses the runtime double)
cargo clippy --workspace --all-targets
cargo fmt --all --check
# Desktop client
cd apps/desktop
pnpm install
pnpm typecheck && pnpm build # frontend
../../scripts/prepare-sidecar.sh # stage gravityd as the bundled sidecar (once,
# and after daemon changes)
pnpm tauri dev # run the app
No source file may exceed 400 lines. Split by concern instead of growing a
file: crates/gravityd/src/db/, crates/gravityd/src/ws/ and apps/desktop/src/app/
are the reference examples.
./scripts/check-file-length.sh # enforces the limit for Rust + CSS
TypeScript is covered by oxlint's max-lines rule instead, so the limit is
checked in two places for two toolchains.
The desktop client carries the JS/TS toolchain; pnpm check runs the whole
gate and is what CI (and a pre-push hook, if you add one) should call:
cd apps/desktop
pnpm check # typecheck + format + lint + knip + tests-with-coverage + fallow
| Command | Tool | What it enforces |
|---|---|---|
pnpm typecheck | tsc | strict types, no emit |
pnpm format:check / pnpm format | oxfmt | formatting (.oxfmtrc.json) |
pnpm lint / pnpm lint:fix | oxlint | correctness, suspicious, perf, React/a11y, max-lines: 400 (.oxlintrc.json) |
pnpm knip | knip | unused files, exports and dependencies (knip.json) |
pnpm test / pnpm test:watch | vitest + jsdom + Testing Library | unit and component tests (vitest.config.ts) |
pnpm test:coverage | vitest + v8 | writes coverage/coverage-final.json |
pnpm fallow | fallow | dead code, duplication, complexity and CRAP thresholds (fallow.config.json) |
fallow health scores untested complexity as CRAP (CC² × (1 − coverage)³ + CC),
so it only passes with a real coverage report: fallow.config.json points
health.coverage at coverage/coverage-final.json, and pnpm check runs
test:coverage before fallow. Running pnpm fallow on its own after changing
code will read a stale report — run pnpm test:coverage first.
Components are tested against FakeDaemon (src/test/fakeDaemon.ts), an
in-memory implementation of the DaemonApi interface in src/protocol/api.ts.
That interface exists so nothing in the UI depends on the concrete
DaemonClient, and so the double needs no type casts. DaemonClient itself is
tested against a scripted WebSocket (src/test/fakeWebSocket.ts).
.githooks/pre-commit runs oxfmt, oxlint and fallow's coverage-independent
checks (dead-code, dupes) whenever a commit touches
apps/desktop/. Enable it once per clone (it is a native git hook — no
dependency, and core.hooksPath is relative so it covers every worktree):
git config core.hooksPath .githooks
fallow health is deliberately left out of the hook: it needs a fresh coverage
report, and regenerating one on every commit is too slow. pnpm check covers it.
The hook inspects the working tree rather than the index, so a partial staged-hunk commit is checked against the files as they are on disk. Resolve validation failures before committing.
Open the release DMG and drag Gravity to Applications. On Windows, run
the release x64-setup.exe installer. The app bundles
the daemon as a signed sidecar; on first launch a setup wizard either installs
it as a launchd user agent on this Mac (one click, no sudo) or attaches to a
remote daemon with a device token. Headless machines skip the app:
./gravityd service install from the release tarball does the same
install, and service status / service restart / service uninstall manage
it.
A daemon already answering on 49777 stops the install: Gravity refuses to
start rather than run a second one against the same state. If something else —
not a daemon — holds 49777, the app-managed service waits a few seconds, then
serves on a free port instead and the app follows it there. That moves the bot
bus off the allowlisted http://127.0.0.1:49777/mcp URL, which a policy-managed
Mac silently drops, so Settings → Connection flags it and the daemon restarts
onto 49777 as soon as it comes free. Set negotiate_port = false in
gravityd.toml to get a daemon that refuses to start instead.
scripts/dev.sh starts a workspace-private daemon and the client against it —
nothing touches ~/.gravity, so it can run beside the installed production
daemon and beside other checkouts:
./scripts/dev.sh # gravityd + the real Tauri window
./scripts/dev.sh web # gravityd + Vite frontend in the browser
GRAVITY_RUNTIME=double ./scripts/dev.sh # deterministic echo runtime, no tokens spent
The daemon gets its own home at .dev/gravityd/ (gitignored) with a generated
gravityd.toml. Ports derive from CONDUCTOR_PORT when Conductor sets it —
frontend on CONDUCTOR_PORT, daemon on CONDUCTOR_PORT+1, defaulting to
1420/1421 — so parallel workspaces do not collide. In browser mode the script
prints the localStorage snippet that points the page at that daemon; the
Tauri window picks the daemon up from GRAVITY_HOME.
Conductor drives the same script from .conductor/settings.toml: the dev,
dev-double, app and test run scripts, with setup installing the
frontend dependencies.
mkdir -p ~/.gravity
cp ops/gravityd.example.toml ~/.gravity/gravityd.toml # optional; defaults are sane
cargo run -p gravityd # or: gravityd --config <path>
curl http://127.0.0.1:49777/health
On first start the daemon generates ~/.gravity/secrets/client.token
(mode 0600), which the desktop client reads automatically on the same machine.
For a remote client (laptop over Tailscale), do not copy the owner token.
Create a device-scoped credential instead — Devices panel in the app, or
create_device over the protocol — choose read and/or control grants,
and enter the one-time token plus the Tailscale host/port in the remote app's
connection settings. Revoking the device immediately prevents reconnection.
gravityd backup # ~/.gravity/backups/backup-<timestamp>
gravityd backup --out /path/to/dir
gravityd restore --from /path/to/dir # refuses if a db exists
gravityd restore --from /path/to/dir --overwrite # moves current db aside first
Backups use SQLite's online backup API and include configuration manifests
(project/bot config files) but never secrets or bot workspaces. Retention
pruning (messages, deliveries, routine runs; FTS index kept in sync) runs
daily by default — see [retention] in gravityd.toml.
cargo build --release
./target/release/gravityd service install # binary → ~/.gravity/bin, launchd user agent
sudo pmset -a sleep 0 disablesleep 1 # keep the mini awake
service install copies the invoked binary to ~/.gravity/bin/gravityd,
writes ~/.gravity/gravityd.toml if missing, and bootstraps the
in.mikolajczuk.gravityd launchd agent — rerun it after a rebuild to upgrade in
place. service restart bounces that agent without touching the install, which
kills every running bot session mid-turn; Settings → Connection offers the same
thing behind a confirmation. ops/in.mikolajczuk.gravityd.plist remains for
fully manual setups.
~/.gravity/projects/<p>/bots/<b>)
with system.md, mcp.json, and a workspace containing CLAUDE.md and
cooperative .claude/settings.json (permission rules + lifecycle hooks that
report state to the daemon). The runtime adapter spawns claude there in a
PTY; the client renders it with xterm.js, preserving native prompts.[msg #42 from BOB · task] …)
to the session's inbox socket, retries with backoff, and surfaces terminal
failures. Bots consume and acknowledge via the gravity-bus MCP server
(send_message, complete_task, check_inbox, …) with per-bot scoped
tokens.control grant can type and resize its terminal.See CONTRIBUTING.md for setup, validation, and pull requests.
Gravity is licensed under the MIT License. Third-party dependencies retain their respective licenses.