claude-mpm 5.4.85__py3-none-any.whl → 5.6.76__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 +109 -706
- claude_mpm/agents/WORKFLOW.md +2 -0
- claude_mpm/agents/templates/circuit-breakers.md +26 -17
- claude_mpm/auth/__init__.py +35 -0
- claude_mpm/auth/callback_server.py +328 -0
- claude_mpm/auth/models.py +104 -0
- claude_mpm/auth/oauth_manager.py +266 -0
- claude_mpm/auth/providers/__init__.py +12 -0
- claude_mpm/auth/providers/base.py +165 -0
- claude_mpm/auth/providers/google.py +261 -0
- claude_mpm/auth/token_storage.py +252 -0
- claude_mpm/cli/commands/autotodos.py +566 -0
- claude_mpm/cli/commands/commander.py +216 -0
- claude_mpm/cli/commands/hook_errors.py +60 -60
- claude_mpm/cli/commands/mcp.py +29 -17
- claude_mpm/cli/commands/mcp_command_router.py +39 -0
- claude_mpm/cli/commands/mcp_service_commands.py +304 -0
- claude_mpm/cli/commands/monitor.py +2 -2
- claude_mpm/cli/commands/mpm_init/core.py +2 -2
- claude_mpm/cli/commands/oauth.py +481 -0
- 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 +128 -16
- claude_mpm/cli/helpers.py +1 -1
- claude_mpm/cli/parsers/base_parser.py +84 -1
- claude_mpm/cli/parsers/commander_parser.py +116 -0
- claude_mpm/cli/parsers/mcp_parser.py +79 -0
- claude_mpm/cli/parsers/oauth_parser.py +165 -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 +345 -40
- claude_mpm/cli/startup_display.py +76 -7
- claude_mpm/cli/startup_logging.py +2 -2
- claude_mpm/cli/startup_migrations.py +236 -0
- claude_mpm/cli/utils.py +7 -3
- claude_mpm/commander/__init__.py +78 -0
- claude_mpm/commander/adapters/__init__.py +60 -0
- claude_mpm/commander/adapters/auggie.py +260 -0
- claude_mpm/commander/adapters/base.py +288 -0
- claude_mpm/commander/adapters/claude_code.py +392 -0
- claude_mpm/commander/adapters/codex.py +237 -0
- claude_mpm/commander/adapters/communication.py +366 -0
- claude_mpm/commander/adapters/example_usage.py +310 -0
- claude_mpm/commander/adapters/mpm.py +389 -0
- claude_mpm/commander/adapters/registry.py +204 -0
- claude_mpm/commander/api/__init__.py +16 -0
- claude_mpm/commander/api/app.py +121 -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 +226 -0
- claude_mpm/commander/api/routes/work.py +296 -0
- claude_mpm/commander/api/schemas.py +186 -0
- claude_mpm/commander/chat/__init__.py +7 -0
- claude_mpm/commander/chat/cli.py +149 -0
- claude_mpm/commander/chat/commands.py +124 -0
- claude_mpm/commander/chat/repl.py +1957 -0
- claude_mpm/commander/config.py +51 -0
- claude_mpm/commander/config_loader.py +115 -0
- claude_mpm/commander/core/__init__.py +10 -0
- claude_mpm/commander/core/block_manager.py +325 -0
- claude_mpm/commander/core/response_manager.py +323 -0
- claude_mpm/commander/daemon.py +603 -0
- claude_mpm/commander/env_loader.py +59 -0
- claude_mpm/commander/events/__init__.py +26 -0
- claude_mpm/commander/events/manager.py +392 -0
- claude_mpm/commander/frameworks/__init__.py +12 -0
- claude_mpm/commander/frameworks/base.py +233 -0
- claude_mpm/commander/frameworks/claude_code.py +58 -0
- claude_mpm/commander/frameworks/mpm.py +57 -0
- claude_mpm/commander/git/__init__.py +5 -0
- claude_mpm/commander/git/worktree_manager.py +212 -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 +868 -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/memory/__init__.py +45 -0
- claude_mpm/commander/memory/compression.py +347 -0
- claude_mpm/commander/memory/embeddings.py +230 -0
- claude_mpm/commander/memory/entities.py +310 -0
- claude_mpm/commander/memory/example_usage.py +290 -0
- claude_mpm/commander/memory/integration.py +325 -0
- claude_mpm/commander/memory/search.py +381 -0
- claude_mpm/commander/memory/store.py +657 -0
- claude_mpm/commander/models/__init__.py +18 -0
- claude_mpm/commander/models/events.py +127 -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 +403 -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 +410 -0
- claude_mpm/commander/runtime/__init__.py +10 -0
- claude_mpm/commander/runtime/executor.py +191 -0
- claude_mpm/commander/runtime/monitor.py +346 -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 +362 -0
- claude_mpm/commander/web/__init__.py +1 -0
- claude_mpm/commander/work/__init__.py +30 -0
- claude_mpm/commander/work/executor.py +207 -0
- claude_mpm/commander/work/queue.py +405 -0
- claude_mpm/commander/workflow/__init__.py +27 -0
- claude_mpm/commander/workflow/event_handler.py +241 -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/config/skill_sources.py +16 -0
- claude_mpm/constants.py +5 -0
- claude_mpm/core/claude_runner.py +152 -0
- claude_mpm/core/config.py +35 -22
- claude_mpm/core/config_constants.py +74 -9
- claude_mpm/core/constants.py +56 -12
- claude_mpm/core/hook_manager.py +53 -4
- claude_mpm/core/interactive_session.py +5 -4
- claude_mpm/core/logger.py +26 -9
- claude_mpm/core/logging_utils.py +39 -13
- claude_mpm/core/network_config.py +148 -0
- claude_mpm/core/oneshot_session.py +7 -6
- claude_mpm/core/output_style_manager.py +52 -12
- claude_mpm/core/socketio_pool.py +47 -15
- claude_mpm/core/unified_config.py +10 -6
- claude_mpm/core/unified_paths.py +68 -80
- 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 +485 -0
- claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
- claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
- claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
- claude_mpm/hooks/claude_hooks/installer.py +291 -59
- claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
- claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
- claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
- claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
- claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
- claude_mpm/hooks/claude_hooks/services/container.py +326 -0
- claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
- claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
- claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
- claude_mpm/hooks/session_resume_hook.py +89 -1
- claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
- claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
- claude_mpm/init.py +22 -15
- claude_mpm/mcp/__init__.py +9 -0
- claude_mpm/mcp/google_workspace_server.py +610 -0
- claude_mpm/scripts/claude-hook-handler.sh +46 -19
- claude_mpm/services/agents/agent_recommendation_service.py +8 -8
- claude_mpm/services/agents/agent_selection_service.py +2 -2
- 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/agents/single_tier_deployment_service.py +4 -4
- 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/command_deployment_service.py +44 -26
- 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/hook_installer_service.py +77 -8
- 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/mcp_config_manager.py +99 -19
- claude_mpm/services/mcp_service_registry.py +294 -0
- claude_mpm/services/monitor/daemon_manager.py +15 -4
- claude_mpm/services/monitor/management/lifecycle.py +8 -2
- claude_mpm/services/monitor/server.py +111 -16
- claude_mpm/services/pm_skills_deployer.py +261 -87
- claude_mpm/services/skills/git_skill_source_manager.py +130 -10
- claude_mpm/services/skills/selective_skill_deployer.py +142 -16
- claude_mpm/services/skills/skill_discovery_service.py +74 -4
- claude_mpm/services/skills_deployer.py +31 -5
- claude_mpm/services/socketio/handlers/hook.py +14 -7
- claude_mpm/services/socketio/server/main.py +12 -4
- claude_mpm/skills/__init__.py +2 -1
- 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-pause/SKILL.md +170 -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/registry.py +295 -90
- claude_mpm/skills/skill_manager.py +4 -4
- claude_mpm-5.6.76.dist-info/METADATA +416 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
- 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.76.dist-info}/licenses/LICENSE +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
- {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
"""Claude Code CLI runtime adapter.
|
|
2
|
+
|
|
3
|
+
This module implements the RuntimeAdapter interface for the Claude Code CLI tool.
|
|
4
|
+
It handles launching Claude Code, detecting its various states, and parsing its output.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
import shlex
|
|
10
|
+
from typing import List, Optional, Set
|
|
11
|
+
|
|
12
|
+
from .base import (
|
|
13
|
+
Capability,
|
|
14
|
+
ParsedResponse,
|
|
15
|
+
RuntimeAdapter,
|
|
16
|
+
RuntimeCapability,
|
|
17
|
+
RuntimeInfo,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ClaudeCodeAdapter(RuntimeAdapter):
|
|
24
|
+
"""Adapter for Claude Code CLI.
|
|
25
|
+
|
|
26
|
+
This adapter provides integration with the Claude Code command-line interface,
|
|
27
|
+
handling its unique prompt formats, error messages, and interactive behaviors.
|
|
28
|
+
|
|
29
|
+
Example:
|
|
30
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
31
|
+
>>> cmd = adapter.build_launch_command("/home/user/project")
|
|
32
|
+
>>> print(cmd)
|
|
33
|
+
cd '/home/user/project' && claude --dangerously-skip-permissions
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
# Idle detection patterns (Claude Code prompt indicators)
|
|
37
|
+
IDLE_PATTERNS = [
|
|
38
|
+
r"^>\s*$", # Simple prompt
|
|
39
|
+
r"claude>\s*$", # Named prompt
|
|
40
|
+
r"╭─+╮", # Box drawing (Claude's UI)
|
|
41
|
+
r"What would you like", # Claude asking for input
|
|
42
|
+
r"How can I help", # Alternative greeting
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
# Error patterns - detect various error conditions
|
|
46
|
+
ERROR_PATTERNS = [
|
|
47
|
+
r"Error:",
|
|
48
|
+
r"Failed:",
|
|
49
|
+
r"Exception:",
|
|
50
|
+
r"Permission denied",
|
|
51
|
+
r"not found",
|
|
52
|
+
r"Traceback \(most recent call last\)",
|
|
53
|
+
r"FATAL:",
|
|
54
|
+
r"✗", # Claude's error indicator
|
|
55
|
+
r"command not found",
|
|
56
|
+
r"cannot access",
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
# Question patterns - detect when Claude is asking for confirmation
|
|
60
|
+
QUESTION_PATTERNS = [
|
|
61
|
+
r"Which option",
|
|
62
|
+
r"Should I proceed",
|
|
63
|
+
r"Please choose",
|
|
64
|
+
r"\(y/n\)\?",
|
|
65
|
+
r"Are you sure",
|
|
66
|
+
r"Do you want",
|
|
67
|
+
r"\[Y/n\]",
|
|
68
|
+
r"\[yes/no\]",
|
|
69
|
+
r"Select an option",
|
|
70
|
+
r"Choose from",
|
|
71
|
+
]
|
|
72
|
+
|
|
73
|
+
# ANSI escape code pattern for stripping color/formatting codes
|
|
74
|
+
ANSI_ESCAPE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def name(self) -> str:
|
|
78
|
+
"""Return the runtime identifier.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
The string "claude-code"
|
|
82
|
+
"""
|
|
83
|
+
return "claude-code"
|
|
84
|
+
|
|
85
|
+
@property
|
|
86
|
+
def capabilities(self) -> Set[Capability]:
|
|
87
|
+
"""Return the set of capabilities supported by Claude Code.
|
|
88
|
+
|
|
89
|
+
Claude Code is a full-featured AI coding assistant with comprehensive
|
|
90
|
+
tool use, file operations, git integration, and reasoning capabilities.
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Set of all supported Capability enums
|
|
94
|
+
"""
|
|
95
|
+
return {
|
|
96
|
+
Capability.TOOL_USE,
|
|
97
|
+
Capability.FILE_EDIT,
|
|
98
|
+
Capability.FILE_CREATE,
|
|
99
|
+
Capability.GIT_OPERATIONS,
|
|
100
|
+
Capability.SHELL_COMMANDS,
|
|
101
|
+
Capability.WEB_SEARCH,
|
|
102
|
+
Capability.COMPLEX_REASONING,
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def runtime_info(self) -> RuntimeInfo:
|
|
107
|
+
"""Return detailed runtime information."""
|
|
108
|
+
return RuntimeInfo(
|
|
109
|
+
name="claude-code",
|
|
110
|
+
version=None, # Version detection could be added
|
|
111
|
+
capabilities={
|
|
112
|
+
RuntimeCapability.FILE_READ,
|
|
113
|
+
RuntimeCapability.FILE_EDIT,
|
|
114
|
+
RuntimeCapability.FILE_CREATE,
|
|
115
|
+
RuntimeCapability.BASH_EXECUTION,
|
|
116
|
+
RuntimeCapability.GIT_OPERATIONS,
|
|
117
|
+
RuntimeCapability.TOOL_USE,
|
|
118
|
+
RuntimeCapability.WEB_SEARCH,
|
|
119
|
+
RuntimeCapability.COMPLEX_REASONING,
|
|
120
|
+
RuntimeCapability.AGENT_DELEGATION, # Claude Code supports Task tool
|
|
121
|
+
RuntimeCapability.HOOKS, # Claude Code supports hooks
|
|
122
|
+
RuntimeCapability.SKILLS, # Claude Code can load skills
|
|
123
|
+
RuntimeCapability.MONITOR, # Can be monitored
|
|
124
|
+
},
|
|
125
|
+
command="claude",
|
|
126
|
+
supports_agents=True, # Claude Code supports agent delegation
|
|
127
|
+
instruction_file="CLAUDE.md",
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
def build_launch_command(
|
|
131
|
+
self, project_path: str, agent_prompt: Optional[str] = None
|
|
132
|
+
) -> str:
|
|
133
|
+
"""Generate shell command to start Claude Code.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
project_path: Absolute path to the project directory
|
|
137
|
+
agent_prompt: Optional system prompt to configure Claude's behavior
|
|
138
|
+
|
|
139
|
+
Returns:
|
|
140
|
+
Shell command string ready to execute in bash
|
|
141
|
+
|
|
142
|
+
Example:
|
|
143
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
144
|
+
>>> adapter.build_launch_command("/home/user/project")
|
|
145
|
+
"cd '/home/user/project' && claude --dangerously-skip-permissions"
|
|
146
|
+
>>> adapter.build_launch_command("/home/user/project", "You are a Python expert")
|
|
147
|
+
"cd '/home/user/project' && claude --system-prompt 'You are a Python expert' --dangerously-skip-permissions"
|
|
148
|
+
|
|
149
|
+
Note:
|
|
150
|
+
Uses --dangerously-skip-permissions for automated operation.
|
|
151
|
+
This is appropriate for MPM Commander's controlled environment.
|
|
152
|
+
"""
|
|
153
|
+
# shlex.quote prevents shell injection
|
|
154
|
+
quoted_path = shlex.quote(project_path) # nosec B604
|
|
155
|
+
cmd = f"cd {quoted_path} && claude"
|
|
156
|
+
|
|
157
|
+
if agent_prompt:
|
|
158
|
+
quoted_prompt = shlex.quote(agent_prompt) # nosec B604
|
|
159
|
+
cmd += f" --system-prompt {quoted_prompt}"
|
|
160
|
+
|
|
161
|
+
# Skip permissions for automated operation
|
|
162
|
+
cmd += " --dangerously-skip-permissions"
|
|
163
|
+
|
|
164
|
+
logger.debug(f"Built launch command: {cmd}")
|
|
165
|
+
return cmd
|
|
166
|
+
|
|
167
|
+
def format_input(self, message: str) -> str:
|
|
168
|
+
"""Prepare message for Claude Code's input format.
|
|
169
|
+
|
|
170
|
+
Claude Code accepts plain text input, so this method simply
|
|
171
|
+
strips leading/trailing whitespace.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
message: The user message to send
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Formatted message (whitespace-trimmed)
|
|
178
|
+
|
|
179
|
+
Example:
|
|
180
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
181
|
+
>>> adapter.format_input(" Fix the bug in main.py ")
|
|
182
|
+
'Fix the bug in main.py'
|
|
183
|
+
"""
|
|
184
|
+
formatted = message.strip()
|
|
185
|
+
logger.debug(f"Formatted input: {formatted[:100]}...")
|
|
186
|
+
return formatted
|
|
187
|
+
|
|
188
|
+
def strip_ansi(self, text: str) -> str:
|
|
189
|
+
"""Remove ANSI escape codes from text.
|
|
190
|
+
|
|
191
|
+
Args:
|
|
192
|
+
text: Text potentially containing ANSI escape sequences
|
|
193
|
+
|
|
194
|
+
Returns:
|
|
195
|
+
Clean text with ANSI codes removed
|
|
196
|
+
|
|
197
|
+
Example:
|
|
198
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
199
|
+
>>> adapter.strip_ansi("\\x1b[32mSuccess\\x1b[0m")
|
|
200
|
+
'Success'
|
|
201
|
+
"""
|
|
202
|
+
return self.ANSI_ESCAPE.sub("", text)
|
|
203
|
+
|
|
204
|
+
def detect_idle(self, output: str) -> bool:
|
|
205
|
+
"""Recognize when Claude Code is waiting for input.
|
|
206
|
+
|
|
207
|
+
Checks the last line of output against known idle patterns
|
|
208
|
+
such as the prompt indicator or greeting messages.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
output: Raw output from Claude Code (may contain ANSI codes)
|
|
212
|
+
|
|
213
|
+
Returns:
|
|
214
|
+
True if Claude is in an idle state awaiting input
|
|
215
|
+
|
|
216
|
+
Example:
|
|
217
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
218
|
+
>>> adapter.detect_idle("Done editing file.\\n> ")
|
|
219
|
+
True
|
|
220
|
+
>>> adapter.detect_idle("Processing request...")
|
|
221
|
+
False
|
|
222
|
+
>>> adapter.detect_idle("╭─────────────────╮\\nWhat would you like me to help with?")
|
|
223
|
+
True
|
|
224
|
+
"""
|
|
225
|
+
if not output:
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
clean = self.strip_ansi(output)
|
|
229
|
+
lines = clean.strip().split("\n")
|
|
230
|
+
|
|
231
|
+
if not lines:
|
|
232
|
+
return False
|
|
233
|
+
|
|
234
|
+
last_line = lines[-1].strip()
|
|
235
|
+
|
|
236
|
+
# Check against all idle patterns
|
|
237
|
+
for pattern in self.IDLE_PATTERNS:
|
|
238
|
+
if re.search(pattern, last_line):
|
|
239
|
+
logger.debug(f"Detected idle state with pattern: {pattern}")
|
|
240
|
+
return True
|
|
241
|
+
|
|
242
|
+
return False
|
|
243
|
+
|
|
244
|
+
def detect_error(self, output: str) -> Optional[str]:
|
|
245
|
+
"""Recognize error states and extract error message.
|
|
246
|
+
|
|
247
|
+
Searches the output for known error patterns and returns
|
|
248
|
+
the line containing the error for context.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
output: Raw output from Claude Code
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
Error message string if error detected, None otherwise
|
|
255
|
+
|
|
256
|
+
Example:
|
|
257
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
258
|
+
>>> adapter.detect_error("Error: File not found: config.py")
|
|
259
|
+
'Error: File not found: config.py'
|
|
260
|
+
>>> adapter.detect_error("File edited successfully")
|
|
261
|
+
None
|
|
262
|
+
>>> adapter.detect_error("Traceback (most recent call last):\\n File...")
|
|
263
|
+
'Traceback (most recent call last):'
|
|
264
|
+
"""
|
|
265
|
+
clean = self.strip_ansi(output)
|
|
266
|
+
|
|
267
|
+
for pattern in self.ERROR_PATTERNS:
|
|
268
|
+
match = re.search(pattern, clean, re.IGNORECASE)
|
|
269
|
+
if match:
|
|
270
|
+
# Extract the line containing the error for context
|
|
271
|
+
for line in clean.split("\n"):
|
|
272
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
273
|
+
error_msg = line.strip()
|
|
274
|
+
logger.warning(f"Detected error: {error_msg}")
|
|
275
|
+
return error_msg
|
|
276
|
+
|
|
277
|
+
return None
|
|
278
|
+
|
|
279
|
+
def detect_question(
|
|
280
|
+
self, output: str
|
|
281
|
+
) -> tuple[bool, Optional[str], Optional[List[str]]]:
|
|
282
|
+
"""Detect if Claude is asking a question and extract options.
|
|
283
|
+
|
|
284
|
+
Searches for question patterns and attempts to extract the question
|
|
285
|
+
text along with any numbered options presented.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
output: Raw output from Claude Code
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
Tuple of (is_question, question_text, options)
|
|
292
|
+
- is_question: True if a question was detected
|
|
293
|
+
- question_text: The question text if found
|
|
294
|
+
- options: List of option strings if numbered options found
|
|
295
|
+
|
|
296
|
+
Example:
|
|
297
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
298
|
+
>>> is_q, text, opts = adapter.detect_question("Should I proceed? (y/n)?")
|
|
299
|
+
>>> is_q
|
|
300
|
+
True
|
|
301
|
+
>>> text
|
|
302
|
+
'Should I proceed? (y/n)?'
|
|
303
|
+
>>> is_q, text, opts = adapter.detect_question(
|
|
304
|
+
... "Which option:\\n1. Create new file\\n2. Edit existing"
|
|
305
|
+
... )
|
|
306
|
+
>>> opts
|
|
307
|
+
['Create new file', 'Edit existing']
|
|
308
|
+
"""
|
|
309
|
+
clean = self.strip_ansi(output)
|
|
310
|
+
|
|
311
|
+
for pattern in self.QUESTION_PATTERNS:
|
|
312
|
+
if re.search(pattern, clean, re.IGNORECASE):
|
|
313
|
+
# Try to extract question and options
|
|
314
|
+
lines = clean.strip().split("\n")
|
|
315
|
+
question = None
|
|
316
|
+
options = []
|
|
317
|
+
|
|
318
|
+
for line in lines:
|
|
319
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
320
|
+
question = line.strip()
|
|
321
|
+
|
|
322
|
+
# Look for numbered options (1., 2., 1), 2), etc.)
|
|
323
|
+
opt_match = re.match(r"^\s*(\d+)[.):]\s*(.+)$", line)
|
|
324
|
+
if opt_match:
|
|
325
|
+
options.append(opt_match.group(2).strip())
|
|
326
|
+
|
|
327
|
+
logger.debug(
|
|
328
|
+
f"Detected question: {question}, options: {options if options else 'none'}"
|
|
329
|
+
)
|
|
330
|
+
return True, question, options if options else None
|
|
331
|
+
|
|
332
|
+
return False, None, None
|
|
333
|
+
|
|
334
|
+
def parse_response(self, output: str) -> ParsedResponse:
|
|
335
|
+
"""Extract meaningful content from Claude Code output.
|
|
336
|
+
|
|
337
|
+
Combines all detection logic (idle, error, questions) into a
|
|
338
|
+
single structured response object.
|
|
339
|
+
|
|
340
|
+
Args:
|
|
341
|
+
output: Raw output from Claude Code
|
|
342
|
+
|
|
343
|
+
Returns:
|
|
344
|
+
ParsedResponse with all detected states and content
|
|
345
|
+
|
|
346
|
+
Example:
|
|
347
|
+
>>> adapter = ClaudeCodeAdapter()
|
|
348
|
+
>>> response = adapter.parse_response("Error: Invalid input\\n> ")
|
|
349
|
+
>>> response.is_error
|
|
350
|
+
True
|
|
351
|
+
>>> response.is_complete
|
|
352
|
+
True
|
|
353
|
+
>>> response.error_message
|
|
354
|
+
'Error: Invalid input'
|
|
355
|
+
|
|
356
|
+
>>> response = adapter.parse_response("File created: test.py\\n> ")
|
|
357
|
+
>>> response.content
|
|
358
|
+
'File created: test.py\\n> '
|
|
359
|
+
>>> response.is_complete
|
|
360
|
+
True
|
|
361
|
+
>>> response.is_error
|
|
362
|
+
False
|
|
363
|
+
"""
|
|
364
|
+
if not output:
|
|
365
|
+
return ParsedResponse(
|
|
366
|
+
content="",
|
|
367
|
+
is_complete=False,
|
|
368
|
+
is_error=False,
|
|
369
|
+
is_question=False,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
clean = self.strip_ansi(output)
|
|
373
|
+
error_msg = self.detect_error(output)
|
|
374
|
+
is_question, question_text, options = self.detect_question(output)
|
|
375
|
+
is_complete = self.detect_idle(output)
|
|
376
|
+
|
|
377
|
+
response = ParsedResponse(
|
|
378
|
+
content=clean,
|
|
379
|
+
is_complete=is_complete,
|
|
380
|
+
is_error=error_msg is not None,
|
|
381
|
+
error_message=error_msg,
|
|
382
|
+
is_question=is_question,
|
|
383
|
+
question_text=question_text,
|
|
384
|
+
options=options,
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
logger.debug(
|
|
388
|
+
f"Parsed response: complete={is_complete}, error={error_msg is not None}, "
|
|
389
|
+
f"question={is_question}"
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
return response
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Codex CLI runtime adapter.
|
|
2
|
+
|
|
3
|
+
This module implements the RuntimeAdapter interface for Codex,
|
|
4
|
+
an AI coding assistant (limited support currently).
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
import shlex
|
|
10
|
+
from typing import List, Optional, Set
|
|
11
|
+
|
|
12
|
+
from .base import (
|
|
13
|
+
Capability,
|
|
14
|
+
ParsedResponse,
|
|
15
|
+
RuntimeAdapter,
|
|
16
|
+
RuntimeCapability,
|
|
17
|
+
RuntimeInfo,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class CodexAdapter(RuntimeAdapter):
|
|
24
|
+
"""Adapter for Codex CLI.
|
|
25
|
+
|
|
26
|
+
Codex is an AI coding assistant. This adapter provides basic support,
|
|
27
|
+
with limited capabilities compared to Claude Code or MPM.
|
|
28
|
+
|
|
29
|
+
Note:
|
|
30
|
+
Agent delegation and advanced features not yet supported.
|
|
31
|
+
|
|
32
|
+
Example:
|
|
33
|
+
>>> adapter = CodexAdapter()
|
|
34
|
+
>>> cmd = adapter.build_launch_command("/home/user/project")
|
|
35
|
+
>>> print(cmd)
|
|
36
|
+
cd '/home/user/project' && codex
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
# Idle detection patterns
|
|
40
|
+
IDLE_PATTERNS = [
|
|
41
|
+
r"^>\s*$", # Simple prompt
|
|
42
|
+
r"codex>\s*$", # Named prompt
|
|
43
|
+
r"Ready",
|
|
44
|
+
r"Waiting for input",
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
# Error patterns
|
|
48
|
+
ERROR_PATTERNS = [
|
|
49
|
+
r"Error:",
|
|
50
|
+
r"Failed:",
|
|
51
|
+
r"Exception:",
|
|
52
|
+
r"Permission denied",
|
|
53
|
+
r"not found",
|
|
54
|
+
r"Traceback",
|
|
55
|
+
r"FATAL:",
|
|
56
|
+
]
|
|
57
|
+
|
|
58
|
+
# Question patterns
|
|
59
|
+
QUESTION_PATTERNS = [
|
|
60
|
+
r"Which option",
|
|
61
|
+
r"Should I proceed",
|
|
62
|
+
r"Please choose",
|
|
63
|
+
r"\(y/n\)\?",
|
|
64
|
+
r"Are you sure",
|
|
65
|
+
r"\[Y/n\]",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
# ANSI escape code pattern
|
|
69
|
+
ANSI_ESCAPE = re.compile(r"\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])")
|
|
70
|
+
|
|
71
|
+
@property
|
|
72
|
+
def name(self) -> str:
|
|
73
|
+
"""Return the runtime identifier."""
|
|
74
|
+
return "codex"
|
|
75
|
+
|
|
76
|
+
@property
|
|
77
|
+
def capabilities(self) -> Set[Capability]:
|
|
78
|
+
"""Return the set of capabilities supported by Codex."""
|
|
79
|
+
return {
|
|
80
|
+
Capability.TOOL_USE,
|
|
81
|
+
Capability.FILE_EDIT,
|
|
82
|
+
Capability.FILE_CREATE,
|
|
83
|
+
Capability.SHELL_COMMANDS,
|
|
84
|
+
Capability.COMPLEX_REASONING,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@property
|
|
88
|
+
def runtime_info(self) -> RuntimeInfo:
|
|
89
|
+
"""Return detailed runtime information."""
|
|
90
|
+
return RuntimeInfo(
|
|
91
|
+
name="codex",
|
|
92
|
+
version=None, # Version detection could be added
|
|
93
|
+
capabilities={
|
|
94
|
+
RuntimeCapability.FILE_READ,
|
|
95
|
+
RuntimeCapability.FILE_EDIT,
|
|
96
|
+
RuntimeCapability.FILE_CREATE,
|
|
97
|
+
RuntimeCapability.BASH_EXECUTION,
|
|
98
|
+
RuntimeCapability.TOOL_USE,
|
|
99
|
+
RuntimeCapability.COMPLEX_REASONING,
|
|
100
|
+
},
|
|
101
|
+
command="codex",
|
|
102
|
+
supports_agents=False, # No agent support
|
|
103
|
+
instruction_file=None, # No custom instructions support
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
def build_launch_command(
|
|
107
|
+
self, project_path: str, agent_prompt: Optional[str] = None
|
|
108
|
+
) -> str:
|
|
109
|
+
"""Generate shell command to start Codex.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
project_path: Absolute path to the project directory
|
|
113
|
+
agent_prompt: Optional system prompt (may not be supported)
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
Shell command string ready to execute
|
|
117
|
+
|
|
118
|
+
Example:
|
|
119
|
+
>>> adapter = CodexAdapter()
|
|
120
|
+
>>> adapter.build_launch_command("/home/user/project")
|
|
121
|
+
"cd '/home/user/project' && codex"
|
|
122
|
+
"""
|
|
123
|
+
quoted_path = shlex.quote(project_path)
|
|
124
|
+
cmd = f"cd {quoted_path} && codex"
|
|
125
|
+
|
|
126
|
+
# Note: Codex may not support custom prompts
|
|
127
|
+
# Adjust based on actual Codex CLI capabilities
|
|
128
|
+
if agent_prompt:
|
|
129
|
+
logger.warning("Codex may not support custom prompts")
|
|
130
|
+
|
|
131
|
+
logger.debug(f"Built Codex launch command: {cmd}")
|
|
132
|
+
return cmd
|
|
133
|
+
|
|
134
|
+
def format_input(self, message: str) -> str:
|
|
135
|
+
"""Prepare message for Codex's input format."""
|
|
136
|
+
formatted = message.strip()
|
|
137
|
+
logger.debug(f"Formatted input: {formatted[:100]}...")
|
|
138
|
+
return formatted
|
|
139
|
+
|
|
140
|
+
def strip_ansi(self, text: str) -> str:
|
|
141
|
+
"""Remove ANSI escape codes from text."""
|
|
142
|
+
return self.ANSI_ESCAPE.sub("", text)
|
|
143
|
+
|
|
144
|
+
def detect_idle(self, output: str) -> bool:
|
|
145
|
+
"""Recognize when Codex is waiting for input."""
|
|
146
|
+
if not output:
|
|
147
|
+
return False
|
|
148
|
+
|
|
149
|
+
clean = self.strip_ansi(output)
|
|
150
|
+
lines = clean.strip().split("\n")
|
|
151
|
+
|
|
152
|
+
if not lines:
|
|
153
|
+
return False
|
|
154
|
+
|
|
155
|
+
last_line = lines[-1].strip()
|
|
156
|
+
|
|
157
|
+
for pattern in self.IDLE_PATTERNS:
|
|
158
|
+
if re.search(pattern, last_line):
|
|
159
|
+
logger.debug(f"Detected idle state with pattern: {pattern}")
|
|
160
|
+
return True
|
|
161
|
+
|
|
162
|
+
return False
|
|
163
|
+
|
|
164
|
+
def detect_error(self, output: str) -> Optional[str]:
|
|
165
|
+
"""Recognize error states and extract error message."""
|
|
166
|
+
clean = self.strip_ansi(output)
|
|
167
|
+
|
|
168
|
+
for pattern in self.ERROR_PATTERNS:
|
|
169
|
+
match = re.search(pattern, clean, re.IGNORECASE)
|
|
170
|
+
if match:
|
|
171
|
+
for line in clean.split("\n"):
|
|
172
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
173
|
+
error_msg = line.strip()
|
|
174
|
+
logger.warning(f"Detected error: {error_msg}")
|
|
175
|
+
return error_msg
|
|
176
|
+
|
|
177
|
+
return None
|
|
178
|
+
|
|
179
|
+
def detect_question(
|
|
180
|
+
self, output: str
|
|
181
|
+
) -> tuple[bool, Optional[str], Optional[List[str]]]:
|
|
182
|
+
"""Detect if Codex is asking a question."""
|
|
183
|
+
clean = self.strip_ansi(output)
|
|
184
|
+
|
|
185
|
+
for pattern in self.QUESTION_PATTERNS:
|
|
186
|
+
if re.search(pattern, clean, re.IGNORECASE):
|
|
187
|
+
lines = clean.strip().split("\n")
|
|
188
|
+
question = None
|
|
189
|
+
options = []
|
|
190
|
+
|
|
191
|
+
for line in lines:
|
|
192
|
+
if re.search(pattern, line, re.IGNORECASE):
|
|
193
|
+
question = line.strip()
|
|
194
|
+
|
|
195
|
+
# Look for numbered options
|
|
196
|
+
opt_match = re.match(r"^\s*(\d+)[.):]\s*(.+)$", line)
|
|
197
|
+
if opt_match:
|
|
198
|
+
options.append(opt_match.group(2).strip())
|
|
199
|
+
|
|
200
|
+
logger.debug(
|
|
201
|
+
f"Detected question: {question}, options: {options if options else 'none'}"
|
|
202
|
+
)
|
|
203
|
+
return True, question, options if options else None
|
|
204
|
+
|
|
205
|
+
return False, None, None
|
|
206
|
+
|
|
207
|
+
def parse_response(self, output: str) -> ParsedResponse:
|
|
208
|
+
"""Extract meaningful content from Codex output."""
|
|
209
|
+
if not output:
|
|
210
|
+
return ParsedResponse(
|
|
211
|
+
content="",
|
|
212
|
+
is_complete=False,
|
|
213
|
+
is_error=False,
|
|
214
|
+
is_question=False,
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
clean = self.strip_ansi(output)
|
|
218
|
+
error_msg = self.detect_error(output)
|
|
219
|
+
is_question, question_text, options = self.detect_question(output)
|
|
220
|
+
is_complete = self.detect_idle(output)
|
|
221
|
+
|
|
222
|
+
response = ParsedResponse(
|
|
223
|
+
content=clean,
|
|
224
|
+
is_complete=is_complete,
|
|
225
|
+
is_error=error_msg is not None,
|
|
226
|
+
error_message=error_msg,
|
|
227
|
+
is_question=is_question,
|
|
228
|
+
question_text=question_text,
|
|
229
|
+
options=options,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
logger.debug(
|
|
233
|
+
f"Parsed response: complete={is_complete}, error={error_msg is not None}, "
|
|
234
|
+
f"question={is_question}"
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return response
|