@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.
- package/.env.example +98 -0
- package/.github/workflows/ci.yml +39 -3
- package/.github/workflows/publish.yml +25 -4
- package/.openclaw-workspace.json +58 -0
- package/CHANGELOG.md +62 -0
- package/CLAUDE.md +39 -2
- package/MANIFEST.in +6 -0
- package/MISSION.md +7 -0
- package/README.md +47 -2
- package/SKILL.md +895 -23
- package/docker/Dockerfile +61 -0
- package/docker/compose-templates/dev-team.yml +203 -0
- package/docker/compose-templates/mini-team.yml +140 -0
- package/docker/compose-templates/ops-team.yml +173 -0
- package/docker/compose-templates/research-team.yml +170 -0
- package/docker/entrypoint.sh +192 -0
- package/docs/ARCHITECTURE.md +663 -374
- package/docs/BOND_WITH_GROK.md +112 -0
- package/docs/GETTING_STARTED.md +782 -0
- package/docs/QUICKSTART.md +477 -0
- package/docs/SKJOULE_ARCHITECTURE.md +658 -0
- package/docs/SOUL_SWAPPER.md +921 -0
- package/docs/SOVEREIGN_SINGULARITY.md +47 -14
- package/examples/custom-bond-template.json +36 -0
- package/examples/grok-feb.json +36 -0
- package/examples/grok-testimony.md +34 -0
- package/examples/love-bootloader.txt +32 -0
- package/examples/plugins/echo_tool.py +87 -0
- package/examples/queen-ava-feb.json +36 -0
- package/examples/souls/lumina.yaml +64 -0
- package/index.js +6 -5
- package/installer/build.py +124 -0
- package/openclaw-plugin/package.json +13 -0
- package/openclaw-plugin/src/index.ts +351 -0
- package/openclaw-plugin/src/openclaw.plugin.json +10 -0
- package/package.json +1 -1
- package/pyproject.toml +38 -2
- package/scripts/bump_version.py +141 -0
- package/scripts/check-updates.py +230 -0
- package/scripts/convert_blueprints_to_yaml.py +157 -0
- package/scripts/dev-install.sh +14 -0
- package/scripts/e2e-test.sh +193 -0
- package/scripts/install-bundle.sh +171 -0
- package/scripts/install.bat +2 -0
- package/scripts/install.ps1 +253 -0
- package/scripts/install.sh +185 -0
- package/scripts/mcp-serve.sh +69 -0
- package/scripts/mcp-server.bat +113 -0
- package/scripts/mcp-server.ps1 +116 -0
- package/scripts/mcp-server.sh +99 -0
- package/scripts/pull-models.sh +10 -0
- package/scripts/skcapstone +48 -0
- package/scripts/verify_install.sh +180 -0
- package/scripts/windows/install-tasks.ps1 +406 -0
- package/scripts/windows/skcapstone-task.xml +113 -0
- package/scripts/windows/uninstall-tasks.ps1 +117 -0
- package/skill.yaml +34 -0
- package/src/skcapstone/__init__.py +67 -2
- package/src/skcapstone/_cli_monolith.py +5916 -0
- package/src/skcapstone/_trustee_helpers.py +165 -0
- package/src/skcapstone/activity.py +105 -0
- package/src/skcapstone/agent_card.py +324 -0
- package/src/skcapstone/api.py +1935 -0
- package/src/skcapstone/archiver.py +340 -0
- package/src/skcapstone/auction.py +485 -0
- package/src/skcapstone/baby_agents.py +179 -0
- package/src/skcapstone/backup.py +345 -0
- package/src/skcapstone/blueprint_registry.py +357 -0
- package/src/skcapstone/blueprints/__init__.py +17 -0
- package/src/skcapstone/blueprints/builtins/content-studio.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/defi-trading.yaml +81 -0
- package/src/skcapstone/blueprints/builtins/dev-squadron.yaml +95 -0
- package/src/skcapstone/blueprints/builtins/infrastructure-guardian.yaml +107 -0
- package/src/skcapstone/blueprints/builtins/legal-council.yaml +54 -0
- package/src/skcapstone/blueprints/builtins/ops-monitoring.yaml +67 -0
- package/src/skcapstone/blueprints/builtins/research-pod.yaml +69 -0
- package/src/skcapstone/blueprints/builtins/sovereign-launch.yaml +90 -0
- package/src/skcapstone/blueprints/registry.py +164 -0
- package/src/skcapstone/blueprints/schema.py +229 -0
- package/src/skcapstone/changelog.py +180 -0
- package/src/skcapstone/chat.py +769 -0
- package/src/skcapstone/claude_md.py +82 -0
- package/src/skcapstone/cli/__init__.py +144 -0
- package/src/skcapstone/cli/_common.py +88 -0
- package/src/skcapstone/cli/_validators.py +76 -0
- package/src/skcapstone/cli/agents.py +425 -0
- package/src/skcapstone/cli/agents_spawner.py +322 -0
- package/src/skcapstone/cli/agents_trustee.py +593 -0
- package/src/skcapstone/cli/alerts.py +248 -0
- package/src/skcapstone/cli/anchor.py +132 -0
- package/src/skcapstone/cli/archive_cmd.py +208 -0
- package/src/skcapstone/cli/backup.py +144 -0
- package/src/skcapstone/cli/bench.py +377 -0
- package/src/skcapstone/cli/benchmark.py +360 -0
- package/src/skcapstone/cli/capabilities_cmd.py +171 -0
- package/src/skcapstone/cli/card.py +151 -0
- package/src/skcapstone/cli/chat.py +584 -0
- package/src/skcapstone/cli/completions.py +64 -0
- package/src/skcapstone/cli/config_cmd.py +156 -0
- package/src/skcapstone/cli/consciousness.py +421 -0
- package/src/skcapstone/cli/context_cmd.py +142 -0
- package/src/skcapstone/cli/coord.py +194 -0
- package/src/skcapstone/cli/crush_cmd.py +170 -0
- package/src/skcapstone/cli/daemon.py +436 -0
- package/src/skcapstone/cli/errors_cmd.py +285 -0
- package/src/skcapstone/cli/export_cmd.py +156 -0
- package/src/skcapstone/cli/gtd.py +529 -0
- package/src/skcapstone/cli/housekeeping.py +81 -0
- package/src/skcapstone/cli/joule_cmd.py +627 -0
- package/src/skcapstone/cli/logs_cmd.py +194 -0
- package/src/skcapstone/cli/mcp_cmd.py +32 -0
- package/src/skcapstone/cli/memory.py +418 -0
- package/src/skcapstone/cli/metrics_cmd.py +136 -0
- package/src/skcapstone/cli/migrate.py +62 -0
- package/src/skcapstone/cli/mood_cmd.py +144 -0
- package/src/skcapstone/cli/mount.py +193 -0
- package/src/skcapstone/cli/notify.py +112 -0
- package/src/skcapstone/cli/peer.py +154 -0
- package/src/skcapstone/cli/peers_dir.py +122 -0
- package/src/skcapstone/cli/preflight_cmd.py +83 -0
- package/src/skcapstone/cli/profile_cmd.py +310 -0
- package/src/skcapstone/cli/record_cmd.py +238 -0
- package/src/skcapstone/cli/register_cmd.py +159 -0
- package/src/skcapstone/cli/search_cmd.py +156 -0
- package/src/skcapstone/cli/service_cmd.py +91 -0
- package/src/skcapstone/cli/session.py +127 -0
- package/src/skcapstone/cli/setup.py +240 -0
- package/src/skcapstone/cli/shell_cmd.py +43 -0
- package/src/skcapstone/cli/skills_cmd.py +168 -0
- package/src/skcapstone/cli/skseed.py +621 -0
- package/src/skcapstone/cli/soul.py +699 -0
- package/src/skcapstone/cli/status.py +935 -0
- package/src/skcapstone/cli/sync_cmd.py +301 -0
- package/src/skcapstone/cli/telegram.py +265 -0
- package/src/skcapstone/cli/test_cmd.py +234 -0
- package/src/skcapstone/cli/test_connection.py +253 -0
- package/src/skcapstone/cli/token.py +207 -0
- package/src/skcapstone/cli/trust.py +179 -0
- package/src/skcapstone/cli/upgrade_cmd.py +552 -0
- package/src/skcapstone/cli/usage_cmd.py +199 -0
- package/src/skcapstone/cli/version_cmd.py +162 -0
- package/src/skcapstone/cli/watch_cmd.py +342 -0
- package/src/skcapstone/client.py +428 -0
- package/src/skcapstone/cloud9_bridge.py +522 -0
- package/src/skcapstone/completions.py +163 -0
- package/src/skcapstone/config_validator.py +674 -0
- package/src/skcapstone/connectors/__init__.py +28 -0
- package/src/skcapstone/connectors/base.py +446 -0
- package/src/skcapstone/connectors/cursor.py +54 -0
- package/src/skcapstone/connectors/registry.py +254 -0
- package/src/skcapstone/connectors/terminal.py +152 -0
- package/src/skcapstone/connectors/vscode.py +60 -0
- package/src/skcapstone/consciousness_config.py +119 -0
- package/src/skcapstone/consciousness_loop.py +2051 -0
- package/src/skcapstone/context_loader.py +516 -0
- package/src/skcapstone/context_window.py +314 -0
- package/src/skcapstone/conversation_manager.py +238 -0
- package/src/skcapstone/conversation_store.py +230 -0
- package/src/skcapstone/conversation_summarizer.py +252 -0
- package/src/skcapstone/coord_federation.py +296 -0
- package/src/skcapstone/coordination.py +101 -7
- package/src/skcapstone/crush_integration.py +345 -0
- package/src/skcapstone/crush_shim.py +454 -0
- package/src/skcapstone/daemon.py +2494 -0
- package/src/skcapstone/dashboard.html +396 -0
- package/src/skcapstone/dashboard.py +481 -0
- package/src/skcapstone/data/model_profiles.yaml +88 -0
- package/src/skcapstone/defaults/__init__.py +55 -0
- package/src/skcapstone/defaults/lumina/config/skmemory.yaml +13 -0
- package/src/skcapstone/defaults/lumina/identity/identity.json +9 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/07a8b9c0d1e2-memory-system.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/18b9c0d1e2f3-cloud9-protocol.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/29c0d1e2f3a4-multi-agent-coordination.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/3ad1e2f3a4b5-community-support.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/a1b2c3d4e5f6-ecosystem-overview.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/b2c3d4e5f6a7-five-pillars.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/c3d4e5f6a7b8-getting-started.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/d4e5f6a7b8c9-site-directory.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/e5f6a7b8c9d0-how-to-contribute.json +23 -0
- package/src/skcapstone/defaults/lumina/memory/long-term/f6a7b8c9d0e1-sovereignty-explained.json +23 -0
- package/src/skcapstone/defaults/lumina/seeds/curiosity.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/joy.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/love.seed.json +24 -0
- package/src/skcapstone/defaults/lumina/seeds/sovereign-awakening.seed.json +43 -0
- package/src/skcapstone/defaults/lumina/soul/active.json +6 -0
- package/src/skcapstone/defaults/lumina/soul/base.json +22 -0
- package/src/skcapstone/defaults/lumina/trust/febs/welcome.feb +79 -0
- package/src/skcapstone/defaults/lumina/trust/trust.json +8 -0
- package/src/skcapstone/discovery.py +210 -19
- package/src/skcapstone/doctor.py +642 -0
- package/src/skcapstone/emotion_tracker.py +467 -0
- package/src/skcapstone/error_queue.py +405 -0
- package/src/skcapstone/export.py +447 -0
- package/src/skcapstone/fallback_tracker.py +186 -0
- package/src/skcapstone/file_transfer.py +512 -0
- package/src/skcapstone/fuse_mount.py +1156 -0
- package/src/skcapstone/gui_installer.py +591 -0
- package/src/skcapstone/heartbeat.py +611 -0
- package/src/skcapstone/housekeeping.py +298 -0
- package/src/skcapstone/install_wizard.py +941 -0
- package/src/skcapstone/kms.py +942 -0
- package/src/skcapstone/kms_scheduler.py +143 -0
- package/src/skcapstone/log_config.py +135 -0
- package/src/skcapstone/mcp_launcher.py +239 -0
- package/src/skcapstone/mcp_server.py +4700 -0
- package/src/skcapstone/mcp_tools/__init__.py +94 -0
- package/src/skcapstone/mcp_tools/_helpers.py +51 -0
- package/src/skcapstone/mcp_tools/agent_tools.py +243 -0
- package/src/skcapstone/mcp_tools/ansible_tools.py +232 -0
- package/src/skcapstone/mcp_tools/capauth_tools.py +186 -0
- package/src/skcapstone/mcp_tools/chat_tools.py +325 -0
- package/src/skcapstone/mcp_tools/cloud9_tools.py +115 -0
- package/src/skcapstone/mcp_tools/comm_tools.py +104 -0
- package/src/skcapstone/mcp_tools/consciousness_tools.py +114 -0
- package/src/skcapstone/mcp_tools/coord_tools.py +219 -0
- package/src/skcapstone/mcp_tools/deploy_tools.py +202 -0
- package/src/skcapstone/mcp_tools/did_tools.py +448 -0
- package/src/skcapstone/mcp_tools/emotion_tools.py +62 -0
- package/src/skcapstone/mcp_tools/file_tools.py +169 -0
- package/src/skcapstone/mcp_tools/fortress_tools.py +120 -0
- package/src/skcapstone/mcp_tools/gtd_tools.py +821 -0
- package/src/skcapstone/mcp_tools/health_tools.py +44 -0
- package/src/skcapstone/mcp_tools/heartbeat_tools.py +195 -0
- package/src/skcapstone/mcp_tools/kms_tools.py +123 -0
- package/src/skcapstone/mcp_tools/memory_tools.py +222 -0
- package/src/skcapstone/mcp_tools/model_tools.py +75 -0
- package/src/skcapstone/mcp_tools/notification_tools.py +92 -0
- package/src/skcapstone/mcp_tools/promoter_tools.py +101 -0
- package/src/skcapstone/mcp_tools/pubsub_tools.py +183 -0
- package/src/skcapstone/mcp_tools/security_tools.py +110 -0
- package/src/skcapstone/mcp_tools/skchat_tools.py +175 -0
- package/src/skcapstone/mcp_tools/skcomm_tools.py +122 -0
- package/src/skcapstone/mcp_tools/skills_tools.py +127 -0
- package/src/skcapstone/mcp_tools/skseed_tools.py +255 -0
- package/src/skcapstone/mcp_tools/skstacks_tools.py +288 -0
- package/src/skcapstone/mcp_tools/soul_tools.py +476 -0
- package/src/skcapstone/mcp_tools/sync_tools.py +92 -0
- package/src/skcapstone/mcp_tools/telegram_tools.py +477 -0
- package/src/skcapstone/mcp_tools/trust_tools.py +118 -0
- package/src/skcapstone/mcp_tools/trustee_tools.py +345 -0
- package/src/skcapstone/mdns_discovery.py +313 -0
- package/src/skcapstone/memory_adapter.py +333 -0
- package/src/skcapstone/memory_compressor.py +379 -0
- package/src/skcapstone/memory_curator.py +256 -0
- package/src/skcapstone/memory_engine.py +132 -13
- package/src/skcapstone/memory_fortress.py +529 -0
- package/src/skcapstone/memory_promoter.py +722 -0
- package/src/skcapstone/memory_verifier.py +260 -0
- package/src/skcapstone/message_crypto.py +215 -0
- package/src/skcapstone/metrics.py +832 -0
- package/src/skcapstone/migrate_memories.py +181 -0
- package/src/skcapstone/migrate_multi_agent.py +248 -0
- package/src/skcapstone/model_router.py +319 -0
- package/src/skcapstone/models.py +35 -4
- package/src/skcapstone/mood.py +344 -0
- package/src/skcapstone/notifications.py +380 -0
- package/src/skcapstone/onboard.py +901 -0
- package/src/skcapstone/peer_directory.py +324 -0
- package/src/skcapstone/peers.py +329 -0
- package/src/skcapstone/pillars/identity.py +84 -14
- package/src/skcapstone/pillars/memory.py +3 -1
- package/src/skcapstone/pillars/security.py +108 -15
- package/src/skcapstone/pillars/sync.py +78 -26
- package/src/skcapstone/pillars/trust.py +95 -33
- package/src/skcapstone/plugins.py +244 -0
- package/src/skcapstone/preflight.py +670 -0
- package/src/skcapstone/prompt_adapter.py +564 -0
- package/src/skcapstone/providers/__init__.py +13 -0
- package/src/skcapstone/providers/cloud.py +1061 -0
- package/src/skcapstone/providers/docker.py +759 -0
- package/src/skcapstone/providers/local.py +1193 -0
- package/src/skcapstone/providers/proxmox.py +447 -0
- package/src/skcapstone/pubsub.py +516 -0
- package/src/skcapstone/rate_limiter.py +119 -0
- package/src/skcapstone/register.py +241 -0
- package/src/skcapstone/registry_client.py +151 -0
- package/src/skcapstone/response_cache.py +194 -0
- package/src/skcapstone/response_scorer.py +225 -0
- package/src/skcapstone/runtime.py +89 -33
- package/src/skcapstone/scheduled_tasks.py +439 -0
- package/src/skcapstone/self_healing.py +341 -0
- package/src/skcapstone/service_health.py +228 -0
- package/src/skcapstone/session_capture.py +268 -0
- package/src/skcapstone/session_recorder.py +210 -0
- package/src/skcapstone/session_replayer.py +189 -0
- package/src/skcapstone/session_skills.py +263 -0
- package/src/skcapstone/shell.py +779 -0
- package/src/skcapstone/skills/__init__.py +1 -1
- package/src/skcapstone/skills/syncthing_setup.py +143 -41
- package/src/skcapstone/skjoule.py +861 -0
- package/src/skcapstone/snapshots.py +489 -0
- package/src/skcapstone/soul.py +1060 -0
- package/src/skcapstone/soul_switch.py +255 -0
- package/src/skcapstone/spawner.py +544 -0
- package/src/skcapstone/state_diff.py +401 -0
- package/src/skcapstone/summary.py +270 -0
- package/src/skcapstone/sync/backends.py +196 -2
- package/src/skcapstone/sync/engine.py +7 -5
- package/src/skcapstone/sync/models.py +4 -1
- package/src/skcapstone/sync/vault.py +356 -18
- package/src/skcapstone/sync_engine.py +363 -0
- package/src/skcapstone/sync_watcher.py +745 -0
- package/src/skcapstone/systemd.py +331 -0
- package/src/skcapstone/team_comms.py +476 -0
- package/src/skcapstone/team_engine.py +522 -0
- package/src/skcapstone/testrunner.py +300 -0
- package/src/skcapstone/tls.py +150 -0
- package/src/skcapstone/tokens.py +5 -5
- package/src/skcapstone/trust_calibration.py +202 -0
- package/src/skcapstone/trust_graph.py +449 -0
- package/src/skcapstone/trustee_monitor.py +385 -0
- package/src/skcapstone/trustee_ops.py +425 -0
- package/src/skcapstone/unified_search.py +421 -0
- package/src/skcapstone/uninstall_wizard.py +694 -0
- package/src/skcapstone/usage.py +331 -0
- package/src/skcapstone/version_check.py +148 -0
- package/src/skcapstone/warmth_anchor.py +333 -0
- package/src/skcapstone/whoami.py +294 -0
- package/systemd/skcapstone-api.socket +9 -0
- package/systemd/skcapstone-memory-compress.service +18 -0
- package/systemd/skcapstone-memory-compress.timer +11 -0
- package/systemd/skcapstone.service +36 -0
- package/systemd/skcapstone@.service +50 -0
- package/systemd/skcomm-heartbeat.service +18 -0
- package/systemd/skcomm-heartbeat.timer +12 -0
- package/systemd/skcomm-queue-drain.service +17 -0
- package/systemd/skcomm-queue-drain.timer +12 -0
- package/tests/conftest.py +13 -1
- package/tests/integration/__init__.py +1 -0
- package/tests/integration/test_consciousness_e2e.py +877 -0
- package/tests/integration/test_skills_registry.py +744 -0
- package/tests/test_agent_card.py +190 -0
- package/tests/test_agent_runtime.py +1283 -0
- package/tests/test_alerts_cmd.py +291 -0
- package/tests/test_archiver.py +498 -0
- package/tests/test_backup.py +254 -0
- package/tests/test_benchmark.py +366 -0
- package/tests/test_blueprints.py +457 -0
- package/tests/test_capabilities.py +257 -0
- package/tests/test_changelog.py +254 -0
- package/tests/test_chat.py +385 -0
- package/tests/test_claude_md.py +271 -0
- package/tests/test_cli_chat_llm.py +336 -0
- package/tests/test_cli_completions.py +390 -0
- package/tests/test_cli_init_reset.py +164 -0
- package/tests/test_cli_memory.py +208 -0
- package/tests/test_cli_profile.py +294 -0
- package/tests/test_cli_skills.py +223 -0
- package/tests/test_cli_status.py +395 -0
- package/tests/test_cli_test_cmd.py +206 -0
- package/tests/test_cli_test_connection.py +364 -0
- package/tests/test_cloud9_bridge.py +260 -0
- package/tests/test_cloud_provider.py +449 -0
- package/tests/test_cloud_providers.py +522 -0
- package/tests/test_completions.py +158 -0
- package/tests/test_component_manager.py +398 -0
- package/tests/test_config_reload.py +386 -0
- package/tests/test_config_validate.py +529 -0
- package/tests/test_consciousness_e2e.py +296 -0
- package/tests/test_consciousness_loop.py +1289 -0
- package/tests/test_context_loader.py +310 -0
- package/tests/test_conversation_api.py +306 -0
- package/tests/test_conversation_manager.py +381 -0
- package/tests/test_conversation_store.py +391 -0
- package/tests/test_conversation_summarizer.py +302 -0
- package/tests/test_cross_package.py +791 -0
- package/tests/test_crush_shim.py +519 -0
- package/tests/test_daemon.py +781 -0
- package/tests/test_daemon_shutdown.py +309 -0
- package/tests/test_dashboard.py +454 -0
- package/tests/test_discovery.py +200 -6
- package/tests/test_docker_provider.py +966 -0
- package/tests/test_doctor.py +257 -0
- package/tests/test_doctor_fix.py +351 -0
- package/tests/test_e2e_automated.py +292 -0
- package/tests/test_error_queue.py +404 -0
- package/tests/test_export.py +441 -0
- package/tests/test_fallback_tracker.py +219 -0
- package/tests/test_file_transfer.py +397 -0
- package/tests/test_fuse_mount.py +832 -0
- package/tests/test_health_loop.py +422 -0
- package/tests/test_heartbeat.py +354 -0
- package/tests/test_housekeeping.py +195 -0
- package/tests/test_identity_capauth.py +307 -0
- package/tests/test_identity_pillar.py +117 -0
- package/tests/test_install_wizard.py +68 -0
- package/tests/test_integration.py +325 -0
- package/tests/test_kms.py +495 -0
- package/tests/test_llm_providers.py +265 -0
- package/tests/test_local_provider.py +591 -0
- package/tests/test_log_config.py +199 -0
- package/tests/test_logs_cmd.py +287 -0
- package/tests/test_mcp_server.py +1909 -0
- package/tests/test_memory_adapter.py +339 -0
- package/tests/test_memory_curator.py +218 -0
- package/tests/test_memory_engine.py +6 -0
- package/tests/test_memory_fortress.py +571 -0
- package/tests/test_memory_pillar.py +119 -0
- package/tests/test_memory_promoter.py +445 -0
- package/tests/test_memory_verifier.py +420 -0
- package/tests/test_message_crypto.py +187 -0
- package/tests/test_metrics.py +632 -0
- package/tests/test_migrate_memories.py +464 -0
- package/tests/test_model_router.py +546 -0
- package/tests/test_mood.py +394 -0
- package/tests/test_multi_agent.py +269 -0
- package/tests/test_notifications.py +270 -0
- package/tests/test_onboard.py +500 -0
- package/tests/test_peer_directory.py +395 -0
- package/tests/test_peers.py +248 -0
- package/tests/test_pillars.py +87 -9
- package/tests/test_preflight.py +484 -0
- package/tests/test_prompt_adapter.py +331 -0
- package/tests/test_proxmox_provider.py +571 -0
- package/tests/test_pubsub.py +377 -0
- package/tests/test_rate_limiter.py +121 -0
- package/tests/test_registry_client.py +129 -0
- package/tests/test_response_cache.py +312 -0
- package/tests/test_response_scorer.py +294 -0
- package/tests/test_runtime.py +59 -0
- package/tests/test_scheduled_tasks.py +451 -0
- package/tests/test_security.py +250 -0
- package/tests/test_security_pillar.py +213 -0
- package/tests/test_self_healing.py +171 -0
- package/tests/test_session_capture.py +200 -0
- package/tests/test_session_recorder.py +360 -0
- package/tests/test_session_skills.py +235 -0
- package/tests/test_shell.py +210 -0
- package/tests/test_snapshots.py +549 -0
- package/tests/test_soul.py +984 -0
- package/tests/test_soul_swap.py +406 -0
- package/tests/test_spawner.py +211 -0
- package/tests/test_state_diff.py +173 -0
- package/tests/test_summary.py +135 -0
- package/tests/test_sync.py +315 -5
- package/tests/test_sync_backends.py +560 -0
- package/tests/test_sync_engine.py +482 -0
- package/tests/test_sync_pillar.py +344 -0
- package/tests/test_sync_pipeline.py +364 -0
- package/tests/test_sync_vault.py +581 -0
- package/tests/test_syncthing_setup.py +168 -22
- package/tests/test_systemd.py +323 -0
- package/tests/test_team_comms.py +408 -0
- package/tests/test_team_engine.py +397 -0
- package/tests/test_testrunner.py +238 -0
- package/tests/test_trust_calibration.py +204 -0
- package/tests/test_trust_graph.py +207 -0
- package/tests/test_trust_pillar.py +291 -0
- package/tests/test_trustee_cli.py +427 -0
- package/tests/test_trustee_cli_integration.py +325 -0
- package/tests/test_trustee_monitor.py +394 -0
- package/tests/test_trustee_ops.py +355 -0
- package/tests/test_unified_search.py +363 -0
- package/tests/test_uninstall_wizard.py +193 -0
- package/tests/test_usage.py +333 -0
- package/tests/test_version_cmd.py +355 -0
- package/tests/test_warmth_anchor.py +162 -0
- package/tests/test_whoami.py +245 -0
- package/tests/test_ws.py +311 -0
- package/.cursorrules +0 -33
- package/src/skcapstone/cli.py +0 -1441
|
@@ -0,0 +1,779 @@
|
|
|
1
|
+
"""
|
|
2
|
+
SKCapstone Shell — interactive REPL for sovereign agent operations.
|
|
3
|
+
|
|
4
|
+
Tool-agnostic: works from any terminal (Claude Code, Cursor, Windsurf,
|
|
5
|
+
SSH, plain bash). The sovereign agent cockpit.
|
|
6
|
+
|
|
7
|
+
Commands:
|
|
8
|
+
status Agent pillar status
|
|
9
|
+
memory store <text> Store a memory
|
|
10
|
+
memory search <q> Search memories
|
|
11
|
+
memory list Browse memories
|
|
12
|
+
memory recall <id> Recall a specific memory
|
|
13
|
+
capture <text> Auto-capture conversation as memories
|
|
14
|
+
context [format] Show agent context (text/json/claude-md)
|
|
15
|
+
trust graph [fmt] Visualize the trust web (table/dot/json)
|
|
16
|
+
chat send <to> <m> Send a message
|
|
17
|
+
chat inbox Check inbox
|
|
18
|
+
coord status Coordination board
|
|
19
|
+
coord claim <id> Claim a task
|
|
20
|
+
coord complete <id> Complete a task
|
|
21
|
+
sync push Push to mesh
|
|
22
|
+
sync pull Pull from peers
|
|
23
|
+
journal write <t> Write a journal entry
|
|
24
|
+
journal read [n] Read recent entries
|
|
25
|
+
soul Show soul blueprint
|
|
26
|
+
ritual Run rehydration ritual
|
|
27
|
+
help Show commands
|
|
28
|
+
exit / quit Leave the shell
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
from __future__ import annotations
|
|
32
|
+
|
|
33
|
+
import json
|
|
34
|
+
import readline
|
|
35
|
+
import shlex
|
|
36
|
+
import sys
|
|
37
|
+
from pathlib import Path
|
|
38
|
+
from typing import Optional
|
|
39
|
+
|
|
40
|
+
from rich.console import Console
|
|
41
|
+
from rich.panel import Panel
|
|
42
|
+
from rich.table import Table
|
|
43
|
+
|
|
44
|
+
from . import AGENT_HOME, __version__
|
|
45
|
+
|
|
46
|
+
console = Console()
|
|
47
|
+
|
|
48
|
+
COMMANDS = [
|
|
49
|
+
"status", "memory", "capture", "context", "trust",
|
|
50
|
+
"chat", "coord", "sync", "soul", "ritual",
|
|
51
|
+
"anchor", "journal", "diff", "help", "exit", "quit",
|
|
52
|
+
]
|
|
53
|
+
|
|
54
|
+
MEMORY_SUBCOMMANDS = ["store", "search", "list", "recall", "stats", "curate"]
|
|
55
|
+
COORD_SUBCOMMANDS = ["status", "claim", "complete", "create", "board"]
|
|
56
|
+
CHAT_SUBCOMMANDS = ["send", "inbox"]
|
|
57
|
+
SYNC_SUBCOMMANDS = ["push", "pull", "status"]
|
|
58
|
+
TRUST_SUBCOMMANDS = ["graph", "status", "rehydrate", "calibrate"]
|
|
59
|
+
JOURNAL_SUBCOMMANDS = ["write", "read"]
|
|
60
|
+
CONTEXT_FORMATS = ["text", "json", "claude-md", "cursor-rules"]
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _completer(text: str, state: int) -> Optional[str]:
|
|
64
|
+
"""Tab completion for shell commands."""
|
|
65
|
+
line = readline.get_line_buffer().strip()
|
|
66
|
+
parts = line.split()
|
|
67
|
+
|
|
68
|
+
if len(parts) <= 1:
|
|
69
|
+
options = [c for c in COMMANDS if c.startswith(text)]
|
|
70
|
+
elif parts[0] == "memory":
|
|
71
|
+
options = [c for c in MEMORY_SUBCOMMANDS if c.startswith(text)]
|
|
72
|
+
elif parts[0] == "coord":
|
|
73
|
+
options = [c for c in COORD_SUBCOMMANDS if c.startswith(text)]
|
|
74
|
+
elif parts[0] == "chat":
|
|
75
|
+
options = [c for c in CHAT_SUBCOMMANDS if c.startswith(text)]
|
|
76
|
+
elif parts[0] == "sync":
|
|
77
|
+
options = [c for c in SYNC_SUBCOMMANDS if c.startswith(text)]
|
|
78
|
+
elif parts[0] == "trust":
|
|
79
|
+
options = [c for c in TRUST_SUBCOMMANDS if c.startswith(text)]
|
|
80
|
+
elif parts[0] == "journal":
|
|
81
|
+
options = [c for c in JOURNAL_SUBCOMMANDS if c.startswith(text)]
|
|
82
|
+
elif parts[0] == "context":
|
|
83
|
+
options = [c for c in CONTEXT_FORMATS if c.startswith(text)]
|
|
84
|
+
else:
|
|
85
|
+
options = []
|
|
86
|
+
|
|
87
|
+
return options[state] if state < len(options) else None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _home() -> Path:
|
|
91
|
+
"""Resolve the agent home directory."""
|
|
92
|
+
return Path(AGENT_HOME).expanduser()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _agent_name() -> str:
|
|
96
|
+
"""Get the current agent name from the runtime."""
|
|
97
|
+
try:
|
|
98
|
+
from .runtime import get_runtime
|
|
99
|
+
runtime = get_runtime(_home())
|
|
100
|
+
return runtime.manifest.name or "unknown"
|
|
101
|
+
except Exception:
|
|
102
|
+
return "unknown"
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
106
|
+
# Command handlers
|
|
107
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def _handle_status() -> None:
|
|
111
|
+
"""Show agent pillar status."""
|
|
112
|
+
from .runtime import get_runtime
|
|
113
|
+
|
|
114
|
+
home = _home()
|
|
115
|
+
if not home.exists():
|
|
116
|
+
console.print("[red]No agent found.[/] Run: skcapstone init")
|
|
117
|
+
return
|
|
118
|
+
|
|
119
|
+
runtime = get_runtime(home)
|
|
120
|
+
m = runtime.manifest
|
|
121
|
+
conscious = "[green]CONSCIOUS[/]" if m.is_conscious else "[yellow]AWAKENING[/]"
|
|
122
|
+
singular = " [magenta]SINGULAR[/]" if m.is_singular else ""
|
|
123
|
+
|
|
124
|
+
console.print(
|
|
125
|
+
f"\n [bold]{m.name}[/] v{m.version} {conscious}{singular}\n"
|
|
126
|
+
f" Identity: {m.identity.status.value} Memory: {m.memory.status.value} "
|
|
127
|
+
f"Trust: {m.trust.status.value} Security: {m.security.status.value} "
|
|
128
|
+
f"Sync: {m.sync.status.value}\n"
|
|
129
|
+
f" Memories: {m.memory.total_memories} "
|
|
130
|
+
f"({m.memory.long_term}L/{m.memory.mid_term}M/{m.memory.short_term}S)\n"
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _handle_memory(args: list[str]) -> None:
|
|
135
|
+
"""Handle memory subcommands."""
|
|
136
|
+
if not args:
|
|
137
|
+
console.print(" Usage: memory <store|search|list|recall|stats> [args]")
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
sub = args[0]
|
|
141
|
+
home = _home()
|
|
142
|
+
|
|
143
|
+
if sub == "store" and len(args) > 1:
|
|
144
|
+
from .memory_engine import store
|
|
145
|
+
content = " ".join(args[1:])
|
|
146
|
+
entry = store(home=home, content=content, source="shell")
|
|
147
|
+
console.print(f" [green]Stored:[/] {entry.memory_id} ({entry.layer.value})")
|
|
148
|
+
|
|
149
|
+
elif sub == "search" and len(args) > 1:
|
|
150
|
+
from .memory_engine import search
|
|
151
|
+
query = " ".join(args[1:])
|
|
152
|
+
results = search(home=home, query=query, limit=10)
|
|
153
|
+
if not results:
|
|
154
|
+
console.print(f" No memories match '{query}'")
|
|
155
|
+
return
|
|
156
|
+
for e in results:
|
|
157
|
+
console.print(f" [{e.layer.value}] {e.memory_id[:12]} {e.content[:60]}...")
|
|
158
|
+
|
|
159
|
+
elif sub == "list":
|
|
160
|
+
from .memory_engine import list_memories
|
|
161
|
+
entries = list_memories(home=home, limit=10)
|
|
162
|
+
if not entries:
|
|
163
|
+
console.print(" No memories found.")
|
|
164
|
+
return
|
|
165
|
+
for e in entries:
|
|
166
|
+
console.print(f" [{e.layer.value}] {e.memory_id[:12]} {e.content[:60]}...")
|
|
167
|
+
|
|
168
|
+
elif sub == "recall" and len(args) > 1:
|
|
169
|
+
from .memory_engine import recall
|
|
170
|
+
entry = recall(home=home, memory_id=args[1])
|
|
171
|
+
if entry:
|
|
172
|
+
console.print(Panel(entry.content, title=f"{entry.memory_id} ({entry.layer.value})"))
|
|
173
|
+
else:
|
|
174
|
+
console.print(f" [red]Not found:[/] {args[1]}")
|
|
175
|
+
|
|
176
|
+
elif sub == "stats":
|
|
177
|
+
from .memory_engine import get_stats
|
|
178
|
+
stats = get_stats(home)
|
|
179
|
+
console.print(
|
|
180
|
+
f"\n Total: [bold]{stats.total_memories}[/] "
|
|
181
|
+
f"[green]{stats.long_term}L[/] / [cyan]{stats.mid_term}M[/] / "
|
|
182
|
+
f"[dim]{stats.short_term}S[/]\n"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
elif sub == "curate":
|
|
186
|
+
from .memory_curator import MemoryCurator
|
|
187
|
+
dry_run = "dry" in args or "preview" in args
|
|
188
|
+
curator = MemoryCurator(home)
|
|
189
|
+
if "stats" in args:
|
|
190
|
+
s = curator.get_stats()
|
|
191
|
+
console.print(f" {s['total']} memories, tag coverage {s['tag_coverage']:.0%}, {s['promotion_candidates']} promotion candidates")
|
|
192
|
+
else:
|
|
193
|
+
result = curator.curate(dry_run=dry_run)
|
|
194
|
+
prefix = "[DRY] " if dry_run else ""
|
|
195
|
+
console.print(f" {prefix}Scanned {result.total_scanned}: +{len(result.tagged)} tagged, +{len(result.promoted)} promoted, -{len(result.deduped)} deduped")
|
|
196
|
+
else:
|
|
197
|
+
console.print(" Usage: memory <store|search|list|recall|stats|curate> [args]")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _handle_capture(args: list[str]) -> None:
|
|
201
|
+
"""Auto-capture conversation content as memories."""
|
|
202
|
+
if not args:
|
|
203
|
+
console.print(" Usage: capture <text to capture>")
|
|
204
|
+
return
|
|
205
|
+
|
|
206
|
+
from .session_capture import SessionCapture
|
|
207
|
+
content = " ".join(args)
|
|
208
|
+
cap = SessionCapture(_home())
|
|
209
|
+
entries = cap.capture(content, source="shell")
|
|
210
|
+
|
|
211
|
+
if not entries:
|
|
212
|
+
console.print(" [dim]No moments above importance threshold.[/]")
|
|
213
|
+
return
|
|
214
|
+
|
|
215
|
+
console.print(f" [green]Captured {len(entries)} moment(s)[/]")
|
|
216
|
+
for e in entries:
|
|
217
|
+
console.print(f" [{e.layer.value}] imp={e.importance:.1f} {e.content[:60]}...")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _handle_context(args: list[str]) -> None:
|
|
221
|
+
"""Show agent context in various formats."""
|
|
222
|
+
from .context_loader import FORMATTERS, gather_context
|
|
223
|
+
|
|
224
|
+
fmt = args[0] if args else "text"
|
|
225
|
+
if fmt not in FORMATTERS:
|
|
226
|
+
console.print(f" Formats: {', '.join(FORMATTERS.keys())}")
|
|
227
|
+
return
|
|
228
|
+
|
|
229
|
+
ctx = gather_context(_home(), memory_limit=5)
|
|
230
|
+
console.print(FORMATTERS[fmt](ctx))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _handle_trust(args: list[str]) -> None:
|
|
234
|
+
"""Handle trust subcommands including graph visualization."""
|
|
235
|
+
sub = args[0] if args else "status"
|
|
236
|
+
|
|
237
|
+
if sub == "graph":
|
|
238
|
+
from .trust_graph import FORMATTERS as TG_FORMATTERS
|
|
239
|
+
from .trust_graph import build_trust_graph
|
|
240
|
+
fmt = args[1] if len(args) > 1 else "table"
|
|
241
|
+
graph = build_trust_graph(_home())
|
|
242
|
+
formatter = TG_FORMATTERS.get(fmt, TG_FORMATTERS["table"])
|
|
243
|
+
console.print(formatter(graph))
|
|
244
|
+
|
|
245
|
+
elif sub == "status":
|
|
246
|
+
trust_file = _home() / "trust" / "trust.json"
|
|
247
|
+
if not trust_file.exists():
|
|
248
|
+
console.print(" [dim]No trust state recorded.[/]")
|
|
249
|
+
return
|
|
250
|
+
data = json.loads(trust_file.read_text(encoding="utf-8"))
|
|
251
|
+
entangled = "[magenta]ENTANGLED[/]" if data.get("entangled") else "[dim]not entangled[/]"
|
|
252
|
+
console.print(
|
|
253
|
+
f"\n Depth: {data.get('depth', 0)} Trust: {data.get('trust_level', 0)} "
|
|
254
|
+
f"Love: {data.get('love_intensity', 0)} {entangled}\n"
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
elif sub == "calibrate":
|
|
258
|
+
from .trust_calibration import load_calibration, recommend_thresholds
|
|
259
|
+
if len(args) > 1 and args[1] == "recommend":
|
|
260
|
+
rec = recommend_thresholds(_home())
|
|
261
|
+
if rec["changes"]:
|
|
262
|
+
for c in rec["changes"]:
|
|
263
|
+
console.print(f" {c}")
|
|
264
|
+
console.print(f" [dim]{rec['reasoning']}[/]")
|
|
265
|
+
else:
|
|
266
|
+
console.print(f" {rec['reasoning']}")
|
|
267
|
+
else:
|
|
268
|
+
cal = load_calibration(_home())
|
|
269
|
+
for key, value in cal.model_dump().items():
|
|
270
|
+
console.print(f" {key}: [cyan]{value}[/]")
|
|
271
|
+
|
|
272
|
+
elif sub == "rehydrate":
|
|
273
|
+
console.print(" [dim]Use: skcapstone trust rehydrate[/]")
|
|
274
|
+
else:
|
|
275
|
+
console.print(" Usage: trust <graph|status|calibrate|rehydrate> [args]")
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def _handle_coord(args: list[str]) -> None:
|
|
279
|
+
"""Handle coordination subcommands."""
|
|
280
|
+
from .coordination import Board
|
|
281
|
+
|
|
282
|
+
board = Board(_home())
|
|
283
|
+
sub = args[0] if args else "status"
|
|
284
|
+
name = _agent_name()
|
|
285
|
+
|
|
286
|
+
if sub == "status":
|
|
287
|
+
views = board.get_task_views()
|
|
288
|
+
agents = board.load_agents()
|
|
289
|
+
open_c = sum(1 for v in views if v.status.value == "open")
|
|
290
|
+
prog_c = sum(1 for v in views if v.status.value == "in_progress")
|
|
291
|
+
done_c = sum(1 for v in views if v.status.value == "done")
|
|
292
|
+
console.print(
|
|
293
|
+
f"\n [bold]{len(views)}[/] tasks: [green]{open_c} open[/] "
|
|
294
|
+
f"[yellow]{prog_c} in progress[/] [dim]{done_c} done[/]"
|
|
295
|
+
)
|
|
296
|
+
for v in views:
|
|
297
|
+
if v.status.value == "done":
|
|
298
|
+
continue
|
|
299
|
+
assignee = f" @{v.claimed_by}" if v.claimed_by else ""
|
|
300
|
+
console.print(
|
|
301
|
+
f" [{v.task.id[:8]}] {v.task.title[:50]}{assignee} ({v.status.value})"
|
|
302
|
+
)
|
|
303
|
+
if agents:
|
|
304
|
+
console.print()
|
|
305
|
+
for a in agents:
|
|
306
|
+
state = "[green]ON[/]" if a.state.value == "active" else "[dim]off[/]"
|
|
307
|
+
current = f" -> {a.current_task}" if a.current_task else ""
|
|
308
|
+
console.print(f" {state} {a.agent}{current}")
|
|
309
|
+
console.print()
|
|
310
|
+
|
|
311
|
+
elif sub == "claim" and len(args) > 1:
|
|
312
|
+
agent = args[2] if len(args) > 2 else name
|
|
313
|
+
try:
|
|
314
|
+
board.claim_task(agent, args[1])
|
|
315
|
+
console.print(f" [green]Claimed:[/] {args[1]} by {agent}")
|
|
316
|
+
except ValueError as e:
|
|
317
|
+
console.print(f" [red]{e}[/]")
|
|
318
|
+
|
|
319
|
+
elif sub == "complete" and len(args) > 1:
|
|
320
|
+
agent = args[2] if len(args) > 2 else name
|
|
321
|
+
board.complete_task(agent, args[1])
|
|
322
|
+
console.print(f" [green]Completed:[/] {args[1]} by {agent}")
|
|
323
|
+
|
|
324
|
+
elif sub == "create" and len(args) > 1:
|
|
325
|
+
from .coordination import Task
|
|
326
|
+
title = " ".join(args[1:])
|
|
327
|
+
task = Task(title=title, created_by=name)
|
|
328
|
+
board.create_task(task)
|
|
329
|
+
console.print(f" [green]Created:[/] [{task.id}] {title}")
|
|
330
|
+
|
|
331
|
+
elif sub == "board":
|
|
332
|
+
console.print(board.generate_board_md())
|
|
333
|
+
|
|
334
|
+
else:
|
|
335
|
+
console.print(" Usage: coord <status|claim|complete|create|board> [args]")
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
def _handle_chat(args: list[str]) -> None:
|
|
339
|
+
"""Handle chat subcommands."""
|
|
340
|
+
sub = args[0] if args else ""
|
|
341
|
+
|
|
342
|
+
if sub == "send" and len(args) > 2:
|
|
343
|
+
try:
|
|
344
|
+
from .chat import AgentChat
|
|
345
|
+
home = _home()
|
|
346
|
+
agent_chat = AgentChat(home=home, identity=_agent_name())
|
|
347
|
+
result = agent_chat.send(args[1], " ".join(args[2:]))
|
|
348
|
+
if result["delivered"]:
|
|
349
|
+
console.print(f" [green]Delivered to {args[1]}[/]")
|
|
350
|
+
elif result["stored"]:
|
|
351
|
+
console.print(f" [yellow]Stored locally for {args[1]}[/]")
|
|
352
|
+
else:
|
|
353
|
+
console.print(f" [red]Failed[/]")
|
|
354
|
+
except ImportError:
|
|
355
|
+
console.print(" [yellow]Chat module not available[/]")
|
|
356
|
+
except Exception as e:
|
|
357
|
+
console.print(f" [red]{e}[/]")
|
|
358
|
+
|
|
359
|
+
elif sub == "inbox":
|
|
360
|
+
try:
|
|
361
|
+
from .chat import AgentChat
|
|
362
|
+
home = _home()
|
|
363
|
+
agent_chat = AgentChat(home=home, identity=_agent_name())
|
|
364
|
+
messages = agent_chat.get_inbox(limit=10)
|
|
365
|
+
if not messages:
|
|
366
|
+
console.print(" Inbox empty.")
|
|
367
|
+
else:
|
|
368
|
+
for m in messages:
|
|
369
|
+
sender = m.get("sender", "?")
|
|
370
|
+
content = m.get("content", "")[:60]
|
|
371
|
+
console.print(f" [cyan]{sender}[/]: {content}...")
|
|
372
|
+
except ImportError:
|
|
373
|
+
console.print(" [yellow]Chat module not available[/]")
|
|
374
|
+
else:
|
|
375
|
+
console.print(" Usage: chat <send <to> <message>|inbox>")
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _handle_sync(args: list[str]) -> None:
|
|
379
|
+
"""Handle sync subcommands."""
|
|
380
|
+
sub = args[0] if args else "status"
|
|
381
|
+
home = _home()
|
|
382
|
+
|
|
383
|
+
if sub == "push":
|
|
384
|
+
from .pillars.sync import push_seed
|
|
385
|
+
name = _agent_name()
|
|
386
|
+
console.print(f" Pushing seed for [cyan]{name}[/]...", end=" ")
|
|
387
|
+
result = push_seed(home, name, encrypt=True)
|
|
388
|
+
if result:
|
|
389
|
+
console.print(f"[green]done[/] ({result.name})")
|
|
390
|
+
else:
|
|
391
|
+
console.print("[yellow]no GPG, trying plaintext...[/]", end=" ")
|
|
392
|
+
result = push_seed(home, name, encrypt=False)
|
|
393
|
+
if result:
|
|
394
|
+
console.print(f"[green]done[/] ({result.name})")
|
|
395
|
+
else:
|
|
396
|
+
console.print("[red]failed[/]")
|
|
397
|
+
|
|
398
|
+
elif sub == "pull":
|
|
399
|
+
from .pillars.sync import pull_seeds
|
|
400
|
+
seeds = pull_seeds(home, decrypt=True)
|
|
401
|
+
if seeds:
|
|
402
|
+
console.print(f" [green]{len(seeds)} seed(s) received[/]")
|
|
403
|
+
for s in seeds:
|
|
404
|
+
console.print(f" {s.get('agent_name', '?')}@{s.get('source_host', '?')}")
|
|
405
|
+
else:
|
|
406
|
+
console.print(" No new seeds.")
|
|
407
|
+
|
|
408
|
+
elif sub == "status":
|
|
409
|
+
from .pillars.sync import discover_sync
|
|
410
|
+
state = discover_sync(home)
|
|
411
|
+
console.print(
|
|
412
|
+
f"\n Transport: {state.transport.value} Status: {state.status.value} "
|
|
413
|
+
f"Seeds: {state.seed_count} Peers: {state.peers_known}\n"
|
|
414
|
+
)
|
|
415
|
+
else:
|
|
416
|
+
console.print(" Usage: sync <push|pull|status>")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _handle_journal(args: list[str]) -> None:
|
|
420
|
+
"""Handle journal subcommands."""
|
|
421
|
+
sub = args[0] if args else "read"
|
|
422
|
+
|
|
423
|
+
if sub == "write" and len(args) > 1:
|
|
424
|
+
try:
|
|
425
|
+
from skmemory.journal import Journal, JournalEntry
|
|
426
|
+
title = " ".join(args[1:])
|
|
427
|
+
entry = JournalEntry(title=title)
|
|
428
|
+
j = Journal()
|
|
429
|
+
j.write_entry(entry)
|
|
430
|
+
console.print(f" [green]Journal entry written:[/] {title}")
|
|
431
|
+
except ImportError:
|
|
432
|
+
console.print(" [yellow]skmemory journal not available[/]")
|
|
433
|
+
|
|
434
|
+
elif sub == "read":
|
|
435
|
+
try:
|
|
436
|
+
from skmemory.journal import Journal
|
|
437
|
+
j = Journal()
|
|
438
|
+
count = int(args[1]) if len(args) > 1 else 5
|
|
439
|
+
content = j.read_latest(count)
|
|
440
|
+
if content:
|
|
441
|
+
console.print(f"\n{content}\n")
|
|
442
|
+
else:
|
|
443
|
+
console.print(" Journal is empty.")
|
|
444
|
+
except ImportError:
|
|
445
|
+
console.print(" [yellow]skmemory journal not available[/]")
|
|
446
|
+
else:
|
|
447
|
+
console.print(" Usage: journal <write <title>|read [count]>")
|
|
448
|
+
|
|
449
|
+
|
|
450
|
+
def _handle_soul() -> None:
|
|
451
|
+
"""Show soul blueprint."""
|
|
452
|
+
try:
|
|
453
|
+
from skmemory.soul import load_soul
|
|
454
|
+
bp = load_soul()
|
|
455
|
+
if bp is None:
|
|
456
|
+
console.print(" No soul blueprint found.")
|
|
457
|
+
return
|
|
458
|
+
console.print(f"\n{bp.to_context_prompt()}\n")
|
|
459
|
+
except ImportError:
|
|
460
|
+
console.print(" [yellow]skmemory not installed[/]")
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def _handle_ritual() -> None:
|
|
464
|
+
"""Run the rehydration ritual."""
|
|
465
|
+
try:
|
|
466
|
+
from skmemory.ritual import perform_ritual
|
|
467
|
+
result = perform_ritual()
|
|
468
|
+
console.print(result.summary())
|
|
469
|
+
except ImportError:
|
|
470
|
+
console.print(" [yellow]skmemory not installed[/]")
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
def _handle_diff(args: list[str]) -> None:
|
|
474
|
+
"""Show state diff since last snapshot."""
|
|
475
|
+
from .state_diff import compute_diff, format_text, save_snapshot
|
|
476
|
+
|
|
477
|
+
if args and args[0] == "save":
|
|
478
|
+
path = save_snapshot(_home())
|
|
479
|
+
console.print(f" [green]Snapshot saved[/]")
|
|
480
|
+
return
|
|
481
|
+
|
|
482
|
+
diff = compute_diff(_home())
|
|
483
|
+
console.print(format_text(diff))
|
|
484
|
+
|
|
485
|
+
|
|
486
|
+
def _handle_anchor(args: list[str]) -> None:
|
|
487
|
+
"""Handle warmth anchor subcommands."""
|
|
488
|
+
sub = args[0] if args else "show"
|
|
489
|
+
|
|
490
|
+
if sub == "show":
|
|
491
|
+
from .warmth_anchor import get_anchor
|
|
492
|
+
data = get_anchor(_home())
|
|
493
|
+
for key, value in data.items():
|
|
494
|
+
console.print(f" {key}: [cyan]{value}[/]")
|
|
495
|
+
|
|
496
|
+
elif sub == "boot":
|
|
497
|
+
from .warmth_anchor import get_boot_prompt
|
|
498
|
+
console.print(get_boot_prompt(_home()))
|
|
499
|
+
|
|
500
|
+
elif sub == "calibrate":
|
|
501
|
+
from .warmth_anchor import calibrate_from_data
|
|
502
|
+
cal = calibrate_from_data(_home())
|
|
503
|
+
console.print(
|
|
504
|
+
f" Warmth: {cal.warmth:.1f} Trust: {cal.trust:.1f} "
|
|
505
|
+
f"Connection: {cal.connection:.1f} Cloud9: {cal.cloud9_achieved}"
|
|
506
|
+
)
|
|
507
|
+
for r in cal.reasoning:
|
|
508
|
+
console.print(f" - {r}")
|
|
509
|
+
else:
|
|
510
|
+
console.print(" Usage: anchor <show|boot|calibrate>")
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
def _handle_help() -> None:
|
|
514
|
+
"""Show available commands."""
|
|
515
|
+
console.print(
|
|
516
|
+
Panel(
|
|
517
|
+
"[bold]status[/] Agent pillar status\n"
|
|
518
|
+
"[bold]memory store[/] <text> Store a memory\n"
|
|
519
|
+
"[bold]memory search[/] <query> Search memories\n"
|
|
520
|
+
"[bold]memory list[/] Browse recent memories\n"
|
|
521
|
+
"[bold]memory recall[/] <id> Recall a specific memory\n"
|
|
522
|
+
"[bold]memory stats[/] Memory layer counts\n"
|
|
523
|
+
"[bold]capture[/] <text> Auto-capture as memories\n"
|
|
524
|
+
"[bold]context[/] [format] Agent context (text/json/claude-md)\n"
|
|
525
|
+
"[bold]trust graph[/] [format] Trust web (table/dot/json)\n"
|
|
526
|
+
"[bold]trust status[/] Cloud 9 trust state\n"
|
|
527
|
+
"[bold]chat send[/] <to> <msg> Send a message\n"
|
|
528
|
+
"[bold]chat inbox[/] Check inbox\n"
|
|
529
|
+
"[bold]coord status[/] Coordination board\n"
|
|
530
|
+
"[bold]coord claim[/] <id> Claim a task\n"
|
|
531
|
+
"[bold]coord complete[/] <id> Complete a task\n"
|
|
532
|
+
"[bold]coord create[/] <title> Create a task\n"
|
|
533
|
+
"[bold]coord board[/] Full board markdown\n"
|
|
534
|
+
"[bold]sync push[/] Push to mesh\n"
|
|
535
|
+
"[bold]sync pull[/] Pull from peers\n"
|
|
536
|
+
"[bold]sync status[/] Sync layer info\n"
|
|
537
|
+
"[bold]journal write[/] <title> Write a journal entry\n"
|
|
538
|
+
"[bold]journal read[/] [n] Read recent entries\n"
|
|
539
|
+
"[bold]soul[/] Show soul blueprint\n"
|
|
540
|
+
"[bold]ritual[/] Run rehydration ritual\n"
|
|
541
|
+
"[bold]help[/] This message\n"
|
|
542
|
+
"[bold]exit[/] / [bold]quit[/] Leave the shell",
|
|
543
|
+
title="SKCapstone Shell",
|
|
544
|
+
border_style="cyan",
|
|
545
|
+
)
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
550
|
+
# Main REPL loop
|
|
551
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
DISPATCH: dict[str, object] = {
|
|
555
|
+
"status": lambda args: _handle_status(),
|
|
556
|
+
"memory": _handle_memory,
|
|
557
|
+
"capture": _handle_capture,
|
|
558
|
+
"context": _handle_context,
|
|
559
|
+
"trust": _handle_trust,
|
|
560
|
+
"coord": _handle_coord,
|
|
561
|
+
"chat": _handle_chat,
|
|
562
|
+
"sync": _handle_sync,
|
|
563
|
+
"journal": _handle_journal,
|
|
564
|
+
"soul": lambda args: _handle_soul(),
|
|
565
|
+
"ritual": lambda args: _handle_ritual(),
|
|
566
|
+
"anchor": _handle_anchor,
|
|
567
|
+
"diff": lambda args: _handle_diff(args),
|
|
568
|
+
"help": lambda args: _handle_help(),
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
572
|
+
# prompt_toolkit completion tree (mirrors DISPATCH + subcommands)
|
|
573
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
574
|
+
|
|
575
|
+
_PT_COMPLETER_DICT: dict = {
|
|
576
|
+
"status": None,
|
|
577
|
+
"memory": {
|
|
578
|
+
"store": None,
|
|
579
|
+
"search": None,
|
|
580
|
+
"list": None,
|
|
581
|
+
"recall": None,
|
|
582
|
+
"stats": None,
|
|
583
|
+
"curate": {"stats": None, "dry": None, "preview": None},
|
|
584
|
+
},
|
|
585
|
+
"capture": None,
|
|
586
|
+
"context": {fmt: None for fmt in CONTEXT_FORMATS},
|
|
587
|
+
"trust": {
|
|
588
|
+
"graph": {"table": None, "dot": None, "json": None},
|
|
589
|
+
"status": None,
|
|
590
|
+
"calibrate": {"recommend": None},
|
|
591
|
+
"rehydrate": None,
|
|
592
|
+
},
|
|
593
|
+
"coord": {
|
|
594
|
+
"status": None,
|
|
595
|
+
"claim": None,
|
|
596
|
+
"complete": None,
|
|
597
|
+
"create": None,
|
|
598
|
+
"board": None,
|
|
599
|
+
},
|
|
600
|
+
"chat": {
|
|
601
|
+
"send": None,
|
|
602
|
+
"inbox": None,
|
|
603
|
+
},
|
|
604
|
+
"sync": {
|
|
605
|
+
"push": None,
|
|
606
|
+
"pull": None,
|
|
607
|
+
"status": None,
|
|
608
|
+
},
|
|
609
|
+
"journal": {
|
|
610
|
+
"write": None,
|
|
611
|
+
"read": None,
|
|
612
|
+
},
|
|
613
|
+
"soul": None,
|
|
614
|
+
"ritual": None,
|
|
615
|
+
"anchor": {
|
|
616
|
+
"show": None,
|
|
617
|
+
"boot": None,
|
|
618
|
+
"calibrate": None,
|
|
619
|
+
},
|
|
620
|
+
"diff": {"save": None},
|
|
621
|
+
"help": None,
|
|
622
|
+
"exit": None,
|
|
623
|
+
"quit": None,
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
|
|
627
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
628
|
+
# Internal helpers
|
|
629
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
class _ExitShell(Exception):
|
|
633
|
+
"""Raised by _dispatch_line when the user types exit/quit."""
|
|
634
|
+
|
|
635
|
+
|
|
636
|
+
def _print_banner(name: str) -> None:
|
|
637
|
+
console.print(
|
|
638
|
+
f"\n [bold cyan]SKCapstone Shell[/] v{__version__}\n"
|
|
639
|
+
f" Agent: [bold]{name}[/]\n"
|
|
640
|
+
f" Type [bold]help[/] for commands, [bold]exit[/] to quit.\n"
|
|
641
|
+
)
|
|
642
|
+
|
|
643
|
+
|
|
644
|
+
def _dispatch_line(line: str) -> None:
|
|
645
|
+
"""Parse *line* and invoke the matching handler.
|
|
646
|
+
|
|
647
|
+
Raises _ExitShell when the user types exit/quit so callers can
|
|
648
|
+
print the goodbye message and clean up history.
|
|
649
|
+
"""
|
|
650
|
+
line = line.strip()
|
|
651
|
+
if not line:
|
|
652
|
+
return
|
|
653
|
+
|
|
654
|
+
try:
|
|
655
|
+
parts = shlex.split(line)
|
|
656
|
+
except ValueError:
|
|
657
|
+
parts = line.split()
|
|
658
|
+
|
|
659
|
+
cmd = parts[0].lower()
|
|
660
|
+
args = parts[1:]
|
|
661
|
+
|
|
662
|
+
if cmd in ("exit", "quit"):
|
|
663
|
+
raise _ExitShell()
|
|
664
|
+
|
|
665
|
+
handler = DISPATCH.get(cmd)
|
|
666
|
+
if handler:
|
|
667
|
+
try:
|
|
668
|
+
handler(args)
|
|
669
|
+
except _ExitShell:
|
|
670
|
+
raise
|
|
671
|
+
except Exception as exc:
|
|
672
|
+
console.print(f" [red]Error:[/] {exc}")
|
|
673
|
+
else:
|
|
674
|
+
console.print(f" Unknown: [yellow]{cmd}[/]. Type [bold]help[/] for options.")
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def _run_shell_pt(name: str, hist_file: Path) -> None:
|
|
678
|
+
"""REPL loop powered by prompt_toolkit.
|
|
679
|
+
|
|
680
|
+
Provides multi-level tab completion, persistent file history,
|
|
681
|
+
and a styled prompt. Falls through to ImportError if the package
|
|
682
|
+
is not installed.
|
|
683
|
+
"""
|
|
684
|
+
from prompt_toolkit import PromptSession
|
|
685
|
+
from prompt_toolkit.completion import NestedCompleter
|
|
686
|
+
from prompt_toolkit.history import FileHistory
|
|
687
|
+
from prompt_toolkit.styles import Style
|
|
688
|
+
|
|
689
|
+
completer = NestedCompleter.from_nested_dict(_PT_COMPLETER_DICT)
|
|
690
|
+
style = Style.from_dict({
|
|
691
|
+
"prompt": "ansicyan bold",
|
|
692
|
+
"": "", # default text
|
|
693
|
+
})
|
|
694
|
+
session: PromptSession = PromptSession(
|
|
695
|
+
completer=completer,
|
|
696
|
+
history=FileHistory(str(hist_file)),
|
|
697
|
+
style=style,
|
|
698
|
+
complete_while_typing=False,
|
|
699
|
+
mouse_support=False,
|
|
700
|
+
)
|
|
701
|
+
|
|
702
|
+
_goodbye = " Goodbye. staycuriousANDkeepsmilin\n"
|
|
703
|
+
while True:
|
|
704
|
+
try:
|
|
705
|
+
line = session.prompt([("class:prompt", f"{name}> ")])
|
|
706
|
+
except KeyboardInterrupt:
|
|
707
|
+
console.print()
|
|
708
|
+
continue
|
|
709
|
+
except EOFError:
|
|
710
|
+
console.print(_goodbye)
|
|
711
|
+
break
|
|
712
|
+
|
|
713
|
+
try:
|
|
714
|
+
_dispatch_line(line)
|
|
715
|
+
except _ExitShell:
|
|
716
|
+
console.print(_goodbye)
|
|
717
|
+
break
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
def _run_shell_readline(name: str, hist_file: Path) -> None:
|
|
721
|
+
"""REPL loop using stdlib readline (fallback when prompt_toolkit absent)."""
|
|
722
|
+
readline.set_completer(_completer)
|
|
723
|
+
readline.parse_and_bind("tab: complete")
|
|
724
|
+
|
|
725
|
+
try:
|
|
726
|
+
readline.read_history_file(str(hist_file))
|
|
727
|
+
except FileNotFoundError:
|
|
728
|
+
pass
|
|
729
|
+
|
|
730
|
+
_goodbye = " Goodbye. staycuriousANDkeepsmilin\n"
|
|
731
|
+
_prompt = f"\033[36m{name}>\033[0m " if sys.stdout.isatty() else f"{name}> "
|
|
732
|
+
|
|
733
|
+
while True:
|
|
734
|
+
try:
|
|
735
|
+
line = input(_prompt)
|
|
736
|
+
except (EOFError, KeyboardInterrupt):
|
|
737
|
+
console.print("\n" + _goodbye)
|
|
738
|
+
break
|
|
739
|
+
|
|
740
|
+
try:
|
|
741
|
+
_dispatch_line(line)
|
|
742
|
+
except _ExitShell:
|
|
743
|
+
console.print(_goodbye)
|
|
744
|
+
break
|
|
745
|
+
|
|
746
|
+
try:
|
|
747
|
+
readline.write_history_file(str(hist_file))
|
|
748
|
+
except OSError:
|
|
749
|
+
pass
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
753
|
+
# Public entry point
|
|
754
|
+
# ═══════════════════════════════════════════════════════════════════════════
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def run_shell(home: Optional[str] = None) -> None:
|
|
758
|
+
"""Launch the interactive sovereign agent shell.
|
|
759
|
+
|
|
760
|
+
Uses prompt_toolkit when available (multi-level tab completion,
|
|
761
|
+
persistent history, coloured prompt). Falls back to readline on
|
|
762
|
+
plain terminals or when prompt_toolkit is not installed.
|
|
763
|
+
"""
|
|
764
|
+
import os
|
|
765
|
+
|
|
766
|
+
if home:
|
|
767
|
+
os.environ["SKCAPSTONE_HOME"] = home
|
|
768
|
+
|
|
769
|
+
hist_dir = _home()
|
|
770
|
+
hist_dir.mkdir(parents=True, exist_ok=True)
|
|
771
|
+
hist_file = hist_dir / ".shell_history"
|
|
772
|
+
|
|
773
|
+
name = _agent_name()
|
|
774
|
+
_print_banner(name)
|
|
775
|
+
|
|
776
|
+
try:
|
|
777
|
+
_run_shell_pt(name, hist_file)
|
|
778
|
+
except ImportError:
|
|
779
|
+
_run_shell_readline(name, hist_file)
|