8e6ee31596
ci / test (push) Successful in 6m18s
History-free snapshot of the private development repository at cbe45c5 (2026-09-09). Code, docs and public catalogue only; no plant or job knowledge layers, no client or job names (tools/scrub.py clean). The intro deck is left out of this snapshot because its slide images could not be checked for job drawings.
326 lines
16 KiB
Markdown
326 lines
16 KiB
Markdown
# Installing WillTools
|
|
|
|
Precast engineering tooling: buttons across four ribbon panels — three
|
|
modelling/ticketing tools plus Snapshot behind **Tools**' `Modeling`
|
|
pulldown, five set-up commands behind the **Config** pulldown, two finished
|
|
and four unfinished analysis tools behind **DesignHub**, and GOD_MOD — plus a
|
|
live bridge that lets Claude Code drive a running Revit session. `README.md`
|
|
has the table of what each one does.
|
|
|
|
Developed and exercised on **Revit 2025.4 · pyRevit 6.5.3 · IronPython 2.7.12**.
|
|
Everything else in this file is about what to check when one of those differs.
|
|
|
|
---
|
|
|
|
## 1. Prerequisites
|
|
|
|
| | Needed for | Notes |
|
|
| --- | --- | --- |
|
|
| **Revit** 2021 or newer | everything | see [§6](#6-a-different-revit-version) before assuming a version works |
|
|
| **pyRevit** 4.8 or newer | everything | <https://github.com/pyrevitlabs/pyRevit/releases> |
|
|
| **Python 3.9+** on the host | `tools/*.py`, `panellift` | not needed to use the ribbon buttons themselves |
|
|
| **Claude Code CLI** | GOD_MOD button only | optional — every engineering button runs without it |
|
|
|
|
**No button that does engineering work needs the CLI, the network or the
|
|
bridge.** Design Extract reads a design packet in-process with a
|
|
regex/geometry parser and never leaves Revit. GOD_MOD is the only button
|
|
that launches the CLI, and it is the developer button.
|
|
|
|
### The Windows Store Python trap
|
|
|
|
If `python` at a terminal prints something like *"Python was not found; run
|
|
without arguments to install from the Microsoft Store"* and then exits
|
|
non-zero, that is not really Python — it is a stub Windows ships in
|
|
`WindowsApps` that only exists to point you at the Store. Every command in
|
|
this file, and `tools/setup_machine.py` itself, will fail in confusing ways
|
|
if that stub is what `python` resolves to.
|
|
|
|
Fix: install Python from <https://www.python.org/downloads/> (check "Add
|
|
python.exe to PATH" during setup), then open a **new** terminal and confirm
|
|
with `python --version` — it should print a real version number, not a
|
|
Store prompt. If both a real install and the Store stub are on `PATH`, the
|
|
Store one usually wins unless you reorder `PATH` or use the `py` launcher
|
|
instead (`py -3 tools/setup_machine.py`).
|
|
|
|
## 2. Install
|
|
|
|
1. **Clone or unzip anywhere you can write to.** A checkout under your user
|
|
folder is easiest. The path is not hardcoded anywhere; the buttons find
|
|
the folder relative to themselves.
|
|
|
|
```bash
|
|
git clone https://github.com/will123song/pyrevit-dev.git
|
|
cd pyrevit-dev
|
|
```
|
|
|
|
2. **Run the setup script.** It installs the host-side Python dependencies
|
|
(`requirements.txt`: PyYAML, pytest, pymupdf) and then *proves* the
|
|
install rather than just reporting exit codes — it runs `panellift`
|
|
against a published worked example and checks the computed stress
|
|
against the number the article prints.
|
|
|
|
```bash
|
|
python tools/setup_machine.py
|
|
```
|
|
|
|
Read the output top to bottom. `PASS` and `SKIP` lines need nothing from
|
|
you; a `SKIP` is a deliberate non-answer (this distribution ships no
|
|
knowledge layers, so the checks that need one stand down cleanly — see
|
|
`knowledge/README.md`), not a problem. A `FAIL` line names what broke and
|
|
what to try — fix the first one and re-run; failures often cascade from
|
|
one root cause. Re-run any time with `--check` to just re-verify without
|
|
changing anything:
|
|
|
|
```bash
|
|
python tools/setup_machine.py --check
|
|
```
|
|
|
|
3. **Register the folder with pyRevit.** pyRevit tab → *Settings* →
|
|
*Custom Extension Directories* → add the folder that **contains**
|
|
`WillTools.extension` (the cloned folder itself, not the `.extension`
|
|
folder inside it) → *Save Settings*.
|
|
|
|
Equivalent to this in `%APPDATA%\pyRevit\pyRevit_config.ini`:
|
|
|
|
```ini
|
|
[core]
|
|
userextensions = ["C:\\path\\to\\pyrevit-dev"]
|
|
```
|
|
|
|
4. **Turn on the routes server** — only if you want the Claude Code bridge
|
|
(GOD_MOD). pyRevit → *Settings* → *Routes* → enable, port `48884`. Or:
|
|
|
|
```ini
|
|
[routes]
|
|
enabled = true
|
|
port = 48884
|
|
host = "127.0.0.1"
|
|
```
|
|
|
|
5. **Restart Revit.** Not "pyRevit → Reload" — see [§7](#7-the-one-rule).
|
|
|
|
A **WillTools** tab appears with four panels, left to right:
|
|
|
|
| Panel | |
|
|
| --- | --- |
|
|
| **Tools** | one button, **Modeling**, with a dropdown arrow (Reinforce Suggestion, Reinforcing QC, Ticket QC), plus **Snapshot** as a large button beside it. |
|
|
| **Setup** | one button, **Config**, with a dropdown arrow: Project Config, Design Extract, Self Check, Job Recon, Takeoff. |
|
|
| **Design** | one button, **DesignHub**, with a dropdown arrow: Panel Lift Check and Panel Analysis (finished), then Beam Analysis, Connection Analysis, Gravity and Lateral, marked `(WIP)`. |
|
|
| **Lab** | **GOD_MOD**, the CLI button. |
|
|
|
|
## 3. Check the install
|
|
|
|
Open a model and press **Self Check**. It probes every assumption these tools
|
|
make — Revit version, pyRevit version, Python engine, routes, library imports,
|
|
unit conversion, whether the CLI is findable, where the data folder resolved
|
|
to, whether a published checklist is available, and whether this model
|
|
actually follows the naming conventions the checks depend on — and prints
|
|
each as OK / WARN / FAIL.
|
|
|
|
Read the FAILs top-down; they cascade. A WARN never blocks a run, and on a
|
|
knowledge-less install several rows are *expected* to read WARN — that is
|
|
the "checks stand down" state, not a broken install.
|
|
|
|
## 4. Try it with no Revit at all
|
|
|
|
```bash
|
|
python -m panellift examples/example-panel.job.json -o report.html
|
|
```
|
|
|
|
That runs the PCI Chapter 8 handling check against a published worked
|
|
example (the ASPIRE Winter 2019 article) and writes an HTML report. It needs
|
|
no Revit, no model, and no knowledge layer — a good first thing to run right
|
|
after `tools/setup_machine.py` to confirm the arithmetic reaches you intact.
|
|
|
|
## 5. Using it on a real job
|
|
|
|
Per model, in order:
|
|
|
|
1. **Extract Design** — pick the design summaries, one extraction terminal
|
|
opens per design, each writes `specs/<DESIGN>.json`.
|
|
2. **Read the spec.** It is JSON on purpose. Fix anything wrong and fill in
|
|
`source.reviewed_by`. Nothing downstream questions the spec again.
|
|
3. **Ticket QC** — checks every piece of those designs against the spec.
|
|
**Needs a knowledge layer with the published checklist** — see below;
|
|
without one it refuses to open with a one-line reason.
|
|
4. Edit the `knowledge.yaml` files as you learn the job — see the layers
|
|
below for which one. They are re-read on every run — **no restart needed
|
|
for knowledge or spec edits.**
|
|
|
|
Everything lands in `_ticketqc/` **beside the model file**:
|
|
|
|
```
|
|
<model folder>/_ticketqc/
|
|
evidence/<DESIGN>.json raw scrape, no interpretation
|
|
specs/<DESIGN>.json the reviewable spec <-- read this
|
|
knowledge.yaml your hand-written rules
|
|
log/<DESIGN>.txt what the extractor printed
|
|
```
|
|
|
|
If the model is not saved, or its folder is read-only, it falls back to
|
|
`%APPDATA%\pyrevit-ticketqc\<model title>\`. Self Check tells you which.
|
|
|
|
### The knowledge layers, and why this distribution has none of them
|
|
|
|
Knowledge is not one file. It is read in layers, each overriding the one
|
|
before it key by key, and the split is by **how long the fact stays true**:
|
|
|
|
| layer | where | what belongs there |
|
|
| --- | --- | --- |
|
|
| defaults | `lib/vibe/knowledge.py` — in this repo | what the code assumes when nobody has said |
|
|
| catalogue | `knowledge/catalogue.yaml` — **not shipped** | published hardware ratings, plus the master shop-ticket checklist |
|
|
| **plant** | `%APPDATA%\WillTools\knowledge.yaml` — **not shipped** | naming conventions, casting-bed rails, **parameter names**, which catalogue item each part number is, rigging mode and dunnage position |
|
|
| **job** | `<model folder>/_ticketqc/knowledge.yaml` — **not shipped** | tolerances, and anything where this job departs from the plant |
|
|
| **project config** | wherever you put it — see below | the whole configuration, pinned to one job |
|
|
|
|
This distribution ships **none of the last four**, deliberately — see
|
|
[`knowledge/README.md`](knowledge/README.md) and
|
|
[`knowledge/catalogue.yaml.template`](knowledge/catalogue.yaml.template) for
|
|
the shape of what a real layer looks like, with every value blanked out.
|
|
Everything on **Tools** and **Setup** except Ticket QC runs without one,
|
|
reading only `knowledge.DEFAULTS`; Self Check reports every plant-specific
|
|
parameter role as unmapped until real layers are wired in. **Ask the owner**
|
|
for real layers when you have a job to run for real — see the README's
|
|
"What to ask the owner for."
|
|
|
|
Copying a job file forward to the next job should be nearly empty work — if
|
|
you find yourself copying a lot, it belongs in the plant layer. Set
|
|
`WILLTOOLS_KNOWLEDGE` to a full path to put the plant layer on a share so a
|
|
whole office reads one file. (`tools/setup_machine.py` deliberately leaves
|
|
this variable unset on a knowledge-less install — see its own comments if
|
|
you are wiring one in by hand rather than through the script.)
|
|
|
|
Handling decisions are plant knowledge too: `handling.stages[].rig_mode`
|
|
(`rolling` or `static`, blank inherits the global mode) and
|
|
`handling.dunnage.mode` (`lifter_rows` or `fraction`, with `fraction` such as
|
|
`0.2` for supports at 0.2L from each end). A plant that has not set dunnage
|
|
still gets transportation as a lift, and the report labels it as approximate.
|
|
|
|
Self Check prints which layers it found, and lists any parameter role still
|
|
unmapped.
|
|
|
|
### Pinning a configuration to a job
|
|
|
|
The **Project Config** button writes every layer above, merged, into one file
|
|
wherever you choose — the job folder on the share, usually, next to the rest
|
|
of the job's paperwork. From then on the model runs on that file, and it is
|
|
the last word: edit it and the next run uses what you wrote.
|
|
|
|
A one-line pointer at `<model folder>/_ticketqc/active-config.txt` records
|
|
where the file went. Press the button again to refresh the file from the
|
|
layers underneath, or to drop back to them.
|
|
|
|
Two things to know before you use it:
|
|
|
|
- **It freezes the plant layer.** The merged copy in the file contains the
|
|
plant's settings as values of its own, so while the file is live, fixing
|
|
something in the plant file no longer reaches this job. That is the point of
|
|
pinning, and it is also how a plant-wide correction quietly fails to arrive.
|
|
- **Do not move or delete the file without clearing the pointer.** Every
|
|
button refuses to run rather than falling back, because a run on the layered
|
|
files when you meant the pinned ones looks entirely normal and is not the
|
|
configuration you set.
|
|
|
|
A new model has no pointer, so it starts from the layered files and gets its
|
|
own configuration the first time the button is pressed there.
|
|
|
|
### Nothing is hard-coded, including public facts
|
|
|
|
The obvious things stay out of the code: client names, job names, parameter
|
|
names. Less obviously, **so do facts that are perfectly public.** A lifting
|
|
insert's rated working load is printed in the manufacturer's catalogue and is
|
|
no secret — but written into a `.py` file it becomes a number nobody can
|
|
correct without a release, and it will be wrong eventually.
|
|
|
|
So a rating is a lookup, and it takes two hops, because a plant models its own
|
|
part number rather than the manufacturer's designation:
|
|
|
|
```
|
|
family name in the model
|
|
-> lifters.catalogue_alias plant layer: P000 -> SAMPLE_ANCHOR_4T
|
|
-> catalogue.inserts product data: SAMPLE_ANCHOR_4T -> 8000 lb
|
|
```
|
|
|
|
Each hop fails differently and is fixed in a different place, so the check
|
|
says which one is missing rather than reporting a bare "unknown". The same
|
|
shape applies to anything else with a published number behind a local name.
|
|
|
|
## 6. A different Revit version
|
|
|
|
The code is written against the stable parts of the API and guards the two
|
|
places Autodesk moved the floor:
|
|
|
|
- **`UnitTypeId`** (Revit 2021+) with a `DisplayUnitType` fallback for 2020 and
|
|
older, resolved once at import so an old host cannot break the whole library.
|
|
- **`ElementId.IntegerValue`** → **`.Value`** (Revit 2024+), read defensively
|
|
at every site.
|
|
|
|
Also version-proofed: the `/exec` snippet namespace skips API types that a
|
|
given release does not have, rather than failing to register.
|
|
|
|
**Not verified anywhere but 2025.** If you move to 2023 or 2026, press Self
|
|
Check first — it reports the host version as a WARN and exercises the imports
|
|
and a unit round-trip, which is where a version problem shows up first.
|
|
|
|
### A different Python engine
|
|
|
|
pyRevit chooses the interpreter. This extension is written in the intersection
|
|
of Python 2 and 3 — no f-strings, no `print` statement, no implicit integer
|
|
division, no `pathlib`, and `unicode`/`basestring` resolved in
|
|
`lib/vibe/compat.py` — so IronPython 2.7, IronPython 3 and CPython 3 should all
|
|
load it. Only IronPython 2.7 has actually been run in anger; Self Check warns
|
|
on anything else.
|
|
|
|
## 7. The one rule
|
|
|
|
**Never use pyRevit → Reload. Restart Revit.**
|
|
|
|
pyRevit's routes handler is owned by the engine that first loaded it. A reload
|
|
tears that engine down and leaves the handler pointing at the corpse; the next
|
|
bridge request runs against a dead engine and takes Revit down with it —
|
|
an unhandled exception off the main thread, which Revit cannot catch.
|
|
|
|
There is no reload endpoint, `rvt.py` refuses to run after it sees two loads in
|
|
one Revit process, and a restart picks up new buttons just as well.
|
|
|
|
Restart Revit after: installing, adding or renaming a button, or adding an icon.
|
|
No restart needed after: editing a `script.py`, a spec, or `knowledge.yaml`.
|
|
|
|
## 8. What is in this distribution
|
|
|
|
```
|
|
pyrevit-dev/
|
|
├── INSTALL.md this file
|
|
├── README.md what the tool is, what needs knowledge
|
|
├── CONTRIBUTING.md tests, the no-client-data rule, PRs
|
|
├── examples/example-panel.job.json a published worked example, try this first
|
|
├── docs/ reference material and design docs
|
|
├── knowledge/README.md why this folder is otherwise empty
|
|
├── knowledge/catalogue.yaml.template the shape of the layer that isn't here
|
|
├── tools/setup_machine.py run this first
|
|
├── tools/rvt.py bridge client (host CPython 3)
|
|
├── panellift/ the handling-check engine, its own test suite
|
|
└── WillTools.extension/
|
|
├── startup.py registers the bridge routes
|
|
├── lib/vibe/ helpers, compat, designspec, knowledge,
|
|
│ ticketqc, ticketchecks, aiextract
|
|
└── WillTools.tab/ the ribbon, four panels left to right:
|
|
├── Tools.panel/ Modeling.pulldown/ — Reinforce
|
|
│ Suggestion · Reinforcing QC · Ticket QC —
|
|
│ plus Snapshot as the one remaining large
|
|
│ button
|
|
├── Setup.panel/ Config.pulldown/ — Project Config ·
|
|
│ Design Extract · Self Check · Job Recon ·
|
|
│ Takeoff
|
|
├── Design.panel/ DesignHub.pulldown/ — Panel Lift Check ·
|
|
│ Panel Analysis (finished, grouped here on
|
|
│ the owner's own instruction), then Beam
|
|
│ Analysis (WIP) · Connection Analysis (WIP)
|
|
│ · Gravity (WIP) · Lateral (WIP)
|
|
└── Lab.panel/ GOD_MOD
|
|
```
|
|
|
|
No model data or knowledge ships with it. `_ticketqc/` folders stay with
|
|
their models; knowledge layers come from the owner, on request — see the
|
|
README's "What to ask the owner for."
|