claude-mpm 5.4.96__py3-none-any.whl → 5.6.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of claude-mpm might be problematic. Click here for more details.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
- claude_mpm/agents/PM_INSTRUCTIONS.md +44 -10
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/commands/autotodos.py +45 -5
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/commands/skill_source.py +51 -2
- claude_mpm/cli/commands/skills.py +5 -3
- claude_mpm/cli/executor.py +32 -17
- claude_mpm/cli/parsers/base_parser.py +17 -0
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/parsers/skill_source_parser.py +4 -0
- claude_mpm/cli/parsers/skills_parser.py +5 -0
- claude_mpm/cli/startup.py +20 -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 +133 -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 +228 -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 +8 -0
- 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/config/skill_sources.py +16 -0
- claude_mpm/core/config.py +32 -19
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +35 -11
- claude_mpm/core/output_style_manager.py +15 -5
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/core/unified_paths.py +68 -80
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/__init__.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/auto_pause_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/event_handlers.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/hook_handler.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/installer.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/memory_integration.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/response_tracking.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/__pycache__/tool_analysis.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +29 -30
- claude_mpm/hooks/claude_hooks/event_handlers.py +90 -99
- claude_mpm/hooks/claude_hooks/hook_handler.py +81 -88
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +116 -8
- claude_mpm/hooks/claude_hooks/memory_integration.py +51 -31
- claude_mpm/hooks/claude_hooks/response_tracking.py +39 -58
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/__init__.cpython-314.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__/connection_manager_http.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager_http.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/duplicate_detector.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/state_manager.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-311.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-312.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/__pycache__/subagent_processor.cpython-314.pyc +0 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +23 -28
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +36 -103
- claude_mpm/hooks/claude_hooks/services/state_manager.py +23 -36
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +47 -73
- claude_mpm/hooks/session_resume_hook.py +22 -18
- claude_mpm/hooks/templates/pre_tool_use_template.py +10 -2
- claude_mpm/scripts/claude-hook-handler.sh +43 -16
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
- claude_mpm/services/event_log.py +8 -0
- claude_mpm/services/pm_skills_deployer.py +84 -6
- claude_mpm/services/skills/git_skill_source_manager.py +130 -10
- claude_mpm/services/skills/selective_skill_deployer.py +28 -0
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +31 -5
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
- claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -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-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/METADATA +18 -4
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/RECORD +190 -79
- claude_mpm/hooks/claude_hooks/services/__pycache__/connection_manager.cpython-311.pyc +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.96.dist-info → claude_mpm-5.6.10.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
"""Basic notification delivery for events.
|
|
2
|
+
|
|
3
|
+
This module provides Notifier which sends notifications for events.
|
|
4
|
+
Currently supports logging, with extensibility for future channels
|
|
5
|
+
(Slack, email, webhooks).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
|
|
11
|
+
from ..models.events import Event
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass
|
|
17
|
+
class NotifierConfig:
|
|
18
|
+
"""Configuration for notifier.
|
|
19
|
+
|
|
20
|
+
Attributes:
|
|
21
|
+
log_level: Logging level for notifications (default: INFO)
|
|
22
|
+
|
|
23
|
+
Future attributes:
|
|
24
|
+
slack_webhook: URL for Slack webhook notifications
|
|
25
|
+
email_config: SMTP configuration for email notifications
|
|
26
|
+
webhook_urls: List of webhook URLs for custom integrations
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
log_level: str = "INFO"
|
|
30
|
+
# Future: slack_webhook, email_config, webhook_urls
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class Notifier:
|
|
34
|
+
"""Sends notifications for events.
|
|
35
|
+
|
|
36
|
+
Currently implements logging-based notifications with configurable
|
|
37
|
+
log levels. Designed for extensibility to support future notification
|
|
38
|
+
channels like Slack, email, and webhooks.
|
|
39
|
+
|
|
40
|
+
Attributes:
|
|
41
|
+
config: Notifier configuration
|
|
42
|
+
|
|
43
|
+
Example:
|
|
44
|
+
>>> config = NotifierConfig(log_level="INFO")
|
|
45
|
+
>>> notifier = Notifier(config)
|
|
46
|
+
>>> await notifier.notify(event)
|
|
47
|
+
>>> await notifier.notify_resolution(event, "User responded")
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, config: NotifierConfig | None = None) -> None:
|
|
51
|
+
"""Initialize notifier.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
config: Optional NotifierConfig (uses defaults if not provided)
|
|
55
|
+
"""
|
|
56
|
+
self.config = config or NotifierConfig()
|
|
57
|
+
|
|
58
|
+
# Map log level string to logging level
|
|
59
|
+
level_map = {
|
|
60
|
+
"DEBUG": logging.DEBUG,
|
|
61
|
+
"INFO": logging.INFO,
|
|
62
|
+
"WARNING": logging.WARNING,
|
|
63
|
+
"ERROR": logging.ERROR,
|
|
64
|
+
"CRITICAL": logging.CRITICAL,
|
|
65
|
+
}
|
|
66
|
+
self._log_level = level_map.get(self.config.log_level.upper(), logging.INFO)
|
|
67
|
+
|
|
68
|
+
logger.debug("Notifier initialized with log level: %s", self.config.log_level)
|
|
69
|
+
|
|
70
|
+
async def notify(self, event: Event) -> None:
|
|
71
|
+
"""Send notification for an event.
|
|
72
|
+
|
|
73
|
+
Currently logs the event at the configured log level. Future versions
|
|
74
|
+
will support additional notification channels.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
event: Event to notify about
|
|
78
|
+
|
|
79
|
+
Example:
|
|
80
|
+
>>> await notifier.notify(event)
|
|
81
|
+
# Logs: [HIGH] Event evt_123: Choose deployment target
|
|
82
|
+
"""
|
|
83
|
+
# Format notification message
|
|
84
|
+
message = self._format_event(event)
|
|
85
|
+
|
|
86
|
+
# Log notification
|
|
87
|
+
logger.log(
|
|
88
|
+
self._log_level,
|
|
89
|
+
"Event notification: %s",
|
|
90
|
+
message,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
# Future: Send to Slack, email, webhooks
|
|
94
|
+
# if self.config.slack_webhook:
|
|
95
|
+
# await self._send_slack(event)
|
|
96
|
+
# if self.config.email_config:
|
|
97
|
+
# await self._send_email(event)
|
|
98
|
+
|
|
99
|
+
async def notify_resolution(self, event: Event, response: str) -> None:
|
|
100
|
+
"""Notify that an event was resolved.
|
|
101
|
+
|
|
102
|
+
Logs the resolution with the user's response. Future versions will
|
|
103
|
+
send resolution notifications to configured channels.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
event: Event that was resolved
|
|
107
|
+
response: User's response to the event
|
|
108
|
+
|
|
109
|
+
Example:
|
|
110
|
+
>>> await notifier.notify_resolution(event, "Deploy to staging")
|
|
111
|
+
# Logs: Event evt_123 resolved: Deploy to staging
|
|
112
|
+
"""
|
|
113
|
+
message = f"Event {event.id} resolved: {response[:100]}"
|
|
114
|
+
|
|
115
|
+
logger.log(
|
|
116
|
+
self._log_level,
|
|
117
|
+
"Event resolution: %s",
|
|
118
|
+
message,
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Future: Send resolution notifications to channels
|
|
122
|
+
|
|
123
|
+
def _format_event(self, event: Event) -> str:
|
|
124
|
+
"""Format event for notification display.
|
|
125
|
+
|
|
126
|
+
Args:
|
|
127
|
+
event: Event to format
|
|
128
|
+
|
|
129
|
+
Returns:
|
|
130
|
+
Formatted notification string
|
|
131
|
+
|
|
132
|
+
Example:
|
|
133
|
+
>>> msg = notifier._format_event(event)
|
|
134
|
+
'[HIGH] evt_123 (proj_456): Choose deployment target'
|
|
135
|
+
"""
|
|
136
|
+
parts = [
|
|
137
|
+
f"[{event.priority.value.upper()}]",
|
|
138
|
+
f"{event.id}",
|
|
139
|
+
f"({event.project_id})",
|
|
140
|
+
f"{event.title}",
|
|
141
|
+
]
|
|
142
|
+
|
|
143
|
+
if event.options:
|
|
144
|
+
parts.append(f"Options: {', '.join(event.options)}")
|
|
145
|
+
|
|
146
|
+
return " ".join(parts)
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-config]
|
|
|
5
5
|
migration_target: /mpm/config
|
|
6
6
|
category: config
|
|
7
7
|
description: Manage Claude MPM configuration
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-config"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-config` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-config` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-config
|
|
10
18
|
|
|
11
19
|
Unified configuration management with auto-detection.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-doctor]
|
|
|
5
5
|
migration_target: /mpm/system:doctor
|
|
6
6
|
category: system
|
|
7
7
|
description: Run diagnostic checks on Claude MPM installation
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-doctor"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-doctor` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-doctor` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-doctor
|
|
10
18
|
|
|
11
19
|
Run comprehensive diagnostics on Claude MPM installation.
|
claude_mpm/commands/mpm-help.md
CHANGED
|
@@ -5,7 +5,15 @@ aliases: [mpm-help]
|
|
|
5
5
|
migration_target: /mpm/system:help
|
|
6
6
|
category: system
|
|
7
7
|
description: Display help for Claude MPM commands
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-help"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-help` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-help` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-help
|
|
10
18
|
|
|
11
19
|
Show help for MPM commands. Delegates to PM agent.
|
claude_mpm/commands/mpm-init.md
CHANGED
|
@@ -5,7 +5,15 @@ aliases: [mpm-init]
|
|
|
5
5
|
migration_target: /mpm/system:init
|
|
6
6
|
category: system
|
|
7
7
|
description: Initialize or update project for Claude Code and MPM
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-init"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-init` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-init` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-init
|
|
10
18
|
|
|
11
19
|
Initialize or intelligently update project for Claude Code and Claude MPM.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-monitor]
|
|
|
5
5
|
migration_target: /mpm/system:monitor
|
|
6
6
|
category: system
|
|
7
7
|
description: Control monitoring server and dashboard
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-monitor"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-monitor` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-monitor` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-monitor
|
|
10
18
|
|
|
11
19
|
Manage Socket.IO monitoring server for real-time dashboard.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-organize]
|
|
|
5
5
|
migration_target: /mpm/system:organize
|
|
6
6
|
category: system
|
|
7
7
|
description: Organize project files with intelligent consolidation
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-organize"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-organize` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-organize` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-organize
|
|
10
18
|
|
|
11
19
|
Organize ALL project files with intelligent detection, consolidation, and pruning.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-postmortem]
|
|
|
5
5
|
migration_target: /mpm/analysis:postmortem
|
|
6
6
|
category: analysis
|
|
7
7
|
description: Analyze session errors and suggest improvements
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-postmortem"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-postmortem` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-postmortem` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-postmortem
|
|
10
18
|
|
|
11
19
|
Analyze session errors and generate improvement suggestions.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-session-resume]
|
|
|
5
5
|
migration_target: /mpm/session:resume
|
|
6
6
|
category: session
|
|
7
7
|
description: Load context from paused session
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-session-resume"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-session-resume` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-session-resume` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-session-resume
|
|
10
18
|
|
|
11
19
|
Load and display context from most recent paused session.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-status]
|
|
|
5
5
|
migration_target: /mpm/system:status
|
|
6
6
|
category: system
|
|
7
7
|
description: Display Claude MPM system status
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-status"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-status` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-status` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-status
|
|
10
18
|
|
|
11
19
|
Show MPM system status. Delegates to PM agent.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-ticket-view]
|
|
|
5
5
|
migration_target: /mpm/ticket:view
|
|
6
6
|
category: tickets
|
|
7
7
|
description: Orchestrate ticketing agent for project management workflows
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-ticket-view"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-ticket-view` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-ticket-view` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-ticket
|
|
10
18
|
|
|
11
19
|
High-level ticketing workflows delegating to ticketing agent.
|
|
@@ -5,7 +5,15 @@ aliases: [mpm-version]
|
|
|
5
5
|
migration_target: /mpm/system:version
|
|
6
6
|
category: system
|
|
7
7
|
description: Show version information
|
|
8
|
+
deprecated: true
|
|
9
|
+
deprecated_in: "5.5.0"
|
|
10
|
+
replacement: "skill:mpm-version"
|
|
8
11
|
---
|
|
12
|
+
|
|
13
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm-version` skill.
|
|
14
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm-version` command instead.
|
|
15
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
16
|
+
|
|
9
17
|
# /mpm-version
|
|
10
18
|
|
|
11
19
|
Display version information for MPM, agents, and skills.
|
claude_mpm/commands/mpm.md
CHANGED
|
@@ -6,7 +6,15 @@ migration_target: /mpm
|
|
|
6
6
|
category: system
|
|
7
7
|
deprecated_aliases: []
|
|
8
8
|
description: Access Claude MPM functionality and manage multi-agent orchestration
|
|
9
|
+
deprecated: true
|
|
10
|
+
deprecated_in: "5.5.0"
|
|
11
|
+
replacement: "skill:mpm"
|
|
9
12
|
---
|
|
13
|
+
|
|
14
|
+
> **Deprecated:** This command file is deprecated in favor of the `mpm` skill.
|
|
15
|
+
> For Claude Code 2.1.3+, use the skill-based `/mpm` command instead.
|
|
16
|
+
> This file is kept for backward compatibility with Claude Code < 2.1.3.
|
|
17
|
+
|
|
10
18
|
# Claude MPM - Multi-Agent Project Manager
|
|
11
19
|
|
|
12
20
|
Access Claude MPM functionality and manage your multi-agent orchestration.
|
|
@@ -25,12 +25,17 @@ from typing import Any, Callable, Dict, List, Union
|
|
|
25
25
|
PresetResolver = Union[List[str], Callable[[], List[str]]]
|
|
26
26
|
|
|
27
27
|
# Core agents included in ALL presets (MIN and MAX)
|
|
28
|
+
# Standard 9 core agents for essential PM workflow functionality
|
|
28
29
|
CORE_AGENTS = [
|
|
29
30
|
"claude-mpm/mpm-agent-manager", # Agent lifecycle management
|
|
30
31
|
"claude-mpm/mpm-skills-manager", # Skills management
|
|
31
|
-
"
|
|
32
|
+
"engineer/core/engineer", # General-purpose implementation
|
|
33
|
+
"universal/research", # Codebase exploration and analysis
|
|
34
|
+
"qa/qa", # Testing and quality assurance
|
|
35
|
+
"qa/web-qa", # Browser-based testing specialist
|
|
32
36
|
"documentation/documentation", # Documentation generation
|
|
33
|
-
"
|
|
37
|
+
"ops/core/ops", # Basic deployment operations
|
|
38
|
+
"documentation/ticketing", # Ticket tracking (essential for PM workflow)
|
|
34
39
|
]
|
|
35
40
|
|
|
36
41
|
PRESETS: Dict[str, Dict[str, Any]] = {
|
|
@@ -39,11 +44,7 @@ PRESETS: Dict[str, Dict[str, Any]] = {
|
|
|
39
44
|
# ========================================
|
|
40
45
|
"minimal": {
|
|
41
46
|
"description": "Core agents only - universal starter kit",
|
|
42
|
-
"agents": CORE_AGENTS
|
|
43
|
-
+ [
|
|
44
|
-
"qa/qa",
|
|
45
|
-
"ops/core/ops",
|
|
46
|
-
],
|
|
47
|
+
"agents": CORE_AGENTS, # All 8 core agents (no additional needed)
|
|
47
48
|
"use_cases": ["Any project type", "Quick start", "Learning"],
|
|
48
49
|
},
|
|
49
50
|
# ========================================
|
|
@@ -54,6 +54,7 @@ class SkillSource:
|
|
|
54
54
|
branch: Git branch to use (default: "main")
|
|
55
55
|
priority: Priority for skill resolution (lower = higher precedence)
|
|
56
56
|
enabled: Whether this source should be synced
|
|
57
|
+
token: Optional GitHub token or env var reference (e.g., "$MY_TOKEN")
|
|
57
58
|
|
|
58
59
|
Priority System:
|
|
59
60
|
- 0: Reserved for system repository (highest precedence)
|
|
@@ -61,6 +62,12 @@ class SkillSource:
|
|
|
61
62
|
- 100-999: Normal priority custom sources
|
|
62
63
|
- 1000+: Low priority custom sources
|
|
63
64
|
|
|
65
|
+
Token Authentication:
|
|
66
|
+
- Direct token: "ghp_xxxxx" (stored in config, not recommended)
|
|
67
|
+
- Env var reference: "$PRIVATE_REPO_TOKEN" (resolved at runtime)
|
|
68
|
+
- If None, falls back to GITHUB_TOKEN or GH_TOKEN env vars
|
|
69
|
+
- Priority: source.token > GITHUB_TOKEN > GH_TOKEN
|
|
70
|
+
|
|
64
71
|
Example:
|
|
65
72
|
>>> source = SkillSource(
|
|
66
73
|
... id="system",
|
|
@@ -70,6 +77,12 @@ class SkillSource:
|
|
|
70
77
|
... )
|
|
71
78
|
>>> source.validate()
|
|
72
79
|
[]
|
|
80
|
+
>>> private_source = SkillSource(
|
|
81
|
+
... id="private",
|
|
82
|
+
... type="git",
|
|
83
|
+
... url="https://github.com/myorg/private-skills",
|
|
84
|
+
... token="$PRIVATE_REPO_TOKEN"
|
|
85
|
+
... )
|
|
73
86
|
"""
|
|
74
87
|
|
|
75
88
|
id: str
|
|
@@ -78,6 +91,7 @@ class SkillSource:
|
|
|
78
91
|
branch: str = "main"
|
|
79
92
|
priority: int = 100
|
|
80
93
|
enabled: bool = True
|
|
94
|
+
token: Optional[str] = None
|
|
81
95
|
|
|
82
96
|
def __post_init__(self):
|
|
83
97
|
"""Validate skill source configuration after initialization.
|
|
@@ -262,6 +276,7 @@ class SkillSourceConfiguration:
|
|
|
262
276
|
branch=source_data.get("branch", "main"),
|
|
263
277
|
priority=source_data.get("priority", 100),
|
|
264
278
|
enabled=source_data.get("enabled", True),
|
|
279
|
+
token=source_data.get("token"),
|
|
265
280
|
)
|
|
266
281
|
sources.append(source)
|
|
267
282
|
except (KeyError, ValueError) as e:
|
|
@@ -326,6 +341,7 @@ class SkillSourceConfiguration:
|
|
|
326
341
|
"branch": source.branch,
|
|
327
342
|
"priority": source.priority,
|
|
328
343
|
"enabled": source.enabled,
|
|
344
|
+
**({"token": source.token} if source.token else {}),
|
|
329
345
|
}
|
|
330
346
|
for source in sources
|
|
331
347
|
]
|
claude_mpm/core/config.py
CHANGED
|
@@ -12,11 +12,10 @@ import threading
|
|
|
12
12
|
from pathlib import Path
|
|
13
13
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
|
14
14
|
|
|
15
|
-
import yaml
|
|
16
|
-
|
|
17
15
|
from claude_mpm.core.logging_utils import get_logger
|
|
18
16
|
|
|
19
|
-
|
|
17
|
+
# Lazy import ConfigurationManager to avoid importing yaml at module level
|
|
18
|
+
# This prevents hook errors when yaml isn't available in the execution environment
|
|
20
19
|
from .exceptions import ConfigurationError, FileOperationError
|
|
21
20
|
from .unified_paths import get_path_manager
|
|
22
21
|
|
|
@@ -104,6 +103,9 @@ class Config:
|
|
|
104
103
|
Config._initialized = True
|
|
105
104
|
logger.debug("Initializing Config singleton for the first time")
|
|
106
105
|
|
|
106
|
+
# Lazy import ConfigurationManager at runtime to avoid yaml import at module level
|
|
107
|
+
from ..utils.config_manager import ConfigurationManager
|
|
108
|
+
|
|
107
109
|
# Initialize instance variables inside the lock to ensure thread safety
|
|
108
110
|
self._config: Dict[str, Any] = {}
|
|
109
111
|
self._env_prefix = env_prefix
|
|
@@ -224,21 +226,6 @@ class Config:
|
|
|
224
226
|
f"Response logging format: {response_logging.get('format', 'json')}"
|
|
225
227
|
)
|
|
226
228
|
|
|
227
|
-
except yaml.YAMLError as e:
|
|
228
|
-
logger.error(f"YAML syntax error in {file_path}: {e}")
|
|
229
|
-
if hasattr(e, "problem_mark"):
|
|
230
|
-
mark = e.problem_mark
|
|
231
|
-
logger.error(f"Error at line {mark.line + 1}, column {mark.column + 1}")
|
|
232
|
-
logger.info(
|
|
233
|
-
"TIP: Validate your YAML at https://www.yamllint.com/ or run: python scripts/validate_configuration.py"
|
|
234
|
-
)
|
|
235
|
-
logger.info(
|
|
236
|
-
"TIP: Common issue - YAML requires spaces, not tabs. Fix with: sed -i '' 's/\t/ /g' "
|
|
237
|
-
+ str(file_path)
|
|
238
|
-
)
|
|
239
|
-
# Store error for later retrieval
|
|
240
|
-
self._config["_load_error"] = str(e)
|
|
241
|
-
|
|
242
229
|
except json.JSONDecodeError as e:
|
|
243
230
|
logger.error(f"JSON syntax error in {file_path}: {e}")
|
|
244
231
|
logger.error(f"Error at line {e.lineno}, column {e.colno}")
|
|
@@ -255,7 +242,28 @@ class Config:
|
|
|
255
242
|
},
|
|
256
243
|
) from e
|
|
257
244
|
except Exception as e:
|
|
258
|
-
#
|
|
245
|
+
# Handle YAML errors without importing yaml at module level
|
|
246
|
+
# ConfigurationManager.load_yaml raises yaml.YAMLError, but we don't want to import yaml
|
|
247
|
+
# Check if it's a YAML error by class name to avoid import
|
|
248
|
+
if e.__class__.__name__ == "YAMLError":
|
|
249
|
+
logger.error(f"YAML syntax error in {file_path}: {e}")
|
|
250
|
+
if hasattr(e, "problem_mark"):
|
|
251
|
+
mark = e.problem_mark
|
|
252
|
+
logger.error(
|
|
253
|
+
f"Error at line {mark.line + 1}, column {mark.column + 1}"
|
|
254
|
+
)
|
|
255
|
+
logger.info(
|
|
256
|
+
"TIP: Validate your YAML at https://www.yamllint.com/ or run: python scripts/validate_configuration.py"
|
|
257
|
+
)
|
|
258
|
+
logger.info(
|
|
259
|
+
"TIP: Common issue - YAML requires spaces, not tabs. Fix with: sed -i '' 's/\t/ /g' "
|
|
260
|
+
+ str(file_path)
|
|
261
|
+
)
|
|
262
|
+
# Store error for later retrieval
|
|
263
|
+
self._config["_load_error"] = str(e)
|
|
264
|
+
return # Don't re-raise, we handled it
|
|
265
|
+
|
|
266
|
+
# Not a YAML error, wrap as configuration error
|
|
259
267
|
raise ConfigurationError(
|
|
260
268
|
f"Unexpected error loading configuration from {file_path}: {e}",
|
|
261
269
|
context={
|
|
@@ -605,6 +613,11 @@ class Config:
|
|
|
605
613
|
"sync_interval": "startup", # Options: "startup", "hourly", "daily", "manual"
|
|
606
614
|
"cache_dir": str(Path.home() / ".claude-mpm" / "cache" / "agents"),
|
|
607
615
|
},
|
|
616
|
+
# Autotodos configuration
|
|
617
|
+
"autotodos": {
|
|
618
|
+
"auto_inject_on_startup": True, # Auto-inject pending todos on PM session start
|
|
619
|
+
"max_todos_per_session": 10, # Max todos to inject per session
|
|
620
|
+
},
|
|
608
621
|
}
|
|
609
622
|
|
|
610
623
|
# Apply defaults for missing keys
|
claude_mpm/core/logger.py
CHANGED
|
@@ -214,19 +214,22 @@ def setup_logging(
|
|
|
214
214
|
|
|
215
215
|
# Console handler
|
|
216
216
|
if console_output:
|
|
217
|
+
# MUST use stderr to avoid corrupting hook JSON output
|
|
218
|
+
# WHY stderr: Hook handlers output JSON to stdout. Logging to stdout
|
|
219
|
+
# corrupts this JSON and causes "hook error" messages from Claude Code.
|
|
217
220
|
if use_streaming:
|
|
218
221
|
# Use streaming handler for single-line INFO messages
|
|
219
|
-
console_handler = StreamingHandler(sys.
|
|
222
|
+
console_handler = StreamingHandler(sys.stderr)
|
|
220
223
|
console_handler.setFormatter(simple_formatter)
|
|
221
224
|
elif use_rich and not json_format:
|
|
222
225
|
# Rich support has been removed, use standard handler
|
|
223
|
-
console_handler = logging.StreamHandler(sys.
|
|
226
|
+
console_handler = logging.StreamHandler(sys.stderr)
|
|
224
227
|
console_handler.setFormatter(simple_formatter)
|
|
225
228
|
else:
|
|
226
|
-
console_handler = logging.StreamHandler(sys.
|
|
229
|
+
console_handler = logging.StreamHandler(sys.stderr)
|
|
227
230
|
console_handler.setFormatter(formatter if json_format else simple_formatter)
|
|
228
231
|
|
|
229
|
-
console_handler.setLevel(
|
|
232
|
+
console_handler.setLevel(log_level) # Respect the requested log level
|
|
230
233
|
logger.addHandler(console_handler)
|
|
231
234
|
|
|
232
235
|
# File handler
|
|
@@ -263,7 +266,7 @@ def setup_logging(
|
|
|
263
266
|
if deleted_count > 0:
|
|
264
267
|
# Log to the new file handler that we're about to add
|
|
265
268
|
pass # Deletion count will be logged when logger is ready
|
|
266
|
-
except Exception:
|
|
269
|
+
except Exception: # nosec B110 - intentional: logging should not break app
|
|
267
270
|
pass # Ignore cleanup errors
|
|
268
271
|
|
|
269
272
|
# Also create a symlink to latest log (with thread safety)
|
|
@@ -305,7 +308,7 @@ def setup_logging(
|
|
|
305
308
|
# Fallback: try to create a regular file with reference to actual log
|
|
306
309
|
try:
|
|
307
310
|
latest_link.write_text(f"Latest log: {log_file.name}\n")
|
|
308
|
-
except Exception:
|
|
311
|
+
except Exception: # nosec B110 - intentional: logging should not break app
|
|
309
312
|
pass # Silently fail - logging should not break the application
|
|
310
313
|
except Exception as e:
|
|
311
314
|
# Catch any other unexpected errors to ensure logging doesn't break
|
|
@@ -381,15 +384,29 @@ def cleanup_old_mpm_logs(
|
|
|
381
384
|
try:
|
|
382
385
|
log_file.unlink()
|
|
383
386
|
deleted_count += 1
|
|
384
|
-
except Exception:
|
|
387
|
+
except Exception: # nosec B110 - intentional: log cleanup is best-effort
|
|
385
388
|
pass # Ignore deletion errors
|
|
386
389
|
|
|
387
390
|
return deleted_count
|
|
388
391
|
|
|
389
392
|
|
|
390
393
|
def get_logger(name: str) -> logging.Logger:
|
|
391
|
-
"""Get a logger instance.
|
|
392
|
-
|
|
394
|
+
"""Get a logger instance.
|
|
395
|
+
|
|
396
|
+
CRITICAL: Respects startup suppression mode (CRITICAL+1) to prevent
|
|
397
|
+
early INFO logs before setup_logging() is called.
|
|
398
|
+
"""
|
|
399
|
+
logger = logging.getLogger(f"claude_mpm.{name}")
|
|
400
|
+
|
|
401
|
+
# Check if root logger is suppressed (startup.py sets CRITICAL+1)
|
|
402
|
+
root_logger = logging.getLogger()
|
|
403
|
+
if root_logger.level > logging.CRITICAL:
|
|
404
|
+
# Suppression active - ensure this logger is also suppressed
|
|
405
|
+
logger.setLevel(logging.CRITICAL + 1)
|
|
406
|
+
logger.handlers = []
|
|
407
|
+
logger.propagate = False
|
|
408
|
+
|
|
409
|
+
return logger
|
|
393
410
|
|
|
394
411
|
|
|
395
412
|
def setup_streaming_logger(name: str, level: str = "INFO") -> logging.Logger:
|
claude_mpm/core/logging_utils.py
CHANGED
|
@@ -103,21 +103,45 @@ class LoggerFactory:
|
|
|
103
103
|
|
|
104
104
|
# Set up root logger
|
|
105
105
|
root_logger = logging.getLogger()
|
|
106
|
-
|
|
106
|
+
|
|
107
|
+
# CRITICAL FIX: Respect existing root logger suppression
|
|
108
|
+
# If root logger is already set to CRITICAL+1 (suppressed by startup.py),
|
|
109
|
+
# don't override it. This prevents logging from appearing during startup
|
|
110
|
+
# before the CLI's setup_logging() runs.
|
|
111
|
+
current_level = root_logger.level
|
|
112
|
+
desired_level = LoggingConfig.LEVELS.get(cls._log_level, logging.INFO)
|
|
113
|
+
|
|
114
|
+
# Only set level if current is unset (0) or lower than desired
|
|
115
|
+
# CRITICAL+1 is 51, so this check preserves suppression
|
|
116
|
+
should_configure_logging = current_level == 0 or (
|
|
117
|
+
current_level < desired_level and current_level <= logging.CRITICAL
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
if should_configure_logging:
|
|
121
|
+
root_logger.setLevel(desired_level)
|
|
122
|
+
# else: root logger is suppressed (CRITICAL+1), keep it suppressed
|
|
107
123
|
|
|
108
124
|
# Remove existing handlers
|
|
109
125
|
root_logger.handlers = []
|
|
110
126
|
|
|
111
|
-
#
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
127
|
+
# CRITICAL FIX: Don't add handlers if logging is suppressed
|
|
128
|
+
# If root logger is at CRITICAL+1 (startup suppression), don't add any handlers
|
|
129
|
+
# This prevents early imports from logging before CLI setup_logging() runs
|
|
130
|
+
if should_configure_logging:
|
|
131
|
+
# Console handler - MUST use stderr to avoid corrupting hook JSON output
|
|
132
|
+
# WHY stderr: Hook handlers output JSON to stdout. Logging to stdout
|
|
133
|
+
# corrupts this JSON and causes "hook error" messages from Claude Code.
|
|
134
|
+
console_handler = logging.StreamHandler(sys.stderr)
|
|
135
|
+
console_handler.setLevel(
|
|
136
|
+
LoggingConfig.LEVELS.get(cls._log_level, logging.INFO)
|
|
137
|
+
)
|
|
138
|
+
console_formatter = logging.Formatter(
|
|
139
|
+
log_format or LoggingConfig.DEFAULT_FORMAT,
|
|
140
|
+
date_format or LoggingConfig.DATE_FORMAT,
|
|
141
|
+
)
|
|
142
|
+
console_handler.setFormatter(console_formatter)
|
|
143
|
+
root_logger.addHandler(console_handler)
|
|
144
|
+
cls._handlers["console"] = console_handler
|
|
121
145
|
|
|
122
146
|
# File handler (optional)
|
|
123
147
|
if log_to_file and cls._log_dir:
|