agy-headless-bridge 1.0.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.
- agy_headless_bridge-1.0.0/LICENSE +21 -0
- agy_headless_bridge-1.0.0/PKG-INFO +380 -0
- agy_headless_bridge-1.0.0/README.md +359 -0
- agy_headless_bridge-1.0.0/pyproject.toml +36 -0
- agy_headless_bridge-1.0.0/setup.cfg +4 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge/__init__.py +6 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge/__main__.py +4 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge/bridge.py +258 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge/mcp_server.py +160 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge.egg-info/PKG-INFO +380 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge.egg-info/SOURCES.txt +14 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge.egg-info/dependency_links.txt +1 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge.egg-info/entry_points.txt +3 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge.egg-info/requires.txt +6 -0
- agy_headless_bridge-1.0.0/src/agy_headless_bridge.egg-info/top_level.txt +1 -0
- agy_headless_bridge-1.0.0/tests/test_smoke.py +67 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 agy-headless-bridge contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: agy-headless-bridge
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Call the Google Antigravity CLI (agy) headlessly from any non-TTY context (subprocess, MCP, CI). Works around upstream bug #76.
|
|
5
|
+
Author: agy-headless-bridge contributors
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/rhishi99/agy-headless-bridge
|
|
8
|
+
Project-URL: Issues, https://github.com/rhishi99/agy-headless-bridge/issues
|
|
9
|
+
Keywords: antigravity,agy,gemini,mcp,claude-code,pty,conpty
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Operating System :: OS Independent
|
|
13
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
14
|
+
Requires-Python: >=3.9
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: pywinpty>=2.0; sys_platform == "win32"
|
|
18
|
+
Provides-Extra: dev
|
|
19
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
20
|
+
Dynamic: license-file
|
|
21
|
+
|
|
22
|
+
<div align="center">
|
|
23
|
+
|
|
24
|
+
# agy-headless-bridge
|
|
25
|
+
|
|
26
|
+
### Call the Google **Antigravity CLI** (`agy`) headlessly โ and actually get output back.
|
|
27
|
+
|
|
28
|
+
Codename **PtyGravity** ยท pty + antiGravity
|
|
29
|
+
|
|
30
|
+
[](https://github.com/rhishi99/agy-headless-bridge/actions/workflows/test.yml)
|
|
31
|
+
[](LICENSE)
|
|
32
|
+
[](https://www.python.org)
|
|
33
|
+
[]()
|
|
34
|
+
|
|
35
|
+
๐ **[Architecture & docs โ rhishi99.github.io/agy-headless-bridge](https://rhishi99.github.io/agy-headless-bridge/)**
|
|
36
|
+
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## TL;DR โ the problem, before & after
|
|
42
|
+
|
|
43
|
+
`agy -p "<prompt>"` prints **nothing** when its stdout is not a real terminal.
|
|
44
|
+
So calling it from a subprocess, an MCP server, CI, or another coding agent
|
|
45
|
+
(Claude Code, Codex, โฆ) returns an empty string and exit `0` โ silently. This
|
|
46
|
+
package gives `agy` a fresh pseudo-terminal so it emits normally, then cleans
|
|
47
|
+
the output.
|
|
48
|
+
|
|
49
|
+
```mermaid
|
|
50
|
+
flowchart TB
|
|
51
|
+
subgraph B["โ BEFORE โ agy -p from any non-TTY caller"]
|
|
52
|
+
direction TB
|
|
53
|
+
a1["subprocess ยท MCP ยท CI ยท agent"] --> a2["agy -p "prompt""]
|
|
54
|
+
a2 --> a3["stdout gated by isatty()"]
|
|
55
|
+
a3 --> a4["(empty string)<br/>exit 0 ยท no error ยท no output"]
|
|
56
|
+
end
|
|
57
|
+
subgraph A["โ
AFTER โ through agy-headless-bridge"]
|
|
58
|
+
direction TB
|
|
59
|
+
b1["subprocess ยท MCP ยท CI ยท agent"] --> b2["run(prompt)"]
|
|
60
|
+
b2 --> b3["allocate fresh pseudo-terminal"]
|
|
61
|
+
b3 --> b4["agy -p "prompt"<br/>isatty() == True"]
|
|
62
|
+
b4 --> b5["clean() strips ANSI/TUI"]
|
|
63
|
+
b5 --> b6["clean text โ"]
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
classDef bad fill:#2a1313,stroke:#f87171,color:#ffd9d9;
|
|
67
|
+
classDef good fill:#0f2a1e,stroke:#34d399,color:#d7ffe9;
|
|
68
|
+
class a1,a2,a3,a4 bad;
|
|
69
|
+
class b1,b2,b3,b4,b5,b6 good;
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
from agy_headless_bridge import run
|
|
74
|
+
print(run("Explain a closure in one line."))
|
|
75
|
+
# -> A closure is a function that remembers variables from the scope where it was defined.
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Three entry points around one core:
|
|
79
|
+
|
|
80
|
+
| Entry point | Invoke | Use for |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| **Library** | `from agy_headless_bridge import run` | embedding agy in Python |
|
|
83
|
+
| **CLI** | `agy-bridge "prompt"` | shell scripts, quick calls |
|
|
84
|
+
| **MCP server** | `python -m agy_headless_bridge.mcp_server` | letting an agent call agy as a tool |
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## The problem in detail โ upstream bug [#76]
|
|
89
|
+
|
|
90
|
+
`agy` gates its stdout on `isatty()`. The instant stdout isn't a terminal, it
|
|
91
|
+
goes silent โ no output, no error, exit `0`:
|
|
92
|
+
|
|
93
|
+
```console
|
|
94
|
+
$ agy -p "say hi" | cat
|
|
95
|
+
$ # empty. exit 0. nothing.
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
The common `winpty agy -p "..."` workaround needs a terminal that **already
|
|
99
|
+
exists**, so it still fails from any automated / non-TTY caller.
|
|
100
|
+
|
|
101
|
+
## The fix โ give agy a tty it didn't ask for
|
|
102
|
+
|
|
103
|
+
Allocate a **brand-new** pseudo-terminal (one that needs no parent tty) and
|
|
104
|
+
attach `agy` to it. Same code path on every OS โ only the pty allocator differs.
|
|
105
|
+
|
|
106
|
+
```mermaid
|
|
107
|
+
flowchart TD
|
|
108
|
+
A["Caller โ non-TTY<br/>Claude Code ยท MCP ยท subprocess ยท CI"] -->|"prompt"| B{{"run(prompt)"}}
|
|
109
|
+
B --> C["find_agy()<br/>$AGY_PATH โ PATH โ OS defaults"]
|
|
110
|
+
C --> D{"sys.platform?"}
|
|
111
|
+
D -->|"win32"| E["pywinpty<br/>PtyProcess.spawn"]
|
|
112
|
+
D -->|"posix"| F["stdlib pty<br/>os.openpty + Popen"]
|
|
113
|
+
E --> G(["fresh pseudo-terminal"])
|
|
114
|
+
F --> G
|
|
115
|
+
G --> H["agy -p prompt<br/>isatty == True โ emits"]
|
|
116
|
+
H -->|"raw bytes + ANSI/TUI chrome"| I["clean()<br/>strip CSI/OSC ยท collapse \r repaints ยท drop spinner glyphs"]
|
|
117
|
+
I -->|"clean text"| A
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
| Platform | pty backend | Status |
|
|
121
|
+
|---|---|---|
|
|
122
|
+
| **Windows** | ConPTY via [`pywinpty`] (`PtyProcess`) | โ
verified (agy 1.0.6) |
|
|
123
|
+
| **Linux / macOS** | stdlib [`pty`] (`os.openpty` + `subprocess.Popen`) | ๐งช implemented, **untested** โ [report results / issues here](https://github.com/rhishi99/agy-headless-bridge/issues/new) (include OS, Python + agy version, and full stderr) |
|
|
124
|
+
|
|
125
|
+
> **Why not just the existing `agy` Claude Code plugins?** They wrap `agy` for
|
|
126
|
+
> *triggering* (slash commands, model selection) but still call `agy -p`
|
|
127
|
+
> directly โ so in any headless context they hit this exact empty-output bug.
|
|
128
|
+
> This package fixes the I/O layer they're missing. **Use both together.**
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Prerequisites
|
|
133
|
+
|
|
134
|
+
Before installing this bridge you need:
|
|
135
|
+
|
|
136
|
+
1. **Python 3.9+** โ `python --version`.
|
|
137
|
+
2. **The Antigravity CLI (`agy`)**, installed and **authenticated**:
|
|
138
|
+
- Install: <https://antigravity.google/cli>
|
|
139
|
+
- Authenticate once interactively (`agy` opens a browser OAuth flow), or set
|
|
140
|
+
`ANTIGRAVITY_API_KEY` in your environment if you use an API key.
|
|
141
|
+
- Verify it runs *in a real terminal*: `agy -p "say hi"` should print a reply.
|
|
142
|
+
(From a pipe it won't โ that's the very bug this package fixes.)
|
|
143
|
+
3. **Windows only:** `pywinpty` (installed automatically as a dependency).
|
|
144
|
+
POSIX uses the stdlib `pty` module โ nothing extra.
|
|
145
|
+
|
|
146
|
+
> This package does **not** install or authenticate `agy`, and does not bundle
|
|
147
|
+
> any credentials. It only spawns the `agy` already on your machine.
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Install
|
|
152
|
+
|
|
153
|
+
Requires **Python 3.9+**.
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
pip install agy-headless-bridge # pywinpty auto-installs on Windows only
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
From source:
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
git clone https://github.com/rhishi99/agy-headless-bridge
|
|
163
|
+
cd agy-headless-bridge
|
|
164
|
+
pip install -e .
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
The bridge locates the binary via, in order: `$AGY_PATH` โ `agy` on `PATH` โ
|
|
168
|
+
OS default install paths.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Usage
|
|
173
|
+
|
|
174
|
+
### Library
|
|
175
|
+
|
|
176
|
+
```python
|
|
177
|
+
from agy_headless_bridge import run, AgyNotFoundError
|
|
178
|
+
|
|
179
|
+
try:
|
|
180
|
+
print(run("reply with exactly: OK", timeout=60))
|
|
181
|
+
except AgyNotFoundError:
|
|
182
|
+
print("install agy first")
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
`run(prompt, timeout=180, agy_path=None) -> str` โ raises `AgyNotFoundError` if
|
|
186
|
+
the binary is missing, `TimeoutError` on timeout, `ValueError` on empty prompt.
|
|
187
|
+
Returns `""` only if agy genuinely emitted nothing.
|
|
188
|
+
|
|
189
|
+
### CLI
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
agy-bridge "reply with exactly: OK"
|
|
193
|
+
python -m agy_headless_bridge "reply with exactly: OK" # equivalent
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### MCP server
|
|
197
|
+
|
|
198
|
+
```bash
|
|
199
|
+
claude mcp add --transport stdio antigravity -- \
|
|
200
|
+
python -m agy_headless_bridge.mcp_server
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
The server speaks JSON-RPC stdio directly (no MCP SDK dependency) and routes
|
|
204
|
+
every call through the pty bridge.
|
|
205
|
+
|
|
206
|
+
**Tool schema** (what an agent โ or you, integrating manually โ sees):
|
|
207
|
+
|
|
208
|
+
| Tool | Argument | Type | Required | Description |
|
|
209
|
+
|---|---|---|---|---|
|
|
210
|
+
| `agy_ask` | `prompt` | string | โ
| one-shot prompt sent to agy |
|
|
211
|
+
| `agy_research` | `query` | string | โ
| wrapped as a deep-research prompt for agy |
|
|
212
|
+
|
|
213
|
+
**Response shape** โ a standard MCP `tools/call` result; the answer is the text
|
|
214
|
+
content:
|
|
215
|
+
|
|
216
|
+
```json
|
|
217
|
+
{
|
|
218
|
+
"jsonrpc": "2.0",
|
|
219
|
+
"id": 2,
|
|
220
|
+
"result": { "content": [ { "type": "text", "text": "<agy's cleaned answer>" } ] }
|
|
221
|
+
}
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
On failure the `text` is an `[agy-mcp] ERROR: ...` string (agy missing, timeout,
|
|
225
|
+
etc.) rather than a JSON-RPC error, so the agent always gets a readable reply.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Use cases & wiring it into your AI coding tools
|
|
230
|
+
|
|
231
|
+
The whole point: let **one** AI coding tool delegate work to **Gemini via
|
|
232
|
+
Antigravity**, headlessly. Common setups:
|
|
233
|
+
|
|
234
|
+
| Use case | How |
|
|
235
|
+
|---|---|
|
|
236
|
+
| Claude Code asks Gemini for a second opinion / diff review | MCP server โ `agy_ask` tool |
|
|
237
|
+
| A CI step runs an `agy` prompt and captures the answer | `agy-bridge "..."` in the workflow |
|
|
238
|
+
| A Python pipeline fans work out to agy | `from agy_headless_bridge import run` |
|
|
239
|
+
| Codex / any MCP-capable agent delegates to agy | register the same MCP server |
|
|
240
|
+
| Cron / scheduled job summarizes logs via agy | `agy-bridge` in the script |
|
|
241
|
+
|
|
242
|
+
### Wire into Claude Code
|
|
243
|
+
|
|
244
|
+
Register the MCP server, then prompt Claude to use it:
|
|
245
|
+
|
|
246
|
+
```bash
|
|
247
|
+
claude mcp add --transport stdio antigravity -- \
|
|
248
|
+
python -m agy_headless_bridge.mcp_server
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
> **Prompt to Claude Code:**
|
|
252
|
+
> *"Use the `agy_ask` tool to ask Antigravity to review this function for edge
|
|
253
|
+
> cases, then summarize its findings for me."*
|
|
254
|
+
|
|
255
|
+
If you also want slash-command triggering and model selection, pair this bridge
|
|
256
|
+
with the community `antigravity-cc` Claude Code plugin โ that handles the
|
|
257
|
+
`/agy:*` commands and Gemini/Claude model swap; this handles the headless I/O.
|
|
258
|
+
|
|
259
|
+
### Wire into Codex (or any MCP client)
|
|
260
|
+
|
|
261
|
+
Add the server to the client's MCP config:
|
|
262
|
+
|
|
263
|
+
```json
|
|
264
|
+
{
|
|
265
|
+
"mcpServers": {
|
|
266
|
+
"antigravity": {
|
|
267
|
+
"command": "python",
|
|
268
|
+
"args": ["-m", "agy_headless_bridge.mcp_server"]
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
> **Prompt to the agent:**
|
|
275
|
+
> *"Call `agy_research` with the query 'idiomatic error handling in Rust' and
|
|
276
|
+
> turn the result into a checklist."*
|
|
277
|
+
|
|
278
|
+
### Use from a shell / CI script
|
|
279
|
+
|
|
280
|
+
```bash
|
|
281
|
+
ANSWER="$(agy-bridge 'Summarize the key risk in this diff in one sentence.')"
|
|
282
|
+
echo "$ANSWER"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Configuration
|
|
288
|
+
|
|
289
|
+
| Env var | Default | Meaning |
|
|
290
|
+
|---|---|---|
|
|
291
|
+
| `AGY_PATH` | auto-detect | Absolute path to the `agy` binary |
|
|
292
|
+
| `AGY_BRIDGE_TIMEOUT` | `180` | Seconds before a call is killed |
|
|
293
|
+
|
|
294
|
+
---
|
|
295
|
+
|
|
296
|
+
## How `clean()` works
|
|
297
|
+
|
|
298
|
+
`agy`'s pty output is a TUI stream, not plain text. `clean()` removes **ANSI
|
|
299
|
+
escapes** (CSI/OSC โ colors, cursor moves), **`\r` repaints** (a spinner
|
|
300
|
+
overwrites one line; only the final paint is kept), and **box-drawing / spinner
|
|
301
|
+
glyphs** (`โญโโฎ โ โ โ โ น`) โ leaving just the model's answer.
|
|
302
|
+
|
|
303
|
+
What comes off the pty vs. what you get back:
|
|
304
|
+
|
|
305
|
+
```text
|
|
306
|
+
RAW (off the pty) CLEANED (returned to you)
|
|
307
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
308
|
+
โ thinkingโฆ\rโ thinkingโฆ\r\x1b[2K A closure is a function that
|
|
309
|
+
\x1b[32mโญโโโโโโโโโโโโโโฎ\x1b[0m captures variables from the
|
|
310
|
+
\x1b[32mโ\x1b[0m A closure is a function scope where it was defined.
|
|
311
|
+
that captures variables from the
|
|
312
|
+
scope where it was defined.
|
|
313
|
+
\x1b[32mโฐโโโโโโโโโโโโโโฏ\x1b[0m
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## Troubleshooting / FAQ
|
|
319
|
+
|
|
320
|
+
**`pip install` fails on Windows building `pywinpty`** โ `pywinpty` is a native
|
|
321
|
+
extension. If pip tries to build from source and errors with a compiler/`cl.exe`
|
|
322
|
+
message, install the **Microsoft C++ Build Tools** (or use a Python where a
|
|
323
|
+
prebuilt `pywinpty` wheel exists โ recent CPython on Windows has them). Upgrade
|
|
324
|
+
pip first: `python -m pip install -U pip`.
|
|
325
|
+
|
|
326
|
+
**`AgyNotFoundError`** โ the bridge can't find `agy`. Set `AGY_PATH` to the
|
|
327
|
+
absolute path of the binary, or make sure `agy` is on your `PATH`
|
|
328
|
+
(`agy --version` should work in your shell).
|
|
329
|
+
|
|
330
|
+
**Empty string returned** โ agy produced no output. Confirm it works in a real
|
|
331
|
+
terminal first: `agy -p "say hi"`. If that's also empty, the problem is agy/auth,
|
|
332
|
+
not the bridge. Re-authenticate (`agy` interactively) or check
|
|
333
|
+
`ANTIGRAVITY_API_KEY`.
|
|
334
|
+
|
|
335
|
+
**`TimeoutError`** โ the call exceeded `AGY_BRIDGE_TIMEOUT` (default 180s). Raise
|
|
336
|
+
it for long prompts: `AGY_BRIDGE_TIMEOUT=600 agy-bridge "..."` or
|
|
337
|
+
`run(prompt, timeout=600)`.
|
|
338
|
+
|
|
339
|
+
**Pseudo-terminal allocation fails** โ rare. On Windows it means `pywinpty`
|
|
340
|
+
isn't importable (reinstall it). On POSIX it means the system is out of pty
|
|
341
|
+
slots or `pty.openpty()` is denied (containers with no `/dev/pts`); run with a
|
|
342
|
+
real pty available.
|
|
343
|
+
|
|
344
|
+
**Garbled / partial output** โ open an
|
|
345
|
+
[issue](https://github.com/rhishi99/agy-headless-bridge/issues/new) with the OS,
|
|
346
|
+
Python + agy version, and the raw output; `clean()` may need another glyph rule.
|
|
347
|
+
|
|
348
|
+
## Development & CI
|
|
349
|
+
|
|
350
|
+
```bash
|
|
351
|
+
pip install -e ".[dev]"
|
|
352
|
+
pytest
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
Unit tests (cleaning, arg validation, binary discovery) always run. The live
|
|
356
|
+
`agy` round-trip test **auto-skips** when `agy` isn't installed โ so CI runners
|
|
357
|
+
(which don't have `agy`) stay green and never need credentials. CI runs on
|
|
358
|
+
Windows + Linux across Python 3.9 and 3.12.
|
|
359
|
+
|
|
360
|
+
---
|
|
361
|
+
|
|
362
|
+
## Scope, non-goals & disclaimer
|
|
363
|
+
|
|
364
|
+
- **Model selection** (Gemini Pro / Flash / Claude inside agy) is *not* handled
|
|
365
|
+
here โ it's an `agy` `settings.json` concern, covered by the `antigravity-cc`
|
|
366
|
+
plugin. Pair the two.
|
|
367
|
+
- Does **not** install or authenticate `agy`, and ships **no credentials**.
|
|
368
|
+
- Automating any vendor CLI may interact with that vendor's terms / rate limits.
|
|
369
|
+
You are responsible for using `agy` within Google's terms of service. This
|
|
370
|
+
project only changes *how stdout is captured* โ it does not bypass auth,
|
|
371
|
+
quotas, or any access control.
|
|
372
|
+
- Not affiliated with Google. *Antigravity* and *agy* are Google products.
|
|
373
|
+
|
|
374
|
+
## License
|
|
375
|
+
|
|
376
|
+
[MIT](LICENSE).
|
|
377
|
+
|
|
378
|
+
[#76]: https://antigravity.google/cli
|
|
379
|
+
[`pywinpty`]: https://github.com/andfoy/pywinpty
|
|
380
|
+
[`pty`]: https://docs.python.org/3/library/pty.html
|