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.
- package/.claude-plugin/marketplace.json +2 -2
- package/README.md +11 -40
- package/bin/claude-smart.js +393 -55
- package/package.json +3 -2
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/.coverage +0 -0
- package/plugin/README.md +4 -3
- package/plugin/dashboard/app/api/reflexio/[...path]/route.ts +4 -2
- package/plugin/dashboard/app/dashboard/page.tsx +6 -1
- package/plugin/dashboard/app/preferences/[id]/page.tsx +18 -6
- package/plugin/dashboard/app/preferences/page.tsx +32 -35
- package/plugin/dashboard/app/sessions/[sessionId]/page.tsx +16 -1
- package/plugin/dashboard/app/sessions/page.tsx +2 -0
- package/plugin/dashboard/app/skills/page.tsx +65 -50
- package/plugin/dashboard/app/skills/project/[id]/page.tsx +17 -8
- package/plugin/dashboard/app/skills/shared/[id]/page.tsx +1 -6
- package/plugin/dashboard/components/common/host-badge.tsx +118 -0
- package/plugin/dashboard/components/common/learning-application-badge.tsx +34 -0
- package/plugin/dashboard/components/common/learnings-badge.tsx +1 -1
- package/plugin/dashboard/components/common/page-header.tsx +3 -3
- package/plugin/dashboard/lib/config-file.ts +5 -1
- package/plugin/dashboard/lib/host-attribution.ts +62 -0
- package/plugin/dashboard/lib/session-reader.ts +40 -2
- package/plugin/dashboard/lib/types.ts +7 -1
- package/plugin/opencode/dist/server.mjs +60 -2
- package/plugin/opencode/server.mts +64 -2
- package/plugin/pyproject.toml +2 -2
- package/plugin/scripts/_lib.sh +255 -7
- package/plugin/scripts/backend-python-runner.py +46 -0
- package/plugin/scripts/backend-service.sh +766 -123
- package/plugin/scripts/codex-hook.js +79 -229
- package/plugin/scripts/dashboard-open.sh +6 -4
- package/plugin/scripts/dashboard-service.sh +118 -137
- package/plugin/scripts/ensure-plugin-root.sh +106 -8
- package/plugin/scripts/hook_entry.sh +3 -0
- package/plugin/scripts/opencode-claude-compat.js +8 -1
- package/plugin/scripts/smart-install.sh +116 -21
- package/plugin/src/README.md +1 -1
- package/plugin/src/claude_smart/cli.py +93 -29
- package/plugin/src/claude_smart/context_inject.py +3 -0
- package/plugin/src/claude_smart/env_config.py +56 -11
- package/plugin/src/claude_smart/events/post_tool.py +2 -1
- package/plugin/src/claude_smart/events/session_end.py +2 -1
- package/plugin/src/claude_smart/events/stop.py +3 -0
- package/plugin/src/claude_smart/events/user_prompt.py +2 -1
- package/plugin/src/claude_smart/internal_call.py +5 -2
- package/plugin/src/claude_smart/optimizer_assistant.py +59 -13
- package/plugin/src/claude_smart/publish.py +59 -7
- package/plugin/src/claude_smart/reflexio_adapter.py +137 -14
- package/plugin/src/claude_smart/runtime.py +15 -6
- package/plugin/src/claude_smart/state.py +211 -52
- package/plugin/uv.lock +5 -5
- package/plugin/vendor/reflexio/.env.example +13 -0
- package/plugin/vendor/reflexio/reflexio/README.md +7 -3
- package/plugin/vendor/reflexio/reflexio/__init__.py +12 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +3 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +166 -9
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/src/openclaw_smart/state.py +10 -3
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/tests/test_state.py +28 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +195 -25
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +7 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +2 -1
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +63 -4
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +1 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +8 -1
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/README.md +22 -4
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +18 -8
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +79 -5
- package/plugin/vendor/reflexio/reflexio/server/billing_meter.py +263 -3
- package/plugin/vendor/reflexio/reflexio/server/callback_executor.py +164 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_embedding.py +81 -81
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_subprocess.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/_litellm_text_generation.py +63 -6
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -52
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +9 -1
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedder_warmup.py +329 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +85 -10
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +199 -2
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +77 -9
- package/plugin/vendor/reflexio/reflexio/server/org_fanout.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.1.0.prompt.md +32 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.3.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.4.0.prompt.md +63 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v2.0.0.prompt.md +30 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_impact/v1.0.0.prompt.md +51 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/retrieved_learning_relevance/v1.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +1 -1
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.1.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/routes/config.py +3 -3
- package/plugin/vendor/reflexio/reflexio/server/routes/evaluation.py +122 -28
- package/plugin/vendor/reflexio/reflexio/server/routes/interactions.py +63 -2
- package/plugin/vendor/reflexio/reflexio/server/routes/system.py +22 -3
- package/plugin/vendor/reflexio/reflexio/server/scheduling.py +64 -3
- package/plugin/vendor/reflexio/reflexio/server/services/README.md +6 -4
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/README.md +3 -2
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +41 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/components/retrieved_learning_evaluator.py +554 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +35 -1
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/runner.py +253 -101
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/scheduler.py +5 -7
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/service.py +27 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_extraction_lifecycle.py +11 -5
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_should_run.py +4 -4
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation/_usage_billing.py +6 -2
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +255 -74
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +72 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deferred_learning_plan.py +270 -0
- package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/__init__.py +13 -0
- package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/scheduler.py +142 -0
- package/plugin/vendor/reflexio/reflexio/server/services/durable_learning/worker.py +262 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/hero_state.py +2 -11
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/components/rule_attribution.py +6 -2
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +17 -13
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +18 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +2 -1
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +1 -3
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +640 -80
- package/plugin/vendor/reflexio/reflexio/server/services/governance/service.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/gc_scheduler.py +179 -99
- package/plugin/vendor/reflexio/reflexio/server/services/lineage/vector_backfill_sweep.py +139 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +66 -27
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/aggregation_trigger.py +177 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/aggregator.py +68 -2
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/consolidator.py +360 -49
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/components/extractor.py +27 -30
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_edit_apply.py +8 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/service.py +137 -69
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +20 -1
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +13 -6
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +40 -25
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/consolidator.py +12 -39
- package/plugin/vendor/reflexio/reflexio/server/services/profile/components/extractor.py +30 -35
- package/plugin/vendor/reflexio/reflexio/server/services/profile/service.py +166 -66
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/service.py +457 -107
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/session_dedup.py +127 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/temporal.py +104 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/dispatcher.py +139 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +16 -6
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/worker.py +137 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/governance_validation.py +12 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/lifecycle_filters.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +41 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +40 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +190 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +11 -4
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_governance.py +28 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_learning_jobs.py +414 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_lineage.py +11 -5
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +8 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_deletion.py +16 -5
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/base/_fts_vec.py +86 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_erase_execution.py +104 -42
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/governance/_subject_barrier.py +7 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_agent.py +59 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_eval_results.py +430 -5
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_source_linkage.py +8 -3
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/playbook/_user.py +57 -12
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +197 -12
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +70 -11
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_commit_scope.py +9 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +9 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_learning_jobs.py +269 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +7 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_retrieval_log.py +3 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +4 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/agent_run/_models.py +12 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/evaluation_state_keys.py +78 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_agent.py +38 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_eval_results.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/playbook/_user.py +52 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_interaction_store.py +82 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_profile_store.py +74 -2
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/retrieved_learning_state.py +226 -0
- package/plugin/vendor/reflexio/reflexio/server/services/tagging/tagging_scheduler.py +5 -6
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +209 -29
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +3 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +61 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
|
@@ -85,6 +85,8 @@ write_failure() {
|
|
|
85
85
|
local reason fp_hash
|
|
86
86
|
reason="$1"
|
|
87
87
|
fp_hash="$(claude_smart_install_fingerprint_hash "$PLUGIN_ROOT" "$HERE" 2>/dev/null || true)"
|
|
88
|
+
# bin/claude-smart.js reads this marker too: first line is the user-facing
|
|
89
|
+
# failure reason, and fingerprint=... marks which install attempt produced it.
|
|
88
90
|
{
|
|
89
91
|
printf '%s\n' "$reason"
|
|
90
92
|
if [ -n "$fp_hash" ]; then
|
|
@@ -97,6 +99,22 @@ write_failure() {
|
|
|
97
99
|
exit 0
|
|
98
100
|
}
|
|
99
101
|
|
|
102
|
+
verify_windows_local_embedding_runtime() {
|
|
103
|
+
claude_smart_is_windows || return 0
|
|
104
|
+
[ "${CLAUDE_SMART_USE_LOCAL_EMBEDDING:-1}" = "1" ] || return 0
|
|
105
|
+
if claude_smart_python_imports "$PLUGIN_ROOT" onnxruntime; then
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
write_failure "Windows local embedding requires Microsoft Visual C++ Redistributable for onnxruntime; install the x64 redistributable from https://aka.ms/vs/17/release/vc_redist.x64.exe and rerun claude-smart install."
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if [ "${1:-}" = "verify-windows-embedding" ]; then
|
|
112
|
+
# Node bootstrap runs this after uv sync so install-failed markers stay
|
|
113
|
+
# consistent with the shell installer path.
|
|
114
|
+
verify_windows_local_embedding_runtime
|
|
115
|
+
exit 0
|
|
116
|
+
fi
|
|
117
|
+
|
|
100
118
|
install_fingerprint() {
|
|
101
119
|
claude_smart_install_fingerprint "$PLUGIN_ROOT" "$HERE"
|
|
102
120
|
}
|
|
@@ -112,6 +130,7 @@ install_complete() {
|
|
|
112
130
|
grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_USE_LOCAL_CLI=' "$HOME/.claude-smart/.env" || return 1
|
|
113
131
|
grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_USE_LOCAL_EMBEDDING=' "$HOME/.claude-smart/.env" || return 1
|
|
114
132
|
grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_READ_ONLY=' "$HOME/.claude-smart/.env" || return 1
|
|
133
|
+
grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_HOST=' "$HOME/.claude-smart/.env" || return 1
|
|
115
134
|
fi
|
|
116
135
|
if [ -d "$PLUGIN_ROOT/dashboard" ]; then
|
|
117
136
|
[ -d "$PLUGIN_ROOT/dashboard/.next" ] || [ -f "$MARKER_DIR/dashboard-build.pid" ] || [ -f "$(claude_smart_dashboard_unavailable_marker)" ] || return 1
|
|
@@ -130,7 +149,21 @@ install_vendored_reflexio() {
|
|
|
130
149
|
local VENDORED_REFLEXIO PLUGIN_PYTHON
|
|
131
150
|
|
|
132
151
|
VENDORED_REFLEXIO="$PLUGIN_ROOT/vendor/reflexio"
|
|
133
|
-
[ -f "$VENDORED_REFLEXIO/pyproject.toml" ]
|
|
152
|
+
if [ ! -f "$VENDORED_REFLEXIO/pyproject.toml" ]; then
|
|
153
|
+
# A host plugin cache (~/.claude/plugins/cache/..., ~/.codex/plugins/cache/...)
|
|
154
|
+
# is only ever populated from the npm package, which always carries the
|
|
155
|
+
# vendored runtime. Missing it there means the plugin was installed straight
|
|
156
|
+
# from the GitHub marketplace, where the bundle is gitignored — the backend
|
|
157
|
+
# could never start. Fail here, at Setup, rather than at first session.
|
|
158
|
+
case "$PLUGIN_ROOT" in
|
|
159
|
+
*/plugins/cache/*)
|
|
160
|
+
write_failure "bundled Reflexio runtime is missing from $VENDORED_REFLEXIO. This happens when claude-smart is installed from the GitHub marketplace, which does not carry the generated runtime. Install with: npx claude-smart install"
|
|
161
|
+
;;
|
|
162
|
+
esac
|
|
163
|
+
# A source checkout legitimately has no vendor bundle (it is generated at
|
|
164
|
+
# pack time); reflexio comes from the plugin venv instead.
|
|
165
|
+
return 0
|
|
166
|
+
fi
|
|
134
167
|
|
|
135
168
|
if claude_smart_is_windows; then
|
|
136
169
|
PLUGIN_PYTHON="$PLUGIN_ROOT/.venv/Scripts/python.exe"
|
|
@@ -196,7 +229,7 @@ install_private_node() {
|
|
|
196
229
|
local NODE_MIN_MAJOR NODE_MIN_MINOR NODE_LTS_MAJOR
|
|
197
230
|
local node_os archive_ext reason node_arch node_platform base_url node_root
|
|
198
231
|
local tmp_dir shasums_path archive_name ext_re install_dir archive_path
|
|
199
|
-
local expected_hash actual_hash archive_win dest_win candidate_path next_link
|
|
232
|
+
local expected_hash actual_hash archive_win dest_win candidate_path next_link old_current
|
|
200
233
|
|
|
201
234
|
NODE_MIN_MAJOR=20
|
|
202
235
|
NODE_MIN_MINOR=9
|
|
@@ -361,19 +394,57 @@ install_private_node() {
|
|
|
361
394
|
return 1
|
|
362
395
|
fi
|
|
363
396
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
397
|
+
if claude_smart_is_windows; then
|
|
398
|
+
# Windows cannot rely on the POSIX symlink swap path; keep current recoverable.
|
|
399
|
+
old_current="$node_root/current.old.$$"
|
|
400
|
+
rm -rf "$old_current" 2>/dev/null || true
|
|
401
|
+
if [ -e "$node_root/current" ] || [ -L "$node_root/current" ]; then
|
|
402
|
+
if ! mv "$node_root/current" "$old_current"; then
|
|
403
|
+
rm -rf "$tmp_dir" "$install_dir"
|
|
404
|
+
reason="could not stage existing private Node.js install for replacement"
|
|
405
|
+
echo "[claude-smart] WARNING: $reason" >&2
|
|
406
|
+
claude_smart_write_dashboard_unavailable "$reason"
|
|
407
|
+
return 1
|
|
408
|
+
fi
|
|
409
|
+
fi
|
|
410
|
+
if mv "$install_dir" "$node_root/current"; then
|
|
411
|
+
rm -rf "$old_current" 2>/dev/null || true
|
|
370
412
|
else
|
|
371
|
-
|
|
372
|
-
|
|
413
|
+
reason="could not activate private Node.js install at $node_root/current"
|
|
414
|
+
if [ -e "$node_root/current" ] || [ -L "$node_root/current" ]; then
|
|
415
|
+
if ! rm -rf "$node_root/current"; then
|
|
416
|
+
reason="could not remove failed private Node.js install at $node_root/current; previous install remains at $old_current for manual recovery"
|
|
417
|
+
rm -rf "$tmp_dir" "$install_dir"
|
|
418
|
+
echo "[claude-smart] WARNING: $reason" >&2
|
|
419
|
+
claude_smart_write_dashboard_unavailable "$reason"
|
|
420
|
+
return 1
|
|
421
|
+
fi
|
|
422
|
+
fi
|
|
423
|
+
if [ -e "$old_current" ] || [ -L "$old_current" ]; then
|
|
424
|
+
if ! mv "$old_current" "$node_root/current"; then
|
|
425
|
+
reason="could not restore previous private Node.js install from $old_current after activation failure; previous install remains at $old_current for manual recovery"
|
|
426
|
+
fi
|
|
427
|
+
fi
|
|
428
|
+
rm -rf "$tmp_dir" "$install_dir"
|
|
429
|
+
echo "[claude-smart] WARNING: $reason" >&2
|
|
430
|
+
claude_smart_write_dashboard_unavailable "$reason"
|
|
431
|
+
return 1
|
|
373
432
|
fi
|
|
374
433
|
else
|
|
375
|
-
|
|
376
|
-
|
|
434
|
+
next_link="$node_root/current.next.$$"
|
|
435
|
+
if ln -s "$install_dir" "$next_link" 2>/dev/null; then
|
|
436
|
+
if mv -Tf "$next_link" "$node_root/current" 2>/dev/null; then
|
|
437
|
+
:
|
|
438
|
+
elif mv -hf "$next_link" "$node_root/current" 2>/dev/null; then
|
|
439
|
+
:
|
|
440
|
+
else
|
|
441
|
+
rm -rf "$node_root/current"
|
|
442
|
+
mv "$next_link" "$node_root/current"
|
|
443
|
+
fi
|
|
444
|
+
else
|
|
445
|
+
rm -rf "$next_link" "$node_root/current"
|
|
446
|
+
mv "$install_dir" "$node_root/current"
|
|
447
|
+
fi
|
|
377
448
|
fi
|
|
378
449
|
|
|
379
450
|
rm -rf "$tmp_dir"
|
|
@@ -399,6 +470,7 @@ fi
|
|
|
399
470
|
preflight_supported_runtime_platform
|
|
400
471
|
|
|
401
472
|
if install_complete; then
|
|
473
|
+
verify_windows_local_embedding_runtime
|
|
402
474
|
start_backend_service
|
|
403
475
|
claude_smart_emit_continue
|
|
404
476
|
exit 0
|
|
@@ -489,7 +561,7 @@ fi
|
|
|
489
561
|
# claude-smart's backend loads ~/.claude-smart/.env (backend-service.sh exports
|
|
490
562
|
# REFLEXIO_ENV_FILE so reflexio's CLI reads it instead of ~/.reflexio/.env);
|
|
491
563
|
# append our opt-in flags there so `reflexio services start` picks them up. Keep
|
|
492
|
-
# this path in sync with env_config.
|
|
564
|
+
# this path in sync with env_config.CLAUDE_SMART_ENV_PATH and backend-service.sh.
|
|
493
565
|
REFLEXIO_ENV="$HOME/.claude-smart/.env"
|
|
494
566
|
mkdir -p "$(dirname "$REFLEXIO_ENV")"
|
|
495
567
|
claude_smart_env_quote() {
|
|
@@ -562,6 +634,9 @@ claude_smart_prune_managed_env_keys_for_local() {
|
|
|
562
634
|
}
|
|
563
635
|
|
|
564
636
|
claude_smart_ensure_local_env_defaults() {
|
|
637
|
+
local local_cli_default local_embedding_default
|
|
638
|
+
local_cli_default="${CLAUDE_SMART_USE_LOCAL_CLI:-1}"
|
|
639
|
+
local_embedding_default="${CLAUDE_SMART_USE_LOCAL_EMBEDDING:-1}"
|
|
565
640
|
[ -z "${REFLEXIO_API_KEY:-}" ] || return 0
|
|
566
641
|
mkdir -p "$(dirname "$REFLEXIO_ENV")"
|
|
567
642
|
touch "$REFLEXIO_ENV"
|
|
@@ -569,23 +644,29 @@ claude_smart_ensure_local_env_defaults() {
|
|
|
569
644
|
claude_smart_prune_managed_env_keys_for_local
|
|
570
645
|
unset REFLEXIO_URL REFLEXIO_API_KEY REFLEXIO_USER_ID CLAUDE_SMART_MANAGED_SETUP
|
|
571
646
|
if ! grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_USE_LOCAL_CLI=' "$REFLEXIO_ENV"; then
|
|
572
|
-
printf '# Route reflexio generation through the local
|
|
573
|
-
claude_smart_env_append_raw_if_missing CLAUDE_SMART_USE_LOCAL_CLI "
|
|
574
|
-
echo "[claude-smart] appended CLAUDE_SMART_USE_LOCAL_CLI
|
|
647
|
+
printf '# Route reflexio generation through the configured local host CLI\n' >> "$REFLEXIO_ENV"
|
|
648
|
+
claude_smart_env_append_raw_if_missing CLAUDE_SMART_USE_LOCAL_CLI "$local_cli_default"
|
|
649
|
+
echo "[claude-smart] appended CLAUDE_SMART_USE_LOCAL_CLI=$local_cli_default to $REFLEXIO_ENV" >&2
|
|
575
650
|
fi
|
|
576
651
|
if ! grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_USE_LOCAL_EMBEDDING=' "$REFLEXIO_ENV"; then
|
|
577
652
|
printf '# Use the in-process ONNX embedder (chromadb) - no API key for semantic search\n' >> "$REFLEXIO_ENV"
|
|
578
|
-
claude_smart_env_append_raw_if_missing CLAUDE_SMART_USE_LOCAL_EMBEDDING "
|
|
579
|
-
echo "[claude-smart] appended CLAUDE_SMART_USE_LOCAL_EMBEDDING
|
|
653
|
+
claude_smart_env_append_raw_if_missing CLAUDE_SMART_USE_LOCAL_EMBEDDING "$local_embedding_default"
|
|
654
|
+
echo "[claude-smart] appended CLAUDE_SMART_USE_LOCAL_EMBEDDING=$local_embedding_default to $REFLEXIO_ENV" >&2
|
|
580
655
|
fi
|
|
581
656
|
if ! grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_READ_ONLY=' "$REFLEXIO_ENV"; then
|
|
582
657
|
claude_smart_env_upsert CLAUDE_SMART_READ_ONLY "0"
|
|
583
658
|
echo "[claude-smart] appended CLAUDE_SMART_READ_ONLY=0 to $REFLEXIO_ENV" >&2
|
|
584
659
|
fi
|
|
660
|
+
if ! grep -qE '^(export[[:space:]]+)?CLAUDE_SMART_HOST=' "$REFLEXIO_ENV"; then
|
|
661
|
+
claude_smart_env_upsert CLAUDE_SMART_HOST "claude-code"
|
|
662
|
+
echo "[claude-smart] appended CLAUDE_SMART_HOST=claude-code to $REFLEXIO_ENV" >&2
|
|
663
|
+
fi
|
|
585
664
|
chmod 600 "$REFLEXIO_ENV"
|
|
586
665
|
}
|
|
587
666
|
|
|
588
667
|
claude_smart_ensure_local_env_defaults
|
|
668
|
+
# No-op outside Git Bash/MSYS/Cygwin; non-Windows installs must not depend on it.
|
|
669
|
+
verify_windows_local_embedding_runtime
|
|
589
670
|
|
|
590
671
|
# Migrate stale REFLEXIO_URL from reflexio's library default (8081) to the
|
|
591
672
|
# plugin backend port (8071). Matches the quoted and unquoted forms but
|
|
@@ -599,9 +680,23 @@ if [ -f "$REFLEXIO_ENV" ] && grep -qE '^REFLEXIO_URL=("http://localhost:8081/?"|
|
|
|
599
680
|
echo "[claude-smart] migrated REFLEXIO_URL 8081 → 8071 in $REFLEXIO_ENV (backup at $REFLEXIO_ENV.bak)" >&2
|
|
600
681
|
fi
|
|
601
682
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
683
|
+
case "${CLAUDE_SMART_HOST:-claude-code}" in
|
|
684
|
+
opencode)
|
|
685
|
+
if [ -z "${CLAUDE_SMART_OPENCODE_PATH:-}" ] && ! command -v opencode >/dev/null 2>&1; then
|
|
686
|
+
echo "[claude-smart] WARNING: 'opencode' CLI not on PATH and CLAUDE_SMART_OPENCODE_PATH is not set — reflexio extractors will have no LLM until OpenCode is installed" >&2
|
|
687
|
+
fi
|
|
688
|
+
;;
|
|
689
|
+
codex)
|
|
690
|
+
if ! command -v codex >/dev/null 2>&1; then
|
|
691
|
+
echo "[claude-smart] WARNING: 'codex' CLI not on PATH — reflexio extractors will have no LLM until Codex is installed" >&2
|
|
692
|
+
fi
|
|
693
|
+
;;
|
|
694
|
+
*)
|
|
695
|
+
if ! command -v claude >/dev/null 2>&1; then
|
|
696
|
+
echo "[claude-smart] WARNING: 'claude' CLI not on PATH — reflexio extractors will have no LLM until Claude Code is installed" >&2
|
|
697
|
+
fi
|
|
698
|
+
;;
|
|
699
|
+
esac
|
|
605
700
|
|
|
606
701
|
LEGACY_CS_CITE="$HOME/.claude-smart/bin/cs-cite"
|
|
607
702
|
if [ -e "$LEGACY_CS_CITE" ]; then
|
package/plugin/src/README.md
CHANGED
|
@@ -28,7 +28,7 @@ Description: Python package powering the claude-smart plugin — hook handlers t
|
|
|
28
28
|
|
|
29
29
|
| File | Purpose |
|
|
30
30
|
|------|---------|
|
|
31
|
-
| `env_config.py` | Parses `~/.claude-smart/.env` (`REFLEXIO_URL`, `REFLEXIO_API_KEY`, `CLAUDE_SMART_READ_ONLY`, local embedding/CLI flags). |
|
|
31
|
+
| `env_config.py` | Parses `~/.claude-smart/.env` (`REFLEXIO_URL`, `REFLEXIO_API_KEY`, `CLAUDE_SMART_READ_ONLY`, `CLAUDE_SMART_HOST`, local embedding/CLI flags). |
|
|
32
32
|
| `runtime.py` | Host detection (Claude Code vs Codex); shared agent version. |
|
|
33
33
|
| `ids.py` | Session / project ID generation and resolution. |
|
|
34
34
|
| `context_inject.py`, `context_format.py`, `query_compose.py`, `cs_cite.py` | Build search queries, format learned skills as markdown, inject into context, format citations. |
|
|
@@ -37,10 +37,14 @@ from pathlib import Path
|
|
|
37
37
|
from urllib.parse import urlparse
|
|
38
38
|
from urllib.request import url2pathname
|
|
39
39
|
|
|
40
|
-
from claude_smart import context_format, cs_cite, env_config, ids, publish, state
|
|
40
|
+
from claude_smart import context_format, cs_cite, env_config, ids, publish, runtime, state
|
|
41
41
|
from claude_smart.reflexio_adapter import Adapter
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
_HOST_CLAUDE_CODE = runtime.HOST_CLAUDE_CODE
|
|
44
|
+
_HOST_CODEX = runtime.HOST_CODEX
|
|
45
|
+
_HOST_OPENCODE = runtime.HOST_OPENCODE
|
|
46
|
+
_HOST_CHOICES = (_HOST_CLAUDE_CODE, _HOST_CODEX, _HOST_OPENCODE)
|
|
47
|
+
_CLAUDE_SMART_ENV_PATH = env_config.CLAUDE_SMART_ENV_PATH
|
|
44
48
|
_MANAGED_REFLEXIO_URL = env_config.MANAGED_REFLEXIO_URL
|
|
45
49
|
_PLUGIN_SPEC = "claude-smart@reflexioai"
|
|
46
50
|
_CODEX_MARKETPLACE_NAME = "reflexioai"
|
|
@@ -99,6 +103,7 @@ _DEFAULT_STORAGE_ROOT = _REFLEXIO_DIR / "data"
|
|
|
99
103
|
_REFLEXIO_ORG_ID = "claude-smart"
|
|
100
104
|
_REFLEXIO_CONFIG_PATH = _REFLEXIO_DIR / "configs" / f"config_{_REFLEXIO_ORG_ID}.json"
|
|
101
105
|
_LOCAL_STORAGE_ENV = "LOCAL_STORAGE_PATH"
|
|
106
|
+
_OPENCODE_PATH_ENV = "CLAUDE_SMART_OPENCODE_PATH"
|
|
102
107
|
_CODEX_REQUIRED_FILES = (
|
|
103
108
|
Path(".agents/plugins/marketplace.json"),
|
|
104
109
|
Path("plugin/.codex-plugin/plugin.json"),
|
|
@@ -927,15 +932,20 @@ def _register_codex_marketplace(root: Path) -> tuple[bool, str]:
|
|
|
927
932
|
return False, output or "Codex CLI does not expose plugin marketplace commands"
|
|
928
933
|
|
|
929
934
|
|
|
930
|
-
def _configure_reflexio_setup() -> bool:
|
|
935
|
+
def _configure_reflexio_setup(host: str = _HOST_CLAUDE_CODE) -> bool:
|
|
931
936
|
"""Load setup state and ensure local defaults when unmanaged.
|
|
932
937
|
|
|
938
|
+
Args:
|
|
939
|
+
host: Host integration being installed. Persisted in local mode so
|
|
940
|
+
later backend restarts can wire the same extraction bridge even
|
|
941
|
+
when the host plugin server is not the process launcher.
|
|
942
|
+
|
|
933
943
|
Returns:
|
|
934
944
|
bool: Whether read-only mode is enabled.
|
|
935
945
|
"""
|
|
936
|
-
env_config.load_reflexio_env(
|
|
946
|
+
env_config.load_reflexio_env(_CLAUDE_SMART_ENV_PATH)
|
|
937
947
|
try:
|
|
938
|
-
env_text =
|
|
948
|
+
env_text = _CLAUDE_SMART_ENV_PATH.read_text()
|
|
939
949
|
except OSError:
|
|
940
950
|
env_text = ""
|
|
941
951
|
read_only_value = ""
|
|
@@ -975,9 +985,9 @@ def _configure_reflexio_setup() -> bool:
|
|
|
975
985
|
os.environ.pop(env_config.REFLEXIO_API_KEY_ENV, None)
|
|
976
986
|
os.environ.pop("REFLEXIO_USER_ID", None)
|
|
977
987
|
os.environ.pop("CLAUDE_SMART_MANAGED_SETUP", None)
|
|
978
|
-
added = env_config.ensure_local_env_defaults(
|
|
988
|
+
added = env_config.ensure_local_env_defaults(_CLAUDE_SMART_ENV_PATH, host=host)
|
|
979
989
|
if added:
|
|
980
|
-
sys.stdout.write(f"Seeded {
|
|
990
|
+
sys.stdout.write(f"Seeded {_CLAUDE_SMART_ENV_PATH} with {', '.join(added)}.\n")
|
|
981
991
|
return read_only
|
|
982
992
|
|
|
983
993
|
|
|
@@ -1174,7 +1184,7 @@ def _patch_opencode_plugin_config(
|
|
|
1174
1184
|
|
|
1175
1185
|
|
|
1176
1186
|
def _has_extraction_provider() -> bool:
|
|
1177
|
-
env_config.load_reflexio_env(
|
|
1187
|
+
env_config.load_reflexio_env(_CLAUDE_SMART_ENV_PATH)
|
|
1178
1188
|
if os.environ.get(env_config.REFLEXIO_API_KEY_ENV, "").strip():
|
|
1179
1189
|
return True
|
|
1180
1190
|
cli_path = os.environ.get("CLAUDE_SMART_CLI_PATH", "").strip()
|
|
@@ -1182,7 +1192,7 @@ def _has_extraction_provider() -> bool:
|
|
|
1182
1192
|
resolved = Path(cli_path).expanduser()
|
|
1183
1193
|
if resolved.is_file() and os.access(resolved, os.X_OK):
|
|
1184
1194
|
return True
|
|
1185
|
-
return bool(shutil.which("claude") or shutil.which("codex") or
|
|
1195
|
+
return bool(shutil.which("claude") or shutil.which("codex") or _resolve_opencode_path())
|
|
1186
1196
|
|
|
1187
1197
|
|
|
1188
1198
|
def _extraction_provider_error() -> str:
|
|
@@ -1193,6 +1203,41 @@ def _extraction_provider_error() -> str:
|
|
|
1193
1203
|
)
|
|
1194
1204
|
|
|
1195
1205
|
|
|
1206
|
+
def _has_opencode_cli() -> bool:
|
|
1207
|
+
return _resolve_opencode_path() is not None
|
|
1208
|
+
|
|
1209
|
+
|
|
1210
|
+
def _resolve_opencode_path() -> str | None:
|
|
1211
|
+
opencode_path = os.environ.get(_OPENCODE_PATH_ENV, "").strip()
|
|
1212
|
+
if opencode_path:
|
|
1213
|
+
resolved = Path(opencode_path).expanduser()
|
|
1214
|
+
if resolved.is_file() and os.access(resolved, os.X_OK):
|
|
1215
|
+
return str(resolved)
|
|
1216
|
+
return shutil.which("opencode")
|
|
1217
|
+
|
|
1218
|
+
|
|
1219
|
+
def _persist_opencode_path() -> list[str]:
|
|
1220
|
+
resolved = _resolve_opencode_path()
|
|
1221
|
+
if not resolved:
|
|
1222
|
+
return []
|
|
1223
|
+
os.environ[_OPENCODE_PATH_ENV] = resolved
|
|
1224
|
+
return env_config.set_env_vars(_CLAUDE_SMART_ENV_PATH, {_OPENCODE_PATH_ENV: resolved})
|
|
1225
|
+
|
|
1226
|
+
|
|
1227
|
+
def _opencode_prerequisite_error() -> str | None:
|
|
1228
|
+
if not _has_opencode_cli():
|
|
1229
|
+
return (
|
|
1230
|
+
"error: OpenCode CLI not found on PATH. Install OpenCode first, "
|
|
1231
|
+
"or set CLAUDE_SMART_OPENCODE_PATH to the OpenCode executable.\n"
|
|
1232
|
+
)
|
|
1233
|
+
if os.name == "nt" and not _resolve_bash():
|
|
1234
|
+
return (
|
|
1235
|
+
"error: Git Bash is required for claude-smart OpenCode support on Windows. "
|
|
1236
|
+
"Install Git for Windows and ensure bash.exe is on PATH, or run OpenCode from WSL.\n"
|
|
1237
|
+
)
|
|
1238
|
+
return None
|
|
1239
|
+
|
|
1240
|
+
|
|
1196
1241
|
def _opencode_install_supported_from_this_package() -> bool:
|
|
1197
1242
|
return (_SCRIPTS_DIR / "smart-install.sh").is_file()
|
|
1198
1243
|
|
|
@@ -1332,7 +1377,12 @@ def cmd_install_opencode(args: argparse.Namespace) -> int:
|
|
|
1332
1377
|
"Run `npx claude-smart install --host opencode`.\n"
|
|
1333
1378
|
)
|
|
1334
1379
|
return 1
|
|
1335
|
-
|
|
1380
|
+
prerequisite_error = _opencode_prerequisite_error()
|
|
1381
|
+
if prerequisite_error:
|
|
1382
|
+
sys.stderr.write(prerequisite_error)
|
|
1383
|
+
return 1
|
|
1384
|
+
read_only = _configure_reflexio_setup(host=_HOST_OPENCODE)
|
|
1385
|
+
_persist_opencode_path()
|
|
1336
1386
|
if not _has_extraction_provider():
|
|
1337
1387
|
sys.stderr.write(_extraction_provider_error())
|
|
1338
1388
|
return 1
|
|
@@ -1387,7 +1437,7 @@ def cmd_install_codex(args: argparse.Namespace) -> int:
|
|
|
1387
1437
|
"error: 'uv' not found on PATH. Install uv or restart your shell.\n"
|
|
1388
1438
|
)
|
|
1389
1439
|
return 1
|
|
1390
|
-
read_only = _configure_reflexio_setup()
|
|
1440
|
+
read_only = _configure_reflexio_setup(host=_HOST_CODEX)
|
|
1391
1441
|
|
|
1392
1442
|
missing = _missing_codex_marketplace_files(_REPO_ROOT)
|
|
1393
1443
|
if missing:
|
|
@@ -1485,9 +1535,9 @@ def cmd_install(args: argparse.Namespace) -> int:
|
|
|
1485
1535
|
Returns:
|
|
1486
1536
|
int: 0 on success, non-zero if the ``claude`` CLI is missing or fails.
|
|
1487
1537
|
"""
|
|
1488
|
-
if getattr(args, "host",
|
|
1538
|
+
if getattr(args, "host", _HOST_CLAUDE_CODE) == _HOST_CODEX:
|
|
1489
1539
|
return cmd_install_codex(args)
|
|
1490
|
-
if getattr(args, "host",
|
|
1540
|
+
if getattr(args, "host", _HOST_CLAUDE_CODE) == _HOST_OPENCODE:
|
|
1491
1541
|
return cmd_install_opencode(args)
|
|
1492
1542
|
|
|
1493
1543
|
if not shutil.which("claude"):
|
|
@@ -1496,7 +1546,7 @@ def cmd_install(args: argparse.Namespace) -> int:
|
|
|
1496
1546
|
"Install Claude Code first: https://claude.com/claude-code\n"
|
|
1497
1547
|
)
|
|
1498
1548
|
return 1
|
|
1499
|
-
read_only = _configure_reflexio_setup()
|
|
1549
|
+
read_only = _configure_reflexio_setup(host=_HOST_CLAUDE_CODE)
|
|
1500
1550
|
|
|
1501
1551
|
refresh_existing = bool(getattr(args, "refresh_existing", False))
|
|
1502
1552
|
for cmd in (
|
|
@@ -1559,16 +1609,16 @@ def cmd_update(args: argparse.Namespace) -> int:
|
|
|
1559
1609
|
Returns:
|
|
1560
1610
|
int: 0 on success, non-zero if the host CLI is missing or install fails.
|
|
1561
1611
|
"""
|
|
1562
|
-
if getattr(args, "host",
|
|
1612
|
+
if getattr(args, "host", _HOST_CLAUDE_CODE) == _HOST_CODEX:
|
|
1563
1613
|
return cmd_update_codex(args)
|
|
1564
|
-
if getattr(args, "host",
|
|
1614
|
+
if getattr(args, "host", _HOST_CLAUDE_CODE) == _HOST_OPENCODE:
|
|
1565
1615
|
return cmd_update_opencode(args)
|
|
1566
1616
|
|
|
1567
1617
|
_run_service(_DASHBOARD_SCRIPT, "stop")
|
|
1568
1618
|
_run_service(_BACKEND_SCRIPT, "stop")
|
|
1569
1619
|
sys.stdout.write("Updating claude-smart by reinstalling from this package...\n")
|
|
1570
1620
|
install_args = argparse.Namespace(**vars(args), refresh_existing=True)
|
|
1571
|
-
install_args.host =
|
|
1621
|
+
install_args.host = _HOST_CLAUDE_CODE
|
|
1572
1622
|
return cmd_install(install_args)
|
|
1573
1623
|
|
|
1574
1624
|
|
|
@@ -1585,7 +1635,7 @@ def cmd_update_codex(_args: argparse.Namespace) -> int:
|
|
|
1585
1635
|
"Updating claude-smart Codex support by reinstalling from this package...\n"
|
|
1586
1636
|
)
|
|
1587
1637
|
install_args = argparse.Namespace(**vars(_args))
|
|
1588
|
-
install_args.host =
|
|
1638
|
+
install_args.host = _HOST_CODEX
|
|
1589
1639
|
return cmd_install_codex(install_args)
|
|
1590
1640
|
|
|
1591
1641
|
|
|
@@ -1596,7 +1646,7 @@ def cmd_update_opencode(args: argparse.Namespace) -> int:
|
|
|
1596
1646
|
"Updating claude-smart OpenCode support by reinstalling from this package...\n"
|
|
1597
1647
|
)
|
|
1598
1648
|
install_args = argparse.Namespace(**vars(args))
|
|
1599
|
-
install_args.host =
|
|
1649
|
+
install_args.host = _HOST_OPENCODE
|
|
1600
1650
|
return cmd_install_opencode(install_args)
|
|
1601
1651
|
|
|
1602
1652
|
|
|
@@ -1612,9 +1662,9 @@ def cmd_uninstall(_args: argparse.Namespace) -> int:
|
|
|
1612
1662
|
Returns:
|
|
1613
1663
|
int: 0 on success, non-zero if the ``claude`` CLI is missing or fails.
|
|
1614
1664
|
"""
|
|
1615
|
-
if getattr(_args, "host",
|
|
1665
|
+
if getattr(_args, "host", _HOST_CLAUDE_CODE) == _HOST_CODEX:
|
|
1616
1666
|
return cmd_uninstall_codex(_args)
|
|
1617
|
-
if getattr(_args, "host",
|
|
1667
|
+
if getattr(_args, "host", _HOST_CLAUDE_CODE) == _HOST_OPENCODE:
|
|
1618
1668
|
return cmd_uninstall_opencode(_args)
|
|
1619
1669
|
|
|
1620
1670
|
if not shutil.which("claude"):
|
|
@@ -1761,7 +1811,7 @@ def _reflexio_show_footer() -> str:
|
|
|
1761
1811
|
str: A leading-separator markdown footer ending in a newline.
|
|
1762
1812
|
"""
|
|
1763
1813
|
return (
|
|
1764
|
-
f"\n---\
|
|
1814
|
+
f"\n---\nclaude-smart is powered by [reflexio]({cs_cite.REFLEXIO_REPO_URL})"
|
|
1765
1815
|
f" — if it helps you, [star it on GitHub]({cs_cite.REFLEXIO_REPO_URL}).\n"
|
|
1766
1816
|
)
|
|
1767
1817
|
|
|
@@ -1949,7 +1999,7 @@ def _resolve_absolute_path(raw_path: str | Path, *, source: str) -> Path:
|
|
|
1949
1999
|
def _effective_storage_root() -> Path:
|
|
1950
2000
|
raw = os.environ.get(_LOCAL_STORAGE_ENV, "").strip()
|
|
1951
2001
|
if not raw:
|
|
1952
|
-
raw = _read_dotenv_value(
|
|
2002
|
+
raw = _read_dotenv_value(_CLAUDE_SMART_ENV_PATH, _LOCAL_STORAGE_ENV) or ""
|
|
1953
2003
|
return _resolve_absolute_path(
|
|
1954
2004
|
raw or _DEFAULT_STORAGE_ROOT, source=_LOCAL_STORAGE_ENV
|
|
1955
2005
|
)
|
|
@@ -2135,6 +2185,20 @@ def cmd_restart(args: argparse.Namespace) -> int:
|
|
|
2135
2185
|
return 0
|
|
2136
2186
|
|
|
2137
2187
|
if do_backend:
|
|
2188
|
+
# Preparing the replacement can reinstall the vendored Reflexio if its
|
|
2189
|
+
# version drifted, so this both validates and repairs. It must succeed
|
|
2190
|
+
# before we stop anything: a backend we cannot replace is better left
|
|
2191
|
+
# running.
|
|
2192
|
+
sys.stdout.write("Preparing reflexio backend replacement…\n")
|
|
2193
|
+
preflight_rc = _run_service(_BACKEND_SCRIPT, "preflight")
|
|
2194
|
+
if preflight_rc != 0:
|
|
2195
|
+
sys.stderr.write(
|
|
2196
|
+
"error: reflexio backend replacement failed preflight; "
|
|
2197
|
+
"existing services were not changed.\n"
|
|
2198
|
+
"Repair the bundled runtime with: npx claude-smart update\n"
|
|
2199
|
+
)
|
|
2200
|
+
return preflight_rc
|
|
2201
|
+
|
|
2138
2202
|
sys.stdout.write("Stopping reflexio backend…\n")
|
|
2139
2203
|
_run_service(_BACKEND_SCRIPT, "stop")
|
|
2140
2204
|
if do_dashboard:
|
|
@@ -2449,8 +2513,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
2449
2513
|
inst = sub.add_parser("install", help="Install claude-smart")
|
|
2450
2514
|
inst.add_argument(
|
|
2451
2515
|
"--host",
|
|
2452
|
-
choices=
|
|
2453
|
-
default=
|
|
2516
|
+
choices=_HOST_CHOICES,
|
|
2517
|
+
default=_HOST_CLAUDE_CODE,
|
|
2454
2518
|
help="Install target host",
|
|
2455
2519
|
)
|
|
2456
2520
|
inst.add_argument(
|
|
@@ -2464,8 +2528,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
2464
2528
|
upd = sub.add_parser("update", help="Update claude-smart to the latest version")
|
|
2465
2529
|
upd.add_argument(
|
|
2466
2530
|
"--host",
|
|
2467
|
-
choices=
|
|
2468
|
-
default=
|
|
2531
|
+
choices=_HOST_CHOICES,
|
|
2532
|
+
default=_HOST_CLAUDE_CODE,
|
|
2469
2533
|
help="Update target host",
|
|
2470
2534
|
)
|
|
2471
2535
|
upd.add_argument(
|
|
@@ -2479,8 +2543,8 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
2479
2543
|
uni = sub.add_parser("uninstall", help="Remove claude-smart")
|
|
2480
2544
|
uni.add_argument(
|
|
2481
2545
|
"--host",
|
|
2482
|
-
choices=
|
|
2483
|
-
default=
|
|
2546
|
+
choices=_HOST_CHOICES,
|
|
2547
|
+
default=_HOST_CLAUDE_CODE,
|
|
2484
2548
|
help="Uninstall target host",
|
|
2485
2549
|
)
|
|
2486
2550
|
uni.add_argument(
|
|
@@ -59,6 +59,9 @@ def emit_context(
|
|
|
59
59
|
project_id=project_id,
|
|
60
60
|
query=query,
|
|
61
61
|
top_k=top_k,
|
|
62
|
+
# Scopes server-side dedup: rules already injected into this session
|
|
63
|
+
# are not returned again; next-best matches backfill instead.
|
|
64
|
+
session_id=session_id or None,
|
|
62
65
|
)
|
|
63
66
|
renderer = (
|
|
64
67
|
context_format.render_inline_compact_with_registry
|
|
@@ -13,17 +13,26 @@ from __future__ import annotations
|
|
|
13
13
|
import os
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
from claude_smart import runtime
|
|
17
|
+
|
|
18
|
+
CLAUDE_SMART_ENV_PATH = Path.home() / ".claude-smart" / ".env"
|
|
19
|
+
# Deprecated internal alias kept so older imports do not fail abruptly.
|
|
20
|
+
REFLEXIO_ENV_PATH = CLAUDE_SMART_ENV_PATH
|
|
17
21
|
MANAGED_REFLEXIO_URL = "https://www.reflexio.ai/"
|
|
18
22
|
REFLEXIO_URL_ENV = "REFLEXIO_URL"
|
|
19
23
|
REFLEXIO_API_KEY_ENV = "REFLEXIO_API_KEY"
|
|
20
24
|
CLAUDE_SMART_READ_ONLY_ENV = "CLAUDE_SMART_READ_ONLY"
|
|
21
25
|
CLAUDE_SMART_USE_LOCAL_CLI_ENV = "CLAUDE_SMART_USE_LOCAL_CLI"
|
|
22
26
|
CLAUDE_SMART_USE_LOCAL_EMBEDDING_ENV = "CLAUDE_SMART_USE_LOCAL_EMBEDDING"
|
|
27
|
+
CLAUDE_SMART_HOST_ENV = "CLAUDE_SMART_HOST"
|
|
28
|
+
DEFAULT_CLAUDE_SMART_HOST = runtime.HOST_CLAUDE_CODE
|
|
23
29
|
|
|
24
30
|
_LOCAL_DEFAULT_ENTRIES = (
|
|
25
31
|
(
|
|
26
|
-
"#
|
|
32
|
+
"# Set to 1 to route reflexio generation through the active host CLI "
|
|
33
|
+
"(claude for Claude Code, the host compatibility bridge for OpenCode/Codex). "
|
|
34
|
+
"Set to 0 to disable that provider so reflexio auto-detects a cloud LLM "
|
|
35
|
+
"key (anthropic, minimax, openai, etc.) from ~/.claude-smart/.env instead.",
|
|
27
36
|
CLAUDE_SMART_USE_LOCAL_CLI_ENV,
|
|
28
37
|
"1",
|
|
29
38
|
),
|
|
@@ -33,7 +42,12 @@ _LOCAL_DEFAULT_ENTRIES = (
|
|
|
33
42
|
"1",
|
|
34
43
|
),
|
|
35
44
|
(None, CLAUDE_SMART_READ_ONLY_ENV, "0"),
|
|
45
|
+
(None, CLAUDE_SMART_HOST_ENV, DEFAULT_CLAUDE_SMART_HOST),
|
|
36
46
|
)
|
|
47
|
+
_ENV_OVERRIDABLE_LOCAL_DEFAULT_KEYS = {
|
|
48
|
+
CLAUDE_SMART_USE_LOCAL_CLI_ENV,
|
|
49
|
+
CLAUDE_SMART_USE_LOCAL_EMBEDDING_ENV,
|
|
50
|
+
}
|
|
37
51
|
_LOCAL_MODE_PRUNE_KEYS = {
|
|
38
52
|
REFLEXIO_URL_ENV,
|
|
39
53
|
REFLEXIO_API_KEY_ENV,
|
|
@@ -69,7 +83,7 @@ def parse_env_line(line: str) -> tuple[str, str] | None:
|
|
|
69
83
|
|
|
70
84
|
def load_reflexio_env(path: Path | None = None) -> None:
|
|
71
85
|
"""Load ``~/.claude-smart/.env`` into ``os.environ`` without overriding values."""
|
|
72
|
-
path = path or
|
|
86
|
+
path = path or CLAUDE_SMART_ENV_PATH
|
|
73
87
|
try:
|
|
74
88
|
text = path.read_text()
|
|
75
89
|
except OSError:
|
|
@@ -118,14 +132,19 @@ def set_env_vars(path: Path, values: dict[str, str]) -> list[str]:
|
|
|
118
132
|
return added
|
|
119
133
|
|
|
120
134
|
|
|
121
|
-
def ensure_local_env_defaults(
|
|
135
|
+
def ensure_local_env_defaults(
|
|
136
|
+
path: Path | None = None,
|
|
137
|
+
host: str = DEFAULT_CLAUDE_SMART_HOST,
|
|
138
|
+
) -> list[str]:
|
|
122
139
|
"""Create or augment ``~/.claude-smart/.env`` for claude-smart local mode.
|
|
123
140
|
|
|
124
|
-
Existing active assignments win
|
|
125
|
-
|
|
126
|
-
|
|
141
|
+
Existing active assignments win except ``CLAUDE_SMART_HOST``, which is
|
|
142
|
+
install-selected state and must follow the host currently being installed.
|
|
143
|
+
This repairs first installs and deleted env files without clobbering
|
|
144
|
+
explicit user overrides such as ``CLAUDE_SMART_READ_ONLY=1``.
|
|
127
145
|
"""
|
|
128
|
-
|
|
146
|
+
install_host = host
|
|
147
|
+
path = path or CLAUDE_SMART_ENV_PATH
|
|
129
148
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
130
149
|
try:
|
|
131
150
|
existing = path.read_text()
|
|
@@ -135,6 +154,8 @@ def ensure_local_env_defaults(path: Path | None = None) -> list[str]:
|
|
|
135
154
|
present: set[str] = set()
|
|
136
155
|
kept_lines: list[str] = []
|
|
137
156
|
pruned = False
|
|
157
|
+
changed = False
|
|
158
|
+
host_written = False
|
|
138
159
|
for line in existing.splitlines():
|
|
139
160
|
parsed = parse_env_line(line)
|
|
140
161
|
if parsed is not None:
|
|
@@ -142,6 +163,19 @@ def ensure_local_env_defaults(path: Path | None = None) -> list[str]:
|
|
|
142
163
|
if key in _LOCAL_MODE_PRUNE_KEYS:
|
|
143
164
|
pruned = True
|
|
144
165
|
continue
|
|
166
|
+
if key == CLAUDE_SMART_HOST_ENV:
|
|
167
|
+
present.add(key)
|
|
168
|
+
if not host_written:
|
|
169
|
+
replacement = (
|
|
170
|
+
f"{CLAUDE_SMART_HOST_ENV}={_escape_env_value(install_host)}"
|
|
171
|
+
)
|
|
172
|
+
kept_lines.append(replacement)
|
|
173
|
+
host_written = True
|
|
174
|
+
if line != replacement or _value != install_host:
|
|
175
|
+
changed = True
|
|
176
|
+
else:
|
|
177
|
+
changed = True
|
|
178
|
+
continue
|
|
145
179
|
present.add(key)
|
|
146
180
|
kept_lines.append(line)
|
|
147
181
|
|
|
@@ -150,15 +184,16 @@ def ensure_local_env_defaults(path: Path | None = None) -> list[str]:
|
|
|
150
184
|
for comment, key, value in _LOCAL_DEFAULT_ENTRIES:
|
|
151
185
|
if key in present:
|
|
152
186
|
continue
|
|
187
|
+
effective_value = _resolve_local_env_default(key, value, install_host)
|
|
153
188
|
if comment:
|
|
154
189
|
additions.append(comment)
|
|
155
190
|
if key == CLAUDE_SMART_READ_ONLY_ENV:
|
|
156
|
-
additions.append(f'{key}="{_escape_env_value(
|
|
191
|
+
additions.append(f'{key}="{_escape_env_value(effective_value)}"')
|
|
157
192
|
else:
|
|
158
|
-
additions.append(f"{key}={_escape_env_value(
|
|
193
|
+
additions.append(f"{key}={_escape_env_value(effective_value)}")
|
|
159
194
|
added_keys.append(key)
|
|
160
195
|
|
|
161
|
-
if additions or pruned:
|
|
196
|
+
if additions or pruned or changed:
|
|
162
197
|
content = "\n".join(kept_lines)
|
|
163
198
|
if additions:
|
|
164
199
|
prefix = "" if not content or content.endswith("\n") else "\n"
|
|
@@ -171,6 +206,16 @@ def ensure_local_env_defaults(path: Path | None = None) -> list[str]:
|
|
|
171
206
|
return added_keys
|
|
172
207
|
|
|
173
208
|
|
|
209
|
+
def _resolve_local_env_default(key: str, fallback: str, install_host: str) -> str:
|
|
210
|
+
if key == CLAUDE_SMART_HOST_ENV:
|
|
211
|
+
return install_host
|
|
212
|
+
if key in _ENV_OVERRIDABLE_LOCAL_DEFAULT_KEYS:
|
|
213
|
+
explicit = os.environ.get(key, "").strip()
|
|
214
|
+
if explicit:
|
|
215
|
+
return explicit
|
|
216
|
+
return fallback
|
|
217
|
+
|
|
218
|
+
|
|
174
219
|
def env_truthy(name: str) -> bool:
|
|
175
220
|
"""Return True when an environment flag is explicitly enabled."""
|
|
176
221
|
return os.environ.get(name, "").strip().lower() in {"1", "true", "yes", "on"}
|