openreflex 0.5.2__tar.gz → 0.7.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.5.2/src/openreflex.egg-info → openreflex-0.7.0}/PKG-INFO +31 -23
- {openreflex-0.5.2 → openreflex-0.7.0}/README.md +29 -21
- {openreflex-0.5.2 → openreflex-0.7.0}/pyproject.toml +2 -2
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/__init__.py +1 -1
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/cli.py +55 -5
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/decision.py +42 -10
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/engine.py +63 -25
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/entrypoint.py +46 -1
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/hooks.py +2 -2
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/install.py +55 -15
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/mcp_contracts.py +7 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/mcp_server.py +81 -28
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/metrics.py +40 -1
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/models.py +25 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/policies/default.toml +1 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/reflex_index.py +40 -9
- openreflex-0.7.0/src/openreflex/reflexes.py +326 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/store.py +64 -2
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/tui.py +4 -4
- {openreflex-0.5.2 → openreflex-0.7.0/src/openreflex.egg-info}/PKG-INFO +31 -23
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex.egg-info/SOURCES.txt +3 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_completion_recap_semantics.py +6 -5
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_engine.py +31 -2
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_install_cli.py +58 -11
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_mcp_metrics_benchmark.py +18 -5
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_outcome_closure.py +2 -1
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_policy_recap.py +3 -2
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_project_map.py +0 -1
- openreflex-0.7.0/tests/test_project_reflexes.py +351 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_reflex_index.py +29 -1
- openreflex-0.7.0/tests/test_release_pipeline.py +49 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_routing_control.py +9 -4
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_tui_memory.py +26 -2
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_work_modes_tokens.py +1 -1
- {openreflex-0.5.2 → openreflex-0.7.0}/LICENSE +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/setup.cfg +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/__main__.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/benchmark.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/claude_ui.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/control.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/detect.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/execution_state.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/integrations/opencode.ts +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/learning.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/policy.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/privacy.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/project.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/project_map.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/project_memory.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/routing.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/updater.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex/usage.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex.egg-info/dependency_links.txt +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex.egg-info/entry_points.txt +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex.egg-info/requires.txt +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/src/openreflex.egg-info/top_level.txt +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_execution_state.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_hooks.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_primer_background.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_project_memory.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_project_scope.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_quality_hardening.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_release_consistency.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_reliability_lifecycle.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_stress.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_token_receiver.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_tokens_cli.py +0 -0
- {openreflex-0.5.2 → openreflex-0.7.0}/tests/test_update_cli.py +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openreflex
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 0.7.0
|
|
4
|
+
Summary: Ambient project muscle memory for AI coding agents: learns and reuses project Reflexes automatically
|
|
5
5
|
Author: vishnu-77
|
|
6
6
|
License-Expression: MIT
|
|
7
7
|
Project-URL: Homepage, https://openreflex.cc
|
|
@@ -54,9 +54,10 @@ Dynamic: license-file
|
|
|
54
54
|
|
|
55
55
|
## What is OpenReflex
|
|
56
56
|
|
|
57
|
-
OpenReflex gives AI coding agents muscle memory across coding, investigation, and reasoning work.
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
OpenReflex gives AI coding agents ambient project muscle memory across coding, investigation, and reasoning work.
|
|
58
|
+
Lifecycle hooks quietly record how work actually goes, compile repeated project behaviour into reusable Reflexes,
|
|
59
|
+
and inject only the relevant project procedure into new tasks. The model does not need to remember to call OpenReflex,
|
|
60
|
+
and MCP diagnostics are opt-in rather than loaded into normal agent context.
|
|
60
61
|
|
|
61
62
|
You install it once and keep working normally. Core memory stays on your machine in a local SQLite database.
|
|
62
63
|
There is no OpenReflex account or hosted service. Optional Claude token accounting uses Claude Code's local
|
|
@@ -64,17 +65,18 @@ OpenTelemetry export to a loopback-only OpenReflex receiver and stores counts on
|
|
|
64
65
|
|
|
65
66
|
<p align="center">
|
|
66
67
|
<a href="docs/walkthrough.md">
|
|
67
|
-
<img alt="Recording of a
|
|
68
|
+
<img alt="Recording of a Claude Code session with OpenReflex: OpenReflex injects compact prior context, Claude fixes the task and runs verification, and lifecycle hooks close the outcome automatically" src="https://raw.githubusercontent.com/vishnu-77/openreflex/main/docs/screenshots/claude-code-session.gif" width="860">
|
|
68
69
|
</a>
|
|
69
70
|
<br>
|
|
70
|
-
<sub>A
|
|
71
|
+
<sub>A recorded Claude Code session. OpenReflex injects compact relevant context before work; lifecycle hooks capture tools and verification and close the execution automatically. MCP is not required for the normal path. The step-by-step walkthrough is in <a href="docs/walkthrough.md">docs/walkthrough.md</a>.</sub>
|
|
71
72
|
</p>
|
|
72
73
|
|
|
73
74
|
## Why OpenReflex
|
|
74
75
|
|
|
75
|
-
- **It
|
|
76
|
-
|
|
77
|
-
|
|
76
|
+
- **It learns how the project behaves.** OpenReflex compiles successful work into project-scoped Reflexes such as
|
|
77
|
+
authentication changes, Helm configuration, database migrations or CI work. Resolution combines semantic intent,
|
|
78
|
+
task family and module/file locality, so a novel task can reuse a project procedure without repeating an
|
|
79
|
+
earlier task.
|
|
78
80
|
- **It catches loops while they happen.** Repeated failing commands, identical retries, stalled progress, and
|
|
79
81
|
runaway context growth raise one alert that says whether to continue, pivot to another approach, or stop and
|
|
80
82
|
check in with you, never a stream of nags.
|
|
@@ -98,7 +100,7 @@ Requires Python 3.11 or newer.
|
|
|
98
100
|
```bash
|
|
99
101
|
pipx install openreflex # or: uv tool install openreflex
|
|
100
102
|
cd your-project
|
|
101
|
-
openreflex install claude-code #
|
|
103
|
+
openreflex install claude-code # hooks-only ambient runtime; enables this project
|
|
102
104
|
```
|
|
103
105
|
|
|
104
106
|
```
|
|
@@ -106,8 +108,8 @@ OPENREFLEX / CONNECT
|
|
|
106
108
|
agent claude-code
|
|
107
109
|
project D:\demo\shop
|
|
108
110
|
config updated 2 files
|
|
111
|
+
D:\demo\shop\.openreflex.json
|
|
109
112
|
D:\demo\shop\.claude\settings.json
|
|
110
|
-
D:\demo\shop\.mcp.json
|
|
111
113
|
memory enabled
|
|
112
114
|
storage local
|
|
113
115
|
|
|
@@ -134,10 +136,10 @@ OpenReflex installs per project with `openreflex install <agent>`, or as a plugi
|
|
|
134
136
|
|
|
135
137
|
| Agent | Connects through | Install | Verified |
|
|
136
138
|
|---|---|---|---|
|
|
137
|
-
| **Claude Code** | Plugin, or hooks
|
|
138
|
-
| **Codex** | Plugin, or hooks
|
|
139
|
-
| **Cursor** |
|
|
140
|
-
| **OpenCode** |
|
|
139
|
+
| **Claude Code** | Plugin hooks, or project hooks | `claude plugin marketplace add vishnu-77/openreflex` then `claude plugin install openreflex@openreflex`, or `openreflex install claude-code` | Live sessions |
|
|
140
|
+
| **Codex** | Plugin hooks, or project hooks | `codex plugin marketplace add vishnu-77/openreflex`, or `openreflex install codex`, then trust the hooks once in `/hooks` | Live sessions |
|
|
141
|
+
| **Cursor** | Lifecycle hooks | `openreflex install cursor` | Protocol and fuzz tests |
|
|
142
|
+
| **OpenCode** | Local lifecycle plugin | `openreflex install opencode` | Protocol and fuzz tests |
|
|
141
143
|
|
|
142
144
|
With a plugin install, enable each project with `openreflex approve`. Per-agent guides:
|
|
143
145
|
[Claude Code](https://openreflex.cc/claude-code), [Codex](https://openreflex.cc/codex), [Cursor](https://openreflex.cc/cursor),
|
|
@@ -166,8 +168,9 @@ Execution -caused-> Outcome -caused-> Experience -caused-> Lesson
|
|
|
166
168
|
left and with the best untried alternative. The alert ends with a recommendation to **continue**, **pivot**
|
|
167
169
|
to another strategy, or **stop** and ask the user. Each problem, pivot, or stop is raised once, with a cooldown
|
|
168
170
|
between messages.
|
|
169
|
-
- **After a task:**
|
|
170
|
-
|
|
171
|
+
- **After a task:** lifecycle hooks close the execution automatically from work-mode-appropriate evidence; the model
|
|
172
|
+
does not need to call OpenReflex. Explicit MCP outcome/path operations remain available only for manual or diagnostic
|
|
173
|
+
workflows. The **Path check** says either
|
|
171
174
|
`better option: <path>` when comparable completed tasks support it or `better option: none proven`.
|
|
172
175
|
Recommendations are never presented as paths that were actually executed.
|
|
173
176
|
|
|
@@ -180,10 +183,13 @@ Set optional limits for every task with `OPENREFLEX_BUDGET`, for example `calls=
|
|
|
180
183
|
Routing, scoring, budget and recap settings come from a versioned policy: the packaged defaults, overridden by
|
|
181
184
|
`~/.openreflex/config.toml` and then by `.openreflex.toml` in the project.
|
|
182
185
|
|
|
183
|
-
|
|
186
|
+
Normal work does **not** load OpenReflex MCP tools. If you explicitly want model-facing introspection for a project,
|
|
187
|
+
enable it with `openreflex diagnostics enable <agent>`. Disable it again with
|
|
188
|
+
`openreflex diagnostics disable <agent>`. The standalone MCP Registry package remains available for users who
|
|
189
|
+
deliberately install OpenReflex as an MCP server.
|
|
184
190
|
|
|
185
191
|
<details>
|
|
186
|
-
<summary><strong>The
|
|
192
|
+
<summary><strong>The optional MCP diagnostics/admin tools</strong></summary>
|
|
187
193
|
|
|
188
194
|
| Tool | What it does |
|
|
189
195
|
|---|---|
|
|
@@ -206,8 +212,9 @@ Agents can also query OpenReflex directly through its MCP server.
|
|
|
206
212
|
|
|
207
213
|
| Command | Purpose |
|
|
208
214
|
|---|---|
|
|
209
|
-
| `install <agent> [--dry-run]` |
|
|
210
|
-
| `
|
|
215
|
+
| `install <agent> [--dry-run]` | Install the hooks-only ambient runtime and enable the project |
|
|
216
|
+
| `diagnostics enable/disable <agent>` | Explicitly opt in/out of model-facing MCP diagnostics |
|
|
217
|
+
| `uninstall <agent> [--dry-run]` | Remove OpenReflex's integration entries; captured memory is kept |
|
|
211
218
|
| `update [--check] [--reinstall]` | Check or update a managed pipx/uv-tool installation; optionally force a reinstall |
|
|
212
219
|
| `self reinstall` | Repair/reinstall the managed OpenReflex package while preserving memory/config |
|
|
213
220
|
| `self uninstall --yes` | Remove only the managed OpenReflex package; memory/config remain on disk |
|
|
@@ -219,7 +226,8 @@ Agents can also query OpenReflex directly through its MCP server.
|
|
|
219
226
|
| `forget --yes` | Delete the project's data |
|
|
220
227
|
| `tokens enable` / `tokens status` / `tokens disable` | Opt in to local Claude Code token accounting, inspect it, or remove OpenReflex-owned telemetry settings |
|
|
221
228
|
| `benchmark` | Run the simulated benchmark |
|
|
222
|
-
| `hook <agent> <event>`
|
|
229
|
+
| `hook <agent> <event>` | Used by ambient agent integrations |
|
|
230
|
+
| `mcp` | Run the optional diagnostics/admin MCP server explicitly |
|
|
223
231
|
|
|
224
232
|
## Privacy
|
|
225
233
|
|
|
@@ -22,9 +22,10 @@
|
|
|
22
22
|
|
|
23
23
|
## What is OpenReflex
|
|
24
24
|
|
|
25
|
-
OpenReflex gives AI coding agents muscle memory across coding, investigation, and reasoning work.
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
OpenReflex gives AI coding agents ambient project muscle memory across coding, investigation, and reasoning work.
|
|
26
|
+
Lifecycle hooks quietly record how work actually goes, compile repeated project behaviour into reusable Reflexes,
|
|
27
|
+
and inject only the relevant project procedure into new tasks. The model does not need to remember to call OpenReflex,
|
|
28
|
+
and MCP diagnostics are opt-in rather than loaded into normal agent context.
|
|
28
29
|
|
|
29
30
|
You install it once and keep working normally. Core memory stays on your machine in a local SQLite database.
|
|
30
31
|
There is no OpenReflex account or hosted service. Optional Claude token accounting uses Claude Code's local
|
|
@@ -32,17 +33,18 @@ OpenTelemetry export to a loopback-only OpenReflex receiver and stores counts on
|
|
|
32
33
|
|
|
33
34
|
<p align="center">
|
|
34
35
|
<a href="docs/walkthrough.md">
|
|
35
|
-
<img alt="Recording of a
|
|
36
|
+
<img alt="Recording of a Claude Code session with OpenReflex: OpenReflex injects compact prior context, Claude fixes the task and runs verification, and lifecycle hooks close the outcome automatically" src="https://raw.githubusercontent.com/vishnu-77/openreflex/main/docs/screenshots/claude-code-session.gif" width="860">
|
|
36
37
|
</a>
|
|
37
38
|
<br>
|
|
38
|
-
<sub>A
|
|
39
|
+
<sub>A recorded Claude Code session. OpenReflex injects compact relevant context before work; lifecycle hooks capture tools and verification and close the execution automatically. MCP is not required for the normal path. The step-by-step walkthrough is in <a href="docs/walkthrough.md">docs/walkthrough.md</a>.</sub>
|
|
39
40
|
</p>
|
|
40
41
|
|
|
41
42
|
## Why OpenReflex
|
|
42
43
|
|
|
43
|
-
- **It
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
- **It learns how the project behaves.** OpenReflex compiles successful work into project-scoped Reflexes such as
|
|
45
|
+
authentication changes, Helm configuration, database migrations or CI work. Resolution combines semantic intent,
|
|
46
|
+
task family and module/file locality, so a novel task can reuse a project procedure without repeating an
|
|
47
|
+
earlier task.
|
|
46
48
|
- **It catches loops while they happen.** Repeated failing commands, identical retries, stalled progress, and
|
|
47
49
|
runaway context growth raise one alert that says whether to continue, pivot to another approach, or stop and
|
|
48
50
|
check in with you, never a stream of nags.
|
|
@@ -66,7 +68,7 @@ Requires Python 3.11 or newer.
|
|
|
66
68
|
```bash
|
|
67
69
|
pipx install openreflex # or: uv tool install openreflex
|
|
68
70
|
cd your-project
|
|
69
|
-
openreflex install claude-code #
|
|
71
|
+
openreflex install claude-code # hooks-only ambient runtime; enables this project
|
|
70
72
|
```
|
|
71
73
|
|
|
72
74
|
```
|
|
@@ -74,8 +76,8 @@ OPENREFLEX / CONNECT
|
|
|
74
76
|
agent claude-code
|
|
75
77
|
project D:\demo\shop
|
|
76
78
|
config updated 2 files
|
|
79
|
+
D:\demo\shop\.openreflex.json
|
|
77
80
|
D:\demo\shop\.claude\settings.json
|
|
78
|
-
D:\demo\shop\.mcp.json
|
|
79
81
|
memory enabled
|
|
80
82
|
storage local
|
|
81
83
|
|
|
@@ -102,10 +104,10 @@ OpenReflex installs per project with `openreflex install <agent>`, or as a plugi
|
|
|
102
104
|
|
|
103
105
|
| Agent | Connects through | Install | Verified |
|
|
104
106
|
|---|---|---|---|
|
|
105
|
-
| **Claude Code** | Plugin, or hooks
|
|
106
|
-
| **Codex** | Plugin, or hooks
|
|
107
|
-
| **Cursor** |
|
|
108
|
-
| **OpenCode** |
|
|
107
|
+
| **Claude Code** | Plugin hooks, or project hooks | `claude plugin marketplace add vishnu-77/openreflex` then `claude plugin install openreflex@openreflex`, or `openreflex install claude-code` | Live sessions |
|
|
108
|
+
| **Codex** | Plugin hooks, or project hooks | `codex plugin marketplace add vishnu-77/openreflex`, or `openreflex install codex`, then trust the hooks once in `/hooks` | Live sessions |
|
|
109
|
+
| **Cursor** | Lifecycle hooks | `openreflex install cursor` | Protocol and fuzz tests |
|
|
110
|
+
| **OpenCode** | Local lifecycle plugin | `openreflex install opencode` | Protocol and fuzz tests |
|
|
109
111
|
|
|
110
112
|
With a plugin install, enable each project with `openreflex approve`. Per-agent guides:
|
|
111
113
|
[Claude Code](https://openreflex.cc/claude-code), [Codex](https://openreflex.cc/codex), [Cursor](https://openreflex.cc/cursor),
|
|
@@ -134,8 +136,9 @@ Execution -caused-> Outcome -caused-> Experience -caused-> Lesson
|
|
|
134
136
|
left and with the best untried alternative. The alert ends with a recommendation to **continue**, **pivot**
|
|
135
137
|
to another strategy, or **stop** and ask the user. Each problem, pivot, or stop is raised once, with a cooldown
|
|
136
138
|
between messages.
|
|
137
|
-
- **After a task:**
|
|
138
|
-
|
|
139
|
+
- **After a task:** lifecycle hooks close the execution automatically from work-mode-appropriate evidence; the model
|
|
140
|
+
does not need to call OpenReflex. Explicit MCP outcome/path operations remain available only for manual or diagnostic
|
|
141
|
+
workflows. The **Path check** says either
|
|
139
142
|
`better option: <path>` when comparable completed tasks support it or `better option: none proven`.
|
|
140
143
|
Recommendations are never presented as paths that were actually executed.
|
|
141
144
|
|
|
@@ -148,10 +151,13 @@ Set optional limits for every task with `OPENREFLEX_BUDGET`, for example `calls=
|
|
|
148
151
|
Routing, scoring, budget and recap settings come from a versioned policy: the packaged defaults, overridden by
|
|
149
152
|
`~/.openreflex/config.toml` and then by `.openreflex.toml` in the project.
|
|
150
153
|
|
|
151
|
-
|
|
154
|
+
Normal work does **not** load OpenReflex MCP tools. If you explicitly want model-facing introspection for a project,
|
|
155
|
+
enable it with `openreflex diagnostics enable <agent>`. Disable it again with
|
|
156
|
+
`openreflex diagnostics disable <agent>`. The standalone MCP Registry package remains available for users who
|
|
157
|
+
deliberately install OpenReflex as an MCP server.
|
|
152
158
|
|
|
153
159
|
<details>
|
|
154
|
-
<summary><strong>The
|
|
160
|
+
<summary><strong>The optional MCP diagnostics/admin tools</strong></summary>
|
|
155
161
|
|
|
156
162
|
| Tool | What it does |
|
|
157
163
|
|---|---|
|
|
@@ -174,8 +180,9 @@ Agents can also query OpenReflex directly through its MCP server.
|
|
|
174
180
|
|
|
175
181
|
| Command | Purpose |
|
|
176
182
|
|---|---|
|
|
177
|
-
| `install <agent> [--dry-run]` |
|
|
178
|
-
| `
|
|
183
|
+
| `install <agent> [--dry-run]` | Install the hooks-only ambient runtime and enable the project |
|
|
184
|
+
| `diagnostics enable/disable <agent>` | Explicitly opt in/out of model-facing MCP diagnostics |
|
|
185
|
+
| `uninstall <agent> [--dry-run]` | Remove OpenReflex's integration entries; captured memory is kept |
|
|
179
186
|
| `update [--check] [--reinstall]` | Check or update a managed pipx/uv-tool installation; optionally force a reinstall |
|
|
180
187
|
| `self reinstall` | Repair/reinstall the managed OpenReflex package while preserving memory/config |
|
|
181
188
|
| `self uninstall --yes` | Remove only the managed OpenReflex package; memory/config remain on disk |
|
|
@@ -187,7 +194,8 @@ Agents can also query OpenReflex directly through its MCP server.
|
|
|
187
194
|
| `forget --yes` | Delete the project's data |
|
|
188
195
|
| `tokens enable` / `tokens status` / `tokens disable` | Opt in to local Claude Code token accounting, inspect it, or remove OpenReflex-owned telemetry settings |
|
|
189
196
|
| `benchmark` | Run the simulated benchmark |
|
|
190
|
-
| `hook <agent> <event>`
|
|
197
|
+
| `hook <agent> <event>` | Used by ambient agent integrations |
|
|
198
|
+
| `mcp` | Run the optional diagnostics/admin MCP server explicitly |
|
|
191
199
|
|
|
192
200
|
## Privacy
|
|
193
201
|
|
|
@@ -4,8 +4,8 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "openreflex"
|
|
7
|
-
version = "0.
|
|
8
|
-
description = "
|
|
7
|
+
version = "0.7.0"
|
|
8
|
+
description = "Ambient project muscle memory for AI coding agents: learns and reuses project Reflexes automatically"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "MIT"
|
|
11
11
|
license-files = ["LICENSE"]
|
|
@@ -80,6 +80,33 @@ def cmd_install(args) -> int:
|
|
|
80
80
|
return 0
|
|
81
81
|
|
|
82
82
|
|
|
83
|
+
def cmd_diagnostics(args) -> int:
|
|
84
|
+
from .install import install_diagnostics, uninstall_diagnostics
|
|
85
|
+
|
|
86
|
+
project = _project(args.project)
|
|
87
|
+
operation = install_diagnostics if args.diagnostics_action == "enable" else uninstall_diagnostics
|
|
88
|
+
changes = operation(args.agent, project, dry_run=args.dry_run)
|
|
89
|
+
verb = "would update" if args.dry_run else ("enabled" if args.diagnostics_action == "enable" else "disabled")
|
|
90
|
+
print("OPENREFLEX / DIAGNOSTICS")
|
|
91
|
+
print(f" action {args.diagnostics_action}")
|
|
92
|
+
print(f" agent {args.agent}")
|
|
93
|
+
print(f" project {project}")
|
|
94
|
+
if changes:
|
|
95
|
+
print(f" config {verb} {len(changes)} file{'s' if len(changes) != 1 else ''}")
|
|
96
|
+
for change in changes:
|
|
97
|
+
print(f" {change}")
|
|
98
|
+
else:
|
|
99
|
+
state = "already enabled" if args.diagnostics_action == "enable" else "already disabled"
|
|
100
|
+
print(f" config {state}")
|
|
101
|
+
print(" runtime ambient hooks remain active")
|
|
102
|
+
if not args.dry_run and args.diagnostics_action == "enable":
|
|
103
|
+
print("\n [ok] optional MCP diagnostics enabled")
|
|
104
|
+
print(" note normal OpenReflex work does not require MCP calls")
|
|
105
|
+
elif not args.dry_run:
|
|
106
|
+
print("\n [ok] model-facing MCP diagnostics disabled")
|
|
107
|
+
return 0
|
|
108
|
+
|
|
109
|
+
|
|
83
110
|
def cmd_uninstall(args) -> int:
|
|
84
111
|
from .install import uninstall
|
|
85
112
|
|
|
@@ -122,6 +149,8 @@ def cmd_status(args) -> int:
|
|
|
122
149
|
print(f"OpenReflex {__version__} - {project}")
|
|
123
150
|
print(f" enabled: {data['enabled']} agents: {', '.join(data['engagement']['agents']) or '-'}")
|
|
124
151
|
print(f" tasks: {data['engagement']['tasks']} experiences: {data['engagement']['experiences']} lessons: {data['lessons']}")
|
|
152
|
+
print(f" project reflexes: {data['project_reflexes']['visible']} "
|
|
153
|
+
f"({data['project_reflexes']['learned']} learned / {data['project_reflexes']['proven']} proven)")
|
|
125
154
|
print(f" first session captured: {data['activation']['first_session_captured']} "
|
|
126
155
|
f"seconds to first task: {data['activation']['seconds_to_first_task']}")
|
|
127
156
|
print(f" tasks that used prior experience: {reuse['benefit_rate']}")
|
|
@@ -132,6 +161,13 @@ def cmd_status(args) -> int:
|
|
|
132
161
|
f"{eff['without_prior_experience']['model_tokens']} (observational)")
|
|
133
162
|
print(f" path checks: {path_check['comparisons']} better option found: {path_check['better_option_found']} "
|
|
134
163
|
f"routing agreement: {routing['agreement']}")
|
|
164
|
+
overhead = data["openreflex_overhead"]
|
|
165
|
+
print(f" OpenReflex context: {overhead['estimated_context_tokens']:,} estimated tokens across "
|
|
166
|
+
f"{overhead['context_injected_tasks']} task(s); {overhead['zero_injection_tasks']} substantial task(s) at zero")
|
|
167
|
+
print(f" diagnostic MCP: {overhead['diagnostic_mcp_calls']} call(s), "
|
|
168
|
+
f"{overhead['diagnostic_mcp_tokens_estimate']:,} estimated result tokens")
|
|
169
|
+
if overhead["estimated_context_tax"] is not None:
|
|
170
|
+
print(f" estimated context tax: {overhead['estimated_context_tax']:.2%} of observed model-token usage")
|
|
135
171
|
print(f" live alerts: {data['live_alerts'] or '-'}")
|
|
136
172
|
control = data["execution_control"]
|
|
137
173
|
print(f" verdicts: {control['verdicts'] or '-'} tasks within tool-call budget: {control['tasks_within_tool_call_budget']}")
|
|
@@ -240,7 +276,8 @@ def cmd_update(args) -> int:
|
|
|
240
276
|
print(f" note expected {status.latest}; executable reports {installed}")
|
|
241
277
|
print(" memory preserved")
|
|
242
278
|
print(" config preserved")
|
|
243
|
-
print("\nRestart active Claude Code, Codex, Cursor, or OpenCode sessions to reload hooks
|
|
279
|
+
print("\nRestart active Claude Code, Codex, Cursor, or OpenCode sessions to reload hooks"
|
|
280
|
+
" (and diagnostics MCP if you explicitly enabled it).")
|
|
244
281
|
return 0
|
|
245
282
|
|
|
246
283
|
|
|
@@ -266,7 +303,8 @@ def cmd_self(args) -> int:
|
|
|
266
303
|
print("\n [ok] package reinstalled")
|
|
267
304
|
print(" memory preserved")
|
|
268
305
|
print(" config preserved")
|
|
269
|
-
print("\nRestart active agent sessions to reload hooks
|
|
306
|
+
print("\nRestart active agent sessions to reload hooks"
|
|
307
|
+
" (and diagnostics MCP if you explicitly enabled it).")
|
|
270
308
|
return 0
|
|
271
309
|
|
|
272
310
|
if args.self_action == "uninstall":
|
|
@@ -397,7 +435,7 @@ def cmd_benchmark(args) -> int:
|
|
|
397
435
|
def build_parser() -> argparse.ArgumentParser:
|
|
398
436
|
parser = argparse.ArgumentParser(
|
|
399
437
|
prog="openreflex", description="Ambient execution intelligence for coding agents.",
|
|
400
|
-
epilog="Also available: tui, memory, tokens, statusline (run `openreflex <name> --help` for details).",
|
|
438
|
+
epilog="Also available: tui, memory, reflexes, tokens, statusline (run `openreflex <name> --help` for details).",
|
|
401
439
|
)
|
|
402
440
|
parser.add_argument("--version", action="version", version=__version__)
|
|
403
441
|
sub = parser.add_subparsers(dest="command", required=True)
|
|
@@ -420,14 +458,26 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
420
458
|
command.add_argument("--project")
|
|
421
459
|
command.set_defaults(func=func)
|
|
422
460
|
|
|
423
|
-
for name, func, text in (("install", cmd_install, "Connect OpenReflex
|
|
424
|
-
("uninstall", cmd_uninstall, "Disconnect OpenReflex
|
|
461
|
+
for name, func, text in (("install", cmd_install, "Connect the ambient OpenReflex hook runtime for an agent"),
|
|
462
|
+
("uninstall", cmd_uninstall, "Disconnect OpenReflex while preserving memory")):
|
|
425
463
|
command = sub.add_parser(name, help=text)
|
|
426
464
|
command.add_argument("agent", choices=AGENT_CHOICES)
|
|
427
465
|
command.add_argument("--project")
|
|
428
466
|
command.add_argument("--dry-run", action="store_true")
|
|
429
467
|
command.set_defaults(func=func)
|
|
430
468
|
|
|
469
|
+
diagnostics = sub.add_parser(
|
|
470
|
+
"diagnostics",
|
|
471
|
+
help="Opt in/out of the model-facing MCP diagnostics surface; ambient hooks do not require it",
|
|
472
|
+
)
|
|
473
|
+
diagnostics_sub = diagnostics.add_subparsers(dest="diagnostics_action", required=True)
|
|
474
|
+
for action in ("enable", "disable"):
|
|
475
|
+
command = diagnostics_sub.add_parser(action, help=f"{action.capitalize()} optional MCP diagnostics")
|
|
476
|
+
command.add_argument("agent", choices=AGENT_CHOICES)
|
|
477
|
+
command.add_argument("--project")
|
|
478
|
+
command.add_argument("--dry-run", action="store_true")
|
|
479
|
+
command.set_defaults(func=cmd_diagnostics)
|
|
480
|
+
|
|
431
481
|
update = sub.add_parser("update", help="Check PyPI and safely update the managed OpenReflex installation")
|
|
432
482
|
update_group = update.add_mutually_exclusive_group()
|
|
433
483
|
update_group.add_argument("--check", action="store_true", help="Only report whether an update is available")
|
|
@@ -5,7 +5,7 @@ from __future__ import annotations
|
|
|
5
5
|
from dataclasses import asdict, dataclass, field
|
|
6
6
|
from typing import Iterable
|
|
7
7
|
|
|
8
|
-
from .models import CandidatePath, Experience
|
|
8
|
+
from .models import CandidatePath, Execution, Experience, Task
|
|
9
9
|
from .policy import Policy
|
|
10
10
|
|
|
11
11
|
|
|
@@ -46,6 +46,7 @@ class DecisionSnapshot:
|
|
|
46
46
|
outcome: str | None = None
|
|
47
47
|
model_tokens: int | None = None
|
|
48
48
|
token_usage: dict[str, int | float] = field(default_factory=dict)
|
|
49
|
+
reflex_name: str | None = None
|
|
49
50
|
|
|
50
51
|
def as_dict(self) -> dict:
|
|
51
52
|
return asdict(self)
|
|
@@ -102,7 +103,8 @@ def make_snapshot(*, now: float, phase: str, action: str, best: CandidatePath |
|
|
|
102
103
|
actual_tool_calls: int | None = None, actual_tokens: int | None = None,
|
|
103
104
|
elapsed_seconds: float | None = None, outcome: str | None = None,
|
|
104
105
|
expected_regret: float | None = None, model_tokens: int | None = None,
|
|
105
|
-
token_usage: dict[str, int | float] | None = None
|
|
106
|
+
token_usage: dict[str, int | float] | None = None,
|
|
107
|
+
reflex_name: str | None = None) -> DecisionSnapshot:
|
|
106
108
|
if best is None:
|
|
107
109
|
components = {name: 0.0 for name in policy.table("score.weights")}
|
|
108
110
|
score = reflex_score(components, policy)
|
|
@@ -116,7 +118,7 @@ def make_snapshot(*, now: float, phase: str, action: str, best: CandidatePath |
|
|
|
116
118
|
reason_codes=[], visibility=_visibility(phase, event, policy), event=event,
|
|
117
119
|
actual_tool_calls=actual_tool_calls, actual_tokens=actual_tokens,
|
|
118
120
|
elapsed_seconds=elapsed_seconds, outcome=outcome, model_tokens=model_tokens,
|
|
119
|
-
token_usage=token_usage or {},
|
|
121
|
+
token_usage=token_usage or {}, reflex_name=reflex_name,
|
|
120
122
|
)
|
|
121
123
|
|
|
122
124
|
components = score_components(best, paths, experiences, policy)
|
|
@@ -139,7 +141,7 @@ def make_snapshot(*, now: float, phase: str, action: str, best: CandidatePath |
|
|
|
139
141
|
score_components=components, reason_codes=reason_codes, visibility=_visibility(phase, event, policy),
|
|
140
142
|
event=event, actual_tool_calls=actual_tool_calls, actual_tokens=actual_tokens,
|
|
141
143
|
elapsed_seconds=elapsed_seconds, outcome=outcome, model_tokens=model_tokens,
|
|
142
|
-
token_usage=token_usage or {},
|
|
144
|
+
token_usage=token_usage or {}, reflex_name=reflex_name,
|
|
143
145
|
)
|
|
144
146
|
|
|
145
147
|
|
|
@@ -148,8 +150,8 @@ def render_recap(snapshot: DecisionSnapshot, previous: DecisionSnapshot | None =
|
|
|
148
150
|
summary = []
|
|
149
151
|
if snapshot.outcome:
|
|
150
152
|
summary.append(snapshot.outcome)
|
|
151
|
-
if snapshot.
|
|
152
|
-
summary.append(snapshot.
|
|
153
|
+
if snapshot.reflex_name:
|
|
154
|
+
summary.append(snapshot.reflex_name)
|
|
153
155
|
|
|
154
156
|
cost = []
|
|
155
157
|
if snapshot.actual_tool_calls is not None:
|
|
@@ -164,9 +166,9 @@ def render_recap(snapshot: DecisionSnapshot, previous: DecisionSnapshot | None =
|
|
|
164
166
|
comparison = None
|
|
165
167
|
if snapshot.outcome != "unknown":
|
|
166
168
|
if snapshot.expected_regret is not None and snapshot.next_best_strategy:
|
|
167
|
-
comparison =
|
|
169
|
+
comparison = "Path check · another approach may be better"
|
|
168
170
|
else:
|
|
169
|
-
comparison = "Path check · better option
|
|
171
|
+
comparison = "Path check · no better option proven"
|
|
170
172
|
|
|
171
173
|
state = {
|
|
172
174
|
"success": "COMPLETE",
|
|
@@ -191,11 +193,13 @@ def render_recap(snapshot: DecisionSnapshot, previous: DecisionSnapshot | None =
|
|
|
191
193
|
success = "?" if snapshot.success_probability is None else f"{snapshot.success_probability:.0%}"
|
|
192
194
|
calls = "?" if snapshot.estimated_tool_calls is None else f"~{snapshot.estimated_tool_calls:.0f} calls"
|
|
193
195
|
tokens = "?" if snapshot.estimated_tokens is None else f"~{snapshot.estimated_tokens / 1000:.0f}k tokens"
|
|
194
|
-
|
|
196
|
+
label = snapshot.reflex_name or "working approach"
|
|
197
|
+
return (f"{first}\n{snapshot.evidence_count} experiences · {label} · {success} · "
|
|
195
198
|
f"{calls} · {tokens} · +{snapshot.context_tokens} context tokens")
|
|
196
199
|
|
|
197
200
|
success = "?" if snapshot.success_probability is None else f"{snapshot.success_probability:.0%}"
|
|
198
|
-
|
|
201
|
+
label = snapshot.reflex_name or "working approach"
|
|
202
|
+
message = (f"{first}\n{snapshot.action} · {label} · {success} · "
|
|
199
203
|
f"budget {snapshot.budget_used:.0%}")
|
|
200
204
|
if snapshot.next_best_strategy and snapshot.action in {"pivot", "stop"}:
|
|
201
205
|
message += f" · next {snapshot.next_best_strategy}"
|
|
@@ -233,3 +237,31 @@ def render_trace(snapshots: list[DecisionSnapshot]) -> str:
|
|
|
233
237
|
detail += f" · {snapshot.event}"
|
|
234
238
|
lines.append(f"{minute:02d}:{second:02d} {label:<9} {detail}")
|
|
235
239
|
return "\n".join(lines)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def render_paths(execution: Execution, task: Task, paths: list[CandidatePath]) -> str:
|
|
243
|
+
best = min(paths, key=lambda p: (p.dominated_by is not None, not p.within_limits, -p.score))
|
|
244
|
+
chosen = next((p for p in paths if p.id == execution.chosen_path_id), None)
|
|
245
|
+
recommended = next((p for p in paths if p.id == execution.recommended_path_id), None)
|
|
246
|
+
lines = ["OPENREFLEX / PATHS", "",
|
|
247
|
+
f"Task {task.task_class} - {task.description}",
|
|
248
|
+
f"Recommended {recommended.strategy if recommended else 'n/a'}"]
|
|
249
|
+
if chosen is not None:
|
|
250
|
+
lines.append(f"Followed {chosen.strategy} "
|
|
251
|
+
f"({'inferred from tool calls' if execution.chosen_inferred else 'explicit'})")
|
|
252
|
+
else:
|
|
253
|
+
lines.append("Followed not yet determined" +
|
|
254
|
+
(" (execution still running)" if execution.ended_at is None
|
|
255
|
+
else " (no strategy could be inferred from tool-call evidence)"))
|
|
256
|
+
lines += ["", "CANDIDATES"]
|
|
257
|
+
for path in paths:
|
|
258
|
+
tags = ", ".join(tag for tag in (("recommended" if path is recommended else None),
|
|
259
|
+
("followed" if path is chosen else None)) if tag)
|
|
260
|
+
note = (f"dominated by {path.dominated_by}" if path.dominated_by else
|
|
261
|
+
"over your limit" if not path.within_limits else
|
|
262
|
+
(f"{path.score:+.2f} vs {best.score:+.2f}" if path is not best else ""))
|
|
263
|
+
suffix = " ".join(part for part in (f"[{tags}]" if tags else "", f"({note})" if note else "") if part)
|
|
264
|
+
lines.append(f" {path.strategy:<14} score {path.score:+.2f} success {path.success_probability:.0%} "
|
|
265
|
+
f"~{path.tool_calls:.0f} calls ~{path.time_seconds / 60:.0f} min risk {path.risk:.2f} "
|
|
266
|
+
f"{suffix}".rstrip())
|
|
267
|
+
return "\n".join(lines)
|