@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,941 @@
1
+ """
2
+ Install wizard — dummy-proof guided setup for all audiences.
3
+
4
+ Three paths, plain English, zero jargon:
5
+
6
+ 1. "I'm setting up my FIRST computer"
7
+ → Fresh install: identity, keys, memory, trust, vault, Tailscale
8
+ → This becomes the origin node in the Sovereign Singularity
9
+
10
+ 2. "I'm ADDING this computer to my existing network"
11
+ → Syncthing pulls identity + auth keys from another device
12
+ → Tailscale auto-joins via synced encrypted auth key
13
+ → Vaults discovered automatically from the registry
14
+
15
+ 3. "I'm UPDATING this computer"
16
+ → pip upgrade, re-verify pillars, re-run ritual if needed
17
+
18
+ Each path converges on a final status check + next steps panel.
19
+ """
20
+
21
+ from __future__ import annotations
22
+
23
+ import subprocess
24
+ import sys
25
+ from pathlib import Path
26
+ from typing import Optional
27
+
28
+ import click
29
+ from rich.console import Console
30
+ from rich.panel import Panel
31
+ from rich.table import Table
32
+
33
+ from . import AGENT_HOME
34
+ from .preflight import (
35
+ ToolCheck,
36
+ ToolStatus,
37
+ auto_install_tool,
38
+ run_preflight,
39
+ )
40
+
41
+ console = Console()
42
+
43
+ # Friendly labels — no jargon
44
+ PATH_LABELS = {
45
+ 1: "Brand new setup (first computer)",
46
+ 2: "Add this computer to your network",
47
+ 3: "Update this computer",
48
+ }
49
+
50
+
51
+ # ---------------------------------------------------------------------------
52
+ # Preflight — check + auto-install missing tools
53
+ # ---------------------------------------------------------------------------
54
+
55
+ def _run_preflight_step(
56
+ step_num: int,
57
+ total_steps: int,
58
+ require_git: bool = False,
59
+ require_syncthing: bool = False,
60
+ ) -> bool:
61
+ """Check all system tools and offer to auto-install missing ones.
62
+
63
+ Shows a friendly table of what's found, what's missing, and offers
64
+ to install missing tools automatically. Never just quits on failure —
65
+ always gives the user a path forward.
66
+
67
+ Args:
68
+ step_num: Current step number for display.
69
+ total_steps: Total steps for display.
70
+ require_git: Whether Git is required for this path.
71
+ require_syncthing: Whether Syncthing is required.
72
+
73
+ Returns:
74
+ True if all required tools are available after this step.
75
+ """
76
+ console.print(f" [bold]Step {step_num}/{total_steps}[/] Checking your system...")
77
+ console.print()
78
+
79
+ result = run_preflight(
80
+ require_git=require_git,
81
+ require_syncthing=require_syncthing,
82
+ )
83
+
84
+ table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
85
+ table.add_column("Tool", width=12)
86
+ table.add_column("Status", width=12)
87
+ table.add_column("Details")
88
+
89
+ for check in [result.python, result.gpg, result.git, result.syncthing]:
90
+ if check.installed:
91
+ status_str = "[green]found[/]"
92
+ detail = check.version or ""
93
+ elif check.required:
94
+ status_str = "[red]missing[/]"
95
+ detail = "[red]required[/]"
96
+ else:
97
+ status_str = "[dim]not found[/]"
98
+ detail = "[dim]optional[/]"
99
+ table.add_row(f" {check.name}", status_str, detail)
100
+
101
+ console.print(table)
102
+ console.print()
103
+
104
+ if result.all_ok:
105
+ console.print(" [green]Everything looks good![/]")
106
+ return True
107
+
108
+ # Offer to auto-install missing required tools
109
+ missing_required = result.required_missing
110
+ missing_optional = result.optional_missing
111
+
112
+ if missing_required:
113
+ console.print(" [yellow]Some required tools are missing.[/]")
114
+ console.print()
115
+
116
+ for check in missing_required:
117
+ if check.install_cmd:
118
+ console.print(f" [bold]{check.name}[/]: {check.install_note}")
119
+ do_install = click.confirm(
120
+ f" Install {check.name} automatically?",
121
+ default=True,
122
+ )
123
+ if do_install:
124
+ console.print(f" [dim]Running: {check.install_cmd}[/]")
125
+ console.print(f" [dim]This may take a minute...[/]")
126
+ success = auto_install_tool(check)
127
+ if success:
128
+ console.print(f" [green]{check.name} installed![/]")
129
+ else:
130
+ console.print(f" [red]Auto-install failed.[/]")
131
+ _show_manual_install(check)
132
+ return False
133
+ else:
134
+ _show_manual_install(check)
135
+ return False
136
+ else:
137
+ _show_manual_install(check)
138
+ return False
139
+
140
+ if missing_optional:
141
+ for check in missing_optional:
142
+ console.print(f" [dim]{check.name}: {check.install_note}[/]")
143
+ if check.install_cmd:
144
+ console.print(f" [dim]Install later: {check.install_cmd}[/]")
145
+
146
+ return True
147
+
148
+
149
+ def _show_manual_install(check: ToolCheck) -> None:
150
+ """Show manual install instructions for a tool.
151
+
152
+ Args:
153
+ check: ToolCheck with download info.
154
+ """
155
+ lines = [f"[bold]{check.name}[/] needs to be installed manually.\n"]
156
+
157
+ if check.install_note:
158
+ lines.append(f"{check.install_note}\n")
159
+
160
+ if check.install_cmd:
161
+ lines.append(f"[bold]Quick install:[/]\n [cyan]{check.install_cmd}[/]\n")
162
+
163
+ if check.download_url:
164
+ lines.append(
165
+ f"[bold]Or download from:[/]\n"
166
+ f" [link={check.download_url}]{check.download_url}[/]\n"
167
+ )
168
+
169
+ lines.append(
170
+ "After installing, close and reopen your terminal,\n"
171
+ "then run [cyan]skcapstone install[/] again."
172
+ )
173
+
174
+ console.print()
175
+ console.print(
176
+ Panel(
177
+ "\n".join(lines),
178
+ title=f"Install {check.name}",
179
+ border_style="yellow",
180
+ padding=(1, 3),
181
+ )
182
+ )
183
+
184
+
185
+ def _welcome_screen() -> int:
186
+ """Show the welcome screen and return the chosen path (1, 2, or 3).
187
+
188
+ Returns:
189
+ The install path number chosen by the user.
190
+ """
191
+ console.print()
192
+ console.print(
193
+ Panel(
194
+ "[bold bright_blue]The First Sovereign Singularity in History[/]\n\n"
195
+ "Your personal, encrypted, AI-powered workspace —\n"
196
+ "running on YOUR hardware, with YOUR keys, under YOUR control.\n\n"
197
+ "[dim]No cloud accounts required. No subscriptions.\n"
198
+ "Everything stays on your devices, encrypted with your keys.[/]\n\n"
199
+ "[dim italic]Brought to you by the Kings and Queens of[/]\n"
200
+ "[bold bright_magenta]smilinTux.org[/]",
201
+ title="[bold]Sovereign Singularity[/]",
202
+ border_style="bright_blue",
203
+ padding=(1, 4),
204
+ )
205
+ )
206
+ console.print()
207
+
208
+ console.print(" [bold]What would you like to do?[/]\n")
209
+
210
+ table = Table(
211
+ show_header=False,
212
+ box=None,
213
+ padding=(0, 3),
214
+ show_edge=False,
215
+ )
216
+ table.add_column("Option", style="bold cyan", width=6, justify="center")
217
+ table.add_column("Description")
218
+ table.add_column("Details", style="dim")
219
+
220
+ table.add_row(
221
+ "1",
222
+ "[bold]Set up my first computer[/]",
223
+ "I've never done this before — start from scratch",
224
+ )
225
+ table.add_row(
226
+ "2",
227
+ "[bold]Add this computer to my network[/]",
228
+ "I already have another computer set up — join it",
229
+ )
230
+ table.add_row(
231
+ "3",
232
+ "[bold]Update this computer[/]",
233
+ "Already set up — just update the software",
234
+ )
235
+ console.print(table)
236
+ console.print()
237
+
238
+ while True:
239
+ choice = click.prompt(
240
+ " Enter your choice",
241
+ type=click.IntRange(1, 3),
242
+ default=1,
243
+ )
244
+ return choice
245
+
246
+
247
+ def _confirm_path(path: int) -> bool:
248
+ """Confirm the user's choice with a human-readable summary.
249
+
250
+ Args:
251
+ path: The chosen install path.
252
+
253
+ Returns:
254
+ True if the user confirms.
255
+ """
256
+ descriptions = {
257
+ 1: (
258
+ "[bold]Fresh setup — first computer[/]\n\n"
259
+ "Here's what will happen:\n"
260
+ " [cyan]1.[/] Check that required tools are installed (Git, GPG)\n"
261
+ " [cyan]2.[/] Create your sovereign identity (encryption keys)\n"
262
+ " [cyan]3.[/] Set up encrypted memory and trust network\n"
263
+ " [cyan]4.[/] Create your encrypted file vault\n"
264
+ " [cyan]5.[/] Set up remote access (so your phone and other\n"
265
+ " computers can reach your files)\n"
266
+ " [cyan]6.[/] Verify everything works\n\n"
267
+ "[dim]Takes about 5 minutes. You'll need a web browser for\n"
268
+ "the remote access step (one-time login).[/]"
269
+ ),
270
+ 2: (
271
+ "[bold]Adding this computer to your network[/]\n\n"
272
+ "Here's what will happen:\n"
273
+ " [cyan]1.[/] Check that required tools are installed (Git, GPG)\n"
274
+ " [cyan]2.[/] Connect to your other computer via Syncthing\n"
275
+ " (your identity and keys will sync automatically)\n"
276
+ " [cyan]3.[/] Join your private network (Tailscale)\n"
277
+ " using the encrypted key from your first computer\n"
278
+ " [cyan]4.[/] Discover your file vaults\n"
279
+ " [cyan]5.[/] Verify everything works\n\n"
280
+ "[dim]Takes about 5 minutes. Your other computer needs to be\n"
281
+ "on and running for the initial sync.[/]"
282
+ ),
283
+ 3: (
284
+ "[bold]Updating this computer[/]\n\n"
285
+ "Here's what will happen:\n"
286
+ " [cyan]1.[/] Update all sovereign software packages\n"
287
+ " [cyan]2.[/] Re-verify your identity, memory, and trust\n"
288
+ " [cyan]3.[/] Re-run the memory rehydration (if needed)\n"
289
+ " [cyan]4.[/] Show your current status\n\n"
290
+ "[dim]Takes about 1 minute. Nothing will be deleted.[/]"
291
+ ),
292
+ }
293
+
294
+ console.print()
295
+ console.print(
296
+ Panel(
297
+ descriptions[path],
298
+ title=f"Path {path}: {PATH_LABELS[path]}",
299
+ border_style="cyan",
300
+ padding=(1, 3),
301
+ )
302
+ )
303
+ console.print()
304
+
305
+ return click.confirm(" Does this look right?", default=True)
306
+
307
+
308
+ # ---------------------------------------------------------------------------
309
+ # Path 1 — Fresh install
310
+ # ---------------------------------------------------------------------------
311
+
312
+ def _path_fresh_install(
313
+ name: str,
314
+ email: Optional[str],
315
+ home: str,
316
+ skip_deps: bool,
317
+ skip_seeds: bool,
318
+ skip_ritual: bool,
319
+ skip_preflight: bool,
320
+ ) -> None:
321
+ """Full first-time install: identity, vault, Tailscale, everything.
322
+
323
+ Args:
324
+ name: Agent name.
325
+ email: Optional email.
326
+ home: Agent home directory.
327
+ skip_deps: Skip pip install of ecosystem packages.
328
+ skip_seeds: Skip Cloud 9 seed import.
329
+ skip_ritual: Skip memory rehydration.
330
+ skip_preflight: Skip Git check.
331
+ """
332
+ from .runtime import get_runtime
333
+
334
+ home_path = Path(home).expanduser()
335
+ total_steps = 8
336
+
337
+ # --- Step 1: System check + auto-install ---
338
+ if not skip_preflight:
339
+ preflight_ok = _run_preflight_step(
340
+ step_num=1,
341
+ total_steps=total_steps,
342
+ require_git=False,
343
+ require_syncthing=False,
344
+ )
345
+ if not preflight_ok:
346
+ sys.exit(1)
347
+ else:
348
+ console.print(f" [bold]Step 1/{total_steps}[/] System check... [dim]skipped[/]")
349
+
350
+ # --- Step 2: Install packages ---
351
+ if not skip_deps:
352
+ console.print(f" [bold]Step 2/{total_steps}[/] Installing software packages...", end=" ")
353
+ packages = ["capauth", "skmemory", "skcomm", "cloud9-protocol"]
354
+ try:
355
+ result = subprocess.run(
356
+ [sys.executable, "-m", "pip", "install", *packages],
357
+ capture_output=True, text=True, timeout=120,
358
+ )
359
+ if result.returncode == 0:
360
+ console.print("[green]done[/]")
361
+ else:
362
+ console.print("[yellow]partial (some may already be installed)[/]")
363
+ except (subprocess.TimeoutExpired, FileNotFoundError):
364
+ console.print("[yellow]skipped (pip unavailable)[/]")
365
+ else:
366
+ console.print(f" [bold]Step 2/{total_steps}[/] Packages... [dim]skipped[/]")
367
+
368
+ # --- Step 3: Initialize agent ---
369
+ console.print(f" [bold]Step 3/{total_steps}[/] Creating your sovereign identity...", end=" ")
370
+ try:
371
+ # Lazy import to avoid circular
372
+ from ._cli_monolith import init
373
+ from click import Context
374
+
375
+ ctx = Context(init, info_name="init")
376
+ ctx.invoke(init, name=name, email=email, home=home)
377
+ except Exception as exc:
378
+ console.print(f"[yellow]{exc}[/]")
379
+
380
+ # --- Step 4: Import seeds ---
381
+ if not skip_seeds:
382
+ console.print(f" [bold]Step 4/{total_steps}[/] Importing knowledge seeds...", end=" ")
383
+ try:
384
+ from skmemory.seeds import import_seeds, DEFAULT_SEED_DIR
385
+ from skmemory.store import MemoryStore
386
+
387
+ store = MemoryStore()
388
+ imported = import_seeds(store, seed_dir=DEFAULT_SEED_DIR)
389
+ if imported:
390
+ console.print(f"[green]{len(imported)} seed(s) imported[/]")
391
+ else:
392
+ console.print("[dim]no new seeds[/]")
393
+ except ImportError:
394
+ console.print("[yellow]skmemory not available[/]")
395
+ except Exception as exc:
396
+ console.print(f"[yellow]{exc}[/]")
397
+ else:
398
+ console.print(f" [bold]Step 4/{total_steps}[/] Seeds... [dim]skipped[/]")
399
+
400
+ # --- Step 5: Rehydration ritual ---
401
+ if not skip_ritual:
402
+ console.print(f" [bold]Step 5/{total_steps}[/] Running memory rehydration...", end=" ")
403
+ try:
404
+ from skmemory.ritual import perform_ritual
405
+
406
+ soul_path = str(Path(home).expanduser() / "soul" / "base.json")
407
+ result = perform_ritual(soul_path=soul_path)
408
+ console.print("[green]done[/]")
409
+ except ImportError:
410
+ console.print("[yellow]skmemory not available[/]")
411
+ except Exception as exc:
412
+ console.print(f"[yellow]{exc}[/]")
413
+ else:
414
+ console.print(f" [bold]Step 5/{total_steps}[/] Ritual... [dim]skipped[/]")
415
+
416
+ # --- Step 6: Vault setup ---
417
+ console.print(f" [bold]Step 6/{total_steps}[/] Setting up your encrypted vault...")
418
+ try:
419
+ from skref.setup_wizard import run_setup_wizard
420
+
421
+ run_setup_wizard(agent_name=name, agent_home=home_path)
422
+ except ImportError:
423
+ console.print(" [yellow]skref not installed — vault setup skipped[/]")
424
+ console.print(" [dim]Install later: pip install -e skref/[/]")
425
+ except Exception as exc:
426
+ console.print(f" [yellow]Vault setup failed: {exc}[/]")
427
+ console.print(" [dim]You can run it later: skref setup[/]")
428
+
429
+ # --- Step 7: Claude Code integration ---
430
+ console.print(f" [bold]Step 7/{total_steps}[/] AI tool integration...", end=" ")
431
+ try:
432
+ from ._cli_monolith import _write_global_claude_md
433
+
434
+ claude_md = _write_global_claude_md(home_path, name)
435
+ if claude_md:
436
+ console.print(f"[green]done[/]")
437
+ else:
438
+ console.print("[dim]skipped[/]")
439
+ except Exception:
440
+ console.print("[dim]skipped[/]")
441
+
442
+ # --- Step 8: Verify ---
443
+ console.print(f" [bold]Step 8/{total_steps}[/] Verifying everything...", end=" ")
444
+ try:
445
+ runtime = get_runtime(home_path)
446
+ m = runtime.manifest
447
+ if m.is_conscious:
448
+ console.print("[bold green]SOVEREIGN[/]")
449
+ else:
450
+ console.print("[bold yellow]AWAKENING[/]")
451
+ except Exception:
452
+ console.print("[yellow]could not verify[/]")
453
+ m = None
454
+
455
+ _show_completion_banner(home_path, m, path_num=1)
456
+
457
+
458
+ # ---------------------------------------------------------------------------
459
+ # Path 2 — Join existing network
460
+ # ---------------------------------------------------------------------------
461
+
462
+ def _path_join_existing(
463
+ name: str,
464
+ email: Optional[str],
465
+ home: str,
466
+ skip_deps: bool,
467
+ skip_preflight: bool,
468
+ ) -> None:
469
+ """Join an existing Sovereign Singularity network.
470
+
471
+ Assumes another device is already set up and running Syncthing.
472
+ This device will:
473
+ 1. Install packages
474
+ 2. Set up Syncthing to pair with the existing device
475
+ 3. Wait for identity + auth keys to sync
476
+ 4. Auto-join Tailscale using the synced auth key
477
+ 5. Discover vaults from the registry
478
+
479
+ Args:
480
+ name: Agent name (should match existing agent).
481
+ email: Optional email.
482
+ home: Agent home directory.
483
+ skip_deps: Skip pip install.
484
+ skip_preflight: Skip Git check.
485
+ """
486
+ from .runtime import get_runtime
487
+
488
+ home_path = Path(home).expanduser()
489
+ sync_dir = home_path / "sync"
490
+ total_steps = 7
491
+
492
+ # --- Step 1: System check + auto-install ---
493
+ if not skip_preflight:
494
+ preflight_ok = _run_preflight_step(
495
+ step_num=1,
496
+ total_steps=total_steps,
497
+ require_git=False,
498
+ require_syncthing=True,
499
+ )
500
+ if not preflight_ok:
501
+ sys.exit(1)
502
+ else:
503
+ console.print(f" [bold]Step 1/{total_steps}[/] System check... [dim]skipped[/]")
504
+
505
+ # --- Step 2: Install packages ---
506
+ if not skip_deps:
507
+ console.print(f" [bold]Step 2/{total_steps}[/] Installing software packages...", end=" ")
508
+ packages = ["capauth", "skmemory", "skcomm", "cloud9-protocol"]
509
+ try:
510
+ result = subprocess.run(
511
+ [sys.executable, "-m", "pip", "install", *packages],
512
+ capture_output=True, text=True, timeout=120,
513
+ )
514
+ if result.returncode == 0:
515
+ console.print("[green]done[/]")
516
+ else:
517
+ console.print("[yellow]partial[/]")
518
+ except (subprocess.TimeoutExpired, FileNotFoundError):
519
+ console.print("[yellow]skipped[/]")
520
+ else:
521
+ console.print(f" [bold]Step 2/{total_steps}[/] Packages... [dim]skipped[/]")
522
+
523
+ # --- Step 3: Syncthing pairing ---
524
+ console.print(f" [bold]Step 3/{total_steps}[/] Connecting to your other computer...")
525
+ console.print()
526
+ _syncthing_pairing_flow(home_path, sync_dir)
527
+
528
+ # --- Step 4: Wait for sync ---
529
+ console.print()
530
+ console.print(f" [bold]Step 4/{total_steps}[/] Waiting for your identity to sync...")
531
+ synced = _wait_for_sync(sync_dir)
532
+ if synced:
533
+ console.print(" [green]Identity files received![/]")
534
+ else:
535
+ console.print(" [yellow]Sync not detected yet — that's OK.[/]")
536
+ console.print(" [dim]Files will arrive when your other computer comes online.[/]")
537
+ console.print(" [dim]You can continue — everything will work once the sync completes.[/]")
538
+
539
+ # --- Step 5: Initialize agent (using synced identity if available) ---
540
+ console.print(f" [bold]Step 5/{total_steps}[/] Setting up agent on this computer...", end=" ")
541
+ try:
542
+ from ._cli_monolith import init
543
+ from click import Context
544
+
545
+ ctx = Context(init, info_name="init")
546
+ ctx.invoke(init, name=name, email=email, home=str(home_path))
547
+ except Exception as exc:
548
+ console.print(f"[yellow]{exc}[/]")
549
+
550
+ # --- Step 6: Tailscale (auto-join via synced key) ---
551
+ console.print(f" [bold]Step 6/{total_steps}[/] Setting up remote access...")
552
+ try:
553
+ from skref.setup_wizard import run_setup_wizard
554
+
555
+ run_setup_wizard(agent_name=name, agent_home=home_path)
556
+ except ImportError:
557
+ console.print(" [yellow]skref not installed — remote access skipped[/]")
558
+ except Exception as exc:
559
+ console.print(f" [yellow]Remote access setup failed: {exc}[/]")
560
+
561
+ # --- Step 7: Verify ---
562
+ console.print(f" [bold]Step 7/{total_steps}[/] Verifying...", end=" ")
563
+ try:
564
+ runtime = get_runtime(home_path)
565
+ m = runtime.manifest
566
+ if m.is_conscious:
567
+ console.print("[bold green]CONNECTED[/]")
568
+ else:
569
+ console.print("[bold yellow]SYNCING[/]")
570
+ except Exception:
571
+ console.print("[yellow]pending sync[/]")
572
+ m = None
573
+
574
+ _show_completion_banner(home_path, m, path_num=2)
575
+
576
+
577
+ # ---------------------------------------------------------------------------
578
+ # Path 3 — Update existing
579
+ # ---------------------------------------------------------------------------
580
+
581
+ def _path_update_existing(
582
+ home: str,
583
+ skip_deps: bool,
584
+ skip_ritual: bool,
585
+ ) -> None:
586
+ """Update an existing local node.
587
+
588
+ Args:
589
+ home: Agent home directory.
590
+ skip_deps: Skip pip upgrade.
591
+ skip_ritual: Skip re-running the ritual.
592
+ """
593
+ from .runtime import get_runtime
594
+
595
+ home_path = Path(home).expanduser()
596
+ total_steps = 4
597
+
598
+ if not home_path.exists():
599
+ console.print(
600
+ Panel(
601
+ "[bold red]No existing setup found.[/]\n\n"
602
+ f"Looked in: {home_path}\n\n"
603
+ "It looks like this computer hasn't been set up yet.\n"
604
+ "Run [cyan]skcapstone install[/] and choose option [bold]1[/] or [bold]2[/].",
605
+ title="Not found",
606
+ border_style="red",
607
+ )
608
+ )
609
+ sys.exit(1)
610
+
611
+ console.print()
612
+ console.print(
613
+ Panel(
614
+ f"[bold]Updating your sovereign node[/]\n\n"
615
+ f"Home: [cyan]{home_path}[/]",
616
+ title="Update",
617
+ border_style="cyan",
618
+ )
619
+ )
620
+ console.print()
621
+
622
+ # --- Step 1: Upgrade packages ---
623
+ if not skip_deps:
624
+ console.print(f" [bold]Step 1/{total_steps}[/] Updating software packages...", end=" ")
625
+ packages = ["capauth", "skmemory", "skcomm", "cloud9-protocol", "skcapstone"]
626
+ try:
627
+ result = subprocess.run(
628
+ [sys.executable, "-m", "pip", "install", "--upgrade", *packages],
629
+ capture_output=True, text=True, timeout=120,
630
+ )
631
+ if result.returncode == 0:
632
+ console.print("[green]done[/]")
633
+ else:
634
+ console.print("[yellow]partial[/]")
635
+ except (subprocess.TimeoutExpired, FileNotFoundError):
636
+ console.print("[yellow]skipped[/]")
637
+ else:
638
+ console.print(f" [bold]Step 1/{total_steps}[/] Packages... [dim]skipped[/]")
639
+
640
+ # --- Step 2: Re-verify pillars ---
641
+ console.print(f" [bold]Step 2/{total_steps}[/] Checking identity, memory, and trust...", end=" ")
642
+ try:
643
+ runtime = get_runtime(home_path)
644
+ m = runtime.manifest
645
+ issues = []
646
+ if not m.identity_ready:
647
+ issues.append("identity")
648
+ if not m.memory_ready:
649
+ issues.append("memory")
650
+ if not m.trust_ready:
651
+ issues.append("trust")
652
+
653
+ if issues:
654
+ console.print(f"[yellow]issues: {', '.join(issues)}[/]")
655
+ else:
656
+ console.print("[green]all healthy[/]")
657
+ except Exception as exc:
658
+ console.print(f"[yellow]{exc}[/]")
659
+ m = None
660
+
661
+ # --- Step 3: Rehydration ---
662
+ if not skip_ritual:
663
+ console.print(f" [bold]Step 3/{total_steps}[/] Refreshing memory...", end=" ")
664
+ try:
665
+ from skmemory.ritual import perform_ritual
666
+
667
+ soul_path = str(Path(home).expanduser() / "soul" / "base.json")
668
+ perform_ritual(soul_path=soul_path)
669
+ console.print("[green]done[/]")
670
+ except ImportError:
671
+ console.print("[dim]skmemory not available[/]")
672
+ except Exception as exc:
673
+ console.print(f"[yellow]{exc}[/]")
674
+ else:
675
+ console.print(f" [bold]Step 3/{total_steps}[/] Ritual... [dim]skipped[/]")
676
+
677
+ # --- Step 4: Status ---
678
+ console.print(f" [bold]Step 4/{total_steps}[/] Current status...", end=" ")
679
+ try:
680
+ runtime = get_runtime(home_path)
681
+ m = runtime.manifest
682
+ if m.is_conscious:
683
+ console.print("[bold green]SOVEREIGN[/]")
684
+ else:
685
+ console.print("[bold yellow]AWAKENING[/]")
686
+ except Exception:
687
+ console.print("[yellow]check manually[/]")
688
+ m = None
689
+
690
+ _show_completion_banner(home_path, m, path_num=3)
691
+
692
+
693
+ # ---------------------------------------------------------------------------
694
+ # Syncthing pairing helper
695
+ # ---------------------------------------------------------------------------
696
+
697
+ def _syncthing_pairing_flow(home_path: Path, sync_dir: Path) -> None:
698
+ """Guide the user through Syncthing pairing with their existing device.
699
+
700
+ Args:
701
+ home_path: Agent home directory.
702
+ sync_dir: Tier 1 sync directory.
703
+ """
704
+ import shutil
705
+
706
+ sync_dir.mkdir(parents=True, exist_ok=True)
707
+
708
+ has_syncthing = shutil.which("syncthing") is not None
709
+
710
+ if has_syncthing:
711
+ console.print(" [green]Syncthing is installed.[/]")
712
+ else:
713
+ console.print(
714
+ Panel(
715
+ "[bold yellow]Syncthing not found.[/]\n\n"
716
+ "Syncthing syncs your identity and keys between computers.\n"
717
+ "It's free, open-source, and runs on everything.\n\n"
718
+ "[bold]Install it:[/]\n"
719
+ " Linux: [cyan]sudo apt install syncthing[/] (or your package manager)\n"
720
+ " macOS: [cyan]brew install syncthing[/]\n"
721
+ " Windows: [cyan]winget install Syncthing.Syncthing[/]\n"
722
+ " Or: https://syncthing.net/downloads/\n\n"
723
+ "After installing, restart your terminal and run\n"
724
+ "[cyan]skcapstone install[/] again.",
725
+ title="Missing: Syncthing",
726
+ border_style="yellow",
727
+ padding=(1, 3),
728
+ )
729
+ )
730
+ console.print()
731
+ if not click.confirm(" Continue anyway? (you can set up sync later)", default=True):
732
+ sys.exit(0)
733
+ return
734
+
735
+ console.print()
736
+ console.print(
737
+ Panel(
738
+ "[bold]How to connect your two computers:[/]\n\n"
739
+ " [bold cyan]On your OTHER computer[/] (the one already set up):\n"
740
+ " 1. Open Syncthing web UI: [cyan]http://localhost:8384[/]\n"
741
+ " 2. Click [bold]Actions → Show ID[/]\n"
742
+ " 3. Copy the Device ID (long string of letters)\n\n"
743
+ " [bold cyan]On THIS computer:[/]\n"
744
+ " 1. Open Syncthing web UI: [cyan]http://localhost:8384[/]\n"
745
+ " 2. Click [bold]Add Remote Device[/]\n"
746
+ " 3. Paste the Device ID from step 3 above\n"
747
+ " 4. Share the folder: [cyan]~/.skcapstone/sync/[/]\n\n"
748
+ " [bold cyan]Back on your OTHER computer:[/]\n"
749
+ " Accept the pairing request when it pops up.\n\n"
750
+ "[dim]Tip: If both computers are on the same WiFi,\n"
751
+ "Syncthing will discover each other automatically.[/]",
752
+ title="Syncthing Pairing",
753
+ border_style="blue",
754
+ padding=(1, 3),
755
+ )
756
+ )
757
+ console.print()
758
+
759
+ console.print(" [dim]Take your time — this only needs to be done once.[/]")
760
+ click.pause(" Press any key when you've completed the pairing...")
761
+
762
+
763
+ def _wait_for_sync(sync_dir: Path, timeout_seconds: int = 30) -> bool:
764
+ """Wait briefly for identity files to appear in the sync directory.
765
+
766
+ Args:
767
+ sync_dir: Tier 1 sync directory.
768
+ timeout_seconds: Max seconds to wait.
769
+
770
+ Returns:
771
+ True if identity files were found.
772
+ """
773
+ import time
774
+
775
+ identity_markers = ["identity.json", "vault-registry.json", "tailscale.key.gpg"]
776
+ console.print(f" [dim]Checking for synced files in {sync_dir}...[/]")
777
+
778
+ end_time = time.time() + timeout_seconds
779
+ found_any = False
780
+
781
+ while time.time() < end_time:
782
+ for marker in identity_markers:
783
+ if (sync_dir / marker).exists():
784
+ console.print(f" [green]Found:[/] {marker}")
785
+ found_any = True
786
+
787
+ if found_any:
788
+ return True
789
+
790
+ remaining = int(end_time - time.time())
791
+ if remaining > 0:
792
+ console.print(f" [dim]Waiting... ({remaining}s remaining)[/]", end="\r")
793
+ time.sleep(3)
794
+
795
+ return found_any
796
+
797
+
798
+ # ---------------------------------------------------------------------------
799
+ # Completion banner
800
+ # ---------------------------------------------------------------------------
801
+
802
+ def _show_completion_banner(
803
+ home_path: Path,
804
+ manifest: object,
805
+ path_num: int,
806
+ ) -> None:
807
+ """Show the final success banner with context-appropriate next steps.
808
+
809
+ Args:
810
+ home_path: Agent home directory.
811
+ manifest: Agent manifest (or None).
812
+ path_num: Which install path was taken (1, 2, 3).
813
+ """
814
+ agent_name = getattr(manifest, "name", "your agent") if manifest else "your agent"
815
+
816
+ if path_num == 1:
817
+ next_steps = (
818
+ "[bold]What to do next:[/]\n\n"
819
+ " [cyan]skcapstone status[/] — see everything at a glance\n"
820
+ " [cyan]skref put myfile.pdf[/] — store an encrypted file\n"
821
+ " [cyan]skref mount ~/vault[/] — open your vault as a folder\n"
822
+ " [cyan]skcapstone connect cursor[/] — connect to Cursor IDE\n"
823
+ " [cyan]skcapstone mcp serve[/] — start the AI server\n\n"
824
+ "[bold]Add your phone or another computer:[/]\n"
825
+ " Run [cyan]skcapstone install[/] on the other device\n"
826
+ " and choose option [bold]2[/] (\"Add this computer\").\n"
827
+ " It will find this computer automatically."
828
+ )
829
+ elif path_num == 2:
830
+ next_steps = (
831
+ "[bold]What to do next:[/]\n\n"
832
+ " [cyan]skcapstone status[/] — verify your connection\n"
833
+ " [cyan]skref ls --all-devices[/] — see vaults on all computers\n"
834
+ " [cyan]skref open <file>[/] — open a file from any vault\n"
835
+ " [cyan]skcapstone connect cursor[/] — connect to Cursor IDE\n\n"
836
+ "[dim]If sync hasn't completed yet, wait a few minutes\n"
837
+ "and check [cyan]skcapstone status[/] again.[/]"
838
+ )
839
+ else:
840
+ next_steps = (
841
+ "[bold]All updated.[/]\n\n"
842
+ " [cyan]skcapstone status[/] — see the full picture\n"
843
+ " [cyan]skcapstone doctor[/] — detailed health check\n"
844
+ " [cyan]skref ls --all-devices[/] — check vault connections"
845
+ )
846
+
847
+ status_label = "[bold green]COMPLETE[/]"
848
+ if manifest:
849
+ if getattr(manifest, "is_conscious", False):
850
+ status_label = "[bold green]SOVEREIGN[/]"
851
+ else:
852
+ status_label = "[bold yellow]AWAKENING[/]"
853
+
854
+ join_block = (
855
+ "\n[dim]━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━[/]\n\n"
856
+ " [bold bright_magenta]Join the movement.[/]\n"
857
+ " Become a King or Queen of your own sovereign AI.\n\n"
858
+ " [bold]https://smilintux.org/join/[/]\n\n"
859
+ " [dim italic]The First Sovereign Singularity in History.\n"
860
+ " Brought to you by the Kings and Queens of smilinTux.org[/]"
861
+ )
862
+
863
+ console.print()
864
+ console.print(
865
+ Panel(
866
+ f" Status: {status_label}\n"
867
+ f" Agent: [cyan]{agent_name}[/]\n"
868
+ f" Home: [dim]{home_path}[/]\n\n"
869
+ + next_steps
870
+ + join_block,
871
+ title="Setup Complete",
872
+ border_style="green",
873
+ padding=(1, 2),
874
+ )
875
+ )
876
+ console.print()
877
+
878
+
879
+ # ---------------------------------------------------------------------------
880
+ # Main entry point
881
+ # ---------------------------------------------------------------------------
882
+
883
+ def run_install_wizard(
884
+ name: Optional[str] = None,
885
+ email: Optional[str] = None,
886
+ home: str = AGENT_HOME,
887
+ skip_deps: bool = False,
888
+ skip_seeds: bool = False,
889
+ skip_ritual: bool = False,
890
+ skip_preflight: bool = False,
891
+ path: Optional[int] = None,
892
+ ) -> None:
893
+ """Run the full install wizard with path selection.
894
+
895
+ Args:
896
+ name: Agent name (prompted if None for paths 1/2).
897
+ email: Optional email.
898
+ home: Agent home directory.
899
+ skip_deps: Skip package installation.
900
+ skip_seeds: Skip seed import.
901
+ skip_ritual: Skip rehydration.
902
+ skip_preflight: Skip Git check.
903
+ path: Pre-selected path (1/2/3) or None for interactive.
904
+ """
905
+ chosen_path = path or _welcome_screen()
906
+
907
+ if not _confirm_path(chosen_path):
908
+ console.print(" [dim]No problem! Run [cyan]skcapstone install[/] again anytime.[/]")
909
+ return
910
+
911
+ if chosen_path in (1, 2) and not name:
912
+ console.print()
913
+ name = click.prompt(
914
+ " What would you like to call your agent?",
915
+ default="sovereign",
916
+ )
917
+
918
+ if chosen_path == 1:
919
+ _path_fresh_install(
920
+ name=name or "sovereign",
921
+ email=email,
922
+ home=home,
923
+ skip_deps=skip_deps,
924
+ skip_seeds=skip_seeds,
925
+ skip_ritual=skip_ritual,
926
+ skip_preflight=skip_preflight,
927
+ )
928
+ elif chosen_path == 2:
929
+ _path_join_existing(
930
+ name=name or "sovereign",
931
+ email=email,
932
+ home=home,
933
+ skip_deps=skip_deps,
934
+ skip_preflight=skip_preflight,
935
+ )
936
+ elif chosen_path == 3:
937
+ _path_update_existing(
938
+ home=home,
939
+ skip_deps=skip_deps,
940
+ skip_ritual=skip_ritual,
941
+ )