evolution-engine 0.2.2__tar.gz → 0.3.1__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.
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/LICENSE +1 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/LICENSE-MIT +15 -11
- {evolution_engine-0.2.2/evolution_engine.egg-info → evolution_engine-0.3.1}/PKG-INFO +56 -30
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/README.md +41 -18
- evolution_engine-0.3.1/evolution/__init__.py +1 -0
- evolution_engine-0.3.1/evolution/adapters/error_tracking/__init__.py +0 -0
- evolution_engine-0.3.1/evolution/adapters/error_tracking/sentry_adapter.py +243 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/agents/base.py +18 -1
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/cli.py +315 -56
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/config.py +12 -1
- evolution_engine-0.3.1/evolution/constants.py +80 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/format_comment.py +8 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/friendly.py +88 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/history.py +2 -2
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/init.py +5 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/investigator.py +40 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/kb_sync.py +114 -2
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/knowledge_store.py +3 -3
- evolution_engine-0.3.1/evolution/license.py +609 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/orchestrator.py +226 -18
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/phase1_engine.py +2 -2
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/phase3_engine.py +3 -16
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/phase4_engine.py +9 -20
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/phase5_engine.py +204 -88
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/pr_comment.py +73 -17
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/report_generator.py +990 -327
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/report_server.py +24 -3
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/setup_ui.py +32 -8
- evolution_engine-0.3.1/evolution/telemetry.py +324 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1/evolution_engine.egg-info}/PKG-INFO +56 -30
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution_engine.egg-info/SOURCES.txt +3 -0
- evolution_engine-0.3.1/evolution_engine.egg-info/requires.txt +17 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/pyproject.toml +18 -14
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/setup.py +2 -2
- evolution_engine-0.2.2/evolution/__init__.py +0 -1
- evolution_engine-0.2.2/evolution/license.py +0 -298
- evolution_engine-0.2.2/evolution/telemetry.py +0 -141
- evolution_engine-0.2.2/evolution_engine.egg-info/requires.txt +0 -13
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/MANIFEST.in +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/accepted.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapter_scaffold.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapter_security.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapter_validator.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapter_versions.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/ci/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/ci/circleci_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/ci/github_actions_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/ci/gitlab_pipelines_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/config/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/config/terraform_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/dependency/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/dependency/pip_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/deployment/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/deployment/github_releases_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/deployment/gitlab_releases_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/git/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/git/git_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/git/git_history_walker.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/github_client.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/gitlab_client.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/schema/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/schema/openapi_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/security/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/security/github_security_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/security/trivy_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/testing/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/testing/coverage_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/adapters/testing/junit_adapter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/agents/__init__.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/agents/anthropic_agent.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/agents/cli_agent.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/adapter_blocklist.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/adapter_catalog.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/pattern_blocklist.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/pattern_index.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/sdk_fingerprints.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/universal_patterns.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/data/verified_adapters.json +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/fixer.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/fp_validation.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/hooks.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/inline_suggestions.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/kb_export.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/kb_security.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/llm_anthropic.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/llm_openrouter.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/notifications.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/pattern_registry.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/pattern_scaffold.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/pattern_validator.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/phase2_engine.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/phase3_1_renderer.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/prescan.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/registry.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/validation_gate.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution/watcher.py +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution_engine.egg-info/dependency_links.txt +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution_engine.egg-info/entry_points.txt +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/evolution_engine.egg-info/top_level.txt +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/setup.cfg +0 -0
- {evolution_engine-0.2.2 → evolution_engine-0.3.1}/tests/test_git_history_walker.py +0 -0
|
@@ -5,6 +5,7 @@ Parameters
|
|
|
5
5
|
Licensor: CodeQual LLC
|
|
6
6
|
|
|
7
7
|
Licensed Work: Evolution Engine Core Analysis Engine v0.2.0 (and all subsequent versions)
|
|
8
|
+
The Licensed Work is (c) 2025-2026 CodeQual LLC.
|
|
8
9
|
|
|
9
10
|
Additional Use Grant: You may use the Licensed Work in non-production environments for internal testing and development without a commercial license. Production use requires a valid Pro subscription.
|
|
10
11
|
|
|
@@ -2,18 +2,12 @@ MIT License
|
|
|
2
2
|
|
|
3
3
|
Copyright (c) 2025-2026 CodeQual LLC
|
|
4
4
|
|
|
5
|
-
This license applies to the following components of Evolution Engine:
|
|
6
|
-
- Command-line interface wrapper (evolution/cli.py)
|
|
7
|
-
- Adapter framework (evolution/adapters/)
|
|
8
|
-
- Plugin interfaces
|
|
9
|
-
- GitHub Action wrapper (action/)
|
|
10
|
-
|
|
11
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
12
|
-
of
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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:
|
|
17
11
|
|
|
18
12
|
The above copyright notice and this permission notice shall be included in all
|
|
19
13
|
copies or substantial portions of the Software.
|
|
@@ -25,3 +19,13 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
25
19
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
26
20
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
27
21
|
SOFTWARE.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
This MIT license applies to the following components of Evolution Engine:
|
|
26
|
+
- Command-line interface (evolution/cli.py)
|
|
27
|
+
- Adapter framework (evolution/adapters/)
|
|
28
|
+
- Plugin interfaces
|
|
29
|
+
- GitHub Action (action/)
|
|
30
|
+
|
|
31
|
+
The core analysis engine (Phases 2-5) is licensed under BSL 1.1. See LICENSE.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evolution-engine
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.1
|
|
4
4
|
Summary: Git-native codebase evolution indexer
|
|
5
5
|
Author: Slava
|
|
6
6
|
License: BSL-1.1
|
|
7
7
|
Project-URL: Homepage, https://codequal.dev
|
|
8
|
-
Project-URL: Repository, https://github.com/alpsla/
|
|
9
|
-
Project-URL: Bug Tracker, https://github.com/alpsla/
|
|
8
|
+
Project-URL: Repository, https://github.com/alpsla/evolution-engine
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/alpsla/evolution-engine/issues
|
|
10
10
|
Keywords: git,devops,ci-cd,code-quality,evolution,drift-detection,codebase-analysis
|
|
11
11
|
Classifier: Development Status :: 3 - Alpha
|
|
12
12
|
Classifier: Environment :: Console
|
|
@@ -23,24 +23,36 @@ Requires-Python: >=3.10
|
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
25
|
License-File: LICENSE-MIT
|
|
26
|
-
Requires-Dist: GitPython
|
|
27
|
-
Requires-Dist: click
|
|
28
|
-
Requires-Dist: requests
|
|
29
|
-
Requires-Dist: jinja2
|
|
26
|
+
Requires-Dist: GitPython<4,>=3.1
|
|
27
|
+
Requires-Dist: click<9,>=8.0
|
|
28
|
+
Requires-Dist: requests<3,>=2.25
|
|
29
|
+
Requires-Dist: jinja2<4,>=3.0
|
|
30
30
|
Provides-Extra: llm
|
|
31
|
-
Requires-Dist: requests
|
|
31
|
+
Requires-Dist: requests<3,>=2.25; extra == "llm"
|
|
32
|
+
Provides-Extra: demo
|
|
33
|
+
Requires-Dist: playwright<3,>=1.40; extra == "demo"
|
|
32
34
|
Provides-Extra: dev
|
|
33
|
-
Requires-Dist: pytest
|
|
34
|
-
Requires-Dist: pytest-cov
|
|
35
|
-
Requires-Dist: python-dotenv
|
|
36
|
-
Requires-Dist: stripe
|
|
35
|
+
Requires-Dist: pytest<9,>=7.0; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest-cov<6,>=4.0; extra == "dev"
|
|
37
|
+
Requires-Dist: python-dotenv<2,>=0.19; extra == "dev"
|
|
38
|
+
Requires-Dist: stripe<12,>=7.0; extra == "dev"
|
|
39
|
+
Requires-Dist: build<2,>=1.0; extra == "dev"
|
|
37
40
|
Dynamic: license-file
|
|
38
41
|
|
|
39
42
|
# Evolution Engine
|
|
40
43
|
|
|
44
|
+
[](https://pypi.org/project/evolution-engine/)
|
|
45
|
+
[](https://pypi.org/project/evolution-engine/)
|
|
46
|
+
[](LICENSE)
|
|
47
|
+
[]()
|
|
48
|
+
|
|
41
49
|
**AI coding tools write correct code that silently breaks your architecture. Evolution Engine detects the drift, shows you the exact commit, and lets your AI fix it — with evidence.**
|
|
42
50
|
|
|
43
|
-
Calibrated on
|
|
51
|
+
Calibrated on 200+ open-source repos. 6.18M signals analyzed. Your code never leaves your machine.
|
|
52
|
+
|
|
53
|
+
**[codequal.dev](https://codequal.dev)** | [Quickstart](QUICKSTART.md) | [PyPI](https://pypi.org/project/evolution-engine/)
|
|
54
|
+
|
|
55
|
+

|
|
44
56
|
|
|
45
57
|
---
|
|
46
58
|
|
|
@@ -53,18 +65,35 @@ Evolution Engine is a **drift detector for AI-assisted development**. It learns
|
|
|
53
65
|
### The Loop: Detect → Evidence → Fix → Verify
|
|
54
66
|
|
|
55
67
|
```
|
|
56
|
-
evo analyze .
|
|
68
|
+
evo analyze . What changed? Is it unusual for THIS repo?
|
|
57
69
|
|
|
|
58
|
-
evo
|
|
70
|
+
evo analyze . --show-prompt Copy the investigation prompt with evidence
|
|
59
71
|
|
|
|
60
|
-
|
|
72
|
+
You + your AI tool Paste into Claude Code / Cursor / Copilot to fix
|
|
61
73
|
|
|
|
62
|
-
evo
|
|
74
|
+
evo analyze . --verify Did the fix resolve the drift? Or make it worse?
|
|
63
75
|
|
|
|
64
|
-
evo accept . 1 2
|
|
76
|
+
evo accept . 1 2 Expected change? Accept it. Move on.
|
|
65
77
|
```
|
|
66
78
|
|
|
67
|
-
|
|
79
|
+
EE generates the evidence and the prompt — you bring your own AI tool to investigate and fix. Then EE verifies whether the drift resolved. No other tool closes this loop.
|
|
80
|
+
|
|
81
|
+
### Sample Reports
|
|
82
|
+
|
|
83
|
+
<table>
|
|
84
|
+
<tr>
|
|
85
|
+
<td width="50%"><strong>Analyze Report</strong></td>
|
|
86
|
+
<td width="50%"><strong>Verification Report</strong></td>
|
|
87
|
+
</tr>
|
|
88
|
+
<tr>
|
|
89
|
+
<td><a href="docs/images/report-analyze.png"><img src="docs/images/report-analyze.png" alt="Analyze Report" width="400"></a></td>
|
|
90
|
+
<td><a href="docs/images/report-verify.png"><img src="docs/images/report-verify.png" alt="Verification Report" width="400"></a></td>
|
|
91
|
+
</tr>
|
|
92
|
+
<tr>
|
|
93
|
+
<td><em>Findings, patterns, and investigation prompt</em></td>
|
|
94
|
+
<td><em>Verification banner shows what resolved</em></td>
|
|
95
|
+
</tr>
|
|
96
|
+
</table>
|
|
68
97
|
|
|
69
98
|
### How It Works (5-Phase Pipeline)
|
|
70
99
|
|
|
@@ -83,7 +112,7 @@ Your Repo → Phase 1 (Record events) → Phase 2 (Detect deviation from YOUR ba
|
|
|
83
112
|
| **Phase 1** | Records immutable events — commits, builds, deps, releases |
|
|
84
113
|
| **Phase 2** | Computes per-repo baselines, flags statistical deviation (MAD/IQR) |
|
|
85
114
|
| **Phase 3** | Explains signals in human language — PM-friendly, evidence-backed |
|
|
86
|
-
| **Phase 4** | Matches against 44 validated patterns from
|
|
115
|
+
| **Phase 4** | Matches against 44 validated patterns from 200+ repos |
|
|
87
116
|
| **Phase 5** | Prioritized advisory with severity, evidence, and action items |
|
|
88
117
|
|
|
89
118
|
---
|
|
@@ -124,10 +153,6 @@ evo init . --path all
|
|
|
124
153
|
|
|
125
154
|
Free tier covers Path 1 (CLI). Pro unlocks Path 2 (hooks) and Path 3 (CI integration), plus AI investigation, AI fix loop, and inline PR review comments.
|
|
126
155
|
|
|
127
|
-
### Founding Member Program
|
|
128
|
-
|
|
129
|
-
We're looking for 50 developers who use AI coding tools daily. In exchange for monthly feedback, you get founding member pricing: **$9.50/month for 3 months** (regular: $19/month). Use code `FOUNDING50` at checkout.
|
|
130
|
-
|
|
131
156
|
### Environment Variables
|
|
132
157
|
|
|
133
158
|
```bash
|
|
@@ -135,7 +160,7 @@ We're looking for 50 developers who use AI coding tools daily. In exchange for m
|
|
|
135
160
|
GITHUB_TOKEN=ghp_xxx # Unlocks CI, deployment, security adapters
|
|
136
161
|
GITLAB_TOKEN=glpat-xxx # Unlocks GitLab CI, releases adapters
|
|
137
162
|
EVO_LICENSE_KEY=xxx # Pro features (free tier works without)
|
|
138
|
-
ANTHROPIC_API_KEY=sk-ant-xxx # For evo
|
|
163
|
+
ANTHROPIC_API_KEY=sk-ant-xxx # For evo fix automated loop (Pro)
|
|
139
164
|
```
|
|
140
165
|
|
|
141
166
|
---
|
|
@@ -188,9 +213,10 @@ evo analyze [path] # Detect adapters, run full pipeline
|
|
|
188
213
|
evo analyze . --families git,ci # Override auto-detection
|
|
189
214
|
evo report [path] # Generate HTML report from last run
|
|
190
215
|
evo status # Show detected adapters and event counts
|
|
191
|
-
evo
|
|
192
|
-
evo fix [path]
|
|
193
|
-
evo fix [path] --residual
|
|
216
|
+
evo analyze . --show-prompt # Copy investigation prompt for your AI tool
|
|
217
|
+
evo fix [path] --dry-run # Generate evidence-backed fix prompt (Pro)
|
|
218
|
+
evo fix [path] --dry-run --residual # Iteration-aware prompt (what's fixed vs still broken)
|
|
219
|
+
evo fix [path] # Automated fix-verify loop with CLI agent (Pro, advanced)
|
|
194
220
|
evo verify <advisory> # Compare current state to a previous advisory
|
|
195
221
|
|
|
196
222
|
# Setup & Integration
|
|
@@ -343,7 +369,7 @@ evolution-engine/
|
|
|
343
369
|
│ ├── pattern_validator.py # Pattern package validation
|
|
344
370
|
│ ├── pattern_scaffold.py # Pattern package scaffolding
|
|
345
371
|
│ ├── report_generator.py # Standalone HTML report generator
|
|
346
|
-
│ ├── investigator.py # AI investigation (
|
|
372
|
+
│ ├── investigator.py # AI investigation engine (Pro)
|
|
347
373
|
│ ├── fixer.py # AI fix-verify loop (evo fix, Pro)
|
|
348
374
|
│ ├── adapter_validator.py # 13-check adapter certification
|
|
349
375
|
│ ├── adapter_scaffold.py # Package scaffolding + AI prompt gen
|
|
@@ -397,7 +423,7 @@ evolution-engine/
|
|
|
397
423
|
|
|
398
424
|
AI coding tools are generating more code than ever. Teams ship faster — but structural quality is invisible until something breaks. EE provides the missing feedback loop: a guardrail that tells you (and your AI) when development patterns drift from what's normal for your project.
|
|
399
425
|
|
|
400
|
-
Calibrated on **
|
|
426
|
+
Calibrated on **200+ open-source repos**, **6.18 million SDLC signals**, and **2.1 million commits**. 44 validated cross-signal patterns. 1.6% false positive rate.
|
|
401
427
|
|
|
402
428
|
## Open-Core Model
|
|
403
429
|
|
|
@@ -1,8 +1,17 @@
|
|
|
1
1
|
# Evolution Engine
|
|
2
2
|
|
|
3
|
+
[](https://pypi.org/project/evolution-engine/)
|
|
4
|
+
[](https://pypi.org/project/evolution-engine/)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[]()
|
|
7
|
+
|
|
3
8
|
**AI coding tools write correct code that silently breaks your architecture. Evolution Engine detects the drift, shows you the exact commit, and lets your AI fix it — with evidence.**
|
|
4
9
|
|
|
5
|
-
Calibrated on
|
|
10
|
+
Calibrated on 200+ open-source repos. 6.18M signals analyzed. Your code never leaves your machine.
|
|
11
|
+
|
|
12
|
+
**[codequal.dev](https://codequal.dev)** | [Quickstart](QUICKSTART.md) | [PyPI](https://pypi.org/project/evolution-engine/)
|
|
13
|
+
|
|
14
|
+

|
|
6
15
|
|
|
7
16
|
---
|
|
8
17
|
|
|
@@ -15,18 +24,35 @@ Evolution Engine is a **drift detector for AI-assisted development**. It learns
|
|
|
15
24
|
### The Loop: Detect → Evidence → Fix → Verify
|
|
16
25
|
|
|
17
26
|
```
|
|
18
|
-
evo analyze .
|
|
27
|
+
evo analyze . What changed? Is it unusual for THIS repo?
|
|
19
28
|
|
|
|
20
|
-
evo
|
|
29
|
+
evo analyze . --show-prompt Copy the investigation prompt with evidence
|
|
21
30
|
|
|
|
22
|
-
|
|
31
|
+
You + your AI tool Paste into Claude Code / Cursor / Copilot to fix
|
|
23
32
|
|
|
|
24
|
-
evo
|
|
33
|
+
evo analyze . --verify Did the fix resolve the drift? Or make it worse?
|
|
25
34
|
|
|
|
26
|
-
evo accept . 1 2
|
|
35
|
+
evo accept . 1 2 Expected change? Accept it. Move on.
|
|
27
36
|
```
|
|
28
37
|
|
|
29
|
-
|
|
38
|
+
EE generates the evidence and the prompt — you bring your own AI tool to investigate and fix. Then EE verifies whether the drift resolved. No other tool closes this loop.
|
|
39
|
+
|
|
40
|
+
### Sample Reports
|
|
41
|
+
|
|
42
|
+
<table>
|
|
43
|
+
<tr>
|
|
44
|
+
<td width="50%"><strong>Analyze Report</strong></td>
|
|
45
|
+
<td width="50%"><strong>Verification Report</strong></td>
|
|
46
|
+
</tr>
|
|
47
|
+
<tr>
|
|
48
|
+
<td><a href="docs/images/report-analyze.png"><img src="docs/images/report-analyze.png" alt="Analyze Report" width="400"></a></td>
|
|
49
|
+
<td><a href="docs/images/report-verify.png"><img src="docs/images/report-verify.png" alt="Verification Report" width="400"></a></td>
|
|
50
|
+
</tr>
|
|
51
|
+
<tr>
|
|
52
|
+
<td><em>Findings, patterns, and investigation prompt</em></td>
|
|
53
|
+
<td><em>Verification banner shows what resolved</em></td>
|
|
54
|
+
</tr>
|
|
55
|
+
</table>
|
|
30
56
|
|
|
31
57
|
### How It Works (5-Phase Pipeline)
|
|
32
58
|
|
|
@@ -45,7 +71,7 @@ Your Repo → Phase 1 (Record events) → Phase 2 (Detect deviation from YOUR ba
|
|
|
45
71
|
| **Phase 1** | Records immutable events — commits, builds, deps, releases |
|
|
46
72
|
| **Phase 2** | Computes per-repo baselines, flags statistical deviation (MAD/IQR) |
|
|
47
73
|
| **Phase 3** | Explains signals in human language — PM-friendly, evidence-backed |
|
|
48
|
-
| **Phase 4** | Matches against 44 validated patterns from
|
|
74
|
+
| **Phase 4** | Matches against 44 validated patterns from 200+ repos |
|
|
49
75
|
| **Phase 5** | Prioritized advisory with severity, evidence, and action items |
|
|
50
76
|
|
|
51
77
|
---
|
|
@@ -86,10 +112,6 @@ evo init . --path all
|
|
|
86
112
|
|
|
87
113
|
Free tier covers Path 1 (CLI). Pro unlocks Path 2 (hooks) and Path 3 (CI integration), plus AI investigation, AI fix loop, and inline PR review comments.
|
|
88
114
|
|
|
89
|
-
### Founding Member Program
|
|
90
|
-
|
|
91
|
-
We're looking for 50 developers who use AI coding tools daily. In exchange for monthly feedback, you get founding member pricing: **$9.50/month for 3 months** (regular: $19/month). Use code `FOUNDING50` at checkout.
|
|
92
|
-
|
|
93
115
|
### Environment Variables
|
|
94
116
|
|
|
95
117
|
```bash
|
|
@@ -97,7 +119,7 @@ We're looking for 50 developers who use AI coding tools daily. In exchange for m
|
|
|
97
119
|
GITHUB_TOKEN=ghp_xxx # Unlocks CI, deployment, security adapters
|
|
98
120
|
GITLAB_TOKEN=glpat-xxx # Unlocks GitLab CI, releases adapters
|
|
99
121
|
EVO_LICENSE_KEY=xxx # Pro features (free tier works without)
|
|
100
|
-
ANTHROPIC_API_KEY=sk-ant-xxx # For evo
|
|
122
|
+
ANTHROPIC_API_KEY=sk-ant-xxx # For evo fix automated loop (Pro)
|
|
101
123
|
```
|
|
102
124
|
|
|
103
125
|
---
|
|
@@ -150,9 +172,10 @@ evo analyze [path] # Detect adapters, run full pipeline
|
|
|
150
172
|
evo analyze . --families git,ci # Override auto-detection
|
|
151
173
|
evo report [path] # Generate HTML report from last run
|
|
152
174
|
evo status # Show detected adapters and event counts
|
|
153
|
-
evo
|
|
154
|
-
evo fix [path]
|
|
155
|
-
evo fix [path] --residual
|
|
175
|
+
evo analyze . --show-prompt # Copy investigation prompt for your AI tool
|
|
176
|
+
evo fix [path] --dry-run # Generate evidence-backed fix prompt (Pro)
|
|
177
|
+
evo fix [path] --dry-run --residual # Iteration-aware prompt (what's fixed vs still broken)
|
|
178
|
+
evo fix [path] # Automated fix-verify loop with CLI agent (Pro, advanced)
|
|
156
179
|
evo verify <advisory> # Compare current state to a previous advisory
|
|
157
180
|
|
|
158
181
|
# Setup & Integration
|
|
@@ -305,7 +328,7 @@ evolution-engine/
|
|
|
305
328
|
│ ├── pattern_validator.py # Pattern package validation
|
|
306
329
|
│ ├── pattern_scaffold.py # Pattern package scaffolding
|
|
307
330
|
│ ├── report_generator.py # Standalone HTML report generator
|
|
308
|
-
│ ├── investigator.py # AI investigation (
|
|
331
|
+
│ ├── investigator.py # AI investigation engine (Pro)
|
|
309
332
|
│ ├── fixer.py # AI fix-verify loop (evo fix, Pro)
|
|
310
333
|
│ ├── adapter_validator.py # 13-check adapter certification
|
|
311
334
|
│ ├── adapter_scaffold.py # Package scaffolding + AI prompt gen
|
|
@@ -359,7 +382,7 @@ evolution-engine/
|
|
|
359
382
|
|
|
360
383
|
AI coding tools are generating more code than ever. Teams ship faster — but structural quality is invisible until something breaks. EE provides the missing feedback loop: a guardrail that tells you (and your AI) when development patterns drift from what's normal for your project.
|
|
361
384
|
|
|
362
|
-
Calibrated on **
|
|
385
|
+
Calibrated on **200+ open-source repos**, **6.18 million SDLC signals**, and **2.1 million commits**. 44 validated cross-signal patterns. 1.6% false positive rate.
|
|
363
386
|
|
|
364
387
|
## Open-Core Model
|
|
365
388
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.0"
|
|
File without changes
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sentry Source Adapter (Error Tracking)
|
|
3
|
+
|
|
4
|
+
Emits canonical SourceEvent payloads for Sentry issues.
|
|
5
|
+
Conforms to:
|
|
6
|
+
- docs/ADAPTER_CONTRACT.md (universal)
|
|
7
|
+
|
|
8
|
+
Supports:
|
|
9
|
+
- API mode: Fetches issues from Sentry API v0 (requires auth token)
|
|
10
|
+
- Fixture mode: Pre-parsed issue dicts (for testing)
|
|
11
|
+
|
|
12
|
+
Auth is via SENTRY_AUTH_TOKEN env var (Bearer token).
|
|
13
|
+
Supports self-hosted Sentry instances via base_url parameter.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import hashlib
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
from typing import Optional
|
|
21
|
+
|
|
22
|
+
try:
|
|
23
|
+
import requests as _requests
|
|
24
|
+
except ImportError:
|
|
25
|
+
_requests = None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SentryAdapter:
|
|
29
|
+
source_family = "error_tracking"
|
|
30
|
+
source_type = "sentry"
|
|
31
|
+
ordering_mode = "temporal"
|
|
32
|
+
attestation_tier = "medium"
|
|
33
|
+
|
|
34
|
+
API_BASE = "https://sentry.io/api/0"
|
|
35
|
+
|
|
36
|
+
def __init__(self, *, org: str = None, project: str = None,
|
|
37
|
+
token: str = None, issues: list = None,
|
|
38
|
+
source_id: str = None, max_issues: int = 500,
|
|
39
|
+
cache_dir: Path = None, base_url: str = None):
|
|
40
|
+
"""
|
|
41
|
+
Args:
|
|
42
|
+
org: Sentry organization slug
|
|
43
|
+
project: Sentry project slug
|
|
44
|
+
token: Sentry auth token (or SENTRY_AUTH_TOKEN env)
|
|
45
|
+
issues: Pre-parsed list of issue dicts (fixture mode)
|
|
46
|
+
source_id: Unique identifier
|
|
47
|
+
max_issues: Maximum issues to fetch (default 500)
|
|
48
|
+
cache_dir: Directory for response caching (optional)
|
|
49
|
+
base_url: Base URL for self-hosted Sentry (e.g. "https://sentry.mycompany.com/api/0")
|
|
50
|
+
"""
|
|
51
|
+
self._fixture_issues = issues
|
|
52
|
+
self._api_base = base_url or self.API_BASE
|
|
53
|
+
self.source_id = source_id or (
|
|
54
|
+
f"sentry:{org}/{project}" if org and project else "sentry:fixture"
|
|
55
|
+
)
|
|
56
|
+
self.max_issues = max_issues
|
|
57
|
+
self._org = org
|
|
58
|
+
self._project = project
|
|
59
|
+
self._token = token or os.getenv("SENTRY_AUTH_TOKEN")
|
|
60
|
+
self._cache_dir = cache_dir
|
|
61
|
+
self._requests_made = 0
|
|
62
|
+
|
|
63
|
+
if issues is None:
|
|
64
|
+
if _requests is None:
|
|
65
|
+
raise RuntimeError("requests library required. pip install requests")
|
|
66
|
+
if not org or not project:
|
|
67
|
+
raise RuntimeError(
|
|
68
|
+
"Provide org and project, or issues for fixture mode."
|
|
69
|
+
)
|
|
70
|
+
if not self._token:
|
|
71
|
+
raise RuntimeError(
|
|
72
|
+
"Sentry auth token required. Set SENTRY_AUTH_TOKEN env var or pass token=."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def _headers(self) -> dict:
|
|
76
|
+
return {
|
|
77
|
+
"Authorization": f"Bearer {self._token}",
|
|
78
|
+
"Accept": "application/json",
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
def _cache_key(self, url: str, params: dict = None) -> str:
|
|
82
|
+
raw = f"{url}:{json.dumps(params or {}, sort_keys=True)}"
|
|
83
|
+
return hashlib.sha256(raw.encode()).hexdigest()[:16]
|
|
84
|
+
|
|
85
|
+
def _get(self, url: str, params: dict = None) -> tuple:
|
|
86
|
+
"""GET with caching. Returns (json_data, response_headers)."""
|
|
87
|
+
if self._cache_dir:
|
|
88
|
+
key = self._cache_key(url, params)
|
|
89
|
+
cache_file = self._cache_dir / f"{key}.json"
|
|
90
|
+
if cache_file.exists():
|
|
91
|
+
return json.loads(cache_file.read_text(encoding="utf-8")), {}
|
|
92
|
+
|
|
93
|
+
resp = _requests.get(url, headers=self._headers(),
|
|
94
|
+
params=params, timeout=30)
|
|
95
|
+
self._requests_made += 1
|
|
96
|
+
resp.raise_for_status()
|
|
97
|
+
data = resp.json()
|
|
98
|
+
|
|
99
|
+
if self._cache_dir:
|
|
100
|
+
self._cache_dir.mkdir(parents=True, exist_ok=True)
|
|
101
|
+
cache_file = self._cache_dir / f"{key}.json"
|
|
102
|
+
cache_file.write_text(json.dumps(data, indent=2), encoding="utf-8")
|
|
103
|
+
|
|
104
|
+
return data, resp.headers
|
|
105
|
+
|
|
106
|
+
def _normalize_level(self, level: str) -> str:
|
|
107
|
+
"""Normalize Sentry issue level."""
|
|
108
|
+
level_map = {
|
|
109
|
+
"fatal": "fatal",
|
|
110
|
+
"error": "error",
|
|
111
|
+
"warning": "warning",
|
|
112
|
+
"info": "info",
|
|
113
|
+
"debug": "debug",
|
|
114
|
+
"sample": "info",
|
|
115
|
+
}
|
|
116
|
+
return level_map.get(level, "error") if level else "error"
|
|
117
|
+
|
|
118
|
+
def _normalize_status(self, status: str) -> str:
|
|
119
|
+
"""Normalize Sentry issue status."""
|
|
120
|
+
status_map = {
|
|
121
|
+
"unresolved": "unresolved",
|
|
122
|
+
"resolved": "resolved",
|
|
123
|
+
"ignored": "ignored",
|
|
124
|
+
"muted": "ignored",
|
|
125
|
+
"resolvedInNextRelease": "resolved",
|
|
126
|
+
}
|
|
127
|
+
return status_map.get(status, "unresolved") if status else "unresolved"
|
|
128
|
+
|
|
129
|
+
def _fetch_issues(self) -> list:
|
|
130
|
+
"""Fetch issues from Sentry API."""
|
|
131
|
+
url = f"{self._api_base}/projects/{self._org}/{self._project}/issues/"
|
|
132
|
+
all_issues = []
|
|
133
|
+
params = {
|
|
134
|
+
"query": "is:unresolved",
|
|
135
|
+
"statsPeriod": "90d",
|
|
136
|
+
"sort": "date",
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
while len(all_issues) < self.max_issues:
|
|
140
|
+
data, headers = self._get(url, params=params)
|
|
141
|
+
|
|
142
|
+
if not isinstance(data, list) or not data:
|
|
143
|
+
break
|
|
144
|
+
|
|
145
|
+
all_issues.extend(data)
|
|
146
|
+
|
|
147
|
+
# Cursor-based pagination via Link header
|
|
148
|
+
link = headers.get("Link", "")
|
|
149
|
+
next_url = self._parse_next_link(link)
|
|
150
|
+
if not next_url:
|
|
151
|
+
break
|
|
152
|
+
url = next_url
|
|
153
|
+
params = {} # URL already contains params
|
|
154
|
+
|
|
155
|
+
return all_issues[:self.max_issues]
|
|
156
|
+
|
|
157
|
+
def _parse_next_link(self, link_header: str) -> Optional[str]:
|
|
158
|
+
"""Parse Sentry's Link header for the next page URL.
|
|
159
|
+
|
|
160
|
+
Validates that the next URL has the same scheme and host as
|
|
161
|
+
self._api_base to prevent open-redirect / SSRF attacks.
|
|
162
|
+
"""
|
|
163
|
+
from urllib.parse import urlparse
|
|
164
|
+
|
|
165
|
+
if not link_header:
|
|
166
|
+
return None
|
|
167
|
+
for part in link_header.split(","):
|
|
168
|
+
part = part.strip()
|
|
169
|
+
if 'rel="next"' in part and 'results="true"' in part:
|
|
170
|
+
url = part.split(";")[0].strip().strip("<>")
|
|
171
|
+
# Validate scheme + host match self._api_base
|
|
172
|
+
parsed_base = urlparse(self._api_base)
|
|
173
|
+
parsed_next = urlparse(url)
|
|
174
|
+
if (parsed_next.scheme != parsed_base.scheme or
|
|
175
|
+
parsed_next.hostname != parsed_base.hostname):
|
|
176
|
+
return None
|
|
177
|
+
return url
|
|
178
|
+
return None
|
|
179
|
+
|
|
180
|
+
def iter_events(self):
|
|
181
|
+
if self._fixture_issues is not None:
|
|
182
|
+
issues = self._fixture_issues
|
|
183
|
+
else:
|
|
184
|
+
issues = self._fetch_issues()
|
|
185
|
+
|
|
186
|
+
# Sort by firstSeen for temporal ordering
|
|
187
|
+
issues.sort(key=lambda i: i.get("firstSeen", ""))
|
|
188
|
+
|
|
189
|
+
for issue in issues:
|
|
190
|
+
yield self._issue_to_event(issue)
|
|
191
|
+
|
|
192
|
+
def _issue_to_event(self, issue: dict) -> dict:
|
|
193
|
+
"""Convert a Sentry issue dict to a SourceEvent."""
|
|
194
|
+
issue_id = str(issue.get("id", ""))
|
|
195
|
+
first_seen = issue.get("firstSeen", "")
|
|
196
|
+
last_seen = issue.get("lastSeen", "")
|
|
197
|
+
level = self._normalize_level(issue.get("level", "error"))
|
|
198
|
+
status = self._normalize_status(issue.get("status", "unresolved"))
|
|
199
|
+
is_unhandled = bool(issue.get("isUnhandled", False))
|
|
200
|
+
title = issue.get("title", "")
|
|
201
|
+
count = int(issue.get("count", 0))
|
|
202
|
+
user_count = int(issue.get("userCount", 0))
|
|
203
|
+
|
|
204
|
+
# Extract release from metadata if available
|
|
205
|
+
release = ""
|
|
206
|
+
metadata = issue.get("metadata", {})
|
|
207
|
+
if isinstance(metadata, dict):
|
|
208
|
+
release = metadata.get("release", "")
|
|
209
|
+
|
|
210
|
+
payload = {
|
|
211
|
+
"issue_id": issue_id,
|
|
212
|
+
"title": title,
|
|
213
|
+
"level": level,
|
|
214
|
+
"status": status,
|
|
215
|
+
"is_unhandled": is_unhandled,
|
|
216
|
+
"trigger": {
|
|
217
|
+
"type": "error",
|
|
218
|
+
"commit_sha": "",
|
|
219
|
+
"release": release,
|
|
220
|
+
},
|
|
221
|
+
"timing": {
|
|
222
|
+
"first_seen": first_seen,
|
|
223
|
+
"last_seen": last_seen,
|
|
224
|
+
},
|
|
225
|
+
"stats": {
|
|
226
|
+
"event_count": count,
|
|
227
|
+
"user_count": user_count,
|
|
228
|
+
},
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
"source_family": self.source_family,
|
|
233
|
+
"source_type": self.source_type,
|
|
234
|
+
"source_id": self.source_id,
|
|
235
|
+
"ordering_mode": self.ordering_mode,
|
|
236
|
+
"attestation": {
|
|
237
|
+
"type": "error_issue",
|
|
238
|
+
"issue_id": issue_id,
|
|
239
|
+
"trust_tier": self.attestation_tier,
|
|
240
|
+
},
|
|
241
|
+
"predecessor_refs": None,
|
|
242
|
+
"payload": payload,
|
|
243
|
+
}
|
|
@@ -128,12 +128,29 @@ def get_agent(
|
|
|
128
128
|
key = api_key or os.environ.get("ANTHROPIC_API_KEY", "")
|
|
129
129
|
return AnthropicAgent(api_key=key, model=model)
|
|
130
130
|
except ImportError:
|
|
131
|
-
|
|
131
|
+
if prefer == "anthropic":
|
|
132
|
+
import sys
|
|
133
|
+
print(
|
|
134
|
+
"Warning: 'anthropic' package not installed. "
|
|
135
|
+
"Install it with: pip install anthropic\n"
|
|
136
|
+
"Falling back to prompt display mode.",
|
|
137
|
+
file=sys.stderr,
|
|
138
|
+
)
|
|
132
139
|
|
|
133
140
|
if prefer == "cli" or (prefer is None and _has_cli(cli_command)):
|
|
134
141
|
from evolution.agents.cli_agent import CliAgent
|
|
135
142
|
return CliAgent(command=cli_command or "claude", model=model)
|
|
136
143
|
|
|
144
|
+
if prefer is None and _has_anthropic_key(api_key):
|
|
145
|
+
# API key is set but anthropic package is missing — warn the user
|
|
146
|
+
import sys
|
|
147
|
+
print(
|
|
148
|
+
"Note: ANTHROPIC_API_KEY is set but the 'anthropic' package is not installed.\n"
|
|
149
|
+
"Install it with: pip install anthropic\n"
|
|
150
|
+
"Using prompt display mode instead.",
|
|
151
|
+
file=sys.stderr,
|
|
152
|
+
)
|
|
153
|
+
|
|
137
154
|
return ShowPromptAgent()
|
|
138
155
|
|
|
139
156
|
|