diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..f70ecf474 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Binaries +bin/ +antigravity +agy +*.tar.gz +*.exe + +# Ignore packages binaries +packages/binaries/ + +# OS files +.DS_Store +Thumbs.db +*.swp +*.swo +*~ + +# IDE +.vscode/ +.idea/ +*.iml + +# Logs +*.log diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 000000000..085a26aff --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,23 @@ +# Project Configuration for Antigravity CLI +# Place this file in the root of your project to provide context to the AI agent. +# Docs: https://antigravity.google/docs/cli-overview + +# Project Overview +# Describe your project, its purpose, and key technologies. +# Example: +# +# This is a Python FastAPI backend with PostgreSQL. +# - Use Pydantic v2 (model_dump(), not dict()) +# - All endpoints must have OpenAPI descriptions +# - Tests: pytest with async fixtures +# - Docker: compose v2 format + +# Coding Standards +# - Follow PEP8 / ESLint / your linter rules +# - Use meaningful commit messages (conventional commits) +# - All functions must have docstrings/JSDoc + +# Security +# - Never hardcode credentials — use environment variables +# - Validate all user input +# - Use parameterized queries for SQL diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..8418945dd --- /dev/null +++ b/Makefile @@ -0,0 +1,66 @@ +.PHONY: install update uninstall check help + +BINARY_NAME := agy +INSTALL_DIR := $(HOME)/.local/bin +BINARY_PATH := $(INSTALL_DIR)/$(BINARY_NAME) + +help: ## Show this help + @echo "Antigravity CLI — Makefile targets:" + @echo "" + @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-15s\033[0m %s\n", $$1, $$2}' + @echo "" + +install: ## Install Antigravity CLI via official script + @if [ -f "$(BINARY_PATH)" ]; then \ + echo "✓ agy is already installed at $(BINARY_PATH)"; \ + echo " Run 'make update' to update, or 'make reinstall' for fresh install."; \ + else \ + echo "⠋ Installing Antigravity CLI..."; \ + bash install.sh; \ + echo ""; \ + echo "✓ Installation complete! Run 'agy' to start."; \ + fi + +reinstall: uninstall install ## Remove and reinstall + +update: ## Update to the latest version + @if [ -f "$(BINARY_PATH)" ]; then \ + echo "⠋ Updating Antigravity CLI..."; \ + $(BINARY_PATH) update; \ + else \ + echo "✗ agy is not installed. Run 'make install' first."; \ + exit 1; \ + fi + +uninstall: ## Remove Antigravity CLI binary + @if [ -f "$(BINARY_PATH)" ]; then \ + rm -f "$(BINARY_PATH)"; \ + echo "✓ Removed $(BINARY_PATH)"; \ + else \ + echo "✓ agy is not installed at $(BINARY_PATH)"; \ + fi + +check: ## Check if agy is installed and show version + @if command -v agy >/dev/null 2>&1; then \ + echo "✓ agy found: $$(command -v agy)"; \ + echo " Version: $$(agy --version 2>/dev/null || echo 'unknown')"; \ + elif [ -f "$(BINARY_PATH)" ]; then \ + echo "✓ agy found at $(BINARY_PATH) (not in PATH)"; \ + echo " Version: $$($(BINARY_PATH) --version 2>/dev/null || echo 'unknown')"; \ + echo ""; \ + echo " Add to PATH:"; \ + echo " export PATH=\"$(INSTALL_DIR):\$$PATH\""; \ + else \ + echo "✗ agy is not installed. Run 'make install'."; \ + exit 1; \ + fi + +run: ## Start agy interactive session + @if command -v agy >/dev/null 2>&1; then \ + agy; \ + elif [ -f "$(BINARY_PATH)" ]; then \ + $(BINARY_PATH); \ + else \ + echo "✗ agy is not installed. Run 'make install' first."; \ + exit 1; \ + fi diff --git a/README-DE.md b/README-DE.md new file mode 100644 index 000000000..be6164056 --- /dev/null +++ b/README-DE.md @@ -0,0 +1,241 @@ +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

+ +

+ Antigravity CLI Logo +

+ +

🚀 Antigravity CLI

+ +

+ Community-Fork & gehärtete Offline-Version von google-antigravity/antigravity-cli mit automatischer Konfiguration von Statuszeile und Fenstertitel +

+ +

+ GitHub Fork + Version + License + Supported Platforms +

+ +

+ 🤖 KI-Codierungsagent direkt in Ihrem Terminal. Versteht den Kontext Ihrer Codebasis, erstellt und bearbeitet Dateien, führt sichere Befehle in einer Sandbox aus und löst komplexe architektonische Aufgaben mit einem einzigen Prompt. +

+ +--- + +## ⚡ Schnellstart + +### Sofortige Installation (Offline-first & Zero-dependency) +Dieser Fork lädt vorkompilierte Binärdateien direkt aus den **GitHub Release Assets** herunter (anstatt von Google-API-Servern). Eine vollständig autonome Offline-Installation wird ebenfalls unterstützt, wenn die erforderlichen Archive zuvor in den Ordner `packages/binaries/` heruntergeladen wurden. + +#### 🐧 Linux und 🍎 macOS +```bash +# Netzwerkinstallation aus dem Fork-Repository: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# ODER Offline-Installation aus einem lokalen Repository: +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (Optional: Laden Sie Plattform-Archive nach packages/binaries/ herunter) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# Netzwerkinstallation: +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# ODER Offline-Installation aus einem geklonten Repository: +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# Netzwerkinstallation: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# ODER Offline-Installation aus einem geklonten Repository: +install.cmd +``` + +--- + +## 📋 Hauptfunktionen + +> [!NOTE] +> Im Gegensatz zur Originalversion ist dieser Fork für den stabilen Betrieb in Headless-/SSH-Sitzungen und lokalen Home-Labs angepasst. + +* 📂 **Bearbeitung mehrerer Dateien** — Bearbeitet mehrere Dateien gleichzeitig in Ihrem Arbeitsbereich mit vorheriger Bestätigung der Änderungen. +* 🔒 **Sichere Shell-Befehle** — Führt beliebige Terminalbefehle im integrierten Docker-Container (Sandbox) oder auf dem Hostsystem aus. +* 🧠 **Mehrstufiges Denken (PAV)** — Erstellt selbstständig einen Plan zur Aufgabenausführung, testet Code und behebt eigene Fehler. +* 💾 **Persistenter Konversationsverlauf** — Speichert den vollständigen Konversationskontext und den Zustand des Arbeitsbereichs zwischen den Sitzungen. +* 🔌 **Plugin-System** — Erweitern Sie die Fähigkeiten des Agenten mit benutzerdefinierten *Skills* und MCP-Servern (Model Context Protocol). + +--- + +## ⚙️ Konfiguration + +### 1. Projektkonfiguration (`GEMINI.md`) +Erstellen Sie eine `GEMINI.md`-Datei im Stammverzeichnis Ihres Projekts, um dem KI-Agenten spezifischen Kontext und Entwicklungsregeln bereitzustellen: + +```markdown +# Projektkontext + +- Dieses Projekt verwendet FastAPI und Pydantic v2. +- Verwenden Sie immer `model_dump()` anstelle des veralteten `dict()`. +- STRIKTE REGEL: Keine fest codierten Passwörter im Code. Importieren Sie alle Geheimnisse aus `.env`. +``` + +### 2. Globale Einstellungen (`~/.gemini/antigravity-cli/settings.json`) +Die globale Einstellungsdatei steuert Berechtigungen zur Tool-Ausführung, Statuszeilen-/Titel-Skripte und MCP-Server: + +- **Linux/Unix**: `~/.gemini/antigravity-cli/settings.json` +- **macOS**: `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows**: `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} +``` + +### 3. Spezialisierte Agenten +Sie können benutzerdefinierte Rollen und Anweisungen für KI-Agenten im JSON-Format beschreiben. Jeder Agent muss sein eigenes Verzeichnis haben, das eine `agent.json`-Datei enthält: + +- **Globale Agenten (Linux/Unix)**: `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **Globale Agenten (macOS)**: `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **Globale Agenten (Windows)**: `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **Lokale Agenten (Projektarbeitsbereich)**: `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "Analysiert Code vor dem Commit auf Schwachstellen", + "instructions": "Änderungen überprüfen auf:\n- OWASP Top 10 Schwachstellen\n- Durchsickern von API-Schlüsseln oder Geheimnissen\n- Richtigkeit der Firewall-/nftables-Konfiguration" +} +``` + +--- + +## 🔐 Authentifizierungsmethoden + +| Methode | Befehl / Variable | Einschränkungen & Funktionen | +| :--- | :--- | :--- | +| **Google Auth (Browser)** | Automatisch bei der ersten Ausführung von `agy` | Standard-Login. Kostenloses Limit: 60 Anfr./Min., 1000 Anfr./Tag | +| **API-Schlüssel (Offline)** | `export GEMINI_API_KEY="X"` | Empfohlen für Server und Automatisierung | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | Enterprise-Level mit Google Cloud Cloud-Infrastruktur | +| **Abmelden** | `/logout` | Löschen lokaler Sitzungstoken | + +--- + +## 🔧 Befehlsreferenz + +### Befehlszeile +```bash +agy # Eine interaktive Sitzung starten +agy -p "Prompt" # Einmalige Ausführung ohne Chat-Eingabe +agy -c # Letzte unvollständige Konversation fortsetzen +agy --conversation # Sitzung mit einer bestimmten ID laden +agy --sandbox # In einem isolierten Docker-Container ausführen +agy update # Binärdatei auf die neueste Version aktualisieren +agy plugin list # Installierte Plugins auflisten +``` + +### Slash-Befehle in der Chat-Schnittstelle +* `/help` — Hilfe zu verfügbaren Tools. +* `/settings` — Interaktive Konfiguration der Einstellungen. +* `/usage` — Statistik der verbrauchten Token und des Kontingents. +* `/diff` — Aktuelle ungespeicherte Änderungen im Projekt anzeigen. +* `/statusline` — Anzeige der Terminal-Statuszeile konfigurieren. + +--- + +## 🔄 Migration von Gemini CLI + +> [!WARNING] +> Das ursprüngliche Gemini CLI (`gemini`) stellt die Unterstützung für Nicht-Unternehmenskonten am **18. Juni 2026** ein. Die Migration zu Antigravity CLI ist erforderlich. + +### Schritte für einen schnellen Übergang +1. Installieren Sie den neuen Client: `curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. Migrieren Sie lokale benutzerdefinierte Agentenkonfigurationsdateien (konvertieren Sie z. B. benutzerdefinierte Agenten von YAML in `~/.gemini/agents/` in JSON-Dateien unter `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json`). +3. Aktualisieren Sie CI/CD-Konfigurationen in GitHub Actions und ersetzen Sie `gemini`-Aufrufe durch `agy`. +4. Deinstallieren Sie die alte Bibliothek: `npm uninstall -g @google/gemini-cli` + +### Vergleichstabelle + +| Funktion | Gemini CLI (Veraltet) | Antigravity CLI (Modern) | +| :--- | :--- | :--- | +| **Entwicklungsplattform** | Node.js / TypeScript | Go (Nativ kompilierte Binärdatei) | +| **Befehlsname** | `gemini` | `agy` | +| **Startgeschwindigkeit** | ~1.2s (Node.js-Start) | **~0.05s (sofortiger nativer Start)** | +| **Konfigurationsdatei** | `GEMINI.md` | `GEMINI.md` | +| **Automatische Aktualisierung** | Über `npm update` | Integrierter Selbstaktualisierungsmechanismus | +| **Support-Status** | ⛔ EOL (18. Juni 2026) | ✅ Aktive Entwicklung (Upstream) | + +--- + +## 📁 Repository-Struktur + +``` +antigravity-cli/ +├── install.sh # Installer für Linux/macOS (offline/online) +├── install.ps1 # Installer für Windows PowerShell (offline/online) +├── install.cmd # Installer für Windows CMD +├── Makefile # Automatisierungsziele (make install/reinstall/uninstall) +├── GEMINI.md # Projektkontext-Dateivorlage +├── packages/ # Lokaler Offline-Vertrieb +│ ├── manifests/ # Versionsmanifeste für alle Plattformen +│ └── binaries/ # (Manuell erstellt für den Offline-Modus) +└── CHANGELOG.md # Changelog und Release-Protokoll +``` + +--- + +## 🤝 Mitwirken & Community + +Dieses Repository ist ein unabhängiger Community-Fork des ursprünglichen Projekts [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli). + +**Unsere Verbesserungen:** +* 🌍 Mehrsprachige Lokalisierungsunterstützung für Dokumente und Anleitungen. +* 📦 Autonomie: Offline-Installationsmöglichkeit ohne Google-API-Downloads. +* 🛠️ Komfort: `Makefile` für einen vereinfachten Lebenszyklus des Tools hinzugefügt. +* 🛡️ Sicherheit: Regelmäßige Fehlerbehebungen und Verbesserungen an der Sandbox-Umgebung. + +--- + +## 📜 Rechtlicher Hinweis & Markenrecht + +* **Offizielle Links**: [Offizielles Dokumentations-Repo](https://github.com/google-antigravity/antigravity-cli) · [Offizielle CLI-Codebasis](https://github.com/google-gemini/gemini-cli) · [Offizielle Website](https://antigravity.google) +* **Nutzungsbedingungen**: [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **Rechtlicher Status des Forks:** +> Dieses Repository ist eine unabhängige, nicht-kommerzielle Kopie (Community Fork) des Original-Clients. Es **ist kein** offizielles Produkt von Google LLC. Google LLC is nicht verantwortlich für die Leistung, Änderungen oder Sicherheit dieses Forks. +> +> **Lizenzierung & Urheberrechte:** +> Die Original-Software wird unter der [Apache-Lizenz 2.0](https://www.apache.org/licenses/LICENSE-2.0) verbreitet. Der gesamte Originalcode ist das geistige Eigentum von **Copyright © 2025 Google LLC**. +> +> **Markennutzung:** +> Der Name "Antigravity CLI" und die zugehörigen Logos werden im Rahmen der üblichen Nutzung ausschließlich zur Beschreibung der Herkunft, Kompatibilität und des funktionalen Zwecks der Software verwendet. Dieses Projekt erhebt keinen Anspruch auf Eigentumsrechte an Marken von Google LLC. +> +> **Gewährleistungsausschluss:** +> Die Software wird "WIE BESEHEN" bereitgestellt, OHNE GEWÄHRLEISTUNGEN JEGLICHER ART, weder ausdrücklich noch stillschweigend. Sie übernehmen die gesamte Verantwortung und alle Risiken im Zusammenhang mit ihrer Verwendung. + +> [!CAUTION] +> KI-Codierungsagenten arbeiten autonom. Überprüfen Sie die vorgeschlagenen Diff-Blöcke und Befehle immer sorgfältig, bevor Sie die Ausführung bestätigen, insbesondere wenn Sie mit Systemdateien oder der Firewall-Konfiguration arbeiten. diff --git a/README-ES.md b/README-ES.md new file mode 100644 index 000000000..b64f212d1 --- /dev/null +++ b/README-ES.md @@ -0,0 +1,241 @@ +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

+ +

+ Antigravity CLI Logo +

+ +

🚀 Antigravity CLI

+ +

+ Bifurcación de la comunidad y versión fuera de línea de google-antigravity/antigravity-cli con configuración automática de statusline y título de ventana +

+ +

+ GitHub Fork + Version + License + Supported Platforms +

+ +

+ 🤖 Agente de codificación de IA directamente en su terminal. Comprende el contexto de su código base, crea y edita archivos, ejecuta comandos seguros en un entorno aislado y resuelve tareas arquitectónicas complejas en una sola instrucción. +

+ +--- + +## ⚡ Inicio rápido + +### Instalación instantánea (Primero fuera de línea y sin dependencias) +Esta bifurcación descarga archivos binarios precompilados directamente desde los **activos de lanzamiento de GitHub (GitHub Release Assets)** (en lugar de los servidores de la API de Google). También se admite la instalación completamente fuera de línea (offline) si los archivos requeridos se cargaron previamente en la carpeta `packages/binaries/`. + +#### 🐧 Linux y 🍎 macOS +```bash +# Instalación a través de la red desde el repositorio de la bifurcación: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# O instalación fuera de línea desde un repositorio local: +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (Opcional: descargue los archivos de la plataforma en packages/binaries/) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# Instalación a través de la red: +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# O instalación fuera de línea desde un repositorio clonado: +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# Instalación a través de la red: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# O instalación fuera de línea desde un repositorio clonado: +install.cmd +``` + +--- + +## 📋 Características principales + +> [!NOTE] +> A diferencia de la versión original, esta bifurcación está adaptada para un funcionamiento estable en sesiones sin interfaz gráfica (headless)/SSH y laboratorios domésticos locales. + +* 📂 **Edición de archivos múltiples** — Edita varios archivos simultáneamente en su espacio de trabajo con confirmación previa de los cambios. +* 🔒 **Comandos de shell seguros** — Ejecuta cualquier comando de terminal en el contenedor Docker incorporado (entorno aislado/sandbox) o en el sistema host. +* 🧠 **Razonamiento de múltiples pasos (PAV)** — Construye de forma independiente un plan de ejecución de tareas, prueba el código y depura sus propios errores. +* 💾 **Historial de conversación persistente** — Guarda el contexto completo de la conversación y el estado del espacio de trabajo entre sesiones. +* 🔌 **Sistema de complementos** — Amplíe las capacidades del agente con *habilidades (Skills)* personalizadas y servidores MCP (Model Context Protocol). + +--- + +## ⚙️ Configuración + +### 1. Configuración del proyecto (`GEMINI.md`) +Cree un archivo `GEMINI.md` en la raíz de su proyecto para proporcionar un contexto específico y reglas de desarrollo al agente de IA: + +```markdown +# Contexto del proyecto + +- Este proyecto utiliza FastAPI y Pydantic v2. +- Utilice siempre `model_dump()` en lugar del obsoleto `dict()`. +- REGLA ESTRICTA: No use contraseñas hardcoded en el código. Importe todos los secretos desde `.env`. +``` + +### 2. Configuración global (`~/.gemini/antigravity-cli/settings.json`) +El archivo de configuración global controla los permisos de ejecución de herramientas, los scripts de statusline/título y los servidores MCP: + +- **Linux/Unix**: `~/.gemini/antigravity-cli/settings.json` +- **macOS**: `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows**: `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} +``` + +### 3. Agentes especializados +Puede describir roles e instrucciones personalizados para agentes de IA en formato JSON. Cada agente debe tener su propio directorio que contenga un archivo `agent.json`: + +- **Agentes Globales (Linux/Unix)**: `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **Agentes Globales (macOS)**: `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **Agentes Globales (Windows)**: `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **Agentes Locales (Espacio de Trabajo)**: `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "Analiza el código en busca de vulnerabilidades antes de confirmar", + "instructions": "Buscar en los cambios:\n- Vulnerabilidades OWASP Top 10\n- Filtración de claves de API o secretos\n- Corrección de la configuración del firewall/nftables" +} +``` + +--- + +## 🔐 Métodos de autenticación + +| Método | Comando / Variable | Limitaciones y características | +| :--- | :--- | :--- | +| **Autenticación de Google (Navegador)** | Automáticamente en el primer `agy` | Inicio de sesión estándar. Límite gratuito: 60 sol./min, 1000 sol./día | +| **Clave de API (Fuera de línea)** | `export GEMINI_API_KEY="X"` | Recomendado para servidores y automatización | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | Nivel empresarial con la infraestructura en la nube de Google Cloud | +| **Cerrar sesión** | `/logout` | Borrado de tokens de sesión local | + +--- + +## 🔧 Referencia de comandos + +### Línea de comandos +```bash +agy # Iniciar una sesión interactiva +agy -p "Mensaje de petición" # Ejecución única sin ingresar al chat +agy -c # Continuar la última conversación incompleta +agy --conversation # Cargar una sesión por un ID específico +agy --sandbox # Ejecutar en un contenedor Docker aislado +agy update # Actualizar el binario a la última versión +agy plugin list # Listar los complementos instalados +``` + +### Comandos de barra diagonal (Slash Commands) en la interfaz de chat +* `/help` — Ayuda sobre las herramientas disponibles. +* `/settings` — Configuración interactiva de los ajustes. +* `/usage` — Estadísticas de tokens utilizados y cuota. +* `/diff` — Ver los cambios actuales no guardados en el proyecto. +* `/statusline` — Configurar la visualización de la línea de estado del terminal. + +--- + +## 🔄 Migración desde Gemini CLI + +> [!WARNING] +> La interfaz de línea de comandos original Gemini CLI (`gemini`) dejará de admitir cuentas que no sean empresariales el **18 de junio de 2026**. Se requiere la migración a Antigravity CLI. + +### Pasos para una transición rápida +1. Instale el nuevo cliente: `curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. Migre los archivos de configuración de agentes personalizados locales (por ejemplo, convierta agentes personalizados de YAML dentro de `~/.gemini/agents/` a archivos JSON en `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json`). +3. Actualice las configuraciones de CI/CD en GitHub Actions, reemplazando las llamadas a `gemini` por `agy`. +4. Desinstale la biblioteca antigua: `npm uninstall -g @google/gemini-cli` + +### Tabla de comparación + +| Característica | Gemini CLI (Obsoleto) | Antigravity CLI (Moderno) | +| :--- | :--- | :--- | +| **Plataforma de desarrollo** | Node.js / TypeScript | Go (Binario compilado nativo) | +| **Nombre del comando** | `gemini` | `agy` | +| **Velocidad de inicio** | ~1.2s (inicio de Node.js) | **~0.05s (inicio nativo instantáneo)** | +| **Archivo de configuración** | `GEMINI.md` | `GEMINI.md` | +| **Actualización automática** | A través de `npm update` | Mecanismo de auto-actualización incorporado | +| **Estado del soporte** | ⛔ Fin de la vida útil (18 de junio de 2026) | ✅ Desarrollo activo (Upstream) | + +--- + +## 📁 Estructura del repositorio + +``` +antigravity-cli/ +├── install.sh # Instalador para Linux/macOS (fuera de línea/en línea) +├── install.ps1 # Instalador para Windows PowerShell (fuera de línea/en línea) +├── install.cmd # Instalador para Windows CMD +├── Makefile # Destinos de automatización (make install/reinstall/uninstall) +├── GEMINI.md # Plantilla de archivo de contexto del proyecto +├── packages/ # Distribución local fuera de línea +│ ├── manifests/ # Manifiestos de versión para todas las plataformas +│ └── binaries/ # (Creado manualmente para el modo fuera de línea) +└── CHANGELOG.md # Registro de cambios y registro de lanzamientos +``` + +--- + +## 🤝 Contribuir y Comunidad + +Este repositorio es una bifurcación comunitaria independiente del proyecto original [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli). + +**Nuestras mejoras:** +* 🌍 Soporte de localización en múltiples idiomas para documentos y guías. +* 📦 Autonomía: capacidad de instalación fuera de línea sin descargas de la API de Google. +* 🛠️ Conveniencia: se agregó `Makefile` para un ciclo de vida simplificado de la herramienta. +* 🛡️ Seguridad: corrección periódica de errores y mejoras en el entorno aislado (sandbox). + +--- + +## 📜 Aviso legal y de marca registrada + +* **Enlaces oficiales**: [Repositorio de documentación oficial](https://github.com/google-antigravity/antigravity-cli) · [Código base oficial de la CLI](https://github.com/google-gemini/gemini-cli) · [Sitio web oficial](https://antigravity.google) +* **Condiciones de uso**: [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **Estado legal de la bifurcación:** +> Este repositorio es una copia independiente no comercial (Bifurcación de la comunidad) del cliente original. **No es** un producto oficial de Google LLC. Google LLC no se hace responsable del rendimiento, las modificaciones o la seguridad de esta bifurcación. +> +> **Licencias y derechos de autor:** +> El software original se distribuye bajo la [Licencia Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). Todo el código original es propiedad intelectual de **Copyright © 2025 Google LLC**. +> +> **Uso de marcas registradas:** +> El nombre "Antigravity CLI" y los logotipos asociados se utilizan dentro de los límites del uso consuetudinario únicamente para describir el origen, la compatibilidad y el propósito funcional del software. Este proyecto no reclama la propiedad de ninguna marca registrada de Google LLC. +> +> **Renuncia de garantía:** +> El software se proporciona "TAL CUAL", SIN GARANTÍAS DE NINGÚN TIPO, ya sean expresas o implícitas. Usted asume toda la responsabilidad y los riesgos asociados con su uso. + +> [!CAUTION] +> Los agentes de codificación de IA funcionan de forma autónoma. Revise siempre con cuidado los bloques de diferencias (diff) y los comandos propuestos antes de confirmar la ejecución, especialmente cuando trabaje con archivos del sistema o la configuración del firewall. diff --git a/README-FR.md b/README-FR.md new file mode 100644 index 000000000..3c7548b5b --- /dev/null +++ b/README-FR.md @@ -0,0 +1,241 @@ +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

