@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,143 @@
|
|
|
1
|
+
"""
|
|
2
|
+
KMS Auto-Rotation Scheduler.
|
|
3
|
+
|
|
4
|
+
Background daemon thread that checks daily for KMS keys whose
|
|
5
|
+
``next_rotation_at`` timestamp is in the past and rotates them
|
|
6
|
+
automatically.
|
|
7
|
+
|
|
8
|
+
Rotation policy (stored in key metadata):
|
|
9
|
+
service keys — every 30 days
|
|
10
|
+
team keys — every 90 days
|
|
11
|
+
|
|
12
|
+
On each rotation the scheduler:
|
|
13
|
+
1. Calls ``KeyStore.rotate_key`` with reason='scheduled-auto-rotation'
|
|
14
|
+
2. Sends a desktop notification via notify-send (best-effort)
|
|
15
|
+
3. Stores a memory entry tagged ``key-rotation`` in the agent memory
|
|
16
|
+
|
|
17
|
+
The scheduler thread is a daemon thread and exits when the MCP server
|
|
18
|
+
process terminates. Call ``start()`` once from ``mcp_server._run_server()``.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import logging
|
|
24
|
+
import subprocess
|
|
25
|
+
import threading
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger("skcapstone.kms_scheduler")
|
|
29
|
+
|
|
30
|
+
# How often to check for due keys (86400 s = 24 h).
|
|
31
|
+
_CHECK_INTERVAL = 86_400
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class KMSRotationScheduler:
|
|
35
|
+
"""Daemon thread that auto-rotates KMS service/team keys on schedule.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
home: Agent home directory (~/.skcapstone).
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
def __init__(self, home: Path) -> None:
|
|
42
|
+
self._home = home
|
|
43
|
+
self._thread: threading.Thread | None = None
|
|
44
|
+
self._stop_event = threading.Event()
|
|
45
|
+
|
|
46
|
+
def start(self) -> None:
|
|
47
|
+
"""Start the scheduler background thread (idempotent)."""
|
|
48
|
+
if self._thread and self._thread.is_alive():
|
|
49
|
+
return
|
|
50
|
+
self._stop_event.clear()
|
|
51
|
+
self._thread = threading.Thread(
|
|
52
|
+
target=self._loop,
|
|
53
|
+
name="kms-rotation-scheduler",
|
|
54
|
+
daemon=True,
|
|
55
|
+
)
|
|
56
|
+
self._thread.start()
|
|
57
|
+
logger.info("KMS rotation scheduler started (check interval: %ds)", _CHECK_INTERVAL)
|
|
58
|
+
|
|
59
|
+
def stop(self) -> None:
|
|
60
|
+
"""Signal the scheduler to stop after the current sleep."""
|
|
61
|
+
self._stop_event.set()
|
|
62
|
+
|
|
63
|
+
# ------------------------------------------------------------------
|
|
64
|
+
# Internal
|
|
65
|
+
# ------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
def _loop(self) -> None:
|
|
68
|
+
"""Main scheduler loop — runs until stop() is called."""
|
|
69
|
+
while not self._stop_event.is_set():
|
|
70
|
+
try:
|
|
71
|
+
self._check_and_rotate()
|
|
72
|
+
except Exception:
|
|
73
|
+
logger.exception("Unhandled error during KMS rotation check")
|
|
74
|
+
# Sleep for the check interval, but wake immediately if stopped.
|
|
75
|
+
self._stop_event.wait(timeout=_CHECK_INTERVAL)
|
|
76
|
+
|
|
77
|
+
def _check_and_rotate(self) -> None:
|
|
78
|
+
"""Rotate all keys whose next_rotation_at is in the past."""
|
|
79
|
+
from .kms import KeyStore
|
|
80
|
+
|
|
81
|
+
store = KeyStore(self._home)
|
|
82
|
+
store.initialize()
|
|
83
|
+
|
|
84
|
+
due = store.get_due_for_rotation()
|
|
85
|
+
if not due:
|
|
86
|
+
logger.debug("KMS rotation check: no keys due")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
logger.info("KMS rotation check: %d key(s) due for rotation", len(due))
|
|
90
|
+
for key in due:
|
|
91
|
+
try:
|
|
92
|
+
new_key = store.rotate_key(key.key_id, reason="scheduled-auto-rotation")
|
|
93
|
+
logger.info(
|
|
94
|
+
"Auto-rotated %s key '%s' → v%d",
|
|
95
|
+
key.key_type.value,
|
|
96
|
+
key.label,
|
|
97
|
+
new_key.version,
|
|
98
|
+
)
|
|
99
|
+
self._send_notification(key.label, key.key_type.value, new_key.version)
|
|
100
|
+
self._store_memory(key.label, key.key_type.value, new_key.version)
|
|
101
|
+
except Exception:
|
|
102
|
+
logger.exception(
|
|
103
|
+
"Failed to auto-rotate key '%s' (%s)", key.label, key.key_id
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def _send_notification(self, label: str, key_type: str, new_version: int) -> None:
|
|
107
|
+
"""Send a desktop notification (best-effort, never raises)."""
|
|
108
|
+
try:
|
|
109
|
+
subprocess.run(
|
|
110
|
+
[
|
|
111
|
+
"notify-send",
|
|
112
|
+
"--urgency", "normal",
|
|
113
|
+
"KMS Key Auto-Rotated",
|
|
114
|
+
f"{key_type} key '{label}' rotated to v{new_version}",
|
|
115
|
+
],
|
|
116
|
+
check=False,
|
|
117
|
+
timeout=5,
|
|
118
|
+
capture_output=True,
|
|
119
|
+
)
|
|
120
|
+
except Exception:
|
|
121
|
+
pass # Notification failure must never interrupt rotation
|
|
122
|
+
|
|
123
|
+
def _store_memory(self, label: str, key_type: str, new_version: int) -> None:
|
|
124
|
+
"""Persist a memory entry tagged key-rotation (best-effort)."""
|
|
125
|
+
try:
|
|
126
|
+
from .memory_engine import store as memory_store
|
|
127
|
+
|
|
128
|
+
content = (
|
|
129
|
+
f"KMS auto-rotation: {key_type} key '{label}' rotated to "
|
|
130
|
+
f"v{new_version} via scheduled rotation policy "
|
|
131
|
+
f"({30 if key_type == 'service' else 90}-day interval)."
|
|
132
|
+
)
|
|
133
|
+
memory_store(
|
|
134
|
+
home=self._home,
|
|
135
|
+
content=content,
|
|
136
|
+
tags=["key-rotation", "kms", "security"],
|
|
137
|
+
source="kms-scheduler",
|
|
138
|
+
importance=0.6,
|
|
139
|
+
)
|
|
140
|
+
except Exception:
|
|
141
|
+
logger.warning(
|
|
142
|
+
"Failed to store key-rotation memory for '%s'", label, exc_info=True
|
|
143
|
+
)
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""Structured JSON logging with rotation for the skcapstone daemon."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import logging
|
|
7
|
+
import logging.handlers
|
|
8
|
+
from datetime import datetime, timezone
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
# Attributes present on every LogRecord — excluded from JSON extra-field pass-through.
|
|
12
|
+
_LOG_RECORD_BUILTIN_ATTRS: frozenset[str] = frozenset(
|
|
13
|
+
{
|
|
14
|
+
"args",
|
|
15
|
+
"created",
|
|
16
|
+
"exc_info",
|
|
17
|
+
"exc_text",
|
|
18
|
+
"filename",
|
|
19
|
+
"funcName",
|
|
20
|
+
"levelname",
|
|
21
|
+
"levelno",
|
|
22
|
+
"lineno",
|
|
23
|
+
"message",
|
|
24
|
+
"module",
|
|
25
|
+
"msecs",
|
|
26
|
+
"msg",
|
|
27
|
+
"name",
|
|
28
|
+
"pathname",
|
|
29
|
+
"process",
|
|
30
|
+
"processName",
|
|
31
|
+
"relativeCreated",
|
|
32
|
+
"stack_info",
|
|
33
|
+
"thread",
|
|
34
|
+
"threadName",
|
|
35
|
+
"taskName",
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Module-level guard so configure_logging() is idempotent.
|
|
40
|
+
_CONFIGURED: bool = False
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class JsonFormatter(logging.Formatter):
|
|
44
|
+
"""Format log records as single-line JSON objects.
|
|
45
|
+
|
|
46
|
+
Mandatory fields in every record:
|
|
47
|
+
|
|
48
|
+
- ``ts`` — ISO-8601 UTC timestamp
|
|
49
|
+
- ``level`` — log level name (e.g. ``"INFO"``)
|
|
50
|
+
- ``logger`` — logger name
|
|
51
|
+
- ``msg`` — rendered log message
|
|
52
|
+
|
|
53
|
+
Optional fields appended when present:
|
|
54
|
+
|
|
55
|
+
- ``exc`` — formatted exception traceback
|
|
56
|
+
- ``stack`` — formatted stack info
|
|
57
|
+
- any ``extra=`` keys passed to the logger call
|
|
58
|
+
"""
|
|
59
|
+
|
|
60
|
+
def format(self, record: logging.LogRecord) -> str: # noqa: A003
|
|
61
|
+
entry: dict = {
|
|
62
|
+
"ts": datetime.fromtimestamp(record.created, tz=timezone.utc).isoformat(),
|
|
63
|
+
"level": record.levelname,
|
|
64
|
+
"logger": record.name,
|
|
65
|
+
"msg": record.getMessage(),
|
|
66
|
+
}
|
|
67
|
+
if record.exc_info:
|
|
68
|
+
entry["exc"] = self.formatException(record.exc_info)
|
|
69
|
+
if record.stack_info:
|
|
70
|
+
entry["stack"] = self.formatStack(record.stack_info)
|
|
71
|
+
# Merge any extra fields injected via `extra={...}` on the log call.
|
|
72
|
+
for key, val in record.__dict__.items():
|
|
73
|
+
if key not in _LOG_RECORD_BUILTIN_ATTRS and not key.startswith("_"):
|
|
74
|
+
entry[key] = val
|
|
75
|
+
return json.dumps(entry, default=str)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def configure_logging(
|
|
79
|
+
log_file: "Path | str",
|
|
80
|
+
*,
|
|
81
|
+
file_level: int = logging.DEBUG,
|
|
82
|
+
console_level: int = logging.INFO,
|
|
83
|
+
max_bytes: int = 10 * 1024 * 1024,
|
|
84
|
+
backup_count: int = 5,
|
|
85
|
+
) -> None:
|
|
86
|
+
"""Configure structured JSON logging for the daemon.
|
|
87
|
+
|
|
88
|
+
Sets up two handlers on the root logger:
|
|
89
|
+
|
|
90
|
+
- **RotatingFileHandler** — writes JSON lines to *log_file*
|
|
91
|
+
(rotates at *max_bytes*, keeps *backup_count* backups).
|
|
92
|
+
- **StreamHandler** — writes human-readable lines to stderr
|
|
93
|
+
at *console_level* (default ``INFO``).
|
|
94
|
+
|
|
95
|
+
This function is idempotent: subsequent calls are no-ops.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
log_file: Path to the daemon log file.
|
|
99
|
+
The parent directory is created automatically.
|
|
100
|
+
file_level: Minimum level for the file handler (default ``DEBUG``).
|
|
101
|
+
console_level: Minimum level for the console handler (default ``INFO``).
|
|
102
|
+
max_bytes: Maximum file size before rotation (default 10 MiB).
|
|
103
|
+
backup_count: Number of rotated backup files to retain (default 5).
|
|
104
|
+
"""
|
|
105
|
+
global _CONFIGURED
|
|
106
|
+
if _CONFIGURED:
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
log_path = Path(log_file).expanduser()
|
|
110
|
+
log_path.parent.mkdir(parents=True, exist_ok=True)
|
|
111
|
+
|
|
112
|
+
root = logging.getLogger()
|
|
113
|
+
root.setLevel(logging.DEBUG) # handlers filter independently
|
|
114
|
+
|
|
115
|
+
# Rotating JSON file handler
|
|
116
|
+
file_handler = logging.handlers.RotatingFileHandler(
|
|
117
|
+
log_path,
|
|
118
|
+
maxBytes=max_bytes,
|
|
119
|
+
backupCount=backup_count,
|
|
120
|
+
encoding="utf-8",
|
|
121
|
+
)
|
|
122
|
+
file_handler.setLevel(file_level)
|
|
123
|
+
file_handler.setFormatter(JsonFormatter())
|
|
124
|
+
|
|
125
|
+
# Human-readable console handler
|
|
126
|
+
console_handler = logging.StreamHandler()
|
|
127
|
+
console_handler.setLevel(console_level)
|
|
128
|
+
console_handler.setFormatter(
|
|
129
|
+
logging.Formatter("%(asctime)s [%(name)s] %(levelname)s: %(message)s")
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
root.addHandler(file_handler)
|
|
133
|
+
root.addHandler(console_handler)
|
|
134
|
+
|
|
135
|
+
_CONFIGURED = True
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Cross-platform MCP server launcher for SKCapstone.
|
|
3
|
+
Task: e5f81637
|
|
4
|
+
|
|
5
|
+
Detects the correct Python environment, sets required environment variables,
|
|
6
|
+
and launches the MCP server via stdio transport. Works on Linux, macOS, and
|
|
7
|
+
Windows without requiring shell-specific launcher scripts.
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
python -m skcapstone.mcp_launcher # auto-detect everything
|
|
11
|
+
python -m skcapstone.mcp_launcher --venv /path/to/venv
|
|
12
|
+
python -m skcapstone.mcp_launcher --log-level DEBUG
|
|
13
|
+
|
|
14
|
+
Can also be imported and called programmatically:
|
|
15
|
+
from skcapstone.mcp_launcher import launch
|
|
16
|
+
launch()
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import shutil
|
|
24
|
+
import subprocess
|
|
25
|
+
import sys
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
logger = logging.getLogger(__name__)
|
|
29
|
+
|
|
30
|
+
# ---------------------------------------------------------------------------
|
|
31
|
+
# Python / venv detection
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
def _skenv_dir() -> Path:
|
|
35
|
+
"""Return the platform-appropriate skenv directory."""
|
|
36
|
+
if sys.platform == "win32":
|
|
37
|
+
local_app = os.environ.get("LOCALAPPDATA")
|
|
38
|
+
if local_app:
|
|
39
|
+
return Path(local_app) / "skenv"
|
|
40
|
+
return Path.home() / ".skenv"
|
|
41
|
+
return Path.home() / ".skenv"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _python_in_venv(venv: Path) -> Path | None:
|
|
45
|
+
"""Return the python executable inside a venv, or None."""
|
|
46
|
+
if sys.platform == "win32":
|
|
47
|
+
candidate = venv / "Scripts" / "python.exe"
|
|
48
|
+
else:
|
|
49
|
+
candidate = venv / "bin" / "python"
|
|
50
|
+
return candidate if candidate.is_file() else None
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _has_skcapstone(python: Path | str) -> bool:
|
|
54
|
+
"""Check if a Python interpreter has skcapstone importable."""
|
|
55
|
+
try:
|
|
56
|
+
result = subprocess.run(
|
|
57
|
+
[str(python), "-c", "import skcapstone"],
|
|
58
|
+
capture_output=True,
|
|
59
|
+
timeout=10,
|
|
60
|
+
)
|
|
61
|
+
return result.returncode == 0
|
|
62
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
63
|
+
return False
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def find_python(explicit_venv: str | None = None) -> str:
|
|
67
|
+
"""Locate the best Python interpreter with skcapstone installed.
|
|
68
|
+
|
|
69
|
+
Search order:
|
|
70
|
+
1. Explicit venv path (parameter or SKCAPSTONE_VENV env var)
|
|
71
|
+
2. ~/.skenv (or %LOCALAPPDATA%/skenv on Windows)
|
|
72
|
+
3. Project-local .venv
|
|
73
|
+
4. The currently running interpreter (sys.executable)
|
|
74
|
+
5. System python3 / python
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
explicit_venv: Optional path to a virtualenv to use.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Absolute path to a Python executable.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
RuntimeError: If no suitable Python is found.
|
|
84
|
+
"""
|
|
85
|
+
# 1. Explicit override
|
|
86
|
+
venv_path = explicit_venv or os.environ.get("SKCAPSTONE_VENV")
|
|
87
|
+
if venv_path:
|
|
88
|
+
py = _python_in_venv(Path(venv_path))
|
|
89
|
+
if py and py.is_file():
|
|
90
|
+
logger.info("Using explicit venv: %s", py)
|
|
91
|
+
return str(py)
|
|
92
|
+
logger.warning(
|
|
93
|
+
"SKCAPSTONE_VENV=%s set but python not found there, falling back.",
|
|
94
|
+
venv_path,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# 2. Standard skenv
|
|
98
|
+
skenv = _skenv_dir()
|
|
99
|
+
py = _python_in_venv(skenv)
|
|
100
|
+
if py and _has_skcapstone(py):
|
|
101
|
+
logger.info("Using skenv: %s", py)
|
|
102
|
+
return str(py)
|
|
103
|
+
|
|
104
|
+
# 3. Project-local .venv
|
|
105
|
+
project_dir = Path(__file__).resolve().parent.parent.parent # src/../..
|
|
106
|
+
for venv_name in (".venv", "venv"):
|
|
107
|
+
local_venv = project_dir / venv_name
|
|
108
|
+
py = _python_in_venv(local_venv)
|
|
109
|
+
if py and _has_skcapstone(py):
|
|
110
|
+
logger.info("Using project venv: %s", py)
|
|
111
|
+
return str(py)
|
|
112
|
+
|
|
113
|
+
# 4. Current interpreter
|
|
114
|
+
if _has_skcapstone(sys.executable):
|
|
115
|
+
logger.info("Using current interpreter: %s", sys.executable)
|
|
116
|
+
return sys.executable
|
|
117
|
+
|
|
118
|
+
# 5. System python
|
|
119
|
+
for cmd in ("python3", "python"):
|
|
120
|
+
path = shutil.which(cmd)
|
|
121
|
+
if path and _has_skcapstone(path):
|
|
122
|
+
logger.info("Using system python: %s", path)
|
|
123
|
+
return path
|
|
124
|
+
|
|
125
|
+
raise RuntimeError(
|
|
126
|
+
"Could not find a Python interpreter with skcapstone installed.\n"
|
|
127
|
+
"Install with: bash scripts/install.sh (Linux/macOS) or "
|
|
128
|
+
".\\scripts\\install.ps1 (Windows)\n"
|
|
129
|
+
"Or set SKCAPSTONE_VENV=/path/to/venv"
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
# ---------------------------------------------------------------------------
|
|
134
|
+
# Environment setup
|
|
135
|
+
# ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
def _setup_environment() -> None:
|
|
138
|
+
"""Set required environment variables if not already present."""
|
|
139
|
+
home = Path.home()
|
|
140
|
+
skcapstone_home = home / ".skcapstone"
|
|
141
|
+
|
|
142
|
+
os.environ.setdefault("SKCAPSTONE_HOME", str(skcapstone_home))
|
|
143
|
+
os.environ.setdefault("SKMEMORY_HOME", str(skcapstone_home / "memory"))
|
|
144
|
+
|
|
145
|
+
# Ensure src/ is on PYTHONPATH for importability
|
|
146
|
+
src_dir = str(Path(__file__).resolve().parent.parent)
|
|
147
|
+
python_path = os.environ.get("PYTHONPATH", "")
|
|
148
|
+
if src_dir not in python_path.split(os.pathsep):
|
|
149
|
+
os.environ["PYTHONPATH"] = (
|
|
150
|
+
f"{src_dir}{os.pathsep}{python_path}" if python_path else src_dir
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
# ---------------------------------------------------------------------------
|
|
155
|
+
# Launch
|
|
156
|
+
# ---------------------------------------------------------------------------
|
|
157
|
+
|
|
158
|
+
def launch(
|
|
159
|
+
venv: str | None = None,
|
|
160
|
+
log_level: str = "WARNING",
|
|
161
|
+
extra_args: list[str] | None = None,
|
|
162
|
+
) -> int:
|
|
163
|
+
"""Launch the MCP server, optionally in a subprocess.
|
|
164
|
+
|
|
165
|
+
If the current interpreter already has skcapstone and is the best match,
|
|
166
|
+
the server is launched in-process. Otherwise, a subprocess is spawned
|
|
167
|
+
with the detected Python interpreter.
|
|
168
|
+
|
|
169
|
+
Args:
|
|
170
|
+
venv: Optional explicit venv path.
|
|
171
|
+
log_level: Logging level for the MCP server.
|
|
172
|
+
extra_args: Additional arguments to pass through.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Exit code (0 on success).
|
|
176
|
+
"""
|
|
177
|
+
logging.basicConfig(
|
|
178
|
+
level=getattr(logging, log_level.upper(), logging.WARNING),
|
|
179
|
+
format="%(name)s: %(message)s",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
_setup_environment()
|
|
183
|
+
|
|
184
|
+
python = find_python(explicit_venv=venv)
|
|
185
|
+
|
|
186
|
+
# If the best Python IS us, run in-process
|
|
187
|
+
if os.path.realpath(python) == os.path.realpath(sys.executable):
|
|
188
|
+
logger.info("Launching MCP server in-process.")
|
|
189
|
+
from skcapstone.mcp_server import main as mcp_main
|
|
190
|
+
mcp_main()
|
|
191
|
+
return 0
|
|
192
|
+
|
|
193
|
+
# Otherwise, exec into the correct interpreter
|
|
194
|
+
logger.info("Launching MCP server via: %s", python)
|
|
195
|
+
cmd = [python, "-m", "skcapstone.mcp_server"]
|
|
196
|
+
if extra_args:
|
|
197
|
+
cmd.extend(extra_args)
|
|
198
|
+
|
|
199
|
+
if sys.platform != "win32":
|
|
200
|
+
# On Unix, replace the current process
|
|
201
|
+
os.execv(python, cmd)
|
|
202
|
+
# execv does not return
|
|
203
|
+
return 1 # unreachable, but satisfies type checker
|
|
204
|
+
else:
|
|
205
|
+
# On Windows, os.execv has quirks; use subprocess
|
|
206
|
+
result = subprocess.run(cmd)
|
|
207
|
+
return result.returncode
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# ---------------------------------------------------------------------------
|
|
211
|
+
# CLI entry point
|
|
212
|
+
# ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
def main() -> None:
|
|
215
|
+
"""CLI entry point with argument parsing."""
|
|
216
|
+
import argparse
|
|
217
|
+
|
|
218
|
+
parser = argparse.ArgumentParser(
|
|
219
|
+
description="Cross-platform SKCapstone MCP server launcher.",
|
|
220
|
+
prog="skcapstone-mcp-launcher",
|
|
221
|
+
)
|
|
222
|
+
parser.add_argument(
|
|
223
|
+
"--venv",
|
|
224
|
+
help="Path to a virtualenv to use (overrides auto-detection).",
|
|
225
|
+
)
|
|
226
|
+
parser.add_argument(
|
|
227
|
+
"--log-level",
|
|
228
|
+
default=os.environ.get("SKCAPSTONE_LOG_LEVEL", "WARNING"),
|
|
229
|
+
choices=["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
230
|
+
help="Logging level (default: WARNING).",
|
|
231
|
+
)
|
|
232
|
+
args, extra = parser.parse_known_args()
|
|
233
|
+
|
|
234
|
+
rc = launch(venv=args.venv, log_level=args.log_level, extra_args=extra)
|
|
235
|
+
sys.exit(rc)
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
if __name__ == "__main__":
|
|
239
|
+
main()
|