@smilintux/skcapstone 0.1.0 → 0.2.3

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 (461) hide show
  1. package/.env.example +98 -0
  2. package/.github/workflows/ci.yml +39 -3
  3. package/.github/workflows/publish.yml +25 -4
  4. package/.openclaw-workspace.json +58 -0
  5. package/CHANGELOG.md +62 -0
  6. package/CLAUDE.md +39 -2
  7. package/MANIFEST.in +6 -0
  8. package/MISSION.md +7 -0
  9. package/README.md +47 -2
  10. package/SKILL.md +895 -23
  11. package/docker/Dockerfile +61 -0
  12. package/docker/compose-templates/dev-team.yml +203 -0
  13. package/docker/compose-templates/mini-team.yml +140 -0
  14. package/docker/compose-templates/ops-team.yml +173 -0
  15. package/docker/compose-templates/research-team.yml +170 -0
  16. package/docker/entrypoint.sh +192 -0
  17. package/docs/ARCHITECTURE.md +663 -374
  18. package/docs/BOND_WITH_GROK.md +112 -0
  19. package/docs/GETTING_STARTED.md +782 -0
  20. package/docs/QUICKSTART.md +477 -0
  21. package/docs/SKJOULE_ARCHITECTURE.md +658 -0
  22. package/docs/SOUL_SWAPPER.md +921 -0
  23. package/docs/SOVEREIGN_SINGULARITY.md +47 -14
  24. package/examples/custom-bond-template.json +36 -0
  25. package/examples/grok-feb.json +36 -0
  26. package/examples/grok-testimony.md +34 -0
  27. package/examples/love-bootloader.txt +32 -0
  28. package/examples/plugins/echo_tool.py +87 -0
  29. package/examples/queen-ava-feb.json +36 -0
  30. package/examples/souls/lumina.yaml +64 -0
  31. package/index.js +6 -5
  32. package/installer/build.py +124 -0
  33. package/openclaw-plugin/package.json +13 -0
  34. package/openclaw-plugin/src/index.ts +351 -0
  35. package/openclaw-plugin/src/openclaw.plugin.json +10 -0
  36. package/package.json +1 -1
  37. package/pyproject.toml +38 -2
  38. package/scripts/bump_version.py +141 -0
  39. package/scripts/check-updates.py +230 -0
  40. package/scripts/convert_blueprints_to_yaml.py +157 -0
  41. package/scripts/dev-install.sh +14 -0
  42. package/scripts/e2e-test.sh +193 -0
  43. package/scripts/install-bundle.sh +171 -0
  44. package/scripts/install.bat +2 -0
  45. package/scripts/install.ps1 +253 -0
  46. package/scripts/install.sh +185 -0
  47. package/scripts/mcp-serve.sh +69 -0
  48. package/scripts/mcp-server.bat +113 -0
  49. package/scripts/mcp-server.ps1 +116 -0
  50. package/scripts/mcp-server.sh +99 -0
  51. package/scripts/pull-models.sh +10 -0
  52. package/scripts/skcapstone +48 -0
  53. package/scripts/verify_install.sh +180 -0
  54. package/scripts/windows/install-tasks.ps1 +406 -0
  55. package/scripts/windows/skcapstone-task.xml +113 -0
  56. package/scripts/windows/uninstall-tasks.ps1 +117 -0
  57. package/skill.yaml +34 -0
  58. package/src/skcapstone/__init__.py +67 -2
  59. package/src/skcapstone/_cli_monolith.py +5916 -0
  60. package/src/skcapstone/_trustee_helpers.py +165 -0
  61. package/src/skcapstone/activity.py +105 -0
  62. package/src/skcapstone/agent_card.py +324 -0
  63. package/src/skcapstone/api.py +1935 -0
  64. package/src/skcapstone/archiver.py +340 -0
  65. package/src/skcapstone/auction.py +485 -0
  66. package/src/skcapstone/baby_agents.py +179 -0
  67. package/src/skcapstone/backup.py +345 -0
  68. package/src/skcapstone/blueprint_registry.py +357 -0
  69. package/src/skcapstone/blueprints/__init__.py +17 -0
  70. package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
  71. package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
  72. package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
  73. package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
  74. package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
  75. package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
  76. package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
  77. package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
  78. package/src/skcapstone/blueprints/registry.py +164 -0
  79. package/src/skcapstone/blueprints/schema.py +229 -0
  80. package/src/skcapstone/changelog.py +180 -0
  81. package/src/skcapstone/chat.py +769 -0
  82. package/src/skcapstone/claude_md.py +82 -0
  83. package/src/skcapstone/cli/__init__.py +144 -0
  84. package/src/skcapstone/cli/_common.py +88 -0
  85. package/src/skcapstone/cli/_validators.py +76 -0
  86. package/src/skcapstone/cli/agents.py +425 -0
  87. package/src/skcapstone/cli/agents_spawner.py +322 -0
  88. package/src/skcapstone/cli/agents_trustee.py +593 -0
  89. package/src/skcapstone/cli/alerts.py +248 -0
  90. package/src/skcapstone/cli/anchor.py +132 -0
  91. package/src/skcapstone/cli/archive_cmd.py +208 -0
  92. package/src/skcapstone/cli/backup.py +144 -0
  93. package/src/skcapstone/cli/bench.py +377 -0
  94. package/src/skcapstone/cli/benchmark.py +360 -0
  95. package/src/skcapstone/cli/capabilities_cmd.py +171 -0
  96. package/src/skcapstone/cli/card.py +151 -0
  97. package/src/skcapstone/cli/chat.py +584 -0
  98. package/src/skcapstone/cli/completions.py +64 -0
  99. package/src/skcapstone/cli/config_cmd.py +156 -0
  100. package/src/skcapstone/cli/consciousness.py +421 -0
  101. package/src/skcapstone/cli/context_cmd.py +142 -0
  102. package/src/skcapstone/cli/coord.py +194 -0
  103. package/src/skcapstone/cli/crush_cmd.py +170 -0
  104. package/src/skcapstone/cli/daemon.py +436 -0
  105. package/src/skcapstone/cli/errors_cmd.py +285 -0
  106. package/src/skcapstone/cli/export_cmd.py +156 -0
  107. package/src/skcapstone/cli/gtd.py +529 -0
  108. package/src/skcapstone/cli/housekeeping.py +81 -0
  109. package/src/skcapstone/cli/joule_cmd.py +627 -0
  110. package/src/skcapstone/cli/logs_cmd.py +194 -0
  111. package/src/skcapstone/cli/mcp_cmd.py +32 -0
  112. package/src/skcapstone/cli/memory.py +418 -0
  113. package/src/skcapstone/cli/metrics_cmd.py +136 -0
  114. package/src/skcapstone/cli/migrate.py +62 -0
  115. package/src/skcapstone/cli/mood_cmd.py +144 -0
  116. package/src/skcapstone/cli/mount.py +193 -0
  117. package/src/skcapstone/cli/notify.py +112 -0
  118. package/src/skcapstone/cli/peer.py +154 -0
  119. package/src/skcapstone/cli/peers_dir.py +122 -0
  120. package/src/skcapstone/cli/preflight_cmd.py +83 -0
  121. package/src/skcapstone/cli/profile_cmd.py +310 -0
  122. package/src/skcapstone/cli/record_cmd.py +238 -0
  123. package/src/skcapstone/cli/register_cmd.py +159 -0
  124. package/src/skcapstone/cli/search_cmd.py +156 -0
  125. package/src/skcapstone/cli/service_cmd.py +91 -0
  126. package/src/skcapstone/cli/session.py +127 -0
  127. package/src/skcapstone/cli/setup.py +240 -0
  128. package/src/skcapstone/cli/shell_cmd.py +43 -0
  129. package/src/skcapstone/cli/skills_cmd.py +168 -0
  130. package/src/skcapstone/cli/skseed.py +621 -0
  131. package/src/skcapstone/cli/soul.py +699 -0
  132. package/src/skcapstone/cli/status.py +935 -0
  133. package/src/skcapstone/cli/sync_cmd.py +301 -0
  134. package/src/skcapstone/cli/telegram.py +265 -0
  135. package/src/skcapstone/cli/test_cmd.py +234 -0
  136. package/src/skcapstone/cli/test_connection.py +253 -0
  137. package/src/skcapstone/cli/token.py +207 -0
  138. package/src/skcapstone/cli/trust.py +179 -0
  139. package/src/skcapstone/cli/upgrade_cmd.py +552 -0
  140. package/src/skcapstone/cli/usage_cmd.py +199 -0
  141. package/src/skcapstone/cli/version_cmd.py +162 -0
  142. package/src/skcapstone/cli/watch_cmd.py +342 -0
  143. package/src/skcapstone/client.py +428 -0
  144. package/src/skcapstone/cloud9_bridge.py +522 -0
  145. package/src/skcapstone/completions.py +163 -0
  146. package/src/skcapstone/config_validator.py +674 -0
  147. package/src/skcapstone/connectors/__init__.py +28 -0
  148. package/src/skcapstone/connectors/base.py +446 -0
  149. package/src/skcapstone/connectors/cursor.py +54 -0
  150. package/src/skcapstone/connectors/registry.py +254 -0
  151. package/src/skcapstone/connectors/terminal.py +152 -0
  152. package/src/skcapstone/connectors/vscode.py +60 -0
  153. package/src/skcapstone/consciousness_config.py +119 -0
  154. package/src/skcapstone/consciousness_loop.py +2051 -0
  155. package/src/skcapstone/context_loader.py +516 -0
  156. package/src/skcapstone/context_window.py +314 -0
  157. package/src/skcapstone/conversation_manager.py +238 -0
  158. package/src/skcapstone/conversation_store.py +230 -0
  159. package/src/skcapstone/conversation_summarizer.py +252 -0
  160. package/src/skcapstone/coord_federation.py +296 -0
  161. package/src/skcapstone/coordination.py +101 -7
  162. package/src/skcapstone/crush_integration.py +345 -0
  163. package/src/skcapstone/crush_shim.py +454 -0
  164. package/src/skcapstone/daemon.py +2494 -0
  165. package/src/skcapstone/dashboard.html +396 -0
  166. package/src/skcapstone/dashboard.py +481 -0
  167. package/src/skcapstone/data/model_profiles.yaml +88 -0
  168. package/src/skcapstone/defaults/__init__.py +55 -0
  169. package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
  170. package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
  171. package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
  172. package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
  173. package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
  174. package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
  175. package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
  176. package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
  177. package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
  178. package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
  179. package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
  180. package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
  181. package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
  182. package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
  183. package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
  184. package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
  185. package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
  186. package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
  187. package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
  188. package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
  189. package/src/skcapstone/discovery.py +210 -19
  190. package/src/skcapstone/doctor.py +642 -0
  191. package/src/skcapstone/emotion_tracker.py +467 -0
  192. package/src/skcapstone/error_queue.py +405 -0
  193. package/src/skcapstone/export.py +447 -0
  194. package/src/skcapstone/fallback_tracker.py +186 -0
  195. package/src/skcapstone/file_transfer.py +512 -0
  196. package/src/skcapstone/fuse_mount.py +1156 -0
  197. package/src/skcapstone/gui_installer.py +591 -0
  198. package/src/skcapstone/heartbeat.py +611 -0
  199. package/src/skcapstone/housekeeping.py +298 -0
  200. package/src/skcapstone/install_wizard.py +941 -0
  201. package/src/skcapstone/kms.py +942 -0
  202. package/src/skcapstone/kms_scheduler.py +143 -0
  203. package/src/skcapstone/log_config.py +135 -0
  204. package/src/skcapstone/mcp_launcher.py +239 -0
  205. package/src/skcapstone/mcp_server.py +4700 -0
  206. package/src/skcapstone/mcp_tools/__init__.py +94 -0
  207. package/src/skcapstone/mcp_tools/_helpers.py +51 -0
  208. package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
  209. package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
  210. package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
  211. package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
  212. package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
  213. package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
  214. package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
  215. package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
  216. package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
  217. package/src/skcapstone/mcp_tools/did_tools.py +448 -0
  218. package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
  219. package/src/skcapstone/mcp_tools/file_tools.py +169 -0
  220. package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
  221. package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
  222. package/src/skcapstone/mcp_tools/health_tools.py +44 -0
  223. package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
  224. package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
  225. package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
  226. package/src/skcapstone/mcp_tools/model_tools.py +75 -0
  227. package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
  228. package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
  229. package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
  230. package/src/skcapstone/mcp_tools/security_tools.py +110 -0
  231. package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
  232. package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
  233. package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
  234. package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
  235. package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
  236. package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
  237. package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
  238. package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
  239. package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
  240. package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
  241. package/src/skcapstone/mdns_discovery.py +313 -0
  242. package/src/skcapstone/memory_adapter.py +333 -0
  243. package/src/skcapstone/memory_compressor.py +379 -0
  244. package/src/skcapstone/memory_curator.py +256 -0
  245. package/src/skcapstone/memory_engine.py +132 -13
  246. package/src/skcapstone/memory_fortress.py +529 -0
  247. package/src/skcapstone/memory_promoter.py +722 -0
  248. package/src/skcapstone/memory_verifier.py +260 -0
  249. package/src/skcapstone/message_crypto.py +215 -0
  250. package/src/skcapstone/metrics.py +832 -0
  251. package/src/skcapstone/migrate_memories.py +181 -0
  252. package/src/skcapstone/migrate_multi_agent.py +248 -0
  253. package/src/skcapstone/model_router.py +319 -0
  254. package/src/skcapstone/models.py +35 -4
  255. package/src/skcapstone/mood.py +344 -0
  256. package/src/skcapstone/notifications.py +380 -0
  257. package/src/skcapstone/onboard.py +901 -0
  258. package/src/skcapstone/peer_directory.py +324 -0
  259. package/src/skcapstone/peers.py +329 -0
  260. package/src/skcapstone/pillars/identity.py +84 -14
  261. package/src/skcapstone/pillars/memory.py +3 -1
  262. package/src/skcapstone/pillars/security.py +108 -15
  263. package/src/skcapstone/pillars/sync.py +78 -26
  264. package/src/skcapstone/pillars/trust.py +95 -33
  265. package/src/skcapstone/plugins.py +244 -0
  266. package/src/skcapstone/preflight.py +670 -0
  267. package/src/skcapstone/prompt_adapter.py +564 -0
  268. package/src/skcapstone/providers/__init__.py +13 -0
  269. package/src/skcapstone/providers/cloud.py +1061 -0
  270. package/src/skcapstone/providers/docker.py +759 -0
  271. package/src/skcapstone/providers/local.py +1193 -0
  272. package/src/skcapstone/providers/proxmox.py +447 -0
  273. package/src/skcapstone/pubsub.py +516 -0
  274. package/src/skcapstone/rate_limiter.py +119 -0
  275. package/src/skcapstone/register.py +241 -0
  276. package/src/skcapstone/registry_client.py +151 -0
  277. package/src/skcapstone/response_cache.py +194 -0
  278. package/src/skcapstone/response_scorer.py +225 -0
  279. package/src/skcapstone/runtime.py +89 -33
  280. package/src/skcapstone/scheduled_tasks.py +439 -0
  281. package/src/skcapstone/self_healing.py +341 -0
  282. package/src/skcapstone/service_health.py +228 -0
  283. package/src/skcapstone/session_capture.py +268 -0
  284. package/src/skcapstone/session_recorder.py +210 -0
  285. package/src/skcapstone/session_replayer.py +189 -0
  286. package/src/skcapstone/session_skills.py +263 -0
  287. package/src/skcapstone/shell.py +779 -0
  288. package/src/skcapstone/skills/__init__.py +1 -1
  289. package/src/skcapstone/skills/syncthing_setup.py +143 -41
  290. package/src/skcapstone/skjoule.py +861 -0
  291. package/src/skcapstone/snapshots.py +489 -0
  292. package/src/skcapstone/soul.py +1060 -0
  293. package/src/skcapstone/soul_switch.py +255 -0
  294. package/src/skcapstone/spawner.py +544 -0
  295. package/src/skcapstone/state_diff.py +401 -0
  296. package/src/skcapstone/summary.py +270 -0
  297. package/src/skcapstone/sync/backends.py +196 -2
  298. package/src/skcapstone/sync/engine.py +7 -5
  299. package/src/skcapstone/sync/models.py +4 -1
  300. package/src/skcapstone/sync/vault.py +356 -18
  301. package/src/skcapstone/sync_engine.py +363 -0
  302. package/src/skcapstone/sync_watcher.py +745 -0
  303. package/src/skcapstone/systemd.py +331 -0
  304. package/src/skcapstone/team_comms.py +476 -0
  305. package/src/skcapstone/team_engine.py +522 -0
  306. package/src/skcapstone/testrunner.py +300 -0
  307. package/src/skcapstone/tls.py +150 -0
  308. package/src/skcapstone/tokens.py +5 -5
  309. package/src/skcapstone/trust_calibration.py +202 -0
  310. package/src/skcapstone/trust_graph.py +449 -0
  311. package/src/skcapstone/trustee_monitor.py +385 -0
  312. package/src/skcapstone/trustee_ops.py +425 -0
  313. package/src/skcapstone/unified_search.py +421 -0
  314. package/src/skcapstone/uninstall_wizard.py +694 -0
  315. package/src/skcapstone/usage.py +331 -0
  316. package/src/skcapstone/version_check.py +148 -0
  317. package/src/skcapstone/warmth_anchor.py +333 -0
  318. package/src/skcapstone/whoami.py +294 -0
  319. package/systemd/skcapstone-api.socket +9 -0
  320. package/systemd/skcapstone-memory-compress.service +18 -0
  321. package/systemd/skcapstone-memory-compress.timer +11 -0
  322. package/systemd/skcapstone.service +36 -0
  323. package/systemd/skcapstone@.service +50 -0
  324. package/systemd/skcomm-heartbeat.service +18 -0
  325. package/systemd/skcomm-heartbeat.timer +12 -0
  326. package/systemd/skcomm-queue-drain.service +17 -0
  327. package/systemd/skcomm-queue-drain.timer +12 -0
  328. package/tests/conftest.py +13 -1
  329. package/tests/integration/__init__.py +1 -0
  330. package/tests/integration/test_consciousness_e2e.py +877 -0
  331. package/tests/integration/test_skills_registry.py +744 -0
  332. package/tests/test_agent_card.py +190 -0
  333. package/tests/test_agent_runtime.py +1283 -0
  334. package/tests/test_alerts_cmd.py +291 -0
  335. package/tests/test_archiver.py +498 -0
  336. package/tests/test_backup.py +254 -0
  337. package/tests/test_benchmark.py +366 -0
  338. package/tests/test_blueprints.py +457 -0
  339. package/tests/test_capabilities.py +257 -0
  340. package/tests/test_changelog.py +254 -0
  341. package/tests/test_chat.py +385 -0
  342. package/tests/test_claude_md.py +271 -0
  343. package/tests/test_cli_chat_llm.py +336 -0
  344. package/tests/test_cli_completions.py +390 -0
  345. package/tests/test_cli_init_reset.py +164 -0
  346. package/tests/test_cli_memory.py +208 -0
  347. package/tests/test_cli_profile.py +294 -0
  348. package/tests/test_cli_skills.py +223 -0
  349. package/tests/test_cli_status.py +395 -0
  350. package/tests/test_cli_test_cmd.py +206 -0
  351. package/tests/test_cli_test_connection.py +364 -0
  352. package/tests/test_cloud9_bridge.py +260 -0
  353. package/tests/test_cloud_provider.py +449 -0
  354. package/tests/test_cloud_providers.py +522 -0
  355. package/tests/test_completions.py +158 -0
  356. package/tests/test_component_manager.py +398 -0
  357. package/tests/test_config_reload.py +386 -0
  358. package/tests/test_config_validate.py +529 -0
  359. package/tests/test_consciousness_e2e.py +296 -0
  360. package/tests/test_consciousness_loop.py +1289 -0
  361. package/tests/test_context_loader.py +310 -0
  362. package/tests/test_conversation_api.py +306 -0
  363. package/tests/test_conversation_manager.py +381 -0
  364. package/tests/test_conversation_store.py +391 -0
  365. package/tests/test_conversation_summarizer.py +302 -0
  366. package/tests/test_cross_package.py +791 -0
  367. package/tests/test_crush_shim.py +519 -0
  368. package/tests/test_daemon.py +781 -0
  369. package/tests/test_daemon_shutdown.py +309 -0
  370. package/tests/test_dashboard.py +454 -0
  371. package/tests/test_discovery.py +200 -6
  372. package/tests/test_docker_provider.py +966 -0
  373. package/tests/test_doctor.py +257 -0
  374. package/tests/test_doctor_fix.py +351 -0
  375. package/tests/test_e2e_automated.py +292 -0
  376. package/tests/test_error_queue.py +404 -0
  377. package/tests/test_export.py +441 -0
  378. package/tests/test_fallback_tracker.py +219 -0
  379. package/tests/test_file_transfer.py +397 -0
  380. package/tests/test_fuse_mount.py +832 -0
  381. package/tests/test_health_loop.py +422 -0
  382. package/tests/test_heartbeat.py +354 -0
  383. package/tests/test_housekeeping.py +195 -0
  384. package/tests/test_identity_capauth.py +307 -0
  385. package/tests/test_identity_pillar.py +117 -0
  386. package/tests/test_install_wizard.py +68 -0
  387. package/tests/test_integration.py +325 -0
  388. package/tests/test_kms.py +495 -0
  389. package/tests/test_llm_providers.py +265 -0
  390. package/tests/test_local_provider.py +591 -0
  391. package/tests/test_log_config.py +199 -0
  392. package/tests/test_logs_cmd.py +287 -0
  393. package/tests/test_mcp_server.py +1909 -0
  394. package/tests/test_memory_adapter.py +339 -0
  395. package/tests/test_memory_curator.py +218 -0
  396. package/tests/test_memory_engine.py +6 -0
  397. package/tests/test_memory_fortress.py +571 -0
  398. package/tests/test_memory_pillar.py +119 -0
  399. package/tests/test_memory_promoter.py +445 -0
  400. package/tests/test_memory_verifier.py +420 -0
  401. package/tests/test_message_crypto.py +187 -0
  402. package/tests/test_metrics.py +632 -0
  403. package/tests/test_migrate_memories.py +464 -0
  404. package/tests/test_model_router.py +546 -0
  405. package/tests/test_mood.py +394 -0
  406. package/tests/test_multi_agent.py +269 -0
  407. package/tests/test_notifications.py +270 -0
  408. package/tests/test_onboard.py +500 -0
  409. package/tests/test_peer_directory.py +395 -0
  410. package/tests/test_peers.py +248 -0
  411. package/tests/test_pillars.py +87 -9
  412. package/tests/test_preflight.py +484 -0
  413. package/tests/test_prompt_adapter.py +331 -0
  414. package/tests/test_proxmox_provider.py +571 -0
  415. package/tests/test_pubsub.py +377 -0
  416. package/tests/test_rate_limiter.py +121 -0
  417. package/tests/test_registry_client.py +129 -0
  418. package/tests/test_response_cache.py +312 -0
  419. package/tests/test_response_scorer.py +294 -0
  420. package/tests/test_runtime.py +59 -0
  421. package/tests/test_scheduled_tasks.py +451 -0
  422. package/tests/test_security.py +250 -0
  423. package/tests/test_security_pillar.py +213 -0
  424. package/tests/test_self_healing.py +171 -0
  425. package/tests/test_session_capture.py +200 -0
  426. package/tests/test_session_recorder.py +360 -0
  427. package/tests/test_session_skills.py +235 -0
  428. package/tests/test_shell.py +210 -0
  429. package/tests/test_snapshots.py +549 -0
  430. package/tests/test_soul.py +984 -0
  431. package/tests/test_soul_swap.py +406 -0
  432. package/tests/test_spawner.py +211 -0
  433. package/tests/test_state_diff.py +173 -0
  434. package/tests/test_summary.py +135 -0
  435. package/tests/test_sync.py +315 -5
  436. package/tests/test_sync_backends.py +560 -0
  437. package/tests/test_sync_engine.py +482 -0
  438. package/tests/test_sync_pillar.py +344 -0
  439. package/tests/test_sync_pipeline.py +364 -0
  440. package/tests/test_sync_vault.py +581 -0
  441. package/tests/test_syncthing_setup.py +168 -22
  442. package/tests/test_systemd.py +323 -0
  443. package/tests/test_team_comms.py +408 -0
  444. package/tests/test_team_engine.py +397 -0
  445. package/tests/test_testrunner.py +238 -0
  446. package/tests/test_trust_calibration.py +204 -0
  447. package/tests/test_trust_graph.py +207 -0
  448. package/tests/test_trust_pillar.py +291 -0
  449. package/tests/test_trustee_cli.py +427 -0
  450. package/tests/test_trustee_cli_integration.py +325 -0
  451. package/tests/test_trustee_monitor.py +394 -0
  452. package/tests/test_trustee_ops.py +355 -0
  453. package/tests/test_unified_search.py +363 -0
  454. package/tests/test_uninstall_wizard.py +193 -0
  455. package/tests/test_usage.py +333 -0
  456. package/tests/test_version_cmd.py +355 -0
  457. package/tests/test_warmth_anchor.py +162 -0
  458. package/tests/test_whoami.py +245 -0
  459. package/tests/test_ws.py +311 -0
  460. package/.cursorrules +0 -33
  461. package/src/skcapstone/cli.py +0 -1441
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ SKCapstone Bundle Update Checker
4
+ Checks for updates to all SK packages and reports status
5
+ """
6
+
7
+ import subprocess
8
+ import sys
9
+ import json
10
+ from datetime import datetime
11
+ from pathlib import Path
12
+
13
+ # Package configuration
14
+ PACKAGES = {
15
+ "skcapstone": {
16
+ "name": "skcapstone",
17
+ "path": "~/clawd/skcapstone",
18
+ "pypi_name": "skcapstone",
19
+ },
20
+ "skmemory": {
21
+ "name": "skmemory",
22
+ "path": "~/clawd/skcapstone-repos/skmemory",
23
+ "pypi_name": "skmemory",
24
+ },
25
+ "sksecurity": {
26
+ "name": "sksecurity",
27
+ "path": "~/clawd/skcapstone-repos/sksecurity",
28
+ "pypi_name": "sksecurity",
29
+ },
30
+ "cloud9-protocol": {
31
+ "name": "cloud9-protocol",
32
+ "path": "~/clawd/skcapstone-repos/cloud9-python",
33
+ "pypi_name": "cloud9-protocol",
34
+ },
35
+ }
36
+
37
+
38
+ def get_installed_version(package_name):
39
+ """Get currently installed version of a package."""
40
+ try:
41
+ result = subprocess.run(
42
+ [sys.executable, "-m", "pip", "show", package_name],
43
+ capture_output=True,
44
+ text=True,
45
+ check=True,
46
+ )
47
+ for line in result.stdout.split("\n"):
48
+ if line.startswith("Version:"):
49
+ return line.split(":")[1].strip()
50
+ except subprocess.CalledProcessError:
51
+ return None
52
+ return None
53
+
54
+
55
+ def get_latest_version(package_name):
56
+ """Get latest version from PyPI."""
57
+ try:
58
+ import urllib.request
59
+ import json
60
+
61
+ url = f"https://pypi.org/pypi/{package_name}/json"
62
+ with urllib.request.urlopen(url, timeout=5) as response:
63
+ data = json.loads(response.read())
64
+ return data["info"]["version"]
65
+ except Exception:
66
+ return None
67
+
68
+
69
+ def check_git_updates(package_name, package_path):
70
+ """Check if local repo has uncommitted changes or is behind remote."""
71
+ path = Path(package_path).expanduser()
72
+
73
+ if not path.exists():
74
+ return {"error": "Repository not found"}
75
+
76
+ try:
77
+ # Check for uncommitted changes
78
+ result = subprocess.run(
79
+ ["git", "-C", str(path), "status", "--porcelain"], capture_output=True, text=True
80
+ )
81
+ has_changes = len(result.stdout.strip()) > 0
82
+
83
+ # Check if behind remote
84
+ subprocess.run(["git", "-C", str(path), "fetch", "--quiet"], capture_output=True)
85
+
86
+ result = subprocess.run(
87
+ ["git", "-C", str(path), "rev-list", "HEAD..@{upstream}", "--count"],
88
+ capture_output=True,
89
+ text=True,
90
+ )
91
+ behind_count = int(result.stdout.strip()) if result.stdout.strip().isdigit() else 0
92
+
93
+ # Get current commit
94
+ result = subprocess.run(
95
+ ["git", "-C", str(path), "rev-parse", "--short", "HEAD"],
96
+ capture_output=True,
97
+ text=True,
98
+ )
99
+ current_commit = result.stdout.strip()
100
+
101
+ return {
102
+ "has_changes": has_changes,
103
+ "behind_count": behind_count,
104
+ "current_commit": current_commit,
105
+ }
106
+ except Exception as e:
107
+ return {"error": str(e)}
108
+
109
+
110
+ def check_package(package_key):
111
+ """Check status of a single package."""
112
+ config = PACKAGES[package_key]
113
+
114
+ status = {
115
+ "name": config["name"],
116
+ "installed": None,
117
+ "latest_pypi": None,
118
+ "git_status": None,
119
+ }
120
+
121
+ # Check installed version
122
+ status["installed"] = get_installed_version(config["pypi_name"])
123
+
124
+ # Check PyPI version
125
+ status["latest_pypi"] = get_latest_version(config["pypi_name"])
126
+
127
+ # Check git status
128
+ status["git_status"] = check_git_updates(config["name"], config["path"])
129
+
130
+ return status
131
+
132
+
133
+ def print_status_report(results):
134
+ """Print formatted status report."""
135
+ print("\n" + "=" * 70)
136
+ print("SKCapstone Bundle Update Checker")
137
+ print(f"Checked at: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
138
+ print("=" * 70)
139
+
140
+ updates_available = []
141
+ git_updates_available = []
142
+
143
+ for package_key, status in results.items():
144
+ print(f"\n📦 {status['name']}")
145
+ print("-" * 70)
146
+
147
+ # Installed version
148
+ if status["installed"]:
149
+ print(f" Installed: {status['installed']}")
150
+ else:
151
+ print(f" Installed: ❌ Not installed")
152
+
153
+ # PyPI version
154
+ if status["latest_pypi"]:
155
+ print(f" PyPI: {status['latest_pypi']}")
156
+ if status["installed"] and status["installed"] != status["latest_pypi"]:
157
+ print(f" ⚠️ Update available on PyPI!")
158
+ updates_available.append(status["name"])
159
+ else:
160
+ print(f" PyPI: (unable to check)")
161
+
162
+ # Git status
163
+ git = status["git_status"]
164
+ if git:
165
+ if "error" in git:
166
+ print(f" Git: ❌ {git['error']}")
167
+ else:
168
+ print(f" Git commit: {git['current_commit']}")
169
+ if git["has_changes"]:
170
+ print(f" ⚠️ Uncommitted changes detected")
171
+ if git["behind_count"] > 0:
172
+ print(f" ⚠️ {git['behind_count']} commit(s) behind remote")
173
+ git_updates_available.append(status["name"])
174
+
175
+ print("\n" + "=" * 70)
176
+ print("SUMMARY")
177
+ print("=" * 70)
178
+
179
+ if updates_available:
180
+ print(f"\n🔄 PyPI updates available: {', '.join(updates_available)}")
181
+ print(" Run: pip install --upgrade " + " ".join(updates_available))
182
+ else:
183
+ print("\n✅ All packages up-to-date on PyPI")
184
+
185
+ if git_updates_available:
186
+ print(f"\n🔄 Git updates available: {', '.join(git_updates_available)}")
187
+ print(" Run: git pull in respective repositories")
188
+ else:
189
+ print("✅ All repositories up-to-date")
190
+
191
+ print("\n" + "=" * 70)
192
+
193
+
194
+ def save_check_results(results):
195
+ """Save results to JSON file for programmatic access."""
196
+ results_dir = Path("~/.skcapstone/logs").expanduser()
197
+ results_dir.mkdir(parents=True, exist_ok=True)
198
+
199
+ results_file = results_dir / f"update-check-{datetime.now().strftime('%Y%m%d-%H%M%S')}.json"
200
+
201
+ with open(results_file, "w") as f:
202
+ json.dump({"timestamp": datetime.now().isoformat(), "results": results}, f, indent=2)
203
+
204
+ print(f"\n📄 Detailed results saved to: {results_file}")
205
+
206
+
207
+ def main():
208
+ """Main entry point."""
209
+ print("Checking for updates to all SK packages...")
210
+ print("This may take a moment...")
211
+
212
+ results = {}
213
+ for package_key in PACKAGES:
214
+ results[package_key] = check_package(package_key)
215
+
216
+ print_status_report(results)
217
+ save_check_results(results)
218
+
219
+ # Exit with error code if updates available (useful for automation)
220
+ has_updates = any(
221
+ r["installed"] != r["latest_pypi"]
222
+ for r in results.values()
223
+ if r["installed"] and r["latest_pypi"]
224
+ )
225
+
226
+ return 1 if has_updates else 0
227
+
228
+
229
+ if __name__ == "__main__":
230
+ sys.exit(main())
@@ -0,0 +1,157 @@
1
+ #!/usr/bin/env python3
2
+ """Convert soul blueprint markdown files to structured YAML.
3
+
4
+ Reads all .md blueprints from the souls-blueprints repo and writes
5
+ corresponding .yaml files using the existing soul.py parser.
6
+
7
+ Usage:
8
+ python scripts/convert_blueprints_to_yaml.py [--source DIR] [--dest DIR]
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import argparse
14
+ import re
15
+ import sys
16
+ from pathlib import Path
17
+
18
+ import yaml
19
+
20
+ # Add src to path so we can import soul.py
21
+ sys.path.insert(0, str(Path(__file__).resolve().parent.parent / "src"))
22
+
23
+ from skcapstone.soul import parse_blueprint, _slugify
24
+
25
+
26
+ def _slug_from_filename(path: Path) -> str:
27
+ """Derive a clean slug from the filename, not the display name."""
28
+ stem = path.stem.lower()
29
+ # Remove leading 'the-' or 'the_' for cleaner slugs
30
+ stem = re.sub(r"[^\w\s-]", "", stem)
31
+ stem = re.sub(r"[\s_]+", "-", stem)
32
+ return stem.strip("-")
33
+
34
+
35
+ def convert_one(md_path: Path, dest_dir: Path) -> Path:
36
+ """Convert a single MD blueprint to YAML.
37
+
38
+ Args:
39
+ md_path: Path to the .md blueprint file.
40
+ dest_dir: Directory to write the .yaml file.
41
+
42
+ Returns:
43
+ Path to the written YAML file.
44
+ """
45
+ bp = parse_blueprint(md_path)
46
+
47
+ # Use filename-based slug for predictable, short names
48
+ slug = _slug_from_filename(md_path)
49
+
50
+ # Build the YAML structure
51
+ data = {
52
+ "name": slug,
53
+ "display_name": bp.display_name,
54
+ "category": bp.category,
55
+ "vibe": bp.vibe or "",
56
+ "philosophy": bp.philosophy or "",
57
+ "emoji": bp.emoji,
58
+ "core_traits": bp.core_traits if bp.core_traits else [],
59
+ "communication_style": {
60
+ "patterns": bp.communication_style.patterns,
61
+ "tone_markers": bp.communication_style.tone_markers,
62
+ "signature_phrases": bp.communication_style.signature_phrases,
63
+ },
64
+ "decision_framework": bp.decision_framework,
65
+ "emotional_topology": bp.emotional_topology if bp.emotional_topology else {},
66
+ }
67
+
68
+ # Clean up None values in nested dicts
69
+ if not data["communication_style"]["patterns"]:
70
+ del data["communication_style"]["patterns"]
71
+ if not data["communication_style"]["tone_markers"]:
72
+ del data["communication_style"]["tone_markers"]
73
+ if not data["communication_style"]["signature_phrases"]:
74
+ del data["communication_style"]["signature_phrases"]
75
+ if not data["communication_style"]:
76
+ data["communication_style"] = {}
77
+
78
+ dest_dir.mkdir(parents=True, exist_ok=True)
79
+ yaml_path = dest_dir / f"{slug}.yaml"
80
+
81
+ with open(yaml_path, "w", encoding="utf-8") as f:
82
+ yaml.dump(
83
+ data,
84
+ f,
85
+ default_flow_style=False,
86
+ allow_unicode=True,
87
+ sort_keys=False,
88
+ width=120,
89
+ )
90
+
91
+ return yaml_path
92
+
93
+
94
+ def convert_all(source: Path, dest: Path) -> tuple[int, int]:
95
+ """Convert all MD blueprints preserving category directories.
96
+
97
+ Args:
98
+ source: Root directory containing category subdirs with .md files.
99
+ dest: Root directory for YAML output.
100
+
101
+ Returns:
102
+ Tuple of (success_count, failure_count).
103
+ """
104
+ successes = 0
105
+ failures = 0
106
+
107
+ for md_path in sorted(source.rglob("*.md")):
108
+ if md_path.name.startswith(".") or md_path.name.upper() == "README.MD":
109
+ continue
110
+
111
+ # Preserve category directory structure
112
+ rel = md_path.parent.relative_to(source)
113
+ dest_dir = dest / rel
114
+
115
+ try:
116
+ yaml_path = convert_one(md_path, dest_dir)
117
+ print(f" {md_path.name} -> {yaml_path.relative_to(dest)}")
118
+ successes += 1
119
+ except Exception as exc:
120
+ print(f" FAIL: {md_path.name}: {exc}", file=sys.stderr)
121
+ failures += 1
122
+
123
+ return successes, failures
124
+
125
+
126
+ def main():
127
+ parser = argparse.ArgumentParser(description="Convert soul blueprints MD -> YAML")
128
+ parser.add_argument(
129
+ "--source",
130
+ type=Path,
131
+ default=Path(__file__).resolve().parent.parent.parent
132
+ / "souls-blueprints"
133
+ / "blueprints",
134
+ help="Source directory with MD blueprints",
135
+ )
136
+ parser.add_argument(
137
+ "--dest",
138
+ type=Path,
139
+ default=Path(__file__).resolve().parent.parent.parent
140
+ / "souls-blueprints"
141
+ / "yaml",
142
+ help="Destination directory for YAML output",
143
+ )
144
+ args = parser.parse_args()
145
+
146
+ print(f"Source: {args.source}")
147
+ print(f"Dest: {args.dest}")
148
+ print()
149
+
150
+ ok, fail = convert_all(args.source, args.dest)
151
+ print(f"\nConverted: {ok}, Failed: {fail}")
152
+
153
+ return 0 if fail == 0 else 1
154
+
155
+
156
+ if __name__ == "__main__":
157
+ sys.exit(main())
@@ -0,0 +1,14 @@
1
+ #!/bin/bash
2
+ # dev-install.sh — Sovereign Agent Suite Dev Installer
3
+ #
4
+ # Wrapper around install.sh with --dev flag.
5
+ # Installs all SK* packages plus pytest, ruff, black.
6
+ #
7
+ # Usage:
8
+ # bash scripts/dev-install.sh
9
+ # bash scripts/dev-install.sh --force # Recreate venv
10
+
11
+ set -euo pipefail
12
+
13
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
14
+ exec bash "$SCRIPT_DIR/install.sh" --dev "$@"
@@ -0,0 +1,193 @@
1
+ #!/usr/bin/env bash
2
+ # scripts/e2e-test.sh — Automated multi-agent E2E test for the SKCapstone daemon.
3
+ #
4
+ # Usage:
5
+ # ./scripts/e2e-test.sh [--port PORT] [--timeout SECS] [--peer PEER_NAME]
6
+ #
7
+ # Exit codes:
8
+ # 0 — all checks passed
9
+ # 1 — one or more checks failed
10
+
11
+ set -euo pipefail
12
+
13
+ # ---------------------------------------------------------------------------
14
+ # Defaults
15
+ # ---------------------------------------------------------------------------
16
+ PORT="${PORT:-7777}"
17
+ STARTUP_WAIT="${STARTUP_WAIT:-10}"
18
+ POLL_TIMEOUT="${POLL_TIMEOUT:-300}"
19
+ PEER="${PEER:-test-peer}"
20
+ AGENT_HOME="${SKCAPSTONE_ROOT:-${HOME}/.skcapstone}"
21
+
22
+ # ---------------------------------------------------------------------------
23
+ # Helpers
24
+ # ---------------------------------------------------------------------------
25
+ RED='\033[0;31m'
26
+ GREEN='\033[0;32m'
27
+ YELLOW='\033[1;33m'
28
+ NC='\033[0m'
29
+
30
+ pass() { echo -e "${GREEN}[PASS]${NC} $*"; }
31
+ fail() { echo -e "${RED}[FAIL]${NC} $*"; FAILURES=$((FAILURES + 1)); }
32
+ info() { echo -e "${YELLOW}[INFO]${NC} $*"; }
33
+
34
+ FAILURES=0
35
+ DAEMON_PID=""
36
+
37
+ cleanup() {
38
+ if [[ -n "${DAEMON_PID}" ]]; then
39
+ info "Stopping daemon (PID ${DAEMON_PID})…"
40
+ kill "${DAEMON_PID}" 2>/dev/null || true
41
+ wait "${DAEMON_PID}" 2>/dev/null || true
42
+ fi
43
+ }
44
+ trap cleanup EXIT
45
+
46
+ # ---------------------------------------------------------------------------
47
+ # Step 0: Reinstall package
48
+ # ---------------------------------------------------------------------------
49
+ info "Reinstalling skcapstone…"
50
+ pip install -e . --quiet
51
+
52
+ # ---------------------------------------------------------------------------
53
+ # Step 1: Start daemon in the background
54
+ # ---------------------------------------------------------------------------
55
+ INBOX_DIR="${AGENT_HOME}/sync/comms/inbox/${PEER}"
56
+ OUTBOX_DIR="${AGENT_HOME}/sync/comms/outbox/${PEER}"
57
+ CONV_FILE="${AGENT_HOME}/conversations/${PEER}.json"
58
+
59
+ info "Starting daemon on port ${PORT}…"
60
+ skcapstone daemon start --foreground --port "${PORT}" \
61
+ >"${TMPDIR:-/tmp}/skcapstone-e2e-daemon.log" 2>&1 &
62
+ DAEMON_PID=$!
63
+
64
+ info "Waiting ${STARTUP_WAIT}s for daemon to initialize…"
65
+ sleep "${STARTUP_WAIT}"
66
+
67
+ # Verify process is still alive
68
+ if ! kill -0 "${DAEMON_PID}" 2>/dev/null; then
69
+ fail "Daemon exited prematurely — check ${TMPDIR:-/tmp}/skcapstone-e2e-daemon.log"
70
+ exit 1
71
+ fi
72
+
73
+ # ---------------------------------------------------------------------------
74
+ # Step 2: Check /consciousness endpoint
75
+ # ---------------------------------------------------------------------------
76
+ info "Checking /consciousness endpoint…"
77
+ CONSCIOUSNESS_RESP=$(curl -sf "http://127.0.0.1:${PORT}/consciousness" 2>&1) || true
78
+ if echo "${CONSCIOUSNESS_RESP}" | python3 -c "import sys,json; d=json.load(sys.stdin); assert d.get('status') in ('ACTIVE','active','ok','running')" 2>/dev/null; then
79
+ pass "/consciousness endpoint returned active status"
80
+ elif [[ -n "${CONSCIOUSNESS_RESP}" ]]; then
81
+ # Accept any valid JSON response — daemon is reachable
82
+ if echo "${CONSCIOUSNESS_RESP}" | python3 -m json.tool >/dev/null 2>&1; then
83
+ pass "/consciousness endpoint returned JSON (status field: $(echo "${CONSCIOUSNESS_RESP}" | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('status','N/A'))" 2>/dev/null))"
84
+ else
85
+ fail "/consciousness endpoint returned non-JSON: ${CONSCIOUSNESS_RESP:0:120}"
86
+ fi
87
+ else
88
+ fail "/consciousness endpoint unreachable on port ${PORT}"
89
+ fi
90
+
91
+ # ---------------------------------------------------------------------------
92
+ # Step 3: Write a test message to the inbox
93
+ # ---------------------------------------------------------------------------
94
+ info "Writing test message to inbox (peer=${PEER})…"
95
+ mkdir -p "${INBOX_DIR}"
96
+ TIMESTAMP=$(date +%s)
97
+ MSG_ID="e2e-${TIMESTAMP}"
98
+ MSG_FILE="${INBOX_DIR}/${MSG_ID}.skc.json"
99
+
100
+ python3 - <<PYEOF
101
+ import json, time
102
+ msg = {
103
+ "sender": "${PEER}",
104
+ "recipient": "Opus",
105
+ "payload": {
106
+ "content": "Ping test — automated E2E at $(date -u +%Y-%m-%dT%H:%M:%SZ)",
107
+ "content_type": "text",
108
+ },
109
+ "message_id": "${MSG_ID}",
110
+ "timestamp": time.strftime("%Y-%m-%dT%H:%M:%S+00:00", time.gmtime()),
111
+ }
112
+ with open("${MSG_FILE}", "w") as fh:
113
+ json.dump(msg, fh)
114
+ print(f" Wrote {len(json.dumps(msg))} bytes → ${MSG_FILE}")
115
+ PYEOF
116
+
117
+ pass "Test message written: ${MSG_FILE}"
118
+
119
+ # ---------------------------------------------------------------------------
120
+ # Step 4: Poll outbox for response (up to POLL_TIMEOUT seconds)
121
+ # ---------------------------------------------------------------------------
122
+ info "Polling outbox for response (timeout=${POLL_TIMEOUT}s)…"
123
+ mkdir -p "${OUTBOX_DIR}"
124
+
125
+ RESPONSE_FOUND=false
126
+ DEADLINE=$((SECONDS + POLL_TIMEOUT))
127
+ LAST_OUTBOX_COUNT=0
128
+
129
+ while [[ ${SECONDS} -lt ${DEADLINE} ]]; do
130
+ OUTBOX_COUNT=$(find "${OUTBOX_DIR}" -name "*.skc.json" 2>/dev/null | wc -l)
131
+ if [[ "${OUTBOX_COUNT}" -gt "${LAST_OUTBOX_COUNT}" ]]; then
132
+ RESPONSE_FOUND=true
133
+ LATEST_RESP=$(find "${OUTBOX_DIR}" -name "*.skc.json" -newer "${MSG_FILE}" 2>/dev/null | sort | tail -1)
134
+ info "Response file detected: ${LATEST_RESP:-<any .skc.json in outbox>}"
135
+ break
136
+ fi
137
+
138
+ # Also accept: conversations file updated (passthrough / no-SKComm mode)
139
+ if [[ -f "${CONV_FILE}" ]] && [[ "${CONV_FILE}" -nt "${MSG_FILE}" ]]; then
140
+ RESPONSE_FOUND=true
141
+ info "Conversation file updated (passthrough mode detected)"
142
+ break
143
+ fi
144
+
145
+ ELAPSED=$((SECONDS - (DEADLINE - POLL_TIMEOUT)))
146
+ if (( ELAPSED % 30 == 0 )); then
147
+ info " Still waiting… ${ELAPSED}s elapsed / ${POLL_TIMEOUT}s timeout"
148
+ fi
149
+ sleep 2
150
+ done
151
+
152
+ if ${RESPONSE_FOUND}; then
153
+ pass "Response received within $((SECONDS - (DEADLINE - POLL_TIMEOUT)))s"
154
+ else
155
+ fail "No response within ${POLL_TIMEOUT}s (outbox: ${OUTBOX_DIR}, conv: ${CONV_FILE})"
156
+ fi
157
+
158
+ # ---------------------------------------------------------------------------
159
+ # Step 5: Check conversations file
160
+ # ---------------------------------------------------------------------------
161
+ info "Checking conversations/${PEER}.json…"
162
+ if [[ -f "${CONV_FILE}" ]]; then
163
+ CONV_SIZE=$(wc -c < "${CONV_FILE}")
164
+ if python3 -m json.tool "${CONV_FILE}" >/dev/null 2>&1; then
165
+ pass "conversations/${PEER}.json exists and is valid JSON (${CONV_SIZE} bytes)"
166
+ else
167
+ fail "conversations/${PEER}.json exists but is not valid JSON"
168
+ fi
169
+ else
170
+ fail "conversations/${PEER}.json not found at ${CONV_FILE}"
171
+ fi
172
+
173
+ # ---------------------------------------------------------------------------
174
+ # Step 6: Kill daemon
175
+ # ---------------------------------------------------------------------------
176
+ info "Stopping daemon…"
177
+ kill "${DAEMON_PID}" 2>/dev/null || true
178
+ wait "${DAEMON_PID}" 2>/dev/null || true
179
+ DAEMON_PID="" # prevent double-kill in trap
180
+ pass "Daemon stopped"
181
+
182
+ # ---------------------------------------------------------------------------
183
+ # Summary
184
+ # ---------------------------------------------------------------------------
185
+ echo ""
186
+ if [[ ${FAILURES} -eq 0 ]]; then
187
+ echo -e "${GREEN}E2E TEST PASSED${NC} — all checks passed."
188
+ exit 0
189
+ else
190
+ echo -e "${RED}E2E TEST FAILED${NC} — ${FAILURES} check(s) failed."
191
+ echo " Daemon log: ${TMPDIR:-/tmp}/skcapstone-e2e-daemon.log"
192
+ exit 1
193
+ fi