claude-smart 0.2.48 → 0.2.50

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 (193) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/README.md +11 -40
  3. package/bin/claude-smart.js +393 -55
  4. package/package.json +3 -2
  5. package/plugin/.claude-plugin/plugin.json +1 -1
  6. package/plugin/.codex-plugin/plugin.json +1 -1
  7. package/plugin/.coverage +0 -0
  8. package/plugin/README.md +4 -3
  9. package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +4 -2
  10. package/plugin/dashboard/app/dashboard/page.tsx +6 -1
  11. package/plugin/dashboard/app/preferences/[id]/page.tsx +18 -6
  12. package/plugin/dashboard/app/preferences/page.tsx +32 -35
  13. package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +16 -1
  14. package/plugin/dashboard/app/sessions/page.tsx +2 -0
  15. package/plugin/dashboard/app/skills/page.tsx +65 -50
  16. package/plugin/dashboard/app/skills/project/[id]/page.tsx +17 -8
  17. package/plugin/dashboard/app/skills/shared/[id]/page.tsx +1 -6
  18. package/plugin/dashboard/components/common/host-badge.tsx +118 -0
  19. package/plugin/dashboard/components/common/learning-application-badge.tsx +34 -0
  20. package/plugin/dashboard/components/common/learnings-badge.tsx +1 -1
  21. package/plugin/dashboard/components/common/page-header.tsx +3 -3
  22. package/plugin/dashboard/lib/config-file.ts +5 -1
  23. package/plugin/dashboard/lib/host-attribution.ts +62 -0
  24. package/plugin/dashboard/lib/session-reader.ts +40 -2
  25. package/plugin/dashboard/lib/types.ts +7 -1
  26. package/plugin/opencode/dist/server.mjs +60 -2
  27. package/plugin/opencode/server.mts +64 -2
  28. package/plugin/pyproject.toml +2 -2
  29. package/plugin/scripts/_lib.sh +255 -7
  30. package/plugin/scripts/backend-python-runner.py +46 -0
  31. package/plugin/scripts/backend-service.sh +766 -123
  32. package/plugin/scripts/codex-hook.js +79 -229
  33. package/plugin/scripts/dashboard-open.sh +6 -4
  34. package/plugin/scripts/dashboard-service.sh +118 -137
  35. package/plugin/scripts/ensure-plugin-root.sh +106 -8
  36. package/plugin/scripts/hook_entry.sh +3 -0
  37. package/plugin/scripts/opencode-claude-compat.js +8 -1
  38. package/plugin/scripts/smart-install.sh +116 -21
  39. package/plugin/src/README.md +1 -1
  40. package/plugin/src/claude_smart/cli.py +93 -29
  41. package/plugin/src/claude_smart/context_inject.py +3 -0
  42. package/plugin/src/claude_smart/env_config.py +56 -11
  43. package/plugin/src/claude_smart/events/post_tool.py +2 -1
  44. package/plugin/src/claude_smart/events/session_end.py +2 -1
  45. package/plugin/src/claude_smart/events/stop.py +3 -0
  46. package/plugin/src/claude_smart/events/user_prompt.py +2 -1
  47. package/plugin/src/claude_smart/internal_call.py +5 -2
  48. package/plugin/src/claude_smart/optimizer_assistant.py +59 -13
  49. package/plugin/src/claude_smart/publish.py +59 -7
  50. package/plugin/src/claude_smart/reflexio_adapter.py +137 -14
  51. package/plugin/src/claude_smart/runtime.py +15 -6
  52. package/plugin/src/claude_smart/state.py +211 -52
  53. package/plugin/uv.lock +5 -5
  54. package/plugin/vendor/reflexio/.env.example +13 -0
  55. package/plugin/vendor/reflexio/reflexio/README.md +7 -3
  56. package/plugin/vendor/reflexio/reflexio/__init__.py +12 -0
  57. package/plugin/vendor/reflexio/reflexio/cli/README.md +3 -0
  58. package/plugin/vendor/reflexio/reflexio/client/client.py +166 -9
  59. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +10 -3
  60. package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +28 -0
  61. package/plugin/vendor/reflexio/reflexio/lib/_search.py +41 -0
  62. package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +195 -25
  63. package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +7 -1
  64. package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +2 -1
  65. package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +63 -4
  66. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +1 -0
  67. package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +8 -1
  68. package/plugin/vendor/reflexio/reflexio/models/config_schema.py +1 -1
  69. package/plugin/vendor/reflexio/reflexio/server/README.md +22 -4
  70. package/plugin/vendor/reflexio/reflexio/server/__init__.py +18 -8
  71. package/plugin/vendor/reflexio/reflexio/server/__main__.py +6 -0
  72. package/plugin/vendor/reflexio/reflexio/server/api.py +79 -5
  73. package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +263 -3
  74. package/plugin/vendor/reflexio/reflexio/server/callback_executor.py +164 -0
  75. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +81 -81
  76. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +28 -0
  77. package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +63 -6
  78. package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -52
  79. package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1 -0
  80. package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +28 -0
  81. package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +9 -1
  82. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedder_warmup.py +329 -0
  83. package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +85 -10
  84. package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +199 -2
  85. package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +77 -9
  86. package/plugin/vendor/reflexio/reflexio/server/org_fanout.py +184 -0
  87. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +1 -1
  88. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.1.0.prompt.md +32 -0
  89. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +1 -1
  90. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.4.0.prompt.md +63 -0
  91. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +1 -1
  92. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v2.0.0.prompt.md +30 -0
  93. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_impact/v1.0.0.prompt.md +51 -0
  94. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_relevance/v1.0.0.prompt.md +39 -0
  95. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +1 -1
  96. package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.1.0.prompt.md +43 -0
  97. package/plugin/vendor/reflexio/reflexio/server/routes/config.py +3 -3
  98. package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +122 -28
  99. package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +63 -2
  100. package/plugin/vendor/reflexio/reflexio/server/routes/system.py +22 -3
  101. package/plugin/vendor/reflexio/reflexio/server/scheduling.py +64 -3
  102. package/plugin/vendor/reflexio/reflexio/server/services/README.md +6 -4
  103. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +3 -2
  104. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +41 -0
  105. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/retrieved_learning_evaluator.py +554 -0
  106. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +35 -1
  107. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/runner.py +253 -101
  108. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/scheduler.py +5 -7
  109. package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/service.py +27 -0
  110. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +11 -5
  111. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +4 -4
  112. package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +6 -2
  113. package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +255 -74
  114. package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +72 -0
  115. package/plugin/vendor/reflexio/reflexio/server/services/deferred_learning_plan.py +270 -0
  116. package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/__init__.py +13 -0
  117. package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/scheduler.py +142 -0
  118. package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/worker.py +262 -0
  119. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/hero_state.py +2 -11
  120. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/rule_attribution.py +6 -2
  121. package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +17 -13
  122. package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +18 -3
  123. package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +12 -1
  124. package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +1 -1
  125. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +2 -1
  126. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +1 -3
  127. package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +66 -0
  128. package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +640 -80
  129. package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +2 -0
  130. package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +179 -99
  131. package/plugin/vendor/reflexio/reflexio/server/services/lineage/vector_backfill_sweep.py +139 -0
  132. package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +66 -27
  133. package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_trigger.py +177 -0
  134. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -2
  135. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +360 -49
  136. package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/extractor.py +27 -30
  137. package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +8 -1
  138. package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +137 -69
  139. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +20 -1
  140. package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +13 -6
  141. package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +40 -25
  142. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/consolidator.py +12 -39
  143. package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +30 -35
  144. package/plugin/vendor/reflexio/reflexio/server/services/profile/service.py +166 -66
  145. package/plugin/vendor/reflexio/reflexio/server/services/reflection/service.py +457 -107
  146. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/session_dedup.py +127 -0
  147. package/plugin/vendor/reflexio/reflexio/server/services/retrieval/temporal.py +104 -0
  148. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/dispatcher.py +139 -0
  149. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +16 -6
  150. package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/worker.py +137 -0
  151. package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +12 -0
  152. package/plugin/vendor/reflexio/reflexio/server/services/storage/lifecycle_filters.py +54 -0
  153. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +41 -3
  154. package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +40 -2
  155. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
  156. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +190 -2
  157. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +11 -4
  158. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +28 -1
  159. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_learning_jobs.py +414 -0
  160. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -5
  161. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +8 -3
  162. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +4 -0
  163. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +16 -5
  164. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +86 -2
  165. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +104 -42
  166. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +7 -1
  167. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +59 -7
  168. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +430 -5
  169. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +8 -3
  170. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +57 -12
  171. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +197 -12
  172. package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +70 -11
  173. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +17 -0
  174. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_commit_scope.py +9 -0
  175. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +9 -2
  176. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_learning_jobs.py +269 -0
  177. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +7 -0
  178. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +3 -1
  179. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +4 -0
  180. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +12 -1
  181. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/evaluation_state_keys.py +78 -0
  182. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +38 -0
  183. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +171 -0
  184. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +52 -1
  185. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +82 -1
  186. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_profile_store.py +74 -2
  187. package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/retrieved_learning_state.py +226 -0
  188. package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +5 -6
  189. package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +209 -29
  190. package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +3 -0
  191. package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -0
  192. package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +33 -0
  193. package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
