@smilintux/skcapstone 0.9.0 → 0.12.5

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 (284) hide show
  1. package/.env.example +10 -4
  2. package/.github/workflows/ci.yml +2 -2
  3. package/.github/workflows/publish.yml +9 -2
  4. package/.openclaw-workspace.json +2 -2
  5. package/CLAUDE.md +37 -0
  6. package/MISSION.md +17 -2
  7. package/README.md +282 -3
  8. package/docker/Dockerfile +7 -7
  9. package/docker/compose-templates/dev-team.yml +12 -12
  10. package/docker/compose-templates/mini-team.yml +9 -9
  11. package/docker/compose-templates/ops-team.yml +10 -10
  12. package/docker/compose-templates/research-team.yml +10 -10
  13. package/docker/entrypoint.sh +4 -4
  14. package/docs/ADR-optional-integration-backbone.md +181 -0
  15. package/docs/ARCHITECTURE.md +186 -43
  16. package/docs/BOND_WITH_GROK.md +6 -6
  17. package/docs/CUSTOM_AGENT.md +278 -1
  18. package/docs/DREAMING.md +70 -0
  19. package/docs/GETTING_STARTED.md +10 -7
  20. package/docs/QUICKSTART.md +10 -6
  21. package/docs/SKJOULE_ARCHITECTURE.md +3 -3
  22. package/docs/SOUL_SWAPPER.md +5 -5
  23. package/docs/hammertime-audit.md +402 -0
  24. package/docs/sk-integration-HANDOFF.md +117 -0
  25. package/docs/skscheduler.md +155 -0
  26. package/docs/superpowers/examples/jobs.yaml +31 -0
  27. package/docs/superpowers/plans/2026-06-08-skscheduler.md +1265 -0
  28. package/docs/superpowers/specs/2026-06-08-skscheduler-design.md +186 -0
  29. package/examples/custom-bond-template.json +1 -1
  30. package/examples/grok-feb.json +1 -1
  31. package/examples/queen-ava-feb.json +1 -1
  32. package/launchd/com.skcapstone.daemon.plist +52 -0
  33. package/launchd/com.skcapstone.memory-compress.plist +45 -0
  34. package/launchd/com.skcapstone.skcomms-heartbeat.plist +33 -0
  35. package/launchd/com.skcapstone.skcomms-queue-drain.plist +34 -0
  36. package/launchd/install-launchd.sh +156 -0
  37. package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/index.ts +3 -2
  38. package/package.json +1 -1
  39. package/pyproject.toml +16 -10
  40. package/scripts/archive-sessions.sh +95 -0
  41. package/scripts/check-updates.py +4 -4
  42. package/scripts/install-bundle.sh +8 -8
  43. package/scripts/install.ps1 +12 -11
  44. package/scripts/install.sh +196 -11
  45. package/scripts/model-fallback-monitor.sh +102 -0
  46. package/scripts/notion-api.py +259 -0
  47. package/scripts/nvidia-proxy.mjs +908 -0
  48. package/scripts/proxy-monitor.sh +89 -0
  49. package/scripts/refresh-anthropic-token.sh +172 -0
  50. package/scripts/release.sh +98 -0
  51. package/scripts/session-to-memory.py +219 -0
  52. package/scripts/skgateway.mjs +856 -0
  53. package/scripts/telegram-catchup-all.sh +147 -0
  54. package/scripts/verify_install.sh +2 -2
  55. package/scripts/wargov-ufo-capture/README.md +43 -0
  56. package/scripts/wargov-ufo-capture/cdp_capture_release2.py +273 -0
  57. package/scripts/wargov-ufo-capture/cdp_capture_splc_doj.py +246 -0
  58. package/scripts/wargov-ufo-capture/cdp_finish.py +271 -0
  59. package/scripts/wargov-ufo-capture/cdp_probe.py +188 -0
  60. package/scripts/wargov-ufo-capture/cdp_splc_pressrelease.py +101 -0
  61. package/scripts/wargov-ufo-capture/parse_csv.py +95 -0
  62. package/scripts/wargov-ufo-capture/pull_dvids.sh +107 -0
  63. package/scripts/watch-anthropic-token.sh +212 -0
  64. package/scripts/windows/install-tasks.ps1 +7 -7
  65. package/scripts/windows/skcapstone-task.xml +1 -1
  66. package/src/skcapstone/__init__.py +45 -3
  67. package/src/skcapstone/_cli_monolith.py +20 -15
  68. package/src/skcapstone/activity.py +5 -1
  69. package/src/skcapstone/agent_card.py +3 -2
  70. package/src/skcapstone/api.py +41 -40
  71. package/src/skcapstone/auction.py +14 -11
  72. package/src/skcapstone/backup.py +2 -1
  73. package/src/skcapstone/blueprint_registry.py +4 -3
  74. package/src/skcapstone/blueprints/builtins/itil-operations.yaml +40 -0
  75. package/src/skcapstone/brain_first.py +238 -0
  76. package/src/skcapstone/changelog.py +1 -1
  77. package/src/skcapstone/chat.py +22 -17
  78. package/src/skcapstone/cli/__init__.py +9 -1
  79. package/src/skcapstone/cli/_common.py +1 -0
  80. package/src/skcapstone/cli/agents_spawner.py +5 -2
  81. package/src/skcapstone/cli/alerts.py +25 -4
  82. package/src/skcapstone/cli/bench.py +15 -15
  83. package/src/skcapstone/cli/chat.py +7 -4
  84. package/src/skcapstone/cli/consciousness.py +5 -2
  85. package/src/skcapstone/cli/context_cmd.py +18 -4
  86. package/src/skcapstone/cli/daemon.py +121 -42
  87. package/src/skcapstone/cli/gtd.py +26 -1
  88. package/src/skcapstone/cli/housekeeping.py +3 -3
  89. package/src/skcapstone/cli/identity_cmd.py +378 -0
  90. package/src/skcapstone/cli/joule_cmd.py +7 -3
  91. package/src/skcapstone/cli/memory.py +8 -6
  92. package/src/skcapstone/cli/peers_dir.py +1 -1
  93. package/src/skcapstone/cli/register_cmd.py +29 -3
  94. package/src/skcapstone/cli/scheduler_cmd.py +167 -0
  95. package/src/skcapstone/cli/session.py +25 -0
  96. package/src/skcapstone/cli/setup.py +96 -29
  97. package/src/skcapstone/cli/shell_cmd.py +53 -1
  98. package/src/skcapstone/cli/skills_cmd.py +2 -2
  99. package/src/skcapstone/cli/soul.py +8 -5
  100. package/src/skcapstone/cli/status.py +37 -11
  101. package/src/skcapstone/cli/telegram.py +21 -0
  102. package/src/skcapstone/cli/test_cmd.py +5 -5
  103. package/src/skcapstone/cli/test_connection.py +2 -2
  104. package/src/skcapstone/cli/upgrade_cmd.py +23 -14
  105. package/src/skcapstone/cli/version_cmd.py +1 -1
  106. package/src/skcapstone/cli/watch_cmd.py +9 -6
  107. package/src/skcapstone/cloud9_bridge.py +14 -14
  108. package/src/skcapstone/codex_setup.py +255 -0
  109. package/src/skcapstone/config_validator.py +7 -4
  110. package/src/skcapstone/consciousness_config.py +5 -1
  111. package/src/skcapstone/consciousness_loop.py +313 -273
  112. package/src/skcapstone/context_loader.py +121 -0
  113. package/src/skcapstone/coord_federation.py +2 -1
  114. package/src/skcapstone/coordination.py +23 -6
  115. package/src/skcapstone/crush_integration.py +2 -1
  116. package/src/skcapstone/daemon.py +151 -88
  117. package/src/skcapstone/dashboard.py +10 -10
  118. package/src/skcapstone/data/sk-agent-picker.sh +421 -0
  119. package/src/skcapstone/data/systemd/skcapstone-api.socket +9 -0
  120. package/src/skcapstone/data/systemd/skcapstone-memory-compress.service +18 -0
  121. package/src/skcapstone/data/systemd/skcapstone-memory-compress.timer +11 -0
  122. package/src/skcapstone/data/systemd/skcapstone.service +37 -0
  123. package/src/skcapstone/data/systemd/skcapstone@.service +50 -0
  124. package/src/skcapstone/data/systemd/skcomms-heartbeat.service +18 -0
  125. package/{systemd/skcomm-heartbeat.timer → src/skcapstone/data/systemd/skcomms-heartbeat.timer} +2 -2
  126. package/src/skcapstone/data/systemd/skcomms-queue-drain.service +17 -0
  127. package/{systemd/skcomm-queue-drain.timer → src/skcapstone/data/systemd/skcomms-queue-drain.timer} +2 -2
  128. package/src/skcapstone/defaults/claude/CLAUDE.md +67 -0
  129. package/src/skcapstone/defaults/claude/settings.json +74 -0
  130. package/src/skcapstone/defaults/lumina/config/claude-hooks.md +57 -0
  131. package/src/skcapstone/defaults/lumina/config/skgraph.yaml +55 -10
  132. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +79 -13
  133. package/src/skcapstone/defaults/lumina/config/skvector.yaml +60 -9
  134. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +2 -2
  135. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +2 -2
  136. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +9 -9
  137. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +2 -2
  138. package/src/skcapstone/defaults/unhinged.json +13 -0
  139. package/src/skcapstone/discovery.py +43 -20
  140. package/src/skcapstone/doctor.py +941 -22
  141. package/src/skcapstone/dreaming.py +1183 -109
  142. package/src/skcapstone/emotion_tracker.py +2 -2
  143. package/src/skcapstone/export.py +4 -3
  144. package/src/skcapstone/fuse_mount.py +35 -25
  145. package/src/skcapstone/gui_installer.py +2 -2
  146. package/src/skcapstone/heartbeat.py +34 -30
  147. package/src/skcapstone/housekeeping.py +14 -14
  148. package/src/skcapstone/install_wizard.py +209 -7
  149. package/src/skcapstone/itil.py +13 -4
  150. package/src/skcapstone/kms_scheduler.py +10 -8
  151. package/src/skcapstone/launchd.py +426 -0
  152. package/src/skcapstone/mcp_launcher.py +15 -1
  153. package/src/skcapstone/mcp_server.py +341 -49
  154. package/src/skcapstone/mcp_tools/__init__.py +2 -0
  155. package/src/skcapstone/mcp_tools/_helpers.py +2 -2
  156. package/src/skcapstone/mcp_tools/ansible_tools.py +7 -4
  157. package/src/skcapstone/mcp_tools/brain_first_tools.py +90 -0
  158. package/src/skcapstone/mcp_tools/capauth_tools.py +7 -4
  159. package/src/skcapstone/mcp_tools/comm_tools.py +10 -10
  160. package/src/skcapstone/mcp_tools/coord_tools.py +8 -4
  161. package/src/skcapstone/mcp_tools/did_tools.py +11 -8
  162. package/src/skcapstone/mcp_tools/gtd_tools.py +4 -4
  163. package/src/skcapstone/mcp_tools/memory_tools.py +6 -2
  164. package/src/skcapstone/mcp_tools/notification_tools.py +22 -6
  165. package/src/skcapstone/mcp_tools/{skcomm_tools.py → skcomms_tools.py} +14 -14
  166. package/src/skcapstone/mcp_tools/soul_tools.py +8 -2
  167. package/src/skcapstone/mdns_discovery.py +2 -2
  168. package/src/skcapstone/memory_curator.py +1 -1
  169. package/src/skcapstone/memory_engine.py +10 -3
  170. package/src/skcapstone/metrics.py +30 -16
  171. package/src/skcapstone/migrate_memories.py +4 -3
  172. package/src/skcapstone/migrate_multi_agent.py +8 -7
  173. package/src/skcapstone/models.py +47 -5
  174. package/src/skcapstone/notifications.py +42 -18
  175. package/src/skcapstone/onboard.py +1000 -126
  176. package/src/skcapstone/operator_link.py +170 -0
  177. package/src/skcapstone/peer_directory.py +4 -4
  178. package/src/skcapstone/peers.py +19 -19
  179. package/src/skcapstone/pillars/__init__.py +7 -5
  180. package/src/skcapstone/pillars/consciousness.py +191 -0
  181. package/src/skcapstone/pillars/identity.py +51 -7
  182. package/src/skcapstone/pillars/memory.py +9 -3
  183. package/src/skcapstone/pillars/sync.py +2 -2
  184. package/src/skcapstone/preflight.py +3 -3
  185. package/src/skcapstone/providers/docker.py +28 -28
  186. package/src/skcapstone/register.py +6 -6
  187. package/src/skcapstone/registry_client.py +5 -4
  188. package/src/skcapstone/runtime.py +14 -3
  189. package/src/skcapstone/scheduled_tasks.py +254 -19
  190. package/src/skcapstone/scheduler_jobs.py +456 -0
  191. package/src/skcapstone/scheduler_runner.py +239 -0
  192. package/src/skcapstone/scheduler_state.py +162 -0
  193. package/src/skcapstone/sdk.py +310 -0
  194. package/src/skcapstone/service_health.py +279 -39
  195. package/src/skcapstone/session_briefing.py +108 -0
  196. package/src/skcapstone/session_capture.py +1 -1
  197. package/src/skcapstone/shell.py +7 -1
  198. package/src/skcapstone/soul.py +3 -1
  199. package/src/skcapstone/soul_switch.py +3 -1
  200. package/src/skcapstone/summary.py +6 -6
  201. package/src/skcapstone/sync_engine.py +15 -15
  202. package/src/skcapstone/sync_watcher.py +2 -2
  203. package/src/skcapstone/systemd.py +72 -21
  204. package/src/skcapstone/team_comms.py +8 -8
  205. package/src/skcapstone/team_engine.py +1 -1
  206. package/src/skcapstone/testrunner.py +3 -3
  207. package/src/skcapstone/trust_graph.py +40 -5
  208. package/src/skcapstone/unified_search.py +15 -6
  209. package/src/skcapstone/uninstall_wizard.py +11 -3
  210. package/src/skcapstone/version_check.py +8 -4
  211. package/src/skcapstone/warmth_anchor.py +4 -2
  212. package/src/skcapstone/whoami.py +4 -4
  213. package/systemd/skcapstone.service +4 -6
  214. package/systemd/skcapstone@.service +7 -8
  215. package/systemd/skcomms-heartbeat.service +21 -0
  216. package/systemd/skcomms-heartbeat.timer +12 -0
  217. package/systemd/skcomms-queue-drain.service +17 -0
  218. package/systemd/skcomms-queue-drain.timer +12 -0
  219. package/tests/conftest.py +39 -0
  220. package/tests/integration/test_consciousness_e2e.py +39 -39
  221. package/tests/test_agent_card.py +1 -1
  222. package/tests/test_agent_home_scaffold.py +34 -0
  223. package/tests/test_alerts_consumer_topics.py +27 -0
  224. package/tests/test_backup.py +2 -1
  225. package/tests/test_chat.py +6 -6
  226. package/tests/test_claude_md.py +2 -2
  227. package/tests/test_cli_skills.py +10 -10
  228. package/tests/test_cli_test_cmd.py +4 -4
  229. package/tests/test_cli_test_connection.py +1 -1
  230. package/tests/test_cloud9_bridge.py +6 -6
  231. package/tests/test_consciousness_e2e.py +1 -1
  232. package/tests/test_consciousness_loop.py +10 -10
  233. package/tests/test_coordination.py +25 -0
  234. package/tests/test_cross_package.py +21 -21
  235. package/tests/test_daemon.py +4 -4
  236. package/tests/test_daemon_shutdown.py +1 -1
  237. package/tests/test_docker_provider.py +29 -29
  238. package/tests/test_doctor.py +400 -0
  239. package/tests/test_doctor_skscheduler.py +50 -0
  240. package/tests/test_dreaming_engine.py +147 -0
  241. package/tests/test_dreaming_gtd_capture.py +35 -0
  242. package/tests/test_e2e_automated.py +8 -5
  243. package/tests/test_fuse_mount.py +10 -10
  244. package/tests/test_gtd_brief.py +46 -0
  245. package/tests/test_gtd_malformed_tolerance.py +31 -0
  246. package/tests/test_housekeeping.py +15 -15
  247. package/tests/test_identity_migrate.py +251 -0
  248. package/tests/test_integration_backbone.py +598 -0
  249. package/tests/test_itil_gtd_lifecycle.py +37 -0
  250. package/tests/test_jobs_dropins.py +84 -0
  251. package/tests/test_mcp_server.py +82 -37
  252. package/tests/test_models.py +48 -4
  253. package/tests/test_multi_agent.py +31 -29
  254. package/tests/test_notifications.py +122 -32
  255. package/tests/test_onboard.py +63 -75
  256. package/tests/test_operator_link.py +78 -0
  257. package/tests/test_peers.py +14 -14
  258. package/tests/test_pillars.py +98 -0
  259. package/tests/test_preflight.py +3 -3
  260. package/tests/test_runtime.py +21 -0
  261. package/tests/test_scheduled_tasks.py +11 -6
  262. package/tests/test_scheduler_cli.py +47 -0
  263. package/tests/test_scheduler_features.py +133 -0
  264. package/tests/test_scheduler_integration.py +87 -0
  265. package/tests/test_scheduler_jobs.py +155 -0
  266. package/tests/test_scheduler_runner.py +64 -0
  267. package/tests/test_scheduler_state.py +57 -0
  268. package/tests/test_sdk.py +70 -0
  269. package/tests/test_service_health_incidents.py +34 -0
  270. package/tests/test_service_registry.py +52 -0
  271. package/tests/test_session_briefing.py +130 -0
  272. package/tests/test_snapshots.py +4 -4
  273. package/tests/test_sync_pipeline.py +26 -26
  274. package/tests/test_team_comms.py +2 -2
  275. package/tests/test_testrunner.py +2 -2
  276. package/tests/test_trust_graph.py +18 -0
  277. package/tests/test_unified_search.py +2 -2
  278. package/tests/test_version_check.py +10 -0
  279. package/tests/test_version_cmd.py +8 -8
  280. package/tests/test_whoami.py +1 -1
  281. package/systemd/skcomm-heartbeat.service +0 -18
  282. package/systemd/skcomm-queue-drain.service +0 -17
  283. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/package.json +0 -0
  284. /package/{openclaw-plugin → openclaw-plugin.archived-2026-04-23}/src/openclaw.plugin.json +0 -0
