claude-mpm 5.4.85__py3-none-any.whl → 5.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- claude_mpm/VERSION +1 -1
- claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
- claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
- claude_mpm/agents/PM_INSTRUCTIONS.md +101 -703
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +46 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/run.py +35 -3
- claude_mpm/cli/executor.py +119 -16
- claude_mpm/cli/parsers/base_parser.py +71 -1
- claude_mpm/cli/parsers/commander_parser.py +83 -0
- claude_mpm/cli/parsers/run_parser.py +10 -0
- claude_mpm/cli/startup.py +54 -16
- claude_mpm/cli/startup_display.py +72 -5
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +72 -0
- claude_mpm/commander/adapters/__init__.py +31 -0
- claude_mpm/commander/adapters/base.py +191 -0
- claude_mpm/commander/adapters/claude_code.py +361 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +105 -0
- claude_mpm/commander/api/errors.py +112 -0
- claude_mpm/commander/api/routes/__init__.py +8 -0
- claude_mpm/commander/api/routes/events.py +184 -0
- claude_mpm/commander/api/routes/inbox.py +171 -0
- claude_mpm/commander/api/routes/messages.py +148 -0
- claude_mpm/commander/api/routes/projects.py +271 -0
- claude_mpm/commander/api/routes/sessions.py +215 -0
- claude_mpm/commander/api/routes/work.py +260 -0
- claude_mpm/commander/api/schemas.py +182 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +107 -0
- claude_mpm/commander/chat/commands.py +96 -0
- claude_mpm/commander/chat/repl.py +310 -0
- claude_mpm/commander/config.py +49 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/daemon.py +398 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +332 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +143 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +62 -0
- claude_mpm/commander/inbox/__init__.py +16 -0
- claude_mpm/commander/inbox/dedup.py +128 -0
- claude_mpm/commander/inbox/inbox.py +224 -0
- claude_mpm/commander/inbox/models.py +70 -0
- claude_mpm/commander/instance_manager.py +337 -0
- claude_mpm/commander/llm/__init__.py +6 -0
- claude_mpm/commander/llm/openrouter_client.py +167 -0
- claude_mpm/commander/llm/summarizer.py +70 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +121 -0
- claude_mpm/commander/models/project.py +162 -0
- claude_mpm/commander/models/work.py +214 -0
- claude_mpm/commander/parsing/__init__.py +20 -0
- claude_mpm/commander/parsing/extractor.py +132 -0
- claude_mpm/commander/parsing/output_parser.py +270 -0
- claude_mpm/commander/parsing/patterns.py +100 -0
- claude_mpm/commander/persistence/__init__.py +11 -0
- claude_mpm/commander/persistence/event_store.py +274 -0
- claude_mpm/commander/persistence/state_store.py +309 -0
- claude_mpm/commander/persistence/work_store.py +164 -0
- claude_mpm/commander/polling/__init__.py +13 -0
- claude_mpm/commander/polling/event_detector.py +104 -0
- claude_mpm/commander/polling/output_buffer.py +49 -0
- claude_mpm/commander/polling/output_poller.py +153 -0
- claude_mpm/commander/project_session.py +268 -0
- claude_mpm/commander/proxy/__init__.py +12 -0
- claude_mpm/commander/proxy/formatter.py +89 -0
- claude_mpm/commander/proxy/output_handler.py +191 -0
- claude_mpm/commander/proxy/relay.py +155 -0
- claude_mpm/commander/registry.py +404 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +316 -0
- claude_mpm/commander/session/__init__.py +6 -0
- claude_mpm/commander/session/context.py +81 -0
- claude_mpm/commander/session/manager.py +59 -0
- claude_mpm/commander/tmux_orchestrator.py +361 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +189 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +219 -0
- claude_mpm/commander/workflow/notifier.py +146 -0
- claude_mpm/commands/mpm-config.md +8 -0
- claude_mpm/commands/mpm-doctor.md +8 -0
- claude_mpm/commands/mpm-help.md +8 -0
- claude_mpm/commands/mpm-init.md +8 -0
- claude_mpm/commands/mpm-monitor.md +8 -0
- claude_mpm/commands/mpm-organize.md +8 -0
- claude_mpm/commands/mpm-postmortem.md +8 -0
- claude_mpm/commands/mpm-session-resume.md +9 -1
- claude_mpm/commands/mpm-status.md +8 -0
- claude_mpm/commands/mpm-ticket-view.md +8 -0
- claude_mpm/commands/mpm-version.md +8 -0
- claude_mpm/commands/mpm.md +8 -0
- claude_mpm/config/agent_presets.py +8 -7
- claude_mpm/core/config.py +5 -0
- claude_mpm/core/hook_manager.py +51 -3
- claude_mpm/core/logger.py +10 -7
- claude_mpm/core/logging_utils.py +4 -2
- claude_mpm/core/output_style_manager.py +15 -5
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
- claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
- claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
- claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
- claude_mpm/dashboard/static/svelte-build/index.html +9 -9
- claude_mpm/experimental/cli_enhancements.py +2 -1
- claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
- claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
- claude_mpm/hooks/claude_hooks/auto_pause_handler.py +486 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +250 -11
- claude_mpm/hooks/claude_hooks/hook_handler.py +106 -89
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +69 -5
- claude_mpm/hooks/claude_hooks/response_tracking.py +3 -1
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +20 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +14 -77
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +30 -6
- claude_mpm/hooks/session_resume_hook.py +85 -1
- claude_mpm/init.py +1 -1
- claude_mpm/scripts/claude-hook-handler.sh +36 -10
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/cache_git_manager.py +1 -1
- claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
- claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
- claude_mpm/services/cli/__init__.py +3 -0
- claude_mpm/services/cli/incremental_pause_manager.py +561 -0
- claude_mpm/services/cli/session_resume_helper.py +10 -2
- claude_mpm/services/delegation_detector.py +175 -0
- claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
- claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
- claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
- claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
- claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
- claude_mpm/services/diagnostics/models.py +14 -1
- claude_mpm/services/event_log.py +325 -0
- claude_mpm/services/infrastructure/__init__.py +4 -0
- claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
- claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +106 -16
- claude_mpm/services/pm_skills_deployer.py +259 -87
- claude_mpm/services/skills/git_skill_source_manager.py +51 -2
- claude_mpm/services/skills/selective_skill_deployer.py +114 -16
- claude_mpm/services/skills/skill_discovery_service.py +57 -3
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
- claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
- claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -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-management/SKILL.md +312 -0
- claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
- claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
- claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
- claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
- claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
- claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.6.1.dist-info/METADATA +391 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/RECORD +244 -145
- 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-5.4.85.dist-info/METADATA +0 -1023
- /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
- /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.85.dist-info → claude_mpm-5.6.1.dist-info}/WHEEL +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/entry_points.txt +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Commander parser module for claude-mpm CLI.
|
|
3
|
+
|
|
4
|
+
WHY: This module provides the commander subcommand for interactive instance management
|
|
5
|
+
and chat interface.
|
|
6
|
+
|
|
7
|
+
DESIGN DECISION: Uses subparser pattern consistent with other commands (run, agents, etc.)
|
|
8
|
+
to provide a clean interface for Commander mode.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import argparse
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def add_commander_subparser(subparsers: argparse._SubParsersAction) -> None:
|
|
16
|
+
"""
|
|
17
|
+
Add commander subcommand parser.
|
|
18
|
+
|
|
19
|
+
WHY: Provides interactive mode for managing and chatting with multiple Claude instances.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
subparsers: The subparsers object to add the commander parser to
|
|
23
|
+
"""
|
|
24
|
+
commander_parser = subparsers.add_parser(
|
|
25
|
+
"commander",
|
|
26
|
+
help="Interactive Commander mode for managing multiple Claude instances",
|
|
27
|
+
description="""
|
|
28
|
+
Commander Mode - Interactive Instance Management
|
|
29
|
+
|
|
30
|
+
Commander provides an interactive REPL interface for:
|
|
31
|
+
- Starting and stopping Claude Code/MPM instances in tmux
|
|
32
|
+
- Connecting to instances and sending natural language commands
|
|
33
|
+
- Managing multiple concurrent projects
|
|
34
|
+
- Viewing instance status and output
|
|
35
|
+
|
|
36
|
+
Commands:
|
|
37
|
+
list, ls, instances List active instances
|
|
38
|
+
start <path> Start new instance at path
|
|
39
|
+
--framework <cc|mpm> Specify framework (default: cc)
|
|
40
|
+
--name <name> Specify instance name (default: dir name)
|
|
41
|
+
stop <name> Stop an instance
|
|
42
|
+
connect <name> Connect to an instance
|
|
43
|
+
disconnect Disconnect from current instance
|
|
44
|
+
status Show current session status
|
|
45
|
+
help Show help message
|
|
46
|
+
exit, quit, q Exit Commander
|
|
47
|
+
|
|
48
|
+
Natural Language:
|
|
49
|
+
When connected to an instance, any input that is not a built-in
|
|
50
|
+
command will be sent to the connected instance as a message.
|
|
51
|
+
|
|
52
|
+
Examples:
|
|
53
|
+
claude-mpm commander
|
|
54
|
+
> start ~/myproject --framework cc --name myapp
|
|
55
|
+
> connect myapp
|
|
56
|
+
> Fix the authentication bug in login.py
|
|
57
|
+
> disconnect
|
|
58
|
+
> exit
|
|
59
|
+
""",
|
|
60
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Optional: Port for internal services
|
|
64
|
+
commander_parser.add_argument(
|
|
65
|
+
"--port",
|
|
66
|
+
type=int,
|
|
67
|
+
default=8765,
|
|
68
|
+
help="Port for internal services (default: 8765)",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Optional: State directory
|
|
72
|
+
commander_parser.add_argument(
|
|
73
|
+
"--state-dir",
|
|
74
|
+
type=Path,
|
|
75
|
+
help="Directory for state persistence (optional)",
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Debug mode
|
|
79
|
+
commander_parser.add_argument(
|
|
80
|
+
"--debug",
|
|
81
|
+
action="store_true",
|
|
82
|
+
help="Enable debug logging",
|
|
83
|
+
)
|
|
@@ -85,6 +85,16 @@ def add_run_arguments(parser: argparse.ArgumentParser) -> None:
|
|
|
85
85
|
action="store_true",
|
|
86
86
|
help="Pass --resume flag to Claude Code to resume the last conversation",
|
|
87
87
|
)
|
|
88
|
+
run_group.add_argument(
|
|
89
|
+
"--chrome",
|
|
90
|
+
action="store_true",
|
|
91
|
+
help="Enable Claude in Chrome integration (passed to Claude Code)",
|
|
92
|
+
)
|
|
93
|
+
run_group.add_argument(
|
|
94
|
+
"--no-chrome",
|
|
95
|
+
action="store_true",
|
|
96
|
+
help="Disable Claude in Chrome integration (passed to Claude Code)",
|
|
97
|
+
)
|
|
88
98
|
|
|
89
99
|
# Dependency checking options
|
|
90
100
|
dep_group = parser.add_argument_group("dependency options")
|
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
|
|
|
@@ -315,7 +316,7 @@ def deploy_output_style_on_startup():
|
|
|
315
316
|
Deploys all styles:
|
|
316
317
|
- claude-mpm.md (professional mode)
|
|
317
318
|
- claude-mpm-teacher.md (teaching mode)
|
|
318
|
-
- claude-mpm-
|
|
319
|
+
- claude-mpm-research.md (research mode - for codebase analysis)
|
|
319
320
|
"""
|
|
320
321
|
try:
|
|
321
322
|
from ..core.output_style_manager import OutputStyleManager
|
|
@@ -1139,33 +1140,70 @@ def show_skill_summary():
|
|
|
1139
1140
|
|
|
1140
1141
|
|
|
1141
1142
|
def verify_and_show_pm_skills():
|
|
1142
|
-
"""Verify PM skills and display status.
|
|
1143
|
+
"""Verify PM skills and display status with enhanced validation.
|
|
1143
1144
|
|
|
1144
|
-
WHY: PM skills are
|
|
1145
|
-
|
|
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.
|
|
1149
|
+
|
|
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
|
|
1146
1154
|
"""
|
|
1147
1155
|
try:
|
|
1148
1156
|
from pathlib import Path
|
|
1149
1157
|
|
|
1150
|
-
from ..services.pm_skills_deployer import
|
|
1158
|
+
from ..services.pm_skills_deployer import (
|
|
1159
|
+
REQUIRED_PM_SKILLS,
|
|
1160
|
+
PMSkillsDeployerService,
|
|
1161
|
+
)
|
|
1151
1162
|
|
|
1152
1163
|
deployer = PMSkillsDeployerService()
|
|
1153
1164
|
project_dir = Path.cwd()
|
|
1154
1165
|
|
|
1155
|
-
|
|
1166
|
+
# Verify with auto-repair enabled
|
|
1167
|
+
result = deployer.verify_pm_skills(project_dir, auto_repair=True)
|
|
1156
1168
|
|
|
1157
1169
|
if result.verified:
|
|
1158
|
-
# Show verified status
|
|
1159
|
-
|
|
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
|
+
)
|
|
1160
1175
|
else:
|
|
1161
|
-
#
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
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
|
+
)
|
|
1167
1197
|
else:
|
|
1168
|
-
|
|
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}")
|
|
1169
1207
|
|
|
1170
1208
|
except ImportError:
|
|
1171
1209
|
# PM skills deployer not available - skip silently
|
|
@@ -7,7 +7,7 @@ Shows welcome message, version info, ASCII art, and what's new section.
|
|
|
7
7
|
import os
|
|
8
8
|
import re
|
|
9
9
|
import shutil
|
|
10
|
-
import subprocess
|
|
10
|
+
import subprocess # nosec B404 - required for git operations
|
|
11
11
|
from pathlib import Path
|
|
12
12
|
from typing import List
|
|
13
13
|
|
|
@@ -65,8 +65,8 @@ def _get_recent_commits(max_commits: int = 3) -> List[str]:
|
|
|
65
65
|
if not is_git_repository("."):
|
|
66
66
|
return []
|
|
67
67
|
|
|
68
|
-
# Run git log with custom format
|
|
69
|
-
result = subprocess.run(
|
|
68
|
+
# Run git log with custom format (safe - no user input)
|
|
69
|
+
result = subprocess.run( # nosec B603 B607
|
|
70
70
|
["git", "log", "--format=%h • %ar • %s", f"-{max_commits}"],
|
|
71
71
|
capture_output=True,
|
|
72
72
|
text=True,
|
|
@@ -216,6 +216,59 @@ def _get_cwd_display(max_width: int = 40) -> str:
|
|
|
216
216
|
return "..." + cwd[-(max_width - 3) :]
|
|
217
217
|
|
|
218
218
|
|
|
219
|
+
def _count_mpm_skills() -> int:
|
|
220
|
+
"""
|
|
221
|
+
Count user-level MPM skills from ~/.claude/skills/.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
Number of skill directories with SKILL.md files
|
|
225
|
+
"""
|
|
226
|
+
try:
|
|
227
|
+
user_skills_dir = Path.home() / ".claude" / "skills"
|
|
228
|
+
if not user_skills_dir.exists():
|
|
229
|
+
return 0
|
|
230
|
+
|
|
231
|
+
# Count directories with SKILL.md (skill directories)
|
|
232
|
+
skill_count = 0
|
|
233
|
+
for item in user_skills_dir.iterdir():
|
|
234
|
+
if item.is_dir():
|
|
235
|
+
skill_file = item / "SKILL.md"
|
|
236
|
+
if skill_file.exists():
|
|
237
|
+
skill_count += 1
|
|
238
|
+
# Also count standalone .md files (legacy format)
|
|
239
|
+
elif item.is_file() and item.suffix == ".md" and item.name != "README.md":
|
|
240
|
+
skill_count += 1
|
|
241
|
+
|
|
242
|
+
return skill_count
|
|
243
|
+
except Exception:
|
|
244
|
+
# Silent failure - return 0 if any error
|
|
245
|
+
return 0
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _count_deployed_agents() -> int:
|
|
249
|
+
"""
|
|
250
|
+
Count deployed agents from .claude/agents/.
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Number of deployed agent files
|
|
254
|
+
"""
|
|
255
|
+
try:
|
|
256
|
+
deploy_target = Path.cwd() / ".claude" / "agents"
|
|
257
|
+
if not deploy_target.exists():
|
|
258
|
+
return 0
|
|
259
|
+
|
|
260
|
+
# Count .md files, excluding README and other docs
|
|
261
|
+
agent_files = [
|
|
262
|
+
f
|
|
263
|
+
for f in deploy_target.glob("*.md")
|
|
264
|
+
if not f.name.startswith(("README", "INSTRUCTIONS", "."))
|
|
265
|
+
]
|
|
266
|
+
return len(agent_files)
|
|
267
|
+
except Exception:
|
|
268
|
+
# Silent failure - return 0 if any error
|
|
269
|
+
return 0
|
|
270
|
+
|
|
271
|
+
|
|
219
272
|
def _format_two_column_line(
|
|
220
273
|
left: str, right: str, left_panel_width: int, right_panel_width: int
|
|
221
274
|
) -> str:
|
|
@@ -402,11 +455,25 @@ def display_startup_banner(version: str, logging_level: str) -> None:
|
|
|
402
455
|
)
|
|
403
456
|
)
|
|
404
457
|
|
|
405
|
-
# Line 10: Model info | separator
|
|
458
|
+
# Line 10: Model info with counts | separator
|
|
406
459
|
separator = "─" * right_panel_width
|
|
460
|
+
agent_count = _count_deployed_agents()
|
|
461
|
+
skill_count = _count_mpm_skills()
|
|
462
|
+
|
|
463
|
+
# Format: "Sonnet 4.5 · 44 agents, 19 skills"
|
|
464
|
+
if agent_count > 0 or skill_count > 0:
|
|
465
|
+
counts_text = []
|
|
466
|
+
if agent_count > 0:
|
|
467
|
+
counts_text.append(f"{agent_count} agent{'s' if agent_count != 1 else ''}")
|
|
468
|
+
if skill_count > 0:
|
|
469
|
+
counts_text.append(f"{skill_count} skill{'s' if skill_count != 1 else ''}")
|
|
470
|
+
model_info = f"Sonnet 4.5 · {', '.join(counts_text)}"
|
|
471
|
+
else:
|
|
472
|
+
model_info = "Sonnet 4.5 · Claude MPM"
|
|
473
|
+
|
|
407
474
|
lines.append(
|
|
408
475
|
_format_two_column_line(
|
|
409
|
-
|
|
476
|
+
model_info, separator, left_panel_width, right_panel_width
|
|
410
477
|
)
|
|
411
478
|
)
|
|
412
479
|
|
|
@@ -672,7 +672,7 @@ async def trigger_vector_search_indexing(project_root: Optional[Path] = None) ->
|
|
|
672
672
|
# Using installed binary
|
|
673
673
|
cmd = [vector_search_path, "index", str(project_root)]
|
|
674
674
|
|
|
675
|
-
logger.
|
|
675
|
+
logger.debug(
|
|
676
676
|
"MCP Vector Search: Starting background indexing for improved code search"
|
|
677
677
|
)
|
|
678
678
|
|
|
@@ -761,7 +761,7 @@ def _start_vector_search_subprocess(project_root: Optional[Path] = None) -> None
|
|
|
761
761
|
else:
|
|
762
762
|
cmd = [vector_search_path, "index", str(project_root)]
|
|
763
763
|
|
|
764
|
-
logger.
|
|
764
|
+
logger.debug(
|
|
765
765
|
"MCP Vector Search: Starting background indexing for improved code search"
|
|
766
766
|
)
|
|
767
767
|
|
claude_mpm/cli/utils.py
CHANGED
|
@@ -107,7 +107,7 @@ def get_agent_versions_display() -> Optional[str]:
|
|
|
107
107
|
base_version_tuple
|
|
108
108
|
)
|
|
109
109
|
output_lines.append(f"\n Base Agent Version: {base_version_str}")
|
|
110
|
-
except Exception:
|
|
110
|
+
except Exception: # nosec B110 - intentional: version display is optional
|
|
111
111
|
pass
|
|
112
112
|
|
|
113
113
|
# Check for agents needing migration
|
|
@@ -173,8 +173,12 @@ def setup_logging(args) -> object:
|
|
|
173
173
|
if not hasattr(args, "logging") or args.logging is None:
|
|
174
174
|
args.logging = LogLevel.OFF.value
|
|
175
175
|
|
|
176
|
+
# Handle deprecated --verbose flag
|
|
177
|
+
if hasattr(args, "verbose") and args.verbose and args.logging == LogLevel.OFF.value:
|
|
178
|
+
args.logging = LogLevel.INFO.value
|
|
179
|
+
|
|
176
180
|
# Handle deprecated --debug flag
|
|
177
|
-
if hasattr(args, "debug") and args.debug
|
|
181
|
+
if hasattr(args, "debug") and args.debug:
|
|
178
182
|
args.logging = LogLevel.DEBUG.value
|
|
179
183
|
|
|
180
184
|
# Only setup logging if not OFF
|
|
@@ -204,7 +208,7 @@ def ensure_directories() -> None:
|
|
|
204
208
|
from ..init import ensure_directories as init_ensure_directories
|
|
205
209
|
|
|
206
210
|
init_ensure_directories()
|
|
207
|
-
except Exception:
|
|
211
|
+
except Exception: # nosec B110
|
|
208
212
|
# Continue even if initialization fails
|
|
209
213
|
# The individual commands will handle missing directories as needed
|
|
210
214
|
pass
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""MPM Commander - Multi-Project Orchestration.
|
|
2
|
+
|
|
3
|
+
This module provides the core infrastructure for managing multiple projects
|
|
4
|
+
with isolated state, work queues, and tool sessions.
|
|
5
|
+
|
|
6
|
+
Key Components:
|
|
7
|
+
- ProjectRegistry: Thread-safe project management
|
|
8
|
+
- Project models: Data structures for state and sessions
|
|
9
|
+
- TmuxOrchestrator: Tmux session and pane management
|
|
10
|
+
- Config loading: .claude-mpm/ directory configuration
|
|
11
|
+
- CommanderDaemon: Main daemon process for orchestration
|
|
12
|
+
- ProjectSession: Per-project lifecycle management
|
|
13
|
+
- InstanceManager: Framework selection and instance lifecycle
|
|
14
|
+
- Frameworks: Claude Code, MPM framework abstractions
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> from claude_mpm.commander import ProjectRegistry
|
|
18
|
+
>>> registry = ProjectRegistry()
|
|
19
|
+
>>> project = registry.register("/path/to/project")
|
|
20
|
+
>>> registry.update_state(project.id, ProjectState.WORKING)
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from claude_mpm.commander.config import DaemonConfig
|
|
24
|
+
from claude_mpm.commander.config_loader import load_project_config
|
|
25
|
+
from claude_mpm.commander.daemon import CommanderDaemon
|
|
26
|
+
from claude_mpm.commander.frameworks import (
|
|
27
|
+
BaseFramework,
|
|
28
|
+
ClaudeCodeFramework,
|
|
29
|
+
InstanceInfo,
|
|
30
|
+
MPMFramework,
|
|
31
|
+
)
|
|
32
|
+
from claude_mpm.commander.instance_manager import (
|
|
33
|
+
FrameworkNotFoundError,
|
|
34
|
+
InstanceAlreadyExistsError,
|
|
35
|
+
InstanceManager,
|
|
36
|
+
InstanceNotFoundError,
|
|
37
|
+
)
|
|
38
|
+
from claude_mpm.commander.models import (
|
|
39
|
+
Project,
|
|
40
|
+
ProjectState,
|
|
41
|
+
ThreadMessage,
|
|
42
|
+
ToolSession,
|
|
43
|
+
)
|
|
44
|
+
from claude_mpm.commander.project_session import ProjectSession, SessionState
|
|
45
|
+
from claude_mpm.commander.registry import ProjectRegistry
|
|
46
|
+
from claude_mpm.commander.tmux_orchestrator import (
|
|
47
|
+
TmuxNotFoundError,
|
|
48
|
+
TmuxOrchestrator,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"BaseFramework",
|
|
53
|
+
"ClaudeCodeFramework",
|
|
54
|
+
"CommanderDaemon",
|
|
55
|
+
"DaemonConfig",
|
|
56
|
+
"FrameworkNotFoundError",
|
|
57
|
+
"InstanceAlreadyExistsError",
|
|
58
|
+
"InstanceInfo",
|
|
59
|
+
"InstanceManager",
|
|
60
|
+
"InstanceNotFoundError",
|
|
61
|
+
"MPMFramework",
|
|
62
|
+
"Project",
|
|
63
|
+
"ProjectRegistry",
|
|
64
|
+
"ProjectSession",
|
|
65
|
+
"ProjectState",
|
|
66
|
+
"SessionState",
|
|
67
|
+
"ThreadMessage",
|
|
68
|
+
"TmuxNotFoundError",
|
|
69
|
+
"TmuxOrchestrator",
|
|
70
|
+
"ToolSession",
|
|
71
|
+
"load_project_config",
|
|
72
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Runtime adapters for MPM Commander.
|
|
2
|
+
|
|
3
|
+
This package provides adapters for different AI coding tools, allowing
|
|
4
|
+
the TmuxOrchestrator to interface with various runtimes in a uniform way.
|
|
5
|
+
|
|
6
|
+
Two types of adapters:
|
|
7
|
+
- RuntimeAdapter: Synchronous parsing and state detection
|
|
8
|
+
- CommunicationAdapter: Async I/O and state management
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from .base import Capability, ParsedResponse, RuntimeAdapter
|
|
12
|
+
from .claude_code import ClaudeCodeAdapter
|
|
13
|
+
from .communication import (
|
|
14
|
+
AdapterResponse,
|
|
15
|
+
AdapterState,
|
|
16
|
+
BaseCommunicationAdapter,
|
|
17
|
+
ClaudeCodeCommunicationAdapter,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
# Communication adapters (async I/O)
|
|
22
|
+
"AdapterResponse",
|
|
23
|
+
"AdapterState",
|
|
24
|
+
"BaseCommunicationAdapter",
|
|
25
|
+
# Runtime adapters (parsing)
|
|
26
|
+
"Capability",
|
|
27
|
+
"ClaudeCodeAdapter",
|
|
28
|
+
"ClaudeCodeCommunicationAdapter",
|
|
29
|
+
"ParsedResponse",
|
|
30
|
+
"RuntimeAdapter",
|
|
31
|
+
]
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"""Base runtime adapter interface for MPM Commander.
|
|
2
|
+
|
|
3
|
+
This module defines the abstract interface for runtime adapters that bridge
|
|
4
|
+
between the TmuxOrchestrator and various AI coding tools (Claude Code, Cursor, etc.).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from abc import ABC, abstractmethod
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from enum import Enum
|
|
10
|
+
from typing import List, Optional, Set
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class Capability(Enum):
|
|
14
|
+
"""Capabilities that a runtime adapter can provide."""
|
|
15
|
+
|
|
16
|
+
TOOL_USE = "tool_use"
|
|
17
|
+
FILE_EDIT = "file_edit"
|
|
18
|
+
FILE_CREATE = "file_create"
|
|
19
|
+
GIT_OPERATIONS = "git_operations"
|
|
20
|
+
SHELL_COMMANDS = "shell_commands"
|
|
21
|
+
WEB_SEARCH = "web_search"
|
|
22
|
+
COMPLEX_REASONING = "complex_reasoning"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass
|
|
26
|
+
class ParsedResponse:
|
|
27
|
+
"""Parsed output from a runtime tool.
|
|
28
|
+
|
|
29
|
+
Attributes:
|
|
30
|
+
content: The extracted text content, with ANSI codes removed
|
|
31
|
+
is_complete: True if tool is waiting for input (idle state)
|
|
32
|
+
is_error: True if an error was detected in the output
|
|
33
|
+
error_message: The error message if is_error is True
|
|
34
|
+
is_question: True if tool is asking a question
|
|
35
|
+
question_text: The question text if is_question is True
|
|
36
|
+
options: List of options if presenting a choice
|
|
37
|
+
|
|
38
|
+
Example:
|
|
39
|
+
>>> response = ParsedResponse(
|
|
40
|
+
... content="File created successfully",
|
|
41
|
+
... is_complete=True,
|
|
42
|
+
... is_error=False,
|
|
43
|
+
... error_message=None,
|
|
44
|
+
... is_question=False,
|
|
45
|
+
... question_text=None,
|
|
46
|
+
... options=None
|
|
47
|
+
... )
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
content: str
|
|
51
|
+
is_complete: bool
|
|
52
|
+
is_error: bool
|
|
53
|
+
error_message: Optional[str] = None
|
|
54
|
+
is_question: bool = False
|
|
55
|
+
question_text: Optional[str] = None
|
|
56
|
+
options: Optional[List[str]] = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class RuntimeAdapter(ABC):
|
|
60
|
+
"""Abstract base class for runtime adapters.
|
|
61
|
+
|
|
62
|
+
A runtime adapter provides the interface between the TmuxOrchestrator
|
|
63
|
+
and a specific AI coding tool. It handles:
|
|
64
|
+
- Launching the tool with appropriate settings
|
|
65
|
+
- Formatting input messages
|
|
66
|
+
- Detecting tool states (idle, error, questioning)
|
|
67
|
+
- Parsing tool output into structured responses
|
|
68
|
+
|
|
69
|
+
Example:
|
|
70
|
+
>>> class MyAdapter(RuntimeAdapter):
|
|
71
|
+
... @property
|
|
72
|
+
... def name(self) -> str:
|
|
73
|
+
... return "my-tool"
|
|
74
|
+
...
|
|
75
|
+
... def build_launch_command(self, project_path: str) -> str:
|
|
76
|
+
... return f"cd {project_path} && my-tool --interactive"
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
@abstractmethod
|
|
80
|
+
def build_launch_command(
|
|
81
|
+
self, project_path: str, agent_prompt: Optional[str] = None
|
|
82
|
+
) -> str:
|
|
83
|
+
"""Generate shell command to start the tool.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
project_path: Absolute path to the project directory
|
|
87
|
+
agent_prompt: Optional system prompt to configure the agent
|
|
88
|
+
|
|
89
|
+
Returns:
|
|
90
|
+
Shell command string ready to execute
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> adapter.build_launch_command("/home/user/project", "You are a Python expert")
|
|
94
|
+
'cd /home/user/project && claude --system-prompt "You are a Python expert"'
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
def format_input(self, message: str) -> str:
|
|
99
|
+
"""Prepare message for tool's input format.
|
|
100
|
+
|
|
101
|
+
Args:
|
|
102
|
+
message: The user message to send
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Formatted message ready to send to the tool
|
|
106
|
+
|
|
107
|
+
Example:
|
|
108
|
+
>>> adapter.format_input("Fix the bug in main.py")
|
|
109
|
+
'Fix the bug in main.py'
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
def detect_idle(self, output: str) -> bool:
|
|
114
|
+
"""Recognize when tool is waiting for input.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
output: Raw output from the tool (may contain ANSI codes)
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True if the tool is in an idle state awaiting input
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
>>> adapter.detect_idle("Done editing file.\\n> ")
|
|
124
|
+
True
|
|
125
|
+
>>> adapter.detect_idle("Processing request...")
|
|
126
|
+
False
|
|
127
|
+
"""
|
|
128
|
+
|
|
129
|
+
@abstractmethod
|
|
130
|
+
def detect_error(self, output: str) -> Optional[str]:
|
|
131
|
+
"""Recognize error states, return error message if found.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
output: Raw output from the tool
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Error message string if error detected, None otherwise
|
|
138
|
+
|
|
139
|
+
Example:
|
|
140
|
+
>>> adapter.detect_error("Error: File not found: config.py")
|
|
141
|
+
'Error: File not found: config.py'
|
|
142
|
+
>>> adapter.detect_error("File edited successfully")
|
|
143
|
+
None
|
|
144
|
+
"""
|
|
145
|
+
|
|
146
|
+
@abstractmethod
|
|
147
|
+
def parse_response(self, output: str) -> ParsedResponse:
|
|
148
|
+
"""Extract meaningful content from output.
|
|
149
|
+
|
|
150
|
+
This method combines all detection logic (idle, error, questions)
|
|
151
|
+
into a single structured response.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
output: Raw output from the tool
|
|
155
|
+
|
|
156
|
+
Returns:
|
|
157
|
+
ParsedResponse with all detected states and content
|
|
158
|
+
|
|
159
|
+
Example:
|
|
160
|
+
>>> response = adapter.parse_response("Error: Invalid input\\n> ")
|
|
161
|
+
>>> response.is_error
|
|
162
|
+
True
|
|
163
|
+
>>> response.is_complete
|
|
164
|
+
True
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
@property
|
|
168
|
+
@abstractmethod
|
|
169
|
+
def capabilities(self) -> Set[Capability]:
|
|
170
|
+
"""What this tool can do.
|
|
171
|
+
|
|
172
|
+
Returns:
|
|
173
|
+
Set of Capability enums indicating supported features
|
|
174
|
+
|
|
175
|
+
Example:
|
|
176
|
+
>>> adapter.capabilities
|
|
177
|
+
{Capability.FILE_EDIT, Capability.TOOL_USE}
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
@property
|
|
181
|
+
@abstractmethod
|
|
182
|
+
def name(self) -> str:
|
|
183
|
+
"""Runtime identifier.
|
|
184
|
+
|
|
185
|
+
Returns:
|
|
186
|
+
Unique name for this runtime adapter
|
|
187
|
+
|
|
188
|
+
Example:
|
|
189
|
+
>>> adapter.name
|
|
190
|
+
'claude-code'
|
|
191
|
+
"""
|