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
|
@@ -3,18 +3,31 @@
|
|
|
3
3
|
|
|
4
4
|
const { spawn, spawnSync } = require("node:child_process");
|
|
5
5
|
const fs = require("node:fs");
|
|
6
|
-
const http = require("node:http");
|
|
7
6
|
const os = require("node:os");
|
|
8
7
|
const path = require("node:path");
|
|
9
8
|
|
|
10
9
|
const HOME = os.homedir();
|
|
11
10
|
const STATE_DIR = path.join(HOME, ".claude-smart");
|
|
12
11
|
const REFLEXIO_DIR = path.join(HOME, ".reflexio");
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
const SERVICE_SCRIPT_TIMEOUT_MS = parsePositiveInteger(
|
|
13
|
+
process.env.CLAUDE_SMART_SERVICE_SCRIPT_TIMEOUT_MS,
|
|
14
|
+
45_000,
|
|
15
|
+
);
|
|
16
|
+
const DEFAULT_BACKEND_PORT = parsePort(process.env.BACKEND_PORT, 8071);
|
|
17
|
+
const DEFAULT_EMBEDDING_PORT = parsePort(process.env.EMBEDDING_PORT, 8072);
|
|
18
|
+
const DASHBOARD_PORT = parsePort(process.env.DASHBOARD_PORT, parsePort(process.env.PORT, 3001));
|
|
16
19
|
const LOG_MAX_BYTES = 10000000;
|
|
17
20
|
|
|
21
|
+
function parsePort(value, fallback) {
|
|
22
|
+
const port = Number(value);
|
|
23
|
+
return Number.isInteger(port) && port > 0 && port <= 65535 ? port : fallback;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parsePositiveInteger(value, fallback) {
|
|
27
|
+
const parsed = Number(value);
|
|
28
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
function emitOk() {
|
|
19
32
|
process.stdout.write('{"continue":true}\n');
|
|
20
33
|
}
|
|
@@ -207,27 +220,10 @@ function uvPath() {
|
|
|
207
220
|
return commandPath(process.platform === "win32" ? ["uv.exe", "uv"] : ["uv"]);
|
|
208
221
|
}
|
|
209
222
|
|
|
210
|
-
function npmPath() {
|
|
211
|
-
return commandPath(process.platform === "win32" ? ["npm.cmd", "npm.exe", "npm"] : ["npm"]);
|
|
212
|
-
}
|
|
213
|
-
|
|
214
223
|
function bashPath() {
|
|
215
224
|
return commandPath(process.platform === "win32" ? ["bash.exe", "bash"] : ["bash"]);
|
|
216
225
|
}
|
|
217
226
|
|
|
218
|
-
function stateFile(name) {
|
|
219
|
-
return path.join(STATE_DIR, name);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
function backendUrlFile() {
|
|
223
|
-
return stateFile("backend-url");
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
function writeBackendUrl(port) {
|
|
227
|
-
ensureDir(STATE_DIR);
|
|
228
|
-
fs.writeFileSync(backendUrlFile(), `http://localhost:${port}/\n`);
|
|
229
|
-
}
|
|
230
|
-
|
|
231
227
|
function unquoteEnvValue(value) {
|
|
232
228
|
const trimmed = String(value || "").trim();
|
|
233
229
|
if (trimmed.length >= 2) {
|
|
@@ -279,12 +275,6 @@ function loadReflexioEnv() {
|
|
|
279
275
|
}
|
|
280
276
|
}
|
|
281
277
|
|
|
282
|
-
function reflexioUrlIsRemote() {
|
|
283
|
-
const url = process.env.REFLEXIO_URL || "";
|
|
284
|
-
if (!url) return false;
|
|
285
|
-
return !/^http:\/\/(?:localhost|127\.0\.0\.1|0\.0\.0\.0|\[::1\])(?::\d+)?\/?$/i.test(url);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
278
|
function codexCompatPath(root) {
|
|
289
279
|
const filename = process.platform === "win32"
|
|
290
280
|
? "codex-claude-compat.cmd"
|
|
@@ -294,69 +284,9 @@ function codexCompatPath(root) {
|
|
|
294
284
|
|
|
295
285
|
function readBackendUrl() {
|
|
296
286
|
if (process.env.REFLEXIO_URL) return process.env.REFLEXIO_URL;
|
|
297
|
-
try {
|
|
298
|
-
const value = fs.readFileSync(backendUrlFile(), "utf8").trim();
|
|
299
|
-
if (value) return value;
|
|
300
|
-
} catch {
|
|
301
|
-
// Fall through to default.
|
|
302
|
-
}
|
|
303
287
|
return `http://localhost:${DEFAULT_BACKEND_PORT}/`;
|
|
304
288
|
}
|
|
305
289
|
|
|
306
|
-
function healthOk(port, pathname, markerHeader) {
|
|
307
|
-
return new Promise((resolve) => {
|
|
308
|
-
const req = http.request(
|
|
309
|
-
{
|
|
310
|
-
host: "127.0.0.1",
|
|
311
|
-
port,
|
|
312
|
-
path: pathname,
|
|
313
|
-
method: "GET",
|
|
314
|
-
timeout: 1200,
|
|
315
|
-
},
|
|
316
|
-
(res) => {
|
|
317
|
-
const ok = res.statusCode && res.statusCode >= 200 && res.statusCode < 400;
|
|
318
|
-
const markerOk = markerHeader ? Boolean(res.headers[markerHeader]) : true;
|
|
319
|
-
res.resume();
|
|
320
|
-
resolve(Boolean(ok && markerOk));
|
|
321
|
-
},
|
|
322
|
-
);
|
|
323
|
-
req.on("timeout", () => req.destroy());
|
|
324
|
-
req.on("error", () => resolve(false));
|
|
325
|
-
req.end();
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
function portOccupied(port) {
|
|
330
|
-
return new Promise((resolve) => {
|
|
331
|
-
const req = http.request(
|
|
332
|
-
{
|
|
333
|
-
host: "127.0.0.1",
|
|
334
|
-
port,
|
|
335
|
-
path: "/",
|
|
336
|
-
method: "GET",
|
|
337
|
-
timeout: 900,
|
|
338
|
-
},
|
|
339
|
-
(res) => {
|
|
340
|
-
res.resume();
|
|
341
|
-
resolve(true);
|
|
342
|
-
},
|
|
343
|
-
);
|
|
344
|
-
req.on("timeout", () => req.destroy());
|
|
345
|
-
req.on("error", (err) => {
|
|
346
|
-
resolve(err && err.code !== "ECONNREFUSED");
|
|
347
|
-
});
|
|
348
|
-
req.end();
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
async function waitForHealth(port, pathname, markerHeader, attempts) {
|
|
353
|
-
for (let i = 0; i < attempts; i += 1) {
|
|
354
|
-
if (await healthOk(port, pathname, markerHeader)) return true;
|
|
355
|
-
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
356
|
-
}
|
|
357
|
-
return false;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
290
|
function detached(command, args, options = {}) {
|
|
361
291
|
const child = spawn(command, args, {
|
|
362
292
|
cwd: options.cwd,
|
|
@@ -386,167 +316,87 @@ function startInstallerDetached(root, reason) {
|
|
|
386
316
|
return true;
|
|
387
317
|
}
|
|
388
318
|
|
|
389
|
-
function
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
319
|
+
function runServiceScript(root, scriptName, action, logName) {
|
|
320
|
+
trimLog(path.join(STATE_DIR, logName));
|
|
321
|
+
const bash = bashPath();
|
|
322
|
+
const script = path.join(root, "scripts", scriptName);
|
|
323
|
+
if (!bash || !fs.existsSync(script)) {
|
|
324
|
+
appendLog(
|
|
325
|
+
logName,
|
|
326
|
+
`[claude-smart] codex hook: cannot run ${scriptName}; bash or script missing`,
|
|
327
|
+
);
|
|
328
|
+
emitOk();
|
|
329
|
+
return 0;
|
|
395
330
|
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
331
|
+
const result = spawnSync(bash, [script, action], {
|
|
332
|
+
cwd: root,
|
|
333
|
+
env: {
|
|
334
|
+
...process.env,
|
|
335
|
+
PLUGIN_ROOT: root,
|
|
336
|
+
CLAUDE_PLUGIN_ROOT: root,
|
|
337
|
+
CLAUDE_SMART_HOST: "codex",
|
|
338
|
+
CLAUDE_SMART_CLI_PATH: process.env.CLAUDE_SMART_CLI_PATH || codexCompatPath(root),
|
|
339
|
+
BACKEND_PORT: String(DEFAULT_BACKEND_PORT),
|
|
340
|
+
EMBEDDING_PORT: String(DEFAULT_EMBEDDING_PORT),
|
|
341
|
+
DASHBOARD_PORT: String(DASHBOARD_PORT),
|
|
342
|
+
PORT: String(DASHBOARD_PORT),
|
|
343
|
+
REFLEXIO_URL: readBackendUrl(),
|
|
344
|
+
},
|
|
345
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
346
|
+
timeout: SERVICE_SCRIPT_TIMEOUT_MS,
|
|
347
|
+
windowsHide: true,
|
|
348
|
+
});
|
|
349
|
+
const stderr = String(result.stderr || "").trim();
|
|
350
|
+
if (stderr) appendLog(logName, stderr);
|
|
351
|
+
if (result.error) {
|
|
352
|
+
appendLog(
|
|
353
|
+
logName,
|
|
354
|
+
`[claude-smart] codex hook: ${scriptName} ${action} failed: ${result.error.message}`,
|
|
355
|
+
);
|
|
405
356
|
}
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
357
|
+
if (result.signal) {
|
|
358
|
+
appendLog(
|
|
359
|
+
logName,
|
|
360
|
+
`[claude-smart] codex hook: ${scriptName} ${action} terminated by ${result.signal}`,
|
|
361
|
+
);
|
|
362
|
+
}
|
|
363
|
+
emitNormalizedHookOutput(result.stdout);
|
|
364
|
+
return typeof result.status === "number" ? result.status : 0;
|
|
411
365
|
}
|
|
412
366
|
|
|
413
367
|
function ensurePluginRoot(root) {
|
|
414
368
|
ensureDir(REFLEXIO_DIR);
|
|
415
369
|
const link = path.join(REFLEXIO_DIR, "plugin-root");
|
|
370
|
+
const metadata = path.join(REFLEXIO_DIR, "plugin-root.txt");
|
|
371
|
+
let blocked = false;
|
|
416
372
|
try {
|
|
417
|
-
fs.
|
|
418
|
-
|
|
419
|
-
|
|
373
|
+
const existing = fs.lstatSync(link);
|
|
374
|
+
if (existing.isSymbolicLink() || existing.isFile()) {
|
|
375
|
+
fs.rmSync(link, { recursive: true, force: true });
|
|
376
|
+
} else {
|
|
377
|
+
blocked = true;
|
|
378
|
+
}
|
|
379
|
+
} catch (err) {
|
|
380
|
+
if (!err || err.code !== "ENOENT") blocked = true;
|
|
381
|
+
}
|
|
382
|
+
if (blocked) {
|
|
383
|
+
fs.writeFileSync(metadata, `${root}\n`);
|
|
384
|
+
return;
|
|
420
385
|
}
|
|
421
386
|
try {
|
|
422
387
|
fs.symlinkSync(root, link, process.platform === "win32" ? "junction" : "dir");
|
|
388
|
+
fs.writeFileSync(metadata, `${root}\n`);
|
|
423
389
|
} catch {
|
|
424
|
-
fs.writeFileSync(
|
|
390
|
+
fs.writeFileSync(metadata, `${root}\n`);
|
|
425
391
|
}
|
|
426
392
|
}
|
|
427
393
|
|
|
428
394
|
async function startBackend(root) {
|
|
429
|
-
|
|
430
|
-
if (process.env.CLAUDE_SMART_BACKEND_AUTOSTART === "0") {
|
|
431
|
-
emitOk();
|
|
432
|
-
return;
|
|
433
|
-
}
|
|
434
|
-
if (reflexioUrlIsRemote()) {
|
|
435
|
-
appendLog("backend.log", "[claude-smart] backend: remote REFLEXIO_URL configured; skipping local backend start");
|
|
436
|
-
emitOk();
|
|
437
|
-
return;
|
|
438
|
-
}
|
|
439
|
-
const pidFile = path.join(STATE_DIR, "backend.pid");
|
|
440
|
-
for (const port of [DEFAULT_BACKEND_PORT, FALLBACK_BACKEND_PORT]) {
|
|
441
|
-
if (pidAlive(readPid(pidFile)) && await healthOk(port, "/health")) {
|
|
442
|
-
writeBackendUrl(port);
|
|
443
|
-
emitOk();
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if (await healthOk(port, "/health")) {
|
|
447
|
-
writeBackendUrl(port);
|
|
448
|
-
emitOk();
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
}
|
|
452
|
-
const uv = uvPath();
|
|
453
|
-
if (!uv) {
|
|
454
|
-
startInstallerDetached(root, "backend: uv not on PATH");
|
|
455
|
-
}
|
|
456
|
-
const readyUv = uvPath();
|
|
457
|
-
if (!readyUv) {
|
|
458
|
-
appendLog("backend.log", "[claude-smart] backend: uv not on PATH; installer recovery scheduled; skipping");
|
|
459
|
-
emitOk();
|
|
460
|
-
return;
|
|
461
|
-
}
|
|
462
|
-
let selectedPort = DEFAULT_BACKEND_PORT;
|
|
463
|
-
if (await portOccupied(DEFAULT_BACKEND_PORT)) {
|
|
464
|
-
appendLog("backend.log", "[claude-smart] backend: port 8071 occupied; trying 8072");
|
|
465
|
-
selectedPort = FALLBACK_BACKEND_PORT;
|
|
466
|
-
}
|
|
467
|
-
const backendUrl = `http://localhost:${selectedPort}/`;
|
|
468
|
-
const env = {
|
|
469
|
-
...process.env,
|
|
470
|
-
BACKEND_PORT: String(selectedPort),
|
|
471
|
-
REFLEXIO_URL: backendUrl,
|
|
472
|
-
CLAUDE_SMART_USE_LOCAL_CLI: process.env.CLAUDE_SMART_USE_LOCAL_CLI || "1",
|
|
473
|
-
CLAUDE_SMART_USE_LOCAL_EMBEDDING: process.env.CLAUDE_SMART_USE_LOCAL_EMBEDDING || "1",
|
|
474
|
-
CLAUDE_SMART_HOST: "codex",
|
|
475
|
-
CLAUDE_SMART_CLI_PATH: process.env.CLAUDE_SMART_CLI_PATH || codexCompatPath(root),
|
|
476
|
-
INTERACTION_CLEANUP_THRESHOLD: process.env.INTERACTION_CLEANUP_THRESHOLD || "500",
|
|
477
|
-
INTERACTION_CLEANUP_DELETE_COUNT: process.env.INTERACTION_CLEANUP_DELETE_COUNT || "200",
|
|
478
|
-
};
|
|
479
|
-
const pid = detached(
|
|
480
|
-
readyUv,
|
|
481
|
-
[
|
|
482
|
-
"run",
|
|
483
|
-
"--project",
|
|
484
|
-
root,
|
|
485
|
-
"--quiet",
|
|
486
|
-
"reflexio",
|
|
487
|
-
"services",
|
|
488
|
-
"start",
|
|
489
|
-
"--only",
|
|
490
|
-
"backend",
|
|
491
|
-
"--no-reload",
|
|
492
|
-
],
|
|
493
|
-
{ cwd: root, env },
|
|
494
|
-
);
|
|
495
|
-
writePid(pidFile, pid);
|
|
496
|
-
if (await waitForHealth(selectedPort, "/health", null, 10)) {
|
|
497
|
-
writeBackendUrl(selectedPort);
|
|
498
|
-
}
|
|
499
|
-
emitOk();
|
|
395
|
+
runServiceScript(root, "backend-service.sh", "start", "backend.log");
|
|
500
396
|
}
|
|
501
397
|
|
|
502
398
|
async function startDashboard(root) {
|
|
503
|
-
|
|
504
|
-
emitOk();
|
|
505
|
-
return;
|
|
506
|
-
}
|
|
507
|
-
const dashboard = path.join(root, "dashboard");
|
|
508
|
-
if (!fs.existsSync(dashboard)) {
|
|
509
|
-
emitOk();
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
const pidFile = path.join(STATE_DIR, "dashboard.pid");
|
|
513
|
-
if (
|
|
514
|
-
pidAlive(readPid(pidFile)) &&
|
|
515
|
-
await healthOk(DASHBOARD_PORT, "/api/health", "x-claude-smart-dashboard")
|
|
516
|
-
) {
|
|
517
|
-
emitOk();
|
|
518
|
-
return;
|
|
519
|
-
}
|
|
520
|
-
const npm = npmPath();
|
|
521
|
-
if (!npm) {
|
|
522
|
-
startInstallerDetached(root, "dashboard: npm not on PATH");
|
|
523
|
-
}
|
|
524
|
-
const readyNpm = npmPath();
|
|
525
|
-
if (!readyNpm) {
|
|
526
|
-
appendLog("dashboard.log", "[claude-smart] dashboard: npm not on PATH; installer recovery scheduled; skipping");
|
|
527
|
-
emitOk();
|
|
528
|
-
return;
|
|
529
|
-
}
|
|
530
|
-
if (!fs.existsSync(path.join(dashboard, ".next"))) {
|
|
531
|
-
const buildPidFile = path.join(STATE_DIR, "dashboard-build.pid");
|
|
532
|
-
if (!pidAlive(readPid(buildPidFile))) {
|
|
533
|
-
const pid = detached(readyNpm, ["run", "build"], { cwd: dashboard });
|
|
534
|
-
writePid(buildPidFile, pid);
|
|
535
|
-
appendLog("dashboard.log", "[claude-smart] dashboard: .next missing; started background build");
|
|
536
|
-
}
|
|
537
|
-
emitOk();
|
|
538
|
-
return;
|
|
539
|
-
}
|
|
540
|
-
const env = {
|
|
541
|
-
...process.env,
|
|
542
|
-
PORT: String(DASHBOARD_PORT),
|
|
543
|
-
REFLEXIO_URL: readBackendUrl(),
|
|
544
|
-
CLAUDE_SMART_DASHBOARD_WORKSPACE: process.cwd(),
|
|
545
|
-
};
|
|
546
|
-
const pid = detached(readyNpm, ["run", "start"], { cwd: dashboard, env });
|
|
547
|
-
writePid(pidFile, pid);
|
|
548
|
-
await waitForHealth(DASHBOARD_PORT, "/api/health", "x-claude-smart-dashboard", 5);
|
|
549
|
-
emitOk();
|
|
399
|
+
runServiceScript(root, "dashboard-service.sh", "start", "dashboard.log");
|
|
550
400
|
}
|
|
551
401
|
|
|
552
402
|
function runHook(root, event) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Start backend + dashboard (idempotent), wait briefly for dashboard to come
|
|
3
|
-
# up, print statuses, then open
|
|
3
|
+
# up, print statuses, then open the dashboard in the default browser.
|
|
4
4
|
#
|
|
5
5
|
# Exists so the /claude-smart:dashboard slash command can invoke a single
|
|
6
6
|
# plain `bash <script>` with no inline $(...) or ${...:-...} expansion in
|
|
@@ -18,6 +18,8 @@ STATE_DIR="$HOME/.claude-smart"
|
|
|
18
18
|
BACKEND_LOG="$STATE_DIR/backend.log"
|
|
19
19
|
DASHBOARD_LOG="$STATE_DIR/dashboard.log"
|
|
20
20
|
DASHBOARD_UNAVAILABLE="$(claude_smart_dashboard_unavailable_marker)"
|
|
21
|
+
BACKEND_PORT="${BACKEND_PORT:-8071}"
|
|
22
|
+
DASHBOARD_PORT="${DASHBOARD_PORT:-${PORT:-3001}}"
|
|
21
23
|
|
|
22
24
|
# Capture start-command output so we can surface fatal errors (e.g. uv/npm
|
|
23
25
|
# missing, port collisions, .next not built) rather than silently swallow
|
|
@@ -64,7 +66,7 @@ failed=0
|
|
|
64
66
|
if [ "$backend_status" = "not running" ]; then
|
|
65
67
|
failed=1
|
|
66
68
|
echo ""
|
|
67
|
-
echo "ERROR: backend failed to start on http://localhost
|
|
69
|
+
echo "ERROR: backend failed to start on http://localhost:$BACKEND_PORT"
|
|
68
70
|
[ -n "$backend_start_out" ] && echo "$backend_start_out"
|
|
69
71
|
show_log_tail "backend" "$BACKEND_LOG"
|
|
70
72
|
fi
|
|
@@ -75,7 +77,7 @@ if [ "$dashboard_status" = "not running" ]; then
|
|
|
75
77
|
if claude_smart_pid_alive_file "$BUILD_PID_FILE"; then
|
|
76
78
|
echo "dashboard: still building (first-run cost, ~1-2 min). Re-run /claude-smart:dashboard in a minute."
|
|
77
79
|
else
|
|
78
|
-
echo "ERROR: dashboard failed to start on http://localhost
|
|
80
|
+
echo "ERROR: dashboard failed to start on http://localhost:$DASHBOARD_PORT"
|
|
79
81
|
[ -n "$dashboard_start_out" ] && echo "$dashboard_start_out"
|
|
80
82
|
if [ -f "$DASHBOARD_UNAVAILABLE" ]; then
|
|
81
83
|
echo ""
|
|
@@ -92,7 +94,7 @@ if [ "$failed" = "1" ]; then
|
|
|
92
94
|
exit 1
|
|
93
95
|
fi
|
|
94
96
|
|
|
95
|
-
URL="http://localhost
|
|
97
|
+
URL="http://localhost:$DASHBOARD_PORT"
|
|
96
98
|
PY_BIN=$(claude_smart_resolve_python || true)
|
|
97
99
|
if [ -n "$PY_BIN" ] && "$PY_BIN" -m webbrowser "$URL"; then
|
|
98
100
|
echo "Opened $URL"
|