claude-mpm 5.4.55__py3-none-any.whl → 5.6.1__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.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md +413 -0
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +109 -1925
- claude_mpm/agents/PM_INSTRUCTIONS.md +111 -686
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/__init__.py +5 -1
- claude_mpm/cli/commands/agents.py +2 -4
- claude_mpm/cli/commands/agents_reconcile.py +197 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skills.py +166 -14
- claude_mpm/cli/executor.py +120 -16
- claude_mpm/cli/interactive/__init__.py +10 -0
- claude_mpm/cli/interactive/agent_wizard.py +30 -50
- claude_mpm/cli/interactive/questionary_styles.py +65 -0
- claude_mpm/cli/interactive/skill_selector.py +481 -0
- claude_mpm/cli/parsers/base_parser.py +76 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/startup.py +276 -403
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +72 -0
- claude_mpm/commander/adapters/__init__.py +31 -0
- claude_mpm/commander/adapters/base.py +191 -0
- claude_mpm/commander/adapters/claude_code.py +361 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +105 -0
- claude_mpm/commander/api/errors.py +112 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +215 -0
- claude_mpm/commander/api/routes/work.py +260 -0
- claude_mpm/commander/api/schemas.py +182 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +107 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -0
- claude_mpm/commander/config.py +49 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/daemon.py +398 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +143 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +62 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +337 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +121 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +309 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +404 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +316 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +361 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +189 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +219 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +9 -1
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/config.py +5 -0
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/logger.py +10 -7
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/output_style_manager.py +31 -13
- claude_mpm/core/unified_config.py +54 -8
- claude_mpm/core/unified_paths.py +30 -13
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +69 -5
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
- claude_mpm/hooks/session_resume_hook.py +85 -1
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +36 -10
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/agent_template_builder.py +8 -0
- claude_mpm/services/agents/deployment/deployment_reconciler.py +577 -0
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/deployment/startup_reconciliation.py +138 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +7 -4
- claude_mpm/services/agents/startup_sync.py +5 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +261 -85
- claude_mpm/services/skills/git_skill_source_manager.py +75 -10
- claude_mpm/services/skills/selective_skill_deployer.py +177 -80
- claude_mpm/services/skills/skill_discovery_service.py +57 -3
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- 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/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-bug-reporting/SKILL.md +248 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-delegation-patterns/SKILL.md +167 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
- claude_mpm/skills/bundled/pm/mpm-git-file-tracking/SKILL.md +113 -0
- claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
- claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
- claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
- claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
- claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
- claude_mpm/skills/bundled/pm/mpm-pr-workflow/SKILL.md +124 -0
- claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-ticketing-integration/SKILL.md +154 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-verification-protocols/SKILL.md +198 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -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/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 +4 -4
- claude_mpm/utils/agent_dependency_loader.py +103 -4
- claude_mpm/utils/robust_installer.py +45 -24
- claude_mpm-5.6.1.dist-info/METADATA +391 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +377 -166
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/correlation_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm-5.4.55.dist-info/METADATA +0 -999
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.55.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
|
@@ -682,7 +682,7 @@ class GitSkillSourceManager:
|
|
|
682
682
|
try:
|
|
683
683
|
with open(etag_cache_file, encoding="utf-8") as f:
|
|
684
684
|
etag_cache = json.load(f)
|
|
685
|
-
except Exception:
|
|
685
|
+
except Exception: # nosec B110 - intentional: proceed without cache on read failure
|
|
686
686
|
pass
|
|
687
687
|
|
|
688
688
|
cached_etag = etag_cache.get(str(local_path))
|
|
@@ -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()
|
|
@@ -1082,11 +1090,16 @@ class GitSkillSourceManager:
|
|
|
1082
1090
|
)
|
|
1083
1091
|
|
|
1084
1092
|
# Cleanup: Remove skills from target directory that aren't in the filtered set
|
|
1085
|
-
# This ensures only
|
|
1093
|
+
# This ensures only agent-referenced skills remain deployed
|
|
1086
1094
|
removed_skills = self._cleanup_unfiltered_skills(target_dir, all_skills)
|
|
1087
1095
|
if removed_skills:
|
|
1088
1096
|
self.logger.info(
|
|
1089
|
-
f"Removed {len(removed_skills)} skills not
|
|
1097
|
+
f"Removed {len(removed_skills)} orphaned skills not referenced by agents: {removed_skills[:10]}"
|
|
1098
|
+
+ (
|
|
1099
|
+
f" (and {len(removed_skills) - 10} more)"
|
|
1100
|
+
if len(removed_skills) > 10
|
|
1101
|
+
else ""
|
|
1102
|
+
)
|
|
1090
1103
|
)
|
|
1091
1104
|
|
|
1092
1105
|
self.logger.info(
|
|
@@ -1130,6 +1143,7 @@ class GitSkillSourceManager:
|
|
|
1130
1143
|
self.logger.info(
|
|
1131
1144
|
f"Deployment complete: {len(deployed)} deployed, "
|
|
1132
1145
|
f"{len(skipped)} skipped, {len(errors)} errors"
|
|
1146
|
+
+ (f", {len(removed_skills)} removed" if removed_skills else "")
|
|
1133
1147
|
)
|
|
1134
1148
|
|
|
1135
1149
|
return {
|
|
@@ -1140,6 +1154,8 @@ class GitSkillSourceManager:
|
|
|
1140
1154
|
"skipped_skills": skipped,
|
|
1141
1155
|
"errors": errors,
|
|
1142
1156
|
"filtered_count": filtered_count,
|
|
1157
|
+
"removed_count": len(removed_skills),
|
|
1158
|
+
"removed_skills": removed_skills,
|
|
1143
1159
|
}
|
|
1144
1160
|
|
|
1145
1161
|
def _cleanup_unfiltered_skills(
|
|
@@ -1147,6 +1163,10 @@ class GitSkillSourceManager:
|
|
|
1147
1163
|
) -> List[str]:
|
|
1148
1164
|
"""Remove skills from target directory that aren't in the filtered skill list.
|
|
1149
1165
|
|
|
1166
|
+
CRITICAL: Only removes MPM-managed skills (those in our cache). Custom user skills
|
|
1167
|
+
are preserved. This prevents accidental deletion of user-created skills that were
|
|
1168
|
+
never part of MPM's skill repository.
|
|
1169
|
+
|
|
1150
1170
|
Uses fuzzy matching to handle both exact deployment names and short skill names.
|
|
1151
1171
|
For example:
|
|
1152
1172
|
- "toolchains-python-frameworks-flask" (deployed dir) matches "flask" (filter)
|
|
@@ -1197,6 +1217,40 @@ class GitSkillSourceManager:
|
|
|
1197
1217
|
|
|
1198
1218
|
return False
|
|
1199
1219
|
|
|
1220
|
+
def is_mpm_managed_skill(skill_dir_name: str) -> bool:
|
|
1221
|
+
"""Check if skill is managed by MPM (exists in our cache).
|
|
1222
|
+
|
|
1223
|
+
Custom user skills (not in cache) are NEVER deleted, even if not in filter.
|
|
1224
|
+
Only MPM-managed skills (in cache but not in filter) are candidates for removal.
|
|
1225
|
+
|
|
1226
|
+
Args:
|
|
1227
|
+
skill_dir_name: Name of deployed skill directory
|
|
1228
|
+
|
|
1229
|
+
Returns:
|
|
1230
|
+
True if skill exists in MPM cache (MPM-managed), False if custom user skill
|
|
1231
|
+
"""
|
|
1232
|
+
# Check all configured skill sources for this skill
|
|
1233
|
+
for source in self.config.get_enabled_sources():
|
|
1234
|
+
cache_path = self._get_source_cache_path(source)
|
|
1235
|
+
if not cache_path.exists():
|
|
1236
|
+
continue
|
|
1237
|
+
|
|
1238
|
+
# Check if this skill directory exists anywhere in the cache
|
|
1239
|
+
# Use glob to find matching directories recursively
|
|
1240
|
+
matches = list(cache_path.rglob(f"*{skill_dir_name}*"))
|
|
1241
|
+
if matches:
|
|
1242
|
+
# Found in cache - this is MPM-managed
|
|
1243
|
+
self.logger.debug(
|
|
1244
|
+
f"Skill '{skill_dir_name}' found in cache at {matches[0]} - MPM-managed"
|
|
1245
|
+
)
|
|
1246
|
+
return True
|
|
1247
|
+
|
|
1248
|
+
# Not found in any cache - this is a custom user skill
|
|
1249
|
+
self.logger.debug(
|
|
1250
|
+
f"Skill '{skill_dir_name}' not found in cache - custom user skill, preserving"
|
|
1251
|
+
)
|
|
1252
|
+
return False
|
|
1253
|
+
|
|
1200
1254
|
# Check each directory in target_dir
|
|
1201
1255
|
if not target_dir.exists():
|
|
1202
1256
|
return removed_skills
|
|
@@ -1213,6 +1267,15 @@ class GitSkillSourceManager:
|
|
|
1213
1267
|
|
|
1214
1268
|
# Check if this skill directory should be kept (fuzzy matching)
|
|
1215
1269
|
if not should_keep_skill(item.name):
|
|
1270
|
+
# CRITICAL: Check if this is an MPM-managed skill before deletion
|
|
1271
|
+
if not is_mpm_managed_skill(item.name):
|
|
1272
|
+
# This is a custom user skill - NEVER delete
|
|
1273
|
+
self.logger.debug(
|
|
1274
|
+
f"Preserving custom user skill (not in MPM cache): {item.name}"
|
|
1275
|
+
)
|
|
1276
|
+
continue
|
|
1277
|
+
|
|
1278
|
+
# It's MPM-managed but not in filter - safe to remove
|
|
1216
1279
|
try:
|
|
1217
1280
|
# Security: Validate path is within target_dir
|
|
1218
1281
|
if not self._validate_safe_path(target_dir, item):
|
|
@@ -1228,7 +1291,9 @@ class GitSkillSourceManager:
|
|
|
1228
1291
|
shutil.rmtree(item)
|
|
1229
1292
|
|
|
1230
1293
|
removed_skills.append(item.name)
|
|
1231
|
-
self.logger.info(
|
|
1294
|
+
self.logger.info(
|
|
1295
|
+
f"Removed orphaned MPM-managed skill: {item.name}"
|
|
1296
|
+
)
|
|
1232
1297
|
|
|
1233
1298
|
except Exception as e:
|
|
1234
1299
|
self.logger.warning(
|
|
@@ -44,13 +44,56 @@ from typing import Any, Dict, List, Set, Tuple
|
|
|
44
44
|
import yaml
|
|
45
45
|
|
|
46
46
|
from claude_mpm.core.logging_config import get_logger
|
|
47
|
-
from claude_mpm.services.skills.skill_to_agent_mapper import SkillToAgentMapper
|
|
48
47
|
|
|
49
48
|
logger = get_logger(__name__)
|
|
50
49
|
|
|
51
50
|
# Deployment tracking index file
|
|
52
51
|
DEPLOYED_INDEX_FILE = ".mpm-deployed-skills.json"
|
|
53
52
|
|
|
53
|
+
# Core skills that are universally useful across all projects
|
|
54
|
+
# These are deployed when skill mapping returns too many skills (>60)
|
|
55
|
+
# Target: ~25-30 core skills for balanced functionality
|
|
56
|
+
CORE_SKILLS = {
|
|
57
|
+
# Universal debugging and verification (4 skills)
|
|
58
|
+
"universal-debugging-systematic-debugging",
|
|
59
|
+
"universal-debugging-verification-before-completion",
|
|
60
|
+
"universal-verification-pre-merge",
|
|
61
|
+
"universal-verification-screenshot",
|
|
62
|
+
# Universal testing patterns (2 skills)
|
|
63
|
+
"universal-testing-test-driven-development",
|
|
64
|
+
"universal-testing-testing-anti-patterns",
|
|
65
|
+
# Universal architecture and design (1 skill)
|
|
66
|
+
"universal-architecture-software-patterns",
|
|
67
|
+
# Universal infrastructure (3 skills)
|
|
68
|
+
"universal-infrastructure-env-manager",
|
|
69
|
+
"universal-infrastructure-docker",
|
|
70
|
+
"universal-infrastructure-github-actions",
|
|
71
|
+
# Universal collaboration (1 skill)
|
|
72
|
+
"universal-collaboration-stacked-prs",
|
|
73
|
+
# Universal emergency/operations (1 skill)
|
|
74
|
+
"toolchains-universal-emergency-release",
|
|
75
|
+
"toolchains-universal-dependency-audit",
|
|
76
|
+
# Common language toolchains (6 skills)
|
|
77
|
+
"toolchains-typescript-core",
|
|
78
|
+
"toolchains-python-core",
|
|
79
|
+
"toolchains-javascript-tooling-biome",
|
|
80
|
+
"toolchains-python-tooling-mypy",
|
|
81
|
+
"toolchains-typescript-testing-vitest",
|
|
82
|
+
"toolchains-python-frameworks-flask",
|
|
83
|
+
# Common web frameworks (4 skills)
|
|
84
|
+
"toolchains-javascript-frameworks-nextjs",
|
|
85
|
+
"toolchains-nextjs-core",
|
|
86
|
+
"toolchains-typescript-frameworks-nodejs-backend",
|
|
87
|
+
"toolchains-javascript-frameworks-react-state-machine",
|
|
88
|
+
# Common testing tools (2 skills)
|
|
89
|
+
"toolchains-javascript-testing-playwright",
|
|
90
|
+
"toolchains-typescript-testing-jest",
|
|
91
|
+
# Common data/UI tools (3 skills)
|
|
92
|
+
"universal-data-xlsx",
|
|
93
|
+
"toolchains-ui-styling-tailwind",
|
|
94
|
+
"toolchains-ui-components-headlessui",
|
|
95
|
+
}
|
|
96
|
+
|
|
54
97
|
|
|
55
98
|
def parse_agent_frontmatter(agent_file: Path) -> Dict[str, Any]:
|
|
56
99
|
"""Parse YAML frontmatter from agent markdown file.
|
|
@@ -140,22 +183,14 @@ def get_skills_from_agent(frontmatter: Dict[str, Any]) -> Set[str]:
|
|
|
140
183
|
def get_skills_from_mapping(agent_ids: List[str]) -> Set[str]:
|
|
141
184
|
"""Get skills for agents using SkillToAgentMapper inference.
|
|
142
185
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
CRITICAL DESIGN DECISION: This function ONLY returns skills for the DEPLOYED agents
|
|
147
|
-
provided in agent_ids. It does NOT return skills for all agents in the mapping
|
|
148
|
-
configuration (skill_to_agent_mapping.yaml lists 41 agents, but only 33 may be deployed).
|
|
186
|
+
DEPRECATED: This function is deprecated as of Phase 3 refactor.
|
|
187
|
+
Skills are now declared exclusively in agent frontmatter.
|
|
149
188
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
specialized agents exist, we skip "engineer" if specialized agents are present.
|
|
189
|
+
The static skill_to_agent_mapping.yaml is no longer used for skill deployment.
|
|
190
|
+
Each agent must declare its skills in frontmatter or it gets zero skills.
|
|
153
191
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
- User may only have 33 agents deployed in ~/.claude/agents/
|
|
157
|
-
- Without filtering, we'd deploy skills for all 41 agents (over-deployment)
|
|
158
|
-
- Solution: Only query skills for DEPLOYED agents (passed in agent_ids)
|
|
192
|
+
This function remains for backward compatibility but is NO LONGER CALLED
|
|
193
|
+
by get_required_skills_from_agents().
|
|
159
194
|
|
|
160
195
|
Args:
|
|
161
196
|
agent_ids: List of DEPLOYED agent identifiers (e.g., ["python-engineer", "typescript-engineer"])
|
|
@@ -163,67 +198,106 @@ def get_skills_from_mapping(agent_ids: List[str]) -> Set[str]:
|
|
|
163
198
|
|
|
164
199
|
Returns:
|
|
165
200
|
Set of unique skill names inferred from mapping configuration for DEPLOYED agents only
|
|
201
|
+
NOTE: This is now an empty set as the function is deprecated.
|
|
166
202
|
|
|
167
203
|
Example:
|
|
168
|
-
>>> #
|
|
204
|
+
>>> # DEPRECATED - use frontmatter instead
|
|
169
205
|
>>> deployed_agent_ids = ["python-engineer", "typescript-engineer", "qa"]
|
|
170
|
-
>>> skills = get_skills_from_mapping(deployed_agent_ids)
|
|
171
|
-
>>> print(f"Found {len(skills)} skills for {len(deployed_agent_ids)} deployed agents")
|
|
206
|
+
>>> skills = get_skills_from_mapping(deployed_agent_ids) # Returns empty set
|
|
172
207
|
"""
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
# agents like "python-engineer", "typescript-engineer", etc.
|
|
181
|
-
#
|
|
182
|
-
# Solution: Filter out "engineer" from agent_ids if specialized agents exist
|
|
183
|
-
specialized_engineers = [
|
|
184
|
-
aid for aid in agent_ids if aid.endswith("-engineer") and aid != "engineer"
|
|
185
|
-
]
|
|
186
|
-
|
|
187
|
-
# If specialized engineers exist, exclude generic "engineer" from skill mapping
|
|
188
|
-
# This prevents deploying 100+ skills when only a subset is needed
|
|
189
|
-
agents_to_query = agent_ids
|
|
190
|
-
if specialized_engineers and "engineer" in agent_ids:
|
|
191
|
-
agents_to_query = [aid for aid in agent_ids if aid != "engineer"]
|
|
192
|
-
logger.info(
|
|
193
|
-
f"Excluding generic 'engineer' agent from skill mapping "
|
|
194
|
-
f"(found {len(specialized_engineers)} specialized engineers: "
|
|
195
|
-
f"{', '.join(specialized_engineers[:5])}{'...' if len(specialized_engineers) > 5 else ''})"
|
|
196
|
-
)
|
|
208
|
+
# DEPRECATED: Return empty set
|
|
209
|
+
logger.warning(
|
|
210
|
+
"get_skills_from_mapping() is DEPRECATED and returns empty set. "
|
|
211
|
+
"Skills are now declared in agent frontmatter only. "
|
|
212
|
+
"Update your agents with 'skills:' field in frontmatter."
|
|
213
|
+
)
|
|
214
|
+
return set()
|
|
197
215
|
|
|
198
|
-
# IMPORTANT: Only query skills for DEPLOYED agents (those in agent_ids)
|
|
199
|
-
# Do NOT query all agents from skill_to_agent_mapping.yaml (that's 41 agents)
|
|
200
|
-
for agent_id in agents_to_query:
|
|
201
|
-
agent_skills = mapper.get_skills_for_agent(agent_id)
|
|
202
|
-
if agent_skills:
|
|
203
|
-
all_skills.update(agent_skills)
|
|
204
|
-
logger.debug(f"Mapped {len(agent_skills)} skills to {agent_id}")
|
|
205
216
|
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
217
|
+
def extract_skills_from_content(agent_file: Path) -> Set[str]:
|
|
218
|
+
"""Extract skill names from [SKILL: skill-name] markers in agent file content.
|
|
219
|
+
|
|
220
|
+
This function complements frontmatter skill extraction by finding inline
|
|
221
|
+
skill references in the agent's markdown content body.
|
|
222
|
+
|
|
223
|
+
Supports multiple formats:
|
|
224
|
+
- Bold marker: **[SKILL: skill-name]**
|
|
225
|
+
- Plain marker: [SKILL: skill-name]
|
|
226
|
+
- Backtick list: - `skill-name` - Description
|
|
227
|
+
- With spaces: [SKILL: skill-name ]
|
|
211
228
|
|
|
229
|
+
Args:
|
|
230
|
+
agent_file: Path to agent markdown file
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Set of skill names found in content body
|
|
234
|
+
|
|
235
|
+
Example:
|
|
236
|
+
>>> skills = extract_skills_from_content(Path("pm.md"))
|
|
237
|
+
>>> # Finds skills from markers like **[SKILL: mpm-delegation-patterns]**
|
|
238
|
+
>>> # Also finds from lists like - `mpm-teaching-mode` - Description
|
|
239
|
+
>>> print(f"Found {len(skills)} skills in content")
|
|
240
|
+
"""
|
|
241
|
+
try:
|
|
242
|
+
content = agent_file.read_text(encoding="utf-8")
|
|
212
243
|
except Exception as e:
|
|
213
|
-
logger.warning(f"Failed to
|
|
214
|
-
logger.info("Falling back to frontmatter-only skill discovery")
|
|
244
|
+
logger.warning(f"Failed to read {agent_file}: {e}")
|
|
215
245
|
return set()
|
|
216
246
|
|
|
247
|
+
skills = set()
|
|
248
|
+
|
|
249
|
+
# Pattern 1: [SKILL: skill-name] markers (with optional markdown bold)
|
|
250
|
+
# Handles: **[SKILL: skill-name]** or [SKILL: skill-name]
|
|
251
|
+
# Pattern breakdown:
|
|
252
|
+
# - \*{0,2}: Optional bold markdown (0-2 asterisks)
|
|
253
|
+
# - \[SKILL:\s*: Opening bracket with optional whitespace
|
|
254
|
+
# - ([a-zA-Z0-9_-]+): Skill name (capture group)
|
|
255
|
+
# - \s*\]: Closing bracket with optional whitespace
|
|
256
|
+
# - \*{0,2}: Optional closing bold markdown
|
|
257
|
+
pattern1 = r"\*{0,2}\[SKILL:\s*([a-zA-Z0-9_-]+)\s*\]\*{0,2}"
|
|
258
|
+
matches1 = re.findall(pattern1, content, re.IGNORECASE)
|
|
259
|
+
skills.update(matches1)
|
|
260
|
+
|
|
261
|
+
# Pattern 2: Backtick list items with mpm-* or toolchains-* skills
|
|
262
|
+
# Handles: - `mpm-skill-name` - Description
|
|
263
|
+
# Pattern breakdown:
|
|
264
|
+
# - ^-\s+: Start with dash and whitespace (list item)
|
|
265
|
+
# - `: Opening backtick
|
|
266
|
+
# - ((?:mpm-|toolchains-|universal-)[a-zA-Z0-9_-]+): Skill name starting with prefix
|
|
267
|
+
# - `: Closing backtick
|
|
268
|
+
# - \s+-: Followed by whitespace and dash (description separator)
|
|
269
|
+
pattern2 = r"^-\s+`((?:mpm-|toolchains-|universal-)[a-zA-Z0-9_-]+)`\s+-"
|
|
270
|
+
matches2 = re.findall(pattern2, content, re.MULTILINE | re.IGNORECASE)
|
|
271
|
+
skills.update(matches2)
|
|
272
|
+
|
|
273
|
+
if skills:
|
|
274
|
+
logger.debug(
|
|
275
|
+
f"Found {len(skills)} skills from content markers in {agent_file.name}"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
return skills
|
|
279
|
+
|
|
217
280
|
|
|
218
281
|
def get_required_skills_from_agents(agents_dir: Path) -> Set[str]:
|
|
219
282
|
"""Extract all skills referenced by deployed agents.
|
|
220
283
|
|
|
221
|
-
|
|
222
|
-
1.
|
|
223
|
-
2.
|
|
284
|
+
MAJOR CHANGE (Phase 3): Now uses TWO sources for skill discovery:
|
|
285
|
+
1. Frontmatter-declared skills (skills: field)
|
|
286
|
+
2. Content body markers ([SKILL: skill-name])
|
|
287
|
+
|
|
288
|
+
The static skill_to_agent_mapping.yaml is DEPRECATED. Each agent must
|
|
289
|
+
declare its skills via frontmatter OR inline markers.
|
|
224
290
|
|
|
225
|
-
This
|
|
226
|
-
|
|
291
|
+
This change:
|
|
292
|
+
- Eliminates dual-source complexity (frontmatter + mapping)
|
|
293
|
+
- Makes skill requirements explicit per agent
|
|
294
|
+
- Enables per-agent customization via frontmatter or inline markers
|
|
295
|
+
- Removes dependency on static YAML mapping
|
|
296
|
+
- Fixes PM skills being removed as orphaned (they use inline markers)
|
|
297
|
+
|
|
298
|
+
Special handling for PM_INSTRUCTIONS.md:
|
|
299
|
+
- Also scans .claude-mpm/PM_INSTRUCTIONS.md for skill markers
|
|
300
|
+
- PM instructions are not in agents_dir but contain [SKILL: ...] references
|
|
227
301
|
|
|
228
302
|
Args:
|
|
229
303
|
agents_dir: Path to deployed agents directory (e.g., .claude/agents/)
|
|
@@ -242,43 +316,66 @@ def get_required_skills_from_agents(agents_dir: Path) -> Set[str]:
|
|
|
242
316
|
|
|
243
317
|
# Scan all agent markdown files
|
|
244
318
|
agent_files = list(agents_dir.glob("*.md"))
|
|
245
|
-
logger.debug(f"Scanning {len(agent_files)} agent files in {agents_dir}")
|
|
246
319
|
|
|
247
|
-
#
|
|
320
|
+
# Special case: Add PM_INSTRUCTIONS.md if it exists
|
|
321
|
+
# PM instructions live in .claude-mpm/ not .claude/agents/
|
|
322
|
+
pm_instructions = agents_dir.parent.parent / ".claude-mpm" / "PM_INSTRUCTIONS.md"
|
|
323
|
+
if pm_instructions.exists():
|
|
324
|
+
agent_files.append(pm_instructions)
|
|
325
|
+
logger.debug("Added PM_INSTRUCTIONS.md for skill scanning")
|
|
326
|
+
|
|
327
|
+
logger.debug(f"Scanning {len(agent_files)} agent files (including PM instructions)")
|
|
328
|
+
|
|
329
|
+
# Use TWO sources: frontmatter AND content markers
|
|
248
330
|
frontmatter_skills = set()
|
|
249
|
-
|
|
331
|
+
content_skills = set()
|
|
250
332
|
|
|
251
333
|
for agent_file in agent_files:
|
|
252
334
|
agent_id = agent_file.stem
|
|
253
|
-
agent_ids.append(agent_id)
|
|
254
335
|
|
|
336
|
+
# Source 1: Extract from frontmatter
|
|
255
337
|
frontmatter = parse_agent_frontmatter(agent_file)
|
|
256
|
-
|
|
338
|
+
agent_fm_skills = get_skills_from_agent(frontmatter)
|
|
257
339
|
|
|
258
|
-
if
|
|
259
|
-
frontmatter_skills.update(
|
|
340
|
+
if agent_fm_skills:
|
|
341
|
+
frontmatter_skills.update(agent_fm_skills)
|
|
260
342
|
logger.debug(
|
|
261
|
-
f"Agent {agent_id}: {len(
|
|
343
|
+
f"Agent {agent_id}: {len(agent_fm_skills)} skills from frontmatter"
|
|
262
344
|
)
|
|
263
345
|
|
|
264
|
-
|
|
346
|
+
# Source 2: Extract from content body [SKILL: ...] markers
|
|
347
|
+
agent_content_skills = extract_skills_from_content(agent_file)
|
|
348
|
+
|
|
349
|
+
if agent_content_skills:
|
|
350
|
+
content_skills.update(agent_content_skills)
|
|
351
|
+
logger.debug(
|
|
352
|
+
f"Agent {agent_id}: {len(agent_content_skills)} skills from content markers"
|
|
353
|
+
)
|
|
265
354
|
|
|
266
|
-
|
|
267
|
-
|
|
355
|
+
if not agent_fm_skills and not agent_content_skills:
|
|
356
|
+
logger.debug(
|
|
357
|
+
f"Agent {agent_id}: No skills declared (checked frontmatter + content)"
|
|
358
|
+
)
|
|
268
359
|
|
|
269
360
|
# Combine both sources
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
# Normalize skill paths: convert slashes to dashes for compatibility with deployment
|
|
273
|
-
# SkillToAgentMapper returns paths like "toolchains/python/frameworks/django"
|
|
274
|
-
# but deployment expects "toolchains-python-frameworks-django"
|
|
275
|
-
normalized_skills = {skill.replace("/", "-") for skill in required_skills}
|
|
361
|
+
all_skills = frontmatter_skills | content_skills
|
|
276
362
|
|
|
277
363
|
logger.info(
|
|
278
|
-
f"
|
|
279
|
-
f"
|
|
364
|
+
f"Found {len(all_skills)} unique skills "
|
|
365
|
+
f"({len(frontmatter_skills)} from frontmatter, "
|
|
366
|
+
f"{len(content_skills)} from content markers)"
|
|
280
367
|
)
|
|
281
368
|
|
|
369
|
+
# Normalize skill paths: convert slashes to dashes for compatibility with deployment
|
|
370
|
+
# Some skills may use slash format, normalize to dashes
|
|
371
|
+
normalized_skills = {skill.replace("/", "-") for skill in all_skills}
|
|
372
|
+
|
|
373
|
+
if normalized_skills != all_skills:
|
|
374
|
+
logger.debug(
|
|
375
|
+
f"Normalized {len(all_skills)} skills to {len(normalized_skills)} "
|
|
376
|
+
"(converted slashes to dashes)"
|
|
377
|
+
)
|
|
378
|
+
|
|
282
379
|
return normalized_skills
|
|
283
380
|
|
|
284
381
|
|
|
@@ -163,10 +163,22 @@ class SkillDiscoveryService:
|
|
|
163
163
|
skill_md_files = list(self.skills_dir.rglob("SKILL.md"))
|
|
164
164
|
|
|
165
165
|
# Also find legacy *.md files in top-level directory for backward compatibility
|
|
166
|
+
# Exclude common non-skill documentation files
|
|
167
|
+
excluded_filenames = {
|
|
168
|
+
"skill.md", # Case variations of SKILL.md
|
|
169
|
+
"readme.md",
|
|
170
|
+
"claude.md",
|
|
171
|
+
"contributing.md",
|
|
172
|
+
"changelog.md",
|
|
173
|
+
"license.md",
|
|
174
|
+
"authors.md",
|
|
175
|
+
"code_of_conduct.md",
|
|
176
|
+
}
|
|
177
|
+
|
|
166
178
|
legacy_md_files = [
|
|
167
179
|
f
|
|
168
180
|
for f in self.skills_dir.glob("*.md")
|
|
169
|
-
if f.name
|
|
181
|
+
if f.name.lower() not in excluded_filenames
|
|
170
182
|
]
|
|
171
183
|
|
|
172
184
|
all_skill_files = skill_md_files + legacy_md_files
|
|
@@ -255,7 +267,35 @@ class SkillDiscoveryService:
|
|
|
255
267
|
try:
|
|
256
268
|
frontmatter, body = self._extract_frontmatter(content)
|
|
257
269
|
except Exception as e:
|
|
258
|
-
|
|
270
|
+
# Only log as debug for documentation files to reduce noise
|
|
271
|
+
# Common documentation files (CLAUDE.md, README.md) are expected to lack skill frontmatter
|
|
272
|
+
relative_path = (
|
|
273
|
+
skill_file.relative_to(self.skills_dir)
|
|
274
|
+
if skill_file.is_relative_to(self.skills_dir)
|
|
275
|
+
else skill_file
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
# Check if this looks like a documentation file
|
|
279
|
+
is_documentation = any(
|
|
280
|
+
doc_pattern in skill_file.name.lower()
|
|
281
|
+
for doc_pattern in [
|
|
282
|
+
"readme",
|
|
283
|
+
"claude",
|
|
284
|
+
"contributing",
|
|
285
|
+
"changelog",
|
|
286
|
+
"license",
|
|
287
|
+
]
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
if is_documentation:
|
|
291
|
+
self.logger.debug(
|
|
292
|
+
f"Skipping documentation file {relative_path} (no skill frontmatter): {e}"
|
|
293
|
+
)
|
|
294
|
+
else:
|
|
295
|
+
# For actual skill files with invalid YAML, use warning level
|
|
296
|
+
self.logger.warning(
|
|
297
|
+
f"Failed to parse skill frontmatter in {relative_path}: {e}"
|
|
298
|
+
)
|
|
259
299
|
return None
|
|
260
300
|
|
|
261
301
|
# Validate required fields
|
|
@@ -354,10 +394,24 @@ class SkillDiscoveryService:
|
|
|
354
394
|
frontmatter_text = match.group(1)
|
|
355
395
|
body = match.group(2)
|
|
356
396
|
|
|
357
|
-
# Parse YAML
|
|
397
|
+
# Parse YAML with improved error handling
|
|
358
398
|
try:
|
|
359
399
|
frontmatter = yaml.safe_load(frontmatter_text)
|
|
360
400
|
except yaml.YAMLError as e:
|
|
401
|
+
# Provide more specific error message with context
|
|
402
|
+
error_line = getattr(e, "problem_mark", None)
|
|
403
|
+
if error_line:
|
|
404
|
+
line_num = error_line.line + 1
|
|
405
|
+
col_num = error_line.column + 1
|
|
406
|
+
# Extract problematic line for context
|
|
407
|
+
lines = frontmatter_text.split("\n")
|
|
408
|
+
problem_line = (
|
|
409
|
+
lines[error_line.line] if error_line.line < len(lines) else ""
|
|
410
|
+
)
|
|
411
|
+
raise ValueError(
|
|
412
|
+
f"Invalid YAML in frontmatter at line {line_num}, column {col_num}: {e.problem}\n"
|
|
413
|
+
f" Problematic line: {problem_line.strip()}"
|
|
414
|
+
) from e
|
|
361
415
|
raise ValueError(f"Invalid YAML in frontmatter: {e}") from e
|
|
362
416
|
|
|
363
417
|
if not isinstance(frontmatter, dict):
|
|
@@ -118,7 +118,8 @@ class HookEventHandler(BaseEventHandler):
|
|
|
118
118
|
self.server.active_sessions[session_id] = {
|
|
119
119
|
"session_id": session_id,
|
|
120
120
|
"start_time": datetime.now(timezone.utc).isoformat(),
|
|
121
|
-
"
|
|
121
|
+
"current_agent": agent_type, # Current active agent
|
|
122
|
+
"agents": [agent_type], # All agents used in this session
|
|
122
123
|
"status": ServiceState.RUNNING,
|
|
123
124
|
"prompt": data.get("prompt", "")[:100], # First 100 chars
|
|
124
125
|
"last_activity": datetime.now(timezone.utc).isoformat(),
|
|
@@ -169,7 +170,8 @@ class HookEventHandler(BaseEventHandler):
|
|
|
169
170
|
self.server.active_sessions[session_id] = {
|
|
170
171
|
"session_id": session_id,
|
|
171
172
|
"start_time": datetime.now(timezone.utc).isoformat(),
|
|
172
|
-
"
|
|
173
|
+
"current_agent": "pm", # Current active agent
|
|
174
|
+
"agents": ["pm"], # All agents used in this session
|
|
173
175
|
"status": ServiceState.RUNNING,
|
|
174
176
|
"prompt": data.get("prompt_text", "")[:100],
|
|
175
177
|
"working_directory": data.get("working_directory", ""),
|
|
@@ -200,11 +202,16 @@ class HookEventHandler(BaseEventHandler):
|
|
|
200
202
|
# Update session with new agent
|
|
201
203
|
if hasattr(self.server, "active_sessions"):
|
|
202
204
|
if session_id in self.server.active_sessions:
|
|
203
|
-
self.server.active_sessions[session_id]
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
205
|
+
session = self.server.active_sessions[session_id]
|
|
206
|
+
session["current_agent"] = agent_type
|
|
207
|
+
session["status"] = "delegated"
|
|
208
|
+
session["last_activity"] = datetime.now(timezone.utc).isoformat()
|
|
209
|
+
|
|
210
|
+
# Add to agents list if not already present
|
|
211
|
+
if "agents" not in session:
|
|
212
|
+
session["agents"] = []
|
|
213
|
+
if agent_type not in session["agents"]:
|
|
214
|
+
session["agents"].append(agent_type)
|
|
208
215
|
|
|
209
216
|
self.logger.debug(
|
|
210
217
|
f"Updated session delegation: session={session_id[:8]}..., agent={agent_type}"
|