multi-forge 0.2.1__tar.gz → 0.4.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.
- {multi_forge-0.2.1 → multi_forge-0.4.0}/PKG-INFO +15 -9
- {multi_forge-0.2.1 → multi_forge-0.4.0}/README.md +14 -8
- {multi_forge-0.2.1 → multi_forge-0.4.0}/pyproject.toml +7 -6
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/__init__.py +1 -1
- multi_forge-0.4.0/src/forge/cli/activity.py +151 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/auth.py +1 -3
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/backend.py +23 -13
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/claude.py +10 -11
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/config_cmd.py +118 -14
- multi_forge-0.4.0/src/forge/cli/editor.py +44 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/extensions.py +24 -21
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/gc.py +4 -3
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/guards.py +4 -3
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/commands.py +30 -55
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/direct_commands.py +116 -47
- multi_forge-0.4.0/src/forge/cli/hooks/policy.py +212 -0
- multi_forge-0.4.0/src/forge/cli/hooks/protocols.py +53 -0
- multi_forge-0.4.0/src/forge/cli/launch_confirmation.py +134 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/logs.py +35 -13
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/main.py +74 -9
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/memory.py +357 -497
- multi_forge-0.2.1/src/forge/cli/session_handoff.py → multi_forge-0.4.0/src/forge/cli/memory_report.py +31 -29
- multi_forge-0.2.1/src/forge/cli/handoff.py → multi_forge-0.4.0/src/forge/cli/memory_writer.py +23 -18
- multi_forge-0.4.0/src/forge/cli/output.py +92 -0
- multi_forge-0.2.1/src/forge/cli/guard.py → multi_forge-0.4.0/src/forge/cli/policy.py +127 -139
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/proxy.py +148 -77
- multi_forge-0.4.0/src/forge/cli/proxy_audit.py +253 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/proxy_costs.py +203 -55
- multi_forge-0.4.0/src/forge/cli/runtime.py +105 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/search.py +22 -20
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/session.py +23 -46
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/session_fork.py +266 -50
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/session_lifecycle.py +293 -175
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/session_manage.py +94 -30
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/status_line.py +490 -161
- multi_forge-0.4.0/src/forge/cli/statusline/__init__.py +9 -0
- multi_forge-0.4.0/src/forge/cli/statusline/context.py +144 -0
- multi_forge-0.4.0/src/forge/cli/statusline/names.py +61 -0
- multi_forge-0.4.0/src/forge/cli/statusline/palette.py +149 -0
- multi_forge-0.4.0/src/forge/cli/statusline/registry.py +379 -0
- multi_forge-0.4.0/src/forge/cli/statusline/throttle.py +172 -0
- multi_forge-0.4.0/src/forge/cli/transfer.py +186 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/workflow.py +70 -13
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/backends/litellm.yaml +12 -0
- multi_forge-0.4.0/src/forge/config/defaults/templates/anthropic-passthrough.yaml +30 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-anthropic-local.yaml +1 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-anthropic.yaml +3 -3
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-gemini.yaml +1 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-anthropic.yaml +3 -3
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-gemini-flash.yaml +4 -4
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-gemini.yaml +1 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-minimax.yaml +6 -4
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/loader.py +6 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/schema.py +245 -11
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/auth/capabilities.py +2 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/auth/template_secrets.py +23 -8
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/data/model_catalog.yaml +95 -3
- multi_forge-0.4.0/src/forge/core/invoker/__init__.py +23 -0
- multi_forge-0.4.0/src/forge/core/invoker/claude.py +457 -0
- multi_forge-0.4.0/src/forge/core/invoker/types.py +111 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/__init__.py +1 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/clients/litellm.py +58 -7
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/clients/openai_compat.py +26 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/clients/openrouter.py +8 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/credentials.py +20 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/types.py +6 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/models/__init__.py +0 -11
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/gc.py +23 -12
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/resolution.py +1 -1
- multi_forge-0.4.0/src/forge/core/ops/transfer.py +265 -0
- multi_forge-0.4.0/src/forge/core/ops/usage_summary.py +364 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/__init__.py +17 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/cost_tracking.py +72 -54
- multi_forge-0.4.0/src/forge/core/reactive/env.py +363 -0
- multi_forge-0.4.0/src/forge/core/reactive/headless_json.py +133 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/proxy.py +1 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/routing.py +2 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/session_runner.py +122 -25
- multi_forge-0.4.0/src/forge/core/reactive/structured_output.py +153 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/tagger.py +35 -4
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/reactive/throttle.py +1 -1
- multi_forge-0.4.0/src/forge/core/runtime/__init__.py +27 -0
- multi_forge-0.4.0/src/forge/core/runtime/registry.py +211 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/state/__init__.py +8 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/state/io.py +18 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/transcript.py +1 -1
- multi_forge-0.4.0/src/forge/core/usage/__init__.py +56 -0
- multi_forge-0.4.0/src/forge/core/usage/billing.py +28 -0
- multi_forge-0.4.0/src/forge/core/usage/correlation.py +91 -0
- multi_forge-0.4.0/src/forge/core/usage/emit.py +440 -0
- multi_forge-0.4.0/src/forge/core/usage/ledger.py +306 -0
- multi_forge-0.4.0/src/forge/core/usage/vocabulary.py +70 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/workqueue/queue.py +12 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/__init__.py +3 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/exceptions.py +3 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/installer.py +6 -6
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/preset.py +2 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/tracking.py +2 -2
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/__init__.py +2 -2
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/deterministic/__init__.py +3 -3
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/deterministic/base.py +1 -1
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/deterministic/coding_standards.py +26 -14
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/deterministic/registry.py +5 -5
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/deterministic/tdd.py +2 -2
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/engine.py +3 -3
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/protocols.py +1 -1
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/queries.py +2 -2
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/semantic/__init__.py +2 -2
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/semantic/promotion.py +2 -2
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/semantic/supervisor.py +47 -26
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/semantic/verdict.py +1 -1
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/store.py +1 -1
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/team/handlers.py +22 -6
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/types.py +9 -3
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/workflow/branches.py +3 -3
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/workflow/divergence.py +1 -1
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/workflow/policy.py +4 -4
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/workflow/stages.py +2 -2
- multi_forge-0.4.0/src/forge/proxy/audit_logger.py +464 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/client_adapter.py +17 -7
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/converters.py +7 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/cost_logger.py +35 -10
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/cost_tracker.py +31 -28
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/data_models.py +6 -1
- multi_forge-0.4.0/src/forge/proxy/intercept.py +314 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/metrics.py +38 -8
- multi_forge-0.4.0/src/forge/proxy/passthrough.py +281 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/proxies.py +1 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/proxy_orchestrator.py +146 -8
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/server.py +586 -67
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/utils.py +27 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/adversarial.py +4 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/consensus.py +4 -0
- multi_forge-0.4.0/src/forge/review/engine.py +357 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/models.py +14 -9
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/runtime_config.py +141 -15
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/claude/__init__.py +14 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/claude/invoke.py +21 -4
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/claude/paths.py +14 -4
- multi_forge-0.4.0/src/forge/session/claude/relocate.py +171 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/effective.py +7 -3
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/manager.py +355 -84
- multi_forge-0.4.0/src/forge/session/memory_inheritance.py +78 -0
- multi_forge-0.2.1/src/forge/session/handoff_agent.py → multi_forge-0.4.0/src/forge/session/memory_writer.py +80 -71
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/models.py +52 -26
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/passport.py +29 -57
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/plan_resolution.py +1 -1
- multi_forge-0.4.0/src/forge/session/prev_sessions.py +236 -0
- multi_forge-0.4.0/src/forge/session/project_memory.py +284 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/shadow_curation.py +48 -27
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/store.py +32 -0
- multi_forge-0.2.1/src/forge/session/handoff.py → multi_forge-0.4.0/src/forge/session/transfer.py +309 -87
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/sidecar/container.py +84 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/panel/SKILL.md +4 -4
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/SKILL.md +44 -44
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/10-resume.md +66 -5
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/11-config.md +4 -4
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/12-search.md +3 -3
- multi_forge-0.2.1/src/skills/qa/resources/checklist/13-guard.md → multi_forge-0.4.0/src/skills/qa/resources/checklist/13-policy.md +37 -31
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/15-skills.md +6 -6
- multi_forge-0.2.1/src/skills/qa/resources/checklist/16-handoff.md → multi_forge-0.4.0/src/skills/qa/resources/checklist/16-memory.md +48 -58
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/17-info.md +16 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/18-disable.md +10 -10
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/20-cleanup.md +5 -0
- multi_forge-0.2.1/src/skills/qa/resources/checklist/3-auth.md → multi_forge-0.4.0/src/skills/qa/resources/checklist/3-authentication.md +9 -22
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/4-proxy.md +89 -21
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/5-session.md +172 -54
- multi_forge-0.2.1/src/skills/qa/resources/checklist/6-hooks.md → multi_forge-0.4.0/src/skills/qa/resources/checklist/6-hook.md +1 -1
- multi_forge-0.4.0/src/skills/qa/resources/checklist/7-costs.md +464 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/8-status-line.md +110 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/9-direct-commands.md +40 -21
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist.md +23 -16
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/report-template.md +2 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/scripts/start-container.sh +37 -18
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/scripts/walkthrough-state.py +3 -5
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/SKILL.md +5 -5
- multi_forge-0.2.1/src/skills/review/references/claude-4.7.md → multi_forge-0.4.0/src/skills/review/references/claude-4.8.md +85 -69
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/references/gemini-3.1.md +2 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/references/skills-writing-guide.md +1 -1
- multi_forge-0.4.0/src/skills/review/resources/code.md +1 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review-docs/SKILL.md +2 -2
- multi_forge-0.4.0/src/skills/review-docs/resources/docs.md +1 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/understand/SKILL.md +2 -2
- multi_forge-0.4.0/src/skills/understand/resources/code.md +163 -0
- multi_forge-0.4.0/src/skills/understand/resources/docs.md +177 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/walkthrough/SKILL.md +2 -2
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/walkthrough/resources/checklist.md +25 -22
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/walkthrough/scripts/walkthrough-state.py +3 -5
- multi_forge-0.2.1/src/forge/cli/hooks/policy.py +0 -151
- multi_forge-0.2.1/src/forge/cli/session_memory.py +0 -38
- multi_forge-0.2.1/src/forge/core/data/pricing.yaml +0 -140
- multi_forge-0.2.1/src/forge/core/models/pricing.py +0 -165
- multi_forge-0.2.1/src/forge/core/reactive/env.py +0 -180
- multi_forge-0.2.1/src/forge/core/reactive/structured_output.py +0 -62
- multi_forge-0.2.1/src/forge/review/engine.py +0 -358
- multi_forge-0.2.1/src/forge/session/memory_inheritance.py +0 -252
- multi_forge-0.2.1/src/forge/session/prev_sessions.py +0 -128
- multi_forge-0.2.1/src/skills/qa/resources/checklist/7-costs.md +0 -309
- multi_forge-0.2.1/src/skills/review/resources/code-anthropic.md +0 -1
- multi_forge-0.2.1/src/skills/review-docs/resources/docs-anthropic.md +0 -1
- multi_forge-0.2.1/src/skills/understand/resources/code-anthropic.md +0 -1
- multi_forge-0.2.1/src/skills/understand/resources/docs-anthropic.md +0 -1
- {multi_forge-0.2.1 → multi_forge-0.4.0}/.gitignore +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/LICENSE +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/NOTICE +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/agents/.gitkeep +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/commands/.gitkeep +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/backend/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/backend/adapters/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/backend/adapters/litellm.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/backend/creation.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/backend/registry.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/_group.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/_helpers.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/install.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/read_hygiene.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/hooks/verification.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/cli/session_addendum.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/dataclass_utils.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/backends/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-gemini-flash-local.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-gemini-local.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-gemini-test.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-openai-codex-local.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-openai-local.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/litellm-openai.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-deepseek.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-glm.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-kimi.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-openai-codex.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-openai.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/config/defaults/templates/openrouter-qwen.yaml +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/auth/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/auth/credentials_file.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/auth/protocols.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/auth/secrets.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/data/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/data/system_prompt_addendums/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/data/system_prompt_addendums/gemini.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/data/system_prompt_addendums/openai.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/clients/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/clients/base.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/detection.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/errors.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/llm/protocols.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/logging.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/models/catalog.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/models/types.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/naming.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/context.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/proxy.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/session.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/ops/session_context.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/paths.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/process.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/state/exceptions.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/state/lock.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/state/timestamps.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/typing_helpers.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/workqueue/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/core/workqueue/types.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/cli.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/hooks.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/models.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/settings_merge.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/install/version.py +0 -0
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/team/__init__.py +0 -0
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/team/config.py +0 -0
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/team/prompts.py +0 -0
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/workflow/__init__.py +0 -0
- {multi_forge-0.2.1/src/forge/guard → multi_forge-0.4.0/src/forge/policy}/workflow/config.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/base_client.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/client_factory.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/error_hints.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/model_spec.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/proxy_identity.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/proxy/proxy_startup.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/codereview-performance.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/codereview-quick.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/codereview-security.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/codereview.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/docreview-quick.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/docreview.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/resources/thinkdeep.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/routing.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/review/synthesis.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/bm25_store.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/content_store.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/engine.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/exceptions.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/extractor.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/index_state.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/store.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/search/tokenizer.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/active.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/artifacts.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/claude/cleanup.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/cleanup.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/config.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/direct_model.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/exceptions.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/hooks/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/hooks/models.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/hooks/session_start.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/identity.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/index.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/overrides.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/validation.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/worktree/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/worktree/cleanup.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/worktree/config_copy.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/session/worktree/create.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/sidecar/__init__.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/sidecar/docker.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/forge/sidecar/secrets.py +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/analyze/SKILL.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/challenge/SKILL.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/consensus/SKILL.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/consensus/resources/code_consensus_evaluation.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/consensus/resources/consensus_evaluation.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/consensus/resources/synthesis.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/debate/SKILL.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/debate/resources/code_debate_evaluation.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/debate/resources/debate_evaluation.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/panel/resources/synthesis.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/0-enable.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/1-preflight.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/14-workflow.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/qa/resources/checklist/19-uninstall.md +0 -0
- /multi_forge-0.2.1/src/skills/qa/resources/checklist/2-extensions.md → /multi_forge-0.4.0/src/skills/qa/resources/checklist/2-extension.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/references/claude-4.6.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/references/gpt-5.5.md +0 -0
- /multi_forge-0.2.1/src/skills/review/resources/code.md → /multi_forge-0.4.0/src/skills/review/resources/code-anthropic.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/resources/code-gemini.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review/resources/code-openai.md +0 -0
- /multi_forge-0.2.1/src/skills/review-docs/resources/docs.md → /multi_forge-0.4.0/src/skills/review-docs/resources/docs-anthropic.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review-docs/resources/docs-gemini.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/review-docs/resources/docs-openai.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/smoke-test/SKILL.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/smoke-test/scripts/smoke-test.sh +0 -0
- /multi_forge-0.2.1/src/skills/understand/resources/code.md → /multi_forge-0.4.0/src/skills/understand/resources/code-anthropic.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/understand/resources/code-gemini.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/understand/resources/code-openai.md +0 -0
- /multi_forge-0.2.1/src/skills/understand/resources/docs.md → /multi_forge-0.4.0/src/skills/understand/resources/docs-anthropic.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/understand/resources/docs-gemini.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/understand/resources/docs-openai.md +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/walkthrough/scripts/run-in-repo.sh +0 -0
- {multi_forge-0.2.1 → multi_forge-0.4.0}/src/skills/walkthrough/scripts/setup-test-repo.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-forge
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.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
|
|
@@ -41,9 +41,13 @@ Description-Content-Type: text/markdown
|
|
|
41
41
|
|
|
42
42
|
# Multi-Forge
|
|
43
43
|
|
|
44
|
+
<p align="left">
|
|
45
|
+
<img src="assets/logo.jpg" alt="Dusk" width=240">
|
|
46
|
+
</p>
|
|
47
|
+
|
|
44
48
|
[](https://pypi.org/project/multi-forge/)
|
|
45
49
|
[](https://pypi.org/project/multi-forge/)
|
|
46
|
-
[](LICENSE)
|
|
47
51
|
|
|
48
52
|
> [!WARNING]
|
|
49
53
|
> **Research Preview** -- Forge is under active development. APIs, commands, and file formats may change without notice
|
|
@@ -92,7 +96,7 @@ Running `claude` directly bypasses session tracking. When you launch through For
|
|
|
92
96
|
| Hook-driven artifacts | No | Yes -- plan snapshots, transcript capture |
|
|
93
97
|
| Policy enforcement | No | Yes -- TDD, coding standards, supervisor |
|
|
94
98
|
| Search across sessions | No | Yes -- `forge search` indexes transcripts |
|
|
95
|
-
|
|
|
99
|
+
| Project memory | No | Yes -- passported docs auto-updated on exit |
|
|
96
100
|
|
|
97
101
|
Even without a proxy, `forge session start` gives you session tracking, hooks, and the status line (direct mode is the
|
|
98
102
|
default). The proxy adds multi-model routing on top. (`forge claude start` is also available as a bare launcher with
|
|
@@ -205,9 +209,10 @@ inside Claude Code for an interactive walkthrough.
|
|
|
205
209
|
| ---------------------- | -------------------------------------------- |
|
|
206
210
|
| `forge claude` | Bare launch, settings preset management |
|
|
207
211
|
| `forge session` | Named sessions, worktrees, resume, fork |
|
|
212
|
+
| `forge memory` | Project memory passports, shadow proposals |
|
|
208
213
|
| `forge proxy` | Model routing, templates, tier mappings |
|
|
209
214
|
| `forge authentication` | Credential management (`credentials.yaml`) |
|
|
210
|
-
| `forge
|
|
215
|
+
| `forge policy` | Policy enforcement, plan supervision |
|
|
211
216
|
| `forge workflow` | Workflow runners (panel, analyze, debate) |
|
|
212
217
|
| `forge search` | Transcript search across sessions |
|
|
213
218
|
| `forge config` | Runtime preferences (`~/.forge/config.yaml`) |
|
|
@@ -218,11 +223,12 @@ Run `forge <command> --help` for details on any command.
|
|
|
218
223
|
|
|
219
224
|
## Documentation
|
|
220
225
|
|
|
221
|
-
| Audience | Location | Contents
|
|
222
|
-
| ---------------- | ---------------------------------- |
|
|
223
|
-
| **Users** | [docs/end-user/](docs/end-user/) | Tour, guides for sessions, proxies, policies, ...
|
|
224
|
-
| **Developers** | [docs/developer/](docs/developer/) | Setup, coding standards, testing guidelines
|
|
225
|
-
| **Architecture** | [docs/design.md](docs/design.md) | System narrative, data flow, invariants
|
|
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 |
|
|
226
232
|
|
|
227
233
|
## Contributing
|
|
228
234
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
# Multi-Forge
|
|
2
2
|
|
|
3
|
+
<p align="left">
|
|
4
|
+
<img src="assets/logo.jpg" alt="Dusk" width=240">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
3
7
|
[](https://pypi.org/project/multi-forge/)
|
|
4
8
|
[](https://pypi.org/project/multi-forge/)
|
|
5
|
-
[](LICENSE)
|
|
6
10
|
|
|
7
11
|
> [!WARNING]
|
|
8
12
|
> **Research Preview** -- Forge is under active development. APIs, commands, and file formats may change without notice
|
|
@@ -51,7 +55,7 @@ Running `claude` directly bypasses session tracking. When you launch through For
|
|
|
51
55
|
| Hook-driven artifacts | No | Yes -- plan snapshots, transcript capture |
|
|
52
56
|
| Policy enforcement | No | Yes -- TDD, coding standards, supervisor |
|
|
53
57
|
| Search across sessions | No | Yes -- `forge search` indexes transcripts |
|
|
54
|
-
|
|
|
58
|
+
| Project memory | No | Yes -- passported docs auto-updated on exit |
|
|
55
59
|
|
|
56
60
|
Even without a proxy, `forge session start` gives you session tracking, hooks, and the status line (direct mode is the
|
|
57
61
|
default). The proxy adds multi-model routing on top. (`forge claude start` is also available as a bare launcher with
|
|
@@ -164,9 +168,10 @@ inside Claude Code for an interactive walkthrough.
|
|
|
164
168
|
| ---------------------- | -------------------------------------------- |
|
|
165
169
|
| `forge claude` | Bare launch, settings preset management |
|
|
166
170
|
| `forge session` | Named sessions, worktrees, resume, fork |
|
|
171
|
+
| `forge memory` | Project memory passports, shadow proposals |
|
|
167
172
|
| `forge proxy` | Model routing, templates, tier mappings |
|
|
168
173
|
| `forge authentication` | Credential management (`credentials.yaml`) |
|
|
169
|
-
| `forge
|
|
174
|
+
| `forge policy` | Policy enforcement, plan supervision |
|
|
170
175
|
| `forge workflow` | Workflow runners (panel, analyze, debate) |
|
|
171
176
|
| `forge search` | Transcript search across sessions |
|
|
172
177
|
| `forge config` | Runtime preferences (`~/.forge/config.yaml`) |
|
|
@@ -177,11 +182,12 @@ Run `forge <command> --help` for details on any command.
|
|
|
177
182
|
|
|
178
183
|
## Documentation
|
|
179
184
|
|
|
180
|
-
| Audience | Location | Contents
|
|
181
|
-
| ---------------- | ---------------------------------- |
|
|
182
|
-
| **Users** | [docs/end-user/](docs/end-user/) | Tour, guides for sessions, proxies, policies, ...
|
|
183
|
-
| **Developers** | [docs/developer/](docs/developer/) | Setup, coding standards, testing guidelines
|
|
184
|
-
| **Architecture** | [docs/design.md](docs/design.md) | System narrative, data flow, invariants
|
|
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 |
|
|
185
191
|
|
|
186
192
|
## Contributing
|
|
187
193
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "multi-forge"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.4.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"
|
|
@@ -104,10 +104,9 @@ markers = [
|
|
|
104
104
|
asyncio_mode = "auto"
|
|
105
105
|
|
|
106
106
|
[tool.mypy]
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# collision; the walkthrough/ copy is the one type-checked.
|
|
107
|
+
# Same-named standalone scripts in separate skill directories collide in mypy's
|
|
108
|
+
# module map during broad scans. Keep this exclusion local to the duplicate
|
|
109
|
+
# script name; it is not a source-of-truth relationship between the skills.
|
|
111
110
|
exclude = [
|
|
112
111
|
'src/skills/qa/scripts/walkthrough-state\.py$',
|
|
113
112
|
]
|
|
@@ -123,10 +122,11 @@ disallow_untyped_calls = false
|
|
|
123
122
|
constraint-dependencies = [
|
|
124
123
|
"filelock>=3.20.3",
|
|
125
124
|
"virtualenv>=20.36.1",
|
|
126
|
-
"aiohttp>=3.
|
|
125
|
+
"aiohttp>=3.14.0",
|
|
127
126
|
"cryptography>=46.0.7",
|
|
128
127
|
"python-multipart>=0.0.26",
|
|
129
128
|
"Pygments>=2.20.0",
|
|
129
|
+
"starlette>=1.0.1",
|
|
130
130
|
]
|
|
131
131
|
# litellm pins fastapi-sso<0.17.0 but 0.16.x has a CSRF vulnerability.
|
|
132
132
|
# Forge doesn't use fastapi-sso directly; override to force the patched version.
|
|
@@ -149,6 +149,7 @@ dev = [
|
|
|
149
149
|
"types-PyYAML>=6.0.0",
|
|
150
150
|
"types-requests>=2.31.0",
|
|
151
151
|
"python-dotenv>=1.2.1",
|
|
152
|
+
"httpx2>=2.3.0",
|
|
152
153
|
]
|
|
153
154
|
provider-check = [
|
|
154
155
|
"anthropic>=0.100.0",
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
"""``forge activity`` — per-session Forge *automation* activity (supervisor, memory
|
|
2
|
+
writer, workflow verbs) + policy decisions. NOT your full interactive Claude usage.
|
|
3
|
+
|
|
4
|
+
Reads the two already-captured planes (usage ledger + ``confirmed.policy.decisions``)
|
|
5
|
+
via :func:`forge.core.ops.usage_summary.build_session_activity_summary` and renders a
|
|
6
|
+
table. Cost is reported-or-estimated (best-effort attribution) — ``forge proxy costs show``
|
|
7
|
+
stays the authoritative spend view.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
from dataclasses import asdict
|
|
15
|
+
from datetime import datetime, timedelta, timezone
|
|
16
|
+
|
|
17
|
+
import click
|
|
18
|
+
from rich.console import Console
|
|
19
|
+
from rich.table import Table
|
|
20
|
+
|
|
21
|
+
from forge.cli.output import print_error_with_tip
|
|
22
|
+
from forge.core.ops.session_context import (
|
|
23
|
+
SessionContextError,
|
|
24
|
+
resolve_session_identifier,
|
|
25
|
+
)
|
|
26
|
+
from forge.core.ops.usage_summary import (
|
|
27
|
+
SessionActivitySummary,
|
|
28
|
+
build_session_activity_summary,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
console = Console()
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@click.command("activity")
|
|
35
|
+
@click.argument("session", required=False)
|
|
36
|
+
@click.option("--json", "as_json", is_flag=True, help="Output as JSON")
|
|
37
|
+
@click.option("--days", "-d", type=int, default=30, help="Look back this many days (default: 30)")
|
|
38
|
+
@click.option("--all", "all_time", is_flag=True, help="Report all time (ignore --days)")
|
|
39
|
+
def activity_cmd(session: str | None, as_json: bool, days: int, all_time: bool) -> None:
|
|
40
|
+
"""Show Forge's automation activity for a session: supervisor checks, cost, tokens.
|
|
41
|
+
|
|
42
|
+
This is what Forge did *on top of* your session — the supervisor, memory writer,
|
|
43
|
+
and workflow verbs (panel/debate/...) — plus policy decisions. It is **not** your
|
|
44
|
+
full interactive Claude usage. Reads the usage ledger and the session's
|
|
45
|
+
policy-decision log. Cost is reported-or-estimated (best-effort attribution);
|
|
46
|
+
'forge proxy costs show' is the authoritative spend view.
|
|
47
|
+
|
|
48
|
+
\b
|
|
49
|
+
Examples:
|
|
50
|
+
forge activity # current session ($FORGE_SESSION)
|
|
51
|
+
forge activity planner # a named session (or Claude UUID)
|
|
52
|
+
forge activity --all --json # full history, JSON
|
|
53
|
+
"""
|
|
54
|
+
try:
|
|
55
|
+
session_name, forge_root = resolve_session_identifier(session)
|
|
56
|
+
except SessionContextError as e:
|
|
57
|
+
if as_json:
|
|
58
|
+
click.echo(json.dumps({"error": str(e)}))
|
|
59
|
+
else:
|
|
60
|
+
print_error_with_tip(
|
|
61
|
+
str(e),
|
|
62
|
+
"Run 'forge session list' to see sessions.",
|
|
63
|
+
console=console,
|
|
64
|
+
)
|
|
65
|
+
sys.exit(1)
|
|
66
|
+
|
|
67
|
+
since = None if all_time else datetime.now(timezone.utc) - timedelta(days=days)
|
|
68
|
+
summary = build_session_activity_summary(session_name, forge_root, since=since)
|
|
69
|
+
|
|
70
|
+
if as_json:
|
|
71
|
+
console.print_json(data=asdict(summary))
|
|
72
|
+
return
|
|
73
|
+
_render(summary, days=None if all_time else days)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _render(summary: SessionActivitySummary, *, days: int | None) -> None:
|
|
77
|
+
scope = "all time" if days is None else f"last {days}d"
|
|
78
|
+
if summary.is_empty:
|
|
79
|
+
console.print(f"[dim]No Forge activity for session '{summary.session}' ({scope}).[/dim]")
|
|
80
|
+
return
|
|
81
|
+
|
|
82
|
+
console.print(f"\n[bold]Forge activity — {summary.session}[/bold] [dim]({scope})[/dim]")
|
|
83
|
+
console.print(
|
|
84
|
+
"[dim]Forge automation (supervisor, memory writer, workflow verbs) — "
|
|
85
|
+
"not your full interactive session.[/dim]"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
if summary.commands:
|
|
89
|
+
# The Workers column only earns its width when a fan-out (panel/debate/...) ran;
|
|
90
|
+
# supervisor/memory-writer have no workers, so most sessions skip it.
|
|
91
|
+
show_workers = any(c.workers for c in summary.commands)
|
|
92
|
+
table = Table(show_header=True, header_style="bold", box=None, padding=(0, 2))
|
|
93
|
+
table.add_column("Command", style="cyan")
|
|
94
|
+
table.add_column("Calls", justify="right")
|
|
95
|
+
if show_workers:
|
|
96
|
+
table.add_column("Workers", justify="right", style="dim")
|
|
97
|
+
table.add_column("Errors", justify="right")
|
|
98
|
+
table.add_column("Tokens in/out", justify="right", style="dim")
|
|
99
|
+
table.add_column("Cost", justify="right", style="dim")
|
|
100
|
+
for c in summary.commands:
|
|
101
|
+
errors = f"[red]{c.errors}[/red]" if c.errors else "0"
|
|
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 "-"
|
|
104
|
+
row = [c.command, str(c.calls)]
|
|
105
|
+
if show_workers:
|
|
106
|
+
row.append(str(c.workers) if c.workers else "-")
|
|
107
|
+
row += [errors, tokens, cost]
|
|
108
|
+
table.add_row(*row)
|
|
109
|
+
console.print(table)
|
|
110
|
+
|
|
111
|
+
pol = summary.policy
|
|
112
|
+
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
|
+
)
|
|
117
|
+
for warning in pol.recent_warnings:
|
|
118
|
+
console.print(f" [yellow]•[/yellow] {warning}")
|
|
119
|
+
|
|
120
|
+
if summary.subagents:
|
|
121
|
+
console.print(f"\n[bold]Subagents[/bold]: {summary.subagents}")
|
|
122
|
+
|
|
123
|
+
total_cost = f"~{_fmt_usd(summary.total_cost_micro_usd)}" if summary.total_cost_micro_usd is not None else "n/a"
|
|
124
|
+
console.print(
|
|
125
|
+
f"\n[dim]Total:[/dim] {summary.total_events} events · "
|
|
126
|
+
f"{summary.total_input_tokens}/{summary.total_output_tokens} tok · {total_cost}"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
for note in _footnotes(summary):
|
|
130
|
+
console.print(f"[dim]{note}[/dim]")
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _footnotes(summary: SessionActivitySummary) -> list[str]:
|
|
134
|
+
notes: list[str] = []
|
|
135
|
+
if summary.cost_partial:
|
|
136
|
+
notes.append("cost is best-effort and partial (some calls report no cost)")
|
|
137
|
+
if summary.policy is not None and summary.policy.log_capped:
|
|
138
|
+
notes.append("policy decision log is at capacity — older decisions may not be shown")
|
|
139
|
+
if summary.session_tagging_partial:
|
|
140
|
+
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")
|
|
142
|
+
return notes
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _fmt_usd(micros: int | None) -> str:
|
|
146
|
+
if micros is None:
|
|
147
|
+
return "-"
|
|
148
|
+
dollars = micros / 1_000_000
|
|
149
|
+
if dollars and abs(dollars) < 0.01:
|
|
150
|
+
return f"${dollars:.4f}"
|
|
151
|
+
return f"${dollars:.2f}"
|
|
@@ -408,8 +408,7 @@ def status(profile: str | None) -> None:
|
|
|
408
408
|
help="Profile to remove credentials from (default: 'default' or FORGE_PROFILE)",
|
|
409
409
|
)
|
|
410
410
|
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
411
|
-
|
|
412
|
-
def logout(profile: str | None, yes: bool, force: bool) -> None:
|
|
411
|
+
def logout(profile: str | None, yes: bool) -> None:
|
|
413
412
|
"""Remove stored credentials for a profile.
|
|
414
413
|
|
|
415
414
|
Deletes the profile from ~/.forge/credentials.yaml.
|
|
@@ -421,7 +420,6 @@ def logout(profile: str | None, yes: bool, force: bool) -> None:
|
|
|
421
420
|
forge authentication logout --profile work
|
|
422
421
|
forge authentication logout -y # Skip confirmation
|
|
423
422
|
"""
|
|
424
|
-
yes = yes or force
|
|
425
423
|
profile_name = resolve_profile(profile)
|
|
426
424
|
|
|
427
425
|
if not yes:
|
|
@@ -21,6 +21,7 @@ from forge.backend import BackendManager
|
|
|
21
21
|
from forge.backend.adapters import get_adapter
|
|
22
22
|
from forge.backend.creation import create_backend_config, get_backend_config_path
|
|
23
23
|
from forge.backend.registry import BackendRegistryStore, is_pid_alive
|
|
24
|
+
from forge.cli.output import print_error_with_tip, print_tip
|
|
24
25
|
from forge.core.paths import display_path, get_forge_home
|
|
25
26
|
|
|
26
27
|
|
|
@@ -64,7 +65,7 @@ def list_cmd(as_json: bool) -> None:
|
|
|
64
65
|
|
|
65
66
|
if not backends:
|
|
66
67
|
console.print("No backends found.")
|
|
67
|
-
|
|
68
|
+
print_tip("Run 'forge backend create litellm'.", console=console)
|
|
68
69
|
return
|
|
69
70
|
|
|
70
71
|
table = Table(title="Forge Backends")
|
|
@@ -146,7 +147,7 @@ def show_cmd(backend_id: str, raw: bool) -> None:
|
|
|
146
147
|
console.print(syntax)
|
|
147
148
|
else:
|
|
148
149
|
console.print(f"\n[dim]No config found for adapter '{adapter_type}'.[/dim]")
|
|
149
|
-
|
|
150
|
+
print_tip(f"Run 'forge backend create {adapter_type}'.", blank_before=False, console=console)
|
|
150
151
|
|
|
151
152
|
|
|
152
153
|
@backend.command("create")
|
|
@@ -166,10 +167,13 @@ def create_cmd(adapter: str, config: Path | None) -> None:
|
|
|
166
167
|
|
|
167
168
|
config_path = get_backend_config_path(adapter)
|
|
168
169
|
if config_path.exists():
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
170
|
+
print_error_with_tip(
|
|
171
|
+
f"Backend config already exists: {display_path(config_path)}",
|
|
172
|
+
"Start an instance with:",
|
|
173
|
+
commands=[f"forge backend start {adapter} --port 4000"],
|
|
174
|
+
console=console,
|
|
175
|
+
)
|
|
176
|
+
sys.exit(1)
|
|
173
177
|
|
|
174
178
|
try:
|
|
175
179
|
config_path = create_backend_config(
|
|
@@ -194,9 +198,12 @@ def start_cmd(adapter: str, port: int) -> None:
|
|
|
194
198
|
|
|
195
199
|
config_path = get_backend_config_path(adapter)
|
|
196
200
|
if not config_path.exists():
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
201
|
+
print_error_with_tip(
|
|
202
|
+
f"Backend config not found for '{adapter}'",
|
|
203
|
+
"Create it first:",
|
|
204
|
+
commands=[f"forge backend create {adapter}"],
|
|
205
|
+
console=console,
|
|
206
|
+
)
|
|
200
207
|
sys.exit(1)
|
|
201
208
|
|
|
202
209
|
backend_id = f"{adapter}-{port}"
|
|
@@ -244,8 +251,7 @@ def stop_cmd(adapter: str, port: int) -> None:
|
|
|
244
251
|
help="Delete specific instance (if not specified, deletes config)",
|
|
245
252
|
)
|
|
246
253
|
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
247
|
-
|
|
248
|
-
def delete_cmd(adapter: str, port: int | None, yes: bool, force: bool) -> None:
|
|
254
|
+
def delete_cmd(adapter: str, port: int | None, yes: bool) -> None:
|
|
249
255
|
"""Delete a backend instance or config.
|
|
250
256
|
|
|
251
257
|
Without --port: Deletes the backend config (stops all instances first).
|
|
@@ -253,7 +259,6 @@ def delete_cmd(adapter: str, port: int | None, yes: bool, force: bool) -> None:
|
|
|
253
259
|
"""
|
|
254
260
|
import shutil
|
|
255
261
|
|
|
256
|
-
yes = yes or force
|
|
257
262
|
console = Console(width=200)
|
|
258
263
|
|
|
259
264
|
if port is not None:
|
|
@@ -271,7 +276,12 @@ def delete_cmd(adapter: str, port: int | None, yes: bool, force: bool) -> None:
|
|
|
271
276
|
else:
|
|
272
277
|
backend_dir = get_forge_home() / "backends" / adapter
|
|
273
278
|
if not backend_dir.exists():
|
|
274
|
-
|
|
279
|
+
print_error_with_tip(
|
|
280
|
+
f"Backend config not found for '{adapter}'",
|
|
281
|
+
"Create it first:",
|
|
282
|
+
commands=[f"forge backend create {adapter}"],
|
|
283
|
+
console=console,
|
|
284
|
+
)
|
|
275
285
|
sys.exit(1)
|
|
276
286
|
|
|
277
287
|
if not yes and not click.confirm(f"Delete backend config for '{adapter}' (stops all instances)?"):
|
|
@@ -20,10 +20,9 @@ from rich.console import Console
|
|
|
20
20
|
|
|
21
21
|
from forge.core.paths import display_path
|
|
22
22
|
from forge.proxy.proxies import (
|
|
23
|
+
ProxyNotFoundError,
|
|
23
24
|
ProxyRegistryCorruptedError,
|
|
24
|
-
ProxyRegistryStore,
|
|
25
25
|
ProxyResolutionError,
|
|
26
|
-
resolve_proxy,
|
|
27
26
|
)
|
|
28
27
|
from forge.session.direct_model import apply_direct_model_env
|
|
29
28
|
|
|
@@ -208,20 +207,22 @@ def start_cmd(
|
|
|
208
207
|
proxy_display: str | None = None
|
|
209
208
|
|
|
210
209
|
if proxy_id:
|
|
211
|
-
|
|
210
|
+
from forge.proxy.proxy_orchestrator import ProxyStartError, ensure_proxy
|
|
212
211
|
|
|
213
212
|
try:
|
|
214
|
-
|
|
213
|
+
entry, started = ensure_proxy(proxy_id)
|
|
215
214
|
except ProxyRegistryCorruptedError as e:
|
|
216
215
|
click.echo(f"Error: {e}")
|
|
217
216
|
sys.exit(1)
|
|
218
|
-
|
|
219
|
-
try:
|
|
220
|
-
entry = resolve_proxy(registry, proxy_id)
|
|
221
|
-
except ProxyResolutionError as e:
|
|
217
|
+
except (ProxyResolutionError, ProxyStartError) as e:
|
|
222
218
|
click.echo(f"Error: {e}")
|
|
219
|
+
if isinstance(e, ProxyNotFoundError):
|
|
220
|
+
click.echo("Tip: Run 'forge proxy template list' to see available templates.")
|
|
223
221
|
sys.exit(1)
|
|
224
222
|
|
|
223
|
+
if started:
|
|
224
|
+
console.print(f"[dim]Started proxy '{entry.proxy_id}' from template '{proxy_id}'.[/dim]")
|
|
225
|
+
|
|
225
226
|
try:
|
|
226
227
|
_healthcheck_proxy(
|
|
227
228
|
base_url=entry.base_url,
|
|
@@ -391,13 +392,11 @@ def preset_edit() -> None:
|
|
|
391
392
|
|
|
392
393
|
@preset.command("reset")
|
|
393
394
|
@click.option("--yes", "-y", is_flag=True, help="Skip confirmation prompt")
|
|
394
|
-
|
|
395
|
-
def preset_reset(yes: bool, force: bool) -> None:
|
|
395
|
+
def preset_reset(yes: bool) -> None:
|
|
396
396
|
"""Reset settings preset to built-in defaults."""
|
|
397
397
|
from forge.core.state import atomic_write_text
|
|
398
398
|
from forge.install.preset import get_builtin_preset_json, get_preset_path
|
|
399
399
|
|
|
400
|
-
yes = yes or force
|
|
401
400
|
preset_path = get_preset_path()
|
|
402
401
|
|
|
403
402
|
if not yes:
|