claude-smart 0.2.45 → 0.2.47

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 (353) hide show
  1. package/.claude-plugin/marketplace.json +3 -3
  2. package/README.md +47 -10
  3. package/bin/claude-smart.js +535 -23
  4. package/package.json +22 -3
  5. package/plugin/.claude-plugin/plugin.json +4 -2
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/README.md +27 -6
  8. package/plugin/commands/clear-all.md +1 -0
  9. package/plugin/commands/dashboard.md +1 -0
  10. package/plugin/commands/learn.md +1 -0
  11. package/plugin/commands/restart.md +1 -0
  12. package/plugin/commands/show.md +1 -0
  13. package/plugin/dashboard/app/configure/env/page.tsx +40 -14
  14. package/plugin/dashboard/app/configure/server/page.tsx +51 -1
  15. package/plugin/dashboard/app/layout.tsx +20 -0
  16. package/plugin/dashboard/app/preferences/[id]/page.tsx +8 -8
  17. package/plugin/dashboard/app/skills/project/[id]/page.tsx +7 -3
  18. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +10 -3
  19. package/plugin/dashboard/lib/claude-settings-file.ts +20 -10
  20. package/plugin/dashboard/lib/reflexio-client.ts +16 -0
  21. package/plugin/dashboard/lib/status.ts +10 -3
  22. package/plugin/dashboard/lib/types.ts +18 -6
  23. package/plugin/hooks/codex-hooks.json +0 -1
  24. package/plugin/opencode/assistant-buffer.ts +108 -0
  25. package/plugin/opencode/dist/assistant-buffer.js +96 -0
  26. package/plugin/opencode/dist/internal.js +12 -0
  27. package/plugin/opencode/dist/payload.js +85 -0
  28. package/plugin/opencode/dist/server.mjs +232 -0
  29. package/plugin/opencode/internal.ts +18 -0
  30. package/plugin/opencode/package.json +3 -0
  31. package/plugin/opencode/payload.ts +90 -0
  32. package/plugin/opencode/server.mts +251 -0
  33. package/plugin/opencode/tsconfig.json +13 -0
  34. package/plugin/pyproject.toml +25 -4
  35. package/plugin/scripts/_lib.sh +5 -1
  36. package/plugin/scripts/backend-service.sh +22 -2
  37. package/plugin/scripts/ensure-plugin-root.sh +4 -4
  38. package/plugin/scripts/hook_entry.sh +1 -1
  39. package/plugin/scripts/opencode-claude-compat +4 -0
  40. package/plugin/scripts/opencode-claude-compat.cmd +3 -0
  41. package/plugin/scripts/opencode-claude-compat.js +225 -0
  42. package/plugin/scripts/smart-install.sh +17 -9
  43. package/plugin/src/README.md +1 -1
  44. package/plugin/src/claude_smart/cli.py +498 -12
  45. package/plugin/src/claude_smart/context_format.py +9 -9
  46. package/plugin/src/claude_smart/cs_cite.py +66 -19
  47. package/plugin/src/claude_smart/env_config.py +12 -4
  48. package/plugin/src/claude_smart/events/session_start.py +26 -7
  49. package/plugin/src/claude_smart/events/stop.py +1 -1
  50. package/plugin/src/claude_smart/ids.py +1 -1
  51. package/plugin/src/claude_smart/reflexio_adapter.py +5 -5
  52. package/plugin/src/claude_smart/runtime.py +7 -1
  53. package/plugin/uv.lock +5 -5
  54. package/plugin/vendor/reflexio/.env.example +54 -30
  55. package/plugin/vendor/reflexio/README.md +17 -11
  56. package/plugin/vendor/reflexio/pyproject.toml +13 -1
  57. package/plugin/vendor/reflexio/reflexio/README.md +4 -1
  58. package/plugin/vendor/reflexio/reflexio/cli/README.md +9 -7
  59. package/plugin/vendor/reflexio/reflexio/cli/__main__.py +10 -1
  60. package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +22 -5
  61. package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +34 -4
  62. package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +12 -7
  63. package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +13 -6
  64. package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +212 -52
  65. package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +15 -2
  66. package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +292 -22
  67. package/plugin/vendor/reflexio/reflexio/cli/log_format.py +29 -4
  68. package/plugin/vendor/reflexio/reflexio/cli/run_services.py +17 -8
  69. package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +13 -10
  70. package/plugin/vendor/reflexio/reflexio/cli/utils.py +44 -1
  71. package/plugin/vendor/reflexio/reflexio/client/client.py +154 -13
  72. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +135 -257
  73. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/bin/openclaw-smart.js +13 -0
  74. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/npm/openclaw-smart/package.json +15 -0
  75. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/README.md +38 -0
  76. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +151 -110
  77. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +20 -10
  78. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +28 -6
  79. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/pyproject.toml +42 -0
  80. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/_lib.sh +371 -0
  81. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-log-runner.sh +33 -0
  82. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/backend-service.sh +271 -0
  83. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/cli.sh +68 -0
  84. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/dashboard-open.sh +15 -0
  85. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/ensure-plugin-root.sh +84 -0
  86. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/hook_entry.sh +106 -0
  87. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/npm-cli.js +219 -0
  88. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/scripts/smart-install.sh +269 -0
  89. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/clear-all/SKILL.md +8 -0
  90. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/dashboard/SKILL.md +8 -0
  91. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/learn/SKILL.md +10 -0
  92. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +15 -44
  93. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/restart/SKILL.md +6 -0
  94. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/show/SKILL.md +8 -0
  95. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/cli.py +634 -0
  96. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_format.py +224 -0
  97. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/context_inject.py +79 -0
  98. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/__init__.py +0 -0
  99. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/after_tool_call.py +160 -0
  100. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/agent_end.py +187 -0
  101. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_prompt_build.py +69 -0
  102. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/before_tool_call.py +30 -0
  103. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_end.py +36 -0
  104. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/events/session_start.py +130 -0
  105. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/hook.py +131 -0
  106. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/ids.py +94 -0
  107. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/internal_call.py +75 -0
  108. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/oc_cite.py +196 -0
  109. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/optimizer_assistant.py +272 -0
  110. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/publish.py +96 -0
  111. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/query_compose.py +66 -0
  112. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/reflexio_adapter.py +336 -0
  113. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/runtime.py +47 -0
  114. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/stall_banner.py +61 -0
  115. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +323 -0
  116. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/__init__.py +0 -0
  117. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/__init__.py +0 -0
  118. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_e2e_session_loop.py +190 -0
  119. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_publish_to_local_reflexio_integration.py +112 -0
  120. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_recursion_guard_integration.py +86 -0
  121. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/integration/test_search_inject_integration.py +144 -0
  122. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_cli.py +184 -0
  123. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_after_tool_call.py +142 -0
  124. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_agent_end.py +233 -0
  125. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_prompt_build.py +116 -0
  126. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_before_tool_call.py +35 -0
  127. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_end.py +47 -0
  128. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_events_session_start.py +109 -0
  129. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_hook_dispatch.py +117 -0
  130. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_ids.py +41 -0
  131. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_internal_call.py +50 -0
  132. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_oc_cite.py +88 -0
  133. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_publish.py +48 -0
  134. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_query_compose.py +48 -0
  135. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_reflexio_adapter.py +188 -0
  136. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_runtime.py +41 -0
  137. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +235 -0
  138. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/__mocks__/plugin-entry-stub.ts +6 -0
  139. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_npm_cli.test.ts +56 -0
  140. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests-ts/test_shim_dispatch.test.ts +171 -0
  141. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tsconfig.build.json +18 -0
  142. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{tsconfig.json → plugin/tsconfig.json} +2 -2
  143. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/uv.lock +3835 -0
  144. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/vitest.config.ts +18 -0
  145. package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +246 -10
  146. package/plugin/vendor/reflexio/reflexio/lib/_base.py +15 -0
  147. package/plugin/vendor/reflexio/reflexio/lib/_config.py +11 -4
  148. package/plugin/vendor/reflexio/reflexio/lib/_generation.py +17 -5
  149. package/plugin/vendor/reflexio/reflexio/lib/_lineage_parity_readers.py +187 -0
  150. package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +224 -21
  151. package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +1 -1
  152. package/plugin/vendor/reflexio/reflexio/lib/_search.py +52 -25
  153. package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +11 -1
  154. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +1 -0
  155. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +127 -38
  156. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +4 -0
  157. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/governance.py +117 -0
  158. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +64 -41
  159. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +19 -0
  160. package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +12 -5
  161. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +97 -3
  162. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +5 -1
  163. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +5 -1
  164. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +104 -7
  165. package/plugin/vendor/reflexio/reflexio/models/structured_output.py +148 -0
  166. package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +3 -3
  167. package/plugin/vendor/reflexio/reflexio/server/README.md +58 -33
  168. package/plugin/vendor/reflexio/reflexio/server/__init__.py +29 -5
  169. package/plugin/vendor/reflexio/reflexio/server/__main__.py +2 -2
  170. package/plugin/vendor/reflexio/reflexio/server/api.py +899 -103
  171. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +7 -0
  172. package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +4 -2
  173. package/plugin/vendor/reflexio/reflexio/server/auth.py +90 -0
  174. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +173 -0
  175. package/plugin/vendor/reflexio/reflexio/server/billing_signals.py +64 -0
  176. package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +46 -16
  177. package/plugin/vendor/reflexio/reflexio/server/env_utils.py +65 -0
  178. package/plugin/vendor/reflexio/reflexio/server/extensions.py +213 -0
  179. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -4
  180. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +264 -77
  181. package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +69 -7
  182. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +4 -4
  183. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1 -1
  184. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +75 -16
  185. package/plugin/vendor/reflexio/reflexio/server/llm/providers/openclaw_provider.py +280 -0
  186. package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +12 -1
  187. package/plugin/vendor/reflexio/reflexio/server/llm/token_accounting.py +48 -0
  188. package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +107 -17
  189. package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +360 -4
  190. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +1 -1
  191. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.7.0.prompt.md +85 -0
  192. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +1 -1
  193. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.3.0.prompt.md +253 -0
  194. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.2.prompt.md +1 -1
  195. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +74 -0
  196. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.3.prompt.md +1 -1
  197. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.3.0.prompt.md +266 -0
  198. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.4.0.prompt.md +278 -0
  199. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +1 -1
  200. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.4.0.prompt.md +137 -0
  201. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +1 -1
  202. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.3.0.prompt.md +34 -0
  203. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +5 -12
  204. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +5 -12
  205. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.2.0.prompt.md +155 -0
  206. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/tagging/v1.0.0.prompt.md +23 -0
  207. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +89 -32
  208. package/plugin/vendor/reflexio/reflexio/server/services/README.md +28 -11
  209. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +22 -0
  210. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/__init__.py +1 -0
  211. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +4 -2
  212. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +1 -0
  213. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/__init__.py +7 -0
  214. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluator.py → components/evaluator.py} +3 -16
  215. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +28 -72
  216. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{group_evaluation_runner.py → runner.py} +12 -10
  217. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{delayed_group_evaluator.py → scheduler.py} +10 -2
  218. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/{agent_success_evaluation_service.py → service.py} +36 -1
  219. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +160 -0
  220. package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +5 -0
  221. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +1 -1
  222. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/README.md +9 -0
  223. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/__init__.py +0 -0
  224. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{rule_attribution.py → components/rule_attribution.py} +10 -9
  225. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +15 -24
  226. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +238 -164
  227. package/plugin/vendor/reflexio/reflexio/server/services/extraction/README.md +31 -0
  228. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +13 -3
  229. package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +3 -2
  230. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +32 -118
  231. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +47 -16
  232. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +105 -23
  233. package/plugin/vendor/reflexio/reflexio/server/services/governance/config.py +52 -0
  234. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +378 -0
  235. package/plugin/vendor/reflexio/reflexio/server/services/governance/subject_refs.py +34 -0
  236. package/plugin/vendor/reflexio/reflexio/server/services/lineage/__init__.py +0 -0
  237. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +219 -0
  238. package/plugin/vendor/reflexio/reflexio/server/services/lineage/resolve.py +98 -0
  239. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +26 -0
  240. package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +18 -11
  241. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_prompt_processing.py +100 -0
  242. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/__init__.py +5 -0
  243. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_aggregator.py → components/aggregator.py} +334 -178
  244. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_consolidator.py → components/consolidator.py} +168 -62
  245. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_extractor.py → components/extractor.py} +5 -1
  246. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +74 -0
  247. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +33 -3
  248. package/plugin/vendor/reflexio/reflexio/server/services/playbook/{playbook_generation_service.py → service.py} +121 -19
  249. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/README.md +10 -0
  250. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +2 -1
  251. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +178 -30
  252. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/README.md +9 -0
  253. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/__init__.py +17 -0
  254. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_deduplicator.py → components/consolidator.py} +7 -6
  255. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_extractor.py → components/extractor.py} +31 -22
  256. package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +6 -13
  257. package/plugin/vendor/reflexio/reflexio/server/services/profile/{profile_generation_service.py → service.py} +32 -50
  258. package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +4 -8
  259. package/plugin/vendor/reflexio/reflexio/server/services/reflection/components/__init__.py +7 -0
  260. package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +12 -1
  261. package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_service.py → service.py} +36 -16
  262. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/recency.py +211 -0
  263. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +99 -6
  264. package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +36 -1
  265. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/README.md +8 -0
  266. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +1 -2
  267. package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +31 -0
  268. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +681 -0
  269. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +9 -9
  270. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +16 -2
  271. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +4 -1
  272. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +644 -197
  273. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +106 -119
  274. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +1965 -0
  275. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +630 -0
  276. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +38 -6
  277. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +31 -1369
  278. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +756 -210
  279. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +148 -34
  280. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +23 -9
  281. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +5 -2
  282. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/__init__.py +13 -0
  283. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +952 -0
  284. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +189 -0
  285. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_optimization.py +247 -0
  286. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +145 -0
  287. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +838 -0
  288. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +150 -23
  289. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +2 -8
  290. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +5 -0
  291. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +40 -57
  292. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_governance.py +148 -0
  293. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_lineage.py +215 -0
  294. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +20 -0
  295. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +4 -677
  296. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +134 -1
  297. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +62 -0
  298. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +51 -0
  299. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +17 -0
  300. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/__init__.py +13 -0
  301. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +365 -0
  302. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +124 -0
  303. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_optimization.py +85 -0
  304. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_source_linkage.py +47 -0
  305. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +333 -0
  306. package/plugin/vendor/reflexio/reflexio/server/services/tagging/README.md +8 -0
  307. package/plugin/vendor/reflexio/reflexio/server/services/tagging/__init__.py +1 -0
  308. package/plugin/vendor/reflexio/reflexio/server/services/tagging/service.py +200 -0
  309. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +149 -0
  310. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +372 -34
  311. package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +2 -1
  312. package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +120 -1
  313. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +4 -0
  314. package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/search_settings.json +5 -0
  315. package/plugin/vendor/reflexio/reflexio/server/tracing.py +30 -0
  316. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +18 -0
  317. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -26
  318. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +41 -4
  319. package/scripts/setup-claude-smart.sh +8 -3
  320. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +0 -517
  321. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +0 -473
  322. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +0 -2156
  323. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +0 -18
  324. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +0 -241
  325. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +0 -140
  326. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +0 -113
  327. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +0 -52
  328. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +0 -103
  329. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +0 -156
  330. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +0 -134
  331. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +0 -24
  332. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +0 -278
  333. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +0 -164
  334. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +0 -36
  335. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +0 -35
  336. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +0 -27
  337. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +0 -31
  338. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +0 -42
  339. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +0 -49
  340. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +0 -91
  341. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +0 -50
  342. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +0 -13
  343. package/plugin/vendor/reflexio/reflexio/server/_auth.py +0 -25
  344. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +0 -192
  345. package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +0 -303
  346. package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +0 -138
  347. package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +0 -1125
  348. /package/plugin/vendor/reflexio/reflexio/integrations/{__init__.py → openclaw/plugin/src/openclaw_smart/__init__.py} +0 -0
  349. /package/plugin/vendor/reflexio/reflexio/integrations/openclaw/{types → plugin/types}/openclaw.d.ts +0 -0
  350. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{distribution.py → components/distribution.py} +0 -0
  351. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{hero_state.py → components/hero_state.py} +0 -0
  352. /package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/{shadow_aggregation.py → components/shadow_aggregation.py} +0 -0
  353. /package/plugin/vendor/reflexio/reflexio/server/services/reflection/{reflection_extractor.py → components/extractor.py} +0 -0
