multi-forge 0.4.0__tar.gz → 0.6.0__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 (368) hide show
  1. {multi_forge-0.4.0 → multi_forge-0.6.0}/PKG-INFO +16 -11
  2. {multi_forge-0.4.0 → multi_forge-0.6.0}/README.md +15 -10
  3. {multi_forge-0.4.0 → multi_forge-0.6.0}/pyproject.toml +1 -1
  4. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/__init__.py +1 -1
  5. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/activity.py +24 -7
  6. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/extensions.py +69 -8
  7. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/gc.py +4 -4
  8. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/_group.py +5 -4
  9. multi_forge-0.6.0/src/forge/cli/hooks/codex_patch.py +129 -0
  10. multi_forge-0.6.0/src/forge/cli/hooks/codex_policy.py +197 -0
  11. multi_forge-0.6.0/src/forge/cli/hooks/codex_transfer.py +113 -0
  12. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/commands.py +223 -19
  13. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/direct_commands.py +116 -4
  14. multi_forge-0.6.0/src/forge/cli/hooks/policy.py +315 -0
  15. multi_forge-0.6.0/src/forge/cli/hooks/protocols.py +57 -0
  16. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/memory.py +6 -6
  17. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/policy.py +248 -8
  18. multi_forge-0.6.0/src/forge/cli/runtime.py +241 -0
  19. multi_forge-0.6.0/src/forge/cli/session_codex.py +489 -0
  20. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/session_fork.py +29 -3
  21. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/session_lifecycle.py +87 -175
  22. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/session_manage.py +35 -8
  23. multi_forge-0.6.0/src/forge/cli/session_model_pin.py +196 -0
  24. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/status_line.py +5 -1
  25. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/transfer.py +12 -4
  26. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/anthropic-passthrough.yaml +3 -2
  27. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +5 -1
  28. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-anthropic.yaml +6 -3
  29. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +5 -3
  30. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/auth/capabilities.py +17 -2
  31. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/data/model_catalog.yaml +41 -3
  32. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/invoker/__init__.py +3 -0
  33. multi_forge-0.4.0/src/forge/core/invoker/claude.py → multi_forge-0.6.0/src/forge/core/invoker/_lifecycle.py +150 -174
  34. multi_forge-0.6.0/src/forge/core/invoker/claude.py +154 -0
  35. multi_forge-0.6.0/src/forge/core/invoker/codex.py +243 -0
  36. multi_forge-0.6.0/src/forge/core/invoker/codex_stream.py +158 -0
  37. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/invoker/types.py +11 -0
  38. multi_forge-0.6.0/src/forge/core/ops/codex_bridge.py +390 -0
  39. multi_forge-0.6.0/src/forge/core/ops/codex_enrollment.py +307 -0
  40. multi_forge-0.6.0/src/forge/core/ops/codex_interactive.py +560 -0
  41. multi_forge-0.6.0/src/forge/core/ops/codex_session.py +579 -0
  42. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/gc.py +27 -9
  43. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/resolution.py +7 -7
  44. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/session.py +7 -7
  45. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/transfer.py +18 -4
  46. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/usage_summary.py +180 -44
  47. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/env.py +92 -17
  48. multi_forge-0.6.0/src/forge/core/run_id.py +44 -0
  49. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/runtime/__init__.py +18 -0
  50. multi_forge-0.6.0/src/forge/core/runtime/codex_preflight.py +541 -0
  51. multi_forge-0.6.0/src/forge/core/runtime/codex_rollouts.py +187 -0
  52. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/runtime/registry.py +71 -26
  53. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/usage/__init__.py +2 -0
  54. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/usage/emit.py +65 -0
  55. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/usage/ledger.py +5 -1
  56. multi_forge-0.6.0/src/forge/install/codex_hooks.py +515 -0
  57. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/installer.py +126 -0
  58. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/models.py +33 -0
  59. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/deterministic/base.py +15 -8
  60. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/engine.py +115 -63
  61. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/queries.py +2 -2
  62. multi_forge-0.6.0/src/forge/policy/semantic/plan_check.py +561 -0
  63. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/semantic/promotion.py +1 -1
  64. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/semantic/supervisor.py +4 -4
  65. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/types.py +8 -5
  66. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/cost_logger.py +83 -1
  67. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/data_models.py +4 -2
  68. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/server.py +45 -0
  69. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/models.py +7 -0
  70. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/extractor.py +1 -1
  71. multi_forge-0.6.0/src/forge/session/codex_handoff.py +253 -0
  72. multi_forge-0.6.0/src/forge/session/codex_invoke.py +81 -0
  73. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/direct_model.py +4 -0
  74. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/exceptions.py +13 -0
  75. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/manager.py +28 -6
  76. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/models.py +51 -1
  77. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/overrides.py +9 -0
  78. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/transfer.py +153 -21
  79. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/panel/SKILL.md +3 -3
  80. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/2-extension.md +43 -0
  81. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist.md +13 -11
  82. multi_forge-0.4.0/src/forge/cli/hooks/policy.py +0 -212
  83. multi_forge-0.4.0/src/forge/cli/hooks/protocols.py +0 -53
  84. multi_forge-0.4.0/src/forge/cli/runtime.py +0 -105
  85. {multi_forge-0.4.0 → multi_forge-0.6.0}/.gitignore +0 -0
  86. {multi_forge-0.4.0 → multi_forge-0.6.0}/LICENSE +0 -0
  87. {multi_forge-0.4.0 → multi_forge-0.6.0}/NOTICE +0 -0
  88. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/agents/.gitkeep +0 -0
  89. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/commands/.gitkeep +0 -0
  90. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/backend/__init__.py +0 -0
  91. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/backend/adapters/__init__.py +0 -0
  92. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/backend/adapters/litellm.py +0 -0
  93. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/backend/creation.py +0 -0
  94. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/backend/registry.py +0 -0
  95. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/__init__.py +0 -0
  96. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/auth.py +0 -0
  97. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/backend.py +0 -0
  98. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/claude.py +0 -0
  99. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/config_cmd.py +0 -0
  100. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/editor.py +0 -0
  101. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/guards.py +0 -0
  102. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/__init__.py +0 -0
  103. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/_helpers.py +0 -0
  104. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/install.py +0 -0
  105. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/read_hygiene.py +0 -0
  106. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/hooks/verification.py +0 -0
  107. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/launch_confirmation.py +0 -0
  108. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/logs.py +0 -0
  109. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/main.py +0 -0
  110. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/memory_report.py +0 -0
  111. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/memory_writer.py +0 -0
  112. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/output.py +0 -0
  113. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/proxy.py +0 -0
  114. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/proxy_audit.py +0 -0
  115. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/proxy_costs.py +0 -0
  116. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/search.py +0 -0
  117. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/session.py +0 -0
  118. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/session_addendum.py +0 -0
  119. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/statusline/__init__.py +0 -0
  120. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/statusline/context.py +0 -0
  121. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/statusline/names.py +0 -0
  122. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/statusline/palette.py +0 -0
  123. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/statusline/registry.py +0 -0
  124. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/statusline/throttle.py +0 -0
  125. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/cli/workflow.py +0 -0
  126. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/__init__.py +0 -0
  127. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/dataclass_utils.py +0 -0
  128. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/__init__.py +0 -0
  129. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/backends/__init__.py +0 -0
  130. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/backends/litellm.yaml +0 -0
  131. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/__init__.py +0 -0
  132. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
  133. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +0 -0
  134. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +0 -0
  135. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -0
  136. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
  137. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
  138. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
  139. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
  140. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -0
  141. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-gemini.yaml +0 -0
  142. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
  143. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
  144. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-minimax.yaml +0 -0
  145. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
  146. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
  147. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
  148. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/loader.py +0 -0
  149. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/config/schema.py +0 -0
  150. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/__init__.py +0 -0
  151. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/auth/__init__.py +0 -0
  152. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/auth/credentials_file.py +0 -0
  153. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/auth/protocols.py +0 -0
  154. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/auth/secrets.py +0 -0
  155. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/auth/template_secrets.py +0 -0
  156. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/data/__init__.py +0 -0
  157. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
  158. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
  159. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
  160. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/__init__.py +0 -0
  161. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/clients/__init__.py +0 -0
  162. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/clients/base.py +0 -0
  163. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/clients/litellm.py +0 -0
  164. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/clients/openai_compat.py +0 -0
  165. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/clients/openrouter.py +0 -0
  166. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/credentials.py +0 -0
  167. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/detection.py +0 -0
  168. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/errors.py +0 -0
  169. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/protocols.py +0 -0
  170. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/llm/types.py +0 -0
  171. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/logging.py +0 -0
  172. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/models/__init__.py +0 -0
  173. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/models/catalog.py +0 -0
  174. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/models/types.py +0 -0
  175. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/naming.py +0 -0
  176. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/__init__.py +0 -0
  177. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/context.py +0 -0
  178. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/proxy.py +0 -0
  179. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/ops/session_context.py +0 -0
  180. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/paths.py +0 -0
  181. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/process.py +0 -0
  182. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/__init__.py +0 -0
  183. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/cost_tracking.py +0 -0
  184. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/headless_json.py +0 -0
  185. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/proxy.py +0 -0
  186. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/routing.py +0 -0
  187. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/session_runner.py +0 -0
  188. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/structured_output.py +0 -0
  189. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/tagger.py +0 -0
  190. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/reactive/throttle.py +0 -0
  191. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/state/__init__.py +0 -0
  192. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/state/exceptions.py +0 -0
  193. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/state/io.py +0 -0
  194. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/state/lock.py +0 -0
  195. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/state/timestamps.py +0 -0
  196. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/transcript.py +0 -0
  197. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/typing_helpers.py +0 -0
  198. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/usage/billing.py +0 -0
  199. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/usage/correlation.py +0 -0
  200. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/usage/vocabulary.py +0 -0
  201. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/workqueue/__init__.py +0 -0
  202. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/workqueue/queue.py +0 -0
  203. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/core/workqueue/types.py +0 -0
  204. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/__init__.py +0 -0
  205. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/cli.py +0 -0
  206. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/exceptions.py +0 -0
  207. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/hooks.py +0 -0
  208. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/preset.py +0 -0
  209. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/settings_merge.py +0 -0
  210. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/tracking.py +0 -0
  211. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/install/version.py +0 -0
  212. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/__init__.py +0 -0
  213. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/deterministic/__init__.py +0 -0
  214. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/deterministic/coding_standards.py +0 -0
  215. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/deterministic/registry.py +0 -0
  216. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/deterministic/tdd.py +0 -0
  217. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/protocols.py +0 -0
  218. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/semantic/__init__.py +0 -0
  219. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/semantic/verdict.py +0 -0
  220. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/store.py +0 -0
  221. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/team/__init__.py +0 -0
  222. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/team/config.py +0 -0
  223. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/team/handlers.py +0 -0
  224. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/team/prompts.py +0 -0
  225. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/workflow/__init__.py +0 -0
  226. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/workflow/branches.py +0 -0
  227. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/workflow/config.py +0 -0
  228. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/workflow/divergence.py +0 -0
  229. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/workflow/policy.py +0 -0
  230. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/policy/workflow/stages.py +0 -0
  231. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/__init__.py +0 -0
  232. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/audit_logger.py +0 -0
  233. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/base_client.py +0 -0
  234. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/client_adapter.py +0 -0
  235. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/client_factory.py +0 -0
  236. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/converters.py +0 -0
  237. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/cost_tracker.py +0 -0
  238. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/error_hints.py +0 -0
  239. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/intercept.py +0 -0
  240. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/metrics.py +0 -0
  241. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/model_spec.py +0 -0
  242. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/passthrough.py +0 -0
  243. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/proxies.py +0 -0
  244. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/proxy_identity.py +0 -0
  245. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/proxy_orchestrator.py +0 -0
  246. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/proxy_startup.py +0 -0
  247. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/proxy/utils.py +0 -0
  248. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/__init__.py +0 -0
  249. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/adversarial.py +0 -0
  250. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/consensus.py +0 -0
  251. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/engine.py +0 -0
  252. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/__init__.py +0 -0
  253. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/codereview-performance.md +0 -0
  254. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/codereview-quick.md +0 -0
  255. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/codereview-security.md +0 -0
  256. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/codereview.md +0 -0
  257. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/docreview-quick.md +0 -0
  258. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/docreview.md +0 -0
  259. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/resources/thinkdeep.md +0 -0
  260. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/routing.py +0 -0
  261. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/review/synthesis.py +0 -0
  262. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/runtime_config.py +0 -0
  263. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/__init__.py +0 -0
  264. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/bm25_store.py +0 -0
  265. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/content_store.py +0 -0
  266. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/engine.py +0 -0
  267. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/exceptions.py +0 -0
  268. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/index_state.py +0 -0
  269. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/store.py +0 -0
  270. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/search/tokenizer.py +0 -0
  271. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/__init__.py +0 -0
  272. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/active.py +0 -0
  273. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/artifacts.py +0 -0
  274. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/claude/__init__.py +0 -0
  275. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/claude/cleanup.py +0 -0
  276. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/claude/invoke.py +0 -0
  277. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/claude/paths.py +0 -0
  278. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/claude/relocate.py +0 -0
  279. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/cleanup.py +0 -0
  280. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/config.py +0 -0
  281. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/effective.py +0 -0
  282. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/hooks/__init__.py +0 -0
  283. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/hooks/models.py +0 -0
  284. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/hooks/session_start.py +0 -0
  285. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/identity.py +0 -0
  286. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/index.py +0 -0
  287. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/memory_inheritance.py +0 -0
  288. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/memory_writer.py +0 -0
  289. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/passport.py +0 -0
  290. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/plan_resolution.py +0 -0
  291. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/prev_sessions.py +0 -0
  292. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/project_memory.py +0 -0
  293. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/shadow_curation.py +0 -0
  294. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/store.py +0 -0
  295. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/validation.py +0 -0
  296. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/worktree/__init__.py +0 -0
  297. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/worktree/cleanup.py +0 -0
  298. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/worktree/config_copy.py +0 -0
  299. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/session/worktree/create.py +0 -0
  300. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/sidecar/__init__.py +0 -0
  301. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/sidecar/container.py +0 -0
  302. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/sidecar/docker.py +0 -0
  303. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/forge/sidecar/secrets.py +0 -0
  304. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/analyze/SKILL.md +0 -0
  305. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/challenge/SKILL.md +0 -0
  306. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/consensus/SKILL.md +0 -0
  307. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/consensus/resources/code_consensus_evaluation.md +0 -0
  308. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/consensus/resources/consensus_evaluation.md +0 -0
  309. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/consensus/resources/synthesis.md +0 -0
  310. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/debate/SKILL.md +0 -0
  311. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/debate/resources/code_debate_evaluation.md +0 -0
  312. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/debate/resources/debate_evaluation.md +0 -0
  313. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/panel/resources/synthesis.md +0 -0
  314. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/SKILL.md +0 -0
  315. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/0-enable.md +0 -0
  316. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
  317. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/10-resume.md +0 -0
  318. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/11-config.md +0 -0
  319. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/12-search.md +0 -0
  320. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/13-policy.md +0 -0
  321. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
  322. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/15-skills.md +0 -0
  323. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/16-memory.md +0 -0
  324. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/17-info.md +0 -0
  325. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/18-disable.md +0 -0
  326. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
  327. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
  328. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/3-authentication.md +0 -0
  329. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/4-proxy.md +0 -0
  330. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/5-session.md +0 -0
  331. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/6-hook.md +0 -0
  332. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/7-costs.md +0 -0
  333. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/8-status-line.md +0 -0
  334. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/checklist/9-direct-commands.md +0 -0
  335. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/resources/report-template.md +0 -0
  336. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/scripts/start-container.sh +0 -0
  337. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/qa/scripts/walkthrough-state.py +0 -0
  338. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/SKILL.md +0 -0
  339. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/references/claude-4.6.md +0 -0
  340. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/references/claude-4.8.md +0 -0
  341. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/references/gemini-3.1.md +0 -0
  342. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/references/gpt-5.5.md +0 -0
  343. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/references/skills-writing-guide.md +0 -0
  344. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/resources/code-anthropic.md +0 -0
  345. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/resources/code-gemini.md +0 -0
  346. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/resources/code-openai.md +0 -0
  347. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review/resources/code.md +0 -0
  348. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review-docs/SKILL.md +0 -0
  349. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
  350. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review-docs/resources/docs-gemini.md +0 -0
  351. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review-docs/resources/docs-openai.md +0 -0
  352. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/review-docs/resources/docs.md +0 -0
  353. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/smoke-test/SKILL.md +0 -0
  354. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
  355. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/SKILL.md +0 -0
  356. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/code-anthropic.md +0 -0
  357. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/code-gemini.md +0 -0
  358. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/code-openai.md +0 -0
  359. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/code.md +0 -0
  360. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/docs-anthropic.md +0 -0
  361. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/docs-gemini.md +0 -0
  362. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/docs-openai.md +0 -0
  363. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/understand/resources/docs.md +0 -0
  364. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/walkthrough/SKILL.md +0 -0
  365. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/walkthrough/resources/checklist.md +0 -0
  366. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
  367. {multi_forge-0.4.0 → multi_forge-0.6.0}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
  368. {multi_forge-0.4.0 → multi_forge-0.6.0}/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.4.0
