claude-mpm 5.4.21__py3-none-any.whl → 5.4.59__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/BASE_AGENT.md +164 -0
- claude_mpm/agents/BASE_ENGINEER.md +658 -0
- claude_mpm/agents/MEMORY.md +1 -1
- claude_mpm/agents/PM_INSTRUCTIONS.md +771 -1019
- claude_mpm/agents/WORKFLOW.md +5 -254
- claude_mpm/agents/agent_loader.py +1 -1
- claude_mpm/agents/base_agent.json +31 -0
- claude_mpm/agents/frontmatter_validator.py +2 -2
- claude_mpm/cli/commands/agent_state_manager.py +10 -10
- claude_mpm/cli/commands/agents.py +9 -9
- claude_mpm/cli/commands/auto_configure.py +4 -4
- claude_mpm/cli/commands/configure.py +1 -1
- claude_mpm/cli/commands/configure_agent_display.py +12 -0
- claude_mpm/cli/commands/mpm_init/core.py +72 -0
- claude_mpm/cli/commands/postmortem.py +1 -1
- 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/interactive/agent_wizard.py +2 -2
- 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 +506 -180
- 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/config.py +2 -4
- claude_mpm/core/framework/loaders/agent_loader.py +1 -1
- claude_mpm/core/framework/loaders/instruction_loader.py +52 -11
- claude_mpm/core/optimized_startup.py +61 -0
- claude_mpm/core/shared/config_loader.py +3 -1
- claude_mpm/core/unified_agent_registry.py +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/env.js +1 -0
- 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/4TdZjIqw.js +1 -0
- 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/BSNlmTZj.js +1 -0
- 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/NqQ1dWOy.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/RJiighC3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Vzk33B_K.js +2 -0
- 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.m1gL8KXf.js +1 -0
- 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 -0
- claude_mpm/dashboard/static/svelte-build/favicon.svg +7 -0
- claude_mpm/dashboard/static/svelte-build/index.html +36 -0
- 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/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/hook_handler.py +149 -1
- 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/hooks/claude_hooks/services/connection_manager.py +26 -6
- claude_mpm/hooks/kuzu_memory_hook.py +5 -5
- claude_mpm/init.py +276 -0
- claude_mpm/models/git_repository.py +3 -3
- claude_mpm/scripts/start_activity_logging.py +0 -0
- claude_mpm/services/agents/agent_builder.py +3 -3
- claude_mpm/services/agents/cache_git_manager.py +6 -6
- claude_mpm/services/agents/deployment/agent_deployment.py +29 -7
- claude_mpm/services/agents/deployment/agent_discovery_service.py +4 -2
- claude_mpm/services/agents/deployment/agent_format_converter.py +25 -13
- claude_mpm/services/agents/deployment/agent_template_builder.py +31 -19
- claude_mpm/services/agents/deployment/agents_directory_resolver.py +2 -2
- 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 +169 -26
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +98 -75
- claude_mpm/services/agents/git_source_manager.py +23 -4
- claude_mpm/services/agents/recommender.py +5 -3
- claude_mpm/services/agents/single_tier_deployment_service.py +2 -2
- claude_mpm/services/agents/sources/git_source_sync_service.py +121 -10
- claude_mpm/services/agents/startup_sync.py +22 -2
- claude_mpm/services/diagnostics/checks/agent_check.py +2 -2
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +1 -1
- claude_mpm/services/git/git_operations_service.py +8 -8
- claude_mpm/services/monitor/management/lifecycle.py +7 -1
- claude_mpm/services/monitor/server.py +473 -3
- 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/services/socketio/dashboard_server.py +1 -0
- claude_mpm/services/socketio/event_normalizer.py +37 -6
- claude_mpm/services/socketio/server/core.py +262 -123
- claude_mpm/skills/bundled/security-scanning.md +112 -0
- claude_mpm/skills/skill_manager.py +98 -3
- claude_mpm/templates/.pre-commit-config.yaml +112 -0
- claude_mpm/utils/agent_dependency_loader.py +14 -2
- claude_mpm/utils/agent_filters.py +1 -1
- claude_mpm/utils/migration.py +4 -4
- claude_mpm/utils/robust_installer.py +47 -3
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/METADATA +7 -4
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/RECORD +175 -81
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.21.dist-info → claude_mpm-5.4.59.dist-info}/top_level.txt +0 -0
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 = {}
|
|
@@ -34,7 +34,7 @@ class GitRepository:
|
|
|
34
34
|
def cache_path(self) -> Path:
|
|
35
35
|
"""Return cache directory path for this repository.
|
|
36
36
|
|
|
37
|
-
Cache structure: ~/.claude-mpm/cache/
|
|
37
|
+
Cache structure: ~/.claude-mpm/cache/agents/{owner}/{repo}/{subdirectory}/
|
|
38
38
|
|
|
39
39
|
Returns:
|
|
40
40
|
Absolute path to cache directory for this repository
|
|
@@ -45,10 +45,10 @@ class GitRepository:
|
|
|
45
45
|
... subdirectory="agents"
|
|
46
46
|
... )
|
|
47
47
|
>>> repo.cache_path
|
|
48
|
-
Path('/Users/user/.claude-mpm/cache/
|
|
48
|
+
Path('/Users/user/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents')
|
|
49
49
|
"""
|
|
50
50
|
home = Path.home()
|
|
51
|
-
base_cache = home / ".claude-mpm" / "cache" / "
|
|
51
|
+
base_cache = home / ".claude-mpm" / "cache" / "agents"
|
|
52
52
|
|
|
53
53
|
# Extract owner and repo from URL
|
|
54
54
|
owner, repo = self._parse_github_url(self.url)
|
|
File without changes
|
|
@@ -206,8 +206,8 @@ class AgentBuilderService:
|
|
|
206
206
|
"""
|
|
207
207
|
errors = []
|
|
208
208
|
|
|
209
|
-
# Required fields
|
|
210
|
-
required_fields = ["id", "name", "prompt"
|
|
209
|
+
# Required fields (model is optional - defaults to sonnet if not specified)
|
|
210
|
+
required_fields = ["id", "name", "prompt"]
|
|
211
211
|
for field in required_fields:
|
|
212
212
|
if field not in config:
|
|
213
213
|
errors.append(f"Missing required field: {field}")
|
|
@@ -219,7 +219,7 @@ class AgentBuilderService:
|
|
|
219
219
|
except AgentDeploymentError as e:
|
|
220
220
|
errors.append(str(e))
|
|
221
221
|
|
|
222
|
-
# Validate model
|
|
222
|
+
# Validate model (only if present)
|
|
223
223
|
if "model" in config:
|
|
224
224
|
try:
|
|
225
225
|
self._validate_model(config["model"])
|
|
@@ -29,7 +29,7 @@ Error Handling:
|
|
|
29
29
|
|
|
30
30
|
Example:
|
|
31
31
|
>>> from pathlib import Path
|
|
32
|
-
>>> manager = CacheGitManager(Path.home() / ".claude-mpm/cache/
|
|
32
|
+
>>> manager = CacheGitManager(Path.home() / ".claude-mpm/cache/agents")
|
|
33
33
|
>>> if manager.is_git_repo():
|
|
34
34
|
... status = manager.get_status()
|
|
35
35
|
... print(f"Branch: {status['branch']}, Uncommitted: {len(status['uncommitted'])}")
|
|
@@ -76,7 +76,7 @@ class CacheGitManager:
|
|
|
76
76
|
timeout: Git command timeout in seconds (default: 30)
|
|
77
77
|
|
|
78
78
|
Example:
|
|
79
|
-
>>> cache_dir = Path.home() / ".claude-mpm/cache/
|
|
79
|
+
>>> cache_dir = Path.home() / ".claude-mpm/cache/agents"
|
|
80
80
|
>>> manager = CacheGitManager(cache_dir)
|
|
81
81
|
"""
|
|
82
82
|
self.cache_path = Path(cache_path)
|
|
@@ -105,12 +105,12 @@ class CacheGitManager:
|
|
|
105
105
|
|
|
106
106
|
Example:
|
|
107
107
|
>>> # Case 1: cache_path inside repo (searches upward)
|
|
108
|
-
>>> # cache_path: ~/.claude-mpm/cache/
|
|
109
|
-
>>> # Found at: ~/.claude-mpm/cache/
|
|
108
|
+
>>> # cache_path: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/agents
|
|
109
|
+
>>> # Found at: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents
|
|
110
110
|
|
|
111
111
|
>>> # Case 2: repo nested in cache_path (searches downward)
|
|
112
|
-
>>> # cache_path: ~/.claude-mpm/cache/
|
|
113
|
-
>>> # Found at: ~/.claude-mpm/cache/
|
|
112
|
+
>>> # cache_path: ~/.claude-mpm/cache/agents
|
|
113
|
+
>>> # Found at: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents
|
|
114
114
|
"""
|
|
115
115
|
# Strategy 1: Search upward (cache_path is inside repo)
|
|
116
116
|
current = self.cache_path
|
|
@@ -876,13 +876,13 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
876
876
|
user_agents_dir = potential_user_dir
|
|
877
877
|
self.logger.info(f"Found user agents at: {user_agents_dir}")
|
|
878
878
|
|
|
879
|
-
# Check for
|
|
880
|
-
|
|
879
|
+
# Check for agents cache (from Git sources)
|
|
880
|
+
agents_cache_dir = None
|
|
881
881
|
cache_dir = user_home / ".claude-mpm" / "cache"
|
|
882
|
-
|
|
883
|
-
if
|
|
884
|
-
|
|
885
|
-
self.logger.info(f"Found
|
|
882
|
+
potential_cache_dir = cache_dir / "agents"
|
|
883
|
+
if potential_cache_dir.exists():
|
|
884
|
+
agents_cache_dir = potential_cache_dir
|
|
885
|
+
self.logger.info(f"Found agents cache at: {agents_cache_dir}")
|
|
886
886
|
|
|
887
887
|
# Get agents with version comparison and cleanup (4-tier discovery)
|
|
888
888
|
agents_to_deploy, agent_sources, cleanup_results = (
|
|
@@ -890,7 +890,7 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
890
890
|
system_templates_dir=system_templates_dir,
|
|
891
891
|
project_agents_dir=project_agents_dir,
|
|
892
892
|
user_agents_dir=user_agents_dir,
|
|
893
|
-
|
|
893
|
+
agents_cache_dir=agents_cache_dir, # NEW: 4th tier
|
|
894
894
|
working_directory=self.working_directory,
|
|
895
895
|
excluded_agents=excluded_agents,
|
|
896
896
|
config=config,
|
|
@@ -898,6 +898,9 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
898
898
|
)
|
|
899
899
|
)
|
|
900
900
|
|
|
901
|
+
# Keep track of all enabled agents before filtering (for cleanup)
|
|
902
|
+
all_enabled_agents = agents_to_deploy.copy()
|
|
903
|
+
|
|
901
904
|
# Compare with deployed versions if agents directory exists
|
|
902
905
|
if agents_dir.exists():
|
|
903
906
|
comparison_results = self.multi_source_service.compare_deployed_versions(
|
|
@@ -954,6 +957,25 @@ class AgentDeploymentService(ConfigServiceBase, AgentDeploymentInterface):
|
|
|
954
957
|
f"All {len(comparison_results.get('up_to_date', []))} agents are up to date"
|
|
955
958
|
)
|
|
956
959
|
|
|
960
|
+
# Cleanup excluded agents (remove agents not in deployment list)
|
|
961
|
+
# CRITICAL: Use all_enabled_agents (before filtering for updates) to preserve up-to-date agents
|
|
962
|
+
# Bug fix (1M-XXX): Previously used filtered agents_to_deploy which could be empty,
|
|
963
|
+
# causing all agents to be removed when everything was up-to-date
|
|
964
|
+
exclusion_cleanup_results = self.multi_source_service.cleanup_excluded_agents(
|
|
965
|
+
deployed_agents_dir=agents_dir,
|
|
966
|
+
agents_to_deploy=all_enabled_agents,
|
|
967
|
+
)
|
|
968
|
+
|
|
969
|
+
# Add exclusion cleanup results to main cleanup results
|
|
970
|
+
if exclusion_cleanup_results.get("removed"):
|
|
971
|
+
cleanup_results.setdefault("excluded_removed", []).extend(
|
|
972
|
+
exclusion_cleanup_results["removed"]
|
|
973
|
+
)
|
|
974
|
+
self.logger.info(
|
|
975
|
+
f"Removed {len(exclusion_cleanup_results['removed'])} excluded agents: "
|
|
976
|
+
f"{', '.join(exclusion_cleanup_results['removed'])}"
|
|
977
|
+
)
|
|
978
|
+
|
|
957
979
|
# Convert to list of Path objects
|
|
958
980
|
template_files = list(agents_to_deploy.values())
|
|
959
981
|
|
|
@@ -248,7 +248,9 @@ class AgentDiscoveryService:
|
|
|
248
248
|
return agent_info
|
|
249
249
|
|
|
250
250
|
except yaml.YAMLError as e:
|
|
251
|
-
self.logger.
|
|
251
|
+
self.logger.warning(
|
|
252
|
+
f"Invalid YAML frontmatter in {template_file.name}: {e}"
|
|
253
|
+
)
|
|
252
254
|
return None
|
|
253
255
|
except Exception as e:
|
|
254
256
|
self.logger.error(
|
|
@@ -431,7 +433,7 @@ class AgentDiscoveryService:
|
|
|
431
433
|
return True
|
|
432
434
|
|
|
433
435
|
except yaml.YAMLError:
|
|
434
|
-
self.logger.
|
|
436
|
+
self.logger.warning(
|
|
435
437
|
f"Invalid YAML frontmatter in template: {template_file.name}"
|
|
436
438
|
)
|
|
437
439
|
return False
|
|
@@ -137,8 +137,8 @@ class AgentFormatConverter:
|
|
|
137
137
|
else:
|
|
138
138
|
pass
|
|
139
139
|
|
|
140
|
-
# Extract additional fields
|
|
141
|
-
model = self.extract_yaml_field(yaml_content, "model")
|
|
140
|
+
# Extract additional fields - model is optional (Claude Code uses conversation model if not set)
|
|
141
|
+
model = self.extract_yaml_field(yaml_content, "model") # None if not specified
|
|
142
142
|
author = (
|
|
143
143
|
self.extract_yaml_field(yaml_content, "author")
|
|
144
144
|
or "claude-mpm@anthropic.com"
|
|
@@ -147,7 +147,7 @@ class AgentFormatConverter:
|
|
|
147
147
|
# Extract instructions from YAML content
|
|
148
148
|
instructions = self._extract_instructions_from_yaml(yaml_content, agent_name)
|
|
149
149
|
|
|
150
|
-
# Map model names to Claude Code format
|
|
150
|
+
# Map model names to Claude Code format (only if model is specified)
|
|
151
151
|
model_map = {
|
|
152
152
|
"claude-3-5-sonnet-20241022": "sonnet",
|
|
153
153
|
"claude-3-5-sonnet": "sonnet",
|
|
@@ -159,7 +159,8 @@ class AgentFormatConverter:
|
|
|
159
159
|
"opus": "opus",
|
|
160
160
|
}
|
|
161
161
|
|
|
162
|
-
|
|
162
|
+
# Only map model if it's not None (preserve None for agents without model field)
|
|
163
|
+
mapped_model = model_map.get(model, model) if model is not None else None
|
|
163
164
|
|
|
164
165
|
# Create multiline description with example (Claude Code format)
|
|
165
166
|
multiline_description = f"""{description}
|
|
@@ -172,16 +173,27 @@ assistant: "I'll use the {name} agent to provide specialized assistance."
|
|
|
172
173
|
|
|
173
174
|
# Build new YAML frontmatter - Claude Code compatible format
|
|
174
175
|
# NOTE: Removed tags field and other non-essential fields for Claude Code compatibility
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
author: "{author}"
|
|
182
|
-
---
|
|
176
|
+
frontmatter_lines = [
|
|
177
|
+
"---",
|
|
178
|
+
f"name: {name}",
|
|
179
|
+
"description: |",
|
|
180
|
+
f" {self._indent_text(multiline_description, 2)}",
|
|
181
|
+
]
|
|
183
182
|
|
|
184
|
-
|
|
183
|
+
# Only include model field if explicitly set in source
|
|
184
|
+
if mapped_model is not None:
|
|
185
|
+
frontmatter_lines.append(f"model: {mapped_model}")
|
|
186
|
+
|
|
187
|
+
frontmatter_lines.extend(
|
|
188
|
+
[
|
|
189
|
+
f'version: "{version}"',
|
|
190
|
+
f'author: "{author}"',
|
|
191
|
+
"---",
|
|
192
|
+
"",
|
|
193
|
+
]
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
new_frontmatter = "\n".join(frontmatter_lines)
|
|
185
197
|
|
|
186
198
|
return new_frontmatter + instructions
|
|
187
199
|
|
|
@@ -135,8 +135,8 @@ class AgentTemplateBuilder:
|
|
|
135
135
|
break
|
|
136
136
|
|
|
137
137
|
# Stop at common repository root indicators (check AFTER finding BASE-AGENT.md)
|
|
138
|
-
#
|
|
139
|
-
if current_dir.name in [".claude-mpm", "
|
|
138
|
+
# Stop at cache root or .claude-mpm directory
|
|
139
|
+
if current_dir.name in [".claude-mpm", "cache"]:
|
|
140
140
|
self.logger.debug(
|
|
141
141
|
f"Reached repository root indicator at: {current_dir}"
|
|
142
142
|
)
|
|
@@ -419,7 +419,7 @@ class AgentTemplateBuilder:
|
|
|
419
419
|
if non_standard:
|
|
420
420
|
self.logger.info(f"Using non-standard tools: {non_standard}")
|
|
421
421
|
|
|
422
|
-
# Extract model from template
|
|
422
|
+
# Extract model from template (no fallback - preserve None if not specified)
|
|
423
423
|
capabilities_model = (
|
|
424
424
|
capabilities.get("model") if isinstance(capabilities, dict) else None
|
|
425
425
|
)
|
|
@@ -428,7 +428,7 @@ class AgentTemplateBuilder:
|
|
|
428
428
|
template_data.get("model")
|
|
429
429
|
or capabilities_model
|
|
430
430
|
or template_data.get("configuration_fields", {}).get("model")
|
|
431
|
-
|
|
431
|
+
# No default fallback - preserve None if not set
|
|
432
432
|
)
|
|
433
433
|
|
|
434
434
|
# Convert tools list to comma-separated string (without spaces for compatibility)
|
|
@@ -448,11 +448,11 @@ class AgentTemplateBuilder:
|
|
|
448
448
|
"opus": "opus",
|
|
449
449
|
}
|
|
450
450
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
model
|
|
451
|
+
# Only map model if it's not None
|
|
452
|
+
if model is not None:
|
|
453
|
+
if model in model_map:
|
|
454
|
+
model = model_map[model]
|
|
455
|
+
# If model is specified but not in map, keep as-is (no default)
|
|
456
456
|
|
|
457
457
|
# Get response format from template or use base agent default
|
|
458
458
|
template_data.get("response", {}).get("format", "structured")
|
|
@@ -559,8 +559,9 @@ class AgentTemplateBuilder:
|
|
|
559
559
|
f"description: {self._format_description_for_yaml(description)}"
|
|
560
560
|
)
|
|
561
561
|
|
|
562
|
-
# Add model field (required for Claude Code)
|
|
563
|
-
|
|
562
|
+
# Add model field only if explicitly set (not required for Claude Code)
|
|
563
|
+
if model is not None:
|
|
564
|
+
frontmatter_lines.append(f"model: {model}")
|
|
564
565
|
|
|
565
566
|
# Add type field (important for agent categorization)
|
|
566
567
|
if agent_type and agent_type != "general":
|
|
@@ -718,21 +719,32 @@ Only include memories that are:
|
|
|
718
719
|
"description", f"{name} agent for specialized tasks"
|
|
719
720
|
)
|
|
720
721
|
|
|
721
|
-
# Get tools and model
|
|
722
|
+
# Get tools and model (no fallback for model)
|
|
722
723
|
raw_tools = merged_config.get("tools")
|
|
723
724
|
tools = self.normalize_tools_input(raw_tools)
|
|
724
|
-
model = merged_config.get("model"
|
|
725
|
+
model = merged_config.get("model") # No default - preserve None
|
|
725
726
|
|
|
726
727
|
# Format tools as YAML list
|
|
727
728
|
tools_yaml = self.format_yaml_list(tools, 2)
|
|
728
729
|
|
|
729
730
|
# Build YAML content with only essential fields
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
731
|
+
yaml_lines = [
|
|
732
|
+
f"name: {name}",
|
|
733
|
+
f"description: {description}",
|
|
734
|
+
]
|
|
735
|
+
|
|
736
|
+
# Only include model if explicitly set
|
|
737
|
+
if model is not None:
|
|
738
|
+
yaml_lines.append(f"model: {model}")
|
|
739
|
+
|
|
740
|
+
yaml_lines.extend(
|
|
741
|
+
[
|
|
742
|
+
"tools:",
|
|
743
|
+
tools_yaml,
|
|
744
|
+
]
|
|
745
|
+
)
|
|
746
|
+
|
|
747
|
+
return "\n".join(yaml_lines) + "\n"
|
|
736
748
|
|
|
737
749
|
def merge_narrative_fields(self, base_data: dict, template_data: dict) -> dict:
|
|
738
750
|
"""
|
|
@@ -8,7 +8,7 @@ DEPLOYMENT ARCHITECTURE:
|
|
|
8
8
|
|
|
9
9
|
Agent Source Locations (Discovery):
|
|
10
10
|
-----------------------------------
|
|
11
|
-
1. System Agents: ~/.claude-mpm/cache/
|
|
11
|
+
1. System Agents: ~/.claude-mpm/cache/agents/bobmatnyc/claude-mpm-agents/
|
|
12
12
|
- Synced from GitHub repository
|
|
13
13
|
- Read-only (managed by git pull)
|
|
14
14
|
- 44+ agents organized by category
|
|
@@ -39,7 +39,7 @@ Why Project-Level Deployment?
|
|
|
39
39
|
Example Flow:
|
|
40
40
|
-------------
|
|
41
41
|
1. User runs: claude-mpm agents deploy
|
|
42
|
-
2. Agents synced from GitHub → ~/.claude-mpm/cache/
|
|
42
|
+
2. Agents synced from GitHub → ~/.claude-mpm/cache/agents/
|
|
43
43
|
3. Agents deployed FROM cache → .claude/agents/
|
|
44
44
|
4. Claude Code discovers agents FROM .claude/agents/
|
|
45
45
|
|