@giamme/forge 0.8.0 → 0.10.0
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.
- package/CHANGELOG.md +50 -0
- package/README.md +106 -1766
- package/SKILL.md +22 -3
- package/VERSION +1 -1
- package/docs/README.md +26 -0
- package/docs/add-ons.md +33 -0
- package/docs/artifacts.md +84 -0
- package/docs/commands.md +365 -0
- package/docs/dataset.md +29 -0
- package/docs/decomposed-runs.md +310 -0
- package/docs/development.md +95 -0
- package/docs/forge-ui.md +128 -0
- package/docs/fractal.md +181 -0
- package/docs/install.md +160 -0
- package/docs/memory.md +156 -0
- package/docs/models.md +118 -0
- package/docs/reviews.md +245 -0
- package/docs/safety.md +55 -0
- package/docs/troubleshooting.md +54 -0
- package/forge +20 -0
- package/package.json +3 -2
- package/references/dataset.md +18 -1
- package/references/decompose.md +20 -2
- package/references/fractal.md +1 -1
- package/references/harnesses.md +18 -6
- package/references/solo.md +1 -1
- package/references/ui.md +27 -5
- package/scripts/forge-config.py +347 -0
- package/scripts/forge-control.py +645 -0
- package/scripts/forge-dispatch.sh +7 -1
- package/scripts/forge-issues.py +806 -0
- package/scripts/forge-lib-task.sh +234 -35
- package/scripts/forge-parallel-options.sh +3 -1
- package/scripts/forge-parallel.sh +3 -2
- package/scripts/forge-route.py +184 -27
- package/scripts/forge-runs-rm.py +840 -0
- package/scripts/forge-runs.py +139 -0
- package/scripts/forge-runtime.py +59 -6
- package/scripts/forge-schedule.py +227 -11
- package/scripts/forge-ui.py +85 -19
- package/scripts/forge-version.py +54 -12
- package/scripts/forge_dataset/__init__.py +77 -6
- package/scripts/forge_dataset/capture.py +5 -2
- package/scripts/forge_dataset/harvest.py +23 -1
- package/scripts/forge_dataset/importer.py +420 -18
- package/scripts/forge_fractal/__init__.py +2 -1
- package/scripts/forge_registry.py +168 -16
- package/ui/README.md +116 -23
- package/ui/THIRD_PARTY_NOTICES.md +108 -0
- package/ui/docs/api.md +283 -23
- package/ui/forge_ui/__main__.py +10 -2
- package/ui/forge_ui/control.py +575 -0
- package/ui/forge_ui/export.py +33 -9
- package/ui/forge_ui/fractal.py +1 -2
- package/ui/forge_ui/reader.py +121 -8
- package/ui/forge_ui/server.py +135 -5
- package/ui/forge_ui/static/D-DINCondensed-Bold.woff2 +0 -0
- package/ui/forge_ui/static/app.js +358 -131
- package/ui/forge_ui/static/cloud3d.js +3 -2
- package/ui/forge_ui/static/cloudui.js +8 -5
- package/ui/forge_ui/static/controls.js +619 -0
- package/ui/forge_ui/static/convo.js +295 -0
- package/ui/forge_ui/static/datatable.js +232 -0
- package/ui/forge_ui/static/diffview.js +29 -10
- package/ui/forge_ui/static/findings3d.js +134 -0
- package/ui/forge_ui/static/findingsview.js +412 -0
- package/ui/forge_ui/static/graph.js +3 -3
- package/ui/forge_ui/static/icons.js +94 -0
- package/ui/forge_ui/static/index.html +31 -13
- package/ui/forge_ui/static/iso.js +70 -19
- package/ui/forge_ui/static/leaderboard.js +44 -48
- package/ui/forge_ui/static/panel.js +92 -36
- package/ui/forge_ui/static/rundelete.js +388 -0
- package/ui/forge_ui/static/runmodel.js +17 -1
- package/ui/forge_ui/static/scene3d.js +518 -43
- package/ui/forge_ui/static/search.js +2 -1
- package/ui/forge_ui/static/settings.js +399 -0
- package/ui/forge_ui/static/shell.js +137 -0
- package/ui/forge_ui/static/stagectl.js +150 -0
- package/ui/forge_ui/static/style.css +1099 -469
- package/ui/forge_ui/static/theme.js +25 -0
- package/ui/forge_ui/static/uikit.js +161 -0
- package/ui/forge_ui/transcripts.py +608 -0
package/docs/fractal.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
# Fractal execution
|
|
2
|
+
|
|
3
|
+
[← README](../README.md) · [All docs](README.md)
|
|
4
|
+
|
|
5
|
+
Fractal is an optional implementation backend for both solo and decomposed runs. It uses
|
|
6
|
+
the existing Forge dispatcher for Codex, Claude, OpenClaude, OpenCode and Antigravity.
|
|
7
|
+
Each work step gets a fresh model invocation; preserved files and child results provide
|
|
8
|
+
continuity when a parent resumes. Independent QA reviews the complete Forge task diff.
|
|
9
|
+
|
|
10
|
+
`--decompose-level` plans top-level Forge tasks before execution. `--fractal` lets an
|
|
11
|
+
implementer request nested children during execution. They can be used together, and
|
|
12
|
+
solo runs can use Fractal without a top-level decomposition.
|
|
13
|
+
|
|
14
|
+
## Install the optional runtime
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
forge fractal install --dry-run
|
|
18
|
+
forge fractal install --yes --with-prerequisites
|
|
19
|
+
forge fractal doctor --spec sol --spec opus --json
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
The managed runtime pins **Fractal 1.2.0** at revision
|
|
23
|
+
`18793200c0d7e8cdb2db369ea3abe5647a1e15e4`, checks the source archive checksum,
|
|
24
|
+
and includes the required `wiki` executable. It prefers an existing Python 3.12–3.14;
|
|
25
|
+
with prerequisite installation selected, it can provision uv, Python 3.13 and tmux.
|
|
26
|
+
tmux installation uses an existing Homebrew on macOS or apt on Debian/Ubuntu.
|
|
27
|
+
The [installation reference](../references/fractal.md#installation-and-provenance) covers
|
|
28
|
+
consent, supported bootstrap platforms, provenance and failure diagnostics.
|
|
29
|
+
|
|
30
|
+
Installation never enables Fractal for a run, adds no command and leaves your shell startup
|
|
31
|
+
files alone.
|
|
32
|
+
Doctor checks the runtime and advertised harness capabilities without calling a provider.
|
|
33
|
+
|
|
34
|
+
## Start and inspect a run
|
|
35
|
+
|
|
36
|
+
Through the skill:
|
|
37
|
+
|
|
38
|
+
```text
|
|
39
|
+
/forge "add retry with exponential backoff to the HTTP client" --dwarf sol:high --fractal
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Or prepare a solo run directory outside the product repository and invoke the runner:
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
FORGE_RUN="$HOME/.local/state/forge/examples/http-client"
|
|
46
|
+
mkdir -p "$FORGE_RUN"
|
|
47
|
+
printf '%s\n' 'Add bounded exponential backoff to the HTTP client and run its tests.' > "$FORGE_RUN/prompt.md"
|
|
48
|
+
bash scripts/forge-solo.sh "$FORGE_RUN" --repo /absolute/path/to/product \
|
|
49
|
+
--dwarf sol:high --qa opus --fractal --fractal-concurrency 3
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Choose a new run directory for unrelated work. Backend selection, routing pools and limits
|
|
53
|
+
are stored with the run; resume and explicit retry retain them. For a prepared, approved
|
|
54
|
+
decomposed plan, select the backend on `run`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
bash scripts/forge-parallel.sh run /absolute/path/to/plan --fractal --dry-run
|
|
58
|
+
bash scripts/forge-parallel.sh run /absolute/path/to/plan --fractal
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Dry runs show resolved model pools, limits and proposed workspaces without installation
|
|
62
|
+
or provider calls. When a selected runtime is unavailable, interactive runs offer
|
|
63
|
+
installation; unattended runs return an actionable error instead of changing backends.
|
|
64
|
+
|
|
65
|
+
Discover the managed run ID, then inspect it from another terminal:
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
forge fractal runs --repo /absolute/path/to/product --json
|
|
69
|
+
forge fractal open # live runs index
|
|
70
|
+
forge fractal status RUN_ID --json
|
|
71
|
+
forge fractal tree RUN_ID --json
|
|
72
|
+
forge fractal logs RUN_ID --follow
|
|
73
|
+
forge fractal report RUN_ID --html /absolute/path/to/report.html
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`RUN_ID` is the `run-…` identifier returned by `runs`, not the solo directory or plan name.
|
|
77
|
+
The [run inspector](../references/fractal-dashboard.md) opens on progress and problems, then lets
|
|
78
|
+
readers inspect task QA/history or node decisions, logs and changes. It refreshes about every
|
|
79
|
+
two seconds without replacing the workspace; search, selection, focus and scrolling remain in
|
|
80
|
+
place. “Pause live updates” affects the browser only. Browser access is read-only, bound to
|
|
81
|
+
`127.0.0.1` with an ephemeral access token. Closing its server does not stop execution.
|
|
82
|
+
Portable HTML embeds complete captured history and logs, works offline and needs no Fractal
|
|
83
|
+
runtime to read.
|
|
84
|
+
|
|
85
|
+
## Automatic recursive decomposition
|
|
86
|
+
|
|
87
|
+
`--fractal` permits workers to request children. To require a planning decision before
|
|
88
|
+
every eligible node implements, use `--fractal-auto-decompose`:
|
|
89
|
+
|
|
90
|
+
```text
|
|
91
|
+
/forge "<goal>" --dwarf luna:high --qa sol:high --fractal-auto-decompose --fractal-planner sol:xhigh --fractal-depth 3 --fractal-nodes 24
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
After the top-level Forge approval, the planner automatically splits useful work or
|
|
95
|
+
records why it is atomic. Children repeat this decision within the approved ownership
|
|
96
|
+
and frozen limits. `--fractal-planner` selects a separate decision model; when omitted,
|
|
97
|
+
Forge reuses the configured top-level planner, then the task's initial root model.
|
|
98
|
+
Decisions use disposable snapshots, persist for resume, and appear in tree/status/HTML
|
|
99
|
+
reports. Ordinary `--fractal` and existing runs retain their current behavior. Start a
|
|
100
|
+
new run to change policy or planner. See the [full decision contract](../references/fractal.md#automatic-decomposition-decisions).
|
|
101
|
+
|
|
102
|
+
## Bounds, recovery and acceptance
|
|
103
|
+
|
|
104
|
+
Defaults per Forge task are two nesting levels below implementation, three unsettled
|
|
105
|
+
direct children per node, twelve lifetime nodes, six iterations per node per attempt,
|
|
106
|
+
and a 45-minute attempt deadline excluding explicit pauses. Three model slots are shared
|
|
107
|
+
across the whole Forge run, including QA. `--max-parallel` bounds top-level task pipelines;
|
|
108
|
+
`--fractal-concurrency` bounds model invocations across their nested trees.
|
|
109
|
+
|
|
110
|
+
Child difficulty selects a frozen `--dwarf-low`, `--dwarf-medium` or `--dwarf-high` pool,
|
|
111
|
+
falling back to the configured general dwarf, then the parent. Dependencies and owned
|
|
112
|
+
paths constrain admission; overlapping ownership is serialized. Fractal never implies
|
|
113
|
+
`--yolo-dwarf`. Costs are observational; unavailable usage stays unknown and dollar-cap
|
|
114
|
+
requests are rejected.
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
forge fractal pause RUN_ID
|
|
118
|
+
forge fractal resume RUN_ID
|
|
119
|
+
forge fractal stop RUN_ID
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Pause and stop preserve work and diagnostics. Run-level resume recovers recorded execution
|
|
123
|
+
and the Forge pipeline without repeating accepted stages. Use explicit runner retry for
|
|
124
|
+
a new attempt, retaining work and the original Forge QA baseline. Controls also accept
|
|
125
|
+
`--task TASK_ID` and `--task TASK_ID --node NODE_ID`; those IDs come from `tree` output.
|
|
126
|
+
|
|
127
|
+
Managed data lives in `${XDG_STATE_HOME:-$HOME/.local/state}/forge/fractal/`. Solo execution
|
|
128
|
+
uses an isolated snapshot and imports changes only after checking source drift, preserving
|
|
129
|
+
staging and leaving changes uncommitted. Decomposed candidates return to Forge's existing
|
|
130
|
+
review/integration pipeline. **Fractal completion is not Forge acceptance**: QA, repository
|
|
131
|
+
verification and authorization to update the user's branch remain distinct.
|
|
132
|
+
|
|
133
|
+
See the [full Fractal reference](../references/fractal.md) for limits, child requests and
|
|
134
|
+
recovery details, and [verification evidence](../tests/fractal-verification.md) for the tested
|
|
135
|
+
platforms and browser checks. No paid-provider or speed/cost claim follows from fake-CLI tests.
|
|
136
|
+
|
|
137
|
+
## `forge fractal` command group
|
|
138
|
+
|
|
139
|
+
Run it as `forge fractal`, or `forge fractal` from a git checkout.
|
|
140
|
+
This group manages Fractal runs; it does not replace the `/forge` skill or runner scripts.
|
|
141
|
+
|
|
142
|
+
| Command | Purpose |
|
|
143
|
+
| --- | --- |
|
|
144
|
+
| `install [--dry-run] [--yes] [--with-prerequisites]` | preview or install the isolated pinned runtime |
|
|
145
|
+
| `doctor [--spec SPEC] [--json]` | check runtime/hooks/tmux and optional repeated harness specs without provider calls |
|
|
146
|
+
| `runs [--repo DIR] [--json] [--html PATH]` | discover managed runs, optionally filtered by repository |
|
|
147
|
+
| `status\|tree\|activity RUN_ID` | inspect progress, hierarchy and history |
|
|
148
|
+
| `logs\|costs\|messages\|config RUN_ID` | inspect diagnostics, observed accounting, radio traffic and effective settings |
|
|
149
|
+
| `pause\|resume\|stop RUN_ID` | request a control and record its observed state |
|
|
150
|
+
| `open [RUN_ID] [--port PORT]` | open the read-only live dashboard or runs index |
|
|
151
|
+
| `report RUN_ID [--html PATH]` | capture a self-contained HTML report; default filename is `RUN_ID.html` |
|
|
152
|
+
|
|
153
|
+
Inspection commands accept `--task TASK_ID`, `--node NODE_ID`, `--json`, `--html PATH`,
|
|
154
|
+
`--offset N` and `--limit N` (1–1000). They currently return a common JSON run projection;
|
|
155
|
+
the command names do not imply separate output schemas. `logs --follow` follows changes.
|
|
156
|
+
HTML exports capture full history and logs. Controls require an explicit run ID, and a
|
|
157
|
+
node control also requires its task ID; they never guess the latest run. Use each command's
|
|
158
|
+
`--help` for its accepted options. Installation is also available through
|
|
159
|
+
`bash scripts/forge-install-fractal.sh`.
|
|
160
|
+
|
|
161
|
+
Fractal adds a managed workspace alongside the runner artifacts described here:
|
|
162
|
+
|
|
163
|
+
```text
|
|
164
|
+
${XDG_STATE_HOME:-$HOME/.local/state}/forge/fractal/
|
|
165
|
+
runtime/ isolated pinned Fractal and wiki environment
|
|
166
|
+
provenance.json installed versions and source checksum
|
|
167
|
+
installs/ installation attempts and diagnostics
|
|
168
|
+
runs/<run-id>/run.json effective routing, limits and runner binding
|
|
169
|
+
runs/<run-id>/controls/ requested controls and observed results
|
|
170
|
+
runs/<run-id>/tasks/<task-id>/
|
|
171
|
+
request.json / state.json task request and execution checkpoint
|
|
172
|
+
initialization.json control repository and Fractal ledger provenance
|
|
173
|
+
control/ Fractal metadata, separate from product changes
|
|
174
|
+
nodes/<node-id>/product/ isolated product candidate
|
|
175
|
+
nodes/<node-id>/steps/ fresh dispatcher attempts and raw logs
|
|
176
|
+
outcome.json captured Forge acceptance and verification
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
The original solo/plan directory stores `fractal-selection.json`; decomposed plans also
|
|
180
|
+
store `fractal-routing.json`. Prefer `forge fractal runs`, `tree`, `logs` and `report`
|
|
181
|
+
to direct ledger access. Inspection and stop never delete managed work.
|
package/docs/install.md
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Install and update
|
|
2
|
+
|
|
3
|
+
[← README](../README.md) · [All docs](README.md)
|
|
4
|
+
|
|
5
|
+
Install forge from npm or a git checkout, check which agent CLIs it can reach, and keep every harness installation current.
|
|
6
|
+
|
|
7
|
+
## Requirements
|
|
8
|
+
|
|
9
|
+
forge runs on macOS and Linux. It needs bash, git and Python 3.9 or later, plus the agent
|
|
10
|
+
CLIs you route models through; the doctor below shows which ones it finds. Installing from
|
|
11
|
+
npm brings none of these with it.
|
|
12
|
+
|
|
13
|
+
## From npm
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g @giamme/forge
|
|
17
|
+
forge install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
`forge install` links the installed package into each harness, as the installer does for a
|
|
21
|
+
checkout below. It is a step of its own rather than an npm install script, so it works where
|
|
22
|
+
scripts are turned off (`--ignore-scripts`). To update:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
npm install -g @giamme/forge@latest && forge install
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The links point into npm's global package directory, so they keep working across updates;
|
|
29
|
+
`forge install` refreshes antigravity's copied plugin. Under nvm, that directory belongs to
|
|
30
|
+
one Node version: after switching Node, install forge on the new one and run `forge install`
|
|
31
|
+
again.
|
|
32
|
+
|
|
33
|
+
> **Foundry users:** Foundry's `forge` has the same name, and whichever directory comes first
|
|
34
|
+
> on your `PATH` wins; npm's global bin usually comes first. Runs start from your harness
|
|
35
|
+
> (`/forge`), so this command is only for `forge install` and the optional tools. If you keep
|
|
36
|
+
> Foundry's `forge` first, run ours as `"$(npm root -g)/@giamme/forge/forge"`.
|
|
37
|
+
|
|
38
|
+
## From a git checkout
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
git clone https://github.com/Giamme/forge.git ~/.claude/skills/forge
|
|
42
|
+
bash ~/.claude/skills/forge/scripts/forge-install.sh
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Clone anywhere you like — the installer works out its own location and links from there.
|
|
46
|
+
`~/.claude/skills/forge` is just the tidiest home: claude reads that path directly, and
|
|
47
|
+
opencode and grok scan it on their own.
|
|
48
|
+
|
|
49
|
+
The installer also offers optional Fractal support in an interactive terminal. Declining
|
|
50
|
+
or a Fractal installation failure does not prevent ordinary Forge setup. See
|
|
51
|
+
[Fractal installation](fractal.md#install-the-optional-runtime) for prerequisites and a separate install.
|
|
52
|
+
|
|
53
|
+
## Check what forge can reach
|
|
54
|
+
|
|
55
|
+
`<forge>` is your checkout, or `"$(npm root -g)/@giamme/forge"` for an npm install:
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
bash <forge>/scripts/forge-dispatch.sh doctor
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
```
|
|
62
|
+
HARNESS STATUS DETAIL
|
|
63
|
+
codex found /Users/you/.nvm/versions/node/v24.18.0/bin/codex
|
|
64
|
+
claude found /Users/you/.local/bin/claude
|
|
65
|
+
openclaude found /Users/you/.nvm/versions/node/v24.18.0/bin/openclaude
|
|
66
|
+
opencode found /Users/you/.opencode/bin/opencode
|
|
67
|
+
antigravity MISSING agy not on PATH
|
|
68
|
+
grok found /Users/you/.grok/bin/grok
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
A `MISSING` harness is fine — forge only needs the ones you actually route models through.
|
|
72
|
+
|
|
73
|
+
**Binary presence is not reachability.** A harness can be installed and still fail on auth or
|
|
74
|
+
billing (opencode in particular fails this way), and that failure only surfaces mid-dispatch.
|
|
75
|
+
To prove a pairing end to end before spending a real run:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
bash <forge>/scripts/forge-dispatch.sh dwarf <spec> --prompt-file /dev/stdin --repo <scratch dir> \
|
|
79
|
+
<<< 'Reply with exactly: FORGE_OK'
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## What the installer does per harness
|
|
83
|
+
|
|
84
|
+
Each CLI discovers skills differently, and the differences are not guessable — so verify
|
|
85
|
+
with the listed check rather than trusting file placement.
|
|
86
|
+
|
|
87
|
+
| Harness | Mechanism | Verify with |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| claude | `~/.claude/skills/forge/` | `/forge` appears in the skill list |
|
|
90
|
+
| openclaude | symlink into `~/.openclaude/skills/` | `openclaude skills list --json` |
|
|
91
|
+
| codex | symlink into **`~/.codex/skills/`** | `codex exec "do you have a skill named forge?"` |
|
|
92
|
+
| opencode | **nothing** — it scans `~/.claude/` itself | `opencode debug skill` |
|
|
93
|
+
| antigravity | `agy plugin install` of a generated wrapper | `agy --print='/forge'` |
|
|
94
|
+
|
|
95
|
+
> **The one ongoing caveat:** `agy plugin install` *copies* the files instead of referencing
|
|
96
|
+
> them. The other four track the source directory live, so an edit reaches them instantly —
|
|
97
|
+
> antigravity keeps running whatever it copied. **Re-run `forge-install.sh` after any edit**,
|
|
98
|
+
> or agy silently runs a stale version.
|
|
99
|
+
|
|
100
|
+
`forge-install.sh --dry-run` prints what it would do and touches nothing. It never clobbers
|
|
101
|
+
a real directory that isn't a symlink.
|
|
102
|
+
|
|
103
|
+
## Updating all harness installations
|
|
104
|
+
|
|
105
|
+
Run the updater from your Forge checkout (or through one of its skill symlinks):
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
./forge update # same as: bash ~/.claude/skills/forge/scripts/forge-update.sh
|
|
109
|
+
./forge version # VERSION, checkout commit, tracked upstream, last known release
|
|
110
|
+
./forge version --check # ask the upstream for its newest release now
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
It fetches the current branch's configured upstream, fast-forwards the checkout, and
|
|
114
|
+
runs Forge's installer to refresh Claude, OpenClaude and Codex links, OpenCode discovery,
|
|
115
|
+
and Antigravity's copied plugin. Harnesses that are not present are skipped. Restart
|
|
116
|
+
active harness sessions afterward to load the updated skill.
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
# Preview without fetching or changing files:
|
|
120
|
+
bash <forge-checkout>/scripts/forge-update.sh --dry-run
|
|
121
|
+
|
|
122
|
+
# Refresh harness installations after local edits, without fetching:
|
|
123
|
+
bash <forge-checkout>/scripts/forge-update.sh --local
|
|
124
|
+
|
|
125
|
+
# Point a copied installation's updater at the original checkout:
|
|
126
|
+
bash <path>/scripts/forge-update.sh --source <forge-checkout>
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
When a solo run, `plan` or `run` finishes, forge checks the checkout's upstream for a newer
|
|
130
|
+
release tag at most once a day (`git ls-remote`, 5-second timeout, cached in
|
|
131
|
+
`${XDG_STATE_HOME:-~/.local/state}/forge/update-check.json`) and, if there is one, prints a
|
|
132
|
+
single line on stderr: `forge 0.8 is available (you have 0.7.1): forge update`. The check is
|
|
133
|
+
silent offline, in a copied installation without Git, in CI (`CI` set) and with
|
|
134
|
+
`FORGE_UPDATE_CHECK=off`, and it never changes a run's outcome or exit code.
|
|
135
|
+
|
|
136
|
+
An npm installation has no Git upstream, so there the same daily check asks the npm registry
|
|
137
|
+
for the package's `latest` (`npm_config_registry` when you set one, as npm does), and the line
|
|
138
|
+
names npm's command instead: `forge 0.9.0 is available (you have 0.8): npm install -g
|
|
139
|
+
@giamme/forge@latest && forge install`. `forge version` shows the same command.
|
|
140
|
+
|
|
141
|
+
The default update requires a clean Git checkout and a branch with an upstream. Local
|
|
142
|
+
changes, detached HEAD, fetch failures and diverged branches stop the update without
|
|
143
|
+
resetting or stashing your work. `--local` deliberately allows current local edits.
|
|
144
|
+
Real skill directories are preserved; a conflicting directory or plugin installation
|
|
145
|
+
failure produces a nonzero exit and an explanation. After fixing an installation
|
|
146
|
+
failure, use `--local` to retry from the updated source. Updating Forge does not prompt
|
|
147
|
+
for optional Ripwire installation.
|
|
148
|
+
|
|
149
|
+
An npm installation is not a Git checkout, so the updater stops there and prints the npm
|
|
150
|
+
command [above](#from-npm) instead. `--local` still refreshes its harness installations.
|
|
151
|
+
|
|
152
|
+
## `scripts/forge-install.sh`
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
forge-install.sh [--dry-run]
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Links this directory into every installed harness. Skips harnesses that aren't present,
|
|
159
|
+
skips a destination that exists and isn't a symlink (it will never clobber a directory you
|
|
160
|
+
may have edited in place), and reports opencode as auto-detected.
|
package/docs/memory.md
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Project memory
|
|
2
|
+
|
|
3
|
+
[← README](../README.md) · [All docs](README.md)
|
|
4
|
+
|
|
5
|
+
What forge learns about a repository, where it keeps it, what each role is told, and what forge has spent there.
|
|
6
|
+
|
|
7
|
+
Every forge dispatch is a clean slate, so without help the tenth run in a repo rediscovers
|
|
8
|
+
what the first one learned. forge keeps that in the repo, in two files:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
.forge/memory.md small, capped, injected into every prompt
|
|
12
|
+
.forge/ledger.tsv append-only, one row per dispatch, never injected
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The split is the design. Injected context is paid for on every dispatch of every future run,
|
|
16
|
+
so "remember everything" and "stay small" pull against each other — the ledger remembers at
|
|
17
|
+
zero context cost and is read only when asked, while `memory.md` is capped at 40 lines / 4 KB.
|
|
18
|
+
|
|
19
|
+
```markdown
|
|
20
|
+
## Verify
|
|
21
|
+
- pytest -q runs the suite; make test also lints and is 4x slower
|
|
22
|
+
|
|
23
|
+
## Known traps
|
|
24
|
+
- tests/test_api.py::test_timeout is flaky under -n auto [tests/test_api.py]
|
|
25
|
+
|
|
26
|
+
## Recurring QA findings
|
|
27
|
+
- 3x: route added without updating openapi.yaml [src/routes]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## How a fact gets in
|
|
31
|
+
|
|
32
|
+
The role that just did the work writes it down, in its final message, next to the verdict:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
FORGE_LEARNING: trap | src/proto/*_pb2.py is generated, never hand-edit [src/proto]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
No extra dispatch and no separate model guessing what mattered — the one that hit the
|
|
39
|
+
landmine is the one that records it. Everything after that is deterministic bash.
|
|
40
|
+
|
|
41
|
+
**What stops it becoming a junk drawer:**
|
|
42
|
+
|
|
43
|
+
| Guard | Effect |
|
|
44
|
+
|---|---|
|
|
45
|
+
| recurrence threshold | a `finding` needs **two distinct runs** before it is injected. One occurrence is an incident, not a pattern |
|
|
46
|
+
| stale anchors | an entry ending in `[path]` is dropped once that path stops existing, so memory stays true as code moves |
|
|
47
|
+
| hard cap | over 40 lines / 4 KB, lowest-count and least-recent facts are trimmed; one entry is clamped to 180 chars |
|
|
48
|
+
| bias to silence | the prompt says most runs teach nothing durable and emitting nothing is correct. A false fact costs every future run; a missing one costs a single rediscovery |
|
|
49
|
+
|
|
50
|
+
Only `verify` and `trap` are kept on first sighting — those are facts, true the first time.
|
|
51
|
+
|
|
52
|
+
## Slices, and what forge writes
|
|
53
|
+
|
|
54
|
+
| Role | Gets |
|
|
55
|
+
|---|---|
|
|
56
|
+
| planner, dwarf | Verify + Known traps + Recurring findings |
|
|
57
|
+
| qa | Known traps + Recurring findings |
|
|
58
|
+
|
|
59
|
+
QA is not told the build command; it is not building anything. It *is* told the recurring
|
|
60
|
+
findings, so it can notice this is the fourth time the same bug shipped.
|
|
61
|
+
|
|
62
|
+
`.forge/` is the **only** thing forge writes into your working tree rather than onto a branch
|
|
63
|
+
of its own. It is excluded from every captured diff, so a reviewer never sees it as the
|
|
64
|
+
dwarf's work — but it *will* show up as untracked in `git status` after your first run.
|
|
65
|
+
|
|
66
|
+
## What to do with `.forge/` when it appears
|
|
67
|
+
|
|
68
|
+
forge never commits it. That decision is yours, and there are three sane answers:
|
|
69
|
+
|
|
70
|
+
| You want | Do this |
|
|
71
|
+
|---|---|
|
|
72
|
+
| memory shared with your team, and with future you | **commit `.forge/`** — the intended case; every teammate's runs then build on the same facts |
|
|
73
|
+
| memory kept to your machine | `echo '.forge/' >> .gitignore` |
|
|
74
|
+
| no memory at all | `--no-memory`, or `FORGE_MEMORY=off` in your environment |
|
|
75
|
+
|
|
76
|
+
Two things that are easy to get wrong:
|
|
77
|
+
|
|
78
|
+
**Gitignoring is not the same as turning it off.** An ignored `.forge/` is still written and
|
|
79
|
+
still injected into every prompt — it just stops travelling. Only `--no-memory` /
|
|
80
|
+
`FORGE_MEMORY=off` actually disables the feature.
|
|
81
|
+
|
|
82
|
+
**Never commit one file and ignore the other.** `memory.md` is *rebuilt from* `ledger.tsv` on
|
|
83
|
+
every write, so a clone that has the memory but not the ledger loses everything the moment
|
|
84
|
+
anyone runs forge there — the rebuild regenerates `memory.md` from that machine's own short
|
|
85
|
+
ledger and the shared facts are gone, silently. Verified behaviour, not a theoretical risk:
|
|
86
|
+
|
|
87
|
+
```
|
|
88
|
+
teammate A commits memory.md only, ledger.tsv gitignored
|
|
89
|
+
memory.md: - pytest -q runs the suite
|
|
90
|
+
- src/a.py is generated [src/a.py]
|
|
91
|
+
|
|
92
|
+
teammate B clones, runs forge once, learns one unrelated thing
|
|
93
|
+
memory.md: - b learned something new ← A's two facts destroyed
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Commit both, or neither. If you commit them, expect `ledger.tsv` to gain a line per dispatch
|
|
97
|
+
— small, but it will appear in your diffs; that growth is the price of the recurrence
|
|
98
|
+
counting that keeps `memory.md` honest.
|
|
99
|
+
|
|
100
|
+
If you contribute to someone else's repo, gitignore it. A pull request containing forge's
|
|
101
|
+
notes to itself is noise to a reviewer who does not use forge.
|
|
102
|
+
|
|
103
|
+
## `scripts/forge-memory.sh`
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
forge-memory.sh inject <repo> <planner|dwarf|qa> # the block to prepend to a prompt
|
|
107
|
+
forge-memory.sh note <planner|dwarf|qa> # the instruction to append
|
|
108
|
+
forge-memory.sh record <repo> --last <file> --role <r> [--run-id X] [--task T]
|
|
109
|
+
[--model M] [--verdict V] [--duration S]
|
|
110
|
+
forge-memory.sh show <repo>
|
|
111
|
+
forge-memory.sh prune <repo> # re-apply staleness and the cap now
|
|
112
|
+
forge-memory.sh spend <repo> [--run <run-id>] # what forge has cost this repo
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`record` appends to the ledger and then rebuilds `memory.md` from the whole ledger — the
|
|
116
|
+
rebuild is idempotent, so a crashed run can leave the ledger a row short but can never leave
|
|
117
|
+
memory half-written. Editing `memory.md` by hand therefore does not stick; remove the ledger
|
|
118
|
+
rows instead.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# what would a dwarf actually be told about this repo?
|
|
122
|
+
bash scripts/forge-memory.sh inject ~/dev/api dwarf
|
|
123
|
+
|
|
124
|
+
# start over
|
|
125
|
+
rm -rf ~/dev/api/.forge
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
## Spend
|
|
129
|
+
|
|
130
|
+
The ledger already holds a row per dispatch and is never injected into a prompt, so
|
|
131
|
+
accounting from it costs nothing:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
bash scripts/forge-memory.sh spend ~/dev/api
|
|
135
|
+
```
|
|
136
|
+
```
|
|
137
|
+
14 dispatch(es) across 3 run(s), 1h12m of model time
|
|
138
|
+
Durations sum concurrent work, so they exceed the wall-clock of a parallel run.
|
|
139
|
+
|
|
140
|
+
by model disp total mean
|
|
141
|
+
sol:xhigh 4 38m20s 9m35s
|
|
142
|
+
opus 7 22m10s 3m10s
|
|
143
|
+
gemini:low:antigravity 3 11m30s 3m50s
|
|
144
|
+
|
|
145
|
+
by role disp total
|
|
146
|
+
dwarf 7 49m50s
|
|
147
|
+
qa 7 22m10s
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
**Time and dispatch counts, never tokens or money.** The six CLIs expose usage differently
|
|
151
|
+
or not at all, and a number forge cannot actually measure would be worse than no number.
|
|
152
|
+
|
|
153
|
+
`duration_s` is the ledger's tenth column, appended at the end on purpose: a ledger written
|
|
154
|
+
by an older forge — possibly already committed and shared — has nine fields, and awk yields
|
|
155
|
+
an empty string for the missing one instead of shifting every column. Those rows are counted
|
|
156
|
+
but not timed, and the output says so.
|
package/docs/models.md
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# Models, efforts and harnesses
|
|
2
|
+
|
|
3
|
+
[← README](../README.md) · [All docs](README.md)
|
|
4
|
+
|
|
5
|
+
How forge names a model (`alias:effort:harness`), what each harness offers, how effort is validated and clamped, when to drop the sandbox, and how to teach forge a new model.
|
|
6
|
+
|
|
7
|
+
## The spec: `alias:effort:harness`
|
|
8
|
+
|
|
9
|
+
Everywhere forge takes a model, it takes the same three-field spec. Each field after the
|
|
10
|
+
first is optional:
|
|
11
|
+
|
|
12
|
+
| Spec | Resolves to |
|
|
13
|
+
|---|---|
|
|
14
|
+
| `sol` | alias `sol`, default effort for the role, its default harness (codex) |
|
|
15
|
+
| `sol:xhigh` | `sol` at `xhigh` effort on its default harness |
|
|
16
|
+
| `sol:xhigh:openclaude` | same model and effort, run through the `openclaude` CLI |
|
|
17
|
+
| `opus::claude` | explicit harness, effort left to the role default — the middle field may be empty |
|
|
18
|
+
| `gpt-5.6-sol::codex` | not in the registry? passed through as a literal model id — but only with an explicit harness |
|
|
19
|
+
|
|
20
|
+
An alias with no registry row still works, provided you name the harness. forge won't guess
|
|
21
|
+
a default harness for a model it has never seen; that guess would silently spend quota on
|
|
22
|
+
the wrong provider.
|
|
23
|
+
|
|
24
|
+
**Role defaults when you leave things out:**
|
|
25
|
+
|
|
26
|
+
- `--dwarf` missing → forge **asks**. Every dispatch spends real quota on your account, so
|
|
27
|
+
picking for you isn't its call.
|
|
28
|
+
- `--qa` missing → `opus`, which runs locally through `claude` and adds no cost surprise.
|
|
29
|
+
- effort missing → dwarf builds at `medium`, qa reviews at `xhigh`. A reviewer thinking
|
|
30
|
+
*less* than the implementer tends to wave things through, which wastes the stage entirely.
|
|
31
|
+
|
|
32
|
+
## Effort, ceilings and clamping
|
|
33
|
+
|
|
34
|
+
Every harness has its own effort ladder:
|
|
35
|
+
|
|
36
|
+
| Harness | Ladder |
|
|
37
|
+
|---|---|
|
|
38
|
+
| codex | `low medium high xhigh max ultra` |
|
|
39
|
+
| claude | `low medium high xhigh max` |
|
|
40
|
+
| openclaude | `low medium high xhigh max ultracode` |
|
|
41
|
+
| antigravity | `low medium high` |
|
|
42
|
+
| grok | `low medium high xhigh max` (grok-4.7 offers up to `xhigh`) |
|
|
43
|
+
| opencode | provider-specific; forwarded verbatim |
|
|
44
|
+
|
|
45
|
+
And support is more irregular than one ceiling per backend:
|
|
46
|
+
|
|
47
|
+
- **Ceilings** — `luna` tops out at `max` on codex, so `luna:ultra` clamps down.
|
|
48
|
+
- **Gaps** — `gemini-pro` offers `low` and `high` but *not* `medium`, so `gemini-pro:medium`
|
|
49
|
+
clamps to `low`.
|
|
50
|
+
- **None at all** — Claude models on antigravity take no effort flag; passing one errors, so
|
|
51
|
+
forge reports it and drops it.
|
|
52
|
+
|
|
53
|
+
When forge clamps, it says so (`clamped=ultra -> max`) and the orchestrator passes that on
|
|
54
|
+
to you. Getting less thinking than you paid for, silently, is exactly the failure worth ten
|
|
55
|
+
lines of validation to prevent — and it's a real one: **codex accepts an invalid effort
|
|
56
|
+
without complaint and quietly runs at some other level.** forge validates first.
|
|
57
|
+
|
|
58
|
+
A word that isn't a recognized effort at all is an error, not a clamp.
|
|
59
|
+
|
|
60
|
+
## Yolo mode
|
|
61
|
+
|
|
62
|
+
`--yolo-dwarf` and `--yolo-qa` drop that role's sandbox and approval gates:
|
|
63
|
+
|
|
64
|
+
| Harness | Flag used |
|
|
65
|
+
|---|---|
|
|
66
|
+
| codex | `--dangerously-bypass-approvals-and-sandbox` |
|
|
67
|
+
| claude / openclaude / antigravity | `--dangerously-skip-permissions` |
|
|
68
|
+
| opencode | `--auto` |
|
|
69
|
+
| grok | `--always-approve` |
|
|
70
|
+
|
|
71
|
+
Each flag applies to **one role only**, so `--yolo-dwarf` alone is the normal combination:
|
|
72
|
+
the implementer gets a free hand, the reviewer stays fenced in and still can't edit the code
|
|
73
|
+
it's judging.
|
|
74
|
+
|
|
75
|
+
You mostly don't need it. On codex, claude and openclaude the sandboxed roles are already
|
|
76
|
+
useful — the dwarf edits files and runs tests, qa reads and runs commands but cannot write.
|
|
77
|
+
Reach for yolo when a role genuinely must escape the sandbox: installing dependencies,
|
|
78
|
+
writing outside the repo, touching the network.
|
|
79
|
+
|
|
80
|
+
> **Antigravity is the exception worth knowing.** Without yolo it cannot run *any* shell
|
|
81
|
+
> command, and a single denied command **aborts the whole run** rather than degrading — a
|
|
82
|
+
> dwarf that tries to run the test suite ends with an error and an empty diff. forge detects
|
|
83
|
+
> this and appends a note telling that role not to attempt commands, which is why an
|
|
84
|
+
> antigravity dwarf reports its work as unverified by execution. If you want a Gemini dwarf
|
|
85
|
+
> that can actually run the tests it writes, it needs `--yolo-dwarf`.
|
|
86
|
+
|
|
87
|
+
## Model registry
|
|
88
|
+
|
|
89
|
+
`registry.tsv` is the single source of truth for alias resolution. Four tab-separated
|
|
90
|
+
columns, plus an optional fifth:
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
# alias harness model effort [timeout]
|
|
94
|
+
sol codex gpt-6.1-sol <=ultra
|
|
95
|
+
sol openclaude gpt-6.1-sol <=max
|
|
96
|
+
gemini-pro antigravity gemini-3.1-pro low,high
|
|
97
|
+
opus antigravity claude-opus-4-6-thinking none
|
|
98
|
+
grok grok grok-4.7 <=xhigh
|
|
99
|
+
grok opencode github-copilot/grok-4.6 -
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| `effort` form | Means |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `<=X` | any effort on that harness's ladder up to and including `X` |
|
|
105
|
+
| `a,b,c` | exactly these efforts — gaps are real, and this is how they're expressed |
|
|
106
|
+
| `none` | takes no effort flag at all; passing one errors |
|
|
107
|
+
| `-` | no validated ladder; forwarded verbatim |
|
|
108
|
+
|
|
109
|
+
The optional fifth column, `timeout`, is a per-model default dispatch limit in seconds (`-`
|
|
110
|
+
or absent = the built-in default). It applies only when neither `--timeout` nor
|
|
111
|
+
`FORGE_TIMEOUT` was given and is scaled by the resolved effort (see
|
|
112
|
+
[timeout precedence](commands.md#scriptsforge-dispatchsh)). Rows without it behave exactly as before.
|
|
113
|
+
|
|
114
|
+
**Row order is meaningful:** the *first* row for an alias is that alias's default harness.
|
|
115
|
+
`--dwarf sol` means sol on codex; `--dwarf sol:high:openclaude` moves the same model
|
|
116
|
+
elsewhere.
|
|
117
|
+
|
|
118
|
+
Adding a model is one line in this file. Nothing else needs to change.
|