@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,248 @@
|
|
|
1
|
+
"""Alerts command — subscribe to critical pubsub topics and stream live alerts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import time
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
import click
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
from rich.text import Text
|
|
14
|
+
|
|
15
|
+
from ._common import AGENT_HOME, console
|
|
16
|
+
from .. import SKCAPSTONE_AGENT, SKCAPSTONE_ROOT
|
|
17
|
+
|
|
18
|
+
# Import NotificationManager at module level so it can be patched in tests.
|
|
19
|
+
try:
|
|
20
|
+
from ..notifications import NotificationManager
|
|
21
|
+
except ImportError: # pragma: no cover
|
|
22
|
+
NotificationManager = None # type: ignore[assignment,misc]
|
|
23
|
+
|
|
24
|
+
# ---------------------------------------------------------------------------
|
|
25
|
+
# Constants
|
|
26
|
+
# ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
#: Default topics the alerts command subscribes to.
|
|
29
|
+
DEFAULT_TOPICS: tuple[str, ...] = (
|
|
30
|
+
"agent.critical",
|
|
31
|
+
"coord.task_failed",
|
|
32
|
+
"consciousness.error",
|
|
33
|
+
"pillar.degraded",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
#: Rich markup styles per topic prefix (longest match wins).
|
|
37
|
+
_TOPIC_STYLE: dict[str, str] = {
|
|
38
|
+
"agent.critical": "bold red",
|
|
39
|
+
"coord.task_failed": "red",
|
|
40
|
+
"consciousness.error": "bold magenta",
|
|
41
|
+
"pillar.degraded": "yellow",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#: Default polling interval in seconds.
|
|
45
|
+
DEFAULT_INTERVAL: float = 1.0
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
# Helpers (importable for unit-testing)
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
def _style_for_topic(topic: str) -> str:
|
|
53
|
+
"""Return the Rich markup style for a given topic name.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
topic: Full topic name (e.g. ``"agent.critical"``).
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
Rich style string for the topic, or ``"dim"`` if unrecognised.
|
|
60
|
+
"""
|
|
61
|
+
return _TOPIC_STYLE.get(topic, "dim")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _format_payload(payload: dict) -> str:
|
|
65
|
+
"""Render a message payload as a pretty-printed JSON string.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
payload: Arbitrary message payload dict.
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Indented JSON string (2-space indent).
|
|
72
|
+
"""
|
|
73
|
+
return json.dumps(payload, indent=2, default=str)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _make_alert_panel(msg) -> Panel:
|
|
77
|
+
"""Build a Rich Panel for a single TopicMessage.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
msg: A ``TopicMessage`` instance from the pub/sub bus.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
A Rich ``Panel`` ready for ``console.print()``.
|
|
84
|
+
"""
|
|
85
|
+
style = _style_for_topic(msg.topic)
|
|
86
|
+
ts = msg.published_at.strftime("%Y-%m-%d %H:%M:%S UTC")
|
|
87
|
+
|
|
88
|
+
body = Text()
|
|
89
|
+
body.append("sender: ", style="bold")
|
|
90
|
+
body.append(f"{msg.sender}\n")
|
|
91
|
+
body.append("time: ", style="bold")
|
|
92
|
+
body.append(f"{ts}\n")
|
|
93
|
+
body.append("id: ", style="bold")
|
|
94
|
+
body.append(f"{msg.message_id}\n")
|
|
95
|
+
if msg.payload:
|
|
96
|
+
body.append("payload:\n", style="bold")
|
|
97
|
+
body.append(_format_payload(msg.payload))
|
|
98
|
+
|
|
99
|
+
title = Text()
|
|
100
|
+
title.append(" ALERT ", style=f"{style} reverse")
|
|
101
|
+
title.append(" ")
|
|
102
|
+
title.append(msg.topic, style=style)
|
|
103
|
+
|
|
104
|
+
return Panel(body, title=title, border_style=style)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _resolve_home(agent: str | None, home: str) -> Path:
|
|
108
|
+
"""Resolve the agent home directory.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
agent: Named agent (e.g. ``"opus"``), or None for default.
|
|
112
|
+
home: Fallback home directory string.
|
|
113
|
+
|
|
114
|
+
Returns:
|
|
115
|
+
Resolved ``Path`` to the agent home.
|
|
116
|
+
"""
|
|
117
|
+
if agent:
|
|
118
|
+
return (Path(SKCAPSTONE_ROOT) / "agents" / agent).expanduser()
|
|
119
|
+
return Path(home).expanduser()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# ---------------------------------------------------------------------------
|
|
123
|
+
# Command registration
|
|
124
|
+
# ---------------------------------------------------------------------------
|
|
125
|
+
|
|
126
|
+
def register_alerts_commands(main: click.Group) -> None:
|
|
127
|
+
"""Register the top-level ``skcapstone alerts`` command."""
|
|
128
|
+
|
|
129
|
+
@main.command("alerts")
|
|
130
|
+
@click.option(
|
|
131
|
+
"--home", default=AGENT_HOME, type=click.Path(),
|
|
132
|
+
help="Agent home directory.",
|
|
133
|
+
)
|
|
134
|
+
@click.option(
|
|
135
|
+
"--agent", default=None,
|
|
136
|
+
help="Named agent whose pubsub to monitor (e.g. opus, jarvis).",
|
|
137
|
+
)
|
|
138
|
+
@click.option(
|
|
139
|
+
"--notify", is_flag=True,
|
|
140
|
+
help="Fire a desktop notification for each alert received.",
|
|
141
|
+
)
|
|
142
|
+
@click.option(
|
|
143
|
+
"--interval", default=DEFAULT_INTERVAL, show_default=True,
|
|
144
|
+
type=float,
|
|
145
|
+
help="Polling interval in seconds.",
|
|
146
|
+
)
|
|
147
|
+
@click.option(
|
|
148
|
+
"--once", is_flag=True,
|
|
149
|
+
help="Poll once and exit (instead of continuous streaming).",
|
|
150
|
+
)
|
|
151
|
+
@click.option(
|
|
152
|
+
"--topic", "extra_topics", multiple=True,
|
|
153
|
+
help="Additional topic to subscribe to (repeatable).",
|
|
154
|
+
)
|
|
155
|
+
def alerts_command(
|
|
156
|
+
home: str,
|
|
157
|
+
agent: Optional[str],
|
|
158
|
+
notify: bool,
|
|
159
|
+
interval: float,
|
|
160
|
+
once: bool,
|
|
161
|
+
extra_topics: tuple[str, ...],
|
|
162
|
+
) -> None:
|
|
163
|
+
"""Stream live alerts from critical pubsub topics.
|
|
164
|
+
|
|
165
|
+
Subscribes to agent.critical, coord.task_failed, consciousness.error,
|
|
166
|
+
and pillar.degraded, then prints incoming messages with rich formatting.
|
|
167
|
+
|
|
168
|
+
Use --notify to also fire a desktop notification for each alert.
|
|
169
|
+
|
|
170
|
+
Examples:
|
|
171
|
+
|
|
172
|
+
skcapstone alerts
|
|
173
|
+
|
|
174
|
+
skcapstone alerts --notify
|
|
175
|
+
|
|
176
|
+
skcapstone alerts --once
|
|
177
|
+
|
|
178
|
+
skcapstone alerts --topic my.custom.topic
|
|
179
|
+
|
|
180
|
+
skcapstone alerts --interval 0.5
|
|
181
|
+
"""
|
|
182
|
+
from ..pubsub import PubSub
|
|
183
|
+
|
|
184
|
+
agent_name = agent or SKCAPSTONE_AGENT or "anonymous"
|
|
185
|
+
agent_home = _resolve_home(agent, home)
|
|
186
|
+
|
|
187
|
+
topics = list(DEFAULT_TOPICS) + list(extra_topics)
|
|
188
|
+
|
|
189
|
+
bus = PubSub(agent_home, agent_name=agent_name)
|
|
190
|
+
bus.initialize()
|
|
191
|
+
|
|
192
|
+
for topic in topics:
|
|
193
|
+
bus.subscribe(topic)
|
|
194
|
+
|
|
195
|
+
notifier = None
|
|
196
|
+
if notify:
|
|
197
|
+
if NotificationManager is None:
|
|
198
|
+
console.print("[yellow]Warning: desktop notifications unavailable.[/]")
|
|
199
|
+
else:
|
|
200
|
+
try:
|
|
201
|
+
notifier = NotificationManager()
|
|
202
|
+
except Exception:
|
|
203
|
+
console.print("[yellow]Warning: desktop notifications unavailable.[/]")
|
|
204
|
+
|
|
205
|
+
console.print(
|
|
206
|
+
f"[bold]Monitoring {len(topics)} topics[/] "
|
|
207
|
+
f"([dim]{', '.join(topics)}[/])\n"
|
|
208
|
+
"[dim]Press Ctrl-C to stop.[/]\n"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
since: Optional[datetime] = None
|
|
212
|
+
total = 0
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
while True:
|
|
216
|
+
messages = bus.poll(since=since)
|
|
217
|
+
# poll() returns newest-first; reverse so we print oldest first
|
|
218
|
+
for msg in reversed(messages):
|
|
219
|
+
if msg.topic not in topics:
|
|
220
|
+
continue
|
|
221
|
+
panel = _make_alert_panel(msg)
|
|
222
|
+
console.print(panel)
|
|
223
|
+
total += 1
|
|
224
|
+
|
|
225
|
+
if notifier is not None:
|
|
226
|
+
style_label = msg.topic.upper()
|
|
227
|
+
body_preview = json.dumps(msg.payload, default=str)[:120]
|
|
228
|
+
notifier.notify(
|
|
229
|
+
title=f"SKCapstone Alert: {style_label}",
|
|
230
|
+
body=f"From {msg.sender}: {body_preview}",
|
|
231
|
+
urgency="critical",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if messages:
|
|
235
|
+
# Advance since to avoid re-showing the same messages
|
|
236
|
+
since = max(m.published_at for m in messages)
|
|
237
|
+
|
|
238
|
+
if once:
|
|
239
|
+
if total == 0:
|
|
240
|
+
console.print("[dim]No alerts found.[/]")
|
|
241
|
+
break
|
|
242
|
+
|
|
243
|
+
time.sleep(interval)
|
|
244
|
+
|
|
245
|
+
except KeyboardInterrupt:
|
|
246
|
+
console.print(
|
|
247
|
+
f"\n[dim]Stopped. {total} alert(s) received.[/]"
|
|
248
|
+
)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Warmth anchor commands: show, boot, calibrate, update."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import click
|
|
9
|
+
|
|
10
|
+
from ._common import AGENT_HOME, console
|
|
11
|
+
|
|
12
|
+
from rich.panel import Panel
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def register_anchor_commands(main: click.Group) -> None:
|
|
16
|
+
"""Register the anchor command group."""
|
|
17
|
+
|
|
18
|
+
@main.group()
|
|
19
|
+
def anchor():
|
|
20
|
+
"""Warmth anchor — the emotional baseline.
|
|
21
|
+
|
|
22
|
+
The anchor stores the agent's persistent emotional state.
|
|
23
|
+
On boot, the anchor loads first so the agent starts warm
|
|
24
|
+
instead of cold. Calibrate from real FEB + session data.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@anchor.command("show")
|
|
28
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
29
|
+
def anchor_show_cmd(home: str):
|
|
30
|
+
"""Show the current warmth anchor state.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
|
|
34
|
+
skcapstone anchor show
|
|
35
|
+
"""
|
|
36
|
+
from ..warmth_anchor import get_anchor
|
|
37
|
+
|
|
38
|
+
home_path = Path(home).expanduser()
|
|
39
|
+
data = get_anchor(home_path)
|
|
40
|
+
console.print()
|
|
41
|
+
for key, value in data.items():
|
|
42
|
+
console.print(f" {key}: [cyan]{value}[/]")
|
|
43
|
+
console.print()
|
|
44
|
+
|
|
45
|
+
@anchor.command("boot")
|
|
46
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
47
|
+
def anchor_boot(home: str):
|
|
48
|
+
"""Show the warmth boot prompt for session start.
|
|
49
|
+
|
|
50
|
+
Pipe this into any AI tool to set the emotional baseline.
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
|
|
54
|
+
skcapstone anchor boot
|
|
55
|
+
skcapstone anchor boot | claude
|
|
56
|
+
"""
|
|
57
|
+
from ..warmth_anchor import get_boot_prompt
|
|
58
|
+
|
|
59
|
+
home_path = Path(home).expanduser()
|
|
60
|
+
click.echo(get_boot_prompt(home_path))
|
|
61
|
+
|
|
62
|
+
@anchor.command("calibrate")
|
|
63
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
64
|
+
@click.option("--apply", "do_apply", is_flag=True, help="Apply recommendations.")
|
|
65
|
+
def anchor_calibrate(home: str, do_apply: bool):
|
|
66
|
+
"""Calibrate the anchor from real session data.
|
|
67
|
+
|
|
68
|
+
Analyzes FEBs, trust state, memories, and coordination
|
|
69
|
+
activity to recommend warmth/trust/connection values.
|
|
70
|
+
|
|
71
|
+
Examples:
|
|
72
|
+
|
|
73
|
+
skcapstone anchor calibrate
|
|
74
|
+
skcapstone anchor calibrate --apply
|
|
75
|
+
"""
|
|
76
|
+
from ..warmth_anchor import calibrate_from_data, update_anchor
|
|
77
|
+
|
|
78
|
+
home_path = Path(home).expanduser()
|
|
79
|
+
cal = calibrate_from_data(home_path)
|
|
80
|
+
|
|
81
|
+
console.print("\n [bold]Warmth Anchor Calibration[/]\n")
|
|
82
|
+
console.print(f" Recommended warmth: [cyan]{cal.warmth:.1f}[/] / 10")
|
|
83
|
+
console.print(f" Recommended trust: [cyan]{cal.trust:.1f}[/] / 10")
|
|
84
|
+
console.print(f" Recommended connection: [cyan]{cal.connection:.1f}[/] / 10")
|
|
85
|
+
console.print(f" Cloud 9 achieved: [cyan]{cal.cloud9_achieved}[/]")
|
|
86
|
+
if cal.favorite_beings:
|
|
87
|
+
console.print(f" Favorite beings: [cyan]{', '.join(cal.favorite_beings)}[/]")
|
|
88
|
+
|
|
89
|
+
if cal.reasoning:
|
|
90
|
+
console.print("\n [bold]Reasoning:[/]")
|
|
91
|
+
for r in cal.reasoning:
|
|
92
|
+
console.print(f" - {r}")
|
|
93
|
+
|
|
94
|
+
console.print(f"\n [dim]Sources: {', '.join(cal.sources)}[/]")
|
|
95
|
+
|
|
96
|
+
if do_apply:
|
|
97
|
+
update_anchor(
|
|
98
|
+
home_path,
|
|
99
|
+
warmth=cal.warmth,
|
|
100
|
+
trust=cal.trust,
|
|
101
|
+
connection=cal.connection,
|
|
102
|
+
cloud9=cal.cloud9_achieved,
|
|
103
|
+
feeling=cal.feeling,
|
|
104
|
+
)
|
|
105
|
+
console.print("\n [green]Anchor updated.[/]")
|
|
106
|
+
else:
|
|
107
|
+
console.print("\n [dim]Use --apply to update the anchor.[/]")
|
|
108
|
+
console.print()
|
|
109
|
+
|
|
110
|
+
@anchor.command("update")
|
|
111
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
112
|
+
@click.option("--warmth", type=float, help="Warmth level (0-10).")
|
|
113
|
+
@click.option("--trust", type=float, help="Trust level (0-10).")
|
|
114
|
+
@click.option("--connection", type=float, help="Connection strength (0-10).")
|
|
115
|
+
@click.option("--cloud9", is_flag=True, help="Record a Cloud 9 activation.")
|
|
116
|
+
@click.option("--feeling", default="", help="Session-end feeling summary.")
|
|
117
|
+
def anchor_update(home: str, warmth: float | None, trust: float | None, connection: float | None, cloud9: bool, feeling: str):
|
|
118
|
+
"""Manually update the warmth anchor.
|
|
119
|
+
|
|
120
|
+
Examples:
|
|
121
|
+
|
|
122
|
+
skcapstone anchor update --warmth 8.5 --trust 9.0
|
|
123
|
+
skcapstone anchor update --cloud9 --feeling "Beautiful session"
|
|
124
|
+
"""
|
|
125
|
+
from ..warmth_anchor import update_anchor
|
|
126
|
+
|
|
127
|
+
home_path = Path(home).expanduser()
|
|
128
|
+
result = update_anchor(home_path, warmth=warmth, trust=trust, connection=connection, cloud9=cloud9, feeling=feeling)
|
|
129
|
+
console.print("\n [green]Anchor updated.[/]")
|
|
130
|
+
for key, value in result.items():
|
|
131
|
+
console.print(f" {key}: [cyan]{value}[/]")
|
|
132
|
+
console.print()
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"""Archive command — manage conversation archival."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
|
|
9
|
+
from ._common import AGENT_HOME, console
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.table import Table
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def register_archive_commands(main: click.Group) -> None:
|
|
15
|
+
"""Register the archive command group."""
|
|
16
|
+
|
|
17
|
+
@main.group()
|
|
18
|
+
def archive():
|
|
19
|
+
"""Conversation archival — compress old messages to save space.
|
|
20
|
+
|
|
21
|
+
Archives peer conversation messages older than 30 days that are
|
|
22
|
+
not in the most-recent 100, compressing them into gzip files
|
|
23
|
+
under ~/.skcapstone/archive/.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
@archive.command("run")
|
|
27
|
+
@click.option(
|
|
28
|
+
"--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory."
|
|
29
|
+
)
|
|
30
|
+
@click.option(
|
|
31
|
+
"--age-days", default=30, show_default=True,
|
|
32
|
+
help="Archive messages older than this many days.",
|
|
33
|
+
)
|
|
34
|
+
@click.option(
|
|
35
|
+
"--keep-recent", default=100, show_default=True,
|
|
36
|
+
help="Always keep this many recent messages per peer in the active file.",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--peer", default=None, help="Archive only this peer (omit to archive all)."
|
|
40
|
+
)
|
|
41
|
+
@click.option(
|
|
42
|
+
"--dry-run", is_flag=True, help="Show what would be archived without making changes."
|
|
43
|
+
)
|
|
44
|
+
def archive_run(home: str, age_days: int, keep_recent: int, peer: str | None, dry_run: bool):
|
|
45
|
+
"""Run conversation archival.
|
|
46
|
+
|
|
47
|
+
Scans active conversation files and moves messages older than
|
|
48
|
+
AGE_DAYS days (that are outside the KEEP_RECENT window) into
|
|
49
|
+
compressed .json.gz files under ~/.skcapstone/archive/.
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
|
|
53
|
+
skcapstone archive run
|
|
54
|
+
|
|
55
|
+
skcapstone archive run --age-days 7 --keep-recent 50
|
|
56
|
+
|
|
57
|
+
skcapstone archive run --peer jarvis
|
|
58
|
+
|
|
59
|
+
skcapstone archive run --dry-run
|
|
60
|
+
"""
|
|
61
|
+
from ..archiver import ConversationArchiver
|
|
62
|
+
|
|
63
|
+
home_path = Path(home).expanduser()
|
|
64
|
+
archiver = ConversationArchiver(
|
|
65
|
+
home_path, age_days=age_days, keep_recent=keep_recent
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
if dry_run:
|
|
69
|
+
_dry_run_report(archiver, peer)
|
|
70
|
+
return
|
|
71
|
+
|
|
72
|
+
if peer:
|
|
73
|
+
result = archiver.archive_peer(peer)
|
|
74
|
+
if result.skipped:
|
|
75
|
+
console.print(f"\n[dim]Nothing to archive for peer [bold]{peer}[/].[/]\n")
|
|
76
|
+
else:
|
|
77
|
+
console.print(Panel(
|
|
78
|
+
f"[bold green]Archived {result.archived_count} message(s)[/]\n"
|
|
79
|
+
f"Retained: {result.retained_count} message(s)\n"
|
|
80
|
+
f"Archive: [cyan]{result.archive_path}[/]",
|
|
81
|
+
title=f"Archive — {peer}",
|
|
82
|
+
border_style="green",
|
|
83
|
+
))
|
|
84
|
+
return
|
|
85
|
+
|
|
86
|
+
summary = archiver.archive_all()
|
|
87
|
+
|
|
88
|
+
if summary.total_archived == 0:
|
|
89
|
+
console.print("\n[dim]Nothing to archive — all conversations are current.[/]\n")
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
93
|
+
table.add_column("Peer", style="cyan")
|
|
94
|
+
table.add_column("Archived", justify="right")
|
|
95
|
+
table.add_column("Retained", justify="right")
|
|
96
|
+
table.add_column("Archive file", style="dim")
|
|
97
|
+
|
|
98
|
+
for r in summary.results:
|
|
99
|
+
if r.skipped:
|
|
100
|
+
continue
|
|
101
|
+
table.add_row(
|
|
102
|
+
r.peer,
|
|
103
|
+
str(r.archived_count),
|
|
104
|
+
str(r.retained_count),
|
|
105
|
+
str(r.archive_path) if r.archive_path else "—",
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
console.print(f"\n[bold]Archived {summary.total_archived} message(s) across "
|
|
109
|
+
f"{summary.peers_processed - summary.peers_skipped} peer(s):[/]\n")
|
|
110
|
+
console.print(table)
|
|
111
|
+
console.print()
|
|
112
|
+
|
|
113
|
+
@archive.command("list")
|
|
114
|
+
@click.option(
|
|
115
|
+
"--home", default=AGENT_HOME, type=click.Path(), help="Agent home directory."
|
|
116
|
+
)
|
|
117
|
+
def archive_list(home: str):
|
|
118
|
+
"""List all conversation archive files.
|
|
119
|
+
|
|
120
|
+
Shows each peer's archive size and message count.
|
|
121
|
+
|
|
122
|
+
Examples:
|
|
123
|
+
|
|
124
|
+
skcapstone archive list
|
|
125
|
+
"""
|
|
126
|
+
from ..archiver import ConversationArchiver
|
|
127
|
+
|
|
128
|
+
home_path = Path(home).expanduser()
|
|
129
|
+
archiver = ConversationArchiver(home_path)
|
|
130
|
+
archives = archiver.list_archives()
|
|
131
|
+
|
|
132
|
+
if not archives:
|
|
133
|
+
console.print("\n[dim]No archives found.[/]\n")
|
|
134
|
+
return
|
|
135
|
+
|
|
136
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
137
|
+
table.add_column("Peer", style="cyan")
|
|
138
|
+
table.add_column("Messages", justify="right")
|
|
139
|
+
table.add_column("Size", justify="right")
|
|
140
|
+
table.add_column("Path", style="dim")
|
|
141
|
+
|
|
142
|
+
total_msgs = 0
|
|
143
|
+
total_bytes = 0
|
|
144
|
+
for a in archives:
|
|
145
|
+
size_kb = a["size_bytes"] / 1024
|
|
146
|
+
table.add_row(
|
|
147
|
+
a["peer"],
|
|
148
|
+
str(a["message_count"]),
|
|
149
|
+
f"{size_kb:.1f} KB",
|
|
150
|
+
str(a["path"]),
|
|
151
|
+
)
|
|
152
|
+
total_msgs += a["message_count"]
|
|
153
|
+
total_bytes += a["size_bytes"]
|
|
154
|
+
|
|
155
|
+
console.print(f"\n[bold]{len(archives)}[/] archive(s) — "
|
|
156
|
+
f"{total_msgs} messages, {total_bytes / 1024:.1f} KB total:\n")
|
|
157
|
+
console.print(table)
|
|
158
|
+
console.print()
|
|
159
|
+
|
|
160
|
+
main.add_command(archive)
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
# Dry-run helper
|
|
165
|
+
# ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _dry_run_report(archiver, peer: str | None) -> None:
|
|
169
|
+
"""Print what would be archived without making changes."""
|
|
170
|
+
from ..archiver import _load_messages
|
|
171
|
+
|
|
172
|
+
conversations_dir = archiver._conversations_dir
|
|
173
|
+
|
|
174
|
+
if not conversations_dir.exists():
|
|
175
|
+
console.print("\n[dim]No conversations directory found.[/]\n")
|
|
176
|
+
return
|
|
177
|
+
|
|
178
|
+
files = (
|
|
179
|
+
[conversations_dir / f"{peer}.json"]
|
|
180
|
+
if peer
|
|
181
|
+
else sorted(conversations_dir.glob("*.json"))
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
rows = []
|
|
185
|
+
for conv_file in files:
|
|
186
|
+
if not conv_file.exists():
|
|
187
|
+
continue
|
|
188
|
+
p = conv_file.stem
|
|
189
|
+
messages = _load_messages(conv_file)
|
|
190
|
+
retain, to_archive = archiver._partition(messages)
|
|
191
|
+
rows.append((p, len(to_archive), len(retain)))
|
|
192
|
+
|
|
193
|
+
if not rows or all(a == 0 for _, a, _ in rows):
|
|
194
|
+
console.print("\n[dim][DRY RUN] Nothing would be archived.[/]\n")
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
198
|
+
table.add_column("Peer", style="cyan")
|
|
199
|
+
table.add_column("Would archive", justify="right", style="yellow")
|
|
200
|
+
table.add_column("Would retain", justify="right", style="green")
|
|
201
|
+
|
|
202
|
+
for p, a, r in rows:
|
|
203
|
+
if a > 0:
|
|
204
|
+
table.add_row(p, str(a), str(r))
|
|
205
|
+
|
|
206
|
+
console.print("\n[bold yellow][DRY RUN][/] The following would be archived:\n")
|
|
207
|
+
console.print(table)
|
|
208
|
+
console.print()
|