@@ -3,7 +3,7 @@
3
3
  Exposes the following subcommands:
4
4
 
5
5
  - ``install``: register the GitHub marketplace and install the plugin into
6
- Claude Code, then seed ``~/.reflexio/.env`` with the local-provider flags.
6
+ Claude Code, then seed ``~/.claude-smart/.env`` with the local-provider flags.
7
7
  - ``update``: update the plugin to the latest version via the native Claude
8
8
  Code plugin CLI.
9
9
  - ``uninstall``: remove the plugin from Claude Code via the native plugin
@@ -21,6 +21,10 @@ Exposes the following subcommands:
21
21
  from __future__ import annotations
22
22
 
23
23
  import argparse
24
+ from collections.abc import Iterable, Iterator
25
+ from contextlib import contextmanager
26
+ from dataclasses import dataclass
27
+ from hashlib import sha256
24
28
  import json
25
29
  import os
26
30
  import re
@@ -29,11 +33,11 @@ import shutil
29
33
  import subprocess
30
34
  import sys
31
35
  import time
32
- from collections.abc import Iterable
33
- from dataclasses import dataclass
34
36
  from pathlib import Path
37
+ from urllib.parse import urlparse
38
+ from urllib.request import url2pathname
35
39
 
36
- from claude_smart import context_format, env_config, ids, publish, state
40
+ from claude_smart import context_format, cs_cite, env_config, ids, publish, state
37
41
  from claude_smart.reflexio_adapter import Adapter
