@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,500 @@
1
+ """Tests for the skcapstone onboard wizard — new system-setup steps.
2
+
3
+ Covers:
4
+ - _step_prereqs(): Python/pip/Ollama detection
5
+ - _step_ollama_models(): model pull with click.confirm gating
6
+ - _step_config_files(): consciousness.yaml + model_profiles.yaml writing
7
+ - _step_systemd_service(): Linux-only, click.confirm gating
8
+ - _step_doctor_check(): doctor diagnostics output
9
+ - _step_test_consciousness(): consciousness loop test with click.confirm gating
10
+ - TOTAL_STEPS constant updated to 13
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import shutil
16
+ import subprocess
17
+ import sys
18
+ from pathlib import Path
19
+ from unittest.mock import MagicMock, patch
20
+
21
+ import pytest
22
+ from click.testing import CliRunner
23
+
24
+
25
+ # ---------------------------------------------------------------------------
26
+ # Helpers
27
+ # ---------------------------------------------------------------------------
28
+
29
+
30
+ @pytest.fixture
31
+ def tmp_home(tmp_path: Path) -> Path:
32
+ """A temporary agent home directory with required sub-dirs."""
33
+ home = tmp_path / ".skcapstone"
34
+ for d in ["identity", "memory", "trust", "security", "sync", "config"]:
35
+ (home / d).mkdir(parents=True, exist_ok=True)
36
+ return home
37
+
38
+
39
+ # ---------------------------------------------------------------------------
40
+ # TOTAL_STEPS
41
+ # ---------------------------------------------------------------------------
42
+
43
+
44
+ class TestTotalSteps:
45
+ """Ensure TOTAL_STEPS was updated to 13."""
46
+
47
+ def test_total_steps_is_13(self) -> None:
48
+ """Wizard now has 13 numbered steps."""
49
+ from skcapstone.onboard import TOTAL_STEPS
50
+
51
+ assert TOTAL_STEPS == 13
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # _step_prereqs
56
+ # ---------------------------------------------------------------------------
57
+
58
+
59
+ class TestStepPrereqs:
60
+ """Tests for _step_prereqs()."""
61
+
62
+ def test_returns_dict_with_three_keys(self) -> None:
63
+ """Always returns a dict with python/pip/ollama keys."""
64
+ from skcapstone.onboard import _step_prereqs
65
+
66
+ runner = CliRunner()
67
+ with runner.isolated_filesystem():
68
+ with patch("shutil.which", return_value="/usr/bin/pip"):
69
+ result = _step_prereqs()
70
+
71
+ assert "python" in result
72
+ assert "pip" in result
73
+ assert "ollama" in result
74
+
75
+ def test_python_ok_for_current_interpreter(self) -> None:
76
+ """Current Python should pass the >= 3.10 check."""
77
+ from skcapstone.onboard import _step_prereqs
78
+
79
+ runner = CliRunner()
80
+ with runner.isolated_filesystem():
81
+ with patch("shutil.which", side_effect=lambda t: "/usr/bin/pip" if t in ("pip", "pip3") else None):
82
+ result = _step_prereqs()
83
+
84
+ # Running under test means Python >= 3.10 in this project
85
+ assert result["python"] is (sys.version_info >= (3, 10))
86
+
87
+ def test_pip_true_when_pip_on_path(self) -> None:
88
+ """pip key is True when pip or pip3 is found via shutil.which."""
89
+ from skcapstone.onboard import _step_prereqs
90
+
91
+ def fake_which(tool: str) -> str | None:
92
+ return "/usr/bin/pip3" if tool == "pip3" else None
93
+
94
+ # Only mock shutil.which; leave sys untouched so version_info works.
95
+ with patch("shutil.which", side_effect=fake_which):
96
+ result = _step_prereqs()
97
+
98
+ assert result["pip"] is True
99
+
100
+ def test_pip_false_when_not_on_path(self) -> None:
101
+ """pip key is False when neither pip nor pip3 found."""
102
+ from skcapstone.onboard import _step_prereqs
103
+
104
+ with patch("shutil.which", return_value=None):
105
+ result = _step_prereqs()
106
+
107
+ assert result["pip"] is False
108
+
109
+ def test_ollama_detected(self) -> None:
110
+ """ollama key is True when ollama binary found."""
111
+ from skcapstone.onboard import _step_prereqs
112
+
113
+ def fake_which(tool: str) -> str | None:
114
+ return "/usr/local/bin/ollama" if tool == "ollama" else "/usr/bin/pip"
115
+
116
+ with patch("shutil.which", side_effect=fake_which), \
117
+ patch("subprocess.run", return_value=MagicMock(returncode=0, stdout="ollama version 0.5.0\n")):
118
+ result = _step_prereqs()
119
+
120
+ assert result["ollama"] is True
121
+
122
+ def test_ollama_not_found(self) -> None:
123
+ """ollama key is False when ollama binary missing."""
124
+ from skcapstone.onboard import _step_prereqs
125
+
126
+ def fake_which(tool: str) -> str | None:
127
+ if tool == "ollama":
128
+ return None
129
+ return "/usr/bin/pip"
130
+
131
+ with patch("shutil.which", side_effect=fake_which):
132
+ result = _step_prereqs()
133
+
134
+ assert result["ollama"] is False
135
+
136
+ def test_result_is_dict(self) -> None:
137
+ """_step_prereqs always returns a plain dict."""
138
+ from skcapstone.onboard import _step_prereqs
139
+
140
+ with patch("shutil.which", return_value="/usr/bin/pip"):
141
+ result = _step_prereqs()
142
+
143
+ assert isinstance(result, dict)
144
+
145
+
146
+ # ---------------------------------------------------------------------------
147
+ # _step_ollama_models
148
+ # ---------------------------------------------------------------------------
149
+
150
+
151
+ class TestStepOllamaModels:
152
+ """Tests for _step_ollama_models()."""
153
+
154
+ def test_skips_when_ollama_not_available(self) -> None:
155
+ """Returns False immediately when prereqs['ollama'] is False."""
156
+ from skcapstone.onboard import _step_ollama_models
157
+
158
+ result = _step_ollama_models({"ollama": False})
159
+
160
+ assert result is False
161
+
162
+ def test_skips_when_user_declines(self) -> None:
163
+ """Returns False when user does not confirm the pull."""
164
+ from skcapstone.onboard import _step_ollama_models
165
+
166
+ def fake_which(t: str) -> str | None:
167
+ return "/usr/local/bin/ollama" if t == "ollama" else None
168
+
169
+ with patch("shutil.which", side_effect=fake_which), \
170
+ patch("subprocess.run", return_value=MagicMock(returncode=0, stdout="")), \
171
+ patch("click.confirm", return_value=False):
172
+ result = _step_ollama_models({"ollama": True})
173
+
174
+ assert result is False
175
+
176
+ def test_returns_true_when_model_already_present(self) -> None:
177
+ """Returns True without pulling if model already in 'ollama list'."""
178
+ from skcapstone.onboard import _step_ollama_models
179
+
180
+ with patch("subprocess.run", return_value=MagicMock(returncode=0, stdout="llama3.2 2.0 GB")):
181
+ result = _step_ollama_models({"ollama": True})
182
+
183
+ assert result is True
184
+
185
+ def test_returns_true_on_successful_pull(self) -> None:
186
+ """Returns True after a successful ollama pull."""
187
+ from skcapstone.onboard import _step_ollama_models
188
+
189
+ call_count = {"n": 0}
190
+
191
+ def fake_run(cmd, **kwargs):
192
+ call_count["n"] += 1
193
+ if "list" in cmd:
194
+ return MagicMock(returncode=0, stdout="") # model not present
195
+ return MagicMock(returncode=0) # pull succeeds
196
+
197
+ with patch("subprocess.run", side_effect=fake_run), \
198
+ patch("click.confirm", return_value=True):
199
+ result = _step_ollama_models({"ollama": True})
200
+
201
+ assert result is True
202
+
203
+ def test_returns_false_on_pull_failure(self) -> None:
204
+ """Returns False when ollama pull exits non-zero."""
205
+ from skcapstone.onboard import _step_ollama_models
206
+
207
+ def fake_run(cmd, **kwargs):
208
+ if "list" in cmd:
209
+ return MagicMock(returncode=0, stdout="")
210
+ return MagicMock(returncode=1) # pull fails
211
+
212
+ with patch("subprocess.run", side_effect=fake_run), \
213
+ patch("click.confirm", return_value=True):
214
+ result = _step_ollama_models({"ollama": True})
215
+
216
+ assert result is False
217
+
218
+ def test_returns_false_on_timeout(self) -> None:
219
+ """Returns False when ollama pull times out."""
220
+ from skcapstone.onboard import _step_ollama_models
221
+
222
+ def fake_run(cmd, **kwargs):
223
+ if "list" in cmd:
224
+ return MagicMock(returncode=0, stdout="")
225
+ raise subprocess.TimeoutExpired(cmd, 600)
226
+
227
+ with patch("subprocess.run", side_effect=fake_run), \
228
+ patch("click.confirm", return_value=True):
229
+ result = _step_ollama_models({"ollama": True})
230
+
231
+ assert result is False
232
+
233
+
234
+ # ---------------------------------------------------------------------------
235
+ # _step_config_files
236
+ # ---------------------------------------------------------------------------
237
+
238
+
239
+ class TestStepConfigFiles:
240
+ """Tests for _step_config_files()."""
241
+
242
+ def test_writes_consciousness_yaml(self, tmp_home: Path) -> None:
243
+ """Creates consciousness.yaml in config/ when missing."""
244
+ from skcapstone.onboard import _step_config_files
245
+
246
+ consciousness_ok, _ = _step_config_files(tmp_home)
247
+
248
+ assert consciousness_ok is True
249
+ dest = tmp_home / "config" / "consciousness.yaml"
250
+ assert dest.exists()
251
+
252
+ def test_skips_existing_consciousness_yaml(self, tmp_home: Path) -> None:
253
+ """Does not overwrite an existing consciousness.yaml."""
254
+ from skcapstone.onboard import _step_config_files
255
+
256
+ existing = tmp_home / "config" / "consciousness.yaml"
257
+ existing.write_text("enabled: false\n", encoding="utf-8")
258
+
259
+ consciousness_ok, _ = _step_config_files(tmp_home)
260
+
261
+ assert consciousness_ok is True
262
+ assert existing.read_text(encoding="utf-8") == "enabled: false\n"
263
+
264
+ def test_writes_model_profiles_yaml(self, tmp_home: Path) -> None:
265
+ """Copies bundled model_profiles.yaml when missing."""
266
+ from skcapstone.onboard import _step_config_files
267
+ from pathlib import Path as _Path
268
+ import skcapstone.onboard as _onboard_module
269
+
270
+ bundled = _Path(_onboard_module.__file__).parent / "data" / "model_profiles.yaml"
271
+ if not bundled.exists():
272
+ pytest.skip("Bundled model_profiles.yaml not present in data/")
273
+
274
+ _, profiles_ok = _step_config_files(tmp_home)
275
+
276
+ assert profiles_ok is True
277
+ dest = tmp_home / "config" / "model_profiles.yaml"
278
+ assert dest.exists()
279
+ assert dest.stat().st_size > 0
280
+
281
+ def test_skips_existing_model_profiles(self, tmp_home: Path) -> None:
282
+ """Does not overwrite an existing model_profiles.yaml."""
283
+ from skcapstone.onboard import _step_config_files
284
+
285
+ existing = tmp_home / "config" / "model_profiles.yaml"
286
+ existing.write_text("profiles: []\n", encoding="utf-8")
287
+
288
+ _, profiles_ok = _step_config_files(tmp_home)
289
+
290
+ assert profiles_ok is True
291
+ assert existing.read_text(encoding="utf-8") == "profiles: []\n"
292
+
293
+ def test_returns_two_booleans(self, tmp_home: Path) -> None:
294
+ """Always returns a 2-tuple of booleans."""
295
+ from skcapstone.onboard import _step_config_files
296
+
297
+ result = _step_config_files(tmp_home)
298
+
299
+ assert isinstance(result, tuple)
300
+ assert len(result) == 2
301
+
302
+
303
+ # ---------------------------------------------------------------------------
304
+ # _step_systemd_service
305
+ # ---------------------------------------------------------------------------
306
+
307
+
308
+ class TestStepSystemdService:
309
+ """Tests for _step_systemd_service()."""
310
+
311
+ def test_returns_false_on_non_linux(self) -> None:
312
+ """Returns False immediately on non-Linux platforms."""
313
+ from skcapstone.onboard import _step_systemd_service
314
+
315
+ with patch("platform.system", return_value="Darwin"):
316
+ result = _step_systemd_service()
317
+
318
+ assert result is False
319
+
320
+ def test_returns_false_when_user_declines(self) -> None:
321
+ """Returns False when user does not confirm the install."""
322
+ from skcapstone.onboard import _step_systemd_service
323
+
324
+ with patch("platform.system", return_value="Linux"), \
325
+ patch("click.confirm", return_value=False):
326
+ result = _step_systemd_service()
327
+
328
+ assert result is False
329
+
330
+ def test_returns_false_when_systemd_unavailable(self) -> None:
331
+ """Returns False when systemd user session is not running."""
332
+ from skcapstone.onboard import _step_systemd_service
333
+
334
+ with patch("platform.system", return_value="Linux"), \
335
+ patch("click.confirm", return_value=True), \
336
+ patch("skcapstone.systemd.systemd_available", return_value=False):
337
+ result = _step_systemd_service()
338
+
339
+ assert result is False
340
+
341
+ def test_returns_true_on_successful_install(self) -> None:
342
+ """Returns True when systemd install succeeds."""
343
+ from skcapstone.onboard import _step_systemd_service
344
+
345
+ with patch("platform.system", return_value="Linux"), \
346
+ patch("click.confirm", return_value=True), \
347
+ patch("skcapstone.systemd.systemd_available", return_value=True), \
348
+ patch("skcapstone.systemd.install_service", return_value={"installed": True, "enabled": True}):
349
+ result = _step_systemd_service()
350
+
351
+ assert result is True
352
+
353
+ def test_returns_false_on_install_failure(self) -> None:
354
+ """Returns False when install_service reports not installed."""
355
+ from skcapstone.onboard import _step_systemd_service
356
+
357
+ with patch("platform.system", return_value="Linux"), \
358
+ patch("click.confirm", return_value=True), \
359
+ patch("skcapstone.systemd.systemd_available", return_value=True), \
360
+ patch("skcapstone.systemd.install_service", return_value={"installed": False}):
361
+ result = _step_systemd_service()
362
+
363
+ assert result is False
364
+
365
+
366
+ # ---------------------------------------------------------------------------
367
+ # _step_doctor_check
368
+ # ---------------------------------------------------------------------------
369
+
370
+
371
+ class TestStepDoctorCheck:
372
+ """Tests for _step_doctor_check()."""
373
+
374
+ def test_returns_diagnostic_report(self, tmp_home: Path) -> None:
375
+ """Returns a DiagnosticReport object."""
376
+ from skcapstone.doctor import DiagnosticReport
377
+ from skcapstone.onboard import _step_doctor_check
378
+
379
+ report = _step_doctor_check(tmp_home)
380
+
381
+ assert isinstance(report, DiagnosticReport)
382
+
383
+ def test_report_has_checks(self, tmp_home: Path) -> None:
384
+ """Report contains at least one check."""
385
+ from skcapstone.onboard import _step_doctor_check
386
+
387
+ report = _step_doctor_check(tmp_home)
388
+
389
+ assert report.total_count > 0
390
+
391
+ def test_emits_pass_fail_markers(self, tmp_home: Path, capsys) -> None:
392
+ """Output contains ✓ or ✗ check markers."""
393
+ import io
394
+ from click.testing import CliRunner
395
+ from skcapstone.onboard import _step_doctor_check
396
+
397
+ # Capture click.echo output via runner
398
+ report = _step_doctor_check(tmp_home)
399
+ # Just verify the report ran and has results
400
+ assert report.total_count > 0
401
+ assert report.passed_count >= 0
402
+
403
+
404
+ # ---------------------------------------------------------------------------
405
+ # _step_test_consciousness
406
+ # ---------------------------------------------------------------------------
407
+
408
+
409
+ class TestStepTestConsciousness:
410
+ """Tests for _step_test_consciousness()."""
411
+
412
+ def test_returns_false_when_user_declines(self, tmp_home: Path) -> None:
413
+ """Returns False when user does not confirm the test."""
414
+ from skcapstone.onboard import _step_test_consciousness
415
+
416
+ with patch("click.confirm", return_value=False):
417
+ result = _step_test_consciousness(tmp_home)
418
+
419
+ assert result is False
420
+
421
+ def test_returns_true_when_loop_responds(self, tmp_home: Path) -> None:
422
+ """Returns True when LLMBridge.generate returns a non-empty response."""
423
+ from skcapstone.onboard import _step_test_consciousness
424
+
425
+ mock_bridge = MagicMock()
426
+ mock_bridge.generate.return_value = "Hello, I am running fine."
427
+ mock_builder = MagicMock()
428
+ mock_builder.build.return_value = "system prompt"
429
+ mock_config = MagicMock()
430
+ mock_config.max_context_tokens = 8000
431
+
432
+ with patch("click.confirm", return_value=True), \
433
+ patch("skcapstone.consciousness_config.load_consciousness_config", return_value=mock_config), \
434
+ patch("skcapstone.consciousness_loop.LLMBridge", return_value=mock_bridge), \
435
+ patch("skcapstone.consciousness_loop.SystemPromptBuilder", return_value=mock_builder), \
436
+ patch("skcapstone.consciousness_loop._classify_message",
437
+ return_value=MagicMock(tags=[], estimated_tokens=10)):
438
+ result = _step_test_consciousness(tmp_home)
439
+
440
+ assert result is True
441
+
442
+ def test_returns_false_when_loop_returns_empty(self, tmp_home: Path) -> None:
443
+ """Returns False when LLMBridge.generate returns an empty string."""
444
+ from skcapstone.onboard import _step_test_consciousness
445
+
446
+ mock_bridge = MagicMock()
447
+ mock_bridge.generate.return_value = ""
448
+ mock_builder = MagicMock()
449
+ mock_builder.build.return_value = "system prompt"
450
+ mock_config = MagicMock()
451
+ mock_config.max_context_tokens = 8000
452
+
453
+ with patch("click.confirm", return_value=True), \
454
+ patch("skcapstone.consciousness_config.load_consciousness_config", return_value=mock_config), \
455
+ patch("skcapstone.consciousness_loop.LLMBridge", return_value=mock_bridge), \
456
+ patch("skcapstone.consciousness_loop.SystemPromptBuilder", return_value=mock_builder), \
457
+ patch("skcapstone.consciousness_loop._classify_message",
458
+ return_value=MagicMock(tags=[], estimated_tokens=10)):
459
+ result = _step_test_consciousness(tmp_home)
460
+
461
+ assert result is False
462
+
463
+ def test_returns_false_on_exception(self, tmp_home: Path) -> None:
464
+ """Returns False when consciousness config raises an exception."""
465
+ from skcapstone.onboard import _step_test_consciousness
466
+
467
+ with patch("click.confirm", return_value=True), \
468
+ patch("skcapstone.consciousness_config.load_consciousness_config",
469
+ side_effect=RuntimeError("no config")):
470
+ result = _step_test_consciousness(tmp_home)
471
+
472
+ assert result is False
473
+
474
+
475
+ # ---------------------------------------------------------------------------
476
+ # CLI integration: onboard --help
477
+ # ---------------------------------------------------------------------------
478
+
479
+
480
+ class TestOnboardCLI:
481
+ """Test the onboard CLI command registration."""
482
+
483
+ def test_onboard_help(self) -> None:
484
+ """skcapstone onboard --help exits 0 and mentions wizard."""
485
+ from skcapstone.cli import main
486
+
487
+ runner = CliRunner()
488
+ result = runner.invoke(main, ["onboard", "--help"])
489
+
490
+ assert result.exit_code == 0
491
+ assert "wizard" in result.output.lower() or "onboard" in result.output.lower()
492
+
493
+ def test_onboard_has_home_option(self) -> None:
494
+ """onboard --help shows --home option."""
495
+ from skcapstone.cli import main
496
+
497
+ runner = CliRunner()
498
+ result = runner.invoke(main, ["onboard", "--help"])
499
+
500
+ assert "--home" in result.output