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.
- {multi_forge-0.7.0 → multi_forge-0.7.1}/PKG-INFO +5 -5
- {multi_forge-0.7.0 → multi_forge-0.7.1}/README.md +4 -4
- {multi_forge-0.7.0 → multi_forge-0.7.1}/pyproject.toml +1 -1
- multi_forge-0.7.1/src/forge/__init__.py +11 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/registry.py +20 -22
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/codex_policy.py +2 -10
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/commands.py +154 -194
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/direct_commands.py +86 -250
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/logs.py +5 -3
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/policy.py +118 -298
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/runtime.py +3 -3
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/search.py +98 -33
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session.py +5 -100
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_fork.py +19 -77
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_lifecycle.py +21 -72
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_manage.py +14 -11
- multi_forge-0.7.1/src/forge/cli/session_supervisor_options.py +102 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/throttle.py +2 -13
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/loader.py +8 -27
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/credentials_file.py +6 -18
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/claude_session.py +24 -23
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_session.py +6 -7
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/gc.py +1 -1
- multi_forge-0.7.1/src/forge/core/ops/policy.py +429 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/session.py +102 -3
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/session_context.py +2 -7
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/registry.py +13 -32
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime_vocab.py +1 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/__init__.py +14 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/io.py +31 -2
- {multi_forge-0.7.0/src/forge/proxy → multi_forge-0.7.1/src/forge/core/state}/retention.py +1 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/timestamps.py +5 -0
- multi_forge-0.7.1/src/forge/core/state/versioned_store.py +59 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/downstream.py +14 -24
- multi_forge-0.7.1/src/forge/core/telemetry/jsonl_io.py +37 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/upstream.py +13 -23
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/__init__.py +0 -2
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/ledger.py +16 -68
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/vocabulary.py +3 -5
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/installer.py +2 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/models.py +0 -9
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/tracking.py +24 -19
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/base.py +9 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/shadow.py +1 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/shadow_runner.py +1 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/supervisor.py +3 -10
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/audit_logger.py +4 -7
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxies.py +31 -19
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/utils.py +1 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/runtime_config.py +4 -18
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/__init__.py +8 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/bm25_store.py +16 -22
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/content_store.py +16 -22
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/exceptions.py +25 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/index_state.py +17 -22
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/store.py +19 -19
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/active.py +4 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/relocate.py +5 -15
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/consumer_lanes.py +3 -3
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/hooks/session_start.py +7 -10
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/index.py +8 -19
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/manager.py +15 -27
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/memory_writer.py +3 -11
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/models.py +6 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/shadow_curation.py +2 -7
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/transfer.py +1 -1
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/17-info.md +1 -1
- multi_forge-0.7.0/src/forge/__init__.py +0 -3
- {multi_forge-0.7.0 → multi_forge-0.7.1}/.gitignore +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/LICENSE +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/NOTICE +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/agents/.gitkeep +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/commands/.gitkeep +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/adapters/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/adapters/litellm.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/creation.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/remote/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/remote/base.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/remote/openrouter.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/backend/sources.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/activity.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/auth.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/backend.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/claude.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/codex.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/config_cmd.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/consumer_lane_freeze.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/editor.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/extensions.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/gc.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/guards.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/_group.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/_helpers.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/codex_patch.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/codex_transfer.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/install.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/policy.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/protocols.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/read_hygiene.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/hooks/verification.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/main.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/memory.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/memory_report.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/memory_writer.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/model.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/output.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/proxy.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/proxy_audit.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/proxy_costs.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_codex.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_lane.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_memory.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_model_pin.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_resume_modes.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_rewind.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/session_routing.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/status_line.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/context.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/names.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/palette.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/statusline/registry.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/telemetry.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/trace.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/transfer.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/cli/workflow.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/dataclass_utils.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/backends/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/backends/litellm.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/anthropic-passthrough.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/codex-responses-local.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-anthropic.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-gemini.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-gemini.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-minimax.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/config/schema.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/capabilities.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/protocols.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/secrets.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/auth/template_secrets.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/backend_dependency.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/credential_registry.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/model_catalog.yaml +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/effort.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/_lifecycle.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/claude.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/codex.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/codex_stream.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/invoker/types.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/lanes.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/base.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/litellm.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/openai_compat.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/clients/openrouter.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/credentials.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/detection.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/errors.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/protocols.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/llm/types.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/logging.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/models/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/models/catalog.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/models/types.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/naming.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/backend_reconcile.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_bridge.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_enrollment.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/codex_interactive.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/context.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/provider_trace.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/proxy.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/resolution.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/transfer.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/ops/usage_summary.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/paths.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/process.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/provider_types.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/cost_tracking.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/env.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/headless_json.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/proxy.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/routing.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/session_runner.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/structured_output.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/tagger.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/reactive/throttle.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/run_id.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/codex_preflight.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/codex_preflight_cache.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/runtime/codex_rollouts.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/exceptions.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/state/lock.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/caps.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/telemetry/vocabulary.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/transcript.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/typing_helpers.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/billing.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/correlation.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/emit.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/usage/measurement.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/workqueue/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/workqueue/queue.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/core/workqueue/types.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/cli.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/codex_hooks.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/exceptions.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/hooks.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/preset.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/settings_merge.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/install/version.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/coding_standards.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/registry.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/deterministic/tdd.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/engine.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/protocols.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/queries.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/plan_check.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/semantic/verdict.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/store.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/supervisor_lane_degrade.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/config.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/handlers.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/team/prompts.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/types.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/branches.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/config.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/divergence.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/policy.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/policy/workflow/stages.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/base_client.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/client_adapter.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/client_factory.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/converters.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/cost_logger.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/cost_tracker.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/data_models.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/error_hints.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/intercept.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/metrics.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/model_spec.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/passthrough.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/provider_trace_logger.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxy_identity.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxy_orchestrator.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/proxy_startup.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/responses_ingress.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/responses_passthrough.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/server.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/proxy/stream_relay.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/adversarial.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/consensus.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/engine.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/models.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/code_consensus_evaluation.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/code_debate_evaluation.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview-performance.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview-quick.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview-security.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/codereview.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/consensus_evaluation.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/debate_evaluation.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/docreview-quick.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/docreview.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/resources/thinkdeep.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/routing.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/review/synthesis.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/engine.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/extractor.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/search/tokenizer.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/addendum.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/artifacts.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/cleanup.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/invoke.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/claude/paths.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/cleanup.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/codex_handoff.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/codex_invoke.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/config.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/context_limit.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/direct_model.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/effective.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/exceptions.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/hooks/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/hooks/models.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/identity.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/launch.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/launch_confirmation.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/memory_inheritance.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/model_pin.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/overrides.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/passport.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/plan_resolution.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/prev_sessions.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/project_memory.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/rewind.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/store.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/validation.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/cleanup.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/config_copy.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/session/worktree/create.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/__init__.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/container.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/docker.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/forge/sidecar/secrets.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/analyze/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/challenge/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/consensus/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/consensus/resources/synthesis.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/debate/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/panel/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/panel/resources/synthesis.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/0-enable.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/10-resume.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/11-config.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/12-search.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/13-policy.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/15-skills.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/16-memory.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/18-disable.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/2-extension.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/20-cleanup.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/3-authentication.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/4-proxy.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/5-session.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/6-hook.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/7-costs.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/8-status-line.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist/9-direct-commands.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/checklist.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/resources/report-template.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/scripts/start-container.sh +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/qa/scripts/walkthrough-state.py +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/claude-4.6.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/claude-4.8.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/gemini-3.1.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/gpt-5.5.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/references/skills-writing-guide.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code-anthropic.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code-gemini.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code-openai.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review/resources/code.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs-anthropic.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs-gemini.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs-openai.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/review-docs/resources/docs.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/smoke-test/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code-anthropic.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code-gemini.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code-openai.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/code.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs-anthropic.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs-gemini.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs-openai.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/understand/resources/docs.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/SKILL.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/resources/checklist.md +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
- {multi_forge-0.7.0 → multi_forge-0.7.1}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
- {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.
|
|
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
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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)
|
|
@@ -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
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
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
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
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
|
-
|
|
1723
|
-
|
|
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("
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
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")
|