multi-forge 0.7.0__tar.gz → 0.7.1__tar.gz

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 (413) hide show
  1. {multi_forge-0.7.0 → multi_forge-0.7.1}/PKG-INFO +5 -5
  2. {multi_forge-0.7.0 → multi_forge-0.7.1}/README.md +4 -4
  3. {multi_forge-0.7.0 → multi_forge-0.7.1}/pyproject.toml +1 -1
  4. multi_forge-0.7.1/src/forge/__init__.py +11 -0
  5. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/registry.py +20 -22
  6. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/codex_policy.py +2 -10
  7. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/commands.py +154 -194
  8. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/direct_commands.py +86 -250
  9. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/logs.py +5 -3
  10. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/policy.py +118 -298
  11. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/runtime.py +3 -3
  12. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/search.py +98 -33
  13. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session.py +5 -100
  14. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_fork.py +19 -77
  15. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_lifecycle.py +21 -72
  16. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_manage.py +14 -11
  17. multi_forge-0.7.1/src/forge/cli/session_supervisor_options.py +102 -0
  18. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/throttle.py +2 -13
  19. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/loader.py +8 -27
  20. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/credentials_file.py +6 -18
  21. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/claude_session.py +24 -23
  22. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_session.py +6 -7
  23. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/gc.py +1 -1
  24. multi_forge-0.7.1/src/forge/core/ops/policy.py +429 -0
  25. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/session.py +102 -3
  26. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/session_context.py +2 -7
  27. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/registry.py +13 -32
  28. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime_vocab.py +1 -1
  29. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/__init__.py +14 -1
  30. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/io.py +31 -2
  31. {multi_forge-0.7.0/src/forge/proxy → multi_forge-0.7.1/src/forge/core/state}/retention.py +1 -1
  32. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/timestamps.py +5 -0
  33. multi_forge-0.7.1/src/forge/core/state/versioned_store.py +59 -0
  34. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/downstream.py +14 -24
  35. multi_forge-0.7.1/src/forge/core/telemetry/jsonl_io.py +37 -0
  36. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/upstream.py +13 -23
  37. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/__init__.py +0 -2
  38. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/ledger.py +16 -68
  39. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/vocabulary.py +3 -5
  40. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/installer.py +2 -1
  41. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/models.py +0 -9
  42. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/tracking.py +24 -19
  43. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/base.py +9 -0
  44. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/shadow.py +1 -1
  45. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/shadow_runner.py +1 -1
  46. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/supervisor.py +3 -10
  47. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/audit_logger.py +4 -7
  48. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxies.py +31 -19
  49. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/utils.py +1 -1
  50. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/runtime_config.py +4 -18
  51. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/__init__.py +8 -0
  52. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/bm25_store.py +16 -22
  53. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/content_store.py +16 -22
  54. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/exceptions.py +25 -1
  55. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/index_state.py +17 -22
  56. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/store.py +19 -19
  57. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/active.py +4 -0
  58. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/relocate.py +5 -15
  59. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/consumer_lanes.py +3 -3
  60. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/hooks/session_start.py +7 -10
  61. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/index.py +8 -19
  62. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/manager.py +15 -27
  63. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/memory_writer.py +3 -11
  64. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/models.py +6 -0
  65. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/shadow_curation.py +2 -7
  66. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/transfer.py +1 -1
  67. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/17-info.md +1 -1
  68. multi_forge-0.7.0/src/forge/__init__.py +0 -3
  69. {multi_forge-0.7.0 → multi_forge-0.7.1}/.gitignore +0 -0
  70. {multi_forge-0.7.0 → multi_forge-0.7.1}/LICENSE +0 -0
  71. {multi_forge-0.7.0 → multi_forge-0.7.1}/NOTICE +0 -0
  72. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/agents/.gitkeep +0 -0
  73. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/commands/.gitkeep +0 -0
  74. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/__init__.py +0 -0
  75. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/adapters/__init__.py +0 -0
  76. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/adapters/litellm.py +0 -0
  77. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/creation.py +0 -0
  78. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/remote/__init__.py +0 -0
  79. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/remote/base.py +0 -0
  80. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/remote/openrouter.py +0 -0
  81. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/sources.py +0 -0
  82. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/__init__.py +0 -0
  83. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/activity.py +0 -0
  84. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/auth.py +0 -0
  85. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/backend.py +0 -0
  86. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/claude.py +0 -0
  87. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/codex.py +0 -0
  88. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/config_cmd.py +0 -0
  89. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/consumer_lane_freeze.py +0 -0
  90. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/editor.py +0 -0
  91. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/extensions.py +0 -0
  92. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/gc.py +0 -0
  93. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/guards.py +0 -0
  94. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/__init__.py +0 -0
  95. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/_group.py +0 -0
  96. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/_helpers.py +0 -0
  97. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/codex_patch.py +0 -0
  98. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/codex_transfer.py +0 -0
  99. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/install.py +0 -0
  100. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/policy.py +0 -0
  101. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/protocols.py +0 -0
  102. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/read_hygiene.py +0 -0
  103. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/verification.py +0 -0
  104. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/main.py +0 -0
  105. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/memory.py +0 -0
  106. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/memory_report.py +0 -0
  107. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/memory_writer.py +0 -0
  108. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/model.py +0 -0
  109. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/output.py +0 -0
  110. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/proxy.py +0 -0
  111. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/proxy_audit.py +0 -0
  112. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/proxy_costs.py +0 -0
  113. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_codex.py +0 -0
  114. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_lane.py +0 -0
  115. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_memory.py +0 -0
  116. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_model_pin.py +0 -0
  117. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_resume_modes.py +0 -0
  118. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_rewind.py +0 -0
  119. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_routing.py +0 -0
  120. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/status_line.py +0 -0
  121. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/__init__.py +0 -0
  122. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/context.py +0 -0
  123. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/names.py +0 -0
  124. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/palette.py +0 -0
  125. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/registry.py +0 -0
  126. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/telemetry.py +0 -0
  127. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/trace.py +0 -0
  128. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/transfer.py +0 -0
  129. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/workflow.py +0 -0
  130. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/__init__.py +0 -0
  131. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/dataclass_utils.py +0 -0
  132. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/__init__.py +0 -0
  133. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/backends/__init__.py +0 -0
  134. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/backends/litellm.yaml +0 -0
  135. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/__init__.py +0 -0
  136. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -0
  137. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/codex-responses-local.yaml +0 -0
  138. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +0 -0
  139. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-anthropic.yaml +0 -0
  140. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
  141. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +0 -0
  142. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +0 -0
  143. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -0
  144. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
  145. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
  146. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
  147. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +0 -0
  148. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
  149. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -0
  150. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-gemini.yaml +0 -0
  151. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
  152. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
  153. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-minimax.yaml +0 -0
  154. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
  155. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
  156. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
  157. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/schema.py +0 -0
  158. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/__init__.py +0 -0
  159. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/__init__.py +0 -0
  160. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/capabilities.py +0 -0
  161. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/protocols.py +0 -0
  162. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/secrets.py +0 -0
  163. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/template_secrets.py +0 -0
  164. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/backend_dependency.py +0 -0
  165. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/credential_registry.py +0 -0
  166. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/__init__.py +0 -0
  167. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/model_catalog.yaml +0 -0
  168. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
  169. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
  170. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
  171. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/effort.py +0 -0
  172. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/__init__.py +0 -0
  173. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/_lifecycle.py +0 -0
  174. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/claude.py +0 -0
  175. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/codex.py +0 -0
  176. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/codex_stream.py +0 -0
  177. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/types.py +0 -0
  178. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/lanes.py +0 -0
  179. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/__init__.py +0 -0
  180. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/__init__.py +0 -0
  181. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/base.py +0 -0
  182. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/litellm.py +0 -0
  183. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/openai_compat.py +0 -0
  184. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/openrouter.py +0 -0
  185. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/credentials.py +0 -0
  186. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/detection.py +0 -0
  187. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/errors.py +0 -0
  188. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/protocols.py +0 -0
  189. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/types.py +0 -0
  190. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/logging.py +0 -0
  191. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/models/__init__.py +0 -0
  192. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/models/catalog.py +0 -0
  193. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/models/types.py +0 -0
  194. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/naming.py +0 -0
  195. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/__init__.py +0 -0
  196. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/backend_reconcile.py +0 -0
  197. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_bridge.py +0 -0
  198. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_enrollment.py +0 -0
  199. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_interactive.py +0 -0
  200. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/context.py +0 -0
  201. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/provider_trace.py +0 -0
  202. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/proxy.py +0 -0
  203. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/resolution.py +0 -0
  204. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/transfer.py +0 -0
  205. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/usage_summary.py +0 -0
  206. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/paths.py +0 -0
  207. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/process.py +0 -0
  208. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/provider_types.py +0 -0
  209. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/__init__.py +0 -0
  210. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/cost_tracking.py +0 -0
  211. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/env.py +0 -0
  212. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/headless_json.py +0 -0
  213. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/proxy.py +0 -0
  214. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/routing.py +0 -0
  215. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/session_runner.py +0 -0
  216. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/structured_output.py +0 -0
  217. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/tagger.py +0 -0
  218. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/throttle.py +0 -0
  219. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/run_id.py +0 -0
  220. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/__init__.py +0 -0
  221. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/codex_preflight.py +0 -0
  222. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/codex_preflight_cache.py +0 -0
  223. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/codex_rollouts.py +0 -0
  224. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/exceptions.py +0 -0
  225. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/lock.py +0 -0
  226. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/__init__.py +0 -0
  227. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/caps.py +0 -0
  228. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/vocabulary.py +0 -0
  229. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/transcript.py +0 -0
  230. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/typing_helpers.py +0 -0
  231. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/billing.py +0 -0
  232. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/correlation.py +0 -0
  233. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/emit.py +0 -0
  234. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/measurement.py +0 -0
  235. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/workqueue/__init__.py +0 -0
  236. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/workqueue/queue.py +0 -0
  237. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/workqueue/types.py +0 -0
  238. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/__init__.py +0 -0
  239. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/cli.py +0 -0
  240. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/codex_hooks.py +0 -0
  241. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/exceptions.py +0 -0
  242. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/hooks.py +0 -0
  243. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/preset.py +0 -0
  244. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/settings_merge.py +0 -0
  245. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/version.py +0 -0
  246. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/__init__.py +0 -0
  247. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/__init__.py +0 -0
  248. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/coding_standards.py +0 -0
  249. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/registry.py +0 -0
  250. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/tdd.py +0 -0
  251. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/engine.py +0 -0
  252. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/protocols.py +0 -0
  253. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/queries.py +0 -0
  254. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/__init__.py +0 -0
  255. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/plan_check.py +0 -0
  256. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/verdict.py +0 -0
  257. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/store.py +0 -0
  258. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/supervisor_lane_degrade.py +0 -0
  259. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/__init__.py +0 -0
  260. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/config.py +0 -0
  261. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/handlers.py +0 -0
  262. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/prompts.py +0 -0
  263. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/types.py +0 -0
  264. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/__init__.py +0 -0
  265. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/branches.py +0 -0
  266. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/config.py +0 -0
  267. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/divergence.py +0 -0
  268. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/policy.py +0 -0
  269. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/stages.py +0 -0
  270. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/__init__.py +0 -0
  271. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/base_client.py +0 -0
  272. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/client_adapter.py +0 -0
  273. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/client_factory.py +0 -0
  274. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/converters.py +0 -0
  275. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/cost_logger.py +0 -0
  276. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/cost_tracker.py +0 -0
  277. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/data_models.py +0 -0
  278. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/error_hints.py +0 -0
  279. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/intercept.py +0 -0
  280. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/metrics.py +0 -0
  281. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/model_spec.py +0 -0
  282. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/passthrough.py +0 -0
  283. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/provider_trace_logger.py +0 -0
  284. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxy_identity.py +0 -0
  285. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxy_orchestrator.py +0 -0
  286. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxy_startup.py +0 -0
  287. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/responses_ingress.py +0 -0
  288. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/responses_passthrough.py +0 -0
  289. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/server.py +0 -0
  290. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/stream_relay.py +0 -0
  291. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/__init__.py +0 -0
  292. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/adversarial.py +0 -0
  293. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/consensus.py +0 -0
  294. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/engine.py +0 -0
  295. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/models.py +0 -0
  296. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/__init__.py +0 -0
  297. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
  298. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/code_debate_evaluation.md +0 -0
  299. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview-performance.md +0 -0
  300. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview-quick.md +0 -0
  301. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview-security.md +0 -0
  302. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview.md +0 -0
  303. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/consensus_evaluation.md +0 -0
  304. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/debate_evaluation.md +0 -0
  305. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/docreview-quick.md +0 -0
  306. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/docreview.md +0 -0
  307. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/thinkdeep.md +0 -0
  308. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/routing.py +0 -0
  309. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/synthesis.py +0 -0
  310. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/engine.py +0 -0
  311. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/extractor.py +0 -0
  312. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/tokenizer.py +0 -0
  313. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/__init__.py +0 -0
  314. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/addendum.py +0 -0
  315. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/artifacts.py +0 -0
  316. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/__init__.py +0 -0
  317. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/cleanup.py +0 -0
  318. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/invoke.py +0 -0
  319. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/paths.py +0 -0
  320. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/cleanup.py +0 -0
  321. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/codex_handoff.py +0 -0
  322. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/codex_invoke.py +0 -0
  323. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/config.py +0 -0
  324. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/context_limit.py +0 -0
  325. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/direct_model.py +0 -0
  326. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/effective.py +0 -0
  327. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/exceptions.py +0 -0
  328. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/hooks/__init__.py +0 -0
  329. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/hooks/models.py +0 -0
  330. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/identity.py +0 -0
  331. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/launch.py +0 -0
  332. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/launch_confirmation.py +0 -0
  333. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/memory_inheritance.py +0 -0
  334. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/model_pin.py +0 -0
  335. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/overrides.py +0 -0
  336. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/passport.py +0 -0
  337. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/plan_resolution.py +0 -0
  338. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/prev_sessions.py +0 -0
  339. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/project_memory.py +0 -0
  340. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/rewind.py +0 -0
  341. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/store.py +0 -0
  342. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/validation.py +0 -0
  343. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/__init__.py +0 -0
  344. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/cleanup.py +0 -0
  345. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/config_copy.py +0 -0
  346. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/create.py +0 -0
  347. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/__init__.py +0 -0
  348. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/container.py +0 -0
  349. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/docker.py +0 -0
  350. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/secrets.py +0 -0
  351. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/analyze/SKILL.md +0 -0
  352. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/challenge/SKILL.md +0 -0
  353. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/consensus/SKILL.md +0 -0
  354. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/consensus/resources/synthesis.md +0 -0
  355. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/debate/SKILL.md +0 -0
  356. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/panel/SKILL.md +0 -0
  357. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/panel/resources/synthesis.md +0 -0
  358. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/SKILL.md +0 -0
  359. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/0-enable.md +0 -0
  360. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
  361. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/10-resume.md +0 -0
  362. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/11-config.md +0 -0
  363. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/12-search.md +0 -0
  364. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/13-policy.md +0 -0
  365. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
  366. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/15-skills.md +0 -0
  367. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/16-memory.md +0 -0
  368. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/18-disable.md +0 -0
  369. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
  370. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/2-extension.md +0 -0
  371. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
  372. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/3-authentication.md +0 -0
  373. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/4-proxy.md +0 -0
  374. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/5-session.md +0 -0
  375. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/6-hook.md +0 -0
  376. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/7-costs.md +0 -0
  377. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/8-status-line.md +0 -0
  378. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/9-direct-commands.md +0 -0
  379. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist.md +0 -0
  380. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/report-template.md +0 -0
  381. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/scripts/start-container.sh +0 -0
  382. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/scripts/walkthrough-state.py +0 -0
  383. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/SKILL.md +0 -0
  384. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/claude-4.6.md +0 -0
  385. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/claude-4.8.md +0 -0
  386. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/gemini-3.1.md +0 -0
  387. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/gpt-5.5.md +0 -0
  388. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/skills-writing-guide.md +0 -0
  389. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code-anthropic.md +0 -0
  390. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code-gemini.md +0 -0
  391. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code-openai.md +0 -0
  392. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code.md +0 -0
  393. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/SKILL.md +0 -0
  394. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
  395. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs-gemini.md +0 -0
  396. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs-openai.md +0 -0
  397. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs.md +0 -0
  398. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/smoke-test/SKILL.md +0 -0
  399. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
  400. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/SKILL.md +0 -0
  401. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code-anthropic.md +0 -0
  402. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code-gemini.md +0 -0
  403. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code-openai.md +0 -0
  404. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code.md +0 -0
  405. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs-anthropic.md +0 -0
  406. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs-gemini.md +0 -0
  407. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs-openai.md +0 -0
  408. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs.md +0 -0
  409. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/SKILL.md +0 -0
  410. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/resources/checklist.md +0 -0
  411. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
  412. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
  413. {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/scripts/walkthrough-state.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: multi-forge
3
- Version: 0.7.0
3
+ Version: 0.7.1
4
4
  Summary: Multi-runtime agent toolkit: proxy routing, cost control, session management, policy enforcement, and workflow orchestration
5
5
  Project-URL: Homepage, https://github.com/hapa1i/multi-forge
6
6
  Project-URL: Repository, https://github.com/hapa1i/multi-forge
@@ -56,10 +56,10 @@ Description-Content-Type: text/markdown
56
56
  **Multi-runtime agent toolkit: proxy routing, cost control, session management, and policy enforcement for coding
57
57
  agents.**
58
58
 
59
- Forge sits between you and your coding agent (Claude Code by default, with Codex as an alternate runtime and Gemini
60
- next), adding persistent sessions, multi-provider model routing, cost visibility with spend caps, and autonomous
61
- verification. You run `forge session start` instead of `claude`; Forge then routes to your chosen model provider, tracks
62
- state across sessions, and enforces configured policies.
59
+ Forge sits between you and your coding agent (Claude Code by default, with Codex as an alternate runtime), adding
60
+ persistent sessions, multi-provider model routing, cost visibility with spend caps, and autonomous verification. You run
61
+ `forge session start` instead of `claude`; Forge then routes to your chosen model provider, tracks state across
62
+ sessions, and enforces configured policies.
63
63
 
64
64
  ```bash
65
65
  # Use Claude with session tracking (no proxy needed)
@@ -15,10 +15,10 @@
15
15
  **Multi-runtime agent toolkit: proxy routing, cost control, session management, and policy enforcement for coding
16
16
  agents.**
17
17
 
18
- Forge sits between you and your coding agent (Claude Code by default, with Codex as an alternate runtime and Gemini
19
- next), adding persistent sessions, multi-provider model routing, cost visibility with spend caps, and autonomous
20
- verification. You run `forge session start` instead of `claude`; Forge then routes to your chosen model provider, tracks
21
- state across sessions, and enforces configured policies.
18
+ Forge sits between you and your coding agent (Claude Code by default, with Codex as an alternate runtime), adding
19
+ persistent sessions, multi-provider model routing, cost visibility with spend caps, and autonomous verification. You run
20
+ `forge session start` instead of `claude`; Forge then routes to your chosen model provider, tracks state across
21
+ sessions, and enforces configured policies.
22
22
 
23
23
  ```bash
24
24
  # Use Claude with session tracking (no proxy needed)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "multi-forge"
3
- version = "0.7.0"
3
+ version = "0.7.1"
4
4
  description = "Multi-runtime agent toolkit: proxy routing, cost control, session management, policy enforcement, and workflow orchestration"
5
5
  readme = "README.md"
6
6
  license = "Apache-2.0"
@@ -0,0 +1,11 @@
1
+ """Multi-Forge - Multi-runtime agent toolkit."""
2
+
3
+ from importlib.metadata import PackageNotFoundError, version
4
+
5
+ try:
6
+ # Single source of truth is pyproject.toml; reading installed metadata keeps
7
+ # ``__version__`` from drifting (a hardcoded literal once shipped 0.5.0 while
8
+ # the package was 0.6.0, poisoning forge_version in manifests and work items).
9
+ __version__ = version("multi-forge")
10
+ except PackageNotFoundError: # source tree without an installed dist
11
+ __version__ = "0.0.0+unknown"
@@ -12,11 +12,10 @@ Ownership: Forge Backend Manager (`forge model backend` CLI).
12
12
 
13
13
  from __future__ import annotations
14
14
 
15
- import json
16
15
  import logging
17
16
  from dataclasses import asdict, dataclass, field
18
17
  from pathlib import Path
19
- from typing import Callable, Literal
18
+ from typing import Any, Callable, Literal, NoReturn
20
19
 
21
20
  import dacite
22
21
 
@@ -26,6 +25,7 @@ from forge.core.state import (
26
25
  StateUnreadableError,
27
26
  atomic_write_json,
28
27
  file_lock_for_target,
28
+ read_versioned_json_object,
29
29
  )
30
30
 
31
31
  logger = logging.getLogger(__name__)
@@ -99,6 +99,16 @@ def _uses_old_backend_id_process_records(data: object) -> bool:
99
99
  return any(isinstance(entry, dict) and "backend_id" in entry for entry in entries.values())
100
100
 
101
101
 
102
+ def _handle_registry_version_mismatch(path: Path, data: dict[str, Any], version: Any) -> NoReturn:
103
+ if version == 1 and _uses_old_backend_id_process_records(data):
104
+ raise BackendRegistryCorruptedError(str(path), OLD_BACKEND_ID_PROCESS_FIELD_TIP)
105
+ raise BackendRegistryCorruptedError(
106
+ str(path),
107
+ f"incompatible version {version} (this Forge expects {BACKEND_REGISTRY_VERSION}). "
108
+ f"Delete this file and retry.",
109
+ )
110
+
111
+
102
112
  class BackendRegistryStore:
103
113
  """Manage the backend registry at ~/.forge/backends/index.json.
104
114
 
@@ -121,26 +131,14 @@ class BackendRegistryStore:
121
131
  if not self.exists():
122
132
  return BackendRegistry()
123
133
 
124
- try:
125
- with open(self._registry_path, encoding="utf-8") as f:
126
- data = json.load(f)
127
- except json.JSONDecodeError as e:
128
- raise BackendRegistryCorruptedError(str(self._registry_path), f"invalid JSON: {e}")
129
- except OSError as e:
130
- # A failed read is environmental, not corruption -- forge clean must not delete it.
131
- raise BackendRegistryUnreadableError(str(self._registry_path), f"read error: {e}")
132
-
133
- version = data.get("version")
134
- if version is None:
135
- raise BackendRegistryCorruptedError(str(self._registry_path), "missing version field")
136
- if version != BACKEND_REGISTRY_VERSION:
137
- if version == 1 and _uses_old_backend_id_process_records(data):
138
- raise BackendRegistryCorruptedError(str(self._registry_path), OLD_BACKEND_ID_PROCESS_FIELD_TIP)
139
- raise BackendRegistryCorruptedError(
140
- str(self._registry_path),
141
- f"incompatible version {version} (this Forge expects {BACKEND_REGISTRY_VERSION}). "
142
- f"Delete this file and retry.",
143
- )
134
+ data = read_versioned_json_object(
135
+ self._registry_path,
136
+ version_key="version",
137
+ expected_version=BACKEND_REGISTRY_VERSION,
138
+ corrupted_error=BackendRegistryCorruptedError,
139
+ unreadable_error=BackendRegistryUnreadableError,
140
+ on_version_mismatch=_handle_registry_version_mismatch,
141
+ )
144
142
 
145
143
  try:
146
144
  return dacite.from_dict(
@@ -23,7 +23,7 @@ from typing import Any
23
23
 
24
24
  from forge.cli.hooks.codex_patch import PatchFileOp, parse_apply_patch
25
25
  from forge.cli.hooks.policy import format_deny_text, format_needs_review_text
26
- from forge.policy.deterministic.base import is_under_directory
26
+ from forge.policy.deterministic.base import tests_first_sort_key
27
27
  from forge.policy.types import ActionContext, CompositeDecision
28
28
 
29
29
  _MAX_CONTENT_CHARS = 5000 # same truncation convention as ClaudeHookAdapter
@@ -186,12 +186,4 @@ def sort_contexts_tests_first(contexts: list[ActionContext]) -> list[ActionConte
186
186
  patch order is preserved within each bucket.
187
187
  """
188
188
 
189
- def _key(ctx: ActionContext) -> int:
190
- path = ctx.target_path or ""
191
- if is_under_directory(path, "tests"):
192
- return 0
193
- if is_under_directory(path, "src"):
194
- return 2
195
- return 1
196
-
197
- return sorted(contexts, key=_key)
189
+ return sorted(contexts, key=lambda ctx: tests_first_sort_key(ctx.target_path))
@@ -79,6 +79,109 @@ from .verification import _run_verification_check
79
79
  logger = logging.getLogger(__name__)
80
80
 
81
81
 
82
+ @dataclass(frozen=True)
83
+ class _TranscriptCaptureResult:
84
+ copied: bool
85
+ dst_abs: Path
86
+ dst_rel: Path
87
+ manifest_updated: bool
88
+ manifest_error: str | None
89
+
90
+
91
+ def _capture_transcript_artifact(
92
+ *,
93
+ cwd: Path,
94
+ store: Any,
95
+ manifest: Any,
96
+ transcript_path: str,
97
+ session_id: str,
98
+ reason: str,
99
+ confirmed_by: str,
100
+ event_label: str,
101
+ fail_on_copy_error: bool,
102
+ ) -> _TranscriptCaptureResult:
103
+ """Copy a transcript into artifacts and reconcile the manifest."""
104
+ project_root = resolve_forge_root(cwd)
105
+ paths = get_artifact_paths(project_root, manifest.name)
106
+
107
+ src = Path(transcript_path)
108
+ dst_abs = paths.transcripts_abs / f"{session_id}.jsonl"
109
+ dst_rel = paths.transcripts_rel / f"{session_id}.jsonl"
110
+
111
+ try:
112
+ copied = safe_copy_file(src, dst_abs, overwrite=True)
113
+ except Exception:
114
+ if fail_on_copy_error:
115
+ raise
116
+ copied = False
117
+
118
+ if dst_abs.is_file():
119
+ _copy_transcript_to_pending_runs(dst_abs, session_id=session_id)
120
+
121
+ manifest_updated = True
122
+ manifest_error: str | None = None
123
+
124
+ try:
125
+
126
+ def _mutate(m: object) -> None:
127
+ from forge.session.models import SessionState
128
+
129
+ if not isinstance(m, SessionState):
130
+ raise TypeError(f"Expected SessionState, got {type(m)}")
131
+
132
+ artifacts = m.confirmed.artifacts
133
+ _append_artifact_entry(
134
+ artifacts,
135
+ kind="transcripts",
136
+ entry={
137
+ "captured_at": now_iso(),
138
+ "reason": reason,
139
+ "source_path": transcript_path,
140
+ "session_id": session_id,
141
+ "copied_path": str(dst_rel),
142
+ "copied": copied,
143
+ },
144
+ )
145
+
146
+ m.confirmed.claude_session_id = session_id
147
+ m.confirmed.transcript_path = transcript_path
148
+
149
+ from forge import __version__
150
+ from forge.session.models import PolicyConfirmed
151
+
152
+ if m.confirmed.policy:
153
+ m.confirmed.policy.forge_version = __version__
154
+ else:
155
+ m.confirmed.policy = PolicyConfirmed(forge_version=__version__)
156
+
157
+ m.confirmed.confirmed_at = now_iso()
158
+ m.confirmed.confirmed_by = confirmed_by
159
+
160
+ store.update(timeout_s=HOOK_LOCK_TIMEOUT_S, mutate=_mutate)
161
+ try:
162
+ from forge.session.index import IndexStore
163
+
164
+ IndexStore().update_uuid(manifest.name, session_id, forge_root=str(store.forge_root))
165
+ except Exception:
166
+ logger.debug("%s hook: index UUID sync failed", event_label, exc_info=True)
167
+
168
+ except FileLockTimeoutError:
169
+ manifest_updated = False
170
+ manifest_error = "lock_contended"
171
+
172
+ except Exception as e:
173
+ manifest_updated = False
174
+ manifest_error = str(e)
175
+
176
+ return _TranscriptCaptureResult(
177
+ copied=copied,
178
+ dst_abs=dst_abs,
179
+ dst_rel=dst_rel,
180
+ manifest_updated=manifest_updated,
181
+ manifest_error=manifest_error,
182
+ )
183
+
184
+
82
185
  @hooks.command(name="session-start")
83
186
  @click.option(
84
187
  "--cwd",
@@ -397,85 +500,31 @@ def stop() -> None:
397
500
  _output_json({"success": False, "error": "missing_session_id"})
398
501
  return
399
502
 
400
- project_root = resolve_forge_root(cwd)
401
- paths = get_artifact_paths(project_root, manifest.name)
402
-
403
- src = Path(transcript_path)
404
- dst_abs = paths.transcripts_abs / f"{session_id}.jsonl"
405
- dst_rel = paths.transcripts_rel / f"{session_id}.jsonl"
406
-
407
503
  try:
408
504
  # Claude can invoke Stop repeatedly for the same session UUID as the
409
505
  # transcript grows turn by turn. Refresh the UUID-named artifact so
410
506
  # search/index consumers see the latest snapshot instead of the first
411
507
  # turn that happened to be copied.
412
- copied = safe_copy_file(src, dst_abs, overwrite=True)
508
+ capture = _capture_transcript_artifact(
509
+ cwd=cwd,
510
+ store=store,
511
+ manifest=manifest,
512
+ transcript_path=transcript_path,
513
+ session_id=session_id,
514
+ reason="stop",
515
+ confirmed_by="hook:stop",
516
+ event_label="Stop",
517
+ fail_on_copy_error=True,
518
+ )
413
519
  except Exception as e:
414
520
  _output_json({"success": False, "error": "copy_failed", "message": str(e)})
415
521
  return
416
522
 
417
- # Copy transcript to pending run directories (QA/walkthrough artifacts)
418
- _copy_transcript_to_pending_runs(dst_abs, session_id=session_id)
419
-
420
- # Track manifest update outcome (but don't return early - we still want to enqueue)
421
- manifest_updated = True
422
- manifest_error: str | None = None
423
-
424
- try:
425
-
426
- def _mutate(m: object) -> None:
427
- from forge.session.models import SessionState
428
-
429
- if not isinstance(m, SessionState):
430
- raise TypeError(f"Expected SessionState, got {type(m)}")
431
-
432
- artifacts = m.confirmed.artifacts
433
- _append_artifact_entry(
434
- artifacts,
435
- kind="transcripts",
436
- entry={
437
- "captured_at": now_iso(),
438
- "reason": "stop",
439
- "source_path": transcript_path,
440
- "session_id": session_id,
441
- "copied_path": str(dst_rel),
442
- "copied": copied,
443
- },
444
- )
445
-
446
- # Stop carries the live conversation identity. SessionStart can
447
- # lag or report an inherited UUID for native fork launches, so
448
- # reconcile from Stop before later cleanup/resume code reads it.
449
- m.confirmed.claude_session_id = session_id
450
- m.confirmed.transcript_path = transcript_path
451
-
452
- # Record policy provenance (always on Stop, per design §4.1.6)
453
- from forge import __version__
454
- from forge.session.models import PolicyConfirmed
455
-
456
- if m.confirmed.policy:
457
- m.confirmed.policy.forge_version = __version__
458
- else:
459
- m.confirmed.policy = PolicyConfirmed(forge_version=__version__)
460
-
461
- m.confirmed.confirmed_at = now_iso()
462
- m.confirmed.confirmed_by = "hook:stop"
463
-
464
- store.update(timeout_s=HOOK_LOCK_TIMEOUT_S, mutate=_mutate)
465
- try:
466
- from forge.session.index import IndexStore
467
-
468
- IndexStore().update_uuid(manifest.name, session_id, forge_root=str(store.forge_root))
469
- except Exception:
470
- logger.debug("Stop hook: index UUID sync failed", exc_info=True)
471
-
472
- except FileLockTimeoutError:
473
- manifest_updated = False
474
- manifest_error = "lock_contended"
475
-
476
- except Exception as e:
477
- manifest_updated = False
478
- manifest_error = str(e)
523
+ copied = capture.copied
524
+ dst_abs = capture.dst_abs
525
+ dst_rel = capture.dst_rel
526
+ manifest_updated = capture.manifest_updated
527
+ manifest_error = capture.manifest_error
479
528
 
480
529
  # Run verification check (Ralph-Wiggum pattern)
481
530
  # This must happen AFTER transcript copy so we have the artifact to check
@@ -658,75 +707,22 @@ def stop_failure() -> None:
658
707
  _output_json({"success": True, "action": "skip", "reason": "no_session_id"})
659
708
  return
660
709
 
661
- project_root = resolve_forge_root(cwd)
662
- paths = get_artifact_paths(project_root, manifest.name)
663
-
664
- src = Path(transcript_path)
665
- dst_abs = paths.transcripts_abs / f"{session_id}.jsonl"
666
- dst_rel = paths.transcripts_rel / f"{session_id}.jsonl"
667
-
668
- try:
669
- # Keep the session artifact aligned with the latest transcript snapshot
670
- # here too; StopFailure can arrive after earlier Stop captures.
671
- copied = safe_copy_file(src, dst_abs, overwrite=True)
672
- except Exception:
673
- copied = False
674
-
675
- # Only fan out and enqueue if the artifact actually exists on disk.
676
- # Otherwise we'd consume pending-transcript markers and create index
677
- # markers that retry until poison handling kicks in.
678
- if dst_abs.is_file():
679
- _copy_transcript_to_pending_runs(dst_abs, session_id=session_id)
680
-
681
- # Best-effort manifest update
682
- try:
683
-
684
- def _mutate(m: object) -> None:
685
- from forge.session.models import SessionState
686
-
687
- if not isinstance(m, SessionState):
688
- raise TypeError(f"Expected SessionState, got {type(m)}")
689
-
690
- artifacts = m.confirmed.artifacts
691
- _append_artifact_entry(
692
- artifacts,
693
- kind="transcripts",
694
- entry={
695
- "captured_at": now_iso(),
696
- "reason": "stop-failure",
697
- "source_path": transcript_path,
698
- "session_id": session_id,
699
- "copied_path": str(dst_rel),
700
- "copied": copied,
701
- },
702
- )
703
-
704
- # StopFailure is a last-chance transcript capture. If it carries a
705
- # newer Claude session ID than SessionStart recorded, reconcile the
706
- # manifest the same way Stop does.
707
- m.confirmed.claude_session_id = session_id
708
- m.confirmed.transcript_path = transcript_path
709
-
710
- from forge import __version__
711
- from forge.session.models import PolicyConfirmed
712
-
713
- if m.confirmed.policy:
714
- m.confirmed.policy.forge_version = __version__
715
- else:
716
- m.confirmed.policy = PolicyConfirmed(forge_version=__version__)
717
-
718
- m.confirmed.confirmed_at = now_iso()
719
- m.confirmed.confirmed_by = "hook:stop-failure"
720
-
721
- store.update(timeout_s=HOOK_LOCK_TIMEOUT_S, mutate=_mutate)
722
- try:
723
- from forge.session.index import IndexStore
724
-
725
- IndexStore().update_uuid(manifest.name, session_id, forge_root=str(store.forge_root))
726
- except Exception:
727
- logger.debug("StopFailure hook: index UUID sync failed", exc_info=True)
728
- except Exception:
729
- pass # Best-effort: never fail on StopFailure
710
+ # Keep the session artifact aligned with the latest transcript snapshot
711
+ # here too; StopFailure can arrive after earlier Stop captures.
712
+ capture = _capture_transcript_artifact(
713
+ cwd=cwd,
714
+ store=store,
715
+ manifest=manifest,
716
+ transcript_path=transcript_path,
717
+ session_id=session_id,
718
+ reason="stop-failure",
719
+ confirmed_by="hook:stop-failure",
720
+ event_label="StopFailure",
721
+ fail_on_copy_error=False,
722
+ )
723
+ copied = capture.copied
724
+ dst_abs = capture.dst_abs
725
+ dst_rel = capture.dst_rel
730
726
 
731
727
  # Only enqueue work markers if the artifact exists on disk.
732
728
  # Enqueuing for a nonexistent artifact wastes retries until poison handling.
@@ -1719,17 +1715,11 @@ def _copy_transcript_to_pending_runs(transcript_path: Path, *, session_id: str |
1719
1715
  # --- Team Hook Handlers ---
1720
1716
 
1721
1717
 
1722
- @hooks.command(name="teammate-idle")
1723
- def teammate_idle() -> None:
1724
- """Handle TeammateIdle hook from Claude Code.
1725
-
1726
- Exit 0: allow teammate to go idle.
1727
- Exit 2: teammate continues working (stderr = feedback).
1728
- """
1729
- data, err = _read_stdin_json()
1718
+ def _team_supervisor_hook(log_label: str, handler: Callable[..., tuple[int, str]]) -> None:
1719
+ data, _err = _read_stdin_json()
1730
1720
  if data is None:
1731
1721
  sys.exit(0)
1732
- logger.debug("teammate-idle: session=%s", str(data.get("session_id", "?"))[:12])
1722
+ logger.debug("%s: session=%s", log_label, str(data.get("session_id", "?"))[:12])
1733
1723
 
1734
1724
  try:
1735
1725
  cwd = Path.cwd().resolve()
@@ -1746,10 +1736,7 @@ def teammate_idle() -> None:
1746
1736
  sys.exit(0)
1747
1737
 
1748
1738
  from forge.cli.consumer_lane_freeze import persist_lane_freeze
1749
- from forge.policy.team.handlers import (
1750
- TEAM_SUPERVISOR_CONSUMER,
1751
- handle_teammate_idle,
1752
- )
1739
+ from forge.policy.team.handlers import TEAM_SUPERVISOR_CONSUMER
1753
1740
  from forge.session.consumer_lanes import read_bound_backend_id, read_bound_lane
1754
1741
 
1755
1742
  dispatched_lane = read_bound_lane(manifest, TEAM_SUPERVISOR_CONSUMER)
@@ -1763,13 +1750,25 @@ def teammate_idle() -> None:
1763
1750
  cache_key = _safe_cache_key(data.get("session_id"))
1764
1751
  exit_code, feedback = _run_team_handler(
1765
1752
  cache_key,
1766
- lambda cache: handle_teammate_idle(data, config, cache, backend_id=backend_id, on_dispatch=_freeze),
1753
+ lambda cache: handler(data, config, cache, backend_id=backend_id, on_dispatch=_freeze),
1767
1754
  )
1768
1755
  if exit_code == 2 and feedback:
1769
1756
  print(feedback, file=sys.stderr)
1770
1757
  sys.exit(exit_code)
1771
1758
 
1772
1759
 
1760
+ @hooks.command(name="teammate-idle")
1761
+ def teammate_idle() -> None:
1762
+ """Handle TeammateIdle hook from Claude Code.
1763
+
1764
+ Exit 0: allow teammate to go idle.
1765
+ Exit 2: teammate continues working (stderr = feedback).
1766
+ """
1767
+ from forge.policy.team.handlers import handle_teammate_idle
1768
+
1769
+ _team_supervisor_hook("teammate-idle", handle_teammate_idle)
1770
+
1771
+
1773
1772
  @hooks.command(name="task-completed")
1774
1773
  def task_completed() -> None:
1775
1774
  """Handle TaskCompleted hook from Claude Code.
@@ -1777,48 +1776,9 @@ def task_completed() -> None:
1777
1776
  Exit 0: task marked as completed.
1778
1777
  Exit 2: task stays open (stderr = feedback to teammate).
1779
1778
  """
1780
- data, err = _read_stdin_json()
1781
- if data is None:
1782
- sys.exit(0)
1783
- logger.debug("task-completed: session=%s", str(data.get("session_id", "?"))[:12])
1784
-
1785
- try:
1786
- cwd = Path.cwd().resolve()
1787
- store = resolve_session_store(cwd, session_id=data.get("session_id"))
1788
- if store is None:
1789
- sys.exit(0)
1790
- manifest = store.read()
1791
- effective = compute_effective_intent(manifest)
1792
- except Exception:
1793
- sys.exit(0)
1794
-
1795
- config = effective.policy.team_supervisor if effective.policy else None
1796
- if not config or not config.enabled:
1797
- sys.exit(0)
1798
-
1799
- from forge.cli.consumer_lane_freeze import persist_lane_freeze
1800
- from forge.policy.team.handlers import (
1801
- TEAM_SUPERVISOR_CONSUMER,
1802
- handle_task_completed,
1803
- )
1804
- from forge.session.consumer_lanes import read_bound_backend_id, read_bound_lane
1779
+ from forge.policy.team.handlers import handle_task_completed
1805
1780
 
1806
- dispatched_lane = read_bound_lane(manifest, TEAM_SUPERVISOR_CONSUMER)
1807
- backend_id = read_bound_backend_id(manifest, TEAM_SUPERVISOR_CONSUMER)
1808
-
1809
- def _freeze() -> None:
1810
- # Fires from _run_supervisor's on_dispatch, so the lane freezes only on a real dispatch
1811
- # (not a cache/tagger/depth skip). Short HOOK_LOCK_TIMEOUT_S keeps the hook responsive (T6a).
1812
- persist_lane_freeze(store, TEAM_SUPERVISOR_CONSUMER, dispatched_lane, timeout_s=HOOK_LOCK_TIMEOUT_S)
1813
-
1814
- cache_key = _safe_cache_key(data.get("session_id"))
1815
- exit_code, feedback = _run_team_handler(
1816
- cache_key,
1817
- lambda cache: handle_task_completed(data, config, cache, backend_id=backend_id, on_dispatch=_freeze),
1818
- )
1819
- if exit_code == 2 and feedback:
1820
- print(feedback, file=sys.stderr)
1821
- sys.exit(exit_code)
1781
+ _team_supervisor_hook("task-completed", handle_task_completed)
1822
1782
 
1823
1783
 
1824
1784
  @hooks.command(name="read-hygiene")