@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,238 @@
|
|
|
1
|
+
"""Session record/replay commands.
|
|
2
|
+
|
|
3
|
+
skcapstone record [--output FILE.jsonl]
|
|
4
|
+
Start the MCP server in recording mode. All tool calls +
|
|
5
|
+
responses are captured as JSONL. Sessions are also auto-saved
|
|
6
|
+
to ~/.skcapstone/sessions/ (last 5 kept).
|
|
7
|
+
|
|
8
|
+
skcapstone replay FILE.jsonl [--dry-run]
|
|
9
|
+
Play back a recorded session. --dry-run prints what would
|
|
10
|
+
be called without executing any handlers.
|
|
11
|
+
|
|
12
|
+
skcapstone sessions list
|
|
13
|
+
List all auto-saved sessions.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
import click
|
|
24
|
+
|
|
25
|
+
from ._common import AGENT_HOME, console
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def register_record_commands(main: click.Group) -> None:
|
|
29
|
+
"""Register record, replay, and sessions commands."""
|
|
30
|
+
|
|
31
|
+
# ------------------------------------------------------------------
|
|
32
|
+
# skcapstone record
|
|
33
|
+
# ------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
@main.command("record")
|
|
36
|
+
@click.option(
|
|
37
|
+
"--output", "-o",
|
|
38
|
+
default=None,
|
|
39
|
+
type=click.Path(),
|
|
40
|
+
help="Write tool calls to this JSONL file (in addition to auto-session).",
|
|
41
|
+
)
|
|
42
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
43
|
+
def record(output: str | None, home: str) -> None:
|
|
44
|
+
"""Start MCP server in recording mode.
|
|
45
|
+
|
|
46
|
+
Every tool call and its response is captured as a JSONL line.
|
|
47
|
+
Sessions are also auto-saved to ~/.skcapstone/sessions/ with
|
|
48
|
+
the last 5 retained.
|
|
49
|
+
|
|
50
|
+
\b
|
|
51
|
+
Examples:
|
|
52
|
+
skcapstone record
|
|
53
|
+
skcapstone record --output /tmp/debug.jsonl
|
|
54
|
+
SKCAPSTONE_RECORD_FILE=/tmp/debug.jsonl skcapstone mcp serve
|
|
55
|
+
"""
|
|
56
|
+
if output:
|
|
57
|
+
os.environ["SKCAPSTONE_RECORD_FILE"] = str(Path(output).expanduser())
|
|
58
|
+
console.print(f" [green]Recording to:[/] {output}")
|
|
59
|
+
|
|
60
|
+
home_path = Path(home).expanduser()
|
|
61
|
+
if not home_path.exists():
|
|
62
|
+
console.print(
|
|
63
|
+
"[bold red]No agent found.[/] Run [cyan]skcapstone init[/] first."
|
|
64
|
+
)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
console.print(
|
|
68
|
+
" [dim]Auto-session saved to[/] "
|
|
69
|
+
f"[cyan]{home_path / 'sessions'}[/] (last 5 kept)"
|
|
70
|
+
)
|
|
71
|
+
console.print(" [dim]Starting MCP server … (Ctrl-C to stop)[/]\n")
|
|
72
|
+
|
|
73
|
+
import asyncio
|
|
74
|
+
from ..mcp_server import _run_server
|
|
75
|
+
try:
|
|
76
|
+
asyncio.run(_run_server())
|
|
77
|
+
except KeyboardInterrupt:
|
|
78
|
+
console.print("\n [yellow]Recording stopped.[/]")
|
|
79
|
+
|
|
80
|
+
# ------------------------------------------------------------------
|
|
81
|
+
# skcapstone replay
|
|
82
|
+
# ------------------------------------------------------------------
|
|
83
|
+
|
|
84
|
+
@main.command("replay")
|
|
85
|
+
@click.argument("session_file", type=click.Path(exists=True))
|
|
86
|
+
@click.option(
|
|
87
|
+
"--dry-run", "dry_run",
|
|
88
|
+
is_flag=True,
|
|
89
|
+
default=False,
|
|
90
|
+
help="Print what would be called without executing handlers.",
|
|
91
|
+
)
|
|
92
|
+
@click.option(
|
|
93
|
+
"--format", "fmt",
|
|
94
|
+
type=click.Choice(["text", "json"]),
|
|
95
|
+
default="text",
|
|
96
|
+
help="Output format.",
|
|
97
|
+
)
|
|
98
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
99
|
+
def replay(
|
|
100
|
+
session_file: str,
|
|
101
|
+
dry_run: bool,
|
|
102
|
+
fmt: str,
|
|
103
|
+
home: str,
|
|
104
|
+
) -> None:
|
|
105
|
+
"""Replay a recorded JSONL session.
|
|
106
|
+
|
|
107
|
+
\b
|
|
108
|
+
Modes:
|
|
109
|
+
--dry-run Print tool names + arguments only (no execution).
|
|
110
|
+
(default) Execute each tool call against the live handlers
|
|
111
|
+
and compare results to the recording.
|
|
112
|
+
|
|
113
|
+
\b
|
|
114
|
+
Examples:
|
|
115
|
+
skcapstone replay session-20260302T120000.jsonl --dry-run
|
|
116
|
+
skcapstone replay /tmp/debug.jsonl
|
|
117
|
+
skcapstone replay /tmp/debug.jsonl --format json
|
|
118
|
+
"""
|
|
119
|
+
from ..session_replayer import SessionReplayer
|
|
120
|
+
|
|
121
|
+
path = Path(session_file).expanduser()
|
|
122
|
+
replayer = SessionReplayer(path, dry_run=dry_run)
|
|
123
|
+
results = list(replayer.replay())
|
|
124
|
+
|
|
125
|
+
if fmt == "json":
|
|
126
|
+
# Pure JSON output — no decorative header so callers can parse stdout.
|
|
127
|
+
rows = []
|
|
128
|
+
for r in results:
|
|
129
|
+
rows.append({
|
|
130
|
+
"index": r.index,
|
|
131
|
+
"tool": r.tool,
|
|
132
|
+
"arguments": r.arguments,
|
|
133
|
+
"recorded_result": r.recorded_result,
|
|
134
|
+
"replayed_result": r.replayed_result,
|
|
135
|
+
"duration_ms": r.duration_ms,
|
|
136
|
+
"match": r.match,
|
|
137
|
+
"error": r.error,
|
|
138
|
+
})
|
|
139
|
+
click.echo(json.dumps(rows, indent=2, default=str))
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
# Text output
|
|
143
|
+
mode_label = "[yellow]DRY-RUN[/]" if dry_run else "[green]LIVE[/]"
|
|
144
|
+
console.print(f"\n Replaying [bold]{path.name}[/] {mode_label}\n")
|
|
145
|
+
if not results:
|
|
146
|
+
console.print(" [dim]Session file is empty.[/]\n")
|
|
147
|
+
return
|
|
148
|
+
|
|
149
|
+
mismatches = 0
|
|
150
|
+
errors = 0
|
|
151
|
+
for r in results:
|
|
152
|
+
args_preview = json.dumps(r.arguments, ensure_ascii=False)
|
|
153
|
+
if len(args_preview) > 80:
|
|
154
|
+
args_preview = args_preview[:77] + "..."
|
|
155
|
+
|
|
156
|
+
if dry_run:
|
|
157
|
+
status = "[dim]SKIP[/]"
|
|
158
|
+
elif r.error:
|
|
159
|
+
status = "[red]ERROR[/]"
|
|
160
|
+
errors += 1
|
|
161
|
+
elif r.match is True:
|
|
162
|
+
status = "[green]MATCH[/]"
|
|
163
|
+
elif r.match is False:
|
|
164
|
+
status = "[yellow]DIFF[/]"
|
|
165
|
+
mismatches += 1
|
|
166
|
+
else:
|
|
167
|
+
status = "[dim]?[/]"
|
|
168
|
+
|
|
169
|
+
console.print(
|
|
170
|
+
f" [{r.index:>3}] {status} [cyan]{r.tool}[/]"
|
|
171
|
+
f" [dim]{args_preview}[/]"
|
|
172
|
+
f" [dim]{r.duration_ms}ms[/]"
|
|
173
|
+
)
|
|
174
|
+
if r.error:
|
|
175
|
+
console.print(f" [red]{r.error}[/]")
|
|
176
|
+
|
|
177
|
+
total = len(results)
|
|
178
|
+
console.print(f"\n {total} call(s) replayed", end="")
|
|
179
|
+
if not dry_run:
|
|
180
|
+
if mismatches:
|
|
181
|
+
console.print(f" [yellow]{mismatches} diff(s)[/]", end="")
|
|
182
|
+
if errors:
|
|
183
|
+
console.print(f" [red]{errors} error(s)[/]", end="")
|
|
184
|
+
console.print("\n")
|
|
185
|
+
|
|
186
|
+
if not dry_run and (mismatches or errors):
|
|
187
|
+
sys.exit(1)
|
|
188
|
+
|
|
189
|
+
# ------------------------------------------------------------------
|
|
190
|
+
# skcapstone sessions
|
|
191
|
+
# ------------------------------------------------------------------
|
|
192
|
+
|
|
193
|
+
@main.group("sessions")
|
|
194
|
+
def sessions_group() -> None:
|
|
195
|
+
"""Manage auto-saved MCP sessions."""
|
|
196
|
+
|
|
197
|
+
@sessions_group.command("list")
|
|
198
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
199
|
+
@click.option("--limit", default=10, help="Max sessions to show.")
|
|
200
|
+
def sessions_list(home: str, limit: int) -> None:
|
|
201
|
+
"""List auto-saved session files newest-first.
|
|
202
|
+
|
|
203
|
+
\b
|
|
204
|
+
Example:
|
|
205
|
+
skcapstone sessions list
|
|
206
|
+
skcapstone sessions list --limit 3
|
|
207
|
+
"""
|
|
208
|
+
from ..session_recorder import list_sessions, load_session
|
|
209
|
+
|
|
210
|
+
home_path = Path(home).expanduser()
|
|
211
|
+
files = list_sessions(home_path)
|
|
212
|
+
|
|
213
|
+
if not files:
|
|
214
|
+
console.print("\n [dim]No sessions found in "
|
|
215
|
+
f"{home_path / 'sessions'}[/]\n")
|
|
216
|
+
return
|
|
217
|
+
|
|
218
|
+
console.print(f"\n [bold]{len(files)}[/] session(s) found "
|
|
219
|
+
f"(showing up to {limit}):\n")
|
|
220
|
+
for f in files[:limit]:
|
|
221
|
+
try:
|
|
222
|
+
entries = load_session(f)
|
|
223
|
+
count = len(entries)
|
|
224
|
+
tools = list({e.get("tool", "?") for e in entries})
|
|
225
|
+
tools_str = ", ".join(sorted(tools)[:5])
|
|
226
|
+
if len(tools) > 5:
|
|
227
|
+
tools_str += f" +{len(tools) - 5}"
|
|
228
|
+
except Exception:
|
|
229
|
+
count = 0
|
|
230
|
+
tools_str = "[dim]unreadable[/]"
|
|
231
|
+
|
|
232
|
+
size_kb = f.stat().st_size / 1024
|
|
233
|
+
console.print(
|
|
234
|
+
f" [cyan]{f.name}[/] "
|
|
235
|
+
f"[dim]{count} calls {size_kb:.1f}KB {tools_str}[/]"
|
|
236
|
+
)
|
|
237
|
+
console.print(f" [dim]{f}[/]")
|
|
238
|
+
console.print()
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""Register command — auto-register SK* skills and MCP servers.
|
|
2
|
+
|
|
3
|
+
Detects the user's environments (OpenClaw, Claude Code, Cursor, VS Code,
|
|
4
|
+
OpenCode CLI, mcporter) and registers SKILL.md symlinks + MCP server entries.
|
|
5
|
+
|
|
6
|
+
Commands:
|
|
7
|
+
skcapstone register — register all SK* packages
|
|
8
|
+
skcapstone register --dry-run — show what would be done
|
|
9
|
+
skcapstone register --env claude-code — target specific environment
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
from typing import Optional
|
|
16
|
+
|
|
17
|
+
import click
|
|
18
|
+
|
|
19
|
+
from ._common import AGENT_HOME, console
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def register_register_commands(main: click.Group) -> None:
|
|
23
|
+
"""Register the 'register' command on the main CLI group."""
|
|
24
|
+
|
|
25
|
+
@main.command()
|
|
26
|
+
@click.option(
|
|
27
|
+
"--workspace",
|
|
28
|
+
default=None,
|
|
29
|
+
help="Workspace root directory (default: ~/clawd/).",
|
|
30
|
+
type=click.Path(),
|
|
31
|
+
)
|
|
32
|
+
@click.option(
|
|
33
|
+
"--env",
|
|
34
|
+
"target_env",
|
|
35
|
+
default=None,
|
|
36
|
+
help="Target a specific environment (e.g. claude-code, cursor, mcporter).",
|
|
37
|
+
)
|
|
38
|
+
@click.option(
|
|
39
|
+
"--dry-run",
|
|
40
|
+
is_flag=True,
|
|
41
|
+
default=False,
|
|
42
|
+
help="Show what would be done without making changes.",
|
|
43
|
+
)
|
|
44
|
+
def register(
|
|
45
|
+
workspace: Optional[str],
|
|
46
|
+
target_env: Optional[str],
|
|
47
|
+
dry_run: bool,
|
|
48
|
+
) -> None:
|
|
49
|
+
"""Register all SK* skills and MCP servers in detected environments.
|
|
50
|
+
|
|
51
|
+
Auto-detects your development environments (Claude Code, Cursor,
|
|
52
|
+
VS Code, OpenClaw, OpenCode, mcporter) and ensures all SK* skill
|
|
53
|
+
manifests and MCP server entries are properly configured.
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
|
|
57
|
+
skcapstone register # auto-detect and register
|
|
58
|
+
skcapstone register --dry-run # preview what would happen
|
|
59
|
+
skcapstone register --env claude-code # target Claude Code only
|
|
60
|
+
"""
|
|
61
|
+
from skmemory.register import detect_environments
|
|
62
|
+
from skcapstone.register import register_all
|
|
63
|
+
|
|
64
|
+
workspace_path = Path(workspace).expanduser() if workspace else None
|
|
65
|
+
environments = [target_env] if target_env else None
|
|
66
|
+
|
|
67
|
+
console.print()
|
|
68
|
+
console.print("[bold cyan]SK* Registration[/]")
|
|
69
|
+
console.print()
|
|
70
|
+
|
|
71
|
+
# Show detected environments
|
|
72
|
+
detected = detect_environments()
|
|
73
|
+
console.print(" [bold]Detected environments:[/]")
|
|
74
|
+
for env in detected:
|
|
75
|
+
marker = "[green]●[/]" if (environments is None or env in environments) else "[dim]○[/]"
|
|
76
|
+
console.print(f" {marker} {env}")
|
|
77
|
+
if not detected:
|
|
78
|
+
console.print(" [dim]None detected[/]")
|
|
79
|
+
console.print()
|
|
80
|
+
console.print(" [yellow]Tip:[/] Install OpenClaw, Claude Code, or Cursor to enable registration.")
|
|
81
|
+
console.print()
|
|
82
|
+
|
|
83
|
+
if dry_run:
|
|
84
|
+
console.print(" [yellow]Dry run — no changes will be made.[/]")
|
|
85
|
+
console.print()
|
|
86
|
+
|
|
87
|
+
# Run registration
|
|
88
|
+
results = register_all(
|
|
89
|
+
workspace=workspace_path,
|
|
90
|
+
environments=environments,
|
|
91
|
+
dry_run=dry_run,
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
# Display results
|
|
95
|
+
from rich.table import Table
|
|
96
|
+
|
|
97
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
98
|
+
table.add_column("Package", style="cyan")
|
|
99
|
+
table.add_column("Skill", style="dim")
|
|
100
|
+
table.add_column("MCP")
|
|
101
|
+
table.add_column("OpenClaw Plugin")
|
|
102
|
+
|
|
103
|
+
for name, pkg_result in results.get("packages", {}).items():
|
|
104
|
+
skill_info = pkg_result.get("skill", {})
|
|
105
|
+
skill_action = skill_info.get("action", "—")
|
|
106
|
+
|
|
107
|
+
if skill_action == "created":
|
|
108
|
+
skill_str = "[green]created[/]"
|
|
109
|
+
elif skill_action == "exists":
|
|
110
|
+
skill_str = "[dim]exists[/]"
|
|
111
|
+
elif skill_action == "error":
|
|
112
|
+
skill_str = f"[red]{skill_info.get('error', 'error')}[/]"
|
|
113
|
+
elif skill_action == "dry-run":
|
|
114
|
+
skill_str = "[yellow]would create[/]"
|
|
115
|
+
else:
|
|
116
|
+
skill_str = f"[dim]{skill_action}[/]"
|
|
117
|
+
|
|
118
|
+
mcp_info = pkg_result.get("mcp", {})
|
|
119
|
+
if not mcp_info:
|
|
120
|
+
mcp_str = "[dim]—[/]"
|
|
121
|
+
elif isinstance(mcp_info, dict):
|
|
122
|
+
parts = []
|
|
123
|
+
for env_name, action in mcp_info.items():
|
|
124
|
+
if action == "created":
|
|
125
|
+
parts.append(f"[green]{env_name}[/]")
|
|
126
|
+
elif action == "exists":
|
|
127
|
+
parts.append(f"[dim]{env_name}[/]")
|
|
128
|
+
elif action == "dry-run":
|
|
129
|
+
parts.append(f"[yellow]{env_name}[/]")
|
|
130
|
+
else:
|
|
131
|
+
parts.append(f"{env_name}:{action}")
|
|
132
|
+
mcp_str = ", ".join(parts) if parts else "[dim]—[/]"
|
|
133
|
+
else:
|
|
134
|
+
mcp_str = str(mcp_info)
|
|
135
|
+
|
|
136
|
+
plugin_action = pkg_result.get("openclaw_plugin", "")
|
|
137
|
+
if plugin_action == "created":
|
|
138
|
+
plugin_str = "[green]created[/]"
|
|
139
|
+
elif plugin_action == "exists":
|
|
140
|
+
plugin_str = "[dim]exists[/]"
|
|
141
|
+
elif plugin_action == "dry-run":
|
|
142
|
+
plugin_str = "[yellow]would create[/]"
|
|
143
|
+
elif plugin_action and plugin_action.startswith("error"):
|
|
144
|
+
plugin_str = f"[red]{plugin_action}[/]"
|
|
145
|
+
elif not plugin_action:
|
|
146
|
+
plugin_str = "[dim]—[/]"
|
|
147
|
+
else:
|
|
148
|
+
plugin_str = f"[dim]{plugin_action}[/]"
|
|
149
|
+
|
|
150
|
+
table.add_row(name, skill_str, mcp_str, plugin_str)
|
|
151
|
+
|
|
152
|
+
console.print(table)
|
|
153
|
+
console.print()
|
|
154
|
+
|
|
155
|
+
if dry_run:
|
|
156
|
+
console.print(" [yellow]No changes made (dry run).[/]")
|
|
157
|
+
else:
|
|
158
|
+
console.print(" [green]Registration complete.[/]")
|
|
159
|
+
console.print()
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
"""Search command: full-text search across memories, conversations, and messages."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
from rich.table import Table
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
from ._common import AGENT_HOME, console
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
# Source icons shown in the table
|
|
17
|
+
_SOURCE_ICON = {
|
|
18
|
+
"memory": "🧠",
|
|
19
|
+
"conversation": "💬",
|
|
20
|
+
"message": "📨",
|
|
21
|
+
"journal": "📓",
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
_SOURCE_COLOR = {
|
|
25
|
+
"memory": "cyan",
|
|
26
|
+
"conversation": "green",
|
|
27
|
+
"message": "yellow",
|
|
28
|
+
"journal": "magenta",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
_VALID_SOURCES = ("memory", "conversation", "message", "journal")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _fmt_ts(ts) -> str:
|
|
35
|
+
"""Format a datetime for display, returning '--' for None."""
|
|
36
|
+
if ts is None:
|
|
37
|
+
return "--"
|
|
38
|
+
return ts.strftime("%Y-%m-%d %H:%M")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def register_search_commands(main: click.Group) -> None:
|
|
42
|
+
"""Register the top-level ``search`` command."""
|
|
43
|
+
|
|
44
|
+
@main.command("search")
|
|
45
|
+
@click.argument("query")
|
|
46
|
+
@click.option(
|
|
47
|
+
"--home",
|
|
48
|
+
default=AGENT_HOME,
|
|
49
|
+
type=click.Path(),
|
|
50
|
+
help="Agent home directory.",
|
|
51
|
+
)
|
|
52
|
+
@click.option(
|
|
53
|
+
"--type", "-t",
|
|
54
|
+
"source_types",
|
|
55
|
+
multiple=True,
|
|
56
|
+
type=click.Choice(_VALID_SOURCES),
|
|
57
|
+
help=(
|
|
58
|
+
"Restrict search to a specific source type. "
|
|
59
|
+
"May be repeated: -t memory -t conversation"
|
|
60
|
+
),
|
|
61
|
+
)
|
|
62
|
+
@click.option(
|
|
63
|
+
"--limit", "-n",
|
|
64
|
+
default=20,
|
|
65
|
+
show_default=True,
|
|
66
|
+
help="Maximum number of results.",
|
|
67
|
+
)
|
|
68
|
+
@click.option(
|
|
69
|
+
"--json-out",
|
|
70
|
+
is_flag=True,
|
|
71
|
+
help="Output results as JSON.",
|
|
72
|
+
)
|
|
73
|
+
def search_cmd(query, home, source_types, limit, json_out):
|
|
74
|
+
"""Search across memories, conversations, and messages.
|
|
75
|
+
|
|
76
|
+
QUERY is matched case-insensitively against all data stores.
|
|
77
|
+
Results are ranked by relevance and recency.
|
|
78
|
+
|
|
79
|
+
Examples:\n
|
|
80
|
+
skcapstone search "consciousness"\n
|
|
81
|
+
skcapstone search "Opus" --type conversation\n
|
|
82
|
+
skcapstone search "trust" -t memory -t journal -n 10\n
|
|
83
|
+
skcapstone search "sprint" --json-out | jq .[].preview
|
|
84
|
+
"""
|
|
85
|
+
from ..unified_search import search as unified_search, SOURCE_ALL
|
|
86
|
+
|
|
87
|
+
home_path = Path(home).expanduser()
|
|
88
|
+
if not home_path.exists():
|
|
89
|
+
if json_out:
|
|
90
|
+
print(json.dumps([]))
|
|
91
|
+
return
|
|
92
|
+
console.print(
|
|
93
|
+
"[bold red]No agent found.[/] Run [cyan]skcapstone init[/] first."
|
|
94
|
+
)
|
|
95
|
+
sys.exit(1)
|
|
96
|
+
|
|
97
|
+
sources = frozenset(source_types) if source_types else SOURCE_ALL
|
|
98
|
+
|
|
99
|
+
results = unified_search(home=home_path, query=query, sources=sources, limit=limit)
|
|
100
|
+
|
|
101
|
+
if json_out:
|
|
102
|
+
output = [
|
|
103
|
+
{
|
|
104
|
+
"source": r.source,
|
|
105
|
+
"id": r.result_id,
|
|
106
|
+
"title": r.title,
|
|
107
|
+
"preview": r.preview,
|
|
108
|
+
"score": round(r.score, 4),
|
|
109
|
+
"timestamp": r.timestamp.isoformat() if r.timestamp else None,
|
|
110
|
+
"metadata": r.metadata,
|
|
111
|
+
}
|
|
112
|
+
for r in results
|
|
113
|
+
]
|
|
114
|
+
print(json.dumps(output, indent=2))
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
if not results:
|
|
118
|
+
console.print(
|
|
119
|
+
f"\n [dim]No results for '[/]{query}[dim]'[/] "
|
|
120
|
+
f"across {', '.join(sorted(sources))}.\n"
|
|
121
|
+
)
|
|
122
|
+
return
|
|
123
|
+
|
|
124
|
+
source_label = (
|
|
125
|
+
", ".join(sorted(sources))
|
|
126
|
+
if len(sources) < len(SOURCE_ALL)
|
|
127
|
+
else "all sources"
|
|
128
|
+
)
|
|
129
|
+
console.print(
|
|
130
|
+
f"\n [bold]{len(results)}[/] result"
|
|
131
|
+
f"{'s' if len(results) != 1 else ''} for "
|
|
132
|
+
f"[bold cyan]'{query}'[/] in {source_label}:\n"
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
136
|
+
table.add_column("Type", max_width=14)
|
|
137
|
+
table.add_column("Title", max_width=30)
|
|
138
|
+
table.add_column("Preview", max_width=55)
|
|
139
|
+
table.add_column("Score", justify="right", max_width=7)
|
|
140
|
+
table.add_column("Date", justify="right", style="dim", max_width=16)
|
|
141
|
+
|
|
142
|
+
for r in results:
|
|
143
|
+
icon = _SOURCE_ICON.get(r.source, "?")
|
|
144
|
+
color = _SOURCE_COLOR.get(r.source, "white")
|
|
145
|
+
source_text = Text(f"{icon} {r.source}", style=color)
|
|
146
|
+
preview_clipped = r.preview[:110] + ("…" if len(r.preview) > 110 else "")
|
|
147
|
+
table.add_row(
|
|
148
|
+
source_text,
|
|
149
|
+
r.title,
|
|
150
|
+
preview_clipped,
|
|
151
|
+
f"{r.score:.2f}",
|
|
152
|
+
_fmt_ts(r.timestamp),
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
console.print(table)
|
|
156
|
+
console.print()
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"""CLI command: skcapstone service check — service health checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from rich.table import Table
|
|
9
|
+
|
|
10
|
+
from ._common import console
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def register_service_commands(main: click.Group) -> None:
|
|
14
|
+
"""Register the ``service`` command group on the main CLI."""
|
|
15
|
+
|
|
16
|
+
@main.group()
|
|
17
|
+
def service():
|
|
18
|
+
"""Service infrastructure commands."""
|
|
19
|
+
|
|
20
|
+
@service.command("check")
|
|
21
|
+
@click.option("--json-out", is_flag=True, help="Output as machine-readable JSON.")
|
|
22
|
+
def service_check(json_out: bool):
|
|
23
|
+
"""Check health of all services in the sovereign stack.
|
|
24
|
+
|
|
25
|
+
Pings SKVector (Qdrant), SKGraph (FalkorDB), Syncthing,
|
|
26
|
+
skcapstone daemon, and skchat daemon. Reports status,
|
|
27
|
+
latency, and any errors.
|
|
28
|
+
"""
|
|
29
|
+
from ..service_health import check_all_services
|
|
30
|
+
|
|
31
|
+
results = check_all_services()
|
|
32
|
+
|
|
33
|
+
if json_out:
|
|
34
|
+
up = sum(1 for r in results if r["status"] == "up")
|
|
35
|
+
down = sum(1 for r in results if r["status"] == "down")
|
|
36
|
+
data = {
|
|
37
|
+
"summary": {
|
|
38
|
+
"total": len(results),
|
|
39
|
+
"up": up,
|
|
40
|
+
"down": down,
|
|
41
|
+
"unknown": len(results) - up - down,
|
|
42
|
+
},
|
|
43
|
+
"services": results,
|
|
44
|
+
}
|
|
45
|
+
click.echo(json.dumps(data, indent=2))
|
|
46
|
+
return
|
|
47
|
+
|
|
48
|
+
console.print()
|
|
49
|
+
|
|
50
|
+
table = Table(
|
|
51
|
+
show_header=True, header_style="bold", box=None, padding=(0, 2),
|
|
52
|
+
)
|
|
53
|
+
table.add_column("Service", style="cyan", no_wrap=True)
|
|
54
|
+
table.add_column("URL", style="dim", no_wrap=True)
|
|
55
|
+
table.add_column("Status")
|
|
56
|
+
table.add_column("Latency", justify="right", style="dim")
|
|
57
|
+
table.add_column("Version", style="dim")
|
|
58
|
+
table.add_column("Error", style="dim")
|
|
59
|
+
|
|
60
|
+
status_styles = {
|
|
61
|
+
"up": "[green]UP[/]",
|
|
62
|
+
"down": "[red]DOWN[/]",
|
|
63
|
+
"unknown": "[yellow]UNKNOWN[/]",
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for r in results:
|
|
67
|
+
status_str = status_styles.get(r["status"], r["status"])
|
|
68
|
+
latency_str = f"{r['latency_ms']:.0f}ms" if r["latency_ms"] else "-"
|
|
69
|
+
version_str = str(r["version"]) if r["version"] else "-"
|
|
70
|
+
error_str = r["error"][:50] if r["error"] else "-"
|
|
71
|
+
table.add_row(
|
|
72
|
+
r["name"], r["url"], status_str,
|
|
73
|
+
latency_str, version_str, error_str,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
console.print(table)
|
|
77
|
+
|
|
78
|
+
up_count = sum(1 for r in results if r["status"] == "up")
|
|
79
|
+
down_count = sum(1 for r in results if r["status"] == "down")
|
|
80
|
+
total = len(results)
|
|
81
|
+
|
|
82
|
+
console.print()
|
|
83
|
+
if down_count == 0:
|
|
84
|
+
console.print(f" [green]All {up_count}/{total} reachable services are healthy.[/]")
|
|
85
|
+
else:
|
|
86
|
+
console.print(
|
|
87
|
+
f" [green]{up_count}[/] up, "
|
|
88
|
+
f"[red]{down_count}[/] down "
|
|
89
|
+
f"out of {total} services."
|
|
90
|
+
)
|
|
91
|
+
console.print()
|