3
+ Version: 0.6.0
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,15 +56,18 @@ 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 today, Codex and Gemini next), adding persistent sessions,
60
- multi-provider model routing, cost visibility with spend caps, and autonomous verification. You run
61
- `forge session start` instead of `claude`, and Forge handles the rest -- routing to your chosen model provider, tracking
62
- state across sessions, and enforcing policies.
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`, and Forge handles the rest -- routing to your chosen
62
+ model provider, tracking state across sessions, and enforcing policies.
63
63
 
64
64
  ```bash
65
65
  # Use Claude with session tracking (no proxy needed)
66
66
  forge session start
67
67
 
68
+ # Or run a different runtime entirely -- Codex as an alternate frontend
69
+ forge session start --runtime codex # interactive TUI; hooks/policy need a one-time Codex trust enrollment
70
+
68
71
  # Or route through different model providers (after creating proxies -- see Quick Start)
69
72
  forge session start planner --proxy openrouter-openai # GPT for planning
70
73
  forge session start --proxy openrouter-gemini # Gemini for review
@@ -223,12 +226,14 @@ Run `forge <command> --help` for details on any command.
223
226
 
224
227
  ## Documentation
225
228
 
226
- | Audience | Location | Contents |
227
- | ---------------- | ---------------------------------- | ---------------------------------------------------- |
228
- | **Users** | [docs/end-user/](docs/end-user/) | Tour, guides for sessions, proxies, policies, ... |
229
- | **Developers** | [docs/developer/](docs/developer/) | Setup, coding standards, testing guidelines |
230
- | **Architecture** | [docs/design.md](docs/design.md) | System narrative, data flow, invariants |
231
- | **Work Board** | [docs/board/](docs/board/) | Cards, checklists, change log, implementation memory |
229
+ | Audience | Location | Contents |
230
+ | ------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
231
+ | **Users** | [docs/end-user/](docs/end-user/) | Tour, guides for sessions, proxies, policies, ... |
232
+ | **Developers** | [docs/developer/](docs/developer/) | Setup, coding standards, testing guidelines |
233
+ | **Architecture** | [docs/design.md](docs/design.md) | Core system narrative, data flow, invariants |
234
+ | **Workflow design** | [docs/design_workflows.md](docs/design_workflows.md) | Policy, skills, workflow runners, memory architecture |
235
+ | **CLI reference** | [docs/cli_reference.md](docs/cli_reference.md) | Terminal and direct-command inventory |
236
+ | **Work Board** | [docs/board/](docs/board/) | Cards, checklists, change log, implementation memory |
232
237
 
