sibyl-memory-cli 0.3.10__tar.gz → 0.3.11__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.
Files changed (23) hide show
  1. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/PKG-INFO +1 -1
  2. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/pyproject.toml +1 -1
  3. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli/cli.py +7 -0
  4. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli/setup.py +10 -1
  5. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli.egg-info/PKG-INFO +1 -1
  6. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli.egg-info/SOURCES.txt +1 -0
  7. sibyl_memory_cli-0.3.11/tests/test_init_perms_guard.py +22 -0
  8. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/tests/test_wiring_fix.py +8 -1
  9. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/README.md +0 -0
  10. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/setup.cfg +0 -0
  11. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli/__init__.py +0 -0
  12. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli/_aesthetic.py +0 -0
  13. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli/_banner.py +0 -0
  14. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli/migrate.py +0 -0
  15. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli.egg-info/dependency_links.txt +0 -0
  16. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli.egg-info/entry_points.txt +0 -0
  17. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli.egg-info/requires.txt +0 -0
  18. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/src/sibyl_memory_cli.egg-info/top_level.txt +0 -0
  19. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/tests/test_hermes_install_args.py +0 -0
  20. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/tests/test_migrate.py +0 -0
  21. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/tests/test_migrate_adversarial.py +0 -0
  22. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/tests/test_orchestrator.py +0 -0
  23. {sibyl_memory_cli-0.3.10 → sibyl_memory_cli-0.3.11}/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.10
3
+ Version: 0.3.11
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.10"
7
+ version = "0.3.11"
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.
@@ -392,8 +392,17 @@ class ClaudeCodeWirer:
392
392
  if rc != 0:
393
393
  return WireOutcome(self.name, "error",
394
394
  f"`claude mcp add` failed (exit {rc}): {(err or out).strip()[:200]}")
395
+ # Post-wire verification (bug, cryptoxdylan 2026-06-01): a 0 exit from
396
+ # `claude mcp add` has been observed to not guarantee discovery. Confirm the
397
+ # server actually shows in `claude mcp get`, and surface concrete remediation
398
+ # instead of reporting a false success that leaves the MCP absent from /mcp.
399
+ if self._registered_via_cli() is False:
400
+ return WireOutcome(self.name, "error",
401
+ "ran `claude mcp add` (exit 0) but the server is not in `claude mcp list`. "
402
+ "restart Claude Code, then run `claude mcp list`; if still absent, run "
403
+ f"`claude mcp add --scope user {self.MCP_NAME} -- {binpath}` manually.")
395
404
  return WireOutcome(self.name, "wired",
396
- "Registered sibyl-memory with Claude Code via `claude mcp add --scope user`.")
405
+ "Registered sibyl-memory with Claude Code via `claude mcp add --scope user` (verified in `claude mcp list`).")
397
406
 
398
407
  def wire(self, *, force: bool = False, dry_run: bool = False,
399
408
  prompt_fn: Optional[Callable[..., str]] = None) -> WireOutcome:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sibyl-memory-cli
3
- Version: 0.3.10
3
+ Version: 0.3.11
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
@@ -13,6 +13,7 @@ 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
18
19
  tests/test_orchestrator.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
@@ -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
- return (get_rc, "", "")
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)