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,122 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
from fontTools.ttLib import TTFont
|
|
4
|
+
import sys
|
|
5
|
+
import json
|
|
6
|
+
|
|
7
|
+
# map of characters to extract
|
|
8
|
+
metrics_to_extract = {
|
|
9
|
+
# Font name
|
|
10
|
+
"AMS-Regular": {
|
|
11
|
+
u"\u21e2": None, # \dashrightarrow
|
|
12
|
+
u"\u21e0": None, # \dashleftarrow
|
|
13
|
+
},
|
|
14
|
+
"Main-Regular": {
|
|
15
|
+
# Skew and italic metrics can't be easily parsed from the TTF. Instead,
|
|
16
|
+
# we map each character to a "base character", which is a character
|
|
17
|
+
# from the same font with correct italic and skew metrics. A character
|
|
18
|
+
# maps to None if it doesn't have a base.
|
|
19
|
+
|
|
20
|
+
#u"\u2209": None, # \notin
|
|
21
|
+
#u"\u2260": None, # \neq
|
|
22
|
+
u"\u2245": None, # \cong
|
|
23
|
+
u"\u2026": None, # \ldots
|
|
24
|
+
u"\u22ef": None, # \cdots
|
|
25
|
+
u"\u22f1": None, # \ddots
|
|
26
|
+
u"\u22ee": None, # \vdots
|
|
27
|
+
u"\u22ee": None, # \vdots
|
|
28
|
+
u"\u22a8": None, # \models
|
|
29
|
+
u"\u22c8": None, # \bowtie
|
|
30
|
+
u"\u2250": None, # \doteq
|
|
31
|
+
u"\u23b0": None, # \lmoustache
|
|
32
|
+
u"\u23b1": None, # \rmoustache
|
|
33
|
+
u"\u27ee": None, # \lgroup
|
|
34
|
+
u"\u27ef": None, # \rgroup
|
|
35
|
+
u"\u27f5": None, # \longleftarrow
|
|
36
|
+
u"\u27f8": None, # \Longleftarrow
|
|
37
|
+
u"\u27f6": None, # \longrightarrow
|
|
38
|
+
u"\u27f9": None, # \Longrightarrow
|
|
39
|
+
u"\u27f7": None, # \longleftrightarrow
|
|
40
|
+
u"\u27fa": None, # \Longleftrightarrow
|
|
41
|
+
u"\u21a6": None, # \mapsto
|
|
42
|
+
u"\u27fc": None, # \longmapsto
|
|
43
|
+
u"\u21a9": None, # \hookleftarrow
|
|
44
|
+
u"\u21aa": None, # \hookrightarrow
|
|
45
|
+
u"\u21cc": None, # \rightleftharpoons
|
|
46
|
+
},
|
|
47
|
+
"Main-Bold": {
|
|
48
|
+
u"\u2245": None, # \cong
|
|
49
|
+
},
|
|
50
|
+
"Size1-Regular": {
|
|
51
|
+
u"\u222c": u"\u222b", # \iint, based on \int
|
|
52
|
+
u"\u222d": u"\u222b", # \iiint, based on \int
|
|
53
|
+
},
|
|
54
|
+
"Size2-Regular": {
|
|
55
|
+
u"\u222c": u"\u222b", # \iint, based on \int
|
|
56
|
+
u"\u222d": u"\u222b", # \iiint, based on \int
|
|
57
|
+
},
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main():
|
|
62
|
+
start_json = json.load(sys.stdin)
|
|
63
|
+
|
|
64
|
+
for font in start_json:
|
|
65
|
+
fontInfo = TTFont("../../fonts/KaTeX_" + font + ".ttf")
|
|
66
|
+
glyf = fontInfo["glyf"]
|
|
67
|
+
widths = fontInfo.getGlyphSet()
|
|
68
|
+
unitsPerEm = float(fontInfo["head"].unitsPerEm)
|
|
69
|
+
|
|
70
|
+
# We keep ALL Unicode cmaps, not just fontInfo["cmap"].getcmap(3, 1).
|
|
71
|
+
# This is playing it extra safe, since it reports inconsistencies.
|
|
72
|
+
# Platform 0 is Unicode, platform 3 is Windows. For platform 3,
|
|
73
|
+
# encoding 1 is UCS-2 and encoding 10 is UCS-4.
|
|
74
|
+
cmap = [t.cmap for t in fontInfo["cmap"].tables
|
|
75
|
+
if (t.platformID == 0)
|
|
76
|
+
or (t.platformID == 3 and t.platEncID in (1, 10))]
|
|
77
|
+
|
|
78
|
+
chars = metrics_to_extract.get(font, {})
|
|
79
|
+
chars[u"\u0020"] = None # space
|
|
80
|
+
chars[u"\u00a0"] = None # nbsp
|
|
81
|
+
|
|
82
|
+
for char, base_char in chars.items():
|
|
83
|
+
code = ord(char)
|
|
84
|
+
names = set(t.get(code) for t in cmap)
|
|
85
|
+
if not names:
|
|
86
|
+
sys.stderr.write(
|
|
87
|
+
"Codepoint {} of font {} maps to no name\n"
|
|
88
|
+
.format(code, font))
|
|
89
|
+
continue
|
|
90
|
+
if len(names) != 1:
|
|
91
|
+
sys.stderr.write(
|
|
92
|
+
"Codepoint {} of font {} maps to multiple names: {}\n"
|
|
93
|
+
.format(code, font, ", ".join(sorted(names))))
|
|
94
|
+
continue
|
|
95
|
+
name = names.pop()
|
|
96
|
+
|
|
97
|
+
height = depth = italic = skew = width = 0
|
|
98
|
+
glyph = glyf[name]
|
|
99
|
+
if glyph.numberOfContours:
|
|
100
|
+
height = glyph.yMax / unitsPerEm
|
|
101
|
+
depth = -glyph.yMin / unitsPerEm
|
|
102
|
+
width = widths[name].width / unitsPerEm
|
|
103
|
+
if base_char:
|
|
104
|
+
base_char_str = str(ord(base_char))
|
|
105
|
+
base_metrics = start_json[font][base_char_str]
|
|
106
|
+
italic = base_metrics["italic"]
|
|
107
|
+
skew = base_metrics["skew"]
|
|
108
|
+
width = base_metrics["width"]
|
|
109
|
+
|
|
110
|
+
start_json[font][str(code)] = {
|
|
111
|
+
"height": height,
|
|
112
|
+
"depth": depth,
|
|
113
|
+
"italic": italic,
|
|
114
|
+
"skew": skew,
|
|
115
|
+
"width": width
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
sys.stdout.write(
|
|
119
|
+
json.dumps(start_json, separators=(',', ':'), sort_keys=True))
|
|
120
|
+
|
|
121
|
+
if __name__ == "__main__":
|
|
122
|
+
main()
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
props = ['depth', 'height', 'italic', 'skew']
|
|
7
|
+
|
|
8
|
+
if len(sys.argv) > 1:
|
|
9
|
+
if sys.argv[1] == '--width':
|
|
10
|
+
props.append('width')
|
|
11
|
+
|
|
12
|
+
data = json.load(sys.stdin)
|
|
13
|
+
sys.stdout.write(
|
|
14
|
+
"// This file is GENERATED by buildMetrics.sh. DO NOT MODIFY.\n")
|
|
15
|
+
sep = "export default {\n "
|
|
16
|
+
for font in sorted(data):
|
|
17
|
+
sys.stdout.write(sep + json.dumps(font))
|
|
18
|
+
sep = ": {\n "
|
|
19
|
+
for glyph in sorted(data[font], key=int):
|
|
20
|
+
sys.stdout.write(sep + json.dumps(glyph) + ": ")
|
|
21
|
+
|
|
22
|
+
values = [value if value != 0.0 else 0 for value in
|
|
23
|
+
[data[font][glyph][key] for key in props]]
|
|
24
|
+
|
|
25
|
+
sys.stdout.write(json.dumps(values))
|
|
26
|
+
sep = ",\n "
|
|
27
|
+
sep = ",\n },\n "
|
|
28
|
+
sys.stdout.write(",\n },\n};\n")
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
class CharInfoWord(object):
|
|
2
|
+
def __init__(self, word):
|
|
3
|
+
b1, b2, b3, b4 = (word >> 24,
|
|
4
|
+
(word & 0xff0000) >> 16,
|
|
5
|
+
(word & 0xff00) >> 8,
|
|
6
|
+
word & 0xff)
|
|
7
|
+
|
|
8
|
+
self.width_index = b1
|
|
9
|
+
self.height_index = b2 >> 4
|
|
10
|
+
self.depth_index = b2 & 0x0f
|
|
11
|
+
self.italic_index = (b3 & 0b11111100) >> 2
|
|
12
|
+
self.tag = b3 & 0b11
|
|
13
|
+
self.remainder = b4
|
|
14
|
+
|
|
15
|
+
def has_ligkern(self):
|
|
16
|
+
return self.tag == 1
|
|
17
|
+
|
|
18
|
+
def ligkern_start(self):
|
|
19
|
+
return self.remainder
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class LigKernProgram(object):
|
|
23
|
+
def __init__(self, program):
|
|
24
|
+
self.program = program
|
|
25
|
+
|
|
26
|
+
def execute(self, start, next_char):
|
|
27
|
+
curr_instruction = start
|
|
28
|
+
while True:
|
|
29
|
+
instruction = self.program[curr_instruction]
|
|
30
|
+
(skip, inst_next_char, op, remainder) = instruction
|
|
31
|
+
|
|
32
|
+
if inst_next_char == next_char:
|
|
33
|
+
if op < 128:
|
|
34
|
+
# Don't worry about ligatures for now, we only need kerns
|
|
35
|
+
return None
|
|
36
|
+
else:
|
|
37
|
+
return 256 * (op - 128) + remainder
|
|
38
|
+
elif skip >= 128:
|
|
39
|
+
return None
|
|
40
|
+
else:
|
|
41
|
+
curr_instruction += 1 + skip
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class TfmCharMetrics(object):
|
|
45
|
+
def __init__(self, width, height, depth, italic, kern_table):
|
|
46
|
+
self.width = width
|
|
47
|
+
self.height = height
|
|
48
|
+
self.depth = depth
|
|
49
|
+
self.italic_correction = italic
|
|
50
|
+
self.kern_table = kern_table
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TfmFile(object):
|
|
54
|
+
def __init__(self, start_char, end_char, char_info, width_table,
|
|
55
|
+
height_table, depth_table, italic_table, ligkern_table,
|
|
56
|
+
kern_table):
|
|
57
|
+
self.start_char = start_char
|
|
58
|
+
self.end_char = end_char
|
|
59
|
+
self.char_info = char_info
|
|
60
|
+
self.width_table = width_table
|
|
61
|
+
self.height_table = height_table
|
|
62
|
+
self.depth_table = depth_table
|
|
63
|
+
self.italic_table = italic_table
|
|
64
|
+
self.ligkern_program = LigKernProgram(ligkern_table)
|
|
65
|
+
self.kern_table = kern_table
|
|
66
|
+
|
|
67
|
+
def get_char_metrics(self, char_num, fix_rsfs=False):
|
|
68
|
+
"""Return glyph metrics for a unicode code point.
|
|
69
|
+
|
|
70
|
+
Arguments:
|
|
71
|
+
char_num: a unicode code point
|
|
72
|
+
fix_rsfs: adjust for rsfs10.tfm's different indexing system
|
|
73
|
+
"""
|
|
74
|
+
if char_num < self.start_char or char_num > self.end_char:
|
|
75
|
+
raise RuntimeError("Invalid character number")
|
|
76
|
+
|
|
77
|
+
if fix_rsfs:
|
|
78
|
+
# all of the char_nums contained start from zero in rsfs10.tfm
|
|
79
|
+
info = self.char_info[char_num - self.start_char]
|
|
80
|
+
else:
|
|
81
|
+
info = self.char_info[char_num + self.start_char]
|
|
82
|
+
|
|
83
|
+
char_kern_table = {}
|
|
84
|
+
if info.has_ligkern():
|
|
85
|
+
for char in range(self.start_char, self.end_char + 1):
|
|
86
|
+
kern = self.ligkern_program.execute(info.ligkern_start(), char)
|
|
87
|
+
if kern:
|
|
88
|
+
char_kern_table[char] = self.kern_table[kern]
|
|
89
|
+
|
|
90
|
+
return TfmCharMetrics(
|
|
91
|
+
self.width_table[info.width_index],
|
|
92
|
+
self.height_table[info.height_index],
|
|
93
|
+
self.depth_table[info.depth_index],
|
|
94
|
+
self.italic_table[info.italic_index],
|
|
95
|
+
char_kern_table)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class TfmReader(object):
|
|
99
|
+
def __init__(self, f):
|
|
100
|
+
self.f = f
|
|
101
|
+
|
|
102
|
+
def read_byte(self):
|
|
103
|
+
return ord(self.f.read(1))
|
|
104
|
+
|
|
105
|
+
def read_halfword(self):
|
|
106
|
+
b1 = self.read_byte()
|
|
107
|
+
b2 = self.read_byte()
|
|
108
|
+
return (b1 << 8) | b2
|
|
109
|
+
|
|
110
|
+
def read_word(self):
|
|
111
|
+
b1 = self.read_byte()
|
|
112
|
+
b2 = self.read_byte()
|
|
113
|
+
b3 = self.read_byte()
|
|
114
|
+
b4 = self.read_byte()
|
|
115
|
+
return (b1 << 24) | (b2 << 16) | (b3 << 8) | b4
|
|
116
|
+
|
|
117
|
+
def read_fixword(self):
|
|
118
|
+
word = self.read_word()
|
|
119
|
+
|
|
120
|
+
neg = False
|
|
121
|
+
if word & 0x80000000:
|
|
122
|
+
neg = True
|
|
123
|
+
word = (-word & 0xffffffff)
|
|
124
|
+
|
|
125
|
+
return (-1 if neg else 1) * word / float(1 << 20)
|
|
126
|
+
|
|
127
|
+
def read_bcpl(self, length):
|
|
128
|
+
str_length = self.read_byte()
|
|
129
|
+
data = self.f.read(length - 1)
|
|
130
|
+
return data[:str_length]
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def read_tfm_file(file_name):
|
|
134
|
+
with open(file_name, 'rb') as f:
|
|
135
|
+
reader = TfmReader(f)
|
|
136
|
+
|
|
137
|
+
# file_size
|
|
138
|
+
reader.read_halfword()
|
|
139
|
+
header_size = reader.read_halfword()
|
|
140
|
+
|
|
141
|
+
start_char = reader.read_halfword()
|
|
142
|
+
end_char = reader.read_halfword()
|
|
143
|
+
|
|
144
|
+
width_table_size = reader.read_halfword()
|
|
145
|
+
height_table_size = reader.read_halfword()
|
|
146
|
+
depth_table_size = reader.read_halfword()
|
|
147
|
+
italic_table_size = reader.read_halfword()
|
|
148
|
+
|
|
149
|
+
ligkern_table_size = reader.read_halfword()
|
|
150
|
+
kern_table_size = reader.read_halfword()
|
|
151
|
+
|
|
152
|
+
# extensible_table_size
|
|
153
|
+
reader.read_halfword()
|
|
154
|
+
# parameter_table_size
|
|
155
|
+
reader.read_halfword()
|
|
156
|
+
|
|
157
|
+
# checksum
|
|
158
|
+
reader.read_word()
|
|
159
|
+
# design_size
|
|
160
|
+
reader.read_fixword()
|
|
161
|
+
|
|
162
|
+
if header_size > 2:
|
|
163
|
+
# coding_scheme
|
|
164
|
+
reader.read_bcpl(40)
|
|
165
|
+
|
|
166
|
+
if header_size > 12:
|
|
167
|
+
# font_family
|
|
168
|
+
reader.read_bcpl(20)
|
|
169
|
+
|
|
170
|
+
for i in range(header_size - 17):
|
|
171
|
+
reader.read_word()
|
|
172
|
+
|
|
173
|
+
char_info = []
|
|
174
|
+
for i in range(start_char, end_char + 1):
|
|
175
|
+
char_info.append(CharInfoWord(reader.read_word()))
|
|
176
|
+
|
|
177
|
+
width_table = []
|
|
178
|
+
for i in range(width_table_size):
|
|
179
|
+
width_table.append(reader.read_fixword())
|
|
180
|
+
|
|
181
|
+
height_table = []
|
|
182
|
+
for i in range(height_table_size):
|
|
183
|
+
height_table.append(reader.read_fixword())
|
|
184
|
+
|
|
185
|
+
depth_table = []
|
|
186
|
+
for i in range(depth_table_size):
|
|
187
|
+
depth_table.append(reader.read_fixword())
|
|
188
|
+
|
|
189
|
+
italic_table = []
|
|
190
|
+
for i in range(italic_table_size):
|
|
191
|
+
italic_table.append(reader.read_fixword())
|
|
192
|
+
|
|
193
|
+
ligkern_table = []
|
|
194
|
+
for i in range(ligkern_table_size):
|
|
195
|
+
skip = reader.read_byte()
|
|
196
|
+
next_char = reader.read_byte()
|
|
197
|
+
op = reader.read_byte()
|
|
198
|
+
remainder = reader.read_byte()
|
|
199
|
+
|
|
200
|
+
ligkern_table.append((skip, next_char, op, remainder))
|
|
201
|
+
|
|
202
|
+
kern_table = []
|
|
203
|
+
for i in range(kern_table_size):
|
|
204
|
+
kern_table.append(reader.read_fixword())
|
|
205
|
+
|
|
206
|
+
# There is more information, like the ligkern, kern, extensible, and
|
|
207
|
+
# param table, but we don't need these for now
|
|
208
|
+
|
|
209
|
+
return TfmFile(start_char, end_char, char_info, width_table,
|
|
210
|
+
height_table, depth_table, italic_table,
|
|
211
|
+
ligkern_table, kern_table)
|
|
@@ -13,9 +13,9 @@ for structured memory storage with semantic search capabilities.
|
|
|
13
13
|
DESIGN DECISIONS:
|
|
14
14
|
- Priority 10 for early execution to enrich prompts before other hooks
|
|
15
15
|
- Uses subprocess to call kuzu-memory directly for maximum compatibility
|
|
16
|
-
- Graceful degradation if kuzu-memory is not
|
|
16
|
+
- Graceful degradation if kuzu-memory is not installed
|
|
17
17
|
- Automatic extraction and storage of important information
|
|
18
|
-
- kuzu-memory
|
|
18
|
+
- kuzu-memory is an OPTIONAL dependency (install with: pip install claude-mpm[memory])
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
21
|
import json
|
|
@@ -51,9 +51,9 @@ class KuzuMemoryHook(SubmitHook):
|
|
|
51
51
|
self.enabled = self.kuzu_memory_cmd is not None
|
|
52
52
|
|
|
53
53
|
if not self.enabled:
|
|
54
|
-
logger.
|
|
55
|
-
"Kuzu-memory not found
|
|
56
|
-
"
|
|
54
|
+
logger.debug(
|
|
55
|
+
"Kuzu-memory not found. Graph-based memory disabled. "
|
|
56
|
+
"To enable: pip install claude-mpm[memory] (requires cmake)"
|
|
57
57
|
)
|
|
58
58
|
else:
|
|
59
59
|
logger.info(f"Kuzu-memory integration enabled: {self.kuzu_memory_cmd}")
|
claude_mpm/init.py
CHANGED
|
@@ -163,6 +163,15 @@ class ProjectInitializer:
|
|
|
163
163
|
f"✓ Found {agent_count} project agent(s) in .claude-mpm/agents/"
|
|
164
164
|
)
|
|
165
165
|
|
|
166
|
+
# Verify and deploy PM skills (non-blocking)
|
|
167
|
+
self._verify_and_deploy_pm_skills(project_root, is_mcp_mode)
|
|
168
|
+
|
|
169
|
+
# Setup security hooks (auto-install pre-commit, detect-secrets)
|
|
170
|
+
self._setup_security_hooks(project_root, is_mcp_mode)
|
|
171
|
+
|
|
172
|
+
# Perform security checks (non-blocking)
|
|
173
|
+
self._check_security_risks(project_root, is_mcp_mode)
|
|
174
|
+
|
|
166
175
|
return True
|
|
167
176
|
|
|
168
177
|
except Exception as e:
|
|
@@ -170,6 +179,68 @@ class ProjectInitializer:
|
|
|
170
179
|
print(f"✗ Failed to create .claude-mpm/ directory: {e}")
|
|
171
180
|
return False
|
|
172
181
|
|
|
182
|
+
def _verify_and_deploy_pm_skills(
|
|
183
|
+
self, project_root: Path, is_mcp_mode: bool = False
|
|
184
|
+
) -> None:
|
|
185
|
+
"""Verify PM skills are deployed and auto-deploy if missing.
|
|
186
|
+
|
|
187
|
+
Non-blocking operation that gracefully handles errors.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
project_root: Project root directory
|
|
191
|
+
is_mcp_mode: Whether running in MCP mode (suppress console output)
|
|
192
|
+
"""
|
|
193
|
+
try:
|
|
194
|
+
from claude_mpm.services.pm_skills_deployer import PMSkillsDeployerService
|
|
195
|
+
|
|
196
|
+
deployer = PMSkillsDeployerService()
|
|
197
|
+
result = deployer.verify_pm_skills(project_root)
|
|
198
|
+
|
|
199
|
+
if not result.verified:
|
|
200
|
+
# Log warnings
|
|
201
|
+
for warning in result.warnings:
|
|
202
|
+
self.logger.warning(warning)
|
|
203
|
+
|
|
204
|
+
# Auto-deploy PM skills
|
|
205
|
+
self.logger.info("Auto-deploying PM skills...")
|
|
206
|
+
deploy_result = deployer.deploy_pm_skills(project_root)
|
|
207
|
+
|
|
208
|
+
if deploy_result.success:
|
|
209
|
+
self.logger.info(
|
|
210
|
+
f"PM skills deployed: {len(deploy_result.deployed)} deployed, "
|
|
211
|
+
f"{len(deploy_result.skipped)} skipped"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
# Print to console if not in MCP mode
|
|
215
|
+
if not is_mcp_mode:
|
|
216
|
+
if deploy_result.deployed:
|
|
217
|
+
print(
|
|
218
|
+
f"✓ Deployed {len(deploy_result.deployed)} PM skill(s) "
|
|
219
|
+
f"to .claude-mpm/skills/pm/"
|
|
220
|
+
)
|
|
221
|
+
else:
|
|
222
|
+
self.logger.warning(
|
|
223
|
+
f"PM skills deployment had errors: {len(deploy_result.errors)}"
|
|
224
|
+
)
|
|
225
|
+
if not is_mcp_mode and deploy_result.errors:
|
|
226
|
+
print(
|
|
227
|
+
f"⚠ PM skills deployment had {len(deploy_result.errors)} error(s)"
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
# Skills verified successfully
|
|
231
|
+
registry = deployer._load_registry(project_root)
|
|
232
|
+
skill_count = len(registry.get("skills", []))
|
|
233
|
+
self.logger.debug(f"PM skills verified: {skill_count} skills")
|
|
234
|
+
|
|
235
|
+
if not is_mcp_mode and skill_count > 0:
|
|
236
|
+
print(f"✓ Verified {skill_count} PM skill(s)")
|
|
237
|
+
|
|
238
|
+
except ImportError:
|
|
239
|
+
self.logger.debug("PM skills deployer not available")
|
|
240
|
+
except Exception as e:
|
|
241
|
+
self.logger.warning(f"PM skills verification failed: {e}")
|
|
242
|
+
# Don't print to console - this is a non-critical failure
|
|
243
|
+
|
|
173
244
|
def _migrate_project_agents(self):
|
|
174
245
|
"""Migrate agents from old subdirectory structure to direct agents directory.
|
|
175
246
|
|
|
@@ -319,6 +390,211 @@ class ProjectInitializer:
|
|
|
319
390
|
if not dst_file.exists():
|
|
320
391
|
shutil.copy2(template_file, dst_file)
|
|
321
392
|
|
|
393
|
+
def _setup_security_hooks(
|
|
394
|
+
self, project_root: Path, is_mcp_mode: bool = False
|
|
395
|
+
) -> None:
|
|
396
|
+
"""Automatically install pre-commit hooks for secret scanning.
|
|
397
|
+
|
|
398
|
+
This method:
|
|
399
|
+
1. Installs pre-commit and detect-secrets if missing
|
|
400
|
+
2. Copies .pre-commit-config.yaml to project root
|
|
401
|
+
3. Runs pre-commit install to set up git hooks
|
|
402
|
+
4. Creates .secrets.baseline for detect-secrets
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
project_root: Project root directory
|
|
406
|
+
is_mcp_mode: Whether running in MCP mode (suppress console output)
|
|
407
|
+
"""
|
|
408
|
+
try:
|
|
409
|
+
import subprocess
|
|
410
|
+
|
|
411
|
+
# Only set up hooks if this is a git repository
|
|
412
|
+
if not (project_root / ".git").exists():
|
|
413
|
+
self.logger.debug("Not a git repository, skipping security hooks setup")
|
|
414
|
+
return
|
|
415
|
+
|
|
416
|
+
# Check/install pre-commit
|
|
417
|
+
try:
|
|
418
|
+
subprocess.run(
|
|
419
|
+
["pre-commit", "--version"],
|
|
420
|
+
capture_output=True,
|
|
421
|
+
text=True,
|
|
422
|
+
timeout=2,
|
|
423
|
+
check=True,
|
|
424
|
+
)
|
|
425
|
+
except (
|
|
426
|
+
subprocess.CalledProcessError,
|
|
427
|
+
subprocess.TimeoutExpired,
|
|
428
|
+
FileNotFoundError,
|
|
429
|
+
):
|
|
430
|
+
self.logger.info("Installing pre-commit...")
|
|
431
|
+
try:
|
|
432
|
+
subprocess.run(
|
|
433
|
+
[sys.executable, "-m", "pip", "install", "pre-commit"],
|
|
434
|
+
capture_output=True,
|
|
435
|
+
text=True,
|
|
436
|
+
timeout=60,
|
|
437
|
+
check=True,
|
|
438
|
+
)
|
|
439
|
+
self.logger.info("pre-commit installed successfully")
|
|
440
|
+
except subprocess.CalledProcessError as e:
|
|
441
|
+
self.logger.warning(f"Failed to install pre-commit: {e}")
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
# Check/install detect-secrets
|
|
445
|
+
try:
|
|
446
|
+
subprocess.run(
|
|
447
|
+
["detect-secrets", "--version"],
|
|
448
|
+
capture_output=True,
|
|
449
|
+
text=True,
|
|
450
|
+
timeout=2,
|
|
451
|
+
check=True,
|
|
452
|
+
)
|
|
453
|
+
except (
|
|
454
|
+
subprocess.CalledProcessError,
|
|
455
|
+
subprocess.TimeoutExpired,
|
|
456
|
+
FileNotFoundError,
|
|
457
|
+
):
|
|
458
|
+
self.logger.info("Installing detect-secrets...")
|
|
459
|
+
try:
|
|
460
|
+
subprocess.run(
|
|
461
|
+
[sys.executable, "-m", "pip", "install", "detect-secrets"],
|
|
462
|
+
capture_output=True,
|
|
463
|
+
text=True,
|
|
464
|
+
timeout=60,
|
|
465
|
+
check=True,
|
|
466
|
+
)
|
|
467
|
+
self.logger.info("detect-secrets installed successfully")
|
|
468
|
+
except subprocess.CalledProcessError as e:
|
|
469
|
+
self.logger.warning(f"Failed to install detect-secrets: {e}")
|
|
470
|
+
return
|
|
471
|
+
|
|
472
|
+
# Copy .pre-commit-config.yaml to project root if it doesn't exist
|
|
473
|
+
precommit_config = project_root / ".pre-commit-config.yaml"
|
|
474
|
+
if not precommit_config.exists():
|
|
475
|
+
template_dir = Path(__file__).parent / "templates"
|
|
476
|
+
template_config = template_dir / ".pre-commit-config.yaml"
|
|
477
|
+
|
|
478
|
+
if template_config.exists():
|
|
479
|
+
shutil.copy2(template_config, precommit_config)
|
|
480
|
+
self.logger.info("Copied .pre-commit-config.yaml to project root")
|
|
481
|
+
else:
|
|
482
|
+
self.logger.warning("Template .pre-commit-config.yaml not found")
|
|
483
|
+
return
|
|
484
|
+
|
|
485
|
+
# Create .secrets.baseline if it doesn't exist
|
|
486
|
+
secrets_baseline = project_root / ".secrets.baseline"
|
|
487
|
+
if not secrets_baseline.exists():
|
|
488
|
+
try:
|
|
489
|
+
subprocess.run(
|
|
490
|
+
["detect-secrets", "scan", "--baseline", ".secrets.baseline"],
|
|
491
|
+
cwd=str(project_root),
|
|
492
|
+
capture_output=True,
|
|
493
|
+
text=True,
|
|
494
|
+
timeout=30,
|
|
495
|
+
check=True,
|
|
496
|
+
)
|
|
497
|
+
self.logger.info("Created .secrets.baseline")
|
|
498
|
+
except subprocess.CalledProcessError as e:
|
|
499
|
+
self.logger.warning(f"Failed to create .secrets.baseline: {e}")
|
|
500
|
+
|
|
501
|
+
# Install git hooks
|
|
502
|
+
try:
|
|
503
|
+
subprocess.run(
|
|
504
|
+
["pre-commit", "install"],
|
|
505
|
+
cwd=str(project_root),
|
|
506
|
+
capture_output=True,
|
|
507
|
+
text=True,
|
|
508
|
+
timeout=30,
|
|
509
|
+
check=True,
|
|
510
|
+
)
|
|
511
|
+
self.logger.info("Pre-commit hooks installed in git repository")
|
|
512
|
+
|
|
513
|
+
if not is_mcp_mode:
|
|
514
|
+
print("✓ Security hooks installed (pre-commit + detect-secrets)")
|
|
515
|
+
|
|
516
|
+
except subprocess.CalledProcessError as e:
|
|
517
|
+
self.logger.warning(f"Failed to install pre-commit hooks: {e}")
|
|
518
|
+
|
|
519
|
+
except Exception as e:
|
|
520
|
+
self.logger.debug(f"Security hooks setup failed: {e}")
|
|
521
|
+
# Don't print to console - this is a non-critical failure
|
|
522
|
+
|
|
523
|
+
def _check_security_risks(
|
|
524
|
+
self, project_root: Path, is_mcp_mode: bool = False
|
|
525
|
+
) -> None:
|
|
526
|
+
"""Check for potential security risks like exposed config files.
|
|
527
|
+
|
|
528
|
+
Non-blocking operation that warns about security issues.
|
|
529
|
+
|
|
530
|
+
Args:
|
|
531
|
+
project_root: Project root directory
|
|
532
|
+
is_mcp_mode: Whether running in MCP mode (suppress console output)
|
|
533
|
+
"""
|
|
534
|
+
try:
|
|
535
|
+
import subprocess
|
|
536
|
+
|
|
537
|
+
security_issues = []
|
|
538
|
+
|
|
539
|
+
# Common secret file patterns to check
|
|
540
|
+
secret_patterns = [
|
|
541
|
+
".mcp-vector-search/config.json",
|
|
542
|
+
".mcp/config.json",
|
|
543
|
+
"openrouter.json",
|
|
544
|
+
"anthropic-config.json",
|
|
545
|
+
"credentials.json",
|
|
546
|
+
"secrets.json",
|
|
547
|
+
"api-keys.json",
|
|
548
|
+
]
|
|
549
|
+
|
|
550
|
+
for pattern in secret_patterns:
|
|
551
|
+
file_path = project_root / pattern
|
|
552
|
+
if file_path.exists():
|
|
553
|
+
# Check if file is tracked by git
|
|
554
|
+
try:
|
|
555
|
+
result = subprocess.run(
|
|
556
|
+
["git", "ls-files", str(file_path)],
|
|
557
|
+
check=False,
|
|
558
|
+
cwd=str(project_root),
|
|
559
|
+
capture_output=True,
|
|
560
|
+
text=True,
|
|
561
|
+
timeout=2,
|
|
562
|
+
)
|
|
563
|
+
if result.stdout.strip():
|
|
564
|
+
security_issues.append(
|
|
565
|
+
f"⚠️ SECURITY: {pattern} is tracked by git (may contain secrets)"
|
|
566
|
+
)
|
|
567
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
568
|
+
pass
|
|
569
|
+
|
|
570
|
+
# Check if file is ignored by .gitignore
|
|
571
|
+
try:
|
|
572
|
+
result = subprocess.run(
|
|
573
|
+
["git", "check-ignore", str(file_path)],
|
|
574
|
+
check=False,
|
|
575
|
+
cwd=str(project_root),
|
|
576
|
+
capture_output=True,
|
|
577
|
+
text=True,
|
|
578
|
+
timeout=2,
|
|
579
|
+
)
|
|
580
|
+
if result.returncode != 0: # File NOT ignored
|
|
581
|
+
security_issues.append(
|
|
582
|
+
f"⚠️ WARNING: {pattern} exists but not in .gitignore"
|
|
583
|
+
)
|
|
584
|
+
except (subprocess.TimeoutExpired, FileNotFoundError):
|
|
585
|
+
pass
|
|
586
|
+
|
|
587
|
+
# Print security warnings if not in MCP mode
|
|
588
|
+
if security_issues and not is_mcp_mode:
|
|
589
|
+
print("\n🔒 Security Check:")
|
|
590
|
+
for issue in security_issues:
|
|
591
|
+
print(f" {issue}")
|
|
592
|
+
print()
|
|
593
|
+
|
|
594
|
+
except Exception as e:
|
|
595
|
+
self.logger.debug(f"Security check failed: {e}")
|
|
596
|
+
# Don't print to console - this is a non-critical failure
|
|
597
|
+
|
|
322
598
|
def validate_dependencies(self) -> Dict[str, bool]:
|
|
323
599
|
"""Validate that all required dependencies are available."""
|
|
324
600
|
dependencies = {}
|