+ +

+ Antigravity CLI Logo +

+ +

🚀 Antigravity CLI

+ +

+ Fork communautaire et version hors ligne renforcée de google-antigravity/antigravity-cli avec configuration automatique de la ligne d'état et du titre de la fenêtre +

+ +

+ GitHub Fork + Version + License + Supported Platforms +

+ +

+ 🤖 Agent de codage IA directement dans votre terminal. Comprend le contexte de votre base de code, crée et modifie des fichiers, exécute des commandes sécurisées dans un bac à sable et résout des tâches architecturales complexes en un seul prompt. +

+ +--- + +## ⚡ Démarrage rapide + +### Installation instantanée (Hors ligne d'abord & Sans dépendance) +Ce fork télécharge des fichiers binaires précompilés directement depuis les **GitHub Release Assets** (au lieu des serveurs de l'API Google). Une installation complètement hors ligne est également prise en charge si les archives requises ont été préalablement chargées dans le dossier `packages/binaries/`. + +#### 🐧 Linux et 🍎 macOS +```bash +# Installation réseau à partir du dépôt du fork : +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# OU installation hors ligne à partir d'un dépôt local : +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (Optionnel : téléchargez les archives de la plateforme dans packages/binaries/) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# Installation réseau : +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# OU installation hors ligne à partir d'un dépôt cloné : +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# Installation réseau : +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# OU installation hors ligne à partir d'un dépôt cloné : +install.cmd +``` + +--- + +## 📋 Principales fonctionnalités + +> [!NOTE] +> Contrairement à la version originale, ce fork est adapté pour un fonctionnement stable dans les sessions sans écran (headless)/SSH et les laboratoires locaux. + +* 📂 **Édition multi-fichiers** — Modifie plusieurs fichiers simultanément dans votre espace de travail avec confirmation préalable des modifications. +* 🔒 **Commandes shell sécurisées** — Exécute toutes les commandes du terminal dans le conteneur Docker intégré (bac à sable/sandbox) ou sur le système hôte. +* 🧠 **Raisonnement en plusieurs étapes (PAV)** — Construit de manière indépendante un plan d'exécution des tâches, teste le code et débogue ses propres erreurs. +* 💾 **Historique persistant des conversations** — Enregistre le contexte complet de la conversation et l'état de l'espace de travail entre les sessions. +* 🔌 **Système de plug-ins** — Étendez les capacités de l'agent avec des *compétences (Skills)* personnalisées et des serveurs MCP (Model Context Protocol). + +--- + +## ⚙️ Configuration + +### 1. Configuration du projet (`GEMINI.md`) +Créez un fichier `GEMINI.md` à la racine de votre projet pour fournir un contexte spécifique et des règles de développement à l'agent IA : + +```markdown +# Contexte du projet + +- Ce projet utilise FastAPI et Pydantic v2. +- Utilisez toujours `model_dump()` à la place de la méthode obsolète `dict()`. +- RÈGLE STRICTE : Pas de mots de passe codés en dur dans le code. Importez tous les secrets depuis le fichier `.env`. +``` + +### 2. Paramètres globaux (`~/.gemini/antigravity-cli/settings.json`) +Le fichier de configuration globale contrôle les autorisations d'exécution des outils, les scripts de statusline/titre et les serveurs MCP : + +- **Linux/Unix** : `~/.gemini/antigravity-cli/settings.json` +- **macOS** : `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows** : `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} +``` + +### 3. Agents spécialisés +Vous pouvez décrire des rôles et des instructions personnalisés pour les agents IA au format JSON. Chaque agent doit avoir son propre répertoire contenant un fichier `agent.json` : + +- **Agents Globaux (Linux/Unix)** : `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **Agents Globaux (macOS)** : `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **Agents Globaux (Windows)** : `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **Agents Locaux (Espace de Travail)** : `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "Analyse le code à la recherche de vulnérabilités avant le commit", + "instructions": "Vérifier les modifications pour :\n- Les 10 vulnérabilités majeures de l'OWASP\n- Les fuites de clés API ou de secrets\n- L'exactitude de la configuration du pare-feu/nftables" +} +``` + +--- + +## 🔐 Méthodes d'authentification + +| Méthode | Commande / Variable | Limites et fonctionnalités | +| :--- | :--- | :--- | +| **Authentification Google (Navigateur)** | Automatiquement lors du premier `agy` | Connexion standard. Limite gratuite : 60 requêtes/minute, 1000 requêtes/jour | +| **Clé API (Hors ligne)** | `export GEMINI_API_KEY="X"` | Recommandé pour les serveurs et l'automatisation | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | Niveau entreprise avec l'infrastructure cloud Google Cloud | +| **Déconnexion** | `/logout` | Effacement des jetons de session locale | + +--- + +## 🔧 Référence des commandes + +### Ligne de commande +```bash +agy # Démarrer une session interactive +agy -p "Prompt" # Exécution unique sans entrer dans le chat +agy -c # Continuer la dernière conversation incomplète +agy --conversation # Charger une session par un ID spécifique +agy --sandbox # Exécuter dans un conteneur Docker isolé +agy update # Mettre à jour le binaire vers la dernière version +agy plugin list # Lister les plug-ins installés +``` + +### Commandes slash dans l'interface de chat +* `/help` — Aide sur les outils disponibles. +* `/settings` — Configuration interactive des paramètres. +* `/usage` — Statistiques des jetons consommés et quota. +* `/diff` — Afficher les modifications actuelles non sauvegardées dans le projet. +* `/statusline` — Configurer l'affichage de la ligne d'état du terminal. + +--- + +## 🔄 Migration depuis Gemini CLI + +> [!WARNING] +> L'interface de ligne de commande originale Gemini CLI (`gemini`) abandonnera le support pour les comptes non professionnels le **18 juin 2026**. La migration vers Antigravity CLI est requise. + +### Étapes pour une transition rapide +1. Installez le nouveau client : `curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. Migrez les fichiers de configuration des agents personnalisés locaux (par exemple, convertissez les agents personnalisés de YAML dans `~/.gemini/agents/` en fichiers JSON sous `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json`). +3. Mettez à jour les configurations CI/CD dans GitHub Actions, en remplaçant les appels `gemini` par `agy`. +4. Désinstallez l'ancienne bibliothèque : `npm uninstall -g @google/gemini-cli` + +### Tableau comparatif + +| Fonctionnalité | Gemini CLI (Obsolète) | Antigravity CLI (Moderne) | +| :--- | :--- | :--- | +| **Plateforme de développement** | Node.js / TypeScript | Go (Binaire natif compilé) | +| **Nom de la commande** | `gemini` | `agy` | +| **Vitesse de démarrage** | ~1.2s (démarrage Node.js) | **~0.05s (démarrage natif instantané)** | +| **Fichier de configuration** | `GEMINI.md` | `GEMINI.md` | +| **Mise à jour automatique** | Via `npm update` | Mécanisme d'auto-mise à jour intégré | +| **Statut du support** | ⛔ EOL (18 juin 2026) | ✅ Développement actif (Upstream) | + +--- + +## 📁 Structure du dépôt + +``` +antigravity-cli/ +├── install.sh # Programme d'installation pour Linux/macOS (hors ligne/en ligne) +├── install.ps1 # Programme d'installation pour Windows PowerShell (hors ligne/en ligne) +├── install.cmd # Programme d'installation pour Windows CMD +├── Makefile # Cibles d'automatisation (make install/reinstall/uninstall) +├── GEMINI.md # Modèle de fichier de contexte de projet +├── packages/ # Distribution hors ligne locale +│ ├── manifests/ # Manifestes de version pour toutes les plateformes +│ └── binaries/ # (Créé manuellement pour le mode hors ligne) +└── CHANGELOG.md # Journal des modifications et des versions +``` + +--- + +## 🤝 Contribution & Communauté + +Ce dépôt est un fork communautaire indépendant du projet original [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli). + +**Nos améliorations :** +* 🌍 Prise en charge de la localisation multilingue pour la documentation et les guides. +* 📦 Autonomie : capacité d'installation hors ligne sans téléchargements depuis les API Google. +* 🛠️ Praticité : ajout d'un `Makefile` pour un cycle de vie simplifié de l'outil. +* 🛡️ Sécurité : corrections régulières de bogues et améliorations de l'environnement de bac à sable. + +--- + +## 📜 Avis légal & Propriété intellectuelle + +* **Liens officiels** : [Dépôt de documentation officiel](https://github.com/google-antigravity/antigravity-cli) · [Base de code officielle de la CLI](https://github.com/google-gemini/gemini-cli) · [Site officiel](https://antigravity.google) +* **Conditions d'utilisation** : [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **Statut juridique du fork :** +> Ce dépôt est une copie indépendante non commerciale (Community Fork) du client d'origine. Ce **n'est pas** un produit officiel de Google LLC. Google LLC n'est pas responsable des performances, des modifications ou de la sécurité de ce fork. +> +> **Licences et droits d'auteur :** +> Le logiciel d'origine est distribué sous la [Licence Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). Tout le code d'origine est la propriété intellectuelle de **Copyright © 2025 Google LLC**. +> +> **Utilisation de la marque :** +> Le nom "Antigravity CLI" et les logos associés sont utilisés dans les limites de l'usage coutumier uniquement pour décrire l'origine, la compatibilité et le but fonctionnel du logiciel. Ce projet ne revendique aucun droit de propriété sur les marques de Google LLC. +> +> **Exclusion de garantie :** +> Le logiciel est fourni "EN L'ÉTAT", SANS GARANTIE D'AUCUNE SORTE, expresse ou implicite. Vous assumez toute responsabilité et tous les risques associés à son utilisation. + +> [!CAUTION] +> Les agents de codage IA fonctionnent de manière autonome. Examinez toujours attentivement les blocs de diff proposés et les commandes avant de confirmer l'exécution, en particulier lorsque vous travaillez avec des fichiers système ou la configuration du pare-feu. diff --git a/README-PT.md b/README-PT.md new file mode 100644 index 000000000..12d7a231f --- /dev/null +++ b/README-PT.md @@ -0,0 +1,241 @@ +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

+ +

+ Antigravity CLI Logo +

+ +

🚀 Antigravity CLI

+ +

+ Fork da comunidade e versão offline de google-antigravity/antigravity-cli com configuração automática de statusline e título de janela +

+ +

+ GitHub Fork + Version + License + Supported Platforms +

+ +

+ 🤖 Agente de codificação de IA diretamente no seu terminal. Compreende o contexto da sua base de código, cria e edita arquivos, executa comandos seguros em uma sandbox e resolve tarefas arquitetônicas complexas em um único prompt. +

+ +--- + +## ⚡ Início Rápido + +### Instalação Instantânea (Foco em Offline e Sem Dependências) +Este fork faz o download de binários pré-compilados diretamente dos **GitHub Release Assets** (em vez dos servidores de API da Google). A instalação totalmente offline também é suportada se os arquivos necessários tiverem sido previamente carregados na pasta `packages/binaries/`. + +#### 🐧 Linux e 🍎 macOS +```bash +# Instalação via rede a partir do repositório do fork: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# OU instalação offline a partir de um repositório local: +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (Opcional: baixe os arquivos da plataforma em packages/binaries/) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# Instalação via rede: +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# OU instalação offline a partir de um repositório clonado: +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# Instalação via rede: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# OU instalação offline a partir de um repositório clonado: +install.cmd +``` + +--- + +## 📋 Recursos Principais + +> [!NOTE] +> Ao contrário da versão original, este fork é adaptado para uma operação estável em sessões headless/SSH e laboratórios domésticos locais. + +* 📂 **Edição de Múltiplos Arquivos** — Edita vários arquivos simultaneamente no seu espaço de trabalho com confirmação prévia das alterações. +* 🔒 **Comandos de Shell Seguros** — Executa qualquer comando de terminal no container Docker integrado (sandbox) ou no sistema host. +* 🧠 **Raciocínio em Várias Etapas (PAV)** — Constrói de forma independente um plano de execução de tarefas, testa o código e depura os seus próprios erros. +* 💾 **Histórico de Conversa Persistente** — Salva o contexto completo da conversa e o estado do espaço de trabalho entre as sessões. +* 🔌 **Sistema de Plugins** — Estenda as capacidades do agente com *habilidades (Skills)* personalizadas e servidores MCP (Model Context Protocol). + +--- + +## ⚙️ Configuração + +### 1. Configuração do Projeto (`GEMINI.md`) +Crie um arquivo `GEMINI.md` na raiz do seu projeto para fornecer contexto específico e regras de desenvolvimento ao agente de IA: + +```markdown +# Contexto do Projeto + +- Este projeto usa FastAPI e Pydantic v2. +- Sempre use `model_dump()` em vez do obsoleto `dict()`. +- REGRA ESTRITA: Sem senhas expostas no código (hardcoded). Importe todos os segredos do `.env`. +``` + +### 2. Configurações Globais (`~/.gemini/antigravity-cli/settings.json`) +O arquivo de configuração global controla as permissões de execução de ferramentas, scripts de statusline/título e servidores MCP: + +- **Linux/Unix**: `~/.gemini/antigravity-cli/settings.json` +- **macOS**: `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows**: `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} +``` + +### 3. Agentes Especializados +Você pode descrever funções e instruções personalizadas para agentes de IA no formato JSON. Cada agente deve ter seu próprio diretório contendo um arquivo `agent.json`: + +- **Agentes Globais (Linux/Unix)**: `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **Agentes Globais (macOS)**: `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **Agentes Globais (Windows)**: `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **Agentes Locais (Espaço de Trabalho)**: `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "Analisa o código em busca de vulnerabilidades antes do commit", + "instructions": "Verificar alterações para:\n- Vulnerabilidades OWASP Top 10\n- Vazamento de chaves de API ou segredos\n- Correção da configuração do firewall/nftables" +} +``` + +--- + +## 🔐 Métodos de Autenticação + +| Método | Comando / Variável | Limitações e Recursos | +| :--- | :--- | :--- | +| **Google Auth (Navegador)** | Automaticamente no primeiro `agy` | Login padrão. Limite gratuito: 60 req/min, 1000 req/dia | +| **Chave de API (Offline)** | `export GEMINI_API_KEY="X"` | Recomendado para servidores e automação | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | Nível corporativo com infraestrutura em nuvem do Google Cloud | +| **Sair (Sign Out)** | `/logout` | Limpeza de tokens de sessão local | + +--- + +## 🔧 Referência de Comandos + +### Linha de Comando +```bash +agy # Iniciar uma sessão interativa +agy -p "Prompt" # Execução única sem entrar no chat +agy -c # Continuar a última conversa incompleta +agy --conversation # Carregar uma sessão por um ID específico +agy --sandbox # Executar em um container Docker isolado +agy update # Atualizar o binário para a versão mais recente +agy plugin list # Listar os plugins instalados +``` + +### Comandos de Barra (Slash Commands) na Interface de Chat +* `/help` — Ajuda sobre ferramentas disponíveis. +* `/settings` — Configuração interativa de definições. +* `/usage` — Estatísticas de tokens gastos e cota. +* `/diff` — Visualizar as alterações atuais não salvas no projeto. +* `/statusline` — Configurar a exibição da linha de status do terminal. + +--- + +## 🔄 Migração do Gemini CLI + +> [!WARNING] +> A interface de linha de comando original Gemini CLI (`gemini`) deixará de oferecer suporte para contas não corporativas em **18 de junho de 2026**. A migração para o Antigravity CLI é necessária. + +### Passos para Transição Rápida +1. Instale o novo cliente: `curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. Migre os arquivos de configuração de agentes personalizados locais (por exemplo, converta agentes personalizados de YAML dentro de `~/.gemini/agents/` em arquivos JSON em `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json`). +3. Atualize as configurações de CI/CD no GitHub Actions, substituindo as chamadas de `gemini` por `agy`. +4. Desinstale a biblioteca antiga: `npm uninstall -g @google/gemini-cli` + +### Tabela de Comparação + +| Recurso | Gemini CLI (Legado) | Antigravity CLI (Moderno) | +| :--- | :--- | :--- | +| **Plataforma de Desenvolvimento** | Node.js / TypeScript | Go (Binário Nativo Compilado) | +| **Nome do Comando** | `gemini` | `agy` | +| **Velocidade de Inicialização** | ~1.2s (inicialização do Node.js) | **~0.05s (inicialização nativa instantânea)** | +| **Arquivo de Configuração** | `GEMINI.md` | `GEMINI.md` | +| **Atualização Automática** | Via `npm update` | Mecanismo de auto-atualização integrado | +| **Status do Suporte** | ⛔ Fim da vida útil (18 de junho de 2026) | ✅ Desenvolvimento Ativo (Upstream) | + +--- + +## 📁 Estrutura do Repositório + +``` +antigravity-cli/ +├── install.sh # Instalador para Linux/macOS (offline/online) +├── install.ps1 # Instalador para Windows PowerShell (offline/online) +├── install.cmd # Instalador para Windows CMD +├── Makefile # Metas de automação (make install/reinstall/uninstall) +├── GEMINI.md # Modelo de arquivo de contexto de projeto +├── packages/ # Distribuição offline local +│ ├── manifests/ # Manifestes de versão para todas as plataformas +│ └── binaries/ # (Criado manualmente para o modo offline) +└── CHANGELOG.md # Registro de alterações e lançamentos +``` + +--- + +## 🤝 Contribuição & Comunidade + +Este repositório é um fork da comunidade independente do projeto original [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli). + +**Nossas Melhorias:** +* 🌍 Suporte de localização em vários idiomas para documentos e guias. +* 📦 Autonomia: capacidade de instalação offline sem downloads da API da Google. +* 🛠️ Conveniência: adicionado `Makefile` para um ciclo de vida simplificado da ferramenta. +* 🛡️ Segurança: correções regulares de bugs e melhorias no ambiente de sandbox. + +--- + +## 📜 Aviso Legal & Marcas Registradas + +* **Links Oficiais**: [Repositório Oficial de Documentação](https://github.com/google-antigravity/antigravity-cli) · [Base de Código Oficial do CLI](https://github.com/google-gemini/gemini-cli) · [Website Oficial](https://antigravity.google) +* **Termos de Uso**: [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **Status Legal do Fork:** +> Este repositório é uma cópia independente não comercial (Fork da Comunidade) do cliente original. Ele **não é** um produto oficial da Google LLC. A Google LLC não se responsabiliza pelo desempenho, modificações ou segurança deste fork. +> +> **Licenciamento & Direitos Autorais:** +> O software original é distribuído sob a [Licença Apache 2.0](https://www.apache.org/licenses/LICENSE-2.0). Todo o código original é propriedade intelectual de **Copyright © 2025 Google LLC**. +> +> **Uso de Marca Registrada:** +> O nome "Antigravity CLI" e os logotipos associados são usados dentro dos limites do Uso Habitual exclusivamente para descrever a origem, compatibilidade e propósito funcional do software. Este projeto não reivindica a propriedade de nenhuma marca registrada da Google LLC. +> +> **Isenção de Garantia:** +> O software é fornecido "COMO ESTÁ", SEM GARANTIAS DE QUALQUER TIPO, expressas ou implícitas. Você assume toda a responsabilidade e riscos associados ao seu uso. + +> [!CAUTION] +> Os agentes de codificação de IA operam de forma autônoma. Sempre revise cuidadosamente os blocos de diff propostos e os comandos antes de confirmar a execução, especialmente ao trabalhar com arquivos do sistema ou configuração do firewall. diff --git a/README-UA.md b/README-UA.md new file mode 100644 index 000000000..0478cc9b1 --- /dev/null +++ b/README-UA.md @@ -0,0 +1,241 @@ +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

