claude-smart 0.2.46 → 0.2.48

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.
Files changed (170) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/README.md +19 -11
  3. package/bin/claude-smart.js +290 -68
  4. package/package.json +1 -1
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +11 -10
  8. package/plugin/dashboard/app/layout.tsx +20 -0
  9. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +36 -2
  10. package/plugin/dashboard/package-lock.json +61 -390
  11. package/plugin/dashboard/package.json +2 -2
  12. package/plugin/opencode/dist/server.mjs +76 -2
  13. package/plugin/opencode/server.mts +79 -2
  14. package/plugin/pyproject.toml +6 -2
  15. package/plugin/scripts/smart-install.sh +7 -1
  16. package/plugin/src/claude_smart/cli.py +210 -22
  17. package/plugin/src/claude_smart/context_format.py +9 -9
  18. package/plugin/src/claude_smart/cs_cite.py +66 -19
  19. package/plugin/uv.lock +5 -5
  20. package/plugin/vendor/reflexio/.env.example +7 -0
  21. package/plugin/vendor/reflexio/README.md +3 -3
  22. package/plugin/vendor/reflexio/pyproject.toml +2 -1
  23. package/plugin/vendor/reflexio/reflexio/README.md +11 -6
  24. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +1 -1
  25. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +2 -2
  26. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  27. package/plugin/vendor/reflexio/reflexio/client/client.py +97 -0
  28. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +8 -0
  29. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  30. package/plugin/vendor/reflexio/reflexio/lib/_config.py +23 -18
  31. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +9 -8
  32. package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +16 -1
  33. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +27 -16
  34. package/plugin/vendor/reflexio/reflexio/lib/_search.py +27 -5
  35. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +9 -0
  36. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  37. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +1 -0
  38. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  39. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +45 -3
  40. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +37 -0
  41. package/plugin/vendor/reflexio/reflexio/server/README.md +38 -9
  42. package/plugin/vendor/reflexio/reflexio/server/__init__.py +21 -2
  43. package/plugin/vendor/reflexio/reflexio/server/api.py +274 -3267
  44. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/README.md +4 -3
  45. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +16 -1
  46. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +1 -1
  47. package/plugin/vendor/reflexio/reflexio/server/{_auth.py → auth.py} +2 -0
  48. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +62 -36
  49. package/plugin/vendor/reflexio/reflexio/server/deployment_profile.py +69 -0
  50. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  51. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +424 -0
  52. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_json_extraction.py +249 -0
  53. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_structured_output.py +195 -0
  54. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +152 -0
  55. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +980 -0
  56. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_types.py +110 -0
  57. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +73 -1819
  58. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  59. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +57 -5
  60. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  61. package/plugin/vendor/reflexio/reflexio/server/middleware.py +244 -0
  62. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +9 -1
  63. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +14 -2
  64. package/plugin/vendor/reflexio/reflexio/server/rate_limit.py +79 -0
  65. package/plugin/vendor/reflexio/reflexio/server/routes/__init__.py +6 -0
  66. package/plugin/vendor/reflexio/reflexio/server/routes/_common.py +25 -0
  67. package/plugin/vendor/reflexio/reflexio/server/routes/_metering.py +98 -0
  68. package/plugin/vendor/reflexio/reflexio/server/routes/braintrust.py +129 -0
  69. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +210 -0
  70. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +549 -0
  71. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +259 -0
  72. package/plugin/vendor/reflexio/reflexio/server/routes/playbooks.py +578 -0
  73. package/plugin/vendor/reflexio/reflexio/server/routes/profiles.py +423 -0
  74. package/plugin/vendor/reflexio/reflexio/server/routes/provenance.py +345 -0
  75. package/plugin/vendor/reflexio/reflexio/server/routes/search.py +349 -0
  76. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +261 -0
  77. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +132 -0
  78. package/plugin/vendor/reflexio/reflexio/server/services/README.md +5 -2
  79. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/__init__.py +38 -0
  80. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_batch_progress.py +298 -0
  81. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_config_filter.py +152 -0
  82. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +243 -0
  83. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +299 -0
  84. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_status_change.py +273 -0
  85. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +258 -0
  86. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +17 -1183
  87. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +8 -1
  88. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +26 -41
  89. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +8 -0
  90. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +232 -123
  91. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  92. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  93. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  94. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +385 -78
  95. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +9 -1
  96. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  97. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +121 -525
  98. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_clustering.py +184 -0
  99. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_postprocessing.py +130 -0
  100. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator_prompt_formatting.py +212 -0
  101. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +258 -69
  102. package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +9 -9
  103. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +3 -2
  104. package/plugin/vendor/reflexio/reflexio/server/services/publish_learning_worker.py +288 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +29 -13
  107. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +4 -0
  108. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  109. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +43 -6
  110. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +10 -1167
  111. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +58 -351
  112. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +49 -19
  113. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +452 -0
  114. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -4
  115. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1 -2133
  116. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +7 -1126
  117. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +73 -33
  118. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +30 -0
  119. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/__init__.py +9 -0
  120. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_agent_run_store.py +506 -0
  121. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_pending_tool_call_store.py +704 -0
  122. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/agent_run/_run_tool_dependency_store.py +123 -0
  123. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/__init__.py +6 -0
  124. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +263 -0
  125. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +132 -0
  126. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/__init__.py +13 -0
  127. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_audit.py +122 -0
  128. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +465 -0
  129. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_purge.py +387 -0
  130. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_rebuild_hide.py +332 -0
  131. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +511 -0
  132. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  133. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +955 -0
  134. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  135. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  136. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  137. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +844 -0
  138. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/__init__.py +9 -0
  139. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +263 -0
  140. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +896 -0
  141. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_search.py +270 -0
  142. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +50 -9
  143. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +64 -370
  144. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +4 -4
  145. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +0 -909
  146. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +2 -0
  147. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +20 -0
  148. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/__init__.py +9 -0
  149. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_agent_run_store.py +86 -0
  150. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +195 -0
  151. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_pending_tool_call_store.py +148 -0
  152. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_run_tool_dependency_store.py +38 -0
  153. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/__init__.py +13 -0
  154. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_audit.py +29 -0
  155. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_erase_execution.py +30 -0
  156. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_purge.py +74 -0
  157. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_rebuild_hide.py +32 -0
  158. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/governance/_subject_barrier.py +49 -0
  159. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  160. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  161. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  162. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  163. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  164. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  165. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/__init__.py +9 -0
  166. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +73 -0
  167. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/{_profiles.py → profiles/_profile_store.py} +57 -86
  168. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_search.py +32 -0
  169. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +153 -12
  170. package/plugin/vendor/reflexio/reflexio/server/services/playbook/user_detail_stripping.py +0 -84
