sibyl-memory-cli 0.3.10__tar.gz → 0.3.13__tar.gz
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.
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/PKG-INFO +1 -1
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/pyproject.toml +1 -1
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli/cli.py +22 -2
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli/migrate.py +2 -2
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli/setup.py +42 -7
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/PKG-INFO +1 -1
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/SOURCES.txt +2 -0
- sibyl_memory_cli-0.3.13/tests/test_init_perms_guard.py +22 -0
- sibyl_memory_cli-0.3.13/tests/test_migrate_force_2026_06_05.py +67 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/tests/test_wiring_fix.py +8 -1
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/README.md +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/setup.cfg +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli/__init__.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli/_aesthetic.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli/_banner.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/dependency_links.txt +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/entry_points.txt +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/requires.txt +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/top_level.txt +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/tests/test_hermes_install_args.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/tests/test_migrate.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/tests/test_migrate_adversarial.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/tests/test_orchestrator.py +0 -0
- {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/tests/test_setup.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sibyl-memory-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
4
4
|
Summary: Command-line interface for the Sibyl Memory Plugin. `sibyl init` activates, `sibyl upgrade` runs the staker / subscription flow, `sibyl status` shows current tier and DB stats, `sibyl whoami` gives a one-line account summary, `sibyl devices` lists active devices and supports per-device revoke.
|
|
5
5
|
Author-email: "SIBYL, Sibyl Labs LLC" <sibyl@sibyllabs.org>
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sibyl-memory-cli"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.13"
|
|
8
8
|
description = "Command-line interface for the Sibyl Memory Plugin. `sibyl init` activates, `sibyl upgrade` runs the staker / subscription flow, `sibyl status` shows current tier and DB stats, `sibyl whoami` gives a one-line account summary, `sibyl devices` lists active devices and supports per-device revoke."
|
|
9
9
|
authors = [{ name = "SIBYL, Sibyl Labs LLC", email = "sibyl@sibyllabs.org" }]
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -163,6 +163,13 @@ def write_credentials_atomic(creds: dict, path: Path = DEFAULT_CRED_PATH) -> Pat
|
|
|
163
163
|
"""
|
|
164
164
|
path = path.expanduser()
|
|
165
165
|
path.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
166
|
+
# mkdir's mode is ignored when the dir already exists (bug, dor_alpha 2026-06-01):
|
|
167
|
+
# a pre-existing 0755 ~/.sibyl-memory left credentials world-readable. Tighten
|
|
168
|
+
# explicitly to cover the pre-existing-directory case.
|
|
169
|
+
try:
|
|
170
|
+
os.chmod(path.parent, 0o700)
|
|
171
|
+
except OSError:
|
|
172
|
+
pass
|
|
166
173
|
data = json.dumps(creds, indent=2).encode("utf-8")
|
|
167
174
|
tmp = path.with_suffix(path.suffix + ".tmp")
|
|
168
175
|
# Clean any leftover .tmp from a crashed prior write so O_EXCL can succeed.
|
|
@@ -548,7 +555,15 @@ def cmd_status(args: argparse.Namespace) -> int:
|
|
|
548
555
|
tier_cache = Path(args.tier_cache).expanduser()
|
|
549
556
|
if tier_cache.exists():
|
|
550
557
|
cache = json.loads(tier_cache.read_text(encoding="utf-8"))
|
|
551
|
-
|
|
558
|
+
# checked_at is written by _capcheck.py as epoch seconds (float), but
|
|
559
|
+
# older caches / future formats may carry an ISO string. Render both;
|
|
560
|
+
# never index a float (TypeError on every `sibyl status` run with a
|
|
561
|
+
# populated tier cache; Discord report 2026-06-10).
|
|
562
|
+
checked = cache.get("checked_at")
|
|
563
|
+
if isinstance(checked, (int, float)) and not isinstance(checked, bool):
|
|
564
|
+
checked = time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(checked))
|
|
565
|
+
checked = str(checked)[:19] if checked else "?"
|
|
566
|
+
print(a.kv("Tier cache", f"{cache.get('tier','?')} (checked {checked})"))
|
|
552
567
|
else:
|
|
553
568
|
print(a.kv("Tier cache", "—"))
|
|
554
569
|
|
|
@@ -1049,7 +1064,7 @@ def cmd_migrate(args: argparse.Namespace) -> int:
|
|
|
1049
1064
|
io = _migrate_io()
|
|
1050
1065
|
report = M.run_guided_setup(
|
|
1051
1066
|
home=home, cwd=cwd, db_path=db_path, backup_parent=backup_parent,
|
|
1052
|
-
io=io, debloat=not args.no_debloat,
|
|
1067
|
+
io=io, debloat=not args.no_debloat, force=getattr(args, "force", False),
|
|
1053
1068
|
)
|
|
1054
1069
|
|
|
1055
1070
|
ph = report.get("phases", {})
|
|
@@ -1189,6 +1204,11 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1189
1204
|
"--yes", "-y", action="store_true",
|
|
1190
1205
|
help="Skip the initial confirm (the trim step still always asks separately)",
|
|
1191
1206
|
)
|
|
1207
|
+
p_migrate.add_argument(
|
|
1208
|
+
"--force", action="store_true",
|
|
1209
|
+
help="Overwrite an existing non-sibyl memory provider when wiring a harness "
|
|
1210
|
+
"(without this, migrate stops at that harness and tells you to re-run with --force)",
|
|
1211
|
+
)
|
|
1192
1212
|
p_migrate.set_defaults(func=cmd_migrate)
|
|
1193
1213
|
|
|
1194
1214
|
return p
|
|
@@ -330,7 +330,7 @@ class GuidedIO:
|
|
|
330
330
|
def run_guided_setup(*, home=None, cwd=None, db_path=None, backup_parent=None,
|
|
331
331
|
io: Optional[GuidedIO] = None, wirers: Optional[dict] = None,
|
|
332
332
|
extract_fn: Optional[Callable[[Path, Path], None]] = None,
|
|
333
|
-
debloat: bool = True, now=None) -> dict:
|
|
333
|
+
debloat: bool = True, force: bool = False, now=None) -> dict:
|
|
334
334
|
"""The assembled guided flow: backup -> auto-wire each harness (instructions on
|
|
335
335
|
failure) -> extraction handoff -> verify -> confirmed debloat. Returns a structured
|
|
336
336
|
report. `extract_fn(backup_dir, db_path)` performs/simulates extraction; default
|
|
@@ -367,7 +367,7 @@ def run_guided_setup(*, home=None, cwd=None, db_path=None, backup_parent=None,
|
|
|
367
367
|
if w.current_state().get("wired_with_sibyl"):
|
|
368
368
|
wire_report[name] = "already"
|
|
369
369
|
continue
|
|
370
|
-
outcome = w.wire()
|
|
370
|
+
outcome = w.wire(force=force)
|
|
371
371
|
wire_report[name] = outcome.status
|
|
372
372
|
if outcome.status not in ("wired", "already"):
|
|
373
373
|
io.say(f"{name}: auto-wire incomplete ({outcome.message}). Do this manually:")
|
|
@@ -222,6 +222,15 @@ class HermesWirer:
|
|
|
222
222
|
loaded = yaml.safe_load(raw)
|
|
223
223
|
if isinstance(loaded, dict):
|
|
224
224
|
cfg = loaded
|
|
225
|
+
elif loaded is not None:
|
|
226
|
+
# Fail fast: a non-mapping top level means the config is not
|
|
227
|
+
# something we can merge into. Silently reinitializing would
|
|
228
|
+
# destroy the user's existing Hermes settings.
|
|
229
|
+
raise ValueError(
|
|
230
|
+
f"{self.config_path} top level is not a YAML mapping "
|
|
231
|
+
f"(got {type(loaded).__name__}). Fix the file or move it "
|
|
232
|
+
"aside and re-run; it was not modified."
|
|
233
|
+
)
|
|
225
234
|
if not isinstance(cfg.get("memory"), dict):
|
|
226
235
|
cfg["memory"] = {}
|
|
227
236
|
cfg["memory"]["provider"] = "sibyl"
|
|
@@ -338,6 +347,7 @@ class ClaudeCodeWirer:
|
|
|
338
347
|
settings_exists = self.settings_path.exists()
|
|
339
348
|
mcp_servers: dict = {}
|
|
340
349
|
sibyl_block: Optional[dict] = None
|
|
350
|
+
settings_parse_error: Optional[str] = None
|
|
341
351
|
if settings_exists:
|
|
342
352
|
try:
|
|
343
353
|
cfg = json.loads(self.settings_path.read_text(encoding="utf-8"))
|
|
@@ -346,8 +356,8 @@ class ClaudeCodeWirer:
|
|
|
346
356
|
if isinstance(raw_servers, dict):
|
|
347
357
|
mcp_servers = raw_servers
|
|
348
358
|
sibyl_block = mcp_servers.get("sibyl-memory")
|
|
349
|
-
except Exception:
|
|
350
|
-
|
|
359
|
+
except Exception as e:
|
|
360
|
+
settings_parse_error = f"{type(e).__name__}: {e}"
|
|
351
361
|
mcp_binary = self._mcp_binary_found()
|
|
352
362
|
cli_registered = self._registered_via_cli() # None when no `claude` CLI
|
|
353
363
|
# Source of truth: when the claude CLI exists, trust `claude mcp get` (where
|
|
@@ -359,6 +369,7 @@ class ClaudeCodeWirer:
|
|
|
359
369
|
return {
|
|
360
370
|
"settings_path": str(self.settings_path),
|
|
361
371
|
"settings_exists": settings_exists,
|
|
372
|
+
"settings_parse_error": settings_parse_error,
|
|
362
373
|
"mcp_servers_count": len(mcp_servers),
|
|
363
374
|
"sibyl_mcp": sibyl_block,
|
|
364
375
|
"mcp_binary_found": mcp_binary,
|
|
@@ -392,8 +403,17 @@ class ClaudeCodeWirer:
|
|
|
392
403
|
if rc != 0:
|
|
393
404
|
return WireOutcome(self.name, "error",
|
|
394
405
|
f"`claude mcp add` failed (exit {rc}): {(err or out).strip()[:200]}")
|
|
406
|
+
# Post-wire verification (bug, cryptoxdylan 2026-06-01): a 0 exit from
|
|
407
|
+
# `claude mcp add` has been observed to not guarantee discovery. Confirm the
|
|
408
|
+
# server actually shows in `claude mcp get`, and surface concrete remediation
|
|
409
|
+
# instead of reporting a false success that leaves the MCP absent from /mcp.
|
|
410
|
+
if self._registered_via_cli() is False:
|
|
411
|
+
return WireOutcome(self.name, "error",
|
|
412
|
+
"ran `claude mcp add` (exit 0) but the server is not in `claude mcp list`. "
|
|
413
|
+
"restart Claude Code, then run `claude mcp list`; if still absent, run "
|
|
414
|
+
f"`claude mcp add --scope user {self.MCP_NAME} -- {binpath}` manually.")
|
|
395
415
|
return WireOutcome(self.name, "wired",
|
|
396
|
-
"Registered sibyl-memory with Claude Code via `claude mcp add --scope user
|
|
416
|
+
"Registered sibyl-memory with Claude Code via `claude mcp add --scope user` (verified in `claude mcp list`).")
|
|
397
417
|
|
|
398
418
|
def wire(self, *, force: bool = False, dry_run: bool = False,
|
|
399
419
|
prompt_fn: Optional[Callable[..., str]] = None) -> WireOutcome:
|
|
@@ -484,12 +504,27 @@ class ClaudeCodeWirer:
|
|
|
484
504
|
def _write_settings_with_sibyl(self) -> None:
|
|
485
505
|
cfg: dict = {}
|
|
486
506
|
if self.settings_path.exists():
|
|
487
|
-
|
|
488
|
-
|
|
507
|
+
raw = self.settings_path.read_text(encoding="utf-8")
|
|
508
|
+
if raw.strip():
|
|
509
|
+
try:
|
|
510
|
+
loaded = json.loads(raw)
|
|
511
|
+
except ValueError as e:
|
|
512
|
+
# Fail fast: never atomically replace a corrupt settings.json
|
|
513
|
+
# with a sibyl-only file (that destroys the user's other
|
|
514
|
+
# mcpServers, permissions, hooks, env).
|
|
515
|
+
raise ValueError(
|
|
516
|
+
f"{self.settings_path} is not valid JSON ({e}). "
|
|
517
|
+
"Fix the file or move it aside and re-run setup; "
|
|
518
|
+
"your settings were not modified."
|
|
519
|
+
) from e
|
|
489
520
|
if isinstance(loaded, dict):
|
|
490
521
|
cfg = loaded
|
|
491
|
-
|
|
492
|
-
|
|
522
|
+
else:
|
|
523
|
+
raise ValueError(
|
|
524
|
+
f"{self.settings_path} top level is not a JSON object "
|
|
525
|
+
f"(got {type(loaded).__name__}). Fix the file or move it "
|
|
526
|
+
"aside and re-run setup; your settings were not modified."
|
|
527
|
+
)
|
|
493
528
|
if not isinstance(cfg.get("mcpServers"), dict):
|
|
494
529
|
cfg["mcpServers"] = {}
|
|
495
530
|
cfg["mcpServers"]["sibyl-memory"] = self.SIBYL_MCP_BLOCK
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sibyl-memory-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.13
|
|
4
4
|
Summary: Command-line interface for the Sibyl Memory Plugin. `sibyl init` activates, `sibyl upgrade` runs the staker / subscription flow, `sibyl status` shows current tier and DB stats, `sibyl whoami` gives a one-line account summary, `sibyl devices` lists active devices and supports per-device revoke.
|
|
5
5
|
Author-email: "SIBYL, Sibyl Labs LLC" <sibyl@sibyllabs.org>
|
|
6
6
|
License: MIT
|
{sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/SOURCES.txt
RENAMED
|
@@ -13,8 +13,10 @@ src/sibyl_memory_cli.egg-info/entry_points.txt
|
|
|
13
13
|
src/sibyl_memory_cli.egg-info/requires.txt
|
|
14
14
|
src/sibyl_memory_cli.egg-info/top_level.txt
|
|
15
15
|
tests/test_hermes_install_args.py
|
|
16
|
+
tests/test_init_perms_guard.py
|
|
16
17
|
tests/test_migrate.py
|
|
17
18
|
tests/test_migrate_adversarial.py
|
|
19
|
+
tests/test_migrate_force_2026_06_05.py
|
|
18
20
|
tests/test_orchestrator.py
|
|
19
21
|
tests/test_setup.py
|
|
20
22
|
tests/test_wiring_fix.py
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"""Regression: a pre-existing loose `~/.sibyl-memory` must be tightened to 0700.
|
|
2
|
+
|
|
3
|
+
`mkdir(mode=0o700)` is a no-op when the directory already exists, so a dir that
|
|
4
|
+
was created earlier at 0755 kept loose permissions on the credentials directory.
|
|
5
|
+
`write_credentials_atomic` now chmods the parent to 0700 explicitly.
|
|
6
|
+
Source: beta security report (dor_alpha, 2026-06-01).
|
|
7
|
+
"""
|
|
8
|
+
import os
|
|
9
|
+
import stat
|
|
10
|
+
from sibyl_memory_cli.cli import write_credentials_atomic
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def test_preexisting_loose_dir_is_tightened(tmp_path):
|
|
14
|
+
d = tmp_path / ".sibyl-memory"
|
|
15
|
+
d.mkdir()
|
|
16
|
+
os.chmod(d, 0o755) # simulate a pre-existing loose directory
|
|
17
|
+
assert stat.S_IMODE(d.stat().st_mode) == 0o755
|
|
18
|
+
|
|
19
|
+
write_credentials_atomic({"tenant_id": "t"}, path=d / "credentials.json")
|
|
20
|
+
|
|
21
|
+
assert stat.S_IMODE(d.stat().st_mode) == 0o700, "parent dir not tightened to 0700"
|
|
22
|
+
assert stat.S_IMODE((d / "credentials.json").stat().st_mode) == 0o600
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Regression (bugflow 2026-06-05): `sibyl migrate --force` must reach the wirers.
|
|
2
|
+
|
|
3
|
+
Onboarding dead-end: when a detected harness already had a non-sibyl memory
|
|
4
|
+
provider, the wirer refused with "Use --force to overwrite." but
|
|
5
|
+
`run_guided_setup` called `wire()` with no `force`, and `sibyl migrate` had no
|
|
6
|
+
`--force` flag to pass. The flag now threads cli -> run_guided_setup(force=) ->
|
|
7
|
+
wire(force=force).
|
|
8
|
+
"""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from sibyl_memory_cli import migrate as M
|
|
12
|
+
from sibyl_memory_cli.setup import WireOutcome
|
|
13
|
+
from sibyl_memory_client import MemoryClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class _RecordingWirer:
|
|
17
|
+
"""A fake harness wirer that records the `force` kwarg it was called with."""
|
|
18
|
+
|
|
19
|
+
name = "rec"
|
|
20
|
+
|
|
21
|
+
def __init__(self):
|
|
22
|
+
self.seen_force = None
|
|
23
|
+
|
|
24
|
+
def is_present(self):
|
|
25
|
+
return True
|
|
26
|
+
|
|
27
|
+
def current_state(self):
|
|
28
|
+
return {"wired_with_sibyl": False}
|
|
29
|
+
|
|
30
|
+
def wire(self, *, force: bool = False, dry_run: bool = False, prompt_fn=None):
|
|
31
|
+
self.seen_force = force
|
|
32
|
+
return WireOutcome(self.name, "wired", "ok")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _home_with_memory(tmp_path):
|
|
36
|
+
h = tmp_path / "home"
|
|
37
|
+
(h / "proj").mkdir(parents=True)
|
|
38
|
+
(h / "proj" / "CLAUDE.md").write_text("# memory\n- a fact worth keeping\n")
|
|
39
|
+
db = h / ".sibyl-memory" / "memory.db"
|
|
40
|
+
db.parent.mkdir(parents=True)
|
|
41
|
+
return h, db
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _fake_extract(_backup_dir, db_path):
|
|
45
|
+
MemoryClient.local(str(db_path), tenant_id="qa").set_entity("f", "a", {"v": 1})
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def test_force_true_threads_to_wirer(tmp_path):
|
|
49
|
+
h, db = _home_with_memory(tmp_path)
|
|
50
|
+
rec = _RecordingWirer()
|
|
51
|
+
M.run_guided_setup(
|
|
52
|
+
home=h, cwd=h / "proj", db_path=db, backup_parent=tmp_path / "bk",
|
|
53
|
+
io=M.GuidedIO(scripted=["n"]), wirers={"rec": rec},
|
|
54
|
+
extract_fn=_fake_extract, force=True,
|
|
55
|
+
)
|
|
56
|
+
assert rec.seen_force is True
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_force_defaults_false(tmp_path):
|
|
60
|
+
h, db = _home_with_memory(tmp_path)
|
|
61
|
+
rec = _RecordingWirer()
|
|
62
|
+
M.run_guided_setup(
|
|
63
|
+
home=h, cwd=h / "proj", db_path=db, backup_parent=tmp_path / "bk",
|
|
64
|
+
io=M.GuidedIO(scripted=["n"]), wirers={"rec": rec},
|
|
65
|
+
extract_fn=_fake_extract,
|
|
66
|
+
)
|
|
67
|
+
assert rec.seen_force is False
|
|
@@ -16,13 +16,20 @@ def _with_cli(monkeypatch):
|
|
|
16
16
|
|
|
17
17
|
def _mock_run(monkeypatch, *, get_rc=1, add_rc=0, add_err="boom"):
|
|
18
18
|
calls = []
|
|
19
|
+
state = {"added": False}
|
|
19
20
|
def fake(cmd, *, timeout=20.0):
|
|
20
21
|
calls.append(cmd)
|
|
21
22
|
if cmd[:3] == ["claude", "mcp", "get"]:
|
|
22
|
-
|
|
23
|
+
# Model real CLI state: once a successful `add` has run the server is
|
|
24
|
+
# registered, so the post-wire verification `get` succeeds. Before that
|
|
25
|
+
# it returns get_rc (1 = not yet registered).
|
|
26
|
+
return (0, "", "") if state["added"] else (get_rc, "", "")
|
|
23
27
|
if cmd[:3] == ["claude", "mcp", "add"]:
|
|
28
|
+
if add_rc == 0:
|
|
29
|
+
state["added"] = True
|
|
24
30
|
return (add_rc, "", "" if add_rc == 0 else add_err)
|
|
25
31
|
if cmd[:3] == ["claude", "mcp", "remove"]:
|
|
32
|
+
state["added"] = False
|
|
26
33
|
return (0, "", "")
|
|
27
34
|
return (0, "", "")
|
|
28
35
|
monkeypatch.setattr(S, "_run", fake)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/requires.txt
RENAMED
|
File without changes
|
{sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.13}/src/sibyl_memory_cli.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|