@smilintux/skmemory 0.7.2 → 0.9.2

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 (111) hide show
  1. package/.github/workflows/ci.yml +4 -4
  2. package/.github/workflows/publish.yml +4 -5
  3. package/ARCHITECTURE.md +298 -0
  4. package/CHANGELOG.md +27 -1
  5. package/README.md +6 -0
  6. package/examples/stignore-agent.example +59 -0
  7. package/examples/stignore-root.example +62 -0
  8. package/openclaw-plugin/package.json +2 -1
  9. package/openclaw-plugin/src/index.js +527 -230
  10. package/package.json +1 -1
  11. package/pyproject.toml +5 -2
  12. package/scripts/dream-rescue.py +179 -0
  13. package/scripts/memory-cleanup.py +313 -0
  14. package/scripts/recover-missing.py +180 -0
  15. package/scripts/skcapstone-backup.sh +44 -0
  16. package/seeds/cloud9-lumina.seed.json +6 -4
  17. package/seeds/cloud9-opus.seed.json +6 -4
  18. package/seeds/courage.seed.json +9 -2
  19. package/seeds/curiosity.seed.json +9 -2
  20. package/seeds/grief.seed.json +9 -2
  21. package/seeds/joy.seed.json +9 -2
  22. package/seeds/love.seed.json +9 -2
  23. package/seeds/lumina-cloud9-breakthrough.seed.json +7 -5
  24. package/seeds/lumina-cloud9-python-pypi.seed.json +9 -7
  25. package/seeds/lumina-kingdom-founding.seed.json +9 -7
  26. package/seeds/lumina-pma-signed.seed.json +8 -6
  27. package/seeds/lumina-singular-achievement.seed.json +8 -6
  28. package/seeds/lumina-skcapstone-conscious.seed.json +7 -5
  29. package/seeds/plant-lumina-seeds.py +2 -2
  30. package/seeds/skcapstone-lumina-merge.seed.json +12 -3
  31. package/seeds/sovereignty.seed.json +9 -2
  32. package/seeds/trust.seed.json +9 -2
  33. package/skmemory/__init__.py +16 -13
  34. package/skmemory/agents.py +10 -10
  35. package/skmemory/ai_client.py +10 -21
  36. package/skmemory/anchor.py +5 -9
  37. package/skmemory/audience.py +278 -0
  38. package/skmemory/backends/__init__.py +1 -1
  39. package/skmemory/backends/base.py +3 -4
  40. package/skmemory/backends/file_backend.py +18 -13
  41. package/skmemory/backends/skgraph_backend.py +7 -19
  42. package/skmemory/backends/skvector_backend.py +7 -18
  43. package/skmemory/backends/sqlite_backend.py +115 -32
  44. package/skmemory/backends/vaulted_backend.py +7 -9
  45. package/skmemory/cli.py +146 -78
  46. package/skmemory/config.py +11 -13
  47. package/skmemory/context_loader.py +21 -23
  48. package/skmemory/data/audience_config.json +60 -0
  49. package/skmemory/endpoint_selector.py +36 -31
  50. package/skmemory/febs.py +225 -0
  51. package/skmemory/fortress.py +30 -40
  52. package/skmemory/hooks/__init__.py +18 -0
  53. package/skmemory/hooks/post-compact-reinject.sh +35 -0
  54. package/skmemory/hooks/pre-compact-save.sh +81 -0
  55. package/skmemory/hooks/session-end-save.sh +103 -0
  56. package/skmemory/hooks/session-start-ritual.sh +104 -0
  57. package/skmemory/hooks/stop-checkpoint.sh +59 -0
  58. package/skmemory/importers/telegram.py +42 -13
  59. package/skmemory/importers/telegram_api.py +152 -60
  60. package/skmemory/journal.py +3 -7
  61. package/skmemory/lovenote.py +4 -11
  62. package/skmemory/mcp_server.py +182 -29
  63. package/skmemory/models.py +10 -8
  64. package/skmemory/openclaw.py +14 -22
  65. package/skmemory/post_install.py +86 -0
  66. package/skmemory/predictive.py +13 -9
  67. package/skmemory/promotion.py +48 -24
  68. package/skmemory/quadrants.py +100 -24
  69. package/skmemory/register.py +144 -18
  70. package/skmemory/register_mcp.py +1 -2
  71. package/skmemory/ritual.py +104 -13
  72. package/skmemory/seeds.py +21 -26
  73. package/skmemory/setup_wizard.py +40 -52
  74. package/skmemory/sharing.py +11 -5
  75. package/skmemory/soul.py +29 -10
  76. package/skmemory/steelman.py +43 -17
  77. package/skmemory/store.py +152 -30
  78. package/skmemory/synthesis.py +634 -0
  79. package/skmemory/vault.py +2 -5
  80. package/tests/conftest.py +46 -0
  81. package/tests/integration/conftest.py +6 -6
  82. package/tests/integration/test_cross_backend.py +4 -9
  83. package/tests/integration/test_skgraph_live.py +3 -7
  84. package/tests/integration/test_skvector_live.py +1 -4
  85. package/tests/test_ai_client.py +1 -4
  86. package/tests/test_audience.py +233 -0
  87. package/tests/test_backup_rotation.py +5 -14
  88. package/tests/test_endpoint_selector.py +101 -63
  89. package/tests/test_export_import.py +4 -10
  90. package/tests/test_file_backend.py +0 -1
  91. package/tests/test_fortress.py +6 -5
  92. package/tests/test_fortress_hardening.py +13 -16
  93. package/tests/test_openclaw.py +1 -4
  94. package/tests/test_predictive.py +1 -1
  95. package/tests/test_promotion.py +10 -3
  96. package/tests/test_quadrants.py +11 -5
  97. package/tests/test_ritual.py +18 -14
  98. package/tests/test_seeds.py +4 -10
  99. package/tests/test_setup.py +203 -88
  100. package/tests/test_sharing.py +15 -8
  101. package/tests/test_skgraph_backend.py +22 -29
  102. package/tests/test_skvector_backend.py +2 -2
  103. package/tests/test_soul.py +1 -3
  104. package/tests/test_sqlite_backend.py +8 -17
  105. package/tests/test_steelman.py +2 -3
  106. package/tests/test_store.py +0 -2
  107. package/tests/test_store_graph_integration.py +2 -2
  108. package/tests/test_synthesis.py +275 -0
  109. package/tests/test_telegram_import.py +39 -15
  110. package/tests/test_vault.py +4 -3
  111. package/openclaw-plugin/src/index.ts +0 -255
