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
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
Check,
|
|
14
14
|
BookMarked,
|
|
15
15
|
Hash,
|
|
16
|
-
FolderGit2,
|
|
17
16
|
Clock,
|
|
18
17
|
FileText,
|
|
19
18
|
} from "lucide-react";
|
|
@@ -298,10 +297,6 @@ export default function SharedSkillDetailPage({
|
|
|
298
297
|
|
|
299
298
|
{playbook && (
|
|
300
299
|
<div className="flex items-center gap-2 flex-wrap">
|
|
301
|
-
<Badge variant="outline" className="gap-1.5">
|
|
302
|
-
<FolderGit2 className="h-3 w-3" />
|
|
303
|
-
{playbook.agent_version || "default"}
|
|
304
|
-
</Badge>
|
|
305
300
|
{editing ? (
|
|
306
301
|
<ReviewStatusBadge
|
|
307
302
|
status={playbook.playbook_status}
|
|
@@ -472,7 +467,7 @@ export default function SharedSkillDetailPage({
|
|
|
472
467
|
value={formatTimestamp(playbook.created_at)}
|
|
473
468
|
/>
|
|
474
469
|
<Meta
|
|
475
|
-
label="
|
|
470
|
+
label="Agent version"
|
|
476
471
|
value={playbook.agent_version || "default"}
|
|
477
472
|
mono
|
|
478
473
|
/>
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import { Terminal } from "lucide-react";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import type { RequestHostAttribution } from "@/lib/host-attribution";
|
|
4
|
+
import { cn } from "@/lib/utils";
|
|
5
|
+
import type { Host } from "@/lib/types";
|
|
6
|
+
|
|
7
|
+
// Exact product colors from the current official app assets. Label colors meet
|
|
8
|
+
// WCAG AA for the badge's small text: Claude 5.65:1, Codex 5.39:1, OpenCode 18.93:1.
|
|
9
|
+
const HOST_META: Record<
|
|
10
|
+
Exclude<Host, "unknown">,
|
|
11
|
+
{ label: string; badgeClass: string; dotClass: string }
|
|
12
|
+
> = {
|
|
13
|
+
"claude-code": {
|
|
14
|
+
label: "Claude Code",
|
|
15
|
+
badgeClass: "border-[#D97757] bg-[#D97757] text-[#2A120B]",
|
|
16
|
+
dotClass: "bg-[#D97757]",
|
|
17
|
+
},
|
|
18
|
+
codex: {
|
|
19
|
+
label: "Codex",
|
|
20
|
+
badgeClass: "border-[#0169CC] bg-[#0169CC] text-white",
|
|
21
|
+
dotClass: "bg-[#0169CC]",
|
|
22
|
+
},
|
|
23
|
+
opencode: {
|
|
24
|
+
label: "OpenCode",
|
|
25
|
+
badgeClass:
|
|
26
|
+
"border-[#131010] bg-[#131010] text-white dark:border-white/30",
|
|
27
|
+
dotClass: "bg-[#131010] dark:bg-white",
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export function HostBadge({
|
|
32
|
+
host,
|
|
33
|
+
display = "badge",
|
|
34
|
+
size = "md",
|
|
35
|
+
className,
|
|
36
|
+
}: {
|
|
37
|
+
host: Host | null;
|
|
38
|
+
display?: "badge" | "provenance";
|
|
39
|
+
size?: "md" | "sm";
|
|
40
|
+
className?: string;
|
|
41
|
+
}) {
|
|
42
|
+
const knownHost = host && host !== "unknown" ? host : null;
|
|
43
|
+
const meta = knownHost ? HOST_META[knownHost] : null;
|
|
44
|
+
const label = meta?.label ?? "unknown host";
|
|
45
|
+
|
|
46
|
+
if (display === "provenance") {
|
|
47
|
+
return (
|
|
48
|
+
<span
|
|
49
|
+
className={cn(
|
|
50
|
+
"inline-flex items-center gap-1 text-[11px] text-muted-foreground",
|
|
51
|
+
className,
|
|
52
|
+
)}
|
|
53
|
+
title={
|
|
54
|
+
knownHost
|
|
55
|
+
? `This learning was generated from a ${label} session; it is not limited to that host.`
|
|
56
|
+
: "The session host for this learning's generation request was not recorded."
|
|
57
|
+
}
|
|
58
|
+
>
|
|
59
|
+
<span>Generated via</span>
|
|
60
|
+
<span
|
|
61
|
+
aria-hidden="true"
|
|
62
|
+
className={cn(
|
|
63
|
+
"h-1.5 w-1.5 shrink-0 rounded-full",
|
|
64
|
+
meta
|
|
65
|
+
? meta.dotClass
|
|
66
|
+
: "border border-dashed border-muted-foreground/70",
|
|
67
|
+
)}
|
|
68
|
+
/>
|
|
69
|
+
<span className="font-medium text-foreground/75">{label}</span>
|
|
70
|
+
</span>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const compact = size === "sm";
|
|
75
|
+
const sizeClass = compact ? "h-4 px-1.5 text-[10px]" : "h-5";
|
|
76
|
+
const badgeLabel = knownHost ? label : "Host unknown";
|
|
77
|
+
return (
|
|
78
|
+
<Badge
|
|
79
|
+
variant="outline"
|
|
80
|
+
className={cn(
|
|
81
|
+
"gap-1",
|
|
82
|
+
meta
|
|
83
|
+
? meta.badgeClass
|
|
84
|
+
: "border-dashed text-muted-foreground",
|
|
85
|
+
sizeClass,
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
title={knownHost ? `Session host: ${label}` : "Session host was not recorded"}
|
|
89
|
+
>
|
|
90
|
+
<Terminal
|
|
91
|
+
className={cn(
|
|
92
|
+
knownHost ? "text-current" : "text-muted-foreground",
|
|
93
|
+
compact ? "h-2.5 w-2.5" : "h-3 w-3",
|
|
94
|
+
)}
|
|
95
|
+
/>
|
|
96
|
+
{badgeLabel}
|
|
97
|
+
</Badge>
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function LearningHostProvenance({
|
|
102
|
+
attribution,
|
|
103
|
+
requestId,
|
|
104
|
+
}: {
|
|
105
|
+
attribution: RequestHostAttribution;
|
|
106
|
+
requestId: string;
|
|
107
|
+
}) {
|
|
108
|
+
if (attribution.unavailable) {
|
|
109
|
+
return <span className="text-muted-foreground">Unavailable</span>;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return (
|
|
113
|
+
<HostBadge
|
|
114
|
+
host={attribution.hosts.get(requestId) ?? null}
|
|
115
|
+
display="provenance"
|
|
116
|
+
/>
|
|
117
|
+
);
|
|
118
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Sparkles } from "lucide-react";
|
|
2
|
+
import { Badge } from "@/components/ui/badge";
|
|
3
|
+
import { formatRelative } from "@/lib/format";
|
|
4
|
+
import type { PlaybookApplicationStat } from "@/lib/types";
|
|
5
|
+
|
|
6
|
+
export function LearningApplicationBadge({
|
|
7
|
+
stat,
|
|
8
|
+
}: {
|
|
9
|
+
stat: PlaybookApplicationStat | undefined;
|
|
10
|
+
}) {
|
|
11
|
+
if (!stat || stat.applied_count === 0) {
|
|
12
|
+
return (
|
|
13
|
+
<Badge
|
|
14
|
+
variant="outline"
|
|
15
|
+
className="h-5 text-[10px] text-muted-foreground"
|
|
16
|
+
title="No citations recorded yet for this learning. It will count once an assistant reply cites it."
|
|
17
|
+
>
|
|
18
|
+
Never applied
|
|
19
|
+
</Badge>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const last = formatRelative(stat.last_applied_at);
|
|
24
|
+
return (
|
|
25
|
+
<Badge
|
|
26
|
+
variant="outline"
|
|
27
|
+
className="h-5 gap-1 border-amber-500/45 bg-amber-500/10 text-[10px] text-amber-700 dark:text-amber-300"
|
|
28
|
+
title={`Last applied ${last}`}
|
|
29
|
+
>
|
|
30
|
+
<Sparkles className="h-2.5 w-2.5 text-amber-500" />
|
|
31
|
+
Applied {stat.applied_count}×{stat.last_applied_at ? ` · ${last}` : ""}
|
|
32
|
+
</Badge>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -7,7 +7,7 @@ export function PageHeader({
|
|
|
7
7
|
className,
|
|
8
8
|
}: {
|
|
9
9
|
title: string;
|
|
10
|
-
description?:
|
|
10
|
+
description?: React.ReactNode;
|
|
11
11
|
actions?: React.ReactNode;
|
|
12
12
|
className?: string;
|
|
13
13
|
}) {
|
|
@@ -21,9 +21,9 @@ export function PageHeader({
|
|
|
21
21
|
<div className="min-w-[min(18rem,100%)] flex-1">
|
|
22
22
|
<h1 className="text-2xl font-semibold">{title}</h1>
|
|
23
23
|
{description && (
|
|
24
|
-
<
|
|
24
|
+
<div className="text-sm text-muted-foreground mt-1 max-w-3xl">
|
|
25
25
|
{description}
|
|
26
|
-
</
|
|
26
|
+
</div>
|
|
27
27
|
)}
|
|
28
28
|
</div>
|
|
29
29
|
{actions && (
|
|
@@ -27,6 +27,10 @@ const BOOL_KEYS = new Set([
|
|
|
27
27
|
"CLAUDE_SMART_READ_ONLY",
|
|
28
28
|
]);
|
|
29
29
|
|
|
30
|
+
function defaultReflexioUrl(): string {
|
|
31
|
+
return `http://localhost:${process.env.BACKEND_PORT || "8071"}/`;
|
|
32
|
+
}
|
|
33
|
+
|
|
30
34
|
function envPath(): string {
|
|
31
35
|
return path.join(os.homedir(), ".reflexio", ".env");
|
|
32
36
|
}
|
|
@@ -49,7 +53,7 @@ function parseLine(line: string): { key: string; value: string } | null {
|
|
|
49
53
|
|
|
50
54
|
export async function readConfig(): Promise<ClaudeSmartConfig> {
|
|
51
55
|
const defaults: ClaudeSmartConfig = {
|
|
52
|
-
REFLEXIO_URL:
|
|
56
|
+
REFLEXIO_URL: defaultReflexioUrl(),
|
|
53
57
|
REFLEXIO_API_KEY: "",
|
|
54
58
|
CLAUDE_SMART_USE_LOCAL_CLI: false,
|
|
55
59
|
CLAUDE_SMART_USE_LOCAL_EMBEDDING: false,
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useEffect, useState } from "react";
|
|
2
|
+
import type { Host, SessionSummary } from "./types";
|
|
3
|
+
|
|
4
|
+
export interface RequestHostAttribution {
|
|
5
|
+
hosts: ReadonlyMap<string, Host | null>;
|
|
6
|
+
unavailable: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function hostsByRequestId(
|
|
10
|
+
sessions: SessionSummary[],
|
|
11
|
+
): Map<string, Host | null> {
|
|
12
|
+
const result = new Map<string, Host | null>();
|
|
13
|
+
for (const session of sessions) {
|
|
14
|
+
for (const requestId of session.request_ids) {
|
|
15
|
+
result.set(requestId, session.host);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function useRequestHostAttribution(): RequestHostAttribution | null {
|
|
22
|
+
const [attribution, setAttribution] =
|
|
23
|
+
useState<RequestHostAttribution | null>(null);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
let cancelled = false;
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const timeout = setTimeout(() => controller.abort(), 5_000);
|
|
29
|
+
|
|
30
|
+
fetch("/api/sessions", {
|
|
31
|
+
cache: "no-store",
|
|
32
|
+
signal: controller.signal,
|
|
33
|
+
})
|
|
34
|
+
.then(async (response) => {
|
|
35
|
+
if (!response.ok) throw new Error(`sessions ${response.status}`);
|
|
36
|
+
const data = await response.json();
|
|
37
|
+
return (data.sessions ?? []) as SessionSummary[];
|
|
38
|
+
})
|
|
39
|
+
.then((sessions) => {
|
|
40
|
+
if (!cancelled) {
|
|
41
|
+
setAttribution({
|
|
42
|
+
hosts: hostsByRequestId(sessions),
|
|
43
|
+
unavailable: false,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
.catch(() => {
|
|
48
|
+
if (!cancelled) {
|
|
49
|
+
setAttribution({ hosts: new Map(), unavailable: true });
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
.finally(() => clearTimeout(timeout));
|
|
53
|
+
|
|
54
|
+
return () => {
|
|
55
|
+
cancelled = true;
|
|
56
|
+
controller.abort();
|
|
57
|
+
clearTimeout(timeout);
|
|
58
|
+
};
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
return attribution;
|
|
62
|
+
}
|
|
@@ -10,6 +10,7 @@ import path from "node:path";
|
|
|
10
10
|
import os from "node:os";
|
|
11
11
|
import type {
|
|
12
12
|
CitedItem,
|
|
13
|
+
Host,
|
|
13
14
|
PlaybookApplicationStat,
|
|
14
15
|
SessionDetail,
|
|
15
16
|
SessionSummary,
|
|
@@ -18,6 +19,18 @@ import type {
|
|
|
18
19
|
UserActionType,
|
|
19
20
|
} from "./types";
|
|
20
21
|
|
|
22
|
+
const VALID_HOSTS: ReadonlySet<Host> = new Set([
|
|
23
|
+
"claude-code",
|
|
24
|
+
"codex",
|
|
25
|
+
"opencode",
|
|
26
|
+
"unknown",
|
|
27
|
+
]);
|
|
28
|
+
|
|
29
|
+
function parseRecordedHost(value: unknown): Host | null {
|
|
30
|
+
if (typeof value !== "string") return null;
|
|
31
|
+
return VALID_HOSTS.has(value as Host) ? (value as Host) : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
21
34
|
// Mirrors _TOOL_DATA_FIELD_MAX_LEN in plugin/src/claude_smart/state.py — we
|
|
22
35
|
// truncate to the same length the publisher ships to reflexio so the
|
|
23
36
|
// dashboard renders the exact bytes the extractor sees.
|
|
@@ -41,6 +54,8 @@ type RawRecord = {
|
|
|
41
54
|
content?: string;
|
|
42
55
|
ts?: number;
|
|
43
56
|
user_id?: string;
|
|
57
|
+
host?: string;
|
|
58
|
+
request_id?: string;
|
|
44
59
|
tool_name?: string;
|
|
45
60
|
tool_input?: Record<string, unknown>;
|
|
46
61
|
tool_output?: string;
|
|
@@ -280,6 +295,8 @@ function foldTurns(records: RawRecord[]): {
|
|
|
280
295
|
lastTs: number | null;
|
|
281
296
|
firstTs: number | null;
|
|
282
297
|
preview: string | null;
|
|
298
|
+
host: Host | null;
|
|
299
|
+
requestIds: string[];
|
|
283
300
|
} {
|
|
284
301
|
let published = 0;
|
|
285
302
|
let pendingTools: ToolUsed[] = [];
|
|
@@ -288,9 +305,17 @@ function foldTurns(records: RawRecord[]): {
|
|
|
288
305
|
let lastTs: number | null = null;
|
|
289
306
|
let firstTs: number | null = null;
|
|
290
307
|
let preview: string | null = null;
|
|
308
|
+
let host: Host | null = null;
|
|
309
|
+
const requestIds = new Set<string>();
|
|
291
310
|
|
|
292
311
|
for (let idx = 0; idx < records.length; idx++) {
|
|
293
312
|
const rec = records[idx];
|
|
313
|
+
if (host === null && rec.host !== undefined) {
|
|
314
|
+
host = parseRecordedHost(rec.host);
|
|
315
|
+
}
|
|
316
|
+
if (typeof rec.request_id === "string" && rec.request_id) {
|
|
317
|
+
requestIds.add(rec.request_id);
|
|
318
|
+
}
|
|
294
319
|
if (typeof rec.published_up_to === "number") {
|
|
295
320
|
published = rec.published_up_to;
|
|
296
321
|
pendingTools = [];
|
|
@@ -366,6 +391,8 @@ function foldTurns(records: RawRecord[]): {
|
|
|
366
391
|
lastTs,
|
|
367
392
|
firstTs,
|
|
368
393
|
preview,
|
|
394
|
+
host,
|
|
395
|
+
requestIds: Array.from(requestIds),
|
|
369
396
|
};
|
|
370
397
|
}
|
|
371
398
|
|
|
@@ -391,6 +418,8 @@ export async function listSessions(): Promise<SessionSummary[]> {
|
|
|
391
418
|
lastTs,
|
|
392
419
|
firstTs,
|
|
393
420
|
preview,
|
|
421
|
+
host,
|
|
422
|
+
requestIds,
|
|
394
423
|
} = foldTurns(records);
|
|
395
424
|
summaries.push({
|
|
396
425
|
session_id: entry.replace(/\.jsonl$/, ""),
|
|
@@ -401,6 +430,8 @@ export async function listSessions(): Promise<SessionSummary[]> {
|
|
|
401
430
|
published_up_to: publishedUpTo,
|
|
402
431
|
preview,
|
|
403
432
|
source: "local",
|
|
433
|
+
host,
|
|
434
|
+
request_ids: requestIds,
|
|
404
435
|
});
|
|
405
436
|
}
|
|
406
437
|
summaries.sort((a, b) => (b.last_activity ?? 0) - (a.last_activity ?? 0));
|
|
@@ -456,6 +487,13 @@ export async function readSession(
|
|
|
456
487
|
} catch {
|
|
457
488
|
return null;
|
|
458
489
|
}
|
|
459
|
-
const { turns, publishedUpTo } =
|
|
460
|
-
|
|
490
|
+
const { turns, publishedUpTo, learningInteractionCount, host } =
|
|
491
|
+
foldTurns(records);
|
|
492
|
+
return {
|
|
493
|
+
session_id: sessionId,
|
|
494
|
+
turns,
|
|
495
|
+
learning_interaction_count: learningInteractionCount,
|
|
496
|
+
published_up_to: publishedUpTo,
|
|
497
|
+
host,
|
|
498
|
+
};
|
|
461
499
|
}
|
|
@@ -41,6 +41,8 @@ export interface Interaction {
|
|
|
41
41
|
tools_used: ToolUsed[];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
export type Host = "claude-code" | "codex" | "opencode" | "unknown";
|
|
45
|
+
|
|
44
46
|
export interface UserPlaybook {
|
|
45
47
|
user_playbook_id: number;
|
|
46
48
|
user_id: string | null;
|
|
@@ -103,12 +105,16 @@ export interface SessionSummary {
|
|
|
103
105
|
published_up_to: number;
|
|
104
106
|
preview: string | null;
|
|
105
107
|
source: "local";
|
|
108
|
+
host: Host | null;
|
|
109
|
+
request_ids: string[];
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
export interface SessionDetail {
|
|
109
113
|
session_id: string;
|
|
110
114
|
turns: SessionTurn[];
|
|
115
|
+
learning_interaction_count: number;
|
|
111
116
|
published_up_to: number;
|
|
117
|
+
host: Host | null;
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
export interface ClaudeSmartConfig {
|
|
@@ -167,7 +173,7 @@ export interface ReflexioConfig {
|
|
|
167
173
|
export interface PlaybookApplicationStat {
|
|
168
174
|
real_id: string;
|
|
169
175
|
citation_id?: string;
|
|
170
|
-
kind: "playbook" | "profile";
|
|
176
|
+
kind: "playbook" | "profile" | "user_playbook" | "agent_playbook";
|
|
171
177
|
source_kind?: "user_playbook" | "agent_playbook" | "profile";
|
|
172
178
|
title: string;
|
|
173
179
|
href?: string;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { existsSync, readFileSync, realpathSync } from "node:fs";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
4
|
+
import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { AssistantBuffer } from "./assistant-buffer.js";
|
|
7
7
|
import { sessionIDFrom } from "./internal.js";
|
|
@@ -84,6 +84,64 @@ const SCRIPTS_DIR = resolve(PLUGIN_ROOT, "scripts");
|
|
|
84
84
|
const HOOK_ENTRY = resolve(SCRIPTS_DIR, "hook_entry.sh");
|
|
85
85
|
const BACKEND_SERVICE = resolve(SCRIPTS_DIR, "backend-service.sh");
|
|
86
86
|
const DASHBOARD_SERVICE = resolve(SCRIPTS_DIR, "dashboard-service.sh");
|
|
87
|
+
function commandPath(names) {
|
|
88
|
+
const pathParts = (process.env.PATH || "").split(delimiter).filter(Boolean);
|
|
89
|
+
for (const dir of pathParts) {
|
|
90
|
+
for (const name of names) {
|
|
91
|
+
const candidate = join(dir, name);
|
|
92
|
+
if (existsSync(candidate))
|
|
93
|
+
return candidate;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
const WINDOWS_SYSTEM_BASH_SUFFIXES = [
|
|
99
|
+
"\\windows\\system32\\bash.exe",
|
|
100
|
+
"\\windows\\sysnative\\bash.exe",
|
|
101
|
+
"\\windows\\syswow64\\bash.exe",
|
|
102
|
+
];
|
|
103
|
+
function windowsPathText(path) {
|
|
104
|
+
return path.replace(/\//g, "\\").toLowerCase();
|
|
105
|
+
}
|
|
106
|
+
function isWindowsSystemBash(path) {
|
|
107
|
+
const normalized = windowsPathText(path);
|
|
108
|
+
return WINDOWS_SYSTEM_BASH_SUFFIXES.some((suffix) => normalized.endsWith(suffix));
|
|
109
|
+
}
|
|
110
|
+
function pathCommandCandidates(names) {
|
|
111
|
+
// Return every PATH match so Windows can skip System32 bash and still find Git Bash.
|
|
112
|
+
const pathParts = (process.env.PATH || "").split(delimiter).filter(Boolean);
|
|
113
|
+
const candidates = [];
|
|
114
|
+
for (const dir of pathParts) {
|
|
115
|
+
for (const name of names) {
|
|
116
|
+
const candidate = join(dir, name);
|
|
117
|
+
if (existsSync(candidate))
|
|
118
|
+
candidates.push(candidate);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return candidates;
|
|
122
|
+
}
|
|
123
|
+
function firstUsableBash(candidates) {
|
|
124
|
+
for (const candidate of candidates) {
|
|
125
|
+
const resolved = existsSync(candidate) ? candidate : commandPath([candidate]);
|
|
126
|
+
if (resolved && !isWindowsSystemBash(resolved))
|
|
127
|
+
return resolved;
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
function bashPath() {
|
|
132
|
+
if (process.platform !== "win32")
|
|
133
|
+
return commandPath(["bash"]);
|
|
134
|
+
const bashEnv = (process.env.BASH || "").trim();
|
|
135
|
+
return firstUsableBash([
|
|
136
|
+
...(bashEnv ? [bashEnv] : []),
|
|
137
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
138
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
139
|
+
...pathCommandCandidates(["bash.exe", "bash"]),
|
|
140
|
+
"bash.exe",
|
|
141
|
+
"bash",
|
|
142
|
+
]);
|
|
143
|
+
}
|
|
144
|
+
const RESOLVED_BASH = bashPath();
|
|
87
145
|
function contextFrom(result) {
|
|
88
146
|
const hookOutput = result.hookSpecificOutput;
|
|
89
147
|
if (!hookOutput || typeof hookOutput !== "object")
|
|
@@ -109,7 +167,7 @@ function parseFirstJsonObject(text) {
|
|
|
109
167
|
}
|
|
110
168
|
function runScript(script, args, payload) {
|
|
111
169
|
return new Promise((resolvePromise) => {
|
|
112
|
-
const child = spawn("bash", [script, ...args], {
|
|
170
|
+
const child = spawn(RESOLVED_BASH || "bash", [script, ...args], {
|
|
113
171
|
cwd: PLUGIN_ROOT,
|
|
114
172
|
env: {
|
|
115
173
|
...process.env,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawn } from "node:child_process"
|
|
2
2
|
import { existsSync, readFileSync, realpathSync } from "node:fs"
|
|
3
3
|
import { homedir } from "node:os"
|
|
4
|
-
import { dirname, isAbsolute, join, relative, resolve } from "node:path"
|
|
4
|
+
import { delimiter, dirname, isAbsolute, join, relative, resolve } from "node:path"
|
|
5
5
|
import { fileURLToPath } from "node:url"
|
|
6
6
|
|
|
7
7
|
import { AssistantBuffer } from "./assistant-buffer.js"
|
|
@@ -103,6 +103,68 @@ const HOOK_ENTRY = resolve(SCRIPTS_DIR, "hook_entry.sh")
|
|
|
103
103
|
const BACKEND_SERVICE = resolve(SCRIPTS_DIR, "backend-service.sh")
|
|
104
104
|
const DASHBOARD_SERVICE = resolve(SCRIPTS_DIR, "dashboard-service.sh")
|
|
105
105
|
|
|
106
|
+
function commandPath(names: string[]): string | undefined {
|
|
107
|
+
const pathParts = (process.env.PATH || "").split(delimiter).filter(Boolean)
|
|
108
|
+
for (const dir of pathParts) {
|
|
109
|
+
for (const name of names) {
|
|
110
|
+
const candidate = join(dir, name)
|
|
111
|
+
if (existsSync(candidate)) return candidate
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return undefined
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const WINDOWS_SYSTEM_BASH_SUFFIXES = [
|
|
118
|
+
"\\windows\\system32\\bash.exe",
|
|
119
|
+
"\\windows\\sysnative\\bash.exe",
|
|
120
|
+
"\\windows\\syswow64\\bash.exe",
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
function windowsPathText(path: string): string {
|
|
124
|
+
return path.replace(/\//g, "\\").toLowerCase()
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function isWindowsSystemBash(path: string): boolean {
|
|
128
|
+
const normalized = windowsPathText(path)
|
|
129
|
+
return WINDOWS_SYSTEM_BASH_SUFFIXES.some((suffix) => normalized.endsWith(suffix))
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function pathCommandCandidates(names: string[]): string[] {
|
|
133
|
+
// Return every PATH match so Windows can skip System32 bash and still find Git Bash.
|
|
134
|
+
const pathParts = (process.env.PATH || "").split(delimiter).filter(Boolean)
|
|
135
|
+
const candidates: string[] = []
|
|
136
|
+
for (const dir of pathParts) {
|
|
137
|
+
for (const name of names) {
|
|
138
|
+
const candidate = join(dir, name)
|
|
139
|
+
if (existsSync(candidate)) candidates.push(candidate)
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return candidates
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function firstUsableBash(candidates: string[]): string | undefined {
|
|
146
|
+
for (const candidate of candidates) {
|
|
147
|
+
const resolved = existsSync(candidate) ? candidate : commandPath([candidate])
|
|
148
|
+
if (resolved && !isWindowsSystemBash(resolved)) return resolved
|
|
149
|
+
}
|
|
150
|
+
return undefined
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function bashPath(): string | undefined {
|
|
154
|
+
if (process.platform !== "win32") return commandPath(["bash"])
|
|
155
|
+
const bashEnv = (process.env.BASH || "").trim()
|
|
156
|
+
return firstUsableBash([
|
|
157
|
+
...(bashEnv ? [bashEnv] : []),
|
|
158
|
+
"C:\\Program Files\\Git\\bin\\bash.exe",
|
|
159
|
+
"C:\\Program Files (x86)\\Git\\bin\\bash.exe",
|
|
160
|
+
...pathCommandCandidates(["bash.exe", "bash"]),
|
|
161
|
+
"bash.exe",
|
|
162
|
+
"bash",
|
|
163
|
+
])
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const RESOLVED_BASH = bashPath()
|
|
167
|
+
|
|
106
168
|
function contextFrom(result: HookResult): string {
|
|
107
169
|
const hookOutput = result.hookSpecificOutput
|
|
108
170
|
if (!hookOutput || typeof hookOutput !== "object") return ""
|
|
@@ -126,7 +188,7 @@ function parseFirstJsonObject(text: string): HookResult {
|
|
|
126
188
|
|
|
127
189
|
function runScript(script: string, args: string[], payload?: Record<string, unknown>): Promise<HookResult> {
|
|
128
190
|
return new Promise((resolvePromise) => {
|
|
129
|
-
const child = spawn("bash", [script, ...args], {
|
|
191
|
+
const child = spawn(RESOLVED_BASH || "bash", [script, ...args], {
|
|
130
192
|
cwd: PLUGIN_ROOT,
|
|
131
193
|
env: {
|
|
132
194
|
...process.env,
|
package/plugin/pyproject.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "claude-smart"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.50"
|
|
4
4
|
description = "Self-improving Claude Code, Codex, and OpenCode plugin that turns corrections into Preferences, Project-specific skills, and Shared skills"
|
|
5
5
|
keywords = [
|
|
6
6
|
"claude",
|
|
@@ -26,7 +26,7 @@ dependencies = [
|
|
|
26
26
|
# normal installs; vendor releases may override it after uv sync by
|
|
27
27
|
# installing plugin/vendor/reflexio. Keep in sync via
|
|
28
28
|
# scripts/sync-reflexio-dep.py.
|
|
29
|
-
"reflexio-ai>=0.2.
|
|
29
|
+
"reflexio-ai>=0.2.28",
|
|
30
30
|
# Needed when Reflexio runs the local embedding provider (ONNXMiniLM_L6_V2).
|
|
31
31
|
# Pulls in onnxruntime + tokenizers; the ~80 MB ONNX model itself is
|
|
32
32
|
# downloaded on first use, not at install time.
|