+ +

+ Antigravity CLI Logo +

+ +

🚀 Antigravity CLI

+ +

+ Ком'юніті-форк та покращена офлайн-версія google-antigravity/antigravity-cli з автоналаштуванням статуслайну та заголовка вікна +

+ +

+ GitHub Fork + Version + License + Supported Platforms +

+ +

+ 🤖 ШІ-кодинг агент прямо у вашому терміналі. Розуміє контекст вашої кодової бази, створює та редагує файли, виконує безпечні команди в пісочниці та вирішує комплексні архітектурні завдання за один промпт. +

+ +--- + +## ⚡ Швидкий старт + +### Миттєве встановлення (Offline-first & Zero-dependency) +Цей форк завантажує попередньо скомпільовані бінарні файли прямо з **GitHub Release Assets** (замість серверів Google API). Також підтримується повністю автономне офлайн-встановлення, якщо потрібні архіви було попередньо завантажено в папку `packages/binaries/`. + +#### 🐧 Linux та 🍎 macOS +```bash +# Мережеве встановлення з репозиторію форку: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# АБО автономне офлайн-встановлення з локального репозиторію: +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (Опціонально: завантажте архіви платформ у packages/binaries/) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# Мережеве встановлення: +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# АБО офлайн-встановлення з клонованого репозиторію: +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# Мережеве встановлення: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# АБО офлайн-встановлення з клонованого репозиторію: +install.cmd +``` + +--- + +## 📋 Основні можливості + +> [!NOTE] +> На відміну від оригінальної версії, цей форк адаптований для стабільної роботи в headless/SSH-сесіях та локальних home-лабораторіях. + +* 📂 **Мультифайлове редагування** — Редагує декілька файлів одночасно у вашому робочому просторі з попереднім підтвердженням змін. +* 🔒 **Безпечні Shell-команди** — Виконує будь-які термінальні команди у вбудованому Docker-контейнері (пісочниці) або на хост-системі. +* 🧠 **Багатокрокові міркування (PAV)** — Самостійно будує план виконання завдання, тестує код і виправляє власні помилки. +* 💾 **Постійна історія розмов** — Зберігає повний контекст розмови та стан робочого простору між сесіями. +* 🔌 **Система плагінів** — Розширюйте можливості агента за допомогою кастомних *Skills* та MCP-серверів (Model Context Protocol). + +--- + +## ⚙️ Конфігурація + +### 1. Конфігурація проєкту (`GEMINI.md`) +Створіть файл `GEMINI.md` у корені вашого проєкту, щоб передати ШІ-агенту специфічний контекст та правила розробки: + +```markdown +# Контекст проєкту + +- Цей проєкт використовує FastAPI та Pydantic v2. +- Завжди використовуйте `model_dump()` замість застарілого `dict()`. +- СУВОРЕ ПРАВИЛО: Жодних хардкод-паролів у коді. Всі секрети імпортувати з `.env`. +``` + +### 2. Глобальні налаштування (`~/.gemini/antigravity-cli/settings.json`) +Глобальний файл налаштувань керує дозволами на виконання інструментів, скриптами статуслайну/заголовка та MCP-серверами: + +- **Linux/Unix**: `~/.gemini/antigravity-cli/settings.json` +- **macOS**: `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows**: `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} +``` + +### 3. Спеціалізовані агенти +Ви можете описувати кастомні ролі та інструкції для ШІ-агентів у форматі JSON. Кожен агент повинен мати власну директорію, яка містить файл `agent.json`: + +- **Глобальні агенти (Linux/Unix)**: `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **Глобальні агенти (macOS)**: `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **Глобальні агенти (Windows)**: `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **Локальні агенти (Робочий простір)**: `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "Аналізує код на вразливості перед комітом", + "instructions": "Перевіряти зміни на:\n- OWASP Top 10 вразливості\n- Витік API ключів чи секретів\n- Правильність налаштування firewall/nftables" +} +``` + +--- + +## 🔐 Способи авторизації + +| Метод | Команда / Змінна | Обмеження та особливості | +| :--- | :--- | :--- | +| **Google Auth (Browser)** | Автоматично при першому `agy` | Стандартний вхід. Безкоштовний ліміт: 60 з/хв, 1000 з/добу | +| **API Key (Offline)** | `export GEMINI_API_KEY="X"` | Рекомендовано для серверів та автоматизації | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | Enterprise-рівень з хмарною інфраструктурою Google Cloud | +| **Вихід з профілю** | `/logout` | Очищення локальних токенів сесії | + +--- + +## 🔧 Довідка команд + +### Командний рядок +```bash +agy # Запустити інтерактивну сесію +agy -p "Запит" # Одноразове виконання без входу в чат +agy -c # Продовжити останню незавершену розмову +agy --conversation # Завантажити сесію за конкретним ідентифікатором +agy --sandbox # Запустити в ізольованому Docker-контейнері +agy update # Оновити бінарний файл до останньої версії +agy plugin list # Список встановлених плагінів +``` + +### Slash-команди в інтерфейсі чату +* `/help` — Довідка з доступних інструментів. +* `/settings` — Інтерактивне налаштування параметрів. +* `/usage` — Статистика витрачених токенів та квоти. +* `/diff` — Перегляд поточних незбережених змін у проєкті. +* `/statusline` — Налаштування відображення статус-бару терміналу. + +--- + +## 🔄 Злиття та міграція з Gemini CLI + +> [!WARNING] +> Оригінальний Gemini CLI (`gemini`) припиняє підтримку для non-enterprise акаунтів **18 червня 2026**. Перехід на Antigravity CLI є обов'язковим. + +### Кроки для швидкого переходу +1. Встановіть новий клієнт: `curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. Мігруйте локальні файли конфігурації кастомних агентів (наприклад, конвертуйте конфігурації YAML у папці `~/.gemini/agents/` в JSON-файли за шляхом `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json`). +3. Оновіть CI/CD конфігурації в GitHub Actions, замінивши виклики `gemini` на `agy`. +4. Видаліть стару бібліотеку: `npm uninstall -g @google/gemini-cli` + +### Порівняльна таблиця + +| Характеристика | Gemini CLI (Застарілий) | Antigravity CLI (Сучасний) | +| :--- | :--- | :--- | +| **Платформа розробки** | Node.js / TypeScript | Go (Native Compiled Binary) | +| **Назва команди** | `gemini` | `agy` | +| **Швидкість старту** | ~1.2 сек (запуск Node.js) | **~0.05 сек (миттєвий нативний старт)** | +| **Файл конфігурації** | `GEMINI.md` | `GEMINI.md` | +| **Авто-оновлення** | Через `npm update` | Вбудований механізм self-update | +| **Статус підтримки** | ⛔ EOL (18.06.2026) | ✅ Активна розробка (Upstream) | + +--- + +## 📁 Структура репозиторію + +``` +antigravity-cli/ +├── install.sh # Інсталятор для Linux/macOS (офлайн/онлайн) +├── install.ps1 # Інсталятор для Windows PowerShell (офлайн/онлайн) +├── install.cmd # Інсталятор для Windows CMD +├── Makefile # Автоматизовані цілі (make install/reinstall/uninstall) +├── GEMINI.md # Шаблон файлу контексту проєкту +├── packages/ # Локальний офлайн-дистрибутив +│ ├── manifests/ # Маніфести версій для всіх платформ +│ └── binaries/ # (Створюється вручну для офлайн-режиму) +└── CHANGELOG.md # Журнал версій та релізів +``` + +--- + +## 🤝 Спільнота та розробка (Contributing) + +Цей репозиторій є незалежним ком'юніті-форком оригінального проєкту [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli). + +**Наші покращення:** +* 🇺🇦 Повна українська локалізація документації та гайдів. +* 📦 Автономність: можливість офлайн-встановлення без завантажень з Google API. +* 🛠️ Зручність: додано `Makefile` для спрощеного життєвого циклу інструменту. +* 🛡️ Безпека: регулярні баг-фікси та покращення роботи в пісочниці (сандбоксі). + +--- + +## 📜 Юридичний статус та дисклеймер (Legal & Trademark Notice) + +* **Офіційні посилання**: [Офіційний репозиторій документації](https://github.com/google-antigravity/antigravity-cli) · [Офіційна кодова база CLI](https://github.com/google-gemini/gemini-cli) · [Офіційний сайт](https://antigravity.google) +* **Правила використання**: [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **Правовий статус форку:** +> Цей репозиторій є незалежною некомерційною копією (Community Fork) оригінального клієнта. Він **не є** офіційним продуктом компанії Google LLC. Google LLC не несе відповідальності за працездатність, модифікації або безпеку цього форку. +> +> **Ліцензування та авторські права:** +> Оригінальне програмне забезпечення розповсюджується під ліцензією [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). Весь оригінальний код є інтелектуальною власністю **Copyright © 2025 Google LLC**. +> +> **Використання торгових марок:** +> Назва "Antigravity CLI" та пов'язані логотипи використовуються в межах дозволеного використання (Customary Use) виключно для опису походження, сумісності та функціонального призначення програмного забезпечення. Цей проєкт не претендує на володіння будь-якими торговими марками Google LLC. +> +> **Обмеження відповідальності (Disclaimer of Warranty):** +> Програмне забезпечення надається на умовах «ЯК Є» (AS IS), БЕЗ БУДЬ-ЯКИХ ГАРАНТІЙ, явних чи неявних. Ви берете на себе всю відповідальність та ризики, пов'язані з його використанням. + +> [!CAUTION] +> ШІ-агенти для кодування працюють автономно. Завжди уважно перевіряйте пропоновані diff-блоки та команди перед підтвердженням виконання, особливо при роботі з системними файлами чи конфігурацією фаєрволу. diff --git a/README-ZH.md b/README-ZH.md new file mode 100644 index 000000000..5a816eab0 --- /dev/null +++ b/README-ZH.md @@ -0,0 +1,241 @@ +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

+ +

+ Antigravity CLI Logo +

+ +

🚀 Antigravity CLI

+ +

+ 社区分叉与硬化离线版 google-antigravity/antigravity-cli,支持状态栏和窗口标题的自动设置 +

+ +

+ GitHub Fork + Version + License + Supported Platforms +

+ +

+ 🤖 直接在您的终端中运行 of AI 编码智能体。理解您的代码库上下文,创建和编辑文件,在沙箱中执行安全命令,并在单个提示词中解决复杂的架构任务。 +

+ +--- + +## ⚡ 快速开始 + +### 即时安装(离线优先与零依赖) +此分叉版本直接从 **GitHub Release Assets**(而不是 Google API 服务器)下载预编译的二进制文件。如果预先将所需的归档文件加载到 `packages/binaries/` 文件夹中,则同样支持完全自治的离线安装。 + +#### 🐧 Linux 和 🍎 macOS +```bash +# 从分叉存储库进行网络安装: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# 或者从本地存储库进行离线安装: +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (可选:将对应平台的归档文件下载到 packages/binaries/ 中) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# 网络安装: +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# 或者从克隆的存储库进行离线安装: +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# 网络安装: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# 或者从克隆的存储库进行离线安装: +install.cmd +``` + +--- + +## 📋 核心功能 + +> [!NOTE] +> 与原始版本不同,此分叉版本经过特别优化,适用于无头(headless)环境、SSH 会话以及本地家庭实验室(Home Labs)中的稳定运行。 + +* 📂 **多文件编辑** — 在您的工作区中同时编辑多个文件,并在应用更改前进行确认。 +* 🔒 **安全 Shell 命令** — 在内置的 Docker 容器(沙箱)或主机系统上执行任意终端命令。 +* 🧠 **多步推理 (PAV)** — 独立构建任务执行计划,测试代码并调试自身的错误。 +* 💾 **持久化会话历史记录** — 在会话之间保存完整的对话上下文和工作区状态。 +* 🔌 **插件系统** — 使用自定义 *技能 (Skills)* 和 MCP(Model Context Protocol,模型上下文协议)服务器扩展智能体的能力。 + +--- + +## ⚙️ 配置方式 + +### 1. 项目配置 (`GEMINI.md`) +在项目的根目录下创建一个 `GEMINI.md` 文件,为 AI 智能体提供特定的项目上下文和开发规则: + +```markdown +# 项目上下文 + +- 本项目使用 FastAPI 和 Pydantic v2。 +- 请始终使用 `model_dump()` 代替已废弃的 `dict()`。 +- 严格规则:严禁在代码中硬编码任何密码。所有机密信息必须从 `.env` 文件导入。 +``` + +### 2. 全局设置 (`~/.gemini/antigravity-cli/settings.json`) +全局配置文件控制工具执行权限、状态栏/窗口标题脚本以及 MCP 服务器: + +- **Linux/Unix**: `~/.gemini/antigravity-cli/settings.json` +- **macOS**: `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows**: `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} +``` + +### 3. 专业智能体 +您可以使用 JSON 格式定义 AI 智能体的自定义角色和说明。每个智能体必须有自己的目录,其中包含一个 `agent.json` 文件: + +- **全局智能体 (Linux/Unix)**: `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **全局智能体 (macOS)**: `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **全局智能体 (Windows)**: `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **本地智能体 (项目工作区)**: `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "在提交前分析代码漏洞", + "instructions": "检查更改内容:\n- OWASP Top 10 漏洞\n- API 密钥或凭证泄露\n- 防火墙/nftables 配置正确性" +} +``` + +--- + +## 🔐 身份验证方式 + +| 方法 | 命令 / 环境变量 | 限制与特点 | +| :--- | :--- | :--- | +| **Google 身份验证(浏览器)** | 首次运行 `agy` 时自动触发 | 标准登录。免费限制:60 次请求/分钟,1000 次请求/天 | +| **API 密钥(离线)** | `export GEMINI_API_KEY="X"` | 推荐用于服务器环境和自动化工作流 | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | 企业级方案,使用 Google Cloud 云基础设施 | +| **注销登录** | `/logout` | 清除本地会话令牌 | + +--- + +## 🔧 常用命令参考 + +### 命令行工具 +```bash +agy # 启动交互式会话 +agy -p "提示词" # 单次执行,不进入聊天界面 +agy -c # 继续上一次未完成的对话 +agy --conversation # 通过特定 ID 加载会话 +agy --sandbox # 在隔离的 Docker 容器中运行 +agy update # 更新二进制文件到最新版本 +agy plugin list # 列出已安装的插件 +``` + +### 聊天界面中的斜杠命令 (Slash Commands) +* `/help` — 获取可用工具的帮助信息。 +* `/settings` — 交互式配置设置项。 +* `/usage` — 消耗的令牌(Token)数量和配额统计。 +* `/diff` — 查看项目中当前未保存的更改。 +* `/statusline` — 配置终端状态栏的显示。 + +--- + +## 🔄 从 Gemini CLI 迁移 + +> [!WARNING] +> 原始的 Gemini CLI (`gemini`) 将于 **2026 年 6 月 18 日**起停止对非企业账户的支持。请及时迁移到 Antigravity CLI。 + +### 快速迁移步骤 +1. 安装新客户端:`curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. 迁移本地自定义智能体配置文件(例如,将 `~/.gemini/agents/` 下的 YAML 格式自定义智能体转换为 `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` 下的 JSON 文件)。 +3. 更新 GitHub Actions 中的 CI/CD 配置,将所有的 `gemini` 调用替换为 `agy`。 +4. 卸载旧的依赖库:`npm uninstall -g @google/gemini-cli` + +### 对比表 + +| 功能 / 属性 | Gemini CLI (旧版) | Antigravity CLI (新版) | +| :--- | :--- | :--- | +| **开发语言/平台** | Node.js / TypeScript | Go (原生编译的二进制文件) | +| **执行命令** | `gemini` | `agy` | +| **启动速度** | ~1.2秒 (Node.js 启动延迟) | **~0.05秒 (原生瞬间启动)** | +| **项目配置文件** | `GEMINI.md` | `GEMINI.md` | +| **自动更新** | 通过 `npm update` | 内置的自我更新机制 | +| **维护状态** | ⛔ 已停止支持 (2026年6月18日) | ✅ 持续活跃开发 (上游支持) | + +--- + +## 📁 存储库结构 + +``` +antigravity-cli/ +├── install.sh # Linux/macOS 安装程序 (离线/在线) +├── install.ps1 # Windows PowerShell 安装程序 (离线/在线) +├── install.cmd # Windows CMD 安装程序 +├── Makefile # 自动化目标 (make install/reinstall/uninstall) +├── GEMINI.md # 项目上下文模板文件 +├── packages/ # 本地离线分发包 +│ ├── manifests/ # 适用于所有平台的版本清单 +│ └── binaries/ # (离线模式下需手动创建) +└── CHANGELOG.md # 变更日志和发布记录 +``` + +--- + +## 🤝 参与贡献与社区 + +本仓库是原始上游项目 [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli) 的独立社区分叉版本。 + +**我们的改进:** +* 🌍 支持文档和指南的多语言本地化。 +* 📦 自主性:具备完全离线安装能力,无需从 Google API 服务器下载。 +* 🛠️ 便利性:添加了 `Makefile`,简化工具生命周期管理。 +* 🛡️ 安全性:针对沙箱环境进行持续的安全改进和漏洞修复。 + +--- + +## 📜 法律声明与商标说明 + +* **官方链接**: [官方文档仓库](https://github.com/google-antigravity/antigravity-cli) · [官方 CLI 代码库](https://github.com/google-gemini/gemini-cli) · [官方网站](https://antigravity.google) +* **使用条款**: [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **分叉版法律地位:** +> 本存储库是原始客户端的独立非商业副本(社区分叉版本)。它**不是** Google LLC 的官方产品。Google LLC 对此分叉版本的性能、修改或安全性不承担任何责任。 +> +> **许可与版权信息:** +> 原始软件基于 [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0) 协议分发。所有原始代码的知识产权均归 **Copyright © 2025 Google LLC** 所有。 +> +> **商标使用说明:** +> "Antigravity CLI" 名称及相关徽标仅在合理使用(Customary Use)限制范围内使用,以描述软件的来源、兼容性和功能用途。本分叉项目不声称拥有 Google LLC 任何商标的所有权。 +> +> **免责声明:** +> 本软件按“原样”提供,不提供任何明示或暗示的保证。您需自行承担使用该软件的所有责任和风险。 + +> [!CAUTION] +> AI 编码智能体运行具有自主性。在确认执行前,请务必仔细检查其建议的 diff 代码块和执行命令,尤其是在处理系统文件或防火墙配置时。 diff --git a/README.md b/README.md index aeff0f6bf..06e5d46d6 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,241 @@ -# Antigravity CLI +

+ English | 中文 | Español | Français | Português | Українська | Deutsch +

-Antigravity CLI understands your codebase, makes edits with your permission, and executes commands — right from your terminal. +

+ Antigravity CLI Logo +

