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.
Files changed (322) hide show
  1. claude_mpm/VERSION +1 -1
  2. claude_mpm/agents/CLAUDE_MPM_OUTPUT_STYLE.md +8 -5
  3. claude_mpm/agents/{CLAUDE_MPM_FOUNDERS_OUTPUT_STYLE.md → CLAUDE_MPM_RESEARCH_OUTPUT_STYLE.md} +14 -6
  4. claude_mpm/agents/PM_INSTRUCTIONS.md +109 -706
  5. claude_mpm/agents/WORKFLOW.md +2 -0
  6. claude_mpm/agents/templates/circuit-breakers.md +26 -17
  7. claude_mpm/auth/__init__.py +35 -0
  8. claude_mpm/auth/callback_server.py +328 -0
  9. claude_mpm/auth/models.py +104 -0
  10. claude_mpm/auth/oauth_manager.py +266 -0
  11. claude_mpm/auth/providers/__init__.py +12 -0
  12. claude_mpm/auth/providers/base.py +165 -0
  13. claude_mpm/auth/providers/google.py +261 -0
  14. claude_mpm/auth/token_storage.py +252 -0
  15. claude_mpm/cli/commands/autotodos.py +566 -0
  16. claude_mpm/cli/commands/commander.py +216 -0
  17. claude_mpm/cli/commands/hook_errors.py +60 -60
  18. claude_mpm/cli/commands/mcp.py +29 -17
  19. claude_mpm/cli/commands/mcp_command_router.py +39 -0
  20. claude_mpm/cli/commands/mcp_service_commands.py +304 -0
  21. claude_mpm/cli/commands/monitor.py +2 -2
  22. claude_mpm/cli/commands/mpm_init/core.py +2 -2
  23. claude_mpm/cli/commands/oauth.py +481 -0
  24. claude_mpm/cli/commands/run.py +35 -3
  25. claude_mpm/cli/commands/skill_source.py +51 -2
  26. claude_mpm/cli/commands/skills.py +5 -3
  27. claude_mpm/cli/executor.py +128 -16
  28. claude_mpm/cli/helpers.py +1 -1
  29. claude_mpm/cli/parsers/base_parser.py +84 -1
  30. claude_mpm/cli/parsers/commander_parser.py +116 -0
  31. claude_mpm/cli/parsers/mcp_parser.py +79 -0
  32. claude_mpm/cli/parsers/oauth_parser.py +165 -0
  33. claude_mpm/cli/parsers/run_parser.py +10 -0
  34. claude_mpm/cli/parsers/skill_source_parser.py +4 -0
  35. claude_mpm/cli/parsers/skills_parser.py +5 -0
  36. claude_mpm/cli/startup.py +345 -40
  37. claude_mpm/cli/startup_display.py +76 -7
  38. claude_mpm/cli/startup_logging.py +2 -2
  39. claude_mpm/cli/startup_migrations.py +236 -0
  40. claude_mpm/cli/utils.py +7 -3
  41. claude_mpm/commander/__init__.py +78 -0
  42. claude_mpm/commander/adapters/__init__.py +60 -0
  43. claude_mpm/commander/adapters/auggie.py +260 -0
  44. claude_mpm/commander/adapters/base.py +288 -0
  45. claude_mpm/commander/adapters/claude_code.py +392 -0
  46. claude_mpm/commander/adapters/codex.py +237 -0
  47. claude_mpm/commander/adapters/communication.py +366 -0
  48. claude_mpm/commander/adapters/example_usage.py +310 -0
  49. claude_mpm/commander/adapters/mpm.py +389 -0
  50. claude_mpm/commander/adapters/registry.py +204 -0
  51. claude_mpm/commander/api/__init__.py +16 -0
  52. claude_mpm/commander/api/app.py +121 -0
  53. claude_mpm/commander/api/errors.py +133 -0
  54. claude_mpm/commander/api/routes/__init__.py +8 -0
  55. claude_mpm/commander/api/routes/events.py +184 -0
  56. claude_mpm/commander/api/routes/inbox.py +171 -0
  57. claude_mpm/commander/api/routes/messages.py +148 -0
  58. claude_mpm/commander/api/routes/projects.py +271 -0
  59. claude_mpm/commander/api/routes/sessions.py +226 -0
  60. claude_mpm/commander/api/routes/work.py +296 -0
  61. claude_mpm/commander/api/schemas.py +186 -0
  62. claude_mpm/commander/chat/__init__.py +7 -0
  63. claude_mpm/commander/chat/cli.py +149 -0
  64. claude_mpm/commander/chat/commands.py +124 -0
  65. claude_mpm/commander/chat/repl.py +1957 -0
  66. claude_mpm/commander/config.py +51 -0
  67. claude_mpm/commander/config_loader.py +115 -0
  68. claude_mpm/commander/core/__init__.py +10 -0
  69. claude_mpm/commander/core/block_manager.py +325 -0
  70. claude_mpm/commander/core/response_manager.py +323 -0
  71. claude_mpm/commander/daemon.py +603 -0
  72. claude_mpm/commander/env_loader.py +59 -0
  73. claude_mpm/commander/events/__init__.py +26 -0
  74. claude_mpm/commander/events/manager.py +392 -0
  75. claude_mpm/commander/frameworks/__init__.py +12 -0
  76. claude_mpm/commander/frameworks/base.py +233 -0
  77. claude_mpm/commander/frameworks/claude_code.py +58 -0
  78. claude_mpm/commander/frameworks/mpm.py +57 -0
  79. claude_mpm/commander/git/__init__.py +5 -0
  80. claude_mpm/commander/git/worktree_manager.py +212 -0
  81. claude_mpm/commander/inbox/__init__.py +16 -0
  82. claude_mpm/commander/inbox/dedup.py +128 -0
  83. claude_mpm/commander/inbox/inbox.py +224 -0
  84. claude_mpm/commander/inbox/models.py +70 -0
  85. claude_mpm/commander/instance_manager.py +868 -0
  86. claude_mpm/commander/llm/__init__.py +6 -0
  87. claude_mpm/commander/llm/openrouter_client.py +167 -0
  88. claude_mpm/commander/llm/summarizer.py +70 -0
  89. claude_mpm/commander/memory/__init__.py +45 -0
  90. claude_mpm/commander/memory/compression.py +347 -0
  91. claude_mpm/commander/memory/embeddings.py +230 -0
  92. claude_mpm/commander/memory/entities.py +310 -0
  93. claude_mpm/commander/memory/example_usage.py +290 -0
  94. claude_mpm/commander/memory/integration.py +325 -0
  95. claude_mpm/commander/memory/search.py +381 -0
  96. claude_mpm/commander/memory/store.py +657 -0
  97. claude_mpm/commander/models/__init__.py +18 -0
  98. claude_mpm/commander/models/events.py +127 -0
  99. claude_mpm/commander/models/project.py +162 -0
  100. claude_mpm/commander/models/work.py +214 -0
  101. claude_mpm/commander/parsing/__init__.py +20 -0
  102. claude_mpm/commander/parsing/extractor.py +132 -0
  103. claude_mpm/commander/parsing/output_parser.py +270 -0
  104. claude_mpm/commander/parsing/patterns.py +100 -0
  105. claude_mpm/commander/persistence/__init__.py +11 -0
  106. claude_mpm/commander/persistence/event_store.py +274 -0
  107. claude_mpm/commander/persistence/state_store.py +403 -0
  108. claude_mpm/commander/persistence/work_store.py +164 -0
  109. claude_mpm/commander/polling/__init__.py +13 -0
  110. claude_mpm/commander/polling/event_detector.py +104 -0
  111. claude_mpm/commander/polling/output_buffer.py +49 -0
  112. claude_mpm/commander/polling/output_poller.py +153 -0
  113. claude_mpm/commander/project_session.py +268 -0
  114. claude_mpm/commander/proxy/__init__.py +12 -0
  115. claude_mpm/commander/proxy/formatter.py +89 -0
  116. claude_mpm/commander/proxy/output_handler.py +191 -0
  117. claude_mpm/commander/proxy/relay.py +155 -0
  118. claude_mpm/commander/registry.py +410 -0
  119. claude_mpm/commander/runtime/__init__.py +10 -0
  120. claude_mpm/commander/runtime/executor.py +191 -0
  121. claude_mpm/commander/runtime/monitor.py +346 -0
  122. claude_mpm/commander/session/__init__.py +6 -0
  123. claude_mpm/commander/session/context.py +81 -0
  124. claude_mpm/commander/session/manager.py +59 -0
  125. claude_mpm/commander/tmux_orchestrator.py +362 -0
  126. claude_mpm/commander/web/__init__.py +1 -0
  127. claude_mpm/commander/work/__init__.py +30 -0
  128. claude_mpm/commander/work/executor.py +207 -0
  129. claude_mpm/commander/work/queue.py +405 -0
  130. claude_mpm/commander/workflow/__init__.py +27 -0
  131. claude_mpm/commander/workflow/event_handler.py +241 -0
  132. claude_mpm/commander/workflow/notifier.py +146 -0
  133. claude_mpm/commands/mpm-config.md +8 -0
  134. claude_mpm/commands/mpm-doctor.md +8 -0
  135. claude_mpm/commands/mpm-help.md +8 -0
  136. claude_mpm/commands/mpm-init.md +8 -0
  137. claude_mpm/commands/mpm-monitor.md +8 -0
  138. claude_mpm/commands/mpm-organize.md +8 -0
  139. claude_mpm/commands/mpm-postmortem.md +8 -0
  140. claude_mpm/commands/mpm-session-resume.md +9 -1
  141. claude_mpm/commands/mpm-status.md +8 -0
  142. claude_mpm/commands/mpm-ticket-view.md +8 -0
  143. claude_mpm/commands/mpm-version.md +8 -0
  144. claude_mpm/commands/mpm.md +8 -0
  145. claude_mpm/config/agent_presets.py +8 -7
  146. claude_mpm/config/skill_sources.py +16 -0
  147. claude_mpm/constants.py +5 -0
  148. claude_mpm/core/claude_runner.py +152 -0
  149. claude_mpm/core/config.py +35 -22
  150. claude_mpm/core/config_constants.py +74 -9
  151. claude_mpm/core/constants.py +56 -12
  152. claude_mpm/core/hook_manager.py +53 -4
  153. claude_mpm/core/interactive_session.py +5 -4
  154. claude_mpm/core/logger.py +26 -9
  155. claude_mpm/core/logging_utils.py +39 -13
  156. claude_mpm/core/network_config.py +148 -0
  157. claude_mpm/core/oneshot_session.py +7 -6
  158. claude_mpm/core/output_style_manager.py +52 -12
  159. claude_mpm/core/socketio_pool.py +47 -15
  160. claude_mpm/core/unified_config.py +10 -6
  161. claude_mpm/core/unified_paths.py +68 -80
  162. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.C33zOoyM.css +1 -0
  163. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.CW1J-YuA.css +1 -0
  164. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cs_tUR18.js → 1WZnGYqX.js} +1 -1
  165. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CDuw-vjf.js → 67pF3qNn.js} +1 -1
  166. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bTOqqlTd.js → 6RxdMKe4.js} +1 -1
  167. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DwBR2MJi.js → 8cZrfX0h.js} +1 -1
  168. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{ZGh7QtNv.js → 9a6T2nm-.js} +1 -1
  169. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D9lljYKQ.js → B443AUzu.js} +1 -1
  170. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{RJiighC3.js → B8AwtY2H.js} +1 -1
  171. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{uuIeMWc-.js → BF15LAsF.js} +1 -1
  172. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{D3k0OPJN.js → BRcwIQNr.js} +1 -1
  173. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CyWMqx4W.js → BV6nKitt.js} +1 -1
  174. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CiIAseT4.js → BViJ8lZt.js} +5 -5
  175. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CBBdVcY8.js → BcQ-Q0FE.js} +1 -1
  176. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BovzEFCE.js → Bpyvgze_.js} +1 -1
  177. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BzTRqg-z.js +1 -0
  178. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/C0Fr8dve.js +1 -0
  179. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{eNVUfhuA.js → C3rbW_a-.js} +1 -1
  180. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{GYwsonyD.js → C8WYN38h.js} +1 -1
  181. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BIF9m_hv.js → C9I8FlXH.js} +1 -1
  182. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B0uc0UOD.js → CIQcWgO2.js} +3 -3
  183. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Be7GpZd6.js → CIctN7YN.js} +1 -1
  184. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Bh0LDWpI.js → CKrS_JZW.js} +2 -2
  185. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DUrLdbGD.js → CR6P9C4A.js} +1 -1
  186. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7xVLGWV.js → CRRR9MD_.js} +1 -1
  187. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/CRcR2DqT.js +334 -0
  188. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dhb8PKl3.js → CSXtMOf0.js} +1 -1
  189. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BPYeabCQ.js → CT-sbxSk.js} +1 -1
  190. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{sQeU3Y1z.js → CWm6DJsp.js} +1 -1
  191. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CnA0NrzZ.js → CpqQ1Kzn.js} +1 -1
  192. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4B-KCzX.js → D2nGpDRe.js} +1 -1
  193. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DGkLK5U1.js → D9iCMida.js} +1 -1
  194. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{BofRWZRR.js → D9ykgMoY.js} +1 -1
  195. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DmxopI1J.js → DL2Ldur1.js} +1 -1
  196. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C30mlcqg.js → DPfltzjH.js} +1 -1
  197. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Vzk33B_K.js → DR8nis88.js} +2 -2
  198. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DI7hHRFL.js → DUliQN2b.js} +1 -1
  199. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C4JcI4KD.js → DXlhR01x.js} +1 -1
  200. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{bT1r9zLR.js → D_lyTybS.js} +1 -1
  201. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DZX00Y4g.js → DngoTTgh.js} +1 -1
  202. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzZX-COe.js → DqkmHtDC.js} +1 -1
  203. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{B7RN905-.js → DsDh8EYs.js} +1 -1
  204. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DLVjFsZ3.js → DypDmXgd.js} +1 -1
  205. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{iEWssX7S.js → IPYC-LnN.js} +1 -1
  206. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/JTLiF7dt.js +24 -0
  207. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DaimHw_p.js → JpevfAFt.js} +1 -1
  208. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{DY1XQ8fi.js → R8CEIRAd.js} +1 -1
  209. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Dle-35c7.js → Zxy7qc-l.js} +2 -2
  210. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/q9Hm6zAU.js +1 -0
  211. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{C_Usid8X.js → qtd3IeO4.js} +2 -2
  212. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{CzeYkLYB.js → ulBFON_C.js} +2 -2
  213. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/{Cfqx1Qun.js → wQVh1CoA.js} +1 -1
  214. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/{app.D6-I5TpK.js → app.Dr7t0z2J.js} +2 -2
  215. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.BGhZHUS3.js +1 -0
  216. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{0.m1gL8KXf.js → 0.RgBboRvH.js} +1 -1
  217. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/{1.CgNOuw-d.js → 1.DG-KkbDf.js} +1 -1
  218. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.D_jnf-x6.js +1 -0
  219. claude_mpm/dashboard/static/svelte-build/_app/version.json +1 -1
  220. claude_mpm/dashboard/static/svelte-build/index.html +9 -9
  221. claude_mpm/experimental/cli_enhancements.py +2 -1
  222. claude_mpm/hooks/claude_hooks/INTEGRATION_EXAMPLE.md +243 -0
  223. claude_mpm/hooks/claude_hooks/README_AUTO_PAUSE.md +403 -0
  224. claude_mpm/hooks/claude_hooks/auto_pause_handler.py +485 -0
  225. claude_mpm/hooks/claude_hooks/event_handlers.py +466 -136
  226. claude_mpm/hooks/claude_hooks/hook_handler.py +204 -104
  227. claude_mpm/hooks/claude_hooks/hook_wrapper.sh +6 -11
  228. claude_mpm/hooks/claude_hooks/installer.py +291 -59
  229. claude_mpm/hooks/claude_hooks/memory_integration.py +52 -32
  230. claude_mpm/hooks/claude_hooks/response_tracking.py +43 -60
  231. claude_mpm/hooks/claude_hooks/services/__init__.py +21 -0
  232. claude_mpm/hooks/claude_hooks/services/connection_manager.py +41 -26
  233. claude_mpm/hooks/claude_hooks/services/connection_manager_http.py +38 -105
  234. claude_mpm/hooks/claude_hooks/services/container.py +326 -0
  235. claude_mpm/hooks/claude_hooks/services/protocols.py +328 -0
  236. claude_mpm/hooks/claude_hooks/services/state_manager.py +25 -38
  237. claude_mpm/hooks/claude_hooks/services/subagent_processor.py +75 -77
  238. claude_mpm/hooks/session_resume_hook.py +89 -1
  239. claude_mpm/hooks/templates/pre_tool_use_simple.py +6 -6
  240. claude_mpm/hooks/templates/pre_tool_use_template.py +16 -8
  241. claude_mpm/init.py +22 -15
  242. claude_mpm/mcp/__init__.py +9 -0
  243. claude_mpm/mcp/google_workspace_server.py +610 -0
  244. claude_mpm/scripts/claude-hook-handler.sh +46 -19
  245. claude_mpm/services/agents/agent_recommendation_service.py +8 -8
  246. claude_mpm/services/agents/agent_selection_service.py +2 -2
  247. claude_mpm/services/agents/cache_git_manager.py +1 -1
  248. claude_mpm/services/agents/deployment/remote_agent_discovery_service.py +3 -0
  249. claude_mpm/services/agents/loading/framework_agent_loader.py +75 -2
  250. claude_mpm/services/agents/single_tier_deployment_service.py +4 -4
  251. claude_mpm/services/cli/__init__.py +3 -0
  252. claude_mpm/services/cli/incremental_pause_manager.py +561 -0
  253. claude_mpm/services/cli/session_resume_helper.py +10 -2
  254. claude_mpm/services/command_deployment_service.py +44 -26
  255. claude_mpm/services/delegation_detector.py +175 -0
  256. claude_mpm/services/diagnostics/checks/agent_sources_check.py +30 -0
  257. claude_mpm/services/diagnostics/checks/configuration_check.py +24 -0
  258. claude_mpm/services/diagnostics/checks/installation_check.py +22 -0
  259. claude_mpm/services/diagnostics/checks/mcp_services_check.py +23 -0
  260. claude_mpm/services/diagnostics/doctor_reporter.py +31 -1
  261. claude_mpm/services/diagnostics/models.py +14 -1
  262. claude_mpm/services/event_log.py +325 -0
  263. claude_mpm/services/hook_installer_service.py +77 -8
  264. claude_mpm/services/infrastructure/__init__.py +4 -0
  265. claude_mpm/services/infrastructure/context_usage_tracker.py +291 -0
  266. claude_mpm/services/infrastructure/resume_log_generator.py +24 -5
  267. claude_mpm/services/mcp_config_manager.py +99 -19
  268. claude_mpm/services/mcp_service_registry.py +294 -0
  269. claude_mpm/services/monitor/daemon_manager.py +15 -4
  270. claude_mpm/services/monitor/management/lifecycle.py +8 -2
  271. claude_mpm/services/monitor/server.py +111 -16
  272. claude_mpm/services/pm_skills_deployer.py +261 -87
  273. claude_mpm/services/skills/git_skill_source_manager.py +130 -10
  274. claude_mpm/services/skills/selective_skill_deployer.py +142 -16
  275. claude_mpm/services/skills/skill_discovery_service.py +74 -4
  276. claude_mpm/services/skills_deployer.py +31 -5
  277. claude_mpm/services/socketio/handlers/hook.py +14 -7
  278. claude_mpm/services/socketio/server/main.py +12 -4
  279. claude_mpm/skills/__init__.py +2 -1
  280. claude_mpm/skills/bundled/pm/mpm/SKILL.md +38 -0
  281. claude_mpm/skills/bundled/pm/mpm-agent-update-workflow/SKILL.md +75 -0
  282. claude_mpm/skills/bundled/pm/mpm-circuit-breaker-enforcement/SKILL.md +476 -0
  283. claude_mpm/skills/bundled/pm/mpm-config/SKILL.md +29 -0
  284. claude_mpm/skills/bundled/pm/mpm-doctor/SKILL.md +53 -0
  285. claude_mpm/skills/bundled/pm/mpm-help/SKILL.md +35 -0
  286. claude_mpm/skills/bundled/pm/mpm-init/SKILL.md +125 -0
  287. claude_mpm/skills/bundled/pm/mpm-monitor/SKILL.md +32 -0
  288. claude_mpm/skills/bundled/pm/mpm-organize/SKILL.md +121 -0
  289. claude_mpm/skills/bundled/pm/mpm-postmortem/SKILL.md +22 -0
  290. claude_mpm/skills/bundled/pm/mpm-session-management/SKILL.md +312 -0
  291. claude_mpm/skills/bundled/pm/mpm-session-pause/SKILL.md +170 -0
  292. claude_mpm/skills/bundled/pm/mpm-session-resume/SKILL.md +31 -0
  293. claude_mpm/skills/bundled/pm/mpm-status/SKILL.md +37 -0
  294. claude_mpm/skills/bundled/pm/{pm-teaching-mode → mpm-teaching-mode}/SKILL.md +2 -2
  295. claude_mpm/skills/bundled/pm/mpm-ticket-view/SKILL.md +110 -0
  296. claude_mpm/skills/bundled/pm/mpm-tool-usage-guide/SKILL.md +386 -0
  297. claude_mpm/skills/bundled/pm/mpm-version/SKILL.md +21 -0
  298. claude_mpm/skills/registry.py +295 -90
  299. claude_mpm/skills/skill_manager.py +4 -4
  300. claude_mpm-5.6.76.dist-info/METADATA +416 -0
  301. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/RECORD +312 -175
  302. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/WHEEL +1 -1
  303. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/entry_points.txt +2 -0
  304. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/0.DWzvg0-y.css +0 -1
  305. claude_mpm/dashboard/static/svelte-build/_app/immutable/assets/2.ThTw9_ym.css +0 -1
  306. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/4TdZjIqw.js +0 -1
  307. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/5shd3_w0.js +0 -24
  308. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/BKjSRqUr.js +0 -1
  309. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Da0KfYnO.js +0 -1
  310. claude_mpm/dashboard/static/svelte-build/_app/immutable/chunks/Dfy6j1xT.js +0 -323
  311. claude_mpm/dashboard/static/svelte-build/_app/immutable/entry/start.NWzMBYRp.js +0 -1
  312. claude_mpm/dashboard/static/svelte-build/_app/immutable/nodes/2.C0GcWctS.js +0 -1
  313. claude_mpm-5.4.85.dist-info/METADATA +0 -1023
  314. /claude_mpm/skills/bundled/pm/{pm-bug-reporting/pm-bug-reporting.md → mpm-bug-reporting/SKILL.md} +0 -0
  315. /claude_mpm/skills/bundled/pm/{pm-delegation-patterns → mpm-delegation-patterns}/SKILL.md +0 -0
  316. /claude_mpm/skills/bundled/pm/{pm-git-file-tracking → mpm-git-file-tracking}/SKILL.md +0 -0
  317. /claude_mpm/skills/bundled/pm/{pm-pr-workflow → mpm-pr-workflow}/SKILL.md +0 -0
  318. /claude_mpm/skills/bundled/pm/{pm-ticketing-integration → mpm-ticketing-integration}/SKILL.md +0 -0
  319. /claude_mpm/skills/bundled/pm/{pm-verification-protocols → mpm-verification-protocols}/SKILL.md +0 -0
  320. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE +0 -0
  321. {claude_mpm-5.4.85.dist-info → claude_mpm-5.6.76.dist-info}/licenses/LICENSE-FAQ.md +0 -0
  322. {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