@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,142 @@
1
+ """Context loader commands: show, generate."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import click
8
+
9
+ from ._common import AGENT_HOME, console
10
+
11
+
12
+ def register_context_commands(main: click.Group) -> None:
13
+ """Register the context command group."""
14
+
15
+ @main.group()
16
+ def context():
17
+ """Universal AI agent context loader.
18
+
19
+ Outputs agent identity, pillar status, board state, and recent
20
+ memories in formats consumable by any AI tool. Tool-agnostic:
21
+ works with Claude Code, Cursor, Windsurf, Aider, or any terminal.
22
+ """
23
+
24
+ @context.command("show")
25
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
26
+ @click.option(
27
+ "--format",
28
+ "fmt",
29
+ type=click.Choice(["text", "json", "claude-md", "cursor-rules"]),
30
+ default="text",
31
+ help="Output format (default: text).",
32
+ )
33
+ @click.option("--memories", "-n", default=10, help="Max recent memories to include.")
34
+ def context_show(home: str, fmt: str, memories: int):
35
+ """Show the agent's full context.
36
+
37
+ Pipe into any AI tool or redirect to a file:
38
+
39
+ skcapstone context show # terminal
40
+ skcapstone context show --format json # machine-readable
41
+ skcapstone context show --format claude-md # for Claude Code
42
+ skcapstone context show | claude # pipe to Claude Code CLI
43
+
44
+ Examples:
45
+ skcapstone context show --format claude-md > CLAUDE.md
46
+ skcapstone context show --format cursor-rules > .cursor/rules/agent.mdc
47
+ """
48
+ home_path = Path(home).expanduser()
49
+ if fmt == "claude-md":
50
+ from ..claude_md import generate_claude_md
51
+ click.echo(generate_claude_md(home_path, memory_limit=memories))
52
+ else:
53
+ from ..context_loader import FORMATTERS, gather_context
54
+ ctx = gather_context(home_path, memory_limit=memories)
55
+ click.echo(FORMATTERS[fmt](ctx))
56
+
57
+ @context.command("generate")
58
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
59
+ @click.option("--memories", "-n", default=10, help="Max recent memories to include.")
60
+ @click.option(
61
+ "--target",
62
+ type=click.Choice(["claude-md", "cursor-rules", "both"]),
63
+ default="both",
64
+ help="Which config file(s) to generate.",
65
+ )
66
+ def context_generate(home: str, memories: int, target: str):
67
+ """Auto-generate AI tool config files from agent context.
68
+
69
+ Writes CLAUDE.md (for Claude Code CLI) and/or
70
+ .cursor/rules/agent.mdc (for Cursor) in the current directory.
71
+
72
+ Examples:
73
+ skcapstone context generate # both files
74
+ skcapstone context generate --target claude-md # CLAUDE.md only
75
+ """
76
+ from ..context_loader import FORMATTERS, gather_context
77
+
78
+ home_path = Path(home).expanduser()
79
+ ctx = gather_context(home_path, memory_limit=memories)
80
+
81
+ cwd = Path.cwd()
82
+
83
+ if target in ("claude-md", "both"):
84
+ claude_path = cwd / "CLAUDE.md"
85
+ claude_path.write_text(FORMATTERS["claude-md"](ctx), encoding="utf-8")
86
+ console.print(f" [green]Written:[/] {claude_path}")
87
+
88
+ if target in ("cursor-rules", "both"):
89
+ rules_dir = cwd / ".cursor" / "rules"
90
+ rules_dir.mkdir(parents=True, exist_ok=True)
91
+ rules_path = rules_dir / "agent.mdc"
92
+ rules_path.write_text(FORMATTERS["cursor-rules"](ctx), encoding="utf-8")
93
+ console.print(f" [green]Written:[/] {rules_path}")
94
+
95
+ console.print()
96
+
97
+ @main.command("refresh-context")
98
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
99
+ @click.option("--memories", "-n", default=10, help="Max recent memories to embed.")
100
+ @click.option(
101
+ "--dest",
102
+ default=None,
103
+ type=click.Path(),
104
+ help="Destination path for CLAUDE.md (default: repo root or cwd).",
105
+ )
106
+ @click.option("--backup", is_flag=True, default=False, help="Rename existing CLAUDE.md to .bak before writing.")
107
+ def refresh_context(home: str, memories: int, dest: str | None, backup: bool):
108
+ """Regenerate CLAUDE.md from current agent state.
109
+
110
+ Writes CLAUDE.md to the git repository root (or cwd if not in a
111
+ git repository). Useful as a pre-commit hook or alias.
112
+
113
+ Examples:
114
+ skcapstone refresh-context
115
+ skcapstone refresh-context --dest /path/to/project/CLAUDE.md
116
+ skcapstone refresh-context --backup
117
+ """
118
+ import subprocess
119
+
120
+ from ..claude_md import write_claude_md
121
+
122
+ home_path = Path(home).expanduser()
123
+
124
+ if dest:
125
+ target = Path(dest).expanduser().resolve()
126
+ if target.is_dir():
127
+ target = target / "CLAUDE.md"
128
+ else:
129
+ # Walk up from cwd to find git root; fall back to cwd.
130
+ try:
131
+ result = subprocess.run(
132
+ ["git", "rev-parse", "--show-toplevel"],
133
+ capture_output=True,
134
+ text=True,
135
+ check=True,
136
+ )
137
+ target = Path(result.stdout.strip()) / "CLAUDE.md"
138
+ except (subprocess.CalledProcessError, FileNotFoundError):
139
+ target = Path.cwd() / "CLAUDE.md"
140
+
141
+ write_claude_md(home_path, target, memory_limit=memories, backup=backup)
142
+ console.print(f" [green]Written:[/] {target}")
@@ -0,0 +1,194 @@
1
+ """Coordination board commands: status, create, claim, complete, board, changelog, briefing."""
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
+ from ._validators import validate_agent_name, validate_task_id
12
+
13
+ from rich.panel import Panel
14
+ from rich.table import Table
15
+ from rich.text import Text
16
+
17
+
18
+ def register_coord_commands(main: click.Group) -> None:
19
+ """Register the coord command group."""
20
+
21
+ @main.group()
22
+ def coord():
23
+ """Multi-agent coordination board.
24
+
25
+ Create tasks, claim work, and track progress across
26
+ agents. All data lives in ~/.skcapstone/coordination/
27
+ and syncs via Syncthing. Conflict-free by design.
28
+ """
29
+
30
+ @coord.command("status")
31
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
32
+ def coord_status(home):
33
+ """Show the coordination board overview."""
34
+ from ..coordination import Board
35
+
36
+ home_path = Path(home).expanduser()
37
+ board = Board(home_path)
38
+ views = board.get_task_views()
39
+ agents = board.load_agents()
40
+
41
+ if not views and not agents:
42
+ console.print("\n [dim]Board is empty. Create tasks with:[/]")
43
+ console.print(" [cyan]skcapstone coord create --title 'My Task'[/]\n")
44
+ return
45
+
46
+ open_count = sum(1 for v in views if v.status.value == "open")
47
+ progress_count = sum(1 for v in views if v.status.value == "in_progress")
48
+ claimed_count = sum(1 for v in views if v.status.value == "claimed")
49
+ done_count = sum(1 for v in views if v.status.value == "done")
50
+
51
+ console.print()
52
+ console.print(Panel(
53
+ f"[bold]Tasks:[/] {len(views)} total "
54
+ f"[green]{open_count} open[/] "
55
+ f"[cyan]{claimed_count} claimed[/] "
56
+ f"[yellow]{progress_count} in progress[/] "
57
+ f"[dim]{done_count} done[/]",
58
+ title="Coordination Board", border_style="bright_blue",
59
+ ))
60
+
61
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
62
+ table.add_column("ID", style="cyan", max_width=10)
63
+ table.add_column("Title", style="bold")
64
+ table.add_column("Priority")
65
+ table.add_column("Status")
66
+ table.add_column("Assignee", style="dim")
67
+ table.add_column("Tags", style="dim")
68
+
69
+ priority_colors = {"critical": "bold red", "high": "red", "medium": "yellow", "low": "dim"}
70
+ status_colors = {"open": "green", "claimed": "cyan", "in_progress": "yellow", "done": "dim", "blocked": "red"}
71
+
72
+ for v in views:
73
+ if v.status.value == "done":
74
+ continue
75
+ t = v.task
76
+ p_style = priority_colors.get(t.priority.value, "dim")
77
+ s_style = status_colors.get(v.status.value, "dim")
78
+ table.add_row(t.id, t.title, Text(t.priority.value.upper(), style=p_style),
79
+ Text(v.status.value.upper(), style=s_style), v.claimed_by or "", ", ".join(t.tags))
80
+
81
+ console.print(table)
82
+
83
+ if agents:
84
+ console.print()
85
+ for ag in agents:
86
+ icon = {"active": "[green]ACTIVE[/]", "idle": "[yellow]IDLE[/]"}.get(ag.state.value, "[dim]OFFLINE[/]")
87
+ current = f" -> [cyan]{ag.current_task}[/]" if ag.current_task else ""
88
+ console.print(f" {icon} [bold]{ag.agent}[/]{current}")
89
+ console.print()
90
+
91
+ @coord.command("create")
92
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
93
+ @click.option("--title", required=True, help="Task title.")
94
+ @click.option("--desc", default="", help="Task description.")
95
+ @click.option("--priority", type=click.Choice(["critical", "high", "medium", "low"]), default="medium")
96
+ @click.option("--tag", multiple=True, help="Tags (repeatable).")
97
+ @click.option("--by", default="human", help="Creator name.")
98
+ @click.option("--criteria", multiple=True, help="Acceptance criteria (repeatable).")
99
+ @click.option("--dep", multiple=True, help="Dependency task IDs (repeatable).")
100
+ def coord_create(home, title, desc, priority, tag, by, criteria, dep):
101
+ """Create a new task on the board."""
102
+ from ..coordination import Board, Task, TaskPriority
103
+
104
+ validate_agent_name(by)
105
+ for d in dep:
106
+ validate_task_id(d)
107
+
108
+ home_path = Path(home).expanduser()
109
+ board = Board(home_path)
110
+ task = Task(title=title, description=desc, priority=TaskPriority(priority),
111
+ tags=list(tag), created_by=by, acceptance_criteria=list(criteria), dependencies=list(dep))
112
+ path = board.create_task(task)
113
+ console.print(f"\n [green]Created:[/] [{task.id}] {task.title}")
114
+ console.print(f" [dim]{path}[/]\n")
115
+
116
+ @coord.command("claim")
117
+ @click.argument("task_id")
118
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
119
+ @click.option("--agent", required=True, help="Agent name claiming the task.")
120
+ def coord_claim(task_id, home, agent):
121
+ """Claim a task for an agent."""
122
+ from ..coordination import Board
123
+
124
+ validate_task_id(task_id)
125
+ validate_agent_name(agent)
126
+
127
+ home_path = Path(home).expanduser()
128
+ board = Board(home_path)
129
+ try:
130
+ ag = board.claim_task(agent, task_id)
131
+ console.print(f"\n [green]Claimed:[/] [{task_id}] by [bold]{ag.agent}[/]\n")
132
+ except ValueError as e:
133
+ console.print(f"\n [red]Error:[/] {e}\n")
134
+ sys.exit(1)
135
+
136
+ @coord.command("complete")
137
+ @click.argument("task_id")
138
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
139
+ @click.option("--agent", required=True, help="Agent name completing the task.")
140
+ def coord_complete(task_id, home, agent):
141
+ """Mark a task as completed."""
142
+ from ..coordination import Board
143
+
144
+ validate_task_id(task_id)
145
+ validate_agent_name(agent)
146
+
147
+ home_path = Path(home).expanduser()
148
+ board = Board(home_path)
149
+ ag = board.complete_task(agent, task_id)
150
+ # board.complete_task() automatically mints Joules via _mint_joules_for_task
151
+ console.print(f"\n [green]Completed:[/] [{task_id}] by [bold]{ag.agent}[/]\n")
152
+
153
+ @coord.command("board")
154
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
155
+ def coord_board(home):
156
+ """Generate and display the BOARD.md overview."""
157
+ from ..coordination import Board
158
+
159
+ home_path = Path(home).expanduser()
160
+ board = Board(home_path)
161
+ path = board.write_board_md()
162
+ md = board.generate_board_md()
163
+ console.print(md)
164
+ console.print(f"\n [dim]Written to {path}[/]\n")
165
+
166
+ @coord.command("changelog")
167
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
168
+ @click.option("--output", "-o", default=None, type=click.Path(), help="Output file path.")
169
+ def coord_changelog(home, output):
170
+ """Generate CHANGELOG.md from completed board tasks."""
171
+ from ..changelog import generate_changelog, write_changelog
172
+
173
+ home_path = Path(home).expanduser()
174
+ out_path = Path(output) if output else None
175
+ path = write_changelog(home_path, out_path)
176
+
177
+ content = generate_changelog(home_path)
178
+ console.print(content[:3000])
179
+ if len(content) > 3000:
180
+ console.print(f"\n [dim]... ({len(content)} chars total)[/]")
181
+ console.print(f"\n [green]Written to {path}[/]\n")
182
+
183
+ @coord.command("briefing")
184
+ @click.option("--home", default=AGENT_HOME, type=click.Path())
185
+ @click.option("--format", "fmt", type=click.Choice(["text", "json"]), default="text")
186
+ def coord_briefing(home, fmt):
187
+ """Print the full coordination protocol for any AI agent."""
188
+ from ..coordination import get_briefing_text, get_briefing_json
189
+
190
+ home_path = Path(home).expanduser()
191
+ if fmt == "json":
192
+ click.echo(get_briefing_json(home_path))
193
+ else:
194
+ click.echo(get_briefing_text(home_path))
@@ -0,0 +1,170 @@
1
+ """Crush (charmbracelet/crush) integration commands.
2
+
3
+ Subcommands:
4
+ skcapstone crush setup — install config + soul instructions
5
+ skcapstone crush config — print the generated crush.json
6
+ skcapstone crush status — show installation status
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json
12
+
13
+ import click
14
+ from rich.console import Console
15
+ from rich.panel import Panel
16
+ from rich.table import Table
17
+
18
+ console = Console()
19
+
20
+
21
+ def register_crush_commands(main: click.Group) -> None:
22
+ """Register the crush command group."""
23
+
24
+ @main.group()
25
+ def crush():
26
+ """Crush terminal AI client integration.
27
+
28
+ Wire the charmbracelet/crush TUI to skcapstone MCP and
29
+ load your soul blueprint as its system instructions.
30
+
31
+ https://github.com/charmbracelet/crush
32
+ """
33
+
34
+ @crush.command("setup")
35
+ @click.option(
36
+ "--overwrite",
37
+ is_flag=True,
38
+ default=False,
39
+ help="Overwrite existing crush config files.",
40
+ )
41
+ def crush_setup(overwrite: bool) -> None:
42
+ """Set up Crush: write crush.json + instructions.md.
43
+
44
+ Writes:
45
+ ~/.config/crush/crush.json — MCP wiring + permissions
46
+ ~/.config/crush/instructions.md — soul blueprint as system prompt
47
+ """
48
+ from ..crush_integration import setup_crush
49
+
50
+ console.print()
51
+ with console.status(" Configuring Crush…", spinner="dots"):
52
+ result = setup_crush(overwrite=overwrite)
53
+
54
+ t = Table.grid(padding=(0, 2))
55
+ t.add_column(style="dim", width=20)
56
+ t.add_column()
57
+
58
+ if result["installed"]:
59
+ t.add_row("Binary", f"[green]{result['binary_path']}[/]")
60
+ else:
61
+ t.add_row("Binary", "[yellow]not found[/]")
62
+ if result.get("install_hint"):
63
+ t.add_row("Install with", f"[cyan]{result['install_hint']}[/]")
64
+
65
+ t.add_row("Config", f"[dim]{result['config_path']}[/]")
66
+ t.add_row("Instructions", f"[dim]{result['instructions_path']}[/]")
67
+
68
+ console.print(
69
+ Panel(
70
+ t,
71
+ title="[bold]Crush Setup Complete[/]",
72
+ border_style="bright_blue",
73
+ )
74
+ )
75
+
76
+ console.print()
77
+ console.print(
78
+ " [dim]Run crush in your project directory to start coding with your sovereign agent.[/]"
79
+ )
80
+ if not result["installed"]:
81
+ console.print()
82
+ hint = result.get("install_hint", "go install github.com/charmbracelet/crush@latest")
83
+ console.print(f" [bold]Install crush:[/] [cyan]{hint}[/]")
84
+ console.print()
85
+
86
+ @crush.command("config")
87
+ @click.option(
88
+ "--json-only",
89
+ "json_only",
90
+ is_flag=True,
91
+ default=False,
92
+ help="Print raw JSON without formatting.",
93
+ )
94
+ def crush_config(json_only: bool) -> None:
95
+ """Print the crush.json config that will be written."""
96
+ from ..crush_integration import generate_crush_config
97
+
98
+ config = generate_crush_config()
99
+ if json_only:
100
+ click.echo(json.dumps(config, indent=2))
101
+ else:
102
+ console.print()
103
+ console.print_json(json.dumps(config, indent=2))
104
+ console.print()
105
+ console.print(
106
+ f" [dim]Write this to [cyan]~/.config/crush/crush.json[/] "
107
+ f"with: skcapstone crush setup[/]"
108
+ )
109
+ console.print()
110
+
111
+ @crush.command("status")
112
+ def crush_status() -> None:
113
+ """Show Crush installation and config status."""
114
+ from ..crush_integration import (
115
+ find_crush_binary,
116
+ get_install_hint,
117
+ is_crush_installed,
118
+ )
119
+ from pathlib import Path
120
+
121
+ crush_config_dir = Path("~/.config/crush").expanduser()
122
+ crush_json = crush_config_dir / "crush.json"
123
+ instructions_md = crush_config_dir / "instructions.md"
124
+
125
+ console.print()
126
+ t = Table(
127
+ title="Crush Status",
128
+ border_style="bright_blue",
129
+ show_header=True,
130
+ )
131
+ t.add_column("Item", style="bold", width=22)
132
+ t.add_column("Status")
133
+ t.add_column("Detail", style="dim")
134
+
135
+ # Binary
136
+ binary = find_crush_binary()
137
+ if binary:
138
+ t.add_row("Binary", "[green]FOUND[/]", str(binary))
139
+ else:
140
+ hint = get_install_hint()
141
+ t.add_row("Binary", "[yellow]MISSING[/]", hint)
142
+
143
+ # crush.json
144
+ if crush_json.exists():
145
+ try:
146
+ data = json.loads(crush_json.read_text(encoding="utf-8"))
147
+ mcp_count = len(data.get("mcp", {}))
148
+ t.add_row(
149
+ "crush.json",
150
+ "[green]OK[/]",
151
+ f"{mcp_count} MCP server(s) — {str(crush_json)}",
152
+ )
153
+ except Exception:
154
+ t.add_row("crush.json", "[red]CORRUPT[/]", str(crush_json))
155
+ else:
156
+ t.add_row("crush.json", "[yellow]MISSING[/]", "run: skcapstone crush setup")
157
+
158
+ # instructions.md
159
+ if instructions_md.exists():
160
+ size = instructions_md.stat().st_size
161
+ t.add_row("instructions.md", "[green]OK[/]", f"{size} bytes — {str(instructions_md)}")
162
+ else:
163
+ t.add_row(
164
+ "instructions.md",
165
+ "[yellow]MISSING[/]",
166
+ "run: skcapstone crush setup",
167
+ )
168
+
169
+ console.print(t)
170
+ console.print()