@@ -0,0 +1,90 @@
1
+ """Brain-First Protocol MCP tools.
2
+
3
+ Exposes the brain-first memory consultation as an MCP tool so that
4
+ any MCP client can ask "what do I already know about this?" before
5
+ acting on a task.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from mcp.types import TextContent, Tool
11
+
12
+ from ._helpers import _json_response
13
+
14
+ TOOLS: list[Tool] = [
15
+ Tool(
16
+ name="brain_first_check",
17
+ description=(
18
+ "Brain-First Protocol: consult the agent's memory before "
19
+ "acting on a task. Extracts keywords from the given context, "
20
+ "searches memory for relevant prior knowledge, and returns "
21
+ "any matching memories. Use this before starting new work to "
22
+ "avoid duplicating effort or missing prior decisions."
23
+ ),
24
+ inputSchema={
25
+ "type": "object",
26
+ "properties": {
27
+ "context": {
28
+ "type": "string",
29
+ "description": (
30
+ "The task description, prompt, or action context "
31
+ "to search memory for"
32
+ ),
33
+ },
34
+ "tags": {
35
+ "type": "array",
36
+ "items": {"type": "string"},
37
+ "description": "Optional tag filter for the memory search",
38
+ },
39
+ "max_results": {
40
+ "type": "integer",
41
+ "description": "Max memories to return (default: from config, usually 5)",
42
+ },
43
+ },
44
+ "required": ["context"],
45
+ },
46
+ ),
47
+ ]
48
+
49
+
50
+ async def _handle_brain_first_check(args: dict) -> list[TextContent]:
51
+ """Run a brain-first memory consultation."""
52
+ from ..brain_first import BrainFirstConfig, brain_first_check, _load_config
53
+
54
+ context = args.get("context", "")
55
+ if not context:
56
+ return _json_response({"error": "context is required"})
57
+
58
+ config = _load_config()
59
+
60
+ # Allow per-call override of max_results
61
+ max_results = args.get("max_results")
62
+ if max_results is not None:
63
+ config.max_results = max_results
64
+
65
+ result = brain_first_check(
66
+ context=context,
67
+ config=config,
68
+ tags=args.get("tags"),
69
+ )
70
+
71
+ response = {
72
+ "enabled": result.enabled,
73
+ "query": result.query,
74
+ "keywords": result.keywords,
75
+ "memories_found": len(result.memories),
76
+ "memories": result.memories,
77
+ }
78
+
79
+ if result.error:
80
+ response["warning"] = result.error
81
+
82
+ if result.has_memories:
83
+ response["context_block"] = result.as_context()
84
+
85
+ return _json_response(response)
86
+
87
+
88
+ HANDLERS: dict = {
89
+ "brain_first_check": _handle_brain_first_check,
90
+ }
@@ -8,11 +8,14 @@ Exposes two tools:
8
8
  from __future__ import annotations
9
9
 
10
10
  import json as _json
11
+ import logging
11
12
 
12
13
  from mcp.types import TextContent, Tool
13
14
 
14
15
  from ._helpers import _error_response, _home, _json_response
15
16
 
17
+ logger = logging.getLogger(__name__)
18
+
16
19
  TOOLS: list[Tool] = [
17
20
  Tool(
18
21
  name="capauth_status",
@@ -82,8 +85,8 @@ async def _handle_capauth_status(_args: dict) -> list[TextContent]:
82
85
  if did_key_file.exists():
83
86
  try:
84
87
  result["did_key_file"] = did_key_file.read_text(encoding="utf-8").strip()
85
- except Exception:
86
- pass
88
+ except Exception as exc:
89
+ logger.warning("Failed to read DID key file: %s", exc)
87
90
 
88
91
  # Check identity file
89
92
  identity_file = home / "identity" / "identity.json"
@@ -92,8 +95,8 @@ async def _handle_capauth_status(_args: dict) -> list[TextContent]:
92
95
  ident = _json.loads(identity_file.read_text(encoding="utf-8"))
93
96
  result["identity_name"] = ident.get("name")
94
97
  result["identity_fingerprint"] = ident.get("fingerprint")
95
- except Exception:
96
- pass
98
+ except Exception as exc:
99
+ logger.warning("Failed to read identity.json for capauth status: %s", exc)
97
100
 
98
101
  return _json_response(result)
99
102
 
@@ -1,4 +1,4 @@
1
- """SKComm send/receive messaging tools."""
1
+ """SKComms send/receive messaging tools."""
2
2
 
3
3
  from __future__ import annotations
4
4
 
@@ -10,7 +10,7 @@ TOOLS: list[Tool] = [
10
10
  Tool(
11
11
  name="send_message",
12
12
  description=(
13
- "Send a message to another agent via SKComm. "
13
+ "Send a message to another agent via SKComms. "
14
14
  "Routes through available transports (Syncthing, file)."
15
15
  ),
16
16
  inputSchema={
@@ -36,7 +36,7 @@ TOOLS: list[Tool] = [
36
36
  Tool(
37
37
  name="check_inbox",
38
38
  description=(
39
- "Check for new incoming messages across all SKComm transports. "
39
+ "Check for new incoming messages across all SKComms transports. "
40
40
  "Returns any unread message envelopes."
41
41
  ),
42
42
  inputSchema={"type": "object", "properties": {}, "required": []},
@@ -45,15 +45,15 @@ TOOLS: list[Tool] = [
45
45
 
46
46
 
47
47
  async def _handle_send_message(args: dict) -> list[TextContent]:
48
- """Send a message via SKComm."""
48
+ """Send a message via SKComms."""
49
49
  recipient = args.get("recipient", "")
50
50
  message = args.get("message", "")
51
51
  if not recipient or not message:
52
52
  return _error_response("recipient and message are required")
53
53
 
54
54
  try:
55
- from skcomm.core import SKComm
56
- comm = SKComm.from_config()
55
+ from skcomms.core import SKComms
56
+ comm = SKComms.from_config()
57
57
  report = comm.send(recipient, message)
58
58
  return _json_response({
59
59
  "sent": report.success,
@@ -68,7 +68,7 @@ async def _handle_send_message(args: dict) -> list[TextContent]:
68
68
  ],
69
69
  })
70
70
  except ImportError:
71
- return _error_response("SKComm not installed. Run: pip install skcomm")
71
+ return _error_response("SKComms not installed. Run: pip install skcomms")
72
72
  except Exception as exc:
73
73
  return _error_response(f"Send failed: {exc}")
74
74
 
@@ -76,8 +76,8 @@ async def _handle_send_message(args: dict) -> list[TextContent]:
76
76
  async def _handle_check_inbox(_args: dict) -> list[TextContent]:
77
77
  """Check for incoming messages."""
78
78
  try:
79
- from skcomm.core import SKComm
80
- comm = SKComm.from_config()
79
+ from skcomms.core import SKComms
80
+ comm = SKComms.from_config()
81
81
  envelopes = comm.receive()
82
82
  return _json_response([
83
83
  {
@@ -93,7 +93,7 @@ async def _handle_check_inbox(_args: dict) -> list[TextContent]:
93
93
  for e in envelopes
94
94
  ])
95
95
  except ImportError:
96
- return _error_response("SKComm not installed. Run: pip install skcomm")
96
+ return _error_response("SKComms not installed. Run: pip install skcomms")
97
97
  except Exception as exc:
98
98
  return _error_response(f"Inbox check failed: {exc}")
99
99
 
@@ -2,10 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
6
+
5
7
  from mcp.types import TextContent, Tool
6
8
 
7
9
  from ._helpers import _error_response, _home, _json_response, _shared_root
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
  TOOLS: list[Tool] = [
10
14
  Tool(
11
15
  name="coord_status",
@@ -149,8 +153,8 @@ async def _handle_coord_claim(args: dict) -> list[TextContent]:
149
153
  try:
150
154
  from .. import activity
151
155
  activity.push("task.claimed", {"task_id": task_id, "agent": agent_name})
152
- except Exception:
153
- pass
156
+ except Exception as exc:
157
+ logger.warning("Failed to push task.claimed activity for %s: %s", task_id, exc)
154
158
  return _json_response({
155
159
  "claimed": True,
156
160
  "task_id": task_id,
@@ -175,8 +179,8 @@ async def _handle_coord_complete(args: dict) -> list[TextContent]:
175
179
  try:
176
180
  from .. import activity
177
181
  activity.push("task.completed", {"task_id": task_id, "agent": agent_name})
178
- except Exception:
179
- pass
182
+ except Exception as exc:
183
+ logger.warning("Failed to push task.completed activity for %s: %s", task_id, exc)
180
184
  return _json_response({
181
185
  "completed": True,
182
186
  "task_id": task_id,
@@ -10,6 +10,7 @@ Exposes four tools:
10
10
  from __future__ import annotations
11
11
 
12
12
  import json as _json
13
+ import logging
13
14
  import os as _os
14
15
  import socket as _socket
15
16
  from pathlib import Path
@@ -18,6 +19,8 @@ from mcp.types import TextContent, Tool
18
19
 
19
20
  from ._helpers import _error_response, _home, _json_response
20
21
 
22
+ logger = logging.getLogger(__name__)
23
+
21
24
  TOOLS: list[Tool] = [
22
25
  Tool(
23
26
  name="did_show",
@@ -165,8 +168,8 @@ def _load_policy() -> dict:
165
168
  if p.exists():
166
169
  try:
167
170
  return _json.loads(p.read_text(encoding="utf-8"))
168
- except Exception:
169
- pass
171
+ except Exception as exc:
172
+ logger.warning("Failed to load DID policy from %s: %s", p, exc)
170
173
  return dict(_POLICY_DEFAULT)
171
174
 
172
175
 
@@ -191,8 +194,8 @@ def _resolve_tailnet(tailnet_hostname: str, tailnet_name: str) -> tuple[str, str
191
194
  if not tailnet_hostname:
192
195
  try:
193
196
  tailnet_hostname = _socket.gethostname()
194
- except Exception:
195
- pass
197
+ except Exception as exc:
198
+ logger.warning("Failed to resolve hostname for tailnet DID: %s", exc)
196
199
  if not tailnet_name:
197
200
  tailnet_name = _os.environ.get("SKWORLD_TAILNET", "")
198
201
  return tailnet_hostname, tailnet_name
@@ -297,8 +300,8 @@ async def _handle_did_verify_peer(args: dict) -> list[TextContent]:
297
300
  _json.dumps(peer_data, indent=2, default=str),
298
301
  encoding="utf-8",
299
302
  )
300
- except Exception:
301
- pass
303
+ except Exception as exc:
304
+ logger.warning("Failed to cache did:key back to peer file %s: %s", peer_file, exc)
302
305
 
303
306
  return _json_response({
304
307
  "name": name,
@@ -361,11 +364,11 @@ async def _handle_did_publish(args: dict) -> list[TextContent]:
361
364
  except Exception as exc:
362
365
  errors.append(f"{path}: {exc}")
363
366
 
364
- skcomm_home = Path(_os.environ.get("SKCOMM_HOME", str(Path.home() / ".skcomm")))
367
+ skcomms_home = Path(_os.environ.get("SKCOMMS_HOME", str(Path.home() / ".skcomms")))
365
368
  did_dir = _home() / "did"
366
369
 
367
370
  # Tier 2 (mesh) — always written; used by Tailscale Serve
368
- _write(skcomm_home / "well-known" / "did.json", _json.dumps(docs[DIDTier.WEB_MESH], indent=2))
371
+ _write(skcomms_home / "well-known" / "did.json", _json.dumps(docs[DIDTier.WEB_MESH], indent=2))
369
372
  # Tier 1 (did:key) — always written; self-contained anchor
370
373
  _write(did_dir / "key.json", _json.dumps(docs[DIDTier.KEY], indent=2))
371
374
  _write(did_dir / "did_key.txt", gen._ctx.did_key_id)
@@ -382,7 +382,7 @@ async def _handle_gtd_capture(args: dict) -> list[TextContent]:
382
382
  return _json_response({
383
383
  "captured": True,
384
384
  "id": item["id"],
385
- "text": item["text"],
385
+ "text": item.get("text") or item.get("title") or "",
386
386
  "source": item["source"],
387
387
  "privacy": item["privacy"],
388
388
  "context": item["context"],
@@ -498,7 +498,7 @@ async def _handle_gtd_clarify(args: dict) -> list[TextContent]:
498
498
  return _json_response({
499
499
  "clarified": True,
500
500
  "id": item["id"],
501
- "text": item["text"],
501
+ "text": item.get("text") or item.get("title") or "",
502
502
  "destination": dest_name,
503
503
  "status": item["status"],
504
504
  "priority": item.get("priority"),
@@ -550,7 +550,7 @@ async def _handle_gtd_move(args: dict) -> list[TextContent]:
550
550
  return _json_response({
551
551
  "moved": True,
552
552
  "id": item["id"],
553
- "text": item["text"],
553
+ "text": item.get("text") or item.get("title") or "",
554
554
  "from": source_list,
555
555
  "to": dest_name,
556
556
  "status": item["status"],
@@ -582,7 +582,7 @@ async def _handle_gtd_done(args: dict) -> list[TextContent]:
582
582
  return _json_response({
583
583
  "done": True,
584
584
  "id": item["id"],
585
- "text": item["text"],
585
+ "text": item.get("text") or item.get("title") or "",
586
586
  "from": source_list,
587
587
  "completed_at": item["completed_at"],
588
588
  "archive_count": len(archive),
@@ -2,10 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
6
+
5
7
  from mcp.types import TextContent, Tool
6
8
 
7
9
  from ._helpers import _error_response, _home, _json_response
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
  TOOLS: list[Tool] = [
10
14
  Tool(
11
15
  name="memory_store",
@@ -130,8 +134,8 @@ async def _handle_memory_store(args: dict) -> list[TextContent]:
130
134
  "importance": entry.importance,
131
135
  "tags": entry.tags,
132
136
  })
133
- except Exception:
134
- pass
137
+ except Exception as exc:
138
+ logger.warning("Failed to push memory.stored activity for %s: %s", entry.memory_id, exc)
135
139
  return _json_response({
136
140
  "memory_id": entry.memory_id,
137
141
  "layer": entry.layer.value,
@@ -54,10 +54,17 @@ async def _handle_send_notification(args: dict) -> list[TextContent]:
54
54
  if urgency not in {"low", "normal", "critical"}:
55
55
  return _error_response("urgency must be one of: low, normal, critical")
56
56
 
57
+ from ..notifications import desktop_notifications_enabled
58
+
59
+ if not desktop_notifications_enabled():
60
+ timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
61
+ return _json_response({"sent": False, "suppressed": True, "timestamp": timestamp})
62
+
57
63
  # Run notify-send in a subprocess (non-blocking).
58
64
  proc = await asyncio.create_subprocess_exec(
59
65
  "notify-send",
60
- "--urgency", urgency,
66
+ "--urgency",
67
+ urgency,
61
68
  title,
62
69
  body,
63
70
  stdout=asyncio.subprocess.DEVNULL,
@@ -71,16 +78,25 @@ async def _handle_send_notification(args: dict) -> list[TextContent]:
71
78
 
72
79
  timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat()
73
80
 
74
- # Log notification to skcomm/notifications/ (not memory/).
81
+ # Log notification to skcomms/notifications/ (not memory/).
75
82
  try:
76
83
  import json as _j
77
84
  import uuid
85
+
78
86
  home = _home()
79
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
80
- notif_dir = home / "agents" / agent_name / "skcomm" / "notifications"
87
+ from .. import active_agent_name
88
+
89
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
90
+ notif_dir = home / "agents" / agent_name / "skcomms" / "notifications"
81
91
  notif_dir.mkdir(parents=True, exist_ok=True)
82
- entry = {"id": uuid.uuid4().hex[:12], "type": "notification-sent",
83
- "title": title, "body": body, "urgency": urgency, "timestamp": timestamp}
92
+ entry = {
93
+ "id": uuid.uuid4().hex[:12],
94
+ "type": "notification-sent",
95
+ "title": title,
96
+ "body": body,
97
+ "urgency": urgency,
98
+ "timestamp": timestamp,
99
+ }
84
100
  (notif_dir / f"{entry['id']}.json").write_text(_j.dumps(entry, indent=2))
85
101
  except Exception:
86
102
  pass # notification log failure must not block the response
@@ -1,8 +1,8 @@
1
- """SKComm notification and status tools.
1
+ """SKComms notification and status tools.
2
2
 
3
3
  Exposes two tools:
4
- comm_notify — Send a notification via SKComm transports
5
- comm_status — Show SKComm subsystem status
4
+ comm_notify — Send a notification via SKComms transports
5
+ comm_status — Show SKComms subsystem status
6
6
  """
