aofire-python-agent 0.1.0__py3-none-any.whl
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.
- aofire_python_agent-0.1.0.dist-info/METADATA +405 -0
- aofire_python_agent-0.1.0.dist-info/RECORD +23 -0
- aofire_python_agent-0.1.0.dist-info/WHEEL +5 -0
- aofire_python_agent-0.1.0.dist-info/entry_points.txt +7 -0
- aofire_python_agent-0.1.0.dist-info/licenses/LICENSE +28 -0
- aofire_python_agent-0.1.0.dist-info/top_level.txt +1 -0
- python_agent/CLAUDE.md +105 -0
- python_agent/__init__.py +3 -0
- python_agent/agent_utils.py +61 -0
- python_agent/call_graph.py +694 -0
- python_agent/coding_agent.py +193 -0
- python_agent/convergence_agent.py +362 -0
- python_agent/dag_integrity.py +198 -0
- python_agent/dag_utils.py +181 -0
- python_agent/discovery_agent.py +348 -0
- python_agent/divergence_agent.py +302 -0
- python_agent/ontology.py +270 -0
- python_agent/planning_agent.py +83 -0
- python_agent/py.typed +0 -0
- python_agent/rules.py +383 -0
- python_agent/tool_guard.py +164 -0
- python_agent/tools/__init__.py +0 -0
- python_agent/types.py +38 -0
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: aofire-python-agent
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Claude-powered Python coding and planning agents with enforced quality standards
|
|
5
|
+
Author-email: Ed Hodapp <ed@hodapp.com>
|
|
6
|
+
License-Expression: BSD-3-Clause
|
|
7
|
+
Project-URL: Homepage, https://github.com/edhodapp/python-agent
|
|
8
|
+
Project-URL: Repository, https://github.com/edhodapp/python-agent
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Requires-Dist: claude-agent-sdk
|
|
13
|
+
Requires-Dist: pydantic>=2.12
|
|
14
|
+
Provides-Extra: dev
|
|
15
|
+
Requires-Dist: flake8-pyproject; extra == "dev"
|
|
16
|
+
Requires-Dist: hypothesis; extra == "dev"
|
|
17
|
+
Requires-Dist: mutmut<3; extra == "dev"
|
|
18
|
+
Requires-Dist: mypy; extra == "dev"
|
|
19
|
+
Requires-Dist: pydantic>=2.12; extra == "dev"
|
|
20
|
+
Requires-Dist: pytest; extra == "dev"
|
|
21
|
+
Requires-Dist: pytest-asyncio; extra == "dev"
|
|
22
|
+
Requires-Dist: pytest-cov; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-mock; extra == "dev"
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# aofire-python-agent
|
|
27
|
+
|
|
28
|
+
[](https://github.com/edhodapp/python-agent/actions/workflows/lint.yml)
|
|
29
|
+
[](https://github.com/edhodapp/python-agent/actions/workflows/typecheck.yml)
|
|
30
|
+
[](https://github.com/edhodapp/python-agent/actions/workflows/test.yml)
|
|
31
|
+
[](https://github.com/edhodapp/python-agent/actions/workflows/taint.yml)
|
|
32
|
+
[](https://github.com/edhodapp/python-agent/actions/workflows/fuzz.yml)
|
|
33
|
+
[](https://github.com/edhodapp/python-agent/actions/workflows/mutation.yml)
|
|
34
|
+
|
|
35
|
+
Claude-powered Python agents with ontology-driven project planning,
|
|
36
|
+
autonomous code generation, and defense-in-depth security hardening.
|
|
37
|
+
|
|
38
|
+
The pipeline takes a project from idea to production code through
|
|
39
|
+
structured ontology exploration, branching solution candidates, and
|
|
40
|
+
an autonomous coding agent that enforces 10 quality gates before
|
|
41
|
+
every commit.
|
|
42
|
+
|
|
43
|
+
**BSD 3-Clause.** Python 3.11+. Requires the
|
|
44
|
+
[Claude Agent SDK](https://github.com/anthropics/claude-agent-sdk-python).
|
|
45
|
+
|
|
46
|
+
## What This Is
|
|
47
|
+
|
|
48
|
+
A monorepo containing six CLI tools and a shared ontology framework:
|
|
49
|
+
|
|
50
|
+
| Tool | Mode | Purpose |
|
|
51
|
+
|------|------|---------|
|
|
52
|
+
| `aofire-discovery-agent` | Interactive | Build a domain ontology through conversation |
|
|
53
|
+
| `aofire-divergence-agent` | Autonomous | Generate N candidate solution architectures |
|
|
54
|
+
| `aofire-convergence-agent` | Interactive | Compare, select, and refine candidates |
|
|
55
|
+
| `aofire-coding-agent` | Autonomous | Write production-quality code with Sonnet/Opus escalation |
|
|
56
|
+
| `aofire-planning-agent` | Interactive | Freeform project design (no ontology) |
|
|
57
|
+
| `aofire-call-graph` | Analysis | Source-to-sink taint analysis with CWE tagging |
|
|
58
|
+
|
|
59
|
+
Plus shared infrastructure:
|
|
60
|
+
|
|
61
|
+
| Module | Purpose |
|
|
62
|
+
|--------|---------|
|
|
63
|
+
| `ontology.py` | 16 Pydantic models: entities, relationships, modules, DAG |
|
|
64
|
+
| `types.py` | `Annotated` + `Literal` shared type definitions |
|
|
65
|
+
| `dag_utils.py` | DAG persistence with HMAC integrity signing |
|
|
66
|
+
| `dag_integrity.py` | HMAC verification + injection pattern scanning |
|
|
67
|
+
| `tool_guard.py` | `can_use_tool` callback: Bash blocklist + path confinement |
|
|
68
|
+
| `rules.py` | System prompts with `frame_data()` content framing |
|
|
69
|
+
|
|
70
|
+
## Install
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip install aofire-python-agent
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
For development (includes test/analysis tools):
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/edhodapp/python-agent.git
|
|
80
|
+
cd python-agent
|
|
81
|
+
python3 -m venv .venv
|
|
82
|
+
.venv/bin/pip install -e ".[dev]"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## The Ontology Pipeline
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
aofire-discovery-agent --> aofire-divergence-agent --> aofire-convergence-agent --> aofire-coding-agent
|
|
89
|
+
(interactive) (autonomous) (interactive) (autonomous)
|
|
90
|
+
Build domain Generate N Compare, select, Write code to
|
|
91
|
+
ontology solution candidates accept, refine production standards
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
All state is saved to an **ontology DAG** (a JSON file). Each node is a
|
|
95
|
+
complete ontology snapshot. Each edge records a design decision. You can
|
|
96
|
+
backtrack to any prior state and explore a different path.
|
|
97
|
+
|
|
98
|
+
### Step 1: Discovery
|
|
99
|
+
|
|
100
|
+
Build a domain ontology interactively. The agent asks questions about
|
|
101
|
+
your domain and constructs entities, relationships, and constraints.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
aofire-discovery-agent "A URL shortener service" --dag-file shortener.json
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Example session:
|
|
108
|
+
|
|
109
|
+
```
|
|
110
|
+
Planner: I'll help you design a URL shortener. Who are the users?
|
|
111
|
+
|
|
112
|
+
> Anyone can follow a link. Registered users create short URLs.
|
|
113
|
+
|
|
114
|
+
[Agent proposes entities: User, ShortURL, relationship: User owns ShortURL]
|
|
115
|
+
|
|
116
|
+
> show
|
|
117
|
+
Entities (2):
|
|
118
|
+
user: User [username, api_key]
|
|
119
|
+
short_url: ShortURL [slug, target_url, click_count]
|
|
120
|
+
Relationships (1):
|
|
121
|
+
user --owns--> short_url (one_to_many)
|
|
122
|
+
Open Questions (2):
|
|
123
|
+
[open] q1: Storage backend?
|
|
124
|
+
[open] q2: Slug format?
|
|
125
|
+
|
|
126
|
+
> save initial domain model
|
|
127
|
+
Saved snapshot: 20260401T120000...
|
|
128
|
+
|
|
129
|
+
> quit
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Commands: `show`, `save [label]`, `back`, `quit`/`exit`/`done`
|
|
133
|
+
|
|
134
|
+
Options:
|
|
135
|
+
- `--dag-file PATH` -- DAG JSON file (default: `ontology.json`)
|
|
136
|
+
- `-m MODEL` -- model (default: `claude-opus-4-6`)
|
|
137
|
+
|
|
138
|
+
### Step 2: Divergence
|
|
139
|
+
|
|
140
|
+
Autonomously generate multiple solution candidates. The agent identifies
|
|
141
|
+
key architectural decision points, then generates one complete solution
|
|
142
|
+
per strategy.
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
aofire-divergence-agent --dag-file shortener.json -n 3
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
Identifying 3 strategies...
|
|
150
|
+
Generating candidate: monolith-sqlite...
|
|
151
|
+
Created: monolith-sqlite
|
|
152
|
+
Generating candidate: microservices-postgres...
|
|
153
|
+
Created: microservices-postgres
|
|
154
|
+
Generating candidate: serverless-dynamo...
|
|
155
|
+
Created: serverless-dynamo
|
|
156
|
+
|
|
157
|
+
Done. 3 candidates. Cost: $0.1234
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Each candidate fills in the solution domain: modules, classes, functions,
|
|
161
|
+
data models, external dependencies, and test strategies. The DAG now has
|
|
162
|
+
three branching children.
|
|
163
|
+
|
|
164
|
+
Options:
|
|
165
|
+
- `--dag-file PATH` -- DAG JSON file (required)
|
|
166
|
+
- `-n N` -- number of candidates (default: 3)
|
|
167
|
+
- `-m MODEL` -- model (default: `claude-sonnet-4-6`)
|
|
168
|
+
- `--max-budget USD` -- spending cap (default: 5.0)
|
|
169
|
+
|
|
170
|
+
### Step 3: Convergence
|
|
171
|
+
|
|
172
|
+
Compare candidates, select one, and refine it interactively. The LLM
|
|
173
|
+
has context of all candidates and assists with comparisons.
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
aofire-convergence-agent --dag-file shortener.json
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
```
|
|
180
|
+
> list
|
|
181
|
+
1. monolith-sqlite: Entities (2), Modules (4)...
|
|
182
|
+
2. microservices-postgres: Entities (2), Modules (6)...
|
|
183
|
+
3. serverless-dynamo: Entities (2), Modules (5)...
|
|
184
|
+
|
|
185
|
+
> compare monolith-sqlite and microservices-postgres on complexity
|
|
186
|
+
[LLM explains trade-offs between the two approaches]
|
|
187
|
+
|
|
188
|
+
> select 1
|
|
189
|
+
Selected: monolith-sqlite
|
|
190
|
+
|
|
191
|
+
> show
|
|
192
|
+
[Full ontology: entities, relationships, modules with classes/functions]
|
|
193
|
+
|
|
194
|
+
> accept
|
|
195
|
+
Accepted: monolith-sqlite. You can now refine.
|
|
196
|
+
|
|
197
|
+
> Add rate limiting to the API module
|
|
198
|
+
[LLM proposes ontology update with new RateLimiter class]
|
|
199
|
+
|
|
200
|
+
> save final design
|
|
201
|
+
> quit
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Commands: `list`, `select <n>`, `back`, `show`, `accept`, `save [label]`.
|
|
205
|
+
Any other text goes to the LLM (e.g., "compare", "explain", "refine").
|
|
206
|
+
|
|
207
|
+
Options:
|
|
208
|
+
- `--dag-file PATH` -- DAG JSON file (required)
|
|
209
|
+
- `-m MODEL` -- model (default: `claude-opus-4-6`)
|
|
210
|
+
|
|
211
|
+
### Step 4: Coding
|
|
212
|
+
|
|
213
|
+
The coding agent writes code, runs all quality checks, and iterates until
|
|
214
|
+
everything passes. Starts with Sonnet for cost efficiency; automatically
|
|
215
|
+
escalates to Opus if it gets stuck.
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
aofire-coding-agent "Implement the URL shortener from the accepted design" -d ./shortener --dag-file shortener.json
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
Pass `--dag-file` to give the coding agent the ontology as structured design
|
|
222
|
+
context. The agent sees entities, module specs, function signatures, and test
|
|
223
|
+
strategies from the accepted design.
|
|
224
|
+
|
|
225
|
+
The agent's workflow (11 steps):
|
|
226
|
+
1. Read existing code
|
|
227
|
+
2. Write/modify code
|
|
228
|
+
3. flake8 (complexity <= 5)
|
|
229
|
+
4. mypy --strict
|
|
230
|
+
5. pytest (100% branch coverage)
|
|
231
|
+
6. Iterate on failures
|
|
232
|
+
7. mutmut (100% kill rate)
|
|
233
|
+
8. Fuzz tests for external-input functions
|
|
234
|
+
9. aofire-call-graph taint analysis
|
|
235
|
+
10. Functional test gap analysis
|
|
236
|
+
11. Commit
|
|
237
|
+
|
|
238
|
+
If the agent can't fix an issue (e.g., needs a `# type: ignore`), it
|
|
239
|
+
presents grouped findings for user approval rather than silently
|
|
240
|
+
suppressing.
|
|
241
|
+
|
|
242
|
+
Options:
|
|
243
|
+
- `-d DIR` -- project directory (default: `.`)
|
|
244
|
+
- `-m MODEL` -- initial model (default: `claude-sonnet-4-6`)
|
|
245
|
+
- `--max-turns N` -- step limit (default: 30)
|
|
246
|
+
- `--max-budget USD` -- spending cap (default: 5.0)
|
|
247
|
+
- `--dag-file PATH` -- ontology DAG JSON file for design context (optional)
|
|
248
|
+
|
|
249
|
+
### Backtracking
|
|
250
|
+
|
|
251
|
+
Re-run convergence on the same DAG to navigate back and try a different
|
|
252
|
+
branch. All intermediate states are preserved:
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
aofire-convergence-agent --dag-file shortener.json
|
|
256
|
+
> back
|
|
257
|
+
> select 2
|
|
258
|
+
> accept
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Standalone Planning Agent
|
|
262
|
+
|
|
263
|
+
For freeform project design without the ontology pipeline:
|
|
264
|
+
|
|
265
|
+
```bash
|
|
266
|
+
aofire-planning-agent "A CLI tool that converts CSV to JSON with schema validation"
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Uses Opus by default. Produces a structured markdown plan. Type `quit` to end.
|
|
270
|
+
|
|
271
|
+
## Static Analysis: aofire-call-graph
|
|
272
|
+
|
|
273
|
+
Source-to-sink taint analysis using Python's `ast` module. Traces data
|
|
274
|
+
flow from external inputs through the call graph to dangerous sinks.
|
|
275
|
+
Each finding tagged with a CWE code.
|
|
276
|
+
|
|
277
|
+
```bash
|
|
278
|
+
aofire-call-graph src/python_agent/ # text report
|
|
279
|
+
aofire-call-graph src/python_agent/ --sarif # SARIF JSON for CI
|
|
280
|
+
aofire-call-graph src/python_agent/ --include-sanitized # show all paths
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Sources detected: `input()`, `json.loads`, `open()`, `.model_validate()`,
|
|
284
|
+
`.parse_args()`, `.query()` (SDK responses).
|
|
285
|
+
|
|
286
|
+
Sinks detected: `eval`/`exec`, `subprocess`/`os.system`, `.write()`,
|
|
287
|
+
`.query()` (prompt injection), `print()` (info exposure).
|
|
288
|
+
|
|
289
|
+
Suppress acknowledged findings with mandatory comments:
|
|
290
|
+
```python
|
|
291
|
+
# taint: ignore[CWE-200] -- Interactive agent displays LLM output to user
|
|
292
|
+
async def run(description: str, model: str) -> None:
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Security Hardening
|
|
296
|
+
|
|
297
|
+
Defense-in-depth across all agents:
|
|
298
|
+
|
|
299
|
+
| Layer | Defense | Protects Against |
|
|
300
|
+
|-------|---------|-----------------|
|
|
301
|
+
| 1 | `frame_data()` content framing | Prompt injection via embedded data |
|
|
302
|
+
| 2 | HMAC-SHA256 DAG integrity | File tampering between sessions |
|
|
303
|
+
| 3 | Pydantic `BaseModel` validation | Malformed data at construction |
|
|
304
|
+
| 4 | `can_use_tool` callback (tool guard) | Dangerous Bash commands + path escape |
|
|
305
|
+
| 5 | Injection pattern scanner | Common injection phrases in text fields |
|
|
306
|
+
| 6 | Framing escape detection | `</ontology-data>` breakout attempts |
|
|
307
|
+
| 7 | Call graph taint analysis | Unguarded source-to-sink data flows |
|
|
308
|
+
| 8 | Taint suppressions with mandatory comments | Acknowledged risks with audit trail |
|
|
309
|
+
| 9 | User approval workflow | Silent suppression by autonomous agent |
|
|
310
|
+
|
|
311
|
+
The coding agent's tool guard blocks: `curl`, `wget`, `ssh`, `sudo`,
|
|
312
|
+
`rm -rf /`, `dd`, `mkfs`, `chmod 777`, `chown`, `pkill`, writes to
|
|
313
|
+
`/etc`, `~/.ssh`, `~/.bashrc`. File operations confined to project
|
|
314
|
+
directory.
|
|
315
|
+
|
|
316
|
+
## The Ontology Format
|
|
317
|
+
|
|
318
|
+
16 Pydantic models capturing both problem and solution domains:
|
|
319
|
+
|
|
320
|
+
**Problem domain:** Entity, Property, PropertyType, Relationship,
|
|
321
|
+
DomainConstraint
|
|
322
|
+
|
|
323
|
+
**Solution domain:** ModuleSpec, ClassSpec, FunctionSpec, DataModel,
|
|
324
|
+
ExternalDependency
|
|
325
|
+
|
|
326
|
+
**Planning state:** OpenQuestion (unresolved decisions)
|
|
327
|
+
|
|
328
|
+
**DAG:** DAGNode (ontology snapshots), DAGEdge (design decisions),
|
|
329
|
+
OntologyDAG (the full versioned graph)
|
|
330
|
+
|
|
331
|
+
Type constraints enforced via `Annotated` types: `SafeId` (alphanumeric,
|
|
332
|
+
max 100 chars), `ShortName` (max 100 chars), `Description` (max 2000
|
|
333
|
+
chars). Enum fields use `Literal` types: `PropertyKind`, `Cardinality`,
|
|
334
|
+
`ModuleStatus`, `Priority`.
|
|
335
|
+
|
|
336
|
+
## Quality Standards
|
|
337
|
+
|
|
338
|
+
All code produced by these agents (and the agents themselves) meets:
|
|
339
|
+
|
|
340
|
+
1. **flake8 clean** with `--max-complexity=5`
|
|
341
|
+
2. **mypy --strict** with zero errors
|
|
342
|
+
3. **100% branch coverage** via pytest (570 tests)
|
|
343
|
+
4. **100% mutant kill rate** via mutmut v2
|
|
344
|
+
5. **Fuzz testing** via hypothesis on all external-input functions
|
|
345
|
+
6. **Call graph taint analysis** with CWE tagging
|
|
346
|
+
7. **Functional test gap analysis** as final verification step
|
|
347
|
+
8. **Prompt injection hardening** across all agents
|
|
348
|
+
|
|
349
|
+
See `CLAUDE.md` for the complete coding standards.
|
|
350
|
+
|
|
351
|
+
## Project Status
|
|
352
|
+
|
|
353
|
+
**Version:** 0.1.0
|
|
354
|
+
|
|
355
|
+
**What works:**
|
|
356
|
+
- Full ontology pipeline: discovery, divergence, convergence
|
|
357
|
+
- Autonomous coding agent with Sonnet/Opus escalation
|
|
358
|
+
- All security hardening layers active
|
|
359
|
+
- 570 tests, 14 source files, all quality gates pass
|
|
360
|
+
- aofire-call-graph reports clean (no unguarded taint paths)
|
|
361
|
+
|
|
362
|
+
**What's next:**
|
|
363
|
+
- Wire coding agent to consume ontology nodes as implementation specs
|
|
364
|
+
- End-to-end pipeline test (idea to running code)
|
|
365
|
+
- CI setup (GitHub Actions)
|
|
366
|
+
- Performance requirements as soft ontology constraints (advisory, not blocking)
|
|
367
|
+
- Tier 2 hardening: coding agent command audit log by default
|
|
368
|
+
|
|
369
|
+
## Development
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
python3 -m venv .venv
|
|
373
|
+
.venv/bin/pip install -e ".[dev]"
|
|
374
|
+
|
|
375
|
+
# Full quality gate
|
|
376
|
+
.venv/bin/flake8 --max-complexity=5 src/ tests/
|
|
377
|
+
.venv/bin/mypy --strict src/
|
|
378
|
+
.venv/bin/pytest --cov --cov-branch --cov-report=term-missing
|
|
379
|
+
.venv/bin/mutmut run
|
|
380
|
+
.venv/bin/pytest tests/test_fuzz.py --hypothesis-profile=ci
|
|
381
|
+
.venv/bin/aofire-call-graph src/python_agent/
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
## Local PyPI with devpi
|
|
385
|
+
|
|
386
|
+
```bash
|
|
387
|
+
# Setup (first time)
|
|
388
|
+
pip install devpi-server devpi-client
|
|
389
|
+
devpi-server --init && devpi-server --start --port 3141
|
|
390
|
+
devpi use http://localhost:3141
|
|
391
|
+
devpi user -c myuser password=
|
|
392
|
+
devpi login myuser
|
|
393
|
+
devpi index -c dev bases=root/pypi
|
|
394
|
+
devpi use myuser/dev
|
|
395
|
+
|
|
396
|
+
# Publish
|
|
397
|
+
devpi upload
|
|
398
|
+
|
|
399
|
+
# Install from local index
|
|
400
|
+
pip install aofire-python-agent -i http://localhost:3141/myuser/dev/+simple/
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
## License
|
|
404
|
+
|
|
405
|
+
BSD 3-Clause. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
aofire_python_agent-0.1.0.dist-info/licenses/LICENSE,sha256=fOlGyPlQVBNVcSNHKxnZMgHRdWd2yOP430P3qJa-Nh8,1496
|
|
2
|
+
python_agent/CLAUDE.md,sha256=Ysi_gKNMWsQoLwSy2OUBtLwrkXOnznkCFBkRHF3KqYI,7386
|
|
3
|
+
python_agent/__init__.py,sha256=nyQD_E7D_F5fDIBMOMrmLgqXCQInZwf-sl-iq3m2zeo,79
|
|
4
|
+
python_agent/agent_utils.py,sha256=32N8Lq_7DTC7VOBZ48L230TocL0gj7m-FPNWhYLgYNM,1530
|
|
5
|
+
python_agent/call_graph.py,sha256=FxiZ1PKUFfratne_nC7b3nCm1cKAc2pelP6LyLlKLdk,19660
|
|
6
|
+
python_agent/coding_agent.py,sha256=quDblkKDPO9U-MedcUSKtnUxIU43NX7t7gCwgLwmPuM,5310
|
|
7
|
+
python_agent/convergence_agent.py,sha256=d9h4qqAGnU9ttrMK9aw5IgDc0sVyW-JQe6A_SaS6ihE,9675
|
|
8
|
+
python_agent/dag_integrity.py,sha256=LiGaigeH9cvy4jvUehZfWHxFUktY4fTvUg1VvqeicCI,5681
|
|
9
|
+
python_agent/dag_utils.py,sha256=u_qoqii4GYbnOHMSFBOVNN28elOsRgkffH0pFQxxCO8,4596
|
|
10
|
+
python_agent/discovery_agent.py,sha256=K5FE22meadAOVmpu3_K14Cn3PiCFHJYH42qmcvJIvaQ,9495
|
|
11
|
+
python_agent/divergence_agent.py,sha256=O_r4Qwaa_CtmTgHqqvwXQhNNxmkOwy8cgx5PfmSidMo,8067
|
|
12
|
+
python_agent/ontology.py,sha256=1ltUnkdvO9MJBV19KPMwqNOTUcDmgKPo8EjlgVpFnLY,6012
|
|
13
|
+
python_agent/planning_agent.py,sha256=d4ns9e7cSWE0Kmou-9kLNQPE6ugx9OtNA85m6xlnJH8,2275
|
|
14
|
+
python_agent/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
python_agent/rules.py,sha256=BEFXdVtjVTvpvnKR-B1WwAYKHrApkfynmv967Mrm3IM,13003
|
|
16
|
+
python_agent/tool_guard.py,sha256=75s_IJ6VAZVRX4GqZ45amAqyss7SbkKTrSeVCsdFzyM,3967
|
|
17
|
+
python_agent/types.py,sha256=0znWiWxZPAmXvVL3vrBLsmmzpyq3JnMR8ff3dYMaxkg,775
|
|
18
|
+
python_agent/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
19
|
+
aofire_python_agent-0.1.0.dist-info/METADATA,sha256=Ka6Ay_o_vQsksD8yGMRHmPs4_18xvnAHNpZTx09Gt64,13887
|
|
20
|
+
aofire_python_agent-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
21
|
+
aofire_python_agent-0.1.0.dist-info/entry_points.txt,sha256=m3NnzPLKLTxQemjfBP5MgH6buLmhC6ZL4wl8jB7tWnc,360
|
|
22
|
+
aofire_python_agent-0.1.0.dist-info/top_level.txt,sha256=Vevsacnx_Xm_bXJtxyGqCbQU69PbzXHe0F1t0ynfgDc,13
|
|
23
|
+
aofire_python_agent-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
[console_scripts]
|
|
2
|
+
aofire-call-graph = python_agent.call_graph:main
|
|
3
|
+
aofire-coding-agent = python_agent.coding_agent:main
|
|
4
|
+
aofire-convergence-agent = python_agent.convergence_agent:main
|
|
5
|
+
aofire-discovery-agent = python_agent.discovery_agent:main
|
|
6
|
+
aofire-divergence-agent = python_agent.divergence_agent:main
|
|
7
|
+
aofire-planning-agent = python_agent.planning_agent:main
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, Ed Hodapp
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
python_agent
|
python_agent/CLAUDE.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Python Agent — Project Instructions
|
|
2
|
+
|
|
3
|
+
## Python Standards
|
|
4
|
+
|
|
5
|
+
All Python code must meet these standards before commit:
|
|
6
|
+
|
|
7
|
+
1. **flake8 clean** — zero warnings, default rules
|
|
8
|
+
2. **McCabe Cyclomatic Complexity <= 5** — `flake8 --max-complexity=5`
|
|
9
|
+
3. **100% branch coverage** — `pytest --cov --cov-branch --cov-report=term-missing`
|
|
10
|
+
4. **pytest** for all tests, **pytest-mock** for mocking
|
|
11
|
+
5. **pyflakes clean** (included in flake8)
|
|
12
|
+
6. **Mutation testing with mutmut** — 100% kill rate required
|
|
13
|
+
- Use mutmut v2 (`pip install 'mutmut<3'`). v3's test mapping doesn't work with non-standard layouts.
|
|
14
|
+
- Configure in `pyproject.toml`: `paths_to_mutate`, `runner` (must use `.venv/bin/python`).
|
|
15
|
+
- The only acceptable survivors are the `if __name__` guard. Everything else must be killed — no exceptions for "cosmetic" code like help strings or log messages.
|
|
16
|
+
- Surviving mutants = test gaps. Write targeted tests to kill them.
|
|
17
|
+
- **mutmut wraps mutated strings in `XX...XX`.** To kill string mutants, assert `"XX" not in output` rather than only checking for substrings (which still match inside the wrapped version).
|
|
18
|
+
7. **Fuzz testing with hypothesis** — all functions that accept external inputs
|
|
19
|
+
- External inputs: user CLI args, SDK messages/results, keyboard input, filesystem paths.
|
|
20
|
+
- Every such function must have a `@given(...)` test verifying:
|
|
21
|
+
a. **No unhandled exceptions** — any valid-typed input must not crash.
|
|
22
|
+
b. **Return type contract** — return type matches the function's contract.
|
|
23
|
+
c. **Invariants** — domain-specific rules (e.g., `remaining_budget <= max_budget`).
|
|
24
|
+
- Hypothesis profiles in `tests/conftest.py`: `ci` = 200 examples, `dev` = 50.
|
|
25
|
+
- Fuzz tests go in `tests/test_fuzz.py`, separate from example-based tests.
|
|
26
|
+
- Use `io.StringIO` for stdout capture in fuzz tests (hypothesis doesn't support pytest's `capsys` fixture).
|
|
27
|
+
- Run: `.venv/bin/pytest tests/test_fuzz.py` or with profile: `--hypothesis-profile=ci`.
|
|
28
|
+
8. **mypy --strict** — zero errors
|
|
29
|
+
- Run: `.venv/bin/mypy --strict src/`
|
|
30
|
+
- All function signatures must have type annotations (parameters and return types).
|
|
31
|
+
- Use `Annotated` types from `python_agent.types` for constrained strings.
|
|
32
|
+
- Use `Literal` types for enum-like fields (not plain `str`).
|
|
33
|
+
- Use Pydantic `BaseModel` for data structures (not dataclasses).
|
|
34
|
+
- Add `# type: ignore[<code>]` with specific error codes only for third-party libraries without stubs.
|
|
35
|
+
9. **Call graph taint analysis** — no unguarded source-to-sink paths
|
|
36
|
+
- Run: `.venv/bin/aofire-call-graph src/`
|
|
37
|
+
- Traces data flow from external inputs (CLI args, keyboard, file reads, JSON parsing, SDK responses) through the call graph to dangerous sinks (eval, subprocess, file writes, prompt injection).
|
|
38
|
+
- Each finding tagged with a CWE code. Use `--sarif` for CI integration.
|
|
39
|
+
- Fix findings by adding sanitizers: `frame_data()` for prompts, Pydantic validation for data, `tool_guard` for commands.
|
|
40
|
+
10. **Functional test gap analysis** — final step after all other checks pass
|
|
41
|
+
- Read every source function and every test. For each function, enumerate all code paths and identify which are not exercised by any test.
|
|
42
|
+
- Focus on: integration between components, error propagation, boundary conditions, multi-step flows, and real usage edge cases.
|
|
43
|
+
- Write tests to close the gaps found. Iterate until no meaningful gaps remain.
|
|
44
|
+
- This step catches what coverage and mutation testing miss: tests that exist but don't verify the right thing, interactions between functions, and untested error paths.
|
|
45
|
+
|
|
46
|
+
### Venv
|
|
47
|
+
|
|
48
|
+
Always use the project venv. Execute Python through `.venv/bin/python3`,
|
|
49
|
+
`.venv/bin/pytest`, `.venv/bin/flake8`, etc. Never use the system Python.
|
|
50
|
+
|
|
51
|
+
### Before Committing
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
.venv/bin/flake8 --max-complexity=5
|
|
55
|
+
.venv/bin/mypy --strict src/
|
|
56
|
+
.venv/bin/pytest --cov --cov-branch --cov-report=term-missing
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
The only acceptable uncovered line is the `if __name__` guard (`sys.exit`).
|
|
60
|
+
|
|
61
|
+
## Correctness
|
|
62
|
+
|
|
63
|
+
These are foundational. They apply to all code in all languages.
|
|
64
|
+
|
|
65
|
+
- **No one writes correct code — not humans, not AI.** Confidence without evidence is the most dangerous state. The cost of catching a bug grows exponentially the later it's found. Verify now, not later.
|
|
66
|
+
- **A programmer's critical job is proving their code is correct.** Trust nothing without verification.
|
|
67
|
+
- **Failure handling code that is never tested is a liability.** It can generate new errors when it finally runs. When writing functions with failure paths, discuss whether those paths are reachable under test. If not, discuss the cost of making them testable (e.g., dependency injection so tests can supply fakes).
|
|
68
|
+
- **Prefer parameters over hardcoded values.** Enables dependency injection and testability.
|
|
69
|
+
- **An accidental fix is not a fix — it's a clue.** Ask WHY a change affects behavior before shipping it.
|
|
70
|
+
- **Trace symptoms to code paths, not external theories.** When debugging, grep for what produces the output, trace the loop, ask "what are we not exiting and why?" Step UP in abstraction, don't drill down into speculation.
|
|
71
|
+
|
|
72
|
+
## Testing Philosophy
|
|
73
|
+
|
|
74
|
+
These are non-negotiable. They come from hard experience across multiple projects.
|
|
75
|
+
|
|
76
|
+
- **Tests are part of the implementation, not a follow-up.** Code without tests is not done.
|
|
77
|
+
- **Both sides of every conditional.** Not "the important ones" — ALL of them.
|
|
78
|
+
- **Every test MUST have a meaningful assertion.** Never write a test that calls a function and unconditionally passes. Never write `assert len(x) > 0` when you can assert on the actual value.
|
|
79
|
+
- **Test from multiple angles.** Unit test + functional test on the same path catches different bugs.
|
|
80
|
+
- **Reproduce runtime bugs in tests first.** Write a test that triggers the failure (red), fix the code (green), commit both together.
|
|
81
|
+
- **If unsure what to assert, discuss it first.** "What should this test verify?" is always the right question.
|
|
82
|
+
- **The branch coverage analysis itself finds bugs.** Enumerating every conditional and verifying both sides are exercised catches things tests alone miss.
|
|
83
|
+
- **Mutation testing is the proof.** If a mutant survives, the test is broken.
|
|
84
|
+
|
|
85
|
+
## Production vs Prototype
|
|
86
|
+
|
|
87
|
+
Two modes, hard boundary between them:
|
|
88
|
+
|
|
89
|
+
- **Production code:** Full standards, thoroughly tested, no shortcuts.
|
|
90
|
+
- **Prototype code:** Only when the path forward is unclear. Define the questions it answers upfront. When answered, reimplement from scratch to production standards. Never evolve a prototype into production code.
|
|
91
|
+
|
|
92
|
+
## Working Style
|
|
93
|
+
|
|
94
|
+
- Trunk-based development: commit directly to `main`, push after each group of changes.
|
|
95
|
+
- Don't suggest breaks or stopping.
|
|
96
|
+
- When you catch a mistake or unintended change — stop. Understand what changed and why before moving on. Don't rationalize differences away.
|
|
97
|
+
- Never check files into the wrong repo by drifting directories. Stay in this project dir; use absolute paths for anything outside it.
|
|
98
|
+
- Always check if a target file exists before `mv`/`cp`/`Write`. Lost uncommitted work is gone forever.
|
|
99
|
+
- Never hide unexpected messages or errors — fix the source, not the reporter.
|
|
100
|
+
- Revert failed experiments immediately. Only keep changes you have high confidence are correct.
|
|
101
|
+
|
|
102
|
+
## Git
|
|
103
|
+
|
|
104
|
+
- Commit after every group of code changes. Don't wait to be asked.
|
|
105
|
+
- Use the git user.name and user.email configured on the system.
|
python_agent/__init__.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Shared helper functions used by multiple agents."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import re
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
from claude_agent_sdk import TextBlock
|
|
10
|
+
|
|
11
|
+
_ONTOLOGY_BLOCK_RE = re.compile(
|
|
12
|
+
r"```ontology\s*\n(.*?)\n```",
|
|
13
|
+
re.DOTALL,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def print_text_blocks(message: Any) -> None:
|
|
18
|
+
"""Print TextBlock content from an AssistantMessage."""
|
|
19
|
+
for block in message.content:
|
|
20
|
+
if isinstance(block, TextBlock):
|
|
21
|
+
print(block.text)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def collect_response_text(message: Any) -> str:
|
|
25
|
+
"""Extract concatenated text from an AssistantMessage."""
|
|
26
|
+
parts: list[str] = []
|
|
27
|
+
for block in message.content:
|
|
28
|
+
if isinstance(block, TextBlock):
|
|
29
|
+
parts.append(block.text)
|
|
30
|
+
return "\n".join(parts)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def read_user_input() -> str | None:
|
|
34
|
+
"""Read a line from the user. Return None to quit."""
|
|
35
|
+
try:
|
|
36
|
+
user_input = input("\n> ")
|
|
37
|
+
except (EOFError, KeyboardInterrupt):
|
|
38
|
+
print("\nDone.")
|
|
39
|
+
return None
|
|
40
|
+
if user_input.strip().lower() in (
|
|
41
|
+
"quit", "exit", "done",
|
|
42
|
+
):
|
|
43
|
+
return None
|
|
44
|
+
return user_input
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def extract_ontology_json(
|
|
48
|
+
text: str,
|
|
49
|
+
) -> dict[str, Any] | None:
|
|
50
|
+
"""Extract the first ontology JSON block from text.
|
|
51
|
+
|
|
52
|
+
Returns the parsed dict, or None if no valid block found.
|
|
53
|
+
"""
|
|
54
|
+
match = _ONTOLOGY_BLOCK_RE.search(text)
|
|
55
|
+
if match is None:
|
|
56
|
+
return None
|
|
57
|
+
try:
|
|
58
|
+
result: dict[str, Any] = json.loads(match.group(1))
|
|
59
|
+
return result
|
|
60
|
+
except json.JSONDecodeError:
|
|
61
|
+
return None
|