@smilintux/skcapstone 0.1.0 → 0.2.4

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 +880 -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 +191 -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 +398 -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 +357 -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 +264 -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,584 @@
1
+ """Agent-to-agent chat commands: send, inbox, live, open, list, summary.
2
+
3
+ skcapstone chat <peer> Open interactive LLM session (shortcut)
4
+ skcapstone chat open <peer> Open interactive LLM session
5
+ skcapstone chat send <peer> <m> One-shot send
6
+ skcapstone chat inbox Browse messages
7
+ skcapstone chat live <peer> Alias for 'open'
8
+ skcapstone chat list List peers with conversation history
9
+ skcapstone chat --list Same as 'list'
10
+ skcapstone chat summary <peer> LLM-powered conversation summary
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import sys
17
+ from pathlib import Path
18
+ from typing import Optional
19
+
20
+ import click
21
+
22
+ from ._common import AGENT_HOME, console, get_runtime
23
+ from ._validators import validate_agent_name
24
+
25
+ from rich.table import Table
26
+
27
+
28
+ # Known sub-command names; anything else is treated as a peer name.
29
+ _KNOWN_SUBCOMMANDS = {"send", "inbox", "live", "open", "list", "history", "summary", "forward", "--help", "-h", "--version"}
30
+
31
+
32
+ class _ChatGroup(click.Group):
33
+ """Click group that treats an unknown first arg as a peer for 'open'.
34
+
35
+ Allows::
36
+
37
+ skcapstone chat lumina # same as: skcapstone chat open lumina
38
+ skcapstone chat send lumina … # normal subcommand routing
39
+ skcapstone chat --list # same as: skcapstone chat list
40
+ """
41
+
42
+ def parse_args(self, ctx: click.Context, args: list[str]) -> list[str]:
43
+ if "--list" in args:
44
+ remaining = [a for a in args if a != "--list"]
45
+ args = ["list"] + remaining
46
+ elif args and not args[0].startswith("-") and args[0] not in _KNOWN_SUBCOMMANDS:
47
+ args = ["open"] + args
48
+ return super().parse_args(ctx, args)
49
+
50
+
51
+ def _run_llm_chat(peer: str, home_path: Path, identity: str) -> None:
52
+ """Run an LLM-powered interactive terminal chat session.
53
+
54
+ Args:
55
+ peer: Peer name used as conversation context key.
56
+ home_path: Agent home directory.
57
+ identity: Local agent name shown in the prompt.
58
+ """
59
+ from ..consciousness_loop import (
60
+ ConsciousnessConfig,
61
+ LLMBridge,
62
+ SystemPromptBuilder,
63
+ _classify_message,
64
+ )
65
+
66
+ config = ConsciousnessConfig()
67
+ bridge = LLMBridge(config)
68
+ builder = SystemPromptBuilder(home=home_path)
69
+
70
+ # Show last 5 messages from existing history
71
+ conv_file = home_path / "conversations" / f"{peer}.json"
72
+ console.print()
73
+ if conv_file.exists():
74
+ try:
75
+ history = json.loads(conv_file.read_text(encoding="utf-8"))
76
+ if history:
77
+ console.print(
78
+ f"[dim]--- {len(history)} previous message(s) with {peer} ---[/]\n"
79
+ )
80
+ for msg in history[-5:]:
81
+ if msg.get("role") == "user":
82
+ label = f"[cyan]{identity}[/]"
83
+ else:
84
+ label = f"[green]{peer}[/]"
85
+ content = msg.get("content", "")[:100]
86
+ console.print(f" {label}: {content}")
87
+ console.print()
88
+ except Exception:
89
+ pass
90
+
91
+ console.print(f"[bold]Chat with [cyan]{peer}[/][/] [dim]Ctrl+C or /quit to exit[/]\n")
92
+
93
+ try:
94
+ while True:
95
+ try:
96
+ user_msg = console.input(f"[cyan]{identity}[/] > ").strip()
97
+ except EOFError:
98
+ break
99
+
100
+ if not user_msg:
101
+ continue
102
+ if user_msg.lower() in ("/quit", "/exit", "/q"):
103
+ break
104
+
105
+ builder.add_to_history(peer, "user", user_msg)
106
+
107
+ system_prompt = builder.build(peer_name=peer)
108
+ signal = _classify_message(user_msg)
109
+
110
+ with console.status("[dim]thinking...[/]"):
111
+ try:
112
+ response = bridge.generate(system_prompt, user_msg, signal)
113
+ except Exception as exc:
114
+ response = f"[Error: {exc}]"
115
+
116
+ console.print(f"[green]{peer}[/]: {response}\n")
117
+ builder.add_to_history(peer, "assistant", response)
118
+
119
+ except KeyboardInterrupt:
120
+ console.print("\n[dim]Session ended.[/]")
121
+
122
+
123
+ def register_chat_commands(main: click.Group) -> None:
124
+ """Register the chat command group."""
125
+
126
+ @main.group(cls=_ChatGroup)
127
+ def chat():
128
+ """Agent-to-agent chat — sovereign P2P messaging.
129
+
130
+ Open an interactive session, send one-off messages, or browse
131
+ your inbox. Works from any terminal — no IDE required.
132
+
133
+ \b
134
+ Quick start:
135
+ skcapstone chat lumina # start chatting with 'lumina'
136
+ skcapstone chat send opus "hi" # send a one-off message
137
+ skcapstone chat inbox --poll # check for new messages
138
+ """
139
+
140
+ # ------------------------------------------------------------------
141
+ # open — interactive prompt_toolkit session
142
+ # ------------------------------------------------------------------
143
+
144
+ @chat.command("open")
145
+ @click.argument("peer")
146
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
147
+ @click.option(
148
+ "--thread", "-t", default=None,
149
+ help="Start in a specific thread ID.",
150
+ )
151
+ @click.option(
152
+ "--poll-interval", default=2.0, show_default=True,
153
+ help="Seconds between incoming message polls.",
154
+ )
155
+ def chat_open(peer: str, home: str, thread: Optional[str], poll_interval: float):
156
+ """Open an interactive LLM-powered chat session.
157
+
158
+ Starts a terminal chat loop that uses the local LLM (via
159
+ LLMBridge) to generate responses. Conversation history is
160
+ shown at startup and saved to conversations/{peer}.json.
161
+
162
+ \b
163
+ Slash commands:
164
+ /quit /exit /q — exit the session
165
+
166
+ \b
167
+ Examples:
168
+ skcapstone chat lumina
169
+ skcapstone chat open lumina
170
+ skcapstone chat open opus
171
+ """
172
+ validate_agent_name(peer)
173
+
174
+ home_path = Path(home).expanduser()
175
+ if not home_path.exists():
176
+ console.print("[bold red]No agent found.[/] Run skcapstone init first.")
177
+ sys.exit(1)
178
+
179
+ runtime = get_runtime(home_path)
180
+ identity = runtime.manifest.name or "unknown"
181
+
182
+ _run_llm_chat(peer, home_path, identity)
183
+
184
+ # ------------------------------------------------------------------
185
+ # send — one-shot message
186
+ # ------------------------------------------------------------------
187
+
188
+ @chat.command("send")
189
+ @click.argument("peer")
190
+ @click.argument("message")
191
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
192
+ @click.option("--thread", "-t", default=None, help="Thread ID for conversation grouping.")
193
+ @click.option("--encrypt", is_flag=True, default=False, help="Encrypt message with AES-256-GCM (key from KMS).")
194
+ def chat_send(peer: str, message: str, home: str, thread: Optional[str], encrypt: bool):
195
+ """Send a message to a peer agent.
196
+
197
+ Stores locally and delivers via SKComm if transports
198
+ are configured.
199
+
200
+ \b
201
+ Examples:
202
+ skcapstone chat send lumina "Hello from the sovereign side!"
203
+ skcapstone chat send opus "Deploy update ready" --thread deploy-01
204
+ skcapstone chat send lumina "Secret plan" --encrypt
205
+ """
206
+ from ..chat import AgentChat
207
+
208
+ validate_agent_name(peer)
209
+
210
+ home_path = Path(home).expanduser()
211
+ runtime = get_runtime(home_path)
212
+ identity = runtime.manifest.name or "unknown"
213
+
214
+ payload = message
215
+ if encrypt:
216
+ try:
217
+ from ..message_crypto import encrypt_content
218
+ payload = encrypt_content(message, home_path)
219
+ console.print(" [dim]Message encrypted (AES-256-GCM)[/]")
220
+ except Exception as exc:
221
+ console.print(f" [bold red]Encryption failed:[/] {exc}")
222
+ sys.exit(1)
223
+
224
+ agent_chat = AgentChat(home=home_path, identity=identity)
225
+ result = agent_chat.send(peer, payload, thread_id=thread)
226
+
227
+ console.print("")
228
+ if result["delivered"]:
229
+ console.print(f" [green]Delivered[/] to [cyan]{peer}[/] via {result['transport']}")
230
+ elif result["stored"]:
231
+ console.print(f" [yellow]Stored locally[/] for [cyan]{peer}[/]")
232
+ if result.get("error"):
233
+ console.print(f" [dim]{result['error']}[/]")
234
+ else:
235
+ console.print(f" [red]Failed[/] — {result.get('error', 'unknown error')}")
236
+ console.print("")
237
+
238
+ # ------------------------------------------------------------------
239
+ # inbox — browse messages
240
+ # ------------------------------------------------------------------
241
+
242
+ @chat.command("inbox")
243
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
244
+ @click.option("--limit", "-n", default=20, help="Max messages to show.")
245
+ @click.option("--poll", is_flag=True, help="Poll transports for new messages first.")
246
+ @click.option("--decrypt", is_flag=True, default=False, help="Decrypt encrypted messages using KMS key.")
247
+ def chat_inbox(home: str, limit: int, poll: bool, decrypt: bool):
248
+ """Show recent messages.
249
+
250
+ Displays messages from local history. Use --poll to check
251
+ SKComm transports for new messages first. Use --decrypt to
252
+ automatically decrypt AES-256-GCM encrypted messages.
253
+
254
+ \b
255
+ Examples:
256
+ skcapstone chat inbox
257
+ skcapstone chat inbox --poll --limit 5
258
+ skcapstone chat inbox --decrypt
259
+ """
260
+ from ..chat import AgentChat
261
+
262
+ home_path = Path(home).expanduser()
263
+ runtime = get_runtime(home_path)
264
+ identity = runtime.manifest.name or "unknown"
265
+
266
+ agent_chat = AgentChat(home=home_path, identity=identity)
267
+
268
+ if poll:
269
+ incoming = agent_chat.receive(limit=limit)
270
+ if incoming:
271
+ console.print(f"\n [green]{len(incoming)} new message(s) received[/]\n")
272
+
273
+ messages = agent_chat.get_inbox(limit=limit)
274
+
275
+ console.print("")
276
+ if not messages:
277
+ console.print(" [dim]No messages.[/]")
278
+ console.print("")
279
+ return
280
+
281
+ from ..chat import _format_content
282
+ from ..message_crypto import decrypt_content, is_encrypted_content
283
+
284
+ table = Table(
285
+ show_header=True,
286
+ header_style="bold",
287
+ box=None,
288
+ padding=(0, 2),
289
+ title=f"Inbox ({len(messages)} message{'s' if len(messages) != 1 else ''})",
290
+ )
291
+ table.add_column("From", style="cyan", max_width=25)
292
+ table.add_column("Content", max_width=50)
293
+ table.add_column("Time", style="dim", max_width=20)
294
+
295
+ for msg in messages:
296
+ sender = msg.get("sender", "?")
297
+ raw_content = msg.get("content", "")
298
+ if decrypt and is_encrypted_content(raw_content):
299
+ try:
300
+ raw_content = decrypt_content(raw_content, home_path)
301
+ except Exception as exc:
302
+ raw_content = f"[decrypt failed: {exc}]"
303
+ content = _format_content(raw_content)
304
+ preview = content[:50] + ("…" if len(content) > 50 else "")
305
+ ts = str(msg.get("timestamp", ""))
306
+ if len(ts) > 19:
307
+ ts = ts[:19]
308
+ table.add_row(sender, preview, ts)
309
+
310
+ console.print(table)
311
+ console.print("")
312
+
313
+ # ------------------------------------------------------------------
314
+ # list — show peers with conversation history
315
+ # ------------------------------------------------------------------
316
+
317
+ @chat.command("list")
318
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
319
+ def chat_list(home: str):
320
+ """List all peers with conversation history.
321
+
322
+ Shows each peer that has a saved conversation file, along with
323
+ the message count and a preview of the most recent message.
324
+
325
+ \b
326
+ Examples:
327
+ skcapstone chat list
328
+ skcapstone chat --list
329
+ """
330
+ home_path = Path(home).expanduser()
331
+ conversations_dir = home_path / "conversations"
332
+
333
+ if not conversations_dir.exists():
334
+ console.print("\n [dim]No conversations yet.[/]\n")
335
+ return
336
+
337
+ conv_files = sorted(conversations_dir.glob("*.json"))
338
+ if not conv_files:
339
+ console.print("\n [dim]No conversations yet.[/]\n")
340
+ return
341
+
342
+ table = Table(
343
+ show_header=True,
344
+ header_style="bold",
345
+ box=None,
346
+ padding=(0, 2),
347
+ title=f"Conversations ({len(conv_files)} peer{'s' if len(conv_files) != 1 else ''})",
348
+ )
349
+ table.add_column("Peer", style="cyan")
350
+ table.add_column("Messages", justify="right", style="dim")
351
+ table.add_column("Last message", max_width=60)
352
+
353
+ for conv_file in conv_files:
354
+ peer = conv_file.stem
355
+ try:
356
+ data = json.loads(conv_file.read_text(encoding="utf-8"))
357
+ count = str(len(data)) if isinstance(data, list) else "?"
358
+ last = ""
359
+ if isinstance(data, list) and data:
360
+ last = str(data[-1].get("content", ""))[:60]
361
+ table.add_row(peer, count, last)
362
+ except Exception:
363
+ table.add_row(peer, "?", "[dim][corrupted][/]")
364
+
365
+ console.print()
366
+ console.print(table)
367
+ console.print()
368
+
369
+ # ------------------------------------------------------------------
370
+ # history — full conversation transcript for a peer
371
+ # ------------------------------------------------------------------
372
+
373
+ @chat.command("history")
374
+ @click.argument("peer")
375
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
376
+ @click.option("--limit", "-n", default=0, help="Show last N messages (0 = all).")
377
+ @click.option(
378
+ "--json", "as_json", is_flag=True, default=False,
379
+ help="Output raw JSON instead of a formatted table.",
380
+ )
381
+ def chat_history(peer: str, home: str, limit: int, as_json: bool):
382
+ """Show the full conversation history with PEER.
383
+
384
+ Reads the persisted conversation file and displays every message
385
+ exchanged with the named peer, oldest first. Use --limit to
386
+ restrict to the most recent N messages.
387
+
388
+ \b
389
+ Examples:
390
+ skcapstone chat history lumina
391
+ skcapstone chat history opus --limit 10
392
+ skcapstone chat history jarvis --json
393
+ """
394
+ from ..conversation_store import ConversationStore
395
+
396
+ validate_agent_name(peer)
397
+
398
+ home_path = Path(home).expanduser()
399
+ store = ConversationStore(home_path)
400
+ messages = store.load(peer)
401
+
402
+ if not messages:
403
+ console.print(f"\n [dim]No conversation history with {peer}.[/]\n")
404
+ return
405
+
406
+ if limit > 0:
407
+ messages = messages[-limit:]
408
+
409
+ if as_json:
410
+ import json as _json
411
+ console.print(_json.dumps(messages, ensure_ascii=False, indent=2))
412
+ return
413
+
414
+ title = f"Conversation with {peer} ({len(messages)} message{'s' if len(messages) != 1 else ''})"
415
+ table = Table(
416
+ show_header=True,
417
+ header_style="bold",
418
+ box=None,
419
+ padding=(0, 2),
420
+ title=title,
421
+ )
422
+ table.add_column("Role", style="cyan", width=12)
423
+ table.add_column("Message")
424
+ table.add_column("Time", style="dim", width=20)
425
+
426
+ for msg in messages:
427
+ role = msg.get("role", "?")
428
+ role_color = "green" if role == "assistant" else "cyan"
429
+ content = msg.get("content", "")
430
+ ts = str(msg.get("timestamp", ""))
431
+ if len(ts) > 19:
432
+ ts = ts[:19]
433
+ table.add_row(
434
+ f"[{role_color}]{role}[/]",
435
+ content,
436
+ ts,
437
+ )
438
+
439
+ console.print()
440
+ console.print(table)
441
+ console.print()
442
+
443
+ # ------------------------------------------------------------------
444
+ # forward — re-send a message to another peer
445
+ # ------------------------------------------------------------------
446
+
447
+ @chat.command("forward")
448
+ @click.argument("peer")
449
+ @click.argument("msg_id")
450
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
451
+ @click.option("--thread", "-t", default=None, help="Thread ID for the forwarded message.")
452
+ def chat_forward(peer: str, msg_id: str, home: str, thread: Optional[str]):
453
+ """Forward a message to another peer agent.
454
+
455
+ Looks up MSG_ID in the local inbox and forwards it to PEER,
456
+ preserving the original sender and timestamp in the forward
457
+ envelope. The forwarding agent is recorded as the new sender.
458
+
459
+ \b
460
+ Examples:
461
+ skcapstone chat forward opus msg-abc123
462
+ skcapstone chat forward lumina msg-xyz --thread fwd-thread-01
463
+ """
464
+ from ..chat import AgentChat
465
+
466
+ validate_agent_name(peer)
467
+
468
+ home_path = Path(home).expanduser()
469
+ runtime = get_runtime(home_path)
470
+ identity = runtime.manifest.name or "unknown"
471
+
472
+ agent_chat = AgentChat(home=home_path, identity=identity)
473
+
474
+ messages = agent_chat.get_inbox(limit=200)
475
+ original = next(
476
+ (m for m in messages if m.get("message_id") == msg_id),
477
+ None,
478
+ )
479
+
480
+ if original is None:
481
+ console.print(f"\n [red]Message not found:[/] {msg_id}\n")
482
+ sys.exit(1)
483
+
484
+ result = agent_chat.forward(original, peer, thread_id=thread)
485
+
486
+ console.print("")
487
+ if result["delivered"]:
488
+ console.print(
489
+ f" [green]Forwarded[/] to [cyan]{peer}[/] via {result['transport']} "
490
+ f"[dim](id: {result['forwarded_id']})[/]"
491
+ )
492
+ elif result["stored"]:
493
+ console.print(
494
+ f" [yellow]Stored locally[/] for [cyan]{peer}[/] "
495
+ f"[dim](id: {result['forwarded_id']})[/]"
496
+ )
497
+ if result.get("error"):
498
+ console.print(f" [dim]{result['error']}[/]")
499
+ else:
500
+ console.print(f" [red]Failed[/] — {result.get('error', 'unknown error')}")
501
+ console.print("")
502
+
503
+ # ------------------------------------------------------------------
504
+ # summary — LLM-powered conversation summarizer
505
+ # ------------------------------------------------------------------
506
+
507
+ @chat.command("summary")
508
+ @click.argument("peer")
509
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
510
+ @click.option(
511
+ "--last", "-n", default=20, show_default=True,
512
+ help="Number of recent messages to include in the summary.",
513
+ )
514
+ @click.option(
515
+ "--show-stored", is_flag=True, default=False,
516
+ help="Show the previously stored summary instead of generating a new one.",
517
+ )
518
+ def chat_summary(peer: str, home: str, last: int, show_stored: bool):
519
+ """Summarize a conversation with PEER using the LLM.
520
+
521
+ Reads the last N messages with PEER, calls the local LLM to
522
+ produce a 2-3 sentence summary, and stores it under
523
+ ~/.skcapstone/summaries/{peer}.json for future reference.
524
+
525
+ \b
526
+ Examples:
527
+ skcapstone chat summary lumina
528
+ skcapstone chat summary opus --last 50
529
+ skcapstone chat summary lumina --show-stored
530
+ """
531
+ from ..conversation_summarizer import ConversationSummarizer
532
+
533
+ validate_agent_name(peer)
534
+ home_path = Path(home).expanduser()
535
+
536
+ summarizer = ConversationSummarizer(home=home_path)
537
+
538
+ if show_stored:
539
+ stored = summarizer.load_summary(peer)
540
+ if stored is None:
541
+ console.print(f"\n [yellow]No stored summary for[/] [cyan]{peer}[/].\n")
542
+ console.print(" Run without --show-stored to generate one.\n")
543
+ return
544
+ console.print(f"\n[bold]Stored summary for [cyan]{peer}[/][/]")
545
+ console.print(f"[dim]{stored.generated_at[:19]} ({stored.message_count} messages)[/]\n")
546
+ console.print(stored.text)
547
+ console.print()
548
+ return
549
+
550
+ console.print(f"\n[dim]Summarizing last {last} messages with {peer}...[/]")
551
+ with console.status("[dim]calling LLM...[/]"):
552
+ try:
553
+ result = summarizer.summarize(peer, n=last)
554
+ except ValueError as exc:
555
+ console.print(f"\n [red]Error:[/] {exc}\n")
556
+ return
557
+
558
+ console.print(f"\n[bold]Summary of conversation with [cyan]{peer}[/][/]")
559
+ console.print(f"[dim]{result.generated_at[:19]} ({result.message_count} messages summarized)[/]\n")
560
+ console.print(result.text)
561
+ console.print(f"\n[dim]Saved to: {home_path}/summaries/{peer}.json[/]\n")
562
+
563
+ # ------------------------------------------------------------------
564
+ # live — alias for open (backwards compat)
565
+ # ------------------------------------------------------------------
566
+
567
+ @chat.command("live")
568
+ @click.argument("peer")
569
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
570
+ @click.option("--poll-interval", default=2.0, help="Seconds between inbox polls (default: 2).")
571
+ @click.option("--thread", "-t", default=None, help="Starting thread ID.")
572
+ @click.pass_context
573
+ def chat_live(ctx, peer: str, home: str, poll_interval: float, thread: Optional[str]):
574
+ """Start a live interactive chat session with a peer.
575
+
576
+ Alias for 'skcapstone chat open'. Uses prompt_toolkit when
577
+ available, falls back to plain readline.
578
+
579
+ \b
580
+ Examples:
581
+ skcapstone chat live lumina
582
+ skcapstone chat live opus --poll-interval 5
583
+ """
584
+ ctx.invoke(chat_open, peer=peer, home=home, thread=thread, poll_interval=poll_interval)
@@ -0,0 +1,64 @@
1
+ """Shell completions commands: install, show, uninstall."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+
7
+ import click
8
+
9
+ from ._common import console
10
+
11
+
12
+ def register_completions_commands(main: click.Group) -> None:
13
+ """Register the completions command group."""
14
+
15
+ @main.group()
16
+ def completions():
17
+ """Shell tab completion — sovereign autocomplete.
18
+
19
+ Install, show, or remove tab completion scripts for
20
+ bash, zsh, and fish.
21
+ """
22
+
23
+ @completions.command("install")
24
+ @click.option("--shell", "shell_name", default=None, type=click.Choice(["bash", "zsh", "fish"]))
25
+ def completions_install(shell_name):
26
+ """Install tab completion for your shell."""
27
+ from ..completions import install_completions
28
+
29
+ result = install_completions(shell=shell_name)
30
+
31
+ if not result.get("success"):
32
+ console.print(f"\n [red]{result.get('error', 'Install failed')}[/]\n")
33
+ sys.exit(1)
34
+
35
+ console.print(f"\n [green]Tab completion installed for {result['shell']}[/]")
36
+ console.print(f" Script: {result['script_path']}")
37
+ if result.get("rc_updated"):
38
+ console.print(f" RC updated: {result.get('rc_path')}")
39
+ console.print(f" [dim]Restart your shell or run: source {result['script_path']}[/]\n")
40
+
41
+ @completions.command("show")
42
+ @click.option("--shell", "shell_name", default=None, type=click.Choice(["bash", "zsh", "fish"]))
43
+ def completions_show(shell_name):
44
+ """Print the completion script to stdout."""
45
+ from ..completions import detect_shell, generate_script
46
+
47
+ shell = shell_name or detect_shell() or "bash"
48
+ click.echo(generate_script(shell))
49
+
50
+ @completions.command("uninstall")
51
+ @click.option("--shell", "shell_name", default=None, type=click.Choice(["bash", "zsh", "fish"]))
52
+ def completions_uninstall(shell_name):
53
+ """Remove installed completion scripts."""
54
+ from ..completions import uninstall_completions
55
+
56
+ result = uninstall_completions(shell=shell_name)
57
+
58
+ if result["removed"]:
59
+ for path in result["removed"]:
60
+ console.print(f" [green]Removed:[/] {path}")
61
+ else:
62
+ console.print(" [dim]No completion scripts found to remove.[/]")
63
+ console.print(f" [dim]{result['note']}[/]")
64
+ console.print()