claude-smart 0.2.37 → 0.2.39

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.
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.37-green.svg" alt="Version">
16
+ <img src="https://img.shields.io/badge/version-0.2.39-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">
@@ -46,33 +46,6 @@
46
46
 
47
47
  ---
48
48
 
49
- ## Why Learning, Not Memory
50
-
51
- Most memory tools just record. Claude remembers what happened, but doesn't change what it does next.
52
-
53
- `claude-smart` focuses on learning instead.
54
-
55
- Four things this changes:
56
-
57
- - 💡 **Stop repeating the same mistakes:** Produces actionable skills Claude can follow next time; memory only records what happened.
58
-
59
- > *Example:* a deploy fails after Claude bumps `prisma` from 5.x to 6.0; you tell it to roll back because 6.0 breaks nested writes in your order flow.<br>
60
- > **Memory:** “deploy broke after prisma bump; user rolled back”<br>
61
- > **Learning:** “treat major-version bumps of ORMs/DB drivers as breaking — verify with integration tests, not just unit tests”
62
-
63
- - 🚀 **Start from the optimized path:** Records and optimizes the steps that worked so Claude can reuse them, instead of re-exploring each session.
64
- > *Example:* Claude spends several iterations trying to start the local dev environment before discovering that this repo requires `pnpm dev:all` instead of the usual `npm run dev`.<br>
65
- > **Memory:** “user mentioned that `npm run dev` did not work”<br>
66
- > **Learning:** “for this repo, always use `pnpm dev:all` to start the full local stack — `npm run dev` only starts the frontend and causes missing service errors”
67
-
68
- Instead of re-exploring, Claude Code starts from the proven path—reducing planning steps, latency, and token usage.
69
-
70
- - 🌐 **Project-specific and shared skills:** Session memory disappears with the conversation. Project-specific skills preserve repo-local rules, while shared skills roll up patterns that should transfer across projects. Preferences stay scoped to the current project so per-repo preferences don't leak across projects.
71
-
72
- - 🪶 **Better context without prompt bloat:** Distilled, deduplicated skills stay in dozens of tokens—not thousands—even as the project grows.
73
-
74
- ---
75
-
76
49
  ## Quick Start
77
50
 
78
51
  ### Claude Code