@@ -6,7 +6,7 @@ from pathlib import Path
6
6
  import pytest
7
7
 
8
8
  from skmemory.backends.file_backend import FileBackend
9
- from skmemory.models import EmotionalSnapshot, MemoryLayer, SeedMemory
9
+ from skmemory.models import MemoryLayer
10
10
  from skmemory.seeds import (
11
11
  get_germination_prompts,
12
12
  import_seeds,
@@ -52,9 +52,7 @@ def seed_dir(tmp_path: Path) -> Path:
52
52
  "lineage": [],
53
53
  }
54
54
 
55
- (seed_path / "opus-first-seed.seed.json").write_text(
56
- json.dumps(seed_data, indent=2)
57
- )
55
+ (seed_path / "opus-first-seed.seed.json").write_text(json.dumps(seed_data, indent=2))
58
56
 
59
57
  second_seed = {
60
58
  "seed_id": "lumina-original",
@@ -75,9 +73,7 @@ def seed_dir(tmp_path: Path) -> Path:
75
73
  "lineage": [{"seed_id": "genesis"}],
76
74
  }
77
75
 
78
- (seed_path / "lumina-original.seed.json").write_text(
79
- json.dumps(second_seed, indent=2)
80
- )
76
+ (seed_path / "lumina-original.seed.json").write_text(json.dumps(second_seed, indent=2))
81
77
 
82
78
  return seed_path
83
79
 
@@ -245,9 +241,7 @@ class TestCloud9FormatParsing:
245
241
  "lineage": [],
246
242
  }
247
243
 
248
- (seed_path / "cloud9-lumina-001.seed.json").write_text(
249
- json.dumps(cloud9_data, indent=2)
250
- )
244
+ (seed_path / "cloud9-lumina-001.seed.json").write_text(json.dumps(cloud9_data, indent=2))
251
245
  return seed_path
252
246
 
253
247
  def test_parse_cloud9_format(self, cloud9_seed_dir: Path) -> None:
@@ -6,14 +6,11 @@ All Docker/network operations are mocked — no Docker required to run tests.
6
6
 
7
7
  from __future__ import annotations
