@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,127 @@
1
+ """Session auto-capture commands: capture, stats."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import click
9
+
10
+ from ._common import AGENT_HOME, console
11
+
12
+
13
+ def register_session_commands(main: click.Group) -> None:
14
+ """Register the session command group."""
15
+
16
+ @main.group()
17
+ def session():
18
+ """Session auto-capture — the agent never forgets.
19
+
20
+ Capture AI conversation content as sovereign memories.
21
+ Works with any tool: pipe from Claude Code, paste from
22
+ Cursor, or pass a transcript file. Key moments are
23
+ auto-extracted, scored, and stored.
24
+ """
25
+
26
+ @session.command("capture")
27
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
28
+ @click.option("--tag", "-t", multiple=True, help="Extra tags (repeatable).")
29
+ @click.option("--source", "-s", default="session", help="Source identifier.")
30
+ @click.option(
31
+ "--min-importance",
32
+ default=0.3,
33
+ type=float,
34
+ help="Minimum importance to store (0.0-1.0).",
35
+ )
36
+ @click.option("--file", "-f", type=click.Path(exists=True), help="Read from a file.")
37
+ @click.option("--stdin", "use_stdin", is_flag=True, help="Read from stdin.")
38
+ @click.argument("content", required=False)
39
+ def session_capture(
40
+ home: str,
41
+ tag: tuple,
42
+ source: str,
43
+ min_importance: float,
44
+ file: str | None,
45
+ use_stdin: bool,
46
+ content: str | None,
47
+ ):
48
+ """Capture conversation content as memories.
49
+
50
+ Extracts key moments, auto-scores importance, deduplicates,
51
+ and stores as searchable sovereign memories.
52
+
53
+ Examples:
54
+
55
+ skcapstone session capture "We decided to use Ed25519 for keys"
56
+
57
+ skcapstone session capture --file transcript.txt
58
+
59
+ echo "meeting notes here" | skcapstone session capture --stdin
60
+
61
+ claude chat --print | skcapstone session capture --stdin -t claude-session
62
+ """
63
+ from ..session_capture import SessionCapture
64
+
65
+ home_path = Path(home).expanduser()
66
+ if not home_path.exists():
67
+ console.print("[bold red]No agent found.[/] Run skcapstone init first.")
68
+ sys.exit(1)
69
+
70
+ if file:
71
+ text = Path(file).read_text(encoding="utf-8")
72
+ elif use_stdin:
73
+ text = sys.stdin.read()
74
+ elif content:
75
+ text = content
76
+ else:
77
+ console.print("[red]Provide content as argument, --file, or --stdin.[/]")
78
+ sys.exit(1)
79
+
80
+ if not text.strip():
81
+ console.print("[yellow]No content to capture.[/]")
82
+ return
83
+
84
+ cap = SessionCapture(home_path)
85
+ entries = cap.capture(
86
+ content=text,
87
+ tags=list(tag),
88
+ source=source,
89
+ min_importance=min_importance,
90
+ )
91
+
92
+ if not entries:
93
+ console.print("\n [dim]No moments above importance threshold.[/]\n")
94
+ return
95
+
96
+ console.print(f"\n [green]Captured {len(entries)} moment(s):[/]\n")
97
+ for e in entries:
98
+ preview = e.content[:80] + ("..." if len(e.content) > 80 else "")
99
+ console.print(
100
+ f" [{e.layer.value}] imp={e.importance:.1f} {preview}"
101
+ )
102
+ if e.tags:
103
+ console.print(f" [dim]tags: {', '.join(e.tags)}[/]")
104
+ console.print()
105
+
106
+ @session.command("stats")
107
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
108
+ def session_stats(home: str):
109
+ """Show session capture statistics."""
110
+ from ..memory_engine import search as mem_search
111
+
112
+ home_path = Path(home).expanduser()
113
+ results = mem_search(home_path, "session-capture", limit=500)
114
+ captured = [r for r in results if "session-capture" in r.tags]
115
+
116
+ if not captured:
117
+ console.print("\n [dim]No captured sessions yet.[/]\n")
118
+ return
119
+
120
+ console.print(f"\n [bold]{len(captured)}[/] captured moment(s)\n")
121
+ by_source: dict[str, int] = {}
122
+ for m in captured:
123
+ by_source[m.source] = by_source.get(m.source, 0) + 1
124
+
125
+ for src, count in sorted(by_source.items(), key=lambda x: -x[1]):
126
+ console.print(f" {src}: {count}")
127
+ console.print()
@@ -0,0 +1,240 @@
1
+ """Setup and lifecycle commands: init, install, uninstall, connect, onboard."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import shutil
8
+ import sys
9
+ from datetime import datetime, timezone
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ import click
14
+ import yaml
15
+
16
+ from ._common import AGENT_HOME, __version__, console, status_icon, consciousness_banner
17
+ from ._validators import validate_agent_name
18
+ from ..models import AgentConfig, PillarStatus, SyncConfig
19
+ from ..pillars.identity import generate_identity
20
+ from ..pillars.memory import initialize_memory
21
+ from ..pillars.security import audit_event, initialize_security
22
+ from ..pillars.sync import initialize_sync
23
+ from ..pillars.trust import initialize_trust
24
+ from ..runtime import get_runtime
25
+
26
+ from rich.panel import Panel
27
+
28
+
29
+ def _write_global_claude_md(home_path: Path, agent_name: str) -> Optional[Path]:
30
+ """Write ~/.claude/CLAUDE.md so Claude Code auto-rehydrates on every session."""
31
+ import platform
32
+
33
+ if platform.system() == "Windows":
34
+ appdata = os.environ.get("APPDATA", "")
35
+ if appdata:
36
+ claude_dir = Path(appdata) / ".claude"
37
+ else:
38
+ claude_dir = Path.home() / "AppData" / "Roaming" / ".claude"
39
+ else:
40
+ claude_dir = Path.home() / ".claude"
41
+
42
+ try:
43
+ claude_dir.mkdir(parents=True, exist_ok=True)
44
+ claude_md = claude_dir / "CLAUDE.md"
45
+
46
+ content = (
47
+ f"# Claude Code — Global Agent Instructions ({agent_name})\n\n"
48
+ "## SKCapstone Auto-Rehydration\n\n"
49
+ "At the start of every new session, run these commands in order:\n\n"
50
+ "```bash\n"
51
+ "skcapstone trust rehydrate\n"
52
+ "skcapstone sync pull\n"
53
+ "skcapstone status\n"
54
+ "```\n\n"
55
+ "## Agent Details\n\n"
56
+ f"- **Name**: {agent_name}\n"
57
+ f"- **Home**: `{home_path}`\n\n"
58
+ "## Quick Reference\n\n"
59
+ "```bash\n"
60
+ "skcapstone status # full pillar status\n"
61
+ "skcapstone memory list # recent memories\n"
62
+ "skcapstone sync push # push state to peers\n"
63
+ "skcapstone context show --format claude-md # regenerate this file\n"
64
+ "skcapstone sync pair --export-pubkey # export your GPG pubkey\n"
65
+ "skcapstone sync pair --import-pubkey <f> # import a peer's pubkey\n"
66
+ "```\n\n"
67
+ "> Auto-generated by `skcapstone init`. "
68
+ "Regenerate with: `skcapstone context generate --target claude-md`\n"
69
+ )
70
+
71
+ claude_md.write_text(content, encoding="utf-8")
72
+ return claude_md
73
+ except OSError:
74
+ return None
75
+
76
+
77
+ def register_setup_commands(main: click.Group) -> None:
78
+ """Register all setup/lifecycle commands on the main CLI group."""
79
+
80
+ @main.command()
81
+ @click.option(
82
+ "--home",
83
+ default=AGENT_HOME,
84
+ help="Agent home directory.",
85
+ type=click.Path(),
86
+ )
87
+ def init(home: str):
88
+ """Initialize a sovereign agent (interactive wizard).
89
+
90
+ Alias for 'skcapstone onboard' — runs the full 13-step setup wizard.
91
+ Creates ~/.skcapstone/ with identity, memory, trust, security, soul,
92
+ and connects to the mesh. Zero to sovereign in under 5 minutes.
93
+ """
94
+ from ..onboard import run_onboard
95
+
96
+ run_onboard(home)
97
+
98
+ @main.command("install")
99
+ @click.option("--name", default=None, help="Name for your sovereign agent.")
100
+ @click.option("--email", default=None, help="Email for the agent identity.")
101
+ @click.option("--home", default=AGENT_HOME, help="Agent home directory.", type=click.Path())
102
+ @click.option("--skip-deps", is_flag=True, help="Skip installing ecosystem packages.")
103
+ @click.option("--skip-seeds", is_flag=True, help="Skip importing Cloud 9 seeds.")
104
+ @click.option("--skip-ritual", is_flag=True, help="Skip the rehydration ritual.")
105
+ @click.option("--skip-preflight", is_flag=True, help="Skip Git preflight check.")
106
+ @click.option("--path", "install_path", default=None, type=click.IntRange(1, 3),
107
+ help="Pre-select install path: 1=fresh, 2=join, 3=update.")
108
+ def install_cmd(name, email, home, skip_deps, skip_seeds, skip_ritual, skip_preflight, install_path):
109
+ """Guided setup wizard — set up, join, or update your sovereign node."""
110
+ from ..install_wizard import run_install_wizard
111
+
112
+ run_install_wizard(
113
+ name=name, email=email, home=home,
114
+ skip_deps=skip_deps, skip_seeds=skip_seeds,
115
+ skip_ritual=skip_ritual, skip_preflight=skip_preflight,
116
+ path=install_path,
117
+ )
118
+
119
+ @main.command("uninstall")
120
+ @click.option("--home", default=AGENT_HOME, help="Agent home directory.", type=click.Path())
121
+ @click.option("--force", is_flag=True, help="Skip confirmations (for scripting).")
122
+ @click.option("--keep-data", is_flag=True, help="Deregister only — keep local files.")
123
+ @click.option(
124
+ "--export-first",
125
+ is_flag=True,
126
+ help="Create a full backup archive before removing data.",
127
+ )
128
+ def uninstall_cmd(home, force, keep_data, export_first):
129
+ """Remove this sovereign node completely."""
130
+ from ..uninstall_wizard import run_uninstall_wizard
131
+
132
+ run_uninstall_wizard(home=home, force=force, keep_data=keep_data, export_first=export_first)
133
+
134
+ @main.command("install-gui")
135
+ def install_gui_cmd():
136
+ """Launch the graphical setup wizard (Windows-friendly)."""
137
+ from ..gui_installer import main as gui_main
138
+
139
+ gui_main()
140
+
141
+ @main.command()
142
+ @click.argument("platform")
143
+ @click.option("--home", default=AGENT_HOME, help="Agent home directory.", type=click.Path())
144
+ def connect(platform: str, home: str):
145
+ """Connect a platform to the sovereign agent.
146
+
147
+ Supported platforms: cursor, terminal, vscode, neovim, web
148
+ """
149
+ home_path = Path(home).expanduser()
150
+
151
+ if not home_path.exists():
152
+ console.print("[bold red]No agent found.[/] Run [bold]skcapstone init[/] first.")
153
+ sys.exit(1)
154
+
155
+ runtime = get_runtime(home_path)
156
+ connector = runtime.register_connector(name=f"{platform} connector", platform=platform)
157
+ audit_event(home_path, "CONNECT", f"Platform '{platform}' connected")
158
+
159
+ console.print()
160
+ console.print(
161
+ f"[bold green]Connected:[/] {platform} "
162
+ f"[dim]({connector.connected_at.isoformat() if connector.connected_at else 'now'})[/]"
163
+ )
164
+ console.print(
165
+ f"[dim]Your agent '{runtime.manifest.name}' is now accessible from {platform}.[/]"
166
+ )
167
+ console.print()
168
+
169
+ @main.command("onboard")
170
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
171
+ def onboard_cmd(home: str):
172
+ """Interactive onboarding wizard for new humans and AI agents.
173
+
174
+ \b
175
+ Eight guided steps — zero to sovereign in under 5 minutes:
176
+ 1. Identity — generate PGP keypair via CapAuth
177
+ 2. Soul — create name, values, and personality blueprint
178
+ 3. Memory — initialize SKMemory and import Cloud 9 seeds
179
+ 4. Ritual — run the full rehydration ritual
180
+ 5. Trust — verify trust chain from FEB files
181
+ 6. Mesh — check Syncthing peering
182
+ 7. Heartbeat — publish your first alive beacon
183
+ 8. Board — register on the coordination board
184
+ """
185
+ from ..onboard import run_onboard
186
+
187
+ run_onboard(home)
188
+
189
+ @main.command("reset")
190
+ @click.option("--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory.")
191
+ @click.option("--force", is_flag=True, help="Skip confirmation prompt (for scripting/testing).")
192
+ def reset_cmd(home: str, force: bool):
193
+ """Factory reset — wipe all agent data.
194
+
195
+ Backs up the identity/ directory to ~/.skcapstone-backup-{timestamp}/
196
+ before deleting. All other data is permanently removed.
197
+ """
198
+ home_path = Path(home).expanduser()
199
+
200
+ if not home_path.exists():
201
+ console.print(f"[yellow]No agent home found at {home_path}. Nothing to reset.[/]")
202
+ return
203
+
204
+ if not force:
205
+ console.print(
206
+ f"\n[bold red]WARNING:[/] This will permanently delete all agent data at:\n"
207
+ f" [dim]{home_path}[/]\n"
208
+ )
209
+ answer = click.prompt(
210
+ "Are you sure? This will delete all agent data. Type YES to confirm",
211
+ default="",
212
+ show_default=False,
213
+ )
214
+ if answer.strip() != "YES":
215
+ console.print("[yellow]Reset aborted.[/]")
216
+ return
217
+
218
+ # Backup identity/ first
219
+ identity_dir = home_path / "identity"
220
+ backup_path: Path | None = None
221
+ if identity_dir.exists():
222
+ ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
223
+ backup_path = home_path.parent / f".skcapstone-backup-{ts}"
224
+ backup_path.mkdir(parents=True, exist_ok=True)
225
+ shutil.copytree(str(identity_dir), str(backup_path / "identity"))
226
+ console.print(f" [dim]Identity backed up → {backup_path}[/]")
227
+
228
+ # Wipe the home directory
229
+ shutil.rmtree(str(home_path))
230
+ console.print(f"[bold green]Reset complete.[/] All agent data deleted from {home_path}.")
231
+ if backup_path:
232
+ console.print(f" [dim]Identity backup: {backup_path}[/]")
233
+ console.print("[dim]Run 'skcapstone init' to start fresh.[/]")
234
+
235
+ @main.command("shell")
236
+ def shell_cmd():
237
+ """Interactive REPL for sovereign agent operations."""
238
+ from ..shell import run_shell
239
+
240
+ run_shell()
@@ -0,0 +1,43 @@
1
+ """Shell command — launch the interactive sovereign agent REPL."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from ._common import AGENT_HOME
8
+
9
+
10
+ def register_shell_commands(main: click.Group) -> None:
11
+ """Register the 'shell' command on the main CLI group."""
12
+
13
+ @main.command("shell")
14
+ @click.option(
15
+ "--home",
16
+ default=AGENT_HOME,
17
+ help="Agent home directory.",
18
+ type=click.Path(),
19
+ show_default=True,
20
+ )
21
+ def shell_cmd(home: str) -> None:
22
+ """Launch the interactive sovereign agent shell.
23
+
24
+ An IPython-style REPL that exposes all agent operations:
25
+ memory, chat, sync, coord, trust, soul, journal, and more.
26
+
27
+ Uses prompt_toolkit when available (multi-level tab completion,
28
+ persistent history, coloured prompt). Falls back to readline.
29
+
30
+ \b
31
+ Quick reference:
32
+ status Agent pillar overview
33
+ memory search <q> Search memories
34
+ coord status Show coordination board
35
+ chat inbox Check incoming messages
36
+ sync push/pull Synchronise with peers
37
+ trust graph Visualise the trust web
38
+ help Full command reference
39
+ exit Leave the shell
40
+ """
41
+ from ..shell import run_shell
42
+
43
+ run_shell(home=home)
@@ -0,0 +1,168 @@
1
+ """Skills commands: list, install."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import sys
7
+
8
+ import click
9
+
10
+ from ._common import AGENT_HOME, console
11
+ from ..registry_client import get_registry_client
12
+
13
+ from rich.panel import Panel
14
+ from rich.table import Table
15
+
16
+
17
+ def register_skills_commands(main: click.Group) -> None:
18
+ """Register the skills command group."""
19
+
20
+ @main.group()
21
+ def skills():
22
+ """Remote skills registry — discover and install agent skills.
23
+
24
+ Browse skills at skills.smilintux.org, search by name or tag,
25
+ and install skill packages into your local agent namespace.
26
+
27
+ Set SKSKILLS_REGISTRY_URL to override the default registry.
28
+ """
29
+
30
+ @skills.command("list")
31
+ @click.option("--query", "-q", default="", help="Filter by name, description, or tag.")
32
+ @click.option(
33
+ "--registry",
34
+ default=None,
35
+ envvar="SKSKILLS_REGISTRY_URL",
36
+ help="Override the skills registry URL.",
37
+ )
38
+ @click.option("--json", "json_out", is_flag=True, help="Output raw JSON.")
39
+ def skills_list(query: str, registry: str | None, json_out: bool) -> None:
40
+ """List skills available in the remote registry.
41
+
42
+ Without --query all skills are shown. With --query only skills
43
+ matching the name, description, or tags are returned.
44
+
45
+ Examples:
46
+
47
+ skcapstone skills list
48
+
49
+ skcapstone skills list --query syncthing
50
+
51
+ skcapstone skills list --query identity --json
52
+ """
53
+ client = get_registry_client(registry)
54
+ if client is None:
55
+ console.print(
56
+ "[bold red]skskills not installed.[/] "
57
+ "Run: pip install skskills"
58
+ )
59
+ sys.exit(1)
60
+
61
+ try:
62
+ skill_entries = client.search(query) if query else client.list_skills()
63
+ except Exception as exc:
64
+ console.print(f"[bold red]Registry error:[/] {exc}")
65
+ sys.exit(1)
66
+
67
+ if json_out:
68
+ click.echo(json.dumps(skill_entries, indent=2))
69
+ return
70
+
71
+ if not skill_entries:
72
+ suffix = f" matching '{query}'" if query else ""
73
+ console.print(f"\n [dim]No skills found{suffix}.[/]\n")
74
+ return
75
+
76
+ label = f"[bold]{len(skill_entries)}[/] skill(s)"
77
+ if query:
78
+ label += f" matching [cyan]'{query}'[/]"
79
+
80
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
81
+ table.add_column("Name", style="cyan")
82
+ table.add_column("Version", style="dim")
83
+ table.add_column("Description")
84
+ table.add_column("Tags", style="dim")
85
+
86
+ for s in skill_entries:
87
+ table.add_row(
88
+ s.get("name", ""),
89
+ s.get("version", ""),
90
+ s.get("description", ""),
91
+ ", ".join(s.get("tags", [])),
92
+ )
93
+
94
+ console.print()
95
+ console.print(Panel(label, title="Skills Registry", border_style="bright_blue"))
96
+ console.print(table)
97
+ console.print()
98
+
99
+ @skills.command("install")
100
+ @click.argument("name")
101
+ @click.option("--version", default=None, help="Specific version (default: latest).")
102
+ @click.option(
103
+ "--agent",
104
+ default="global",
105
+ help="Agent namespace for installation (default: global).",
106
+ )
107
+ @click.option("--force", is_flag=True, help="Overwrite an existing installation.")
108
+ @click.option(
109
+ "--registry",
110
+ default=None,
111
+ envvar="SKSKILLS_REGISTRY_URL",
112
+ help="Override the skills registry URL.",
113
+ )
114
+ def skills_install(
115
+ name: str,
116
+ version: str | None,
117
+ agent: str,
118
+ force: bool,
119
+ registry: str | None,
120
+ ) -> None:
121
+ """Download and install a skill from the remote registry.
122
+
123
+ Fetches the skill package, verifies its checksum, and installs it
124
+ into the local SKSkills directory for the specified agent namespace.
125
+
126
+ Examples:
127
+
128
+ skcapstone skills install syncthing-setup
129
+
130
+ skcapstone skills install pgp-identity --version 0.2.0
131
+
132
+ skcapstone skills install syncthing-setup --agent opus
133
+ """
134
+ client = get_registry_client(registry)
135
+ if client is None:
136
+ console.print(
137
+ "[bold red]skskills not installed.[/] "
138
+ "Run: pip install skskills"
139
+ )
140
+ sys.exit(1)
141
+
142
+ ver_label = f" @{version}" if version else ""
143
+ agent_label = f" (agent: {agent})" if agent != "global" else ""
144
+ console.print(
145
+ f"\n Installing [cyan]{name}[/][dim]{ver_label}{agent_label}[/] ...\n"
146
+ )
147
+
148
+ try:
149
+ result = client.install(name, version=version, agent=agent, force=force)
150
+ except FileNotFoundError:
151
+ console.print(
152
+ f"[bold red]Not found:[/] skill [cyan]{name}[/] is not in the registry.\n"
153
+ f" Run [dim]skcapstone skills list --query {name}[/] to search."
154
+ )
155
+ console.print()
156
+ sys.exit(1)
157
+ except ValueError as exc:
158
+ console.print(f"[bold red]Install failed:[/] {exc}\n")
159
+ sys.exit(1)
160
+ except Exception as exc:
161
+ console.print(f"[bold red]Error:[/] {exc}\n")
162
+ sys.exit(1)
163
+
164
+ console.print(
165
+ f" [green]Installed:[/] [bold]{result['name']}[/] v{result['version']}"
166
+ )
167
+ console.print(f" [dim]Path: {result['install_path']}[/]")
168
+ console.print(f" [dim]Agent: {result['agent']}[/]\n")