claude-mpm 5.4.65__py3-none-any.whl → 5.4.96__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_FOUNDERS_OUTPUT_STYLE.md +405 -0
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +66 -241
- claude_mpm/agents/CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md +107 -1928
- claude_mpm/agents/PM_INSTRUCTIONS.md +82 -686
- 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 +526 -0
- claude_mpm/cli/commands/configure.py +620 -21
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/skills.py +166 -14
- claude_mpm/cli/executor.py +89 -0
- 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 +59 -1
- claude_mpm/cli/startup.py +184 -358
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/commands/mpm-session-resume.md +1 -1
- claude_mpm/constants.py +1 -0
- claude_mpm/core/claude_runner.py +2 -2
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/interactive_session.py +7 -7
- claude_mpm/core/output_style_manager.py +21 -13
- claude_mpm/core/unified_config.py +50 -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/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.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__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +216 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +28 -4
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_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 +20 -0
- 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/services/agents/cache_git_manager.py +1 -1
- 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/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 +317 -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 +177 -83
- claude_mpm/services/skills/git_skill_source_manager.py +5 -1
- claude_mpm/services/skills/selective_skill_deployer.py +114 -26
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- 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-session-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-teaching-mode/SKILL.md +657 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm/utils/agent_dependency_loader.py +4 -2
- claude_mpm/utils/robust_installer.py +10 -6
- claude_mpm-5.4.96.dist-info/METADATA +377 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.4.96.dist-info}/RECORD +153 -142
- 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-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm-5.4.65.dist-info/METADATA +0 -999
- /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
- /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.4.96.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.4.96.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.4.96.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.4.96.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.65.dist-info → claude_mpm-5.4.96.dist-info}/top_level.txt +0 -0
claude_mpm/cli/startup.py
CHANGED
|
@@ -191,7 +191,8 @@ def should_skip_background_services(args, processed_argv):
|
|
|
191
191
|
skip_commands = ["--version", "-v", "--help", "-h"]
|
|
192
192
|
return any(cmd in (processed_argv or sys.argv[1:]) for cmd in skip_commands) or (
|
|
193
193
|
hasattr(args, "command")
|
|
194
|
-
and args.command
|
|
194
|
+
and args.command
|
|
195
|
+
in ["info", "doctor", "config", "mcp", "configure", "hook-errors", "autotodos"]
|
|
195
196
|
)
|
|
196
197
|
|
|
197
198
|
|
|
@@ -234,7 +235,7 @@ def deploy_bundled_skills():
|
|
|
234
235
|
if not skills_config.get("auto_deploy", True):
|
|
235
236
|
# Auto-deploy disabled, skip silently
|
|
236
237
|
return
|
|
237
|
-
except Exception:
|
|
238
|
+
except Exception: # nosec B110
|
|
238
239
|
# If config loading fails, assume auto-deploy is enabled (default)
|
|
239
240
|
pass
|
|
240
241
|
|
|
@@ -308,68 +309,60 @@ def deploy_output_style_on_startup():
|
|
|
308
309
|
communication without emojis and exclamation points. Styles are project-specific
|
|
309
310
|
to allow different projects to have different communication styles.
|
|
310
311
|
|
|
311
|
-
DESIGN DECISION: This is non-blocking and idempotent. Deploys to
|
|
312
|
-
directory (
|
|
313
|
-
|
|
312
|
+
DESIGN DECISION: This is non-blocking and idempotent. Deploys to user-level
|
|
313
|
+
directory (~/.claude/output-styles/) which is the official Claude Code location
|
|
314
|
+
for custom output styles.
|
|
314
315
|
|
|
315
|
-
Deploys
|
|
316
|
-
- claude-mpm
|
|
317
|
-
- claude-mpm-
|
|
316
|
+
Deploys all styles:
|
|
317
|
+
- claude-mpm.md (professional mode)
|
|
318
|
+
- claude-mpm-teacher.md (teaching mode)
|
|
319
|
+
- claude-mpm-founders.md (founders mode)
|
|
318
320
|
"""
|
|
319
321
|
try:
|
|
320
|
-
import
|
|
321
|
-
from pathlib import Path
|
|
322
|
+
from ..core.output_style_manager import OutputStyleManager
|
|
322
323
|
|
|
323
|
-
#
|
|
324
|
-
|
|
325
|
-
professional_source = package_dir / "CLAUDE_MPM_OUTPUT_STYLE.md"
|
|
326
|
-
teacher_source = package_dir / "CLAUDE_MPM_TEACHER_OUTPUT_STYLE.md"
|
|
327
|
-
|
|
328
|
-
# Target directory (USER-LEVEL for global availability)
|
|
329
|
-
# Claude Code reads output styles from ~/.claude/settings/output-styles/
|
|
330
|
-
user_home = Path.home()
|
|
331
|
-
output_styles_dir = user_home / ".claude" / "settings" / "output-styles"
|
|
332
|
-
professional_target = output_styles_dir / "claude-mpm-style.md"
|
|
333
|
-
teacher_target = output_styles_dir / "claude-mpm-teach.md"
|
|
334
|
-
|
|
335
|
-
# Create directory if it doesn't exist
|
|
336
|
-
output_styles_dir.mkdir(parents=True, exist_ok=True)
|
|
337
|
-
|
|
338
|
-
# Check if already deployed AND up-to-date (compare sizes to detect changes)
|
|
339
|
-
professional_up_to_date = (
|
|
340
|
-
professional_target.exists()
|
|
341
|
-
and professional_source.exists()
|
|
342
|
-
and professional_target.stat().st_size == professional_source.stat().st_size
|
|
343
|
-
)
|
|
344
|
-
teacher_up_to_date = (
|
|
345
|
-
teacher_target.exists()
|
|
346
|
-
and teacher_source.exists()
|
|
347
|
-
and teacher_target.stat().st_size == teacher_source.stat().st_size
|
|
348
|
-
)
|
|
324
|
+
# Initialize the output style manager
|
|
325
|
+
manager = OutputStyleManager()
|
|
349
326
|
|
|
350
|
-
if
|
|
327
|
+
# Check if Claude Code version supports output styles (>= 1.0.83)
|
|
328
|
+
if not manager.supports_output_styles():
|
|
329
|
+
# Skip deployment for older versions
|
|
330
|
+
# The manager will fall back to injecting content directly
|
|
331
|
+
return
|
|
332
|
+
|
|
333
|
+
# Check if all styles are already deployed and up-to-date
|
|
334
|
+
all_up_to_date = True
|
|
335
|
+
for style_config in manager.styles.values():
|
|
336
|
+
source_path = style_config["source"]
|
|
337
|
+
target_path = style_config["target"]
|
|
338
|
+
|
|
339
|
+
if not (
|
|
340
|
+
target_path.exists()
|
|
341
|
+
and source_path.exists()
|
|
342
|
+
and target_path.stat().st_size == source_path.stat().st_size
|
|
343
|
+
):
|
|
344
|
+
all_up_to_date = False
|
|
345
|
+
break
|
|
346
|
+
|
|
347
|
+
if all_up_to_date:
|
|
351
348
|
# Show feedback that output styles are ready
|
|
352
349
|
print("✓ Output styles ready", flush=True)
|
|
353
350
|
return
|
|
354
351
|
|
|
355
|
-
# Deploy
|
|
356
|
-
|
|
357
|
-
if professional_source.exists():
|
|
358
|
-
shutil.copy2(professional_source, professional_target)
|
|
359
|
-
deployed_count += 1
|
|
352
|
+
# Deploy all styles using the manager
|
|
353
|
+
results = manager.deploy_all_styles(activate_default=True)
|
|
360
354
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
deployed_count += 1
|
|
355
|
+
# Count successful deployments
|
|
356
|
+
deployed_count = sum(1 for success in results.values() if success)
|
|
364
357
|
|
|
365
358
|
if deployed_count > 0:
|
|
366
359
|
print(f"✓ Output styles deployed ({deployed_count} styles)", flush=True)
|
|
367
360
|
else:
|
|
368
|
-
#
|
|
361
|
+
# Deployment failed - log but don't fail startup
|
|
369
362
|
from ..core.logger import get_logger
|
|
370
363
|
|
|
371
364
|
logger = get_logger("cli")
|
|
372
|
-
logger.debug("
|
|
365
|
+
logger.debug("Failed to deploy any output styles")
|
|
373
366
|
|
|
374
367
|
except Exception as e:
|
|
375
368
|
# Non-critical - log but don't fail startup
|
|
@@ -458,7 +451,7 @@ def _cleanup_orphaned_agents(deploy_target: Path, deployed_agents: list[str]) ->
|
|
|
458
451
|
return removed_count
|
|
459
452
|
|
|
460
453
|
|
|
461
|
-
def sync_remote_agents_on_startup():
|
|
454
|
+
def sync_remote_agents_on_startup(force_sync: bool = False):
|
|
462
455
|
"""
|
|
463
456
|
Synchronize agent templates from remote sources on startup.
|
|
464
457
|
|
|
@@ -476,6 +469,9 @@ def sync_remote_agents_on_startup():
|
|
|
476
469
|
3. Cleanup orphaned agents (ours but no longer deployed) - Phase 3
|
|
477
470
|
4. Cleanup legacy agent cache directories (after sync/deployment) - Phase 4
|
|
478
471
|
5. Log deployment results
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
479
475
|
"""
|
|
480
476
|
# DEPRECATED: Legacy warning - no-op function, kept for compatibility
|
|
481
477
|
check_legacy_cache()
|
|
@@ -486,7 +482,6 @@ def sync_remote_agents_on_startup():
|
|
|
486
482
|
from pathlib import Path
|
|
487
483
|
|
|
488
484
|
from ..core.shared.config_loader import ConfigLoader
|
|
489
|
-
from ..services.agents.deployment.agent_deployment import AgentDeploymentService
|
|
490
485
|
from ..services.agents.startup_sync import sync_agents_on_startup
|
|
491
486
|
from ..services.profile_manager import ProfileManager
|
|
492
487
|
from ..utils.progress import ProgressBar
|
|
@@ -511,7 +506,7 @@ def sync_remote_agents_on_startup():
|
|
|
511
506
|
)
|
|
512
507
|
|
|
513
508
|
# Phase 1: Sync files from Git sources
|
|
514
|
-
result = sync_agents_on_startup()
|
|
509
|
+
result = sync_agents_on_startup(force_refresh=force_sync)
|
|
515
510
|
|
|
516
511
|
# Only proceed with deployment if sync was enabled and ran
|
|
517
512
|
if result.get("enabled") and result.get("sources_synced", 0) > 0:
|
|
@@ -534,305 +529,89 @@ def sync_remote_agents_on_startup():
|
|
|
534
529
|
logger.warning(f"Agent sync completed with {len(errors)} errors")
|
|
535
530
|
|
|
536
531
|
# Phase 2: Deploy agents from cache to ~/.claude/agents/
|
|
537
|
-
#
|
|
532
|
+
# Use reconciliation service to respect configuration.yaml settings
|
|
538
533
|
try:
|
|
539
|
-
# Initialize deployment service with profile-filtered configuration
|
|
540
|
-
from ..core.config import Config
|
|
541
|
-
|
|
542
|
-
deploy_config = None
|
|
543
|
-
if active_profile and profile_manager.active_profile:
|
|
544
|
-
# Create config with excluded agents based on profile
|
|
545
|
-
# Get all agents that should be excluded (not in enabled list)
|
|
546
|
-
from pathlib import Path
|
|
547
|
-
|
|
548
|
-
cache_dir = Path.home() / ".claude-mpm" / "cache" / "agents"
|
|
549
|
-
if cache_dir.exists():
|
|
550
|
-
# Find all agent files
|
|
551
|
-
# Supports both flat cache and {owner}/{repo}/agents/ structure
|
|
552
|
-
all_agent_files = [
|
|
553
|
-
f
|
|
554
|
-
for f in cache_dir.rglob("*.md")
|
|
555
|
-
if "/agents/" in str(f)
|
|
556
|
-
and f.stem.lower() != "base-agent"
|
|
557
|
-
and f.name.lower()
|
|
558
|
-
not in {"readme.md", "changelog.md", "contributing.md"}
|
|
559
|
-
]
|
|
560
|
-
|
|
561
|
-
# Build exclusion list for agents not in profile
|
|
562
|
-
excluded_agents = []
|
|
563
|
-
for agent_file in all_agent_files:
|
|
564
|
-
agent_name = agent_file.stem
|
|
565
|
-
if not profile_manager.is_agent_enabled(agent_name):
|
|
566
|
-
excluded_agents.append(agent_name)
|
|
567
|
-
|
|
568
|
-
if excluded_agents:
|
|
569
|
-
# Get singleton config and update with profile settings
|
|
570
|
-
# BUGFIX: Config is a singleton that ignores dict parameter if already initialized.
|
|
571
|
-
# Creating Config({...}) doesn't store excluded_agents - use set() instead.
|
|
572
|
-
deploy_config = Config()
|
|
573
|
-
deploy_config.set(
|
|
574
|
-
"agent_deployment.excluded_agents", excluded_agents
|
|
575
|
-
)
|
|
576
|
-
deploy_config.set(
|
|
577
|
-
"agent_deployment.filter_non_mpm_agents", False
|
|
578
|
-
)
|
|
579
|
-
deploy_config.set("agent_deployment.case_sensitive", False)
|
|
580
|
-
deploy_config.set(
|
|
581
|
-
"agent_deployment.exclude_dependencies", False
|
|
582
|
-
)
|
|
583
|
-
logger.info(
|
|
584
|
-
f"Profile '{active_profile}': Excluding {len(excluded_agents)} agents from deployment"
|
|
585
|
-
)
|
|
586
|
-
|
|
587
|
-
deployment_service = AgentDeploymentService(config=deploy_config)
|
|
588
|
-
|
|
589
|
-
# Count agents in cache to show accurate progress
|
|
590
534
|
from pathlib import Path
|
|
591
535
|
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
# BUGFIX (cache-count-inflation): Clean up stale cache files
|
|
597
|
-
# from old repositories before counting to prevent inflated counts.
|
|
598
|
-
# Issue: Old caches like bobmatnyc/claude-mpm-agents/agents/
|
|
599
|
-
# were counted alongside current agents, inflating count
|
|
600
|
-
# from 44 to 85.
|
|
601
|
-
#
|
|
602
|
-
# Solution: Remove files with nested /agents/ paths
|
|
603
|
-
# (e.g., cache/agents/user/repo/agents/...)
|
|
604
|
-
# Keep only current agents (e.g., cache/agents/engineer/...)
|
|
605
|
-
removed_count = 0
|
|
606
|
-
stale_dirs = set()
|
|
607
|
-
|
|
608
|
-
for md_file in cache_dir.rglob("*.md"):
|
|
609
|
-
# Stale cache files have multiple /agents/ in their path RELATIVE to cache_dir
|
|
610
|
-
# Current: cache/agents/bobmatnyc/claude-mpm-agents/agents/engineer/...
|
|
611
|
-
# (1 occurrence in relative path: /agents/)
|
|
612
|
-
# Old flat: cache/agents/engineer/...
|
|
613
|
-
# (0 occurrences in relative path - no repo structure)
|
|
614
|
-
# The issue: str(md_file).count("/agents/") counts BOTH cache/agents/ AND repo/agents/
|
|
615
|
-
# Fix: Count /agents/ in path RELATIVE to cache_dir (after cache/agents/)
|
|
616
|
-
relative_path = str(md_file.relative_to(cache_dir))
|
|
617
|
-
if relative_path.count("/agents/") > 1:
|
|
618
|
-
# Track parent directory for cleanup
|
|
619
|
-
# Extract subdirectory under cache/agents/
|
|
620
|
-
# (e.g., "bobmatnyc")
|
|
621
|
-
parts = md_file.parts
|
|
622
|
-
cache_agents_idx = parts.index("agents")
|
|
623
|
-
if cache_agents_idx + 1 < len(parts):
|
|
624
|
-
stale_subdir = parts[cache_agents_idx + 1]
|
|
625
|
-
# Only remove if it's not a known category directory
|
|
626
|
-
if stale_subdir not in [
|
|
627
|
-
"engineer",
|
|
628
|
-
"ops",
|
|
629
|
-
"qa",
|
|
630
|
-
"universal",
|
|
631
|
-
"documentation",
|
|
632
|
-
"claude-mpm",
|
|
633
|
-
"security",
|
|
634
|
-
]:
|
|
635
|
-
stale_dirs.add(cache_dir / stale_subdir)
|
|
636
|
-
|
|
637
|
-
md_file.unlink()
|
|
638
|
-
removed_count += 1
|
|
639
|
-
|
|
640
|
-
# Remove empty stale directories
|
|
641
|
-
for stale_dir in stale_dirs:
|
|
642
|
-
if stale_dir.exists() and stale_dir.is_dir():
|
|
643
|
-
try:
|
|
644
|
-
# Remove directory and all contents
|
|
645
|
-
import shutil
|
|
646
|
-
|
|
647
|
-
shutil.rmtree(stale_dir)
|
|
648
|
-
except Exception:
|
|
649
|
-
pass # Ignore cleanup errors
|
|
650
|
-
|
|
651
|
-
if removed_count > 0:
|
|
652
|
-
from loguru import logger
|
|
653
|
-
|
|
654
|
-
logger.info(
|
|
655
|
-
f"Cleaned up {removed_count} stale cache files "
|
|
656
|
-
f"from old repositories"
|
|
657
|
-
)
|
|
536
|
+
from ..core.unified_config import UnifiedConfig
|
|
537
|
+
from ..services.agents.deployment.startup_reconciliation import (
|
|
538
|
+
perform_startup_reconciliation,
|
|
539
|
+
)
|
|
658
540
|
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
# (
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
"
|
|
672
|
-
"response_format.md",
|
|
673
|
-
"ticket_completeness_examples.md",
|
|
674
|
-
"validation_templates.md",
|
|
675
|
-
"git_file_tracking.md",
|
|
676
|
-
}
|
|
677
|
-
# Documentation files to exclude (by filename)
|
|
678
|
-
doc_files = {
|
|
679
|
-
"readme.md",
|
|
680
|
-
"changelog.md",
|
|
681
|
-
"contributing.md",
|
|
682
|
-
"implementation-summary.md",
|
|
683
|
-
"reorganization-plan.md",
|
|
684
|
-
"auto-deploy-index.md",
|
|
685
|
-
}
|
|
686
|
-
|
|
687
|
-
# Find all markdown files (after cleanup)
|
|
688
|
-
all_md_files = list(cache_dir.rglob("*.md"))
|
|
689
|
-
|
|
690
|
-
# Filter to only agent files:
|
|
691
|
-
# 1. Must have "/agents/" in path (current structure supports
|
|
692
|
-
# both flat and {owner}/{repo}/agents/ patterns)
|
|
693
|
-
# 2. Must not be in PM templates or doc files
|
|
694
|
-
# 3. Exclude BASE-AGENT.md which is not a deployable agent
|
|
695
|
-
# 4. Exclude build artifacts (dist/, build/, .cache/)
|
|
696
|
-
# to prevent double-counting
|
|
697
|
-
agent_files = [
|
|
698
|
-
f
|
|
699
|
-
for f in all_md_files
|
|
700
|
-
if
|
|
701
|
-
(
|
|
702
|
-
# Must be in an agent directory
|
|
703
|
-
# Supports: cache/agents/{category}/... (flat)
|
|
704
|
-
# Supports: cache/agents/{owner}/{repo}/agents/{category}/... (GitHub sync)
|
|
705
|
-
"/agents/" in str(f)
|
|
706
|
-
# Exclude PM templates, doc files, and BASE-AGENT
|
|
707
|
-
and f.name.lower() not in pm_templates
|
|
708
|
-
and f.name.lower() not in doc_files
|
|
709
|
-
and f.name.lower() != "base-agent.md"
|
|
710
|
-
# Exclude build artifacts (prevents double-counting
|
|
711
|
-
# source + built files)
|
|
712
|
-
and not any(
|
|
713
|
-
part in str(f).split("/")
|
|
714
|
-
for part in ["dist", "build", ".cache"]
|
|
715
|
-
)
|
|
716
|
-
)
|
|
717
|
-
]
|
|
718
|
-
agent_count = len(agent_files)
|
|
719
|
-
|
|
720
|
-
if agent_count > 0:
|
|
721
|
-
# Deploy agents to project-level directory where Claude Code expects them
|
|
722
|
-
deploy_target = Path.cwd() / ".claude" / "agents"
|
|
723
|
-
deployment_result = deployment_service.deploy_agents(
|
|
724
|
-
target_dir=deploy_target,
|
|
725
|
-
force_rebuild=False, # Only deploy if versions differ
|
|
726
|
-
deployment_mode="update", # Version-aware updates
|
|
727
|
-
config=deploy_config, # Pass config to respect profile filtering
|
|
541
|
+
# Load configuration
|
|
542
|
+
unified_config = UnifiedConfig()
|
|
543
|
+
|
|
544
|
+
# Override with profile settings if active
|
|
545
|
+
if active_profile and profile_manager.active_profile:
|
|
546
|
+
# Get enabled agents from profile (returns Set[str])
|
|
547
|
+
profile_enabled_agents = (
|
|
548
|
+
profile_manager.active_profile.get_enabled_agents()
|
|
549
|
+
)
|
|
550
|
+
# Update config with profile's enabled list (convert Set to List)
|
|
551
|
+
unified_config.agents.enabled = list(profile_enabled_agents)
|
|
552
|
+
logger.info(
|
|
553
|
+
f"Profile '{active_profile}': Using {len(profile_enabled_agents)} enabled agents"
|
|
728
554
|
)
|
|
729
555
|
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
agent_count_in_target = len(
|
|
743
|
-
[
|
|
744
|
-
f
|
|
745
|
-
for f in existing_agents
|
|
746
|
-
if not f.name.startswith(("README", "INSTRUCTIONS"))
|
|
747
|
-
]
|
|
748
|
-
)
|
|
749
|
-
if agent_count_in_target > 0:
|
|
750
|
-
# All agents already deployed - count them as skipped
|
|
751
|
-
skipped = agent_count_in_target
|
|
752
|
-
total_configured = agent_count_in_target
|
|
556
|
+
# Perform reconciliation to deploy configured agents
|
|
557
|
+
project_path = Path.cwd()
|
|
558
|
+
agent_result, _skill_result = perform_startup_reconciliation(
|
|
559
|
+
project_path=project_path, config=unified_config, silent=False
|
|
560
|
+
)
|
|
561
|
+
|
|
562
|
+
# Display results with progress bar
|
|
563
|
+
total_operations = (
|
|
564
|
+
len(agent_result.deployed)
|
|
565
|
+
+ len(agent_result.removed)
|
|
566
|
+
+ len(agent_result.unchanged)
|
|
567
|
+
)
|
|
753
568
|
|
|
754
|
-
|
|
569
|
+
if total_operations > 0:
|
|
755
570
|
deploy_progress = ProgressBar(
|
|
756
|
-
total=
|
|
571
|
+
total=total_operations,
|
|
757
572
|
prefix="Deploying agents",
|
|
758
573
|
show_percentage=True,
|
|
759
574
|
show_counter=True,
|
|
760
575
|
)
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
)
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
576
|
+
deploy_progress.update(total_operations)
|
|
577
|
+
|
|
578
|
+
# Build summary message
|
|
579
|
+
deployed = len(agent_result.deployed)
|
|
580
|
+
removed = len(agent_result.removed)
|
|
581
|
+
unchanged = len(agent_result.unchanged)
|
|
582
|
+
|
|
583
|
+
summary_parts = []
|
|
584
|
+
if deployed > 0:
|
|
585
|
+
summary_parts.append(f"{deployed} new")
|
|
586
|
+
if removed > 0:
|
|
587
|
+
summary_parts.append(f"{removed} removed")
|
|
588
|
+
if unchanged > 0:
|
|
589
|
+
summary_parts.append(f"{unchanged} unchanged")
|
|
590
|
+
|
|
591
|
+
summary = f"Complete: {', '.join(summary_parts)}"
|
|
592
|
+
deploy_progress.finish(summary)
|
|
593
|
+
|
|
594
|
+
# Display errors if any
|
|
595
|
+
if agent_result.errors:
|
|
596
|
+
logger.warning(
|
|
597
|
+
f"Agent deployment completed with {len(agent_result.errors)} errors"
|
|
780
598
|
)
|
|
599
|
+
print("\n⚠️ Agent Deployment Errors:")
|
|
600
|
+
max_errors_to_show = 10
|
|
601
|
+
errors_to_display = agent_result.errors[:max_errors_to_show]
|
|
781
602
|
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
if deployed > 0 or updated > 0:
|
|
785
|
-
if removed > 0:
|
|
786
|
-
deploy_progress.finish(
|
|
787
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged, "
|
|
788
|
-
f"{removed} removed ({total_configured} configured from {agent_count} files in cache)"
|
|
789
|
-
)
|
|
790
|
-
else:
|
|
791
|
-
deploy_progress.finish(
|
|
792
|
-
f"Complete: {deployed} new, {updated} updated, {skipped} unchanged "
|
|
793
|
-
f"({total_configured} configured from {agent_count} files in cache)"
|
|
794
|
-
)
|
|
795
|
-
elif removed > 0:
|
|
796
|
-
deploy_progress.finish(
|
|
797
|
-
f"Complete: {total_configured} agents deployed, "
|
|
798
|
-
f"{removed} removed ({agent_count} files in cache)"
|
|
799
|
-
)
|
|
800
|
-
else:
|
|
801
|
-
deploy_progress.finish(
|
|
802
|
-
f"Complete: {total_configured} agents deployed "
|
|
803
|
-
f"({agent_count} files in cache)"
|
|
804
|
-
)
|
|
805
|
-
|
|
806
|
-
# Display deployment errors to user (not just logs)
|
|
807
|
-
deploy_errors = deployment_result.get("errors", [])
|
|
808
|
-
if deploy_errors:
|
|
809
|
-
# Log for debugging
|
|
810
|
-
logger.warning(
|
|
811
|
-
f"Agent deployment completed with {len(deploy_errors)} errors: {deploy_errors}"
|
|
812
|
-
)
|
|
603
|
+
for error in errors_to_display:
|
|
604
|
+
print(f" - {error}")
|
|
813
605
|
|
|
814
|
-
|
|
815
|
-
|
|
606
|
+
if len(agent_result.errors) > max_errors_to_show:
|
|
607
|
+
remaining = len(agent_result.errors) - max_errors_to_show
|
|
608
|
+
print(f" ... and {remaining} more error(s)")
|
|
816
609
|
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
# Format error message for readability
|
|
823
|
-
# Errors typically come as strings like "agent.md: Error message"
|
|
824
|
-
print(f" - {error}")
|
|
825
|
-
|
|
826
|
-
# If more errors exist, show count
|
|
827
|
-
if len(deploy_errors) > max_errors_to_show:
|
|
828
|
-
remaining = len(deploy_errors) - max_errors_to_show
|
|
829
|
-
print(f" ... and {remaining} more error(s)")
|
|
830
|
-
|
|
831
|
-
# Show summary message
|
|
832
|
-
print(
|
|
833
|
-
f"\n❌ Failed to deploy {len(deploy_errors)} agent(s). Please check the error messages above."
|
|
834
|
-
)
|
|
835
|
-
print(" Run with --verbose for detailed error information.\n")
|
|
610
|
+
print(
|
|
611
|
+
f"\n❌ Failed to deploy {len(agent_result.errors)} agent(s). "
|
|
612
|
+
"Please check the error messages above."
|
|
613
|
+
)
|
|
614
|
+
print(" Run with --verbose for detailed error information.\n")
|
|
836
615
|
|
|
837
616
|
except Exception as e:
|
|
838
617
|
# Deployment failure shouldn't block startup
|
|
@@ -857,11 +636,11 @@ def sync_remote_agents_on_startup():
|
|
|
857
636
|
# Cleanup legacy cache even if sync failed
|
|
858
637
|
try:
|
|
859
638
|
cleanup_legacy_agent_cache()
|
|
860
|
-
except Exception:
|
|
639
|
+
except Exception: # nosec B110
|
|
861
640
|
pass # Ignore cleanup errors
|
|
862
641
|
|
|
863
642
|
|
|
864
|
-
def sync_remote_skills_on_startup():
|
|
643
|
+
def sync_remote_skills_on_startup(force_sync: bool = False):
|
|
865
644
|
"""
|
|
866
645
|
Synchronize skill templates from remote sources on startup.
|
|
867
646
|
|
|
@@ -879,6 +658,9 @@ def sync_remote_skills_on_startup():
|
|
|
879
658
|
4. Apply profile filtering if active
|
|
880
659
|
5. Deploy resolved skills to ~/.claude/skills/ - Phase 2 progress bar
|
|
881
660
|
6. Log deployment results with source indication
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
882
664
|
"""
|
|
883
665
|
try:
|
|
884
666
|
from pathlib import Path
|
|
@@ -984,7 +766,7 @@ def sync_remote_skills_on_startup():
|
|
|
984
766
|
|
|
985
767
|
# Sync all sources with progress callback
|
|
986
768
|
results = manager.sync_all_sources(
|
|
987
|
-
force=
|
|
769
|
+
force=force_sync, progress_callback=sync_progress.update
|
|
988
770
|
)
|
|
989
771
|
|
|
990
772
|
# Finish sync progress bar with clear breakdown
|
|
@@ -1093,7 +875,7 @@ def sync_remote_skills_on_startup():
|
|
|
1093
875
|
# Deploy to project-local directory with cleanup
|
|
1094
876
|
deployment_result = manager.deploy_skills(
|
|
1095
877
|
target_dir=Path.cwd() / ".claude" / "skills",
|
|
1096
|
-
force=
|
|
878
|
+
force=force_sync,
|
|
1097
879
|
# CRITICAL FIX: Empty list should mean "deploy no skills", not "deploy all"
|
|
1098
880
|
# When skills_to_deploy is [], we want skill_filter=set() NOT skill_filter=None
|
|
1099
881
|
# None means "no filtering" (deploy all), empty set means "filter to nothing"
|
|
@@ -1358,33 +1140,70 @@ def show_skill_summary():
|
|
|
1358
1140
|
|
|
1359
1141
|
|
|
1360
1142
|
def verify_and_show_pm_skills():
|
|
1361
|
-
"""Verify PM skills and display status.
|
|
1143
|
+
"""Verify PM skills and display status with enhanced validation.
|
|
1144
|
+
|
|
1145
|
+
WHY: PM skills are CRITICAL for PM agent operation. PM must KNOW if
|
|
1146
|
+
framework knowledge is unavailable at startup. Enhanced validation
|
|
1147
|
+
checks all required skills exist, are not corrupted, and auto-repairs
|
|
1148
|
+
if needed.
|
|
1362
1149
|
|
|
1363
|
-
|
|
1364
|
-
|
|
1150
|
+
Shows deployment status:
|
|
1151
|
+
- "✓ PM skills: 8/8 verified" if all required skills are valid
|
|
1152
|
+
- "⚠ PM skills: 2 missing, auto-repairing..." if issues detected
|
|
1153
|
+
- Non-blocking but visible warning if auto-repair fails
|
|
1365
1154
|
"""
|
|
1366
1155
|
try:
|
|
1367
1156
|
from pathlib import Path
|
|
1368
1157
|
|
|
1369
|
-
from ..services.pm_skills_deployer import
|
|
1158
|
+
from ..services.pm_skills_deployer import (
|
|
1159
|
+
REQUIRED_PM_SKILLS,
|
|
1160
|
+
PMSkillsDeployerService,
|
|
1161
|
+
)
|
|
1370
1162
|
|
|
1371
1163
|
deployer = PMSkillsDeployerService()
|
|
1372
1164
|
project_dir = Path.cwd()
|
|
1373
1165
|
|
|
1374
|
-
|
|
1166
|
+
# Verify with auto-repair enabled
|
|
1167
|
+
result = deployer.verify_pm_skills(project_dir, auto_repair=True)
|
|
1375
1168
|
|
|
1376
1169
|
if result.verified:
|
|
1377
|
-
# Show verified status
|
|
1378
|
-
|
|
1170
|
+
# Show verified status with count
|
|
1171
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1172
|
+
print(
|
|
1173
|
+
f"✓ PM skills: {total_required}/{total_required} verified", flush=True
|
|
1174
|
+
)
|
|
1379
1175
|
else:
|
|
1380
|
-
#
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1176
|
+
# Show warning with details
|
|
1177
|
+
missing_count = len(result.missing_skills)
|
|
1178
|
+
corrupted_count = len(result.corrupted_skills)
|
|
1179
|
+
|
|
1180
|
+
# Build status message
|
|
1181
|
+
issues = []
|
|
1182
|
+
if missing_count > 0:
|
|
1183
|
+
issues.append(f"{missing_count} missing")
|
|
1184
|
+
if corrupted_count > 0:
|
|
1185
|
+
issues.append(f"{corrupted_count} corrupted")
|
|
1186
|
+
|
|
1187
|
+
status = ", ".join(issues)
|
|
1188
|
+
|
|
1189
|
+
# Check if auto-repair was attempted
|
|
1190
|
+
if "Auto-repaired" in result.message:
|
|
1191
|
+
# Auto-repair succeeded
|
|
1192
|
+
total_required = len(REQUIRED_PM_SKILLS)
|
|
1193
|
+
print(
|
|
1194
|
+
f"✓ PM skills: {total_required}/{total_required} verified (auto-repaired)",
|
|
1195
|
+
flush=True,
|
|
1196
|
+
)
|
|
1386
1197
|
else:
|
|
1387
|
-
|
|
1198
|
+
# Auto-repair failed or not attempted
|
|
1199
|
+
print(f"⚠ PM skills: {status}", flush=True)
|
|
1200
|
+
|
|
1201
|
+
# Log warnings for debugging
|
|
1202
|
+
from ..core.logger import get_logger
|
|
1203
|
+
|
|
1204
|
+
logger = get_logger("cli")
|
|
1205
|
+
for warning in result.warnings:
|
|
1206
|
+
logger.warning(f"PM skills: {warning}")
|
|
1388
1207
|
|
|
1389
1208
|
except ImportError:
|
|
1390
1209
|
# PM skills deployer not available - skip silently
|
|
@@ -1419,7 +1238,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1419
1238
|
if not chrome_devtools_config.get("auto_install", True):
|
|
1420
1239
|
# Auto-install disabled, skip silently
|
|
1421
1240
|
return
|
|
1422
|
-
except Exception:
|
|
1241
|
+
except Exception: # nosec B110
|
|
1423
1242
|
# If config loading fails, assume auto-install is enabled (default)
|
|
1424
1243
|
pass
|
|
1425
1244
|
|
|
@@ -1437,7 +1256,7 @@ def auto_install_chrome_devtools_on_startup():
|
|
|
1437
1256
|
# Continue execution - chrome-devtools installation failure shouldn't block startup
|
|
1438
1257
|
|
|
1439
1258
|
|
|
1440
|
-
def run_background_services():
|
|
1259
|
+
def run_background_services(force_sync: bool = False):
|
|
1441
1260
|
"""
|
|
1442
1261
|
Initialize all background services on startup.
|
|
1443
1262
|
|
|
@@ -1448,6 +1267,9 @@ def run_background_services():
|
|
|
1448
1267
|
explicitly requests them via agent-manager commands. This prevents unwanted
|
|
1449
1268
|
file creation in project .claude/ directories.
|
|
1450
1269
|
See: SystemInstructionsDeployer and agent_deployment.py line 504-509
|
|
1270
|
+
|
|
1271
|
+
Args:
|
|
1272
|
+
force_sync: Force download even if cache is fresh (bypasses ETag).
|
|
1451
1273
|
"""
|
|
1452
1274
|
# Sync hooks early to ensure up-to-date configuration
|
|
1453
1275
|
# RATIONALE: Hooks should be synced before other services to fix stale configs
|
|
@@ -1458,7 +1280,9 @@ def run_background_services():
|
|
|
1458
1280
|
check_mcp_auto_configuration()
|
|
1459
1281
|
verify_mcp_gateway_startup()
|
|
1460
1282
|
check_for_updates_async()
|
|
1461
|
-
sync_remote_agents_on_startup(
|
|
1283
|
+
sync_remote_agents_on_startup(
|
|
1284
|
+
force_sync=force_sync
|
|
1285
|
+
) # Sync agents from remote sources
|
|
1462
1286
|
show_agent_summary() # Display agent counts after deployment
|
|
1463
1287
|
|
|
1464
1288
|
# Skills deployment order (precedence: remote > bundled)
|
|
@@ -1467,7 +1291,9 @@ def run_background_services():
|
|
|
1467
1291
|
# 3. Discover and link runtime skills (user-added skills)
|
|
1468
1292
|
# This ensures remote skills take precedence over bundled skills when names conflict
|
|
1469
1293
|
deploy_bundled_skills() # Base layer: package-bundled skills
|
|
1470
|
-
sync_remote_skills_on_startup(
|
|
1294
|
+
sync_remote_skills_on_startup(
|
|
1295
|
+
force_sync=force_sync
|
|
1296
|
+
) # Override layer: Git-based skills (takes precedence)
|
|
1471
1297
|
discover_and_link_runtime_skills() # Discovery: user-added skills
|
|
1472
1298
|
show_skill_summary() # Display skill counts after deployment
|
|
1473
1299
|
verify_and_show_pm_skills() # PM skills verification and status
|
|
@@ -1667,7 +1493,7 @@ def verify_mcp_gateway_startup():
|
|
|
1667
1493
|
loop.run_until_complete(
|
|
1668
1494
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1669
1495
|
)
|
|
1670
|
-
except Exception:
|
|
1496
|
+
except Exception: # nosec B110
|
|
1671
1497
|
pass # Ignore cleanup errors
|
|
1672
1498
|
finally:
|
|
1673
1499
|
loop.close()
|
|
@@ -1761,7 +1587,7 @@ def check_for_updates_async():
|
|
|
1761
1587
|
|
|
1762
1588
|
logger = get_logger("upgrade_check")
|
|
1763
1589
|
logger.debug(f"Update check failed (non-critical): {e}")
|
|
1764
|
-
except Exception:
|
|
1590
|
+
except Exception: # nosec B110
|
|
1765
1591
|
pass # Avoid any errors in error handling
|
|
1766
1592
|
finally:
|
|
1767
1593
|
# Properly clean up event loop
|
|
@@ -1776,7 +1602,7 @@ def check_for_updates_async():
|
|
|
1776
1602
|
loop.run_until_complete(
|
|
1777
1603
|
asyncio.gather(*pending, return_exceptions=True)
|
|
1778
1604
|
)
|
|
1779
|
-
except Exception:
|
|
1605
|
+
except Exception: # nosec B110
|
|
1780
1606
|
pass # Ignore cleanup errors
|
|
1781
1607
|
finally:
|
|
1782
1608
|
loop.close()
|