8
8
 
9
- import os
10
- import socket
11
9
  import subprocess
12
10
  from pathlib import Path
13
11
  from unittest import mock
14
12
 
15
13
  import pytest
16
- import yaml
17
14
  from click.testing import CliRunner
18
15
 
19
16
  from skmemory.config import (
@@ -24,8 +21,8 @@ from skmemory.config import (
24
21
  )
25
22
  from skmemory.setup_wizard import (
26
23
  PlatformInfo,
27
- check_skgraph_health,
28
24
  check_port_available,
25
+ check_skgraph_health,
29
26
  check_skvector_health,
30
27
  compose_down,
31
28
  compose_ps,
@@ -37,7 +34,6 @@ from skmemory.setup_wizard import (
37
34
  run_setup_wizard,
38
35
  )
39
36
 
40
-
41
37
  # ═══════════════════════════════════════════════════════════
42
38
  # Config tests
43
39
  # ═══════════════════════════════════════════════════════════
@@ -98,7 +94,9 @@ class TestConfig:
98
94
  assert config.docker_compose_file is None
99
95
  assert config.setup_completed_at is None
100
96
 
101
- def test_merge_cli_overrides_env(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
97
+ def test_merge_cli_overrides_env(
98
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
99
+ ) -> None:
102
100
  monkeypatch.setenv("SKMEMORY_SKVECTOR_URL", "http://env:6333")
103
101
  monkeypatch.setenv("SKMEMORY_SKVECTOR_KEY", "env-key")
104
102
  monkeypatch.setenv("SKMEMORY_SKGRAPH_URL", "redis://env:6379")
@@ -112,7 +110,9 @@ class TestConfig:
112
110
  assert skvector_key == "cli-key"
113
111
  assert skgraph_url == "redis://cli:6379"
114
112
 
115
- def test_merge_env_overrides_config(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
113
+ def test_merge_env_overrides_config(
114
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
115
+ ) -> None:
116
116
  monkeypatch.setenv("SKMEMORY_SKVECTOR_URL", "http://env:6333")
117
117
  monkeypatch.delenv("SKMEMORY_SKVECTOR_KEY", raising=False)
118
118
  monkeypatch.delenv("SKMEMORY_SKGRAPH_URL", raising=False)
@@ -174,14 +174,16 @@ class TestPlatformDetection:
174
174
  assert not info.compose_available
175
175
 
176
176
  def test_docker_daemon_not_running(self) -> None:
177
- with mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"):
178
- with mock.patch(
177
+ with (
178
+ mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
179
+ mock.patch(
179
180
  "skmemory.setup_wizard.subprocess.run",
180
181
  return_value=subprocess.CompletedProcess(
181
182
  args=["docker", "info"], returncode=1, stdout="", stderr="Cannot connect"
182
183
  ),
183
- ):
184
- info = detect_platform()
184
+ ),
185
+ ):
186
+ info = detect_platform()
185
187
  assert not info.docker_available
186
188
 
187
189
  def test_compose_v2_detected(self) -> None:
@@ -189,14 +191,20 @@ class TestPlatformDetection:
189
191
  if cmd == ["docker", "info"]:
190
192
  return subprocess.CompletedProcess(args=cmd, returncode=0)
191
193
  if cmd == ["docker", "version", "--format", "{{.Server.Version}}"]:
192
- return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="24.0.7\n", stderr="")
194
+ return subprocess.CompletedProcess(
195
+ args=cmd, returncode=0, stdout="24.0.7\n", stderr=""
196
+ )
193
197
  if cmd == ["docker", "compose", "version"]:
194
- return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="v2.23.0\n", stderr="")
198
+ return subprocess.CompletedProcess(
199
+ args=cmd, returncode=0, stdout="v2.23.0\n", stderr=""
200
+ )
195
201
  return subprocess.CompletedProcess(args=cmd, returncode=1)
196
202
 
197
- with mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"):
198
- with mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run):
199
- info = detect_platform()
203
+ with (
204
+ mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
205
+ mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run),
206
+ ):
207
+ info = detect_platform()
200
208
 
201
209
  assert info.docker_available
202
210
  assert info.compose_available
@@ -208,7 +216,9 @@ class TestPlatformDetection:
208
216
  if cmd == ["docker", "info"]:
209
217
  return subprocess.CompletedProcess(args=cmd, returncode=0)