@@ -71,7 +71,11 @@ claude_smart_prepend_astral_bins() {
71
71
  claude_smart_prepend_node_bins() {
72
72
  local _CS_NODE_ROOT
73
73
  _CS_NODE_ROOT="$HOME/.claude-smart/node/current"
74
- export PATH="$_CS_NODE_ROOT/bin:$_CS_NODE_ROOT:$PATH"
74
+ if claude_smart_is_windows; then
75
+ export PATH="$_CS_NODE_ROOT:$PATH"
76
+ else
77
+ export PATH="$_CS_NODE_ROOT/bin:$_CS_NODE_ROOT:$PATH"
78
+ fi
75
79
  }
76
80
 
77
81
  claude_smart_env_unquote() {
@@ -89,7 +93,7 @@ claude_smart_env_unquote() {
89
93
  claude_smart_source_reflexio_env() {
90
94
  # claude-smart's env lives at ~/.claude-smart/.env (kept separate from the OSS
91
95
  # reflexio default ~/.reflexio/.env). Stays in sync with
92
- # env_config.REFLEXIO_ENV_PATH and the REFLEXIO_ENV_FILE export in
96
+ # env_config.CLAUDE_SMART_ENV_PATH and the REFLEXIO_ENV_FILE export in
93
97
  # backend-service.sh.
94
98
  local env_file line key value
95
99
  env_file="$HOME/.claude-smart/.env"
@@ -117,7 +121,7 @@ claude_smart_source_reflexio_env() {
117
121
  ;;
118
122
  # CLAUDE_SMART_* flags: respect anything the caller already exported so
119
123
  # per-session overrides (e.g., a manual test) take precedence over the file.
120
- CLAUDE_SMART_USE_LOCAL_CLI|CLAUDE_SMART_USE_LOCAL_EMBEDDING|CLAUDE_SMART_BACKEND_AUTOSTART|CLAUDE_SMART_DASHBOARD_AUTOSTART|CLAUDE_SMART_CLI_PATH|CLAUDE_SMART_CLI_TIMEOUT|CLAUDE_SMART_STATE_DIR|CLAUDE_SMART_ENABLE_OPTIMIZER)
124
+ CLAUDE_SMART_USE_LOCAL_CLI|CLAUDE_SMART_USE_LOCAL_EMBEDDING|CLAUDE_SMART_HOST|CLAUDE_SMART_BACKEND_AUTOSTART|CLAUDE_SMART_DASHBOARD_AUTOSTART|CLAUDE_SMART_CLI_PATH|CLAUDE_SMART_OPENCODE_PATH|CLAUDE_SMART_CLI_TIMEOUT|CLAUDE_SMART_STATE_DIR|CLAUDE_SMART_ENABLE_OPTIMIZER)
121
125
  if [ -z "$(eval "printf '%s' \"\${$key:-}\"")" ]; then
122
126
  value="$(claude_smart_env_unquote "$value")"
123
127
  export "$key=$value"
@@ -139,12 +143,23 @@ claude_smart_reflexio_url_is_remote() {
139
143
  return 0
140
144
  }
141
145
 
146
+ claude_smart_derive_reflexio_url_from_backend_port() {
147
+ local port default_url
148
+ port="${BACKEND_PORT:-8071}"
149
+ default_url="http://localhost:$port/"
150
+ case "${REFLEXIO_URL:-}" in
151
+ ""|"http://localhost:8071"|"http://localhost:8071/"|"http://127.0.0.1:8071"|"http://127.0.0.1:8071/")
152
+ export REFLEXIO_URL="$default_url"
153
+ ;;
154
+ esac
155
+ }
156
+
142
157
  claude_smart_is_internal_invocation_env() {
143
158
  if [ "${CLAUDE_SMART_INTERNAL:-}" = "1" ]; then
144
159
  return 0
145
160
  fi
146
161
  case "${CLAUDE_CODE_ENTRYPOINT:-}" in
147
- ""|"cli"|"claude-desktop") return 1 ;;
162
+ ""|"cli"|"claude-desktop"|"claude-vscode"|"claude-jetbrains") return 1 ;;
148
163
  *) return 0 ;;
