Skip to content

Commit f3aba62

Browse files
committed
feat: implement Ghost Skeptic adversarial validation system
0 parents  commit f3aba62

39 files changed

Lines changed: 4483 additions & 0 deletions

.ghostcoder/team.yml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
team:
2+
lead: backend-architect
3+
reviewers:
4+
- security-engineer
5+
- reality-checker
6+
specialists:
7+
auth: security-engineer
8+
perf: database-optimizer
9+
deploy: devops-automator
10+
11+
rules:
12+
- pattern: "src/auth/"
13+
agent: security-engineer
14+
block_merge_on: high_severity_finding
15+
16+
- pattern: "migrations/"
17+
agent: database-optimizer
18+
require: index_check
19+
20+
- pattern: ".github/workflows/"
21+
agent: devops-automator
22+
notify: "#deployments"

.github/workflows/ci.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: ["3.10", "3.11", "3.12"]
15+
16+
steps:
17+
- uses: actions/checkout@v3
18+
19+
- name: Set up Python ${{ matrix.python-version }}
20+
uses: actions/setup-python@v4
21+
with:
22+
python-version: ${{ matrix.python-version }}
23+
24+
- name: Install dependencies
25+
run: |
26+
python -m pip install --upgrade pip
27+
pip install -e .[dev]
28+
29+
- name: Run Pytest
30+
run: |
31+
pytest

.gitignore

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Python
2+
__pycache__/
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
.pytest_cache/
7+
.venv/
8+
venv/
9+
ENV/
10+
build/
11+
dist/
12+
*.egg-info/
13+
14+
# VS Code extension
15+
vscode/out/
16+
vscode/node_modules/
17+
18+
# Local/System
19+
.DS_Store
20+
Thumbs.db
21+
*.sock
22+
daemon.pid
23+
daemon.log
24+
audit.log
25+
sessions/

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2026 GhostCoder Contributors
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# GhostCoder 👻
2+
3+
An invisible coding partner that runs in the background, watches your terminal and IDE sessions, detects when you're stuck, and dispatches the right specialist agent to help without interrupting your flow.
4+
5+
## Core Concept
6+
GhostCoder does **NOT** have a sidebar or chat interface. It observes:
7+
- Terminal session (pre/post-commands, exit codes, stderr/stdout stack traces).
8+
- File system changes (modified files, contents, open buffers).
9+
10+
When it detects errors or complex tasks, it uses local LLMs to classify the situation and selects an appropriate specialist agent from your local Antigravity agency skills to show a concise, inline, dismissible hint.
11+
12+
## Architecture
13+
14+
```
15+
+-----------------------+
16+
| GhostCoder Daemon |
17+
| (Python) |
18+
+-----------+-----------+
19+
|
20+
+------------------------+------------------------+
21+
| | |
22+
[Unix Domain Socket] [Unix Domain Socket] [Unix Domain Socket]
23+
| | |
24+
v v v
25+
+--------------------+ +--------------------+ +--------------------+
26+
| Shell Plugin | | Neovim Plugin | | VS Code Extension |
27+
| (Bash/Zsh/Fish) | | (Lua) | | (TypeScript) |
28+
+--------------------+ +--------------------+ +--------------------+
29+
```
30+
31+
## Hardware Compatibility & LLMs
32+
GhostCoder is designed to run efficiently on low-end GPUs (e.g., **NVIDIA GTX 1650 4GB VRAM**):
33+
1. **Qwen2.5-0.5B (1GB VRAM)**: Kept loaded in memory for fast command and situation classification.
34+
2. **Qwen2.5-Coder-1.5B (2GB VRAM)**: Loaded on demand for code generation, and automatically unloaded after 30 seconds of idle time.
35+
3. **Fallback**: Auto-falls back to CPU inference (or Gemini API Free Tier) if GPU memory is constrained.
36+
37+
---
38+
39+
## Installation & Setup
40+
41+
1. **Prerequisites**:
42+
- Install [Ollama](https://ollama.ai/) and start the daemon.
43+
- Pull the required models:
44+
```bash
45+
ollama pull qwen2.5:0.5b
46+
ollama pull qwen2.5-coder:1.5b
47+
```
48+
49+
2. **Install package**:
50+
```bash
51+
pip install -e .
52+
```
53+
54+
3. **Initialize Plugins**:
55+
```bash
56+
ghostcoder init
57+
```
58+
This command detects your shell, registers shell hooks, and starts the daemon.
59+
60+
---
61+
62+
## CLI Usage
63+
64+
- `ghostcoder status` - Show loaded models, VRAM usage, and active integrations.
65+
- `ghostcoder logs` - Tail daemon logs.
66+
- `ghostcoder stop` - Stop the background daemon gracefully.

ghostcoder/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
# GhostCoder package
2+
__version__ = "0.1.0"

ghostcoder/agents.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import os
2+
import yaml
3+
import pathlib
4+
from typing import Dict, Any, Optional
5+
6+
class AgentLoader:
7+
def __init__(self):
8+
self.agents: Dict[str, Dict[str, Any]] = {}
9+
self.load_all_agents()
10+
11+
def load_all_agents(self):
12+
# We search in ~/.gemini/config/skills/ and ~/.gemini/antigravity/skills/
13+
search_dirs = [
14+
os.path.expanduser("~/.gemini/config/skills"),
15+
os.path.expanduser("~/.gemini/antigravity/skills")
16+
]
17+
18+
for base_dir in search_dirs:
19+
if not os.path.isdir(base_dir):
20+
continue
21+
22+
p = pathlib.Path(base_dir)
23+
for skill_dir in p.iterdir():
24+
if skill_dir.is_dir() and skill_dir.name.startswith("agency-"):
25+
skill_md_path = skill_dir / "SKILL.md"
26+
if skill_md_path.exists():
27+
self.parse_skill_file(skill_dir.name, skill_md_path)
28+
29+
def parse_skill_file(self, folder_name: str, file_path: pathlib.Path):
30+
try:
31+
with open(file_path, "r", encoding="utf-8") as f:
32+
content = f.read()
33+
34+
# Parse YAML frontmatter
35+
# Example format:
36+
# ---
37+
# name: agency-software-architect
38+
# description: ...
39+
# ---
40+
# Body...
41+
parts = content.split("---")
42+
if len(parts) >= 3:
43+
frontmatter_str = parts[1]
44+
body_str = "---".join(parts[2:])
45+
46+
try:
47+
metadata = yaml.safe_load(frontmatter_str)
48+
except Exception:
49+
metadata = {}
50+
51+
name = metadata.get("name", folder_name)
52+
description = metadata.get("description", "")
53+
54+
self.agents[name] = {
55+
"name": name,
56+
"description": description,
57+
"system_prompt": body_str.strip(),
58+
"folder_name": folder_name,
59+
"path": str(file_path)
60+
}
61+
except Exception as e:
62+
print(f"Error parsing skill file {file_path}: {e}")
63+
64+
def get_agent(self, name: str) -> Optional[Dict[str, Any]]:
65+
# Match by name or folder name (e.g. "agency-reality-checker")
66+
if name in self.agents:
67+
return self.agents[name]
68+
69+
# Try match by suffix
70+
for k, v in self.agents.items():
71+
if k.endswith(name) or v["folder_name"] == name:
72+
return v
73+
return None
74+
75+
def list_available_agents(self) -> Dict[str, str]:
76+
return {name: info["description"] for name, info in self.agents.items()}

0 commit comments

Comments
 (0)