openreflex 0.1.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.
- openreflex-0.1.0/LICENSE +21 -0
- openreflex-0.1.0/PKG-INFO +250 -0
- openreflex-0.1.0/README.md +218 -0
- openreflex-0.1.0/pyproject.toml +57 -0
- openreflex-0.1.0/setup.cfg +4 -0
- openreflex-0.1.0/src/openreflex/__init__.py +3 -0
- openreflex-0.1.0/src/openreflex/__main__.py +5 -0
- openreflex-0.1.0/src/openreflex/benchmark.py +225 -0
- openreflex-0.1.0/src/openreflex/cli.py +238 -0
- openreflex-0.1.0/src/openreflex/detect.py +82 -0
- openreflex-0.1.0/src/openreflex/engine.py +445 -0
- openreflex-0.1.0/src/openreflex/hooks.py +239 -0
- openreflex-0.1.0/src/openreflex/install.py +114 -0
- openreflex-0.1.0/src/openreflex/integrations/opencode.ts +75 -0
- openreflex-0.1.0/src/openreflex/learning.py +151 -0
- openreflex-0.1.0/src/openreflex/mcp_server.py +112 -0
- openreflex-0.1.0/src/openreflex/metrics.py +123 -0
- openreflex-0.1.0/src/openreflex/models.py +195 -0
- openreflex-0.1.0/src/openreflex/privacy.py +119 -0
- openreflex-0.1.0/src/openreflex/project.py +103 -0
- openreflex-0.1.0/src/openreflex/routing.py +66 -0
- openreflex-0.1.0/src/openreflex/store.py +183 -0
- openreflex-0.1.0/src/openreflex.egg-info/PKG-INFO +250 -0
- openreflex-0.1.0/src/openreflex.egg-info/SOURCES.txt +31 -0
- openreflex-0.1.0/src/openreflex.egg-info/dependency_links.txt +1 -0
- openreflex-0.1.0/src/openreflex.egg-info/entry_points.txt +2 -0
- openreflex-0.1.0/src/openreflex.egg-info/requires.txt +8 -0
- openreflex-0.1.0/src/openreflex.egg-info/top_level.txt +1 -0
- openreflex-0.1.0/tests/test_engine.py +147 -0
- openreflex-0.1.0/tests/test_hooks.py +132 -0
- openreflex-0.1.0/tests/test_install_cli.py +97 -0
- openreflex-0.1.0/tests/test_mcp_metrics_benchmark.py +56 -0
- openreflex-0.1.0/tests/test_stress.py +133 -0
openreflex-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 vishnu-77
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: openreflex
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Muscle memory for AI coding agents: learns from every task, advises the next one, and flags failure loops live
|
|
5
|
+
Author: vishnu-77
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/vishnu-77/openreflex
|
|
8
|
+
Project-URL: Repository, https://github.com/vishnu-77/openreflex
|
|
9
|
+
Project-URL: Issues, https://github.com/vishnu-77/openreflex/issues
|
|
10
|
+
Keywords: ai-agents,coding-agents,claude-code,codex,cursor,opencode,mcp,memory,hooks
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Topic :: Software Development
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: mcp<2,>=1.26
|
|
25
|
+
Requires-Dist: pydantic<3,>=2.10
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: pytest<9,>=8; extra == "dev"
|
|
28
|
+
Requires-Dist: ruff>=0.12; extra == "dev"
|
|
29
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
30
|
+
Requires-Dist: twine>=6; extra == "dev"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# OpenReflex
|
|
34
|
+
|
|
35
|
+
**Muscle memory for AI coding agents.**
|
|
36
|
+
|
|
37
|
+
[](https://github.com/vishnu-77/openreflex/actions/workflows/ci.yml)
|
|
38
|
+
[](https://pypi.org/project/openreflex/)
|
|
39
|
+
[](LICENSE)
|
|
40
|
+
|
|
41
|
+
Ambient execution intelligence for AI coding agents. Install it once and keep working normally. It records how
|
|
42
|
+
each task actually went (strategy, tool calls, failures, what fixed them, which files mattered). Before the next
|
|
43
|
+
similar task it hands the agent a short **Execution Context**. While a task runs, it steps in only when the
|
|
44
|
+
evidence says re-planning is worthwhile.
|
|
45
|
+
|
|
46
|
+
Everything runs locally in SQLite. There is no service, no account, and no network access.
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
install once -> work normally -> OpenReflex learns continuously
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Install (under 2 minutes)
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
pipx install openreflex # or: uv tool install openreflex (puts `openreflex` on PATH)
|
|
56
|
+
cd your-project
|
|
57
|
+
openreflex install claude-code # or codex | cursor | opencode; also enables the project
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
`install` writes project-level hook and MCP config for one agent, merging with any existing files, and approves
|
|
61
|
+
the project. Use `--dry-run` to preview the changes.
|
|
62
|
+
|
|
63
|
+
### Plugin install (no project files)
|
|
64
|
+
|
|
65
|
+
| Agent | How |
|
|
66
|
+
|---|---|
|
|
67
|
+
| Claude Code | `claude plugin marketplace add vishnu-77/openreflex` then `claude plugin install openreflex@openreflex`, then `openreflex approve` in each project |
|
|
68
|
+
| Codex | `codex plugin marketplace add vishnu-77/openreflex` (reads `.agents/plugins/marketplace.json`), enable the plugin, trust its hooks once in `/hooks`, then `openreflex approve` |
|
|
69
|
+
| Cursor | copy `plugins/openreflex` to `~/.cursor/plugins/local/openreflex`, then `openreflex approve` |
|
|
70
|
+
|
|
71
|
+
Nothing is captured in a project until it is approved. An unapproved project shows a one-line notice at most
|
|
72
|
+
once a day. To stop capture, run `openreflex revoke`. To delete a project's data, run `openreflex forget --yes`.
|
|
73
|
+
|
|
74
|
+
## How it works
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
agent hooks ──► openreflex hook <agent> <event> ──► Engine ──► Experience Graph (SQLite)
|
|
78
|
+
prompt normalize payload per agent │
|
|
79
|
+
tool start (hooks.py) ├─ before: retrieve experience, score 3 candidate
|
|
80
|
+
tool end │ paths, render Execution Context
|
|
81
|
+
compaction ├─ during: detect failure loops, repeated calls,
|
|
82
|
+
stop │ stagnation, context growth, over-budget
|
|
83
|
+
└─ after: infer outcome + chosen path, estimate
|
|
84
|
+
agent ◄── additionalContext / alerts ◄──────────────────── Execution Regret, extract lessons
|
|
85
|
+
agent ──► MCP tools (get_execution_context, choose_path, record_outcome, search_experience, ...)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Experience Graph.** The nodes are `Task`, `Context`, `CandidatePath`, `Execution`, `ToolCall`, `Outcome`,
|
|
89
|
+
`Experience`, and `Lesson`. They are connected by `used`, `caused`, `failed_with`, `resolved_by`, and
|
|
90
|
+
`recommended_for`:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
Task -caused-> Execution -used-> Context -used-> Experience
|
|
94
|
+
CandidatePath -recommended_for-> Task Lesson -recommended_for-> Task
|
|
95
|
+
Execution -failed_with-> ToolCall -resolved_by-> ToolCall (the edit that fixed it)
|
|
96
|
+
Execution -caused-> Outcome -caused-> Experience -caused-> Lesson
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
**Before a task.** A prompt counts as substantial if it has at least 5 words and isn't a follow-up like "yes,
|
|
100
|
+
continue". For those prompts, OpenReflex:
|
|
101
|
+
1. Retrieves similar past experiences.
|
|
102
|
+
2. Scores `inspect-first`, `test-first`, and `incremental` on success probability, time, tool calls, context
|
|
103
|
+
cost, risk, uncertainty, and reversibility. Each score is a Beta-style blend of a prior and observed outcomes.
|
|
104
|
+
3. Injects a context of at most 1,400 characters: the suggested path, alternatives, likely relevant files, and
|
|
105
|
+
lessons.
|
|
106
|
+
|
|
107
|
+
Nothing is injected when there is no relevant experience, so a new project costs zero context tokens.
|
|
108
|
+
|
|
109
|
+
**During a task.** Each alert kind fires at most once per execution, with a 3-minute cooldown. Stagnation is
|
|
110
|
+
only judged after implementation has started, so exploratory reading does not trigger it. An alert names a
|
|
111
|
+
concrete alternative path and any known fix for the current error.
|
|
112
|
+
|
|
113
|
+
**After a task.** The `Stop` hook finalizes the execution. Later turns reopen it and finalize it again, so the
|
|
114
|
+
same task keeps one experience. Outcome and chosen path come from `record_outcome`/`choose_path` when the agent
|
|
115
|
+
calls them. Otherwise they are inferred: only a check that passed or failed after the last edit counts as
|
|
116
|
+
evidence. **Execution Regret** is the best alternative's expected utility minus the chosen path's realized
|
|
117
|
+
utility. It is withheld (not guessed) when the outcome is unknown.
|
|
118
|
+
|
|
119
|
+
## Agent integrations
|
|
120
|
+
|
|
121
|
+
| | Prompt context | Tool capture | Failure signal | Alerts delivered via | Turn end |
|
|
122
|
+
|---|---|---|---|---|---|
|
|
123
|
+
| Claude Code | `UserPromptSubmit` additionalContext | Pre/PostToolUse | `PostToolUseFailure` | PostToolUse additionalContext | `Stop` |
|
|
124
|
+
| Codex | `UserPromptSubmit` additionalContext | Pre/PostToolUse | parsed from `tool_response` | PostToolUse additionalContext | `Stop` |
|
|
125
|
+
| Cursor | deferred to first `postToolUse`* | pre/postToolUse | `postToolUseFailure` | postToolUse `additional_context` | `stop` |
|
|
126
|
+
| OpenCode | system-prompt transform (plugin) | tool.execute.before/after | `message.part.updated` error | appended to tool output | `session.idle` |
|
|
127
|
+
|
|
128
|
+
\* Cursor's `beforeSubmitPrompt` cannot inject context, so the context is held until the first tool result.
|
|
129
|
+
Cursor can also run Claude Code hook configs. Those calls are attributed to Cursor and de-duplicated.
|
|
130
|
+
|
|
131
|
+
**Claude Code is verified in live sessions** (see [Testing](#testing)). The Codex, Cursor, and OpenCode adapters
|
|
132
|
+
follow each agent's documented hook protocol (checked September 2026) and are tested with protocol-shaped and
|
|
133
|
+
fuzzed payloads, but have not yet run inside live sessions of those agents.
|
|
134
|
+
|
|
135
|
+
A hook always exits 0 and never blocks. Errors go to `~/.openreflex/logs/errors.log`, which
|
|
136
|
+
`openreflex doctor` shows. Measured on Windows: median 381 ms per hook process, where bare Python startup
|
|
137
|
+
is about 255 ms. Sixteen parallel tool-call hooks were stored with no lost writes.
|
|
138
|
+
|
|
139
|
+
## Privacy
|
|
140
|
+
|
|
141
|
+
- Stored per tool call: tool name, a coarse category (`read`/`edit`/`search`/`test`/...), a SHA-256 fingerprint
|
|
142
|
+
of the arguments, project-relative file paths, status, duration, output *size*, and a masked one-line error
|
|
143
|
+
signature. Paths outside the project, file contents, command text, tool output, and transcripts are never stored.
|
|
144
|
+
- Prompts are stored as task descriptions, at most 1,000 characters, with secrets (API keys, tokens, bearer
|
|
145
|
+
headers, private keys) redacted.
|
|
146
|
+
- Data lives in `~/.openreflex/projects/<hash>/experience.sqlite3`. Set `OPENREFLEX_HOME` to relocate
|
|
147
|
+
it, or `OPENREFLEX_DISABLE=1` to turn capture off everywhere.
|
|
148
|
+
|
|
149
|
+
## CLI
|
|
150
|
+
|
|
151
|
+
| Command | Purpose |
|
|
152
|
+
|---|---|
|
|
153
|
+
| `install <agent> [--dry-run]` | Write project hooks + MCP config and approve the project |
|
|
154
|
+
| `approve` / `revoke` | Enable / disable capture for the current project |
|
|
155
|
+
| `status [--json]` | Capture, reuse, outcome, efficiency, regret and routing metrics |
|
|
156
|
+
| `context "<task>"` | Preview the Execution Context a task would get |
|
|
157
|
+
| `doctor` | Installation checks and recent hook errors |
|
|
158
|
+
| `forget --yes` | Delete the project's data |
|
|
159
|
+
| `benchmark` | Simulated baseline-vs-guided benchmark |
|
|
160
|
+
| `hook <agent> <event>` / `mcp` | Used by agent configs |
|
|
161
|
+
|
|
162
|
+
## Measuring the success metrics
|
|
163
|
+
|
|
164
|
+
`openreflex status --json` reports these per project:
|
|
165
|
+
|
|
166
|
+
| Plan metric | Field |
|
|
167
|
+
|---|---|
|
|
168
|
+
| <2-minute activation | `activation.seconds_to_first_task` (approval → first captured task) |
|
|
169
|
+
| >70% first-session capture | `activation.first_session_captured` |
|
|
170
|
+
| >50% weekly active retention | `engagement.active_week_ratio` |
|
|
171
|
+
| >30% tasks benefiting | `experience_reuse.benefit_rate` |
|
|
172
|
+
| 20–30% fewer tool calls, 20% fewer tokens, 15% faster | `efficiency_observational.*_change` (observational only; confounded by task mix) |
|
|
173
|
+
| No success-rate regression | `outcomes.success_rate` |
|
|
174
|
+
| >70% routing agreement | `routing.agreement` vs. `routing.retrospective_best` |
|
|
175
|
+
| Declining regret | `execution_regret.by_class[*].early` vs `recent` |
|
|
176
|
+
|
|
177
|
+
Cross-project aggregation (retention across projects, fleet-level activation) belongs with M5 sync.
|
|
178
|
+
|
|
179
|
+
## Benchmark (simulated)
|
|
180
|
+
|
|
181
|
+
`openreflex benchmark` runs synthetic coding tasks from five task families, in two arms, through the real
|
|
182
|
+
engine. Both arms send real hook events and read back the real contexts and alerts; only the guided arm acts on
|
|
183
|
+
them. Default run: 150 tasks per arm × 3 seeds.
|
|
184
|
+
|
|
185
|
+
| Metric | Baseline | Guided | Change | Target |
|
|
186
|
+
|---|---|---|---|---|
|
|
187
|
+
| Tool calls / task | 13.97 | 9.21 | −34% | −20…−30% |
|
|
188
|
+
| Output tokens / task | 13,008 | 8,394 | −36% | −20% |
|
|
189
|
+
| Time / task | 349 s | 230 s | −34% | −15% |
|
|
190
|
+
| Success rate | 0.687 | 0.809 | +0.12 | ≥ baseline |
|
|
191
|
+
| Regret, first → last quarter | 0.107 → 0.103 | 0.079 → 0.036 | | declining |
|
|
192
|
+
| Routing agreement (2nd half) | – | 0.698 | | > 0.70 (**just missed**) |
|
|
193
|
+
|
|
194
|
+
**How to read this.** These are properties of the simulator, not evidence of real-world gains. Most of the
|
|
195
|
+
tool-call and token reduction comes from one assumption: a relevant-files hint cuts search and read calls from
|
|
196
|
+
6–9 down to 2–3. Time is modeled as calls × 25 s, so it moves exactly with calls. All assumptions are listed in
|
|
197
|
+
`SIMULATION_ASSUMPTIONS` in `benchmark.py` and written into the JSON report. What the benchmark does show: the
|
|
198
|
+
engine learns family-specific best strategies from noisy outcomes, regret declines, and alerts shorten failure
|
|
199
|
+
loops. Routing agreement falls short because candidate evidence is pooled per task class, so families in the
|
|
200
|
+
same class that prefer different strategies dilute each other.
|
|
201
|
+
|
|
202
|
+
## Status
|
|
203
|
+
|
|
204
|
+
| Milestone | State |
|
|
205
|
+
|---|---|
|
|
206
|
+
| **M1**: local engine, schemas, telemetry, MCP, agent integration | Done, for four agents |
|
|
207
|
+
| **M2**: retrieval, graph, Execution Context, cross-session memory | Done. Embeddings are offline lexical feature hashing, not a semantic model |
|
|
208
|
+
| **M3**: candidate paths, risk/reward scoring, live inefficiency detection | Done (deterministic) |
|
|
209
|
+
| **M4**: regret, lessons, cross-agent support, benchmark suite | Done. The benchmark is simulated; a real-task A/B harness is still to do |
|
|
210
|
+
| **M5**: learned Trajectory Router, cloud sync, team sharing | Not started |
|
|
211
|
+
|
|
212
|
+
Suggested next steps, in order:
|
|
213
|
+
1. Run the Codex, Cursor, and OpenCode integrations in live sessions and fix protocol drift.
|
|
214
|
+
2. Weight candidate evidence by similarity instead of pooling by task class; this is the cause of the routing-agreement miss.
|
|
215
|
+
3. Build a real-task A/B harness: the same task set with capture on and off.
|
|
216
|
+
4. Add a pluggable semantic embedder (keep lexical as the offline default) and a vector index once there are more than ~5k experiences.
|
|
217
|
+
5. M5.
|
|
218
|
+
|
|
219
|
+
## Testing
|
|
220
|
+
|
|
221
|
+
| Layer | What runs | Where |
|
|
222
|
+
|---|---|---|
|
|
223
|
+
| Unit + integration | Engine, adapters for all 4 agents, installers, CLI subprocesses, MCP, metrics, benchmark | CI: Linux, macOS, Windows × Python 3.11–3.13 |
|
|
224
|
+
| Stress | 24 parallel hook-process pairs (no lost writes), 3,000-experience retrieval, 400 fuzzed hostile payloads across all agents (only unparseable JSON may be rejected) | CI matrix |
|
|
225
|
+
| Packaging | Build sdist + wheel, `twine check --strict`, install the wheel into a clean venv, smoke-test console script, hooks, status and MCP over stdio | CI: Linux, macOS, Windows |
|
|
226
|
+
| Release | Publish to TestPyPI, then install from TestPyPI on 3 OSes and rerun the smoke test before PyPI | `release.yml` |
|
|
227
|
+
| Live agent | Real headless Claude Code sessions (`scripts/live_claude_code.py`) | Local; needs a signed-in `claude` |
|
|
228
|
+
|
|
229
|
+
The live Claude Code scenarios assert on the captured graph and on the session transcript, not on model wording:
|
|
230
|
+
|
|
231
|
+
1. **plugin-capture:** the plugin is loaded with `--plugin-dir`; a real bug fix is captured with categorized tool calls, an inferred success, and lessons.
|
|
232
|
+
2. **context-injection:** a similar task in a new session gets an Execution Context naming the right file, and the transcript shows the model received it.
|
|
233
|
+
3. **failure-loop-alert:** repeated failing test runs raise one alert, and the transcript shows it reached the model.
|
|
234
|
+
4. **mcp-tools:** the plugin's MCP server starts inside Claude Code and `search_experience` returns the captured files.
|
|
235
|
+
5. **project-install:** `openreflex install claude-code` works without the plugin.
|
|
236
|
+
|
|
237
|
+
## Development
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pip install -e ".[dev]"
|
|
241
|
+
pytest # unit, integration, stress
|
|
242
|
+
ruff check src tests scripts
|
|
243
|
+
python scripts/live_claude_code.py # live Claude Code scenarios (real model calls, default haiku)
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
Layout: `engine.py` (event → graph), `routing.py` (retrieval features, candidates, utility), `detect.py` (live
|
|
247
|
+
alerts), `learning.py` (outcome/strategy inference, regret, lessons), `hooks.py` (agent adapters),
|
|
248
|
+
`mcp_server.py`, `install.py`, `metrics.py`, `benchmark.py`, `integrations/opencode.ts`, and
|
|
249
|
+
`plugins/openreflex` (Claude Code / Codex / Cursor plugin; its hook files are checked against `install.py`
|
|
250
|
+
by tests).
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# OpenReflex
|
|
2
|
+
|
|
3
|
+
**Muscle memory for AI coding agents.**
|
|
4
|
+
|
|
5
|
+
[](https://github.com/vishnu-77/openreflex/actions/workflows/ci.yml)
|
|
6
|
+
[](https://pypi.org/project/openreflex/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
9
|
+
Ambient execution intelligence for AI coding agents. Install it once and keep working normally. It records how
|
|
10
|
+
each task actually went (strategy, tool calls, failures, what fixed them, which files mattered). Before the next
|
|
11
|
+
similar task it hands the agent a short **Execution Context**. While a task runs, it steps in only when the
|
|
12
|
+
evidence says re-planning is worthwhile.
|
|
13
|
+
|
|
14
|
+
Everything runs locally in SQLite. There is no service, no account, and no network access.
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
install once -> work normally -> OpenReflex learns continuously
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Install (under 2 minutes)
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
pipx install openreflex # or: uv tool install openreflex (puts `openreflex` on PATH)
|
|
24
|
+
cd your-project
|
|
25
|
+
openreflex install claude-code # or codex | cursor | opencode; also enables the project
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
`install` writes project-level hook and MCP config for one agent, merging with any existing files, and approves
|
|
29
|
+
the project. Use `--dry-run` to preview the changes.
|
|
30
|
+
|
|
31
|
+
### Plugin install (no project files)
|
|
32
|
+
|
|
33
|
+
| Agent | How |
|
|
34
|
+
|---|---|
|
|
35
|
+
| Claude Code | `claude plugin marketplace add vishnu-77/openreflex` then `claude plugin install openreflex@openreflex`, then `openreflex approve` in each project |
|
|
36
|
+
| Codex | `codex plugin marketplace add vishnu-77/openreflex` (reads `.agents/plugins/marketplace.json`), enable the plugin, trust its hooks once in `/hooks`, then `openreflex approve` |
|
|
37
|
+
| Cursor | copy `plugins/openreflex` to `~/.cursor/plugins/local/openreflex`, then `openreflex approve` |
|
|
38
|
+
|
|
39
|
+
Nothing is captured in a project until it is approved. An unapproved project shows a one-line notice at most
|
|
40
|
+
once a day. To stop capture, run `openreflex revoke`. To delete a project's data, run `openreflex forget --yes`.
|
|
41
|
+
|
|
42
|
+
## How it works
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
agent hooks ──► openreflex hook <agent> <event> ──► Engine ──► Experience Graph (SQLite)
|
|
46
|
+
prompt normalize payload per agent │
|
|
47
|
+
tool start (hooks.py) ├─ before: retrieve experience, score 3 candidate
|
|
48
|
+
tool end │ paths, render Execution Context
|
|
49
|
+
compaction ├─ during: detect failure loops, repeated calls,
|
|
50
|
+
stop │ stagnation, context growth, over-budget
|
|
51
|
+
└─ after: infer outcome + chosen path, estimate
|
|
52
|
+
agent ◄── additionalContext / alerts ◄──────────────────── Execution Regret, extract lessons
|
|
53
|
+
agent ──► MCP tools (get_execution_context, choose_path, record_outcome, search_experience, ...)
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
**Experience Graph.** The nodes are `Task`, `Context`, `CandidatePath`, `Execution`, `ToolCall`, `Outcome`,
|
|
57
|
+
`Experience`, and `Lesson`. They are connected by `used`, `caused`, `failed_with`, `resolved_by`, and
|
|
58
|
+
`recommended_for`:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
Task -caused-> Execution -used-> Context -used-> Experience
|
|
62
|
+
CandidatePath -recommended_for-> Task Lesson -recommended_for-> Task
|
|
63
|
+
Execution -failed_with-> ToolCall -resolved_by-> ToolCall (the edit that fixed it)
|
|
64
|
+
Execution -caused-> Outcome -caused-> Experience -caused-> Lesson
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Before a task.** A prompt counts as substantial if it has at least 5 words and isn't a follow-up like "yes,
|
|
68
|
+
continue". For those prompts, OpenReflex:
|
|
69
|
+
1. Retrieves similar past experiences.
|
|
70
|
+
2. Scores `inspect-first`, `test-first`, and `incremental` on success probability, time, tool calls, context
|
|
71
|
+
cost, risk, uncertainty, and reversibility. Each score is a Beta-style blend of a prior and observed outcomes.
|
|
72
|
+
3. Injects a context of at most 1,400 characters: the suggested path, alternatives, likely relevant files, and
|
|
73
|
+
lessons.
|
|
74
|
+
|
|
75
|
+
Nothing is injected when there is no relevant experience, so a new project costs zero context tokens.
|
|
76
|
+
|
|
77
|
+
**During a task.** Each alert kind fires at most once per execution, with a 3-minute cooldown. Stagnation is
|
|
78
|
+
only judged after implementation has started, so exploratory reading does not trigger it. An alert names a
|
|
79
|
+
concrete alternative path and any known fix for the current error.
|
|
80
|
+
|
|
81
|
+
**After a task.** The `Stop` hook finalizes the execution. Later turns reopen it and finalize it again, so the
|
|
82
|
+
same task keeps one experience. Outcome and chosen path come from `record_outcome`/`choose_path` when the agent
|
|
83
|
+
calls them. Otherwise they are inferred: only a check that passed or failed after the last edit counts as
|
|
84
|
+
evidence. **Execution Regret** is the best alternative's expected utility minus the chosen path's realized
|
|
85
|
+
utility. It is withheld (not guessed) when the outcome is unknown.
|
|
86
|
+
|
|
87
|
+
## Agent integrations
|
|
88
|
+
|
|
89
|
+
| | Prompt context | Tool capture | Failure signal | Alerts delivered via | Turn end |
|
|
90
|
+
|---|---|---|---|---|---|
|
|
91
|
+
| Claude Code | `UserPromptSubmit` additionalContext | Pre/PostToolUse | `PostToolUseFailure` | PostToolUse additionalContext | `Stop` |
|
|
92
|
+
| Codex | `UserPromptSubmit` additionalContext | Pre/PostToolUse | parsed from `tool_response` | PostToolUse additionalContext | `Stop` |
|
|
93
|
+
| Cursor | deferred to first `postToolUse`* | pre/postToolUse | `postToolUseFailure` | postToolUse `additional_context` | `stop` |
|
|
94
|
+
| OpenCode | system-prompt transform (plugin) | tool.execute.before/after | `message.part.updated` error | appended to tool output | `session.idle` |
|
|
95
|
+
|
|
96
|
+
\* Cursor's `beforeSubmitPrompt` cannot inject context, so the context is held until the first tool result.
|
|
97
|
+
Cursor can also run Claude Code hook configs. Those calls are attributed to Cursor and de-duplicated.
|
|
98
|
+
|
|
99
|
+
**Claude Code is verified in live sessions** (see [Testing](#testing)). The Codex, Cursor, and OpenCode adapters
|
|
100
|
+
follow each agent's documented hook protocol (checked September 2026) and are tested with protocol-shaped and
|
|
101
|
+
fuzzed payloads, but have not yet run inside live sessions of those agents.
|
|
102
|
+
|
|
103
|
+
A hook always exits 0 and never blocks. Errors go to `~/.openreflex/logs/errors.log`, which
|
|
104
|
+
`openreflex doctor` shows. Measured on Windows: median 381 ms per hook process, where bare Python startup
|
|
105
|
+
is about 255 ms. Sixteen parallel tool-call hooks were stored with no lost writes.
|
|
106
|
+
|
|
107
|
+
## Privacy
|
|
108
|
+
|
|
109
|
+
- Stored per tool call: tool name, a coarse category (`read`/`edit`/`search`/`test`/...), a SHA-256 fingerprint
|
|
110
|
+
of the arguments, project-relative file paths, status, duration, output *size*, and a masked one-line error
|
|
111
|
+
signature. Paths outside the project, file contents, command text, tool output, and transcripts are never stored.
|
|
112
|
+
- Prompts are stored as task descriptions, at most 1,000 characters, with secrets (API keys, tokens, bearer
|
|
113
|
+
headers, private keys) redacted.
|
|
114
|
+
- Data lives in `~/.openreflex/projects/<hash>/experience.sqlite3`. Set `OPENREFLEX_HOME` to relocate
|
|
115
|
+
it, or `OPENREFLEX_DISABLE=1` to turn capture off everywhere.
|
|
116
|
+
|
|
117
|
+
## CLI
|
|
118
|
+
|
|
119
|
+
| Command | Purpose |
|
|
120
|
+
|---|---|
|
|
121
|
+
| `install <agent> [--dry-run]` | Write project hooks + MCP config and approve the project |
|
|
122
|
+
| `approve` / `revoke` | Enable / disable capture for the current project |
|
|
123
|
+
| `status [--json]` | Capture, reuse, outcome, efficiency, regret and routing metrics |
|
|
124
|
+
| `context "<task>"` | Preview the Execution Context a task would get |
|
|
125
|
+
| `doctor` | Installation checks and recent hook errors |
|
|
126
|
+
| `forget --yes` | Delete the project's data |
|
|
127
|
+
| `benchmark` | Simulated baseline-vs-guided benchmark |
|
|
128
|
+
| `hook <agent> <event>` / `mcp` | Used by agent configs |
|
|
129
|
+
|
|
130
|
+
## Measuring the success metrics
|
|
131
|
+
|
|
132
|
+
`openreflex status --json` reports these per project:
|
|
133
|
+
|
|
134
|
+
| Plan metric | Field |
|
|
135
|
+
|---|---|
|
|
136
|
+
| <2-minute activation | `activation.seconds_to_first_task` (approval → first captured task) |
|
|
137
|
+
| >70% first-session capture | `activation.first_session_captured` |
|
|
138
|
+
| >50% weekly active retention | `engagement.active_week_ratio` |
|
|
139
|
+
| >30% tasks benefiting | `experience_reuse.benefit_rate` |
|
|
140
|
+
| 20–30% fewer tool calls, 20% fewer tokens, 15% faster | `efficiency_observational.*_change` (observational only; confounded by task mix) |
|
|
141
|
+
| No success-rate regression | `outcomes.success_rate` |
|
|
142
|
+
| >70% routing agreement | `routing.agreement` vs. `routing.retrospective_best` |
|
|
143
|
+
| Declining regret | `execution_regret.by_class[*].early` vs `recent` |
|
|
144
|
+
|
|
145
|
+
Cross-project aggregation (retention across projects, fleet-level activation) belongs with M5 sync.
|
|
146
|
+
|
|
147
|
+
## Benchmark (simulated)
|
|
148
|
+
|
|
149
|
+
`openreflex benchmark` runs synthetic coding tasks from five task families, in two arms, through the real
|
|
150
|
+
engine. Both arms send real hook events and read back the real contexts and alerts; only the guided arm acts on
|
|
151
|
+
them. Default run: 150 tasks per arm × 3 seeds.
|
|
152
|
+
|
|
153
|
+
| Metric | Baseline | Guided | Change | Target |
|
|
154
|
+
|---|---|---|---|---|
|
|
155
|
+
| Tool calls / task | 13.97 | 9.21 | −34% | −20…−30% |
|
|
156
|
+
| Output tokens / task | 13,008 | 8,394 | −36% | −20% |
|
|
157
|
+
| Time / task | 349 s | 230 s | −34% | −15% |
|
|
158
|
+
| Success rate | 0.687 | 0.809 | +0.12 | ≥ baseline |
|
|
159
|
+
| Regret, first → last quarter | 0.107 → 0.103 | 0.079 → 0.036 | | declining |
|
|
160
|
+
| Routing agreement (2nd half) | – | 0.698 | | > 0.70 (**just missed**) |
|
|
161
|
+
|
|
162
|
+
**How to read this.** These are properties of the simulator, not evidence of real-world gains. Most of the
|
|
163
|
+
tool-call and token reduction comes from one assumption: a relevant-files hint cuts search and read calls from
|
|
164
|
+
6–9 down to 2–3. Time is modeled as calls × 25 s, so it moves exactly with calls. All assumptions are listed in
|
|
165
|
+
`SIMULATION_ASSUMPTIONS` in `benchmark.py` and written into the JSON report. What the benchmark does show: the
|
|
166
|
+
engine learns family-specific best strategies from noisy outcomes, regret declines, and alerts shorten failure
|
|
167
|
+
loops. Routing agreement falls short because candidate evidence is pooled per task class, so families in the
|
|
168
|
+
same class that prefer different strategies dilute each other.
|
|
169
|
+
|
|
170
|
+
## Status
|
|
171
|
+
|
|
172
|
+
| Milestone | State |
|
|
173
|
+
|---|---|
|
|
174
|
+
| **M1**: local engine, schemas, telemetry, MCP, agent integration | Done, for four agents |
|
|
175
|
+
| **M2**: retrieval, graph, Execution Context, cross-session memory | Done. Embeddings are offline lexical feature hashing, not a semantic model |
|
|
176
|
+
| **M3**: candidate paths, risk/reward scoring, live inefficiency detection | Done (deterministic) |
|
|
177
|
+
| **M4**: regret, lessons, cross-agent support, benchmark suite | Done. The benchmark is simulated; a real-task A/B harness is still to do |
|
|
178
|
+
| **M5**: learned Trajectory Router, cloud sync, team sharing | Not started |
|
|
179
|
+
|
|
180
|
+
Suggested next steps, in order:
|
|
181
|
+
1. Run the Codex, Cursor, and OpenCode integrations in live sessions and fix protocol drift.
|
|
182
|
+
2. Weight candidate evidence by similarity instead of pooling by task class; this is the cause of the routing-agreement miss.
|
|
183
|
+
3. Build a real-task A/B harness: the same task set with capture on and off.
|
|
184
|
+
4. Add a pluggable semantic embedder (keep lexical as the offline default) and a vector index once there are more than ~5k experiences.
|
|
185
|
+
5. M5.
|
|
186
|
+
|
|
187
|
+
## Testing
|
|
188
|
+
|
|
189
|
+
| Layer | What runs | Where |
|
|
190
|
+
|---|---|---|
|
|
191
|
+
| Unit + integration | Engine, adapters for all 4 agents, installers, CLI subprocesses, MCP, metrics, benchmark | CI: Linux, macOS, Windows × Python 3.11–3.13 |
|
|
192
|
+
| Stress | 24 parallel hook-process pairs (no lost writes), 3,000-experience retrieval, 400 fuzzed hostile payloads across all agents (only unparseable JSON may be rejected) | CI matrix |
|
|
193
|
+
| Packaging | Build sdist + wheel, `twine check --strict`, install the wheel into a clean venv, smoke-test console script, hooks, status and MCP over stdio | CI: Linux, macOS, Windows |
|
|
194
|
+
| Release | Publish to TestPyPI, then install from TestPyPI on 3 OSes and rerun the smoke test before PyPI | `release.yml` |
|
|
195
|
+
| Live agent | Real headless Claude Code sessions (`scripts/live_claude_code.py`) | Local; needs a signed-in `claude` |
|
|
196
|
+
|
|
197
|
+
The live Claude Code scenarios assert on the captured graph and on the session transcript, not on model wording:
|
|
198
|
+
|
|
199
|
+
1. **plugin-capture:** the plugin is loaded with `--plugin-dir`; a real bug fix is captured with categorized tool calls, an inferred success, and lessons.
|
|
200
|
+
2. **context-injection:** a similar task in a new session gets an Execution Context naming the right file, and the transcript shows the model received it.
|
|
201
|
+
3. **failure-loop-alert:** repeated failing test runs raise one alert, and the transcript shows it reached the model.
|
|
202
|
+
4. **mcp-tools:** the plugin's MCP server starts inside Claude Code and `search_experience` returns the captured files.
|
|
203
|
+
5. **project-install:** `openreflex install claude-code` works without the plugin.
|
|
204
|
+
|
|
205
|
+
## Development
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
pip install -e ".[dev]"
|
|
209
|
+
pytest # unit, integration, stress
|
|
210
|
+
ruff check src tests scripts
|
|
211
|
+
python scripts/live_claude_code.py # live Claude Code scenarios (real model calls, default haiku)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Layout: `engine.py` (event → graph), `routing.py` (retrieval features, candidates, utility), `detect.py` (live
|
|
215
|
+
alerts), `learning.py` (outcome/strategy inference, regret, lessons), `hooks.py` (agent adapters),
|
|
216
|
+
`mcp_server.py`, `install.py`, `metrics.py`, `benchmark.py`, `integrations/opencode.ts`, and
|
|
217
|
+
`plugins/openreflex` (Claude Code / Codex / Cursor plugin; its hook files are checked against `install.py`
|
|
218
|
+
by tests).
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "openreflex"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Muscle memory for AI coding agents: learns from every task, advises the next one, and flags failure loops live"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = "MIT"
|
|
11
|
+
license-files = ["LICENSE"]
|
|
12
|
+
authors = [{ name = "vishnu-77" }]
|
|
13
|
+
requires-python = ">=3.11"
|
|
14
|
+
dependencies = ["mcp>=1.26,<2", "pydantic>=2.10,<3"]
|
|
15
|
+
keywords = ["ai-agents", "coding-agents", "claude-code", "codex", "cursor", "opencode", "mcp", "memory", "hooks"]
|
|
16
|
+
classifiers = [
|
|
17
|
+
"Development Status :: 3 - Alpha",
|
|
18
|
+
"Environment :: Console",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Operating System :: OS Independent",
|
|
21
|
+
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Programming Language :: Python :: 3.13",
|
|
25
|
+
"Topic :: Software Development",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
[project.urls]
|
|
30
|
+
Homepage = "https://github.com/vishnu-77/openreflex"
|
|
31
|
+
Repository = "https://github.com/vishnu-77/openreflex"
|
|
32
|
+
Issues = "https://github.com/vishnu-77/openreflex/issues"
|
|
33
|
+
|
|
34
|
+
[project.optional-dependencies]
|
|
35
|
+
dev = ["pytest>=8,<9", "ruff>=0.12", "build>=1.2", "twine>=6"]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
openreflex = "openreflex.cli:main"
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.packages.find]
|
|
41
|
+
where = ["src"]
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.package-data]
|
|
44
|
+
openreflex = ["integrations/*.ts"]
|
|
45
|
+
|
|
46
|
+
[tool.pytest.ini_options]
|
|
47
|
+
testpaths = ["tests"]
|
|
48
|
+
pythonpath = ["src"]
|
|
49
|
+
markers = ["slow: subprocess-heavy stress tests"]
|
|
50
|
+
|
|
51
|
+
[tool.ruff]
|
|
52
|
+
line-length = 110
|
|
53
|
+
target-version = "py311"
|
|
54
|
+
|
|
55
|
+
[tool.ruff.lint]
|
|
56
|
+
# Explicit rule set so results don't drift when newer ruff releases change their defaults.
|
|
57
|
+
select = ["E4", "E7", "E9", "F"]
|