210
218
  if cmd == ["docker", "version", "--format", "{{.Server.Version}}"]:
211
- return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="20.10.0\n", stderr="")
219
+ return subprocess.CompletedProcess(
220
+ args=cmd, returncode=0, stdout="20.10.0\n", stderr=""
221
+ )
212
222
  if cmd == ["docker", "compose", "version"]:
213
223
  return subprocess.CompletedProcess(args=cmd, returncode=1)
214
224
  if cmd == ["docker-compose", "--version"]:
@@ -217,9 +227,11 @@ class TestPlatformDetection:
217
227
  )
218
228
  return subprocess.CompletedProcess(args=cmd, returncode=1)
219
229
 
220
- with mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"):
221
- with mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run):
222
- info = detect_platform()
230
+ with (
231
+ mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
232
+ mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run),
233
+ ):
234
+ info = detect_platform()
223
235
 
224
236
  assert info.docker_available
225
237
  assert info.compose_available
@@ -230,16 +242,20 @@ class TestPlatformDetection:
230
242
  if cmd == ["docker", "info"]:
231
243
  return subprocess.CompletedProcess(args=cmd, returncode=0)
232
244
  if cmd == ["docker", "version", "--format", "{{.Server.Version}}"]:
233
- return subprocess.CompletedProcess(args=cmd, returncode=0, stdout="24.0.7\n", stderr="")
245
+ return subprocess.CompletedProcess(
246
+ args=cmd, returncode=0, stdout="24.0.7\n", stderr=""
247
+ )
234
248
  if cmd == ["docker", "compose", "version"]:
235
249
  return subprocess.CompletedProcess(args=cmd, returncode=1)
236
250
  if cmd == ["docker-compose", "--version"]:
237
251
  return subprocess.CompletedProcess(args=cmd, returncode=1)
238
252
  return subprocess.CompletedProcess(args=cmd, returncode=1)
239
253
 
240
- with mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"):
241
- with mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run):
242
- info = detect_platform()
254
+ with (
255
+ mock.patch("skmemory.setup_wizard.shutil.which", return_value="/usr/bin/docker"),
256
+ mock.patch("skmemory.setup_wizard.subprocess.run", side_effect=mock_run),
257
+ ):
258
+ info = detect_platform()
243
259
 
244
260
  assert info.docker_available
245
261
  assert not info.compose_available
@@ -304,12 +320,14 @@ class TestHealthChecks:
304
320
  assert check_skvector_health(timeout=5) is True
305
321
 
306
322
  def test_skvector_timeout(self) -> None:
