Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions DEVELOPER.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Tunables read by the plugin at runtime. Most users don't need to touch these —
| `REFLEXIO_EMBEDDING_PROVIDER` | `local_service` when local embedding is enabled | Embedding provider mode: `cloud`, `local_service`, `internal_service`, `inprocess`, or `off`. |
| `REFLEXIO_EMBEDDING_SERVICE_URL` | `http://127.0.0.1:$EMBEDDING_PORT` | OpenAI-compatible embedding endpoint used by `local_service` and `internal_service`. |
| `EMBEDDING_PORT` | `8072` | Local embedding daemon port. Shared by Claude Code and Codex on the same machine. |
| `REFLEXIO_RERANK_DEVICE` | `cpu` | Torch device for the in-process cross-encoder reranker used by search. Defaults to CPU: auto-selected MPS on Apple Silicon accumulates gigabytes of GPU memory in the backend process for no latency win at this model size. Set to `cuda` only for GPU deployments. |
| `CLAUDE_SMART_ENABLE_OPTIMIZER` | enabled unless set to `0` | Hook-side env var that controls shared skill optimization and rollups during `SessionStart`. Set it in Claude Code settings, not `~/.reflexio/.env`. |
| `CLAUDE_SMART_CITATIONS` | `on` | Controls the final `✨ claude-smart rule applied` marker instruction. `on` (default) injects a compact instruction asking the assistant to cite only memories that materially changed the answer. `off` skips injection of the citation instruction entirely and strips any stray marker line from the assistant text before publishing. Legacy values `auto` and `marker-only` are accepted as enabled aliases. Set in Claude Code settings (hook env), not `~/.reflexio/.env`. |
| `CLAUDE_SMART_CITATION_LINK_STYLE` | `markdown` | Controls the visible links in the final `✨ claude-smart rule applied` marker. `markdown` asks the model to emit `[human title](dashboard URL)` links and is the Codex default because Codex renders markdown links in assistant messages. `osc8` asks it to emit terminal-native OSC 8 hyperlinks so the human title can be clickable without showing the URL; unsupported terminals should fall back to markdown links. |
Expand Down
4 changes: 2 additions & 2 deletions bin/claude-smart.js
Original file line number Diff line number Diff line change
Expand Up @@ -905,7 +905,7 @@ async function installVendoredReflexio(pluginRoot, uv, env) {
process.stdout.write(`Installing bundled Reflexio source from ${vendorRoot}...\n`);
let code = await runChecked(
uv,
["pip", "install", "--project", pluginRoot, "--python", pythonPath, "--quiet", "-e", vendorRoot],
["pip", "install", "--project", pluginRoot, "--python", pythonPath, "--quiet", "--reinstall", "--no-deps", vendorRoot],
{ cwd: pluginRoot, env },
);
if (code !== 0) {
Expand All @@ -914,7 +914,7 @@ async function installVendoredReflexio(pluginRoot, uv, env) {
);
code = await runChecked(
uv,
["pip", "install", "--project", pluginRoot, "--python", pythonPath, "-e", vendorRoot],
["pip", "install", "--project", pluginRoot, "--python", pythonPath, "--reinstall", "--no-deps", vendorRoot],
{ cwd: pluginRoot, env },
);
}
Expand Down
2 changes: 1 addition & 1 deletion plugin/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ dependencies = [
# normal installs; vendor releases may override it after uv sync by
# installing plugin/vendor/reflexio. Keep in sync via
# scripts/sync-reflexio-dep.py.
"reflexio-ai>=0.2.23",
"reflexio-ai>=0.2.25",
# Needed when Reflexio runs the local embedding provider (ONNXMiniLM_L6_V2).
# Pulls in onnxruntime + tokenizers; the ~80 MB ONNX model itself is
# downloaded on first use, not at install time.
Expand Down
54 changes: 50 additions & 4 deletions plugin/scripts/backend-service.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
#
# Subcommands:
# start probe /health; if nothing we recognize is on the port,
# spawn `uv run reflexio services start --only backend
# --no-reload` detached. Polls /health briefly so first
# spawn the prepared venv's `reflexio services start --only
# backend --no-reload` detached. Polls /health briefly so first
# use after session start lands on a warm server, then
# returns a continue payload regardless.
# stop SIGTERM the recorded process group, escalating to
Expand Down Expand Up @@ -183,6 +183,35 @@ port_holder() {
| awk 'NR==2 {print $1" (pid "$2")"; exit}'
}

ensure_vendored_reflexio_active() {
vendor="$PLUGIN_ROOT/vendor/reflexio"
[ -f "$vendor/pyproject.toml" ] || return 0
plugin_python="$(claude_smart_plugin_python "$PLUGIN_ROOT")"
[ -x "$plugin_python" ] || return 0
if "$plugin_python" - "$vendor/pyproject.toml" <<'PY' >/dev/null 2>&1; then
import importlib.metadata
import re
import sys
from pathlib import Path

text = Path(sys.argv[1]).read_text(encoding="utf-8")
match = re.search(r'^version\s*=\s*"([^"]+)"', text, re.MULTILINE)
if not match:
raise SystemExit(1)
expected = match.group(1)
try:
installed = importlib.metadata.version("reflexio-ai")
except importlib.metadata.PackageNotFoundError:
raise SystemExit(1)
raise SystemExit(0 if installed == expected else 1)
PY
return 0
fi
claude_smart_append_capped_log "$LOG_FILE" "$LOG_MAX_BYTES" \
"[claude-smart] backend: repairing vendored Reflexio install before start"
uv pip install --project "$PLUGIN_ROOT" --python "$plugin_python" --quiet --reinstall --no-deps "$vendor" >&2
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

# Full shutdown: kill the recorded process group (if any) then sweep
# both the backend port and the embedding-service port for surviving
# reflexio listeners. Used by both `stop` and the opt-in `session-end`
Expand Down Expand Up @@ -243,6 +272,7 @@ case "$CMD" in
fi
fi
cd "$PLUGIN_ROOT"
ensure_vendored_reflexio_active

# Cap local interaction history to keep the SQLite store small for
# claude-smart users. Reflexio's library defaults are much higher
Expand All @@ -266,6 +296,21 @@ case "$CMD" in
# without touching the file on disk.
export REFLEXIO_STORAGE="sqlite"

backend_pythonpath="${PYTHONPATH:-}"
if [ -d "$PLUGIN_ROOT/vendor/reflexio/reflexio" ]; then
vendor_pythonpath="$PLUGIN_ROOT/vendor/reflexio"
pythonpath_sep=":"
if claude_smart_is_windows; then
# Native Windows Python expects ;-separated Windows-style paths in
# PYTHONPATH; MSYS does not auto-convert arbitrary env vars.
pythonpath_sep=";"
if command -v cygpath >/dev/null 2>&1; then
vendor_pythonpath="$(cygpath -w "$vendor_pythonpath")"
fi
fi
backend_pythonpath="$vendor_pythonpath${backend_pythonpath:+$pythonpath_sep$backend_pythonpath}"
fi

# (nohup; no process groups). backend-log-runner.sh owns stdout/stderr
# capture so process output cannot grow backend.log past its cap.
#
Expand All @@ -277,11 +322,12 @@ case "$CMD" in
# CLAUDE_SMART_BACKEND_WORKERS for power users running concurrent
# Claude Code sessions or wanting zero-downtime recycling.
workers="${CLAUDE_SMART_BACKEND_WORKERS:-1}"
backend_python="$(claude_smart_plugin_python "$PLUGIN_ROOT")"
claude_smart_spawn_detached bash "$HERE/backend-log-runner.sh" \
"$LOG_FILE" "$LOG_MAX_BYTES" -- \
env PYTHONIOENCODING="${PYTHONIOENCODING:-utf-8}" \
uv run --project "$PLUGIN_ROOT" --no-sync --quiet \
reflexio services start --only backend --no-reload --workers "$workers"
PYTHONPATH="$backend_pythonpath" \
"$backend_python" -m reflexio.cli services start --only backend --no-reload --workers "$workers"
svc_pid=$!
Comment thread
coderabbitai[bot] marked this conversation as resolved.
# Record the spawned pid, not a pgid sampled with ps. On POSIX,
# setsid/python os.setsid make this pid the new process group leader;
Expand Down
3 changes: 2 additions & 1 deletion plugin/scripts/cli.sh
Original file line number Diff line number Diff line change
Expand Up @@ -91,4 +91,5 @@ if ! claude_smart_python_imports "$PLUGIN_ROOT" claude_smart.cli; then
fi
fi

exec uv run --project "$PLUGIN_ROOT" --no-sync --quiet python -m claude_smart.cli "$@"
PLUGIN_PYTHON="$(claude_smart_plugin_python "$PLUGIN_ROOT")"
exec "$PLUGIN_PYTHON" -m claude_smart.cli "$@"
1 change: 1 addition & 0 deletions plugin/scripts/ensure-plugin-root.sh
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ if [ ! -f "$TARGET/pyproject.toml" ]; then
echo "[claude-smart] ensure-plugin-root: $TARGET is not a plugin dir (no pyproject.toml)" >&2
exit 1
fi
TARGET="$(cd "$TARGET" && pwd -P)"

LINK="$HOME/.reflexio/plugin-root"
mkdir -p "$(dirname "$LINK")"
Expand Down
8 changes: 5 additions & 3 deletions plugin/scripts/hook_entry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
# CLAUDE_PLUGIN_ROOT points at the plugin dir (dev: <repo>/plugin;
# installed: ~/.claude/plugins/cache/reflexioai/claude-smart/<version>),
# which is also the Python project root with pyproject.toml + uv.lock.
# We invoke via `uv run --project --no-sync` so the prepared env is used without
# undoing editable Reflexio installs from local development.
# We invoke the prepared venv Python directly so hooks use the dependency set
# produced by smart-install.sh, including the vendored Reflexio snapshot it
# installs non-editably (--reinstall --no-deps).
#
# If the Setup hook recorded an install failure at
# ~/.claude-smart/install-failed, short-circuit with a user-visible
Expand Down Expand Up @@ -135,4 +136,5 @@ if ! ensure_hook_package_importable; then
fi

# Stdin is the hook payload JSON — stream it through to the Python CLI.
exec uv run --project "$PLUGIN_ROOT" --no-sync --quiet python -m claude_smart.hook "$HOST" "$EVENT"
PLUGIN_PYTHON="$(claude_smart_plugin_python "$PLUGIN_ROOT")"
exec "$PLUGIN_PYTHON" -m claude_smart.hook "$HOST" "$EVENT"
4 changes: 2 additions & 2 deletions plugin/scripts/smart-install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,12 +142,12 @@ install_vendored_reflexio() {
fi

echo "[claude-smart] installing bundled Reflexio source from $VENDORED_REFLEXIO" >&2
if uv pip install --project "$PLUGIN_ROOT" --python "$PLUGIN_PYTHON" --quiet -e "$VENDORED_REFLEXIO" >&2; then
if uv pip install --project "$PLUGIN_ROOT" --python "$PLUGIN_PYTHON" --quiet --reinstall --no-deps "$VENDORED_REFLEXIO" >&2; then
return 0
fi

echo "[claude-smart] warning: quiet vendored Reflexio install failed in $PLUGIN_ROOT; retrying with full output." >&2
if ! uv pip install --project "$PLUGIN_ROOT" --python "$PLUGIN_PYTHON" -e "$VENDORED_REFLEXIO" >&2; then
if ! uv pip install --project "$PLUGIN_ROOT" --python "$PLUGIN_PYTHON" --reinstall --no-deps "$VENDORED_REFLEXIO" >&2; then
write_failure "vendored Reflexio install failed in $PLUGIN_ROOT"
fi
}
Expand Down
Loading
Loading