A model-agnostic, self-learning, self-healing agent harness — and the Python SDK to build agent swarms on top of it.
You describe a swarm of agents in plain English. forge designs the architecture (which agents, what roles, what tools, what schedule), asks you where to run it, and spins it up.
Drop this repo into any AI IDE — Claude Code, Codex, Cursor, or just a terminal — and say something like:
"build me a swarm that pulls Apollo leads every morning, qualifies them with Claude, and DMs the hot ones to my Slack"
forge proposes the architecture, you approve, and it scaffolds the swarm into one of three places — your choice:
| Where it runs | What you get | Best for |
|---|---|---|
| Local terminal | examples/<name>/run.py you can run on demand or cron |
Hacking on the swarm, version-controlling it, no external dependencies |
| Railway dashboard | A PendingAction in your hosted dashboard; one click and it materializes locally on the next 5-min sync |
Persistent control plane, daily/weekly reports, watching your swarm evolve from anywhere |
| Claude Code subagents | Drop-in .claude/agents/<name>.md files in your repo's .claude/ directory |
Working day-to-day inside Claude Code; subagents you can invoke via /agents |
Pick one, pick all three. The swarm itself is the same; only the runtime changes.
pip install 'forge-harness[dashboard]'
forge new "DM me a Notion summary every morning at 8"forge will:
- Design a swarm (LLM-driven; uses Anthropic Haiku by default ≈ $0.005)
- Show you the proposed architecture (agents, roles, tools, schedule)
- Ask where to run it:
[1] terminal [2] claude [3] dashboard [4] all - Scaffold the artifacts
Open this repo in your IDE. Tell it:
"Use forge to build me a swarm that does X."
The IDE reads forge's docs, runs forge new "X", and walks you through the same flow. (No special integration needed — forge new is just a CLI.)
If you already have the dashboard deployed (see Railway deploy), the orchestrator chat ("Papa Bear") on the Workspace page can also design and propose swarms. Type a description; click Approve on the PendingAction; the next local sync materializes the scaffold.
If you already know the swarm shape and just want the SDK:
from forge import (
HookBus, ToolRegistry, RoleCouncilSpawner, RoleAssignment,
SwarmSpec, Topology, Consensus, attach_healing,
)See the 60-second SDK pitch below.
pip install forge-harness # core SDK + CLI
pip install 'forge-harness[dashboard]' # +FastAPI dashboard, Postgres support
pip install 'forge-harness[intel]' # +auto-research with TavilyDistribution name on PyPI:
forge-harness. Import name in Python:forge. Same pattern aspillow → import PILorscikit-learn → import sklearn.
Every agent harness shipping today locks you into a single model vendor or a single use case:
- Claude Agent SDK — beautiful primitives, but Claude-only.
- OpenAI Agents SDK — same shape, OpenAI-only.
- Ruflo — open and powerful, but TypeScript-heavy and hard to compose.
- OpenHarness, Hermes, Meta-Harness — academic gems, each great at one thing, none complete.
- LangChain / LlamaIndex — kitchen-sink frameworks, no real swarm or recursion model.
- Bespoke
.claude/rigs — every team rebuilds the sameCircuitBreaker,EvalGate,ReasoningBankfrom scratch.
The result: you pick a vendor, you pick a use case, you re-implement the same five primitives every time.
forge picks a different fight.
It's a harness, not a framework — a thin kernel + extensible hook bus + a layered set of opt-in primitives, all model-agnostic. You bring any model (or four). You bring your tools (MCP, Composio, computer-use, CLI subprocesses). forge gives you the swarm topology, the healing, the memory, the skill self-modification, and the observability. You write five lines and a council debates your decision on three different models in parallel and returns a majority verdict — with token cost, replayable traces, and a learning loop that promotes a winning skill version when the eval gate clears.
import asyncio
from forge import (
HookBus, ToolRegistry,
RoleCouncilSpawner, RoleAssignment, SwarmSpec, Topology, Consensus,
attach_healing,
)
async def main():
tools, hooks = ToolRegistry(), HookBus()
attach_healing(hooks) # circuit breaker on every tool
s = RoleCouncilSpawner(tools=tools, hooks=hooks, max_turns=4)
s.set_assignments([
RoleAssignment(profile="anthropic", role="optimist"),
RoleAssignment(profile="anthropic-haiku", role="skeptic"),
RoleAssignment(profile="anthropic-contrarian", role="pragmatist"),
])
result = await s.run(
"Should we deploy this feature today?",
SwarmSpec(topology=Topology.PARALLEL_COUNCIL, consensus=Consensus.MAJORITY,
members=["anthropic", "anthropic-haiku", "anthropic-contrarian"]),
)
print("verdict:", result.verdict.winner)
asyncio.run(main())That's a 3-member parallel council with role-injected disagreement (so they don't all rubber-stamp the same answer), wired through a circuit breaker that trips on tool failures, with traces and token costs logged. ~$0.10 per run, end-to-end.
Now imagine that wired into your nightly cron, learning from its own traces, promoting prompt versions only when they beat current by a 0.05 confidence margin, and exposing every primitive as an MCP tool your Claude Code session can call. That's forge.
forge wasn't designed in a vacuum. It started with a simple question: what does the best 5% of every existing harness look like, and could we lift only the winning patterns?
We assigned three research agents to three clusters, each running a three-round analysis loop (Observe → Stress-test → Converge) on every source. Sixteen sources, ~140 hours of reading distilled into a single rubric, then synthesized through a three-perspective council (simplicity / power / observability).
jbellsolutions/autonomous-sdr-agent— persona/council router pattern, SOUL.md identity filesjbellsolutions/social-sdr— production-gradeCircuitBreaker+ 5-classErrorTypejbellsolutions/Orgo-Computer-Use-Agents— the canonical.claude/filesystem layoutjbellsolutions/coo-agent— three-tier tool fallthrough (MCP → Computer Use → CLI)jbellsolutions/agentstack-fleet-builder— concierge + reinforced-harness separationjbellsolutions/ai-agent-team-reference— 11-role agent taxonomyruvnet/ruflo— topology × consensus as config, MoE-8 router, ReasoningBank, WASM bypass tiernousresearch/hermes-agent— autonomous skill synthesis post-task
HKUDS/OpenHarness— hook bus + dry-runready/warning/blockedverdict + permission modesopenclaw/openclaw— per-session-type tool allowlists, gateway-as-routeryoonholee.com/meta-harness— trace-fidelity invariant: store full execution traces so the optimizer can do counterfactual diagnosiskevinrgu/autoagent—program.mdanti-overfit regularizer,# === FIXED ADAPTER BOUNDARY ===sentinelhyperagent.com— cost dashboard pattern (and a lesson in marketing-vs-substance)
- Composio — session+auth abstraction over 1,000+ SaaS apps
- Claude Agent SDK —
AgentDefinition, lifecycle hooks, permission modes - OpenAI Agents SDK — JSON-Schema tool portability
- Anthropic's Long-Running Agents paper — Initializer→Coding split, git-as-session-journal (resume from
git diff HEAD~N) - Martin Fowler's Harness Engineering — the Guides (feedforward) + Sensors (feedback) vocabulary
| Source | Pattern lifted | Why |
|---|---|---|
| Ruflo | Topology × consensus as config, ReasoningBank 5-stage loop | Most expressive swarm primitive in the field |
| Hermes | Autonomous skill synthesis post-task | Only source with real dynamic skill creation |
| Meta-Harness | Full-fidelity trace store, counterfactual diagnosis | The single most important primitive for true self-improvement |
| OpenHarness | Hook bus, dry-run verdict, permission modes | Cleanest extensibility seam shipped open-source |
| OpenClaw | Per-session-type tool allowlists | Blast-radius reduction for swarms |
| Claude Agent SDK | AgentDefinition pattern, lifecycle hook names |
Industry-standard agent isolation |
| Anthropic paper | Initializer→Coding split, git-as-journal | Resumability without custom session storage |
| Composio | Tool registry as session+auth layer | 1,000+ apps as one decoupled boundary |
| AutoAgent | "Would this still help if this task vanished?" regularizer, FIXED ADAPTER BOUNDARY sentinel, results.tsv ledger |
Anti-overfit guard that ships in the proposer prompt |
| Fowler | Guides + Sensors vocabulary, three-tier regulation | The right names for the right things |
| Justin's repos | .claude/ filesystem contract, ErrorType + CircuitBreaker, eval-gated A/B promotion |
Production primitives ported verbatim |
Skipped on purpose: kitchen-sink frameworks (LangChain), single-vendor SDKs as the kernel layer (forge calls them as profiles instead), academic toy loops without a real eval substrate, Hyperagent's marketing copy.
The full per-source rubric scores live in okay-so-if-i-immutable-seahorse-agent-*.md plan files; that's how every claim above can be audited.
L7 Observability OTel · token+cost telemetry · dry-run verdict · replayable traces
L6 Use-case Personas · skills · routers · heartbeats
L5 Self-improve Skill autosynthesis · eval gate · skill search
L4 Swarm Topology × consensus · sub-agent isolation
L3 Self-healing ErrorType · CircuitBreaker · retry policy
L2 Tools MCP → Computer/Browser → CLI shell · per-persona deny-list
L1 Memory ReasoningBank · git journal · Obsidian vault · cross-project genome · .claude/
L0 Kernel Agent loop · hook lifecycle · provider-as-profile
Why layers, not modules: L0 may not import from any layer above it. L1–L7 may only import from layers below. The hook bus is the cross-cutting seam — every layer subscribes; nothing reaches across. That's what keeps the kernel under 200 lines and the rest swappable.
Why "harness" not "framework": a framework calls your code; forge gives you primitives, you compose them. The kernel is AgentLoop.run(). Everything else is hooks, registries, and adapters.
These aren't aspirations. Every row was demonstrated end-to-end against real APIs during the build:
| Surface | What was tested | Result |
|---|---|---|
| PyPI install | pip install forge-harness in a clean venv |
✅ v0.1.0, 60 top-level exports importable, ~85 KB wheel |
| CI matrix | pytest on ubuntu+macos × Python 3.11/3.12/3.13 + wheel build | ✅ 7/7 jobs green |
| Test suite | 70 tests across kernel, providers, tools, healing, swarm, memory, skills, observability, recursion, CLI | ✅ 70/70 passing |
| Anthropic provider | LIVE Sonnet + Haiku + Sonnet-contrarian as 3 council members | ✅ verdict reached, 17 tool calls, $0.10/run |
| OpenRouter provider | LIVE DeepSeek-chat call via OpenAI-compatible adapter | ✅ correct output, 12 in / 6 out tokens, < $0.001 |
| Composio integration | Live API call via ComposioToolSet |
✅ 1,048 apps available (gmail, github, notion, slack, supabase, …) |
| Real MCP server | npx -y @modelcontextprotocol/server-filesystem rooted at vault |
✅ 14 fs tools registered (fs_vault__read_file, fs_vault__write_file, …) |
| Recursion (LIVE Sonnet) | Read 8 synthetic failing traces, propose harness diffs, fork → apply → score → keep | ✅ 2/2 diffs applied, score 0.00 → 1.00, kept=1, ledger row written, circuits.json and persona file actually mutated by Sonnet's proposed diffs |
| Anti-overfit regularizer | Sonnet's rationales explicitly invoke the AutoAgent guard | ✅ verbatim quote: "This is NOT task-specific … any task using this broken tool will benefit … this generalizes." |
| Tool-using council | Members with access to MCP fs + Obsidian search + shell + git | ✅ Haiku + Contrarian voted WAIT with grounded reasoning ("today is Sunday", "no test results"), 3 sessions traced |
| Memory promotion | High-confidence ReasoningBank memories → Obsidian topics | ✅ 2 promoted to vault topic notes with frontmatter (memory_id, confidence, promoted_count) |
| Claude Code MCP server | claude mcp add forge -- forge mcp |
✅ Connected, 12 forge tools advertised |
| Slash command | /forge council "...", /forge recurse, /forge vault search, etc. |
✅ all 13 verbs wired |
| Skill auto-trigger | Plain-language "run a council on shipping" | ✅ skill at ~/.claude/skills/forge/SKILL.md routes to forge_council |
| Cross-project genome | Promoted memory at ~/.forge/genome.json, recallable from any project |
✅ singleton genome() returns ReasoningBank shared across projects |
pip install "forge-harness[anthropic,mcp]"from forge import (
AgentLoop, AgentDef, HookBus, Verdict,
RoleCouncilSpawner, RoleAssignment, SwarmSpec, Topology, Consensus,
ObsidianVault, ReasoningBank, genome,
ToolRegistry,
attach_healing, ErrorType,
SkillStore, autosynth, evaluate, promote_if_passing,
TraceStore, Telemetry,
recurse_once, propose_with_llm, ResultsLedger,
)The full SDK surface (60 symbols, all stable in v0.1.x) is documented in forge/__init__.py::__all__.
forge doctor # env audit
forge run operator # mock vertical end-to-end
forge run operator_real # live council with MCP filesystem server
forge recurse --home ~/.forge/X # one self-mod cycle
forge recurse-loop --home ~/.forge/X -n 5 # cron-friendly: 5 cycles in series
forge dashboard --home ~/.forge/X # telemetry summary
forge skill list # project skill registry
forge skill search "summarize" # vector search over skills
forge vault write "Q4 plan" :: "ship by Oct" #planning
forge vault search "shipping"
forge heartbeat run --dir ~/.forge/X/.claude/heartbeats
forge mcp # run as MCP stdio serverOne-time setup:
claude mcp add -s user forge -- $(which forge) mcpThen in any Claude Code session:
/forge council "Should we ship today?"
/forge recurse
/forge vault write Q4 plan :: ship by Oct #planning
/forge remember "Friday EOD ships consistently work"
/forge recall shipping decisions
/forge doctor
Or just say it conversationally — "run a council on whether to ship" — and the skill at ~/.claude/skills/forge/SKILL.md will route to forge_council automatically.
forge runs in mock mode out of the box (no keys needed for tests + scaffolding). To go live, drop your keys in ~/.forge/.env:
mkdir -p ~/.forge
cat > ~/.forge/.env <<'EOF'
ANTHROPIC_API_KEY=sk-ant-...
OPENROUTER_API_KEY=sk-or-... # optional — DeepSeek + 100+ models
COMPOSIO_API_KEY=ak_... # optional — 1,000+ SaaS tools
VOYAGE_API_KEY=... # optional — production embeddings
OPENAI_API_KEY=... # optional — alternative embedding source
OTEL_EXPORTER_OTLP_ENDPOINT=... # optional — ship spans to Honeycomb/Jaeger/Datadog
EOF
chmod 600 ~/.forge/.env
forge doctor # verifyforge auto-loads the file on import (and re-overrides empty parent-shell env vars — see "What we fixed" below).
Three tiers, all wired together:
- Per-project working memory at
<your_project>/.claude/forge/— traces, telemetry, project-specific skills, healing circuits. Stays with the repo. - Cross-project genome at
~/.forge/genome.json— high-confidence learnings compound across all projects. Useforge_memory_remember/forge_memory_recallfrom any project. - Obsidian vault at
~/.forge/vault— human-readable knowledge graph.inbox/daily/decisions/topics/agents/folders, YAML frontmatter,[[wiki-link]]parsing, backlinks graph. Open the folder in Obsidian to browse the agent's accumulated knowledge visually.
Memory promotion is automatic: when a ReasoningBank memory crosses confidence threshold + min-use count, promote() writes it as topics/<slug>.md in the vault with full provenance metadata. Re-promotion bumps a promoted_count instead of duplicating.
Three templates ship under forge/scheduler/:
# macOS launchd — nightly self-improve at 02:30
cp forge/scheduler/launchd.plist.template ~/Library/LaunchAgents/com.forge.recurse.plist
launchctl load ~/Library/LaunchAgents/com.forge.recurse.plist
# Linux/macOS cron
crontab -e # paste from forge/scheduler/cron.crontab.template
# CI / GitHub Actions — auto-run nightly recurse + commit ledger updates
mkdir -p .github/workflows
cp forge/scheduler/github_action.yml.template .github/workflows/forge-nightly.ymlThe recurse-loop subcommand was designed for cron specifically: idempotent, structured logs, exit code reflects success.
A "true SDK" is one that survives reality. Here's what reality threw at forge during the build:
Symptom: pip install -e . succeeded, but python -c "import forge" raised ModuleNotFoundError. pytest worked. Direct python invocations didn't.
Root cause: Python 3.14 added a check that skips .pth files where the OS reports UF_HIDDEN. macOS Sonoma+ tags every file written through certain APIs with com.apple.provenance, which sets the hidden flag. The editable-install .pth was being silently skipped at site init.
Fix: the forge shell script was rewritten as a bash wrapper that exports PYTHONPATH directly:
#!/usr/bin/env bash
exec env PYTHONPATH="/path/to/forge" /path/to/.venv/bin/python -m forge.cli "$@"Editable installs still work for python -m forge.cli and pytest. The wrapper handles the script-on-PATH case until Python 3.14 ignores UF_HIDDEN for .pth or hatchling stops triggering the provenance xattr. Documented in CHANGELOG.md.
Symptom: from forge.tools.composio_adapter import ComposioAdapter raised ImportError: cannot import name 'Tool' from partially initialized module 'forge.tools.base'.
Root cause: forge.tools.__init__ imported .base which imported from ..kernel.types, which triggered forge.kernel.__init__ which imported .loop, which imported back into forge.tools.registry — a partially-loaded base.
Fix: every type-only import across the kernel ↔ tools boundary is now TYPE_CHECKING-only. Forward references in method signatures become string-quoted. No runtime cost, no cycle. pytest proved both paths work.
Symptom: forge doctor reported ANTHROPIC_API_KEY: empty even though the key was in ~/.forge/.env.
Root cause: the _dotenv loader used os.environ.setdefault(k, v) — but setdefault doesn't override an existing empty value. The shell had ANTHROPIC_API_KEY="" from somewhere, which beat the file.
Fix: override semantics changed to "missing OR empty value gets replaced from file." Existing non-empty shell exports still win — you can override per-invocation without editing the file.
Symptom: First live recursion run on Sonnet produced 2 diffs but applied 0.
Root cause: the directive was too loose. Sonnet emitted reasonable-looking JSON (target=config/tools.yaml, target=circuits/safety.yaml) but the harness's apply() only knew specific paths and payload key names.
Fix: the PROGRAM_DIRECTIVE was tightened with explicit examples for each op shape showing the exact target paths and required payload keys. Sonnet's next run emitted both diffs in correct shape; both applied; score went 0.00 → 1.00; the candidate was kept.
This is the regularizer working at both the LLM level (anti-overfit reasoning in the prompt) and the gate level (rollback if score didn't improve). Bad proposals get discarded automatically.
Symptom: Live tool-using council had Haiku produce a clean WAIT vote with rationale, but Sonnet returned ''.
Root cause: when an agent exits via max_turns while generating tool calls, the last assistant message is empty (text-less, tool-call-only). The loop returned that as final_text.
Fix: final_text extraction now walks the message list backward and returns the last non-empty assistant text. Combined with max_turns=8 for tool-using councils, every member produces a usable vote.
Symptom: Even after fixing UF_HIDDEN, the editable install didn't work.
Root cause: Python's site module silently skips .pth lines that don't end with a newline. Hatchling on Python 3.14 wrote a 25-byte file with no terminator.
Fix: the bash wrapper avoids the .pth mechanism entirely. The CHANGELOG documents the issue for future maintainers.
Symptom: twine upload returned 403 Forbidden.
Root cause: another project named forge already existed at v0.12.0+. PyPI names are first-come.
Fix: distribution name changed to forge-harness. Python import name stayed forge. Same pattern as pillow → import PIL. Scratched a brief itch to over-think this; moved on.
Symptom: Three Sonnets given the same task all said the same thing. "Consensus" was an artifact, not a signal.
Fix: RoleCouncilSpawner injects per-member system prompts (optimist / skeptic / pragmatist) before each call. Now they actually disagree productively, and a 2-1 verdict means something. The role prompts are documented in forge.swarm.roles::DEFAULT_ROLES and overridable per call.
Everything you need, importable at the top level (from forge import …):
# L0 kernel
AgentLoop, AgentDef, HookBus, HookContext, LoopResult,
Message, AssistantTurn, ToolCall, ToolResult,
Verdict, PermissionMode, ProviderProfile, load_profile
# L1 memory
ObsidianVault, Note, # Obsidian-format vault with backlinks
ReasoningBank, Memory, # vector recall with confidence decay
genome, genome_path, # cross-project singleton
GitJournal, # resume-from-git
ClaudeDir, # canonical filesystem contract
PromotionResult, promote # ReasoningBank → vault promotion
# L2 tools
Tool, ToolRegistry # 3-tier classification + per-agent allow/deny
# L3 healing
CircuitBreaker, CircuitRegistry, CircuitState,
ErrorType, classify, attach_healing # wires breakers into the hook bus
# L4 swarm
Topology, Consensus, SwarmSpec, SwarmResult,
Spawner, RoleCouncilSpawner, RoleAssignment
# L5 skills
SkillStore, SkillRun, SkillSearchIndex,
autosynth, evaluate, promote_if_passing,
MIN_SAMPLES, CONFIDENCE_MARGIN, EvalReport
# L7 observability
TraceStore, Telemetry, SessionStat
# (OTelExporter under forge.observability.otel — optional opentelemetry dep)
# Providers
Provider, make_provider # YAML-driven factory
# Profiles ship: anthropic, anthropic-haiku, anthropic-contrarian,
# openrouter-deepseek, openai-gpt4, ollama-llama3, mock
# Recursion
recurse_once, RecurseResult, # full self-mod cycle
propose, propose_with_llm, # rule-based + LLM proposers
HarnessDiff, TraceAnalyzer, ResultsLedger # diff types + symptom extraction + TSV ledgerFor tools that need vendor SDKs — Composio, MCP, browser automation — import from their submodules:
from forge.tools.builtin.shell import ShellTool, ClaudeCodeTool, CodexCLITool, GeminiCLITool
from forge.tools.builtin.fs import FSReadTool, FSWriteTool
from forge.tools.builtin.browser import HttpFetchTool
from forge.tools.builtin.obsidian import (
ObsidianWriteTool, ObsidianSearchTool, ObsidianReadTool, ObsidianBacklinksTool,
)
from forge.tools.mcp_client import MCPClientPool, load_mcp_servers
from forge.tools.composio_adapter import ComposioAdapter, composio_via_mcp
from forge.observability.otel import OTelExporterEvery external pattern in forge has a paper trail. If you want to read the source material:
| Source | What forge took |
|---|---|
| Ruflo (ruvnet/ruflo) | Provider-as-profile, topology × consensus as config, ReasoningBank 5-stage loop |
| Hermes (nousresearch/hermes-agent) | Autonomous skill synthesis post-task |
| Meta-Harness (yoonholee.com/meta-harness) | Full-fidelity trace store as the recursion substrate |
| OpenHarness (HKUDS/OpenHarness) | Hook bus + permission modes + dry-run verdict |
| OpenClaw (openclaw/openclaw) | Per-session-type tool allowlists, gateway-as-router |
| AutoAgent (kevinrgu/autoagent) | program.md regularizer, # === FIXED ADAPTER BOUNDARY === sentinel, results.tsv ledger |
| Claude Agent SDK (code.claude.com) | AgentDefinition, hook lifecycle, permission modes |
| Anthropic harness paper (anthropic.com) | Initializer→Coding split, git-as-session-journal |
| Composio (composio.dev) | Session+auth tool registry over 1,000+ apps |
| Fowler (martinfowler.com) | Guides (feedforward) + Sensors (feedback) vocabulary |
| Justin's repos (autonomous-sdr, coo-agent, Orgo) | .claude/ filesystem contract, ErrorType + CircuitBreaker, eval-gated A/B promotion |
- PyPI publishing on tag — wire the GH Actions workflow to
twine uploadautomatically whengit tag v*.*.*is pushed - Sphinx / mkdocs API reference site — auto-generated from docstrings; deployed to
forge-harness.dev - Real
OnnxMiniLMdefault embedder — local, free, no network round-trips for the genome - Real OTel default exporter — ship spans to Honeycomb out of the box when
OTEL_EXPORTER_OTLP_ENDPOINTis set - Composio MCP entry enabled by default in
examples/operator_real/mcp.jsonwhenCOMPOSIO_API_KEYis present - TS SDK adapter — for projects that want forge primitives from Node (the Claude Agent SDK route)
- Browser-Use + Airtop tier-2 adapters — first-class browser automation
- Plugin system for new consensus algorithms — Raft, Byzantine, weighted-by-cost-tier
- More heartbeats — daily standup, weekly retro, monthly retrospective templates
- End-to-end Notion / Linear / Slack examples via Composio adapter
Open an issue if you want to drive any of these.
forge stays small on purpose. The kernel is under 200 lines. Every layer above L0 is opt-in. PRs that complicate the kernel without proving value get pushed back; PRs that close a real gap land fast.
See CONTRIBUTING.md for dev setup, layer rules, and the release workflow.
forge: where raw materials become tools. It started as a working title and stuck. The original project named forge on PyPI is something else entirely — we're forge-harness on PyPI, forge everywhere else. If you build something with this and tell us about it, we'll add it to a community gallery.
MIT. See LICENSE.
Built with ⚒️ by Justin Bell. Synthesized from 16 sources, 70 tests, 10 commits, $0.11 of live API spend during the build, and one frustrating evening fighting macOS xattrs.