@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,194 @@
|
|
|
1
|
+
"""Logs command — tail daemon logs with optional filtering."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import re
|
|
6
|
+
import time
|
|
7
|
+
from collections import deque
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import click
|
|
11
|
+
|
|
12
|
+
from ._common import AGENT_HOME, console
|
|
13
|
+
from .. import SKCAPSTONE_ROOT
|
|
14
|
+
|
|
15
|
+
# Log level ordering (lowest → highest)
|
|
16
|
+
_LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
|
|
17
|
+
|
|
18
|
+
# Rich markup style per level
|
|
19
|
+
_LEVEL_STYLE: dict[str, str] = {
|
|
20
|
+
"DEBUG": "dim",
|
|
21
|
+
"INFO": "",
|
|
22
|
+
"WARNING": "yellow",
|
|
23
|
+
"ERROR": "red",
|
|
24
|
+
"CRITICAL": "bold red",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Regex to extract level from daemon log format:
|
|
28
|
+
# "2026-03-02 12:34:56,789 [skcapstone.daemon] INFO: message"
|
|
29
|
+
_LEVEL_RE = re.compile(r"\]\s+(DEBUG|INFO|WARNING|ERROR|CRITICAL):")
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
# ---------------------------------------------------------------------------
|
|
33
|
+
# Helpers (importable for unit-testing)
|
|
34
|
+
# ---------------------------------------------------------------------------
|
|
35
|
+
|
|
36
|
+
def _resolve_log_file(agent: str | None, home: str) -> Path:
|
|
37
|
+
"""Return the path to daemon.log for the given agent/home."""
|
|
38
|
+
if agent:
|
|
39
|
+
base = (Path(SKCAPSTONE_ROOT) / "agents" / agent).expanduser()
|
|
40
|
+
else:
|
|
41
|
+
base = Path(home).expanduser()
|
|
42
|
+
return base / "logs" / "daemon.log"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _parse_level(line: str) -> str | None:
|
|
46
|
+
"""Extract the log level keyword from a formatted log line, or None."""
|
|
47
|
+
m = _LEVEL_RE.search(line)
|
|
48
|
+
return m.group(1) if m else None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _matches_filters(line: str, min_level: str | None, peer: str | None) -> bool:
|
|
52
|
+
"""Return True if *line* passes the active filters.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
line: A single log line (no trailing newline).
|
|
56
|
+
min_level: Minimum log level (e.g. ``"WARNING"``). Lines whose level
|
|
57
|
+
is below this threshold are excluded. ``None`` disables the filter.
|
|
58
|
+
peer: Substring to match against the line (case-insensitive).
|
|
59
|
+
``None`` disables the filter.
|
|
60
|
+
"""
|
|
61
|
+
if min_level:
|
|
62
|
+
lvl = _parse_level(line)
|
|
63
|
+
if lvl is None:
|
|
64
|
+
return False
|
|
65
|
+
if _LEVELS.index(lvl) < _LEVELS.index(min_level):
|
|
66
|
+
return False
|
|
67
|
+
if peer and peer.lower() not in line.lower():
|
|
68
|
+
return False
|
|
69
|
+
return True
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _format_line(line: str) -> str:
|
|
73
|
+
"""Wrap *line* in Rich markup that reflects its log level."""
|
|
74
|
+
lvl = _parse_level(line)
|
|
75
|
+
style = _LEVEL_STYLE.get(lvl or "", "")
|
|
76
|
+
if not style:
|
|
77
|
+
return line
|
|
78
|
+
return f"[{style}]{line}[/]"
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _tail(path: Path, n: int) -> list[str]:
|
|
82
|
+
"""Return the last *n* lines from *path* (with newlines preserved)."""
|
|
83
|
+
with open(path) as fh:
|
|
84
|
+
return list(deque(fh, maxlen=n))
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
# ---------------------------------------------------------------------------
|
|
88
|
+
# Command registration
|
|
89
|
+
# ---------------------------------------------------------------------------
|
|
90
|
+
|
|
91
|
+
def register_logs_commands(main: click.Group) -> None:
|
|
92
|
+
"""Register the top-level ``skcapstone logs`` command."""
|
|
93
|
+
|
|
94
|
+
@main.command("logs")
|
|
95
|
+
@click.option(
|
|
96
|
+
"--home", default=AGENT_HOME, type=click.Path(),
|
|
97
|
+
help="Agent home directory.",
|
|
98
|
+
)
|
|
99
|
+
@click.option(
|
|
100
|
+
"--agent", default=None,
|
|
101
|
+
help="Named agent whose logs to read (e.g. opus, jarvis).",
|
|
102
|
+
)
|
|
103
|
+
@click.option(
|
|
104
|
+
"--follow", "-f", is_flag=True,
|
|
105
|
+
help="Stream new log entries as they arrive (like tail -f).",
|
|
106
|
+
)
|
|
107
|
+
@click.option(
|
|
108
|
+
"--lines", "-n", default=50, show_default=True,
|
|
109
|
+
help="Number of recent lines to show.",
|
|
110
|
+
)
|
|
111
|
+
@click.option(
|
|
112
|
+
"--level",
|
|
113
|
+
default=None,
|
|
114
|
+
type=click.Choice(
|
|
115
|
+
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
|
|
116
|
+
case_sensitive=False,
|
|
117
|
+
),
|
|
118
|
+
help="Minimum log level to display.",
|
|
119
|
+
)
|
|
120
|
+
@click.option(
|
|
121
|
+
"--peer", default=None,
|
|
122
|
+
help="Only show lines that mention this peer name.",
|
|
123
|
+
)
|
|
124
|
+
def logs_command(
|
|
125
|
+
home: str,
|
|
126
|
+
agent: str | None,
|
|
127
|
+
follow: bool,
|
|
128
|
+
lines: int,
|
|
129
|
+
level: str | None,
|
|
130
|
+
peer: str | None,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""Tail daemon logs in real-time.
|
|
133
|
+
|
|
134
|
+
Reads from ~/.skcapstone/logs/daemon.log by default.
|
|
135
|
+
Use --agent or --home to target a specific named agent.
|
|
136
|
+
|
|
137
|
+
Examples:
|
|
138
|
+
|
|
139
|
+
skcapstone logs
|
|
140
|
+
|
|
141
|
+
skcapstone logs -n 100
|
|
142
|
+
|
|
143
|
+
skcapstone logs -f
|
|
144
|
+
|
|
145
|
+
skcapstone logs --level WARNING
|
|
146
|
+
|
|
147
|
+
skcapstone logs --peer opus --follow
|
|
148
|
+
"""
|
|
149
|
+
log_file = _resolve_log_file(agent, home)
|
|
150
|
+
min_level = level.upper() if level else None
|
|
151
|
+
|
|
152
|
+
if not log_file.exists():
|
|
153
|
+
console.print(
|
|
154
|
+
f"[yellow]Log file not found:[/] {log_file}\n"
|
|
155
|
+
"[dim]Start the daemon to create logs.[/]"
|
|
156
|
+
)
|
|
157
|
+
return
|
|
158
|
+
|
|
159
|
+
if not follow:
|
|
160
|
+
# Static mode: read last N lines, apply filters, print, exit.
|
|
161
|
+
raw = _tail(log_file, lines)
|
|
162
|
+
filtered = [
|
|
163
|
+
ln.rstrip("\n")
|
|
164
|
+
for ln in raw
|
|
165
|
+
if _matches_filters(ln.rstrip("\n"), min_level, peer)
|
|
166
|
+
]
|
|
167
|
+
if not filtered:
|
|
168
|
+
console.print("[dim]No matching log lines.[/]")
|
|
169
|
+
return
|
|
170
|
+
for ln in filtered:
|
|
171
|
+
console.print(_format_line(ln))
|
|
172
|
+
return
|
|
173
|
+
|
|
174
|
+
# Follow mode: show initial N lines, then stream new content.
|
|
175
|
+
try:
|
|
176
|
+
with open(log_file) as fh:
|
|
177
|
+
# Emit the last N historical lines first.
|
|
178
|
+
initial = list(deque(fh, maxlen=lines))
|
|
179
|
+
for ln in initial:
|
|
180
|
+
ln = ln.rstrip("\n")
|
|
181
|
+
if _matches_filters(ln, min_level, peer):
|
|
182
|
+
console.print(_format_line(ln))
|
|
183
|
+
|
|
184
|
+
# Poll for new content until Ctrl-C.
|
|
185
|
+
while True:
|
|
186
|
+
chunk = fh.read()
|
|
187
|
+
if chunk:
|
|
188
|
+
for ln in chunk.splitlines():
|
|
189
|
+
if _matches_filters(ln, min_level, peer):
|
|
190
|
+
console.print(_format_line(ln))
|
|
191
|
+
time.sleep(0.2)
|
|
192
|
+
|
|
193
|
+
except KeyboardInterrupt:
|
|
194
|
+
pass
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"""MCP (Model Context Protocol) server commands: serve."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import click
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_mcp_commands(main: click.Group) -> None:
|
|
9
|
+
"""Register the mcp command group."""
|
|
10
|
+
|
|
11
|
+
@main.group()
|
|
12
|
+
def mcp():
|
|
13
|
+
"""MCP (Model Context Protocol) server.
|
|
14
|
+
|
|
15
|
+
Expose sovereign agent capabilities as MCP tools for
|
|
16
|
+
AI platforms like Cursor and Claude Desktop.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@mcp.command("serve")
|
|
20
|
+
def mcp_serve():
|
|
21
|
+
"""Start the MCP server on stdio transport.
|
|
22
|
+
|
|
23
|
+
Exposes agent_status, memory_recall, memory_store, send_message,
|
|
24
|
+
check_inbox, sync_push, sync_pull, coord_status, coord_claim,
|
|
25
|
+
and coord_complete as MCP tools.
|
|
26
|
+
|
|
27
|
+
For Cursor: configure in .cursor/mcp.json.
|
|
28
|
+
For Claude Desktop: add to claude_desktop_config.json.
|
|
29
|
+
"""
|
|
30
|
+
from ..mcp_server import main as mcp_main
|
|
31
|
+
|
|
32
|
+
mcp_main()
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""Memory commands: store, search, list, recall, delete, stats, gc, curate, migrate, verify, reindex."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import sys
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import click
|
|
10
|
+
|
|
11
|
+
from ._common import AGENT_HOME, console, status_icon
|
|
12
|
+
from ._validators import validate_task_id
|
|
13
|
+
from ..pillars.security import audit_event
|
|
14
|
+
|
|
15
|
+
from rich.panel import Panel
|
|
16
|
+
from rich.table import Table
|
|
17
|
+
from rich.text import Text
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def register_memory_commands(main: click.Group) -> None:
|
|
21
|
+
"""Register the memory command group."""
|
|
22
|
+
|
|
23
|
+
@main.group()
|
|
24
|
+
def memory():
|
|
25
|
+
"""Sovereign memory — your agent never forgets.
|
|
26
|
+
|
|
27
|
+
Store, search, recall, and manage memories across
|
|
28
|
+
sessions and platforms.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
@memory.command("store")
|
|
32
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
33
|
+
@click.argument("content")
|
|
34
|
+
@click.option("--tag", "-t", multiple=True, help="Tags for categorization.")
|
|
35
|
+
@click.option("--source", "-s", default="cli", help="Memory source.")
|
|
36
|
+
@click.option("--importance", "-i", default=0.5, type=float, help="Importance 0.0-1.0.")
|
|
37
|
+
@click.option("--layer", "-l", type=click.Choice(["short-term", "mid-term", "long-term"]), default=None)
|
|
38
|
+
def memory_store(home, content, tag, source, importance, layer):
|
|
39
|
+
"""Store a new memory."""
|
|
40
|
+
from ..memory_engine import store as mem_store
|
|
41
|
+
from ..models import MemoryLayer
|
|
42
|
+
|
|
43
|
+
home_path = Path(home).expanduser()
|
|
44
|
+
if not home_path.exists():
|
|
45
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
46
|
+
sys.exit(1)
|
|
47
|
+
|
|
48
|
+
lyr = MemoryLayer(layer) if layer else None
|
|
49
|
+
entry = mem_store(home=home_path, content=content, tags=list(tag),
|
|
50
|
+
source=source, importance=importance, layer=lyr)
|
|
51
|
+
|
|
52
|
+
console.print(f"\n [green]Stored:[/] {entry.memory_id}")
|
|
53
|
+
console.print(f" Layer: [cyan]{entry.layer.value}[/]")
|
|
54
|
+
console.print(f" Tags: {', '.join(entry.tags) if entry.tags else '[dim]none[/]'}")
|
|
55
|
+
console.print(f" Importance: {entry.importance}")
|
|
56
|
+
audit_event(home_path, "MEMORY_STORE", f"Memory {entry.memory_id} stored in {entry.layer.value}")
|
|
57
|
+
console.print()
|
|
58
|
+
|
|
59
|
+
@memory.command("search")
|
|
60
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
61
|
+
@click.argument("query")
|
|
62
|
+
@click.option("--tag", "-t", multiple=True, help="Filter by tag.")
|
|
63
|
+
@click.option("--layer", "-l", type=click.Choice(["short-term", "mid-term", "long-term"]), default=None)
|
|
64
|
+
@click.option("--limit", "-n", default=20, help="Max results.")
|
|
65
|
+
@click.option("--json-out", is_flag=True, help="Output results as JSON.")
|
|
66
|
+
def memory_search(home, query, tag, layer, limit, json_out):
|
|
67
|
+
"""Search memories by content and tags."""
|
|
68
|
+
from ..memory_engine import search as mem_search
|
|
69
|
+
from ..models import MemoryLayer
|
|
70
|
+
|
|
71
|
+
home_path = Path(home).expanduser()
|
|
72
|
+
if not home_path.exists():
|
|
73
|
+
if json_out:
|
|
74
|
+
print(json.dumps([]))
|
|
75
|
+
return
|
|
76
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
77
|
+
sys.exit(1)
|
|
78
|
+
|
|
79
|
+
lyr = MemoryLayer(layer) if layer else None
|
|
80
|
+
tags = list(tag) if tag else None
|
|
81
|
+
results = mem_search(home=home_path, query=query, layer=lyr, tags=tags, limit=limit)
|
|
82
|
+
|
|
83
|
+
if json_out:
|
|
84
|
+
output = [
|
|
85
|
+
{
|
|
86
|
+
"id": entry.memory_id,
|
|
87
|
+
"content": entry.content,
|
|
88
|
+
"tags": entry.tags,
|
|
89
|
+
"importance": entry.importance,
|
|
90
|
+
"layer": entry.layer.value,
|
|
91
|
+
"created_at": entry.created_at.isoformat() if entry.created_at else None,
|
|
92
|
+
}
|
|
93
|
+
for entry in results
|
|
94
|
+
]
|
|
95
|
+
print(json.dumps(output))
|
|
96
|
+
return
|
|
97
|
+
|
|
98
|
+
if not results:
|
|
99
|
+
console.print(f"\n [dim]No memories match '[/]{query}[dim]'[/]\n")
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
console.print(f"\n [bold]{len(results)}[/] memor{'y' if len(results) == 1 else 'ies'} found:\n")
|
|
103
|
+
|
|
104
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
105
|
+
table.add_column("ID", style="cyan", max_width=14)
|
|
106
|
+
table.add_column("Layer", style="dim")
|
|
107
|
+
table.add_column("Content", max_width=50)
|
|
108
|
+
table.add_column("Tags", style="dim")
|
|
109
|
+
table.add_column("Imp", justify="right")
|
|
110
|
+
|
|
111
|
+
for entry in results:
|
|
112
|
+
preview = entry.content[:80] + ("..." if len(entry.content) > 80 else "")
|
|
113
|
+
table.add_row(entry.memory_id, entry.layer.value, preview,
|
|
114
|
+
", ".join(entry.tags) if entry.tags else "", f"{entry.importance:.1f}")
|
|
115
|
+
|
|
116
|
+
console.print(table)
|
|
117
|
+
console.print()
|
|
118
|
+
|
|
119
|
+
@memory.command("list")
|
|
120
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
121
|
+
@click.option("--layer", "-l", type=click.Choice(["short-term", "mid-term", "long-term"]), default=None)
|
|
122
|
+
@click.option("--tag", "-t", multiple=True, help="Filter by tag.")
|
|
123
|
+
@click.option("--limit", "-n", default=50, help="Max results.")
|
|
124
|
+
def memory_list(home, layer, tag, limit):
|
|
125
|
+
"""Browse memories, newest first."""
|
|
126
|
+
from ..memory_engine import list_memories as mem_list
|
|
127
|
+
from ..models import MemoryLayer
|
|
128
|
+
|
|
129
|
+
home_path = Path(home).expanduser()
|
|
130
|
+
if not home_path.exists():
|
|
131
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
132
|
+
sys.exit(1)
|
|
133
|
+
|
|
134
|
+
lyr = MemoryLayer(layer) if layer else None
|
|
135
|
+
tags = list(tag) if tag else None
|
|
136
|
+
entries = mem_list(home=home_path, layer=lyr, tags=tags, limit=limit)
|
|
137
|
+
|
|
138
|
+
if not entries:
|
|
139
|
+
console.print("\n [dim]No memories found.[/]\n")
|
|
140
|
+
return
|
|
141
|
+
|
|
142
|
+
console.print(f"\n [bold]{len(entries)}[/] memor{'y' if len(entries) == 1 else 'ies'}:\n")
|
|
143
|
+
|
|
144
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
145
|
+
table.add_column("ID", style="cyan", max_width=14)
|
|
146
|
+
table.add_column("Layer")
|
|
147
|
+
table.add_column("Content", max_width=50)
|
|
148
|
+
table.add_column("Tags", style="dim")
|
|
149
|
+
table.add_column("Imp", justify="right")
|
|
150
|
+
table.add_column("Accessed", justify="right", style="dim")
|
|
151
|
+
|
|
152
|
+
for entry in entries:
|
|
153
|
+
preview = entry.content[:80] + ("..." if len(entry.content) > 80 else "")
|
|
154
|
+
layer_color = {"long-term": "green", "mid-term": "cyan", "short-term": "dim"}.get(entry.layer.value, "dim")
|
|
155
|
+
table.add_row(entry.memory_id, Text(entry.layer.value, style=layer_color), preview,
|
|
156
|
+
", ".join(entry.tags) if entry.tags else "", f"{entry.importance:.1f}",
|
|
157
|
+
str(entry.access_count))
|
|
158
|
+
|
|
159
|
+
console.print(table)
|
|
160
|
+
console.print()
|
|
161
|
+
|
|
162
|
+
@memory.command("recall")
|
|
163
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
164
|
+
@click.argument("memory_id")
|
|
165
|
+
def memory_recall(home, memory_id):
|
|
166
|
+
"""Recall a specific memory by ID."""
|
|
167
|
+
from ..memory_engine import recall as mem_recall
|
|
168
|
+
|
|
169
|
+
validate_task_id(memory_id) # memory IDs are hex UUIDs
|
|
170
|
+
|
|
171
|
+
home_path = Path(home).expanduser()
|
|
172
|
+
if not home_path.exists():
|
|
173
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
174
|
+
sys.exit(1)
|
|
175
|
+
|
|
176
|
+
entry = mem_recall(home=home_path, memory_id=memory_id)
|
|
177
|
+
if entry is None:
|
|
178
|
+
console.print(f"[red]Memory not found:[/] {memory_id}")
|
|
179
|
+
sys.exit(1)
|
|
180
|
+
|
|
181
|
+
console.print()
|
|
182
|
+
console.print(Panel(
|
|
183
|
+
entry.content,
|
|
184
|
+
title=f"[cyan]{entry.memory_id}[/] — {entry.layer.value}",
|
|
185
|
+
subtitle=f"importance={entry.importance} accessed={entry.access_count} source={entry.source}",
|
|
186
|
+
border_style="bright_blue",
|
|
187
|
+
))
|
|
188
|
+
if entry.tags:
|
|
189
|
+
console.print(f" Tags: {', '.join(entry.tags)}")
|
|
190
|
+
if entry.metadata:
|
|
191
|
+
console.print(f" Metadata: {json.dumps(entry.metadata)}")
|
|
192
|
+
console.print(f" Created: {entry.created_at.isoformat() if entry.created_at else 'unknown'}")
|
|
193
|
+
if entry.accessed_at:
|
|
194
|
+
console.print(f" Last accessed: {entry.accessed_at.isoformat()}")
|
|
195
|
+
console.print()
|
|
196
|
+
|
|
197
|
+
@memory.command("delete")
|
|
198
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
199
|
+
@click.argument("memory_id")
|
|
200
|
+
@click.option("--force", is_flag=True, help="Skip confirmation.")
|
|
201
|
+
def memory_delete(home, memory_id, force):
|
|
202
|
+
"""Delete a memory by ID."""
|
|
203
|
+
from ..memory_engine import delete as mem_delete
|
|
204
|
+
|
|
205
|
+
validate_task_id(memory_id) # memory IDs are hex UUIDs
|
|
206
|
+
|
|
207
|
+
home_path = Path(home).expanduser()
|
|
208
|
+
if not force and not click.confirm(f"Delete memory {memory_id}?"):
|
|
209
|
+
console.print("[yellow]Aborted.[/]")
|
|
210
|
+
return
|
|
211
|
+
|
|
212
|
+
if mem_delete(home_path, memory_id):
|
|
213
|
+
console.print(f"\n [red]Deleted:[/] {memory_id}\n")
|
|
214
|
+
audit_event(home_path, "MEMORY_DELETE", f"Memory {memory_id} deleted")
|
|
215
|
+
else:
|
|
216
|
+
console.print(f"[red]Memory not found:[/] {memory_id}")
|
|
217
|
+
sys.exit(1)
|
|
218
|
+
|
|
219
|
+
@memory.command("stats")
|
|
220
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
221
|
+
def memory_stats(home):
|
|
222
|
+
"""Show memory statistics across all layers."""
|
|
223
|
+
from ..memory_engine import get_stats
|
|
224
|
+
|
|
225
|
+
home_path = Path(home).expanduser()
|
|
226
|
+
if not home_path.exists():
|
|
227
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
228
|
+
sys.exit(1)
|
|
229
|
+
|
|
230
|
+
stats = get_stats(home_path)
|
|
231
|
+
console.print()
|
|
232
|
+
console.print(Panel(
|
|
233
|
+
f"Total: [bold]{stats.total_memories}[/] memories\n"
|
|
234
|
+
f" [green]Long-term:[/] {stats.long_term}\n"
|
|
235
|
+
f" [cyan]Mid-term:[/] {stats.mid_term}\n"
|
|
236
|
+
f" [dim]Short-term:[/] {stats.short_term}\n\n"
|
|
237
|
+
f"Store: {stats.store_path}\n"
|
|
238
|
+
f"Status: {status_icon(stats.status)}",
|
|
239
|
+
title="SKMemory", border_style="bright_blue",
|
|
240
|
+
))
|
|
241
|
+
console.print()
|
|
242
|
+
|
|
243
|
+
@memory.command("gc")
|
|
244
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
245
|
+
def memory_gc(home):
|
|
246
|
+
"""Garbage-collect expired short-term memories."""
|
|
247
|
+
from ..memory_engine import gc_expired
|
|
248
|
+
|
|
249
|
+
home_path = Path(home).expanduser()
|
|
250
|
+
removed = gc_expired(home_path)
|
|
251
|
+
if removed:
|
|
252
|
+
console.print(f"\n [yellow]Cleaned up {removed} expired memor{'y' if removed == 1 else 'ies'}.[/]\n")
|
|
253
|
+
else:
|
|
254
|
+
console.print("\n [green]Nothing to clean up.[/]\n")
|
|
255
|
+
|
|
256
|
+
@memory.command("curate")
|
|
257
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
258
|
+
@click.option("--dry-run", is_flag=True, help="Preview changes without applying.")
|
|
259
|
+
@click.option("--promote", is_flag=True, help="Only run promotion pass.")
|
|
260
|
+
@click.option("--dedupe", is_flag=True, help="Only run deduplication pass.")
|
|
261
|
+
@click.option("--stats", is_flag=True, help="Show curation statistics only.")
|
|
262
|
+
def memory_curate(home, dry_run, promote, dedupe, stats):
|
|
263
|
+
"""Curate memories: auto-tag, promote, deduplicate."""
|
|
264
|
+
from ..memory_curator import MemoryCurator
|
|
265
|
+
|
|
266
|
+
home_path = Path(home).expanduser()
|
|
267
|
+
curator = MemoryCurator(home_path)
|
|
268
|
+
|
|
269
|
+
if stats:
|
|
270
|
+
s = curator.get_stats()
|
|
271
|
+
console.print(f"\n [bold]{s['total']}[/] memories")
|
|
272
|
+
for lyr, count in s.get("layers", {}).items():
|
|
273
|
+
console.print(f" {lyr}: {count}")
|
|
274
|
+
console.print(f" Tag coverage: [bold]{s['tag_coverage']:.0%}[/]")
|
|
275
|
+
console.print(f" Avg importance: [bold]{s['avg_importance']:.2f}[/]")
|
|
276
|
+
console.print(f" Promotion candidates: [bold]{s['promotion_candidates']}[/]")
|
|
277
|
+
if s.get("top_tags"):
|
|
278
|
+
console.print(" Top tags:")
|
|
279
|
+
for tg, count in s["top_tags"][:10]:
|
|
280
|
+
console.print(f" {tg}: {count}")
|
|
281
|
+
console.print()
|
|
282
|
+
return
|
|
283
|
+
|
|
284
|
+
run_promote = promote or (not promote and not dedupe)
|
|
285
|
+
run_dedupe = dedupe or (not promote and not dedupe)
|
|
286
|
+
|
|
287
|
+
prefix = "[DRY RUN] " if dry_run else ""
|
|
288
|
+
console.print(f"\n {prefix}Running curation pass...\n")
|
|
289
|
+
|
|
290
|
+
result = curator.curate(dry_run=dry_run, promote=run_promote, dedupe=run_dedupe)
|
|
291
|
+
|
|
292
|
+
console.print(f" Scanned: {result.total_scanned} memories")
|
|
293
|
+
if result.tagged:
|
|
294
|
+
console.print(f" [cyan]Tagged:[/] {len(result.tagged)} memories received new tags")
|
|
295
|
+
if result.promoted:
|
|
296
|
+
console.print(f" [green]Promoted:[/] {len(result.promoted)} memories moved to higher tier")
|
|
297
|
+
if result.deduped:
|
|
298
|
+
console.print(f" [yellow]Deduped:[/] {len(result.deduped)} duplicate(s) removed")
|
|
299
|
+
if not result.tagged and not result.promoted and not result.deduped:
|
|
300
|
+
console.print(" [dim]Nothing to curate — memories are clean.[/]")
|
|
301
|
+
console.print()
|
|
302
|
+
|
|
303
|
+
@memory.command("migrate")
|
|
304
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
305
|
+
@click.option("--dry-run", is_flag=True, help="Preview without writing.")
|
|
306
|
+
@click.option("--verify", is_flag=True, help="Verify migration integrity.")
|
|
307
|
+
def memory_migrate(home, dry_run, verify):
|
|
308
|
+
"""Migrate JSON memories to the unified three-tier backend."""
|
|
309
|
+
from ..migrate_memories import migrate
|
|
310
|
+
|
|
311
|
+
home_path = Path(home).expanduser()
|
|
312
|
+
if not home_path.exists():
|
|
313
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
314
|
+
sys.exit(1)
|
|
315
|
+
|
|
316
|
+
result = migrate(home_path, dry_run=dry_run, verify=verify)
|
|
317
|
+
|
|
318
|
+
if dry_run:
|
|
319
|
+
console.print(f"\n [bold]DRY RUN:[/] Found {result['total_json']} JSON memories to migrate.\n")
|
|
320
|
+
return
|
|
321
|
+
|
|
322
|
+
if verify:
|
|
323
|
+
verified = result.get("verified", 0)
|
|
324
|
+
missing = result.get("missing", [])
|
|
325
|
+
if not missing:
|
|
326
|
+
console.print(f"\n [green]Verified:[/] All {verified} memories present in unified backend.\n")
|
|
327
|
+
else:
|
|
328
|
+
console.print(f"\n [yellow]Verification:[/] {verified} present, {len(missing)} missing.")
|
|
329
|
+
for mid in missing[:10]:
|
|
330
|
+
console.print(f" [red]Missing:[/] {mid}")
|
|
331
|
+
if len(missing) > 10:
|
|
332
|
+
console.print(f" ... and {len(missing) - 10} more")
|
|
333
|
+
console.print()
|
|
334
|
+
return
|
|
335
|
+
|
|
336
|
+
console.print(f"\n [bold]Migration results:[/]")
|
|
337
|
+
console.print(f" Total JSON memories: {result['total_json']}")
|
|
338
|
+
console.print(f" [green]Migrated:[/] {result['migrated']}")
|
|
339
|
+
console.print(f" [dim]Skipped (existing):[/] {result['skipped_existing']}")
|
|
340
|
+
if result.get("errors"):
|
|
341
|
+
console.print(f" [red]Errors:[/] {len(result['errors'])}")
|
|
342
|
+
for err in result["errors"][:5]:
|
|
343
|
+
console.print(f" {err}")
|
|
344
|
+
console.print()
|
|
345
|
+
|
|
346
|
+
@memory.command("verify")
|
|
347
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
348
|
+
def memory_verify(home):
|
|
349
|
+
"""Check consistency across memory backends."""
|
|
350
|
+
from ..memory_adapter import verify_sync
|
|
351
|
+
|
|
352
|
+
home_path = Path(home).expanduser()
|
|
353
|
+
if not home_path.exists():
|
|
354
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
355
|
+
sys.exit(1)
|
|
356
|
+
|
|
357
|
+
result = verify_sync()
|
|
358
|
+
|
|
359
|
+
console.print("\n [bold]Backend sync status:[/]")
|
|
360
|
+
for name, info in result.get("backends", {}).items():
|
|
361
|
+
ok = info.get("ok", False)
|
|
362
|
+
count = info.get("count", "?")
|
|
363
|
+
icon = "[green]ok[/]" if ok else "[red]error[/]"
|
|
364
|
+
console.print(f" {name}: {icon} ({count} memories)")
|
|
365
|
+
|
|
366
|
+
if result.get("synced"):
|
|
367
|
+
console.print("\n [green]All backends in sync.[/]\n")
|
|
368
|
+
else:
|
|
369
|
+
console.print(f"\n [yellow]Out of sync:[/] {result.get('reason', 'unknown')}\n")
|
|
370
|
+
|
|
371
|
+
@memory.command("reindex")
|
|
372
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
373
|
+
def memory_reindex(home):
|
|
374
|
+
"""Rebuild vector and graph indexes from SQLite primary."""
|
|
375
|
+
from ..memory_adapter import reindex_all
|
|
376
|
+
|
|
377
|
+
home_path = Path(home).expanduser()
|
|
378
|
+
if not home_path.exists():
|
|
379
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
380
|
+
sys.exit(1)
|
|
381
|
+
|
|
382
|
+
console.print("\n Reindexing secondary backends...\n")
|
|
383
|
+
result = reindex_all()
|
|
384
|
+
|
|
385
|
+
if result.get("ok"):
|
|
386
|
+
console.print(f" [green]Done:[/] {result['total']} memories reindexed.")
|
|
387
|
+
console.print(f" Vector: {result['vector_indexed']}")
|
|
388
|
+
console.print(f" Graph: {result['graph_indexed']}")
|
|
389
|
+
else:
|
|
390
|
+
console.print(f" [red]Errors during reindex.[/]")
|
|
391
|
+
for err in result.get("errors", [])[:5]:
|
|
392
|
+
console.print(f" {err}")
|
|
393
|
+
console.print()
|
|
394
|
+
|
|
395
|
+
@memory.command("dedup")
|
|
396
|
+
@click.option("--home", default=AGENT_HOME, type=click.Path())
|
|
397
|
+
def memory_dedup(home):
|
|
398
|
+
"""Deduplicate memories across all tiers.
|
|
399
|
+
|
|
400
|
+
Scans for exact and near-duplicate titles. Keeps the newest
|
|
401
|
+
copy and archives the rest to memory/archive/deduped/.
|
|
402
|
+
"""
|
|
403
|
+
from ..memory_promoter import PromotionEngine
|
|
404
|
+
|
|
405
|
+
home_path = Path(home).expanduser()
|
|
406
|
+
if not home_path.exists():
|
|
407
|
+
console.print("[bold red]No agent found.[/] Run skcapstone init first.")
|
|
408
|
+
sys.exit(1)
|
|
409
|
+
|
|
410
|
+
console.print("\n Scanning for duplicate memories...\n")
|
|
411
|
+
engine = PromotionEngine(home_path)
|
|
412
|
+
removed = engine.dedup_memories()
|
|
413
|
+
|
|
414
|
+
if removed:
|
|
415
|
+
console.print(f" [yellow]Deduped:[/] {removed} duplicate{'s' if removed != 1 else ''} archived.")
|
|
416
|
+
else:
|
|
417
|
+
console.print(" [green]No duplicates found.[/]")
|
|
418
|
+
console.print()
|