233
238
  ## Contributing
234
239
 
@@ -15,15 +15,18 @@
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 today, Codex and Gemini next), adding persistent sessions,
19
- multi-provider model routing, cost visibility with spend caps, and autonomous verification. You run
20
- `forge session start` instead of `claude`, and Forge handles the rest -- routing to your chosen model provider, tracking
21
- state across sessions, and enforcing policies.
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`, and Forge handles the rest -- routing to your chosen
21
+ model provider, tracking state across sessions, and enforcing policies.
22
22
 
23
23
  ```bash
24
24
  # Use Claude with session tracking (no proxy needed)
25
25
  forge session start
26
26
 
27
+ # Or run a different runtime entirely -- Codex as an alternate frontend
28
+ forge session start --runtime codex # interactive TUI; hooks/policy need a one-time Codex trust enrollment
29
+
27
30
  # Or route through different model providers (after creating proxies -- see Quick Start)
28
31
  forge session start planner --proxy openrouter-openai # GPT for planning
29
32
  forge session start --proxy openrouter-gemini # Gemini for review
@@ -182,12 +185,14 @@ Run `forge <command> --help` for details on any command.
182
185
 
183
186
  ## Documentation
184
187
 
185
- | Audience | Location | Contents |
186
- | ---------------- | ---------------------------------- | ---------------------------------------------------- |
187
- | **Users** | [docs/end-user/](docs/end-user/) | Tour, guides for sessions, proxies, policies, ... |
188
- | **Developers** | [docs/developer/](docs/developer/) | Setup, coding standards, testing guidelines |
189
- | **Architecture** | [docs/design.md](docs/design.md) | System narrative, data flow, invariants |
190
- | **Work Board** | [docs/board/](docs/board/) | Cards, checklists, change log, implementation memory |
188
+ | Audience | Location | Contents |
189
+ | ------------------- | ---------------------------------------------------- | ----------------------------------------------------- |
190
+ | **Users** | [docs/end-user/](docs/end-user/) | Tour, guides for sessions, proxies, policies, ... |
191
+ | **Developers** | [docs/developer/](docs/developer/) | Setup, coding standards, testing guidelines |
192
+ | **Architecture** | [docs/design.md](docs/design.md) | Core system narrative, data flow, invariants |
193
+ | **Workflow design** | [docs/design_workflows.md](docs/design_workflows.md) | Policy, skills, workflow runners, memory architecture |
194
+ | **CLI reference** | [docs/cli_reference.md](docs/cli_reference.md) | Terminal and direct-command inventory |
195
+ | **Work Board** | [docs/board/](docs/board/) | Cards, checklists, change log, implementation memory |
191
196
 