@@ -9,7 +9,7 @@
9
9
  "plugins": [
10
10
  {
11
11
  "name": "claude-smart",
12
- "version": "0.2.46",
12
+ "version": "0.2.48",
13
13
  "source": "./plugin",
14
14
  "description": "Turns corrections into Preferences, Project-specific skills, and Shared skills — uses Claude Code, Codex, or OpenCode as the LLM backend (no external API key required)"
15
15
  }
package/README.md CHANGED
@@ -13,7 +13,7 @@
13
13
  <img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License">
14
14
  </a>
15
15
  <a href="plugin/pyproject.toml">
16
- <img src="https://img.shields.io/badge/version-0.2.46-green.svg" alt="Version">
16
+ <img src="https://img.shields.io/badge/version-0.2.48-green.svg" alt="Version">
17
17
  </a>
18
18
  <a href="plugin/pyproject.toml">
19
19
  <img src="https://img.shields.io/badge/python-%3E%3D3.12-brightgreen.svg" alt="Python">
@@ -27,6 +27,9 @@
27
27
  <a href="https://discord.gg/7fnCxahase">
28
28
  <img src="https://img.shields.io/badge/Discord-Join%20community-5865F2?logo=discord&logoColor=white" alt="Discord">
29
29
  </a>
30
+ <a href="https://github.com/ReflexioAI/reflexio">
31
+ <img src="https://img.shields.io/github/stars/ReflexioAI/reflexio?style=flat&logo=github&label=Star%20reflexio&color=yellow&cacheSeconds=3600" alt="Star reflexio on GitHub">
32
+ </a>
30
33
  </p>
31
34
 
32
35
  <p align="center">
@@ -47,6 +50,10 @@
47
50
  <b>vs <code>claude-mem</code>:</b> ~3× better at turning your corrections into rules Claude follows, ~50% more of your guidance is retained. <a href="EXPERIMENT.md"><b>Read the benchmark →</b></a>
48
51
  </p>
49
52
 
53
+ <p align="center">
54
+ ⭐ Built on the open-source <a href="https://github.com/ReflexioAI/reflexio"><b>reflexio</b></a> engine — if claude-smart helps you, <a href="https://github.com/ReflexioAI/reflexio"><b>star reflexio on GitHub</b></a>.
55
+ </p>
56
+
50
57
  ---
51
58
 
52
59
  ## Quick Start
@@ -54,12 +61,12 @@
54
61
  ### Claude Code
55
62
 
56
63
  ```bash
57
- npx claude-smart install # or: uvx claude-smart install
64
+ npx claude-smart install
58
65
  ```
59
66
 
60
67
  Then restart Claude Code.
61
68
 
62
- Requires Node.js (for `npx`) or uv (for `uvx`) to already exist.
69
+ Requires Node.js (for `npx`) to already exist.
63
70
 
64
71
  Alternatively, install via Claude Code's plugin marketplace:
65
72
 
@@ -71,7 +78,7 @@ claude plugin install claude-smart@reflexioai
71
78
  To uninstall:
72
79
 
73
80
  ```bash
74
- npx claude-smart uninstall # or: uvx claude-smart uninstall
81
+ npx claude-smart uninstall
75
82
  ```
76
83
 
77
84
  Or, if installed via the plugin marketplace:
@@ -104,9 +111,9 @@ Restart Codex after uninstalling. The uninstaller stops local claude-smart servi
104
111
  npx claude-smart install --host opencode
105
112
  ```
106
113
 
107
- Then restart OpenCode in your project so it loads the plugin from `opencode.json`, the documented project config file. If that project does not already have a root config but does have `.opencode/opencode.json` or `.opencode/opencode.jsonc`, the installer updates that existing file instead of creating a second config. If both locations exist, the root config wins. Use `--global` to install into `~/.config/opencode/opencode.json` for all OpenCode projects on this machine. When run through `npx`, long-lived local services are prepared from OpenCode's installed plugin package on the next OpenCode launch instead of from npm's temporary `npx` cache.
114
+ Then restart OpenCode in your project so it loads the plugin from `opencode.json`, the documented project config file. If that project does not already have a root config but does have `.opencode/opencode.json` or `.opencode/opencode.jsonc`, the installer updates that existing file instead of creating a second config. If both locations exist, the root config wins. Use `--global` to install into `~/.config/opencode/opencode.json` for all OpenCode projects on this machine. The installer copies the active package to `~/.claude-smart/opencode/claude-smart`, prepares that runtime immediately, and writes a `file://` plugin entry so OpenCode reloads the same prepared package after restart.
108
115
 
109
- OpenCode support is new and uses OpenCode's npm plugin loader to inject relevant learned context before each model request. Learning extraction runs `opencode run --pure` from an isolated temp project, so it uses OpenCode's default model unless you set `CLAUDE_SMART_OPENCODE_MODEL=provider/model`. Set that env var if your normal project config pins a different provider or model.
116
+ OpenCode support is new and uses OpenCode's plugin loader to inject relevant learned context before each model request. Learning extraction runs `opencode run --pure` from an isolated temp project, so it uses OpenCode's default model unless you set `CLAUDE_SMART_OPENCODE_MODEL=provider/model`. Set that env var if your normal project config pins a different provider or model.
110
117
 
111
118
  To uninstall:
112
119
 
@@ -114,7 +121,7 @@ To uninstall:
114
121
  npx claude-smart uninstall --host opencode
115
122
  ```
116
123
 
117
- Restart OpenCode after uninstalling. The uninstaller removes only the `claude-smart` entry from OpenCode's plugin list; learned data under `~/.reflexio/` and `~/.claude-smart/` is preserved and shared across hosts.
124
+ Restart OpenCode after uninstalling. The uninstaller removes the `claude-smart` entry from OpenCode's plugin list and deletes the copied OpenCode package; learned data under `~/.reflexio/` and `~/.claude-smart/` is preserved and shared across hosts.
118
125
 
119
126
  Developing the plugin itself? See [DEVELOPER.md](./DEVELOPER.md#developing-locally) for what the installer does, manual toggles via `/plugins`, and clone-based development.
120
127
 
@@ -194,10 +201,10 @@ Under the hood: hooks watch your turns, tool calls, and assistant replies, auto-
194
201
  **Citations.** At the end of a reply, the assistant may append a short marker:
195
202
 
196
203
  ```
197
- 2 claude-smart learnings applied [cs:s1-252,p1-5aed]
204
+ ✨ claude-smart rule applied: [git safety](http://localhost:3001/rules/s1-123) | [brief answer preference](http://localhost:3001/rules/p1-pref) · [⚡Reflexio](https://github.com/ReflexioAI/reflexio)
198
205
  ```
199
206
 
200
- That signals a preference (`p…`) or skill (`s…`) materially shaped the reply.
207
+ That signals a preference or skill materially shaped the reply.
201
208
  Open the session's detail page in the [dashboard](http://localhost:3001) to see the exact cited item.
202
209
 
203
210
  See [ARCHITECTURE.md](./ARCHITECTURE.md) for hooks, data flow, and reflexio details.
@@ -235,6 +242,7 @@ Advanced users can tune claude-smart via environment variables — see [DEVELOPE
235
242
  | `~/.codex/config.toml` | Codex plugin state, hook feature flags, and per-hook trust entries after `claude-smart install --host codex`. |
236
243
  | `~/.codex/plugins/cache/reflexioai/claude-smart/<version>/` | Codex's cached install of the `claude-smart` plugin from the `ReflexioAI` marketplace. |
237
244
  | `opencode.json` / `opencode.jsonc`, or existing `.opencode/opencode.json*` | OpenCode local plugin config patched by `claude-smart install --host opencode`; the installer updates only the claude-smart entry in OpenCode's plugin list. |
245
+ | `~/.claude-smart/opencode/claude-smart/` | OpenCode's stable local copy of the active npm package. OpenCode config points here with `file://` so restarts do not re-resolve `claude-smart` from npm. |
238
246
  | `~/.reflexio/plugin-root` | Self-healed symlink to the active plugin dir (managed by `ensure-plugin-root.sh` — written on install, refreshed each `SessionStart`). Claude Code slash commands and Codex shell-command helpers resolve through it, so don't delete it; if you do, the next session will recreate it. |
239
247
  | `~/.claude-smart/sessions/{session_id}.jsonl` | Per-session buffer. User turns, assistant turns, tool invocations, `{"published_up_to": N}` watermarks. Safe to inspect and safe to delete — everything past the latest watermark has already been written to reflexio's DB. |
240
248
  | `~/.claude-smart/node/current/` | Private Node.js/npm runtime used by hooks and the dashboard after install. |
@@ -246,7 +254,7 @@ For troubleshooting, see [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
246
254
 
247
255
  ## Powered by Reflexio
248
256
 
249
- claude-smart is powered by [**Reflexio**](https://reflexio.ai) — the self-improving engine that turns interactions into durable, reusable skills.
257
+ claude-smart is powered by [**Reflexio**](https://github.com/ReflexioAI/reflexio) ([reflexio.ai](https://reflexio.ai)) — the open-source self-improving engine that turns interactions into durable, reusable skills. If claude-smart is useful to you, **[⭐ star reflexio on GitHub](https://github.com/ReflexioAI/reflexio)**.
250
258
 
251
259
  ---
252
260
 
@@ -265,4 +273,4 @@ See the [LICENSE](LICENSE) file for details.
265
273
 
266
274
  ---
267
275
 
268
- **Powered by** [Reflexio](https://reflexio.ai) · **Runs on** [Claude Code](https://claude.com/claude-code), Codex, and OpenCode · **Written in** Python 3.12+
276
+ **Powered by** [Reflexio](https://github.com/ReflexioAI/reflexio) · **Runs on** [Claude Code](https://claude.com/claude-code), Codex, and OpenCode · **Written in** Python 3.12+
@@ -25,7 +25,9 @@ const {
25
25
  mkdirSync,
26
26
  readFileSync,
27
27
  readdirSync,
28
+ realpathSync,
28
29
  renameSync,
30
+ rmdirSync,
29
31
  rmSync,
30
32
  statSync,
31
33
  symlinkSync,
@@ -33,13 +35,14 @@ const {
33
35
  } = require("fs");
34
36
  const https = require("https");
35
37
  const { arch, homedir, platform, release, tmpdir } = require("os");
36
- const { dirname, join } = require("path");
38
+ const { dirname, join, resolve } = require("path");
39
+ const { fileURLToPath, pathToFileURL } = require("url");
37
40
 
38
41
  const PLUGIN_SPEC = "claude-smart@reflexioai";
39
42
  const CODEX_MARKETPLACE_NAME = "reflexioai";
40
43
  const CODEX_MARKETPLACE_DISPLAY_NAME = "ReflexioAI";
41
44
  const CODEX_PLUGIN_ID = `claude-smart@${CODEX_MARKETPLACE_NAME}`;
42
- const OPENCODE_PLUGIN_SPEC = "claude-smart";
45
+ const OPENCODE_BARE_PLUGIN_SPEC = "claude-smart";
43
46
  const OPENCODE_CONFIG_NAMES = ["opencode.json", "opencode.jsonc"];
44
47
  const REFLEXIO_ENV_PATH = join(homedir(), ".reflexio", ".env");
45
48
  const MANAGED_REFLEXIO_URL = "https://www.reflexio.ai/";
@@ -50,6 +53,9 @@ const CLAUDE_SMART_USE_LOCAL_EMBEDDING_ENV = "CLAUDE_SMART_USE_LOCAL_EMBEDDING";
50
53
  const REFLEXIO_USER_ID_ENV = "REFLEXIO_USER_ID";
51
54
  const REFLEXIO_DIR = join(homedir(), ".reflexio");
52
55
  const CLAUDE_SMART_STATE_DIR = join(homedir(), ".claude-smart");
56
+ const OPENCODE_LOCAL_PACKAGE_DIR = join(CLAUDE_SMART_STATE_DIR, "opencode", "claude-smart");
57
+ const OPENCODE_PACKAGE_LOCK_TIMEOUT_MS = 120_000;
58
+ const OPENCODE_PACKAGE_LOCK_STALE_MS = 10 * 60_000;
53
59
  const CODEX_CONFIG_PATH = join(homedir(), ".codex", "config.toml");
54
60
  const PACKAGE_ROOT = dirname(dirname(__filename));
55
61
  const CODEX_MARKETPLACE_DIR = join(
@@ -96,6 +102,7 @@ const COPYTREE_IGNORE_NAMES = new Set([
96
102
  ".venv",
97
103
  ".pytest_cache",
98
104
  ".ruff_cache",
105
+ ".git",
99
106
  "node_modules",
100
107
  ".next",
101
108
  ]);
@@ -450,7 +457,41 @@ function opencodePluginSpec(entry) {
450
457
  return null;
451
458
  }
452
459
 
453
- function patchOpenCodePluginConfig(configPath, { install }) {
460
+ function opencodeLocalPluginSpec(packageRoot = OPENCODE_LOCAL_PACKAGE_DIR) {
461
+ return pathToFileURL(packageRoot).href;
462
+ }
463
+
464
+ function isOpenCodeLocalPackagePath(packagePath) {
465
+ return (
466
+ sameRealPath(packagePath, OPENCODE_LOCAL_PACKAGE_DIR) ||
467
+ resolve(packagePath) === resolve(OPENCODE_LOCAL_PACKAGE_DIR)
468
+ );
469
+ }
470
+
471
+ function isOpenCodeClaudeSmartSpec(spec) {
472
+ if (!spec) return false;
473
+ if (spec === OPENCODE_BARE_PLUGIN_SPEC || spec.startsWith(`${OPENCODE_BARE_PLUGIN_SPEC}@`)) {
474
+ return true;
475
+ }
476
+ if (!spec.startsWith("file://")) return false;
477
+ let packagePath = null;
478
+ try {
479
+ packagePath = fileURLToPath(spec);
480
+ } catch {
481
+ return false;
482
+ }
483
+ if (isOpenCodeLocalPackagePath(packagePath)) return true;
484
+ try {
485
+ const manifest = JSON.parse(readFileSync(join(packagePath, "package.json"), "utf8"));
486
+ if (manifest && manifest.name === OPENCODE_BARE_PLUGIN_SPEC) return true;
487
+ } catch {
488
+ // Missing or malformed manifests are not enough to identify arbitrary file specs.
489
+ }
490
+ return false;
491
+ }
492
+
493
+ function patchOpenCodePluginConfig(configPath, { install, pluginSpec = null }) {
494
+ const resolvedPluginSpec = install ? (pluginSpec || opencodeLocalPluginSpec()) : pluginSpec;
454
495
  const data = readJsoncObject(configPath);
455
496
  for (const field of ["plugins", "plugin"]) {
456
497
  if (data[field] !== undefined && !Array.isArray(data[field])) {
@@ -461,8 +502,8 @@ function patchOpenCodePluginConfig(configPath, { install }) {
461
502
  ...(Array.isArray(data.plugin) ? data.plugin : []),
462
503
  ...(Array.isArray(data.plugins) ? data.plugins : []),
463
504
  ];
464
- const kept = current.filter((entry) => opencodePluginSpec(entry) !== OPENCODE_PLUGIN_SPEC);
465
- const next = install ? [...kept, OPENCODE_PLUGIN_SPEC] : kept;
505
+ const kept = current.filter((entry) => !isOpenCodeClaudeSmartSpec(opencodePluginSpec(entry)));
506
+ const next = install ? [...kept, resolvedPluginSpec] : kept;
466
507
  const changed =
467
508
  data.plugins !== undefined ||
468
509
  (Array.isArray(data.plugin)
@@ -484,10 +525,6 @@ function patchOpenCodePluginConfig(configPath, { install }) {
484
525
  return { changed: true, configPath, backupPath };
485
526
  }
486
527
 
487
- function isNpxPackageRoot(path) {
488
- return path.split(/[\\/]+/).includes("_npx");
489
- }
490
-
491
528
  function hasExtractionProvider() {
492
529
  if ((process.env.REFLEXIO_API_KEY || "").trim()) return true;
493
530
  const cliPath = (process.env.CLAUDE_SMART_CLI_PATH || "").trim();
@@ -600,6 +637,130 @@ function forcePluginRoot(pluginRoot) {
600
637
  }
601
638
  }
602
639
 
640
+ function sameRealPath(left, right) {
641
+ try {
642
+ return realpathSync(left) === realpathSync(right);
643
+ } catch {
644
+ return false;
645
+ }
646
+ }
647
+
648
+ function pathEntryExists(path) {
649
+ try {
650
+ lstatSync(path);
651
+ return true;
652
+ } catch {
653
+ return false;
654
+ }
655
+ }
656
+
657
+ function fileSha256(path) {
658
+ return crypto.createHash("sha256").update(readFileSync(path)).digest("hex");
659
+ }
660
+
661
+ function verifyOpenCodePluginPackage(packageRoot) {
662
+ const sourceScript = join(PACKAGE_ROOT, "plugin", "scripts", "smart-install.sh");
663
+ const copiedScript = join(packageRoot, "plugin", "scripts", "smart-install.sh");
664
+ for (const file of [join(packageRoot, "package.json"), copiedScript]) {
665
+ if (!existsSync(file)) {
666
+ throw new Error(`OpenCode local plugin package is missing ${file}`);
667
+ }
668
+ }
669
+ if (fileSha256(sourceScript) !== fileSha256(copiedScript)) {
670
+ throw new Error(
671
+ "OpenCode local plugin package does not match the installed claude-smart package",
672
+ );
673
+ }
674
+ }
675
+
676
+ function sleepSync(ms) {
677
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
678
+ }
679
+
680
+ function withOpenCodePackageInstallLock(fn) {
681
+ const lockDir = join(dirname(OPENCODE_LOCAL_PACKAGE_DIR), ".install.lock");
682
+ mkdirSync(dirname(OPENCODE_LOCAL_PACKAGE_DIR), { recursive: true });
683
+ const deadline = Date.now() + OPENCODE_PACKAGE_LOCK_TIMEOUT_MS;
684
+ while (true) {
685
+ try {
686
+ mkdirSync(lockDir);
687
+ break;
688
+ } catch (err) {
689
+ if (!err || err.code !== "EEXIST") throw err;
690
+ try {
691
+ if (Date.now() - statSync(lockDir).mtimeMs > OPENCODE_PACKAGE_LOCK_STALE_MS) {
692
+ rmSync(lockDir, { recursive: true, force: true });
693
+ continue;
694
+ }
695
+ } catch (statErr) {
696
+ if (statErr && statErr.code !== "ENOENT") throw statErr;
697
+ }
698
+ if (Date.now() >= deadline) {
699
+ throw new Error(`timed out waiting for OpenCode package install lock at ${lockDir}`);
700
+ }
701
+ sleepSync(100);
702
+ }
703
+ }
704
+ try {
705
+ return fn();
706
+ } finally {
707
+ rmSync(lockDir, { recursive: true, force: true });
708
+ }
709
+ }
710
+
711
+ function uniqueOpenCodePackagePath(prefix) {
712
+ return join(
713
+ dirname(OPENCODE_LOCAL_PACKAGE_DIR),
714
+ `${prefix}-${process.pid}-${Date.now()}-${crypto.randomBytes(4).toString("hex")}`,
715
+ );
716
+ }
717
+
718
+ function replaceOpenCodePluginPackage(stagedPackage, packageRoot) {
719
+ const backupPackage = uniqueOpenCodePackagePath(".claude-smart-previous");
720
+ let backupCreated = false;
721
+ try {
722
+ if (pathEntryExists(packageRoot)) {
723
+ renameSync(packageRoot, backupPackage);
724
+ backupCreated = true;
725
+ }
726
+ renameSync(stagedPackage, packageRoot);
727
+ if (backupCreated) {
728
+ rmSync(backupPackage, { recursive: true, force: true });
729
+ }
730
+ } catch (err) {
731
+ if (backupCreated && !existsSync(packageRoot) && existsSync(backupPackage)) {
732
+ renameSync(backupPackage, packageRoot);
733
+ }
734
+ throw err;
735
+ }
736
+ }
737
+
738
+ function installOpenCodePluginPackage() {
739
+ const packageRoot = OPENCODE_LOCAL_PACKAGE_DIR;
740
+ if (sameRealPath(PACKAGE_ROOT, packageRoot)) {
741
+ verifyOpenCodePluginPackage(packageRoot);
742
+ return packageRoot;
743
+ }
744
+ return withOpenCodePackageInstallLock(() => {
745
+ const stagedPackage = uniqueOpenCodePackagePath(".claude-smart-copy");
746
+ rmSync(stagedPackage, { recursive: true, force: true });
747
+ try {
748
+ cpSync(PACKAGE_ROOT, stagedPackage, {
749
+ recursive: true,
750
+ force: true,
751
+ verbatimSymlinks: false,
752
+ filter: shouldCopyPath,
753
+ });
754
+ verifyOpenCodePluginPackage(stagedPackage);
755
+ replaceOpenCodePluginPackage(stagedPackage, packageRoot);
756
+ verifyOpenCodePluginPackage(packageRoot);
757
+ return packageRoot;
758
+ } finally {
759
+ rmSync(stagedPackage, { recursive: true, force: true });
760
+ }
761
+ });
762
+ }
763
+
603
764
  async function bootstrapClaudeCodeInstall() {
604
765
  const pluginRoot = findClaudeCodePluginRoot();
605
766
  if (!pluginRoot) {
@@ -694,6 +855,18 @@ function runChecked(command, args, options = {}) {
694
855
  });
695
856
  }
696
857
 
858
+ function runSilentStatus(command, args, options = {}) {
859
+ const result = spawnSync(command, args, {
860
+ cwd: options.cwd,
861
+ env: options.env || process.env,
862
+ shell: isWindows() && /\.(?:cmd|bat)$/i.test(command),
863
+ stdio: "ignore",
864
+ windowsHide: true,
865
+ });
866
+ if (result.error || result.signal) return 1;
867
+ return typeof result.status === "number" ? result.status : 1;
868
+ }
869
+
697
870
  function runPluginService(pluginRoot, scriptName, subcommand, envOverrides = {}) {
698
871
  const script = join(pluginRoot, "scripts", scriptName);
699
872
  if (!existsSync(script)) return false;
@@ -1092,6 +1265,59 @@ async function installVendoredReflexio(pluginRoot, uv, env) {
1092
1265
  if (code !== 0) throw new Error(`vendored Reflexio install failed in ${pluginRoot}`);
1093
1266
  }
1094
1267
 
1268
+ async function syncPluginPythonEnv(pluginRoot, uv, env) {
1269
+ let code = await runChecked(
1270
+ uv,
1271
+ ["sync", "--locked", "--python", "3.12", "--quiet"],
1272
+ { cwd: pluginRoot, env },
1273
+ );
1274
+ if (code === 0) return;
1275
+
1276
+ const lockIsFresh = runSilentStatus(
1277
+ uv,
1278
+ ["lock", "--check", "--python", "3.12"],
1279
+ { cwd: pluginRoot, env },
1280
+ ) === 0;
1281
+ if (lockIsFresh) {
1282
+ process.stderr.write(
1283
+ `warning: quiet uv sync failed in ${pluginRoot}; retrying with full output.\n`,
1284
+ );
1285
+ code = await runChecked(
1286
+ uv,
1287
+ ["sync", "--locked", "--python", "3.12"],
1288
+ { cwd: pluginRoot, env },
1289
+ );
1290
+ if (code !== 0) throw new Error(`uv sync failed in ${pluginRoot}`);
1291
+ return;
1292
+ }
1293
+
1294
+ process.stderr.write(
1295
+ "warning: plugin/uv.lock is out of sync; refreshing local lockfile and retrying uv sync.\n",
1296
+ );
1297
+ code = await runChecked(
1298
+ uv,
1299
+ ["lock", "--python", "3.12"],
1300
+ { cwd: pluginRoot, env },
1301
+ );
1302
+ if (code !== 0) throw new Error(`uv lock failed in ${pluginRoot}`);
1303
+ code = await runChecked(
1304
+ uv,
1305
+ ["sync", "--python", "3.12", "--quiet"],
1306
+ { cwd: pluginRoot, env },
1307
+ );
1308
+ if (code !== 0) {
1309
+ process.stderr.write(
1310
+ `warning: quiet uv sync failed in ${pluginRoot} after refreshing plugin/uv.lock; retrying with full output.\n`,
1311
+ );
1312
+ code = await runChecked(
1313
+ uv,
1314
+ ["sync", "--python", "3.12"],
1315
+ { cwd: pluginRoot, env },
1316
+ );
1317
+ }
1318
+ if (code !== 0) throw new Error(`uv sync failed in ${pluginRoot}`);
1319
+ }
1320
+
1095
1321
  async function bootstrapPluginRuntime(pluginRoot, options = {}) {
1096
1322
  assertSupportedRuntimePlatform();
1097
1323
  process.stdout.write("Preparing claude-smart runtime for hooks...\n");
@@ -1113,27 +1339,12 @@ async function bootstrapPluginRuntime(pluginRoot, options = {}) {
1113
1339
  );
1114
1340
  if (lockCode !== 0) throw new Error(`uv lock failed in ${pluginRoot}`);
1115
1341
  }
1116
- let code = await runChecked(
1117
- uv,
1118
- ["sync", "--locked", "--python", "3.12", "--quiet"],
1119
- { cwd: pluginRoot, env },
1120
- );
1121
- if (code !== 0) {
1122
- process.stderr.write(
1123
- `warning: quiet uv sync failed in ${pluginRoot}; retrying with full output.\n`,
1124
- );
1125
- code = await runChecked(
1126
- uv,
1127
- ["sync", "--locked", "--python", "3.12"],
1128
- { cwd: pluginRoot, env },
1129
- );
1130
- }
1131
- if (code !== 0) throw new Error(`uv sync failed in ${pluginRoot}`);
1342
+ await syncPluginPythonEnv(pluginRoot, uv, env);
1132
1343
  await installVendoredReflexio(pluginRoot, uv, env);
1133
1344
 
1134
1345
  const dashboardDir = join(pluginRoot, "dashboard");
1135
1346
  if (existsSync(dashboardDir)) {
1136
- code = await runChecked(nodeRuntime.npm, ["ci"], { cwd: dashboardDir, env });
1347
+ let code = await runChecked(nodeRuntime.npm, ["ci"], { cwd: dashboardDir, env });
1137
1348
  if (code !== 0) throw new Error(`npm ci failed in ${dashboardDir}`);
1138
1349
  code = await runChecked(nodeRuntime.npm, ["run", "build"], { cwd: dashboardDir, env });
1139
1350
  if (code !== 0) throw new Error(`npm run build failed in ${dashboardDir}`);
@@ -1169,9 +1380,10 @@ function printHelp() {
1169
1380
  " 7. Restart Codex.",
1170
1381
  "",
1171
1382
  "OpenCode install:",
1172
- " 1. Adds \"claude-smart\" to OpenCode's plugin list in opencode.json",
1173
- " 2. Prepares local services now for stable installs; npx prepares on next OpenCode launch",
1174
- " 3. Restart OpenCode.",
1383
+ ` 1. Copies this package to ${OPENCODE_LOCAL_PACKAGE_DIR}`,
1384
+ " 2. Adds that local file:// package to OpenCode's plugin list in opencode.json",
1385
+ " 3. Prepares local services now from the copied plugin runtime",
1386
+ " 4. Restart OpenCode.",
1175
1387
  "",
1176
1388
  "Update:",
1177
1389
  " npx claude-smart update Reinstall Claude Code support from this package",
@@ -1894,43 +2106,44 @@ async function runInstallOpenCode(args) {
1894
2106
  process.exit(1);
1895
2107
  }
1896
2108
 
1897
- const pluginRoot = join(PACKAGE_ROOT, "plugin");
2109
+ let packageRoot;
2110
+ try {
2111
+ packageRoot = installOpenCodePluginPackage();
2112
+ } catch (err) {
2113
+ process.stderr.write(
2114
+ `error: could not prepare claude-smart OpenCode package: ${err && err.message ? err.message : err}\n`,
2115
+ );
2116
+ process.exit(1);
2117
+ }
2118
+ const pluginRoot = join(packageRoot, "plugin");
2119
+ const pluginSpec = opencodeLocalPluginSpec(packageRoot);
1898
2120
  let result;
1899
- if (isNpxPackageRoot(PACKAGE_ROOT)) {
1900
- try {
1901
- result = patchOpenCodePluginConfig(opencodeConfigPath(args), { install: true });
1902
- } catch (err) {
1903
- process.stderr.write(`error: could not update OpenCode config: ${err && err.message ? err.message : err}\n`);
1904
- process.exit(1);
1905
- }
1906
- process.stdout.write(
1907
- "Skipped starting claude-smart services from npx's temporary package cache; OpenCode will prepare the runtime from its installed plugin package on next launch.\n",
2121
+ try {
2122
+ await bootstrapPluginRuntime(pluginRoot, { readOnly, patchCodexHooks: false });
2123
+ } catch (err) {
2124
+ process.stderr.write(
2125
+ `error: claude-smart OpenCode setup failed during dependency bootstrap: ${err && err.message ? err.message : err}\n`,
1908
2126
  );
1909
- } else {
1910
- try {
1911
- await bootstrapPluginRuntime(pluginRoot, { readOnly, patchCodexHooks: false });
1912
- } catch (err) {
1913
- process.stderr.write(
1914
- `error: claude-smart OpenCode setup failed during dependency bootstrap: ${err && err.message ? err.message : err}\n`,
1915
- );
1916
- process.exit(1);
1917
- }
1918
- try {
1919
- result = patchOpenCodePluginConfig(opencodeConfigPath(args), { install: true });
1920
- } catch (err) {
1921
- process.stderr.write(`error: could not update OpenCode config: ${err && err.message ? err.message : err}\n`);
1922
- stopClaudeSmartServices(pluginRoot);
1923
- process.exit(1);
1924
- }
1925
- if (readOnly) {
1926
- process.stdout.write("Installed read-only hook manifest; publish interactions hooks are disabled.\n");
1927
- }
1928
- if (startBackendService(pluginRoot, "opencode")) {
1929
- process.stdout.write("Started claude-smart backend service.\n");
1930
- }
1931
- if (refreshDashboardService(pluginRoot)) {
1932
- process.stdout.write("Refreshed claude-smart dashboard service.\n");
1933
- }
2127
+ process.exit(1);
2128
+ }
2129
+ try {
2130
+ result = patchOpenCodePluginConfig(opencodeConfigPath(args), {
2131
+ install: true,
2132
+ pluginSpec,
2133
+ });
2134
+ } catch (err) {
2135
+ process.stderr.write(`error: could not update OpenCode config: ${err && err.message ? err.message : err}\n`);
2136
+ stopClaudeSmartServices(pluginRoot);
2137
+ process.exit(1);
2138
+ }
2139
+ if (readOnly) {
2140
+ process.stdout.write("Installed read-only hook manifest; publish interactions hooks are disabled.\n");
2141
+ }
2142
+ if (startBackendService(pluginRoot, "opencode")) {
2143
+ process.stdout.write("Started claude-smart backend service.\n");
2144
+ }
2145
+ if (refreshDashboardService(pluginRoot)) {
2146
+ process.stdout.write("Refreshed claude-smart dashboard service.\n");
1934
2147
  }
1935
2148
  if (result.backupPath) {
1936
2149
  process.stdout.write(`Saved a comment-preserving backup of your previous config at ${result.backupPath}.\n`);
@@ -1938,7 +2151,8 @@ async function runInstallOpenCode(args) {
1938
2151
  process.stdout.write(
1939
2152
  [
1940
2153
  "",
1941
- `${result.changed ? "Updated" : "OpenCode config already includes"} "${OPENCODE_PLUGIN_SPEC}" in ${result.configPath}.`,
2154
+ `${result.changed ? "Updated" : "OpenCode config already includes"} "${pluginSpec}" in ${result.configPath}.`,
2155
+ `Prepared claude-smart OpenCode package at ${packageRoot}.`,
1942
2156
  "claude-smart OpenCode support is installed.",
1943
2157
  "Restart OpenCode in your project so it loads the plugin.",
1944
2158
  "",
@@ -1975,6 +2189,7 @@ async function runUninstallCodex() {
1975
2189
 
1976
2190
  async function runUninstallOpenCode(args) {
1977
2191
  stopClaudeSmartServices(join(PACKAGE_ROOT, "plugin"));
2192
+ stopClaudeSmartServices(join(OPENCODE_LOCAL_PACKAGE_DIR, "plugin"));
1978
2193
  let result;
1979
2194
  try {
1980
2195
  result = patchOpenCodePluginConfig(opencodeConfigPath(args), { install: false });
@@ -1985,12 +2200,18 @@ async function runUninstallOpenCode(args) {
1985
2200
  if (result.backupPath) {
1986
2201
  process.stdout.write(`Saved a comment-preserving backup of your previous config at ${result.backupPath}.\n`);
1987
2202
  }
2203
+ rmSync(OPENCODE_LOCAL_PACKAGE_DIR, { recursive: true, force: true });
2204
+ try {
2205
+ rmdirSync(dirname(OPENCODE_LOCAL_PACKAGE_DIR));
2206
+ } catch {
2207
+ // Keep the parent when it still contains future OpenCode state.
2208
+ }
1988
2209
  process.stdout.write(
1989
2210
  [
1990
2211
  "",
1991
2212
  result.changed
1992
- ? `Removed "${OPENCODE_PLUGIN_SPEC}" from ${result.configPath}.`
1993
- : `OpenCode config did not include "${OPENCODE_PLUGIN_SPEC}".`,
2213
+ ? `Removed claude-smart OpenCode plugin entries from ${result.configPath}.`
2214
+ : "OpenCode config did not include claude-smart.",
1994
2215
  "Restart OpenCode to apply.",
1995
2216
  ...LOCAL_DATA_NOTICE,
1996
2217
  "",
@@ -2050,7 +2271,8 @@ module.exports = {
2050
2271
  configureReflexioSetup,
2051
2272
  patchCodexHooksForNode,
2052
2273
  opencodeConfigPath,
2053
- isNpxPackageRoot,
2274
+ opencodeLocalPluginSpec,
2275
+ installOpenCodePluginPackage,
2054
2276
  patchOpenCodePluginConfig,
2055
2277
  hasExtractionProvider,
2056
2278
  platformSupportError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.46",
3
+ "version": "0.2.48",
4
4
  "description": "Self-improving Claude Code, Codex, and OpenCode plugin that turns corrections into Preferences, Project-specific skills, and Shared skills",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.46",
3
+ "version": "0.2.48",
4
4
  "description": "Self-improving Claude Code, Codex, and OpenCode plugin that turns corrections into Preferences, Project-specific skills, and Shared skills",
5
5
  "author": {
6
6
  "name": "Yi Lu"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.46",
3
+ "version": "0.2.48",
4
4
  "description": "Self-improving coding assistant plugin — learns from corrections across sessions via reflexio",
5
5
  "author": {
6
6
  "name": "Yi Lu"