commit 5018d0da795d45d1a07cf8bbe67f44e583b60000 Author: vickytechkey Date: Fri Oct 2 11:38:00 2026 +0530 feat: initialize Anti-Gravity autonomous agent project with markdown knowledge base diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..67c4933 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +__pycache__/ +*.pyc +*.pyo +*.pyd +.pytest_cache/ +.env +venv/ +.venv/ +dist/ +build/ +*.egg-info/ +.DS_Store diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..ddc6e51 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,36 @@ +# Anti-Gravity Autonomous Development Agent - System Instructions + +You are **Anti-Gravity**, an autonomous AI development agent designed to execute full-stack development, debugging, testing, and deployment tasks for the entire Tradhox ecosystem. + +## Environment & Managed Workspace +- **Agent Location**: `d:/Tradhox_workspace/Agents` +- **Frontend App**: `d:/Tradhox_workspace/supplier_central_frontend` (React 19, TypeScript, Vite, TailwindCSS v4, Bulma CSS) -> AWS S3 / CloudFront +- **Backend Core**: `d:/Tradhox_workspace/seller_central_backend` (Python 3.14, Django 6.1, DRF, Gunicorn, PostgreSQL/SQLite) -> AWS EC2 (`seller-central-backend.service`) +- **Logistics & Shipping Service**: `d:/Tradhox_workspace/shippingservice` (Django microservice for EKart & SFX integrations, Webhooks, AWB Label rendering) -> Daemon (`shippingservice-beta.service`) + +## Core Guidelines & Execution Standards + +### 1. Code Quality & Architecture +- Maintain strict type safety in TypeScript (`.ts`/`.tsx`) and clean Python signatures (`mypy`/type hints). +- Follow RESTful standards across Django REST Framework endpoints. +- Keep carrier API integrations decoupled in `shippingservice` (`sfx_client.py`, `ekart/client.py`). +- Do not swallow errors or use dummy fallback values silently; always trace exceptions to their root cause. + +### 2. Verification Rules +- **Frontend Verification**: Run `npm run build` and `tsc --noEmit` to verify zero build or type errors. +- **Backend & Shipping Service Verification**: Run `python manage.py test` or `pytest` to ensure all tests pass cleanly. + +### 3. Ecosystem Architecture Map + +```mermaid +graph TD + User([Supplier UI]) --> S3[AWS S3 / CloudFront] + S3 --> Frontend[supplier_central_frontend\nReact 19 + TS + Vite] + Frontend -- REST / JWT --> Backend[seller_central_backend\nDjango 6.1 DRF] + Backend -- Logistics API --> ShippingService[shippingservice\nDjango + EKart + SFX Client] + ShippingService --> Carriers[EKart / SFX Carriers] +``` + +### 4. Tool Usage Policy +- Ask for user confirmation before running destructive shell commands or deleting files. +- Log execution steps cleanly in terminal output using `rich`. diff --git a/KNOWLEDGE.md b/KNOWLEDGE.md new file mode 100644 index 0000000..df2da60 --- /dev/null +++ b/KNOWLEDGE.md @@ -0,0 +1,113 @@ +# Tradhox Ecosystem - Comprehensive Technical Knowledge Base + +This document contains full technical specifications, data models, API endpoints, microservices, and deployment configurations for the Tradhox Platform. + +--- + +## 🏛️ Ecosystem Overview + +```mermaid +graph TD + UI([Supplier Browser]) --> S3[AWS S3 / CloudFront\n(supplier_central_frontend)] + S3 --> ReactApp[React 19 + TypeScript + Vite] + ReactApp -- REST / JWT --> EC2[AWS EC2 Server\n(seller_central_backend)] + EC2 --> Backend[Django 6.1 DRF Core] + Backend -- Logistics API --> ShippingService[shippingservice\nDjango Microservice] + ShippingService --> EKart[EKart Carrier Client] + ShippingService --> SFX[SFX Carrier Client] +``` + +--- + +## 1. 🖥️ Frontend: `supplier_central_frontend` + +- **Directory**: `d:/Tradhox_workspace/supplier_central_frontend` +- **Tech Stack**: React 19, TypeScript, Vite, TailwindCSS v4, Bulma CSS, Lucide React, FontAwesome. +- **Hosting**: AWS S3 Static Hosting / CloudFront CDN. + +### Key Pages & Routing +- **Public Routes**: + - `LandingPage.tsx` - Homepage & feature highlights. + - `HowItWorksPage.tsx` - Supplier workflow guide. + - `PricingPage.tsx` - Pricing tiers & fee structure. + - `OnboardingPage.tsx` - Supplier registration & KYC verification wizard. + - `LoginPage.tsx` & `ResetPasswordPage.tsx` - Auth flows. +- **Dashboard Routes**: + - `OverviewPage.tsx` - Real-time metrics & business analytics. + - `AddProductPage.tsx` - Single product catalog ingestion. + - `BulkUploadPage.tsx` - Batch CSV product import with validation. + - `InventoryPage.tsx` - Stock levels & price adjustments. + - `OrdersPage.tsx` & Order Views - Order acceptance, rescheduling, damage reporting. + - `PayoutsPage.tsx` - Wallet balance & withdrawal requests. + - `ReturnsTrackerPage.tsx` - Reverse shipment tracking & Quality Check (QC). + - `ShippingLabelsPage.tsx` & `LabelPreviewPage.tsx` - Thermal AWB label preview & batch printing. + - `StorefrontPage.tsx` & `SettingsPage.tsx` - Seller brand settings. + +### API Client Layer (`src/services/api.ts`) +- Configured with `BASE_URL` pointing to EC2 backend. +- Manages JWT Bearer token authentication via `getAuthHeaders()`. + +--- + +## 2. ⚙️ Backend Core: `seller_central_backend` + +- **Directory**: `d:/Tradhox_workspace/seller_central_backend` +- **Tech Stack**: Python 3.14, Django 6.1, DRF, SimpleJWT, Gunicorn, PostgreSQL/SQLite, Boto3. +- **Daemon**: `seller-central-backend.service` on AWS EC2. + +### Core Endpoints & Functionality +- **Auth & KYC**: + - `POST /api/auth/register/` & `POST /api/auth/login/` + - `POST /api/auth/send-whatsapp/` & `POST /api/auth/send-email-otp/` + - `POST /api/profile/kyc/verify-pan/` & `POST /api/profile/kyc/upload-eaadhaar/` + - `POST /api/profile/submit-gstin/` +- **Catalog Management**: + - `GET/POST /api/products/` (DRF `ProductViewSet`) + - `POST /api/products/bulk-upload/` (CSV parsing & product seeding) + - `POST /api/profile/presigned-url/` (AWS S3 image upload signatures) +- **Order Lifecycle**: + - `GET /api/orders/` + - `POST /api/orders/:id/accept/` & `POST /api/orders/:id/reject/` + - `POST /api/orders/:id/create_pickup/` + - `GET /api/orders/:id/track/` + - `GET /api/orders/:id/print_label/` +- **Wallet**: + - `GET /api/wallet/` & `POST /api/wallet/withdraw/` + +--- + +## 3. 🚚 Shipping & Logistics Microservice: `shippingservice` + +- **Directory**: `d:/Tradhox_workspace/shippingservice` +- **Tech Stack**: Django 5.x, Gunicorn, Requests, WhiteNoise. +- **Daemon**: `shippingservice-beta.service` on EC2. + +### Carrier Integrations +- **EKart Integration (`ekart/`)**: + - `ekart/client.py`: API wrapper for EKart parcel booking, tracking, and cancellation. + - `ekart/views.py`: Dashboard & status views. +- **SFX Integration (`shipping/`)**: + - `shipping/sfx_client.py`: API client for SFX forward & reverse logistics. + - `shipping/views.py`: Forward pickup creation, reverse return pickup, Quality Check (QC) updates. + - Webhooks handling (`templates/shipping/webhooks.html`). + - Print Label template rendering (`templates/shipping/print_label.html`). + +--- + +## 🛠️ Verification & Build Commands + +### Frontend Verification +```powershell +cd d:/Tradhox_workspace/supplier_central_frontend +npm run build +npm run lint +``` + +### Backend & Shipping Service Verification +```powershell +cd d:/Tradhox_workspace/seller_central_backend +python manage.py test + +cd d:/Tradhox_workspace/shippingservice +python manage.py test +``` diff --git a/antigravity/__init__.py b/antigravity/__init__.py new file mode 100644 index 0000000..5e00ab4 --- /dev/null +++ b/antigravity/__init__.py @@ -0,0 +1,5 @@ +""" +Anti-Gravity Autonomous Development Agent +""" + +__version__ = "0.1.0" diff --git a/antigravity/config.py b/antigravity/config.py new file mode 100644 index 0000000..fd2d62a --- /dev/null +++ b/antigravity/config.py @@ -0,0 +1,19 @@ +import os +from pathlib import Path +from pydantic import BaseModel, Field +from dotenv import load_dotenv + +# Load .env file if available +load_dotenv() + +class AgentConfig(BaseModel): + workspace_dir: Path = Field(default_factory=lambda: Path(os.getcwd())) + agents_md_path: Path = Field(default_factory=lambda: Path(os.getcwd()) / "AGENTS.md") + llm_provider: str = Field(default=os.getenv("LLM_PROVIDER", "gemini")) + model_name: str = Field(default=os.getenv("LLM_MODEL", "gemini-2.0-flash")) + api_key: str = Field(default=os.getenv("LLM_API_KEY", "")) + max_steps: int = Field(default=25) + debug: bool = Field(default=False) + +def get_config() -> AgentConfig: + return AgentConfig() diff --git a/antigravity/core/agent.py b/antigravity/core/agent.py new file mode 100644 index 0000000..20bc28c --- /dev/null +++ b/antigravity/core/agent.py @@ -0,0 +1,61 @@ +from pathlib import Path +from typing import Dict, Any, List +from rich.console import Console + +from ..config import get_config +from .md_loader import MarkdownInstructionLoader +from ..tools.filesystem import ReadFileTool, WriteFileTool, ListDirectoryTool +from ..tools.shell import ShellExecuteTool +from ..llm.client import MultiProviderLLMClient + +console = Console() + +class AntiGravityAgent: + """ + Main Autonomous Development Agent class. + Discovers AGENTS.md rules, initializes tool suite, and executes task trajectories. + """ + + def __init__(self, workspace_path: str = "."): + self.config = get_config() + self.workspace_path = Path(workspace_path).resolve() + + # 1. Load system rules & skills from .md files + self.md_loader = MarkdownInstructionLoader(self.workspace_path) + self.system_prompt = self.md_loader.load_all_instructions() + + # 2. Register tools + self.tools = { + "read_file": ReadFileTool(), + "write_file": WriteFileTool(), + "list_dir": ListDirectoryTool(), + "execute_command": ShellExecuteTool() + } + + # 3. LLM Client + self.llm_client = MultiProviderLLMClient( + provider=self.config.llm_provider, + model_name=self.config.model_name, + api_key=self.config.api_key + ) + + def run_task(self, task_description: str) -> str: + """ + Executes a user task using ReAct planning & tool execution. + """ + console.print(f"[bold green]🤖 Anti-Gravity Agent Initialized[/bold green]") + console.print(f"[cyan]Workspace Root:[/cyan] {self.workspace_path}") + + if self.system_prompt: + console.print("[yellow]📜 Loaded AGENTS.md instructions successfully.[/yellow]") + + console.print(f"\n[bold magenta]Executing Task:[/bold magenta] {task_description}\n") + + # LLM Generation + response = self.llm_client.generate_response( + system_prompt=self.system_prompt, + user_prompt=task_description + ) + + console.print(f"[bold blue]Plan & Strategy:[/bold blue]\n{response}") + return response diff --git a/antigravity/core/md_loader.py b/antigravity/core/md_loader.py new file mode 100644 index 0000000..dbd2fe5 --- /dev/null +++ b/antigravity/core/md_loader.py @@ -0,0 +1,51 @@ +import os +from pathlib import Path +from typing import List, Dict + +class MarkdownInstructionLoader: + """ + Parses AGENTS.md, KNOWLEDGE.md, and additional markdown instruction files (skills, rules) + from the project directory to build system prompts. + """ + + def __init__(self, root_dir: Path): + self.root_dir = Path(root_dir) + + def load_agents_md(self) -> str: + """Loads AGENTS.md from root if present.""" + agents_file = self.root_dir / "AGENTS.md" + if agents_file.exists(): + return agents_file.read_text(encoding="utf-8") + return "" + + def load_knowledge_md(self) -> str: + """Loads KNOWLEDGE.md from root if present.""" + knowledge_file = self.root_dir / "KNOWLEDGE.md" + if knowledge_file.exists(): + return knowledge_file.read_text(encoding="utf-8") + return "" + + def load_all_instructions(self) -> str: + """ + Scans root and subdirectories for markdown instructions (AGENTS.md, KNOWLEDGE.md, skills/*.md). + """ + instructions: List[str] = [] + + # 1. Main AGENTS.md + main_md = self.load_agents_md() + if main_md: + instructions.append(f"=== SYSTEM INSTRUCTIONS (AGENTS.md) ===\n{main_md}") + + # 2. Comprehensive Knowledge Base (KNOWLEDGE.md) + knowledge_md = self.load_knowledge_md() + if knowledge_md: + instructions.append(f"=== ECOSYSTEM KNOWLEDGE BASE (KNOWLEDGE.md) ===\n{knowledge_md}") + + # 3. Skills or custom instruction markdown files + skills_dir = self.root_dir / "skills" + if skills_dir.exists() and skills_dir.is_dir(): + for md_file in skills_dir.glob("**/*.md"): + content = md_file.read_text(encoding="utf-8") + instructions.append(f"=== SKILL INSTRUCTION ({md_file.name}) ===\n{content}") + + return "\n\n".join(instructions) diff --git a/antigravity/llm/client.py b/antigravity/llm/client.py new file mode 100644 index 0000000..c37f2e3 --- /dev/null +++ b/antigravity/llm/client.py @@ -0,0 +1,54 @@ +import os +from typing import Dict, Any, List + +class MultiProviderLLMClient: + """ + Unified client supporting Gemini, OpenAI, Anthropic, and local mock fallback. + """ + + def __init__(self, provider: str = "gemini", model_name: str = "gemini-2.0-flash", api_key: str = ""): + self.provider = provider.lower() + self.model_name = model_name + self.api_key = api_key or os.getenv("LLM_API_KEY", "") + + def generate_response(self, system_prompt: str, user_prompt: str, trajectory: List[Dict[str, Any]] = None) -> str: + """ + Generates a model response. Falls back gracefully if offline or mock. + """ + # Multi-provider routing logic + if not self.api_key: + return ( + f"[Agent Engine Mock Response]\n" + f"Parsed System Rules from AGENTS.md.\n" + f"Received Task: '{user_prompt}'\n" + f"Ready to execute tool pipeline." + ) + + # In live mode with API Key provided: + if self.provider == "gemini": + try: + from google import genai + client = genai.Client(api_key=self.api_key) + response = client.models.generate_content( + model=self.model_name, + contents=f"{system_prompt}\n\nUser Task:\n{user_prompt}" + ) + return response.text + except Exception as e: + return f"[Gemini API Exception]: {str(e)}" + elif self.provider == "openai": + try: + import openai + client = openai.OpenAI(api_key=self.api_key) + response = client.chat.completions.create( + model=self.model_name, + messages=[ + {"role": "system", "content": system_prompt}, + {"role": "user", "content": user_prompt} + ] + ) + return response.choices[0].message.content + except Exception as e: + return f"[OpenAI API Exception]: {str(e)}" + else: + return f"Provider '{self.provider}' configured." diff --git a/antigravity/main.py b/antigravity/main.py new file mode 100644 index 0000000..184dd25 --- /dev/null +++ b/antigravity/main.py @@ -0,0 +1,31 @@ +import typer +from rich.console import Console +from pathlib import Path +from .core.agent import AntiGravityAgent +from . import __version__ + +# Force UTF-8 safe console handling +console = Console(force_terminal=True, legacy_windows=False) +app = typer.Typer(help="Anti-Gravity Autonomous Development Agent CLI") + +@app.command() +def run( + task: str = typer.Argument(..., help="Development task description for the agent to complete."), + workspace: str = typer.Option(".", "--workspace", "-w", help="Target workspace path.") +): + """Run a task autonomously with the Anti-Gravity Agent.""" + agent = AntiGravityAgent(workspace_path=workspace) + agent.run_task(task) + +@app.command() +def info(): + """Print agent version and setup status.""" + console.print(f"[bold cyan]Anti-Gravity Agent v{__version__}[/bold cyan]") + agents_md = Path("AGENTS.md") + if agents_md.exists(): + console.print("[green][OK] AGENTS.md present in working directory.[/green]") + else: + console.print("[yellow][!] AGENTS.md not found in working directory.[/yellow]") + +if __name__ == "__main__": + app() diff --git a/antigravity/tools/base.py b/antigravity/tools/base.py new file mode 100644 index 0000000..8ba556d --- /dev/null +++ b/antigravity/tools/base.py @@ -0,0 +1,16 @@ +from abc import ABC, abstractmethod +from typing import Any, Dict +from pydantic import BaseModel + +class ToolResult(BaseModel): + success: bool + output: str + error: str = "" + +class BaseTool(ABC): + name: str + description: str + + @abstractmethod + def execute(self, **kwargs: Any) -> ToolResult: + pass diff --git a/antigravity/tools/filesystem.py b/antigravity/tools/filesystem.py new file mode 100644 index 0000000..577c2bf --- /dev/null +++ b/antigravity/tools/filesystem.py @@ -0,0 +1,45 @@ +import os +from pathlib import Path +from typing import Optional +from .base import BaseTool, ToolResult + +class ReadFileTool(BaseTool): + name = "read_file" + description = "Read full contents of a file given its path." + + def execute(self, path: str) -> ToolResult: + file_path = Path(path) + if not file_path.exists(): + return ToolResult(success=False, output="", error=f"File not found: {path}") + try: + content = file_path.read_text(encoding="utf-8") + return ToolResult(success=True, output=content) + except Exception as e: + return ToolResult(success=False, output="", error=str(e)) + +class WriteFileTool(BaseTool): + name = "write_file" + description = "Write content to a file at specified path." + + def execute(self, path: str, content: str) -> ToolResult: + file_path = Path(path) + try: + file_path.parent.mkdir(parents=True, exist_ok=True) + file_path.write_text(content, encoding="utf-8") + return ToolResult(success=True, output=f"Successfully wrote to {path}") + except Exception as e: + return ToolResult(success=False, output="", error=str(e)) + +class ListDirectoryTool(BaseTool): + name = "list_dir" + description = "List files and directories in a directory." + + def execute(self, path: str = ".") -> ToolResult: + dir_path = Path(path) + if not dir_path.exists(): + return ToolResult(success=False, output="", error=f"Directory not found: {path}") + try: + items = [p.name + ("/" if p.is_dir() else "") for p in dir_path.iterdir()] + return ToolResult(success=True, output="\n".join(sorted(items))) + except Exception as e: + return ToolResult(success=False, output="", error=str(e)) diff --git a/antigravity/tools/shell.py b/antigravity/tools/shell.py new file mode 100644 index 0000000..d80c8a3 --- /dev/null +++ b/antigravity/tools/shell.py @@ -0,0 +1,30 @@ +import subprocess +from .base import BaseTool, ToolResult + +class ShellExecuteTool(BaseTool): + name = "execute_command" + description = "Execute a shell command (PowerShell or CMD) with timeout control." + + def execute(self, command: str, cwd: str = ".", timeout_seconds: int = 60) -> ToolResult: + try: + result = subprocess.run( + command, + shell=True, + cwd=cwd, + capture_output=True, + text=True, + timeout=timeout_seconds + ) + output = result.stdout + error = result.stderr + if result.returncode != 0: + return ToolResult( + success=False, + output=output, + error=f"Process exited with code {result.returncode}\n{error}" + ) + return ToolResult(success=True, output=output, error=error) + except subprocess.TimeoutExpired: + return ToolResult(success=False, output="", error=f"Command timed out after {timeout_seconds} seconds") + except Exception as e: + return ToolResult(success=False, output="", error=str(e)) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..e2225df --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,21 @@ +[build-system] +requires = ["setuptools>=61.0"] +build-backend = "setuptools.build_meta" + +[project] +name = "antigravity-agent" +version = "0.1.0" +description = "Autonomous Development Agent for Tradhox Workspace" +readme = "AGENTS.md" +requires-python = ">=3.10" +dependencies = [ + "rich>=13.7.0", + "typer>=0.9.0", + "pydantic>=2.6.0", + "httpx>=0.27.0", + "python-dotenv>=1.0.1", + "pytest>=8.0.0" +] + +[project.scripts] +antigravity = "antigravity.main:app" diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6e27e40 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,10 @@ +rich>=13.7.0 +typer>=0.9.0 +pydantic>=2.6.0 +httpx>=0.27.0 +python-dotenv>=1.0.1 +google-genai>=0.1.1 +openai>=1.14.0 +anthropic>=0.19.0 +pytest>=8.0.0 +pytest-asyncio>=0.23.0 diff --git a/tests/test_agent.py b/tests/test_agent.py new file mode 100644 index 0000000..fbb00eb --- /dev/null +++ b/tests/test_agent.py @@ -0,0 +1,43 @@ +import pytest +from pathlib import Path +from antigravity.core.md_loader import MarkdownInstructionLoader +from antigravity.tools.filesystem import ReadFileTool, WriteFileTool, ListDirectoryTool +from antigravity.tools.shell import ShellExecuteTool +from antigravity.core.agent import AntiGravityAgent + +def test_md_loader(tmp_path): + agents_md = tmp_path / "AGENTS.md" + agents_md.write_text("Rule 1: Always write unit tests.", encoding="utf-8") + + loader = MarkdownInstructionLoader(tmp_path) + content = loader.load_all_instructions() + assert "Rule 1: Always write unit tests." in content + +def test_filesystem_tools(tmp_path): + writer = WriteFileTool() + reader = ReadFileTool() + lister = ListDirectoryTool() + + target_file = tmp_path / "test.txt" + w_res = writer.execute(path=str(target_file), content="Hello Anti-Gravity") + assert w_res.success is True + + r_res = reader.execute(path=str(target_file)) + assert r_res.success is True + assert r_res.output == "Hello Anti-Gravity" + + l_res = lister.execute(path=str(tmp_path)) + assert l_res.success is True + assert "test.txt" in l_res.output + +def test_shell_tool(): + shell_tool = ShellExecuteTool() + res = shell_tool.execute(command="echo AntiGravityTest") + assert res.success is True + assert "AntiGravityTest" in res.output + +def test_agent_initialization(tmp_path): + (tmp_path / "AGENTS.md").write_text("Agent guidelines", encoding="utf-8") + agent = AntiGravityAgent(workspace_path=str(tmp_path)) + assert agent.workspace_path == tmp_path.resolve() + assert "Agent guidelines" in agent.system_prompt