192
197
  ## Contributing
193
198
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "multi-forge"
3
- version = "0.4.0"
3
+ version = "0.6.0"
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"
@@ -1,3 +1,3 @@
1
1
  """Multi-Forge - Multi-runtime agent toolkit."""
2
2
 
3
- __version__ = "0.4.0"
3
+ __version__ = "0.5.0"
@@ -100,7 +100,10 @@ def _render(summary: SessionActivitySummary, *, days: int | None) -> None:
100
100
  for c in summary.commands:
101
101
  errors = f"[red]{c.errors}[/red]" if c.errors else "0"
102
102
  tokens = f"{c.input_tokens}/{c.output_tokens}" if (c.input_tokens or c.output_tokens) else "-"
103
- cost = f"~{_fmt_usd(c.cost_micro_usd)}" if c.cost_micro_usd is not None else "-"
103
+ if c.cost_micro_usd is None:
104
+ cost = "-"
105
+ else:
106
+ cost = f"{'~' if c.cost_estimated else ''}{_fmt_usd(c.cost_micro_usd)}"
104
107
  row = [c.command, str(c.calls)]
105
108
  if show_workers:
106
109
  row.append(str(c.workers) if c.workers else "-")
@@ -110,17 +113,26 @@ def _render(summary: SessionActivitySummary, *, days: int | None) -> None:
110
113
 