@@ -126,18 +99,38 @@ Developing the plugin itself? See [DEVELOPER.md](./DEVELOPER.md#developing-local
126
99
 
127
100
  > **Not supported:** Claude Code Cowork, claude.ai/code web, or remote Codex environments without local plugin hooks — they run outside your local machine, so the local backend/dashboard and `~/.reflexio/` aren't reachable.
128
101
 
129
- ### Vanilla OS support
102
+ ---
103
+
104
+ ## Why Learning, Not Memory
130
105
 
131
- | Platform | Status | Notes |
132
- | --- | --- | --- |
133
- | Apple Silicon macOS 14+ | Supported | Runtime bootstrap installs private Node/npm, uv, Python 3.12 deps, and dashboard deps. |
134
- | Windows x64 | Supported | Runtime bootstrap uses PowerShell for uv/Node archive extraction and patches Codex hooks to the Node wrapper. |
135
- | Linux | Supported when host hooks are local | Existing Linux behavior is preserved; install coverage depends on available Python wheels. |
136
- | Intel Mac, macOS 13 or older, Windows ARM | Not supported | Current local embedding/ML dependencies do not publish a complete native wheel set for these targets. |
106
+ Most memory tools just record. Claude remembers what happened, but doesn't change what it does next.
107
+
108
+ `claude-smart` focuses on learning instead.
109
+
110
+ Four things this changes:
111
+
112
+ - 💡 **Stop repeating the same mistakes:** Produces actionable skills Claude can follow next time; memory only records what happened.
113
+
114
+ > *Example:* a deploy fails after Claude bumps `prisma` from 5.x to 6.0; you tell it to roll back because 6.0 breaks nested writes in your order flow.<br>
115
+ > **Memory:** “deploy broke after prisma bump; user rolled back”<br>
116
+ > **Learning:** “treat major-version bumps of ORMs/DB drivers as breaking — verify with integration tests, not just unit tests”
117
+
118
+ - 🚀 **Start from the optimized path:** Records and optimizes the steps that worked so Claude can reuse them, instead of re-exploring each session.
119
+ > *Example:* Claude spends several iterations trying to start the local dev environment before discovering that this repo requires `pnpm dev:all` instead of the usual `npm run dev`.<br>
120
+ > **Memory:** “user mentioned that `npm run dev` did not work”<br>
121
+ > **Learning:** “for this repo, always use `pnpm dev:all` to start the full local stack — `npm run dev` only starts the frontend and causes missing service errors”
122
+
123
+ Instead of re-exploring, Claude Code starts from the proven path—reducing planning steps, latency, and token usage.
124
+
125
+ - 🌐 **Project-specific and shared skills:** Session memory disappears with the conversation. Project-specific skills preserve repo-local rules, while shared skills roll up patterns that should transfer across projects. Preferences stay scoped to the current project so per-repo preferences don't leak across projects.
126
+
127
+ - 🪶 **Better context without prompt bloat:** Distilled, deduplicated skills stay in dozens of tokens—not thousands—even as the project grows.
128
+
129
+ ---
130
+
131
+ ## Demo
137
132
 
138
- Network access is required during first install for npm, PyPI/uv, Node.js, and
139
- the first local embedding model download. The ONNX model cache lives at
140
- `~/.cache/chroma/onnx_models/all-MiniLM-L6-v2/`.
133
+ https://github.com/user-attachments/assets/ed84d73a-d5b7-4a0d-ab2c-f2c060c3f276
141
134
 
142
135
  ---
143
136
 
@@ -231,7 +224,7 @@ For troubleshooting, see [TROUBLESHOOTING.md](./TROUBLESHOOTING.md).
231
224
 
232
225
  ## License
233
226
 
234
- This project is licensed under the **Apache License 2.0**. The bundled `reflexio/` submodule is also Apache 2.0. Claude Code is Anthropic's and not covered by this license.
227
+ This project is licensed under the **Apache License 2.0**. Reflexio is a separate Apache 2.0 project consumed as the `reflexio-ai` Python package. Claude Code is Anthropic's and not covered by this license.
235
228
 
236
229
  See the [LICENSE](LICENSE) file for details.
237
230
 
@@ -795,6 +795,36 @@ function ensurePluginRoot(pluginRoot) {
795
795
  }
796
796
  }
797
797
 
798
+ async function installVendoredReflexio(pluginRoot, uv, env) {
799
+ const vendorRoot = join(pluginRoot, "vendor", "reflexio");
800
+ if (!existsSync(join(vendorRoot, "pyproject.toml"))) return;
801
+
802
+ const pythonPath = isWindows()
803
+ ? join(pluginRoot, ".venv", "Scripts", "python.exe")
804
+ : join(pluginRoot, ".venv", "bin", "python");
805
+ if (!existsSync(pythonPath)) {
806
+ throw new Error(`plugin Python was not created by uv sync: ${pythonPath}`);
807
+ }
808
+
809
+ process.stdout.write(`Installing bundled Reflexio source from ${vendorRoot}...\n`);
810
+ let code = await runChecked(
811
+ uv,
812
+ ["pip", "install", "--project", pluginRoot, "--python", pythonPath, "--quiet", "-e", vendorRoot],
813
+ { cwd: pluginRoot, env },
814
+ );
815
+ if (code !== 0) {
816
+ process.stderr.write(
817
+ `warning: quiet vendored Reflexio install failed in ${pluginRoot}; retrying with full output.\n`,
818
+ );
819
+ code = await runChecked(
820
+ uv,
821
+ ["pip", "install", "--project", pluginRoot, "--python", pythonPath, "-e", vendorRoot],
822
+ { cwd: pluginRoot, env },
823
+ );
824
+ }
825
+ if (code !== 0) throw new Error(`vendored Reflexio install failed in ${pluginRoot}`);
826
+ }
827
+
798
828
  async function bootstrapPluginRuntime(pluginRoot, options = {}) {
799
829
  assertSupportedRuntimePlatform();
800
830
  process.stdout.write("Preparing claude-smart runtime for hooks...\n");
@@ -830,6 +860,7 @@ async function bootstrapPluginRuntime(pluginRoot, options = {}) {
830
860
  );
831
861
  }
