claude-smart 0.2.32 → 0.2.34

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 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.32-green.svg" alt="Version">
16
+ <img src="https://img.shields.io/badge/version-0.2.34-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">
@@ -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 REFLEXIO_API_KEY instead.",
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.32",
3
+ "version": "0.2.34",
4
4
  "description": "Self-improving Claude Code plugin — learns from corrections via reflexio",
5
5
  "keywords": [
6
6
  "claude",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.32",
3
+ "version": "0.2.34",
4
4
  "description": "Self-improving Claude Code plugin — learns from corrections across sessions via reflexio",
5
5
  "author": {
6
6
  "name": "Yi Lu"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-smart",
3
- "version": "0.2.32",
3
+ "version": "0.2.34",
4
4
  "description": "Self-improving coding assistant plugin — learns from corrections across sessions via reflexio",
5
5
  "author": {
6
6
  "name": "Yi Lu"
@@ -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;
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "claude-smart"
3
- version = "0.2.32"
3
+ version = "0.2.34"
4
4
  description = "Self-improving Claude Code plugin — learns from corrections via reflexio"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.12"
@@ -63,7 +63,15 @@ 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 identity: file always wins so a rotated key/url overrides
67
+ # whatever a long-lived managed backend or dashboard process inherited.
68
+ REFLEXIO_URL|REFLEXIO_API_KEY|REFLEXIO_USER_ID)
69
+ value="$(claude_smart_env_unquote "$value")"
70
+ export "$key=$value"
71
+ ;;
72
+ # CLAUDE_SMART_* flags: respect anything the caller already exported so
73
+ # per-session overrides (e.g., a manual test) take precedence over the file.
74
+ 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
75
  if [ -z "$(eval "printf '%s' \"\${$key:-}\"")" ]; then
68
76
  value="$(claude_smart_env_unquote "$value")"
69
77
  export "$key=$value"
@@ -180,9 +180,12 @@ function loadReflexioEnv() {
180
180
  } catch {
181
181
  return;
182
182
  }
183
- const allowed = new Set([
183
+ const managedReflexioKeys = new Set([
184
184
  "REFLEXIO_URL",
185
185
  "REFLEXIO_API_KEY",
186
+ "REFLEXIO_USER_ID",
187
+ ]);
188
+ const localConfigKeys = new Set([
186
189
  "CLAUDE_SMART_USE_LOCAL_CLI",
187
190
  "CLAUDE_SMART_USE_LOCAL_EMBEDDING",
188
191
  "CLAUDE_SMART_BACKEND_AUTOSTART",
@@ -199,8 +202,11 @@ function loadReflexioEnv() {
199
202
  const eq = line.indexOf("=");
200
203
  if (eq < 0) continue;
201
204
  const key = line.slice(0, eq).trim();
202
- if (!allowed.has(key) || process.env[key]) continue;
203
- process.env[key] = unquoteEnvValue(line.slice(eq + 1));
205
+ if (managedReflexioKeys.has(key)) {
206
+ process.env[key] = unquoteEnvValue(line.slice(eq + 1));
207
+ } else if (localConfigKeys.has(key) && !process.env[key]) {
208
+ process.env[key] = unquoteEnvValue(line.slice(eq + 1));
209
+ }
204
210
  }
205
211
  }
206
212
 
@@ -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=lambda path: path.stat().st_mtime, reverse=True)
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(_args: argparse.Namespace) -> int:
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 project name; used as reflexio's
33
- ``user_id`` (preferences) so preferences accumulate at the project
34
- level across sessions. ``agent_version`` is hardcoded to
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 project-scoped: filtering by ``user_id=project_id``
254
- mirrors the publish path (``publish_interaction(user_id=project_id, …)``)
255
- so each project only sees the playbooks it produced.
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.
@@ -325,17 +326,10 @@ class Adapter:
325
326
  if client is None:
326
327
  return []
327
328
  try:
328
- if hasattr(client, "get_all_profiles"):
329
- response = client.get_all_profiles(limit=max(top_k * 20, 100))
330
- return [
331
- item
332
- for item in _extract_items(response, "user_profiles")
333
- if _field(item, "user_id") == project_id
334
- ][:top_k]
335
- response = client.search_user_profiles(
329
+ response = client.get_profiles(
336
330
  user_id=project_id,
337
- query="",
338
331
  top_k=top_k,
332
+ status_filter=[None],
339
333
  )
340
334
  except Exception as exc: # noqa: BLE001
341
335
  self._record_read_error("fetch_project_profiles", exc)
@@ -445,12 +439,6 @@ def _extract_items(response: Any, field: str) -> list[Any]:
445
439
  return list(value) if value else []
446
440
 
447
441
 
448
- def _field(item: Any, field: str) -> Any:
449
- if isinstance(item, dict):
450
- return item.get(field)
451
- return getattr(item, field, None)
452
-
453
-
454
442
  def _supports_keyword(callable_obj: Any, keyword: str) -> bool:
455
443
  try:
456
444
  signature = inspect.signature(callable_obj)
package/plugin/uv.lock CHANGED
@@ -419,7 +419,7 @@ wheels = [
419
419
 
420
420
  [[package]]
421
421
  name = "claude-smart"
422
- version = "0.2.32"
422
+ version = "0.2.34"
423
423
  source = { editable = "." }
424
424
  dependencies = [
425
425
  { name = "chromadb" },