111
114
  pol = summary.policy
112
115
  if pol and pol.has_content:
113
- console.print(
114
- f"\n[bold]Supervisor[/bold]: {pol.supervisor_allow} allow · "
115
- f"{pol.supervisor_warn} warn · {pol.supervisor_deny} block"
116
- )
116
+ if pol.plan_check_allow or pol.plan_check_needs_review:
117
+ console.print(
118
+ f"\n[bold]Plan check (tier-1)[/bold]: {pol.plan_check_allow} allow · "
119
+ f"{pol.plan_check_needs_review} needs review"
120
+ )
121
+ if pol.supervisor_allow or pol.supervisor_warn or pol.supervisor_deny or pol.total_warnings:
122
+ console.print(
123
+ f"\n[bold]Supervisor[/bold]: {pol.supervisor_allow} allow · "
124
+ f"{pol.supervisor_warn} warn · {pol.supervisor_deny} block"
125
+ )
117
126
  for warning in pol.recent_warnings:
118
127
  console.print(f" [yellow]•[/yellow] {warning}")
119
128
 
120
129
  if summary.subagents:
121
130
  console.print(f"\n[bold]Subagents[/bold]: {summary.subagents}")
122
131
 
123
- total_cost = f"~{_fmt_usd(summary.total_cost_micro_usd)}" if summary.total_cost_micro_usd is not None else "n/a"
132
+ if summary.total_cost_micro_usd is None:
133
+ total_cost = "n/a"
134
+ else:
135
+ total_cost = f"{'~' if summary.cost_estimated else ''}{_fmt_usd(summary.total_cost_micro_usd)}"
124
136
  console.print(
125
137
  f"\n[dim]Total:[/dim] {summary.total_events} events · "
126
138
  f"{summary.total_input_tokens}/{summary.total_output_tokens} tok · {total_cost}"
@@ -138,7 +150,12 @@ def _footnotes(summary: SessionActivitySummary) -> list[str]:
138
150
  notes.append("policy decision log is at capacity — older decisions may not be shown")
139
151
  if summary.session_tagging_partial:
140
152
  notes.append("some calls (e.g. the action tagger) are not session-attributed")
141
- notes.append("cost is reported-or-estimated, best-effort; 'forge proxy costs show' is the authoritative spend view")
153
+ # "no snapshot estimates" covers both exact sources: the 4g cost-plane root-join
154
+ # and runtime-reported (runtime_native) self-reports. A cost-less summary keeps
155
+ # the generic caveat -- there is no figure to call exact.
156
+ exact = summary.total_cost_micro_usd is not None and not summary.cost_estimated
157
+ evidence = "reported (no snapshot estimates mixed in)" if exact else "reported-or-estimated"
158
+ notes.append(f"cost is {evidence}, best-effort; 'forge proxy costs show' is the authoritative spend view")
142
159
  return notes
143
160
 
144
161
 
@@ -99,15 +99,18 @@ def _parse_modules(modules_str: str | None) -> set[InstallModule] | None:
99
99
  return {InstallModule(m.strip()) for m in modules_str.split(",")}
100
100
 
101
101
 
102
- def _count_actions(plan: InstallPlan) -> tuple[int, int]:
102
+ def _count_actions(plan: InstallPlan) -> tuple[int, int, int]:
103
103
  """Count non-skip actions in a plan.
104
104
 
105
105
  Returns:
106
- Tuple of (file_actions, settings_actions) that are not skips.
106
+ Tuple of (file_actions, settings_actions, codex_actions) that
107
+ actually change something. A codex install/update counts as an
108
+ action so a codex-only change never renders "Already up to date.".
107
109
  """
108
110
  file_actions = sum(1 for f in plan.files if f.action != "skip")
109
111
  settings_actions = sum(1 for s in plan.settings if s.action != "skip")
110
- return file_actions, settings_actions
112
+ codex_actions = 1 if plan.codex is not None and plan.codex.action in ("install", "update") else 0
113
+ return file_actions, settings_actions, codex_actions
111
114
 
112
115
 
113
116
  # Modules that are intentionally empty in the source tree (only .gitkeep).
@@ -165,8 +168,8 @@ def _print_completion_message(
165
168
  tracking: TrackingStore,
166
169
  ) -> None:
167
170
  """Print appropriate completion message based on what was done."""
168
- file_actions, settings_actions = _count_actions(plan)
169
- total_actions = file_actions + settings_actions
171
+ file_actions, settings_actions, codex_actions = _count_actions(plan)
172
+ total_actions = file_actions + settings_actions + codex_actions
170
173
 
171
174
  _warn_if_modules_have_no_files(plan, scope, project_root, tracking)
172
175
 
@@ -178,6 +181,8 @@ def _print_completion_message(
178
181
  parts.append(f"{file_actions} file{'s' if file_actions != 1 else ''}")
179
182
  if settings_actions > 0:
180
183
  parts.append(f"{settings_actions} setting{'s' if settings_actions != 1 else ''}")
184
+ if codex_actions > 0:
185
+ parts.append("Codex hooks")
181
186
  console.print(f"\n[green]Extensions enabled.[/green] ({', '.join(parts)} updated)")
182
187
 
183
188
  print_tip(
@@ -200,6 +205,30 @@ def _print_completion_message(
200
205
  required = gated[0][1].value
201
206
  print_tip(f"Additional skills available with --profile {required}: {skill_list}", console=console)
202
207
 
208
+ _print_codex_completion(plan, scope)
209
+
210
+
211
+ def _print_codex_completion(plan: InstallPlan, scope: InstallScope) -> None:
212
+ """Print the trust-ceremony guidance (or skip notice) for the codex plan.
213
+
214
+ Registration alone is inert: Codex hooks fire only after the user's
215
+ one-time interactive trust ceremony, which Forge can neither perform nor
216
+ verify -- so a fresh registration always names the ceremony explicitly.
217
+ """
218
+ codex = plan.codex
219
+ if codex is None:
220
+ return
221
+ if codex.action in ("install", "update"):
222
+ where = "in any project" if scope == InstallScope.USER else "in this project"
223
+ config = display_path(codex.config_path) if codex.config_path else "config.toml"
224
+ console.print("\n[dim]Next steps (Codex hooks):[/dim]")
225
+ console.print(f" - Forge hooks are registered in {config} but stay inert until trusted.")
226
+ console.print(f" - Run 'codex' interactively {where} and grant trust when prompted (one-time).")
227
+ elif codex.action == "conflict":
228
+ console.print(f"\n[yellow]Warning:[/yellow] Codex hook registration skipped: {codex.reason}")
229
+ elif codex.action == "unavailable":
230
+ console.print(f"\n[dim]Codex hooks skipped: {codex.reason}.[/dim]")
231
+
203
232
 
204
233
  def _validate_anchor(anchor: Path) -> None:
205
234
  """Reject anchors that point inside a ``.claude/`` directory.
@@ -324,6 +353,23 @@ def _print_plan(plan: InstallPlan, dry_run: bool = False) -> None:
324
353
 
325
354
  console.print(table)
326
355
 
356
+ if plan.codex is not None:
357
+ console.print(f"\n{prefix}[bold]Codex hooks (config.toml):[/bold]")
358
+ table = Table(show_header=True, header_style="bold", box=None)
359
+ table.add_column("ACTION", style="dim")
360
+ table.add_column("TARGET")
361
+ table.add_column("REASON", style="dim")
362
+ style = {
363
+ "install": "green",
364
+ "update": "yellow",
365
+ "skip": "dim",
366
+ "conflict": "yellow", # best-effort: degrades to skip, never blocks
367
+ "unavailable": "dim",
368
+ }.get(plan.codex.action, "")
369
+ target = display_path(plan.codex.config_path) if plan.codex.config_path else ""
370
+ table.add_row(plan.codex.action, target, plan.codex.reason or "", style=style)
371
+ console.print(table)
372
+
327
373
  if plan.has_conflicts:
328
374
  console.print(f"\n{prefix}[bold red]Conflicts detected:[/bold red]")
329
375
  for c in plan.conflicts:
@@ -467,7 +513,7 @@ def extensions() -> None:
467
513
  "--with",
468
514
  "-w",
469
515
  "with_modules",
470
- help="Add modules (comma-separated: commands,agents,skills,hooks,status-line,permissions)",
516
+ help="Add modules (comma-separated: commands,agents,skills,hooks,status-line,permissions,codex-hooks)",
471
517
  )
472
518
  @click.option(
473
519
  "--without",
@@ -672,8 +718,8 @@ def sync_cmd(scope: str | None, force: bool) -> None:
672
718
  console.print("\n[red]Sync failed due to conflicts.[/red]")
673
719
  sys.exit(1)
674
720
  else:
675
- file_actions, settings_actions = _count_actions(plan)
676
- total_actions = file_actions + settings_actions
721
+ file_actions, settings_actions, codex_actions = _count_actions(plan)
722
+ total_actions = file_actions + settings_actions + codex_actions
677
723
  if total_actions == 0:
678
724
  console.print("\n[dim]Already up to date.[/dim]")
679
725
  else:
@@ -682,8 +728,15 @@ def sync_cmd(scope: str | None, force: bool) -> None:
682
728
  parts.append(f"{file_actions} file{'s' if file_actions != 1 else ''}")
683
729
  if settings_actions > 0:
684
730
  parts.append(f"{settings_actions} setting{'s' if settings_actions != 1 else ''}")
731
+ if codex_actions > 0:
732
+ parts.append("Codex hooks")
685
733
  console.print(f"\n[green]Sync complete.[/green] ({', '.join(parts)} updated)")
686
734
 
735
+ # A synced block can carry NEW entries whose trust is not yet
736
+ # granted (per-entry trusted_hash) -- the ceremony guidance
737
+ # matters most exactly here.
738
+ _print_codex_completion(plan, install_scope)
739
+
687
740
  except NoForgeInstallationError as e:
688
741
  console.print(f"[red]Error:[/red] {e}")
689
742
  sys.exit(1)
@@ -788,6 +841,10 @@ def disable_cmd(scope: str | None, uninstall_all: bool, yes: bool) -> None:
788
841
  console.print("[bold]Settings:[/bold]")
789
842
  console.print(table)
790
843
 
844
+ if existing.codex_config_path:
845
+ console.print("\n[bold]Codex hooks:[/bold]")
846
+ console.print(f" [red]remove[/red] managed block in {display_path(existing.codex_config_path)}")
847
+
791
848
  if not yes:
792
849
  if not click.confirm("\nProceed with disable?"):
793
850
  console.print("[dim]Cancelled.[/dim]")
@@ -926,6 +983,8 @@ def status_cmd(scope: str | None, path: str | None, show_all: bool, as_json: boo
926
983
  "modules": list(inst.modules_enabled),
927
984
  "files_count": len(inst.files),
928
985
  "settings_count": len(inst.settings_entries),
986
+ "codex_config_path": inst.codex_config_path,
987
+ "codex_commands": list(inst.codex_commands),
929
988
  "installed_at": inst.installed_at,
930
989
  "updated_at": inst.updated_at,
931
990
  }
@@ -966,6 +1025,8 @@ def status_cmd(scope: str | None, path: str | None, show_all: bool, as_json: boo
966
1025
  console.print(f" Modules: {', '.join(installation.modules_enabled)}")
967
1026
  console.print(f" Files: {len(installation.files)}")
968
1027
  console.print(f" Settings: {len(installation.settings_entries)} entries")
1028
+ if installation.codex_config_path:
1029
+ console.print(f" Codex: hooks registered in {display_path(installation.codex_config_path)}")
969
1030
  console.print(f" Installed: {installation.installed_at}")
970
1031
  console.print(f" Updated: {installation.updated_at}")
971
1032
 
@@ -16,9 +16,9 @@ from forge.core.ops.gc import CleanError, CleanReport, collect_clean_report, run
16
16
  @click.command("clean")
17
17
  @click.option(
18
18
  "--scope",
19
- type=click.Choice(["repo", "project", "all"]),
20
- default="repo",
21
- help="Scope: repo (default), project, or all",
19
+ type=click.Choice(["workspace", "project", "all"]),
20
+ default="workspace",
21
+ help="Scope: workspace (default), project, or all",
22
22
  )
23
23
  @click.option("--yes", "-y", is_flag=True, help="Actually delete (default is dry-run)")
24
24
  @click.option("--verbose", "-v", is_flag=True, help="Show individual items")
@@ -31,7 +31,7 @@ def clean_cmd(scope: str, yes: bool, verbose: bool, as_json: bool) -> None:
31
31
  Examples:
32
32
 
33
33
  \b
34
- forge clean # Dry-run (scope: repo)
34
+ forge clean # Dry-run (scope: workspace)
35
35
  forge clean --yes # Actually clean
36
36
  forge clean --scope all --yes # Clean globally
37
37
  forge clean --scope project # Current Forge project only
@@ -8,11 +8,12 @@ import click
8
8
  @click.group(name="hook", hidden=True)
9
9
  @click.pass_context
10
10
  def hooks(ctx: click.Context) -> None:
11
- """Hook handlers invoked by Claude Code.
11
+ """Hook handlers invoked by agent runtimes.
12
12
 
13
- Most subcommands are invoked automatically by Claude Code hooks
14
- configured in .claude/settings.local.json. The 'enable' and
15
- 'disable' subcommands are user-facing.
13
+ Most subcommands are invoked automatically by runtime hooks: Claude Code's
14
+ are configured in .claude/settings.local.json; Codex's (codex-policy-check)
15
+ are registered in a Codex config and require trust enrollment. The 'enable'
16
+ and 'disable' subcommands are user-facing.
16
17
  """
17
18
  from forge.core.logging import configure_debug_logging
18
19
 
@@ -0,0 +1,129 @@
1
+ """Parser for Codex's apply_patch envelope (PreToolUse ``tool_input.command``).
2
+
3
+ System boundary: the patch text arrives in a Codex hook payload (external data),
4
+ so the parser is strict but the caller fails open -- ``None`` means "not a patch
5
+ Forge can reason about", and the hook allows the action rather than guessing.
6
+ Codex's own apply_patch rejects input outside this grammar, so failing open on
7
+ malformed text converges with native behavior.
8
+
9
+ Grammar (codex-cli 0.138.0; witness fixture in ``tests/fixtures/codex/hooks/``)::
10
+
11
+ *** Begin Patch
12
+ *** Add File: <path> | *** Update File: <path> | *** Delete File: <path>
13
+ *** Move to: <path> (only immediately after an Update header)
14
+ <body lines prefixed +, -, space, or @@; blank lines are context>
15
+ *** End of File (tolerated inside add/update sections)
16
+ *** End Patch
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ from dataclasses import dataclass, field
22
+ from typing import Literal
23
+
24
+ from forge.policy.types import extract_added_lines
25
+
26
+ PatchOpKind = Literal["add", "update", "delete"]
27
+
28
+ _BEGIN = "*** Begin Patch"
29
+ _END = "*** End Patch"
30
+ _EOF_MARKER = "*** End of File"
31
+ _MOVE_TO = "*** Move to: "
32
+ _HEADERS: tuple[tuple[str, PatchOpKind], ...] = (
33
+ ("*** Add File: ", "add"),
34
+ ("*** Update File: ", "update"),
35
+ ("*** Delete File: ", "delete"),
36
+ )
37
+
38
+
39
+ @dataclass(frozen=True)
40
+ class PatchFileOp:
41
+ """One file operation parsed from an apply_patch envelope.
42
+
43
+ ``path`` is the post-op path (the "Move to" target when present) -- policies
44
+ judge where content lands, not where it came from. The pre-move path is
45
+ recoverable from ``raw_section``.
46
+ """
47
+
48
+ kind: PatchOpKind
49
+ path: str
50
+ move_to: str | None
51
+ added_content: str # introduced lines ("" for delete)
52
+ raw_section: str # verbatim header + body (raw_diff source)
53
+
54
+
55
+ @dataclass
56
+ class _Section:
57
+ kind: PatchOpKind
58
+ path: str
59
+ move_to: str | None = None
60
+ header_lines: list[str] = field(default_factory=list)
61
+ body: list[str] = field(default_factory=list)
62
+
63
+ def finalize(self) -> PatchFileOp:
64
+ body = "\n".join(self.body)
65
+ return PatchFileOp(
66
+ kind=self.kind,
67
+ path=self.move_to or self.path,
68
+ move_to=self.move_to,
69
+ added_content="" if self.kind == "delete" else extract_added_lines(body),
70
+ raw_section="\n".join(self.header_lines + self.body),
71
+ )
72
+
73
+
74
+ def parse_apply_patch(command: str) -> list[PatchFileOp] | None:
75
+ """Parse an apply_patch envelope into per-file operations.
76
+
77
+ Returns None for anything outside the known grammar (caller fails open);
78
+ an empty envelope (Begin + End only) returns [].
79
+ """
80
+ lines = [line.rstrip("\r") for line in command.split("\n")]
81
+ while lines and not lines[0].strip():
82
+ lines.pop(0)
83
+ while lines and not lines[-1].strip():
84
+ lines.pop()
85
+ if not lines or lines[0] != _BEGIN or lines[-1] != _END or len(lines) < 2:
86
+ return None
87
+
88
+ ops: list[PatchFileOp] = []
89
+ current: _Section | None = None
90
+
91
+ for line in lines[1:-1]:
92
+ header = _match_header(line)
93
+ if header is not None:
94
+ kind, path = header
95
+ if not path:
96
+ return None
97
+ if current is not None:
98
+ ops.append(current.finalize())
99
+ current = _Section(kind=kind, path=path, header_lines=[line])
100
+ elif line.startswith(_MOVE_TO):
101
+ # Only valid immediately after an Update header (no body yet, one move max).
102
+ target = line[len(_MOVE_TO) :].strip()
103
+ if current is None or current.kind != "update" or current.body or current.move_to or not target:
104
+ return None
105
+ current.move_to = target
106
+ current.header_lines.append(line)
107
+ elif line == _EOF_MARKER:
108
+ if current is None or current.kind == "delete":
109
+ return None
110
+ current.body.append(line) # kept verbatim in raw_section; extract_added_lines ignores it
111
+ elif current is None:
112
+ return None # body line before any section header
113
+ elif current.kind == "delete":
114
+ return None # Delete sections are bodyless in the grammar
115
+ elif line == "" or line.startswith(("+", "-", " ", "@@")):
116
+ current.body.append(line)
117
+ else:
118
+ return None
119
+
120
+ if current is not None:
121
+ ops.append(current.finalize())
122
+ return ops
123
+
124
+
125
+ def _match_header(line: str) -> tuple[PatchOpKind, str] | None:
126
+ for prefix, kind in _HEADERS:
127
+ if line.startswith(prefix):
128
+ return kind, line[len(prefix) :].strip()
129
+ return None