149
164
  esac
150
165
  }
@@ -210,16 +225,54 @@ claude_smart_is_windows() {
210
225
  esac
211
226
  }
212
227
 
228
+ claude_smart_to_windows_path() {
229
+ local path
230
+ path="$1"
231
+ if command -v cygpath >/dev/null 2>&1; then
232
+ cygpath -w "$path"
233
+ return $?
234
+ fi
235
+ # Convert MSYS/Cygwin/WSL-style paths to native Windows paths for cmd.exe.
236
+ printf '%s\n' "$path" | awk '
237
+ function slash_to_backslash(value) {
238
+ gsub(/\//, "\\", value)
239
+ return value
240
+ }
241
+ function drive_path(drive, rest) {
242
+ drive = toupper(drive)
243
+ sub(/^\//, "", rest)
244
+ if (rest == "") {
245
+ return drive ":\\"
246
+ }
247
+ return drive ":\\" slash_to_backslash(rest)
248
+ }
249
+ {
250
+ normalized = $0
251
+ gsub(/\\/, "/", normalized)
252
+ if (normalized ~ /^[A-Za-z]:($|\/)/) {
253
+ print drive_path(substr(normalized, 1, 1), substr(normalized, 3))
254
+ } else if (normalized ~ /^\/cygdrive\/[A-Za-z]($|\/)/) {
255
+ print drive_path(substr(normalized, 11, 1), substr(normalized, 12))
256
+ } else if (normalized ~ /^\/mnt\/[A-Za-z]($|\/)/) {
257
+ print drive_path(substr(normalized, 6, 1), substr(normalized, 7))
258
+ } else if (normalized ~ /^\/[A-Za-z]($|\/)/) {
259
+ print drive_path(substr(normalized, 2, 1), substr(normalized, 3))
260
+ } else {
261
+ print slash_to_backslash(normalized)
262
+ }
263
+ }'
264
+ }
265
+
213
266
  # Print the absolute path of a working python interpreter, or nothing
214
267
  # (and return non-zero) if none is usable. On Windows, `python3` is
215
268
  # usually the Microsoft Store "App Execution Alias" stub at
216
269
  # %LocalAppData%\Microsoft\WindowsApps\python3.exe — `command -v python3`
217
270
  # returns truthy but invoking it just prints a "Python was not found"
218
271
  # message and exits non-zero. We probe with `-V` to filter the stub out
219
- # and prefer `python` (the real interpreter when one is installed).
272
+ # and prefer the Windows `py` launcher before `python`/`python3`.
220
273
  claude_smart_resolve_python() {
221
274
  if claude_smart_is_windows; then
222
- for cand in python python3; do
275
+ for cand in py python python3; do
223
276
  if command -v "$cand" >/dev/null 2>&1 && "$cand" -V >/dev/null 2>&1; then
224
277
  command -v "$cand"
225
278
  return 0
@@ -508,7 +561,7 @@ claude_smart_node_satisfies() {
508
561
 
509
562
  claude_smart_resolve_npm() {
510
563
  local cand
511
- for cand in npm npm.cmd; do
564
+ for cand in npm npm.cmd npm.exe; do
512
565
  if command -v "$cand" >/dev/null 2>&1; then
513
566
  command -v "$cand"
514
567
  return 0
@@ -517,6 +570,16 @@ claude_smart_resolve_npm() {
517
570
  return 1
518
571
  }
519
572
 
573
+ claude_smart_opencode_compat_path() {
574
+ local plugin_root filename
575
+ plugin_root="$1"
576
+ filename="opencode-claude-compat"
577
+ if claude_smart_is_windows; then
578
+ filename="opencode-claude-compat.cmd"
579
+ fi
580
+ printf '%s\n' "$plugin_root/scripts/$filename"
581
+ }
582
+
520
583
  claude_smart_npm_available() {
521
584
  local npm_bin
522
585
  npm_bin=$(claude_smart_resolve_npm || true)
@@ -651,6 +714,191 @@ claude_smart_kill_tree() {
651
714
  kill -KILL -- "-$pid" 2>/dev/null || true
652
715
  }
653
716
 
717
+ claude_smart_now_epoch() {
718
+ date +%s 2>/dev/null || {
719
+ _CS_PY=$(claude_smart_resolve_python 2>/dev/null || true)
720
+ [ -n "$_CS_PY" ] || return 1
721
+ "$_CS_PY" - <<'PY'
722
+ import time
723
+
724
+ print(int(time.time()))
725
+ PY
726
+ }
727
+ }
728
+
729
+ claude_smart_path_mtime_epoch() {
730
+ path="$1"
731
+ mtime=""
732
+ [ -e "$path" ] || return 1
733
+ mtime="$(stat -f %m "$path" 2>/dev/null || true)"
734
+ case "$mtime" in
735
+ ''|*[!0-9]*) ;;
736
+ *) printf '%s\n' "$mtime"; return 0 ;;
737
+ esac
738
+ mtime="$(stat -c %Y "$path" 2>/dev/null || true)"
739
+ case "$mtime" in
740
+ ''|*[!0-9]*) ;;
741
+ *) printf '%s\n' "$mtime"; return 0 ;;
742
+ esac
743
+ _CS_PY=$(claude_smart_resolve_python 2>/dev/null || true)
744
+ [ -n "$_CS_PY" ] || return 1
745
+ "$_CS_PY" - "$path" <<'PY'
746
+ import os
747
+ import sys
748
+
749
+ print(int(os.path.getmtime(sys.argv[1])))
750
+ PY
751
+ }
752
+
753
+ claude_smart_service_lock_stale_seconds() {
754
+ value="${CLAUDE_SMART_SERVICE_LOCK_STALE_SECONDS:-60}"
755
+ case "$value" in
756
+ ''|*[!0-9]*) printf '%s\n' "60" ;;
757
+ *) printf '%s\n' "$value" ;;
758
+ esac
759
+ }
760
+
761
+ claude_smart_service_lock() {
762
+ name="$1"
763
+ lock_dir="$HOME/.claude-smart/$name.start.lock"
764
+ pid_file="$lock_dir/pid"
765
+ mkdir -p "$HOME/.claude-smart"
766
+ for _ in 1 2 3; do
767
+ if mkdir "$lock_dir" 2>/dev/null; then
768
+ printf '%s\n' "$$" > "$pid_file"
769
+ return 0
770
+ fi
771
+
772
+ lock_pid="$(cat "$pid_file" 2>/dev/null || true)"
773
+ now="$(claude_smart_now_epoch 2>/dev/null || printf '%s\n' "0")"
774
+ mtime="$(claude_smart_path_mtime_epoch "$lock_dir" 2>/dev/null || printf '%s\n' "$now")"
775
+ age=$((now - mtime))
776
+ stale_after="$(claude_smart_service_lock_stale_seconds)"
777
+
778
+ if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
779
+ return 1
780
+ fi
781
+ if [ -z "$lock_pid" ] && [ "$age" -lt "$stale_after" ]; then
782
+ return 1
783
+ fi
784
+ if [ -n "$lock_pid" ] || [ "$age" -ge "$stale_after" ]; then
785
+ stale_dir="$lock_dir.stale.$$"
786
+ if mv "$lock_dir" "$stale_dir" 2>/dev/null; then
787
+ rm -rf "$stale_dir"
788
+ continue
789
+ fi
790
+ fi
791
+ return 1
792
+ done
793
+ return 1
794
+ }
795
+
796
+ claude_smart_service_unlock() {
797
+ name="$1"
798
+ lock_dir="$HOME/.claude-smart/$name.start.lock"
799
+ pid_file="$lock_dir/pid"
800
+ [ -d "$lock_dir" ] || return 0
801
+ lock_pid="$(cat "$pid_file" 2>/dev/null || true)"
802
+ [ "$lock_pid" = "$$" ] || return 0
803
+ rm -rf "$lock_dir"
804
+ }
805
+
806
+ claude_smart_powershell_bin() {
807
+ for cand in powershell.exe powershell pwsh.exe pwsh; do
808
+ if command -v "$cand" >/dev/null 2>&1; then
809
+ command -v "$cand"
810
+ return 0
811
+ fi
812
+ done
813
+ return 1
814
+ }
815
+
816
+ claude_smart_port_listener_pids() {
817
+ port="$1"
818
+ if claude_smart_is_windows; then
819
+ ps_bin="$(claude_smart_powershell_bin 2>/dev/null || true)"
820
+ if [ -n "$ps_bin" ]; then
821
+ PORT="$port" "$ps_bin" -NoProfile -ExecutionPolicy Bypass -Command \
822
+ '$ErrorActionPreference="SilentlyContinue"; Get-NetTCPConnection -LocalPort $env:PORT -State Listen | Select-Object -ExpandProperty OwningProcess' \
823
+ 2>/dev/null | tr -d '\r' | awk 'NF && !seen[$1]++ {print $1}'
824
+ return 0
825
+ fi
826
+ if command -v netstat >/dev/null 2>&1; then
827
+ netstat -ano 2>/dev/null | awk -v port=":$port" '
828
+ $1 ~ /^TCP/ && $2 ~ port "$" && $4 == "LISTENING" && !seen[$5]++ {print $5}
829
+ '
830
+ return 0
831
+ fi
832
+ fi
833
+ command -v lsof >/dev/null 2>&1 || return 0
834
+ lsof -tiTCP:"$port" -sTCP:LISTEN -P -n 2>/dev/null | awk 'NF && !seen[$1]++ {print $1}'
835
+ }
836
+
837
+ claude_smart_pid_command() {
838
+ pid="$1"
839
+ if claude_smart_is_windows; then
840
+ ps_bin="$(claude_smart_powershell_bin 2>/dev/null || true)"
841
+ if [ -n "$ps_bin" ]; then
842
+ PID="$pid" "$ps_bin" -NoProfile -ExecutionPolicy Bypass -Command \
843
+ '$ErrorActionPreference="SilentlyContinue"; $p=Get-CimInstance Win32_Process -Filter "ProcessId=$env:PID"; if ($p) { $p.CommandLine }' \
844
+ 2>/dev/null | tr -d '\r'
845
+ return 0
846
+ fi
847
+ if command -v tasklist >/dev/null 2>&1; then
848
+ tasklist //FI "PID eq $pid" //FO CSV //NH 2>/dev/null \
849
+ | awk -F, 'NR==1 {gsub(/^"|"$/, "", $1); print $1}'
850
+ return 0
851
+ fi
852
+ fi
853
+ command -v ps >/dev/null 2>&1 || return 0
854
+ ps -ww -p "$pid" -o command= 2>/dev/null || true
855
+ }
856
+
857
+ claude_smart_pid_parent() {
858
+ pid="$1"
859
+ if claude_smart_is_windows; then
860
+ ps_bin="$(claude_smart_powershell_bin 2>/dev/null || true)"
861
+ if [ -n "$ps_bin" ]; then
862
+ PID="$pid" "$ps_bin" -NoProfile -ExecutionPolicy Bypass -Command \
863
+ '$ErrorActionPreference="SilentlyContinue"; $p=Get-CimInstance Win32_Process -Filter "ProcessId=$env:PID"; if ($p) { $p.ParentProcessId }' \
864
+ 2>/dev/null | tr -d '\r'
865
+ return 0
866
+ fi
867
+ fi
868
+ command -v ps >/dev/null 2>&1 || return 0
869
+ ps -p "$pid" -o ppid= 2>/dev/null | awk 'NF {print $1; exit}' || true
870
+ }
871
+
872
+ claude_smart_port_holder() {
873
+ port="$1"
874
+ pid="$(claude_smart_port_listener_pids "$port" | head -n1)"
875
+ [ -n "$pid" ] || return 0
876
+ cmdline="$(claude_smart_pid_command "$pid" | head -n1)"
877
+ [ -n "$cmdline" ] || cmdline="unknown"
878
+ printf '%s (pid %s)\n' "$cmdline" "$pid"
879
+ }
880
+
881
+ claude_smart_reap_port_listeners_matching() {
882
+ port="$1"
883
+ shift || true
884
+ [ "$#" -gt 0 ] || return 1
885
+ killed=1
886
+ pids="$(claude_smart_port_listener_pids "$port" || true)"
887
+ [ -n "$pids" ] || return 1
888
+ for pid in $pids; do
889
+ cmdline="$(claude_smart_pid_command "$pid" | tr '\n' ' ' || true)"
890
+ [ -n "$cmdline" ] || continue
891
+ for pattern in "$@"; do
892
+ if [[ "$cmdline" == $pattern ]]; then
893
+ claude_smart_kill_tree "$pid"
894
+ killed=0
895
+ break
896
+ fi
897
+ done
898
+ done
899
+ return "$killed"
900
+ }
901
+
654
902
  # Return 0 (true) if $1 names a pid file whose pid is currently alive.
