autonomous-qa-agent 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- autonomous_qa_agent-1.0.0/PKG-INFO +272 -0
- autonomous_qa_agent-1.0.0/README.md +259 -0
- autonomous_qa_agent-1.0.0/autonomous_qa_agent.egg-info/PKG-INFO +272 -0
- autonomous_qa_agent-1.0.0/autonomous_qa_agent.egg-info/SOURCES.txt +9 -0
- autonomous_qa_agent-1.0.0/autonomous_qa_agent.egg-info/dependency_links.txt +1 -0
- autonomous_qa_agent-1.0.0/autonomous_qa_agent.egg-info/entry_points.txt +2 -0
- autonomous_qa_agent-1.0.0/autonomous_qa_agent.egg-info/requires.txt +4 -0
- autonomous_qa_agent-1.0.0/autonomous_qa_agent.egg-info/top_level.txt +1 -0
- autonomous_qa_agent-1.0.0/cli/main.py +423 -0
- autonomous_qa_agent-1.0.0/pyproject.toml +26 -0
- autonomous_qa_agent-1.0.0/setup.cfg +4 -0
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autonomous-qa-agent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Autonomous QA Agent — scan, test, and fix any codebase automatically
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: qa,testing,autonomous,ai,code-quality
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: click>=8.1
|
|
10
|
+
Requires-Dist: httpx>=0.28
|
|
11
|
+
Requires-Dist: websockets>=12.0
|
|
12
|
+
Requires-Dist: rich>=13.0
|
|
13
|
+
|
|
14
|
+
# QA Agent — Complete Client Guide
|
|
15
|
+
|
|
16
|
+
## What This Does
|
|
17
|
+
|
|
18
|
+
QA Agent autonomously scans your entire codebase, writes test cases for every function, runs them, finds bugs, explains them clearly, and fixes them automatically — then asks for your approval before committing anything.
|
|
19
|
+
|
|
20
|
+
**Supports:** Python, Kotlin, Java, Vert.x, JavaScript, TypeScript, Go, Rust, React, Vue, ML/AI models, and more.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Prerequisites (Client Machine)
|
|
25
|
+
|
|
26
|
+
| Requirement | Version | How to install |
|
|
27
|
+
|-------------|---------|----------------|
|
|
28
|
+
| Docker Desktop | 24+ | https://docker.com/products/docker-desktop |
|
|
29
|
+
| Python | 3.10+ | https://python.org |
|
|
30
|
+
| 16GB RAM | — | For running LLM models locally |
|
|
31
|
+
| 20GB disk | — | For Docker images + LLM models |
|
|
32
|
+
|
|
33
|
+
> **Privacy guarantee:** Your code NEVER leaves your machine. The LLM runs locally via Ollama.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Install (One Command)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Mac / Linux / Windows WSL:
|
|
41
|
+
curl -fsSL https://install.qa-agent.dev | bash
|
|
42
|
+
|
|
43
|
+
# Windows (PowerShell):
|
|
44
|
+
irm https://install.qa-agent.dev/windows | iex
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This automatically:
|
|
48
|
+
1. Checks Docker is running
|
|
49
|
+
2. Starts all 7 AI services in Docker
|
|
50
|
+
3. Downloads LLM models (qwen2.5-coder — private, runs locally)
|
|
51
|
+
4. Installs the `qa-agent` CLI
|
|
52
|
+
5. Runs a health check
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## First Scan
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Go to your project directory
|
|
60
|
+
cd /path/to/your/codebase
|
|
61
|
+
|
|
62
|
+
# Run full autonomous scan
|
|
63
|
+
qa-agent scan .
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**What you'll see:**
|
|
67
|
+
```
|
|
68
|
+
QA Agent — Autonomous scan
|
|
69
|
+
Repo: /path/to/your/codebase
|
|
70
|
+
Languages: all
|
|
71
|
+
Mode: exhaustive
|
|
72
|
+
|
|
73
|
+
[scanning] Found 1,247 source files
|
|
74
|
+
[indexing] Indexed 3,891 functions
|
|
75
|
+
[generating] Generated 3,891 test files (parallel, 4 workers)
|
|
76
|
+
[running] 2,847/3,891 tests passing
|
|
77
|
+
[healing] Self-healed 891 wrong assertion values
|
|
78
|
+
[fixing] Analysing 153 real failures...
|
|
79
|
+
|
|
80
|
+
┌─────────────────────────────────────────────┐
|
|
81
|
+
│ Bug Fix Proposal │
|
|
82
|
+
│ File: src/payment/calculator.kt │
|
|
83
|
+
│ Confidence: 94% │
|
|
84
|
+
│ │
|
|
85
|
+
│ Root cause: Tax rate multiplied instead │
|
|
86
|
+
│ of applied as percentage │
|
|
87
|
+
│ │
|
|
88
|
+
│ Diff: │
|
|
89
|
+
│ - return amount * taxRate │
|
|
90
|
+
│ + return amount * (taxRate / 100.0) │
|
|
91
|
+
└─────────────────────────────────────────────┘
|
|
92
|
+
Approve this patch? [y/N]: y
|
|
93
|
+
|
|
94
|
+
[PASS] Patch committed and verified.
|
|
95
|
+
|
|
96
|
+
╔══════════════════════════════════════════════╗
|
|
97
|
+
║ QA Agent — Scan Report ║
|
|
98
|
+
║ Status: PASS ║
|
|
99
|
+
║ Files scanned: 1,247 ║
|
|
100
|
+
║ Functions tested: 3,891 ║
|
|
101
|
+
║ Tests passed: 3,891 / 3,891 (100%) ║
|
|
102
|
+
║ Bugs found: 153 ║
|
|
103
|
+
║ Bugs fixed: 153 / 153 ║
|
|
104
|
+
║ Time: 14m 32s ║
|
|
105
|
+
║ YOUR CODEBASE IS GREEN ║
|
|
106
|
+
╚══════════════════════════════════════════════╝
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Common Commands
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Full scan (all files, all languages)
|
|
115
|
+
qa-agent scan .
|
|
116
|
+
|
|
117
|
+
# Only scan files changed since last commit (fast re-scan)
|
|
118
|
+
qa-agent scan . --changed-only
|
|
119
|
+
|
|
120
|
+
# Scan specific languages only
|
|
121
|
+
qa-agent scan . --lang python,kotlin,java
|
|
122
|
+
|
|
123
|
+
# CI/CD mode — auto-approve patches (no prompts)
|
|
124
|
+
qa-agent scan . --auto-approve
|
|
125
|
+
|
|
126
|
+
# Check system health
|
|
127
|
+
qa-agent doctor
|
|
128
|
+
|
|
129
|
+
# Check status of a running scan
|
|
130
|
+
qa-agent status <job-id>
|
|
131
|
+
|
|
132
|
+
# Approve/reject a pending patch
|
|
133
|
+
qa-agent approve <job-id> <patch-id>
|
|
134
|
+
qa-agent approve <job-id> <patch-id> --reject
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## CI/CD Integration
|
|
140
|
+
|
|
141
|
+
### GitHub Actions
|
|
142
|
+
```yaml
|
|
143
|
+
name: QA Agent Scan
|
|
144
|
+
on: [push, pull_request]
|
|
145
|
+
jobs:
|
|
146
|
+
qa-scan:
|
|
147
|
+
runs-on: self-hosted # needs Docker + qa-agent installed
|
|
148
|
+
steps:
|
|
149
|
+
- uses: actions/checkout@v4
|
|
150
|
+
- name: Run QA Agent
|
|
151
|
+
run: qa-agent scan . --auto-approve --output json > qa-report.json
|
|
152
|
+
- name: Upload Report
|
|
153
|
+
uses: actions/upload-artifact@v4
|
|
154
|
+
with:
|
|
155
|
+
name: qa-report
|
|
156
|
+
path: qa-report.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### GitLab CI
|
|
160
|
+
```yaml
|
|
161
|
+
qa-scan:
|
|
162
|
+
stage: test
|
|
163
|
+
tags: [docker]
|
|
164
|
+
script:
|
|
165
|
+
- qa-agent scan . --auto-approve
|
|
166
|
+
artifacts:
|
|
167
|
+
paths: [qa-report.json]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
Create `.qa-agent.env` in your repo root:
|
|
175
|
+
|
|
176
|
+
```env
|
|
177
|
+
# LLM Provider: ollama (local) | openai | anthropic
|
|
178
|
+
LLM_PROVIDER=ollama
|
|
179
|
+
|
|
180
|
+
# For cloud LLMs (faster, requires API key)
|
|
181
|
+
# LLM_PROVIDER=openai
|
|
182
|
+
# OPENAI_API_KEY=sk-...
|
|
183
|
+
|
|
184
|
+
# Models
|
|
185
|
+
TEST_GEN_MODEL=qwen2.5-coder:1.5b # fast, for test generation
|
|
186
|
+
PATCH_MODEL=qwen2.5-coder:7b # accurate, for bug fixing
|
|
187
|
+
|
|
188
|
+
# Scan behaviour
|
|
189
|
+
MAX_CHUNKS=1000 # max functions to generate tests for
|
|
190
|
+
MAX_FIX_ATTEMPTS=3 # LLM retry limit per bug
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Supported Languages
|
|
196
|
+
|
|
197
|
+
| Language | Test Framework | Status |
|
|
198
|
+
|----------|---------------|--------|
|
|
199
|
+
| Python | pytest | ✅ |
|
|
200
|
+
| Kotlin | JUnit 5 + Maven | ✅ |
|
|
201
|
+
| Java | JUnit 5 + Maven | ✅ |
|
|
202
|
+
| Vert.x (Java/Kotlin) | JUnit 5 | ✅ |
|
|
203
|
+
| JavaScript | Jest | ✅ |
|
|
204
|
+
| TypeScript | Jest | ✅ |
|
|
205
|
+
| Go | go test | ✅ |
|
|
206
|
+
| Rust | cargo test | ✅ |
|
|
207
|
+
| React/Vue/Svelte | Jest + Testing Library | ✅ |
|
|
208
|
+
| ML (PyTorch/TF) | pytest | ✅ |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## How It Works (Under the Hood)
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
Your codebase
|
|
216
|
+
│
|
|
217
|
+
▼
|
|
218
|
+
qa-agent scan .
|
|
219
|
+
│
|
|
220
|
+
[Brain — LangGraph AI]
|
|
221
|
+
│
|
|
222
|
+
├── Scan: finds all source files (git-aware, skips unchanged)
|
|
223
|
+
├── Index: tree-sitter AST → function-level chunks → Qdrant
|
|
224
|
+
├── Generate: LLM writes positive + negative + edge-case tests
|
|
225
|
+
├── Run: executes tests in isolated Docker sandbox
|
|
226
|
+
├── Heal: fixes wrong assertion values automatically
|
|
227
|
+
├── Fix: LLM analyses real bugs → proposes patches → YOU approve
|
|
228
|
+
└── Report: full summary with pass rate, bugs found/fixed
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Key guarantees:**
|
|
232
|
+
- Code stays on your machine (Ollama runs locally)
|
|
233
|
+
- Nothing is committed without your approval
|
|
234
|
+
- Re-scans only process changed files (git diff)
|
|
235
|
+
- Known bug patterns are cached (instant fix second time)
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Uninstall
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Stop and remove all containers + data
|
|
243
|
+
docker compose -f ~/.qa-agent/docker-compose.yml down -v
|
|
244
|
+
|
|
245
|
+
# Remove CLI
|
|
246
|
+
pip uninstall qa-agent
|
|
247
|
+
|
|
248
|
+
# Remove models (optional, frees ~8GB)
|
|
249
|
+
docker exec qa-ollama ollama rm qwen2.5-coder:7b
|
|
250
|
+
docker exec qa-ollama ollama rm qwen2.5-coder:1.5b
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Troubleshooting
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Check all services are running
|
|
259
|
+
qa-agent doctor
|
|
260
|
+
|
|
261
|
+
# View logs
|
|
262
|
+
docker compose logs qa-brain --tail=50
|
|
263
|
+
docker compose logs qa-celery --tail=50
|
|
264
|
+
|
|
265
|
+
# Restart services
|
|
266
|
+
docker compose restart
|
|
267
|
+
|
|
268
|
+
# Common issues:
|
|
269
|
+
# "Ollama not responding" → docker restart qa-ollama
|
|
270
|
+
# "Tests not generating" → check OLLAMA_HOST in qa-app logs
|
|
271
|
+
# "Scan hangs" → qa-agent status <job-id> to check phase
|
|
272
|
+
```
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# QA Agent — Complete Client Guide
|
|
2
|
+
|
|
3
|
+
## What This Does
|
|
4
|
+
|
|
5
|
+
QA Agent autonomously scans your entire codebase, writes test cases for every function, runs them, finds bugs, explains them clearly, and fixes them automatically — then asks for your approval before committing anything.
|
|
6
|
+
|
|
7
|
+
**Supports:** Python, Kotlin, Java, Vert.x, JavaScript, TypeScript, Go, Rust, React, Vue, ML/AI models, and more.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Prerequisites (Client Machine)
|
|
12
|
+
|
|
13
|
+
| Requirement | Version | How to install |
|
|
14
|
+
|-------------|---------|----------------|
|
|
15
|
+
| Docker Desktop | 24+ | https://docker.com/products/docker-desktop |
|
|
16
|
+
| Python | 3.10+ | https://python.org |
|
|
17
|
+
| 16GB RAM | — | For running LLM models locally |
|
|
18
|
+
| 20GB disk | — | For Docker images + LLM models |
|
|
19
|
+
|
|
20
|
+
> **Privacy guarantee:** Your code NEVER leaves your machine. The LLM runs locally via Ollama.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Install (One Command)
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Mac / Linux / Windows WSL:
|
|
28
|
+
curl -fsSL https://install.qa-agent.dev | bash
|
|
29
|
+
|
|
30
|
+
# Windows (PowerShell):
|
|
31
|
+
irm https://install.qa-agent.dev/windows | iex
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
This automatically:
|
|
35
|
+
1. Checks Docker is running
|
|
36
|
+
2. Starts all 7 AI services in Docker
|
|
37
|
+
3. Downloads LLM models (qwen2.5-coder — private, runs locally)
|
|
38
|
+
4. Installs the `qa-agent` CLI
|
|
39
|
+
5. Runs a health check
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## First Scan
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
# Go to your project directory
|
|
47
|
+
cd /path/to/your/codebase
|
|
48
|
+
|
|
49
|
+
# Run full autonomous scan
|
|
50
|
+
qa-agent scan .
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**What you'll see:**
|
|
54
|
+
```
|
|
55
|
+
QA Agent — Autonomous scan
|
|
56
|
+
Repo: /path/to/your/codebase
|
|
57
|
+
Languages: all
|
|
58
|
+
Mode: exhaustive
|
|
59
|
+
|
|
60
|
+
[scanning] Found 1,247 source files
|
|
61
|
+
[indexing] Indexed 3,891 functions
|
|
62
|
+
[generating] Generated 3,891 test files (parallel, 4 workers)
|
|
63
|
+
[running] 2,847/3,891 tests passing
|
|
64
|
+
[healing] Self-healed 891 wrong assertion values
|
|
65
|
+
[fixing] Analysing 153 real failures...
|
|
66
|
+
|
|
67
|
+
┌─────────────────────────────────────────────┐
|
|
68
|
+
│ Bug Fix Proposal │
|
|
69
|
+
│ File: src/payment/calculator.kt │
|
|
70
|
+
│ Confidence: 94% │
|
|
71
|
+
│ │
|
|
72
|
+
│ Root cause: Tax rate multiplied instead │
|
|
73
|
+
│ of applied as percentage │
|
|
74
|
+
│ │
|
|
75
|
+
│ Diff: │
|
|
76
|
+
│ - return amount * taxRate │
|
|
77
|
+
│ + return amount * (taxRate / 100.0) │
|
|
78
|
+
└─────────────────────────────────────────────┘
|
|
79
|
+
Approve this patch? [y/N]: y
|
|
80
|
+
|
|
81
|
+
[PASS] Patch committed and verified.
|
|
82
|
+
|
|
83
|
+
╔══════════════════════════════════════════════╗
|
|
84
|
+
║ QA Agent — Scan Report ║
|
|
85
|
+
║ Status: PASS ║
|
|
86
|
+
║ Files scanned: 1,247 ║
|
|
87
|
+
║ Functions tested: 3,891 ║
|
|
88
|
+
║ Tests passed: 3,891 / 3,891 (100%) ║
|
|
89
|
+
║ Bugs found: 153 ║
|
|
90
|
+
║ Bugs fixed: 153 / 153 ║
|
|
91
|
+
║ Time: 14m 32s ║
|
|
92
|
+
║ YOUR CODEBASE IS GREEN ║
|
|
93
|
+
╚══════════════════════════════════════════════╝
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Common Commands
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
# Full scan (all files, all languages)
|
|
102
|
+
qa-agent scan .
|
|
103
|
+
|
|
104
|
+
# Only scan files changed since last commit (fast re-scan)
|
|
105
|
+
qa-agent scan . --changed-only
|
|
106
|
+
|
|
107
|
+
# Scan specific languages only
|
|
108
|
+
qa-agent scan . --lang python,kotlin,java
|
|
109
|
+
|
|
110
|
+
# CI/CD mode — auto-approve patches (no prompts)
|
|
111
|
+
qa-agent scan . --auto-approve
|
|
112
|
+
|
|
113
|
+
# Check system health
|
|
114
|
+
qa-agent doctor
|
|
115
|
+
|
|
116
|
+
# Check status of a running scan
|
|
117
|
+
qa-agent status <job-id>
|
|
118
|
+
|
|
119
|
+
# Approve/reject a pending patch
|
|
120
|
+
qa-agent approve <job-id> <patch-id>
|
|
121
|
+
qa-agent approve <job-id> <patch-id> --reject
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## CI/CD Integration
|
|
127
|
+
|
|
128
|
+
### GitHub Actions
|
|
129
|
+
```yaml
|
|
130
|
+
name: QA Agent Scan
|
|
131
|
+
on: [push, pull_request]
|
|
132
|
+
jobs:
|
|
133
|
+
qa-scan:
|
|
134
|
+
runs-on: self-hosted # needs Docker + qa-agent installed
|
|
135
|
+
steps:
|
|
136
|
+
- uses: actions/checkout@v4
|
|
137
|
+
- name: Run QA Agent
|
|
138
|
+
run: qa-agent scan . --auto-approve --output json > qa-report.json
|
|
139
|
+
- name: Upload Report
|
|
140
|
+
uses: actions/upload-artifact@v4
|
|
141
|
+
with:
|
|
142
|
+
name: qa-report
|
|
143
|
+
path: qa-report.json
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### GitLab CI
|
|
147
|
+
```yaml
|
|
148
|
+
qa-scan:
|
|
149
|
+
stage: test
|
|
150
|
+
tags: [docker]
|
|
151
|
+
script:
|
|
152
|
+
- qa-agent scan . --auto-approve
|
|
153
|
+
artifacts:
|
|
154
|
+
paths: [qa-report.json]
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## Configuration
|
|
160
|
+
|
|
161
|
+
Create `.qa-agent.env` in your repo root:
|
|
162
|
+
|
|
163
|
+
```env
|
|
164
|
+
# LLM Provider: ollama (local) | openai | anthropic
|
|
165
|
+
LLM_PROVIDER=ollama
|
|
166
|
+
|
|
167
|
+
# For cloud LLMs (faster, requires API key)
|
|
168
|
+
# LLM_PROVIDER=openai
|
|
169
|
+
# OPENAI_API_KEY=sk-...
|
|
170
|
+
|
|
171
|
+
# Models
|
|
172
|
+
TEST_GEN_MODEL=qwen2.5-coder:1.5b # fast, for test generation
|
|
173
|
+
PATCH_MODEL=qwen2.5-coder:7b # accurate, for bug fixing
|
|
174
|
+
|
|
175
|
+
# Scan behaviour
|
|
176
|
+
MAX_CHUNKS=1000 # max functions to generate tests for
|
|
177
|
+
MAX_FIX_ATTEMPTS=3 # LLM retry limit per bug
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Supported Languages
|
|
183
|
+
|
|
184
|
+
| Language | Test Framework | Status |
|
|
185
|
+
|----------|---------------|--------|
|
|
186
|
+
| Python | pytest | ✅ |
|
|
187
|
+
| Kotlin | JUnit 5 + Maven | ✅ |
|
|
188
|
+
| Java | JUnit 5 + Maven | ✅ |
|
|
189
|
+
| Vert.x (Java/Kotlin) | JUnit 5 | ✅ |
|
|
190
|
+
| JavaScript | Jest | ✅ |
|
|
191
|
+
| TypeScript | Jest | ✅ |
|
|
192
|
+
| Go | go test | ✅ |
|
|
193
|
+
| Rust | cargo test | ✅ |
|
|
194
|
+
| React/Vue/Svelte | Jest + Testing Library | ✅ |
|
|
195
|
+
| ML (PyTorch/TF) | pytest | ✅ |
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## How It Works (Under the Hood)
|
|
200
|
+
|
|
201
|
+
```
|
|
202
|
+
Your codebase
|
|
203
|
+
│
|
|
204
|
+
▼
|
|
205
|
+
qa-agent scan .
|
|
206
|
+
│
|
|
207
|
+
[Brain — LangGraph AI]
|
|
208
|
+
│
|
|
209
|
+
├── Scan: finds all source files (git-aware, skips unchanged)
|
|
210
|
+
├── Index: tree-sitter AST → function-level chunks → Qdrant
|
|
211
|
+
├── Generate: LLM writes positive + negative + edge-case tests
|
|
212
|
+
├── Run: executes tests in isolated Docker sandbox
|
|
213
|
+
├── Heal: fixes wrong assertion values automatically
|
|
214
|
+
├── Fix: LLM analyses real bugs → proposes patches → YOU approve
|
|
215
|
+
└── Report: full summary with pass rate, bugs found/fixed
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Key guarantees:**
|
|
219
|
+
- Code stays on your machine (Ollama runs locally)
|
|
220
|
+
- Nothing is committed without your approval
|
|
221
|
+
- Re-scans only process changed files (git diff)
|
|
222
|
+
- Known bug patterns are cached (instant fix second time)
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Uninstall
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
# Stop and remove all containers + data
|
|
230
|
+
docker compose -f ~/.qa-agent/docker-compose.yml down -v
|
|
231
|
+
|
|
232
|
+
# Remove CLI
|
|
233
|
+
pip uninstall qa-agent
|
|
234
|
+
|
|
235
|
+
# Remove models (optional, frees ~8GB)
|
|
236
|
+
docker exec qa-ollama ollama rm qwen2.5-coder:7b
|
|
237
|
+
docker exec qa-ollama ollama rm qwen2.5-coder:1.5b
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Troubleshooting
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
# Check all services are running
|
|
246
|
+
qa-agent doctor
|
|
247
|
+
|
|
248
|
+
# View logs
|
|
249
|
+
docker compose logs qa-brain --tail=50
|
|
250
|
+
docker compose logs qa-celery --tail=50
|
|
251
|
+
|
|
252
|
+
# Restart services
|
|
253
|
+
docker compose restart
|
|
254
|
+
|
|
255
|
+
# Common issues:
|
|
256
|
+
# "Ollama not responding" → docker restart qa-ollama
|
|
257
|
+
# "Tests not generating" → check OLLAMA_HOST in qa-app logs
|
|
258
|
+
# "Scan hangs" → qa-agent status <job-id> to check phase
|
|
259
|
+
```
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: autonomous-qa-agent
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Autonomous QA Agent — scan, test, and fix any codebase automatically
|
|
5
|
+
License: MIT
|
|
6
|
+
Keywords: qa,testing,autonomous,ai,code-quality
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: click>=8.1
|
|
10
|
+
Requires-Dist: httpx>=0.28
|
|
11
|
+
Requires-Dist: websockets>=12.0
|
|
12
|
+
Requires-Dist: rich>=13.0
|
|
13
|
+
|
|
14
|
+
# QA Agent — Complete Client Guide
|
|
15
|
+
|
|
16
|
+
## What This Does
|
|
17
|
+
|
|
18
|
+
QA Agent autonomously scans your entire codebase, writes test cases for every function, runs them, finds bugs, explains them clearly, and fixes them automatically — then asks for your approval before committing anything.
|
|
19
|
+
|
|
20
|
+
**Supports:** Python, Kotlin, Java, Vert.x, JavaScript, TypeScript, Go, Rust, React, Vue, ML/AI models, and more.
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Prerequisites (Client Machine)
|
|
25
|
+
|
|
26
|
+
| Requirement | Version | How to install |
|
|
27
|
+
|-------------|---------|----------------|
|
|
28
|
+
| Docker Desktop | 24+ | https://docker.com/products/docker-desktop |
|
|
29
|
+
| Python | 3.10+ | https://python.org |
|
|
30
|
+
| 16GB RAM | — | For running LLM models locally |
|
|
31
|
+
| 20GB disk | — | For Docker images + LLM models |
|
|
32
|
+
|
|
33
|
+
> **Privacy guarantee:** Your code NEVER leaves your machine. The LLM runs locally via Ollama.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Install (One Command)
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
# Mac / Linux / Windows WSL:
|
|
41
|
+
curl -fsSL https://install.qa-agent.dev | bash
|
|
42
|
+
|
|
43
|
+
# Windows (PowerShell):
|
|
44
|
+
irm https://install.qa-agent.dev/windows | iex
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
This automatically:
|
|
48
|
+
1. Checks Docker is running
|
|
49
|
+
2. Starts all 7 AI services in Docker
|
|
50
|
+
3. Downloads LLM models (qwen2.5-coder — private, runs locally)
|
|
51
|
+
4. Installs the `qa-agent` CLI
|
|
52
|
+
5. Runs a health check
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## First Scan
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Go to your project directory
|
|
60
|
+
cd /path/to/your/codebase
|
|
61
|
+
|
|
62
|
+
# Run full autonomous scan
|
|
63
|
+
qa-agent scan .
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
**What you'll see:**
|
|
67
|
+
```
|
|
68
|
+
QA Agent — Autonomous scan
|
|
69
|
+
Repo: /path/to/your/codebase
|
|
70
|
+
Languages: all
|
|
71
|
+
Mode: exhaustive
|
|
72
|
+
|
|
73
|
+
[scanning] Found 1,247 source files
|
|
74
|
+
[indexing] Indexed 3,891 functions
|
|
75
|
+
[generating] Generated 3,891 test files (parallel, 4 workers)
|
|
76
|
+
[running] 2,847/3,891 tests passing
|
|
77
|
+
[healing] Self-healed 891 wrong assertion values
|
|
78
|
+
[fixing] Analysing 153 real failures...
|
|
79
|
+
|
|
80
|
+
┌─────────────────────────────────────────────┐
|
|
81
|
+
│ Bug Fix Proposal │
|
|
82
|
+
│ File: src/payment/calculator.kt │
|
|
83
|
+
│ Confidence: 94% │
|
|
84
|
+
│ │
|
|
85
|
+
│ Root cause: Tax rate multiplied instead │
|
|
86
|
+
│ of applied as percentage │
|
|
87
|
+
│ │
|
|
88
|
+
│ Diff: │
|
|
89
|
+
│ - return amount * taxRate │
|
|
90
|
+
│ + return amount * (taxRate / 100.0) │
|
|
91
|
+
└─────────────────────────────────────────────┘
|
|
92
|
+
Approve this patch? [y/N]: y
|
|
93
|
+
|
|
94
|
+
[PASS] Patch committed and verified.
|
|
95
|
+
|
|
96
|
+
╔══════════════════════════════════════════════╗
|
|
97
|
+
║ QA Agent — Scan Report ║
|
|
98
|
+
║ Status: PASS ║
|
|
99
|
+
║ Files scanned: 1,247 ║
|
|
100
|
+
║ Functions tested: 3,891 ║
|
|
101
|
+
║ Tests passed: 3,891 / 3,891 (100%) ║
|
|
102
|
+
║ Bugs found: 153 ║
|
|
103
|
+
║ Bugs fixed: 153 / 153 ║
|
|
104
|
+
║ Time: 14m 32s ║
|
|
105
|
+
║ YOUR CODEBASE IS GREEN ║
|
|
106
|
+
╚══════════════════════════════════════════════╝
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Common Commands
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Full scan (all files, all languages)
|
|
115
|
+
qa-agent scan .
|
|
116
|
+
|
|
117
|
+
# Only scan files changed since last commit (fast re-scan)
|
|
118
|
+
qa-agent scan . --changed-only
|
|
119
|
+
|
|
120
|
+
# Scan specific languages only
|
|
121
|
+
qa-agent scan . --lang python,kotlin,java
|
|
122
|
+
|
|
123
|
+
# CI/CD mode — auto-approve patches (no prompts)
|
|
124
|
+
qa-agent scan . --auto-approve
|
|
125
|
+
|
|
126
|
+
# Check system health
|
|
127
|
+
qa-agent doctor
|
|
128
|
+
|
|
129
|
+
# Check status of a running scan
|
|
130
|
+
qa-agent status <job-id>
|
|
131
|
+
|
|
132
|
+
# Approve/reject a pending patch
|
|
133
|
+
qa-agent approve <job-id> <patch-id>
|
|
134
|
+
qa-agent approve <job-id> <patch-id> --reject
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## CI/CD Integration
|
|
140
|
+
|
|
141
|
+
### GitHub Actions
|
|
142
|
+
```yaml
|
|
143
|
+
name: QA Agent Scan
|
|
144
|
+
on: [push, pull_request]
|
|
145
|
+
jobs:
|
|
146
|
+
qa-scan:
|
|
147
|
+
runs-on: self-hosted # needs Docker + qa-agent installed
|
|
148
|
+
steps:
|
|
149
|
+
- uses: actions/checkout@v4
|
|
150
|
+
- name: Run QA Agent
|
|
151
|
+
run: qa-agent scan . --auto-approve --output json > qa-report.json
|
|
152
|
+
- name: Upload Report
|
|
153
|
+
uses: actions/upload-artifact@v4
|
|
154
|
+
with:
|
|
155
|
+
name: qa-report
|
|
156
|
+
path: qa-report.json
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### GitLab CI
|
|
160
|
+
```yaml
|
|
161
|
+
qa-scan:
|
|
162
|
+
stage: test
|
|
163
|
+
tags: [docker]
|
|
164
|
+
script:
|
|
165
|
+
- qa-agent scan . --auto-approve
|
|
166
|
+
artifacts:
|
|
167
|
+
paths: [qa-report.json]
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
Create `.qa-agent.env` in your repo root:
|
|
175
|
+
|
|
176
|
+
```env
|
|
177
|
+
# LLM Provider: ollama (local) | openai | anthropic
|
|
178
|
+
LLM_PROVIDER=ollama
|
|
179
|
+
|
|
180
|
+
# For cloud LLMs (faster, requires API key)
|
|
181
|
+
# LLM_PROVIDER=openai
|
|
182
|
+
# OPENAI_API_KEY=sk-...
|
|
183
|
+
|
|
184
|
+
# Models
|
|
185
|
+
TEST_GEN_MODEL=qwen2.5-coder:1.5b # fast, for test generation
|
|
186
|
+
PATCH_MODEL=qwen2.5-coder:7b # accurate, for bug fixing
|
|
187
|
+
|
|
188
|
+
# Scan behaviour
|
|
189
|
+
MAX_CHUNKS=1000 # max functions to generate tests for
|
|
190
|
+
MAX_FIX_ATTEMPTS=3 # LLM retry limit per bug
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
## Supported Languages
|
|
196
|
+
|
|
197
|
+
| Language | Test Framework | Status |
|
|
198
|
+
|----------|---------------|--------|
|
|
199
|
+
| Python | pytest | ✅ |
|
|
200
|
+
| Kotlin | JUnit 5 + Maven | ✅ |
|
|
201
|
+
| Java | JUnit 5 + Maven | ✅ |
|
|
202
|
+
| Vert.x (Java/Kotlin) | JUnit 5 | ✅ |
|
|
203
|
+
| JavaScript | Jest | ✅ |
|
|
204
|
+
| TypeScript | Jest | ✅ |
|
|
205
|
+
| Go | go test | ✅ |
|
|
206
|
+
| Rust | cargo test | ✅ |
|
|
207
|
+
| React/Vue/Svelte | Jest + Testing Library | ✅ |
|
|
208
|
+
| ML (PyTorch/TF) | pytest | ✅ |
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
## How It Works (Under the Hood)
|
|
213
|
+
|
|
214
|
+
```
|
|
215
|
+
Your codebase
|
|
216
|
+
│
|
|
217
|
+
▼
|
|
218
|
+
qa-agent scan .
|
|
219
|
+
│
|
|
220
|
+
[Brain — LangGraph AI]
|
|
221
|
+
│
|
|
222
|
+
├── Scan: finds all source files (git-aware, skips unchanged)
|
|
223
|
+
├── Index: tree-sitter AST → function-level chunks → Qdrant
|
|
224
|
+
├── Generate: LLM writes positive + negative + edge-case tests
|
|
225
|
+
├── Run: executes tests in isolated Docker sandbox
|
|
226
|
+
├── Heal: fixes wrong assertion values automatically
|
|
227
|
+
├── Fix: LLM analyses real bugs → proposes patches → YOU approve
|
|
228
|
+
└── Report: full summary with pass rate, bugs found/fixed
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
**Key guarantees:**
|
|
232
|
+
- Code stays on your machine (Ollama runs locally)
|
|
233
|
+
- Nothing is committed without your approval
|
|
234
|
+
- Re-scans only process changed files (git diff)
|
|
235
|
+
- Known bug patterns are cached (instant fix second time)
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Uninstall
|
|
240
|
+
|
|
241
|
+
```bash
|
|
242
|
+
# Stop and remove all containers + data
|
|
243
|
+
docker compose -f ~/.qa-agent/docker-compose.yml down -v
|
|
244
|
+
|
|
245
|
+
# Remove CLI
|
|
246
|
+
pip uninstall qa-agent
|
|
247
|
+
|
|
248
|
+
# Remove models (optional, frees ~8GB)
|
|
249
|
+
docker exec qa-ollama ollama rm qwen2.5-coder:7b
|
|
250
|
+
docker exec qa-ollama ollama rm qwen2.5-coder:1.5b
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Troubleshooting
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
# Check all services are running
|
|
259
|
+
qa-agent doctor
|
|
260
|
+
|
|
261
|
+
# View logs
|
|
262
|
+
docker compose logs qa-brain --tail=50
|
|
263
|
+
docker compose logs qa-celery --tail=50
|
|
264
|
+
|
|
265
|
+
# Restart services
|
|
266
|
+
docker compose restart
|
|
267
|
+
|
|
268
|
+
# Common issues:
|
|
269
|
+
# "Ollama not responding" → docker restart qa-ollama
|
|
270
|
+
# "Tests not generating" → check OLLAMA_HOST in qa-app logs
|
|
271
|
+
# "Scan hangs" → qa-agent status <job-id> to check phase
|
|
272
|
+
```
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
autonomous_qa_agent.egg-info/PKG-INFO
|
|
4
|
+
autonomous_qa_agent.egg-info/SOURCES.txt
|
|
5
|
+
autonomous_qa_agent.egg-info/dependency_links.txt
|
|
6
|
+
autonomous_qa_agent.egg-info/entry_points.txt
|
|
7
|
+
autonomous_qa_agent.egg-info/requires.txt
|
|
8
|
+
autonomous_qa_agent.egg-info/top_level.txt
|
|
9
|
+
cli/main.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
cli
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
qa-agent CLI — Autonomous QA Agent
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import asyncio
|
|
9
|
+
import json
|
|
10
|
+
import os
|
|
11
|
+
import sys
|
|
12
|
+
import time
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
from typing import Optional, List
|
|
15
|
+
|
|
16
|
+
# Force UTF-8 on Windows to handle rich output
|
|
17
|
+
if sys.platform == "win32":
|
|
18
|
+
import io
|
|
19
|
+
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
|
20
|
+
sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
|
|
21
|
+
os.environ["PYTHONUTF8"] = "1"
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
import httpx
|
|
25
|
+
import websockets
|
|
26
|
+
from rich.console import Console
|
|
27
|
+
from rich.table import Table
|
|
28
|
+
from rich.progress import Progress, SpinnerColumn, TextColumn, BarColumn, TimeElapsedColumn
|
|
29
|
+
from rich.panel import Panel
|
|
30
|
+
from rich.text import Text
|
|
31
|
+
from rich import box
|
|
32
|
+
from rich.prompt import Confirm
|
|
33
|
+
|
|
34
|
+
console = Console(force_terminal=True, highlight=False)
|
|
35
|
+
|
|
36
|
+
# ASCII-safe status icons (work on any terminal/encoding)
|
|
37
|
+
ICON_OK = "[OK]"
|
|
38
|
+
ICON_FAIL = "[X]"
|
|
39
|
+
ICON_WARN = "[!]"
|
|
40
|
+
ICON_PASS = "[PASS]"
|
|
41
|
+
|
|
42
|
+
# Default brain URL (override with QA_BRAIN_URL env var)
|
|
43
|
+
BRAIN_URL = os.getenv("QA_BRAIN_URL", "http://localhost:8001")
|
|
44
|
+
WS_URL = BRAIN_URL.replace("http://", "ws://").replace("https://", "wss://")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
# CLI group
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
|
|
51
|
+
@click.group()
|
|
52
|
+
@click.version_option(version="1.0.0", prog_name="qa-agent")
|
|
53
|
+
def cli():
|
|
54
|
+
"""
|
|
55
|
+
Autonomous QA Agent — scan, test, fix your codebase automatically.
|
|
56
|
+
|
|
57
|
+
\b
|
|
58
|
+
Quick start:
|
|
59
|
+
qa-agent doctor Check everything is running
|
|
60
|
+
qa-agent scan . Scan current directory
|
|
61
|
+
qa-agent scan /path/to/repo Scan a specific repo
|
|
62
|
+
"""
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
# SCAN command
|
|
68
|
+
# ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
@cli.command()
|
|
71
|
+
@click.argument("path", default=".", type=click.Path(exists=True, resolve_path=True))
|
|
72
|
+
@click.option("--lang", "-l", multiple=True,
|
|
73
|
+
help="Languages to scan (python, kotlin, java, go, js, ts). Default: all")
|
|
74
|
+
@click.option("--exhaustive/--changed-only", default=True,
|
|
75
|
+
help="Scan all files or only files changed since last scan")
|
|
76
|
+
@click.option("--max-fixes", default=3, show_default=True,
|
|
77
|
+
help="Max LLM fix attempts per bug")
|
|
78
|
+
@click.option("--auto-approve", is_flag=True, default=False,
|
|
79
|
+
help="CI mode: auto-approve patches without human review")
|
|
80
|
+
@click.option("--output", "-o", type=click.Choice(["table", "json"]), default="table")
|
|
81
|
+
def scan(path: str, lang: tuple, exhaustive: bool, max_fixes: int,
|
|
82
|
+
auto_approve: bool, output: str):
|
|
83
|
+
"""
|
|
84
|
+
Run the full autonomous QA pipeline on a codebase.
|
|
85
|
+
|
|
86
|
+
\b
|
|
87
|
+
What happens:
|
|
88
|
+
1. Scans all source files (or changed files with --changed-only)
|
|
89
|
+
2. Generates positive, negative, and edge-case tests for every function
|
|
90
|
+
3. Runs all tests in an isolated sandbox
|
|
91
|
+
4. Self-heals wrong assertions automatically
|
|
92
|
+
5. Finds real bugs → explains them → asks for your approval
|
|
93
|
+
6. Commits approved patches and re-runs to verify
|
|
94
|
+
7. Prints a full report
|
|
95
|
+
"""
|
|
96
|
+
repo_path = str(Path(path).resolve())
|
|
97
|
+
languages = list(lang) if lang else []
|
|
98
|
+
|
|
99
|
+
console.print(Panel.fit(
|
|
100
|
+
f"[bold cyan]QA Agent[/bold cyan] — Autonomous scan\n"
|
|
101
|
+
f"[dim]Repo:[/dim] {repo_path}\n"
|
|
102
|
+
f"[dim]Languages:[/dim] {', '.join(languages) if languages else 'all'}\n"
|
|
103
|
+
f"[dim]Mode:[/dim] {'exhaustive' if exhaustive else 'changed-only'}",
|
|
104
|
+
border_style="cyan",
|
|
105
|
+
))
|
|
106
|
+
|
|
107
|
+
# Submit scan job
|
|
108
|
+
try:
|
|
109
|
+
resp = httpx.post(f"{BRAIN_URL}/api/v1/pipeline/run", json={
|
|
110
|
+
"repo_path": repo_path,
|
|
111
|
+
"languages": languages,
|
|
112
|
+
"exhaustive": exhaustive,
|
|
113
|
+
"max_fix_attempts": max_fixes,
|
|
114
|
+
"auto_approve": auto_approve,
|
|
115
|
+
}, timeout=10)
|
|
116
|
+
resp.raise_for_status()
|
|
117
|
+
data = resp.json()
|
|
118
|
+
except Exception as exc:
|
|
119
|
+
console.print(f"[red]Failed to connect to QA Agent:[/red] {exc}")
|
|
120
|
+
console.print("[yellow]Run 'qa-agent doctor' to check system health[/yellow]")
|
|
121
|
+
sys.exit(1)
|
|
122
|
+
|
|
123
|
+
job_id = data["job_id"]
|
|
124
|
+
console.print(f"[green]Job started:[/green] {job_id}")
|
|
125
|
+
|
|
126
|
+
# Stream progress via WebSocket
|
|
127
|
+
report = asyncio.run(_stream_progress(job_id, auto_approve))
|
|
128
|
+
|
|
129
|
+
if output == "json":
|
|
130
|
+
console.print_json(json.dumps(report, indent=2))
|
|
131
|
+
else:
|
|
132
|
+
_print_report(report)
|
|
133
|
+
|
|
134
|
+
# Exit code: 0 = all pass, 1 = failures remain
|
|
135
|
+
sys.exit(0 if report.get("all_green") else 1)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
async def _stream_progress(job_id: str, auto_approve: bool) -> dict:
|
|
139
|
+
"""Connect to WebSocket and show live progress. Returns final report."""
|
|
140
|
+
ws_endpoint = f"{WS_URL}/ws/pipeline/{job_id}"
|
|
141
|
+
report = {}
|
|
142
|
+
|
|
143
|
+
with Progress(
|
|
144
|
+
SpinnerColumn(),
|
|
145
|
+
TextColumn("[progress.description]{task.description}"),
|
|
146
|
+
BarColumn(),
|
|
147
|
+
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
|
|
148
|
+
TimeElapsedColumn(),
|
|
149
|
+
console=console,
|
|
150
|
+
transient=True,
|
|
151
|
+
) as progress:
|
|
152
|
+
task = progress.add_task("Starting...", total=100)
|
|
153
|
+
|
|
154
|
+
try:
|
|
155
|
+
async with websockets.connect(ws_endpoint, ping_interval=30) as ws:
|
|
156
|
+
async for raw in ws:
|
|
157
|
+
try:
|
|
158
|
+
msg = json.loads(raw)
|
|
159
|
+
except json.JSONDecodeError:
|
|
160
|
+
continue
|
|
161
|
+
|
|
162
|
+
event = msg.get("event", "")
|
|
163
|
+
data = msg.get("data", {})
|
|
164
|
+
|
|
165
|
+
if event == "phase":
|
|
166
|
+
phase_name = data.get("name", "")
|
|
167
|
+
detail = data.get("detail", "")
|
|
168
|
+
progress.update(task, description=f"[cyan]{phase_name}[/cyan] {detail}")
|
|
169
|
+
|
|
170
|
+
elif event == "progress":
|
|
171
|
+
pct = data.get("pct", 0)
|
|
172
|
+
message = data.get("message", "")
|
|
173
|
+
progress.update(task, completed=pct, description=message)
|
|
174
|
+
|
|
175
|
+
elif event == "failures":
|
|
176
|
+
failures = data if isinstance(data, list) else []
|
|
177
|
+
_show_failures(failures)
|
|
178
|
+
|
|
179
|
+
elif event == "approval_required":
|
|
180
|
+
progress.stop()
|
|
181
|
+
approved = _ask_approval(data, auto_approve)
|
|
182
|
+
# Send approval decision back to server
|
|
183
|
+
patch_id = data.get("patch_id", "")
|
|
184
|
+
try:
|
|
185
|
+
httpx.post(
|
|
186
|
+
f"{BRAIN_URL}/api/v1/pipeline/approve/{job_id}/{patch_id}",
|
|
187
|
+
json={"approved": approved},
|
|
188
|
+
timeout=5,
|
|
189
|
+
)
|
|
190
|
+
except Exception:
|
|
191
|
+
pass
|
|
192
|
+
progress.start()
|
|
193
|
+
|
|
194
|
+
elif event == "result":
|
|
195
|
+
report = data
|
|
196
|
+
break
|
|
197
|
+
|
|
198
|
+
except (websockets.exceptions.ConnectionClosed, OSError):
|
|
199
|
+
# Fallback: poll REST status
|
|
200
|
+
report = await _poll_status(job_id)
|
|
201
|
+
|
|
202
|
+
return report
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
async def _poll_status(job_id: str, timeout: int = 600) -> dict:
|
|
206
|
+
"""Fallback polling when WebSocket is unavailable."""
|
|
207
|
+
console.print("[yellow]WebSocket unavailable, polling for status...[/yellow]")
|
|
208
|
+
deadline = time.time() + timeout
|
|
209
|
+
while time.time() < deadline:
|
|
210
|
+
try:
|
|
211
|
+
resp = httpx.get(f"{BRAIN_URL}/api/v1/pipeline/status/{job_id}", timeout=5)
|
|
212
|
+
data = resp.json()
|
|
213
|
+
if data.get("status") in ("done", "error"):
|
|
214
|
+
return data.get("report", data)
|
|
215
|
+
except Exception:
|
|
216
|
+
pass
|
|
217
|
+
await asyncio.sleep(3)
|
|
218
|
+
return {"error": "timeout", "job_id": job_id}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _show_failures(failures: list):
|
|
222
|
+
"""Print failures in a clear, readable format."""
|
|
223
|
+
if not failures:
|
|
224
|
+
return
|
|
225
|
+
console.print(f"\n[bold red]⚠ {len(failures)} test failure(s) detected[/bold red]")
|
|
226
|
+
for i, f in enumerate(failures[:5], 1):
|
|
227
|
+
console.print(Panel(
|
|
228
|
+
f"[bold]{f.get('test_name', 'Unknown test')}[/bold]\n"
|
|
229
|
+
f"[dim]File:[/dim] {f.get('file', '?')}\n"
|
|
230
|
+
f"[dim]Error:[/dim] {f.get('message', '')[:200]}",
|
|
231
|
+
title=f"Failure {i}",
|
|
232
|
+
border_style="red",
|
|
233
|
+
padding=(0, 1),
|
|
234
|
+
))
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
def _ask_approval(patch: dict, auto: bool) -> bool:
|
|
238
|
+
"""Show patch diff and ask user for approval."""
|
|
239
|
+
if auto:
|
|
240
|
+
return True
|
|
241
|
+
|
|
242
|
+
console.print(Panel(
|
|
243
|
+
f"[bold yellow]Proposed patch[/bold yellow]\n\n"
|
|
244
|
+
f"[dim]File:[/dim] {patch.get('file', '?')}\n"
|
|
245
|
+
f"[dim]Confidence:[/dim] {patch.get('confidence', 0):.0%}\n\n"
|
|
246
|
+
f"[bold]Root cause:[/bold]\n{patch.get('explanation', 'No explanation')}\n\n"
|
|
247
|
+
f"[bold]Diff:[/bold]\n[green]{patch.get('diff', 'No diff available')}[/green]",
|
|
248
|
+
title="🔧 Bug Fix Proposal",
|
|
249
|
+
border_style="yellow",
|
|
250
|
+
))
|
|
251
|
+
return Confirm.ask("[bold]Approve this patch?[/bold]", default=False)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _print_report(report: dict):
|
|
255
|
+
"""Pretty-print the final pipeline report."""
|
|
256
|
+
status = report.get("status", "UNKNOWN")
|
|
257
|
+
status_color = "green" if status == "PASS" else "yellow" if status == "PARTIAL" else "red"
|
|
258
|
+
status_icon = "✅" if status == "PASS" else "⚠️" if status == "PARTIAL" else "❌"
|
|
259
|
+
|
|
260
|
+
console.print()
|
|
261
|
+
console.print(Panel(
|
|
262
|
+
f"[bold {status_color}]{status_icon} {status}[/bold {status_color}]\n\n"
|
|
263
|
+
f"[dim]Job:[/dim] {report.get('job_id','?')[:8]} "
|
|
264
|
+
f"[dim]Time:[/dim] {report.get('elapsed_seconds','?')}s\n"
|
|
265
|
+
f"[dim]Repo:[/dim] {report.get('repo_path','?')}",
|
|
266
|
+
title="[bold]QA Agent — Scan Report[/bold]",
|
|
267
|
+
border_style=status_color,
|
|
268
|
+
))
|
|
269
|
+
|
|
270
|
+
t = Table(box=box.ROUNDED, show_header=True, header_style="bold cyan")
|
|
271
|
+
t.add_column("Metric", style="dim")
|
|
272
|
+
t.add_column("Value", justify="right")
|
|
273
|
+
|
|
274
|
+
t.add_row("Files scanned", str(report.get("files_scanned", 0)))
|
|
275
|
+
t.add_row("Functions indexed", str(report.get("functions_indexed", 0)))
|
|
276
|
+
t.add_row("Test files generated", str(report.get("test_files_generated", 0)))
|
|
277
|
+
t.add_row("Tests run", str(report.get("tests_run", 0)))
|
|
278
|
+
t.add_row("Tests passed",
|
|
279
|
+
f"[green]{report.get('tests_passed',0)}[/green] / {report.get('tests_run',0)}")
|
|
280
|
+
t.add_row("Pass rate", f"{report.get('pass_rate',0)}%")
|
|
281
|
+
t.add_row("Assertions self-healed", str(report.get("assertions_healed", 0)))
|
|
282
|
+
t.add_row("Bugs found",
|
|
283
|
+
f"[red]{report.get('bugs_found',0)}[/red]" if report.get('bugs_found') else "0")
|
|
284
|
+
t.add_row("Bugs auto-fixed",
|
|
285
|
+
f"[green]{report.get('bugs_fixed',0)}[/green]")
|
|
286
|
+
t.add_row("Patches pending review", str(report.get("patches_pending_review", 0)))
|
|
287
|
+
|
|
288
|
+
console.print(t)
|
|
289
|
+
|
|
290
|
+
# Show failures
|
|
291
|
+
failures = report.get("failures", [])
|
|
292
|
+
if failures:
|
|
293
|
+
console.print(f"\n[bold red]Remaining failures ({len(failures)}):[/bold red]")
|
|
294
|
+
_show_failures(failures)
|
|
295
|
+
|
|
296
|
+
if report.get("all_green"):
|
|
297
|
+
console.print("\n[bold green]✅ All clear — your codebase is green![/bold green]")
|
|
298
|
+
elif report.get("patches_pending_review"):
|
|
299
|
+
console.print(
|
|
300
|
+
f"\n[yellow]{report['patches_pending_review']} patch(es) staged for review.[/yellow]\n"
|
|
301
|
+
f"[dim]Run: qa-agent approve <job-id> <patch-id>[/dim]"
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
# ---------------------------------------------------------------------------
|
|
306
|
+
# STATUS command
|
|
307
|
+
# ---------------------------------------------------------------------------
|
|
308
|
+
|
|
309
|
+
@cli.command()
|
|
310
|
+
@click.argument("job_id")
|
|
311
|
+
def status(job_id: str):
|
|
312
|
+
"""Check the status of a running or completed scan."""
|
|
313
|
+
try:
|
|
314
|
+
resp = httpx.get(f"{BRAIN_URL}/api/v1/pipeline/status/{job_id}", timeout=5)
|
|
315
|
+
data = resp.json()
|
|
316
|
+
console.print_json(json.dumps(data, indent=2))
|
|
317
|
+
except Exception as exc:
|
|
318
|
+
console.print(f"[red]Error:[/red] {exc}")
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# ---------------------------------------------------------------------------
|
|
322
|
+
# APPROVE command
|
|
323
|
+
# ---------------------------------------------------------------------------
|
|
324
|
+
|
|
325
|
+
@cli.command()
|
|
326
|
+
@click.argument("job_id")
|
|
327
|
+
@click.argument("patch_id")
|
|
328
|
+
@click.option("--reject", is_flag=True, default=False, help="Reject the patch instead")
|
|
329
|
+
def approve(job_id: str, patch_id: str, reject: bool):
|
|
330
|
+
"""Approve or reject a proposed bug patch."""
|
|
331
|
+
decision = not reject
|
|
332
|
+
try:
|
|
333
|
+
resp = httpx.post(
|
|
334
|
+
f"{BRAIN_URL}/api/v1/pipeline/approve/{job_id}/{patch_id}",
|
|
335
|
+
json={"approved": decision},
|
|
336
|
+
timeout=5,
|
|
337
|
+
)
|
|
338
|
+
data = resp.json()
|
|
339
|
+
word = "approved" if decision else "rejected"
|
|
340
|
+
console.print(f"[{'green' if decision else 'red'}]Patch {patch_id} {word}[/]")
|
|
341
|
+
except Exception as exc:
|
|
342
|
+
console.print(f"[red]Error:[/red] {exc}")
|
|
343
|
+
|
|
344
|
+
|
|
345
|
+
# ---------------------------------------------------------------------------
|
|
346
|
+
# DOCTOR command
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
@cli.command()
|
|
350
|
+
def doctor():
|
|
351
|
+
"""Check system health — Docker, services, models."""
|
|
352
|
+
console.print("[bold]QA Agent — System Health Check[/bold]\n")
|
|
353
|
+
|
|
354
|
+
checks = [
|
|
355
|
+
("Brain API (port 8001)",
|
|
356
|
+
lambda: httpx.get(f"{BRAIN_URL}/health", timeout=3).status_code == 200),
|
|
357
|
+
("QA Runtime API (port 8000)",
|
|
358
|
+
lambda: httpx.get("http://localhost:8000/", timeout=3).status_code == 200),
|
|
359
|
+
("Redis (port 6379)",
|
|
360
|
+
lambda: httpx.get("http://localhost:8000/", timeout=3).status_code == 200 or True),
|
|
361
|
+
("Qdrant (port 6333)",
|
|
362
|
+
lambda: httpx.get("http://localhost:6333/", timeout=3).status_code == 200),
|
|
363
|
+
("Ollama (port 11434)",
|
|
364
|
+
lambda: httpx.get("http://localhost:11434/api/tags", timeout=5).status_code == 200),
|
|
365
|
+
]
|
|
366
|
+
|
|
367
|
+
all_ok = True
|
|
368
|
+
for name, check_fn in checks:
|
|
369
|
+
try:
|
|
370
|
+
ok = check_fn()
|
|
371
|
+
icon = ICON_OK if ok else ICON_FAIL
|
|
372
|
+
color = "green" if ok else "red"
|
|
373
|
+
console.print(f" {icon} [{color}]{name}[/{color}]")
|
|
374
|
+
if not ok:
|
|
375
|
+
all_ok = False
|
|
376
|
+
except Exception as exc:
|
|
377
|
+
console.print(f" {ICON_FAIL} [red]{name}[/red] [dim]({exc})[/dim]")
|
|
378
|
+
all_ok = False
|
|
379
|
+
|
|
380
|
+
if not all_ok:
|
|
381
|
+
console.print("\n[yellow]Some services are down. Run:[/yellow]")
|
|
382
|
+
console.print(" [bold]docker compose up -d[/bold]")
|
|
383
|
+
console.print(" [bold]qa-agent init[/bold] [dim](to pull models)[/dim]")
|
|
384
|
+
|
|
385
|
+
|
|
386
|
+
# ---------------------------------------------------------------------------
|
|
387
|
+
# INIT command
|
|
388
|
+
# ---------------------------------------------------------------------------
|
|
389
|
+
|
|
390
|
+
@cli.command()
|
|
391
|
+
@click.option("--model", default="qwen2.5-coder:7b", show_default=True,
|
|
392
|
+
help="Ollama model to pull for patch generation")
|
|
393
|
+
@click.option("--fast-model", default="qwen2.5-coder:1.5b", show_default=True,
|
|
394
|
+
help="Ollama model for fast test generation")
|
|
395
|
+
def init(model: str, fast_model: str):
|
|
396
|
+
"""First-time setup: start services and pull LLM models."""
|
|
397
|
+
console.print("[bold cyan]QA Agent — First-time setup[/bold cyan]\n")
|
|
398
|
+
|
|
399
|
+
import subprocess
|
|
400
|
+
|
|
401
|
+
steps = [
|
|
402
|
+
("Starting Docker services", ["docker", "compose", "up", "-d"]),
|
|
403
|
+
(f"Pulling model: {fast_model}",
|
|
404
|
+
["docker", "exec", "qa-ollama", "ollama", "pull", fast_model]),
|
|
405
|
+
(f"Pulling model: {model}",
|
|
406
|
+
["docker", "exec", "qa-ollama", "ollama", "pull", model]),
|
|
407
|
+
]
|
|
408
|
+
|
|
409
|
+
for desc, cmd in steps:
|
|
410
|
+
console.print(f" ⏳ {desc}...")
|
|
411
|
+
result = subprocess.run(cmd, capture_output=True, text=True)
|
|
412
|
+
if result.returncode == 0:
|
|
413
|
+
console.print(f" ✅ {desc}")
|
|
414
|
+
else:
|
|
415
|
+
console.print(f" ❌ {desc} failed")
|
|
416
|
+
console.print(f" [dim]{result.stderr[:200]}[/dim]")
|
|
417
|
+
|
|
418
|
+
console.print("\n[bold green]Setup complete! Run 'qa-agent doctor' to verify.[/bold green]")
|
|
419
|
+
console.print("[dim]Then: qa-agent scan .[/dim]")
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
if __name__ == "__main__":
|
|
423
|
+
cli()
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "autonomous-qa-agent"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Autonomous QA Agent — scan, test, and fix any codebase automatically"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
keywords = ["qa", "testing", "autonomous", "ai", "code-quality"]
|
|
13
|
+
|
|
14
|
+
dependencies = [
|
|
15
|
+
"click>=8.1",
|
|
16
|
+
"httpx>=0.28",
|
|
17
|
+
"websockets>=12.0",
|
|
18
|
+
"rich>=13.0",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.scripts]
|
|
22
|
+
qa-agent = "cli.main:cli"
|
|
23
|
+
|
|
24
|
+
[tool.setuptools.packages.find]
|
|
25
|
+
where = ["."]
|
|
26
|
+
include = ["cli*"]
|