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 +32 -39
- package/bin/claude-smart.js +31 -0
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/pyproject.toml +7 -11
- package/plugin/scripts/_lib.sh +26 -0
- package/plugin/scripts/backend-service.sh +17 -1
- package/plugin/scripts/cli.sh +1 -1
- package/plugin/scripts/hook_entry.sh +36 -2
- package/plugin/scripts/smart-install.sh +42 -16
- package/plugin/src/claude_smart/cli.py +132 -15
- package/plugin/src/claude_smart/hook_log.py +1 -1
- package/plugin/src/claude_smart/internal_call.py +11 -11
- package/plugin/uv.lock +918 -910
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.
|
|
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
|
-
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## Why Learning, Not Memory
|
|
130
105
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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
|
-
|
|
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**.
|
|
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
|
|
package/bin/claude-smart.js
CHANGED
|
@@ -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
package/plugin/pyproject.toml
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "claude-smart"
|
|
3
|
-
version = "0.2.
|
|
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
|
-
|
|
9
|
-
#
|
|
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"
|
package/plugin/scripts/_lib.sh
CHANGED
|
@@ -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
|
-
|
|
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,
|
package/plugin/scripts/cli.sh
CHANGED
|
@@ -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
|
|
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.
|
|
3
|
-
#
|
|
4
|
-
#
|
|
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
|