655
903
  # Silent on missing/empty/stale files.
656
904
  claude_smart_pid_alive_file() {
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env python3
2
+ """Run Reflexio CLI with claude-smart metadata visible in process argv."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import os
7
+ import runpy
8
+ import sys
9
+
10
+
11
+ def main() -> int:
12
+ """Mirror claude-smart metadata into env and delegate to Reflexio CLI.
13
+
14
+ Args:
15
+ None: Reads process argv. Arguments before ``--`` are
16
+ ``--claude-smart-*`` metadata kept visible in the process command
17
+ line; arguments after ``--`` become ``reflexio.cli`` arguments.
18
+
19
+ Returns:
20
+ int: Exit status. Returns ``2`` when required argument separators or
21
+ Reflexio CLI arguments are missing.
22
+ """
23
+ try:
24
+ separator = sys.argv.index("--")
25
+ except ValueError:
26
+ return 2
27
+
28
+ metadata_args = sys.argv[1:separator]
29
+ reflexio_args = sys.argv[separator + 1 :]
30
+ if not reflexio_args:
31
+ return 2
32
+
33
+ for arg in metadata_args:
34
+ if not arg.startswith("--claude-smart-") or "=" not in arg:
35
+ continue
36
+ key, value = arg[2:].split("=", 1)
37
+ env_key = key.replace("-", "_").upper()
38
+ os.environ.setdefault(env_key, value)
39
+
40
+ sys.argv = ["reflexio.cli", *reflexio_args]
41
+ runpy.run_module("reflexio.cli", run_name="__main__")
42
+ return 0
43
+
44
+
45
+ if __name__ == "__main__":
46
+ raise SystemExit(main())