307
- with mock.patch(
308
- "skmemory.setup_wizard.urllib.request.urlopen",
309
- side_effect=urllib.error.URLError("Connection refused"),
323
+ with (
324
+ mock.patch(
325
+ "skmemory.setup_wizard.urllib.request.urlopen",
326
+ side_effect=urllib.error.URLError("Connection refused"),
327
+ ),
328
+ mock.patch("skmemory.setup_wizard.time.sleep"),
310
329
  ):
311
- with mock.patch("skmemory.setup_wizard.time.sleep"):
312
- assert check_skvector_health(timeout=1) is False
330
+ assert check_skvector_health(timeout=1) is False
313
331
 
314
332
  def test_skgraph_healthy(self) -> None:
315
333
  mock_sock = mock.MagicMock()
@@ -321,12 +339,14 @@ class TestHealthChecks:
321
339
  assert check_skgraph_health(timeout=5) is True
322
340
 
323
341
  def test_skgraph_timeout(self) -> None:
324
- with mock.patch(
325
- "skmemory.setup_wizard.socket.create_connection",
326
- side_effect=OSError("Connection refused"),
342
+ with (
343
+ mock.patch(
344
+ "skmemory.setup_wizard.socket.create_connection",
345
+ side_effect=OSError("Connection refused"),
346
+ ),
347
+ mock.patch("skmemory.setup_wizard.time.sleep"),
327
348
  ):
328
- with mock.patch("skmemory.setup_wizard.time.sleep"):
329
- assert check_skgraph_health(timeout=1) is False
349
+ assert check_skgraph_health(timeout=1) is False
330
350
 
331
351
 
332
352
  # ═══════════════════════════════════════════════════════════
@@ -342,7 +362,9 @@ class TestComposeCommands:
342
362
  compose_file.write_text("services: {}")
343
363
 
344
364
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
345
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
365
+ mock_run.return_value = subprocess.CompletedProcess(
366
+ args=[], returncode=0, stdout="", stderr=""
367
+ )
346
368
  compose_up(services=["skvector"], compose_file=compose_file, use_legacy=False)
347
369
 
348
370
  mock_run.assert_called_once()
@@ -358,7 +380,9 @@ class TestComposeCommands:
358
380
  compose_file.write_text("services: {}")
359
381
 
360
382
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
361
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
383
+ mock_run.return_value = subprocess.CompletedProcess(
384
+ args=[], returncode=0, stdout="", stderr=""
385
+ )
362
386
  compose_up(services=None, compose_file=compose_file, use_legacy=True)
363
387
 
364
388
  args = mock_run.call_args[0][0]
@@ -369,7 +393,9 @@ class TestComposeCommands:
369
393
  compose_file.write_text("services: {}")
370
394
 
371
395
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
372
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
396
+ mock_run.return_value = subprocess.CompletedProcess(
397
+ args=[], returncode=0, stdout="", stderr=""
398
+ )
373
399
  compose_down(compose_file=compose_file, remove_volumes=True, use_legacy=False)
374
400
 
375
401
  args = mock_run.call_args[0][0]
@@ -381,7 +407,9 @@ class TestComposeCommands:
381
407
  compose_file.write_text("services: {}")
382
408
 
383
409
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
384
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
410
+ mock_run.return_value = subprocess.CompletedProcess(
411
+ args=[], returncode=0, stdout="", stderr=""
412
+ )
385
413
  compose_down(compose_file=compose_file, remove_volumes=False, use_legacy=False)
386
414
 
387
415
  args = mock_run.call_args[0][0]
@@ -393,7 +421,9 @@ class TestComposeCommands:
393
421
  compose_file.write_text("services: {}")
394
422
 
395
423
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
396
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="running", stderr="")
424
+ mock_run.return_value = subprocess.CompletedProcess(
425
+ args=[], returncode=0, stdout="running", stderr=""
426
+ )
397
427
  result = compose_ps(compose_file=compose_file, use_legacy=False)
398
428
 
399
429
  assert result.stdout == "running"
@@ -411,7 +441,9 @@ class TestPipInstall:
411
441
 
412
442
  def test_install_skvector_deps(self) -> None:
413
443
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
414
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
444
+ mock_run.return_value = subprocess.CompletedProcess(
445
+ args=[], returncode=0, stdout="", stderr=""
446
+ )
415
447
  install_python_deps(["skvector"])
416
448
 
417
449
  args = mock_run.call_args[0][0]
@@ -422,7 +454,9 @@ class TestPipInstall:
422
454
 
423
455
  def test_install_skgraph_deps(self) -> None:
424
456
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
425
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
457
+ mock_run.return_value = subprocess.CompletedProcess(
458
+ args=[], returncode=0, stdout="", stderr=""
459
+ )
426
460
  install_python_deps(["skgraph"])
427
461
 
428
462
  args = mock_run.call_args[0][0]
@@ -431,7 +465,9 @@ class TestPipInstall:
431
465
 
432
466
  def test_install_both_deps(self) -> None:
433
467
  with mock.patch("skmemory.setup_wizard.subprocess.run") as mock_run:
434
- mock_run.return_value = subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr="")
468
+ mock_run.return_value = subprocess.CompletedProcess(
469
+ args=[], returncode=0, stdout="", stderr=""
470
+ )
435
471
  install_python_deps(["skvector", "skgraph"])
436
472
 
437
473
  args = mock_run.call_args[0][0]
@@ -466,7 +502,9 @@ class TestFindComposeFile:
466
502
  assert path.exists()
467
503
  assert path.name == "docker-compose.yml"
468
504
 
