@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,257 @@
|
|
|
1
|
+
"""Tests for the skcapstone doctor diagnostics module.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- DiagnosticReport structure and properties
|
|
5
|
+
- Package checks (installed vs missing)
|
|
6
|
+
- Agent home directory checks
|
|
7
|
+
- Identity checks (present vs missing)
|
|
8
|
+
- Memory store checks
|
|
9
|
+
- CLI integration (doctor command via CliRunner)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import json
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from unittest.mock import patch
|
|
17
|
+
|
|
18
|
+
import pytest
|
|
19
|
+
import yaml
|
|
20
|
+
from click.testing import CliRunner
|
|
21
|
+
|
|
22
|
+
from skcapstone.doctor import (
|
|
23
|
+
Check,
|
|
24
|
+
DiagnosticReport,
|
|
25
|
+
_check_agent_home,
|
|
26
|
+
_check_identity,
|
|
27
|
+
_check_memory,
|
|
28
|
+
_check_packages,
|
|
29
|
+
run_diagnostics,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@pytest.fixture
|
|
34
|
+
def agent_home(tmp_path):
|
|
35
|
+
"""Create a fully populated agent home for testing."""
|
|
36
|
+
home = tmp_path / ".skcapstone"
|
|
37
|
+
for d in ["identity", "memory", "trust", "security", "sync", "config",
|
|
38
|
+
"memory/short-term", "memory/mid-term", "memory/long-term",
|
|
39
|
+
"sync/outbox", "sync/inbox"]:
|
|
40
|
+
(home / d).mkdir(parents=True, exist_ok=True)
|
|
41
|
+
|
|
42
|
+
(home / "manifest.json").write_text(json.dumps({
|
|
43
|
+
"name": "TestAgent", "version": "0.1.0",
|
|
44
|
+
}))
|
|
45
|
+
(home / "identity" / "identity.json").write_text(json.dumps({
|
|
46
|
+
"name": "TestAgent",
|
|
47
|
+
"fingerprint": "AABBCCDD11223344",
|
|
48
|
+
"capauth_managed": True,
|
|
49
|
+
}))
|
|
50
|
+
(home / "config" / "config.yaml").write_text(yaml.dump({"agent_name": "TestAgent"}))
|
|
51
|
+
(home / "memory" / "index.json").write_text("{}")
|
|
52
|
+
|
|
53
|
+
# Add a memory file
|
|
54
|
+
(home / "memory" / "short-term" / "mem1.json").write_text(json.dumps({
|
|
55
|
+
"memory_id": "mem1", "content": "test",
|
|
56
|
+
}))
|
|
57
|
+
|
|
58
|
+
return home
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@pytest.fixture
|
|
62
|
+
def empty_home(tmp_path):
|
|
63
|
+
"""A non-existent agent home path."""
|
|
64
|
+
return tmp_path / ".skcapstone-nonexistent"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TestCheck:
|
|
68
|
+
"""Test Check dataclass."""
|
|
69
|
+
|
|
70
|
+
def test_passing_check(self):
|
|
71
|
+
"""Passing check has correct attributes."""
|
|
72
|
+
c = Check(name="test", description="A test", passed=True, detail="ok")
|
|
73
|
+
assert c.passed
|
|
74
|
+
assert c.detail == "ok"
|
|
75
|
+
|
|
76
|
+
def test_failing_check_with_fix(self):
|
|
77
|
+
"""Failing check carries a fix suggestion."""
|
|
78
|
+
c = Check(name="test", description="Broken", passed=False, fix="run this")
|
|
79
|
+
assert not c.passed
|
|
80
|
+
assert c.fix == "run this"
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class TestDiagnosticReport:
|
|
84
|
+
"""Test DiagnosticReport properties."""
|
|
85
|
+
|
|
86
|
+
def test_counts(self):
|
|
87
|
+
"""Report counts passed and failed correctly."""
|
|
88
|
+
report = DiagnosticReport(checks=[
|
|
89
|
+
Check(name="a", description="A", passed=True),
|
|
90
|
+
Check(name="b", description="B", passed=True),
|
|
91
|
+
Check(name="c", description="C", passed=False),
|
|
92
|
+
])
|
|
93
|
+
|
|
94
|
+
assert report.passed_count == 2
|
|
95
|
+
assert report.failed_count == 1
|
|
96
|
+
assert report.total_count == 3
|
|
97
|
+
assert not report.all_passed
|
|
98
|
+
|
|
99
|
+
def test_all_passed(self):
|
|
100
|
+
"""all_passed is True when everything passes."""
|
|
101
|
+
report = DiagnosticReport(checks=[
|
|
102
|
+
Check(name="a", description="A", passed=True),
|
|
103
|
+
])
|
|
104
|
+
assert report.all_passed
|
|
105
|
+
|
|
106
|
+
def test_to_dict(self):
|
|
107
|
+
"""to_dict produces JSON-serializable output."""
|
|
108
|
+
report = DiagnosticReport(
|
|
109
|
+
agent_home="/test",
|
|
110
|
+
checks=[Check(name="x", description="X", passed=True, detail="ok")],
|
|
111
|
+
)
|
|
112
|
+
d = report.to_dict()
|
|
113
|
+
|
|
114
|
+
assert d["agent_home"] == "/test"
|
|
115
|
+
assert d["passed"] == 1
|
|
116
|
+
assert d["total"] == 1
|
|
117
|
+
assert len(d["checks"]) == 1
|
|
118
|
+
json.dumps(d)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
class TestCheckAgentHome:
|
|
122
|
+
"""Test agent home directory checks."""
|
|
123
|
+
|
|
124
|
+
def test_existing_home(self, agent_home):
|
|
125
|
+
"""Fully populated home passes all checks."""
|
|
126
|
+
checks = _check_agent_home(agent_home)
|
|
127
|
+
|
|
128
|
+
names = {c.name for c in checks}
|
|
129
|
+
assert "home:exists" in names
|
|
130
|
+
assert "home:manifest" in names
|
|
131
|
+
assert all(c.passed for c in checks), [c.name for c in checks if not c.passed]
|
|
132
|
+
|
|
133
|
+
def test_missing_home(self, empty_home):
|
|
134
|
+
"""Missing home directory fails immediately."""
|
|
135
|
+
checks = _check_agent_home(empty_home)
|
|
136
|
+
|
|
137
|
+
assert len(checks) == 1
|
|
138
|
+
assert checks[0].name == "home:exists"
|
|
139
|
+
assert not checks[0].passed
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class TestCheckIdentity:
|
|
143
|
+
"""Test identity checks."""
|
|
144
|
+
|
|
145
|
+
def test_identity_present(self, agent_home):
|
|
146
|
+
"""Valid identity file passes checks."""
|
|
147
|
+
checks = _check_identity(agent_home)
|
|
148
|
+
|
|
149
|
+
profile_check = next(c for c in checks if c.name == "identity:profile")
|
|
150
|
+
assert profile_check.passed
|
|
151
|
+
assert "AABBCCDD" in profile_check.detail
|
|
152
|
+
|
|
153
|
+
def test_identity_missing(self, empty_home):
|
|
154
|
+
"""Missing identity directory fails."""
|
|
155
|
+
empty_home.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
(empty_home / "identity").mkdir()
|
|
157
|
+
|
|
158
|
+
checks = _check_identity(empty_home)
|
|
159
|
+
profile_check = next(c for c in checks if c.name == "identity:profile")
|
|
160
|
+
assert not profile_check.passed
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class TestCheckMemory:
|
|
164
|
+
"""Test memory store checks."""
|
|
165
|
+
|
|
166
|
+
def test_memory_healthy(self, agent_home):
|
|
167
|
+
"""Populated memory store passes."""
|
|
168
|
+
checks = _check_memory(agent_home)
|
|
169
|
+
|
|
170
|
+
store_check = next(c for c in checks if c.name == "memory:store")
|
|
171
|
+
assert store_check.passed
|
|
172
|
+
assert "1 memories" in store_check.detail
|
|
173
|
+
|
|
174
|
+
index_check = next(c for c in checks if c.name == "memory:index")
|
|
175
|
+
assert index_check.passed
|
|
176
|
+
|
|
177
|
+
def test_memory_missing(self, empty_home):
|
|
178
|
+
"""Missing memory directory fails."""
|
|
179
|
+
empty_home.mkdir(parents=True, exist_ok=True)
|
|
180
|
+
|
|
181
|
+
checks = _check_memory(empty_home)
|
|
182
|
+
assert len(checks) == 1
|
|
183
|
+
assert not checks[0].passed
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class TestCheckPackages:
|
|
187
|
+
"""Test Python package checks."""
|
|
188
|
+
|
|
189
|
+
def test_skcapstone_is_installed(self):
|
|
190
|
+
"""skcapstone itself should always be importable in tests."""
|
|
191
|
+
checks = _check_packages()
|
|
192
|
+
skcap = next(c for c in checks if c.name == "pkg:skcapstone")
|
|
193
|
+
assert skcap.passed
|
|
194
|
+
|
|
195
|
+
def test_missing_package_detected(self):
|
|
196
|
+
"""A fake package should fail the check."""
|
|
197
|
+
with patch("skcapstone.doctor.importlib.import_module", side_effect=ImportError):
|
|
198
|
+
checks = _check_packages()
|
|
199
|
+
assert all(not c.passed for c in checks)
|
|
200
|
+
assert all(c.fix for c in checks)
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
class TestRunDiagnostics:
|
|
204
|
+
"""Test the full diagnostic run."""
|
|
205
|
+
|
|
206
|
+
def test_full_run_on_populated_home(self, agent_home):
|
|
207
|
+
"""Full diagnostics on a populated home produces a report."""
|
|
208
|
+
report = run_diagnostics(agent_home)
|
|
209
|
+
|
|
210
|
+
assert report.total_count > 0
|
|
211
|
+
assert report.passed_count > 0
|
|
212
|
+
assert report.agent_home == str(agent_home)
|
|
213
|
+
|
|
214
|
+
def test_full_run_on_empty_home(self, empty_home):
|
|
215
|
+
"""Full diagnostics on missing home still produces a report."""
|
|
216
|
+
report = run_diagnostics(empty_home)
|
|
217
|
+
|
|
218
|
+
assert report.total_count > 0
|
|
219
|
+
assert report.failed_count > 0
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
class TestCLIDoctorCommand:
|
|
223
|
+
"""Test the CLI doctor command via CliRunner."""
|
|
224
|
+
|
|
225
|
+
def test_doctor_help(self):
|
|
226
|
+
"""doctor --help works."""
|
|
227
|
+
from skcapstone.cli import main
|
|
228
|
+
|
|
229
|
+
runner = CliRunner()
|
|
230
|
+
result = runner.invoke(main, ["doctor", "--help"])
|
|
231
|
+
assert result.exit_code == 0
|
|
232
|
+
assert "Diagnose" in result.output
|
|
233
|
+
assert "--json-out" in result.output
|
|
234
|
+
|
|
235
|
+
def test_doctor_json_output(self, agent_home):
|
|
236
|
+
"""doctor --json-out produces valid JSON."""
|
|
237
|
+
from skcapstone.cli import main
|
|
238
|
+
|
|
239
|
+
runner = CliRunner()
|
|
240
|
+
result = runner.invoke(main, ["doctor", "--home", str(agent_home), "--json-out"])
|
|
241
|
+
assert result.exit_code == 0
|
|
242
|
+
|
|
243
|
+
data = json.loads(result.output)
|
|
244
|
+
assert "passed" in data
|
|
245
|
+
assert "failed" in data
|
|
246
|
+
assert "checks" in data
|
|
247
|
+
assert isinstance(data["checks"], list)
|
|
248
|
+
|
|
249
|
+
def test_doctor_human_output(self, agent_home):
|
|
250
|
+
"""doctor without --json produces human-readable output."""
|
|
251
|
+
from skcapstone.cli import main
|
|
252
|
+
|
|
253
|
+
runner = CliRunner()
|
|
254
|
+
result = runner.invoke(main, ["doctor", "--home", str(agent_home)])
|
|
255
|
+
assert result.exit_code == 0
|
|
256
|
+
assert "Python Packages" in result.output
|
|
257
|
+
assert "passed" in result.output or "checks" in result.output.lower()
|
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
"""Tests for skcapstone doctor --fix auto-remediation.
|
|
2
|
+
|
|
3
|
+
Covers:
|
|
4
|
+
- run_fixes() creates missing home directory
|
|
5
|
+
- run_fixes() creates missing subdirectories
|
|
6
|
+
- run_fixes() writes a default manifest.json when absent
|
|
7
|
+
- run_fixes() creates memory store layer directories
|
|
8
|
+
- run_fixes() rebuilds a missing memory index from existing files
|
|
9
|
+
- run_fixes() creates sync dir with outbox + inbox
|
|
10
|
+
- run_fixes() skips unfixable checks (packages, identity key)
|
|
11
|
+
- CLI doctor --fix flag appears in --help
|
|
12
|
+
- CLI doctor --fix auto-creates structure and reports fixes
|
|
13
|
+
- CLI doctor --fix --json-out includes fixes key in output
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
import pytest
|
|
22
|
+
from click.testing import CliRunner
|
|
23
|
+
|
|
24
|
+
from skcapstone.doctor import (
|
|
25
|
+
Check,
|
|
26
|
+
DiagnosticReport,
|
|
27
|
+
FixResult,
|
|
28
|
+
run_diagnostics,
|
|
29
|
+
run_fixes,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ---------------------------------------------------------------------------
|
|
34
|
+
# Helpers
|
|
35
|
+
# ---------------------------------------------------------------------------
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _make_report(*checks: Check) -> DiagnosticReport:
|
|
39
|
+
return DiagnosticReport(checks=list(checks), agent_home="/tmp/test")
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _failing(name: str, category: str = "agent") -> Check:
|
|
43
|
+
return Check(name=name, description=name, passed=False, category=category)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
# Unit tests for run_fixes()
|
|
48
|
+
# ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class TestRunFixesHomeDir:
|
|
52
|
+
"""run_fixes creates missing home directory and subdirs."""
|
|
53
|
+
|
|
54
|
+
def test_creates_home_directory(self, tmp_path):
|
|
55
|
+
"""home:exists fix creates the directory."""
|
|
56
|
+
home = tmp_path / ".skcapstone"
|
|
57
|
+
assert not home.exists()
|
|
58
|
+
|
|
59
|
+
report = _make_report(_failing("home:exists"))
|
|
60
|
+
results = run_fixes(report, home)
|
|
61
|
+
|
|
62
|
+
assert home.exists()
|
|
63
|
+
assert len(results) == 1
|
|
64
|
+
assert results[0].success
|
|
65
|
+
assert results[0].check_name == "home:exists"
|
|
66
|
+
|
|
67
|
+
def test_creates_missing_subdirectory(self, tmp_path):
|
|
68
|
+
"""home:{dirname} fix creates each expected subdirectory."""
|
|
69
|
+
home = tmp_path / ".skcapstone"
|
|
70
|
+
home.mkdir()
|
|
71
|
+
|
|
72
|
+
for dirname in ["identity", "memory", "trust", "security", "sync", "config"]:
|
|
73
|
+
assert not (home / dirname).exists()
|
|
74
|
+
report = _make_report(_failing(f"home:{dirname}"))
|
|
75
|
+
results = run_fixes(report, home)
|
|
76
|
+
|
|
77
|
+
assert (home / dirname).exists(), f"{dirname} not created"
|
|
78
|
+
assert results[0].success
|
|
79
|
+
assert results[0].check_name == f"home:{dirname}"
|
|
80
|
+
|
|
81
|
+
def test_subdir_fix_is_idempotent(self, tmp_path):
|
|
82
|
+
"""Re-running a subdir fix on an existing dir does not fail."""
|
|
83
|
+
home = tmp_path / ".skcapstone"
|
|
84
|
+
home.mkdir()
|
|
85
|
+
(home / "memory").mkdir()
|
|
86
|
+
|
|
87
|
+
report = _make_report(_failing("home:memory"))
|
|
88
|
+
results = run_fixes(report, home)
|
|
89
|
+
|
|
90
|
+
assert results[0].success
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class TestRunFixesManifest:
|
|
94
|
+
"""run_fixes writes a default manifest.json."""
|
|
95
|
+
|
|
96
|
+
def test_writes_default_manifest(self, tmp_path):
|
|
97
|
+
"""manifest fix creates a valid JSON file with name + version."""
|
|
98
|
+
home = tmp_path / "myagent"
|
|
99
|
+
home.mkdir()
|
|
100
|
+
|
|
101
|
+
report = _make_report(_failing("home:manifest"))
|
|
102
|
+
results = run_fixes(report, home)
|
|
103
|
+
|
|
104
|
+
manifest = home / "manifest.json"
|
|
105
|
+
assert manifest.exists()
|
|
106
|
+
data = json.loads(manifest.read_text())
|
|
107
|
+
assert "name" in data
|
|
108
|
+
assert "version" in data
|
|
109
|
+
assert results[0].success
|
|
110
|
+
|
|
111
|
+
def test_manifest_fix_skipped_when_corrupt(self, tmp_path):
|
|
112
|
+
"""If manifest.json already exists, fix raises and returns failure."""
|
|
113
|
+
home = tmp_path / "agent"
|
|
114
|
+
home.mkdir()
|
|
115
|
+
(home / "manifest.json").write_text("CORRUPT{{{")
|
|
116
|
+
|
|
117
|
+
report = _make_report(_failing("home:manifest"))
|
|
118
|
+
results = run_fixes(report, home)
|
|
119
|
+
|
|
120
|
+
assert not results[0].success
|
|
121
|
+
assert results[0].error
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class TestRunFixesMemory:
|
|
125
|
+
"""run_fixes creates memory store and rebuilds index."""
|
|
126
|
+
|
|
127
|
+
def test_creates_memory_layer_dirs(self, tmp_path):
|
|
128
|
+
"""memory:store fix creates short-term, mid-term, long-term dirs."""
|
|
129
|
+
home = tmp_path / ".skcapstone"
|
|
130
|
+
home.mkdir()
|
|
131
|
+
|
|
132
|
+
report = _make_report(_failing("memory:store", "memory"))
|
|
133
|
+
results = run_fixes(report, home)
|
|
134
|
+
|
|
135
|
+
for layer in ["short-term", "mid-term", "long-term"]:
|
|
136
|
+
assert (home / "memory" / layer).exists(), f"{layer} not created"
|
|
137
|
+
assert results[0].success
|
|
138
|
+
|
|
139
|
+
def test_rebuilds_empty_index(self, tmp_path):
|
|
140
|
+
"""memory:index fix writes an empty index.json when no memories exist."""
|
|
141
|
+
home = tmp_path / ".skcapstone"
|
|
142
|
+
memory_dir = home / "memory"
|
|
143
|
+
for layer in ["short-term", "mid-term", "long-term"]:
|
|
144
|
+
(memory_dir / layer).mkdir(parents=True)
|
|
145
|
+
|
|
146
|
+
report = _make_report(_failing("memory:index", "memory"))
|
|
147
|
+
results = run_fixes(report, home)
|
|
148
|
+
|
|
149
|
+
index_path = memory_dir / "index.json"
|
|
150
|
+
assert index_path.exists()
|
|
151
|
+
data = json.loads(index_path.read_text())
|
|
152
|
+
assert isinstance(data, dict)
|
|
153
|
+
assert results[0].success
|
|
154
|
+
|
|
155
|
+
def test_rebuilds_index_from_existing_memories(self, tmp_path):
|
|
156
|
+
"""memory:index fix populates index.json from existing memory files."""
|
|
157
|
+
home = tmp_path / ".skcapstone"
|
|
158
|
+
short_term = home / "memory" / "short-term"
|
|
159
|
+
short_term.mkdir(parents=True)
|
|
160
|
+
for layer in ["mid-term", "long-term"]:
|
|
161
|
+
(home / "memory" / layer).mkdir(parents=True)
|
|
162
|
+
|
|
163
|
+
# Write two memory files
|
|
164
|
+
for mem_id in ["abc123", "def456"]:
|
|
165
|
+
(short_term / f"{mem_id}.json").write_text(json.dumps({
|
|
166
|
+
"memory_id": mem_id,
|
|
167
|
+
"content": f"Memory content for {mem_id}",
|
|
168
|
+
"tags": ["test"],
|
|
169
|
+
"importance": 0.8,
|
|
170
|
+
"created_at": "2026-01-01T00:00:00+00:00",
|
|
171
|
+
}))
|
|
172
|
+
|
|
173
|
+
report = _make_report(_failing("memory:index", "memory"))
|
|
174
|
+
results = run_fixes(report, home)
|
|
175
|
+
|
|
176
|
+
index_path = home / "memory" / "index.json"
|
|
177
|
+
data = json.loads(index_path.read_text())
|
|
178
|
+
assert "abc123" in data
|
|
179
|
+
assert "def456" in data
|
|
180
|
+
assert data["abc123"]["layer"] == "short-term"
|
|
181
|
+
assert results[0].success
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
class TestRunFixesSyncDir:
|
|
185
|
+
"""run_fixes creates the sync directory structure."""
|
|
186
|
+
|
|
187
|
+
def test_creates_sync_dir_with_queues(self, tmp_path):
|
|
188
|
+
"""sync:dir fix creates sync/, outbox/, and inbox/."""
|
|
189
|
+
home = tmp_path / ".skcapstone"
|
|
190
|
+
home.mkdir()
|
|
191
|
+
|
|
192
|
+
report = _make_report(_failing("sync:dir", "sync"))
|
|
193
|
+
results = run_fixes(report, home)
|
|
194
|
+
|
|
195
|
+
assert (home / "sync").exists()
|
|
196
|
+
assert (home / "sync" / "outbox").exists()
|
|
197
|
+
assert (home / "sync" / "inbox").exists()
|
|
198
|
+
assert results[0].success
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
class TestRunFixesSkipsUnfixable:
|
|
202
|
+
"""run_fixes silently skips checks with no registered handler."""
|
|
203
|
+
|
|
204
|
+
def test_skips_package_checks(self, tmp_path):
|
|
205
|
+
"""Package installation checks are not auto-fixable."""
|
|
206
|
+
home = tmp_path / ".skcapstone"
|
|
207
|
+
home.mkdir()
|
|
208
|
+
|
|
209
|
+
report = _make_report(_failing("pkg:skcapstone", "packages"))
|
|
210
|
+
results = run_fixes(report, home)
|
|
211
|
+
|
|
212
|
+
# No fix attempted — empty results
|
|
213
|
+
assert results == []
|
|
214
|
+
|
|
215
|
+
def test_skips_identity_key_check(self, tmp_path):
|
|
216
|
+
"""PGP key generation is not auto-fixable."""
|
|
217
|
+
home = tmp_path / ".skcapstone"
|
|
218
|
+
home.mkdir()
|
|
219
|
+
|
|
220
|
+
report = _make_report(_failing("identity:pgp_key", "identity"))
|
|
221
|
+
results = run_fixes(report, home)
|
|
222
|
+
|
|
223
|
+
assert results == []
|
|
224
|
+
|
|
225
|
+
def test_skips_system_tool_check(self, tmp_path):
|
|
226
|
+
"""System tool installs are not auto-fixable."""
|
|
227
|
+
home = tmp_path / ".skcapstone"
|
|
228
|
+
home.mkdir()
|
|
229
|
+
|
|
230
|
+
report = _make_report(_failing("tool:gpg", "system"))
|
|
231
|
+
results = run_fixes(report, home)
|
|
232
|
+
|
|
233
|
+
assert results == []
|
|
234
|
+
|
|
235
|
+
def test_passed_checks_not_attempted(self, tmp_path):
|
|
236
|
+
"""Passing checks generate no fix results."""
|
|
237
|
+
home = tmp_path / ".skcapstone"
|
|
238
|
+
home.mkdir()
|
|
239
|
+
|
|
240
|
+
passing = Check(name="home:exists", description="home", passed=True)
|
|
241
|
+
report = _make_report(passing)
|
|
242
|
+
results = run_fixes(report, home)
|
|
243
|
+
|
|
244
|
+
assert results == []
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
class TestRunFixesMultiple:
|
|
248
|
+
"""run_fixes handles multiple failing checks in one pass."""
|
|
249
|
+
|
|
250
|
+
def test_fixes_multiple_subdirs(self, tmp_path):
|
|
251
|
+
"""Multiple missing subdirs are all created in a single run."""
|
|
252
|
+
home = tmp_path / ".skcapstone"
|
|
253
|
+
home.mkdir()
|
|
254
|
+
|
|
255
|
+
checks = [_failing(f"home:{d}") for d in ["identity", "trust", "config"]]
|
|
256
|
+
report = _make_report(*checks)
|
|
257
|
+
results = run_fixes(report, home)
|
|
258
|
+
|
|
259
|
+
assert len(results) == 3
|
|
260
|
+
assert all(r.success for r in results)
|
|
261
|
+
for d in ["identity", "trust", "config"]:
|
|
262
|
+
assert (home / d).exists()
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
# ---------------------------------------------------------------------------
|
|
266
|
+
# Integration: full diagnostics cycle
|
|
267
|
+
# ---------------------------------------------------------------------------
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
class TestRunDiagnosticsAfterFix:
|
|
271
|
+
"""After running fixes, re-running diagnostics improves the report."""
|
|
272
|
+
|
|
273
|
+
def test_fix_reduces_failure_count(self, tmp_path):
|
|
274
|
+
"""Directories created by fixes pass on the next diagnostic run."""
|
|
275
|
+
home = tmp_path / ".skcapstone"
|
|
276
|
+
home.mkdir()
|
|
277
|
+
|
|
278
|
+
before = run_diagnostics(home)
|
|
279
|
+
before_failed = before.failed_count
|
|
280
|
+
|
|
281
|
+
run_fixes(before, home)
|
|
282
|
+
after = run_diagnostics(home)
|
|
283
|
+
|
|
284
|
+
assert after.failed_count < before_failed
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
# ---------------------------------------------------------------------------
|
|
288
|
+
# CLI integration tests
|
|
289
|
+
# ---------------------------------------------------------------------------
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
class TestCLIDoctorFix:
|
|
293
|
+
"""CLI doctor --fix flag."""
|
|
294
|
+
|
|
295
|
+
def test_fix_flag_in_help(self):
|
|
296
|
+
"""--fix appears in doctor --help output."""
|
|
297
|
+
from skcapstone.cli import main
|
|
298
|
+
|
|
299
|
+
runner = CliRunner()
|
|
300
|
+
result = runner.invoke(main, ["doctor", "--help"])
|
|
301
|
+
assert result.exit_code == 0
|
|
302
|
+
assert "--fix" in result.output
|
|
303
|
+
|
|
304
|
+
def test_fix_creates_dirs_and_reports(self, tmp_path):
|
|
305
|
+
"""doctor --fix on a bare home creates dirs and prints fix results."""
|
|
306
|
+
from skcapstone.cli import main
|
|
307
|
+
|
|
308
|
+
home = tmp_path / ".skcapstone"
|
|
309
|
+
home.mkdir()
|
|
310
|
+
|
|
311
|
+
runner = CliRunner()
|
|
312
|
+
result = runner.invoke(main, ["doctor", "--home", str(home), "--fix"])
|
|
313
|
+
assert result.exit_code == 0
|
|
314
|
+
# At least one successful fix should be reported
|
|
315
|
+
assert "\u2713" in result.output or "Auto-fix" in result.output
|
|
316
|
+
|
|
317
|
+
def test_fix_json_output_includes_fixes_key(self, tmp_path):
|
|
318
|
+
"""doctor --fix --json-out includes a 'fixes' list in JSON output."""
|
|
319
|
+
from skcapstone.cli import main
|
|
320
|
+
|
|
321
|
+
home = tmp_path / ".skcapstone"
|
|
322
|
+
home.mkdir()
|
|
323
|
+
|
|
324
|
+
runner = CliRunner()
|
|
325
|
+
result = runner.invoke(
|
|
326
|
+
main, ["doctor", "--home", str(home), "--fix", "--json-out"]
|
|
327
|
+
)
|
|
328
|
+
assert result.exit_code == 0
|
|
329
|
+
data = json.loads(result.output)
|
|
330
|
+
assert "fixes" in data
|
|
331
|
+
assert isinstance(data["fixes"], list)
|
|
332
|
+
|
|
333
|
+
def test_fix_on_already_healthy_home_is_noop(self, tmp_path):
|
|
334
|
+
"""doctor --fix on a passing home makes no changes and exits cleanly."""
|
|
335
|
+
from skcapstone.cli import main
|
|
336
|
+
|
|
337
|
+
# Build a fully populated home
|
|
338
|
+
home = tmp_path / ".skcapstone"
|
|
339
|
+
for d in ["identity", "memory", "trust", "security", "sync", "config",
|
|
340
|
+
"memory/short-term", "memory/mid-term", "memory/long-term",
|
|
341
|
+
"sync/outbox", "sync/inbox"]:
|
|
342
|
+
(home / d).mkdir(parents=True, exist_ok=True)
|
|
343
|
+
(home / "manifest.json").write_text(json.dumps({"name": "T", "version": "0.1.0"}))
|
|
344
|
+
(home / "identity" / "identity.json").write_text(json.dumps({
|
|
345
|
+
"name": "T", "fingerprint": "AABB1122", "capauth_managed": True,
|
|
346
|
+
}))
|
|
347
|
+
(home / "memory" / "index.json").write_text("{}")
|
|
348
|
+
|
|
349
|
+
runner = CliRunner()
|
|
350
|
+
result = runner.invoke(main, ["doctor", "--home", str(home), "--fix"])
|
|
351
|
+
assert result.exit_code == 0
|