diff --git a/Makefile b/Makefile index 798e11d..eb0c1dc 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ # - git (for the release flow) .PHONY: help bump release publish publish-npm publish-pypi publish-dry \ - check-version check-clean ensure-remote-reflexio unskip-worktree + check-version check-clean check-npm-auth ensure-remote-reflexio unskip-worktree VERSION_FILES := package.json plugin/pyproject.toml \ plugin/.claude-plugin/plugin.json .claude-plugin/marketplace.json \ @@ -36,6 +36,16 @@ check-clean: @git diff --quiet && git diff --cached --quiet \ || { echo "error: working tree is dirty — commit or stash first" >&2; exit 1; } +check-npm-auth: ## Verify npm auth via NPM_TOKEN or `npm whoami`; fail if neither is available + @if [ -n "$$NPM_TOKEN" ]; then \ + echo "→ npm: NPM_TOKEN is set"; \ + elif npm whoami >/dev/null 2>&1; then \ + echo "→ npm: logged in as $$(npm whoami)"; \ + else \ + echo "error: not authenticated via npm whoami and NPM_TOKEN is not set; set NPM_TOKEN for CI or run npm login locally" >&2; \ + exit 1; \ + fi + unskip-worktree: ## Clear skip-worktree on plugin/pyproject.toml and plugin/uv.lock so release edits land in git @echo "→ clearing skip-worktree on $(PYPROJECT) $(LOCK_FILES)" @git update-index --no-skip-worktree $(PYPROJECT) $(LOCK_FILES) 2>/dev/null || true @@ -87,7 +97,7 @@ publish-dry: unskip-worktree ensure-remote-reflexio ## Show what would be publis publish: publish-npm publish-pypi ## Publish to both npm and PyPI -release: check-version check-clean bump ## Bump + commit + tag + publish + push +release: check-version check-clean check-npm-auth bump ## Bump + commit + tag + publish + push @echo "→ committing release v$(VERSION)" git add $(VERSION_FILES) $(LOCK_FILES) git commit -m "Release v$(VERSION)" diff --git a/plugin/commands/clear-all.md b/plugin/commands/clear-all.md index 752da1d..67d2338 100644 --- a/plugin/commands/clear-all.md +++ b/plugin/commands/clear-all.md @@ -1,8 +1,8 @@ --- description: Delete ALL reflexio interactions, profiles, and user playbooks (destructive) -allowed-tools: Bash(uv run:*) +allowed-tools: Bash(bash:*) --- Run this bash command and show its output verbatim: -!`uv run --project "$HOME/.reflexio/plugin-root" --quiet python -m claude_smart.cli clear-all --yes` +!`bash "$HOME/.reflexio/plugin-root/scripts/cli.sh" clear-all --yes` diff --git a/plugin/commands/learn.md b/plugin/commands/learn.md index 890a0e1..28bd2e1 100644 --- a/plugin/commands/learn.md +++ b/plugin/commands/learn.md @@ -1,10 +1,10 @@ --- description: Flag the last turn as a correction so reflexio learns from it -allowed-tools: Bash(uv run:*) +allowed-tools: Bash(bash:*) argument-hint: [note] --- Flag the user's previous turn as a correction and force reflexio to run extraction on this session's interactions now. Run the bash command below and show its output verbatim. -!`uv run --project "$HOME/.reflexio/plugin-root" --quiet python -m claude_smart.cli learn "$ARGUMENTS"` +!`bash "$HOME/.reflexio/plugin-root/scripts/cli.sh" learn "$ARGUMENTS"` diff --git a/plugin/commands/restart.md b/plugin/commands/restart.md index e01c640..5cf58b9 100644 --- a/plugin/commands/restart.md +++ b/plugin/commands/restart.md @@ -1,8 +1,8 @@ --- description: Restart the reflexio backend and dashboard to pick up new changes -allowed-tools: Bash(uv run:*) +allowed-tools: Bash(bash:*) --- Run this bash command and show its output verbatim: -!`uv run --project "$HOME/.reflexio/plugin-root" --quiet python -m claude_smart.cli restart` +!`bash "$HOME/.reflexio/plugin-root/scripts/cli.sh" restart` diff --git a/plugin/commands/show.md b/plugin/commands/show.md index f3ae4d3..84478c2 100644 --- a/plugin/commands/show.md +++ b/plugin/commands/show.md @@ -1,8 +1,8 @@ --- description: Show the current project playbook and session user profiles -allowed-tools: Bash(uv run:*) +allowed-tools: Bash(bash:*) --- Run this bash command and show its output verbatim: -!`uv run --project "$HOME/.reflexio/plugin-root" --quiet python -m claude_smart.cli show` +!`bash "$HOME/.reflexio/plugin-root/scripts/cli.sh" show` diff --git a/plugin/scripts/cli.sh b/plugin/scripts/cli.sh new file mode 100755 index 0000000..c23914c --- /dev/null +++ b/plugin/scripts/cli.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# Wrapper for slash commands that invoke the claude_smart CLI via uv. +# Claude Code runs `!` bash directives in slash command .md files in a +# non-interactive, non-login shell that does NOT source ~/.zshrc or +# ~/.bash_profile. As a result, binaries installed by smart-install.sh +# at ~/.local/bin (e.g. uv from the astral.sh installer) are invisible +# to those directives until the user manually re-sources their shell rc. +# This wrapper bootstraps PATH the same way hook_entry.sh does so the +# slash commands work on a fresh install. +set -eu + +HERE="$(cd "$(dirname "$0")" && pwd)" +# shellcheck source=_lib.sh +. "$HERE/_lib.sh" +claude_smart_source_login_path +claude_smart_prepend_astral_bins + +PLUGIN_ROOT="$(cd "$HERE/.." && pwd)" + +# If the Setup hook recorded an install failure, surface that reason +# instead of falling through to a generic "uv not found" — mirrors the +# branch at hook_entry.sh so slash commands and hooks behave consistently +# on a broken install. +FAILURE_MARKER="$HOME/.claude-smart/install-failed" +if [ -f "$FAILURE_MARKER" ]; then + msg="$(cat "$FAILURE_MARKER" 2>/dev/null || echo "")" + [ -n "$msg" ] || msg="unknown error" + echo "claude-smart is not installed correctly: $msg" >&2 + echo "Re-run the plugin's Setup (restart Claude Code) or fix the underlying issue and delete $FAILURE_MARKER to retry." >&2 + exit 1 +fi + +if ! command -v uv >/dev/null 2>&1; then + echo "claude-smart: 'uv' not found on PATH." >&2 + echo "Install it from https://docs.astral.sh/uv/ or restart Claude Code so the Setup hook can install it." >&2 + exit 1 +fi + +exec uv run --project "$PLUGIN_ROOT" --quiet python -m claude_smart.cli "$@" diff --git a/plugin/src/claude_smart/reflexio_adapter.py b/plugin/src/claude_smart/reflexio_adapter.py index c11eb25..323b733 100644 --- a/plugin/src/claude_smart/reflexio_adapter.py +++ b/plugin/src/claude_smart/reflexio_adapter.py @@ -192,13 +192,13 @@ def fetch_project_profiles(self, project_id: str, top_k: int = 20) -> list[Any]: if client is None: return [] try: - response = client.search_profiles( + response = client.search_user_profiles( user_id=project_id, query="", top_k=top_k, ) except Exception as exc: # noqa: BLE001 - _LOGGER.debug("search_profiles failed: %s", exc) + _LOGGER.debug("search_user_profiles failed: %s", exc) return [] return _extract_items(response, "user_profiles") @@ -250,13 +250,13 @@ def search_profiles( if client is None: return [] try: - response = client.search_profiles( + response = client.search_user_profiles( user_id=project_id, query=query, top_k=top_k, ) except Exception as exc: # noqa: BLE001 - _LOGGER.debug("search_profiles failed: %s", exc) + _LOGGER.debug("search_user_profiles failed: %s", exc) return [] return _extract_items(response, "user_profiles") diff --git a/reflexio b/reflexio index 9143d1b..0c1eced 160000 --- a/reflexio +++ b/reflexio @@ -1 +1 @@ -Subproject commit 9143d1bf74584d0ecced69216c5b50773363c8e0 +Subproject commit 0c1eced05f512463de7f7e9237658784f3415d20