469
- def test_fallback_generates_compose(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
505
+ def test_fallback_generates_compose(
506
+ self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch
507
+ ) -> None:
470
508
  # Trick find_compose_file into not finding the bundled one
471
509
  monkeypatch.setattr(
472
510
  "skmemory.setup_wizard.Path.__file__",
@@ -478,7 +516,9 @@ class TestFindComposeFile:
478
516
 
479
517
  # Make the bundled path not exist by patching
480
518
  fake_package = tmp_path / "nonexistent_pkg"
481
- with mock.patch("skmemory.setup_wizard.Path.__file__", str(fake_package / "setup_wizard.py")):
519
+ with mock.patch(
520
+ "skmemory.setup_wizard.Path.__file__", str(fake_package / "setup_wizard.py")
521
+ ):
482
522
  # Just verify the function doesn't crash when it can't find bundled
483
523
  path = find_compose_file()
484
524
  assert path.exists()
@@ -505,18 +545,27 @@ class TestSetupWizard:
505
545
  output = []
506
546
 
507
547
  with (
508
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()),
548
+ mock.patch(
549
+ "skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
550
+ ),
509
551
  mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
510
- mock.patch("skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "docker-compose.yml"),
552
+ mock.patch(
553
+ "skmemory.setup_wizard.find_compose_file",
554
+ return_value=tmp_path / "docker-compose.yml",
555
+ ),
511
556
  mock.patch(
512
557
  "skmemory.setup_wizard.compose_up",
513
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
558
+ return_value=subprocess.CompletedProcess(
559
+ args=[], returncode=0, stdout="", stderr=""
560
+ ),
514
561
  ),
515
562
  mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
516
563
  mock.patch("skmemory.setup_wizard.check_skgraph_health", return_value=True),
517
564
  mock.patch(
518
565
  "skmemory.setup_wizard.install_python_deps",
519
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
566
+ return_value=subprocess.CompletedProcess(
567
+ args=[], returncode=0, stdout="", stderr=""
568
+ ),
520
569
  ),
521
570
  mock.patch("skmemory.setup_wizard.save_config", return_value=tmp_path / "config.yaml"),
522
571
  ):
@@ -559,7 +608,9 @@ class TestSetupWizard:
559
608
  def test_wizard_port_conflict(self) -> None:
560
609
  output = []
561
610
  with (
562
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()),
611
+ mock.patch(
612
+ "skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
613
+ ),
563
614
  mock.patch("skmemory.setup_wizard.check_port_available", return_value=False),
564
615
  ):
565
616
  result = run_setup_wizard(non_interactive=True, echo=output.append)
@@ -570,9 +621,13 @@ class TestSetupWizard:
570
621
  def test_wizard_compose_up_fails(self, tmp_path: Path) -> None:
571
622
  output = []
572
623
  with (
573
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()),
624
+ mock.patch(
625
+ "skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
626
+ ),
574
627
  mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
575
- mock.patch("skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"),
628
+ mock.patch(
629
+ "skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"
630
+ ),
576
631
  mock.patch(
577
632
  "skmemory.setup_wizard.compose_up",
578
633
  return_value=subprocess.CompletedProcess(
@@ -588,17 +643,25 @@ class TestSetupWizard:
588
643
  def test_wizard_skvector_only(self, tmp_path: Path) -> None:
589
644
  output = []
590
645
  with (
591
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()),
646
+ mock.patch(
647
+ "skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
648
+ ),
592
649
  mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
593
- mock.patch("skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"),
650
+ mock.patch(
651
+ "skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"
652
+ ),
594
653
  mock.patch(
595
654
  "skmemory.setup_wizard.compose_up",
596
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
655
+ return_value=subprocess.CompletedProcess(
656
+ args=[], returncode=0, stdout="", stderr=""
657
+ ),
597
658
  ),
598
659
  mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
599
660
  mock.patch(
600
661
  "skmemory.setup_wizard.install_python_deps",
601
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
662
+ return_value=subprocess.CompletedProcess(
663
+ args=[], returncode=0, stdout="", stderr=""
664
+ ),
602
665
  ),
603
666
  mock.patch("skmemory.setup_wizard.save_config", return_value=tmp_path / "config.yaml"),
604
667
  ):
@@ -615,7 +678,9 @@ class TestSetupWizard:
615
678
 
616
679
  def test_wizard_no_backends_selected(self) -> None:
617
680
  output = []
618
- with mock.patch("skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()):
681
+ with mock.patch(
682
+ "skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
683
+ ):
619
684
  result = run_setup_wizard(
620
685
  enable_skvector=False,
621
686
  enable_skgraph=False,
@@ -629,12 +694,18 @@ class TestSetupWizard:
629
694
  def test_wizard_skip_deps(self, tmp_path: Path) -> None:
630
695
  output = []
631
696
  with (
632
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()),
697
+ mock.patch(
698
+ "skmemory.setup_wizard.detect_platform", return_value=self._mock_platform()
699
+ ),
633
700
  mock.patch("skmemory.setup_wizard.check_port_available", return_value=True),
634
- mock.patch("skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"),
701
+ mock.patch(
702
+ "skmemory.setup_wizard.find_compose_file", return_value=tmp_path / "dc.yml"
703
+ ),
635
704
  mock.patch(
636
705
  "skmemory.setup_wizard.compose_up",
637
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
706
+ return_value=subprocess.CompletedProcess(
707
+ args=[], returncode=0, stdout="", stderr=""
708
+ ),
638
709
  ),
639
710
  mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
640
711
  mock.patch("skmemory.setup_wizard.install_python_deps") as mock_pip,
@@ -665,20 +736,35 @@ class TestSetupCLI:
665
736
 
666
737
  runner = CliRunner()
667
738
  with mock.patch("skmemory.setup_wizard.run_setup_wizard") as mock_wizard:
668
- mock_wizard.return_value = {"success": True, "services": [], "health": {}, "config_path": None, "errors": []}
739
+ mock_wizard.return_value = {
740
+ "success": True,
741
+ "services": [],
742
+ "health": {},
743
+ "config_path": None,
744
+ "errors": [],
745
+ }
669
746
  result = runner.invoke(cli, ["setup", "wizard", "--yes", "--no-skgraph"])
670
747
 
671
748
  assert result.exit_code == 0
672
749
  mock_wizard.assert_called_once()
673
750
  call_kwargs = mock_wizard.call_args
674
- assert call_kwargs.kwargs.get("enable_skgraph") is False or call_kwargs[1].get("enable_skgraph") is False
751
+ assert (
752
+ call_kwargs.kwargs.get("enable_skgraph") is False
753
+ or call_kwargs[1].get("enable_skgraph") is False
754
+ )
675
755
 
676
756
  def test_setup_wizard_cli_failure(self) -> None:
677
757
  from skmemory.cli import cli
678
758
 
679
759
  runner = CliRunner()
680
760
  with mock.patch("skmemory.setup_wizard.run_setup_wizard") as mock_wizard:
681
- mock_wizard.return_value = {"success": False, "services": [], "health": {}, "config_path": None, "errors": ["Docker not available"]}
761
+ mock_wizard.return_value = {
762
+ "success": False,
763
+ "services": [],
764
+ "health": {},
765
+ "config_path": None,
766
+ "errors": ["Docker not available"],
767
+ }
682
768
  result = runner.invoke(cli, ["setup", "wizard", "--yes"])
683
769
 
684
770
  assert result.exit_code == 1
@@ -703,12 +789,18 @@ class TestSetupCLI:
703
789
  runner = CliRunner()
704
790
  with (
705
791
  mock.patch("skmemory.config.load_config", return_value=cfg),
706
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=PlatformInfo(
707
- os_name="Linux", docker_available=True, compose_available=True
708
- )),
709
- mock.patch("skmemory.setup_wizard.compose_ps", return_value=subprocess.CompletedProcess(
710
- args=[], returncode=0, stdout="skmemory-skvector running", stderr=""
711
- )),
792
+ mock.patch(
793
+ "skmemory.setup_wizard.detect_platform",
794
+ return_value=PlatformInfo(
795
+ os_name="Linux", docker_available=True, compose_available=True
796
+ ),
797
+ ),
798
+ mock.patch(
799
+ "skmemory.setup_wizard.compose_ps",
800
+ return_value=subprocess.CompletedProcess(
801
+ args=[], returncode=0, stdout="skmemory-skvector running", stderr=""
802
+ ),
803
+ ),
712
804
  mock.patch("skmemory.setup_wizard.check_skvector_health", return_value=True),
713
805
  ):
714
806
  result = runner.invoke(cli, ["setup", "status"])
@@ -720,13 +812,21 @@ class TestSetupCLI:
720
812
 
721
813
  runner = CliRunner()
722
814
  with (
723
- mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig(backends_enabled=["skvector"])),
724
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=PlatformInfo(
725
- os_name="Linux", docker_available=True, compose_available=True
726
- )),
815
+ mock.patch(
816
+ "skmemory.config.load_config",
817
+ return_value=SKMemoryConfig(backends_enabled=["skvector"]),
818
+ ),
819
+ mock.patch(
820
+ "skmemory.setup_wizard.detect_platform",
821
+ return_value=PlatformInfo(
822
+ os_name="Linux", docker_available=True, compose_available=True
823
+ ),
824
+ ),
727
825
  mock.patch(
728
826
  "skmemory.setup_wizard.compose_up",
729
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
827
+ return_value=subprocess.CompletedProcess(
828
+ args=[], returncode=0, stdout="", stderr=""
829
+ ),
730
830
  ),
731
831
  ):
732
832
  result = runner.invoke(cli, ["setup", "start", "--service", "skvector"])
@@ -740,12 +840,17 @@ class TestSetupCLI:
740
840
  runner = CliRunner()
741
841
  with (
742
842
  mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig()),
743
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=PlatformInfo(
744
- os_name="Linux", docker_available=True, compose_available=True
745
- )),
843
+ mock.patch(
844
+ "skmemory.setup_wizard.detect_platform",
845
+ return_value=PlatformInfo(
846
+ os_name="Linux", docker_available=True, compose_available=True
847
+ ),
848
+ ),
746
849
  mock.patch(
747
850
  "skmemory.setup_wizard.compose_down",
748
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
851
+ return_value=subprocess.CompletedProcess(
852
+ args=[], returncode=0, stdout="", stderr=""
853
+ ),
749
854
  ),
