@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,94 @@
1
+ """MCP tool group modules — split from mcp_server.py for maintainability.
2
+
3
+ Each module exposes:
4
+ TOOLS: list[Tool] — MCP tool definitions
5
+ HANDLERS: dict — {tool_name: async_handler_fn}
6
+
7
+ The ``collect_all_tools`` and ``collect_all_handlers`` functions aggregate
8
+ across every module so mcp_server.py can register them in one shot.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ from typing import Any, Callable, Coroutine
14
+
15
+ from mcp.types import TextContent, Tool
16
+
17
+ from . import (
18
+ agent_tools,
19
+ ansible_tools,
20
+ chat_tools,
21
+ comm_tools,
22
+ consciousness_tools,
23
+ coord_tools,
24
+ deploy_tools,
25
+ did_tools,
26
+ emotion_tools,
27
+ file_tools,
28
+ fortress_tools,
29
+ gtd_tools,
30
+ health_tools,
31
+ heartbeat_tools,
32
+ kms_tools,
33
+ memory_tools,
34
+ model_tools,
35
+ notification_tools,
36
+ promoter_tools,
37
+ pubsub_tools,
38
+ skills_tools,
39
+ skseed_tools,
40
+ skstacks_tools,
41
+ soul_tools,
42
+ sync_tools,
43
+ telegram_tools,
44
+ trust_tools,
45
+ trustee_tools,
46
+ )
47
+
48
+ # Ordered list of all tool-group modules.
49
+ _MODULES = [
50
+ agent_tools,
51
+ memory_tools,
52
+ comm_tools,
53
+ sync_tools,
54
+ coord_tools,
55
+ ansible_tools,
56
+ soul_tools,
57
+ did_tools,
58
+ trust_tools,
59
+ skills_tools,
60
+ chat_tools,
61
+ trustee_tools,
62
+ health_tools,
63
+ heartbeat_tools,
64
+ file_tools,
65
+ gtd_tools,
66
+ pubsub_tools,
67
+ fortress_tools,
68
+ promoter_tools,
69
+ kms_tools,
70
+ skseed_tools,
71
+ skstacks_tools,
72
+ deploy_tools,
73
+ model_tools,
74
+ consciousness_tools,
75
+ emotion_tools,
76
+ notification_tools,
77
+ telegram_tools,
78
+ ]
79
+
80
+
81
+ def collect_all_tools() -> list[Tool]:
82
+ """Return every Tool definition from all group modules."""
83
+ tools: list[Tool] = []
84
+ for mod in _MODULES:
85
+ tools.extend(mod.TOOLS)
86
+ return tools
87
+
88
+
89
+ def collect_all_handlers() -> dict[str, Callable[..., Coroutine[Any, Any, list[TextContent]]]]:
90
+ """Return a merged {name: handler} dict from all group modules."""
91
+ handlers: dict[str, Callable[..., Coroutine[Any, Any, list[TextContent]]]] = {}
92
+ for mod in _MODULES:
93
+ handlers.update(mod.HANDLERS)
94
+ return handlers
@@ -0,0 +1,51 @@
1
+ """Shared helpers for MCP tool modules."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import logging
7
+ from pathlib import Path
8
+ from typing import Any
9
+
10
+ from mcp.types import TextContent
11
+
12
+ from .. import AGENT_HOME, SHARED_ROOT
13
+
14
+ logger = logging.getLogger("skcapstone.mcp")
15
+
16
+
17
+ def _home() -> Path:
18
+ """Resolve the per-agent home directory."""
19
+ return Path(AGENT_HOME).expanduser()
20
+
21
+
22
+ def _shared_root() -> Path:
23
+ """Resolve the shared agent root (coordination, heartbeats, peers)."""
24
+ return Path(SHARED_ROOT).expanduser()
25
+
26
+
27
+ def _json_response(data: Any) -> list[TextContent]:
28
+ """Wrap data as a JSON text content response."""
29
+ return [TextContent(type="text", text=json.dumps(data, indent=2, default=str))]
30
+
31
+
32
+ def _text_response(text: str) -> list[TextContent]:
33
+ """Wrap a plain string as a text content response."""
34
+ return [TextContent(type="text", text=text)]
35
+
36
+
37
+ def _error_response(message: str) -> list[TextContent]:
38
+ """Return an error message as text content."""
39
+ return [TextContent(type="text", text=json.dumps({"error": message}))]
40
+
41
+
42
+ def _get_agent_name(home: Path) -> str:
43
+ """Read the agent name from identity file."""
44
+ identity_path = home / "identity" / "identity.json"
45
+ if identity_path.exists():
46
+ try:
47
+ data = json.loads(identity_path.read_text(encoding="utf-8"))
48
+ return data.get("name", "anonymous")
49
+ except Exception:
50
+ pass
51
+ return "anonymous"
@@ -0,0 +1,243 @@
1
+ """Agent status, context, state diff, and session capture tools."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+
7
+ from mcp.types import TextContent, Tool
8
+
9
+ from ._helpers import _error_response, _home, _json_response, _text_response
10
+
11
+ TOOLS: list[Tool] = [
12
+ Tool(
13
+ name="agent_status",
14
+ description=(
15
+ "Get the sovereign agent's current state: pillar statuses "
16
+ "(identity, memory, trust, security, sync), consciousness "
17
+ "level, connected platforms, and overall health."
18
+ ),
19
+ inputSchema={"type": "object", "properties": {}, "required": []},
20
+ ),
21
+ Tool(
22
+ name="session_capture",
23
+ description=(
24
+ "Capture AI conversation content as sovereign memories. "
25
+ "Extracts key moments, auto-scores importance by topic "
26
+ "novelty and information density, deduplicates against "
27
+ "existing memories, and stores as tagged, searchable "
28
+ "memories. The agent never forgets a conversation."
29
+ ),
30
+ inputSchema={
31
+ "type": "object",
32
+ "properties": {
33
+ "content": {
34
+ "type": "string",
35
+ "description": "Conversation text to capture (any length)",
36
+ },
37
+ "tags": {
38
+ "type": "array",
39
+ "items": {"type": "string"},
40
+ "description": "Extra tags to apply to all captured memories",
41
+ },
42
+ "source": {
43
+ "type": "string",
44
+ "description": "Source identifier (default: 'mcp-session')",
45
+ },
46
+ "min_importance": {
47
+ "type": "number",
48
+ "description": "Minimum importance threshold (default: 0.3)",
49
+ },
50
+ },
51
+ "required": ["content"],
52
+ },
53
+ ),
54
+ Tool(
55
+ name="state_diff",
56
+ description=(
57
+ "Show what changed since the last sync/snapshot. "
58
+ "Compares current agent state to the baseline: new "
59
+ "memories, trust changes, completed tasks, pillar "
60
+ "status changes. Use action='save' to set a new baseline."
61
+ ),
62
+ inputSchema={
63
+ "type": "object",
64
+ "properties": {
65
+ "action": {
66
+ "type": "string",
67
+ "enum": ["diff", "save"],
68
+ "description": "Action: diff (compare) or save (new baseline). Default: diff.",
69
+ },
70
+ },
71
+ "required": [],
72
+ },
73
+ ),
74
+ Tool(
75
+ name="agent_context",
76
+ description=(
77
+ "Get the full agent context: identity, pillar status, "
78
+ "coordination board, recent memories, soul overlay, and "
79
+ "MCP status. Returns everything an AI needs to understand "
80
+ "the sovereign agent's current state. Supports text, JSON, "
81
+ "and claude-md output formats."
82
+ ),
83
+ inputSchema={
84
+ "type": "object",
85
+ "properties": {
86
+ "format": {
87
+ "type": "string",
88
+ "enum": ["text", "json", "claude-md", "cursor-rules"],
89
+ "description": "Output format (default: json)",
90
+ },
91
+ "memories": {
92
+ "type": "integer",
93
+ "description": "Max recent memories to include (default: 10)",
94
+ },
95
+ },
96
+ "required": [],
97
+ },
98
+ ),
99
+ ]
100
+
101
+
102
+ def _get_memory_backend_health() -> dict:
103
+ """Get health status of all memory backends (sqlite, skvector, skgraph)."""
104
+ try:
105
+ from ..memory_adapter import get_unified
106
+
107
+ store = get_unified()
108
+ if store is None:
109
+ return {"json": "ok"}
110
+
111
+ health = store.health()
112
+ backends = {}
113
+ if "primary" in health:
114
+ backends["sqlite"] = "ok" if health["primary"].get("ok") else "error"
115
+ if "vector" in health:
116
+ backends["skvector"] = "ok" if health["vector"].get("ok") else "error"
117
+ if "graph" in health:
118
+ backends["skgraph"] = "ok" if health["graph"].get("ok") else "error"
119
+ return backends or {"json": "ok"}
120
+ except Exception:
121
+ return {"json": "ok"}
122
+
123
+
124
+ async def _handle_agent_status(_args: dict) -> list[TextContent]:
125
+ """Return agent pillar states and consciousness level."""
126
+ from ..runtime import get_runtime
127
+
128
+ home = _home()
129
+ if not home.exists():
130
+ return _error_response("Agent not initialized. Run: skcapstone init")
131
+
132
+ runtime = get_runtime(home)
133
+ m = runtime.manifest
134
+ return _json_response({
135
+ "name": m.name,
136
+ "version": m.version,
137
+ "is_conscious": m.is_conscious,
138
+ "is_singular": m.is_singular,
139
+ "pillars": {
140
+ "identity": {
141
+ "status": m.identity.status.value,
142
+ "fingerprint": m.identity.fingerprint,
143
+ },
144
+ "memory": {
145
+ "status": m.memory.status.value,
146
+ "total": m.memory.total_memories,
147
+ "long_term": m.memory.long_term,
148
+ "mid_term": m.memory.mid_term,
149
+ "short_term": m.memory.short_term,
150
+ "backends": _get_memory_backend_health(),
151
+ },
152
+ "trust": {
153
+ "status": m.trust.status.value,
154
+ "depth": m.trust.depth,
155
+ "trust_level": m.trust.trust_level,
156
+ "love_intensity": m.trust.love_intensity,
157
+ "entangled": m.trust.entangled,
158
+ },
159
+ "security": {
160
+ "status": m.security.status.value,
161
+ "audit_entries": m.security.audit_entries,
162
+ "threats_detected": m.security.threats_detected,
163
+ },
164
+ "sync": {
165
+ "status": m.sync.status.value,
166
+ "seed_count": m.sync.seed_count,
167
+ "transport": m.sync.transport.value if m.sync.transport else None,
168
+ },
169
+ },
170
+ "connectors": [c.platform for c in m.connectors if c.active],
171
+ "last_awakened": m.last_awakened.isoformat() if m.last_awakened else None,
172
+ })
173
+
174
+
175
+ async def _handle_session_capture(args: dict) -> list[TextContent]:
176
+ """Capture conversation content as sovereign memories."""
177
+ from ..session_capture import SessionCapture
178
+
179
+ content = args.get("content", "")
180
+ if not content:
181
+ return _error_response("content is required")
182
+
183
+ home = _home()
184
+ cap = SessionCapture(home)
185
+ entries = cap.capture(
186
+ content=content,
187
+ tags=args.get("tags", []),
188
+ source=args.get("source", "mcp-session"),
189
+ min_importance=args.get("min_importance", 0.3),
190
+ )
191
+
192
+ return _json_response({
193
+ "captured": len(entries),
194
+ "moments": [
195
+ {
196
+ "memory_id": e.memory_id,
197
+ "content": e.content[:200],
198
+ "layer": e.layer.value,
199
+ "importance": e.importance,
200
+ "tags": e.tags,
201
+ }
202
+ for e in entries
203
+ ],
204
+ })
205
+
206
+
207
+ async def _handle_state_diff(args: dict) -> list[TextContent]:
208
+ """Show agent state diff or save a baseline snapshot."""
209
+ from ..state_diff import compute_diff, format_json, save_snapshot
210
+
211
+ home = _home()
212
+ action = args.get("action", "diff")
213
+
214
+ if action == "save":
215
+ path = save_snapshot(home)
216
+ return _json_response({"saved": True, "path": str(path)})
217
+
218
+ diff = compute_diff(home)
219
+ return _json_response(json.loads(format_json(diff)))
220
+
221
+
222
+ async def _handle_agent_context(args: dict) -> list[TextContent]:
223
+ """Return the full agent context in the requested format."""
224
+ from ..context_loader import FORMATTERS, gather_context
225
+
226
+ home = _home()
227
+ fmt = args.get("format", "json")
228
+ limit = args.get("memories", 10)
229
+
230
+ ctx = gather_context(home, memory_limit=limit)
231
+ formatter = FORMATTERS.get(fmt, FORMATTERS["json"])
232
+
233
+ if fmt == "json":
234
+ return _json_response(ctx)
235
+ return _text_response(formatter(ctx))
236
+
237
+
238
+ HANDLERS: dict = {
239
+ "agent_status": _handle_agent_status,
240
+ "session_capture": _handle_session_capture,
241
+ "state_diff": _handle_state_diff,
242
+ "agent_context": _handle_agent_context,
243
+ }
@@ -0,0 +1,232 @@
1
+ """Ansible playbook runner tool — run_ansible_playbook.
2
+
3
+ Streams stdout/stderr lines to the activity feed SSE queue as
4
+ ``ansible.playbook.line`` / ``ansible.playbook.stderr`` events.
5
+ Stores the exit code and play-recap summary in agent memory with
6
+ ``tag=ansible-run``.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import asyncio
12
+ import json
13
+ import shutil
14
+ import uuid
15
+ from pathlib import Path
16
+
17
+ from mcp.types import TextContent, Tool
18
+
19
+ from ._helpers import _error_response, _home, _json_response
20
+
21
+ TOOLS: list[Tool] = [
22
+ Tool(
23
+ name="run_ansible_playbook",
24
+ description=(
25
+ "Run an Ansible playbook via ansible-playbook subprocess. "
26
+ "Streams stdout lines to the activity feed SSE queue as "
27
+ "ansible.playbook.line events (stderr lines as "
28
+ "ansible.playbook.stderr). Stores exit code and play-recap "
29
+ "summary in agent memory with tag=ansible-run. "
30
+ "dry_run=true adds --check (no changes applied). "
31
+ "Requires ansible-playbook binary in PATH."
32
+ ),
33
+ inputSchema={
34
+ "type": "object",
35
+ "properties": {
36
+ "playbook_path": {
37
+ "type": "string",
38
+ "description": (
39
+ "Absolute or relative path to the Ansible playbook YAML file"
40
+ ),
41
+ },
42
+ "inventory": {
43
+ "type": "string",
44
+ "description": (
45
+ "Inventory file path, directory, or comma-separated host pattern"
46
+ ),
47
+ },
48
+ "extra_vars": {
49
+ "type": "object",
50
+ "description": (
51
+ "Extra variables passed to ansible-playbook via --extra-vars "
52
+ "(serialised as a JSON string)"
53
+ ),
54
+ "additionalProperties": True,
55
+ },
56
+ "dry_run": {
57
+ "type": "boolean",
58
+ "description": (
59
+ "If true, pass --check so ansible-playbook simulates changes "
60
+ "without applying them (default: false)"
61
+ ),
62
+ },
63
+ },
64
+ "required": ["playbook_path", "inventory"],
65
+ },
66
+ ),
67
+ ]
68
+
69
+
70
+ # ── handler ───────────────────────────────────────────────────────────────────
71
+
72
+
73
+ async def _handle_run_ansible_playbook(args: dict) -> list[TextContent]:
74
+ """Run an Ansible playbook and stream output to the activity bus."""
75
+ playbook_path = args.get("playbook_path", "").strip()
76
+ inventory = args.get("inventory", "").strip()
77
+ extra_vars: dict = args.get("extra_vars") or {}
78
+ dry_run: bool = bool(args.get("dry_run", False))
79
+
80
+ # --- input validation ---
81
+ if not playbook_path:
82
+ return _error_response("playbook_path is required")
83
+ if not inventory:
84
+ return _error_response("inventory is required")
85
+
86
+ # Require ansible-playbook binary in PATH
87
+ if not shutil.which("ansible-playbook"):
88
+ return _error_response(
89
+ "ansible-playbook binary not found in PATH; "
90
+ "install Ansible first (e.g. pip install ansible or dnf install ansible)"
91
+ )
92
+
93
+ playbook = Path(playbook_path).expanduser().resolve()
94
+ if not playbook.exists():
95
+ return _error_response(f"playbook not found: {playbook_path!r}")
96
+
97
+ # --- build command ---
98
+ cmd: list[str] = [
99
+ "ansible-playbook",
100
+ str(playbook),
101
+ "-i", inventory,
102
+ ]
103
+ if dry_run:
104
+ cmd.append("--check")
105
+ if extra_vars:
106
+ cmd.extend(["--extra-vars", json.dumps(extra_vars)])
107
+
108
+ # --- emit start event ---
109
+ run_id = uuid.uuid4().hex[:8]
110
+
111
+ try:
112
+ from .. import activity as _activity
113
+
114
+ _activity.push("ansible.playbook.start", {
115
+ "run_id": run_id,
116
+ "playbook": str(playbook),
117
+ "inventory": inventory,
118
+ "dry_run": dry_run,
119
+ "cmd": cmd,
120
+ })
121
+ except Exception:
122
+ _activity = None # type: ignore[assignment]
123
+
124
+ # --- stream subprocess output ---
125
+ stdout_lines: list[str] = []
126
+ stderr_lines: list[str] = []
127
+
128
+ try:
129
+ proc = await asyncio.create_subprocess_exec(
130
+ *cmd,
131
+ stdout=asyncio.subprocess.PIPE,
132
+ stderr=asyncio.subprocess.PIPE,
133
+ )
134
+
135
+ async def _drain(
136
+ stream: asyncio.StreamReader,
137
+ store: list[str],
138
+ event_type: str,
139
+ ) -> None:
140
+ while True:
141
+ raw = await stream.readline()
142
+ if not raw:
143
+ break
144
+ line = raw.decode(errors="replace").rstrip("\n")
145
+ store.append(line)
146
+ try:
147
+ if _activity is not None:
148
+ _activity.push(event_type, {"run_id": run_id, "line": line})
149
+ except Exception:
150
+ pass
151
+
152
+ await asyncio.gather(
153
+ _drain(proc.stdout, stdout_lines, "ansible.playbook.line"),
154
+ _drain(proc.stderr, stderr_lines, "ansible.playbook.stderr"),
155
+ )
156
+ await proc.wait()
157
+ exit_code: int = proc.returncode # type: ignore[assignment]
158
+
159
+ except Exception as exc:
160
+ return _error_response(f"Failed to launch ansible-playbook: {exc}")
161
+
162
+ # --- build summary ---
163
+ success = exit_code == 0
164
+
165
+ # Extract PLAY RECAP block lines (host rows contain "ok=" or "failed=")
166
+ recap_lines = [
167
+ line for line in stdout_lines
168
+ if "PLAY RECAP" in line or ("ok=" in line and "changed=" in line)
169
+ ]
170
+
171
+ summary = {
172
+ "run_id": run_id,
173
+ "playbook": str(playbook),
174
+ "inventory": inventory,
175
+ "dry_run": dry_run,
176
+ "exit_code": exit_code,
177
+ "success": success,
178
+ "stdout_lines": len(stdout_lines),
179
+ "stderr_lines": len(stderr_lines),
180
+ "recap": recap_lines,
181
+ }
182
+
183
+ # --- emit completion event ---
184
+ try:
185
+ if _activity is not None:
186
+ _activity.push("ansible.playbook.done", summary)
187
+ except Exception:
188
+ pass
189
+
190
+ # --- store in memory with tag=ansible-run ---
191
+ try:
192
+ from ..memory_engine import store as _mem_store
193
+
194
+ recap_str = " | ".join(recap_lines) if recap_lines else "none"
195
+ _mem_store(
196
+ home=_home(),
197
+ content=(
198
+ f"Ansible run {'(dry-run) ' if dry_run else ''}— "
199
+ f"playbook: {str(playbook)!r}, inventory: {inventory!r}, "
200
+ f"exit_code: {exit_code}, success: {success}. "
201
+ f"Recap: {recap_str}"
202
+ ),
203
+ tags=["ansible-run"],
204
+ source="mcp:run_ansible_playbook",
205
+ importance=0.6 if success else 0.8,
206
+ metadata={
207
+ "run_id": run_id,
208
+ "playbook": str(playbook),
209
+ "inventory": inventory,
210
+ "dry_run": dry_run,
211
+ "exit_code": exit_code,
212
+ "recap": recap_lines,
213
+ },
214
+ )
215
+ except Exception:
216
+ pass # memory failure must not block the tool response
217
+
218
+ if not success:
219
+ # Surface last 20 stderr lines for quick diagnosis
220
+ stderr_tail = stderr_lines[-20:] if len(stderr_lines) > 20 else stderr_lines
221
+ return _json_response({
222
+ **summary,
223
+ "stderr_tail": stderr_tail,
224
+ "error": f"ansible-playbook exited with code {exit_code}",
225
+ })
226
+
227
+ return _json_response(summary)
228
+
229
+
230
+ HANDLERS: dict = {
231
+ "run_ansible_playbook": _handle_run_ansible_playbook,
232
+ }