claude-smart 0.2.49 → 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 +10 -43
- package/bin/claude-smart.js +105 -0
- 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/pyproject.toml +1 -1
- package/plugin/scripts/_lib.sh +197 -1
- package/plugin/scripts/backend-python-runner.py +46 -0
- package/plugin/scripts/backend-service.sh +757 -119
- package/plugin/scripts/codex-hook.js +63 -225
- package/plugin/scripts/dashboard-open.sh +6 -4
- package/plugin/scripts/dashboard-service.sh +117 -136
- package/plugin/scripts/hook_entry.sh +3 -0
- package/plugin/scripts/smart-install.sh +15 -1
- package/plugin/src/claude_smart/cli.py +14 -0
- package/plugin/src/claude_smart/context_inject.py +3 -0
- package/plugin/src/claude_smart/env_config.py +4 -1
- 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 +1 -1
- 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/client/client.py +126 -3
- 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 +177 -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 +66 -3
- 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 +62 -5
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +19 -52
- 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 +20 -5
- 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/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/worker.py +85 -16
- 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_worker.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +510 -63
- 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 +122 -44
- 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 +1 -1
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +50 -0
- 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 +36 -1
- 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/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 +43 -0
- 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/_user.py +45 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_interaction_store.py +146 -6
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/profiles/_profile_store.py +42 -7
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +1 -1
- 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 +50 -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 +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/profiles/_profile_store.py +52 -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
|
@@ -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;
|
package/plugin/pyproject.toml
CHANGED
package/plugin/scripts/_lib.sh
CHANGED
|
@@ -143,12 +143,23 @@ claude_smart_reflexio_url_is_remote() {
|
|
|
143
143
|
return 0
|
|
144
144
|
}
|
|
145
145
|
|
|
146
|
+
claude_smart_derive_reflexio_url_from_backend_port() {
|
|
147
|
+
local port default_url
|
|
148
|
+
port="${BACKEND_PORT:-8071}"
|
|
149
|
+
default_url="http://localhost:$port/"
|
|
150
|
+
case "${REFLEXIO_URL:-}" in
|
|
151
|
+
""|"http://localhost:8071"|"http://localhost:8071/"|"http://127.0.0.1:8071"|"http://127.0.0.1:8071/")
|
|
152
|
+
export REFLEXIO_URL="$default_url"
|
|
153
|
+
;;
|
|
154
|
+
esac
|
|
155
|
+
}
|
|
156
|
+
|
|
146
157
|
claude_smart_is_internal_invocation_env() {
|
|
147
158
|
if [ "${CLAUDE_SMART_INTERNAL:-}" = "1" ]; then
|
|
148
159
|
return 0
|
|
149
160
|
fi
|
|
150
161
|
case "${CLAUDE_CODE_ENTRYPOINT:-}" in
|
|
151
|
-
""|"cli"|"claude-desktop") return 1 ;;
|
|
162
|
+
""|"cli"|"claude-desktop"|"claude-vscode"|"claude-jetbrains") return 1 ;;
|
|
152
163
|
*) return 0 ;;
|
|
153
164
|
esac
|
|
154
165
|
}
|
|
@@ -703,6 +714,191 @@ claude_smart_kill_tree() {
|
|
|
703
714
|
kill -KILL -- "-$pid" 2>/dev/null || true
|
|
704
715
|
}
|
|
705
716
|
|
|
717
|
+
claude_smart_now_epoch() {
|
|
718
|
+
date +%s 2>/dev/null || {
|
|
719
|
+
_CS_PY=$(claude_smart_resolve_python 2>/dev/null || true)
|
|
720
|
+
[ -n "$_CS_PY" ] || return 1
|
|
721
|
+
"$_CS_PY" - <<'PY'
|
|
722
|
+
import time
|
|
723
|
+
|
|
724
|
+
print(int(time.time()))
|
|
725
|
+
PY
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
claude_smart_path_mtime_epoch() {
|
|
730
|
+
path="$1"
|
|
731
|
+
mtime=""
|
|
732
|
+
[ -e "$path" ] || return 1
|
|
733
|
+
mtime="$(stat -f %m "$path" 2>/dev/null || true)"
|
|
734
|
+
case "$mtime" in
|
|
735
|
+
''|*[!0-9]*) ;;
|
|
736
|
+
*) printf '%s\n' "$mtime"; return 0 ;;
|
|
737
|
+
esac
|
|
738
|
+
mtime="$(stat -c %Y "$path" 2>/dev/null || true)"
|
|
739
|
+
case "$mtime" in
|
|
740
|
+
''|*[!0-9]*) ;;
|
|
741
|
+
*) printf '%s\n' "$mtime"; return 0 ;;
|
|
742
|
+
esac
|
|
743
|
+
_CS_PY=$(claude_smart_resolve_python 2>/dev/null || true)
|
|
744
|
+
[ -n "$_CS_PY" ] || return 1
|
|
745
|
+
"$_CS_PY" - "$path" <<'PY'
|
|
746
|
+
import os
|
|
747
|
+
import sys
|
|
748
|
+
|
|
749
|
+
print(int(os.path.getmtime(sys.argv[1])))
|
|
750
|
+
PY
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
claude_smart_service_lock_stale_seconds() {
|
|
754
|
+
value="${CLAUDE_SMART_SERVICE_LOCK_STALE_SECONDS:-60}"
|
|
755
|
+
case "$value" in
|
|
756
|
+
''|*[!0-9]*) printf '%s\n' "60" ;;
|
|
757
|
+
*) printf '%s\n' "$value" ;;
|
|
758
|
+
esac
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
claude_smart_service_lock() {
|
|
762
|
+
name="$1"
|
|
763
|
+
lock_dir="$HOME/.claude-smart/$name.start.lock"
|
|
764
|
+
pid_file="$lock_dir/pid"
|
|
765
|
+
mkdir -p "$HOME/.claude-smart"
|
|
766
|
+
for _ in 1 2 3; do
|
|
767
|
+
if mkdir "$lock_dir" 2>/dev/null; then
|
|
768
|
+
printf '%s\n' "$$" > "$pid_file"
|
|
769
|
+
return 0
|
|
770
|
+
fi
|
|
771
|
+
|
|
772
|
+
lock_pid="$(cat "$pid_file" 2>/dev/null || true)"
|
|
773
|
+
now="$(claude_smart_now_epoch 2>/dev/null || printf '%s\n' "0")"
|
|
774
|
+
mtime="$(claude_smart_path_mtime_epoch "$lock_dir" 2>/dev/null || printf '%s\n' "$now")"
|
|
775
|
+
age=$((now - mtime))
|
|
776
|
+
stale_after="$(claude_smart_service_lock_stale_seconds)"
|
|
777
|
+
|
|
778
|
+
if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then
|
|
779
|
+
return 1
|
|
780
|
+
fi
|
|
781
|
+
if [ -z "$lock_pid" ] && [ "$age" -lt "$stale_after" ]; then
|
|
782
|
+
return 1
|
|
783
|
+
fi
|
|
784
|
+
if [ -n "$lock_pid" ] || [ "$age" -ge "$stale_after" ]; then
|
|
785
|
+
stale_dir="$lock_dir.stale.$$"
|
|
786
|
+
if mv "$lock_dir" "$stale_dir" 2>/dev/null; then
|
|
787
|
+
rm -rf "$stale_dir"
|
|
788
|
+
continue
|
|
789
|
+
fi
|
|
790
|
+
fi
|
|
791
|
+
return 1
|
|
792
|
+
done
|
|
793
|
+
return 1
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
claude_smart_service_unlock() {
|
|
797
|
+
name="$1"
|
|
798
|
+
lock_dir="$HOME/.claude-smart/$name.start.lock"
|
|
799
|
+
pid_file="$lock_dir/pid"
|
|
800
|
+
[ -d "$lock_dir" ] || return 0
|
|
801
|
+
lock_pid="$(cat "$pid_file" 2>/dev/null || true)"
|
|
802
|
+
[ "$lock_pid" = "$$" ] || return 0
|
|
803
|
+
rm -rf "$lock_dir"
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
claude_smart_powershell_bin() {
|
|
807
|
+
for cand in powershell.exe powershell pwsh.exe pwsh; do
|
|
808
|
+
if command -v "$cand" >/dev/null 2>&1; then
|
|
809
|
+
command -v "$cand"
|
|
810
|
+
return 0
|
|
811
|
+
fi
|
|
812
|
+
done
|
|
813
|
+
return 1
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
claude_smart_port_listener_pids() {
|
|
817
|
+
port="$1"
|
|
818
|
+
if claude_smart_is_windows; then
|
|
819
|
+
ps_bin="$(claude_smart_powershell_bin 2>/dev/null || true)"
|
|
820
|
+
if [ -n "$ps_bin" ]; then
|
|
821
|
+
PORT="$port" "$ps_bin" -NoProfile -ExecutionPolicy Bypass -Command \
|
|
822
|
+
'$ErrorActionPreference="SilentlyContinue"; Get-NetTCPConnection -LocalPort $env:PORT -State Listen | Select-Object -ExpandProperty OwningProcess' \
|
|
823
|
+
2>/dev/null | tr -d '\r' | awk 'NF && !seen[$1]++ {print $1}'
|
|
824
|
+
return 0
|
|
825
|
+
fi
|
|
826
|
+
if command -v netstat >/dev/null 2>&1; then
|
|
827
|
+
netstat -ano 2>/dev/null | awk -v port=":$port" '
|
|
828
|
+
$1 ~ /^TCP/ && $2 ~ port "$" && $4 == "LISTENING" && !seen[$5]++ {print $5}
|
|
829
|
+
'
|
|
830
|
+
return 0
|
|
831
|
+
fi
|
|
832
|
+
fi
|
|
833
|
+
command -v lsof >/dev/null 2>&1 || return 0
|
|
834
|
+
lsof -tiTCP:"$port" -sTCP:LISTEN -P -n 2>/dev/null | awk 'NF && !seen[$1]++ {print $1}'
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
claude_smart_pid_command() {
|
|
838
|
+
pid="$1"
|
|
839
|
+
if claude_smart_is_windows; then
|
|
840
|
+
ps_bin="$(claude_smart_powershell_bin 2>/dev/null || true)"
|
|
841
|
+
if [ -n "$ps_bin" ]; then
|
|
842
|
+
PID="$pid" "$ps_bin" -NoProfile -ExecutionPolicy Bypass -Command \
|
|
843
|
+
'$ErrorActionPreference="SilentlyContinue"; $p=Get-CimInstance Win32_Process -Filter "ProcessId=$env:PID"; if ($p) { $p.CommandLine }' \
|
|
844
|
+
2>/dev/null | tr -d '\r'
|
|
845
|
+
return 0
|
|
846
|
+
fi
|
|
847
|
+
if command -v tasklist >/dev/null 2>&1; then
|
|
848
|
+
tasklist //FI "PID eq $pid" //FO CSV //NH 2>/dev/null \
|
|
849
|
+
| awk -F, 'NR==1 {gsub(/^"|"$/, "", $1); print $1}'
|
|
850
|
+
return 0
|
|
851
|
+
fi
|
|
852
|
+
fi
|
|
853
|
+
command -v ps >/dev/null 2>&1 || return 0
|
|
854
|
+
ps -ww -p "$pid" -o command= 2>/dev/null || true
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
claude_smart_pid_parent() {
|
|
858
|
+
pid="$1"
|
|
859
|
+
if claude_smart_is_windows; then
|
|
860
|
+
ps_bin="$(claude_smart_powershell_bin 2>/dev/null || true)"
|
|
861
|
+
if [ -n "$ps_bin" ]; then
|
|
862
|
+
PID="$pid" "$ps_bin" -NoProfile -ExecutionPolicy Bypass -Command \
|
|
863
|
+
'$ErrorActionPreference="SilentlyContinue"; $p=Get-CimInstance Win32_Process -Filter "ProcessId=$env:PID"; if ($p) { $p.ParentProcessId }' \
|
|
864
|
+
2>/dev/null | tr -d '\r'
|
|
865
|
+
return 0
|
|
866
|
+
fi
|
|
867
|
+
fi
|
|
868
|
+
command -v ps >/dev/null 2>&1 || return 0
|
|
869
|
+
ps -p "$pid" -o ppid= 2>/dev/null | awk 'NF {print $1; exit}' || true
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
claude_smart_port_holder() {
|
|
873
|
+
port="$1"
|
|
874
|
+
pid="$(claude_smart_port_listener_pids "$port" | head -n1)"
|
|
875
|
+
[ -n "$pid" ] || return 0
|
|
876
|
+
cmdline="$(claude_smart_pid_command "$pid" | head -n1)"
|
|
877
|
+
[ -n "$cmdline" ] || cmdline="unknown"
|
|
878
|
+
printf '%s (pid %s)\n' "$cmdline" "$pid"
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
claude_smart_reap_port_listeners_matching() {
|
|
882
|
+
port="$1"
|
|
883
|
+
shift || true
|
|
884
|
+
[ "$#" -gt 0 ] || return 1
|
|
885
|
+
killed=1
|
|
886
|
+
pids="$(claude_smart_port_listener_pids "$port" || true)"
|
|
887
|
+
[ -n "$pids" ] || return 1
|
|
888
|
+
for pid in $pids; do
|
|
889
|
+
cmdline="$(claude_smart_pid_command "$pid" | tr '\n' ' ' || true)"
|
|
890
|
+
[ -n "$cmdline" ] || continue
|
|
891
|
+
for pattern in "$@"; do
|
|
892
|
+
if [[ "$cmdline" == $pattern ]]; then
|
|
893
|
+
claude_smart_kill_tree "$pid"
|
|
894
|
+
killed=0
|
|
895
|
+
break
|
|
896
|
+
fi
|
|
897
|
+
done
|
|
898
|
+
done
|
|
899
|
+
return "$killed"
|
|
900
|
+
}
|
|
901
|
+
|
|
706
902
|
# Return 0 (true) if $1 names a pid file whose pid is currently alive.
|
|
707
903
|
# Silent on missing/empty/stale files.
|
|
708
904
|
claude_smart_pid_alive_file() {
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Run Reflexio CLI with claude-smart metadata visible in process argv."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import os
|
|
7
|
+
import runpy
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> int:
|
|
12
|
+
"""Mirror claude-smart metadata into env and delegate to Reflexio CLI.
|
|
13
|
+
|
|
14
|
+
Args:
|
|
15
|
+
None: Reads process argv. Arguments before ``--`` are
|
|
16
|
+
``--claude-smart-*`` metadata kept visible in the process command
|
|
17
|
+
line; arguments after ``--`` become ``reflexio.cli`` arguments.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
int: Exit status. Returns ``2`` when required argument separators or
|
|
21
|
+
Reflexio CLI arguments are missing.
|
|
22
|
+
"""
|
|
23
|
+
try:
|
|
24
|
+
separator = sys.argv.index("--")
|
|
25
|
+
except ValueError:
|
|
26
|
+
return 2
|
|
27
|
+
|
|
28
|
+
metadata_args = sys.argv[1:separator]
|
|
29
|
+
reflexio_args = sys.argv[separator + 1 :]
|
|
30
|
+
if not reflexio_args:
|
|
31
|
+
return 2
|
|
32
|
+
|
|
33
|
+
for arg in metadata_args:
|
|
34
|
+
if not arg.startswith("--claude-smart-") or "=" not in arg:
|
|
35
|
+
continue
|
|
36
|
+
key, value = arg[2:].split("=", 1)
|
|
37
|
+
env_key = key.replace("-", "_").upper()
|
|
38
|
+
os.environ.setdefault(env_key, value)
|
|
39
|
+
|
|
40
|
+
sys.argv = ["reflexio.cli", *reflexio_args]
|
|
41
|
+
runpy.run_module("reflexio.cli", run_name="__main__")
|
|
42
|
+
return 0
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
if __name__ == "__main__":
|
|
46
|
+
raise SystemExit(main())
|