gravity-cli 0.5.6__tar.gz → 0.6.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.
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/AGENT_STANDARD.md +9 -7
- gravity_cli-0.5.6/README.md → gravity_cli-0.6.0/PKG-INFO +72 -6
- gravity_cli-0.5.6/PKG-INFO → gravity_cli-0.6.0/README.md +55 -23
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/RELEASING.md +4 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/TEMPLATES.md +1 -1
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/pyproject.toml +1 -1
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/pyproject.toml +2 -2
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/BUILDER_GUIDE.md +33 -12
- gravity_cli-0.6.0/src/gravity_cli/CHANGELOG.md +37 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/api.py +9 -1
- gravity_cli-0.6.0/src/gravity_cli/changelog.py +55 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/cli.py +66 -6
- gravity_cli-0.6.0/src/gravity_cli/commands/changelog.py +20 -0
- gravity_cli-0.6.0/src/gravity_cli/commands/docs.py +32 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/init.py +3 -1
- gravity_cli-0.6.0/src/gravity_cli/commands/update.py +38 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/config.py +23 -1
- gravity_cli-0.6.0/src/gravity_cli/docs/Gravity.agent.md +721 -0
- gravity_cli-0.6.0/src/gravity_cli/docs/problem-to-agent.md +933 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/ui.py +92 -15
- gravity_cli-0.6.0/src/gravity_cli/updater.py +206 -0
- gravity_cli-0.6.0/src/gravity_cli/workspace.py +152 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/starter/manifest.yaml +1 -1
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/starter/src/agent.py +19 -6
- gravity_cli-0.6.0/tests/conftest.py +16 -0
- gravity_cli-0.6.0/tests/test_agent_guide.py +129 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_api.py +28 -0
- gravity_cli-0.6.0/tests/test_changelog.py +64 -0
- gravity_cli-0.6.0/tests/test_updater.py +352 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_welcome.py +23 -1
- gravity_cli-0.6.0/tests/test_workspace.py +215 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/uv.lock +27 -27
- gravity_cli-0.5.6/tests/conftest.py +0 -8
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/.env.example +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/.github/workflows/ci.yml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/.github/workflows/release.yml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/.gitignore +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/LICENSE +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/NOTICE +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/LICENSE +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/NOTICE +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/README.md +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/__init__.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/__main__.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/catalog.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/catalog.yaml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/catalog_imported.yaml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/contract.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/cron.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/errors.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/export.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/gen_catalog.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/manifest.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/source_scan.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/gravity_schema/validation.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/schema/manifest.schema.json +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/conftest.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_catalog_parity.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_cron.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_errors.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_json_schema.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_kind_outputs.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_manifest.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_slots.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_source_scan.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/packages/gravity-schema/tests/test_validation.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/__init__.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/__main__.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/_runner_script.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/__init__.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/adapt.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/build.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/connect.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/dev.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/guide.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/login.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/push.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/run.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/runs.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/schedule.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/tools.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/commands/validate.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/dev.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/dev_page/index.html +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/localrun.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/manifest.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/project.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/chat/.gitignore +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/chat/manifest.yaml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/chat/requirements.txt +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/chat/src/agent.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/deepagent/.gitignore +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/deepagent/manifest.yaml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/deepagent/requirements.txt +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/deepagent/src/agent.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/minimal/.gitignore +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/minimal/manifest.yaml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/minimal/requirements.txt +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/minimal/src/agent.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/.gitignore +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/manifest.yaml +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/requirements.txt +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/src/agent.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/src/gateway.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/src/nodes/agent.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/src/prompts.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/templates/structured/src/state.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/src/gravity_cli/timeline.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/starter/requirements.txt +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/starter/src/summary.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/starter/tests/test_summary.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_adapt.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_agents.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_build_push.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_connect.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_dev.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_gateway_token.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_localrun.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_manifest_integration.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_run.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_runs.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_schedule.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_session.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_templates.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_timeline.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_tools.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_ui.py +0 -0
- {gravity_cli-0.5.6 → gravity_cli-0.6.0}/tests/test_validate.py +0 -0
|
@@ -25,7 +25,7 @@ my-agent/
|
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
**Only these are bundled.** `gravity build` zips exactly `manifest.yaml`,
|
|
28
|
-
`requirements.txt`, `requirements.lock`, and the contents of `src/` and
|
|
28
|
+
`requirements.txt`, `requirements.lock`, `tools_needed.yaml` (if present), and the contents of `src/` and
|
|
29
29
|
`tests/` (minus `__pycache__`). Anything else — `.venv/`, `build/`, notebooks,
|
|
30
30
|
a `data/` folder — is not shipped, and code that reads it will fail at runtime.
|
|
31
31
|
|
|
@@ -41,14 +41,14 @@ runtime:
|
|
|
41
41
|
framework: langgraph # only value accepted
|
|
42
42
|
python: "3.12" # the platform runs 3.12; validate refuses any other
|
|
43
43
|
entrypoint: src/agent.py:graph # file:object — see §3
|
|
44
|
-
timeout_seconds: 300 # 10–
|
|
44
|
+
timeout_seconds: 300 # 10–28800
|
|
45
45
|
|
|
46
46
|
dependencies:
|
|
47
47
|
lockfile: requirements.lock # leave as-is
|
|
48
48
|
|
|
49
49
|
inputs: # becomes the consumer's form; arrives as state["inputs"]
|
|
50
50
|
- name: query # the key in state["inputs"]
|
|
51
|
-
type: text # text | textarea | number | boolean | static-dropdown | email | url | date | file
|
|
51
|
+
type: text # text | textarea | number | boolean | static-dropdown | email | url | date | secret | file
|
|
52
52
|
label: "What should I do?" # what the consumer sees
|
|
53
53
|
required: true
|
|
54
54
|
# options: [a, b] # static-dropdown only
|
|
@@ -69,6 +69,7 @@ triggers: # optional — what starts a run
|
|
|
69
69
|
# schedule:
|
|
70
70
|
# cron: "0 9 * * 1" # 5 fields: digits, * / , - only (no @daily, no MON); every 5 minutes at the fastest; one day field at most; Asia/Kolkata
|
|
71
71
|
# consumer_can_change: true
|
|
72
|
+
# webhook: [github.push] # run on a GitHub push; the only source today
|
|
72
73
|
|
|
73
74
|
approvals: # optional — tools that pause the run for the consumer's yes
|
|
74
75
|
required_for: [] # each must also be in permissions.tools
|
|
@@ -127,9 +128,10 @@ runner is too old, not your agent.
|
|
|
127
128
|
|
|
128
129
|
**State.** The runtime invokes `graph.astream({"inputs": {...}}, stream_mode="updates")`
|
|
129
130
|
— the keyword is passed only when your `astream` accepts it, so a hand-rolled
|
|
130
|
-
object with a plain `astream(state)` works
|
|
131
|
-
safe habit). The
|
|
132
|
-
|
|
131
|
+
object with a plain `astream(state)` works in `gravity run` (accepting `**kwargs`
|
|
132
|
+
is the safe habit). The hosted platform needs a compiled LangGraph graph: it
|
|
133
|
+
injects its own checkpointer and store into it. The dict is the
|
|
134
|
+
consumer's form answers keyed by `inputs[].name`. Your state
|
|
133
135
|
therefore needs:
|
|
134
136
|
|
|
135
137
|
```python
|
|
@@ -259,7 +261,7 @@ leave their record in `.gravity/runs/`; one run is live per folder).
|
|
|
259
261
|
|
|
260
262
|
| Stage | Catches |
|
|
261
263
|
|---|---|
|
|
262
|
-
| `gravity validate` / `build` (local) | manifest schema · entrypoint file + object exist (static) · `requirements.txt` present · lockfile compiles for aarch64 · undeclared
|
|
264
|
+
| `gravity validate` / `build` (local) | manifest schema · entrypoint file + object exist (static) · `requirements.txt` present · lockfile compiles for aarch64 · undeclared tool names in source (an error for a write tool, a warning for a read tool) |
|
|
263
265
|
| `gravity push` (server) | manifest re-validated with the same rules · `permissions.tools` all exist in the catalog · version not already published |
|
|
264
266
|
| deploy | every pin in the lock installs on ARM64 |
|
|
265
267
|
| first run | module imports · `graph` resolves to something with `astream()` · state has `inputs` · final state has `result` |
|
|
@@ -1,3 +1,20 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: gravity-cli
|
|
3
|
+
Version: 0.6.0
|
|
4
|
+
Summary: gravity cli — init, run, and push python agents to the Gravity marketplace
|
|
5
|
+
License-Expression: Apache-2.0
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
License-File: NOTICE
|
|
8
|
+
Requires-Python: >=3.12
|
|
9
|
+
Requires-Dist: gravity-schema==0.6.0
|
|
10
|
+
Requires-Dist: httpx>=0.27.0
|
|
11
|
+
Requires-Dist: keyring>=25.0.0
|
|
12
|
+
Requires-Dist: python-dotenv>=1.0.0
|
|
13
|
+
Requires-Dist: rich>=13.9.0
|
|
14
|
+
Requires-Dist: typer>=0.15.0
|
|
15
|
+
Requires-Dist: uv>=0.8.0
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
1
18
|
<h1 align="center">Gravity CLI</h1>
|
|
2
19
|
|
|
3
20
|
<p align="center">
|
|
@@ -75,7 +92,7 @@ gravity init my-agent
|
|
|
75
92
|
|
|
76
93
|
## Declare it once, enforced on every call
|
|
77
94
|
|
|
78
|
-
There is no SDK to learn. Your agent is plain LangGraph that reads three environment variables. Everything Gravity enforces comes from `manifest.yaml`:
|
|
95
|
+
There is almost no SDK to learn. Your agent is plain LangGraph that reads three environment variables; the one Gravity package, `gravity`, is preinstalled on the platform and is only needed to load approval-gated tools. Everything Gravity enforces comes from `manifest.yaml`:
|
|
79
96
|
|
|
80
97
|
| You declare in `manifest.yaml` | What Gravity does with it |
|
|
81
98
|
|---|---|
|
|
@@ -91,11 +108,11 @@ The same rules apply on your laptop and when the agent runs hosted, because both
|
|
|
91
108
|
|
|
92
109
|
| | |
|
|
93
110
|
|---|---|
|
|
94
|
-
| Commands |
|
|
111
|
+
| Commands | 21, grouped into build an agent, publish, your agents, account, help and Gravity CLI |
|
|
95
112
|
| Starter templates | 4: `minimal`, `structured`, `deepagent`, `chat` |
|
|
96
|
-
| Tool catalog | 1,
|
|
97
|
-
| Test suite |
|
|
98
|
-
| Runtime contract | 3 environment variables
|
|
113
|
+
| Tool catalog | 1,405 tools across 12 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn, Jira, Confluence, Notion, WhatsApp), plus web search, Firecrawl and two built-ins; 31 of them curated |
|
|
114
|
+
| Test suite | 543 tests (285 CLI, 258 schema), run on Ubuntu and Windows with Python 3.12 and 3.13 |
|
|
115
|
+
| Runtime contract | 3 environment variables; an optional `import gravity` only for approval-gated tools |
|
|
99
116
|
|
|
100
117
|
## Five things to know before you start
|
|
101
118
|
|
|
@@ -194,6 +211,9 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
194
211
|
| | [`gravity connect`](#gravity-connect) | Connect an account your agents act on (Gmail, Slack...) |
|
|
195
212
|
| **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
|
|
196
213
|
| | [`gravity help`](#gravity-help) | Show the list of commands |
|
|
214
|
+
| **Gravity CLI** | [`gravity docs`](#gravity-docs) | Write `Gravity.agent.md` for the coding agent in this folder |
|
|
215
|
+
| | [`gravity update`](#gravity-update) | Update to the latest release and see what is new |
|
|
216
|
+
| | [`gravity changelog`](#gravity-changelog) | What changed in each release |
|
|
197
217
|
|
|
198
218
|
`gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json`; it must start with `http://` or `https://`. The flag applies to the command it is given with; on later commands a `GRAVITY_API_URL` in `~/.gravity/.env` or the shell still wins over `config.json`, and the CLI says so when that is the case. `gravity --version` (or `-V`) prints the installed version. Wherever a command takes `[path]`, it is the agent's folder and defaults to the current one.
|
|
199
219
|
|
|
@@ -412,7 +432,7 @@ gravity logs <run_id>
|
|
|
412
432
|
|
|
413
433
|
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.
|
|
414
434
|
|
|
415
|
-
|
|
435
|
+
31 tools are curated, such as `gmail.read` and `web.search`. The other ~1,370 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.
|
|
416
436
|
|
|
417
437
|
```
|
|
418
438
|
gravity tools [search]
|
|
@@ -485,6 +505,46 @@ gravity guide --save
|
|
|
485
505
|
gravity guide | less
|
|
486
506
|
```
|
|
487
507
|
|
|
508
|
+
### `gravity docs`
|
|
509
|
+
|
|
510
|
+
Write or refresh the two files a coding agent needs to build for Gravity in this folder: `Gravity.agent.md` (how Gravity runs agents, the manifest, every tool this release knows, the output formats, the rules that fail a load) and `problem-to-agent.md` (the method for turning a problem statement into an agent that holds up).
|
|
511
|
+
|
|
512
|
+
```
|
|
513
|
+
gravity docs [--force]
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
| Option | Description |
|
|
517
|
+
|---|---|
|
|
518
|
+
| `--force` | Replace the files even where you edited them, or where a file Gravity did not write has the same name |
|
|
519
|
+
|
|
520
|
+
You rarely need this. Every command writes the two files into the folder it runs in if they are missing (never into your home folder or a drive root; `gravity init` writes them into the new agent), and rewrites them when a newer release changes them. The first line of a file Gravity wrote names the release and a hash of the text after it: if you edit the file the hash no longer matches and Gravity leaves it alone. Your own `agent.md`, `AGENTS.md` and `CLAUDE.md` are never touched. Point your coding agent at `Gravity.agent.md` from them if it does not look there by itself. Set `GRAVITY_AGENT_DOCS=0` to stop the automatic writes.
|
|
521
|
+
|
|
522
|
+
### `gravity update`
|
|
523
|
+
|
|
524
|
+
Check PyPI for a newer release, install it, and show what is new.
|
|
525
|
+
|
|
526
|
+
```
|
|
527
|
+
gravity update [--check]
|
|
528
|
+
```
|
|
529
|
+
|
|
530
|
+
| Option | Description |
|
|
531
|
+
|---|---|
|
|
532
|
+
| `--check` | Only say whether a newer release exists |
|
|
533
|
+
|
|
534
|
+
You do not have to run it: once every six hours a command looks for a newer release, and when there is one it says `Update available 0.6.0 → 0.6.1`, updates, and runs your command again as the new version, which lists what changed. It upgrades the way you installed it (`uv tool` or `pipx`); a copy installed any other way, or from a source checkout, is told the command to run and left alone. Nothing here can stop a command: a failed update is one warning and the command runs on the version you have. Set `GRAVITY_AUTO_UPDATE=0` to turn the check and the install off; CI never updates.
|
|
535
|
+
|
|
536
|
+
### `gravity changelog`
|
|
537
|
+
|
|
538
|
+
What changed in each release, written for builders.
|
|
539
|
+
|
|
540
|
+
```
|
|
541
|
+
gravity changelog [--all]
|
|
542
|
+
```
|
|
543
|
+
|
|
544
|
+
| Option | Description |
|
|
545
|
+
|---|---|
|
|
546
|
+
| `--all` | Every release, not only the one you are on |
|
|
547
|
+
|
|
488
548
|
### `gravity help`
|
|
489
549
|
|
|
490
550
|
Show the list of commands. The same as `gravity -h`.
|
|
@@ -540,6 +600,9 @@ Set `GRAVITY_API_URL` and `GRAVITY_GATEWAY_URL` only to work against services yo
|
|
|
540
600
|
| `GRAVITY_API_URL` | The control plane's address | `https://agents.dev.gravity.fast` |
|
|
541
601
|
| `GRAVITY_API_TOKEN` | A control-plane dev token, used instead of `gravity login`. For development only. | none |
|
|
542
602
|
| `GRAVITY_CONFIG_DIR` | Moves `~/.gravity` elsewhere. Useful for tests and CI. | `~/.gravity` |
|
|
603
|
+
| `GRAVITY_AUTO_UPDATE` | When `0`, commands neither check for a newer release nor install one. Always off when `CI` is set. | `1` |
|
|
604
|
+
| `GRAVITY_AGENT_DOCS` | When `0`, commands do not write `Gravity.agent.md` and `problem-to-agent.md` into the folder they run in. `gravity docs` still does. | `1` |
|
|
605
|
+
| `GRAVITY_UPDATE_URL` | Where the latest version is read from, a JSON document with `info.version`. For tests and mirrors. | PyPI |
|
|
543
606
|
| `GRAVITY_UNATTENDED` | When `1`, calls that need approval are rejected without asking. `gravity schedule` sets it for you. | unset |
|
|
544
607
|
|
|
545
608
|
### What your agent receives
|
|
@@ -775,6 +838,7 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
775
838
|
|
|
776
839
|
## What's new
|
|
777
840
|
|
|
841
|
+
- **0.6.0** Gravity CLI keeps itself up to date and says what changed (`gravity update`, `gravity changelog`). `Gravity.agent.md` and `problem-to-agent.md` appear in the folder you work in, for your coding agent (`gravity docs`). `gravity` on its own shows your version, the agent in the folder, whether you are signed in, and any update. The notes shown after an update are `src/gravity_cli/CHANGELOG.md`.
|
|
778
842
|
- **0.5.5** `manifest.yaml` takes an optional `display:` hint for how a run's result shows: `view` (document, report, table, chart, code or files), `field` (the result key to show) and `chart` (`type: line` or `bar`, `x`, and up to three `y` fields). It is a hint, never a rule: an unknown view or key is ignored, so it never fails a push. 0.5.4 and earlier refuse a manifest that has it.
|
|
779
843
|
- **0.5.4** `gravity agents` shows each agent's review: a REVIEW column (not submitted, submitted, approved, rejected), and under "From the Gravity team" the reviewer's notes and what to do next. An agent the Gravity team turned off shows as `off` with the reason. UPDATED reads "3 min ago" instead of a timestamp, and status and review are coloured. Also, from a full audit of every command: `gravity --version`. Clear messages instead of tracebacks for: a proxy or sign-in page answering instead of Gravity, a refused `GRAVITY_API_TOKEN` or `GRAVITY_DEV_TOKEN`, an expired sign-in code, a non-UTF-8 manifest, an out-of-range `--port`, a missing agent folder, a malformed session file. `gravity validate` now refuses what the build would refuse: a `graph` that is not an async factory, `os.environ` or `asyncio.run` at import, relative imports, unquoted `python: 3.12`, schedules faster than every 5 minutes or with both day fields, out-of-range cron fields; it accepts a re-exported `graph`. `gravity agents` shows the review status and failure reasons; `gravity runs` never shortens an id; `gravity logs` never wraps. `gravity push` refuses a bundle over 30 MB or a manifest over 12 KB before uploading, and says why when a failed version is re-pushed. `gravity build` ships `tools_needed.yaml`, leaves dotfiles out, and refuses to build without `requirements.lock`. `gravity adapt` leaves the folder untouched when a model call fails. `gravity schedule` checks inputs before the first tick and runs on Asia/Kolkata like the hosted schedules. `gravity run` deletes `.gravity/payload.json` after the run, warns when no `.venv` is present, and keeps a plain agent's `result`. `gravity connect <app>` refuses on the shared test account instead of attaching your account to it. The builder guide and agent standard describe the real flow (init, validate, run or dev, build, push, review on /app).
|
|
780
844
|
- **0.5.3** `gravity dev` opens a local dashboard for your agent: a form from your manifest, a live timeline, approvals, the result and run history; runs from `gravity run` in the same folder show up in it live, and the other way round. WhatsApp joins the catalog (51 tools; 1,402 across 15 apps including the curated and built-in ones). Agents can return generated images: put them in a corpus section's `images` and they land in the Word, PowerPoint and Markdown outputs. The agent standard now shows how to generate images and embeddings through the gateway. `NEEDS_RECONNECT` on the shared test account tells you to ask Gravity, not to connect your own account.
|
|
@@ -858,6 +922,8 @@ uv run python -m gravity_schema.gen_catalog --root <platform repo>/code-first
|
|
|
858
922
|
| Document | What it covers |
|
|
859
923
|
|---|---|
|
|
860
924
|
| [Builder guide](src/gravity_cli/BUILDER_GUIDE.md) | How to build a good agent, start to finish, with a complete example. Also `gravity guide` |
|
|
925
|
+
| [`Gravity.agent.md`](src/gravity_cli/docs/Gravity.agent.md) | The guide `gravity docs` writes for coding agents; its tool list is filled in from the catalog of the release that writes it |
|
|
926
|
+
| [`problem-to-agent.md`](src/gravity_cli/docs/problem-to-agent.md) | The method for turning a problem statement into an agent that holds up |
|
|
861
927
|
| [Agent standard](AGENT_STANDARD.md) | The exact rules an agent must follow |
|
|
862
928
|
| [Templates](TEMPLATES.md) | The four templates and when to pick each |
|
|
863
929
|
| [`manifest.schema.json`](packages/gravity-schema/schema/manifest.schema.json) | The manifest as a machine-readable JSON Schema |
|
|
@@ -1,20 +1,3 @@
|
|
|
1
|
-
Metadata-Version: 2.5
|
|
2
|
-
Name: gravity-cli
|
|
3
|
-
Version: 0.5.6
|
|
4
|
-
Summary: gravity cli — init, run, and push python agents to the Gravity marketplace
|
|
5
|
-
License-Expression: Apache-2.0
|
|
6
|
-
License-File: LICENSE
|
|
7
|
-
License-File: NOTICE
|
|
8
|
-
Requires-Python: >=3.12
|
|
9
|
-
Requires-Dist: gravity-schema==0.5.6
|
|
10
|
-
Requires-Dist: httpx>=0.27.0
|
|
11
|
-
Requires-Dist: keyring>=25.0.0
|
|
12
|
-
Requires-Dist: python-dotenv>=1.0.0
|
|
13
|
-
Requires-Dist: rich>=13.9.0
|
|
14
|
-
Requires-Dist: typer>=0.15.0
|
|
15
|
-
Requires-Dist: uv>=0.8.0
|
|
16
|
-
Description-Content-Type: text/markdown
|
|
17
|
-
|
|
18
1
|
<h1 align="center">Gravity CLI</h1>
|
|
19
2
|
|
|
20
3
|
<p align="center">
|
|
@@ -92,7 +75,7 @@ gravity init my-agent
|
|
|
92
75
|
|
|
93
76
|
## Declare it once, enforced on every call
|
|
94
77
|
|
|
95
|
-
There is no SDK to learn. Your agent is plain LangGraph that reads three environment variables. Everything Gravity enforces comes from `manifest.yaml`:
|
|
78
|
+
There is almost no SDK to learn. Your agent is plain LangGraph that reads three environment variables; the one Gravity package, `gravity`, is preinstalled on the platform and is only needed to load approval-gated tools. Everything Gravity enforces comes from `manifest.yaml`:
|
|
96
79
|
|
|
97
80
|
| You declare in `manifest.yaml` | What Gravity does with it |
|
|
98
81
|
|---|---|
|
|
@@ -108,11 +91,11 @@ The same rules apply on your laptop and when the agent runs hosted, because both
|
|
|
108
91
|
|
|
109
92
|
| | |
|
|
110
93
|
|---|---|
|
|
111
|
-
| Commands |
|
|
94
|
+
| Commands | 21, grouped into build an agent, publish, your agents, account, help and Gravity CLI |
|
|
112
95
|
| Starter templates | 4: `minimal`, `structured`, `deepagent`, `chat` |
|
|
113
|
-
| Tool catalog | 1,
|
|
114
|
-
| Test suite |
|
|
115
|
-
| Runtime contract | 3 environment variables
|
|
96
|
+
| Tool catalog | 1,405 tools across 12 apps (Gmail, Slack, Linear, GitHub, Calendar, Drive, Sheets, LinkedIn, Jira, Confluence, Notion, WhatsApp), plus web search, Firecrawl and two built-ins; 31 of them curated |
|
|
97
|
+
| Test suite | 543 tests (285 CLI, 258 schema), run on Ubuntu and Windows with Python 3.12 and 3.13 |
|
|
98
|
+
| Runtime contract | 3 environment variables; an optional `import gravity` only for approval-gated tools |
|
|
116
99
|
|
|
117
100
|
## Five things to know before you start
|
|
118
101
|
|
|
@@ -211,6 +194,9 @@ Run `gravity help` for this list in the terminal, and add `-h` to any command fo
|
|
|
211
194
|
| | [`gravity connect`](#gravity-connect) | Connect an account your agents act on (Gmail, Slack...) |
|
|
212
195
|
| **Help** | [`gravity guide`](#gravity-guide) | Read the builder guide: how to build a good agent |
|
|
213
196
|
| | [`gravity help`](#gravity-help) | Show the list of commands |
|
|
197
|
+
| **Gravity CLI** | [`gravity docs`](#gravity-docs) | Write `Gravity.agent.md` for the coding agent in this folder |
|
|
198
|
+
| | [`gravity update`](#gravity-update) | Update to the latest release and see what is new |
|
|
199
|
+
| | [`gravity changelog`](#gravity-changelog) | What changed in each release |
|
|
214
200
|
|
|
215
201
|
`gravity --api-url URL <command>` saves a different control-plane address to `~/.gravity/config.json`; it must start with `http://` or `https://`. The flag applies to the command it is given with; on later commands a `GRAVITY_API_URL` in `~/.gravity/.env` or the shell still wins over `config.json`, and the CLI says so when that is the case. `gravity --version` (or `-V`) prints the installed version. Wherever a command takes `[path]`, it is the agent's folder and defaults to the current one.
|
|
216
202
|
|
|
@@ -429,7 +415,7 @@ gravity logs <run_id>
|
|
|
429
415
|
|
|
430
416
|
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.
|
|
431
417
|
|
|
432
|
-
|
|
418
|
+
31 tools are curated, such as `gmail.read` and `web.search`. The other ~1,370 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.
|
|
433
419
|
|
|
434
420
|
```
|
|
435
421
|
gravity tools [search]
|
|
@@ -502,6 +488,46 @@ gravity guide --save
|
|
|
502
488
|
gravity guide | less
|
|
503
489
|
```
|
|
504
490
|
|
|
491
|
+
### `gravity docs`
|
|
492
|
+
|
|
493
|
+
Write or refresh the two files a coding agent needs to build for Gravity in this folder: `Gravity.agent.md` (how Gravity runs agents, the manifest, every tool this release knows, the output formats, the rules that fail a load) and `problem-to-agent.md` (the method for turning a problem statement into an agent that holds up).
|
|
494
|
+
|
|
495
|
+
```
|
|
496
|
+
gravity docs [--force]
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
| Option | Description |
|
|
500
|
+
|---|---|
|
|
501
|
+
| `--force` | Replace the files even where you edited them, or where a file Gravity did not write has the same name |
|
|
502
|
+
|
|
503
|
+
You rarely need this. Every command writes the two files into the folder it runs in if they are missing (never into your home folder or a drive root; `gravity init` writes them into the new agent), and rewrites them when a newer release changes them. The first line of a file Gravity wrote names the release and a hash of the text after it: if you edit the file the hash no longer matches and Gravity leaves it alone. Your own `agent.md`, `AGENTS.md` and `CLAUDE.md` are never touched. Point your coding agent at `Gravity.agent.md` from them if it does not look there by itself. Set `GRAVITY_AGENT_DOCS=0` to stop the automatic writes.
|
|
504
|
+
|
|
505
|
+
### `gravity update`
|
|
506
|
+
|
|
507
|
+
Check PyPI for a newer release, install it, and show what is new.
|
|
508
|
+
|
|
509
|
+
```
|
|
510
|
+
gravity update [--check]
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
| Option | Description |
|
|
514
|
+
|---|---|
|
|
515
|
+
| `--check` | Only say whether a newer release exists |
|
|
516
|
+
|
|
517
|
+
You do not have to run it: once every six hours a command looks for a newer release, and when there is one it says `Update available 0.6.0 → 0.6.1`, updates, and runs your command again as the new version, which lists what changed. It upgrades the way you installed it (`uv tool` or `pipx`); a copy installed any other way, or from a source checkout, is told the command to run and left alone. Nothing here can stop a command: a failed update is one warning and the command runs on the version you have. Set `GRAVITY_AUTO_UPDATE=0` to turn the check and the install off; CI never updates.
|
|
518
|
+
|
|
519
|
+
### `gravity changelog`
|
|
520
|
+
|
|
521
|
+
What changed in each release, written for builders.
|
|
522
|
+
|
|
523
|
+
```
|
|
524
|
+
gravity changelog [--all]
|
|
525
|
+
```
|
|
526
|
+
|
|
527
|
+
| Option | Description |
|
|
528
|
+
|---|---|
|
|
529
|
+
| `--all` | Every release, not only the one you are on |
|
|
530
|
+
|
|
505
531
|
### `gravity help`
|
|
506
532
|
|
|
507
533
|
Show the list of commands. The same as `gravity -h`.
|
|
@@ -557,6 +583,9 @@ Set `GRAVITY_API_URL` and `GRAVITY_GATEWAY_URL` only to work against services yo
|
|
|
557
583
|
| `GRAVITY_API_URL` | The control plane's address | `https://agents.dev.gravity.fast` |
|
|
558
584
|
| `GRAVITY_API_TOKEN` | A control-plane dev token, used instead of `gravity login`. For development only. | none |
|
|
559
585
|
| `GRAVITY_CONFIG_DIR` | Moves `~/.gravity` elsewhere. Useful for tests and CI. | `~/.gravity` |
|
|
586
|
+
| `GRAVITY_AUTO_UPDATE` | When `0`, commands neither check for a newer release nor install one. Always off when `CI` is set. | `1` |
|
|
587
|
+
| `GRAVITY_AGENT_DOCS` | When `0`, commands do not write `Gravity.agent.md` and `problem-to-agent.md` into the folder they run in. `gravity docs` still does. | `1` |
|
|
588
|
+
| `GRAVITY_UPDATE_URL` | Where the latest version is read from, a JSON document with `info.version`. For tests and mirrors. | PyPI |
|
|
560
589
|
| `GRAVITY_UNATTENDED` | When `1`, calls that need approval are rejected without asking. `gravity schedule` sets it for you. | unset |
|
|
561
590
|
|
|
562
591
|
### What your agent receives
|
|
@@ -792,6 +821,7 @@ Real tools act on a connected account. Connect one with `gravity-gateway connect
|
|
|
792
821
|
|
|
793
822
|
## What's new
|
|
794
823
|
|
|
824
|
+
- **0.6.0** Gravity CLI keeps itself up to date and says what changed (`gravity update`, `gravity changelog`). `Gravity.agent.md` and `problem-to-agent.md` appear in the folder you work in, for your coding agent (`gravity docs`). `gravity` on its own shows your version, the agent in the folder, whether you are signed in, and any update. The notes shown after an update are `src/gravity_cli/CHANGELOG.md`.
|
|
795
825
|
- **0.5.5** `manifest.yaml` takes an optional `display:` hint for how a run's result shows: `view` (document, report, table, chart, code or files), `field` (the result key to show) and `chart` (`type: line` or `bar`, `x`, and up to three `y` fields). It is a hint, never a rule: an unknown view or key is ignored, so it never fails a push. 0.5.4 and earlier refuse a manifest that has it.
|
|
796
826
|
- **0.5.4** `gravity agents` shows each agent's review: a REVIEW column (not submitted, submitted, approved, rejected), and under "From the Gravity team" the reviewer's notes and what to do next. An agent the Gravity team turned off shows as `off` with the reason. UPDATED reads "3 min ago" instead of a timestamp, and status and review are coloured. Also, from a full audit of every command: `gravity --version`. Clear messages instead of tracebacks for: a proxy or sign-in page answering instead of Gravity, a refused `GRAVITY_API_TOKEN` or `GRAVITY_DEV_TOKEN`, an expired sign-in code, a non-UTF-8 manifest, an out-of-range `--port`, a missing agent folder, a malformed session file. `gravity validate` now refuses what the build would refuse: a `graph` that is not an async factory, `os.environ` or `asyncio.run` at import, relative imports, unquoted `python: 3.12`, schedules faster than every 5 minutes or with both day fields, out-of-range cron fields; it accepts a re-exported `graph`. `gravity agents` shows the review status and failure reasons; `gravity runs` never shortens an id; `gravity logs` never wraps. `gravity push` refuses a bundle over 30 MB or a manifest over 12 KB before uploading, and says why when a failed version is re-pushed. `gravity build` ships `tools_needed.yaml`, leaves dotfiles out, and refuses to build without `requirements.lock`. `gravity adapt` leaves the folder untouched when a model call fails. `gravity schedule` checks inputs before the first tick and runs on Asia/Kolkata like the hosted schedules. `gravity run` deletes `.gravity/payload.json` after the run, warns when no `.venv` is present, and keeps a plain agent's `result`. `gravity connect <app>` refuses on the shared test account instead of attaching your account to it. The builder guide and agent standard describe the real flow (init, validate, run or dev, build, push, review on /app).
|
|
797
827
|
- **0.5.3** `gravity dev` opens a local dashboard for your agent: a form from your manifest, a live timeline, approvals, the result and run history; runs from `gravity run` in the same folder show up in it live, and the other way round. WhatsApp joins the catalog (51 tools; 1,402 across 15 apps including the curated and built-in ones). Agents can return generated images: put them in a corpus section's `images` and they land in the Word, PowerPoint and Markdown outputs. The agent standard now shows how to generate images and embeddings through the gateway. `NEEDS_RECONNECT` on the shared test account tells you to ask Gravity, not to connect your own account.
|
|
@@ -875,6 +905,8 @@ uv run python -m gravity_schema.gen_catalog --root <platform repo>/code-first
|
|
|
875
905
|
| Document | What it covers |
|
|
876
906
|
|---|---|
|
|
877
907
|
| [Builder guide](src/gravity_cli/BUILDER_GUIDE.md) | How to build a good agent, start to finish, with a complete example. Also `gravity guide` |
|
|
908
|
+
| [`Gravity.agent.md`](src/gravity_cli/docs/Gravity.agent.md) | The guide `gravity docs` writes for coding agents; its tool list is filled in from the catalog of the release that writes it |
|
|
909
|
+
| [`problem-to-agent.md`](src/gravity_cli/docs/problem-to-agent.md) | The method for turning a problem statement into an agent that holds up |
|
|
878
910
|
| [Agent standard](AGENT_STANDARD.md) | The exact rules an agent must follow |
|
|
879
911
|
| [Templates](TEMPLATES.md) | The four templates and when to pick each |
|
|
880
912
|
| [`manifest.schema.json`](packages/gravity-schema/schema/manifest.schema.json) | The manifest as a machine-readable JSON Schema |
|
|
@@ -35,6 +35,8 @@ Three places must carry the same number:
|
|
|
35
35
|
| `pyproject.toml` | `"gravity-schema==X.Y.Z"` under `dependencies` |
|
|
36
36
|
| `packages/gravity-schema/pyproject.toml` | `version = "X.Y.Z"` |
|
|
37
37
|
|
|
38
|
+
Add the release to `src/gravity_cli/CHANGELOG.md`: a `## X.Y.Z - date` heading and one `- new|improved|fixed: Headline. Detail.` line per change a builder would care about. It is what `gravity update` shows after an update, and `tests/test_changelog.py` fails a version that has no entry. Set the date to the day you tag.
|
|
39
|
+
|
|
38
40
|
Then refresh the lockfile:
|
|
39
41
|
|
|
40
42
|
```bash
|
|
@@ -116,6 +118,8 @@ gh run rerun <run-id> --failed
|
|
|
116
118
|
|
|
117
119
|
## Rules
|
|
118
120
|
|
|
121
|
+
- **A release reaches every installed CLI within about six hours.** Each command checks PyPI for a newer version and updates itself (`src/gravity_cli/updater.py`), so a bad release is out of your hands quickly: ship the fix as the next version, and yank on PyPI only if it is harmful (a yanked release is no longer offered). Verify step 6 before you walk away.
|
|
122
|
+
|
|
119
123
|
- No copies of the source in other repositories. Pin the PyPI version in your requirements instead.
|
|
120
124
|
- No PyPI API tokens. The trusted publisher configured for this repository is the only credential. If you hold a token for either package, say so and it gets revoked.
|
|
121
125
|
- Every change is a pull request, however small. Review turnaround is same day.
|
|
@@ -169,5 +169,5 @@ src/agent.py exports `graph`
|
|
|
169
169
|
.gitignore .venv and caches. requirements.lock is NOT ignored: it ships.
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
Note: templates declare `python: "3.
|
|
172
|
+
Note: templates declare `python: "3.12"`, the version the platform runs.
|
|
173
173
|
Set `runtime.python: "3.12"` in the manifest before `gravity push`.
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "gravity-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.6.0"
|
|
4
4
|
description = "gravity cli — init, run, and push python agents to the Gravity marketplace"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
license = "Apache-2.0"
|
|
7
7
|
license-files = ["LICENSE", "NOTICE"]
|
|
8
8
|
requires-python = ">=3.12"
|
|
9
9
|
dependencies = [
|
|
10
|
-
"gravity-schema==0.
|
|
10
|
+
"gravity-schema==0.6.0", # released in lockstep: always the same version as this package
|
|
11
11
|
"typer>=0.15.0",
|
|
12
12
|
"httpx>=0.27.0",
|
|
13
13
|
"rich>=13.9.0",
|
|
@@ -7,7 +7,8 @@ and a sign-in (`gravity login`, with your Google account).
|
|
|
7
7
|
|
|
8
8
|
You write an ordinary Python agent with LangGraph and declare in `manifest.yaml` what it may
|
|
9
9
|
touch. When we run it, Gravity gives it tools (Gmail, Linear, Slack…) and models through a
|
|
10
|
-
gateway, and only the tools you declared.
|
|
10
|
+
gateway, and only the tools you declared. The one Gravity package you may touch is `gravity`, which the
|
|
11
|
+
platform preinstalls and which you need only to call approval-gated tools (§6.1).
|
|
11
12
|
|
|
12
13
|
**Contents:** 1 how it works · 2 the folder · 3 the files (copy these) · 4 rules your code must
|
|
13
14
|
follow · 5 output: formatting, formats and the gold standard · 6 add-ons: approvals, platform
|
|
@@ -26,7 +27,7 @@ you (gravity CLI) Gravity (on our machines)
|
|
|
26
27
|
───────────────── ──────────────────────────
|
|
27
28
|
gravity init, change the files (§3)
|
|
28
29
|
gravity validate the same checks the build runs
|
|
29
|
-
gravity run / gravity dev ──► our real gateway and models, on the
|
|
30
|
+
gravity run / gravity dev ──► our real gateway and models, on the accounts you connected
|
|
30
31
|
gravity build, gravity push ──► validate, scan, build the image, start a runtime
|
|
31
32
|
tell us it's ready we review it and list it on /app for users (§9, §10)
|
|
32
33
|
```
|
|
@@ -160,6 +161,11 @@ from langgraph.graph import END, StateGraph
|
|
|
160
161
|
from pydantic import BaseModel, Field
|
|
161
162
|
from summary import count_by_sender # absolute import from src/
|
|
162
163
|
|
|
164
|
+
try:
|
|
165
|
+
import gravity # preinstalled on the platform, not on PyPI; absent in gravity run and in tests
|
|
166
|
+
except ImportError:
|
|
167
|
+
gravity = None
|
|
168
|
+
|
|
163
169
|
|
|
164
170
|
class Digest(BaseModel):
|
|
165
171
|
headline: str = Field(description="the day's email in one sentence, from the given facts only")
|
|
@@ -189,16 +195,24 @@ def _payload(raw: Any) -> Any:
|
|
|
189
195
|
return raw.get("data", raw) if isinstance(raw, dict) else raw
|
|
190
196
|
|
|
191
197
|
|
|
192
|
-
async def
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
198
|
+
async def load_tools() -> dict:
|
|
199
|
+
"""On the platform a tool under approvals.required_for must come from gravity.tools(): it pauses the
|
|
200
|
+
run until a person decides. Anywhere else the MCP client serves the same tools under the same names."""
|
|
201
|
+
if gravity:
|
|
202
|
+
return {t.name: t for t in await gravity.tools()}
|
|
196
203
|
client = MultiServerMCPClient({"gravity": {
|
|
197
204
|
"transport": "streamable_http",
|
|
198
205
|
"url": os.environ["GRAVITY_GATEWAY_URL"],
|
|
199
|
-
"headers": {"Authorization": f"Bearer {
|
|
206
|
+
"headers": {"Authorization": f"Bearer {os.environ['GRAVITY_RUN_TOKEN']}"},
|
|
200
207
|
}})
|
|
201
|
-
|
|
208
|
+
return {t.name: t for t in await client.get_tools()}
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
async def graph():
|
|
212
|
+
# Read the three environment variables here, inside the function, never at import time.
|
|
213
|
+
token = os.environ["GRAVITY_RUN_TOKEN"]
|
|
214
|
+
model = ChatOpenAI(model="openai/gpt-6-luna", base_url=os.environ["GRAVITY_LLM_URL"], api_key=token)
|
|
215
|
+
tools = await load_tools() # only the tools in manifest.yaml
|
|
202
216
|
|
|
203
217
|
async def read(state: State):
|
|
204
218
|
# Tool names use _ instead of . : gmail.read -> tools["gmail_read"]
|
|
@@ -285,7 +299,7 @@ If any of these is broken, the agent fails when we load or run it.
|
|
|
285
299
|
| Every tool you call is in the manifest | `gmail.send` listed under `permissions.tools` | calling a tool you didn't list |
|
|
286
300
|
| Accounts are reached only through tools, models only through `GRAVITY_LLM_URL` | `tools["gmail_read"]`, `ChatOpenAI(base_url=...)` | calling Gmail's API with your own credentials, your own API keys, `OPENAI_API_KEY` |
|
|
287
301
|
| The final state sets `result` | `return {"result": {...}}` | a last node that returns nothing |
|
|
288
|
-
|
|
|
302
|
+
| Gravity imports are optional | `import gravity` behind `try/except ImportError`, only to load approval-gated tools (§6.1) | listing `gravity` in `requirements.txt` (it is not on PyPI), or importing it unguarded |
|
|
289
303
|
| Only declare `messages` in the state if the agent is a conversation | see §6.3 | adding it "just in case" (the agent is then run as a chat) |
|
|
290
304
|
|
|
291
305
|
## 5. Output: formatting, formats and the gold standard
|
|
@@ -438,6 +452,13 @@ the agent, that call is held until a human answers. If it isn't approved, the to
|
|
|
438
452
|
containing `APPROVAL_REJECTED` or `APPROVAL_TIMEOUT` and does nothing, so **always have a
|
|
439
453
|
fallback** (the example keeps a draft). A tool you gate must also be in `permissions.tools`.
|
|
440
454
|
|
|
455
|
+
On the platform the run **pauses** for the decision (up to 7 days) instead of holding a connection, and
|
|
456
|
+
that only works for tools loaded with `await gravity.tools()`: a gated call made through a plain MCP client
|
|
457
|
+
fails there with `APPROVAL_REQUIRED`. The example's `load_tools()` does this, and falls back to the MCP client
|
|
458
|
+
when `gravity` is not installed (`gravity run`, `gravity dev`, tests). After the decision the node that made
|
|
459
|
+
the call runs again from its start, so keep the gated call last in its node. If you wrap the call in a broad
|
|
460
|
+
`except`, re-raise `langgraph.errors.GraphBubbleUp` first (`except GraphBubbleUp: raise`), or the pause is swallowed.
|
|
461
|
+
|
|
441
462
|
### 6.2 Platform adapters: let the user choose where results go
|
|
442
463
|
|
|
443
464
|
For agents whose output could go to different platforms (Gmail or Slack, WordPress or Webflow),
|
|
@@ -615,7 +636,7 @@ return {"result": result, "memory": {"seen": sorted(seen | delivered)[-1000:]}}
|
|
|
615
636
|
|
|
616
637
|
Your agent can be for any field: sales, marketing, finance, support, engineering, operations,
|
|
617
638
|
anything. **The table below is today's starting set, not the limit.** If your agent needs a tool
|
|
618
|
-
that isn't here, run `gravity tools <app>` first: the gateway serves
|
|
639
|
+
that isn't here, run `gravity tools <app>` first: the gateway serves more than 1,300 more tools for the
|
|
619
640
|
same apps. If it still isn't there, ask for it as in §7.1.
|
|
620
641
|
|
|
621
642
|
| App | Tools |
|
|
@@ -737,13 +758,13 @@ see, locally or hosted:
|
|
|
737
758
|
| Message | It means | Fix |
|
|
738
759
|
|---|---|---|
|
|
739
760
|
| `TOOL_NOT_DECLARED` | Your code called a tool that isn't in the manifest | Add it to `permissions.tools` (or the adapter's list), or stop calling it |
|
|
740
|
-
| `NEEDS_RECONNECT` |
|
|
761
|
+
| `NEEDS_RECONNECT` | The account for that app isn't connected, or its sign-in lapsed | Locally: `gravity connect <app>` connects your own account (a `gravity login` run uses the accounts you connected). Only if you run with `GRAVITY_DEV_TOKEN` are you on the shared test account, where `gravity connect` refuses on purpose and you tell us to reconnect it. Hosted, the person running the agent connects it |
|
|
741
762
|
| `TOOL_NOT_FOUND` | A tool in `manifest.yaml` isn't in the catalog | Check the spelling with `gravity tools`. For a new one, ask for it (§7.1) |
|
|
742
763
|
| `APPROVAL_REQUIRED` | An imported write tool has no approval | List it under `approvals.required_for`, with a fallback (§6.1) |
|
|
743
764
|
| `UNDECLARED_TOOL_CALL` | Your code names a write tool the manifest doesn't grant | Declare it (and gate it), or remove the call |
|
|
744
765
|
| `TOOL_UNAVAILABLE` | That tool isn't available yet | Check the spelling against §7, or wait until we've added it (§7.1) |
|
|
745
766
|
| `APPROVAL_REJECTED` / `APPROVAL_TIMEOUT` | A gated call wasn't approved | Expected; your fallback should have run |
|
|
746
|
-
| "
|
|
767
|
+
| "finished without a result" (`gravity run`), or a `null` result on the platform | Your last node didn't set `result` | Return `{"result": ...}` |
|
|
747
768
|
| "could not load the agent" | An import error, a relative import, or building the graph at import | Follow §4 |
|
|
748
769
|
| Lockfile doesn't compile | A dependency has no Linux ARM64 wheel | Choose another version or package |
|
|
749
770
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
Shown by `gravity update` and `gravity changelog`, and after an update. Each release is `## X.Y.Z - date`,
|
|
4
|
+
then one bullet per change: `- new|improved|fixed: A one-line headline. The detail behind it.`
|
|
5
|
+
Write it for a builder, not for a maintainer. `tests/test_changelog.py` fails a release that has no entry.
|
|
6
|
+
|
|
7
|
+
## 0.6.0 - 2026-10-22
|
|
8
|
+
|
|
9
|
+
- new: Gravity CLI keeps itself up to date. Once every six hours a command checks PyPI; when there is a newer release it says so, updates, and runs your command again as the new version. GRAVITY_AUTO_UPDATE=0 turns it off.
|
|
10
|
+
- new: gravity update and gravity changelog. Update on demand (--check only looks), and read what changed in any release.
|
|
11
|
+
- new: Gravity.agent.md and problem-to-agent.md appear in the folder you run gravity in. They tell your coding agent how Gravity runs agents, the manifest, the tools and the build method, and are rewritten on every update. A file you edited is left alone, and your own agent.md is never touched.
|
|
12
|
+
- new: gravity docs writes or refreshes those two files on demand; --force replaces ones you edited.
|
|
13
|
+
- improved: gravity on its own is now a short home screen. It shows your version, the agent in this folder, whether you are signed in and any update, the next step for where you are, and every command on one line per group. gravity -h still lists them all with descriptions.
|
|
14
|
+
|
|
15
|
+
## 0.5.6 - 2026-10-15
|
|
16
|
+
|
|
17
|
+
- new: Firecrawl tools. firecrawl.search and firecrawl.scrape read pages that refuse plain HTTP clients, and web.render is the plain fallback for pages web.fetch returns empty. They run on your own connected Firecrawl account.
|
|
18
|
+
|
|
19
|
+
## 0.5.5 - 2026-10-13
|
|
20
|
+
|
|
21
|
+
- new: manifest.yaml takes an optional display: hint. It tells the page how a run's result shows (document, report, table, chart, code or files). It is a hint, never a rule, so an unknown value never fails a push.
|
|
22
|
+
|
|
23
|
+
## 0.5.4 - 2026-10-10
|
|
24
|
+
|
|
25
|
+
- improved: gravity agents shows the review. A REVIEW column (not submitted, submitted, approved, rejected), each message from the Gravity team with its decision and what to do next, and why an agent was turned off.
|
|
26
|
+
|
|
27
|
+
## 0.5.3 - 2026-10-10
|
|
28
|
+
|
|
29
|
+
- new: WhatsApp in the tool catalog. 51 tools with Composio-managed sign-in.
|
|
30
|
+
- new: gravity dev shows images in outputs, from image data URLs.
|
|
31
|
+
- improved: the builder guide explains NEEDS_RECONNECT on the shared test account: ask Gravity, do not connect your own.
|
|
32
|
+
|
|
33
|
+
## 0.5.2 - 2026-10-10
|
|
34
|
+
|
|
35
|
+
- new: Jira, Confluence and Notion in the tool catalog. 209 more tools, all with Composio-managed sign-in; imported writes need approvals.required_for.
|
|
36
|
+
- new: gravity run keeps get_store() locally in .gravity/store.json, so a monitor's history can be tried before it ships.
|
|
37
|
+
- improved: gravity starts faster.
|
|
@@ -18,6 +18,14 @@ CHECK_URL = "Check GRAVITY_API_URL in ~/.gravity/.env, or api_url in ~/.gravity/
|
|
|
18
18
|
IN_THE_WAY = "Check GRAVITY_API_URL, or whether a proxy or login page is in the way."
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
def _where_the_address_came_from() -> str:
|
|
22
|
+
"""Which setting chose the address and what it is hiding: a stale localhost line in .env beats the saved one."""
|
|
23
|
+
shadowed = config.api_url_shadowed()
|
|
24
|
+
if not shadowed:
|
|
25
|
+
return f"The address comes from {config.api_url_source()}. {CHECK_URL}"
|
|
26
|
+
return f"The address comes from {config.api_url_source()}, which overrides {shadowed} saved in config.json. Remove that setting to use the saved one, or point at another with gravity --api-url."
|
|
27
|
+
|
|
28
|
+
|
|
21
29
|
def _token() -> str:
|
|
22
30
|
# GRAVITY_API_TOKEN (the control plane's dev token) wins over a stored session.
|
|
23
31
|
token = os.environ.get("GRAVITY_API_TOKEN", "").strip()
|
|
@@ -40,7 +48,7 @@ def _request(
|
|
|
40
48
|
with httpx.Client(base_url=config.api_url(), timeout=timeout, headers=sent) as client:
|
|
41
49
|
resp = client.request(method, path, **kw)
|
|
42
50
|
except httpx.TransportError as e:
|
|
43
|
-
raise Problem(f"Can't reach Gravity at {config.api_url()}", str(e),
|
|
51
|
+
raise Problem(f"Can't reach Gravity at {config.api_url()}", str(e), _where_the_address_came_from()) from e
|
|
44
52
|
if resp.status_code == 401 and auth:
|
|
45
53
|
if os.environ.get("GRAVITY_API_TOKEN", "").strip():
|
|
46
54
|
raise Problem("Gravity refused GRAVITY_API_TOKEN", fix="Fix it in ~/.gravity/.env, or remove it and run gravity login.")
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""CHANGELOG.md, shipped inside the package: what each release changed, in the words a builder reads.
|
|
2
|
+
|
|
3
|
+
A release is `## X.Y.Z - date`, followed by bullets `- new|improved|fixed: A sentence. More detail.`
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
CHANGELOG = Path(__file__).parent / "CHANGELOG.md"
|
|
13
|
+
TAGS = ("new", "improved", "fixed")
|
|
14
|
+
_HEADING = re.compile(r"^##\s+(\d+(?:\.\d+)*)\s*(?:[-–—]\s*(.+))?$")
|
|
15
|
+
_BULLET = re.compile(rf"^-\s+({'|'.join(TAGS)}):\s+(.+)$", re.IGNORECASE)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@dataclass(frozen=True)
|
|
19
|
+
class Change:
|
|
20
|
+
tag: str
|
|
21
|
+
text: str
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@dataclass(frozen=True)
|
|
25
|
+
class Release:
|
|
26
|
+
version: str
|
|
27
|
+
date: str
|
|
28
|
+
changes: tuple[Change, ...]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def version_key(version: str) -> tuple[int, ...] | None:
|
|
32
|
+
return tuple(int(part) for part in version.split(".")) if re.fullmatch(r"\d+(\.\d+)*", version or "") else None
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def parse(text: str) -> list[Release]:
|
|
36
|
+
"""Newest first, in the order the file lists them."""
|
|
37
|
+
releases: list[tuple[str, str, list[Change]]] = []
|
|
38
|
+
for line in text.splitlines():
|
|
39
|
+
if heading := _HEADING.match(line.strip()):
|
|
40
|
+
releases.append((heading[1], (heading[2] or "").strip(), []))
|
|
41
|
+
elif releases and (bullet := _BULLET.match(line.strip())):
|
|
42
|
+
releases[-1][2].append(Change(bullet[1].lower(), bullet[2].strip()))
|
|
43
|
+
return [Release(version, date, tuple(changes)) for version, date, changes in releases]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def releases() -> list[Release]:
|
|
47
|
+
return parse(CHANGELOG.read_text(encoding="utf-8"))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def since(previous: str, current: str) -> list[Release]:
|
|
51
|
+
"""Releases after `previous`, up to and including `current`, newest first."""
|
|
52
|
+
low, high = version_key(previous), version_key(current)
|
|
53
|
+
if low is None or high is None:
|
|
54
|
+
return []
|
|
55
|
+
return [r for r in releases() if (key := version_key(r.version)) and low < key <= high]
|