claude-mpm 5.4.36__py3-none-any.whl → 5.4.62__py3-none-any.whl
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.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +5 -0
- claude_mpm/agents/PM_INSTRUCTIONS.md +489 -177
- claude_mpm/agents/base_agent.json +1 -1
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/cli/commands/configure_agent_display.py +12 -0
- claude_mpm/cli/commands/mpm_init/core.py +72 -0
- claude_mpm/cli/commands/profile.py +276 -0
- claude_mpm/cli/commands/skills.py +14 -18
- claude_mpm/cli/executor.py +10 -0
- claude_mpm/cli/parsers/base_parser.py +7 -0
- claude_mpm/cli/parsers/profile_parser.py +147 -0
- claude_mpm/cli/parsers/skills_parser.py +0 -6
- claude_mpm/cli/startup.py +433 -147
- claude_mpm/commands/mpm-config.md +13 -250
- claude_mpm/commands/mpm-doctor.md +9 -22
- claude_mpm/commands/mpm-help.md +5 -206
- claude_mpm/commands/mpm-init.md +81 -507
- claude_mpm/commands/mpm-monitor.md +15 -402
- claude_mpm/commands/mpm-organize.md +61 -441
- claude_mpm/commands/mpm-postmortem.md +6 -108
- claude_mpm/commands/mpm-session-resume.md +12 -363
- claude_mpm/commands/mpm-status.md +5 -69
- claude_mpm/commands/mpm-ticket-view.md +52 -495
- claude_mpm/commands/mpm-version.md +5 -107
- claude_mpm/core/optimized_startup.py +61 -0
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CWc5urbQ.js → 4TdZjIqw.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B0uc0UOD.js +36 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7RN905-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/B7xVLGWV.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BIF9m_hv.js +61 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BPYeabCQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BQaXIfA_.js +331 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uj46x2Wr.js → BSNlmTZj.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Be7GpZd6.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Bh0LDWpI.js +145 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BofRWZRR.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BovzEFCE.js +30 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C30mlcqg.js +165 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4B-KCzX.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C4JcI4KD.js +122 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CBBdVcY8.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CDuw-vjf.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C_Usid8X.js +15 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cfqx1Qun.js +10 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CiIAseT4.js +128 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CmKTTxBW.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CnA0NrzZ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cs_tUR18.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Cu_Erd72.js +261 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CyWMqx4W.js +43 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzZX-COe.js +220 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CzeYkLYB.js +65 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D3k0OPJN.js +4 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/D9lljYKQ.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DGkLK5U1.js +267 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DI7hHRFL.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DLVjFsZ3.js +139 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DUrLdbGD.js +89 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DVp1hx9R.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DY1XQ8fi.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DZX00Y4g.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DaimHw_p.js +68 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +323 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dhb8PKl3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dle-35c7.js +64 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DmxopI1J.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DwBR2MJi.js +60 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/GYwsonyD.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Gi6I4Gst.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DjhvlsAc.js → NqQ1dWOy.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{N4qtv3Hx.js → Vzk33B_K.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/ZGh7QtNv.js +7 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bT1r9zLR.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/bTOqqlTd.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/eNVUfhuA.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/iEWssX7S.js +162 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/sQeU3Y1z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/uuIeMWc-.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.D6-I5TpK.js +2 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.CAGBuiOw.js → 0.m1gL8KXf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.CgNOuw-d.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +10 -10
- claude_mpm/dashboard-svelte/node_modules/katex/src/fonts/generate_fonts.py +58 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_tfms.py +114 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/extract_ttfs.py +122 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/format_json.py +28 -0
- claude_mpm/dashboard-svelte/node_modules/katex/src/metrics/parse_tfm.py +211 -0
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/init.py +276 -0
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/deployment/agent_deployment.py +22 -0
- claude_mpm/services/agents/deployment/agent_discovery_service.py +3 -1
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +37 -17
- claude_mpm/services/agents/deployment/async_agent_deployment.py +31 -27
- claude_mpm/services/agents/deployment/local_template_deployment.py +3 -1
- claude_mpm/services/agents/deployment/multi_source_deployment_service.py +149 -4
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +47 -26
- claude_mpm/services/agents/git_source_manager.py +21 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +116 -5
- claude_mpm/services/monitor/management/lifecycle.py +7 -1
- claude_mpm/services/pm_skills_deployer.py +711 -0
- claude_mpm/services/profile_manager.py +337 -0
- claude_mpm/services/skills/git_skill_source_manager.py +148 -11
- claude_mpm/services/skills/selective_skill_deployer.py +97 -48
- claude_mpm/services/skills_deployer.py +161 -65
- claude_mpm/skills/bundled/collaboration/brainstorming/SKILL.md +79 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/SKILL.md +178 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/agent-prompts.md +577 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/coordination-patterns.md +467 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/examples.md +537 -0
- claude_mpm/skills/bundled/collaboration/dispatching-parallel-agents/references/troubleshooting.md +730 -0
- claude_mpm/skills/bundled/collaboration/git-worktrees.md +317 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/SKILL.md +112 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/code-reviewer-template.md +146 -0
- claude_mpm/skills/bundled/collaboration/requesting-code-review/references/review-examples.md +412 -0
- claude_mpm/skills/bundled/collaboration/stacked-prs.md +251 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/SKILL.md +81 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/best-practices.md +362 -0
- claude_mpm/skills/bundled/collaboration/writing-plans/references/plan-structure-templates.md +312 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/SKILL.md +152 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/advanced-techniques.md +668 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/examples.md +587 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/integration.md +438 -0
- claude_mpm/skills/bundled/debugging/root-cause-tracing/references/tracing-techniques.md +391 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/CREATION-LOG.md +119 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/SKILL.md +148 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/anti-patterns.md +483 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/examples.md +452 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/troubleshooting.md +449 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/references/workflow.md +411 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-academic.md +14 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-1.md +58 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-2.md +68 -0
- claude_mpm/skills/bundled/debugging/systematic-debugging/test-pressure-3.md +69 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/SKILL.md +131 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/gate-function.md +325 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/integration-and-workflows.md +490 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/red-flags-and-failures.md +425 -0
- claude_mpm/skills/bundled/debugging/verification-before-completion/references/verification-patterns.md +499 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/INTEGRATION.md +611 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/README.md +596 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/SKILL.md +260 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/examples/nextjs-env-structure.md +315 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/frameworks.md +436 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/security.md +433 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/synchronization.md +452 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/troubleshooting.md +404 -0
- claude_mpm/skills/bundled/infrastructure/env-manager/references/validation.md +420 -0
- claude_mpm/skills/bundled/main/artifacts-builder/SKILL.md +86 -0
- claude_mpm/skills/bundled/main/internal-comms/SKILL.md +43 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/3p-updates.md +47 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/company-newsletter.md +65 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/faq-answers.md +30 -0
- claude_mpm/skills/bundled/main/internal-comms/examples/general-comms.md +16 -0
- claude_mpm/skills/bundled/main/mcp-builder/SKILL.md +160 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/design_principles.md +412 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/evaluation.md +602 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/mcp_best_practices.md +915 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/node_mcp_server.md +916 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/python_mcp_server.md +752 -0
- claude_mpm/skills/bundled/main/mcp-builder/reference/workflow.md +1237 -0
- claude_mpm/skills/bundled/main/skill-creator/SKILL.md +189 -0
- claude_mpm/skills/bundled/main/skill-creator/references/best-practices.md +500 -0
- claude_mpm/skills/bundled/main/skill-creator/references/creation-workflow.md +464 -0
- claude_mpm/skills/bundled/main/skill-creator/references/examples.md +619 -0
- claude_mpm/skills/bundled/main/skill-creator/references/progressive-disclosure.md +437 -0
- claude_mpm/skills/bundled/main/skill-creator/references/skill-structure.md +231 -0
- claude_mpm/skills/bundled/php/espocrm-development/SKILL.md +170 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/architecture.md +602 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/common-tasks.md +821 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/development-workflow.md +742 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/frontend-customization.md +726 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/hooks-and-services.md +764 -0
- claude_mpm/skills/bundled/php/espocrm-development/references/testing-debugging.md +831 -0
- claude_mpm/skills/bundled/pm/pm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/pm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/pm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/pm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/pm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/react/flexlayout-react.md +742 -0
- claude_mpm/skills/bundled/rust/desktop-applications/SKILL.md +226 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/architecture-patterns.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/native-gui-frameworks.md +901 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/platform-integration.md +775 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/state-management.md +937 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/tauri-framework.md +770 -0
- claude_mpm/skills/bundled/rust/desktop-applications/references/testing-deployment.md +961 -0
- claude_mpm/skills/bundled/security-scanning.md +112 -0
- claude_mpm/skills/bundled/tauri/tauri-async-patterns.md +495 -0
- claude_mpm/skills/bundled/tauri/tauri-build-deploy.md +599 -0
- claude_mpm/skills/bundled/tauri/tauri-command-patterns.md +535 -0
- claude_mpm/skills/bundled/tauri/tauri-error-handling.md +613 -0
- claude_mpm/skills/bundled/tauri/tauri-event-system.md +648 -0
- claude_mpm/skills/bundled/tauri/tauri-file-system.md +673 -0
- claude_mpm/skills/bundled/tauri/tauri-frontend-integration.md +767 -0
- claude_mpm/skills/bundled/tauri/tauri-performance.md +669 -0
- claude_mpm/skills/bundled/tauri/tauri-state-management.md +573 -0
- claude_mpm/skills/bundled/tauri/tauri-testing.md +384 -0
- claude_mpm/skills/bundled/tauri/tauri-window-management.md +628 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/SKILL.md +119 -0
- claude_mpm/skills/bundled/testing/condition-based-waiting/references/patterns-and-implementation.md +253 -0
- claude_mpm/skills/bundled/testing/test-driven-development/SKILL.md +145 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/anti-patterns.md +543 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/examples.md +741 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/integration.md +470 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/philosophy.md +458 -0
- claude_mpm/skills/bundled/testing/test-driven-development/references/workflow.md +639 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/SKILL.md +458 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/examples/example-inspection-report.md +411 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/assertion-quality.md +317 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/inspection-checklist.md +270 -0
- claude_mpm/skills/bundled/testing/test-quality-inspector/references/red-flags.md +436 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/SKILL.md +140 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/completeness-anti-patterns.md +572 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/core-anti-patterns.md +411 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/detection-guide.md +569 -0
- claude_mpm/skills/bundled/testing/testing-anti-patterns/references/tdd-connection.md +695 -0
- claude_mpm/skills/bundled/testing/webapp-testing/SKILL.md +184 -0
- claude_mpm/skills/bundled/testing/webapp-testing/decision-tree.md +459 -0
- claude_mpm/skills/bundled/testing/webapp-testing/playwright-patterns.md +479 -0
- claude_mpm/skills/bundled/testing/webapp-testing/reconnaissance-pattern.md +687 -0
- claude_mpm/skills/bundled/testing/webapp-testing/server-management.md +758 -0
- claude_mpm/skills/bundled/testing/webapp-testing/troubleshooting.md +868 -0
- claude_mpm/skills/skill_manager.py +98 -3
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/METADATA +3 -2
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/RECORD +244 -68
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.B_FtCwCQ.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.Cl_eSA4x.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BgChzWQ1.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CIXEwuWe.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/DMkZpdF2.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/app.DTL5mJO-.js +0 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.DzuEhzqh.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/1.DFLC8jdE.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.DPvEihJJ.js +0 -10
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.36.dist-info → claude_mpm-5.4.62.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Profile Manager Service
|
|
3
|
+
======================
|
|
4
|
+
|
|
5
|
+
Manages agent and skill filtering based on deployment profiles.
|
|
6
|
+
|
|
7
|
+
A profile defines which agents and skills should be deployed, reducing
|
|
8
|
+
context usage by limiting available agents to only what's needed for
|
|
9
|
+
a specific project or workflow.
|
|
10
|
+
|
|
11
|
+
Profile Structure:
|
|
12
|
+
profile:
|
|
13
|
+
name: framework-development
|
|
14
|
+
description: Python backend + TypeScript/Svelte dashboard
|
|
15
|
+
|
|
16
|
+
agents:
|
|
17
|
+
enabled:
|
|
18
|
+
- python-engineer
|
|
19
|
+
- typescript-engineer
|
|
20
|
+
disabled:
|
|
21
|
+
- java-engineer
|
|
22
|
+
- dart-engineer
|
|
23
|
+
|
|
24
|
+
skills:
|
|
25
|
+
enabled:
|
|
26
|
+
- flask
|
|
27
|
+
- pytest
|
|
28
|
+
disabled_categories:
|
|
29
|
+
- wordpress-*
|
|
30
|
+
- react-*
|
|
31
|
+
|
|
32
|
+
Usage:
|
|
33
|
+
# Auto-detect project directory (searches for .claude-mpm in cwd and parents)
|
|
34
|
+
profile_manager = ProfileManager()
|
|
35
|
+
|
|
36
|
+
# Or explicitly specify project directory
|
|
37
|
+
profile_manager = ProfileManager(project_dir=Path("/path/to/project"))
|
|
38
|
+
|
|
39
|
+
profile_manager.load_profile("framework-development")
|
|
40
|
+
|
|
41
|
+
if profile_manager.is_agent_enabled("python-engineer"):
|
|
42
|
+
# Deploy agent
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
if profile_manager.is_skill_enabled("flask"):
|
|
46
|
+
# Deploy skill
|
|
47
|
+
pass
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
import fnmatch
|
|
51
|
+
from pathlib import Path
|
|
52
|
+
from typing import Any, Dict, Optional, Set
|
|
53
|
+
|
|
54
|
+
import yaml
|
|
55
|
+
|
|
56
|
+
from ..core.logger import get_logger
|
|
57
|
+
|
|
58
|
+
logger = get_logger(__name__)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
class ProfileManager:
|
|
62
|
+
"""
|
|
63
|
+
Manages deployment profiles for agent and skill filtering.
|
|
64
|
+
|
|
65
|
+
Provides methods to:
|
|
66
|
+
- Load profiles from YAML files
|
|
67
|
+
- Check if agents are enabled/disabled
|
|
68
|
+
- Check if skills are enabled/disabled (with glob pattern support)
|
|
69
|
+
- Get lists of enabled/disabled entities
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
def __init__(
|
|
73
|
+
self, project_dir: Optional[Path] = None, profiles_dir: Optional[Path] = None
|
|
74
|
+
):
|
|
75
|
+
"""
|
|
76
|
+
Initialize ProfileManager.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
project_dir: Project root directory. If not provided, tries to find
|
|
80
|
+
.claude-mpm directory in current or parent directories.
|
|
81
|
+
profiles_dir: Directory containing profile YAML files. If provided,
|
|
82
|
+
takes precedence over project_dir.
|
|
83
|
+
"""
|
|
84
|
+
if profiles_dir:
|
|
85
|
+
self.profiles_dir = profiles_dir
|
|
86
|
+
elif project_dir:
|
|
87
|
+
self.profiles_dir = Path(project_dir) / ".claude-mpm" / "profiles"
|
|
88
|
+
else:
|
|
89
|
+
# Try to find .claude-mpm directory automatically
|
|
90
|
+
self.profiles_dir = self._find_profiles_dir()
|
|
91
|
+
|
|
92
|
+
self.active_profile: Optional[str] = None
|
|
93
|
+
self._profile_data: Dict[str, Any] = {}
|
|
94
|
+
|
|
95
|
+
# Cached sets for performance
|
|
96
|
+
self._enabled_agents: Set[str] = set()
|
|
97
|
+
self._disabled_agents: Set[str] = set()
|
|
98
|
+
self._enabled_skills: Set[str] = set()
|
|
99
|
+
self._disabled_skill_patterns: list[str] = []
|
|
100
|
+
|
|
101
|
+
def _find_profiles_dir(self) -> Path:
|
|
102
|
+
"""Find profiles directory by searching for .claude-mpm in cwd and parents.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Path to profiles directory (may not exist yet)
|
|
106
|
+
"""
|
|
107
|
+
current = Path.cwd()
|
|
108
|
+
|
|
109
|
+
# Search current directory and up to 5 parent directories
|
|
110
|
+
for _ in range(6):
|
|
111
|
+
profiles_dir = current / ".claude-mpm" / "profiles"
|
|
112
|
+
if profiles_dir.exists():
|
|
113
|
+
logger.debug(f"Found profiles directory at: {profiles_dir}")
|
|
114
|
+
return profiles_dir
|
|
115
|
+
if current.parent == current: # Reached filesystem root
|
|
116
|
+
break
|
|
117
|
+
current = current.parent
|
|
118
|
+
|
|
119
|
+
# Fallback to cwd (directory may not exist yet, which is fine)
|
|
120
|
+
fallback = Path.cwd() / ".claude-mpm" / "profiles"
|
|
121
|
+
logger.debug(f"Profiles directory not found, using fallback: {fallback}")
|
|
122
|
+
return fallback
|
|
123
|
+
|
|
124
|
+
def load_profile(self, profile_name: str) -> bool:
|
|
125
|
+
"""
|
|
126
|
+
Load profile from YAML file.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
profile_name: Name of profile (without .yaml extension)
|
|
130
|
+
|
|
131
|
+
Returns:
|
|
132
|
+
bool: True if profile loaded successfully, False otherwise
|
|
133
|
+
"""
|
|
134
|
+
profile_path = self.profiles_dir / f"{profile_name}.yaml"
|
|
135
|
+
|
|
136
|
+
logger.debug(f"Looking for profile at: {profile_path}")
|
|
137
|
+
|
|
138
|
+
if not profile_path.exists():
|
|
139
|
+
logger.warning(f"Profile not found: {profile_path}")
|
|
140
|
+
return False
|
|
141
|
+
|
|
142
|
+
try:
|
|
143
|
+
with profile_path.open("r") as f:
|
|
144
|
+
self._profile_data = yaml.safe_load(f) or {}
|
|
145
|
+
|
|
146
|
+
# Extract profile metadata
|
|
147
|
+
profile_info = self._profile_data.get("profile", {})
|
|
148
|
+
self.active_profile = profile_info.get("name", profile_name)
|
|
149
|
+
|
|
150
|
+
# Parse agents
|
|
151
|
+
agents_config = self._profile_data.get("agents", {})
|
|
152
|
+
self._enabled_agents = set(agents_config.get("enabled", []))
|
|
153
|
+
self._disabled_agents = set(agents_config.get("disabled", []))
|
|
154
|
+
|
|
155
|
+
# Parse skills
|
|
156
|
+
skills_config = self._profile_data.get("skills", {})
|
|
157
|
+
self._enabled_skills = set(skills_config.get("enabled", []))
|
|
158
|
+
self._disabled_skill_patterns = skills_config.get("disabled_categories", [])
|
|
159
|
+
|
|
160
|
+
logger.info(
|
|
161
|
+
f"Loaded profile '{self.active_profile}': "
|
|
162
|
+
f"{len(self._enabled_agents)} agents, "
|
|
163
|
+
f"{len(self._enabled_skills)} skills enabled"
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
return True
|
|
167
|
+
|
|
168
|
+
except Exception as e:
|
|
169
|
+
logger.error(f"Failed to load profile {profile_name}: {e}")
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
def is_agent_enabled(self, agent_name: str) -> bool:
|
|
173
|
+
"""
|
|
174
|
+
Check if agent is enabled in active profile.
|
|
175
|
+
|
|
176
|
+
If no profile is loaded, all agents are enabled by default.
|
|
177
|
+
|
|
178
|
+
Args:
|
|
179
|
+
agent_name: Name of agent to check
|
|
180
|
+
|
|
181
|
+
Returns:
|
|
182
|
+
bool: True if agent should be deployed
|
|
183
|
+
"""
|
|
184
|
+
if not self.active_profile:
|
|
185
|
+
# No profile active - all agents enabled
|
|
186
|
+
return True
|
|
187
|
+
|
|
188
|
+
# If enabled list exists, agent must be in it
|
|
189
|
+
if self._enabled_agents:
|
|
190
|
+
return agent_name in self._enabled_agents
|
|
191
|
+
|
|
192
|
+
# Otherwise, agent must NOT be in disabled list
|
|
193
|
+
return agent_name not in self._disabled_agents
|
|
194
|
+
|
|
195
|
+
def is_skill_enabled(self, skill_name: str) -> bool:
|
|
196
|
+
"""
|
|
197
|
+
Check if skill is enabled in active profile.
|
|
198
|
+
|
|
199
|
+
Supports both short names (flask) and full names (toolchains-python-frameworks-flask).
|
|
200
|
+
Supports glob pattern matching for disabled_categories.
|
|
201
|
+
|
|
202
|
+
If no profile is loaded, all skills are enabled by default.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
skill_name: Name of skill to check (e.g., "flask", "toolchains-python-frameworks-flask")
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
bool: True if skill should be deployed
|
|
209
|
+
"""
|
|
210
|
+
if not self.active_profile:
|
|
211
|
+
# No profile active - all skills enabled
|
|
212
|
+
return True
|
|
213
|
+
|
|
214
|
+
# Check if skill is explicitly disabled by pattern
|
|
215
|
+
for pattern in self._disabled_skill_patterns:
|
|
216
|
+
if fnmatch.fnmatch(skill_name, pattern):
|
|
217
|
+
logger.debug(
|
|
218
|
+
f"Skill '{skill_name}' matched disabled pattern '{pattern}'"
|
|
219
|
+
)
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
# If enabled list exists, check for match
|
|
223
|
+
if self._enabled_skills:
|
|
224
|
+
# Exact match
|
|
225
|
+
if skill_name in self._enabled_skills:
|
|
226
|
+
return True
|
|
227
|
+
|
|
228
|
+
# Check if full skill name ends with short name from enabled list
|
|
229
|
+
# Example: "toolchains-python-frameworks-flask" matches "flask"
|
|
230
|
+
for short_name in self._enabled_skills:
|
|
231
|
+
if skill_name.endswith(f"-{short_name}"):
|
|
232
|
+
return True
|
|
233
|
+
# Also check if short name is contained as a segment
|
|
234
|
+
if f"-{short_name}-" in skill_name or skill_name.startswith(
|
|
235
|
+
f"{short_name}-"
|
|
236
|
+
):
|
|
237
|
+
return True
|
|
238
|
+
|
|
239
|
+
return False
|
|
240
|
+
|
|
241
|
+
# No enabled list and didn't match disabled pattern - allow it
|
|
242
|
+
return True
|
|
243
|
+
|
|
244
|
+
def get_enabled_agents(self) -> Set[str]:
|
|
245
|
+
"""
|
|
246
|
+
Get set of enabled agent names.
|
|
247
|
+
|
|
248
|
+
Returns:
|
|
249
|
+
Set[str]: Agent names that should be deployed
|
|
250
|
+
"""
|
|
251
|
+
return self._enabled_agents.copy()
|
|
252
|
+
|
|
253
|
+
def get_disabled_agents(self) -> Set[str]:
|
|
254
|
+
"""
|
|
255
|
+
Get set of disabled agent names.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Set[str]: Agent names that should NOT be deployed
|
|
259
|
+
"""
|
|
260
|
+
return self._disabled_agents.copy()
|
|
261
|
+
|
|
262
|
+
def get_enabled_skills(self) -> Set[str]:
|
|
263
|
+
"""
|
|
264
|
+
Get set of explicitly enabled skill names.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Set[str]: Skill names that should be deployed
|
|
268
|
+
"""
|
|
269
|
+
return self._enabled_skills.copy()
|
|
270
|
+
|
|
271
|
+
def get_disabled_skill_patterns(self) -> list[str]:
|
|
272
|
+
"""
|
|
273
|
+
Get list of disabled skill glob patterns.
|
|
274
|
+
|
|
275
|
+
Returns:
|
|
276
|
+
list[str]: Glob patterns for skills that should NOT be deployed
|
|
277
|
+
"""
|
|
278
|
+
return self._disabled_skill_patterns.copy()
|
|
279
|
+
|
|
280
|
+
def get_filtering_summary(self) -> Dict[str, Any]:
|
|
281
|
+
"""
|
|
282
|
+
Get summary of current profile filtering.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
Dict containing:
|
|
286
|
+
- active_profile: Name of active profile (or None)
|
|
287
|
+
- enabled_agents_count: Number of explicitly enabled agents
|
|
288
|
+
- disabled_agents_count: Number of explicitly disabled agents
|
|
289
|
+
- enabled_skills_count: Number of explicitly enabled skills
|
|
290
|
+
- disabled_patterns_count: Number of disabled skill patterns
|
|
291
|
+
"""
|
|
292
|
+
return {
|
|
293
|
+
"active_profile": self.active_profile,
|
|
294
|
+
"enabled_agents_count": len(self._enabled_agents),
|
|
295
|
+
"disabled_agents_count": len(self._disabled_agents),
|
|
296
|
+
"enabled_skills_count": len(self._enabled_skills),
|
|
297
|
+
"disabled_patterns_count": len(self._disabled_skill_patterns),
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
def list_available_profiles(self) -> list[str]:
|
|
301
|
+
"""
|
|
302
|
+
List all available profile names in profiles directory.
|
|
303
|
+
|
|
304
|
+
Returns:
|
|
305
|
+
list[str]: Profile names (without .yaml extension)
|
|
306
|
+
"""
|
|
307
|
+
if not self.profiles_dir.exists():
|
|
308
|
+
return []
|
|
309
|
+
|
|
310
|
+
profiles = []
|
|
311
|
+
for profile_path in self.profiles_dir.glob("*.yaml"):
|
|
312
|
+
profiles.append(profile_path.stem)
|
|
313
|
+
|
|
314
|
+
return sorted(profiles)
|
|
315
|
+
|
|
316
|
+
def get_profile_description(self, profile_name: str) -> Optional[str]:
|
|
317
|
+
"""
|
|
318
|
+
Get description of a profile without loading it fully.
|
|
319
|
+
|
|
320
|
+
Args:
|
|
321
|
+
profile_name: Name of profile
|
|
322
|
+
|
|
323
|
+
Returns:
|
|
324
|
+
Optional[str]: Profile description or None if not found
|
|
325
|
+
"""
|
|
326
|
+
profile_path = self.profiles_dir / f"{profile_name}.yaml"
|
|
327
|
+
|
|
328
|
+
if not profile_path.exists():
|
|
329
|
+
return None
|
|
330
|
+
|
|
331
|
+
try:
|
|
332
|
+
with profile_path.open("r") as f:
|
|
333
|
+
data = yaml.safe_load(f) or {}
|
|
334
|
+
profile_info = data.get("profile", {})
|
|
335
|
+
return profile_info.get("description")
|
|
336
|
+
except Exception:
|
|
337
|
+
return None
|
|
@@ -991,12 +991,17 @@ class GitSkillSourceManager:
|
|
|
991
991
|
progress_callback=None,
|
|
992
992
|
skill_filter: Optional[Set[str]] = None,
|
|
993
993
|
) -> Dict[str, Any]:
|
|
994
|
-
"""Deploy skills from cache to target directory with flat structure.
|
|
994
|
+
"""Deploy skills from cache to target directory with flat structure and automatic cleanup.
|
|
995
995
|
|
|
996
996
|
Flattens nested Git repository structure into Claude Code compatible
|
|
997
997
|
flat directory structure. Each skill directory is copied with a
|
|
998
998
|
hyphen-separated name derived from its path.
|
|
999
999
|
|
|
1000
|
+
CRITICAL: When skill_filter is provided (agent-referenced skills), this function:
|
|
1001
|
+
1. Deploys ONLY the filtered skills
|
|
1002
|
+
2. REMOVES orphaned skills (deployed but not in filter)
|
|
1003
|
+
3. Returns removed_count and removed_skills in result
|
|
1004
|
+
|
|
1000
1005
|
Transformation Example:
|
|
1001
1006
|
Cache: collaboration/dispatching-parallel-agents/SKILL.md
|
|
1002
1007
|
Deploy: collaboration-dispatching-parallel-agents/SKILL.md
|
|
@@ -1006,8 +1011,8 @@ class GitSkillSourceManager:
|
|
|
1006
1011
|
force: Overwrite existing skills
|
|
1007
1012
|
progress_callback: Optional callback(increment: int) called for each skill deployed
|
|
1008
1013
|
skill_filter: Optional set of skill names to deploy (selective deployment).
|
|
1009
|
-
If None, deploys
|
|
1010
|
-
|
|
1014
|
+
If None, deploys ALL skills WITHOUT cleanup.
|
|
1015
|
+
If provided, deploys ONLY filtered skills AND removes orphans.
|
|
1011
1016
|
|
|
1012
1017
|
Returns:
|
|
1013
1018
|
Dict with deployment results:
|
|
@@ -1018,7 +1023,9 @@ class GitSkillSourceManager:
|
|
|
1018
1023
|
"deployed_skills": List[str],
|
|
1019
1024
|
"skipped_skills": List[str],
|
|
1020
1025
|
"errors": List[str],
|
|
1021
|
-
"filtered_count": int # Number of skills filtered out
|
|
1026
|
+
"filtered_count": int, # Number of skills filtered out
|
|
1027
|
+
"removed_count": int, # Number of orphaned skills removed
|
|
1028
|
+
"removed_skills": List[str] # Names of removed orphaned skills
|
|
1022
1029
|
}
|
|
1023
1030
|
|
|
1024
1031
|
Example:
|
|
@@ -1026,10 +1033,10 @@ class GitSkillSourceManager:
|
|
|
1026
1033
|
>>> result = manager.deploy_skills()
|
|
1027
1034
|
>>> print(f"Deployed {result['deployed_count']} skills")
|
|
1028
1035
|
|
|
1029
|
-
# Selective deployment based on agent requirements:
|
|
1036
|
+
# Selective deployment based on agent requirements (with cleanup):
|
|
1030
1037
|
>>> required = {"typescript-core", "react-patterns"}
|
|
1031
1038
|
>>> result = manager.deploy_skills(skill_filter=required)
|
|
1032
|
-
>>> print(f"Deployed {result['deployed_count']}
|
|
1039
|
+
>>> print(f"Deployed {result['deployed_count']}, removed {result['removed_count']} orphans")
|
|
1033
1040
|
"""
|
|
1034
1041
|
if target_dir is None:
|
|
1035
1042
|
target_dir = Path.home() / ".claude" / "skills"
|
|
@@ -1040,6 +1047,7 @@ class GitSkillSourceManager:
|
|
|
1040
1047
|
skipped = []
|
|
1041
1048
|
errors = []
|
|
1042
1049
|
filtered_count = 0
|
|
1050
|
+
removed_skills = [] # Track removed orphaned skills
|
|
1043
1051
|
|
|
1044
1052
|
# Get all skills from all sources
|
|
1045
1053
|
all_skills = self.get_all_skills()
|
|
@@ -1049,12 +1057,31 @@ class GitSkillSourceManager:
|
|
|
1049
1057
|
original_count = len(all_skills)
|
|
1050
1058
|
# Normalize filter to lowercase for case-insensitive matching
|
|
1051
1059
|
normalized_filter = {s.lower() for s in skill_filter}
|
|
1052
|
-
|
|
1053
|
-
|
|
1060
|
+
|
|
1061
|
+
def matches_filter(deployment_name: str) -> bool:
|
|
1062
|
+
"""Match using same fuzzy logic as ProfileManager.is_skill_enabled()"""
|
|
1063
|
+
deployment_lower = deployment_name.lower()
|
|
1064
|
+
|
|
1065
|
+
# Exact match
|
|
1066
|
+
if deployment_lower in normalized_filter:
|
|
1067
|
+
return True
|
|
1068
|
+
|
|
1069
|
+
# Fuzzy match: check if deployment name ends with or contains short name
|
|
1070
|
+
# Example: "toolchains-python-frameworks-flask" matches "flask"
|
|
1071
|
+
for short_name in normalized_filter:
|
|
1072
|
+
if deployment_lower.endswith(f"-{short_name}"):
|
|
1073
|
+
return True
|
|
1074
|
+
# Check if short name is contained as a segment
|
|
1075
|
+
if f"-{short_name}-" in deployment_lower:
|
|
1076
|
+
return True
|
|
1077
|
+
if deployment_lower.startswith(f"{short_name}-"):
|
|
1078
|
+
return True
|
|
1079
|
+
|
|
1080
|
+
return False
|
|
1081
|
+
|
|
1082
|
+
# Match against deployment_name using fuzzy matching
|
|
1054
1083
|
all_skills = [
|
|
1055
|
-
s
|
|
1056
|
-
for s in all_skills
|
|
1057
|
-
if s.get("deployment_name", "").lower() in normalized_filter
|
|
1084
|
+
s for s in all_skills if matches_filter(s.get("deployment_name", ""))
|
|
1058
1085
|
]
|
|
1059
1086
|
filtered_count = original_count - len(all_skills)
|
|
1060
1087
|
self.logger.info(
|
|
@@ -1062,6 +1089,15 @@ class GitSkillSourceManager:
|
|
|
1062
1089
|
f"match agent requirements ({filtered_count} filtered out)"
|
|
1063
1090
|
)
|
|
1064
1091
|
|
|
1092
|
+
# Cleanup: Remove skills from target directory that aren't in the filtered set
|
|
1093
|
+
# This ensures only agent-referenced skills remain deployed
|
|
1094
|
+
removed_skills = self._cleanup_unfiltered_skills(target_dir, all_skills)
|
|
1095
|
+
if removed_skills:
|
|
1096
|
+
self.logger.info(
|
|
1097
|
+
f"Removed {len(removed_skills)} orphaned skills not referenced by agents: {removed_skills[:10]}"
|
|
1098
|
+
+ (f" (and {len(removed_skills) - 10} more)" if len(removed_skills) > 10 else "")
|
|
1099
|
+
)
|
|
1100
|
+
|
|
1065
1101
|
self.logger.info(
|
|
1066
1102
|
f"Deploying {len(all_skills)} skills to {target_dir} (force={force})"
|
|
1067
1103
|
)
|
|
@@ -1103,6 +1139,7 @@ class GitSkillSourceManager:
|
|
|
1103
1139
|
self.logger.info(
|
|
1104
1140
|
f"Deployment complete: {len(deployed)} deployed, "
|
|
1105
1141
|
f"{len(skipped)} skipped, {len(errors)} errors"
|
|
1142
|
+
+ (f", {len(removed_skills)} removed" if removed_skills else "")
|
|
1106
1143
|
)
|
|
1107
1144
|
|
|
1108
1145
|
return {
|
|
@@ -1113,8 +1150,108 @@ class GitSkillSourceManager:
|
|
|
1113
1150
|
"skipped_skills": skipped,
|
|
1114
1151
|
"errors": errors,
|
|
1115
1152
|
"filtered_count": filtered_count,
|
|
1153
|
+
"removed_count": len(removed_skills),
|
|
1154
|
+
"removed_skills": removed_skills,
|
|
1116
1155
|
}
|
|
1117
1156
|
|
|
1157
|
+
def _cleanup_unfiltered_skills(
|
|
1158
|
+
self, target_dir: Path, filtered_skills: List[Dict[str, Any]]
|
|
1159
|
+
) -> List[str]:
|
|
1160
|
+
"""Remove skills from target directory that aren't in the filtered skill list.
|
|
1161
|
+
|
|
1162
|
+
Uses fuzzy matching to handle both exact deployment names and short skill names.
|
|
1163
|
+
For example:
|
|
1164
|
+
- "toolchains-python-frameworks-flask" (deployed dir) matches "flask" (filter)
|
|
1165
|
+
- "toolchains-elixir-frameworks-phoenix-liveview" matches "phoenix-liveview"
|
|
1166
|
+
|
|
1167
|
+
Args:
|
|
1168
|
+
target_dir: Target deployment directory
|
|
1169
|
+
filtered_skills: List of skills that should remain deployed
|
|
1170
|
+
|
|
1171
|
+
Returns:
|
|
1172
|
+
List of skill names that were removed
|
|
1173
|
+
"""
|
|
1174
|
+
import shutil
|
|
1175
|
+
|
|
1176
|
+
removed_skills = []
|
|
1177
|
+
|
|
1178
|
+
# Build set of deployment names (exact matches)
|
|
1179
|
+
expected_deployments = {
|
|
1180
|
+
skill.get("deployment_name").lower()
|
|
1181
|
+
for skill in filtered_skills
|
|
1182
|
+
if skill.get("deployment_name")
|
|
1183
|
+
}
|
|
1184
|
+
|
|
1185
|
+
# Build helper function for fuzzy matching (matches logic from deploy_skills)
|
|
1186
|
+
def should_keep_skill(deployed_dir_name: str) -> bool:
|
|
1187
|
+
"""Check if deployed skill matches any expected deployment using fuzzy matching.
|
|
1188
|
+
|
|
1189
|
+
Matches the same logic as matches_filter() in deploy_skills() at line 1053.
|
|
1190
|
+
"""
|
|
1191
|
+
deployed_lower = deployed_dir_name.lower()
|
|
1192
|
+
|
|
1193
|
+
# Exact match
|
|
1194
|
+
if deployed_lower in expected_deployments:
|
|
1195
|
+
return True
|
|
1196
|
+
|
|
1197
|
+
# Fuzzy match: check if deployment name matches any short name pattern
|
|
1198
|
+
# Example: "toolchains-elixir-frameworks-phoenix-liveview" matches "phoenix-liveview"
|
|
1199
|
+
for expected_name in expected_deployments:
|
|
1200
|
+
# Suffix match: deployment ends with "-shortname"
|
|
1201
|
+
if deployed_lower.endswith(f"-{expected_name}"):
|
|
1202
|
+
return True
|
|
1203
|
+
# Segment match: "-shortname-" appears in deployment
|
|
1204
|
+
if f"-{expected_name}-" in deployed_lower:
|
|
1205
|
+
return True
|
|
1206
|
+
# Prefix match: deployment starts with "shortname-"
|
|
1207
|
+
if deployed_lower.startswith(f"{expected_name}-"):
|
|
1208
|
+
return True
|
|
1209
|
+
|
|
1210
|
+
return False
|
|
1211
|
+
|
|
1212
|
+
# Check each directory in target_dir
|
|
1213
|
+
if not target_dir.exists():
|
|
1214
|
+
return removed_skills
|
|
1215
|
+
|
|
1216
|
+
try:
|
|
1217
|
+
for item in target_dir.iterdir():
|
|
1218
|
+
# Skip files, only process directories
|
|
1219
|
+
if not item.is_dir():
|
|
1220
|
+
continue
|
|
1221
|
+
|
|
1222
|
+
# Skip hidden directories
|
|
1223
|
+
if item.name.startswith("."):
|
|
1224
|
+
continue
|
|
1225
|
+
|
|
1226
|
+
# Check if this skill directory should be kept (fuzzy matching)
|
|
1227
|
+
if not should_keep_skill(item.name):
|
|
1228
|
+
try:
|
|
1229
|
+
# Security: Validate path is within target_dir
|
|
1230
|
+
if not self._validate_safe_path(target_dir, item):
|
|
1231
|
+
self.logger.error(
|
|
1232
|
+
f"Refusing to remove path outside target directory: {item}"
|
|
1233
|
+
)
|
|
1234
|
+
continue
|
|
1235
|
+
|
|
1236
|
+
# Remove the skill directory
|
|
1237
|
+
if item.is_symlink():
|
|
1238
|
+
item.unlink()
|
|
1239
|
+
else:
|
|
1240
|
+
shutil.rmtree(item)
|
|
1241
|
+
|
|
1242
|
+
removed_skills.append(item.name)
|
|
1243
|
+
self.logger.info(f"Removed orphaned skill: {item.name}")
|
|
1244
|
+
|
|
1245
|
+
except Exception as e:
|
|
1246
|
+
self.logger.warning(
|
|
1247
|
+
f"Failed to remove skill directory {item.name}: {e}"
|
|
1248
|
+
)
|
|
1249
|
+
|
|
1250
|
+
except Exception as e:
|
|
1251
|
+
self.logger.error(f"Error during skill cleanup: {e}")
|
|
1252
|
+
|
|
1253
|
+
return removed_skills
|
|
1254
|
+
|
|
1118
1255
|
def _deploy_single_skill(
|
|
1119
1256
|
self, skill: Dict[str, Any], target_dir: Path, deployment_name: str, force: bool
|
|
1120
1257
|
) -> Dict[str, Any]:
|