@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,296 @@
1
+ """
2
+ SKCapstone Coordination Federation — Syncthing-based multi-instance task sync.
3
+
4
+ Watches ~/.skcapstone/coordination/ for incoming task and agent files from
5
+ peer instances. Handles last-writer-wins conflict resolution by mtime and
6
+ announces changes via the coord.sync pubsub topic.
7
+
8
+ Design:
9
+ - Uses watchdog (inotify on Linux) to detect file-system events
10
+ - Debounces events (Syncthing writes in stages)
11
+ - Resolves Syncthing conflict files by mtime: newer wins
12
+ - Publishes coord.sync messages so peers can react immediately
13
+
14
+ Syncthing conflict filename format:
15
+ original-name.sync-conflict-YYYYMMDD-HHMMSS-DEVICEID.json
16
+ → canonical name: original-name.json (conflict suffix stripped)
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import asyncio
22
+ import json
23
+ import logging
24
+ import re
25
+ import time
26
+ from pathlib import Path
27
+ from typing import Callable, Optional
28
+
29
+ from .pubsub import PubSub
30
+
31
+ logger = logging.getLogger("skcapstone.coord_federation")
32
+
33
+ # Syncthing appends this pattern to the stem before the extension on conflict.
34
+ # e.g. "abc1-my-task.sync-conflict-20260302-120000-ABCDEF7.json"
35
+ _CONFLICT_RE = re.compile(r"\.sync-conflict-\d{8}-\d{6}-[A-Z0-9]+$", re.IGNORECASE)
36
+
37
+ # Pub/sub topic name for coordination sync events
38
+ COORD_SYNC_TOPIC = "coord.sync"
39
+
40
+
41
+ # ---------------------------------------------------------------------------
42
+ # Watchdog event handler
43
+ # ---------------------------------------------------------------------------
44
+
45
+
46
+ class _CoordEventHandler:
47
+ """Watchdog FileSystemEventHandler for coordination directory.
48
+
49
+ Filters to .json files only, debounces Syncthing multi-stage writes,
50
+ and dispatches to a callback on create or modify.
51
+
52
+ Args:
53
+ callback: Called with the Path of each relevant changed file.
54
+ debounce_ms: Minimum ms between events for the same file path.
55
+ """
56
+
57
+ def __init__(self, callback: Callable[[Path], None], debounce_ms: int = 500) -> None:
58
+ self._callback = callback
59
+ self._debounce_ms = debounce_ms
60
+ self._last_event: dict[str, float] = {}
61
+
62
+ def _accept(self, path_str: str) -> bool:
63
+ """Return True if this event should be processed (not debounced)."""
64
+ if not path_str.endswith(".json"):
65
+ return False
66
+ now = time.monotonic()
67
+ last = self._last_event.get(path_str, 0.0)
68
+ if (now - last) * 1000 < self._debounce_ms:
69
+ return False
70
+ self._last_event[path_str] = now
71
+ # Prune stale entries every ~60 s
72
+ cutoff = now - 60.0
73
+ self._last_event = {k: v for k, v in self._last_event.items() if v > cutoff}
74
+ return True
75
+
76
+ def _dispatch(self, event) -> None:
77
+ if getattr(event, "is_directory", False):
78
+ return
79
+ src = getattr(event, "src_path", str(event))
80
+ if self._accept(src):
81
+ self._callback(Path(src))
82
+
83
+ def on_created(self, event) -> None: # noqa: D401
84
+ self._dispatch(event)
85
+
86
+ def on_modified(self, event) -> None: # noqa: D401
87
+ self._dispatch(event)
88
+
89
+
90
+ # ---------------------------------------------------------------------------
91
+ # Federation watcher
92
+ # ---------------------------------------------------------------------------
93
+
94
+
95
+ class CoordFederationWatcher:
96
+ """Watches the coordination directory for incoming peer files.
97
+
98
+ Starts an inotify (watchdog) observer on:
99
+ <shared_root>/coordination/tasks/
100
+ <shared_root>/coordination/agents/
101
+
102
+ On every new or modified .json file:
103
+ 1. If it is a Syncthing conflict file, resolve by mtime (newer wins).
104
+ 2. Publish a coord.sync pubsub message announcing the change.
105
+
106
+ Thread safety: the watchdog observer runs in its own thread. File
107
+ callbacks are dispatched back to the asyncio event loop via
108
+ ``asyncio.run_coroutine_threadsafe``.
109
+
110
+ Args:
111
+ shared_root: Path to the shared ~/.skcapstone (or equivalent).
112
+ agent_name: Name used as the pubsub sender.
113
+ debounce_ms: Debounce window in milliseconds (default 500).
114
+ """
115
+
116
+ def __init__(
117
+ self,
118
+ shared_root: Path,
119
+ agent_name: str = "anonymous",
120
+ debounce_ms: int = 500,
121
+ ) -> None:
122
+ self._root = Path(shared_root).expanduser()
123
+ self._coord_dir = self._root / "coordination"
124
+ self._agent_name = agent_name
125
+ self._debounce_ms = debounce_ms
126
+
127
+ self._pubsub: Optional[PubSub] = None
128
+ self._observer = None # watchdog.Observer
129
+ self._loop: Optional[asyncio.AbstractEventLoop] = None
130
+
131
+ # ------------------------------------------------------------------
132
+ # Lifecycle
133
+ # ------------------------------------------------------------------
134
+
135
+ def start(self, loop: asyncio.AbstractEventLoop) -> None:
136
+ """Start the inotify observer (call from the main asyncio thread).
137
+
138
+ Args:
139
+ loop: The running asyncio event loop (used for thread-safe dispatch).
140
+ """
141
+ try:
142
+ from watchdog.observers import Observer
143
+ except ImportError:
144
+ logger.warning(
145
+ "watchdog not installed — coord federation disabled. "
146
+ "Install with: pip install watchdog"
147
+ )
148
+ return
149
+
150
+ self._loop = loop
151
+ self._pubsub = PubSub(self._root, agent_name=self._agent_name)
152
+ self._pubsub.initialize()
153
+
154
+ # Ensure the directories exist before watching
155
+ (self._coord_dir / "tasks").mkdir(parents=True, exist_ok=True)
156
+ (self._coord_dir / "agents").mkdir(parents=True, exist_ok=True)
157
+
158
+ handler = _CoordEventHandler(self._on_fs_event, debounce_ms=self._debounce_ms)
159
+ self._observer = Observer()
160
+ self._observer.schedule(handler, str(self._coord_dir), recursive=True)
161
+ self._observer.start()
162
+ logger.info("CoordFederationWatcher started on %s", self._coord_dir)
163
+
164
+ def stop(self) -> None:
165
+ """Stop the inotify observer."""
166
+ if self._observer is not None:
167
+ self._observer.stop()
168
+ self._observer.join()
169
+ self._observer = None
170
+ logger.info("CoordFederationWatcher stopped")
171
+
172
+ # ------------------------------------------------------------------
173
+ # Event dispatch (watchdog thread → asyncio)
174
+ # ------------------------------------------------------------------
175
+
176
+ def _on_fs_event(self, path: Path) -> None:
177
+ """Called from the watchdog thread. Forwards to the asyncio loop."""
178
+ if self._loop is not None and not self._loop.is_closed():
179
+ asyncio.run_coroutine_threadsafe(self._handle_change(path), self._loop)
180
+
181
+ # ------------------------------------------------------------------
182
+ # Core logic (runs in asyncio)
183
+ # ------------------------------------------------------------------
184
+
185
+ async def _handle_change(self, path: Path) -> None:
186
+ """Process one incoming file change from a peer."""
187
+ if not path.exists():
188
+ return
189
+
190
+ # Check if this is a Syncthing conflict file
191
+ stem = path.stem # e.g. "abc1-task.sync-conflict-20260302-120000-ABC"
192
+ if _CONFLICT_RE.search(stem):
193
+ await self._resolve_conflict(path)
194
+ else:
195
+ await self._announce(path, event="synced")
196
+
197
+ async def _resolve_conflict(self, conflict_path: Path) -> None:
198
+ """Resolve a Syncthing conflict file using last-writer-wins (mtime).
199
+
200
+ The conflict file's stem contains the `.sync-conflict-DATE-TIME-ID`
201
+ suffix. Strip it to find the canonical filename, then keep whichever
202
+ version has the newer mtime and delete the loser.
203
+
204
+ Args:
205
+ conflict_path: Path to the `.sync-conflict-*.json` file.
206
+ """
207
+ stem = conflict_path.stem
208
+ canonical_stem = _CONFLICT_RE.sub("", stem)
209
+ canonical_path = conflict_path.parent / f"{canonical_stem}.json"
210
+
211
+ if not canonical_path.exists():
212
+ # No canonical version — promote conflict to canonical
213
+ try:
214
+ conflict_path.rename(canonical_path)
215
+ logger.info("Promoted conflict to canonical: %s", canonical_path.name)
216
+ await self._announce(canonical_path, event="conflict_resolved")
217
+ except OSError as exc:
218
+ logger.warning("Could not promote conflict file: %s", exc)
219
+ return
220
+
221
+ # Compare modification times
222
+ try:
223
+ conflict_mtime = conflict_path.stat().st_mtime
224
+ canonical_mtime = canonical_path.stat().st_mtime
225
+ except OSError:
226
+ return
227
+
228
+ if conflict_mtime > canonical_mtime:
229
+ # Conflict is newer — replace canonical
230
+ try:
231
+ conflict_path.replace(canonical_path)
232
+ logger.info(
233
+ "Conflict newer — replaced canonical: %s", canonical_path.name
234
+ )
235
+ await self._announce(canonical_path, event="conflict_resolved")
236
+ except OSError as exc:
237
+ logger.warning("Could not replace canonical with conflict: %s", exc)
238
+ else:
239
+ # Canonical is newer (or same age) — drop conflict
240
+ try:
241
+ conflict_path.unlink(missing_ok=True)
242
+ logger.debug("Dropped older conflict: %s", conflict_path.name)
243
+ except OSError as exc:
244
+ logger.warning("Could not remove conflict file: %s", exc)
245
+
246
+ async def _announce(self, path: Path, event: str = "synced") -> None:
247
+ """Publish a coord.sync message for a changed coordination file.
248
+
249
+ Payload schema:
250
+ event — "synced" | "conflict_resolved"
251
+ kind — "tasks" | "agents" | "other"
252
+ file — filename (not full path)
253
+ task_id — (tasks only) task id from JSON
254
+ title — (tasks only) task title
255
+ agent — (agents only) agent name from JSON
256
+ source — announcing agent name
257
+
258
+ Args:
259
+ path: The canonical file that changed.
260
+ event: Human-readable event type.
261
+ """
262
+ if self._pubsub is None:
263
+ return
264
+
265
+ # Determine sub-directory kind
266
+ try:
267
+ rel = path.relative_to(self._coord_dir)
268
+ except ValueError:
269
+ return
270
+ kind = rel.parts[0] if rel.parts else "other" # "tasks" or "agents"
271
+
272
+ # Read file content for metadata
273
+ try:
274
+ data = json.loads(path.read_text(encoding="utf-8"))
275
+ except Exception:
276
+ data = {}
277
+
278
+ payload: dict = {
279
+ "event": event,
280
+ "kind": kind,
281
+ "file": path.name,
282
+ "source": self._agent_name,
283
+ }
284
+ if kind == "tasks":
285
+ payload["task_id"] = data.get("id")
286
+ payload["title"] = data.get("title")
287
+ payload["priority"] = data.get("priority")
288
+ elif kind == "agents":
289
+ payload["agent"] = data.get("agent")
290
+ payload["state"] = data.get("state")
291
+
292
+ try:
293
+ self._pubsub.publish(COORD_SYNC_TOPIC, payload, ttl_seconds=3600)
294
+ logger.info("coord.sync: %s %s/%s", event, kind, path.name)
295
+ except Exception as exc:
296
+ logger.warning("Failed to publish coord.sync: %s", exc)
@@ -14,6 +14,7 @@ Directory layout:
14
14
  from __future__ import annotations
15
15
 
16
16
  import json
17
+ import re
17
18
  import socket
18
19
  import uuid
19
20
  from datetime import datetime, timezone
@@ -24,6 +25,32 @@ from typing import Optional
24
25
  from pydantic import BaseModel, Field
25
26
 
26
27
 
28
+ def _slugify_filename(text: str) -> str:
29
+ """Convert text to a filesystem-safe slug.
30
+
31
+ Removes or replaces characters that are illegal in filenames:
32
+ - Forward slash (/) → dash (-)
33
+ - Backslash (\\) → dash (-)
34
+ - Colon (:) → dash (-)
35
+ - Other special chars → removed
36
+
37
+ Args:
38
+ text: Input string (e.g., task title)
39
+
40
+ Returns:
41
+ Safe filename slug
42
+ """
43
+ slug = text.lower().strip()
44
+ # Replace path separators and other illegal chars with dash
45
+ slug = re.sub(r'[/\\:*?"<>|]', '-', slug)
46
+ # Remove remaining non-word chars (except dash and space)
47
+ slug = re.sub(r'[^\w\s-]', '', slug)
48
+ # Convert spaces and underscores to single dash
49
+ slug = re.sub(r'[\s_]+', '-', slug)
50
+ # Remove leading/trailing dashes
51
+ return slug.strip('-')
52
+
53
+
27
54
  class TaskPriority(str, Enum):
28
55
  """Task urgency levels."""
29
56
 
@@ -113,6 +140,8 @@ class Board:
113
140
 
114
141
  Args:
115
142
  home: Path to ~/.skcapstone (or test equivalent).
143
+ In multi-agent mode, pass the shared root (not the
144
+ per-agent home) so all agents see the same board.
116
145
  """