38
42
 
39
43
  _REFLEXIO_ENV_PATH = env_config.REFLEXIO_ENV_PATH
@@ -56,6 +60,8 @@ _CODEX_PLUGIN_CACHE_DIR = (
56
60
  / "claude-smart"
57
61
  )
58
62
  _CODEX_CLI_TIMEOUT_SECONDS = 30
63
+ _OPENCODE_BARE_PLUGIN_SPEC = "claude-smart"
64
+ _OPENCODE_CONFIG_NAMES = ("opencode.json", "opencode.jsonc")
59
65
  _REFLEXIO_UNREACHABLE_MSG = (
60
66
  "Failed to reach reflexio. Check ~/.claude-smart/backend.log "
61
67
  "or restart Claude Code.\n"
@@ -70,6 +76,9 @@ _BACKEND_SCRIPT = _SCRIPTS_DIR / "backend-service.sh"
70
76
  _DASHBOARD_SCRIPT = _SCRIPTS_DIR / "dashboard-service.sh"
71
77
  _REFLEXIO_DIR = Path.home() / ".reflexio"
72
78
  _STATE_DIR = Path.home() / ".claude-smart"
79
+ _OPENCODE_LOCAL_PACKAGE_DIR = _STATE_DIR / "opencode" / "claude-smart"
80
+ _OPENCODE_PACKAGE_LOCK_TIMEOUT_SECONDS = 120.0
81
+ _OPENCODE_PACKAGE_LOCK_STALE_SECONDS = 10 * 60.0
73
82
  _LOCAL_DATA_NOTICE = (
74
83
  "Local data was kept so reinstalling claude-smart can reuse your learned "
75
84
  "rules, sessions, logs, and local Reflexio data.\n"
@@ -83,7 +92,12 @@ _LOCAL_DATA_NOTICE = (
83
92
  _INSTALL_FAILURE_MARKER = _STATE_DIR / "install-failed"
84
93
  _SERVICE_STATUS_PROBE_FAILED = "probe_failed"
85
94
  _DEFAULT_STORAGE_ROOT = _REFLEXIO_DIR / "data"
86
- _REFLEXIO_CONFIG_PATH = _REFLEXIO_DIR / "configs" / "config_self-host-org.json"
95
+ # Org id claude-smart's backend runs under (set via REFLEXIO_DEFAULT_ORG_ID in
96
+ # backend-service.sh). reflexio names the config file config_<org>.json, so this
97
+ # must stay in sync with that export; using our own org keeps us off the
98
+ # enterprise self-host backend's config_self-host-org.json.
99
+ _REFLEXIO_ORG_ID = "claude-smart"
100
+ _REFLEXIO_CONFIG_PATH = _REFLEXIO_DIR / "configs" / f"config_{_REFLEXIO_ORG_ID}.json"
87
101
  _LOCAL_STORAGE_ENV = "LOCAL_STORAGE_PATH"
88
102
  _CODEX_REQUIRED_FILES = (
89
103
  Path(".agents/plugins/marketplace.json"),
@@ -92,6 +106,9 @@ _CODEX_REQUIRED_FILES = (
92
106
  Path("plugin/scripts/codex-claude-compat"),
93
107
  Path("plugin/scripts/codex-claude-compat.cmd"),
94
108
  Path("plugin/scripts/codex-claude-compat.js"),
109
+ Path("plugin/scripts/opencode-claude-compat"),
110
+ Path("plugin/scripts/opencode-claude-compat.cmd"),
111
+ Path("plugin/scripts/opencode-claude-compat.js"),
95
112
  Path("plugin/scripts/codex-hook.js"),
96
113
  Path("plugin/scripts/backend-log-runner.sh"),
97
114
  Path("plugin/scripts/_codex_env.sh"),
@@ -103,6 +120,7 @@ _COPYTREE_IGNORE = shutil.ignore_patterns(
103
120
  "*.pyo",
104
121
  ".pytest_cache",
105
122
  ".ruff_cache",
123
+ ".git",
106
124
  ".next",
107
125
  "node_modules",
108
126
  )
@@ -963,6 +981,391 @@ def _configure_reflexio_setup() -> bool:
963
981
  return read_only
964
982
 
965
983
 
984
+ def _strip_jsonc(text: str) -> str:
985
+ """Remove JSONC comments and trailing commas without touching strings."""
986
+
987
+ def skip_trivia(index: int) -> int:
988
+ while index < len(text):
989
+ while index < len(text) and text[index].isspace():
990
+ index += 1
991
+ if text[index : index + 2] == "//":
992
+ index += 2
993
+ while index < len(text) and text[index] not in "\r\n":
994
+ index += 1
995
+ continue
996
+ if text[index : index + 2] == "/*":
997
+ index += 2
998
+ while index + 1 < len(text) and text[index : index + 2] != "*/":
999
+ index += 1
1000
+ index = min(index + 2, len(text))
1001
+ continue
1002
+ break
1003
+ return index
1004
+
1005
+ out: list[str] = []
1006
+ in_string = False
1007
+ escaped = False
1008
+ i = 0
1009
+ while i < len(text):
1010
+ ch = text[i]
1011
+ nxt = text[i + 1] if i + 1 < len(text) else ""
1012
+ if in_string:
1013
+ out.append(ch)
1014
+ if escaped:
1015
+ escaped = False
1016
+ elif ch == "\\":
1017
+ escaped = True
1018
+ elif ch == '"':
1019
+ in_string = False
1020
+ i += 1
1021
+ continue
1022
+ if ch == '"':
1023
+ in_string = True
1024
+ out.append(ch)
1025
+ i += 1
1026
+ continue
1027
+ if ch == "/" and nxt == "/":
1028
+ while i < len(text) and text[i] not in "\r\n":
1029
+ i += 1
1030
+ continue
1031
+ if ch == "/" and nxt == "*":
1032
+ i += 2
1033
+ while i + 1 < len(text) and not (text[i] == "*" and text[i + 1] == "/"):
1034
+ i += 1
1035
+ i += 2
1036
+ continue
1037
+ if ch == ",":
1038
+ j = skip_trivia(i + 1)
1039
+ if j < len(text) and text[j] in "}]":
1040
+ i += 1
1041
+ continue
1042
+ out.append(ch)
1043
+ i += 1
1044
+ return "".join(out)
1045
+
1046
+
1047
+ def _read_jsonc_object(path: Path) -> dict[str, object]:
1048
+ try:
1049
+ text = path.read_text() if path.exists() else "{}"
1050
+ parsed = json.loads(_strip_jsonc(text or "{}"))
1051
+ except (OSError, json.JSONDecodeError) as exc:
1052
+ raise ValueError(f"could not parse OpenCode config {path}: {exc}") from exc
1053
+ if not isinstance(parsed, dict):
1054
+ raise ValueError(f"OpenCode config {path} must be a JSON object")
1055
+ return parsed
1056
+
1057
+
1058
+ def _opencode_global_config_dir() -> Path:
1059
+ xdg = os.environ.get("XDG_CONFIG_HOME", "").strip()
1060
+ base = Path(xdg) if xdg else Path.home() / ".config"
1061
+ return base / "opencode"
1062
+
1063
+
1064
+ def _opencode_config_path(*, global_config: bool = False, cwd: Path | None = None) -> Path:
1065
+ if global_config:
1066
+ config_dir = _opencode_global_config_dir()
1067
+ for name in _OPENCODE_CONFIG_NAMES:
1068
+ candidate = config_dir / name
1069
+ if candidate.exists():
1070
+ return candidate
1071
+ return config_dir / "opencode.json"
1072
+ base = cwd or Path.cwd()
1073
+ candidates = [
1074
+ *(base / name for name in _OPENCODE_CONFIG_NAMES),
1075
+ *(base / ".opencode" / name for name in _OPENCODE_CONFIG_NAMES),
1076
+ ]
1077
+ for candidate in candidates:
1078
+ if candidate.exists():
1079
+ return candidate
1080
+ return base / "opencode.json"
1081
+
1082
+
1083
+ def _opencode_plugin_spec(entry: object) -> str | None:
1084
+ if isinstance(entry, str):
1085
+ return entry
1086
+ if isinstance(entry, list) and entry and isinstance(entry[0], str):
1087
+ return entry[0]
1088
+ return None
1089
+
1090
+
1091
+ def _opencode_local_plugin_spec(
1092
+ package_root: Path = _OPENCODE_LOCAL_PACKAGE_DIR,
1093
+ ) -> str:
1094
+ return package_root.resolve(strict=False).as_uri()
1095
+
1096
+
1097
+ def _is_default_opencode_package_path(package_path: Path) -> bool:
1098
+ if _same_real_path(package_path, _OPENCODE_LOCAL_PACKAGE_DIR):
1099
+ return True
1100
+ try:
1101
+ return package_path.resolve(strict=False) == _OPENCODE_LOCAL_PACKAGE_DIR.resolve(
1102
+ strict=False
1103
+ )
1104
+ except OSError:
1105
+ return False
1106
+
1107
+
1108
+ def _is_opencode_claude_smart_spec(spec: str | None) -> bool:
1109
+ if not spec:
1110
+ return False
1111
+ if spec == _OPENCODE_BARE_PLUGIN_SPEC or spec.startswith(
1112
+ f"{_OPENCODE_BARE_PLUGIN_SPEC}@"
1113
+ ):
1114
+ return True
1115
+ parsed = urlparse(spec)
1116
+ if parsed.scheme != "file":
1117
+ return False
1118
+ package_path = Path(url2pathname(parsed.path))
1119
+ if _is_default_opencode_package_path(package_path):
1120
+ return True
1121
+ try:
1122
+ manifest = json.loads((package_path / "package.json").read_text())
1123
+ if manifest.get("name") == _OPENCODE_BARE_PLUGIN_SPEC:
1124
+ return True
1125
+ except (OSError, json.JSONDecodeError):
1126
+ pass
1127
+ return False
1128
+
1129
+
1130
+ def _patch_opencode_plugin_config(
1131
+ config_path: Path, *, install: bool, plugin_spec: str | None = None
1132
+ ) -> tuple[bool, Path]:
1133
+ if install and plugin_spec is None:
1134
+ plugin_spec = _opencode_local_plugin_spec()
1135
+ data = _read_jsonc_object(config_path)
1136
+ for field in ("plugins", "plugin"):
1137
+ value = data.get(field)
1138
+ if value is not None and not isinstance(value, list):
1139
+ raise ValueError(
1140
+ f'OpenCode config {config_path} field "{field}" must be a JSON array'
1141
+ )
1142
+ current_plugin = data.get("plugin")
1143
+ plugins: list[object] = []
1144
+ if isinstance(current_plugin, list):
1145
+ plugins.extend(current_plugin)
1146
+ legacy_plugins = data.get("plugins")
1147
+ if isinstance(legacy_plugins, list):
1148
+ plugins.extend(legacy_plugins)
1149
+ kept = [
1150
+ item
1151
+ for item in plugins
1152
+ if not _is_opencode_claude_smart_spec(_opencode_plugin_spec(item))
1153
+ ]
1154
+ if install:
1155
+ assert plugin_spec is not None
1156
+ next_plugins = [*kept, plugin_spec]
1157
+ else:
1158
+ next_plugins = kept
1159
+ if isinstance(current_plugin, list):
1160
+ changed = ("plugins" in data) or next_plugins != current_plugin
1161
+ else:
1162
+ changed = ("plugins" in data) or (install and bool(next_plugins))
1163
+ if not changed:
1164
+ return False, config_path
1165
+ if config_path.exists():
1166
+ original = config_path.read_text()
1167
+ if original.strip() and original != _strip_jsonc(original):
1168
+ config_path.with_suffix(config_path.suffix + ".bak").write_text(original)
1169
+ data["plugin"] = next_plugins
1170
+ data.pop("plugins", None)
1171
+ config_path.parent.mkdir(parents=True, exist_ok=True)
1172
+ config_path.write_text(json.dumps(data, indent=2) + "\n")
1173
+ return True, config_path
1174
+
1175
+
1176
+ def _has_extraction_provider() -> bool:
1177
+ env_config.load_reflexio_env(_REFLEXIO_ENV_PATH)
1178
+ if os.environ.get(env_config.REFLEXIO_API_KEY_ENV, "").strip():
1179
+ return True
1180
+ cli_path = os.environ.get("CLAUDE_SMART_CLI_PATH", "").strip()
1181
+ if cli_path:
1182
+ resolved = Path(cli_path).expanduser()
1183
+ if resolved.is_file() and os.access(resolved, os.X_OK):
1184
+ return True
1185
+ return bool(shutil.which("claude") or shutil.which("codex") or shutil.which("opencode"))
1186
+
1187
+
1188
+ def _extraction_provider_error() -> str:
1189
+ return (
1190
+ "error: OpenCode support needs a working learning/extraction provider.\n"
1191
+ "Run `npx claude-smart setup` to configure Reflexio, or install "
1192
+ "OpenCode, Claude Code, or Codex so local extraction can use a supported CLI.\n"
1193
+ )
1194
+
1195
+
1196
+ def _opencode_install_supported_from_this_package() -> bool:
1197
+ return (_SCRIPTS_DIR / "smart-install.sh").is_file()
1198
+
1199
+
1200
+ def _file_sha256(path: Path) -> str:
1201
+ return sha256(path.read_bytes()).hexdigest()
1202
+
1203
+
1204
+ def _same_real_path(left: Path, right: Path) -> bool:
1205
+ try:
1206
+ return left.resolve(strict=True) == right.resolve(strict=True)
1207
+ except OSError:
1208
+ return False
1209
+
1210
+
1211
+ def _verify_opencode_plugin_package(package_root: Path) -> None:
1212
+ source_script = _PLUGIN_ROOT / "scripts" / "smart-install.sh"
1213
+ copied_script = package_root / "plugin" / "scripts" / "smart-install.sh"
1214
+ for path in (package_root / "package.json", copied_script):
1215
+ if not path.exists():
1216
+ raise OSError(f"OpenCode local plugin package is missing {path}")
1217
+ if _file_sha256(source_script) != _file_sha256(copied_script):
1218
+ raise OSError(
1219
+ "OpenCode local plugin package does not match the installed claude-smart package"
1220
+ )
1221
+
1222
+
1223
+ @contextmanager
1224
+ def _opencode_package_install_lock(package_root: Path) -> Iterator[None]:
1225
+ lock_dir = package_root.parent / ".install.lock"
1226
+ package_root.parent.mkdir(parents=True, exist_ok=True)
1227
+ deadline = time.monotonic() + _OPENCODE_PACKAGE_LOCK_TIMEOUT_SECONDS
1228
+ while True:
1229
+ try:
1230
+ lock_dir.mkdir()
1231
+ break
1232
+ except FileExistsError:
1233
+ try:
1234
+ stale = (
1235
+ time.time() - lock_dir.stat().st_mtime
1236
+ > _OPENCODE_PACKAGE_LOCK_STALE_SECONDS
1237
+ )
1238
+ except OSError:
1239
+ stale = False
1240
+ if stale:
1241
+ shutil.rmtree(lock_dir, ignore_errors=True)
1242
+ continue
1243
+ if time.monotonic() >= deadline:
1244
+ raise OSError(
1245
+ f"timed out waiting for OpenCode package install lock at {lock_dir}"
1246
+ )
1247
+ time.sleep(0.1)
1248
+ try:
1249
+ yield
1250
+ finally:
1251
+ shutil.rmtree(lock_dir, ignore_errors=True)
1252
+
1253
+
1254
+ def _unique_opencode_package_path(parent: Path, prefix: str) -> Path:
1255
+ return parent / f"{prefix}-{os.getpid()}-{time.monotonic_ns()}"
1256
+
1257
+
1258
+ def _replace_opencode_plugin_package(staged_package: Path, package_root: Path) -> None:
1259
+ backup_package = _unique_opencode_package_path(
1260
+ package_root.parent, ".claude-smart-previous"
1261
+ )
1262
+ backup_created = False
1263
+ try:
1264
+ if package_root.exists() or package_root.is_symlink():
1265
+ package_root.rename(backup_package)
1266
+ backup_created = True
1267
+ staged_package.rename(package_root)
1268
+ if backup_created:
1269
+ shutil.rmtree(backup_package, ignore_errors=True)
1270
+ except OSError:
1271
+ if backup_created and not package_root.exists() and backup_package.exists():
1272
+ backup_package.rename(package_root)
1273
+ raise
1274
+
1275
+
1276
+ def _install_opencode_plugin_package() -> Path:
1277
+ package_root = _OPENCODE_LOCAL_PACKAGE_DIR
1278
+ if _same_real_path(_REPO_ROOT, package_root):
1279
+ _verify_opencode_plugin_package(package_root)
1280
+ return package_root
1281
+ with _opencode_package_install_lock(package_root):
1282
+ staged_package = _unique_opencode_package_path(
1283
+ package_root.parent, ".claude-smart-copy"
1284
+ )
1285
+ shutil.rmtree(staged_package, ignore_errors=True)
1286
+ try:
1287
+ shutil.copytree(_REPO_ROOT, staged_package, ignore=_COPYTREE_IGNORE)
1288
+ _verify_opencode_plugin_package(staged_package)
1289
+ _replace_opencode_plugin_package(staged_package, package_root)
1290
+ _verify_opencode_plugin_package(package_root)
1291
+ finally:
1292
+ shutil.rmtree(staged_package, ignore_errors=True)
1293
+ return package_root
1294
+
1295
+
1296
+ def _bootstrap_opencode_install(read_only: bool) -> tuple[bool, str]:
1297
+ if not _opencode_install_supported_from_this_package():
1298
+ return (
1299
+ False,
1300
+ "OpenCode install is supported from the npm package. "
1301
+ "Run `npx claude-smart install --host opencode`.",
1302
+ )
1303
+ try:
1304
+ package_root = _install_opencode_plugin_package()
1305
+ except OSError as exc:
1306
+ return False, str(exc)
1307
+ plugin_root = package_root / "plugin"
1308
+ try:
1309
+ _force_plugin_root(plugin_root)
1310
+ except OSError as exc:
1311
+ return False, str(exc)
1312
+ bash = _resolve_bash()
1313
+ if not bash:
1314
+ return False, "bash is required to bootstrap claude-smart dependencies"
1315
+ scripts_dir = plugin_root / "scripts"
1316
+ result = subprocess.run([bash, str(scripts_dir / "smart-install.sh")], cwd=plugin_root)
1317
+ if result.returncode != 0:
1318
+ return False, f"smart-install.sh failed in {plugin_root}"
1319
+ if _INSTALL_FAILURE_MARKER.is_file():
1320
+ first_line = _INSTALL_FAILURE_MARKER.read_text().splitlines()
1321
+ reason = (first_line[0].strip() if first_line else "") or "unknown error"
1322
+ return False, reason
1323
+ if read_only:
1324
+ _prune_publish_hooks_for_read_only(plugin_root)
1325
+ return True, str(package_root)
1326
+
1327
+
1328
+ def cmd_install_opencode(args: argparse.Namespace) -> int:
1329
+ if not _opencode_install_supported_from_this_package():
1330
+ sys.stderr.write(
1331
+ "error: OpenCode install is supported from the npm package. "
1332
+ "Run `npx claude-smart install --host opencode`.\n"
1333
+ )
1334
+ return 1
1335
+ read_only = _configure_reflexio_setup()
1336
+ if not _has_extraction_provider():
1337
+ sys.stderr.write(_extraction_provider_error())
1338
+ return 1
1339
+ bootstrapped, package_root_text = _bootstrap_opencode_install(read_only)
1340
+ if not bootstrapped:
1341
+ sys.stderr.write(
1342
+ f"error: claude-smart OpenCode setup failed during dependency bootstrap: {package_root_text}\n"
1343
+ )
1344
+ return 1
1345
+ package_root = Path(package_root_text)
1346
+ plugin_root = package_root / "plugin"
1347
+ plugin_spec = _opencode_local_plugin_spec(package_root)
1348
+ config_path = _opencode_config_path(
1349
+ global_config=bool(getattr(args, "global_config", False))
1350
+ )
1351
+ try:
1352
+ changed, config_path = _patch_opencode_plugin_config(
1353
+ config_path,
1354
+ install=True,
1355
+ plugin_spec=plugin_spec,
1356
+ )
1357
+ except ValueError as exc:
1358
+ sys.stderr.write(f"error: {exc}\n")
1359
+ return 1
1360
+ action = "Updated" if changed else "OpenCode config already includes"
1361
+ sys.stdout.write(
1362
+ f"{action} `{plugin_spec}` in {config_path}.\n"
1363
+ f"Prepared claude-smart runtime at {plugin_root}.\n"
1364
+ "Restart OpenCode in your project so it loads the plugin.\n"
1365
+ )
1366
+ return 0
1367
+
1368
+
966
1369
  def cmd_install_codex(args: argparse.Namespace) -> int:
967
1370
  """Install the claude-smart plugin marketplace for Codex.
968
1371
 
@@ -1073,7 +1476,7 @@ def cmd_install(args: argparse.Namespace) -> int:
1073
1476
 
1074
1477
  Runs ``claude plugin marketplace add`` (pointed at the bundled marketplace
1075
1478
  at ``_REPO_ROOT``) followed by ``claude plugin install``, then appends the
1076
- local-provider flags to ``~/.reflexio/.env`` so reflexio can route
1479
+ local-provider flags to ``~/.claude-smart/.env`` so reflexio can route
1077
1480
  generation through the local Claude Code CLI.
1078
1481
 
1079
1482
  Args:
@@ -1084,6 +1487,8 @@ def cmd_install(args: argparse.Namespace) -> int:
1084
1487
  """
1085
1488
  if getattr(args, "host", "claude-code") == "codex":
1086
1489
  return cmd_install_codex(args)
1490
+ if getattr(args, "host", "claude-code") == "opencode":
1491
+ return cmd_install_opencode(args)
1087
1492
 
1088
1493
  if not shutil.which("claude"):
1089
1494
  sys.stderr.write(
@@ -1156,6 +1561,8 @@ def cmd_update(args: argparse.Namespace) -> int:
1156
1561
  """
1157
1562
  if getattr(args, "host", "claude-code") == "codex":
1158
1563
  return cmd_update_codex(args)
1564
+ if getattr(args, "host", "claude-code") == "opencode":
1565
+ return cmd_update_opencode(args)
1159
1566
 
1160
1567
  _run_service(_DASHBOARD_SCRIPT, "stop")
1161
1568
  _run_service(_BACKEND_SCRIPT, "stop")
@@ -1182,6 +1589,17 @@ def cmd_update_codex(_args: argparse.Namespace) -> int:
1182
1589
  return cmd_install_codex(install_args)
1183
1590
 
1184
1591
 
1592
+ def cmd_update_opencode(args: argparse.Namespace) -> int:
1593
+ _run_service(_DASHBOARD_SCRIPT, "stop")
1594
+ _run_service(_BACKEND_SCRIPT, "stop")
1595
+ sys.stdout.write(
1596
+ "Updating claude-smart OpenCode support by reinstalling from this package...\n"
1597
+ )
1598
+ install_args = argparse.Namespace(**vars(args))
1599
+ install_args.host = "opencode"
1600
+ return cmd_install_opencode(install_args)
1601
+
1602
+
1185
1603
  def cmd_uninstall(_args: argparse.Namespace) -> int:
1186
1604
  """Uninstall claude-smart from Claude Code via the native plugin CLI.
1187
1605
 
@@ -1196,6 +1614,8 @@ def cmd_uninstall(_args: argparse.Namespace) -> int:
1196
1614
  """
1197
1615
  if getattr(_args, "host", "claude-code") == "codex":
1198
1616
  return cmd_uninstall_codex(_args)
1617
+ if getattr(_args, "host", "claude-code") == "opencode":
1618
+ return cmd_uninstall_opencode(_args)
1199
1619
 
1200
1620
  if not shutil.which("claude"):
1201
1621
  sys.stderr.write(
@@ -1256,6 +1676,40 @@ def cmd_uninstall_codex(_args: argparse.Namespace) -> int:
1256
1676
  return 0
1257
1677
 
1258
1678
 
1679
+ def cmd_uninstall_opencode(args: argparse.Namespace) -> int:
1680
+ _run_service(_DASHBOARD_SCRIPT, "stop")
1681
+ _run_service(_BACKEND_SCRIPT, "stop")
1682
+ local_scripts = _OPENCODE_LOCAL_PACKAGE_DIR / "plugin" / "scripts"
1683
+ for script in (
1684
+ local_scripts / "dashboard-service.sh",
1685
+ local_scripts / "backend-service.sh",
1686
+ ):
1687
+ if script.exists():
1688
+ _run_service(script, "stop")
1689
+ config_path = _opencode_config_path(
1690
+ global_config=bool(getattr(args, "global_config", False))
1691
+ )
1692
+ try:
1693
+ changed, config_path = _patch_opencode_plugin_config(config_path, install=False)
1694
+ except ValueError as exc:
1695
+ sys.stderr.write(f"error: {exc}\n")
1696
+ return 1
1697
+ shutil.rmtree(_OPENCODE_LOCAL_PACKAGE_DIR, ignore_errors=True)
1698
+ try:
1699
+ _OPENCODE_LOCAL_PACKAGE_DIR.parent.rmdir()
1700
+ except OSError:
1701
+ pass
1702
+ if changed:
1703
+ sys.stdout.write(f"Removed claude-smart OpenCode plugin entries from {config_path}.\n")
1704
+ else:
1705
+ sys.stdout.write("OpenCode config did not include claude-smart.\n")
1706
+ sys.stdout.write(
1707
+ "Restart OpenCode to apply. "
1708
+ f"{_LOCAL_DATA_NOTICE}"
1709
+ )
1710
+ return 0
1711
+
1712
+
1259
1713
  def cmd_show(args: argparse.Namespace) -> int:
1260
1714
  """Print this project's skills + preferences, plus globally-shared skills.
1261
1715
 
@@ -1292,12 +1746,26 @@ def cmd_show(args: argparse.Namespace) -> int:
1292
1746
  f"First error: {adapter.read_errors[0]}\n"
1293
1747
  )
1294
1748
  return 1
1295
- sys.stdout.write(
1296
- md or f"_No skills or preferences yet for project `{project_id}`._\n"
1297
- )
1749
+ body = md or f"_No skills or preferences yet for project `{project_id}`._\n"
1750
+ sys.stdout.write(body + _reflexio_show_footer())
1298
1751
  return 0
1299
1752
 
1300
1753
 
1754
+ def _reflexio_show_footer() -> str:
1755
+ """Return the markdown attribution footer appended to ``show`` output.
1756
+
1757
+ Reinforces that claude-smart is powered by the open-source reflexio engine
1758
+ and nudges users to star the repo.
1759
+
1760
+ Returns:
1761
+ str: A leading-separator markdown footer ending in a newline.
1762
+ """
1763
+ return (
1764
+ f"\n---\n⭐ claude-smart is powered by [reflexio]({cs_cite.REFLEXIO_REPO_URL})"
1765
+ f" — if it helps you, [star it on GitHub]({cs_cite.REFLEXIO_REPO_URL}).\n"
1766
+ )
1767
+
1768
+
1301
1769
  def cmd_learn(args: argparse.Namespace) -> int:
1302
1770
  """Force reflexio extraction over the active session's interactions.
1303
1771
 
@@ -1981,28 +2449,46 @@ def _build_parser() -> argparse.ArgumentParser:
1981
2449
  inst = sub.add_parser("install", help="Install claude-smart")
1982
2450
  inst.add_argument(
1983
2451
  "--host",
1984
- choices=("claude-code", "codex"),
2452
+ choices=("claude-code", "codex", "opencode"),
1985
2453
  default="claude-code",
1986
2454
  help="Install target host",
1987
2455
  )
2456
+ inst.add_argument(
2457
+ "--global",
2458
+ dest="global_config",
2459
+ action="store_true",
2460
+ help="For OpenCode, patch ~/.config/opencode instead of the project config",
2461
+ )
1988
2462
  inst.set_defaults(func=cmd_install)
1989
2463
 
1990
2464
  upd = sub.add_parser("update", help="Update claude-smart to the latest version")
1991
2465
  upd.add_argument(
1992
2466
  "--host",
1993
- choices=("claude-code", "codex"),
2467
+ choices=("claude-code", "codex", "opencode"),
1994
2468
  default="claude-code",
1995
2469
  help="Update target host",
1996
2470
  )
2471
+ upd.add_argument(
2472
+ "--global",
2473
+ dest="global_config",
2474
+ action="store_true",
2475
+ help="For OpenCode, patch ~/.config/opencode instead of the project config",
2476
+ )
1997
2477
  upd.set_defaults(func=cmd_update)
1998
2478
 
1999
2479
  uni = sub.add_parser("uninstall", help="Remove claude-smart")
2000
2480
  uni.add_argument(
2001
2481
  "--host",
2002
- choices=("claude-code", "codex"),
2482
+ choices=("claude-code", "codex", "opencode"),
2003
2483
  default="claude-code",
2004
2484
  help="Uninstall target host",
2005
2485
  )
2486
+ uni.add_argument(
2487
+ "--global",
2488
+ dest="global_config",
2489
+ action="store_true",
2490
+ help="For OpenCode, patch ~/.config/opencode instead of the project config",
2491
+ )
2006
2492
  uni.set_defaults(func=cmd_uninstall)
2007
2493
 
2008
2494
  sh = sub.add_parser(
@@ -269,7 +269,7 @@ def _compact_citation_instruction(marker_parts: list[str] | None = None) -> str:
269
269
  gate = cs_cite.WHEN_TO_CITE_COMPACT
270
270
  link_style = os.environ.get(_CITATION_LINK_STYLE_ENV, "markdown")
271
271
  if link_style == "osc8" and marker_parts:
272
- marker = f" claude-smart rule applied: {' | '.join(marker_parts)}"
272
+ marker = cs_cite.build_marker(" | ".join(marker_parts), "osc8")
273
273
  separator_instruction = (
274
274
  " Separate multiple linked memories with the visible ` | ` separator."
275
275
  if len(marker_parts) > 1
@@ -281,12 +281,13 @@ def _compact_citation_instruction(marker_parts: list[str] | None = None) -> str:
281
281
  )
282
282
  if link_style == "osc8":
283
283
  return _remoteize_citation_instruction(
284
- f"{gate} If you do, end with `✨ claude-smart rule applied:` "
285
- "followed by the same linked memory text; keep the link, but do "
286
- "not show the URL."
284
+ f"{gate} If you do, end with `{cs_cite.MARKER_PREFIX}` "
285
+ "followed by the same linked memory text, then "
286
+ f"`{cs_cite.marker_attribution('osc8')}` linking to the reflexio "
287
+ "repo; keep the links, but do not show the URL."
287
288
  )
288
289
  if marker_parts:
289
- marker = f" claude-smart rule applied: {' | '.join(marker_parts)}"
290
+ marker = cs_cite.build_marker(" | ".join(marker_parts), "markdown")
290
291
  separator_instruction = (
291
292
  " Separate multiple linked memories with the visible ` | ` separator."
292
293
  if len(marker_parts) > 1
@@ -297,9 +298,8 @@ def _compact_citation_instruction(marker_parts: list[str] | None = None) -> str:
297
298
  f"links: `{marker}`.{separator_instruction}"
298
299
  )
299
300
  return _remoteize_citation_instruction(
300
- f"{gate} If you do, end with one final marker like `✨ claude-smart "
301
- "rule applied: [verify process state](http://localhost:3001/rules/"
302
- "s1-123)` using the shown rule URL."
301
+ f"{gate} If you do, end with one final marker like "
302
+ f"`{cs_cite.MARKDOWN_EXAMPLE_ONE}` using the shown rule URL."
303
303
  )
304
304
 
305
305
 
@@ -337,7 +337,7 @@ def _exact_osc8_marker_instruction(entries: list[dict[str, Any]]) -> str:
337
337
  if not marker_parts:
338
338
  return ""
339
339
 
340
- marker = f" claude-smart rule applied: {' | '.join(marker_parts)}"
340
+ marker = cs_cite.build_marker(" | ".join(marker_parts), "osc8")
341
341
  return (
342
342
  "If any listed memory above was used, copy this exact final marker, "
343
343
  f"preserving its hidden OSC 8 terminal links: `{marker}`. "