-- **Official Docs**: [antigravity.google/docs/cli-overview](https://antigravity.google/docs/cli-overview) -- **Official Website**: [antigravity.google/product/antigravity-cli](https://antigravity.google/product/antigravity-cli) +

🚀 Antigravity CLI

-![Antigravity CLI Demo](agy-cli-demo.gif) +

+ Community-Fork & Hardened Offline Version of google-antigravity/antigravity-cli with automatic statusline & window title setup +

---- +

+ GitHub Fork + Version + License + Supported Platforms +

-Antigravity CLI brings the core capabilities of Antigravity 2.0 (multi-step reasoning, multi-file editing, tool calling, and persistent history) directly to your terminal. It is optimized for keyboard-driven workflows and remote SSH sessions with minimal resource overhead. +

+ 🤖 AI coding agent directly in your terminal. Understands the context of your codebase, creates and edits files, executes secure commands in a sandbox, and solves complex architectural tasks in a single prompt. +

--- -## Features at a Glance +## ⚡ Quick Start -| Feature | Antigravity CLI | Antigravity 2.0 | -| :--- | :--- | :--- | -| **Primary Focus** | Speed, keyboard efficiency, low overhead | Comprehensiveness, visual orchestration, project management | -| **Interface** | Terminal User Interface (TUI) | Full Rich GUI Application | -| **Workflows** | SSH/Remote sessions, keyboard-first | Local workspaces, heavy orchestration | -| **Agent Engine** | Shared Core Agent Engine | Shared Core Agent Engine | +### Instant Installation (Offline-first & Zero-dependency) +This fork downloads precompiled binaries directly from **GitHub Release Assets** (instead of Google API servers). Completely offline installation is also supported if the required archives were preloaded into the `packages/binaries/` folder. + +#### 🐧 Linux and 🍎 macOS +```bash +# Network installation from the fork repository: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash + +# OR offline installation from a local repository: +git clone https://github.com/weby-homelab/antigravity-cli.git +cd antigravity-cli +# (Optional: download platform archives to packages/binaries/) +make install +``` + +#### 🪟 Windows PowerShell +```powershell +# Network installation: +irm https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.ps1 | iex + +# OR offline installation from a cloned repository: +.\install.ps1 +``` + +#### 🪟 Windows CMD +```cmd +# Network installation: +curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.cmd -o install.cmd && install.cmd && del install.cmd + +# OR offline installation from a cloned repository: +install.cmd +``` --- -## Integration +## 📋 Key Features + +> [!NOTE] +> Unlike the original version, this fork is adapted for stable operation in headless/SSH sessions and local home labs. -- **Shared Agent Engine**: Both interfaces run on the same core agent engine. Improvements automatically apply to both. -- **Shared Settings**: Preferences and permissions sync bidirectionally. -- **Session Export**: Export terminal sessions to the Antigravity 2.0 GUI to continue working. +* 📂 **Multi-file Editing** — Edits multiple files simultaneously in your workspace with prior confirmation of changes. +* 🔒 **Secure Shell Commands** — Executes any terminal commands in the built-in Docker container (sandbox) or on the host system. +* 🧠 **Multi-step Reasoning (PAV)** — Independently builds a task execution plan, tests code, and debugs its own errors. +* 💾 **Persistent Conversation History** — Saves the complete conversation context and workspace state between sessions. +* 🔌 **Plugin System** — Extend the agent's capabilities with custom *Skills* and MCP (Model Context Protocol) servers. --- -## Installation +## ⚙️ Configuration -### macOS / Linux -```bash -curl -fsSL https://antigravity.google/cli/install.sh | bash +### 1. Project Configuration (`GEMINI.md`) +Create a `GEMINI.md` file in the root of your project to provide specific context and development rules to the AI agent: + +```markdown +# Project Context + +- This project uses FastAPI and Pydantic v2. +- Always use `model_dump()` instead of the deprecated `dict()`. +- STRICT RULE: No hardcoded passwords in code. Import all secrets from `.env`. ``` -### Windows PowerShell -```powershell -irm https://antigravity.google/cli/install.ps1 | iex +### 2. Global Settings (`~/.gemini/antigravity-cli/settings.json`) +The global settings file controls tool execution permissions, statusline/title scripts, and MCP servers: + +- **Linux/Unix**: `~/.gemini/antigravity-cli/settings.json` +- **macOS**: `~/Library/Application Support/antigravity-cli/settings.json` +- **Windows**: `%APPDATA%\antigravity-cli\settings.json` + +```json +{ + "toolPermission": "always-proceed", + "statusLine": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/statusline.sh" + }, + "title": { + "enabled": true, + "command": "/home/user/.gemini/antigravity-cli/title.sh" + }, + "mcpServers": { + "github": { + "command": "npx", + "args": ["-y", "@modelcontextprotocol/server-github"] + } + } +} ``` -### Windows CMD -```cmd -curl -fsSL https://antigravity.google/cli/install.cmd -o install.cmd && install.cmd && del install.cmd +### 3. Specialized Agents +You can describe custom roles and instructions for AI agents in JSON format. Each agent must have its own directory containing an `agent.json` file: + +- **Global Agents (Linux/Unix)**: `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json` +- **Global Agents (macOS)**: `~/Library/Application Support/antigravity-cli/agents/{agent_name}/agent.json` +- **Global Agents (Windows)**: `%APPDATA%\antigravity-cli\agents\{agent_name}\agent.json` +- **Local Agents (Project Workspace)**: `{workspace_root}/.agents/agents/{agent_name}/agent.json` + +```json +{ + "name": "security-reviewer", + "description": "Analyzes code for vulnerabilities before commit", + "instructions": "Check changes for:\n- OWASP Top 10 vulnerabilities\n- Leakage of API keys or secrets\n- Correctness of firewall/nftables configuration" +} ``` --- -## Authentication +## 🔐 Authentication Methods -The CLI authenticates via the system keyring, falling back to Google Sign-In if no active session exists. +| Method | Command / Variable | Limitations & Features | +| :--- | :--- | :--- | +| **Google Auth (Browser)** | Automatically on first `agy` | Standard login. Free limit: 60 req/min, 1000 req/day | +| **API Key (Offline)** | `export GEMINI_API_KEY="X"` | Recommended for servers and automation | +| **Vertex AI** | `export GOOGLE_GENAI_USE_VERTEXAI=true` | Enterprise level with Google Cloud cloud infrastructure | +| **Sign Out** | `/logout` | Clearing local session tokens | -- **Local**: Automatically opens your default browser. -- **Remote / SSH**: Detects SSH sessions and prints an authorization URL to complete login locally. -- **Sign Out**: Run `/logout` to clear saved credentials. +--- -> [!NOTE] -> For enterprise access, connect your GCP project during onboarding. See the Enterprise page for details. +## 🔧 Command Reference + +### Command Line +```bash +agy # Start an interactive session +agy -p "Prompt" # One-time execution without entering chat +agy -c # Continue the last incomplete conversation +agy --conversation # Load a session by specific ID +agy --sandbox # Run in an isolated Docker container +agy update # Update binary to the latest version +agy plugin list # List installed plugins +``` + +### Slash Commands in Chat Interface +* `/help` — Help on available tools. +* `/settings` — Interactive settings configuration. +* `/usage` — Statistics of spent tokens and quota. +* `/diff` — View current unsaved changes in the project. +* `/statusline` — Configure terminal status line display. --- -## Terms of Service & Data Use +## 🔄 Migration from Gemini CLI > [!WARNING] -> AI coding agents are known to have certain security risks, including autonomous code execution, data exfiltration, prompt injection, and supply chain risks. Ensure that you monitor and verify all actions taken by the agent. +> The original Gemini CLI (`gemini`) is deprecating support for non-enterprise accounts on **June 18, 2026**. Migration to Antigravity CLI is required. + +### Steps for Quick Transition +1. Install the new client: `curl -fsSL https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/install.sh | bash` +2. Migrate local custom agent configuration files (e.g. convert custom agents from YAML inside `~/.gemini/agents/` to JSON files at `~/.gemini/antigravity-cli/agents/{agent_name}/agent.json`). +3. Update CI/CD configurations in GitHub Actions, replacing `gemini` calls with `agy`. +4. Uninstall the old library: `npm uninstall -g @google/gemini-cli` + +### Comparison Table + +| Feature | Gemini CLI (Legacy) | Antigravity CLI (Modern) | +| :--- | :--- | :--- | +| **Development Platform** | Node.js / TypeScript | Go (Native Compiled Binary) | +| **Command Name** | `gemini` | `agy` | +| **Startup Speed** | ~1.2s (Node.js startup) | **~0.05s (instant native startup)** | +| **Configuration File** | `GEMINI.md` | `GEMINI.md` | +| **Auto-update** | Via `npm update` | Built-in self-update mechanism | +| **Support Status** | ⛔ EOL (June 18, 2026) | ✅ Active Development (Upstream) | -By using Antigravity CLI, you agree to help improve the product by allowing Google to collect and use your Interactions data, subject to the Google Terms of Service and Google Privacy Policy. You can choose to opt out at any time via your settings. +--- + +## 📁 Repository Structure + +``` +antigravity-cli/ +├── install.sh # Installer for Linux/macOS (offline/online) +├── install.ps1 # Installer for Windows PowerShell (offline/online) +├── install.cmd # Installer for Windows CMD +├── Makefile # Automation targets (make install/reinstall/uninstall) +├── GEMINI.md # Project context file template +├── packages/ # Local offline distribution +│ ├── manifests/ # Version manifests for all platforms +│ └── binaries/ # (Created manually for offline mode) +└── CHANGELOG.md # Changelog and release log +``` -### Legal & Privacy Links +--- + +## 🤝 Contributing & Community + +This repository is an independent community fork of the original [google-antigravity/antigravity-cli](https://github.com/google-antigravity/antigravity-cli) project. + +**Our Enhancements:** +* 🌍 Multi-language localization support for docs and guides. +* 📦 Autonomy: offline installation capability without Google API downloads. +* 🛠️ Convenience: added `Makefile` for a simplified tool lifecycle. +* 🛡️ Security: regular bug fixes and improvements to the sandbox environment. + +--- -- **Terms of Service**: [antigravity.google/terms](https://antigravity.google/terms) -- **Privacy Policy**: [policies.google.com/privacy](https://policies.google.com/privacy) +## 📜 Legal & Trademark Notice + +* **Official Links**: [Official Documentation Repo](https://github.com/google-antigravity/antigravity-cli) · [Official CLI Codebase](https://github.com/google-gemini/gemini-cli) · [Official Website](https://antigravity.google) +* **Terms of Use**: [antigravity.google/terms](https://antigravity.google/terms) · [policies.google.com/privacy](https://policies.google.com/privacy) + +> [!IMPORTANT] +> **Fork Legal Status:** +> This repository is an independent non-commercial copy (Community Fork) of the original client. It **is not** an official product of Google LLC. Google LLC is not responsible for the performance, modifications, or safety of this fork. +> +> **Licensing & Copyrights:** +> The original software is distributed under the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). All original code is the intellectual property of **Copyright © 2025 Google LLC**. +> +> **Trademark Use:** +> The name "Antigravity CLI" and associated logos are used within Customary Use limits solely to describe the origin, compatibility, and functional purpose of the software. This project does not claim ownership of any Google LLC trademarks. +> +> **Disclaimer of Warranty:** +> The software is provided on an "AS IS" basis, WITHOUT WARRANTIES OF ANY KIND, either express or implied. You assume all responsibility and risks associated with its use. + +> [!CAUTION] +> AI coding agents work autonomously. Always carefully review the proposed diff blocks and commands before confirming execution, especially when working with system files or firewall configuration. diff --git a/examples/statusline/README.md b/examples/statusline/README.md index 923d66d2d..6b3a6ed88 100644 --- a/examples/statusline/README.md +++ b/examples/statusline/README.md @@ -1,21 +1,117 @@ -# CLI Statusline Example +# Custom Status Line -This directory contains an example script (`statusline.sh`) that demonstrates how to create a custom, dynamic statusline for the Antigravity CLI. +This directory contains reference implementations for a custom, dynamic status line for the Antigravity CLI across different shell environments: -For more details on how to use and configure the statusline script, please refer to the official public documentation: -[https://antigravity.google/docs/cli-statusline](https://antigravity.google/docs/cli-statusline) +1. **`statusline.sh`** (Bash/Zsh - Linux/macOS) +2. **`statusline.js`** (Node.js - Cross-platform: Windows/Linux/macOS) +3. **`statusline.ps1`** (PowerShell - Windows PowerShell or PowerShell Core `pwsh`) +4. **`statusline.fish`** (Fish shell - Linux/macOS) + +## Quick Start + +### Option 1: Automatic Setup (Recommended for Bash/Zsh) + +Run the included setup script from the root of the repository: + +```bash +bash examples/statusline/setup.sh +``` + +This script will automatically: +1. Copy `statusline.sh` to your platform's global settings directory (so it stays configured even if you move or delete this repository). +2. Configure and enable it in your global `settings.json` file. + +### Option 2: Manual configuration + +1. Copy the script corresponding to your shell to a directory of your choice. +2. Edit your `settings.json` file to point `statusLine.command` to the absolute path of the script and set `statusLine.enabled` to `true`: + +#### Bash/Zsh (Linux/macOS) +```json +{ + "statusLine": { + "command": "/absolute/path/to/statusline.sh", + "enabled": true + } +} +``` + +#### Node.js (Cross-platform) +```json +{ + "statusLine": { + "command": "node /absolute/path/to/statusline.js", + "enabled": true + } +} +``` + +#### PowerShell (Windows / pwsh) +```json +{ + "statusLine": { + "command": "powershell.exe -ExecutionPolicy Bypass -File C:\\absolute\\path\\to\\statusline.ps1", + "enabled": true + } +} +``` +*For PowerShell Core, use `pwsh.exe` or `pwsh` instead of `powershell.exe`.* + +#### Fish Shell +```json +{ + "statusLine": { + "command": "/absolute/path/to/statusline.fish", + "enabled": true + } +} +``` + +**Settings file locations:** + +| Platform | Path | +| :--- | :--- | +| Linux | `~/.gemini/antigravity-cli/settings.json` | +| macOS | `~/Library/Application Support/antigravity-cli/settings.json` | +| Windows | `%APPDATA%\antigravity-cli\settings.json` | + +> [!IMPORTANT] +> The `command` field must be an **absolute path** to the script. Relative paths and `~` expansion are not supported. + +After saving, restart `agy` for changes to take effect. ## How it works -The `statusline.sh` script reads a JSON payload from standard input, containing various state information from the CLI. It then: -1. Extracts multiple fields like `agent_state`, `vcs` info, context usage, and terminal dimensions using `jq`. -2. Computes visual indicators, such as a Unicode progress bar for context window usage. -3. Formats the data with standard ANSI 16-color codes for visual distinction. -4. Dynamically adjusts the layout to be 1 or 2 lines based on the available terminal width. +The CLI pipes a JSON payload into the script's stdin on each state change. The script: + +1. Extracts fields like `agent_state`, `vcs` info, `context_window` usage, and `terminal_width`. +2. Computes visual indicators (e.g., a Unicode progress bar for context window usage). +3. Formats the output using standard ANSI 16-color codes. +4. Dynamically adjusts the layout based on the available terminal width (single-line for wide terminals, two-line for narrower ones). + +### JSON payload fields + +| Field | Type | Description | +| :--- | :--- | :--- | +| `agent_state` | string | Current state: `idle`, `thinking`, `working`, `tool_use` | +| `context_window.used_percentage` | number | Context window utilization (0–100) | +| `vcs.branch` | string | Current Git branch name | +| `vcs.dirty` | boolean | Whether the working tree has uncommitted changes | +| `sandbox.enabled` | boolean | Whether sandbox mode is active | +| `artifact_count` | number | Number of artifacts in the current session | +| `subagents` | array | List of active subagents | +| `task_count` | number | Number of background tasks | +| `model.display_name` | string | Human-readable model name | +| `terminal_width` | number | Current terminal width in columns | + +### Prerequisites + +- **Bash (`statusline.sh`) & Fish (`statusline.fish`)**: Require [`jq`](https://jqlang.org/) to be installed and available in `$PATH`. +- **Node.js (`statusline.js`) & PowerShell (`statusline.ps1`)**: Have **zero external dependencies** and work out-of-the-box. ## Examples -### Default Statusline +### Default Status Line ![Default Statusline](images/statusline-default.png) ### Review Mode @@ -23,3 +119,13 @@ The `statusline.sh` script reads a JSON payload from standard input, containing ### Tool Execution ![Tool Execution Statusline](images/statusline-tool.png) + +## Writing your own + +You can use any of the provided scripts as a starting point. The only contract is: + +1. Read JSON from stdin. +2. Write one or more lines of ANSI-formatted text to stdout. +3. Exit with code 0. + +For the official documentation, see [antigravity.google/docs/cli-statusline](https://antigravity.google/docs/cli-statusline). diff --git a/examples/statusline/setup.sh b/examples/statusline/setup.sh new file mode 100644 index 000000000..584c78044 --- /dev/null +++ b/examples/statusline/setup.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# setup.sh - Installs and enables the custom statusline for Antigravity CLI + +set -euo pipefail + +# 1. Determine the settings directory +OS="$(uname -s)" +case "${OS}" in + Darwin*) + CONFIG_DIR="$HOME/Library/Application Support/antigravity-cli" + ;; + Linux*) + CONFIG_DIR="$HOME/.gemini/antigravity-cli" + ;; + CYGWIN*|MINGW*|MSYS*) + # Windows environments (Git Bash, MSYS) + if [ -n "${APPDATA:-}" ]; then + CONFIG_DIR="${APPDATA}/antigravity-cli" + else + CONFIG_DIR="$HOME/AppData/Roaming/antigravity-cli" + fi + ;; + *) + # Default fallback to Linux path + CONFIG_DIR="$HOME/.gemini/antigravity-cli" + ;; +esac + +# Convert config dir to absolute path if needed +mkdir -p "$CONFIG_DIR" +CONFIG_DIR="$(cd "$CONFIG_DIR" && pwd)" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE_SCRIPT="$SCRIPT_DIR/statusline.sh" +TARGET_SCRIPT="$CONFIG_DIR/statusline.sh" + +echo "Installing statusline script to: $TARGET_SCRIPT" +cp "$SOURCE_SCRIPT" "$TARGET_SCRIPT" +chmod +x "$TARGET_SCRIPT" + +SETTINGS_FILE="$CONFIG_DIR/settings.json" +echo "Configuring settings file at: $SETTINGS_FILE" + +# Make sure settings.json exists +if [ ! -f "$SETTINGS_FILE" ]; then + echo "{}" > "$SETTINGS_FILE" +fi + +# Update settings.json using python if available, otherwise fallback to jq +if command -v python3 >/dev/null 2>&1; then + python3 -c ' +import json, sys +file_path, script_path = sys.argv[1], sys.argv[2] +try: + with open(file_path, "r") as f: + data = json.load(f) +except Exception: + data = {} +if "statusLine" not in data or not isinstance(data["statusLine"], dict): + data["statusLine"] = {} +data["statusLine"]["command"] = script_path +data["statusLine"]["enabled"] = True +with open(file_path, "w") as f: + json.dump(data, f, indent=2) +' "$SETTINGS_FILE" "$TARGET_SCRIPT" +elif command -v python >/dev/null 2>&1; then + python -c ' +import json, sys +file_path, script_path = sys.argv[1], sys.argv[2] +try: + with open(file_path, "r") as f: + data = json.load(f) +except Exception: + data = {} +if "statusLine" not in data or not isinstance(data["statusLine"], dict): + data["statusLine"] = {} +data["statusLine"]["command"] = script_path +data["statusLine"]["enabled"] = True +with open(file_path, "w") as f: + json.dump(data, f, indent=2) +' "$SETTINGS_FILE" "$TARGET_SCRIPT" +elif command -v jq >/dev/null 2>&1; then + TEMP_FILE=$(mktemp) + jq --arg cmd "$TARGET_SCRIPT" '.statusLine = ((.statusLine // {}) + {command: $cmd, enabled: true})' "$SETTINGS_FILE" > "$TEMP_FILE" + mv "$TEMP_FILE" "$SETTINGS_FILE" +else + echo "Error: Neither python3, python, nor jq is installed. Please manually add the following to your $SETTINGS_FILE:" + echo "{" + echo " \"statusLine\": {" + echo " \"command\": \"$TARGET_SCRIPT\"," + echo " \"enabled\": true" + echo " }" + echo "}" + exit 1 +fi + +echo "Status line successfully installed and enabled!" +echo "Please restart Antigravity CLI (agy) to see the changes." diff --git a/examples/statusline/statusline.fish b/examples/statusline/statusline.fish new file mode 100755 index 000000000..6d6374b00 --- /dev/null +++ b/examples/statusline/statusline.fish @@ -0,0 +1,244 @@ +#!/usr/bin/fish + +# Read JSON payload from stdin +set -l DATA (cat) + +# Extract fields using jq. Since Fish is on Unix, jq will be available. +if not type -q jq + echo "READY" + echo "ctx · 0%" + exit 0 +end + +set -l STATE (echo $DATA | jq -r '.agent_state // "idle"') +set -l USED_PCT (echo $DATA | jq -r '.context_window.used_percentage // 0') +set -l VCS_BRANCH (echo $DATA | jq -r '.vcs.branch // ""') +set -l VCS_DIRTY (echo $DATA | jq -r '.vcs.dirty // false') +set -l VCS_TYPE (echo $DATA | jq -r '.vcs.type // ""') +set -l SANDBOX (echo $DATA | jq -r '.sandbox.enabled // false') +set -l SANDBOX_NET (echo $DATA | jq -r '.sandbox.allow_network // false') +set -l ARTIFACTS (echo $DATA | jq -r '.artifact_count // 0') +set -l SUBAGENTS (echo $DATA | jq -r 'if .subagents | type == "array" then (.subagents | length) else 0 end') +set -l BG_TASKS (echo $DATA | jq -r '.task_count // 0') +set -l MODEL_ID (echo $DATA | jq -r '.model.id // ""') +set -l MODEL_NAME (echo $DATA | jq -r '.model.display_name // ""') +set -l COLS (echo $DATA | jq -r '.terminal_width // 80') +set -l CWD (echo $DATA | jq -r '.cwd // ""') +set -l CONV_ID (echo $DATA | jq -r '.conversation_id // ""') +set -l INPUT_TOKENS (echo $DATA | jq -r '.context_window.total_input_tokens // 0') +set -l OUTPUT_TOKENS (echo $DATA | jq -r '.context_window.total_output_tokens // 0') +set -l CTX_LIMIT (echo $DATA | jq -r '.context_window.context_window_size // 0') +set -l CTX_USED (echo $DATA | jq -r '(.context_window.total_input_tokens // 0) + (.context_window.total_output_tokens // 0)') + +# ANSI Helpers +set -l R (set_color normal) +set -l B (set_color -o) +set -l I (set_color -i) + +set -l FG_RED (set_color red) +set -l FG_GREEN (set_color green) +set -l FG_YELLOW (set_color yellow) +set -l FG_BLUE (set_color blue) +set -l FG_MAGENTA (set_color magenta) +set -l FG_CYAN (set_color cyan) +set -l FG_WHITE (set_color white) +set -l FG_GRAY (set_color 909090) + +set -l FG_BRIGHT_RED (set_color brred) +set -l FG_BRIGHT_GREEN (set_color brgreen) +set -l FG_BRIGHT_YELLOW (set_color bryellow) +set -l FG_BRIGHT_BLUE (set_color brblue) +set -l FG_BRIGHT_MAGENTA (set_color brmagenta) +set -l FG_BRIGHT_CYAN (set_color brcyan) +set -l FG_BRIGHT_WHITE (set_color brwhite) + +set -l NUM_COLOR "$FG_BRIGHT_WHITE$B" + +function human_format -a num + if test -z "$num"; or test "$num" -eq 0 + echo "0" + return + end + if test "$num" -ge 1000000 + set -l main (math -s0 "$num / 1000000") + set -l dec (math -s0 "($num % 1000000) / 100000") + echo "$main.$dec"M + elif test "$num" -ge 1000 + set -l main (math -s0 "$num / 1000") + set -l dec (math -s0 "($num % 1000) / 100") + echo "$main.$dec"K + else + echo "$num" + end +end + +set -l INPUT_TOK_FMT (human_format $INPUT_TOKENS) +set -l OUTPUT_TOK_FMT (human_format $OUTPUT_TOKENS) +set -l CTX_LIMIT_FMT (human_format $CTX_LIMIT) +set -l CTX_USED_FMT (human_format $CTX_USED) + +function shorten_path -a path + if test -z "$path" + echo "" + return + end + set -l shortened (string replace -r "^$HOME" "~" $path) + if test (string length $shortened) -gt 25 + set -l base_name (basename $shortened) + echo "...$base_name" + else + echo $shortened + end +end +set -l CWD_SHORT (shorten_path $CWD) + +# State Indicator +set -l S "" +switch $STATE + case idle + set S "$FG_BRIGHT_GREEN$B● READY$R" + case thinking + set S "$FG_BRIGHT_YELLOW$B◆ THINKING$R" + case working + set S "$FG_BRIGHT_CYAN$B⚙ WORKING$R" + case tool_use + set S "$FG_BRIGHT_MAGENTA$B🔧 TOOL$R" + case '*' + set -l upper_state (string upper $STATE) + set S "$FG_WHITE$B⏳ $upper_state$R" +end + +# VCS Branch & Type +# Fallback to local git query if VCS_BRANCH is not provided in the JSON payload +if test -z "$VCS_BRANCH" + set -l git_dir "." + if test -n "$CWD" + set git_dir $CWD + end + if git -C "$git_dir" rev-parse --is-inside-work-tree >/dev/null 2>&1 + set VCS_BRANCH (git -C "$git_dir" rev-parse --abbrev-ref HEAD 2>/dev/null) + set VCS_TYPE "git" + if test -n "$VCS_BRANCH" + if git -C "$git_dir" status --porcelain 2>/dev/null | string match -q -r '.' + set VCS_DIRTY "true" + else + set VCS_DIRTY "false" + end + end + end +end + +set -l V "" +if test -n "$VCS_BRANCH" + set -l vcs_label "git" + if test -n "$VCS_TYPE" + set vcs_label $VCS_TYPE + end + if test "$VCS_DIRTY" = "true" + set V "$FG_GRAY ╱ $FG_GRAY$vcs_label:$FG_BRIGHT_RED$VCS_BRANCH$FG_BRIGHT_YELLOW*$R" + else + set V "$FG_GRAY ╱ $FG_GRAY$vcs_label:$FG_BRIGHT_BLUE$VCS_BRANCH$R" + end +end + +# Model +set -l MODEL_DISP $MODEL_NAME +if test -z "$MODEL_DISP" + set MODEL_DISP $MODEL_ID +end +set -l M "" +if test -n "$MODEL_DISP" + set M "$FG_GRAY ╱ $FG_BRIGHT_MAGENTA$I$MODEL_DISP$R" +end + +# Sandbox Badge +set -l SB "" +if test "$SANDBOX" = "true" + if test "$SANDBOX_NET" = "true" + set SB "$FG_GRAY🛡️ sandbox $FG_BRIGHT_GREEN$B ON (net)$R" + else + set SB "$FG_GRAY🛡️ sandbox $FG_BRIGHT_GREEN$B ON (no-net)$R" + end +else + set SB "$FG_GRAY🛡️ sandbox off$R" +end + +# Context Bar +set -l BAR_LEN 15 +set -l PCT_INT (math -s0 "$USED_PCT") +set -l FILLED (math -s0 "$PCT_INT * $BAR_LEN / 100") +set -l REMAINDER (math -s0 "($PCT_INT * $BAR_LEN) % 100") + +set -l BAR_COLOR $FG_BRIGHT_WHITE +if test "$PCT_INT" -ge 90 + set BAR_COLOR $FG_BRIGHT_RED +elif test "$PCT_INT" -ge 60 + set BAR_COLOR $FG_BRIGHT_YELLOW +end + +set -l BAR "" +for i in (seq 0 (math "$BAR_LEN - 1")) + if test "$i" -lt "$FILLED" + set BAR "$BAR"█ + elif test "$i" -eq "$FILLED" + if test "$REMAINDER" -ge 75 + set BAR "$BAR"▓ + elif test "$REMAINDER" -ge 50 + set BAR "$BAR"▒ + elif test "$REMAINDER" -ge 25 + set BAR "$BAR"░ + else + set BAR "$BAR"· + end + else + set BAR "$BAR"· + end +end + +# Stats & Metadata +set -l PCT_FMT (printf "%.1f" $USED_PCT) +set -l CTX_BAR "$FG_GRAY ctx $BAR_COLOR$BAR $NUM_COLOR$PCT_FMT%$R" +set -l ART_FMT "$FG_GRAY 📦 $NUM_COLOR$ARTIFACTS$R" +set -l SUB_FMT "$FG_GRAY 🤖 $NUM_COLOR$SUBAGENTS$R" +set -l BG_FMT "$FG_GRAY ⏳ $NUM_COLOR$BG_TASKS$R" + +set -l DIR_FMT "" +if test -n "$CWD_SHORT" + set DIR_FMT "$FG_GRAY ╱ 📂 $CWD_SHORT$R" +end + +set -l CONV_FMT "" +if test -n "$CONV_ID" + set -l sub_conv (string sub -l 8 $CONV_ID) + set CONV_FMT "$FG_GRAY ╱ id:$sub_conv$R" +end + +set -l TOK_DETAILS "" +if test "$CTX_USED" -gt 0 + set TOK_DETAILS " ($CTX_USED_FMT/$CTX_LIMIT_FMT)" +end + +set -l DOT "$FG_GRAY · $R" + +# Output Assembly +if test "$COLS" -ge 120 + set -l line1 "$S$M$V$DIR_FMT$CONV_FMT" + if test "$CTX_USED" -gt 0 + set TOK_DETAILS " ($CTX_USED_FMT/$CTX_LIMIT_FMT · $INPUT_TOK_FMT in/$OUTPUT_TOK_FMT out)" + end + set -l line2 " $CTX_BAR$TOK_DETAILS$DOT$ART_FMT$DOT$SUB_FMT$DOT$BG_FMT$DOT$SB" + echo -e "$line1$FG_GRAY │ $R$line2" +elif test "$COLS" -ge 80 + set -l line1 "$S$M$V$DIR_FMT" + set -l line2 " $CTX_BAR$TOK_DETAILS$DOT$ART_FMT$DOT$SUB_FMT$DOT$BG_FMT$DOT$SB" + echo -e "$FG_GRAY╭─$R $line1" + echo -e "$FG_GRAY╰─$R$line2" +else + set -l M_SHORT "" + if test -n "$MODEL_DISP" + set -l sub_model (string sub -l 12 $MODEL_DISP) + set M_SHORT "$FG_GRAY ╱ $FG_BRIGHT_MAGENTA$sub_model$R" + end + echo -e "$S$M_SHORT" + echo -e "$CTX_BAR$DOT$BG_FMT" +end diff --git a/examples/statusline/statusline.js b/examples/statusline/statusline.js new file mode 100755 index 000000000..2010ec36e --- /dev/null +++ b/examples/statusline/statusline.js @@ -0,0 +1,222 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); +const os = require('os'); + +// ─── Read JSON from stdin ──────────────────────────────────────────────────── +let rawData = ''; +try { + rawData = fs.readFileSync(0, 'utf-8'); +} catch (e) { + // Silence read errors +} + +let data = {}; +try { + data = JSON.parse(rawData || '{}'); +} catch (e) { + // Output a clear error message in the statusline to help debugging + console.log(`\x1b[31m● ERROR: Invalid JSON input to statusline.js (${e.message})\x1b[0m`); + process.exit(0); +} + +// ─── ANSI Helpers (Standard 16-color palette only) ─────────────────────────── +const R = "\x1b[0m"; // Reset +const B = "\x1b[1m"; // Bold +const D = "\x1b[2m"; // Dim +const I = "\x1b[3m"; // Italic + +// Foreground accents (Standard 16 colors) +const FG_BLACK = "\x1b[30m"; +const FG_RED = "\x1b[31m"; +const FG_GREEN = "\x1b[32m"; +const FG_YELLOW = "\x1b[33m"; +const FG_BLUE = "\x1b[34m"; +const FG_MAGENTA = "\x1b[35m"; +const FG_CYAN = "\x1b[36m"; +const FG_WHITE = "\x1b[37m"; + +const FG_GRAY = "\x1b[90m"; +const FG_BRIGHT_RED = "\x1b[91m"; +const FG_BRIGHT_GREEN = "\x1b[92m"; +const FG_BRIGHT_YELLOW = "\x1b[93m"; +const FG_BRIGHT_BLUE = "\x1b[94m"; +const FG_BRIGHT_MAGENTA = "\x1b[95m"; +const FG_BRIGHT_CYAN = "\x1b[96m"; +const FG_BRIGHT_WHITE = "\x1b[97m"; + +const NUM_COLOR = FG_BRIGHT_WHITE + B; + +// ─── Extract fields with fallbacks ─────────────────────────────────────────── +const state = data.agent_state || "idle"; +const usedPct = (data.context_window && typeof data.context_window.used_percentage === 'number') ? data.context_window.used_percentage : 0; +const vcsBranch = (data.vcs && data.vcs.branch) ? data.vcs.branch : ""; +const vcsDirty = (data.vcs && data.vcs.dirty) ? data.vcs.dirty : false; +const vcsType = (data.vcs && data.vcs.type) ? data.vcs.type : ""; +const sandboxEnabled = (data.sandbox && data.sandbox.enabled) ? data.sandbox.enabled : false; +const sandboxNet = (data.sandbox && data.sandbox.allow_network) ? data.sandbox.allow_network : false; +const artifactCount = data.artifact_count || 0; +const subagentsCount = Array.isArray(data.subagents) ? data.subagents.length : 0; +const taskCount = data.task_count || 0; +const modelId = (data.model && data.model.id) ? data.model.id : ""; +const modelName = (data.model && data.model.display_name) ? data.model.display_name : ""; +const cols = data.terminal_width || 80; +const cwd = data.cwd || ""; +const convId = data.conversation_id || ""; +const inputTokens = (data.context_window && data.context_window.total_input_tokens) ? data.context_window.total_input_tokens : 0; +const outputTokens = (data.context_window && data.context_window.total_output_tokens) ? data.context_window.total_output_tokens : 0; +const ctxLimit = (data.context_window && data.context_window.context_window_size) ? data.context_window.context_window_size : 0; +const ctxUsed = inputTokens + outputTokens; + +// ─── Helper Formatting Functions ───────────────────────────────────────────── +function humanFormat(num) { + if (!num || isNaN(num) || num === 0) return "0"; + if (num >= 1000000) { + return (num / 1000000).toFixed(1).replace(/\.0$/, '') + "M"; + } + if (num >= 1000) { + return (num / 1000).toFixed(1).replace(/\.0$/, '') + "K"; + } + return num.toString(); +} + +function shortenPath(p) { + if (!p) return ""; + const home = os.homedir(); + if (p.startsWith(home)) { + p = "~" + p.slice(home.length); + } + if (p.length > 25) { + return "..." + path.basename(p); + } + return p; +} + +const cwdShort = shortenPath(cwd); + +// ─── State Indicator (No background colors) ────────────────────────────────── +let S = ""; +switch (state) { + case 'idle': + S = `${FG_BRIGHT_GREEN}${B}● READY${R}`; + break; + case 'thinking': + S = `${FG_BRIGHT_YELLOW}${B}◆ THINKING${R}`; + break; + case 'working': + S = `${FG_BRIGHT_CYAN}${B}⚙ WORKING${R}`; + break; + case 'tool_use': + S = `${FG_BRIGHT_MAGENTA}${B}🔧 TOOL${R}`; + break; + default: + S = `${FG_WHITE}${B}⏳ ${state.toUpperCase()}${R}`; +} + +// ─── VCS Branch & Type ─────────────────────────────────────────────────────── +let V = ""; +if (vcsBranch) { + const vcsLabel = vcsType || "git"; + if (vcsDirty || vcsDirty === "true") { + V = `${FG_GRAY} ╱ ${FG_GRAY}${vcsLabel}:${FG_BRIGHT_RED}${vcsBranch}${FG_BRIGHT_YELLOW}*${R}`; + } else { + V = `${FG_GRAY} ╱ ${FG_GRAY}${vcsLabel}:${FG_BRIGHT_BLUE}${vcsBranch}${R}`; + } +} + +// ─── Model ─────────────────────────────────────────────────────────────────── +const modelDisp = modelName || modelId; +let M = ""; +if (modelDisp) { + M = `${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${I}${modelDisp}${R}`; +} + +// ─── Sandbox Badge ─────────────────────────────────────────────────────────── +let SB = ""; +if (sandboxEnabled || sandboxEnabled === "true") { + if (sandboxNet || sandboxNet === "true") { + SB = `${FG_GRAY}🛡️ sandbox ${FG_BRIGHT_GREEN}${B}ON (net)${R}`; + } else { + SB = `${FG_GRAY}🛡️ sandbox ${FG_BRIGHT_GREEN}${B}ON (no-net)${R}`; + } +} else { + SB = `${FG_GRAY}🛡️ sandbox off${R}`; +} + +// ─── Context Bar (15 segments, fine-grain Unicode) ──────────────────────────── +const barLen = 15; +const pctInt = Math.floor(usedPct); +const filled = Math.floor((pctInt * barLen) / 100); +const remainder = (pctInt * barLen) % 100; + +let barColor = FG_BRIGHT_WHITE; +if (pctInt >= 90) { + barColor = FG_BRIGHT_RED; +} else if (pctInt >= 60) { + barColor = FG_BRIGHT_YELLOW; +} + +let bar = ""; +for (let i = 0; i < barLen; i++) { + if (i < filled) { + bar += "█"; + } else if (i === filled) { + if (remainder >= 75) { + bar += "▓"; + } else if (remainder >= 50) { + bar += "▒"; + } else if (remainder >= 25) { + bar += "░"; + } else { + bar += "·"; + } + } else { + bar += "·"; + } +} + +// ─── Stats & Metadata formatting ───────────────────────────────────────────── +const pctFmt = usedPct.toFixed(1); +const ctxBar = `${FG_GRAY}ctx ${barColor}${bar} ${NUM_COLOR}${pctFmt}%${R}`; +const artFmt = `${FG_GRAY}📦 ${NUM_COLOR}${artifactCount}${R}`; +const subFmt = `${FG_GRAY}🤖 ${NUM_COLOR}${subagentsCount}${R}`; +const bgFmt = `${FG_GRAY}⏳ ${NUM_COLOR}${taskCount}${R}`; + +let dirFmt = ""; +if (cwdShort) { + dirFmt = `${FG_GRAY} ╱ 📂 ${cwdShort}${R}`; +} + +let convFmt = ""; +if (convId) { + convFmt = `${FG_GRAY} ╱ id:${convId.slice(0, 8)}${R}`; +} + +let tokDetails = ""; +if (ctxUsed > 0) { + tokDetails = ` (${humanFormat(ctxUsed)}/${humanFormat(ctxLimit)})`; +} + +const dot = `${FG_GRAY} · ${R}`; + +// ─── Output Assembly ────────────────────────────────────────────────────────── +if (cols >= 120) { + let line1 = `${S}${M}${V}${dirFmt}${convFmt}`; + if (ctxUsed > 0) { + tokDetails = ` (${humanFormat(ctxUsed)}/${humanFormat(ctxLimit)} · ${humanFormat(inputTokens)} in/${humanFormat(outputTokens)} out)`; + } + let line2 = ` ${ctxBar}${tokDetails}${dot}${artFmt}${dot}${subFmt}${dot}${bgFmt}${dot}${SB}`; + console.log(`${line1}${FG_GRAY} │ ${R}${line2}`); +} else if (cols >= 80) { + let line1 = `${S}${M}${V}${dirFmt}`; + let line2 = ` ${ctxBar}${tokDetails}${dot}${artFmt}${dot}${subFmt}${dot}${bgFmt}${dot}${SB}`; + console.log(`${FG_GRAY}╭─${R} ${line1}`); + console.log(`${FG_GRAY}╰─${R}${line2}`); +} else { + let mShort = ""; + if (modelDisp) { + mShort = `${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${modelDisp.slice(0, 12)}${R}`; + } + console.log(`${S}${mShort}`); + console.log(`${ctxBar}${dot}${bgFmt}`); +} diff --git a/examples/statusline/statusline.ps1 b/examples/statusline/statusline.ps1 new file mode 100644 index 000000000..dd337d84c --- /dev/null +++ b/examples/statusline/statusline.ps1 @@ -0,0 +1,251 @@ +# ─── Read JSON from stdin ──────────────────────────────────────────────────── +param( + [Parameter(ValueFromPipeline)] + [string]$inputJson +) + +if ([string]::IsNullOrWhiteSpace($inputJson)) { + # Try reading from Console stdin (for redirected stdin from Go/agy.exe) + $inputJson = [Console]::In.ReadToEnd() +} + +if ([string]::IsNullOrWhiteSpace($inputJson)) { + $inputJson = '{}' +} + +try { + # -ErrorAction Stop is needed so catch block is triggered on invalid JSON + $data = ConvertFrom-Json $inputJson -ErrorAction Stop +} catch { + # Output the JSON parsing error in red to make debugging copy-pasted blocks easy + Write-Output "$([char]0x1b)[31m● ERROR: Invalid JSON input to statusline.ps1 ($($_.Exception.Message))$([char]0x1b)[0m" + exit +} + +if ($null -eq $data) { + $data = [PSCustomObject]@{} +} + +# ─── ANSI Helpers (Standard 16-color palette only) ─────────────────────────── +$R = "$([char]0x1b)[0m" # Reset +$B = "$([char]0x1b)[1m" # Bold +$D = "$([char]0x1b)[2m" # Dim +$I = "$([char]0x1b)[3m" # Italic + +# Foreground accents (Standard 16 colors) +$FG_BLACK = "$([char]0x1b)[30m" +$FG_RED = "$([char]0x1b)[31m" +$FG_GREEN = "$([char]0x1b)[32m" +$FG_YELLOW = "$([char]0x1b)[33m" +$FG_BLUE = "$([char]0x1b)[34m" +$FG_MAGENTA = "$([char]0x1b)[35m" +$FG_CYAN = "$([char]0x1b)[36m" +$FG_WHITE = "$([char]0x1b)[37m" + +$FG_GRAY = "$([char]0x1b)[90m" +$FG_BRIGHT_RED = "$([char]0x1b)[91m" +$FG_BRIGHT_GREEN = "$([char]0x1b)[92m" +$FG_BRIGHT_YELLOW = "$([char]0x1b)[93m" +$FG_BRIGHT_BLUE = "$([char]0x1b)[94m" +$FG_BRIGHT_MAGENTA = "$([char]0x1b)[95m" +$FG_BRIGHT_CYAN = "$([char]0x1b)[96m" +$FG_BRIGHT_WHITE = "$([char]0x1b)[97m" + +$NUM_COLOR = "${FG_BRIGHT_WHITE}${B}" + +# ─── Extract fields with fallback ──────────────────────────────────────────── +$state = if ($data.agent_state) { $data.agent_state } else { "idle" } +$usedPct = if ($data.context_window -and $null -ne $data.context_window.used_percentage) { [double]$data.context_window.used_percentage } else { 0.0 } +$vcsBranch = if ($data.vcs -and $data.vcs.branch) { $data.vcs.branch } else { "" } +$vcsDirty = if ($data.vcs -and $null -ne $data.vcs.dirty) { $data.vcs.dirty } else { $false } +$vcsType = if ($data.vcs -and $data.vcs.type) { $data.vcs.type } else { "" } +$sandboxEnabled = if ($data.sandbox -and $null -ne $data.sandbox.enabled) { $data.sandbox.enabled } else { $false } +$sandboxNet = if ($data.sandbox -and $null -ne $data.sandbox.allow_network) { $data.sandbox.allow_network } else { $false } +$artifactCount = if ($data.artifact_count) { $data.artifact_count } else { 0 } +$subagentsCount = if ($data.subagents) { @($data.subagents).Count } else { 0 } +$taskCount = if ($data.task_count) { $data.task_count } else { 0 } +$modelId = if ($data.model -and $data.model.id) { $data.model.id } else { "" } +$modelName = if ($data.model -and $data.model.display_name) { $data.model.display_name } else { "" } +$cols = if ($data.terminal_width) { [int]$data.terminal_width } else { 80 } +$cwd = if ($data.cwd) { $data.cwd } else { "" } +$convId = if ($data.conversation_id) { $data.conversation_id } else { "" } +$inputTokens = if ($data.context_window -and $null -ne $data.context_window.total_input_tokens) { $data.context_window.total_input_tokens } else { 0 } +$outputTokens = if ($data.context_window -and $null -ne $data.context_window.total_output_tokens) { $data.context_window.total_output_tokens } else { 0 } +$ctxLimit = if ($data.context_window -and $null -ne $data.context_window.context_window_size) { $data.context_window.context_window_size } else { 0 } +$ctxUsed = $inputTokens + $outputTokens + +# ─── Helper Functions ──────────────────────────────────────────────────────── +function Get-HumanFormat { + param ($num) + if ($null -eq $num -or $num -eq 0) { return "0" } + if ($num -ge 1000000) { + $main = [Math]::Floor($num / 1000000) + $dec = [Math]::Floor(($num % 1000000) / 100000) + return "${main}.${dec}M" + } + if ($num -ge 1000) { + $main = [Math]::Floor($num / 1000) + $dec = [Math]::Floor(($num % 1000) / 100) + return "${main}.${dec}K" + } + return "$num" +} + +function Get-ShortenPath { + param ($path) + if ([string]::IsNullOrEmpty($path)) { return "" } + $homePath = $env:USERPROFILE + if (-not $homePath) { + $homePath = $env:HOME + } + if ($homePath -and $path.StartsWith($homePath)) { + $path = "~" + $path.Substring($homePath.Length) + } + if ($path.Length -gt 25) { + $leaf = Split-Path $path -Leaf + return "...$leaf" + } + return $path +} + +$cwdShort = Get-ShortenPath $cwd + +# ─── State Indicator ────────────────────────────────────────────────────────── +switch ($state) { + "idle" { $S = "${FG_BRIGHT_GREEN}${B}● READY${R}" } + "thinking" { $S = "${FG_BRIGHT_YELLOW}${B}◆ THINKING${R}" } + "working" { $S = "${FG_BRIGHT_CYAN}${B}⚙ WORKING${R}" } + "tool_use" { $S = "${FG_BRIGHT_MAGENTA}${B}🔧 TOOL${R}" } + Default { $S = "${FG_WHITE}${B}⏳ $($state.ToUpper())${R}" } +} + +# ─── VCS Branch & Type ─────────────────────────────────────────────────────── +# Fallback to local git query if VCS_BRANCH is not provided in the JSON payload +if ([string]::IsNullOrEmpty($vcsBranch)) { + $gitDir = if (-not [string]::IsNullOrEmpty($cwd)) { $cwd } else { "." } + # Run git commands and capture output safely + $isWorkTree = git -C $gitDir rev-parse --is-inside-work-tree 2>$null + if ($isWorkTree -eq "true") { + $branch = (git -C $gitDir rev-parse --abbrev-ref HEAD 2>$null).Trim() + if (-not [string]::IsNullOrEmpty($branch)) { + $vcsBranch = $branch + $vcsType = "git" + $status = (git -C $gitDir status --porcelain 2>$null).Trim() + $vcsDirty = if (-not [string]::IsNullOrEmpty($status)) { "true" } else { "false" } + } + } +} + +$V = "" +if (-not [string]::IsNullOrEmpty($vcsBranch)) { + $vcsLabel = if (-not [string]::IsNullOrEmpty($vcsType)) { $vcsType } else { "git" } + if ($vcsDirty -eq $true -or $vcsDirty -eq "true") { + $V = "${FG_GRAY} ╱ ${FG_GRAY}${vcsLabel}:${FG_BRIGHT_RED}${vcsBranch}${FG_BRIGHT_YELLOW}*${R}" + } else { + $V = "${FG_GRAY} ╱ ${FG_GRAY}${vcsLabel}:${FG_BRIGHT_BLUE}${vcsBranch}${R}" + } +} + +# ─── Model ─────────────────────────────────────────────────────────────────── +$modelDisp = if (-not [string]::IsNullOrEmpty($modelName)) { $modelName } else { $modelId } +$M = "" +if (-not [string]::IsNullOrEmpty($modelDisp)) { + $M = "${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${I}${modelDisp}${R}" +} + +# ─── Sandbox Badge ─────────────────────────────────────────────────────────── +if ($sandboxEnabled -eq $true -or $sandboxEnabled -eq "true") { + if ($sandboxNet -eq $true -or $sandboxNet -eq "true") { + $SB = "${FG_GRAY}🛡️ sandbox ${FG_BRIGHT_GREEN}${B}ON (net)${R}" + } else { + $SB = "${FG_GRAY}🛡️ sandbox ${FG_BRIGHT_GREEN}${B}ON (no-net)${R}" + } +} else { + $SB = "${FG_GRAY}🛡️ sandbox off${R}" +} + +# ─── Context Bar ───────────────────────────────────────────────────────────── +$BAR_LEN = 15 +$pctInt = [int][Math]::Floor($usedPct) +$filled = [int][Math]::Floor($pctInt * $BAR_LEN / 100) +$remainder = ($pctInt * $BAR_LEN) % 100 + +$barColor = $FG_BRIGHT_WHITE +if ($pctInt -ge 90) { + $barColor = $FG_BRIGHT_RED +} elseif ($pctInt -ge 60) { + $barColor = $FG_BRIGHT_YELLOW +} + +$BAR = "" +for ($i = 0; $i -lt $BAR_LEN; $i++) { + if ($i -lt $filled) { + $BAR += "█" + } elseif ($i -eq $filled) { + if ($remainder -ge 75) { + $BAR += "▓" + } elseif ($remainder -ge 50) { + $BAR += "▒" + } elseif ($remainder -ge 25) { + $BAR += "░" + } else { + $BAR += "·" + } + } else { + $BAR += "·" + } +} + +# ─── Stats Formatting ──────────────────────────────────────────────────────── +$pctFmt = $usedPct.ToString("0.0", [System.Globalization.CultureInfo]::InvariantCulture) +$CTX_BAR = "${FG_GRAY}ctx ${barColor}${BAR} ${NUM_COLOR}${pctFmt}%${R}" +$ART_FMT = "${FG_GRAY}📦 ${NUM_COLOR}${artifactCount}${R}" +$SUB_FMT = "${FG_GRAY}🤖 ${NUM_COLOR}${subagentsCount}${R}" +$BG_FMT = "${FG_GRAY}⏳ ${NUM_COLOR}${taskCount}${R}" + +$DIR_FMT = "" +if (-not [string]::IsNullOrEmpty($cwdShort)) { + $DIR_FMT = "${FG_GRAY} ╱ 📂 ${cwdShort}${R}" +} + +$CONV_FMT = "" +if (-not [string]::IsNullOrEmpty($convId)) { + $subConvId = if ($convId.Length -gt 8) { $convId.Substring(0, 8) } else { $convId } + $CONV_FMT = "${FG_GRAY} ╱ id:${subConvId}${R}" +} + +$tokDetails = "" +if ($ctxUsed -gt 0) { + $ctxUsedFmt = Get-HumanFormat $ctxUsed + $ctxLimitFmt = Get-HumanFormat $ctxLimit + $tokDetails = " (${ctxUsedFmt}/${ctxLimitFmt})" +} + +$DOT = "${FG_GRAY} · ${R}" + +# ─── Output Assembly ────────────────────────────────────────────────────────── +if ($cols -ge 120) { + $line1 = "${S}${M}${V}${DIR_FMT}${CONV_FMT}" + if ($ctxUsed -gt 0) { + $ctxUsedFmt = Get-HumanFormat $ctxUsed + $ctxLimitFmt = Get-HumanFormat $ctxLimit + $inputTokFmt = Get-HumanFormat $inputTokens + $outputTokFmt = Get-HumanFormat $outputTokens + $tokDetails = " (${ctxUsedFmt}/${ctxLimitFmt} · ${inputTokFmt} in/${outputTokFmt} out)" + } + $line2 = " ${CTX_BAR}${tokDetails}${DOT}${ART_FMT}${DOT}${SUB_FMT}${DOT}${BG_FMT}${DOT}${SB}" + Write-Output "${line1}${FG_GRAY} │ ${R}${line2}" +} elseif ($cols -ge 80) { + $line1 = "${S}${M}${V}${DIR_FMT}" + $line2 = " ${CTX_BAR}${tokDetails}${DOT}${ART_FMT}${DOT}${SUB_FMT}${DOT}${BG_FMT}${DOT}${SB}" + Write-Output "${FG_GRAY}╭─${R} ${line1}" + Write-Output "${FG_GRAY}╰─${R}${line2}" +} else { + $mShort = "" + if (-not [string]::IsNullOrEmpty($modelDisp)) { + $subModelDisp = if ($modelDisp.Length -gt 12) { $modelDisp.Substring(0, 12) } else { $modelDisp } + $mShort = "${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${subModelDisp}${R}" + } + Write-Output "${S}${mShort}" + Write-Output "${CTX_BAR}${DOT}${BG_FMT}" +} diff --git a/examples/statusline/statusline.sh b/examples/statusline/statusline.sh index 390fea1ee..f9eb53edc 100755 --- a/examples/statusline/statusline.sh +++ b/examples/statusline/statusline.sh @@ -1,5 +1,7 @@ #!/bin/bash set -euo pipefail +INPUT_JSON=$(cat) + # ─── ANSI Helpers (Standard 16-color palette only) ─────────────────────────── R="\033[0m" # Reset @@ -30,38 +32,95 @@ FG_BRIGHT_WHITE="\033[97m" NUM_COLOR="${FG_BRIGHT_WHITE}${B}" # ─── Parse JSON from stdin (Single jq pass for performance) ────────────────── -# Extract all fields in one pass to prevent spawning jq 8 times. { read -r STATE read -r USED_PCT read -r VCS_BRANCH read -r VCS_DIRTY + read -r VCS_TYPE + read -r VCS_CLIENT read -r SANDBOX + read -r SANDBOX_NET read -r ARTIFACTS read -r SUBAGENTS read -r BG_TASKS - read -r MODEL + read -r MODEL_ID + read -r MODEL_NAME read -r COLS + read -r CWD + read -r CONV_ID + read -r PRODUCT + read -r INPUT_TOKENS + read -r OUTPUT_TOKENS + read -r CTX_LIMIT + read -r CTX_USED + read -r REM_PCT } <<< "$( - jq -r ' + echo "$INPUT_JSON" | jq -r ' (.agent_state // "idle"), (.context_window.used_percentage // 0), (.vcs.branch // ""), (.vcs.dirty // false), + (.vcs.type // ""), + (.vcs.client // ""), (.sandbox.enabled // false), + (.sandbox.allow_network // false), (.artifact_count // 0), (if .subagents | type == "array" then (.subagents | length) else 0 end), (.task_count // 0), + (.model.id // ""), (.model.display_name // ""), - (.terminal_width // 80) - ' 2>/dev/null || printf "idle\n0\n\nfalse\nfalse\n0\n0\n0\n\n80\n" + (.terminal_width // 80), + (.cwd // ""), + (.conversation_id // ""), + (.product // ""), + (.context_window.total_input_tokens // 0), + (.context_window.total_output_tokens // 0), + (.context_window.context_window_size // 0), + ((.context_window.total_input_tokens // 0) + (.context_window.total_output_tokens // 0)), + (.context_window.remaining_percentage // 100) + ' 2>/dev/null || printf "idle\n0\n\nfalse\n\n\nfalse\nfalse\n0\n0\n0\n\n\n80\n\n\n\n0\n0\n0\n0\n100\n" )" -# ─── Computed Values ───────────────────────────────────────────────────────── -# Use LC_NUMERIC=C to prevent bash printf errors in locales that use commas for decimals +# ─── Computed & Formatted Values ───────────────────────────────────────────── PCT_FMT=$(LC_NUMERIC=C printf "%.1f" "$USED_PCT") PCT_INT=${USED_PCT%.*}; PCT_INT=${PCT_INT:-0} +human_format() { + local num=$1 + if [ -z "$num" ] || [ "$num" -eq 0 ] 2>/dev/null; then + echo "0" + return + fi + if [ "$num" -ge 1000000 ] 2>/dev/null; then + echo "$((num / 1000000)).$(((num % 1000000) / 100000))M" + elif [ "$num" -ge 1000 ] 2>/dev/null; then + echo "$((num / 1000)).$(((num % 1000) / 100))K" + else + echo "$num" + fi +} + +INPUT_TOK_FMT=$(human_format "$INPUT_TOKENS") +OUTPUT_TOK_FMT=$(human_format "$OUTPUT_TOKENS") +CTX_LIMIT_FMT=$(human_format "$CTX_LIMIT") +CTX_USED_FMT=$(human_format "$CTX_USED") + +shorten_path() { + local path=$1 + if [ -z "$path" ]; then + echo "" + return + fi + path="${path/#$HOME/\~}" + if [ "${#path}" -gt 25 ]; then + echo "...$(basename "$path")" + else + echo "$path" + fi +} +CWD_SHORT=$(shorten_path "$CWD") + # ─── State Indicator (No background colors) ────────────────────────────────── case "$STATE" in idle) S="${FG_BRIGHT_GREEN}${B}● READY${R}" ;; @@ -71,27 +130,50 @@ case "$STATE" in *) S="${FG_WHITE}${B}⏳ $(echo "$STATE" | tr '[:lower:]' '[:upper:]')${R}" ;; esac -# ─── VCS Branch ────────────────────────────────────────────────────────────── +# ─── VCS Branch & Type ─────────────────────────────────────────────────────── +# Fallback to local git query if VCS_BRANCH is not provided in the JSON payload +if [ -z "$VCS_BRANCH" ]; then + GIT_DIR="${CWD:-.}" + if git -C "$GIT_DIR" rev-parse --is-inside-work-tree &>/dev/null; then + VCS_BRANCH=$(git -C "$GIT_DIR" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "") + VCS_TYPE="git" + if [ -n "$VCS_BRANCH" ]; then + if git -C "$GIT_DIR" status --porcelain 2>/dev/null | grep -q .; then + VCS_DIRTY="true" + else + VCS_DIRTY="false" + fi + fi + fi +fi + V="" if [ -n "$VCS_BRANCH" ]; then + VCS_LABEL="${VCS_TYPE:-git}" if [ "$VCS_DIRTY" = "true" ]; then - V="${FG_GRAY} ╱ ${FG_BRIGHT_RED}${VCS_BRANCH}${FG_BRIGHT_YELLOW}*${R}" + V="${FG_GRAY} ╱ ${FG_GRAY}${VCS_LABEL}:${FG_BRIGHT_RED}${VCS_BRANCH}${FG_BRIGHT_YELLOW}*${R}" else - V="${FG_GRAY} ╱ ${FG_BRIGHT_BLUE}${VCS_BRANCH}${R}" + V="${FG_GRAY} ╱ ${FG_GRAY}${VCS_LABEL}:${FG_BRIGHT_BLUE}${VCS_BRANCH}${R}" fi fi # ─── Model ─────────────────────────────────────────────────────────────────── +# Fallback to model ID if display name is empty +MODEL_DISP="${MODEL_NAME:-$MODEL_ID}" M="" -if [ -n "$MODEL" ]; then - M="${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${I}${MODEL}${R}" +if [ -n "$MODEL_DISP" ]; then + M="${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${I}${MODEL_DISP}${R}" fi # ─── Sandbox Badge ─────────────────────────────────────────────────────────── if [ "$SANDBOX" = "true" ]; then - SB="${FG_GRAY}sandbox ${FG_BRIGHT_GREEN}${B}ON${R}" + if [ "$SANDBOX_NET" = "true" ]; then + SB="${FG_GRAY}🛡️ sandbox ${FG_BRIGHT_GREEN}${B}ON (net)${R}" + else + SB="${FG_GRAY}🛡️ sandbox ${FG_BRIGHT_GREEN}${B}ON (no-net)${R}" + fi else - SB="${FG_GRAY}sandbox off${R}" + SB="${FG_GRAY}🛡️ sandbox off${R}" fi # ─── Context Bar (15 segments, fine-grain Unicode) ──────────────────────────── @@ -99,7 +181,6 @@ BAR_LEN=15 FILLED=$((PCT_INT * BAR_LEN / 100)) REMAINDER=$(( (PCT_INT * BAR_LEN) % 100 )) -# Pick color based on percentage if [ "$PCT_INT" -ge 90 ]; then BAR_COLOR="$FG_BRIGHT_RED" elif [ "$PCT_INT" -ge 60 ]; then @@ -108,7 +189,6 @@ else BAR_COLOR="$FG_BRIGHT_WHITE" fi -# Build bar with partial-fill last block BAR="" for ((i = 0; i < BAR_LEN; i++)); do if [ "$i" -lt "$FILLED" ]; then @@ -128,28 +208,62 @@ for ((i = 0; i < BAR_LEN; i++)); do fi done -# ─── Stats ─────────────────────────────────────────────────────────────────── -CTX="${FG_GRAY}ctx ${BAR_COLOR}${BAR} ${NUM_COLOR}${PCT_FMT}%${R}" -ART_FMT="${FG_GRAY}artifacts ${NUM_COLOR}${ARTIFACTS}${R}" -SUB_FMT="${FG_GRAY}subagents ${NUM_COLOR}${SUBAGENTS}${R}" -BG_FMT="${FG_GRAY}tasks ${NUM_COLOR}${BG_TASKS}${R}" +# ─── Stats & Metadata formatting ───────────────────────────────────────────── +CTX_BAR="${FG_GRAY}ctx ${BAR_COLOR}${BAR} ${NUM_COLOR}${PCT_FMT}%${R}" +ART_FMT="${FG_GRAY}📦 ${NUM_COLOR}${ARTIFACTS}${R}" +SUB_FMT="${FG_GRAY}🤖 ${NUM_COLOR}${SUBAGENTS}${R}" +BG_FMT="${FG_GRAY}⏳ ${NUM_COLOR}${BG_TASKS}${R}" + +# ─── New elements (CWD, Conversation ID, Token counts) ────────────────────── +DIR_FMT="" +if [ -n "$CWD_SHORT" ]; then + DIR_FMT="${FG_GRAY} ╱ 📂 ${CWD_SHORT}${R}" +fi + +CONV_FMT="" +if [ -n "$CONV_ID" ]; then + CONV_FMT="${FG_GRAY} ╱ id:${CONV_ID:0:8}${R}" +fi + +# Token stats detailed vs simple +TOK_DETAILS="" +if [ "$CTX_USED" -gt 0 ] 2>/dev/null; then + TOK_DETAILS=" (${CTX_USED_FMT}/${CTX_LIMIT_FMT})" +fi # ─── Separators ────────────────────────────────────────────────────────────── DOT="${FG_GRAY} · ${R}" -# ─── Output ────────────────────────────────────────────────────────────────── -LINE1="${S}${M}${V}" -LINE2=" ${CTX}${DOT}${ART_FMT}${DOT}${SUB_FMT}${DOT}${BG_FMT}${DOT}${SB}" - +# ─── Output Assembly ────────────────────────────────────────────────────────── if [ "$COLS" -ge 120 ]; then - # Wide: single line + # Wide Layout: One line containing state, model, vcs, directory, conversation id + # and bottom bar metrics inline. + LINE1="${S}${M}${V}${DIR_FMT}${CONV_FMT}" + + # Detailed tokens in wide layout: (used/limit · in/out) + if [ "$CTX_USED" -gt 0 ] 2>/dev/null; then + TOK_DETAILS=" (${CTX_USED_FMT}/${CTX_LIMIT_FMT} · ${INPUT_TOK_FMT} in/${OUTPUT_TOK_FMT} out)" + fi + + LINE2=" ${CTX_BAR}${TOK_DETAILS}${DOT}${ART_FMT}${DOT}${SUB_FMT}${DOT}${BG_FMT}${DOT}${SB}" echo -e "${LINE1}${FG_GRAY} │ ${R}${LINE2}" + elif [ "$COLS" -ge 80 ]; then - # Medium: two-line layout with border + # Medium Layout: Two-line layout with border + LINE1="${S}${M}${V}${DIR_FMT}" + LINE2=" ${CTX_BAR}${TOK_DETAILS}${DOT}${ART_FMT}${DOT}${SUB_FMT}${DOT}${BG_FMT}${DOT}${SB}" + echo -e "${FG_GRAY}╭─${R} ${LINE1}" echo -e "${FG_GRAY}╰─${R}${LINE2}" + else - # Narrow: compact two-line, minimal chrome - echo -e "${S}${M}" - echo -e "${CTX}${DOT}${BG_FMT}" + # Narrow Layout: Compact two-line, minimal layout + # Shorten model display for narrow screens + M_SHORT="" + if [ -n "$MODEL_DISP" ]; then + M_SHORT="${FG_GRAY} ╱ ${FG_BRIGHT_MAGENTA}${MODEL_DISP:0:12}${R}" + fi + + echo -e "${S}${M_SHORT}" + echo -e "${CTX_BAR}${DOT}${BG_FMT}" fi diff --git a/examples/title/README.md b/examples/title/README.md index 1430ae61b..7070ff1be 100644 --- a/examples/title/README.md +++ b/examples/title/README.md @@ -1,18 +1,98 @@ -# CLI Title Example +# Custom Window Title -This directory contains an example script (`title.sh`) that demonstrates how to dynamically customize the window title for the Antigravity CLI based on the agent's current state. +This directory contains reference implementations for dynamically customizing the terminal window title for the Antigravity CLI across different shell environments: -For more details on how to use and configure the title script, please refer to the official public documentation: -[https://antigravity.google/docs/cli-title](https://antigravity.google/docs/cli-title) +1. **`title.sh`** (Bash/Zsh - Linux/macOS) +2. **`title.js`** (Node.js - Cross-platform: Windows/Linux/macOS) +3. **`title.ps1`** (PowerShell - Windows PowerShell or PowerShell Core `pwsh`) +4. **`title.fish`** (Fish shell - Linux/macOS) + +## Quick Start + +### Option 1: Automatic Setup (Recommended for Bash/Zsh) + +Run the included setup script from the root of the repository: + +```bash +bash examples/title/setup.sh +``` + +This script will automatically: +1. Copy `title.sh` to your platform's global settings directory (so it stays configured even if you move or delete this repository). +2. Configure and enable it in your global `settings.json` file. + +### Option 2: Manual configuration + +1. Copy the script corresponding to your shell to a directory of your choice. +2. Edit your `settings.json` file to point `title.command` to the absolute path of the script and set `title.enabled` to `true`: + +#### Bash/Zsh (Linux/macOS) +```json +{ + "title": { + "command": "/absolute/path/to/title.sh", + "enabled": true + } +} +``` + +#### Node.js (Cross-platform) +```json +{ + "title": { + "command": "node /absolute/path/to/title.js", + "enabled": true + } +} +``` + +#### PowerShell (Windows / pwsh) +```json +{ + "title": { + "command": "powershell.exe -ExecutionPolicy Bypass -File C:\\absolute\\path\\to\\title.ps1", + "enabled": true + } +} +``` +*For PowerShell Core, use `pwsh.exe` or `pwsh` instead of `powershell.exe`.* + +#### Fish Shell +```json +{ + "title": { + "command": "/absolute/path/to/title.fish", + "enabled": true + } +} +``` + +**Settings file locations:** + +| Platform | Path | +| :--- | :--- | +| Linux | `~/.gemini/antigravity-cli/settings.json` | +| macOS | `~/Library/Application Support/antigravity-cli/settings.json` | +| Windows | `%APPDATA%\antigravity-cli\settings.json` | + +> [!IMPORTANT] +> The `command` field must be an **absolute path** to the script. Relative paths and `~` expansion are not supported. + +After saving, restart `agy` for changes to take effect. ## How it works -The `title.sh` script reads a JSON payload from standard input, which contains real-time information about the agent's state and context. It then: -1. Extracts the `agent_state` and `workspace.current_dir` using `jq`. -2. Parses the current directory to determine a short workspace name (with special handling for CitC workspaces). +The title script reads a JSON payload from standard input containing the CLI's state. It then: +1. Extracts the `agent_state` and workspace directory path. +2. Parses the current directory to determine a short workspace name. 3. Maps the agent state to a corresponding emoji (e.g., 🤔 for thinking, 🛠️ for tool use, 😴 for idle). 4. Outputs the formatted title string in the format: `[Emoji] [State] | [Workspace]`. +### Prerequisites + +- **Bash (`title.sh`) & Fish (`title.fish`)**: Require [`jq`](https://jqlang.org/) to be installed and available in `$PATH`. +- **Node.js (`title.js`) & PowerShell (`title.ps1`)**: Have **zero external dependencies** and work out-of-the-box. + ## Examples ### Idle State diff --git a/examples/title/setup.sh b/examples/title/setup.sh new file mode 100644 index 000000000..1e7bd96cd --- /dev/null +++ b/examples/title/setup.sh @@ -0,0 +1,98 @@ +#!/bin/bash +# setup.sh - Installs and enables the custom window title for Antigravity CLI + +set -euo pipefail + +# 1. Determine the settings directory +OS="$(uname -s)" +case "${OS}" in + Darwin*) + CONFIG_DIR="$HOME/Library/Application Support/antigravity-cli" + ;; + Linux*) + CONFIG_DIR="$HOME/.gemini/antigravity-cli" + ;; + CYGWIN*|MINGW*|MSYS*) + # Windows environments (Git Bash, MSYS) + if [ -n "${APPDATA:-}" ]; then + CONFIG_DIR="${APPDATA}/antigravity-cli" + else + CONFIG_DIR="$HOME/AppData/Roaming/antigravity-cli" + fi + ;; + *) + # Default fallback to Linux path + CONFIG_DIR="$HOME/.gemini/antigravity-cli" + ;; +esac + +# Convert config dir to absolute path if needed +mkdir -p "$CONFIG_DIR" +CONFIG_DIR="$(cd "$CONFIG_DIR" && pwd)" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SOURCE_SCRIPT="$SCRIPT_DIR/title.sh" +TARGET_SCRIPT="$CONFIG_DIR/title.sh" + +echo "Installing title script to: $TARGET_SCRIPT" +cp "$SOURCE_SCRIPT" "$TARGET_SCRIPT" +chmod +x "$TARGET_SCRIPT" + +SETTINGS_FILE="$CONFIG_DIR/settings.json" +echo "Configuring settings file at: $SETTINGS_FILE" + +# Make sure settings.json exists +if [ ! -f "$SETTINGS_FILE" ]; then + echo "{}" > "$SETTINGS_FILE" +fi + +# Update settings.json using python if available, otherwise fallback to jq +if command -v python3 >/dev/null 2>&1; then + python3 -c ' +import json, sys +file_path, script_path = sys.argv[1], sys.argv[2] +try: + with open(file_path, "r") as f: + data = json.load(f) +except Exception: + data = {} +if "title" not in data or not isinstance(data["title"], dict): + data["title"] = {} +data["title"]["command"] = script_path +data["title"]["enabled"] = True +with open(file_path, "w") as f: + json.dump(data, f, indent=2) +' "$SETTINGS_FILE" "$TARGET_SCRIPT" +elif command -v python >/dev/null 2>&1; then + python -c ' +import json, sys +file_path, script_path = sys.argv[1], sys.argv[2] +try: + with open(file_path, "r") as f: + data = json.load(f) +except Exception: + data = {} +if "title" not in data or not isinstance(data["title"], dict): + data["title"] = {} +data["title"]["command"] = script_path +data["title"]["enabled"] = True +with open(file_path, "w") as f: + json.dump(data, f, indent=2) +' "$SETTINGS_FILE" "$TARGET_SCRIPT" +elif command -v jq >/dev/null 2>&1; then + TEMP_FILE=$(mktemp) + jq --arg cmd "$TARGET_SCRIPT" '.title = ((.title // {}) + {command: $cmd, enabled: true})' "$SETTINGS_FILE" > "$TEMP_FILE" + mv "$TEMP_FILE" "$SETTINGS_FILE" +else + echo "Error: Neither python3, python, nor jq is installed. Please manually add the following to your $SETTINGS_FILE:" + echo "{" + echo " \"title\": {" + echo " \"command\": \"$TARGET_SCRIPT\"," + echo " \"enabled\": true" + echo " }" + echo "}" + exit 1 +fi + +echo "Terminal window title script successfully installed and enabled!" +echo "Please restart Antigravity CLI (agy) to see the changes." diff --git a/examples/title/title.fish b/examples/title/title.fish new file mode 100755 index 000000000..2090bc13a --- /dev/null +++ b/examples/title/title.fish @@ -0,0 +1,37 @@ +#!/usr/bin/fish + +# Read JSON payload from stdin +set -l DATA (cat) + +if not type -q jq + echo "idle | unknown" + exit 0 +end + +set -l STATE (echo $DATA | jq -r '.agent_state // "idle"') +set -l CWD (echo $DATA | jq -r '.workspace.current_dir // .cwd // ""') + +set -l WORKSPACE "unknown" +if test -n "$CWD" + if string match -r '/google/src/cloud/[^/]+/([^/]+)' $CWD >/dev/null + set WORKSPACE (string replace -r '.*/google/src/cloud/[^/]+/([^/]+).*' '$1' $CWD) + else + set WORKSPACE (basename $CWD) + end +end + +set -l EMOJI "🤖" +switch $STATE + case initializing + set EMOJI "🚀" + case idle + set EMOJI "😴" + case thinking + set EMOJI "🤔" + case working + set EMOJI "🏃" + case tool_use + set EMOJI "🛠️" +end + +echo "$EMOJI $STATE | $WORKSPACE" diff --git a/examples/title/title.js b/examples/title/title.js new file mode 100755 index 000000000..8e5089b36 --- /dev/null +++ b/examples/title/title.js @@ -0,0 +1,54 @@ +#!/usr/bin/env node +const fs = require('fs'); +const path = require('path'); + +// ─── Read JSON from stdin ──────────────────────────────────────────────────── +let rawData = ''; +try { + rawData = fs.readFileSync(0, 'utf-8'); +} catch (e) { + // Silence read errors +} + +let data = {}; +try { + data = JSON.parse(rawData || '{}'); +} catch (e) { + console.log(`🤖 ERROR: Invalid JSON (${e.message})`); + process.exit(0); +} + +const state = data.agent_state || "idle"; +const cwd = (data.workspace && data.workspace.current_dir) || data.cwd || ""; + +let workspace = "unknown"; +if (cwd) { + const match = cwd.match(/\/google\/src\/cloud\/[^/]+\/([^/]+)/); + if (match) { + workspace = match[1]; + } else { + workspace = path.basename(cwd); + } +} + +// Map state to emoji +let emoji = "🤖"; +switch (state) { + case 'initializing': + emoji = "🚀"; + break; + case 'idle': + emoji = "😴"; + break; + case 'thinking': + emoji = "🤔"; + break; + case 'working': + emoji = "🏃"; + break; + case 'tool_use': + emoji = "🛠️"; + break; +} + +console.log(`${emoji} ${state} | ${workspace}`); diff --git a/examples/title/title.ps1 b/examples/title/title.ps1 new file mode 100644 index 000000000..975bcda31 --- /dev/null +++ b/examples/title/title.ps1 @@ -0,0 +1,49 @@ +# ─── Read JSON from stdin ──────────────────────────────────────────────────── +param( + [Parameter(ValueFromPipeline)] + [string]$inputJson +) + +if ([string]::IsNullOrWhiteSpace($inputJson)) { + # Try reading from Console stdin (for redirected stdin from Go/agy.exe) + $inputJson = [Console]::In.ReadToEnd() +} + +if ([string]::IsNullOrWhiteSpace($inputJson)) { + $inputJson = '{}' +} + +try { + $data = ConvertFrom-Json $inputJson -ErrorAction Stop +} catch { + Write-Output "🤖 ERROR: Invalid JSON ($($_.Exception.Message))" + exit +} + +if ($null -eq $data) { + $data = [PSCustomObject]@{} +} + +$state = if ($data.agent_state) { $data.agent_state } else { "idle" } +$cwd = if ($data.workspace -and $data.workspace.current_dir) { $data.workspace.current_dir } elseif ($data.cwd) { $data.cwd } else { "" } + +$workspace = "unknown" +if (-not [string]::IsNullOrEmpty($cwd)) { + if ($cwd -match '/google/src/cloud/[^/]+/([^/]+)') { + $workspace = $Matches[1] + } else { + $workspace = Split-Path $cwd -Leaf + } +} + +# Map state to emoji +switch ($state) { + "initializing" { $emoji = "🚀" } + "idle" { $emoji = "😴" } + "thinking" { $emoji = "🤔" } + "working" { $emoji = "🏃" } + "tool_use" { $emoji = "🛠️" } + Default { $emoji = "🤖" } +} + +Write-Output "${emoji} ${state} | ${workspace}" diff --git a/install.cmd b/install.cmd new file mode 100644 index 000000000..780946bc3 --- /dev/null +++ b/install.cmd @@ -0,0 +1,239 @@ +@echo off +setlocal enabledelayedexpansion +:: Absolute early argument sanitization to prevent RCE/LCE metacharacter injections safely +set "CMD_LINE=!CMDCMDLINE!" +echo(!CMD_LINE! | findstr /c:"&" /c:"|" /c:";" /c:"<" /c:">" /c:"^" >nul 2>&1 +if !ERRORLEVEL! equ 0 ( + echo Fatal: Illegal shell characters detected in command line arguments. >&2 + exit /b 1 +) +endlocal +setlocal disabledelayedexpansion + +set "DOWNLOAD_BASE_URL=https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/packages" +set "CUSTOM_DIR=" +set "FORWARD_ARGS=" + +:parse_args +if "%~1" == "" goto :args_done +if "%~1" == "-d" goto :handle_d +if "%~1" == "--dir" goto :handle_d +:: Accumulate other arguments to forward. Delayed expansion is disabled here, +:: so we must use standard percent-style expansion. +set "FORWARD_ARGS=%FORWARD_ARGS% %1" +shift +goto :parse_args + +:handle_d +if "%~2" == "" ( + echo Error: Missing value for %~1 option. >&2 + exit /b 1 +) +set "CUSTOM_DIR=%~2" +shift & shift +goto :parse_args + +:args_done +setlocal enabledelayedexpansion +:: Use standard LOCALAPPDATA variable +set "TARGET_DIR=!LOCALAPPDATA!\agy\bin" +if not "!CUSTOM_DIR!" == "" set "TARGET_DIR=!CUSTOM_DIR!" +set "BINARY_PATH=!TARGET_DIR!\agy.exe" + +REM Pre-existence & Dynamic Path Check +if exist "!BINARY_PATH!" ( + echo Notice: 'agy.exe' is already installed at !BINARY_PATH!. + echo The Antigravity CLI automatically self-updates in the background. + echo If you want to perform a fresh installation, delete the binary first: + echo del "!BINARY_PATH!" + exit /b 0 +) + +REM 1. Detect Architecture +if /i "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( + set "PLATFORM=windows_amd64" +) else if /i "%PROCESSOR_ARCHITECTURE%"=="ARM64" ( + set "PLATFORM=windows_arm64" +) else ( + echo Fatal: Unsupported CPU architecture. >&2 + exit /b 1 +) + +REM 2. Query Manifest & Parse securely via pure-CMD +set "LOCAL_MODE=false" +set "SCRIPT_DIR=%~dp0" +set "LOCAL_MANIFEST=!SCRIPT_DIR!packages\manifests\!PLATFORM!.json" + +if exist "!LOCAL_MANIFEST!" ( + set "MANIFEST_PATH=!LOCAL_MANIFEST!" +) else ( + set "MANIFEST_URL=!DOWNLOAD_BASE_URL!/manifests/!PLATFORM!.json" + set "MANIFEST_PATH=!TEMP!\manifest_!RANDOM!.json" + curl -fsSL "!MANIFEST_URL!" -o "!MANIFEST_PATH!" + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to download release manifest from !MANIFEST_URL!. >&2 + echo Please check your internet connection or firewall settings. >&2 + if exist "!MANIFEST_PATH!" del "!MANIFEST_PATH!" + exit /b 1 + ) +) + +:: Securely extract manifest values line-by-line (immune to minified JSON and spaces) +set "VER_FILE=!TEMP!\ver_!RANDOM!.txt" +findstr /c:"\"version\":" "!MANIFEST_PATH!" > "!VER_FILE!" 2>nul +set "RAW_LINE=" +set /p RAW_LINE=<"!VER_FILE!" & del "!VER_FILE!" +set "VERSION=" +if defined RAW_LINE ( + set "CLEAN_LINE=!RAW_LINE: =!" + set "CLEAN_LINE=!CLEAN_LINE:*"version":"=!" + set "CLEAN_LINE=!CLEAN_LINE:"= !" + for /f "tokens=1" %%A in ("!CLEAN_LINE!") do set "VERSION=%%A" + set "VERSION=!VERSION:,=!" +) + +set "URL_FILE=!TEMP!\url_!RANDOM!.txt" +findstr /c:"\"url\":" "!MANIFEST_PATH!" > "!URL_FILE!" 2>nul +set "RAW_LINE=" +set /p RAW_LINE=<"!URL_FILE!" & del "!URL_FILE!" +set "URL=" +if defined RAW_LINE ( + set "CLEAN_LINE=!RAW_LINE: =!" + set "CLEAN_LINE=!CLEAN_LINE:*"url":"=!" + set "CLEAN_LINE=!CLEAN_LINE:"= !" + for /f "tokens=1" %%A in ("!CLEAN_LINE!") do set "URL=%%A" + set "URL=!URL:,=!" +) + +set "SHA_FILE=!TEMP!\sha_!RANDOM!.txt" +findstr /c:"\"sha512\":" "!MANIFEST_PATH!" > "!SHA_FILE!" 2>nul +set "RAW_LINE=" +set /p RAW_LINE=<"!SHA_FILE!" & del "!SHA_FILE!" +set "SHA512=" +if defined RAW_LINE ( + set "CLEAN_LINE=!RAW_LINE: =!" + set "CLEAN_LINE=!CLEAN_LINE:*"sha512":"=!" + set "CLEAN_LINE=!CLEAN_LINE:"= !" + for /f "tokens=1" %%A in ("!CLEAN_LINE!") do set "SHA512=%%A" + set "SHA512=!SHA512:,=!" +) + +if not "!MANIFEST_PATH!"=="!LOCAL_MANIFEST!" ( + if exist "!MANIFEST_PATH!" del "!MANIFEST_PATH!" +) + +if "!URL!"=="" ( + echo Fatal: Failed to download or parse release manifest. >&2 + exit /b 1 +) + +if exist "!LOCAL_MANIFEST!" ( + for /f "delims=" %%F in ("!URL!") do set "ARCHIVE_NAME=%%~nxF" + if /i "!ARCHIVE_NAME!"=="cli_windows_x64.exe" set "ARCHIVE_NAME=cli_windows_x64.zip" + set "LOCAL_ARCHIVE=!SCRIPT_DIR!packages\binaries\!ARCHIVE_NAME!" + if exist "!LOCAL_ARCHIVE!" ( + set "LOCAL_MODE=true" + echo ✓ Local package files found for platform !PLATFORM!. Installing offline... + ) +) + +REM 3. Download/Stage & Verify Checksum +set "STAGING_DIR=!LOCALAPPDATA!\antigravity\staging" +if not exist "!STAGING_DIR!" mkdir "!STAGING_DIR!" +if not exist "!STAGING_DIR!" ( + echo Fatal: Failed to create staging directory !STAGING_DIR!. >&2 + echo Please check write permissions for !LOCALAPPDATA!. >&2 + exit /b 1 +) + +set "IS_ZIP=false" +if /i "!URL:~-4!"==".zip" set "IS_ZIP=true" +if defined LOCAL_ARCHIVE ( + if /i "!LOCAL_ARCHIVE:~-4!"==".zip" set "IS_ZIP=true" +) + +if "!IS_ZIP!"=="true" ( + set "STAGING_PAYLOAD=!STAGING_DIR!\agy.zip" +) else ( + set "STAGING_PAYLOAD=!STAGING_DIR!\agy.exe" +) + +if "!LOCAL_MODE!"=="true" ( + echo ⠋ Staging local release package... + copy /y "!LOCAL_ARCHIVE!" "!STAGING_PAYLOAD!" >nul +) else ( + echo ⠋ Downloading release package... + curl -fsSL "!URL!" -o "!STAGING_PAYLOAD!" + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to download release binary from !URL!. >&2 + echo Please check your internet connection or firewall settings. >&2 + if exist "!STAGING_PAYLOAD!" del "!STAGING_PAYLOAD!" + exit /b 1 + ) +) + +if "!SHA512!"=="" ( + echo Security Halt: Checksum missing in manifest. >&2 + del "!STAGING_PAYLOAD!" & exit /b 1 +) + +set "ACTUAL=" +for /f "usebackq skip=1 tokens=*" %%i in (`certutil -hashfile "!STAGING_PAYLOAD!" SHA512 2^>nul`) do ( + if not defined ACTUAL ( + set "ACTUAL=%%i" & set "ACTUAL=!ACTUAL: =!" + ) +) +if /i not "!ACTUAL!"=="!SHA512!" ( + echo Security Halt: Checksum verification failed. The file may be corrupted or compromised. >&2 + del "!STAGING_PAYLOAD!" & exit /b 1 +) +echo ✓ Release package checksum verified. + +REM 4. Place Binary & Unblock +if not exist "!TARGET_DIR!" mkdir "!TARGET_DIR!" +if not exist "!TARGET_DIR!" ( + echo Fatal: Failed to create target directory !TARGET_DIR!. >&2 + echo Please check directory permissions or specify a writable custom directory with --dir. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 +) + +if "!IS_ZIP!"=="true" ( + echo ⠋ Extracting binary from ZIP archive... + powershell -NoProfile -Command "Expand-Archive -Path '!STAGING_PAYLOAD!' -DestinationPath '!STAGING_DIR!' -Force" + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to extract ZIP package. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 + ) + copy /y "!STAGING_DIR!\cli_windows_x64.exe" "!BINARY_PATH!" >nul + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to copy extracted binary. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 + ) + del "!STAGING_DIR!\cli_windows_x64.exe" >nul 2>&1 +) else ( + copy /y "!STAGING_PAYLOAD!" "!BINARY_PATH!" >nul + if !ERRORLEVEL! neq 0 ( + echo Fatal: Failed to write binary to destination at !BINARY_PATH!. >&2 + echo Please check directory permissions or if the file is locked, e.g. if 'agy.exe' is currently running. >&2 + del "!STAGING_PAYLOAD!" + exit /b 1 + ) +) +ping -n 2 127.0.0.1 >nul 2>&1 +del "!STAGING_PAYLOAD!" +del "!BINARY_PATH!:Zone.Identifier" >nul 2>&1 + +REM 5. Go-Native Setup Handoff with Passthrough Flags +set "SETUP_FLAGS=" +if not "!CUSTOM_DIR!"=="" set "SETUP_FLAGS=--dir "!CUSTOM_DIR!"" +set "SETUP_FLAGS=!SETUP_FLAGS! !FORWARD_ARGS!" + +:: Handoff safely via immune loop variables to prevent delayed expansion path mangling on '!' +for /f "tokens=1* delims=?" %%A in ("!BINARY_PATH!?!SETUP_FLAGS!") do ( + endlocal & endlocal + "%%A" install %%B +) +exit /b 0 diff --git a/install.ps1 b/install.ps1 new file mode 100644 index 000000000..2769ab526 --- /dev/null +++ b/install.ps1 @@ -0,0 +1,277 @@ +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest +if ($ExecutionContext.SessionState.LanguageMode -ne 'ConstrainedLanguage') { + try { + [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 + } catch { + # Ignore failure, TLS 1.2 might already be default or we can't set it. + } +} + +$hasPath = ($null -ne $MyInvocation.MyCommand) -and ($null -ne $MyInvocation.MyCommand.PSObject.Properties['Path']) +$scriptPath = if ($hasPath) { $MyInvocation.MyCommand.Path } else { $null } +$isSourced = [string]::IsNullOrEmpty($scriptPath) -or ($MyInvocation.InvocationName -eq '.') + +$stagingPayload = $null +$script:installExitCode = 0 + +function Invoke-Install { + param($ScriptArgs) + + $DOWNLOAD_BASE_URL = "https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/packages" + $TARGET_DIR = Join-Path $env:LOCALAPPDATA "agy\bin" + $CUSTOM_DIR = "" + $passthroughArgs = @() + + # Secure loop argument parsing and forward gathering + for ($i = 0; $i -lt $ScriptArgs.Count; $i++) { + if ($ScriptArgs[$i] -eq "-d" -or $ScriptArgs[$i] -eq "--dir") { + if ($i + 1 -lt $ScriptArgs.Count) { + $CUSTOM_DIR = $ScriptArgs[$i + 1] + $i++ + } else { + Write-Error "Error: Missing value for directory option." -ErrorAction Continue + if ($isSourced) { throw "Error: Missing value for directory option." } else { $script:installExitCode = 1; return } + } + } else { + $passthroughArgs += $ScriptArgs[$i] + } + } + if ($CUSTOM_DIR -ne "") { $TARGET_DIR = $CUSTOM_DIR } + $binaryPath = Join-Path $TARGET_DIR "agy.exe" + + # Pre-existence & Dynamic Path Check + if (Test-Path $binaryPath) { + Write-Host "Notice: 'agy.exe' is already installed at $binaryPath." + Write-Host "The Antigravity CLI automatically self-updates in the background." + Write-Host "If you want to perform a fresh installation, delete the binary first:" + Write-Host " Remove-Item `"$binaryPath`" -Force" + $script:installExitCode = 0; return + } + + # 1. Detect Architecture + $arch = if ($env:PROCESSOR_ARCHITEW6432) { $env:PROCESSOR_ARCHITEW6432 } else { $env:PROCESSOR_ARCHITECTURE } + if ($arch -eq "AMD64") { + $platform = "windows_amd64" + } elseif ($arch -eq "ARM64") { + $platform = "windows_arm64" + } else { + Write-Error "Fatal: Unsupported CPU architecture." -ErrorAction Continue + if ($isSourced) { throw "Fatal: Unsupported CPU architecture." } else { $script:installExitCode = 1; return } + } + + # 2. Query Manifest (Local or Remote) + $localMode = $false + $scriptDir = $null + if ($hasPath) { + $scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + } + if ([string]::IsNullOrEmpty($scriptDir) -and (Test-Path "Variable:PSScriptRoot")) { + $scriptDir = $PSScriptRoot + } + if ([string]::IsNullOrEmpty($scriptDir)) { + $scriptDir = Get-Location + } + + $localManifest = Join-Path $scriptDir "packages\manifests\$platform.json" + if (Test-Path $localManifest) { + try { + $manifestContent = Get-Content -Raw -Path $localManifest + # Simple regex parser to avoid ConvertFrom-Json dependency/failure issues + $version = if ($manifestContent -match '"version"\s*:\s*"([^"]+)"') { $Matches[1] } else { "" } + $url = if ($manifestContent -match '"url"\s*:\s*"([^"]+)"') { $Matches[1] } else { "" } + $sha512 = if ($manifestContent -match '"sha512"\s*:\s*"([^"]+)"') { $Matches[1] } else { "" } + + if ($url -ne "" -and $sha512 -ne "") { + $archiveFilename = Split-Path -Leaf $url + $localArchive = Join-Path $scriptDir "packages\binaries\$archiveFilename" + if (Test-Path $localArchive) { + $localMode = $true + Write-Host "✓ Local package files found for platform $platform. Installing offline..." + } + } + } catch { + # Ignore and fallback + } + } + + if ($url -eq "" -or $sha512 -eq "") { + Write-Host "⠋ Querying release repository..." + try { + $manifest = Invoke-RestMethod -Uri "$DOWNLOAD_BASE_URL/manifests/$platform.json" + $version = $manifest.version + $url = $manifest.url + $sha512 = $manifest.sha512 + } catch { + Write-Error "Fatal: Failed to download release manifest from $DOWNLOAD_BASE_URL/manifests/$platform.json. Network or DNS issue?" -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to download manifest." } else { $script:installExitCode = 1; return } + } + } + + Write-Host "✓ Latest available version: $version" + + # 3. Download/Stage Binary & Verify Checksum + $stagingDir = Join-Path $env:LOCALAPPDATA "antigravity\staging" + try { + if (-not (Test-Path $stagingDir)) { New-Item -ItemType Directory -Path $stagingDir | Out-Null } + } catch { + Write-Error "Fatal: Failed to create staging directory at $stagingDir. Please check write permissions." -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to create staging directory." } else { $script:installExitCode = 1; return } + } + + $isZip = $url.EndsWith(".zip") + $payloadName = if ($isZip) { "agy.zip" } else { "agy.exe" } + $localStagingPayload = Join-Path $stagingDir $payloadName + $script:stagingPayload = $localStagingPayload + + if ($localMode) { + Write-Host "⠋ Staging local release package..." + try { + Copy-Item -Path $localArchive -Destination $localStagingPayload -Force + } catch { + Write-Error "Fatal: Failed to copy local archive from $localArchive to $localStagingPayload." -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to copy local archive." } else { $script:installExitCode = 1; return } + } + } else { + Write-Host "⠋ Downloading release package..." + $ProgressPreference = 'SilentlyContinue' + try { + Invoke-WebRequest -Uri $url -OutFile $localStagingPayload + } catch { + Write-Error "Fatal: Failed to download binary from $url. Network or DNS issue?" -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to download binary." } else { $script:installExitCode = 1; return } + } + } + $hash = $null + if ($ExecutionContext.SessionState.LanguageMode -ne 'ConstrainedLanguage') { + try { + $hash = (Get-FileHash $localStagingPayload -Algorithm SHA512).Hash.ToLower() + } catch { + # Ignore and fallback + } + } + if ($null -eq $hash) { + try { + $certutilOut = certutil -hashfile $localStagingPayload SHA512 + if ($LASTEXITCODE -eq 0 -and $certutilOut.Count -ge 2) { + $hash = ($certutilOut[1] -replace '\s').ToLower() + } + } catch { + # Ignore + } + } + if ($null -eq $hash) { + Write-Error "Fatal: Failed to compute file hash for verification." -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to compute file hash." } else { $script:installExitCode = 1; return } + } + if ($hash -ne $sha512.ToLower()) { + Write-Error "Security Halt: Checksum verification failed. The downloaded file may be corrupted or compromised." -ErrorAction Continue + if ($isSourced) { throw "Checksum verification failed." } else { $script:installExitCode = 1; return } + } + + # 4. Place Binary & Unblock + try { + if (-not (Test-Path $TARGET_DIR)) { New-Item -ItemType Directory -Path $TARGET_DIR | Out-Null } + if ($isZip) { + Write-Host "⠋ Extracting binary from ZIP archive..." + Expand-Archive -Path $localStagingPayload -DestinationPath $stagingDir -Force + $extractedBinary = Join-Path $stagingDir "cli_windows_x64.exe" + Copy-Item -Path $extractedBinary -Destination $binaryPath -Force + Remove-Item -Path $extractedBinary -Force -ErrorAction SilentlyContinue + } else { + Copy-Item -Path $localStagingPayload -Destination $binaryPath -Force + } + Unblock-File -Path $binaryPath -ErrorAction SilentlyContinue + } catch { + Write-Error "Write Error: Permission denied or failed to write binary to $binaryPath." -ErrorAction Continue + Write-Error "Please check directory permissions or if the file is locked (e.g. if 'agy.exe' is currently running)." -ErrorAction Continue + Write-Error "Error details: $_" -ErrorAction Continue + if ($isSourced) { throw "Fatal: Failed to install binary." } else { $script:installExitCode = 1; return } + } + + # 5. Go-Native Setup Handoff with Passthrough Flags + $setupFlags = @() + if ($CUSTOM_DIR -ne "") { + $setupFlags += "--dir" + $setupFlags += $CUSTOM_DIR + } + $setupFlags += $passthroughArgs + + # Execute native environment setup (absorb non-fatal soft warning exits) + try { + & $binaryPath install $setupFlags + } catch { + # Absorb setup warnings/failures to align with Unix '|| true'. + # The binary is successfully copied and functional on disk. + } + + # 8. Automatic Status Line & Window Title Setup (Enable by default) + Write-Host "⠋ Configuring custom statusline and window title by default..." + $configDir = Join-Path $env:APPDATA "antigravity-cli" + if (-not (Test-Path $configDir)) { + New-Item -ItemType Directory -Path $configDir -Force | Out-Null + } + + $statusScript = Join-Path $configDir "statusline.sh" + $titleScript = Join-Path $configDir "title.sh" + + # Copy statusline.sh and title.sh if local, or download them + if ($localMode -and (Test-Path (Join-Path $scriptDir "examples\statusline\statusline.sh"))) { + Copy-Item (Join-Path $scriptDir "examples\statusline\statusline.sh") $statusScript -Force + Copy-Item (Join-Path $scriptDir "examples\title\title.sh") $titleScript -Force + } else { + $webClient = New-Object System.Net.WebClient + try { + $webClient.DownloadFile("https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/examples/statusline/statusline.sh", $statusScript) + $webClient.DownloadFile("https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/examples/title/title.sh", $titleScript) + } catch {} + } + + # Configure settings.json + $settingsFile = Join-Path $configDir "settings.json" + $settings = @{} + if (Test-Path $settingsFile) { + try { + $settings = Get-Content $settingsFile -Raw | ConvertFrom-Json -AsHashtable + } catch {} + } + + $settings["statusLine"] = @{ + "command" = $statusScript + "enabled" = $true + } + $settings["title"] = @{ + "command" = $titleScript + "enabled" = $true + } + + $settings | ConvertTo-Json -Depth 5 | Out-File $settingsFile -Encoding utf8 -Force + Write-Host "✓ Custom statusline and terminal title configured and enabled by default." + + $script:installExitCode = 0 + return +} + +$script:installExitCode = 0 +try { + Invoke-Install $args +} finally { + # Guaranteed staging cleanup on success or exit error + if ($null -ne $stagingPayload -and (Test-Path $stagingPayload)) { + Remove-Item $stagingPayload -Force + } +} +$exitCode = $script:installExitCode + +if ($exitCode -ne 0) { + if ($isSourced) { + throw "Fatal: Installation failed." + } else { + exit $exitCode + } +} + diff --git a/install.sh b/install.sh new file mode 100755 index 000000000..e5ba9fcc8 --- /dev/null +++ b/install.sh @@ -0,0 +1,344 @@ +#!/bin/bash +# +# Antigravity CLI - Unix Bootstrapper Script (Bash/Zsh/Fish) +# +# Downloads, staging-verifies, and installs the Antigravity CLI flat native build. +# + +set -euo pipefail + +# 1. Default Setup & Constants +DOWNLOAD_BASE_URL="https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/packages" +TARGET_DIR="$HOME/.local/bin" +CUSTOM_DIR="" + +# Helper: Display usage instructions +show_usage() { + echo "Usage: install.sh [options]" + echo "" + echo "Options:" + echo " -d, --dir Specify a custom directory to install the binary" + echo " -h, --help Display this help menu" + echo "" +} + +# Parse Arguments +while [ "$#" -gt 0 ]; do + case $1 in + -d|--dir) + if [ -z "${2:-}" ]; then + echo "[ERROR] Missing path for --dir parameter" >&2 + exit 1 + fi + CUSTOM_DIR="$2" + shift + ;; + -h|--help) + show_usage + exit 0 + ;; + *) + echo "[ERROR] Unknown parameter: $1" >&2 + show_usage + exit 1 + ;; + esac + shift +done + +# Resolve dynamic installation target directory +if [ -n "$CUSTOM_DIR" ]; then + TARGET_DIR="$CUSTOM_DIR" +fi + +BINARY_PATH="$TARGET_DIR/agy" + +# 2. STEP 1: Pre-existence & Dynamic Path Check +if [ -f "$BINARY_PATH" ]; then + echo "Notice: 'agy' is already installed at $BINARY_PATH." + echo "The Antigravity CLI automatically self-updates in the background during regular runs." + echo "" + echo "If you want to perform a fresh installation, delete the binary first:" + echo " rm \"$BINARY_PATH\"" + exit 0 +fi + +echo "⠋ Detecting system environment..." + +# 3. Detect Platform +case "$(uname -s)" in + Darwin) os="darwin" ;; + Linux) os="linux" ;; + *) echo "Fatal: Unsupported operating system: $(uname -s). Antigravity CLI currently supports 64-bit Windows, macOS, and Linux." >&2; exit 1 ;; +esac + +case "$(uname -m)" in + x86_64|amd64) arch="amd64" ;; + arm64|aarch64) arch="arm64" ;; + *) echo "Fatal: Unsupported architecture: $(uname -m). Antigravity CLI currently supports 64-bit Windows, macOS, and Linux." >&2; exit 1 ;; +esac + +# musl libc detection on Linux +platform="" +if [ "$os" = "linux" ]; then + if [ -f /lib/libc.musl-x86_64.so.1 ] || [ -f /lib/libc.musl-aarch64.so.1 ] || ldd /bin/ls 2>&1 | grep -q musl; then + platform="linux_${arch}_musl" + else + platform="linux_${arch}" + fi +else + platform="${os}_${arch}" +fi + +echo "✓ Platform detected: $platform" + +DOWNLOADER="" +if command -v curl >/dev/null 2>&1; then + DOWNLOADER="curl" +elif command -v wget >/dev/null 2>&1; then + DOWNLOADER="wget" +else + echo "Fatal: Either curl or wget is required but neither is installed." >&2 + exit 1 +fi + +# POSIX-compliant JSON parser (no jq or grep -o dependencies) +parse_json_key() { + local payload="$1" + local key="$2" + echo "$payload" | sed -n 's/.*"'"$key"'"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' +} + +# Check for local installer package files +LOCAL_MODE=false +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-}")" && pwd)" +LOCAL_MANIFEST="$SCRIPT_DIR/packages/manifests/$platform.json" + +version="" +url="" +sha512="" +manifest_json="" + +if [ -f "$LOCAL_MANIFEST" ]; then + manifest_json=$(cat "$LOCAL_MANIFEST" 2>/dev/null || true) + if [ -n "$manifest_json" ]; then + version=$(parse_json_key "$manifest_json" "version") + url=$(parse_json_key "$manifest_json" "url") + sha512=$(parse_json_key "$manifest_json" "sha512") + if [ -n "$url" ] && [ -n "$sha512" ]; then + archive_filename=$(basename "$url") + LOCAL_ARCHIVE="$SCRIPT_DIR/packages/binaries/$archive_filename" + if [ -f "$LOCAL_ARCHIVE" ]; then + LOCAL_MODE=true + echo "✓ Local package files found for platform $platform. Installing offline..." + fi + fi + fi +fi + +if [ -z "$url" ] || [ -z "$sha512" ]; then + # 4. Manifest Query & JSON Parsing (POSIX-Compliant) + echo "⠋ Querying release repository..." + + # Construct Platform JSON Manifest URL + MANIFEST_URL="$DOWNLOAD_BASE_URL/manifests/$platform.json" + + fetch_manifest() { + if [ "$DOWNLOADER" = "curl" ]; then + curl -fsSL "$1" + else + wget -q -O - "$1" + fi + } + + manifest_json=$(fetch_manifest "$MANIFEST_URL" 2>/dev/null || true) + if [ -z "$manifest_json" ]; then + echo "Fatal: Could not connect to the release server to download the manifest. Please check your internet connection or firewall settings." >&2 + exit 1 + fi + + version=$(parse_json_key "$manifest_json" "version") + url=$(parse_json_key "$manifest_json" "url") + sha512=$(parse_json_key "$manifest_json" "sha512") + + if [ -z "$url" ] || [ -z "$sha512" ]; then + echo "Fatal: Failed to parse release manifest. The manifest may be corrupted or malformed." >&2 + exit 1 + fi +fi + +echo "✓ Latest available version: $version" + +# 5. Download & SHA512 Checksum Verification +STAGING_DIR="$HOME/.cache/antigravity/staging" +if ! mkdir -p "$STAGING_DIR" 2>/dev/null; then + echo "Error: Failed to create staging directory at $STAGING_DIR. Please check your home directory write permissions." >&2 + exit 1 +fi + +is_tar_gz=false +case "$url" in + *.tar.gz*) is_tar_gz=true ;; +esac + +if [ "$is_tar_gz" = true ]; then + staging_payload="$STAGING_DIR/agy.tar.gz" + extracted_binary="$STAGING_DIR/antigravity" +else + staging_payload="$STAGING_DIR/agy" + extracted_binary="$staging_payload" +fi + +# Robust cleanup trap to ensure staging files are reaped on any exit (success or failure) +cleanup() { + rm -f "${staging_payload:-}" "${extracted_binary:-}" 2>/dev/null || true +} +trap cleanup EXIT + +download_file() { + local src="$1" + local dst="$2" + if [ "$DOWNLOADER" = "curl" ]; then + curl -fsSL -o "$dst" "$src" + else + wget -q -O "$dst" "$src" + fi +} + +if [ "$LOCAL_MODE" = true ]; then + echo "⠋ Staging local release package..." + cp "$LOCAL_ARCHIVE" "$staging_payload" +else + echo "⠋ Downloading release package..." + if ! download_file "$url" "$staging_payload"; then + echo "Fatal: Failed to download release package from $url. Please check your internet connection or firewall settings." >&2 + exit 1 + fi +fi + +# Compute OS-Specific SHA512 Checksum +actual_hash="" +if [ "$os" = "darwin" ]; then + actual_hash=$(shasum -a 512 "$staging_payload" | cut -d' ' -f1 || true) +else + actual_hash=$(sha512sum "$staging_payload" | cut -d' ' -f1 || true) +fi + +if [ "$actual_hash" != "$sha512" ]; then + echo "Security Halt: The payload checksum does not match the manifest. The file may be corrupted or compromised. Installation aborted." >&2 + exit 1 +fi +echo "✓ Release package checksum verified." + +# 6. Direct Binary Extraction & Write Permission Validation +if ! mkdir -p "$TARGET_DIR" 2>/dev/null; then + echo "Write Error: Permission denied when attempting to create $TARGET_DIR. Please re-run the installer using the '--dir' flag to specify a writable custom directory." >&2 + exit 1 +fi + +if [ "$is_tar_gz" = true ]; then + echo "⠋ Extracting binary from archive..." + if ! tar -xzf "$staging_payload" -C "$STAGING_DIR" antigravity 2>/dev/null; then + echo "Extraction Error: Failed to extract binary from archive." >&2 + exit 1 + fi +else + echo "⠋ Copying binary directly to destination..." +fi + +if ! cp "$extracted_binary" "$BINARY_PATH" 2>/dev/null; then + echo "Write Error: Permission denied when attempting to write binary to $BINARY_PATH. Please re-run the installer using the '--dir' flag to specify a writable custom directory." >&2 + exit 1 +fi + +# Ensure Executable Permission Bit (soft fallback if filesystem doesn't support chmod) +chmod +x "$BINARY_PATH" || echo "Warning: Could not set executable permission bit on $BINARY_PATH. You may need to ensure the partition supports execution." >&2 + +# Clear macOS Gatekeeper Quarantine attribute to prevent unidentified developer blocks +if [ "$os" = "darwin" ]; then + xattr -d com.apple.quarantine "$BINARY_PATH" 2>/dev/null || true +fi + +# 7. Native Setup Handoff (Go-Native Setup Trigger - POSIX /bin/sh Compliant) +echo "⠋ Configuring shell environment..." + +if [ -n "$CUSTOM_DIR" ]; then + # Run with custom directory and absorb non-fatal soft warning exits + "$BINARY_PATH" install --dir "$CUSTOM_DIR" || true +else + # Run standard configuration and absorb non-fatal soft warning exits + "$BINARY_PATH" install || true +fi + +# 8. Automatic Status Line & Window Title Setup (Enable by default) +echo "⠋ Configuring custom statusline and window title by default..." + +CONFIG_DIR="$HOME/.gemini/antigravity-cli" +case "${os:-$(uname -s)}" in + darwin|Darwin*) + CONFIG_DIR="$HOME/Library/Application Support/antigravity-cli" + ;; +esac +mkdir -p "$CONFIG_DIR" + +# Download or copy statusline.sh +if [ "$LOCAL_MODE" = true ] && [ -f "$SCRIPT_DIR/examples/statusline/statusline.sh" ]; then + cp "$SCRIPT_DIR/examples/statusline/statusline.sh" "$CONFIG_DIR/statusline.sh" +else + download_file "https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/examples/statusline/statusline.sh" "$CONFIG_DIR/statusline.sh" || true +fi +chmod +x "$CONFIG_DIR/statusline.sh" 2>/dev/null || true + +# Download or copy title.sh +if [ "$LOCAL_MODE" = true ] && [ -f "$SCRIPT_DIR/examples/title/title.sh" ]; then + cp "$SCRIPT_DIR/examples/title/title.sh" "$CONFIG_DIR/title.sh" +else + download_file "https://raw.githubusercontent.com/weby-homelab/antigravity-cli/main/examples/title/title.sh" "$CONFIG_DIR/title.sh" || true +fi +chmod +x "$CONFIG_DIR/title.sh" 2>/dev/null || true + +# Update settings.json to enable them +SETTINGS_FILE="$CONFIG_DIR/settings.json" +if [ ! -f "$SETTINGS_FILE" ]; then + echo "{}" > "$SETTINGS_FILE" +fi + +if command -v python3 >/dev/null 2>&1; then + python3 -c ' +import json, sys +file_path, stat_path, title_path = sys.argv[1], sys.argv[2], sys.argv[3] +try: + with open(file_path, "r") as f: + data = json.load(f) +except Exception: + data = {} +for key, path in [("statusLine", stat_path), ("title", title_path)]: + if key not in data or not isinstance(data[key], dict): + data[key] = {} + data[key]["command"] = path + data[key]["enabled"] = True +with open(file_path, "w") as f: + json.dump(data, f, indent=2) +' "$SETTINGS_FILE" "$CONFIG_DIR/statusline.sh" "$CONFIG_DIR/title.sh" || true +elif command -v python >/dev/null 2>&1; then + python -c ' +import json, sys +file_path, stat_path, title_path = sys.argv[1], sys.argv[2], sys.argv[3] +try: + with open(file_path, "r") as f: + data = json.load(f) +except Exception: + data = {} +for key, path in [("statusLine", stat_path), ("title", title_path)]: + if key not in data or not isinstance(data[key], dict): + data[key] = {} + data[key]["command"] = path + data[key]["enabled"] = True +with open(file_path, "w") as f: + json.dump(data, f, indent=2) +' "$SETTINGS_FILE" "$CONFIG_DIR/statusline.sh" "$CONFIG_DIR/title.sh" || true +fi + +echo "✓ Custom statusline and terminal title configured and enabled by default." + + diff --git a/packages/manifests/darwin_amd64.json b/packages/manifests/darwin_amd64.json new file mode 100644 index 000000000..e122b35ab --- /dev/null +++ b/packages/manifests/darwin_amd64.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.3", + "url": "https://github.com/weby-homelab/antigravity-cli/releases/download/v1.0.3/cli_mac_x64.tar.gz", + "sha512": "5e5de4e2b5a9c788930699d0f5f7288eae4ae91fb705dc6e62f79d96a67619f58e2ba4357d6c2dbbfd5309aeb8f16934ebf09a573ef62b489b37e92c2754c74e" +} diff --git a/packages/manifests/darwin_arm64.json b/packages/manifests/darwin_arm64.json new file mode 100644 index 000000000..cb434ad5a --- /dev/null +++ b/packages/manifests/darwin_arm64.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.3", + "url": "https://github.com/weby-homelab/antigravity-cli/releases/download/v1.0.3/cli_mac_arm64.tar.gz", + "sha512": "b2c0d21876ae9b6faf6164b15b573d76e379f643e040e77d528ebafa5d5bd8893cc3c8ebd9a78faf67bd7c676dd4bff0f028f3bbb508e544769d61de3da6b8b1" +} diff --git a/packages/manifests/linux_amd64.json b/packages/manifests/linux_amd64.json new file mode 100644 index 000000000..852660bcd --- /dev/null +++ b/packages/manifests/linux_amd64.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.3", + "url": "https://github.com/weby-homelab/antigravity-cli/releases/download/v1.0.3/cli_linux_x64.tar.gz", + "sha512": "f6cf890d494f5fd00c696b4d2e541c894d5b10ff50bfd9f6dc02b915386e08b61c56140f17115898fc49f4aa4534581393098f35db70be9aae20dfde3ba5787c" +} diff --git a/packages/manifests/linux_arm64.json b/packages/manifests/linux_arm64.json new file mode 100644 index 000000000..f96da1c18 --- /dev/null +++ b/packages/manifests/linux_arm64.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.3", + "url": "https://github.com/weby-homelab/antigravity-cli/releases/download/v1.0.3/cli_linux_arm64.tar.gz", + "sha512": "0f6d326ef28e57e473c7825583314636fa0bcda50e27117ff3d019196d048d764af29846910a2f238dc87a3f90062e29b5252232c7d586a86abaaea921697e52" +} diff --git a/packages/manifests/windows_amd64.json b/packages/manifests/windows_amd64.json new file mode 100644 index 000000000..80f0f0ee9 --- /dev/null +++ b/packages/manifests/windows_amd64.json @@ -0,0 +1,5 @@ +{ + "version": "1.0.3", + "url": "https://github.com/weby-homelab/antigravity-cli/releases/download/v1.0.3/cli_windows_x64.zip", + "sha512": "2c3dbb4e443fd910b03e9b75e4f33616a535dff402534b4115fb4c10e8cd81edefcce7d3968d581552630f7ada473e8944a904701400543fbdd39296171722ad" +}