117
146
 
118
147
  def __init__(self, home: Path) -> None:
@@ -137,7 +166,7 @@ class Board:
137
166
  return tasks
138
167
  for f in sorted(self.tasks_dir.glob("*.json")):
139
168
  try:
140
- data = json.loads(f.read_text())
169
+ data = json.loads(f.read_text(encoding="utf-8"))
141
170
  tasks.append(Task.model_validate(data))
142
171
  except (json.JSONDecodeError, Exception):
143
172
  continue
@@ -154,7 +183,7 @@ class Board:
154
183
  return agents
155
184
  for f in sorted(self.agents_dir.glob("*.json")):
156
185
  try:
157
- data = json.loads(f.read_text())
186
+ data = json.loads(f.read_text(encoding="utf-8"))
158
187
  agents.append(AgentFile.model_validate(data))
159
188
  except (json.JSONDecodeError, Exception):
160
189
  continue
@@ -172,7 +201,7 @@ class Board:
172
201
  path = self.agents_dir / f"{name}.json"
173
202
  if not path.exists():
174
203
  return None
175
- data = json.loads(path.read_text())
204
+ data = json.loads(path.read_text(encoding="utf-8"))
176
205
  return AgentFile.model_validate(data)
177
206
 
178
207
  def save_agent(self, agent: AgentFile) -> Path:
@@ -189,7 +218,7 @@ class Board:
189
218
  path = self.agents_dir / f"{agent.agent}.json"
190
219
  path.write_text(
191
220
  json.dumps(agent.model_dump(), indent=2) + "\n"
192
- )
221
+ , encoding="utf-8")
193
222
  return path
194
223
 
195
224
  def create_task(self, task: Task) -> Path:
@@ -202,13 +231,13 @@ class Board:
202
231
  Path to the written file.
203
232
  """
204
233
  self.ensure_dirs()
205
- slug = task.title.lower().replace(" ", "-")[:40]
234
+ slug = _slugify_filename(task.title)[:40]
206
235
  # Reason: filename includes id + slug for human readability
207
236
  filename = f"{task.id}-{slug}.json"
208
237
  path = self.tasks_dir / filename
209
238
  path.write_text(
210
239
  json.dumps(task.model_dump(), indent=2) + "\n"
211
- )
240
+ , encoding="utf-8")
212
241
  return path
213
242
 
214
243
  def get_task_views(self) -> list[TaskView]:
@@ -309,6 +338,10 @@ class Board:
309
338
  if agent.current_task == task_id:
310
339
  agent.current_task = agent.claimed_tasks[0] if agent.claimed_tasks else None
311
340
  self.save_agent(agent)
341
+
342
+ # Mint Joules for completed task
343
+ _mint_joules_for_task(self, task_id, agent_name)
344
+
312
345
  return agent
313
346
 
314
347
  def generate_board_md(self) -> str:
@@ -380,10 +413,71 @@ class Board:
380
413
  self.ensure_dirs()
381
414
  content = self.generate_board_md()
382
415
  path = self.coord_dir / "BOARD.md"
383
- path.write_text(content)
416
+ path.write_text(content, encoding="utf-8")
384
417
  return path
385
418
 
386
419
 
420
+ # Priority → (work_category, event_type, joules) mapping for Joule minting
421
+ _PRIORITY_JOULE_MAP: dict[str, tuple[str, str, int]] = {
422
+ "critical": ("development", "bug_fix", 500),
423
+ "high": ("development", "code_commit", 100),
424
+ "medium": ("community", "support_ticket", 50),
425
+ "low": ("development", "documentation", 25),
426
+ }
427
+
428
+
429
+ def _mint_joules_for_task(board: Board, task_id: str, agent_name: str) -> None:
430
+ """Mint Joules via SKJoule when a task is completed.
431
+
432
+ Looks up the task to get priority/title, maps priority to a work
433
+ category and Joule amount, then calls JouleEngine.auto_tokenize_task().
434
+ Failures are silently caught so tokenization never blocks task completion.
435
+
436
+ Args:
437
+ board: The Board instance (used to look up task data).
438
+ task_id: ID of the completed task.
439
+ agent_name: Agent who completed the task.
440
+ """
441
+ try:
442
+ from .skjoule import JouleEngine
443
+
444
+ # Find the task to get its metadata
445
+ task_data: dict[str, object] = {"id": task_id, "completed_by": agent_name}
446
+ for t in board.load_tasks():
447
+ if t.id == task_id:
448
+ task_data.update(t.model_dump())
449
+ break
450
+
451
+ priority = str(task_data.get("priority", "medium"))
452
+ category, _event_type, joules = _PRIORITY_JOULE_MAP.get(
453
+ priority, ("community", "support_ticket", 50)
454
+ )
455
+
456
+ # Inject tags so auto_tokenize_task picks up the right category
457
+ tags = list(task_data.get("tags", [])) # type: ignore[arg-type]
458
+ if category == "development":
459
+ if "dev" not in tags and "development" not in tags:
460
+ tags.append("dev")
461
+ elif category == "community":
462
+ if "community" not in tags:
463
+ tags.append("community")
464
+ task_data["tags"] = tags
465
+
466
+ # Use assignee if available, else default to "lumina"
467
+ worker = task_data.get("completed_by") or task_data.get("created_by") or "lumina"
468
+ task_data["completed_by"] = worker
469
+
470
+ engine = JouleEngine()
471
+ record = engine.auto_tokenize_task(task_data)
472
+
473
+ if record:
474
+ title = task_data.get("title", task_id)
475
+ print(f"[SKJoule] Minted {record.joules} Joules for task: {title}")
476
+ except Exception:
477
+ # Never let tokenization failure block task completion
478
+ pass
479
+
480
+
387
481
  _BRIEFING_PROTOCOL = """\
388
482
  # SKCapstone Agent Coordination Protocol
389
483