claude-smart 0.2.32 → 0.2.33
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/README.md +1 -1
- package/bin/claude-smart.js +53 -5
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/dashboard/lib/config-file.ts +2 -0
- package/plugin/dashboard/lib/types.ts +1 -0
- package/plugin/pyproject.toml +1 -1
- package/plugin/scripts/_lib.sh +1 -1
- package/plugin/scripts/codex-hook.js +1 -0
- package/plugin/src/claude_smart/cli.py +54 -6
- package/plugin/src/claude_smart/env_config.py +1 -0
- package/plugin/src/claude_smart/ids.py +32 -0
- package/plugin/src/claude_smart/publish.py +3 -3
- package/plugin/src/claude_smart/reflexio_adapter.py +4 -3
- package/plugin/uv.lock +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
<img src="https://img.shields.io/badge/License-Apache%202.0-blue.svg" alt="License">
|
|
14
14
|
</a>
|
|
15
15
|
<a href="plugin/pyproject.toml">
|
|
16
|
-
<img src="https://img.shields.io/badge/version-0.2.
|
|
16
|
+
<img src="https://img.shields.io/badge/version-0.2.33-green.svg" alt="Version">
|
|
17
17
|
</a>
|
|
18
18
|
<a href="plugin/pyproject.toml">
|
|
19
19
|
<img src="https://img.shields.io/badge/python-%3E%3D3.12-brightgreen.svg" alt="Python">
|
package/bin/claude-smart.js
CHANGED
|
@@ -39,6 +39,7 @@ const CODEX_MARKETPLACE_DISPLAY_NAME = "ReflexioAI";
|
|
|
39
39
|
const CODEX_PLUGIN_ID = `claude-smart@${CODEX_MARKETPLACE_NAME}`;
|
|
40
40
|
const REFLEXIO_ENV_PATH = join(homedir(), ".reflexio", ".env");
|
|
41
41
|
const MANAGED_REFLEXIO_URL = "https://www.reflexio.ai/";
|
|
42
|
+
const REFLEXIO_USER_ID_ENV = "REFLEXIO_USER_ID";
|
|
42
43
|
const REFLEXIO_DIR = join(homedir(), ".reflexio");
|
|
43
44
|
const CLAUDE_SMART_STATE_DIR = join(homedir(), ".claude-smart");
|
|
44
45
|
const CODEX_CONFIG_PATH = join(homedir(), ".codex", "config.toml");
|
|
@@ -256,6 +257,25 @@ function setReflexioEnvVars(values) {
|
|
|
256
257
|
return added;
|
|
257
258
|
}
|
|
258
259
|
|
|
260
|
+
function readReflexioEnvValue(key) {
|
|
261
|
+
const existing = existsSync(REFLEXIO_ENV_PATH)
|
|
262
|
+
? readFileSync(REFLEXIO_ENV_PATH, "utf8")
|
|
263
|
+
: "";
|
|
264
|
+
for (const line of existing.split(/\r?\n/)) {
|
|
265
|
+
const parsed = parseEnvLine(line);
|
|
266
|
+
if (!parsed || parsed.key !== key) continue;
|
|
267
|
+
let value = line.slice(line.indexOf("=") + 1).trim();
|
|
268
|
+
if (
|
|
269
|
+
(value.startsWith('"') && value.endsWith('"')) ||
|
|
270
|
+
(value.startsWith("'") && value.endsWith("'"))
|
|
271
|
+
) {
|
|
272
|
+
value = value.slice(1, -1);
|
|
273
|
+
}
|
|
274
|
+
return value;
|
|
275
|
+
}
|
|
276
|
+
return "";
|
|
277
|
+
}
|
|
278
|
+
|
|
259
279
|
function maskSecret(value) {
|
|
260
280
|
if (!value) return "";
|
|
261
281
|
if (value.length <= 8) return "*".repeat(value.length);
|
|
@@ -267,9 +287,11 @@ function configureReflexioSetup(args) {
|
|
|
267
287
|
const apiKey = parseOptionalArg(args, "--api-key").trim();
|
|
268
288
|
if (apiKey) {
|
|
269
289
|
const reflexioUrl = parseOptionalArg(args, "--reflexio-url") || MANAGED_REFLEXIO_URL;
|
|
290
|
+
const userId = readReflexioEnvValue(REFLEXIO_USER_ID_ENV) || crypto.randomUUID();
|
|
270
291
|
const added = setReflexioEnvVars({
|
|
271
292
|
REFLEXIO_URL: reflexioUrl,
|
|
272
293
|
REFLEXIO_API_KEY: apiKey,
|
|
294
|
+
[REFLEXIO_USER_ID_ENV]: userId,
|
|
273
295
|
});
|
|
274
296
|
const changed = added.length > 0 ? added.join(", ") : "managed Reflexio settings";
|
|
275
297
|
process.stdout.write(
|
|
@@ -302,6 +324,8 @@ function findClaudeCodePluginRoot() {
|
|
|
302
324
|
// Fall through to marketplace/package fallbacks.
|
|
303
325
|
}
|
|
304
326
|
candidates.sort((a, b) => {
|
|
327
|
+
const versionCompare = compareSemverLikePathNames(b, a);
|
|
328
|
+
if (versionCompare !== 0) return versionCompare;
|
|
305
329
|
try {
|
|
306
330
|
return statSync(b).mtimeMs - statSync(a).mtimeMs;
|
|
307
331
|
} catch {
|
|
@@ -323,6 +347,27 @@ function findClaudeCodePluginRoot() {
|
|
|
323
347
|
return null;
|
|
324
348
|
}
|
|
325
349
|
|
|
350
|
+
function semverLikePathName(path) {
|
|
351
|
+
const base = String(path).split(/[\\/]/).pop() || "";
|
|
352
|
+
const match = base.match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
|
|
353
|
+
if (!match) return null;
|
|
354
|
+
return match.slice(1).map((part) => Number.parseInt(part, 10));
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function compareSemverLikePathNames(a, b) {
|
|
358
|
+
const av = semverLikePathName(a);
|
|
359
|
+
const bv = semverLikePathName(b);
|
|
360
|
+
if (av && bv) {
|
|
361
|
+
for (let i = 0; i < 3; i += 1) {
|
|
362
|
+
if (av[i] !== bv[i]) return av[i] - bv[i];
|
|
363
|
+
}
|
|
364
|
+
return 0;
|
|
365
|
+
}
|
|
366
|
+
if (av) return 1;
|
|
367
|
+
if (bv) return -1;
|
|
368
|
+
return 0;
|
|
369
|
+
}
|
|
370
|
+
|
|
326
371
|
function forcePluginRoot(pluginRoot) {
|
|
327
372
|
mkdirSync(REFLEXIO_DIR, { recursive: true });
|
|
328
373
|
const link = join(REFLEXIO_DIR, "plugin-root");
|
|
@@ -808,7 +853,7 @@ function printHelp() {
|
|
|
808
853
|
` 2. claude plugin install ${PLUGIN_SPEC}`,
|
|
809
854
|
" 3. Appends CLAUDE_SMART_USE_LOCAL_CLI=1 and CLAUDE_SMART_USE_LOCAL_EMBEDDING=1",
|
|
810
855
|
" to ~/.reflexio/.env (idempotent).",
|
|
811
|
-
" Passing --api-key writes REFLEXIO_URL and
|
|
856
|
+
" Passing --api-key writes REFLEXIO_URL, REFLEXIO_API_KEY, and REFLEXIO_USER_ID instead.",
|
|
812
857
|
"",
|
|
813
858
|
"Codex install:",
|
|
814
859
|
` 1. Copies the bundled marketplace to ${CODEX_MARKETPLACE_DIR}`,
|
|
@@ -821,6 +866,7 @@ function printHelp() {
|
|
|
821
866
|
"",
|
|
822
867
|
"Update:",
|
|
823
868
|
" npx claude-smart update Update to the latest version",
|
|
869
|
+
" npx claude-smart update --api-key <key> Update and configure managed Reflexio",
|
|
824
870
|
"",
|
|
825
871
|
"Uninstall:",
|
|
826
872
|
" npx claude-smart uninstall Remove the plugin from Claude Code",
|
|
@@ -1246,7 +1292,7 @@ function installCodexPluginCache(pluginRoot) {
|
|
|
1246
1292
|
return cacheDir;
|
|
1247
1293
|
}
|
|
1248
1294
|
|
|
1249
|
-
async function runUpdate() {
|
|
1295
|
+
async function runUpdate(args) {
|
|
1250
1296
|
if (!hasClaudeCli()) {
|
|
1251
1297
|
process.stderr.write(
|
|
1252
1298
|
"error: 'claude' CLI not found on PATH. " +
|
|
@@ -1264,6 +1310,7 @@ async function runUpdate() {
|
|
|
1264
1310
|
}
|
|
1265
1311
|
|
|
1266
1312
|
process.stdout.write("\nclaude-smart updated. Restart Claude Code to apply.\n");
|
|
1313
|
+
if (args.includes("--api-key")) configureReflexioSetup(args);
|
|
1267
1314
|
}
|
|
1268
1315
|
|
|
1269
1316
|
async function runUninstall(args) {
|
|
@@ -1316,6 +1363,8 @@ async function runInstall(args) {
|
|
|
1316
1363
|
}
|
|
1317
1364
|
|
|
1318
1365
|
const source = parseSource(args);
|
|
1366
|
+
configureReflexioSetup(args);
|
|
1367
|
+
|
|
1319
1368
|
const steps = [
|
|
1320
1369
|
{ args: ["plugin", "marketplace", "add", source], label: "Adding marketplace…" },
|
|
1321
1370
|
{ args: ["plugin", "install", PLUGIN_SPEC], label: "Installing claude-smart…" },
|
|
@@ -1331,7 +1380,6 @@ async function runInstall(args) {
|
|
|
1331
1380
|
}
|
|
1332
1381
|
}
|
|
1333
1382
|
|
|
1334
|
-
configureReflexioSetup(args);
|
|
1335
1383
|
try {
|
|
1336
1384
|
const pluginRoot = await bootstrapClaudeCodeInstall();
|
|
1337
1385
|
process.stdout.write(`Prepared claude-smart runtime at ${pluginRoot}.\n`);
|
|
@@ -1364,6 +1412,7 @@ async function runInstallCodex(args) {
|
|
|
1364
1412
|
process.stderr.write("error: 'codex' CLI not found on PATH. Install Codex first.\n");
|
|
1365
1413
|
process.exit(1);
|
|
1366
1414
|
}
|
|
1415
|
+
configureReflexioSetup(args);
|
|
1367
1416
|
|
|
1368
1417
|
const marketplaceRoot = copyCodexMarketplace();
|
|
1369
1418
|
process.stdout.write(`Prepared Codex marketplace at ${marketplaceRoot}.\n`);
|
|
@@ -1406,7 +1455,6 @@ async function runInstallCodex(args) {
|
|
|
1406
1455
|
try {
|
|
1407
1456
|
cacheDir = installCodexPluginCache(codexMarketplacePluginRoot(marketplaceRoot));
|
|
1408
1457
|
process.stdout.write(`Installed Codex plugin cache at ${cacheDir}.\n`);
|
|
1409
|
-
configureReflexioSetup(args);
|
|
1410
1458
|
await bootstrapPluginRuntime(cacheDir);
|
|
1411
1459
|
if (refreshDashboardService(cacheDir)) {
|
|
1412
1460
|
process.stdout.write("Refreshed claude-smart dashboard service.\n");
|
|
@@ -1496,7 +1544,7 @@ async function main() {
|
|
|
1496
1544
|
}
|
|
1497
1545
|
|
|
1498
1546
|
if (cmd === "update") {
|
|
1499
|
-
await runUpdate();
|
|
1547
|
+
await runUpdate(args.slice(1));
|
|
1500
1548
|
return;
|
|
1501
1549
|
}
|
|
1502
1550
|
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import type { ClaudeSmartConfig } from "./types";
|
|
|
11
11
|
const KNOWN_KEYS = [
|
|
12
12
|
"REFLEXIO_URL",
|
|
13
13
|
"REFLEXIO_API_KEY",
|
|
14
|
+
"REFLEXIO_USER_ID",
|
|
14
15
|
"CLAUDE_SMART_USE_LOCAL_CLI",
|
|
15
16
|
"CLAUDE_SMART_USE_LOCAL_EMBEDDING",
|
|
16
17
|
"CLAUDE_SMART_CLI_PATH",
|
|
@@ -49,6 +50,7 @@ export async function readConfig(): Promise<ClaudeSmartConfig> {
|
|
|
49
50
|
const defaults: ClaudeSmartConfig = {
|
|
50
51
|
REFLEXIO_URL: "http://localhost:8071/",
|
|
51
52
|
REFLEXIO_API_KEY: "",
|
|
53
|
+
REFLEXIO_USER_ID: "",
|
|
52
54
|
CLAUDE_SMART_USE_LOCAL_CLI: false,
|
|
53
55
|
CLAUDE_SMART_USE_LOCAL_EMBEDDING: false,
|
|
54
56
|
CLAUDE_SMART_CLI_PATH: "",
|
|
@@ -114,6 +114,7 @@ export interface SessionDetail {
|
|
|
114
114
|
export interface ClaudeSmartConfig {
|
|
115
115
|
REFLEXIO_URL: string;
|
|
116
116
|
REFLEXIO_API_KEY: string;
|
|
117
|
+
REFLEXIO_USER_ID: string;
|
|
117
118
|
REFLEXIO_API_KEY_SET?: boolean;
|
|
118
119
|
CLAUDE_SMART_USE_LOCAL_CLI: boolean;
|
|
119
120
|
CLAUDE_SMART_USE_LOCAL_EMBEDDING: boolean;
|
package/plugin/pyproject.toml
CHANGED
package/plugin/scripts/_lib.sh
CHANGED
|
@@ -63,7 +63,7 @@ claude_smart_source_reflexio_env() {
|
|
|
63
63
|
value="${value#"${value%%[![:space:]]*}"}"
|
|
64
64
|
value="${value%"${value##*[![:space:]]}"}"
|
|
65
65
|
case "$key" in
|
|
66
|
-
REFLEXIO_URL|REFLEXIO_API_KEY|CLAUDE_SMART_USE_LOCAL_CLI|CLAUDE_SMART_USE_LOCAL_EMBEDDING|CLAUDE_SMART_BACKEND_AUTOSTART|CLAUDE_SMART_DASHBOARD_AUTOSTART|CLAUDE_SMART_CLI_PATH|CLAUDE_SMART_CLI_TIMEOUT|CLAUDE_SMART_STATE_DIR|CLAUDE_SMART_ENABLE_OPTIMIZER)
|
|
66
|
+
REFLEXIO_URL|REFLEXIO_API_KEY|REFLEXIO_USER_ID|CLAUDE_SMART_USE_LOCAL_CLI|CLAUDE_SMART_USE_LOCAL_EMBEDDING|CLAUDE_SMART_BACKEND_AUTOSTART|CLAUDE_SMART_DASHBOARD_AUTOSTART|CLAUDE_SMART_CLI_PATH|CLAUDE_SMART_CLI_TIMEOUT|CLAUDE_SMART_STATE_DIR|CLAUDE_SMART_ENABLE_OPTIMIZER)
|
|
67
67
|
if [ -z "$(eval "printf '%s' \"\${$key:-}\"")" ]; then
|
|
68
68
|
value="$(claude_smart_env_unquote "$value")"
|
|
69
69
|
export "$key=$value"
|
|
@@ -30,6 +30,7 @@ import shutil
|
|
|
30
30
|
import subprocess
|
|
31
31
|
import sys
|
|
32
32
|
import time
|
|
33
|
+
import uuid
|
|
33
34
|
from dataclasses import dataclass
|
|
34
35
|
from pathlib import Path
|
|
35
36
|
|
|
@@ -144,15 +145,52 @@ def _seed_reflexio_env() -> list[str]:
|
|
|
144
145
|
|
|
145
146
|
def _seed_managed_reflexio_env(*, api_key: str, reflexio_url: str) -> list[str]:
|
|
146
147
|
"""Write managed Reflexio connection settings to ``~/.reflexio/.env``."""
|
|
148
|
+
user_id = _read_reflexio_env_value(env_config.REFLEXIO_USER_ID_ENV) or str(
|
|
149
|
+
uuid.uuid4()
|
|
150
|
+
)
|
|
147
151
|
return env_config.set_env_vars(
|
|
148
152
|
_REFLEXIO_ENV_PATH,
|
|
149
153
|
{
|
|
150
154
|
env_config.REFLEXIO_URL_ENV: reflexio_url,
|
|
151
155
|
env_config.REFLEXIO_API_KEY_ENV: api_key,
|
|
156
|
+
env_config.REFLEXIO_USER_ID_ENV: user_id,
|
|
152
157
|
},
|
|
153
158
|
)
|
|
154
159
|
|
|
155
160
|
|
|
161
|
+
def _read_reflexio_env_value(key: str) -> str:
|
|
162
|
+
try:
|
|
163
|
+
text = _REFLEXIO_ENV_PATH.read_text()
|
|
164
|
+
except OSError:
|
|
165
|
+
return ""
|
|
166
|
+
for line in text.splitlines():
|
|
167
|
+
parsed = env_config.parse_env_line(line)
|
|
168
|
+
if parsed is None:
|
|
169
|
+
continue
|
|
170
|
+
parsed_key, value = parsed
|
|
171
|
+
if parsed_key == key:
|
|
172
|
+
return value
|
|
173
|
+
return ""
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _semver_tuple(path: Path) -> tuple[int, int, int] | None:
|
|
177
|
+
parts = path.name.split(".", 2)
|
|
178
|
+
if len(parts) != 3:
|
|
179
|
+
return None
|
|
180
|
+
try:
|
|
181
|
+
patch = parts[2].split("-", 1)[0].split("+", 1)[0]
|
|
182
|
+
return (int(parts[0]), int(parts[1]), int(patch))
|
|
183
|
+
except ValueError:
|
|
184
|
+
return None
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _installed_plugin_sort_key(path: Path) -> tuple[int, int, int, int, float]:
|
|
188
|
+
version = _semver_tuple(path)
|
|
189
|
+
if version is not None:
|
|
190
|
+
return (1, *version, path.stat().st_mtime)
|
|
191
|
+
return (0, 0, 0, 0, path.stat().st_mtime)
|
|
192
|
+
|
|
193
|
+
|
|
156
194
|
def _find_claude_code_plugin_root() -> Path | None:
|
|
157
195
|
"""Locate the installed Claude Code plugin root after native install."""
|
|
158
196
|
cache_root = (
|
|
@@ -172,7 +210,7 @@ def _find_claude_code_plugin_root() -> Path | None:
|
|
|
172
210
|
and (child / "scripts" / "smart-install.sh").is_file()
|
|
173
211
|
):
|
|
174
212
|
candidates.append(child)
|
|
175
|
-
candidates.sort(key=
|
|
213
|
+
candidates.sort(key=_installed_plugin_sort_key, reverse=True)
|
|
176
214
|
candidates.extend(
|
|
177
215
|
[
|
|
178
216
|
Path.home()
|
|
@@ -797,6 +835,7 @@ def cmd_install_codex(args: argparse.Namespace) -> int:
|
|
|
797
835
|
"error: 'uv' not found on PATH. Install uv or restart your shell.\n"
|
|
798
836
|
)
|
|
799
837
|
return 1
|
|
838
|
+
_configure_reflexio_setup(args)
|
|
800
839
|
|
|
801
840
|
missing = _missing_codex_marketplace_files(_REPO_ROOT)
|
|
802
841
|
if missing:
|
|
@@ -808,8 +847,6 @@ def cmd_install_codex(args: argparse.Namespace) -> int:
|
|
|
808
847
|
return 1
|
|
809
848
|
marketplace_root = _prepare_codex_local_marketplace()
|
|
810
849
|
|
|
811
|
-
_configure_reflexio_setup(args)
|
|
812
|
-
|
|
813
850
|
hooks_ok, hooks_msg = _enable_codex_plugin_hooks()
|
|
814
851
|
if hooks_ok:
|
|
815
852
|
sys.stdout.write(f"Enabled Codex plugin hooks ({hooks_msg}).\n")
|
|
@@ -899,6 +936,7 @@ def cmd_install(args: argparse.Namespace) -> int:
|
|
|
899
936
|
"Install Claude Code first: https://claude.com/claude-code\n"
|
|
900
937
|
)
|
|
901
938
|
return 1
|
|
939
|
+
_configure_reflexio_setup(args)
|
|
902
940
|
|
|
903
941
|
for cmd in (
|
|
904
942
|
["claude", "plugin", "marketplace", "add", args.source],
|
|
@@ -910,8 +948,6 @@ def cmd_install(args: argparse.Namespace) -> int:
|
|
|
910
948
|
sys.stderr.write(f"error: {' '.join(cmd)} failed (exit {exc.returncode})\n")
|
|
911
949
|
return exc.returncode or 1
|
|
912
950
|
|
|
913
|
-
_configure_reflexio_setup(args)
|
|
914
|
-
|
|
915
951
|
bootstrapped, message = _bootstrap_claude_code_install()
|
|
916
952
|
if not bootstrapped:
|
|
917
953
|
sys.stderr.write(
|
|
@@ -930,7 +966,7 @@ def cmd_install(args: argparse.Namespace) -> int:
|
|
|
930
966
|
return 0
|
|
931
967
|
|
|
932
968
|
|
|
933
|
-
def cmd_update(
|
|
969
|
+
def cmd_update(args: argparse.Namespace) -> int:
|
|
934
970
|
"""Update claude-smart to the latest version via the native plugin CLI.
|
|
935
971
|
|
|
936
972
|
Runs ``claude plugin update claude-smart@reflexioai``.
|
|
@@ -956,6 +992,8 @@ def cmd_update(_args: argparse.Namespace) -> int:
|
|
|
956
992
|
return exc.returncode or 1
|
|
957
993
|
|
|
958
994
|
sys.stdout.write("\nclaude-smart updated. Restart Claude Code to apply.\n")
|
|
995
|
+
if getattr(args, "api_key", ""):
|
|
996
|
+
_configure_reflexio_setup(args)
|
|
959
997
|
return 0
|
|
960
998
|
|
|
961
999
|
|
|
@@ -1744,6 +1782,16 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
1744
1782
|
inst.set_defaults(func=cmd_install)
|
|
1745
1783
|
|
|
1746
1784
|
upd = sub.add_parser("update", help="Update claude-smart to the latest version")
|
|
1785
|
+
upd.add_argument(
|
|
1786
|
+
"--api-key",
|
|
1787
|
+
default="",
|
|
1788
|
+
help="Configure managed Reflexio with this API key after updating",
|
|
1789
|
+
)
|
|
1790
|
+
upd.add_argument(
|
|
1791
|
+
"--reflexio-url",
|
|
1792
|
+
default=_MANAGED_REFLEXIO_URL,
|
|
1793
|
+
help="Managed Reflexio URL used only when --api-key is provided",
|
|
1794
|
+
)
|
|
1747
1795
|
upd.set_defaults(func=cmd_update)
|
|
1748
1796
|
|
|
1749
1797
|
uni = sub.add_parser("uninstall", help="Remove claude-smart")
|
|
@@ -9,6 +9,7 @@ REFLEXIO_ENV_PATH = Path.home() / ".reflexio" / ".env"
|
|
|
9
9
|
MANAGED_REFLEXIO_URL = "https://www.reflexio.ai/"
|
|
10
10
|
REFLEXIO_URL_ENV = "REFLEXIO_URL"
|
|
11
11
|
REFLEXIO_API_KEY_ENV = "REFLEXIO_API_KEY"
|
|
12
|
+
REFLEXIO_USER_ID_ENV = "REFLEXIO_USER_ID"
|
|
12
13
|
|
|
13
14
|
|
|
14
15
|
def parse_env_line(line: str) -> tuple[str, str] | None:
|
|
@@ -19,14 +19,22 @@ from __future__ import annotations
|
|
|
19
19
|
import logging
|
|
20
20
|
import os
|
|
21
21
|
import subprocess # noqa: S404 — git invocation with a fixed flag set.
|
|
22
|
+
import uuid
|
|
22
23
|
from pathlib import Path
|
|
23
24
|
|
|
25
|
+
from claude_smart import env_config
|
|
26
|
+
|
|
24
27
|
_LOGGER = logging.getLogger(__name__)
|
|
25
28
|
|
|
26
29
|
|
|
27
30
|
def resolve_project_id(cwd: str | os.PathLike[str] | None = None) -> str:
|
|
28
31
|
"""Return a stable project identifier for the given working directory.
|
|
29
32
|
|
|
33
|
+
Managed Reflexio installs are user-scoped instead of project-scoped:
|
|
34
|
+
when ``REFLEXIO_API_KEY`` is configured, the persisted
|
|
35
|
+
``REFLEXIO_USER_ID`` UUID is returned. Local installs without an API key
|
|
36
|
+
keep using the git/project name.
|
|
37
|
+
|
|
30
38
|
Prefers the basename of the git toplevel (so worktrees, submodules, and
|
|
31
39
|
`cd src/` all still map to the same project). Falls back to the cwd
|
|
32
40
|
basename when the directory is not inside a git repo.
|
|
@@ -37,6 +45,10 @@ def resolve_project_id(cwd: str | os.PathLike[str] | None = None) -> str:
|
|
|
37
45
|
Returns:
|
|
38
46
|
str: A non-empty identifier. Never raises.
|
|
39
47
|
"""
|
|
48
|
+
managed_user_id = _managed_user_id()
|
|
49
|
+
if managed_user_id:
|
|
50
|
+
return managed_user_id
|
|
51
|
+
|
|
40
52
|
base = Path(cwd) if cwd is not None else Path.cwd()
|
|
41
53
|
try:
|
|
42
54
|
result = subprocess.run( # noqa: S603, S607 — fixed argv, cwd is a Path.
|
|
@@ -54,3 +66,23 @@ def resolve_project_id(cwd: str | os.PathLike[str] | None = None) -> str:
|
|
|
54
66
|
except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
|
|
55
67
|
_LOGGER.debug("git toplevel resolution failed: %s", exc)
|
|
56
68
|
return base.name or "unknown-project"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _managed_user_id() -> str:
|
|
72
|
+
env_config.load_reflexio_env()
|
|
73
|
+
if not os.environ.get(env_config.REFLEXIO_API_KEY_ENV):
|
|
74
|
+
return ""
|
|
75
|
+
user_id = os.environ.get(env_config.REFLEXIO_USER_ID_ENV, "").strip()
|
|
76
|
+
if user_id:
|
|
77
|
+
return user_id
|
|
78
|
+
|
|
79
|
+
user_id = str(uuid.uuid4())
|
|
80
|
+
try:
|
|
81
|
+
env_config.set_env_vars(
|
|
82
|
+
env_config.REFLEXIO_ENV_PATH,
|
|
83
|
+
{env_config.REFLEXIO_USER_ID_ENV: user_id},
|
|
84
|
+
)
|
|
85
|
+
except OSError as exc:
|
|
86
|
+
_LOGGER.debug("could not persist managed Reflexio user id: %s", exc)
|
|
87
|
+
os.environ[env_config.REFLEXIO_USER_ID_ENV] = user_id
|
|
88
|
+
return user_id
|
|
@@ -29,9 +29,9 @@ def publish_unpublished(
|
|
|
29
29
|
|
|
30
30
|
Args:
|
|
31
31
|
session_id (str): Claude Code session id, attached to each interaction.
|
|
32
|
-
project_id (str): Stable
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
project_id (str): Stable user-scope id resolved by ``ids``. Local mode
|
|
33
|
+
uses the project name; managed/API-key mode uses the persisted
|
|
34
|
+
``REFLEXIO_USER_ID`` UUID. ``agent_version`` is hardcoded to
|
|
35
35
|
``"claude-code"`` in the adapter so skills roll up
|
|
36
36
|
globally per agent rather than per project.
|
|
37
37
|
force_extraction (bool): Whether to ask reflexio to run extraction
|
|
@@ -250,9 +250,10 @@ class Adapter:
|
|
|
250
250
|
def fetch_user_playbooks(self, *, project_id: str, top_k: int = 10) -> list[Any]:
|
|
251
251
|
"""Fetch CURRENT user playbooks for ``project_id``.
|
|
252
252
|
|
|
253
|
-
User playbooks are
|
|
254
|
-
|
|
255
|
-
|
|
253
|
+
User playbooks are scoped by the resolved user id: local installs use
|
|
254
|
+
the project name, while managed installs use ``REFLEXIO_USER_ID``.
|
|
255
|
+
Filtering mirrors the publish path
|
|
256
|
+
(``publish_interaction(user_id=project_id, …)``).
|
|
256
257
|
|
|
257
258
|
Args:
|
|
258
259
|
project_id (str): reflexio ``user_id`` for this repo.
|