Gravity CLI
The command-line tool for building, testing and publishing AI agents on Gravity.
Write ordinary LangGraph in Python, declare what your agent may touch in one manifest,
and run it against real tools with exactly those permissions enforced.
Install · Quick start · Commands · Configuration · Manifest · Troubleshooting
gravity run on an agent that called a tool it never declared. The gateway refused that one call, and the run carried on:
inbox-digest@1.0.0 run grt_…4e7b · 2 tools declared
● read 0.9s
✓ gmail.read 3 found 0.8s
● digest 2.3s
◆ openai/gpt-6-luna 2.1s
✗ slack.send refused — not in permissions.tools 0.0s
╭─ result ───────────────────────────────────────────────────╮
│ unread 3 │
│ headline Two invoices and a meeting request need replies. │
╰────────────────────────────────────────────────────────────╯
✓ Done in 3.3s
pipx install gravity-cli
gravity init my-agent
Status.
login,push,runand the tool catalog work out of the box against Gravity's hosted services.gravity loginissues you the gateway token thatrunneeds; nothing goes in.env.
Contents
- Declare it once, enforced on every call
- What is in the box
- Five things to know before you start
- Installation
- Quick start
- How it fits together
- Command reference
- Configuration
- Authentication
- The manifest
- An agent project
- Running a local gateway
- Troubleshooting
- Limitations
- Development
- Documentation
Declare it once, enforced on every call
There is no SDK to learn. Your agent is plain LangGraph that reads three environment variables. Everything Gravity enforces comes from manifest.yaml:
You declare in manifest.yaml |
What Gravity does with it |
|---|---|
inputs |
Renders the form a consumer fills in, and passes the answers in state["inputs"] |
permissions.tools |
Issues a run token that allows exactly those tools. Any other call is refused by the gateway |
approvals.required_for |
Pauses those calls until a person approves them |
triggers.schedule |
Runs the agent on that cron schedule |
state |
Keeps a small memory between runs |
The same rules apply on your laptop and when the agent runs hosted, because both go through the same gateway.
What is in the box
| Commands | 16, grouped into build, publish, inspect, account and help |
| Starter templates | 4: minimal, structured, deepagent, chat |
| Tool catalog | 1,139 tools across 8 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn), 25 of them curated |
| Test suite | 285 tests, run on Ubuntu and Windows with Python 3.12 and 3.13 |
| Runtime contract | 3 environment variables, no Gravity import in your code |
Five things to know before you start
- Inputs arrive in
state["inputs"]; whatever you put instate["result"]is what the consumer gets. graphis anasync deffactory. Read environment variables inside it, never at import time.- A tool that is not in
permissions.toolsis refused, whatever your code does. - A tool listed in
approvals.required_forwaits for a yes in the terminal before it runs. - The platform runs Python 3.12.
gravity initwrites that, andgravity validatefails on anything else.
Installation
Requires Python 3.12 or newer. Agents use uv for their own environments.
pipx install gravity-cli # recommended: gravity gets its own environment
uv tool install gravity-cli # the same, with uv
pip install gravity-cli # into the current environment
gravity then works from any folder. To upgrade: pipx upgrade gravity-cli or uv tool upgrade gravity-cli.
If pip answers No matching distribution found for gravity-cli, your Python is older than 3.12 (check with python --version; conda's base often is). uv tool install gravity-cli avoids the problem, because uv fetches a suitable Python by itself. With pip, pick a newer interpreter: py -3.12 -m pip install gravity-cli on Windows, or python3.12 -m pip install gravity-cli elsewhere.
The first time you run gravity, it shows a short welcome and offers to create your first agent.
Quick start
1. Create and check an agent. No account or setup needed:
gravity init my-agent
cd my-agent
uv venv && uv pip install -r requirements.txt
gravity validate
2. Run it. Needs a gateway and your dev token, see Configuration:
gravity run --input query="hello"
3. Publish it. Needs a Gravity account:
gravity build
gravity login
gravity push
How it fits together
your machine Gravity
──────────── ───────
gravity init / validate / build (nothing leaves your machine)
gravity run ────── dev token ──────────────────→ gateway ──→ tools (MCP) and models (/v1)
└─ starts your agent with a run token ──────→ gateway only the declared tools are allowed
gravity login / push / tools / agents ─────────→ control plane ──→ builds and hosted runs
| Service | What it does | Used by |
|---|---|---|
| Gateway | Serves tools over MCP (/mcp) and models over an OpenAI-compatible API (/v1). Lists its tools at /tools and your connected accounts at /connections. Issues run tokens and refuses any tool a run was not granted. |
run, schedule, adapt, tools, connect, and your agent |
| Control plane | Accounts, pushed agents, builds and hosted runs. | login, logout, whoami, push, agents, runs, logs |
On gravity run, the CLI trades your dev token for a run token scoped to the manifest's tools and starts the agent with only that token. Your own tokens are removed from the agent's environment, so agent code can never act as you.
Command reference
Run gravity help for this list in the terminal, and add -h to any command for its options.
| Group | Command | What it does |
|---|---|---|
| Build an agent | gravity init |
Create a new agent from a template |
gravity validate |
Check your agent for mistakes before it runs | |
gravity run |
Run your agent locally against real tools | |
gravity schedule |
Run your agent on its schedule, locally | |
gravity adapt |
Write a new slot adapter with AI, kept only if its tests pass | |
| Publish | gravity build |
Package your agent into build/agent.zip |
gravity push |
Publish your built agent to Gravity | |
| Your agents | gravity agents |
List your published agents and their review status |
gravity runs |
Show recent runs of one agent | |
gravity logs |
Show the logs of one run | |
gravity tools |
Browse the tools your agent can use | |
| Account | gravity login |
Sign in to Gravity in your browser |
gravity logout |
Sign out | |
gravity whoami |
Show who you are signed in as | |
gravity connect |
Connect an account your agents act on (Gmail, Slack...) | |
| Help | gravity guide |
Read the builder guide: how to build a good agent |
gravity help |
Show the list of commands |
gravity --api-url URL <command> saves a different control-plane address to ~/.gravity/config.json. Wherever a command takes [path], it is the agent's folder and defaults to the current one.
gravity init
Create a new agent that already runs: a filled-in manifest, pinned dependencies and a working src/agent.py.
gravity init [name] [--template TEMPLATE] [--here]
| Argument or option | Description | Default |
|---|---|---|
name |
Name of the agent, and of the folder created. Lowercase letters, digits and single hyphens. | required, unless --here |
--template |
Starting point: minimal, structured, deepagent or chat |
minimal |
--here |
Add only a manifest.yaml to the current folder, for an existing project. The name comes from the folder. |
off |
| Template | What you get |
|---|---|
minimal |
One file. A LangGraph agent that answers a query with the declared tools |
structured |
The same agent split into state, prompts, nodes and gateway modules, for larger projects |
deepagent |
An agent built on the deepagents harness, with file access denied by default |
chat |
A conversational agent that keeps the thread between turns |
gravity init inbox-digest
gravity init support-bot --template chat
gravity init --here
It refuses to overwrite an existing folder or manifest.yaml.
gravity validate
Check an agent on your machine, with no Gravity service involved. It reports every problem at once, each with a suggested fix.
gravity validate [path]
What it checks:
manifest.yamlis valid, field by field- the entrypoint file exists and defines the object the manifest names
requirements.txtexists, and compiles intorequirements.lockfor the platform image (ARM64 Linux) and the manifest's Python version- every adapter a slot declares has a file, and every adapter file is declared
- every tool in the manifest exists in the catalog, the same list the platform's build uses, so a typo fails here instead of after a push
- every imported write tool is under
approvals.required_for - your code never names a write tool the manifest does not grant
- a warning, never a failure, for read tools your code names but the manifest does not declare
gravity validate
gravity validate ./my-agent
Exits with code 1 if anything is wrong.
gravity run
Run the agent locally through the gateway and watch it live: each step, model call and tool call, then a result card.
gravity run [path] [--input KEY=VALUE]... [--reply TEXT] [--llm-url URL]
| Option | Description |
|---|---|
--input KEY=VALUE |
An input, repeatable. KEY=@file.txt reads the value from a file. Inputs with a default in the manifest can be left out. |
--reply TEXT |
Continue the last conversation of a chat agent |
--llm-url URL |
Model endpoint to use instead of GRAVITY_LLM_URL |
gravity run --input query="summarise my unread email"
gravity run --input transcript=@call.txt --input tone=formal
gravity run --reply "make it shorter"
- Requires a gateway token: the one
gravity loginstores, orGRAVITY_DEV_TOKEN. - A call to a tool in
approvals.required_forpauses, shows what it would do, and waits foryorn. With no terminal to ask in, it is rejected. - Exits with code 0 when the run finishes and 1 when it fails.
gravity schedule
Run the agent on its triggers.schedule, the way the platform will, until you press Ctrl+C. Each tick is an ordinary run with the same inputs.
gravity schedule [path] [--input KEY=VALUE]... [--cron EXPR] [--now]
| Option | Description |
|---|---|
--input KEY=VALUE |
An input, repeatable. The same inputs are used for every run. |
--cron EXPR |
Use this 5-field cron schedule instead of the manifest's, if the manifest allows changes |
--now |
Run once right away, then keep to the schedule |
gravity schedule --input send_to=me@example.com
gravity schedule --cron "*/30 * * * *" --now
Nobody is at the terminal during a scheduled run, so calls that need approval are rejected and the agent takes its fallback. A failed run does not stop the schedule.
gravity adapt
Have a coding agent write one new adapter for a slot. The file is kept only if the slot's conformance tests pass; otherwise the file and the manifest are put back as they were.
gravity adapt <slot> <name> --tools TOOLS [--hint TEXT] [--model MODEL] [--path PATH]
| Argument or option | Description | Default |
|---|---|---|
slot |
The slot to extend, for example deliver |
required |
name |
Name of the new adapter, for example slack |
required |
--tools |
Tools the adapter may call, comma-separated. This is what it is granted. | required |
--hint |
Anything the coding agent should know, for example 'post to #leads' |
none |
--model |
Model that writes the adapter | anthropic/claude-sonnet-5 |
--path |
Agent folder | current folder |
gravity adapt deliver slack --tools slack.send,slack.list_channels --hint "post to #leads"
Requires tests/test_<slot>_adapters.py in the project, GRAVITY_DEV_TOKEN and a reachable gateway. It makes up to 3 attempts, feeding each test failure back to the model.
gravity build
Validate the agent, then bundle it into build/agent.zip with its hashes in build/build.json. Nothing leaves your machine, so you can inspect the zip before publishing.
gravity build [path]
The bundle holds manifest.yaml, requirements.txt, requirements.lock, src/ and tests/. The same source always produces the same bytes.
gravity build
gravity push
Upload exactly what gravity build made, then wait while Gravity builds it.
gravity push [path]
- Requires
gravity login, orGRAVITY_API_TOKENfor development. - Refuses if there is no build, or if the source changed since the build. Run
gravity buildagain. - A published version can never be replaced. Raise
versionin the manifest to publish again. - Press Ctrl+C to stop waiting; the build carries on, and
gravity agentsshows its status.
gravity build && gravity push
gravity agents
List your published agents with their version, status and last update.
gravity agents
gravity runs
Show recent hosted runs of one agent, with status, start time and cost.
gravity runs <agent_name>
gravity runs inbox-digest
gravity logs
Show the logs of one hosted run. The run id comes from gravity runs.
gravity logs <run_id>
gravity tools
Browse the tool catalog: the names you can put under permissions.tools, with what each one does. It is the gateway's own list, so it shows exactly what a run can call, and it needs no sign-in.
About 25 tools are curated, such as gmail.read and web.search. The other ~1,100 are imported from the same apps and unreviewed: any imported tool that changes something must also be under approvals.required_for, and gravity validate checks that.
gravity tools [search]
gravity tools # everything
gravity tools gmail # only tools whose name contains "gmail"
gravity login
Sign in to Gravity. It opens your browser, shows a short code to confirm, and stores the session in your operating system's keychain.
gravity login
gravity logout
Remove the stored session.
gravity logout
gravity whoami
Show the account you are signed in as, and whether you hold a gateway token and until when.
gravity whoami
gravity whoami --token # print the gateway token itself, for CI or a teammate's .env
gravity connect
Connect an account your agents' tools act on, through the app's own sign-in page in your browser. With no app, list what is connected.
gravity connect [app]
gravity connect # what is connected, and whose accounts runs act on
gravity connect gmail # opens the sign-in page, then waits until it is done
gravity connect sheets # use the app name from tool names: sheets, calendar, drive, linear, slack, github...
- Uses the gateway token from
gravity login. webtools need no account.
gravity guide
Read the builder guide, which ships with the CLI: how an agent is put together, the rules your code must follow, formatting results, approvals, adapters, scheduling, memory, and a checklist.
gravity guide [--save]
| Option | Description |
|---|---|
--save |
Write BUILDER_GUIDE.md to the current folder instead of showing it |
On a terminal it asks which section you want. Piped, it prints the whole guide.
gravity guide
gravity guide --save
gravity guide | less
gravity help
Show the list of commands. The same as gravity -h.
gravity help
Configuration
Where settings live
Put them in ~/.gravity/.env (on Windows, %USERPROFILE%\.gravity\.env). Every gravity command loads this file, from any folder, so you set a value once. With Gravity's hosted services, gravity login is all you need, and the file can stay empty.
# ~/.gravity/.env
GRAVITY_DEV_TOKEN=<a gateway token, only if gravity login did not issue you one>
That is the only setting most builders ever need. The CLI knows where Gravity's control plane is, and asks it where the gateway is.
When a setting is given in more than one place, the first match wins:
- a variable exported in your shell
~/.gravity/.env~/.gravity/config.json(only the API address, written bygravity --api-url URL <command>)- the built-in default
Never commit a .env file. The .gitignore in every template keeps .env and .gravity/ out of git.
What each task needs
| You want to | Commands | Required | Usually also set |
|---|---|---|---|
| Create, check and package an agent | init, validate, build |
nothing | nothing |
| Run an agent locally | run, schedule, adapt |
gravity login (or GRAVITY_DEV_TOKEN) |
nothing |
| Publish and inspect agents | push, agents, runs, logs, whoami |
gravity login |
nothing |
| Browse tools, connect accounts | tools, connect |
nothing for tools; gravity login for connect |
nothing |
Set GRAVITY_API_URL and GRAVITY_GATEWAY_URL only to work against services you run yourself; see Running a local gateway.
Environment variables
| Variable | What it is | Default |
|---|---|---|
GRAVITY_DEV_TOKEN |
A gateway token. gravity run trades it for a run token scoped to the manifest's tools. Overrides the token gravity login stored; needed only when login did not issue one, such as against a local gateway. |
the token from gravity login |
GRAVITY_GATEWAY_URL |
The gateway's MCP endpoint, ending in /mcp |
the gateway the control plane names in GET /api/cli/config |
GRAVITY_LLM_URL |
The OpenAI-compatible model endpoint | the gateway's address + /v1 |
GRAVITY_API_URL |
The control plane's address | https://agents.dev.gravity.fast |
GRAVITY_API_TOKEN |
A control-plane dev token, used instead of gravity login. For development only. |
none |
GRAVITY_CONFIG_DIR |
Moves ~/.gravity elsewhere. Useful for tests and CI. |
~/.gravity |
GRAVITY_UNATTENDED |
When 1, calls that need approval are rejected without asking. gravity schedule sets it for you. |
unset |
What your agent receives
gravity run starts your agent with exactly these three, and the hosted runner does the same. You never set them yourself:
| Variable | Value |
|---|---|
GRAVITY_GATEWAY_URL |
the gateway's /mcp endpoint, for tools |
GRAVITY_LLM_URL |
the gateway's /v1 endpoint, for models |
GRAVITY_RUN_TOKEN |
a token for this run only. Use it as the bearer token for tools and as the API key for models. |
Authentication
Two separate credentials, for two separate services:
| Credential | Used by | How you get it |
|---|---|---|
| Control-plane session | push, tools, agents, runs, logs, whoami |
gravity login |
| Gateway token | run, schedule, adapt |
gravity login asks Gravity for one and stores it with the session. gravity whoami shows its expiry; gravity whoami --token prints it. GRAVITY_DEV_TOKEN, if set, is used instead. |
gravity login stores the session and gateway token in the operating system's keychain. On machines without one, it falls back to ~/.gravity/session.json, readable only by you. gravity logout removes it.
For development, setting GRAVITY_API_TOKEN to the control plane's dev token skips login. gravity whoami then prints the dev user's id followed by (dev token).
The manifest
manifest.yaml declares everything about the agent that Gravity enforces. Only name and runtime.entrypoint are required; everything else has a default.
The smallest useful manifest:
contract: v1
name: my-agent
version: 1.0.0
runtime:
entrypoint: src/agent.py:graph
inputs:
- name: query
type: text
label: "What should the agent do?"
required: true
permissions:
tools: [example.echo]
A complete manifest, with every section annotated
This one is valid as written.
contract: v1 # manifest contract version; v1 is the only one
name: inbox-digest # lowercase letters, digits, single hyphens; appears in URLs
version: 1.0.0 # semver; a published version is frozen, so bump it to publish again
description: "Summarises unread email and sends a digest"
kind: interactive # transform | interactive | monitor
runtime:
framework: langgraph # the only framework in v1
python: "3.12" # the platform image runs Python 3.12
entrypoint: src/agent.py:graph # file:object, relative to the project folder
timeout_seconds: 300 # 10 to 28800
dependencies:
lockfile: requirements.lock # written by `gravity validate`
inputs: # each becomes a field in the consumer's form
- name: lookback
type: static-dropdown
label: "How far back?"
options: ["1 day", "7 days"]
default: "1 day"
- name: send_to
type: email
label: "Send the digest to"
required: true
permissions:
tools: # the only tools this agent's runs may call
- gmail.read
- gmail.send
models:
tier: standard # standard | premium
approvals:
required_for: # these calls pause until a person approves them
- gmail.send
resources:
memory_mb: 512 # 128 to 4096; a request the platform may lower
triggers:
manual: true # the consumer can start a run
schedule:
cron: "0 8 * * 1-5" # weekdays at 08:00, local time
consumer_can_change: true
state:
max_kb: 64 # memory kept between runs (1 to 256 KB); omit for none
slots: # a part the consumer chooses; each adapter gets only its own tools
deliver:
label: "Where should the digest go?"
default: email
adapters:
email: [gmail.draft] # code in src/adapters/deliver/email.py
slack: [slack.send] # code in src/adapters/deliver/slack.py
outputs: # extra formats the platform renders and delivers itself
report:
renderer: markdown # markdown | docx | pptx | rows
label: "Digest as a document"
destinations: [download]
Field reference
| Field | Required | Default | Rules |
|---|---|---|---|
contract |
no | v1 |
Only v1 exists. |
name |
yes | Lowercase letters, digits and single hyphens, up to 64 characters. Appears in URLs. | |
version |
no | 1.0.0 |
Strict semver, such as 1.2.0. A published version can never change. |
description |
no | empty | Up to 500 characters. |
kind |
no | interactive |
transform (input in, result out), interactive (calls tools mid-run, may converse) or monitor (runs on a schedule or webhook and remembers the last run; needs a trigger). |
runtime.entrypoint |
yes | path/to/file.py:object, inside the project. The object is your graph factory. |
|
runtime.framework |
no | langgraph |
Only langgraph. |
runtime.python |
no | 3.12 |
What the platform image runs. gravity validate fails on any other value, because the hosted build would. |
runtime.timeout_seconds |
no | 300 |
10 to 28800. |
dependencies.lockfile |
no | requirements.lock |
Compiled by gravity validate; ships with the agent. |
inputs[] |
no | none | Up to 50. Each has name (a lowercase identifier, the key in state["inputs"]), type, label, and optionally required, default, options, accept. |
inputs[].type |
yes | text, textarea, number, boolean, static-dropdown (needs options), email, url, date, secret (an API key, password or other confidential value) or file (no default; accept lists extensions such as [pdf, csv]). |
|
permissions.tools |
no | none | Up to 50 catalog names, such as gmail.read. Run gravity tools for the list. |
permissions.models.tier |
no | standard |
standard or premium. |
approvals.required_for |
no | none | Tools that pause until a person approves. Each must also be declared under permissions.tools or a slot adapter. |
resources.memory_mb |
no | 512 |
128 to 4096. A request; the platform may lower it. |
triggers.manual |
no | true |
Whether the consumer can start a run. Something must start the agent: manual, a schedule or a webhook. |
triggers.schedule |
no | none | cron (5 fields, local time, such as "0 9 * * 1") and consumer_can_change (default true). |
triggers.webhook |
no | none | A list of events; github.push is the only one today. |
state.max_kb |
no | no memory | Include state: to keep memory between runs, in state["memory"]. 1 to 256 KB. |
slots.<name> |
no | none | A swappable part: label, default adapter, and adapters mapping each adapter name to the tools it may call. Code lives in src/adapters/<slot>/<adapter>.py. Slot names cannot repeat an input's name. |
outputs.<name> |
no | none | Up to 8 extra formats rendered from the result: renderer (markdown, docx, pptx or rows), label and destinations (download, or a catalog tool such as gmail.send). |
Unknown fields are rejected rather than ignored, so a typo is reported instead of silently dropped.
An agent project
my-agent/
├── manifest.yaml what the agent is and may do
├── requirements.txt your dependencies
├── requirements.lock compiled by `gravity validate`; commit it
└── src/
└── agent.py exports `graph`, an async factory returning a compiled LangGraph graph
Files the CLI writes:
| Path | Written by | Contents |
|---|---|---|
requirements.lock |
validate |
Exact dependency versions for the platform image |
build/agent.zip, build/build.json |
build |
The bundle push uploads, and its hashes |
.gravity/payload.json |
run |
The inputs passed to the agent |
.gravity/thread.json |
run |
The last conversation of a chat agent, for --reply |
.gravity/memory.json |
run |
Memory kept between runs, when the manifest declares state |
The template .gitignore excludes build/, .gravity/ and .env.
Running a local gateway
The gateway belongs to the Gravity platform repository and is run by the Gravity team. Builders outside the team use the hosted gateway instead.
Gateway settings, for the Gravity team
Start it with gravity-gateway serve. It listens on port 8000, prints a dev token, and reads its own .env in the gateway's folder. With nothing set, it serves only the built-in example.echo tool and keeps tokens in memory.
| Variable | What it enables | When you need it |
|---|---|---|
COMPOSIO_API_KEY |
Real tools (Gmail, Slack, Linear and others). Unset: only example.echo. |
To call any real tool |
OPENROUTER_API_KEY |
Model calls on /v1. Unset: /v1 returns 503. |
For any agent that calls a model |
OPENROUTER_BASE_URL |
Where model calls go. Default https://openrouter.ai/api/v1. |
Rarely |
LOCAL_DEV_TOKEN |
A fixed dev token (24+ random characters), so restarts don't change it. Use the same value as GRAVITY_DEV_TOKEN. |
Recommended |
GATEWAY_PORT |
The listening port. Default 8000. |
Only if 8000 is taken |
DATABASE_URL, PLATFORM_DB_SCHEMA |
Keeps run tokens in the platform database instead of memory | Platform development |
PLATFORM_SERVICE_KEY, AWS_REGION |
Lets hosted runs mint tokens and report back | Platform development |
A fully local setup, in ~/.gravity/.env:
GRAVITY_DEV_TOKEN=<the gateway's LOCAL_DEV_TOKEN>
GRAVITY_GATEWAY_URL=http://127.0.0.1:8000/mcp
GRAVITY_API_URL=http://localhost:3010
GRAVITY_API_TOKEN=<the control plane's PLATFORM_DEV_TOKEN>
Real tools act on a connected account. Connect one with gravity-gateway connect --user-id builder:local --toolkit <app>.
Troubleshooting
| Message | Cause and fix |
|---|---|
No matching distribution found for gravity-cli |
pip is running on a Python older than 3.12. Use uv tool install gravity-cli, or see Installation. |
No gateway token |
run, schedule and adapt need a gateway token. Run gravity login, or add GRAVITY_DEV_TOKEN to ~/.gravity/.env. |
Your gateway token has expired |
The token gravity login stored has lapsed. Run gravity login again. |
Can't reach the gateway at … |
No gateway at that address. Set GRAVITY_GATEWAY_URL to your gateway's /mcp address. |
The gateway refused this run |
The gateway rejected the token or a declared tool. Run gravity whoami to check the token, or gravity login for a fresh one. |
You are not signed in |
A control-plane command without credentials. Run gravity login. |
Can't reach Gravity at … |
The control plane did not answer. Check GRAVITY_API_URL in ~/.gravity/.env. |
Missing required input: … |
Pass each required input with --input name=value, or give it a default in the manifest. |
No build to push or The build is stale |
Run gravity build again after any change to src/, the manifest or the requirements. |
✗ requirements.lock in gravity validate |
A dependency has no prebuilt package for the platform image (ARM64 Linux). Pick a version that publishes one, or a different package. |
A tool row shows refused — not in permissions.tools |
The agent called a tool its manifest does not declare. Add it under permissions.tools, then run again. |
What's new
- 0.5.2 Jira, Confluence and Notion join the catalog: 1,351 tools across 11 apps.
get_store()works in local runs, kept in.gravity/store.json. Renewing an expired sign-in keeps your gateway token, sogravity runkeeps working after an hour. Commands start faster: the catalog loads in a quarter of the time. - 0.5.0
gravity connectlinks your own accounts.web.searchandweb.fetchjoin the catalog.gravity validatenow refuses a tool that isn't in the catalog, an imported write tool without an approval, and code that names a write tool the manifest doesn't grant.gravity toolsreads the gateway's list and needs no sign-in. On Windows the session stays in the keychain instead of a file. - 0.4.4 Same code as 0.4.3, released from this repository so the GitHub release, the tag and PyPI line up again. Adds
RELEASING.md. - 0.4.3 Documents the
secretinput type and thesupadata.get_transcripttool. - 0.4.1 New input type
secretfor an API key, password or other confidential value. New built-in toolsupadata.get_transcript(gravity tools supadata).
Limitations
- No offline mode.
gravity runalways talks to a real gateway. There is no mock-tool mode. - Heuristic drift check.
gravity validatefinds undeclared tools by scanning source for imports and tool-shaped strings. Expect some false positives; they are warnings, never failures. - Models are not restricted yet. The gateway forwards whatever model name the agent asks for, and
permissions.models.tieris not enforced. - Hosted history.
gravity runsandgravity logsshow what the control plane returns, which is empty until an agent has hosted runs.
Development
git clone https://github.com/AIGravity/gravity-cli.git
cd gravity-cli
uv sync # both packages and the dev tools
uv run gravity --help
uv run pytest # CLI tests
cd packages/gravity-schema && uv run pytest && cd ../.. # manifest contract tests
uv run ruff check .
The repository holds two packages that are always released together at the same version:
| Package | What it is |
|---|---|
gravity-cli |
The gravity command (src/gravity_cli/) |
gravity-schema |
The manifest contract: models, validation, the tool catalog and its JSON Schema (packages/gravity-schema/). The platform's servers use it too, so a manifest is judged the same way everywhere. |
After changing the manifest model, regenerate the committed JSON Schema with uv run python -m gravity_schema. A test fails until you do.
Continuous integration and releasing
Every pull request and every push to main runs ci.yml:
ruff check, and a check thatuv.lockmatchespyproject.toml- both test suites on Ubuntu and Windows, with Python 3.12 and 3.13
- a build of both packages, installed into a clean environment and used for real
To release, follow RELEASING.md, which has every command and what to do when a step fails. In short:
- Set the new version in three places:
versioninpyproject.toml,versioninpackages/gravity-schema/pyproject.toml, and thegravity-schema==pin inpyproject.toml. Runuv lock, then merge tomain. - Tag the merge commit and push the tag. A tag containing
rc, such asv0.2.0rc1, publishes to TestPyPI. Any otherv*tag, such asv0.2.0, publishes to PyPI and creates a GitHub Release.
release.yml runs the full CI first, refuses a tag that does not match all three versions, and publishes with PyPI trusted publishing, so no PyPI token is stored anywhere.
Each package is published from its own GitHub environment:
| Index | Project | Environment |
|---|---|---|
| pypi.org | gravity-cli |
pypi |
| pypi.org | gravity-schema |
pypi-schema |
| test.pypi.org | gravity-cli |
testpypi |
| test.pypi.org | gravity-schema |
testpypi-schema |
To install a release candidate, whose dependencies live on the real PyPI:
pip install --index-url https://test.pypi.org/simple/ --extra-index-url https://pypi.org/simple/ gravity-cli==0.2.0rc1
After changing catalog.yaml, regenerate the tool lists in the platform repository:
uv run python -m gravity_schema.gen_catalog --root <platform repo>/code-first
Documentation
| Document | What it covers |
|---|---|
| Builder guide | How to build a good agent, start to finish, with a complete example. Also gravity guide |
| Agent standard | The exact rules an agent must follow |
| Templates | The four templates and when to pick each |
manifest.schema.json |
The manifest as a machine-readable JSON Schema |
PyPI · Apache License 2.0 · Copyright 2026 AIGravity · See LICENSE and NOTICE
Metadata
Release files for gravity-cli 0.5.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| gravity_cli-0.5.2.tar.gz | 266.8 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gravity_cli-0.5.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 354.7 kB
Release files / gravity_cli-0.5.2.tar.gz
| Download URL | gravity_cli-0.5.2.tar.gz |
|---|---|
| Size | 266.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
5f4afc3108605c10ea8d2e13e299517715823a5d690332563653c8aefe3fe95a
|
|
BLAKE2b-256 checksum How to use checksums |
cda52a36be0e3dc46cfa15fa7a04765c1b6dc0e15133278c5f104df9ecb2d605
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.
Transparency logRelease files / gravity_cli-0.5.2-py3-none-any.whl
| Download URL | gravity_cli-0.5.2-py3-none-any.whl |
|---|---|
| Size | 88.0 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
b3e8cdb17f76d9e50199b03696d293809b1730e4eded7aa5ea5f2ca3842cc57b
|
|
BLAKE2b-256 checksum How to use checksums |
001ef8f820ca78d100083746563243ec10948276901b92ecdc9be3ba4dbc50e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.
Transparency logSecurity for gravity-cli 0.5.2
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for gravity_cli-0.5.2.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.