7
7
 
8
8
  from __future__ import annotations
@@ -15,7 +15,7 @@ TOOLS: list[Tool] = [
15
15
  Tool(
16
16
  name="comm_notify",
17
17
  description=(
18
- "Send a notification message via SKComm. Routes through "
18
+ "Send a notification message via SKComms. Routes through "
19
19
  "available transports (Syncthing, file, Tailscale). "
20
20
  "Supports urgency levels for priority routing."
21
21
  ),
@@ -46,7 +46,7 @@ TOOLS: list[Tool] = [
46
46
  Tool(
47
47
  name="comm_status",
48
48
  description=(
49
- "Show SKComm subsystem status: installed version, "
49
+ "Show SKComms subsystem status: installed version, "
50
50
  "available transports, connection state, and recent "
51
51
  "delivery statistics."
52
52
  ),
@@ -56,16 +56,16 @@ TOOLS: list[Tool] = [
56
56
 
57
57
 
58
58
  async def _handle_comm_notify(args: dict) -> list[TextContent]:
59
- """Send a notification via SKComm."""
59
+ """Send a notification via SKComms."""
60
60
  recipient = args.get("recipient", "")
61
61
  message = args.get("message", "")
62
62
  if not recipient or not message:
63
63
  return _error_response("recipient and message are required")
64
64
 
65
65
  try:
66
- from skcomm.core import SKComm # type: ignore[import]
66
+ from skcomms.core import SKComms # type: ignore[import]
67
67
 
68
- comm = SKComm.from_config()
68
+ comm = SKComms.from_config()
69
69
  report = comm.send(recipient, message)
70
70
  return _json_response({
71
71
  "sent": report.success,
@@ -81,29 +81,29 @@ async def _handle_comm_notify(args: dict) -> list[TextContent]:
81
81
  ],
82
82
  })
83
83
  except ImportError:
84
- return _error_response("SKComm not installed. Run: pip install skcomm")
84
+ return _error_response("SKComms not installed. Run: pip install skcomms")
85
85
  except Exception as exc:
86
86
  return _error_response(f"Notification send failed: {exc}")
87
87
 
88
88
 
89
89
  async def _handle_comm_status(_args: dict) -> list[TextContent]:
90
- """Show SKComm subsystem status."""
90
+ """Show SKComms subsystem status."""
91
91
  result: dict = {}
92
92
 
93
93
  try:
94
- import skcomm # type: ignore[import]
94
+ import skcomms # type: ignore[import]
95
95
 
96
96
  result["installed"] = True
97
- result["version"] = getattr(skcomm, "__version__", "unknown")
97
+ result["version"] = getattr(skcomms, "__version__", "unknown")
98
98
  except ImportError:
99
99
  result["installed"] = False
100
100
  result["version"] = None
101
101
  return _json_response(result)
102
102
 
103
103
  try:
104
- from skcomm.core import SKComm # type: ignore[import]
104
+ from skcomms.core import SKComms # type: ignore[import]
105
105
 
106
- comm = SKComm.from_config()
106
+ comm = SKComms.from_config()
107
107
  result["transports"] = [
108
108
  t.name for t in getattr(comm, "transports", [])
109
109
  ]
@@ -2,10 +2,14 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import logging
6
+
5
7
  from mcp.types import TextContent, Tool
6
8
 
7
9
  from ._helpers import _error_response, _home, _json_response, _text_response
8
10
 
11
+ logger = logging.getLogger(__name__)
12
+
9
13
  TOOLS: list[Tool] = [
10
14
  Tool(
11
15
  name="soul_list",
@@ -179,6 +183,8 @@ async def _handle_ritual(args: dict) -> list[TextContent]:
179
183
  "journal_entries": result.journal_entries,
180
184
  "germination_prompts": result.germination_prompts,
181
185
  "strongest_memories": result.strongest_memories,
186
+ "song_anchors_loaded": result.song_anchors_loaded,
187
+ "song_anchor_ids": result.song_anchor_ids,
182
188
  "context_prompt": result.context_prompt,
183
189
  })
184
190
  except ImportError:
@@ -250,8 +256,8 @@ async def _handle_soul_list(args: dict) -> list[TextContent]:
250
256
  "source": "installed",
251
257
  "active": name == state.active_soul,
252
258
  })
253
- except Exception:
254
- pass
259
+ except Exception as exc:
260
+ logger.warning("Failed to list installed soul blueprints: %s", exc)
255
261
 
256
262
  # 2) Blueprints repo
257
263
  blueprints_repo = Path.home() / "clawd" / "soul-blueprints" / "blueprints"
@@ -238,8 +238,8 @@ class MDNSDiscovery:
238
238
  agent_name,
239
239
  )
240
240
  return
241
- except Exception:
242
- pass
241
+ except Exception as exc:
242
+ logger.warning("Failed to read existing mDNS heartbeat for %s: %s", agent_name, exc)
243
243
 
244
244
  heartbeat = {
245
245
  "agent_name": agent_name,
@@ -59,7 +59,7 @@ _TAG_PATTERNS: list[tuple[re.Pattern, str]] = [
59
59
  (re.compile(r"\bcapauth\b", re.I), "capauth"),
60
60
  (re.compile(r"\bskcapstone\b", re.I), "skcapstone"),
61
61
  (re.compile(r"\bskmemory\b", re.I), "skmemory"),
62
- (re.compile(r"\bskcomm\b", re.I), "skcomm"),
62
+ (re.compile(r"\bskcomms\b", re.I), "skcomms"),
63
63
  (re.compile(r"\bskchat\b", re.I), "skchat"),
64
64
  (re.compile(r"\bsyncthing\b", re.I), "syncthing"),
65
65
  (re.compile(r"\bMCP\b", re.I), "mcp"),
@@ -25,6 +25,7 @@ from datetime import datetime, timezone
25
25
  from pathlib import Path
26
26
  from typing import Optional
27
27
 
28
+ from . import active_agent_name
28
29
  from .models import MemoryEntry, MemoryLayer, MemoryState, PillarStatus
29
30
 
30
31
  logger = logging.getLogger("skcapstone.memory")
@@ -48,9 +49,15 @@ def _get_unified():
48
49
 
49
50
  def _memory_dir(home: Path) -> Path:
50
51
  """Resolve the memory directory, creating it if needed."""
51
- # Use agent-specific memory directory if agent name is set
52
- agent_name = os.environ.get("SKCAPSTONE_AGENT", "lumina")
53
- mem = home / "agents" / agent_name / "memory"
52
+ # Accept either the shared root (~/.skcapstone) or an agent home
53
+ # (~/.skcapstone/agents/<agent>) and resolve to the active memory dir.
54
+ agent_name = os.environ.get("SKCAPSTONE_AGENT") or active_agent_name()
55
+ if home.parent.name == "agents":
56
+ mem = home / "memory"
57
+ elif agent_name:
58
+ mem = home / "agents" / agent_name / "memory"
59
+ else:
60
+ mem = home / "memory"
54
61
  mem.mkdir(parents=True, exist_ok=True)
55
62
  for layer in MemoryLayer:
56
63
  (mem / layer.value).mkdir(parents=True, exist_ok=True)