@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,104 @@
|
|
|
1
|
+
"""SKComm send/receive messaging tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _error_response, _json_response
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="send_message",
|
|
12
|
+
description=(
|
|
13
|
+
"Send a message to another agent via SKComm. "
|
|
14
|
+
"Routes through available transports (Syncthing, file)."
|
|
15
|
+
),
|
|
16
|
+
inputSchema={
|
|
17
|
+
"type": "object",
|
|
18
|
+
"properties": {
|
|
19
|
+
"recipient": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"description": "Agent name or PGP fingerprint of the recipient",
|
|
22
|
+
},
|
|
23
|
+
"message": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"description": "The message content",
|
|
26
|
+
},
|
|
27
|
+
"urgency": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"enum": ["low", "normal", "high", "critical"],
|
|
30
|
+
"description": "Message urgency (default: normal)",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
"required": ["recipient", "message"],
|
|
34
|
+
},
|
|
35
|
+
),
|
|
36
|
+
Tool(
|
|
37
|
+
name="check_inbox",
|
|
38
|
+
description=(
|
|
39
|
+
"Check for new incoming messages across all SKComm transports. "
|
|
40
|
+
"Returns any unread message envelopes."
|
|
41
|
+
),
|
|
42
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
43
|
+
),
|
|
44
|
+
]
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def _handle_send_message(args: dict) -> list[TextContent]:
|
|
48
|
+
"""Send a message via SKComm."""
|
|
49
|
+
recipient = args.get("recipient", "")
|
|
50
|
+
message = args.get("message", "")
|
|
51
|
+
if not recipient or not message:
|
|
52
|
+
return _error_response("recipient and message are required")
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
from skcomm.core import SKComm
|
|
56
|
+
comm = SKComm.from_config()
|
|
57
|
+
report = comm.send(recipient, message)
|
|
58
|
+
return _json_response({
|
|
59
|
+
"sent": report.success,
|
|
60
|
+
"recipient": recipient,
|
|
61
|
+
"attempts": [
|
|
62
|
+
{
|
|
63
|
+
"transport": a.transport_name,
|
|
64
|
+
"success": a.success,
|
|
65
|
+
"error": a.error,
|
|
66
|
+
}
|
|
67
|
+
for a in report.attempts
|
|
68
|
+
],
|
|
69
|
+
})
|
|
70
|
+
except ImportError:
|
|
71
|
+
return _error_response("SKComm not installed. Run: pip install skcomm")
|
|
72
|
+
except Exception as exc:
|
|
73
|
+
return _error_response(f"Send failed: {exc}")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
async def _handle_check_inbox(_args: dict) -> list[TextContent]:
|
|
77
|
+
"""Check for incoming messages."""
|
|
78
|
+
try:
|
|
79
|
+
from skcomm.core import SKComm
|
|
80
|
+
comm = SKComm.from_config()
|
|
81
|
+
envelopes = comm.receive()
|
|
82
|
+
return _json_response([
|
|
83
|
+
{
|
|
84
|
+
"envelope_id": e.envelope_id[:12],
|
|
85
|
+
"sender": e.sender,
|
|
86
|
+
"recipient": e.recipient,
|
|
87
|
+
"content": e.payload.content[:300],
|
|
88
|
+
"type": e.payload.content_type.value,
|
|
89
|
+
"urgency": e.metadata.urgency.value,
|
|
90
|
+
"thread_id": e.metadata.thread_id,
|
|
91
|
+
"created_at": e.metadata.created_at.isoformat(),
|
|
92
|
+
}
|
|
93
|
+
for e in envelopes
|
|
94
|
+
])
|
|
95
|
+
except ImportError:
|
|
96
|
+
return _error_response("SKComm not installed. Run: pip install skcomm")
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
return _error_response(f"Inbox check failed: {exc}")
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
HANDLERS: dict = {
|
|
102
|
+
"send_message": _handle_send_message,
|
|
103
|
+
"check_inbox": _handle_check_inbox,
|
|
104
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"""Consciousness loop MCP tools — status and testing."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _home, _json_response, _text_response, _error_response
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="consciousness_status",
|
|
12
|
+
description=(
|
|
13
|
+
"Get consciousness loop status: enabled state, messages processed, "
|
|
14
|
+
"responses sent, errors, backend health, inotify state, and "
|
|
15
|
+
"active conversations."
|
|
16
|
+
),
|
|
17
|
+
inputSchema={
|
|
18
|
+
"type": "object",
|
|
19
|
+
"properties": {},
|
|
20
|
+
"required": [],
|
|
21
|
+
},
|
|
22
|
+
),
|
|
23
|
+
Tool(
|
|
24
|
+
name="consciousness_test",
|
|
25
|
+
description=(
|
|
26
|
+
"Test the consciousness pipeline end-to-end with a message. "
|
|
27
|
+
"Classifies the message, builds the agent system prompt, routes "
|
|
28
|
+
"to the appropriate LLM, and returns the response."
|
|
29
|
+
),
|
|
30
|
+
inputSchema={
|
|
31
|
+
"type": "object",
|
|
32
|
+
"properties": {
|
|
33
|
+
"message": {
|
|
34
|
+
"type": "string",
|
|
35
|
+
"description": "The test message to process",
|
|
36
|
+
},
|
|
37
|
+
},
|
|
38
|
+
"required": ["message"],
|
|
39
|
+
},
|
|
40
|
+
),
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def _handle_consciousness_status(arguments: dict) -> list[TextContent]:
|
|
45
|
+
"""Handle consciousness_status tool call."""
|
|
46
|
+
try:
|
|
47
|
+
import urllib.request
|
|
48
|
+
import json
|
|
49
|
+
|
|
50
|
+
url = "http://127.0.0.1:7777/consciousness"
|
|
51
|
+
with urllib.request.urlopen(url, timeout=3) as resp:
|
|
52
|
+
data = json.loads(resp.read())
|
|
53
|
+
return _json_response(data)
|
|
54
|
+
except Exception:
|
|
55
|
+
# Fallback: try to load directly
|
|
56
|
+
try:
|
|
57
|
+
from ..consciousness_config import load_consciousness_config
|
|
58
|
+
from ..consciousness_loop import ConsciousnessConfig, LLMBridge
|
|
59
|
+
|
|
60
|
+
home = _home()
|
|
61
|
+
config = load_consciousness_config(home)
|
|
62
|
+
bridge = LLMBridge(config)
|
|
63
|
+
|
|
64
|
+
return _json_response({
|
|
65
|
+
"enabled": config.enabled,
|
|
66
|
+
"backends": bridge.health_check(),
|
|
67
|
+
"daemon_reachable": False,
|
|
68
|
+
"note": "Loaded directly (daemon not running)",
|
|
69
|
+
})
|
|
70
|
+
except Exception as exc:
|
|
71
|
+
return _error_response(f"Cannot get consciousness status: {exc}")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
async def _handle_consciousness_test(arguments: dict) -> list[TextContent]:
|
|
75
|
+
"""Handle consciousness_test tool call."""
|
|
76
|
+
message = arguments.get("message", "")
|
|
77
|
+
if not message:
|
|
78
|
+
return _error_response("message is required")
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
from ..consciousness_config import load_consciousness_config
|
|
82
|
+
from ..consciousness_loop import (
|
|
83
|
+
LLMBridge,
|
|
84
|
+
SystemPromptBuilder,
|
|
85
|
+
_classify_message,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
home = _home()
|
|
89
|
+
config = load_consciousness_config(home)
|
|
90
|
+
bridge = LLMBridge(config)
|
|
91
|
+
builder = SystemPromptBuilder(home, config.max_context_tokens)
|
|
92
|
+
|
|
93
|
+
signal = _classify_message(message)
|
|
94
|
+
system_prompt = builder.build()
|
|
95
|
+
response = bridge.generate(system_prompt, message, signal)
|
|
96
|
+
|
|
97
|
+
return _json_response({
|
|
98
|
+
"message": message,
|
|
99
|
+
"signal": {
|
|
100
|
+
"tags": signal.tags,
|
|
101
|
+
"estimated_tokens": signal.estimated_tokens,
|
|
102
|
+
},
|
|
103
|
+
"system_prompt_length": len(system_prompt),
|
|
104
|
+
"response": response,
|
|
105
|
+
"response_length": len(response),
|
|
106
|
+
})
|
|
107
|
+
except Exception as exc:
|
|
108
|
+
return _error_response(f"Consciousness test failed: {exc}")
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
HANDLERS = {
|
|
112
|
+
"consciousness_status": _handle_consciousness_status,
|
|
113
|
+
"consciousness_test": _handle_consciousness_test,
|
|
114
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
"""Coordination board tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from mcp.types import TextContent, Tool
|
|
6
|
+
|
|
7
|
+
from ._helpers import _error_response, _home, _json_response, _shared_root
|
|
8
|
+
|
|
9
|
+
TOOLS: list[Tool] = [
|
|
10
|
+
Tool(
|
|
11
|
+
name="coord_status",
|
|
12
|
+
description=(
|
|
13
|
+
"Show the multi-agent coordination board. Lists all tasks "
|
|
14
|
+
"with status, priority, and assignees. Shows active agents."
|
|
15
|
+
),
|
|
16
|
+
inputSchema={"type": "object", "properties": {}, "required": []},
|
|
17
|
+
),
|
|
18
|
+
Tool(
|
|
19
|
+
name="coord_claim",
|
|
20
|
+
description=(
|
|
21
|
+
"Claim a task on the coordination board for an agent. "
|
|
22
|
+
"Prevents duplicate work across agents."
|
|
23
|
+
),
|
|
24
|
+
inputSchema={
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"task_id": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": "The task ID to claim",
|
|
30
|
+
},
|
|
31
|
+
"agent_name": {
|
|
32
|
+
"type": "string",
|
|
33
|
+
"description": "Agent name claiming the task",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
"required": ["task_id", "agent_name"],
|
|
37
|
+
},
|
|
38
|
+
),
|
|
39
|
+
Tool(
|
|
40
|
+
name="coord_complete",
|
|
41
|
+
description=(
|
|
42
|
+
"Mark a task as completed on the coordination board."
|
|
43
|
+
),
|
|
44
|
+
inputSchema={
|
|
45
|
+
"type": "object",
|
|
46
|
+
"properties": {
|
|
47
|
+
"task_id": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"description": "The task ID to complete",
|
|
50
|
+
},
|
|
51
|
+
"agent_name": {
|
|
52
|
+
"type": "string",
|
|
53
|
+
"description": "Agent name completing the task",
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
"required": ["task_id", "agent_name"],
|
|
57
|
+
},
|
|
58
|
+
),
|
|
59
|
+
Tool(
|
|
60
|
+
name="coord_create",
|
|
61
|
+
description=(
|
|
62
|
+
"Create a new task on the coordination board."
|
|
63
|
+
),
|
|
64
|
+
inputSchema={
|
|
65
|
+
"type": "object",
|
|
66
|
+
"properties": {
|
|
67
|
+
"title": {
|
|
68
|
+
"type": "string",
|
|
69
|
+
"description": "Task title",
|
|
70
|
+
},
|
|
71
|
+
"description": {
|
|
72
|
+
"type": "string",
|
|
73
|
+
"description": "Task description",
|
|
74
|
+
},
|
|
75
|
+
"priority": {
|
|
76
|
+
"type": "string",
|
|
77
|
+
"enum": ["critical", "high", "medium", "low"],
|
|
78
|
+
"description": "Task priority (default: medium)",
|
|
79
|
+
},
|
|
80
|
+
"tags": {
|
|
81
|
+
"type": "array",
|
|
82
|
+
"items": {"type": "string"},
|
|
83
|
+
"description": "Task tags",
|
|
84
|
+
},
|
|
85
|
+
"created_by": {
|
|
86
|
+
"type": "string",
|
|
87
|
+
"description": "Creator agent name",
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
"required": ["title"],
|
|
91
|
+
},
|
|
92
|
+
),
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def _handle_coord_status(_args: dict) -> list[TextContent]:
|
|
97
|
+
"""Return coordination board status."""
|
|
98
|
+
from ..coordination import Board
|
|
99
|
+
|
|
100
|
+
board = Board(_shared_root())
|
|
101
|
+
views = board.get_task_views()
|
|
102
|
+
agents = board.load_agents()
|
|
103
|
+
|
|
104
|
+
return _json_response({
|
|
105
|
+
"tasks": [
|
|
106
|
+
{
|
|
107
|
+
"id": v.task.id,
|
|
108
|
+
"title": v.task.title,
|
|
109
|
+
"priority": v.task.priority.value,
|
|
110
|
+
"status": v.status.value,
|
|
111
|
+
"claimed_by": v.claimed_by,
|
|
112
|
+
"tags": v.task.tags,
|
|
113
|
+
"description": v.task.description[:150] if v.task.description else "",
|
|
114
|
+
}
|
|
115
|
+
for v in views
|
|
116
|
+
],
|
|
117
|
+
"agents": [
|
|
118
|
+
{
|
|
119
|
+
"name": a.agent,
|
|
120
|
+
"state": a.state.value,
|
|
121
|
+
"current_task": a.current_task,
|
|
122
|
+
"claimed": a.claimed_tasks,
|
|
123
|
+
"completed_count": len(a.completed_tasks),
|
|
124
|
+
}
|
|
125
|
+
for a in agents
|
|
126
|
+
],
|
|
127
|
+
"summary": {
|
|
128
|
+
"total": len(views),
|
|
129
|
+
"open": sum(1 for v in views if v.status.value == "open"),
|
|
130
|
+
"claimed": sum(1 for v in views if v.status.value == "claimed"),
|
|
131
|
+
"in_progress": sum(1 for v in views if v.status.value == "in_progress"),
|
|
132
|
+
"done": sum(1 for v in views if v.status.value == "done"),
|
|
133
|
+
},
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
async def _handle_coord_claim(args: dict) -> list[TextContent]:
|
|
138
|
+
"""Claim a task on the board."""
|
|
139
|
+
from ..coordination import Board
|
|
140
|
+
|
|
141
|
+
task_id = args.get("task_id", "")
|
|
142
|
+
agent_name = args.get("agent_name", "")
|
|
143
|
+
if not task_id or not agent_name:
|
|
144
|
+
return _error_response("task_id and agent_name are required")
|
|
145
|
+
|
|
146
|
+
board = Board(_shared_root())
|
|
147
|
+
try:
|
|
148
|
+
agent = board.claim_task(agent_name, task_id)
|
|
149
|
+
try:
|
|
150
|
+
from .. import activity
|
|
151
|
+
activity.push("task.claimed", {"task_id": task_id, "agent": agent_name})
|
|
152
|
+
except Exception:
|
|
153
|
+
pass
|
|
154
|
+
return _json_response({
|
|
155
|
+
"claimed": True,
|
|
156
|
+
"task_id": task_id,
|
|
157
|
+
"agent": agent.agent,
|
|
158
|
+
"current_task": agent.current_task,
|
|
159
|
+
})
|
|
160
|
+
except ValueError as exc:
|
|
161
|
+
return _error_response(str(exc))
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
async def _handle_coord_complete(args: dict) -> list[TextContent]:
|
|
165
|
+
"""Complete a task on the board."""
|
|
166
|
+
from ..coordination import Board
|
|
167
|
+
|
|
168
|
+
task_id = args.get("task_id", "")
|
|
169
|
+
agent_name = args.get("agent_name", "")
|
|
170
|
+
if not task_id or not agent_name:
|
|
171
|
+
return _error_response("task_id and agent_name are required")
|
|
172
|
+
|
|
173
|
+
board = Board(_shared_root())
|
|
174
|
+
agent = board.complete_task(agent_name, task_id)
|
|
175
|
+
try:
|
|
176
|
+
from .. import activity
|
|
177
|
+
activity.push("task.completed", {"task_id": task_id, "agent": agent_name})
|
|
178
|
+
except Exception:
|
|
179
|
+
pass
|
|
180
|
+
return _json_response({
|
|
181
|
+
"completed": True,
|
|
182
|
+
"task_id": task_id,
|
|
183
|
+
"agent": agent.agent,
|
|
184
|
+
"completed_tasks": agent.completed_tasks,
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
async def _handle_coord_create(args: dict) -> list[TextContent]:
|
|
189
|
+
"""Create a new task on the board."""
|
|
190
|
+
from ..coordination import Board, Task, TaskPriority
|
|
191
|
+
|
|
192
|
+
title = args.get("title", "")
|
|
193
|
+
if not title:
|
|
194
|
+
return _error_response("title is required")
|
|
195
|
+
|
|
196
|
+
board = Board(_shared_root())
|
|
197
|
+
task = Task(
|
|
198
|
+
title=title,
|
|
199
|
+
description=args.get("description", ""),
|
|
200
|
+
priority=TaskPriority(args.get("priority", "medium")),
|
|
201
|
+
tags=args.get("tags", []),
|
|
202
|
+
created_by=args.get("created_by", "mcp"),
|
|
203
|
+
)
|
|
204
|
+
path = board.create_task(task)
|
|
205
|
+
return _json_response({
|
|
206
|
+
"created": True,
|
|
207
|
+
"task_id": task.id,
|
|
208
|
+
"title": task.title,
|
|
209
|
+
"priority": task.priority.value,
|
|
210
|
+
"path": str(path),
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
HANDLERS: dict = {
|
|
215
|
+
"coord_status": _handle_coord_status,
|
|
216
|
+
"coord_claim": _handle_coord_claim,
|
|
217
|
+
"coord_complete": _handle_coord_complete,
|
|
218
|
+
"coord_create": _handle_coord_create,
|
|
219
|
+
}
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
"""Deploy status tool — platform detection, secrets backend, last deploy, ArgoCD sync."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from mcp.types import TextContent, Tool
|
|
11
|
+
|
|
12
|
+
from ._helpers import _json_response
|
|
13
|
+
|
|
14
|
+
TOOLS: list[Tool] = [
|
|
15
|
+
Tool(
|
|
16
|
+
name="deploy_status",
|
|
17
|
+
description=(
|
|
18
|
+
"Report infrastructure deployment status: detected platform "
|
|
19
|
+
"(swarm/k8s/rke2 from skstacks/v2/ layout), active secrets "
|
|
20
|
+
"backend (SKSTACKS_SECRET_BACKEND env), last deploy commit "
|
|
21
|
+
"(git log --oneline -1), and ArgoCD app-of-apps sync/health "
|
|
22
|
+
"when app-of-apps.yaml is present. Returns structured JSON."
|
|
23
|
+
),
|
|
24
|
+
inputSchema={
|
|
25
|
+
"type": "object",
|
|
26
|
+
"properties": {
|
|
27
|
+
"skstacks_root": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"description": (
|
|
30
|
+
"Absolute path to skstacks/v2/. "
|
|
31
|
+
"Auto-detected from git root or SKSTACKS_V2_ROOT env if omitted."
|
|
32
|
+
),
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
"required": [],
|
|
36
|
+
},
|
|
37
|
+
),
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ── Internal helpers ──────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _git_root() -> Optional[Path]:
|
|
45
|
+
"""Return the git repository root, or None if not in a repo."""
|
|
46
|
+
try:
|
|
47
|
+
result = subprocess.run(
|
|
48
|
+
["git", "rev-parse", "--show-toplevel"],
|
|
49
|
+
capture_output=True,
|
|
50
|
+
text=True,
|
|
51
|
+
timeout=5,
|
|
52
|
+
)
|
|
53
|
+
if result.returncode == 0:
|
|
54
|
+
return Path(result.stdout.strip())
|
|
55
|
+
except (FileNotFoundError, subprocess.TimeoutExpired):
|
|
56
|
+
pass
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _find_skstacks_v2(override: Optional[str] = None) -> Optional[Path]:
|
|
61
|
+
"""Locate the skstacks/v2/ directory.
|
|
62
|
+
|
|
63
|
+
Resolution order:
|
|
64
|
+
1. *override* argument (caller-supplied path)
|
|
65
|
+
2. ``SKSTACKS_V2_ROOT`` environment variable
|
|
66
|
+
3. ``<git-root>/skstacks/v2/``
|
|
67
|
+
4. ``<cwd>/skstacks/v2/``
|
|
68
|
+
"""
|
|
69
|
+
if override:
|
|
70
|
+
p = Path(override).expanduser()
|
|
71
|
+
return p if p.exists() else None
|
|
72
|
+
|
|
73
|
+
env_val = os.environ.get("SKSTACKS_V2_ROOT")
|
|
74
|
+
if env_val:
|
|
75
|
+
p = Path(env_val).expanduser()
|
|
76
|
+
return p if p.exists() else None
|
|
77
|
+
|
|
78
|
+
git_root = _git_root()
|
|
79
|
+
if git_root:
|
|
80
|
+
candidate = git_root / "skstacks" / "v2"
|
|
81
|
+
if candidate.exists():
|
|
82
|
+
return candidate
|
|
83
|
+
|
|
84
|
+
cwd_candidate = Path.cwd() / "skstacks" / "v2"
|
|
85
|
+
if cwd_candidate.exists():
|
|
86
|
+
return cwd_candidate
|
|
87
|
+
|
|
88
|
+
return None
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _detect_platforms(v2_root: Path) -> list[str]:
|
|
92
|
+
"""Return detected platforms from skstacks/v2/ structure.
|
|
93
|
+
|
|
94
|
+
* ``rke2`` — ``platform/rke2/`` directory present
|
|
95
|
+
* ``swarm`` — any ``docker-compose*.j2`` file found recursively
|
|
96
|
+
* ``k8s`` — ``overlays/`` directory present
|
|
97
|
+
"""
|
|
98
|
+
detected: list[str] = []
|
|
99
|
+
|
|
100
|
+
if (v2_root / "platform" / "rke2").exists():
|
|
101
|
+
detected.append("rke2")
|
|
102
|
+
|
|
103
|
+
if list(v2_root.rglob("docker-compose*.j2")):
|
|
104
|
+
detected.append("swarm")
|
|
105
|
+
|
|
106
|
+
if (v2_root / "overlays").exists():
|
|
107
|
+
detected.append("k8s")
|
|
108
|
+
|
|
109
|
+
return detected or ["unknown"]
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _active_secrets_backend() -> str:
|
|
113
|
+
"""Read ``SKSTACKS_SECRET_BACKEND`` env var (default: ``vault-file``)."""
|
|
114
|
+
return os.environ.get("SKSTACKS_SECRET_BACKEND", "vault-file")
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _last_deploy_commit(repo_root: Optional[Path] = None) -> str:
|
|
118
|
+
"""Return ``git log --oneline -1`` from *repo_root* (or CWD)."""
|
|
119
|
+
try:
|
|
120
|
+
result = subprocess.run(
|
|
121
|
+
["git", "log", "--oneline", "-1"],
|
|
122
|
+
capture_output=True,
|
|
123
|
+
text=True,
|
|
124
|
+
timeout=5,
|
|
125
|
+
cwd=str(repo_root) if repo_root else None,
|
|
126
|
+
)
|
|
127
|
+
if result.returncode == 0:
|
|
128
|
+
return result.stdout.strip()
|
|
129
|
+
return f"error: {result.stderr.strip()}"
|
|
130
|
+
except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
|
|
131
|
+
return f"error: {exc}"
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _argocd_sync_status(v2_root: Path) -> Optional[dict]:
|
|
135
|
+
"""Return ArgoCD sync/health for app-of-apps if the manifest exists.
|
|
136
|
+
|
|
137
|
+
Runs ``kubectl get application app-of-apps -n argocd`` and extracts
|
|
138
|
+
``.status.sync.status`` + ``.status.health.status``. When kubectl is
|
|
139
|
+
unavailable or the cluster is unreachable the error is returned in the
|
|
140
|
+
dict rather than raising.
|
|
141
|
+
"""
|
|
142
|
+
aoa_path = v2_root / "cicd" / "argocd" / "app-of-apps.yaml"
|
|
143
|
+
if not aoa_path.exists():
|
|
144
|
+
return None
|
|
145
|
+
|
|
146
|
+
base: dict = {"app": "app-of-apps", "manifest": str(aoa_path)}
|
|
147
|
+
try:
|
|
148
|
+
result = subprocess.run(
|
|
149
|
+
[
|
|
150
|
+
"kubectl", "get", "application", "app-of-apps",
|
|
151
|
+
"-n", "argocd",
|
|
152
|
+
"-o", "jsonpath={.status.sync.status} {.status.health.status}",
|
|
153
|
+
],
|
|
154
|
+
capture_output=True,
|
|
155
|
+
text=True,
|
|
156
|
+
timeout=10,
|
|
157
|
+
)
|
|
158
|
+
if result.returncode == 0:
|
|
159
|
+
parts = result.stdout.strip().split()
|
|
160
|
+
return {
|
|
161
|
+
**base,
|
|
162
|
+
"sync": parts[0] if parts else "Unknown",
|
|
163
|
+
"health": parts[1] if len(parts) > 1 else "Unknown",
|
|
164
|
+
}
|
|
165
|
+
return {**base, "error": result.stderr.strip() or "kubectl returned non-zero"}
|
|
166
|
+
except (FileNotFoundError, subprocess.TimeoutExpired) as exc:
|
|
167
|
+
return {**base, "error": str(exc)}
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
# ── Handler ───────────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
async def _handle_deploy_status(args: dict) -> list[TextContent]:
|
|
174
|
+
"""Return deployment status: platform, secrets backend, last commit, ArgoCD."""
|
|
175
|
+
v2_root = _find_skstacks_v2(args.get("skstacks_root"))
|
|
176
|
+
repo_root = _git_root()
|
|
177
|
+
|
|
178
|
+
if v2_root is None:
|
|
179
|
+
return _json_response({
|
|
180
|
+
"platforms": ["unknown"],
|
|
181
|
+
"skstacks_v2": None,
|
|
182
|
+
"secrets_backend": _active_secrets_backend(),
|
|
183
|
+
"last_deploy": _last_deploy_commit(repo_root),
|
|
184
|
+
"argocd": None,
|
|
185
|
+
"warning": (
|
|
186
|
+
"skstacks/v2/ not found; "
|
|
187
|
+
"set SKSTACKS_V2_ROOT or run from repo root"
|
|
188
|
+
),
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
return _json_response({
|
|
192
|
+
"platforms": _detect_platforms(v2_root),
|
|
193
|
+
"skstacks_v2": str(v2_root),
|
|
194
|
+
"secrets_backend": _active_secrets_backend(),
|
|
195
|
+
"last_deploy": _last_deploy_commit(repo_root),
|
|
196
|
+
"argocd": _argocd_sync_status(v2_root),
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
HANDLERS: dict = {
|
|
201
|
+
"deploy_status": _handle_deploy_status,
|
|
202
|
+
}
|