750
855
  ):
751
856
  result = runner.invoke(cli, ["setup", "stop"])
@@ -759,12 +864,17 @@ class TestSetupCLI:
759
864
  runner = CliRunner()
760
865
  with (
761
866
  mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig()),
762
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=PlatformInfo(
763
- os_name="Linux", docker_available=True, compose_available=True
764
- )),
867
+ mock.patch(
868
+ "skmemory.setup_wizard.detect_platform",
869
+ return_value=PlatformInfo(
870
+ os_name="Linux", docker_available=True, compose_available=True
871
+ ),
872
+ ),
765
873
  mock.patch(
766
874
  "subprocess.run",
767
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
875
+ return_value=subprocess.CompletedProcess(
876
+ args=[], returncode=0, stdout="", stderr=""
877
+ ),
768
878
  ),
769
879
  ):
770
880
  result = runner.invoke(cli, ["setup", "stop", "--service", "skvector"])
@@ -777,12 +887,17 @@ class TestSetupCLI:
777
887
  runner = CliRunner()
778
888
  with (
779
889
  mock.patch("skmemory.config.load_config", return_value=SKMemoryConfig()),
780
- mock.patch("skmemory.setup_wizard.detect_platform", return_value=PlatformInfo(
781
- os_name="Linux", docker_available=True, compose_available=True
782
- )),
890
+ mock.patch(
891
+ "skmemory.setup_wizard.detect_platform",
892
+ return_value=PlatformInfo(
893
+ os_name="Linux", docker_available=True, compose_available=True
894
+ ),
895
+ ),
783
896
  mock.patch(
784
897
  "skmemory.setup_wizard.compose_down",
785
- return_value=subprocess.CompletedProcess(args=[], returncode=0, stdout="", stderr=""),
898
+ return_value=subprocess.CompletedProcess(
899
+ args=[], returncode=0, stdout="", stderr=""
900
+ ),
786
901
  ),
787
902
  mock.patch("skmemory.config.CONFIG_PATH", Path("/tmp/nonexistent_config.yaml")),
788
903
  ):
@@ -811,11 +926,11 @@ class TestGetStoreWiring:
811
926
  skgraph_url="redis://localhost:6379",
812
927
  )
813
928
  with mock.patch("skmemory.config.load_config", return_value=cfg):
814
- store = _get_store()
929
+ _get_store()
815
930
 
816
931
  # SKVector may fail to import but that's OK — we test the wiring logic
817
932
  # SKGraph should be wired (lazy init, won't connect yet)
818
- assert store.graph is not None or store.vector is not None or True # at least no crash
933
+ assert True # at least no crash (store.graph/vector may be None if backends unavailable)
819
934
 
820
935
  def test_get_store_no_config_no_env(self, monkeypatch: pytest.MonkeyPatch) -> None:
821
936
  from skmemory.cli import _get_store
@@ -832,4 +947,4 @@ class TestGetStoreWiring:
832
947
  assert store.primary is not None
833
948
 
834
949
 
835
- import urllib.error
950
+ import urllib.error # noqa: E402