@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,529 @@
1
+ """GTD (Getting Things Done) inbox capture CLI commands."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import json
7
+ from pathlib import Path
8
+
9
+ import click
10
+
11
+ from ._common import AGENT_HOME, console
12
+
13
+ from rich.panel import Panel
14
+ from rich.table import Table
15
+
16
+
17
+ def register_gtd_commands(main: click.Group) -> None:
18
+ """Register the gtd command group."""
19
+
20
+ @main.group()
21
+ def gtd():
22
+ """GTD inbox capture and management.
23
+
24
+ Capture items to your GTD inbox, list inbox contents,
25
+ and view status across all GTD lists. Data lives in
26
+ ~/.skcapstone/coordination/gtd/ as JSON files.
27
+ """
28
+
29
+ @gtd.command("capture")
30
+ @click.argument("text")
31
+ @click.option("--source", "-s", default="manual",
32
+ type=click.Choice(["manual", "telegram", "email", "voice"]),
33
+ help="Where this item came from.")
34
+ @click.option("--privacy", "-p", default="private",
35
+ type=click.Choice(["private", "team", "community", "public"]),
36
+ help="Privacy level.")
37
+ @click.option("--context", "-c", default=None,
38
+ help="GTD context tag, e.g. @computer, @phone, @home.")
39
+ def gtd_capture(text, source, privacy, context):
40
+ """Capture an item to the GTD inbox.
41
+
42
+ Example: skcapstone gtd capture "Buy milk" --context @errands
43
+ """
44
+ from ..mcp_tools.gtd_tools import _make_item, _load_list, _save_list
45
+
46
+ item = _make_item(
47
+ text=text,
48
+ source=source,
49
+ privacy=privacy,
50
+ context=context,
51
+ )
52
+ inbox = _load_list("inbox")
53
+ inbox.append(item)
54
+ _save_list("inbox", inbox)
55
+
56
+ console.print()
57
+ console.print(Panel(
58
+ f"[bold green]Captured![/] ID: [cyan]{item['id']}[/]\n"
59
+ f"[dim]{item['text']}[/]\n"
60
+ f"Source: {item['source']} Privacy: {item['privacy']}"
61
+ + (f" Context: {item['context']}" if item['context'] else ""),
62
+ title="GTD Inbox", border_style="green",
63
+ ))
64
+ console.print(f" Inbox now has [bold]{len(inbox)}[/] item(s).\n")
65
+
66
+ @gtd.command("inbox")
67
+ @click.option("--limit", "-n", default=20, type=int,
68
+ help="Maximum items to show (default: 20).")
69
+ def gtd_inbox(limit):
70
+ """List current GTD inbox items (newest first)."""
71
+ from ..mcp_tools.gtd_tools import _load_list
72
+
73
+ inbox = _load_list("inbox")
74
+ inbox.sort(key=lambda x: x.get("created_at", ""), reverse=True)
75
+ items = inbox[:limit]
76
+
77
+ if not items:
78
+ console.print("\n [dim]Inbox is empty. Capture items with:[/]")
79
+ console.print(" [cyan]skcapstone gtd capture \"Your item text\"[/]\n")
80
+ return
81
+
82
+ console.print()
83
+ table = Table(
84
+ show_header=True, header_style="bold",
85
+ box=None, padding=(0, 2),
86
+ )
87
+ table.add_column("ID", style="cyan", max_width=14)
88
+ table.add_column("Text", style="bold")
89
+ table.add_column("Source", style="dim")
90
+ table.add_column("Context", style="dim")
91
+ table.add_column("Created", style="dim")
92
+
93
+ for it in items:
94
+ created = it.get("created_at", "")[:19].replace("T", " ")
95
+ table.add_row(
96
+ it.get("id", ""),
97
+ it.get("text", "")[:60],
98
+ it.get("source", ""),
99
+ it.get("context", "") or "",
100
+ created,
101
+ )
102
+
103
+ console.print(Panel(
104
+ f"[bold]Inbox:[/] {len(inbox)} total, showing {len(items)}",
105
+ title="GTD Inbox", border_style="bright_blue",
106
+ ))
107
+ console.print(table)
108
+ console.print()
109
+
110
+ @gtd.command("status")
111
+ def gtd_status():
112
+ """Summary of all GTD lists."""
113
+ from ..mcp_tools.gtd_tools import _load_list, _GTD_LISTS
114
+
115
+ console.print()
116
+ total = 0
117
+ rows = []
118
+ for list_name in _GTD_LISTS:
119
+ items = _load_list(list_name)
120
+ count = len(items)
121
+ total += count
122
+ rows.append((list_name, count))
123
+
124
+ table = Table(
125
+ show_header=True, header_style="bold",
126
+ box=None, padding=(0, 2),
127
+ )
128
+ table.add_column("List", style="bold")
129
+ table.add_column("Count", style="cyan", justify="right")
130
+
131
+ for name, count in rows:
132
+ style = "bold yellow" if count > 0 else "dim"
133
+ table.add_row(name, f"[{style}]{count}[/]")
134
+
135
+ console.print(Panel(
136
+ f"[bold]GTD Lists:[/] {total} total items across {len(rows)} lists",
137
+ title="GTD Status", border_style="bright_blue",
138
+ ))
139
+ console.print(table)
140
+ console.print()
141
+
142
+ @gtd.command("clarify")
143
+ @click.argument("item_id")
144
+ @click.option("--actionable/--not-actionable", default=True,
145
+ help="Is this item actionable?")
146
+ @click.option("--steps", "-s", default="single",
147
+ type=click.Choice(["single", "multi"]),
148
+ help="Single action or multi-step project.")
149
+ @click.option("--priority", "-p", default="medium",
150
+ type=click.Choice(["critical", "high", "medium", "low"]),
151
+ help="Priority level.")
152
+ @click.option("--energy", "-e", default="medium",
153
+ type=click.Choice(["high", "medium", "low"]),
154
+ help="Energy level required.")
155
+ @click.option("--context", "-c", default=None,
156
+ help="GTD context tag, e.g. @computer, @phone, @home.")
157
+ @click.option("--delegate-to", "-d", default=None,
158
+ help="Person or agent to delegate to (routes to waiting-for).")
159
+ def gtd_clarify(item_id, actionable, steps, priority, energy, context, delegate_to):
160
+ """Clarify an inbox item and route it to the appropriate list.
161
+
162
+ Example: skcapstone gtd clarify abc123 --actionable --steps single --priority high --context @computer
163
+ """
164
+ from ..mcp_tools.gtd_tools import _handle_gtd_clarify
165
+
166
+ result = asyncio.get_event_loop().run_until_complete(
167
+ _handle_gtd_clarify({
168
+ "item_id": item_id,
169
+ "actionable": actionable,
170
+ "steps": steps,
171
+ "priority": priority,
172
+ "energy": energy,
173
+ "context": context,
174
+ "delegate_to": delegate_to,
175
+ })
176
+ )
177
+ data = json.loads(result[0].text)
178
+
179
+ if "error" in data:
180
+ console.print(f"\n [bold red]Error:[/] {data['error']}\n")
181
+ return
182
+
183
+ console.print()
184
+ console.print(Panel(
185
+ f"[bold green]Clarified![/] ID: [cyan]{data['id']}[/]\n"
186
+ f"[dim]{data['text']}[/]\n"
187
+ f"Destination: [bold]{data['destination']}[/] "
188
+ f"Status: {data['status']} Priority: {data.get('priority', '-')} "
189
+ f"Energy: {data.get('energy', '-')}"
190
+ + (f"\nContext: {data['context']}" if data.get('context') else "")
191
+ + (f"\nDelegated to: {data['delegate_to']}" if data.get('delegate_to') else ""),
192
+ title="GTD Clarify", border_style="green",
193
+ ))
194
+ console.print()
195
+
196
+ @gtd.command("move")
197
+ @click.argument("item_id")
198
+ @click.option("--to", "destination", required=True,
199
+ type=click.Choice(["next", "project", "waiting", "someday", "reference", "done"]),
200
+ help="Destination list.")
201
+ def gtd_move(item_id, destination):
202
+ """Move a GTD item to a different list.
203
+
204
+ Example: skcapstone gtd move abc123 --to next
205
+ """
206
+ from ..mcp_tools.gtd_tools import _handle_gtd_move
207
+
208
+ result = asyncio.get_event_loop().run_until_complete(
209
+ _handle_gtd_move({
210
+ "item_id": item_id,
211
+ "destination": destination,
212
+ })
213
+ )
214
+ data = json.loads(result[0].text)
215
+
216
+ if "error" in data:
217
+ console.print(f"\n [bold red]Error:[/] {data['error']}\n")
218
+ return
219
+
220
+ console.print()
221
+ console.print(Panel(
222
+ f"[bold green]Moved![/] ID: [cyan]{data['id']}[/]\n"
223
+ f"[dim]{data['text']}[/]\n"
224
+ f"From: [bold]{data['from']}[/] -> To: [bold]{data['to']}[/]",
225
+ title="GTD Move", border_style="green",
226
+ ))
227
+ console.print()
228
+
229
+ @gtd.command("done")
230
+ @click.argument("item_id")
231
+ def gtd_done(item_id):
232
+ """Mark a GTD item as done and archive it.
233
+
234
+ Example: skcapstone gtd done abc123
235
+ """
236
+ from ..mcp_tools.gtd_tools import _handle_gtd_done
237
+
238
+ result = asyncio.get_event_loop().run_until_complete(
239
+ _handle_gtd_done({
240
+ "item_id": item_id,
241
+ })
242
+ )
243
+ data = json.loads(result[0].text)
244
+
245
+ if "error" in data:
246
+ console.print(f"\n [bold red]Error:[/] {data['error']}\n")
247
+ return
248
+
249
+ console.print()
250
+ console.print(Panel(
251
+ f"[bold green]Done![/] ID: [cyan]{data['id']}[/]\n"
252
+ f"[dim]{data['text']}[/]\n"
253
+ f"Completed: {data['completed_at'][:19].replace('T', ' ')}\n"
254
+ f"Archive now has [bold]{data['archive_count']}[/] item(s).",
255
+ title="GTD Done", border_style="green",
256
+ ))
257
+ console.print()
258
+
259
+ @gtd.command("next")
260
+ @click.option("--context", "-c", default=None,
261
+ help="Filter by GTD context tag, e.g. @computer, @phone, @home.")
262
+ @click.option("--energy", "-e", default=None,
263
+ type=click.Choice(["high", "medium", "low"]),
264
+ help="Filter by energy level required.")
265
+ @click.option("--priority", "-p", default=None,
266
+ type=click.Choice(["critical", "high", "medium", "low"]),
267
+ help="Filter by priority level.")
268
+ @click.option("--limit", "-n", default=10, type=int,
269
+ help="Maximum items to show (default: 10).")
270
+ def gtd_next(context, energy, priority, limit):
271
+ """View next actions filtered by context, energy, and/or priority.
272
+
273
+ Example: skcapstone gtd next --context @computer --energy high --limit 5
274
+ """
275
+ from ..mcp_tools.gtd_tools import _handle_gtd_next
276
+
277
+ result = asyncio.get_event_loop().run_until_complete(
278
+ _handle_gtd_next({
279
+ "context": context,
280
+ "energy": energy,
281
+ "priority": priority,
282
+ "limit": limit,
283
+ })
284
+ )
285
+ data = json.loads(result[0].text)
286
+
287
+ if "error" in data:
288
+ console.print(f"\n [bold red]Error:[/] {data['error']}\n")
289
+ return
290
+
291
+ items = data.get("items", [])
292
+ if not items:
293
+ console.print("\n [dim]No next actions found matching filters.[/]")
294
+ filters = data.get("filters", {})
295
+ active = {k: v for k, v in filters.items() if v}
296
+ if active:
297
+ console.print(f" Filters: {active}\n")
298
+ else:
299
+ console.print(" [dim]Clarify inbox items to populate next actions.[/]\n")
300
+ return
301
+
302
+ console.print()
303
+ table = Table(
304
+ show_header=True, header_style="bold",
305
+ box=None, padding=(0, 2),
306
+ )
307
+ table.add_column("ID", style="cyan", max_width=14)
308
+ table.add_column("Text", style="bold")
309
+ table.add_column("Priority", style="dim")
310
+ table.add_column("Energy", style="dim")
311
+ table.add_column("Context", style="dim")
312
+ table.add_column("Created", style="dim")
313
+
314
+ for it in items:
315
+ created = it.get("created_at", "")[:19].replace("T", " ")
316
+ pri = it.get("priority", "-") or "-"
317
+ pri_style = {
318
+ "critical": "bold red",
319
+ "high": "bold yellow",
320
+ "medium": "",
321
+ "low": "dim",
322
+ }.get(pri, "")
323
+ table.add_row(
324
+ it.get("id", ""),
325
+ it.get("text", "")[:60],
326
+ f"[{pri_style}]{pri}[/]" if pri_style else pri,
327
+ it.get("energy", "-") or "-",
328
+ it.get("context", "") or "",
329
+ created,
330
+ )
331
+
332
+ console.print(Panel(
333
+ f"[bold]Next Actions:[/] {data['total']} total, showing {data['showing']}",
334
+ title="GTD Next Actions", border_style="bright_blue",
335
+ ))
336
+ console.print(table)
337
+ console.print()
338
+
339
+ @gtd.command("projects")
340
+ @click.option("--status", "-s", default="all",
341
+ type=click.Choice(["active", "stale", "all"]),
342
+ help="Filter by project status (default: all).")
343
+ @click.option("--limit", "-n", default=10, type=int,
344
+ help="Maximum items to show (default: 10).")
345
+ def gtd_projects(status, limit):
346
+ """View GTD projects with status and activity info.
347
+
348
+ Example: skcapstone gtd projects --status active
349
+ """
350
+ from ..mcp_tools.gtd_tools import _handle_gtd_projects
351
+
352
+ result = asyncio.get_event_loop().run_until_complete(
353
+ _handle_gtd_projects({
354
+ "status": status,
355
+ "limit": limit,
356
+ })
357
+ )
358
+ data = json.loads(result[0].text)
359
+
360
+ if "error" in data:
361
+ console.print(f"\n [bold red]Error:[/] {data['error']}\n")
362
+ return
363
+
364
+ projects = data.get("projects", [])
365
+ if not projects:
366
+ console.print(f"\n [dim]No projects found (filter: {data.get('filter', 'all')}).[/]\n")
367
+ return
368
+
369
+ console.print()
370
+ table = Table(
371
+ show_header=True, header_style="bold",
372
+ box=None, padding=(0, 2),
373
+ )
374
+ table.add_column("ID", style="cyan", max_width=14)
375
+ table.add_column("Text", style="bold")
376
+ table.add_column("Status", style="dim")
377
+ table.add_column("Priority", style="dim")
378
+ table.add_column("Days Since", style="dim", justify="right")
379
+ table.add_column("Context", style="dim")
380
+
381
+ for proj in projects:
382
+ status_str = proj.get("status", "")
383
+ status_style = "bold red" if status_str == "stale" else "green"
384
+ days = proj.get("days_since_activity")
385
+ days_str = str(days) if days is not None else "?"
386
+ table.add_row(
387
+ proj.get("id", ""),
388
+ proj.get("text", "")[:60],
389
+ f"[{status_style}]{status_str}[/]",
390
+ proj.get("priority", "-") or "-",
391
+ days_str,
392
+ proj.get("context", "") or "",
393
+ )
394
+
395
+ console.print(Panel(
396
+ f"[bold]Projects:[/] {data['total']} total, showing {data['showing']} (filter: {data.get('filter', 'all')})",
397
+ title="GTD Projects", border_style="bright_blue",
398
+ ))
399
+ console.print(table)
400
+ console.print()
401
+
402
+ @gtd.command("waiting")
403
+ @click.option("--limit", "-n", default=10, type=int,
404
+ help="Maximum items to show (default: 10).")
405
+ def gtd_waiting(limit):
406
+ """View waiting-for items sorted by longest waiting.
407
+
408
+ Example: skcapstone gtd waiting --limit 5
409
+ """
410
+ from ..mcp_tools.gtd_tools import _handle_gtd_waiting
411
+
412
+ result = asyncio.get_event_loop().run_until_complete(
413
+ _handle_gtd_waiting({
414
+ "limit": limit,
415
+ })
416
+ )
417
+ data = json.loads(result[0].text)
418
+
419
+ if "error" in data:
420
+ console.print(f"\n [bold red]Error:[/] {data['error']}\n")
421
+ return
422
+
423
+ items = data.get("items", [])
424
+ if not items:
425
+ console.print("\n [dim]No waiting-for items found.[/]\n")
426
+ return
427
+
428
+ console.print()
429
+ table = Table(
430
+ show_header=True, header_style="bold",
431
+ box=None, padding=(0, 2),
432
+ )
433
+ table.add_column("ID", style="cyan", max_width=14)
434
+ table.add_column("Text", style="bold")
435
+ table.add_column("Waiting On", style="dim")
436
+ table.add_column("Waiting Since", style="dim")
437
+ table.add_column("Days", style="dim", justify="right")
438
+
439
+ for it in items:
440
+ created = it.get("created_at", "")[:10]
441
+ days = it.get("waiting_days")
442
+ days_str = str(days) if days is not None else "?"
443
+ days_style = "bold red" if days is not None and days >= 14 else (
444
+ "bold yellow" if days is not None and days >= 7 else ""
445
+ )
446
+ table.add_row(
447
+ it.get("id", ""),
448
+ it.get("text", "")[:60],
449
+ it.get("delegate_to", "") or "?",
450
+ created,
451
+ f"[{days_style}]{days_str}[/]" if days_style else days_str,
452
+ )
453
+
454
+ console.print(Panel(
455
+ f"[bold]Waiting For:[/] {data['total']} total, showing {data['showing']}",
456
+ title="GTD Waiting For", border_style="bright_blue",
457
+ ))
458
+ console.print(table)
459
+ console.print()
460
+
461
+ @gtd.command("review")
462
+ def gtd_review():
463
+ """Generate a GTD weekly review summary.
464
+
465
+ Shows counts per list, oldest items, longest-waiting items,
466
+ and stale projects.
467
+ """
468
+ from ..mcp_tools.gtd_tools import _handle_gtd_review
469
+
470
+ result = asyncio.get_event_loop().run_until_complete(
471
+ _handle_gtd_review({})
472
+ )
473
+ data = json.loads(result[0].text)
474
+
475
+ console.print()
476
+
477
+ # Counts table
478
+ counts_table = Table(
479
+ show_header=True, header_style="bold",
480
+ box=None, padding=(0, 2),
481
+ )
482
+ counts_table.add_column("List", style="bold")
483
+ counts_table.add_column("Count", style="cyan", justify="right")
484
+
485
+ for name, count in data.get("counts", {}).items():
486
+ style = "bold yellow" if count > 0 else "dim"
487
+ counts_table.add_row(name, f"[{style}]{count}[/]")
488
+
489
+ console.print(Panel(
490
+ f"[bold]Weekly Review[/] - {data['total']} active items | "
491
+ f"{data.get('inbox_needs_clarify', 0)} inbox items need clarifying",
492
+ title="GTD Review", border_style="bright_blue",
493
+ ))
494
+ console.print(counts_table)
495
+
496
+ # Oldest items
497
+ oldest = data.get("oldest_items", [])
498
+ if oldest:
499
+ console.print("\n [bold]Oldest Items:[/]")
500
+ for it in oldest:
501
+ created = it.get("created_at", "")[:10]
502
+ console.print(
503
+ f" [cyan]{it['id']}[/] [{it['list']}] "
504
+ f"{it['text']} [dim]({created})[/]"
505
+ )
506
+
507
+ # Longest waiting
508
+ waiting = data.get("longest_waiting", [])
509
+ if waiting:
510
+ console.print("\n [bold]Longest Waiting:[/]")
511
+ for it in waiting:
512
+ created = it.get("created_at", "")[:10]
513
+ delegate = it.get("delegate_to", "?")
514
+ console.print(
515
+ f" [cyan]{it['id']}[/] -> {delegate} "
516
+ f"{it['text']} [dim]({created})[/]"
517
+ )
518
+
519
+ # Stale projects
520
+ stale = data.get("stale_projects", [])
521
+ if stale:
522
+ console.print("\n [bold]Stale Projects (7+ days):[/]")
523
+ for it in stale:
524
+ console.print(
525
+ f" [cyan]{it['id']}[/] {it['text']} "
526
+ f"[bold red]({it['days_stale']} days)[/]"
527
+ )
528
+
529
+ console.print()
@@ -0,0 +1,81 @@
1
+ """Housekeeping CLI command: prune stale ACKs, envelopes, and seeds."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from ._common import AGENT_HOME, console
8
+
9
+ from rich.table import Table
10
+
11
+
12
+ def register_housekeeping_commands(main: click.Group) -> None:
13
+ """Register the housekeeping command."""
14
+
15
+ @main.command("housekeeping")
16
+ @click.option("--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory.")
17
+ @click.option("--skcomm-home", default="~/.skcomm", type=click.Path(), help="SKComm home directory.")
18
+ @click.option("--dry-run", is_flag=True, help="Report what would be deleted without deleting.")
19
+ def housekeeping(home: str, skcomm_home: str, dry_run: bool):
20
+ """Prune stale ACKs, delivered envelopes, and old seeds.
21
+
22
+ Reclaims disk space from files that accumulate in the agent
23
+ profile but are no longer needed. Safe to run at any time.
24
+
25
+ Examples:
26
+
27
+ skcapstone housekeeping --dry-run
28
+
29
+ skcapstone housekeeping
30
+ """
31
+ from pathlib import Path
32
+ from ..housekeeping import run_housekeeping
33
+
34
+ results = run_housekeeping(
35
+ skcapstone_home=Path(home).expanduser(),
36
+ skcomm_home=Path(skcomm_home).expanduser(),
37
+ dry_run=dry_run,
38
+ )
39
+
40
+ if dry_run:
41
+ console.print("[bold yellow]DRY RUN[/] — no files deleted\n")
42
+
43
+ table = Table(title="Housekeeping Results")
44
+ table.add_column("Target", style="cyan")
45
+ table.add_column("Path", style="dim")
46
+ table.add_column("Size Before", justify="right")
47
+ table.add_column("Action", justify="right", style="green")
48
+
49
+ for key in ("acks", "comms_outbox", "seed_outbox"):
50
+ info = results.get(key, {})
51
+ path = info.get("path", "?")
52
+ size_before = _fmt_size(info.get("size_before", 0))
53
+
54
+ if dry_run:
55
+ action = f"{info.get('would_delete', 0)} would delete"
56
+ else:
57
+ deleted = info.get("deleted", 0)
58
+ freed = _fmt_size(info.get("freed", 0))
59
+ action = f"{deleted} deleted ({freed} freed)"
60
+
61
+ table.add_row(key, path, size_before, action)
62
+
63
+ console.print(table)
64
+
65
+ if not dry_run:
66
+ summary = results.get("summary", {})
67
+ console.print(
68
+ f"\n[bold green]Total:[/] {summary.get('total_deleted', 0)} files deleted, "
69
+ f"{summary.get('total_freed_mb', 0)} MB freed"
70
+ )
71
+
72
+
73
+ def _fmt_size(bytes_val: int) -> str:
74
+ """Format bytes as human-readable size."""
75
+ if bytes_val == 0:
76
+ return "0 B"
77
+ for unit in ("B", "KB", "MB", "GB"):
78
+ if bytes_val < 1024:
79
+ return f"{bytes_val:.1f} {unit}"
80
+ bytes_val /= 1024
81
+ return f"{bytes_val:.1f} TB"