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
|
@@ -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,28 +316,52 @@ 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) {
|
|
@@ -438,127 +392,11 @@ function ensurePluginRoot(root) {
|
|
|
438
392
|
}
|
|
439
393
|
|
|
440
394
|
async function startBackend(root) {
|
|
441
|
-
|
|
442
|
-
if (process.env.CLAUDE_SMART_BACKEND_AUTOSTART === "0") {
|
|
443
|
-
emitOk();
|
|
444
|
-
return;
|
|
445
|
-
}
|
|
446
|
-
if (reflexioUrlIsRemote()) {
|
|
447
|
-
appendLog("backend.log", "[claude-smart] backend: remote REFLEXIO_URL configured; skipping local backend start");
|
|
448
|
-
emitOk();
|
|
449
|
-
return;
|
|
450
|
-
}
|
|
451
|
-
const pidFile = path.join(STATE_DIR, "backend.pid");
|
|
452
|
-
for (const port of [DEFAULT_BACKEND_PORT, FALLBACK_BACKEND_PORT]) {
|
|
453
|
-
if (pidAlive(readPid(pidFile)) && await healthOk(port, "/health")) {
|
|
454
|
-
writeBackendUrl(port);
|
|
455
|
-
emitOk();
|
|
456
|
-
return;
|
|
457
|
-
}
|
|
458
|
-
if (await healthOk(port, "/health")) {
|
|
459
|
-
writeBackendUrl(port);
|
|
460
|
-
emitOk();
|
|
461
|
-
return;
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
const uv = uvPath();
|
|
465
|
-
if (!uv) {
|
|
466
|
-
startInstallerDetached(root, "backend: uv not on PATH");
|
|
467
|
-
}
|
|
468
|
-
const readyUv = uvPath();
|
|
469
|
-
if (!readyUv) {
|
|
470
|
-
appendLog("backend.log", "[claude-smart] backend: uv not on PATH; installer recovery scheduled; skipping");
|
|
471
|
-
emitOk();
|
|
472
|
-
return;
|
|
473
|
-
}
|
|
474
|
-
let selectedPort = DEFAULT_BACKEND_PORT;
|
|
475
|
-
if (await portOccupied(DEFAULT_BACKEND_PORT)) {
|
|
476
|
-
appendLog("backend.log", "[claude-smart] backend: port 8071 occupied; trying 8072");
|
|
477
|
-
selectedPort = FALLBACK_BACKEND_PORT;
|
|
478
|
-
}
|
|
479
|
-
const backendUrl = `http://localhost:${selectedPort}/`;
|
|
480
|
-
const env = {
|
|
481
|
-
...process.env,
|
|
482
|
-
BACKEND_PORT: String(selectedPort),
|
|
483
|
-
REFLEXIO_URL: backendUrl,
|
|
484
|
-
CLAUDE_SMART_USE_LOCAL_CLI: process.env.CLAUDE_SMART_USE_LOCAL_CLI || "1",
|
|
485
|
-
CLAUDE_SMART_USE_LOCAL_EMBEDDING: process.env.CLAUDE_SMART_USE_LOCAL_EMBEDDING || "1",
|
|
486
|
-
CLAUDE_SMART_HOST: "codex",
|
|
487
|
-
CLAUDE_SMART_CLI_PATH: process.env.CLAUDE_SMART_CLI_PATH || codexCompatPath(root),
|
|
488
|
-
INTERACTION_CLEANUP_THRESHOLD: process.env.INTERACTION_CLEANUP_THRESHOLD || "500",
|
|
489
|
-
INTERACTION_CLEANUP_DELETE_COUNT: process.env.INTERACTION_CLEANUP_DELETE_COUNT || "200",
|
|
490
|
-
};
|
|
491
|
-
const pid = detached(
|
|
492
|
-
readyUv,
|
|
493
|
-
[
|
|
494
|
-
"run",
|
|
495
|
-
"--project",
|
|
496
|
-
root,
|
|
497
|
-
"--quiet",
|
|
498
|
-
"reflexio",
|
|
499
|
-
"services",
|
|
500
|
-
"start",
|
|
501
|
-
"--only",
|
|
502
|
-
"backend",
|
|
503
|
-
"--no-reload",
|
|
504
|
-
],
|
|
505
|
-
{ cwd: root, env },
|
|
506
|
-
);
|
|
507
|
-
writePid(pidFile, pid);
|
|
508
|
-
if (await waitForHealth(selectedPort, "/health", null, 10)) {
|
|
509
|
-
writeBackendUrl(selectedPort);
|
|
510
|
-
}
|
|
511
|
-
emitOk();
|
|
395
|
+
runServiceScript(root, "backend-service.sh", "start", "backend.log");
|
|
512
396
|
}
|
|
513
397
|
|
|
514
398
|
async function startDashboard(root) {
|
|
515
|
-
|
|
516
|
-
emitOk();
|
|
517
|
-
return;
|
|
518
|
-
}
|
|
519
|
-
const dashboard = path.join(root, "dashboard");
|
|
520
|
-
if (!fs.existsSync(dashboard)) {
|
|
521
|
-
emitOk();
|
|
522
|
-
return;
|
|
523
|
-
}
|
|
524
|
-
const pidFile = path.join(STATE_DIR, "dashboard.pid");
|
|
525
|
-
if (
|
|
526
|
-
pidAlive(readPid(pidFile)) &&
|
|
527
|
-
await healthOk(DASHBOARD_PORT, "/api/health", "x-claude-smart-dashboard")
|
|
528
|
-
) {
|
|
529
|
-
emitOk();
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
const npm = npmPath();
|
|
533
|
-
if (!npm) {
|
|
534
|
-
startInstallerDetached(root, "dashboard: npm not on PATH");
|
|
535
|
-
}
|
|
536
|
-
const readyNpm = npmPath();
|
|
537
|
-
if (!readyNpm) {
|
|
538
|
-
appendLog("dashboard.log", "[claude-smart] dashboard: npm not on PATH; installer recovery scheduled; skipping");
|
|
539
|
-
emitOk();
|
|
540
|
-
return;
|
|
541
|
-
}
|
|
542
|
-
if (!fs.existsSync(path.join(dashboard, ".next"))) {
|
|
543
|
-
const buildPidFile = path.join(STATE_DIR, "dashboard-build.pid");
|
|
544
|
-
if (!pidAlive(readPid(buildPidFile))) {
|
|
545
|
-
const pid = detached(readyNpm, ["run", "build"], { cwd: dashboard });
|
|
546
|
-
writePid(buildPidFile, pid);
|
|
547
|
-
appendLog("dashboard.log", "[claude-smart] dashboard: .next missing; started background build");
|
|
548
|
-
}
|
|
549
|
-
emitOk();
|
|
550
|
-
return;
|
|
551
|
-
}
|
|
552
|
-
const env = {
|
|
553
|
-
...process.env,
|
|
554
|
-
PORT: String(DASHBOARD_PORT),
|
|
555
|
-
REFLEXIO_URL: readBackendUrl(),
|
|
556
|
-
CLAUDE_SMART_DASHBOARD_WORKSPACE: process.cwd(),
|
|
557
|
-
};
|
|
558
|
-
const pid = detached(readyNpm, ["run", "start"], { cwd: dashboard, env });
|
|
559
|
-
writePid(pidFile, pid);
|
|
560
|
-
await waitForHealth(DASHBOARD_PORT, "/api/health", "x-claude-smart-dashboard", 5);
|
|
561
|
-
emitOk();
|
|
399
|
+
runServiceScript(root, "dashboard-service.sh", "start", "dashboard.log");
|
|
562
400
|
}
|
|
563
401
|
|
|
564
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"
|