832
862
  if (code !== 0) throw new Error(`uv sync failed in ${pluginRoot}`);
863
+ await installVendoredReflexio(pluginRoot, uv, env);
833
864
 
834
865
  const dashboardDir = join(pluginRoot, "dashboard");
835
866
  if (existsSync(dashboardDir)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Self-improving Claude Code plugin — learns from corrections via reflexio",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Self-improving Claude Code plugin — learns from corrections across sessions via reflexio",
5
5
  "author": {
6
6
  "name": "Yi Lu"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.37",
3
+ "version": "0.2.39",
4
4
  "description": "Self-improving coding assistant plugin — learns from corrections across sessions via reflexio",
5
5
  "author": {
6
6
  "name": "Yi Lu"
@@ -1,12 +1,16 @@
1
1
  [project]
2
2
  name = "claude-smart"
3
- version = "0.2.37"
3
+ version = "0.2.39"
4
4
  description = "Self-improving Claude Code plugin — learns from corrections via reflexio"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
7
7
  dependencies = [
8
- "reflexio-ai>=0.2.22",
9
- # Used by reflexio's local embedding provider (ONNXMiniLM_L6_V2).
8
+ # Core Reflexio client/backend package. This is the PyPI baseline used by
9
+ # normal installs; vendor releases may override it after uv sync by
10
+ # installing plugin/vendor/reflexio. Keep in sync via
11
+ # scripts/sync-reflexio-dep.py.
12
+ "reflexio-ai>=0.2.23",
13
+ # Needed when Reflexio runs the local embedding provider (ONNXMiniLM_L6_V2).
10
14
  # Pulls in onnxruntime + tokenizers; the ~80 MB ONNX model itself is
11
15
  # downloaded on first use, not at install time.
12
16
  "chromadb>=0.5",
@@ -18,14 +22,6 @@ claude-smart-hook = "claude_smart.hook:main"
18
22
  claude-smart = "claude_smart.cli:main"
19
23
  claude-smart-optimizer-assistant = "claude_smart.optimizer_assistant:main"
20
24
 
21
- # NOTE: LOCAL DEV ONLY — do not commit this block to main.
22
- # Published installs must resolve reflexio-ai from PyPI. This override
23
- # points at the vendored submodule so editable changes flow through.
24
- # `git update-index --skip-worktree plugin/pyproject.toml plugin/uv.lock`
25
- # hides the local divergence from `git status`.
26
- # [tool.uv.sources]
27
- # reflexio-ai = { path = "/Users/yilu/repos/claude-smart/reflexio", editable = true }
28
-
29
25
  [build-system]
30
26
  requires = ["hatchling"]
31
27
  build-backend = "hatchling.build"
@@ -196,6 +196,30 @@ claude_smart_resolve_python() {
196
196
  return 1
197
197
  }
198
198
 
199
+ claude_smart_plugin_python() {
200
+ local plugin_root
201
+ plugin_root="$1"
202
+ if claude_smart_is_windows; then
203
+ printf '%s\n' "$plugin_root/.venv/Scripts/python.exe"
204
+ else
205
+ printf '%s\n' "$plugin_root/.venv/bin/python"
206
+ fi
207
+ }
208
+
209
+ claude_smart_python_imports() {
210
+ local plugin_root module python_bin
211
+ plugin_root="$1"
212
+ module="$2"
213
+ python_bin="$(claude_smart_plugin_python "$plugin_root")"
214
+ [ -x "$python_bin" ] || return 1
215
+ "$python_bin" - "$module" <<'PY' >/dev/null 2>&1
216
+ import importlib
217
+ import sys
218
+
219
+ importlib.import_module(sys.argv[1])
220
+ PY
221
+ }
222
+
199
223
  claude_smart_download() {
200
224
  local url dest src _CS_PY
201
225
  url="$1"
@@ -298,6 +322,8 @@ claude_smart_install_fingerprint() {
298
322
  printf 'lib=%s\n' "$(claude_smart_fingerprint_file "$script_dir/_lib.sh")"
299
323
  printf 'pyproject=%s\n' "$(claude_smart_fingerprint_file "$plugin_root/pyproject.toml")"
300
324
  printf 'uv_lock=%s\n' "$(claude_smart_fingerprint_file "$plugin_root/uv.lock")"
325
+ printf 'vendor_reflexio_pyproject=%s\n' \
326
+ "$(claude_smart_fingerprint_file "$plugin_root/vendor/reflexio/pyproject.toml")"
301
327
  # Resolved python interpreter — catches a system upgrade (3.12.4 → 3.12.5)
302
328
  # that would otherwise let install_complete return true against a venv
303
329
  # built against a now-deleted interpreter.
@@ -220,6 +220,21 @@ case "$CMD" in
220
220
  export INTERACTION_CLEANUP_THRESHOLD="${INTERACTION_CLEANUP_THRESHOLD:-500}"
221
221
  export INTERACTION_CLEANUP_DELETE_COUNT="${INTERACTION_CLEANUP_DELETE_COUNT:-200}"
222
222
 
223
+ # Keep plugin runtime data in ~/.reflexio even when the backend imports
224
+ # Reflexio from an editable checkout inside a larger repo with its own
225
+ # .env. python-dotenv respects pre-existing env vars, so this prevents a
226
+ # parent REFLEXIO_LOG_DIR from sending claude-smart to enterprise configs.
227
+ export REFLEXIO_LOG_DIR="${REFLEXIO_LOG_DIR:-$HOME}"
228
+
229
+ # Force sqlite: the plugin venv ships only the open-source reflexio
230
+ # package, which doesn't register the Supabase/Postgres storage
231
+ # factories. If the user's ~/.reflexio/.env sets REFLEXIO_STORAGE to
232
+ # something else (common when sharing the file with reflexio_ext),
233
+ # the backend boots but crashes every request. load_dotenv() inside
234
+ # the CLI respects pre-existing env vars, so exporting here wins
235
+ # without touching the file on disk.
236
+ export REFLEXIO_STORAGE="sqlite"
237
+
223
238
  # (nohup; no process groups). backend-log-runner.sh owns stdout/stderr
224
239
  # capture so process output cannot grow backend.log past its cap.
225
240
  #
@@ -233,7 +248,8 @@ case "$CMD" in
233
248
  workers="${CLAUDE_SMART_BACKEND_WORKERS:-1}"
234
249
  claude_smart_spawn_detached bash "$HERE/backend-log-runner.sh" \
235
250
  "$LOG_FILE" "$LOG_MAX_BYTES" -- \
236
- uv run --project "$PLUGIN_ROOT" --quiet \
251
+ env PYTHONIOENCODING="${PYTHONIOENCODING:-utf-8}" \
252
+ uv run --project "$PLUGIN_ROOT" --no-sync --quiet \
237
253
  reflexio services start --only backend --no-reload --workers "$workers"
238
254
  svc_pid=$!
239
255
  # Record the spawned pid, not a pgid sampled with ps. On POSIX,
@@ -63,4 +63,4 @@ if ! command -v uv >/dev/null 2>&1; then
63
63
  fi
64
64
  fi
65
65
 
66
- exec uv run --project "$PLUGIN_ROOT" --quiet python -m claude_smart.cli "$@"
66
+ exec uv run --project "$PLUGIN_ROOT" --no-sync --quiet python -m claude_smart.cli "$@"
@@ -3,7 +3,8 @@
3
3
  # CLAUDE_PLUGIN_ROOT points at the plugin dir (dev: <repo>/plugin;
4
4
  # installed: ~/.claude/plugins/cache/reflexioai/claude-smart/<version>),
5
5
  # which is also the Python project root with pyproject.toml + uv.lock.
6
- # We invoke via `uv run --project` so the pinned env from uv.lock is used.
6
+ # We invoke via `uv run --project --no-sync` so the prepared env is used without
7
+ # undoing editable Reflexio installs from local development.
7
8
  #
8
9
  # If the Setup hook recorded an install failure at
9
10
  # ~/.claude-smart/install-failed, short-circuit with a user-visible
@@ -113,5 +114,38 @@ if ! command -v uv >/dev/null 2>&1; then
113
114
  fi
114
115
  fi
115
116
 
117
+ ensure_hook_package_importable() {
118
+ if claude_smart_python_imports "$PLUGIN_ROOT" claude_smart.hook; then
119
+ return 0
120
+ fi
121
+
122
+ if [ "${CLAUDE_SMART_BOOTSTRAPPING:-}" = "1" ]; then
123
+ return 1
124
+ fi
125
+ if [ ! -x "$PLUGIN_ROOT/scripts/smart-install.sh" ]; then
126
+ return 1
127
+ fi
128
+
129
+ mkdir -p "$STATE_DIR"
130
+ if [ "$EVENT" = "session-start" ]; then
131
+ CLAUDE_SMART_BOOTSTRAPPING=1 bash "$PLUGIN_ROOT/scripts/smart-install.sh" >&2
132
+ else
133
+ {
134
+ printf '%s\n' "[claude-smart] hook: claude_smart.hook is not importable; retrying install in background"
135
+ date 2>/dev/null || true
136
+ } >>"$STATE_DIR/install.log" 2>&1
137
+ claude_smart_spawn_detached env CLAUDE_SMART_BOOTSTRAPPING=1 \
138
+ bash "$PLUGIN_ROOT/scripts/smart-install.sh" \
139
+ >>"$STATE_DIR/install.log" 2>&1 || true
140
+ fi
141
+
142
+ claude_smart_python_imports "$PLUGIN_ROOT" claude_smart.hook
143
+ }
144
+
145
+ if ! ensure_hook_package_importable; then
146
+ claude_smart_emit_continue
147
+ exit 0
148
+ fi
149
+
116
150
  # Stdin is the hook payload JSON — stream it through to the Python CLI.
117
- exec uv run --project "$PLUGIN_ROOT" --quiet python -m claude_smart.hook "$HOST" "$EVENT"
151
+ exec uv run --project "$PLUGIN_ROOT" --no-sync --quiet python -m claude_smart.hook "$HOST" "$EVENT"
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
- # Run once on plugin install. Pulls the reflexio submodule, syncs the
3
- # Python env, and flips on the claude-code LiteLLM provider in reflexio's
4
- # .env so extraction works with no external API key.
2
+ # Run once on plugin install. Syncs the Python env and flips on the
3
+ # claude-code LiteLLM provider in reflexio's .env so extraction works with no
4
+ # external API key.
5
5
  #
6
6
  # On failure, writes the reason to ~/.claude-smart/install-failed so
7
7
  # hook_entry.sh can short-circuit and surface a user-visible message
@@ -105,12 +105,39 @@ install_complete() {
105
105
  [ "$(cat "$SUCCESS_MARKER" 2>/dev/null || true)" = "$(install_fingerprint)" ] || return 1
106
106
  command -v uv >/dev/null 2>&1 || return 1
107
107
  [ -d "$PLUGIN_ROOT/.venv" ] || return 1
108
+ claude_smart_python_imports "$PLUGIN_ROOT" claude_smart.hook || return 1
108
109
  if [ -d "$PLUGIN_ROOT/dashboard" ]; then
109
110
  [ -d "$PLUGIN_ROOT/dashboard/.next" ] || [ -f "$MARKER_DIR/dashboard-build.pid" ] || [ -f "$(claude_smart_dashboard_unavailable_marker)" ] || return 1
110
111
  fi
111
112
  return 0
112
113
  }
113
114
 
115
+ install_vendored_reflexio() {
116
+ local VENDORED_REFLEXIO PLUGIN_PYTHON
117
+
118
+ VENDORED_REFLEXIO="$PLUGIN_ROOT/vendor/reflexio"
119
+ [ -f "$VENDORED_REFLEXIO/pyproject.toml" ] || return 0
120
+
121
+ if claude_smart_is_windows; then
122
+ PLUGIN_PYTHON="$PLUGIN_ROOT/.venv/Scripts/python.exe"
123
+ else
124
+ PLUGIN_PYTHON="$PLUGIN_ROOT/.venv/bin/python"
125
+ fi
126
+ if [ ! -x "$PLUGIN_PYTHON" ]; then
127
+ write_failure "plugin Python was not created by uv sync: $PLUGIN_PYTHON"
128
+ fi
129
+
130
+ echo "[claude-smart] installing bundled Reflexio source from $VENDORED_REFLEXIO" >&2
131
+ if uv pip install --project "$PLUGIN_ROOT" --python "$PLUGIN_PYTHON" --quiet -e "$VENDORED_REFLEXIO" >&2; then
132
+ return 0
133
+ fi
134
+
135
+ echo "[claude-smart] warning: quiet vendored Reflexio install failed in $PLUGIN_ROOT; retrying with full output." >&2
136
+ if ! uv pip install --project "$PLUGIN_ROOT" --python "$PLUGIN_PYTHON" -e "$VENDORED_REFLEXIO" >&2; then
137
+ write_failure "vendored Reflexio install failed in $PLUGIN_ROOT"
138
+ fi
139
+ }
140
+
114
141
  write_success_marker() {
115
142
  install_fingerprint > "$SUCCESS_MARKER"
116
143
  }
@@ -362,19 +389,6 @@ if install_complete; then
362
389
  exit 0
363
390
  fi
364
391
 
365
- # Dev-mode only: when running from a git checkout, pull the reflexio
366
- # submodule so tests/benchmarks can use its sources. In install mode the
367
- # plugin lives under ~/.claude/plugins/cache and reflexio-ai resolves
368
- # from PyPI instead. The guard checks for both `.git` and `.gitmodules`
369
- # at REPO_ROOT to distinguish a dev checkout from a marketplace cache
370
- # (where REPO_ROOT has neither).
371
- if [ -d "$REPO_ROOT/.git" ] && [ -f "$REPO_ROOT/.gitmodules" ]; then
372
- echo "[claude-smart] initializing reflexio submodule..." >&2
373
- if ! (cd "$REPO_ROOT" && git submodule update --init --recursive reflexio) >&2; then
374
- echo "[claude-smart] WARNING: git submodule update failed; continuing with PyPI reflexio-ai" >&2
375
- fi
376
- fi
377
-
378
392
  if ! command -v uv >/dev/null 2>&1; then
379
393
  echo "[claude-smart] uv not found — installing from astral.sh..." >&2
380
394
  # The astral.sh bash installer downloads a zip and unzips it. On
@@ -422,6 +436,18 @@ echo "[claude-smart] running uv sync..." >&2
422
436
  if ! uv sync --locked --python 3.12 --quiet >&2; then
423
437
  write_failure "uv sync failed in $PLUGIN_ROOT — run 'uv sync --locked --python 3.12' there to diagnose"
424
438
  fi
439
+ # Defend against a stale lockfile silently dropping the project install.
440
+ # When plugin/uv.lock pins a different claude-smart version than pyproject.toml,
441
+ # `uv sync --locked` exits 0 but does NOT install the claude_smart package, so
442
+ # every hook fails later with `ModuleNotFoundError: No module named 'claude_smart'`.
443
+ # Catch that here with a clear, user-visible install-failed marker.
444
+ if ! claude_smart_python_imports "$PLUGIN_ROOT" claude_smart.hook; then
445
+ write_failure "uv sync left claude_smart uninstalled in $PLUGIN_ROOT/.venv — plugin/uv.lock is likely out of sync with pyproject.toml (regenerate with 'uv lock --project plugin' and reinstall the plugin)"
446
+ fi
447
+ install_vendored_reflexio
448
+ if ! claude_smart_python_imports "$PLUGIN_ROOT" claude_smart.hook; then
449
+ write_failure "claude-smart package is not importable after vendored Reflexio install in $PLUGIN_ROOT/.venv"
450
+ fi
425
451
 
426
452
  # Reflexio's CLI reads ~/.reflexio/.env (see reflexio/cli/env_loader.py);
427
453
  # append our two opt-in flags there so `reflexio services start` picks