pi-revit 0.5.0 → 0.5.2

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/README.md CHANGED
@@ -1,768 +1,802 @@
1
- # pi-revit
2
-
3
- Native Revit tools for [Pi](https://pi.dev) — ask about, query, script, and modify the open
4
- Autodesk Revit model from your terminal.
5
-
6
- ```text
7
- You: how many levels in the model?
8
- Pi: calls get_model_overview → "There are 14 levels in the Revit model."
9
-
10
- You: select all structural columns
11
- Pi: get_model_overview → get_elements → manage_selection → "Selected 222 structural columns."
12
-
13
- You: rename level 'L1' to 'Ground Floor'
14
- Pi: get_model_overview → set_parameters → "Done — Level 'L1' is now 'Ground Floor'."
15
- ```
16
-
17
- ## How it works
18
-
19
- ```text
20
- Pi terminal session
21
- │ native tools (registered by the pi-revit extension)
22
- ▼
23
- localhost HTTP bridge ← per-start token; connection info in %APPDATA%\RevitBridge\
24
- │
25
- ▼
26
- headless Revit add-in ← no ribbon, no panels; just a bridge
27
- │ ExternalEvent queue (Revit API thread)
28
- ▼
29
- Revit API ← tool-owned model transactions; separate UI/file effects
30
- ```
31
-
32
- The extension discovers its tools from the bridge at startup (retrying in the background until
33
- Revit is up), so the tool list always matches what the add-in serves. Everything between Pi and
34
- Revit is local-machine only; note that Pi sends conversation context and tool results to your
35
- selected LLM provider, like any Pi session.
36
-
37
- ## Safety model
38
-
39
- Be deliberate about pointing an LLM at a real project model. The add-in enforces what it can
40
- enforce mechanically, and is honest about what it cannot:
41
-
42
- - Tool metadata describes its classification; UI actions such as selection and view
43
- activation can change state even when `write` is false. Confirmation policy belongs
44
- to the client. Exact document targeting is enforced separately as described below.
45
- - Parameter writes, C# scripts, temporary isolation, and IFC export own named Revit
46
- transactions. Failure handling is attached after transaction start, and results
47
- check transaction outcomes before claiming commit or rollback. `set_parameters`
48
- defaults to partial success; `atomic: true` rolls back the batch if any update
49
- fails, and `preview: true` rolls back proposed model changes after validation.
50
- Inspect every failed update, the validation status, and `commitWarnings`.
51
- An unconfirmed rollback is reported as such.
52
- - `execute_csharp` is an unrestricted escape hatch by design — scripts have full CLR access.
53
- Treat it like giving the agent a macro editor, on a model you have saved or can restore.
54
- - `execute_csharp` has a dialog guard that attempts dismissive responses to dialogs
55
- raised while the script runs. It does not establish that every Revit dialog or
56
- failure mode can be handled automatically.
57
- - A model transaction does not undo filesystem output or earlier selection/zoom
58
- changes. A failed export can leave incomplete files; its error reports the output
59
- location and observed changed files. An isolation failure reports any earlier
60
- selection action that already completed.
61
-
62
- ### Target the exact open document
63
-
64
- Call `get_model_overview` for the intended model and copy `project.documentId`
65
- unchanged into `expected_document_id` on subsequent operations:
66
-
67
- ```json
68
- {
69
- "expected_document_id": "<project.documentId from the current overview>",
70
- "updates": [{ "element_id": 12345, "parameter": "ALL_MODEL_INSTANCE_COMMENTS", "value": "Reviewed" }]
71
- }
72
- ```
73
-
74
- Replace the placeholders with the current document ID and an actual element ID.
75
- The exact ID is required for `set_parameters`, `transform_elements`,
76
- `delete_elements`, `change_element_types`, `manage_views`, `manage_sheets`,
77
- `manage_sheet_placements`, `manage_schedules`, `create_tags`, `execute_csharp`,
78
- `export_documents`, and `open_view`,
79
- including supported previews. `manage_sheet_placements` requires it even for its
80
- read-only `list` action because the tool is classified as write-capable.
81
- It is also required for selection/zoom
82
- changes and any `manage_selection` call with `isolate_in_view: true`, including
83
- action `get`. Pure reads may omit it; a supplied ID is always checked.
84
-
85
- The identity represents one currently open native document in one loaded bridge
86
- session. It is not a persistent project ID, path, export-folder key, or credential.
87
- Closing/reopening the document or restarting the bridge invalidates prior IDs.
88
- Read the intended document's overview again after those transitions. The guard
89
- checks the actual target on Revit's API thread immediately before execution.
90
-
91
- Legacy `expected_document` titles remain an optional additional sanity check.
92
- A title alone no longer satisfies the required guard, even when it matches.
93
- Clients must refresh discovery and supply the new field after upgrading to 0.3.0.
94
-
95
- Practical advice: work on saved models, keep worksharing backups/central protection as usual,
96
- and review the agent's summary of what changed after any write session.
97
-
98
- ## Requirements
99
-
100
- - Windows 10/11
101
- - Autodesk Revit 2025, 2026, or 2027
102
- - .NET SDK matching your Revit: [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) for Revit 2025/2026, [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) for Revit 2027
103
- - [Node.js 20.3+](https://nodejs.org/)
104
- - [Pi coding agent](https://pi.dev): `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`
105
-
106
- ## Install
107
-
108
- ### One-command install
109
-
110
- Close Revit, then in PowerShell:
111
-
112
- ```powershell
113
- npx.cmd -y pi-revit
114
- ```
115
-
116
- This installs the Pi package, builds and deploys the Revit bridge add-in, creates the
117
- `Documents\pi-revit` workspace, and installs the global `pi-revit` command.
118
-
119
- The installer first checks the selected .NET SDK. If it is missing or too old,
120
- installation stops with the required version, a download link, and retry steps.
121
- Interactive terminals also offer to open the download page. Revit uses a runtime
122
- to run; compiling this add-in also needs the SDK. For Revit 2027, install the
123
- [.NET 10 SDK for Windows x64](https://dotnet.microsoft.com/en-us/download/dotnet/10.0),
124
- reopen PowerShell, and rerun the installer. Existing .NET versions can stay installed.
125
- If an older SDK is still selected, check `dotnet --list-sdks`, your `PATH`, and any
126
- `global.json` in the current directory or its parents.
127
-
128
- Start Revit (click **Always Load** on the unsigned add-in prompt once) and open any
129
- project. No panel or ribbon appears — the add-in is headless.
130
-
131
- ### Manual npm install
132
-
133
- Use this if you prefer to run each step yourself:
134
-
135
- ```powershell
136
- # 1. Install the Pi package from npm. This registers the pi-revit extension and skill.
137
- pi install npm:pi-revit
138
-
139
- # 2. Go to the installed package folder.
140
- cd "$env:USERPROFILE\.pi\agent\npm\node_modules\pi-revit"
141
-
142
- # 3. Build + deploy the Revit add-in (RevitBridge.dll + the Roslyn DLLs for execute_csharp).
143
- npm.cmd run deploy
144
-
145
- # 4. Create the workspace and global pi-revit command.
146
- npm.cmd run setup
147
- ```
148
-
149
- For a non-default Revit install location, use the PowerShell deploy script directly and pass
150
- `-RevitVersion` / `-RevitApiPath`, e.g.:
151
-
152
- ```powershell
153
- powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -RevitVersion 2027 -RevitApiPath "D:\Autodesk\Revit 2027"
154
- ```
155
-
156
- ### Source install
157
-
158
- Use this if you want to run directly from the GitHub checkout instead of the npm package:
159
-
160
- ```powershell
161
- git clone https://github.com/Triavision-ai/pi-revit.git
162
- cd pi-revit
163
- powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1
164
- pi install ./
165
- powershell -ExecutionPolicy Bypass -File scripts\setup-workspace.ps1
166
- ```
167
-
168
- ### Upgrading from 0.2.x
169
-
170
- **Breaking change:** writes and UI mutations now require `expected_document_id`.
171
- Close Revit, update the Pi package and redeploy the add-in using the installation
172
- steps above, then restart Revit and start a fresh Pi session. Both components must
173
- be updated. Call `get_model_overview` and copy `project.documentId` into subsequent
174
- mutating calls; a legacy `expected_document` title alone is insufficient. Refresh
175
- the ID after closing/reopening a document or restarting Revit.
176
-
177
- The exact-ID requirement was introduced in 0.3.0 and also applies to later
178
- releases. `ping` reports package/add-in version mismatches. Setup preserves an
179
- existing workspace `AGENTS.md`; merge updated identity, saved-result retrieval,
180
- output-folder guidance and current skill/manual routing from [the workspace template](workspace/AGENTS.md)
181
- into older workspaces as needed.
182
-
183
- ## Use it
184
-
185
- Open **any terminal** — PowerShell, CMD, or Windows Terminal — and type:
186
-
187
- ```powershell
188
- pi-revit
189
- ```
190
-
191
- That's all. Pi starts with the Revit tools ready:
192
-
193
- ```text
194
- > give me a model overview
195
- > how many doors per level?
196
- > select all structural columns
197
- ```
198
-
199
- **How this works:** the setup step placed a small `pi-revit` command in the same folder as the
200
- `pi` command itself. That folder is on your system PATH — which is exactly why *every* terminal
201
- finds `pi-revit`, with no extra configuration. When you run it, it switches to your workspace at
202
- `Documents\pi-revit` and starts Pi there, so your conventions file (`AGENTS.md`) loads
203
- automatically and all Revit session history lives in one predictable place (`pi-revit -c`
204
- continues the last session). The Revit tools themselves are installed globally in Pi, and the
205
- extension discovers them live from the bridge inside Revit each time a session starts.
206
-
207
- **Per model, automatically:** files sort themselves. Exports land in
208
- `Documents\pi-revit\Models\<model title>--<identity hash>\exports`. The suffix derives
209
- from the normalized saved-file path, cloud region/project/model identity, or Revit
210
- Server path. Distinct saved paths therefore use different destinations even when
211
- their titles or inherited project IDs match. Save As to another path selects a new
212
- destination. Unsaved models or unavailable persistent identities use a token stable
213
- only for that open document; their destination may change after reopening.
214
-
215
- `model.txt` records the identity used. Existing title-only directories remain
216
- untouched; upgrading does not migrate or merge old exports. An explicit
217
- `output_dir` still overrides the default. File attribution uses a directory
218
- snapshot, so avoid unrelated concurrent writers in a shared output directory.
219
-
220
- Plain `pi` from any folder also works; `pi-revit` just adds the right working folder on top.
221
-
222
- ## Guidance and contributor documentation
223
-
224
- The [PI-Revit skill](skills/pi-revit/SKILL.md) is a short entry point. It links
225
- shared execution/recovery/visual rules, workflows, and a
226
- [tool index](skills/pi-revit/references/tool-index.md) with one focused manual
227
- per public tool. Read only the detail relevant to the current task.
228
-
229
- `find_revit_tools` now includes the six Pi-side utilities as well as discovered
230
- bridge tools. Use `scope: "documentation"` to find packaged manuals while Revit
231
- is closed, without contacting the bridge or activating tools:
232
-
233
- ```json
234
- { "scope": "documentation", "names": ["manage_sheets", "manage_sheet_placements"] }
235
- ```
236
-
237
- Read a returned `documentation.path` for details. Registration, activation and
238
- reading a manual are separate actions; a manual is not proof that the selected
239
- bridge supports the tool. Current input schemas remain authoritative.
240
-
241
- Every tool declares what it does **not** cover and what to use instead: another
242
- tool, specific Revit API members, a user action, or "the Revit API does not offer
243
- this" with the evidence. `find_revit_tools` shows these limits with each result.
244
- When no tool matches a request, it returns the remaining route (check the API
245
- documentation, then use custom code) instead of an empty list. Missing a dedicated
246
- tool is therefore never a reason to call an operation impossible. Search with
247
- English task words; you can talk to Pi in any language, and it translates its searches.
248
-
249
- A short PI-Revit protocol is always in Pi's context, whatever the task and whether
250
- or not the skill is read. It covers:
251
-
252
- - checking capability before saying no;
253
- - doing only what was asked, verifying it with the tool's declared method, then
254
- stopping and reporting;
255
- - naming evidence;
256
- - document identity;
257
- - replying in your language.
258
-
259
- If Pi keeps re-checking and adjusting something that is already done, the extension
260
- asks it to compare against your request and report, and to offer extras as suggestions.
261
-
262
- `ping` also reports what is actually loaded: the extension package, guidance
263
- revision, source revision and, per tool, whether the manual matches the connected
264
- bridge's exact contract (`contract_match` / `contract_changed` / `undocumented`).
265
-
266
- For source changes, start with [AGENTS.md](AGENTS.md) and the
267
- [architecture guide](docs/architecture.md). They explain where tools, operating
268
- rules, workflows, future Revit subject skills and API guidance belong. The
269
- [evaluation guide](docs/evaluation.md) records offline checks and the live
270
- measurements still needed before claiming a speed or reliability improvement.
271
-
272
- ## Tools
273
-
274
- | Tool | What it does |
275
- |---|---|
276
- | `find_revit_tools` | Find native/bridge tools and their manuals; activate specialist tools or look up documentation offline. |
277
- | `ping` | Is the bridge reachable? Revit version |
278
- | `manage_revit_instances` | List reachable local Revit sessions or select the target for this Pi session |
279
- | `get_model_overview` | Project info, units, levels, grids, category counts — call first |
280
- | `get_model_coordinates` | Read base points and site/project locations; map internal points through the active shared coordinates |
281
- | `get_elements` | Query/count elements: parameter filters, optional parameter values, pagination |
282
- | `summarize_elements` | Count all matching elements by category, type, level, or raw parameter value |
283
- | `manage_element_sets` | Create, list, read, or forget temporary snapshots of matching host elements |
284
- | `get_element_details` | Parameter values, location, bounding box, materials per element |
285
- | `get_element_types` | Element types / family symbols, optional placed-instance counts |
286
- | `get_linked_models` | Direct Revit link instances, load status, document identities, placement transforms |
287
- | `get_linked_elements` | Query/count one loaded link, with optional bounding boxes in host coordinates |
288
- | `query_spatial_elements` | Find host elements by approximate bounding-box intersection or containment in a region |
289
- | `measure_geometry` | Measure exact point distance or approximate host-element bounding-box separation |
290
- | `get_schedules` | List schedules or read fields, widths, specifications, sort/filter rules, and displayed cells |
291
- | `get_schedule_fields` | Discover eligible field parameter/type pairs for an existing schedule |
292
- | `manage_schedules` | Create or configure regular schedules, including fields, widths, sorting, and filters |
293
- | `create_tags` | Create host element, room, space, or area tags, with partial/atomic batches and preview |
294
- | `get_element_relationships` | Host, type, level, members, joined geometry, and logical dependents of one element |
295
- | `manage_selection` | Get/set/clear the selection, zoom, temporary isolate |
296
- | `open_view` | Activate a view or sheet in the Revit UI (like double-clicking it in the browser) |
297
- | `set_parameters` | Bulk parameter writes and renames, with previews and optional atomic batches |
298
- | `transform_elements` | Move, copy, or rotate a whole selection, with explicit units and preview |
299
- | `delete_elements` | Preview or delete selected elements and report Revit's full deletion set |
300
- | `change_element_types` | Change types per target, with preview and optional atomic rollback |
301
- | `manage_views` | Create plans, isometric 3D views, or sections; duplicate or update views |
302
- | `manage_sheets` | Create, rename, or renumber sheets, with an optional titleblock at creation |
303
- | `manage_sheet_placements` | List, place, or move viewports and schedule instances in sheet paper space |
304
- | `search_api_docs` | Search the offline Revit API docs (works with no document open) |
305
- | `execute_csharp` | Run a C# script with separate JSON inputs in one auto-managed transaction |
306
- | `manage_revit_scripts` | Save immutable local script versions, inspect source, run an exact version, and read history |
307
- | `capture_view` | PNG snapshot of a view to a temp file (read the returned path to see it) |
308
- | `export_documents` | PDF/DWG/PNG/IFC export of sheets and views — sorted into `Models\<title>--<identity hash>\exports` |
309
- | `get_model_health` | Warnings grouped + worksets, phases, design options audit |
310
- | `read_revit_result` | Read bounded fragments of a saved large tool result; extension-only, no Revit call |
311
- | `get_revit_operation` | Inspect a bridge operation receipt and its retained result without waiting for the model thread |
312
-
313
- ### Choose a Revit instance
314
-
315
- `manage_revit_instances` defaults to `action: "list"`. It reports reachable
316
- local bridge sessions with an opaque `bridge_id`, process ID, Revit/add-in
317
- versions, selection status, and operation-tracking support. Choose the intended
318
- session with `action: "select"` and the exact returned `bridge_id`. Selection
319
- belongs to the current Pi extension session; it does not activate a document
320
- or change the model.
321
-
322
- When the first connection finds a sole instance, Pi binds to it automatically.
323
- If several instances are available before a target is bound, explicitly select
324
- one before model calls. Once bound, Pi keeps that exact bridge session. If it
325
- closes or restarts, calls fail instead of switching to another instance, even
326
- when only one remains. List again and explicitly select the intended new
327
- identity. Selection refreshes the tool catalogue; check `tool_catalog_ready`,
328
- and retry `ping` if discovery has not completed. Then read `get_model_overview`
329
- for a fresh document identity before continuing work.
330
-
331
- Each current bridge publishes its own discovery file in
332
- `%APPDATA%\RevitBridge\instances\<bridgeId>.json`. The legacy `bridge.json`
333
- discovery file is still supported. Older bridges without a generation ID receive
334
- an opaque hash selector in the instance list; copy it unchanged rather than
335
- constructing one. Legacy discovery can expose only the older instance named by
336
- that shared file, so updating each add-in enables discovery of all instances.
337
-
338
- Operation receipts and identical `_operation_id` retries route to the original
339
- bridge encoded in the operation ID, even after selecting a different instance.
340
- They do not switch the target for new calls. If the original bridge is unavailable,
341
- the request fails without sending the action to another session.
342
-
343
- ### Read project coordinates
344
-
345
- `get_model_coordinates` reads project/survey base points, the active project
346
- location, site data, and paginated project locations. Supply a length `unit`;
347
- optional `points` accepts up to 100 positions along document internal axes.
348
- Revit's active project location maps those points to shared coordinates. Returned
349
- lengths use the requested unit, while angles and latitude/longitude use degrees.
350
- Location pages default to 50 entries (maximum 100); follow `next_offset`.
351
- The tool requires a project document, makes no coordinate changes, and does not
352
- infer a GIS coordinate reference system or datum.
353
-
354
- ### Query regions and measure distances
355
-
356
- `query_spatial_elements` takes a region `min`/`max` in the document's internal
357
- axes and an explicit length `unit`. It compares host-element axis-aligned
358
- bounding boxes with the region, using `intersects` (default) or `inside`.
359
- Containment requires the whole box to fit; touching boundaries are included.
360
- The nested `query` uses whole-scope element filters and must match at most
361
- 10,000 candidates before region testing. Query-level paging and projections
362
- are not accepted. Results sort by element ID with outer paging (default 100,
363
- maximum 200); follow `next_offset`. `without_bounds_count` reports candidates
364
- omitted because they have no model bounding box.
365
-
366
- `measure_geometry` measures exact Euclidean distance between supplied points
367
- with `mode: "point_distance"`, or approximate bounding-box separation between
368
- two host elements with `mode: "bounding_box_gap"`. Point mode returns signed
369
- A-to-B `delta`; box mode returns nonnegative axis gaps. The required `unit`
370
- applies to all inputs and outputs: millimeters, centimeters, meters, feet, or
371
- inches. Both modes use document internal axes; linked contents are not traversed.
372
-
373
- Bounding-box gaps are lower bounds on geometry separation. Boxes can enclose
374
- nonphysical geometry, so touching/overlapping boxes do not establish a clash.
375
- An optional nonnegative `clearance` in box mode flags gaps strictly below that
376
- threshold; it identifies review candidates, not verified clearance failures.
377
- Keep the returned method and approximation status with any reported result.
378
-
379
- Practical workflows are available in the Pi skill references:
380
- [room documentation](skills/pi-revit/references/room-documentation.md) and
381
- [model audit and export](skills/pi-revit/references/model-audit-export.md).
382
-
383
- ### Read parameter values and summarize queries
384
-
385
- Add `parameter_names` to `get_elements` to read up to 20 requested parameters
386
- alongside element identity fields. Names can be display names, built-in parameter
387
- names, or `guid:<GUID>` identities. `include_type_parameters: true` also reads
388
- those parameters from each element's type. Projections expose missing and
389
- ambiguous matches and include every matching parameter. Raw `value` uses Revit
390
- internal units for measurable numbers; `displayValue` uses document formatting.
391
- Bare counts return no parameter projections.
392
-
393
- `summarize_elements` groups a host-element `query` by `category`, `typeName`,
394
- `levelId`, or `parameter`. Parameter grouping uses exact raw values, with a
395
- separate missing-parameter group, and rejects ambiguous display names. Set
396
- `type_parameter: true` to group by a parameter on the type. The outer
397
- `offset`/`limit` pages groups (default 100, maximum 500); counts always cover the
398
- whole matching scope.
399
-
400
- `summarize_elements` and element-set creation accept category, class, level,
401
- type, active-view, and parameter filters inside `query`. They reject query-level
402
- paging, projections, and `count_only`, and require at most 10,000 matching
403
- elements. Narrow larger queries; an omitted query covers all host instances.
404
-
405
- ### Reuse an element set
406
-
407
- Use `manage_element_sets` with `action: "create"` to snapshot query membership,
408
- then `read` its `set_id`, `list` active-document sets, or `forget` a set. This
409
- changes neither the model nor selection. The bridge retains at most 32 sets;
410
- each expires 30 minutes after creation and belongs to the exact open document
411
- and bridge session. Reads do not renew it. Reopening the document or restarting
412
- the bridge requires a fresh set.
413
-
414
- Read pages return current values for the original members, without reapplying
415
- the query. Deleted or identity-changed members appear in `missing`. Check those
416
- entries before passing surviving IDs to other tools. Pages default to 100
417
- members (maximum 1000); follow `next_offset`, which includes missing members in
418
- `visited_count`. Set reads also support `parameter_names` and
419
- `include_type_parameters`.
420
-
421
- ### Preview parameter changes
422
-
423
- `set_parameters` accepts 1–200 updates and defaults to committing successful
424
- updates while reporting failures. Each update has its own subtransaction.
425
- `atomic: true` rolls back the entire batch if any update fails. `preview: true`
426
- performs eligible commit checks inside a transaction group, then rolls the
427
- group back. Both options require the same exact document identity as an ordinary
428
- write and can be combined.
429
-
430
- Read `commit_validation_performed`: an atomic batch rejected before commit, or
431
- a batch with no accepted updates, has not passed commit-time validation.
432
- Successful preview responses confirm model rollback. Errors report the cleanup
433
- outcome instead; inspect it before retrying.
434
-
435
- Only committed updates appear in `succeeded`. Accepted steps from previews or
436
- rolled-back batches appear in `proposed`, with `updated: 0` and
437
- `committed: false`. Their `before`/`after` values describe each step in input
438
- order, identified by its zero-based `index`. Repeated writes can have intermediate
439
- values; reread the element when final values matter. Inspect `failed` and
440
- `commitWarnings` as well. Numeric write inputs use the explicit `unit` or the
441
- document's display units; raw numeric values in the returned snapshots use
442
- Revit internal units.
443
-
444
- ### Transform, delete, and change types
445
-
446
- These advanced tools can be activated with `find_revit_tools`. They operate on
447
- the active host document and require `expected_document_id` for both previews
448
- and writes. Their results use the same `succeeded`, `proposed`, `failed`,
449
- `committed`, and commit-validation fields as parameter batches. `updated` counts
450
- steps: one whole-selection transform or deletion counts as one step.
451
-
452
- `transform_elements` moves, copies, or rotates 1–200 distinct `element_ids`
453
- together. Always supply `unit`: `millimeters`, `centimeters`, `meters`, `feet`,
454
- or `inches`. Move/copy takes a `translation` vector. Rotation takes an
455
- `axis_origin`, a nonzero dimensionless `axis_direction`, and signed right-hand
456
- `angle_degrees`. Coordinates refer to the document's internal origin and axes;
457
- returned location snapshots use feet, regardless of input units. The selection
458
- succeeds or rolls back as one step. Move/rotate rejects pinned requested
459
- elements; the tool never unpins them automatically. Copy previews return
460
- temporary `created_ids`, which must not be reused after rollback.
461
-
462
- `delete_elements` deletes 1–200 distinct requested elements in one step, rejecting
463
- pinned requested elements. Use `preview: true` to inspect Revit's complete
464
- `deleted_ids` set and its `dependent_ids`, with rollback. A cascade exceeding
465
- 10,000 IDs rolls back. To check a later deletion against the preview, optionally
466
- pass the full `deleted_ids` as `expected_deleted_ids`; a changed set causes
467
- rollback before commit. This compares membership only. The actual deletion is
468
- a new request, not an identical retry of the preview, so do not reuse the
469
- preview's `_operation_id` for it.
470
-
471
- `change_element_types` accepts 1–200 `element_id`/`type_id` pairs in `updates`,
472
- with each target appearing once. Invalid types or pinned targets fail individually;
473
- other valid changes can commit by default. `atomic: true` rolls back the complete
474
- batch on any failure. Some type changes replace the original element: use the
475
- committed `resulting_id` and `unique_id` afterward. Replacement IDs reported in
476
- `proposed` are temporary after preview or atomic rollback.
477
-
478
- Transforms and type changes can affect constrained or hosted elements beyond
479
- the target snapshots. Deletion IDs cover elements returned by Revit's deletion
480
- API, not surviving elements Revit may also modify. Review these results as
481
- bounded descriptions of the operation, not a complete audit of every effect.
482
-
483
- ### Create views and arrange sheets
484
-
485
- Activate `manage_views`, `manage_sheets`, and `manage_sheet_placements` through
486
- `find_revit_tools`. Edits support `preview: true` and require the exact document
487
- identity. Each call edits one view, sheet, or placement in one step. Created IDs
488
- in previews are marked `id_is_temporary` and must not be reused after rollback.
489
- Inspect the common transaction outcome and validation fields. Use `open_view`
490
- to display committed views/sheets and `delete_elements` to remove them.
491
-
492
- `manage_views` supports `create_plan`, `create_3d`, `create_section`, `duplicate`,
493
- and `update`. Creation uses a compatible `view_family_type_id`; discover it with
494
- `get_element_types` and `of_class: "ViewFamilyType"`. Plans also need `level_id`;
495
- 3D creation produces an isometric view. Duplicate/update targets `view_id`.
496
- Duplication options are `duplicate`, `with_detailing`, and `dependent` where
497
- supported. Optional name, scale, and template apply in the same step. Template
498
- ID `-1` removes a template explicitly; incompatible templates or controlled
499
- scale changes fail without automatically removing the template.
500
-
501
- Sections use the document's internal coordinate frame. Supply `origin`, explicit
502
- length `unit`, orthogonal nonzero `viewing_direction` and `up`, and positive
503
- width/height/depth. Width and height center on the origin; depth extends in the
504
- viewing direction. Direction vectors are dimensionless.
505
-
506
- `manage_sheets` creates a sheet from `name` and `number`, optionally using a loaded
507
- titleblock `FamilySymbol` specified by `titleblock_type_id`. Omitting it creates
508
- a sheet without a titleblock. Updates use `sheet_id` and name/number; changing
509
- an existing titleblock type uses `change_element_types` on its instance instead.
510
-
511
- `manage_sheet_placements` lists both viewports and schedule instances for a
512
- `sheet_id` (default 100 per page, maximum 200). Follow `next_offset`. Although
513
- listing changes no model state, the tool's write-capable metadata means
514
- `expected_document_id` is required for this action too.
515
-
516
- To place content, supply `sheet_id`, `view_id`, explicit `unit`, and
517
- `position: [x,y,0]`; to move it, supply `placement_id`, unit, and position.
518
- Positions are paper-space sheet coordinates and must never be multiplied by
519
- view scale. Viewports use their box center excluding the label; schedules use
520
- their insertion point. Returned positions use feet and state `position_kind`.
521
- Viewports support rotation `none`, `clockwise`, or `counterclockwise`; omit
522
- rotation for schedules. Pinned placements cannot be moved, placeholder sheets
523
- cannot receive content, and Revit checks whether a view can be placed.
524
-
525
- ### Configure schedules and create tags
526
-
527
- `manage_schedules` creates a regular schedule from `category` and `name`, with
528
- an optional `area_scheme_id` for area schedules. Configure an existing schedule
529
- with `schedule_id`. Each call is one atomic step, requires `expected_document_id`,
530
- and supports preview rollback. Revision schedules, templates, embedded schedules,
531
- and calculated/combined-field authoring are outside this tool.
532
-
533
- Use `get_schedule_fields` to discover eligible fields for an existing schedule.
534
- It supports localized name filtering and paging (default 100, maximum 200).
535
- `add_fields` identifies each field by its `parameter_id` plus `field_type` pair;
536
- negative built-in parameter IDs are valid. When Count appears in discovery,
537
- pass its returned pair unchanged, just like other fields. Count can also be
538
- added as `{ "field_type": "Count" }` without a parameter ID. A supplied pair
539
- must be eligible for the target schedule; do not guess its parameter ID.
540
- `included` marks pairs already present.
541
-
542
- In contrast, `update_fields`, `sort_fields`, and `filters` use the schedule-local
543
- `field_id` returned by `get_schedules`. These IDs are neither parameter IDs nor
544
- column indices. Read newly committed fields before assigning their sort/filter
545
- rules. New schedule and field IDs returned by previews are temporary.
546
-
547
- Field additions and updates support heading, hidden state, and width, with up
548
- to 50 entries each. Widths require explicit length units and apply to both grid
549
- and sheet columns. `sort_fields` allows four rules; `filters` allows eight.
550
- Supplying either array replaces its entire list, including clearing it with
551
- `[]`; omitting the property preserves it. `is_itemized` controls itemization.
552
-
553
- Filter comparisons are `equals`, `not_equals`, `contains`, `greater_than`, or
554
- `less_than`. Supply a matching `value_type` and value. Measured numeric filters
555
- require an explicit compatible `unit`; unitless numbers must omit it.
556
- `get_schedules` exposes each field's specification, filtering capabilities,
557
- grid/sheet widths in feet, and current sort/filter rules. Returned numeric filter
558
- values use internal units; returned comparison names use Revit enums rather
559
- than the write schema's comparison strings.
560
-
561
- `create_tags` accepts 1–100 host-document targets in one explicit view, using a
562
- loaded tag `FamilySymbol`. Choose `kind` as `element`, `room`, `space`, or `area`.
563
- Each target has `element_id` and `head_position: [x,y,z]` in document internal
564
- coordinates, using the required length `unit`. The position always means the
565
- tag head, including when `leader: true`; returned positions use feet. Linked
566
- targets and face/subelement references are unsupported.
567
-
568
- Element tags support horizontal/vertical orientation. Spatial tags require a
569
- compatible plan view and positions at the spatial element's level; omit their
570
- orientation property. Independent tags cannot use templates, perspective views,
571
- or unlocked 3D views. Creation requires exact document targeting, defaults to
572
- partial success, and supports `atomic: true` and `preview: true`. IDs in
573
- `proposed` are temporary after preview or atomic rollback; use only committed
574
- tag IDs for subsequent calls.
575
-
576
- ### Inspect links, schedules, and relationships
577
-
578
- Call `get_linked_models` before querying a link. Pass its `link_instance_id` and
579
- `linked_document_id` to `get_linked_elements` as `link_instance_id` and
580
- `expected_linked_document_id`. The optional `expected_document_id` still identifies
581
- the active host model. Refresh discovery after unloading or reloading a link.
582
- Queries support `get_elements` filters and pagination, except active-view scoping.
583
- Unloaded links cannot be queried, and nested links are not traversed.
584
-
585
- Keep the full `reference` returned for each linked element: its numeric ID belongs
586
- to the linked document and must not be used with host selection or write tools.
587
- Different placements of one linked model can produce different host coordinates.
588
- Optional `host_bounds` use internal feet and enclose all eight transformed box
589
- corners; they are axis-aligned bounds, not exact geometry. A missing box is null.
590
-
591
- Use `get_schedules` without an ID to list schedules, or with `schedule_id` to read
592
- field definitions, width/specification metadata, sort/filter rules, and displayed
593
- body cells. Text follows Revit's formatting and
594
- can include grouped entries, headings, and totals; rows are not element IDs.
595
- Hidden field definitions need not correspond to displayed columns. Follow both
596
- `next_offset` and `next_column_offset`: finish the column pages for a row page
597
- before moving to the next rows. List/body pages default to 50 entries (maximum
598
- 200); column pages default to 50 (maximum 50).
599
-
600
- `get_element_relationships` reports each requested relationship separately, with
601
- its own count and continuation offset. It reads host-document relationships only;
602
- logical dependents are not a complete deletion-impact prediction. In family
603
- documents, request relationship kinds that exclude `joined`. Relationship pages
604
- default to 100 entries (maximum 200). Link-instance pages default to 100 (maximum
605
- 1000), and linked-element pages default to 200 (maximum 1000).
606
-
607
- ### Reuse a script with structured inputs
608
-
609
- `execute_csharp` accepts an optional `inputs` JSON object separately from its
610
- source code. The script reads it through the `inputs` `JsonElement` global,
611
- for example `inputs.GetProperty("value").GetDouble()`. Inputs default to an
612
- empty object and are not interpolated into source text. Scripts validate their
613
- contents; the serialized object is limited to 100,000 characters.
614
-
615
- `manage_revit_scripts` stores reusable definitions under
616
- `%APPDATA%\pi-revit\scripts`. Its actions are `save`, `list`, `read`, `run`, and
617
- `history`. Saving requires a name, description, code, and `input_types`; it never
618
- executes the script. Versions are immutable 64-character SHA-256 hashes of the
619
- saved definition. Read a version to inspect its source, then explicitly run that
620
- exact name/hash with current `expected_document_id` and inputs. Runs verify
621
- saved content integrity and require a bridge with operation receipts.
622
-
623
- Up to 40 named `input_types` can be declared as string, number, integer, boolean,
624
- object, or array. Every declared input is required and extra inputs are rejected.
625
- Validation covers top-level kinds only: scripts still validate nested objects,
626
- array contents, ranges, units, and model-specific rules. The Pi skill includes
627
- a short save/read/run example using a numeric input.
628
-
629
- Library runs use unrestricted `execute_csharp`, with the same model, UI, file,
630
- and external effects, synchronous execution, transaction, and timeout behavior.
631
- There is no preview mode, scheduled execution, or automatic model saving. A saved
632
- script is not a saved Revit model.
633
-
634
- List/history pages default to 20 entries (maximum 100), with optional exact-name
635
- filtering. Local history stores version, document identity, input hash,
636
- timestamps, and receipt IDs, rather than raw inputs or results. History states
637
- record dispatch/response progress, not a model transaction outcome. After a
638
- timeout or interruption, inspect `get_revit_operation`; an identical retry uses
639
- the original `_operation_id`, script version, inputs, and document identity.
640
-
641
- ### Check an operation after a timeout
642
-
643
- With a bridge that supports operation tracking, Pi automatically assigns an
644
- operation ID to each bridge tool call and includes it in responses or request
645
- errors. Call the native `get_revit_operation` tool with that `operation_id` to
646
- check progress or recover the original result. This extension tool contacts the
647
- operation's original local bridge directly, even when another instance is selected,
648
- so it works while Revit's model thread is busy or no
649
- document is open; the bridge must still be running.
650
-
651
- | Receipt state | Meaning |
652
- |---|---|
653
- | `queued` | Waiting to begin; no tool execution has started. |
654
- | `running` | Execution started; a client timeout or cancellation does not stop it. |
655
- | `succeeded` | The tool returned successfully; inspect its payload for the actual model outcome. |
656
- | `failed` | The call returned an error; this alone does not confirm rollback or absence of effects. |
657
- | `expired_before_start` | Its queue deadline passed before execution; no tool action was performed. |
658
- | `result_unavailable` | The tool finished, but a response could not be retained correctly; effects may already exist. |
659
- | `unknown` | This bridge session has no receipt; the outcome is unknown. |
660
-
661
- A successful receipt is not proof of a committed edit. For example, a completed
662
- parameter preview can have receipt state `succeeded` while its payload reports
663
- `preview: true`, `committed: false`, and proposed changes that were rolled back.
664
- Inspect `committed`, validation flags, failures, and warnings in the retained
665
- tool result.
666
-
667
- To retry the same request, pass its exact ID as `_operation_id` on the original
668
- tool call and preserve all original arguments. Within the same bridge session,
669
- this waits for the existing operation or returns its cached response; it never
670
- executes that ID a second time. Reusing the ID with a different tool or arguments
671
- is rejected. Omitting `_operation_id` creates a new operation, so establish the
672
- previous outcome before retrying an edit that way.
673
-
674
- The bridge retains at most 128 completed full results, with a combined 32 MiB
675
- limit. Older or oversized results can be evicted, but their receipt records
676
- remain reserved for the session and report `result_available: false`. Replaying
677
- an evicted result returns an error without repeating the action. At 10,000
678
- receipt records, new tracked calls are rejected; existing receipts remain
679
- queryable. A bridge restart clears all receipts and changes its identity. Old
680
- IDs cannot be replayed in the new session. An unavailable original bridge or an
681
- `unknown` receipt does not prove that an earlier edit never ran. Changing the
682
- selected instance does not redirect old receipts or retries. Inspect the original
683
- model before deciding on a new operation.
684
-
685
- ### Read complete results
686
-
687
- Requested rows and parameter values are included in model-visible tool content.
688
- Results up to 12,000 characters are complete inline. For a larger result, the Pi
689
- extension saves the complete tool payload as UTF-8 JSON and returns `result_id`,
690
- `file_path`, `total_chars`, `complete_inline: false`, and retrieval instructions.
691
-
692
- Call `read_revit_result` with the returned ID and `offset: 0`, then follow each
693
- `next_offset` until `has_more` is false. A requested fragment is at most 8,000
694
- UTF-16 code units; it may be smaller so the escaped response stays within the
695
- message limit. Concatenate each page's `text` in order. Individual fragments are
696
- not standalone JSON objects from the original result. Use the returned offsets,
697
- not byte counts or a guessed increment.
698
-
699
- Result IDs are registered in memory by the current extension instance. After an
700
- extension reload or new Pi process, an old ID may no longer resolve; use the
701
- original absolute `file_path` with Pi's normal `read` tool while that file remains
702
- available. Saved results live in a unique OS temporary directory, can contain
703
- model data, and are subject to eventual OS/user cleanup. If saving fails after
704
- Revit completed an operation, inspect actual model state before retrying a write.
705
-
706
- Complete payload retrieval does not expand a tool's own query page or declared
707
- limits. Continue `get_elements`/`get_element_types` pagination separately, and
708
- check warning-group or projection truncation indicators. A bridge-only client
709
- must consume `details.payload` for oversized results; `read_revit_result` belongs
710
- to the Pi extension.
711
-
712
- Display-name parameter filters now resolve on every element, including inside a
713
- category/class scope. Explicit built-in IDs and shared GUIDs can retain collector
714
- optimization. In `get_element_details`, `include.parameters` and
715
- `include.type_parameters` are independent; disabling instance parameters still
716
- allows a type-only result.
717
-
718
- ## Limitations — read before using on real projects
719
-
720
- - **Writes change the open model directly.** There is no confirmation prompt.
721
- Typed editing tools provide preview and transaction outcomes; `execute_csharp`
722
- remains unrestricted and attempts rollback after script failure. Parameter
723
- and type-change batches default to partial success, with optional atomic rollback.
724
- Transforms and deletions apply the whole selection in one step. Read the
725
- actual transaction outcome and failed lists. Script result-projection failure
726
- can leave a successful edit committed with a `returnValueError`; filesystem and
727
- UI effects are separate from model rollback.
728
- - The add-in multi-targets .NET 8 (Revit 2025/2026) and .NET 10 (Revit 2027); `deploy.ps1`
729
- auto-detects the Revit versions you have installed and builds only the matching framework(s),
730
- so you only need the SDK for the Revit you run. The 0.4.0 tools were tested in bounded live
731
- workflows on Revit 2025. Revit 2026/2027 and large-model
732
- performance were not tested for this release. Export API/file checks do not
733
- establish full DWG drawing or IFC schema/geometry validation.
734
- - Pi targets one bridge session at a time. Use `manage_revit_instances` to select
735
- among reachable instances. Closing or restarting the selected bridge requires
736
- explicit selection of an available identity; model calls never fall back to another.
737
- - A tool call that outlives its timeout is abandoned client-side but may still complete inside
738
- Revit. Check its receipt with `get_revit_operation`; when retrying the identical request,
739
- reuse `_operation_id`. If its outcome remains unknown, inspect the model before a new write.
740
- - Long-running scripts cannot be interrupted mid-execution (Revit's API is single-threaded);
741
- the `execute_csharp` budget is 120s.
742
-
743
- ## Uninstall
744
-
745
- ### npm install
746
-
747
- Close Revit, then in PowerShell — from any folder **outside** the installed package (Windows
748
- cannot delete a folder your shell is standing in):
749
-
750
- ```powershell
751
- powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.pi\agent\npm\node_modules\pi-revit\scripts\uninstall.ps1"
752
- ```
753
-
754
- ### Source install
755
-
756
- Close Revit, then in PowerShell from the GitHub checkout:
757
-
758
- ```powershell
759
- powershell -ExecutionPolicy Bypass -File scripts\uninstall.ps1
760
- ```
761
-
762
- This removes the Revit bridge add-in (every installed Revit version), the global `pi-revit`
763
- command, the bridge runtime folder (`%APPDATA%\RevitBridge\`), and the Pi package registration
764
- (it tries both the npm and the source-install form, so no extra `pi remove` is needed for
765
- either install kind). Your workspace at `Documents\pi-revit` (notes + session
766
- history) is **preserved** — add `-RemoveWorkspace` to delete it too, or `-RevitVersion 2026` to
767
- target a single Revit version. Pi itself is left installed; remove it with
768
- `npm uninstall -g @earendil-works/pi-coding-agent` if you want.
1
+ # pi-revit
2
+
3
+ Native Revit tools for [Pi](https://pi.dev) — ask about, query, script, and modify the open
4
+ Autodesk Revit model from your terminal.
5
+
6
+ ```text
7
+ You: how many levels in the model?
8
+ Pi: calls get_model_overview → "There are 14 levels in the Revit model."
9
+
10
+ You: select all structural columns
11
+ Pi: get_model_overview → get_elements → manage_selection → "Selected 222 structural columns."
12
+
13
+ You: rename level 'L1' to 'Ground Floor'
14
+ Pi: get_model_overview → set_parameters → "Done — Level 'L1' is now 'Ground Floor'."
15
+ ```
16
+
17
+ ## How it works
18
+
19
+ ```text
20
+ Pi terminal session
21
+ │ native tools (registered by the pi-revit extension)
22
+ ▼
23
+ localhost HTTP bridge ← per-start token; connection info in %APPDATA%\RevitBridge\
24
+ │
25
+ ▼
26
+ headless Revit add-in ← no ribbon, no panels; just a bridge
27
+ │ ExternalEvent queue (Revit API thread)
28
+ ▼
29
+ Revit API ← tool-owned model transactions; separate UI/file effects
30
+ ```
31
+
32
+ The extension discovers its tools from the bridge at startup (retrying in the background until
33
+ Revit is up), so the tool list always matches what the add-in serves. Everything between Pi and
34
+ Revit is local-machine only; note that Pi sends conversation context and tool results to your
35
+ selected LLM provider, like any Pi session.
36
+
37
+ ## Safety model
38
+
39
+ Be deliberate about pointing an LLM at a real project model. The add-in enforces what it can
40
+ enforce mechanically, and is honest about what it cannot:
41
+
42
+ - Tool metadata describes its classification; UI actions such as selection and view
43
+ activation can change state even when `write` is false. Confirmation policy belongs
44
+ to the client. Exact document targeting is enforced separately as described below.
45
+ - Parameter writes, C# scripts, temporary isolation, and IFC export own named Revit
46
+ transactions. Failure handling is attached after transaction start, and results
47
+ check transaction outcomes before claiming commit or rollback. `set_parameters`
48
+ defaults to partial success; `atomic: true` rolls back the batch if any update
49
+ fails, and `preview: true` rolls back proposed model changes after validation.
50
+ Inspect every failed update, the validation status, and `commitWarnings`.
51
+ An unconfirmed rollback is reported as such.
52
+ - `execute_csharp` is an unrestricted escape hatch by design — scripts have full CLR access.
53
+ Treat it like giving the agent a macro editor, on a model you have saved or can restore.
54
+ - `execute_csharp` has a dialog guard that attempts dismissive responses to dialogs
55
+ raised while the script runs. It does not establish that every Revit dialog or
56
+ failure mode can be handled automatically.
57
+ - A model transaction does not undo filesystem output or earlier selection/zoom
58
+ changes. A failed export can leave incomplete files; its error reports the output
59
+ location and observed changed files. An isolation failure reports any earlier
60
+ selection action that already completed.
61
+
62
+ ### Target the exact open document
63
+
64
+ Call `get_model_overview` for the intended model and copy `project.documentId`
65
+ unchanged into `expected_document_id` on subsequent operations:
66
+
67
+ ```json
68
+ {
69
+ "expected_document_id": "<project.documentId from the current overview>",
70
+ "updates": [{ "element_id": 12345, "parameter": "ALL_MODEL_INSTANCE_COMMENTS", "value": "Reviewed" }]
71
+ }
72
+ ```
73
+
74
+ Replace the placeholders with the current document ID and an actual element ID.
75
+ The exact ID is required for `set_parameters`, `transform_elements`,
76
+ `delete_elements`, `change_element_types`, `manage_views`, `manage_sheets`,
77
+ `manage_sheet_placements`, `manage_schedules`, `create_tags`, `execute_csharp`,
78
+ `export_documents`, and `open_view`,
79
+ including supported previews. `manage_sheet_placements` requires it even for its
80
+ read-only `list` action because the tool is classified as write-capable.
81
+ It is also required for selection/zoom
82
+ changes and any `manage_selection` call with `isolate_in_view: true`, including
83
+ action `get`. Pure reads may omit it; a supplied ID is always checked.
84
+
85
+ The identity represents one currently open native document in one loaded bridge
86
+ session. It is not a persistent project ID, path, export-folder key, or credential.
87
+ Closing/reopening the document or restarting the bridge invalidates prior IDs.
88
+ Read the intended document's overview again after those transitions. The guard
89
+ checks the actual target on Revit's API thread immediately before execution.
90
+
91
+ Legacy `expected_document` titles remain an optional additional sanity check.
92
+ A title alone no longer satisfies the required guard, even when it matches.
93
+ Clients must refresh discovery and supply the new field after upgrading to 0.3.0.
94
+
95
+ Practical advice: work on saved models, keep worksharing backups/central protection as usual,
96
+ and review the agent's summary of what changed after any write session.
97
+
98
+ ## Requirements
99
+
100
+ - Windows 10/11
101
+ - Autodesk Revit 2025, 2026, or 2027
102
+ - .NET SDK matching your Revit: [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) for Revit 2025/2026, [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) for Revit 2027
103
+ - [Node.js 20.3+](https://nodejs.org/)
104
+ - [Pi coding agent](https://pi.dev): `npm install -g --ignore-scripts @earendil-works/pi-coding-agent`
105
+
106
+ ## Install
107
+
108
+ ### One-command install
109
+
110
+ Close Revit, then in PowerShell:
111
+
112
+ ```powershell
113
+ npx.cmd -y pi-revit
114
+ ```
115
+
116
+ This installs the Pi package, builds and deploys the Revit bridge add-in, creates the
117
+ `Documents\pi-revit` workspace, and installs the global `pi-revit` command.
118
+
119
+ The installer first checks the selected .NET SDK. If it is missing or too old,
120
+ installation stops with the required version, a download link, and retry steps.
121
+ Interactive terminals also offer to open the download page. Revit uses a runtime
122
+ to run; compiling this add-in also needs the SDK. For Revit 2027, install the
123
+ [.NET 10 SDK for Windows x64](https://dotnet.microsoft.com/en-us/download/dotnet/10.0),
124
+ reopen PowerShell, and rerun the installer. Existing .NET versions can stay installed.
125
+ If an older SDK is still selected, check `dotnet --list-sdks`, your `PATH`, and any
126
+ `global.json` in the current directory or its parents.
127
+
128
+ Start Revit (click **Always Load** on the unsigned add-in prompt once) and open any
129
+ project. No panel or ribbon appears — the add-in is headless.
130
+
131
+ ### Manual npm install
132
+
133
+ Use this if you prefer to run each step yourself:
134
+
135
+ ```powershell
136
+ # 1. Install the Pi package from npm. This registers the pi-revit extension and skill.
137
+ pi install npm:pi-revit
138
+
139
+ # 2. Go to the installed package folder.
140
+ cd "$env:USERPROFILE\.pi\agent\npm\node_modules\pi-revit"
141
+
142
+ # 3. Build + deploy the Revit add-in (RevitBridge.dll + the Roslyn DLLs for execute_csharp).
143
+ npm.cmd run deploy
144
+
145
+ # 4. Create the workspace and global pi-revit command.
146
+ npm.cmd run setup
147
+ ```
148
+
149
+ For a non-default Revit install location, use the PowerShell deploy script directly and pass
150
+ `-RevitVersion` / `-RevitApiPath`, e.g.:
151
+
152
+ ```powershell
153
+ powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1 -RevitVersion 2027 -RevitApiPath "D:\Autodesk\Revit 2027"
154
+ ```
155
+
156
+ ### Source install
157
+
158
+ Use this if you want to run directly from the GitHub checkout instead of the npm package:
159
+
160
+ ```powershell
161
+ git clone https://github.com/Triavision-ai/pi-revit.git
162
+ cd pi-revit
163
+ powershell -ExecutionPolicy Bypass -File scripts\deploy.ps1
164
+ pi install ./
165
+ powershell -ExecutionPolicy Bypass -File scripts\setup-workspace.ps1
166
+ ```
167
+
168
+ ### Upgrading from 0.2.x
169
+
170
+ **Breaking change:** writes and UI mutations now require `expected_document_id`.
171
+ Close Revit, update the Pi package and redeploy the add-in using the installation
172
+ steps above, then restart Revit and start a fresh Pi session. Both components must
173
+ be updated. Call `get_model_overview` and copy `project.documentId` into subsequent
174
+ mutating calls; a legacy `expected_document` title alone is insufficient. Refresh
175
+ the ID after closing/reopening a document or restarting Revit.
176
+
177
+ The exact-ID requirement was introduced in 0.3.0 and also applies to later
178
+ releases. `ping` reports package/add-in version mismatches. Setup preserves an
179
+ existing workspace `AGENTS.md`; merge updated identity, saved-result retrieval,
180
+ output-folder guidance and current skill/manual routing from [the workspace template](workspace/AGENTS.md)
181
+ into older workspaces as needed.
182
+
183
+ ## Use it
184
+
185
+ Open **any terminal** — PowerShell, CMD, or Windows Terminal — and type:
186
+
187
+ ```powershell
188
+ pi-revit
189
+ ```
190
+
191
+ That's all. Pi starts with the Revit tools ready:
192
+
193
+ ```text
194
+ > give me a model overview
195
+ > how many doors per level?
196
+ > select all structural columns
197
+ ```
198
+
199
+ **How this works:** the setup step placed a small `pi-revit` command in the same folder as the
200
+ `pi` command itself. That folder is on your system PATH — which is exactly why *every* terminal
201
+ finds `pi-revit`, with no extra configuration. When you run it, it switches to your workspace at
202
+ `Documents\pi-revit` and starts Pi there, so your conventions file (`AGENTS.md`) loads
203
+ automatically and all Revit session history lives in one predictable place (`pi-revit -c`
204
+ continues the last session). The Revit tools themselves are installed globally in Pi, and the
205
+ extension discovers them live from the bridge inside Revit each time a session starts.
206
+
207
+ **Per model, automatically:** files sort themselves. Exports land in
208
+ `Documents\pi-revit\Models\<model title>--<identity hash>\exports`. The suffix derives
209
+ from the normalized saved-file path, cloud region/project/model identity, or Revit
210
+ Server path. Distinct saved paths therefore use different destinations even when
211
+ their titles or inherited project IDs match. Save As to another path selects a new
212
+ destination. Unsaved models or unavailable persistent identities use a token stable
213
+ only for that open document; their destination may change after reopening.
214
+
215
+ `model.txt` records the identity used. Existing title-only directories remain
216
+ untouched; upgrading does not migrate or merge old exports. An explicit
217
+ `output_dir` still overrides the default. File attribution uses a directory
218
+ snapshot, so avoid unrelated concurrent writers in a shared output directory.
219
+
220
+ Plain `pi` from any folder also works; `pi-revit` just adds the right working folder on top.
221
+
222
+ ## Guidance and contributor documentation
223
+
224
+ The [PI-Revit skill](skills/pi-revit/SKILL.md) is a short entry point. It links
225
+ shared execution/recovery/visual rules, workflows, and a
226
+ [tool index](skills/pi-revit/references/tool-index.md) with one focused manual
227
+ per public tool. Read only the detail relevant to the current task.
228
+
229
+ `find_revit_tools` now includes the six Pi-side utilities as well as discovered
230
+ bridge tools. Use `scope: "documentation"` to find packaged manuals while Revit
231
+ is closed, without contacting the bridge or activating tools:
232
+
233
+ ```json
234
+ { "scope": "documentation", "names": ["manage_sheets", "manage_sheet_placements"] }
235
+ ```
236
+
237
+ Read a returned `documentation.path` for details. Registration, activation and
238
+ reading a manual are separate actions; a manual is not proof that the selected
239
+ bridge supports the tool. Current input schemas remain authoritative.
240
+
241
+ Every tool declares what it does **not** cover and what to use instead: another
242
+ tool, specific Revit API members, a user action, or "the Revit API does not offer
243
+ this" with the evidence. `find_revit_tools` shows these limits with each result.
244
+ When no tool matches a request, it returns the remaining route (check the API
245
+ documentation, then use custom code) instead of an empty list. Missing a dedicated
246
+ tool is therefore never a reason to call an operation impossible. Search with
247
+ English task words; you can talk to Pi in any language, and it translates its searches.
248
+
249
+ `search_api_docs` can check up to 10 API members in one call, separated by `;`. Each API limit
250
+ shown by `find_revit_tools` and in the manuals carries a ready lookup.
251
+
252
+ A short PI-Revit protocol is always in Pi's context, whatever the task and whether
253
+ or not the skill is read. It covers:
254
+
255
+ - checking capability before saying no;
256
+ - doing only what was asked, verifying it with the tool's declared method, then
257
+ stopping and reporting;
258
+ - naming evidence;
259
+ - document identity;
260
+ - replying in your language.
261
+
262
+ If Pi keeps re-checking and adjusting something that is already done, the extension
263
+ asks it to compare against your request and report, and to offer extras as suggestions.
264
+
265
+ `ping` also reports what is actually loaded: the extension package, guidance
266
+ revision, source revision and, per tool, whether the manual matches the connected
267
+ bridge's exact contract (`contract_match` / `contract_changed` / `undocumented`).
268
+
269
+ The [architecture guide](docs/architecture.md) explains how the bridge, Pi extension,
270
+ contracts, discovery and operating guidance work together. The public package contains
271
+ the reusable tools and user documentation; local testing material and model evidence
272
+ are kept outside the distributed package.
273
+
274
+ ## Tools
275
+
276
+ | Tool | What it does | Works in |
277
+ |---|---|---|
278
+ | `find_revit_tools` | Find native/bridge tools and their manuals; activate specialist tools or look up documentation offline. | Pi-side |
279
+ | `ping` | Is the bridge reachable? Revit version | Pi-side |
280
+ | `manage_revit_instances` | List reachable local Revit sessions or select the target for this Pi session | Pi-side |
281
+ | `get_model_overview` | Project info, units, levels, grids, category counts — call first | Project, family |
282
+ | `get_model_coordinates` | Read base points and site/project locations; map internal points through the active shared coordinates | Project |
283
+ | `get_elements` | Query/count elements: parameter filters, optional parameter values, pagination | Project, family |
284
+ | `summarize_elements` | Count all matching elements by category, type, level, or raw parameter value | Project, family |
285
+ | `manage_element_sets` | Create, list, read, or forget temporary snapshots of matching host elements | Project, family |
286
+ | `get_element_details` | Parameter values, location, bounding box, materials per element | Project, family |
287
+ | `get_element_types` | Element types / family symbols, optional placed-instance counts | Project, family |
288
+ | `get_linked_models` | Direct Revit link instances, load status, document identities, placement transforms | Project |
289
+ | `get_linked_elements` | Query/count one loaded link, with optional bounding boxes in host coordinates | Project |
290
+ | `query_spatial_elements` | Find host elements by approximate bounding-box intersection or containment in a region | Project |
291
+ | `measure_geometry` | Measure exact point distance or approximate host-element bounding-box separation | Project, family |
292
+ | `get_schedules` | List schedules or read fields, widths, specifications, sort/filter rules, and displayed cells | Project |
293
+ | `get_schedule_fields` | Discover eligible field parameter/type pairs for an existing schedule | Project |
294
+ | `manage_schedules` | Create or configure regular schedules, including fields, widths, sorting, and filters | Project |
295
+ | `create_tags` | Create host element, room, space, or area tags, with partial/atomic batches and preview | Project |
296
+ | `get_element_relationships` | Host, type, level, members, joined geometry, and logical dependents of one element | Project, family |
297
+ | `manage_selection` | Get/set/clear the selection, zoom, temporary isolate | Project, family |
298
+ | `open_view` | Activate a view or sheet in the Revit UI (like double-clicking it in the browser) | Project, family |
299
+ | `set_parameters` | Bulk parameter writes and renames, with previews and optional atomic batches | Project, family |
300
+ | `transform_elements` | Move, copy, or rotate a whole selection, with explicit units and preview | Project, family |
301
+ | `delete_elements` | Preview or delete selected elements and report Revit's full deletion set | Project, family |
302
+ | `change_element_types` | Change types per target, with preview and optional atomic rollback | Project, family |
303
+ | `manage_views` | Create plans, isometric 3D views, or sections; duplicate or update views | Project, family |
304
+ | `manage_sheets` | Create, rename, or renumber sheets, with an optional titleblock at creation | Project |
305
+ | `manage_sheet_placements` | List, place, or move viewports and schedule instances in sheet paper space | Project |
306
+ | `search_api_docs` | Search the offline Revit API docs (works with no document open) | Any, no document needed |
307
+ | `execute_csharp` | Run a C# script with separate JSON inputs in one auto-managed transaction | Project, family |
308
+ | `manage_revit_scripts` | Save immutable local script versions, inspect source, run an exact version, and read history | Pi-side |
309
+ | `capture_view` | PNG snapshot of a view to a temp file (read the returned path to see it) | Project, family |
310
+ | `export_documents` | PDF/DWG/PNG/IFC export of sheets and views — sorted into `Models\<title>--<identity hash>\exports` | Project, family |
311
+ | `get_model_health` | Warnings grouped + worksets, phases, design options audit | Project, family |
312
+ | `read_revit_result` | Read bounded fragments of a saved large tool result; extension-only, no Revit call | Pi-side |
313
+ | `get_revit_operation` | Inspect a bridge operation receipt and its retained result without waiting for the model thread | Pi-side |
314
+
315
+ **Works in:** "Project, family" tools run in project and family documents. "Project" tools (sheets,
316
+ sheet placements, schedules, tags, spatial queries, links, coordinates) refuse a family document before
317
+ running and name the route to use instead. "Pi-side" utilities run in the extension and do not depend on
318
+ the open document; `search_api_docs` needs a running Revit but no open document. Each manual states this under "Works in".
319
+
320
+ ### Work in family documents
321
+
322
+ `get_model_overview` reports `project.documentKind` (`project` or `family`). For a family it also returns
323
+ a family block: category, current type, type names, and parameters (name, instance or type, formula).
324
+ Family types, parameters and formulas are edited through `FamilyManager` with `execute_csharp`; there is
325
+ no dedicated family-editing tool.
326
+
327
+ ### Choose a Revit instance
328
+
329
+ `manage_revit_instances` defaults to `action: "list"`. It reports reachable
330
+ local bridge sessions with an opaque `bridge_id`, process ID, Revit/add-in
331
+ versions, selection status, and operation-tracking support. Choose the intended
332
+ session with `action: "select"` and the exact returned `bridge_id`. Selection
333
+ belongs to the current Pi extension session; it does not activate a document
334
+ or change the model.
335
+
336
+ When the first connection finds a sole instance, Pi binds to it automatically.
337
+ If several instances are available before a target is bound, explicitly select
338
+ one before model calls. Once bound, Pi keeps that exact bridge session. If it
339
+ closes or restarts, calls fail instead of switching to another instance, even
340
+ when only one remains. List again and explicitly select the intended new
341
+ identity. Selection refreshes the tool catalogue; check `tool_catalog_ready`,
342
+ and retry `ping` if discovery has not completed. Then read `get_model_overview`
343
+ for a fresh document identity before continuing work.
344
+
345
+ Each current bridge publishes its own discovery file in
346
+ `%APPDATA%\RevitBridge\instances\<bridgeId>.json`. The legacy `bridge.json`
347
+ discovery file is still supported. Older bridges without a generation ID receive
348
+ an opaque hash selector in the instance list; copy it unchanged rather than
349
+ constructing one. Legacy discovery can expose only the older instance named by
350
+ that shared file, so updating each add-in enables discovery of all instances.
351
+
352
+ Operation receipts and identical `_operation_id` retries route to the original
353
+ bridge encoded in the operation ID, even after selecting a different instance.
354
+ They do not switch the target for new calls. If the original bridge is unavailable,
355
+ the request fails without sending the action to another session.
356
+
357
+ ### Read project coordinates
358
+
359
+ `get_model_coordinates` reads project/survey base points, the active project
360
+ location, site data, and paginated project locations. Supply a length `unit`;
361
+ optional `points` accepts up to 100 positions along document internal axes.
362
+ Revit's active project location maps those points to shared coordinates. Returned
363
+ lengths use the requested unit, while angles and latitude/longitude use degrees.
364
+ Location pages default to 50 entries (maximum 100); follow `next_offset`.
365
+ The tool requires a project document, makes no coordinate changes, and does not
366
+ infer a GIS coordinate reference system or datum.
367
+
368
+ ### Query regions and measure distances
369
+
370
+ `query_spatial_elements` takes a region `min`/`max` in the document's internal
371
+ axes and an explicit length `unit`. It compares host-element axis-aligned
372
+ bounding boxes with the region, using `intersects` (default) or `inside`.
373
+ Containment requires the whole box to fit; touching boundaries are included.
374
+ The nested `query` uses whole-scope element filters and must match at most
375
+ 10,000 candidates before region testing. Query-level paging and projections
376
+ are not accepted. Results sort by element ID with outer paging (default 100,
377
+ maximum 200); follow `next_offset`. `without_bounds_count` reports candidates
378
+ omitted because they have no model bounding box.
379
+
380
+ `measure_geometry` measures exact Euclidean distance between supplied points
381
+ with `mode: "point_distance"`, or approximate bounding-box separation between
382
+ two host elements with `mode: "bounding_box_gap"`. Point mode returns signed
383
+ A-to-B `delta`; box mode returns nonnegative axis gaps. The required `unit`
384
+ applies to all inputs and outputs: millimeters, centimeters, meters, feet, or
385
+ inches. Both modes use document internal axes; linked contents are not traversed.
386
+
387
+ Bounding-box gaps are lower bounds on geometry separation. Boxes can enclose
388
+ nonphysical geometry, so touching/overlapping boxes do not establish a clash.
389
+ An optional nonnegative `clearance` in box mode flags gaps strictly below that
390
+ threshold; it identifies review candidates, not verified clearance failures.
391
+ Keep the returned method and approximation status with any reported result.
392
+
393
+ Practical workflows are available in the Pi skill references:
394
+ [room documentation](skills/pi-revit/references/room-documentation.md) and
395
+ [model audit and export](skills/pi-revit/references/model-audit-export.md).
396
+
397
+ ### Read parameter values and summarize queries
398
+
399
+ Add `parameter_names` to `get_elements` to read up to 20 requested parameters
400
+ alongside element identity fields. Names can be display names, built-in parameter
401
+ names, or `guid:<GUID>` identities. `include_type_parameters: true` also reads
402
+ those parameters from each element's type. Projections expose missing and
403
+ ambiguous matches and include every matching parameter. Raw `value` uses Revit
404
+ internal units for measurable numbers; `displayValue` uses document formatting.
405
+ Bare counts return no parameter projections.
406
+
407
+ `summarize_elements` groups a host-element `query` by `category`, `typeName`,
408
+ `levelId`, or `parameter`. Parameter grouping uses exact raw values, with a
409
+ separate missing-parameter group, and rejects ambiguous display names. Set
410
+ `type_parameter: true` to group by a parameter on the type. The outer
411
+ `offset`/`limit` pages groups (default 100, maximum 500); counts always cover the
412
+ whole matching scope.
413
+
414
+ `summarize_elements` and element-set creation accept category, class, level,
415
+ type, active-view, and parameter filters inside `query`. They reject query-level
416
+ paging, projections, and `count_only`, and require at most 10,000 matching
417
+ elements. Narrow larger queries; an omitted query covers all host instances.
418
+
419
+ ### Reuse an element set
420
+
421
+ Use `manage_element_sets` with `action: "create"` to snapshot query membership,
422
+ then `read` its `set_id`, `list` active-document sets, or `forget` a set. This
423
+ changes neither the model nor selection. The bridge retains at most 32 sets;
424
+ each expires 30 minutes after creation and belongs to the exact open document
425
+ and bridge session. Reads do not renew it. Reopening the document or restarting
426
+ the bridge requires a fresh set.
427
+
428
+ Read pages return current values for the original members, without reapplying
429
+ the query. Deleted or identity-changed members appear in `missing`. Check those
430
+ entries before passing surviving IDs to other tools. Pages default to 100
431
+ members (maximum 1000); follow `next_offset`, which includes missing members in
432
+ `visited_count`. Set reads also support `parameter_names` and
433
+ `include_type_parameters`.
434
+
435
+ ### Preview parameter changes
436
+
437
+ `set_parameters` accepts 1–200 updates and defaults to committing successful
438
+ updates while reporting failures. Each update has its own subtransaction.
439
+ `atomic: true` rolls back the entire batch if any update fails. `preview: true`
440
+ performs eligible commit checks inside a transaction group, then rolls the
441
+ group back. Both options require the same exact document identity as an ordinary
442
+ write and can be combined.
443
+
444
+ Read `commit_validation_performed`: an atomic batch rejected before commit, or
445
+ a batch with no accepted updates, has not passed commit-time validation.
446
+ Successful preview responses confirm model rollback. Errors report the cleanup
447
+ outcome instead; inspect it before retrying.
448
+
449
+ Only committed updates appear in `succeeded`. Accepted steps from previews or
450
+ rolled-back batches appear in `proposed`, with `updated: 0` and
451
+ `committed: false`. Their `before`/`after` values describe each step in input
452
+ order, identified by its zero-based `index`. Repeated writes can have intermediate
453
+ values; reread the element when final values matter. Inspect `failed` and
454
+ `commitWarnings` as well. Numeric write inputs use the explicit `unit` or the
455
+ document's display units; raw numeric values in the returned snapshots use
456
+ Revit internal units.
457
+
458
+ ### Transform, delete, and change types
459
+
460
+ These advanced tools can be activated with `find_revit_tools`. They operate on
461
+ the active host document and require `expected_document_id` for both previews
462
+ and writes. Their results use the same `succeeded`, `proposed`, `failed`,
463
+ `committed`, and commit-validation fields as parameter batches. `updated` counts
464
+ steps: one whole-selection transform or deletion counts as one step.
465
+
466
+ `transform_elements` moves, copies, or rotates 1–200 distinct `element_ids`
467
+ together. Always supply `unit`: `millimeters`, `centimeters`, `meters`, `feet`,
468
+ or `inches`. Move/copy takes a `translation` vector. Rotation takes an
469
+ `axis_origin`, a nonzero dimensionless `axis_direction`, and signed right-hand
470
+ `angle_degrees`. Coordinates refer to the document's internal origin and axes;
471
+ returned location snapshots use feet, regardless of input units. The selection
472
+ succeeds or rolls back as one step. Move/rotate rejects pinned requested
473
+ elements; the tool never unpins them automatically. Copy previews return
474
+ temporary `created_ids`, which must not be reused after rollback.
475
+
476
+ `delete_elements` deletes 1–200 distinct requested elements in one step, rejecting
477
+ pinned requested elements. Use `preview: true` to inspect Revit's complete
478
+ `deleted_ids` set and its `dependent_ids`, with rollback. A cascade exceeding
479
+ 10,000 IDs rolls back. To check a later deletion against the preview, optionally
480
+ pass the full `deleted_ids` as `expected_deleted_ids`; a changed set causes
481
+ rollback before commit. This compares membership only. The actual deletion is
482
+ a new request, not an identical retry of the preview, so do not reuse the
483
+ preview's `_operation_id` for it.
484
+
485
+ `change_element_types` accepts 1–200 `element_id`/`type_id` pairs in `updates`,
486
+ with each target appearing once. Invalid types or pinned targets fail individually;
487
+ other valid changes can commit by default. `atomic: true` rolls back the complete
488
+ batch on any failure. Some type changes replace the original element: use the
489
+ committed `resulting_id` and `unique_id` afterward. Replacement IDs reported in
490
+ `proposed` are temporary after preview or atomic rollback.
491
+
492
+ Transforms and type changes can affect constrained or hosted elements beyond
493
+ the target snapshots. Deletion IDs cover elements returned by Revit's deletion
494
+ API, not surviving elements Revit may also modify. Review these results as
495
+ bounded descriptions of the operation, not a complete audit of every effect.
496
+
497
+ ### Create views and arrange sheets
498
+
499
+ Activate `manage_views`, `manage_sheets`, and `manage_sheet_placements` through
500
+ `find_revit_tools`. Edits support `preview: true` and require the exact document
501
+ identity. Each call edits one view, sheet, or placement in one step. Created IDs
502
+ in previews are marked `id_is_temporary` and must not be reused after rollback.
503
+ Inspect the common transaction outcome and validation fields. Use `open_view`
504
+ to display committed views/sheets and `delete_elements` to remove them.
505
+
506
+ `manage_views` supports `create_plan`, `create_3d`, `create_section`, `duplicate`,
507
+ and `update`. Creation uses a compatible `view_family_type_id`; discover it with
508
+ `get_element_types` and `of_class: "ViewFamilyType"`. Plans also need `level_id`;
509
+ 3D creation produces an isometric view. Duplicate/update targets `view_id`.
510
+ Duplication options are `duplicate`, `with_detailing`, and `dependent` where
511
+ supported. Optional name, scale, and template apply in the same step. Template
512
+ ID `-1` removes a template explicitly; incompatible templates or controlled
513
+ scale changes fail without automatically removing the template.
514
+
515
+ Sections use the document's internal coordinate frame. Supply `origin`, explicit
516
+ length `unit`, orthogonal nonzero `viewing_direction` and `up`, and positive
517
+ width/height/depth. Width and height center on the origin; depth extends in the
518
+ viewing direction. Direction vectors are dimensionless.
519
+
520
+ `manage_sheets` creates a sheet from `name` and `number`, optionally using a loaded
521
+ titleblock `FamilySymbol` specified by `titleblock_type_id`. Omitting it creates
522
+ a sheet without a titleblock. Updates use `sheet_id` and name/number; changing
523
+ an existing titleblock type uses `change_element_types` on its instance instead.
524
+
525
+ `manage_sheet_placements` lists both viewports and schedule instances for a
526
+ `sheet_id` (default 100 per page, maximum 200). Follow `next_offset`. Although
527
+ listing changes no model state, the tool's write-capable metadata means
528
+ `expected_document_id` is required for this action too.
529
+
530
+ To place content, supply `sheet_id`, `view_id`, explicit `unit`, and
531
+ `position: [x,y,0]`; to move it, supply `placement_id`, unit, and position.
532
+ Positions are paper-space sheet coordinates and must never be multiplied by
533
+ view scale. Viewports use their box center excluding the label; schedules use
534
+ their insertion point. Returned positions use feet and state `position_kind`.
535
+ Viewports support rotation `none`, `clockwise`, or `counterclockwise`; omit
536
+ rotation for schedules. Pinned placements cannot be moved, placeholder sheets
537
+ cannot receive content, and Revit checks whether a view can be placed.
538
+
539
+ ### Configure schedules and create tags
540
+
541
+ `manage_schedules` creates a regular schedule from `category` and `name`, with
542
+ an optional `area_scheme_id` for area schedules. Configure an existing schedule
543
+ with `schedule_id`. Each call is one atomic step, requires `expected_document_id`,
544
+ and supports preview rollback. Revision schedules, templates, embedded schedules,
545
+ and calculated/combined-field authoring are outside this tool.
546
+
547
+ Use `get_schedule_fields` to discover eligible fields for an existing schedule.
548
+ It supports localized name filtering and paging (default 100, maximum 200).
549
+ `add_fields` identifies each field by its `parameter_id` plus `field_type` pair;
550
+ negative built-in parameter IDs are valid. When Count appears in discovery,
551
+ pass its returned pair unchanged, just like other fields. Count can also be
552
+ added as `{ "field_type": "Count" }` without a parameter ID. A supplied pair
553
+ must be eligible for the target schedule; do not guess its parameter ID.
554
+ `included` marks pairs already present.
555
+
556
+ In contrast, `update_fields`, `sort_fields`, and `filters` use the schedule-local
557
+ `field_id` returned by `get_schedules`. These IDs are neither parameter IDs nor
558
+ column indices. Read newly committed fields before assigning their sort/filter
559
+ rules. New schedule and field IDs returned by previews are temporary.
560
+
561
+ Field additions and updates support heading, hidden state, and width, with up
562
+ to 50 entries each. Widths require explicit length units and apply to both grid
563
+ and sheet columns. `sort_fields` allows four rules; `filters` allows eight.
564
+ Supplying either array replaces its entire list, including clearing it with
565
+ `[]`; omitting the property preserves it. `is_itemized` controls itemization.
566
+
567
+ Filter comparisons are `equals`, `not_equals`, `contains`, `greater_than`, or
568
+ `less_than`. Supply a matching `value_type` and value. Measured numeric filters
569
+ require an explicit compatible `unit`; unitless numbers must omit it.
570
+ `get_schedules` exposes each field's specification, filtering capabilities,
571
+ grid/sheet widths in feet, and current sort/filter rules. Returned numeric filter
572
+ values use internal units; returned comparison names use Revit enums rather
573
+ than the write schema's comparison strings.
574
+
575
+ `create_tags` accepts 1–100 host-document targets in one explicit view, using a
576
+ loaded tag `FamilySymbol`. Choose `kind` as `element`, `room`, `space`, or `area`.
577
+ Each target has `element_id` and `head_position: [x,y,z]` in document internal
578
+ coordinates, using the required length `unit`. The position always means the
579
+ tag head, including when `leader: true`; returned positions use feet. Linked
580
+ targets and face/subelement references are unsupported.
581
+
582
+ Element tags support horizontal/vertical orientation. Spatial tags require a
583
+ compatible plan view and positions at the spatial element's level; omit their
584
+ orientation property. Independent tags cannot use templates, perspective views,
585
+ or unlocked 3D views. Creation requires exact document targeting, defaults to
586
+ partial success, and supports `atomic: true` and `preview: true`. IDs in
587
+ `proposed` are temporary after preview or atomic rollback; use only committed
588
+ tag IDs for subsequent calls.
589
+
590
+ ### Inspect links, schedules, and relationships
591
+
592
+ Call `get_linked_models` before querying a link. Pass its `link_instance_id` and
593
+ `linked_document_id` to `get_linked_elements` as `link_instance_id` and
594
+ `expected_linked_document_id`. The optional `expected_document_id` still identifies
595
+ the active host model. Refresh discovery after unloading or reloading a link.
596
+ Queries support `get_elements` filters and pagination, except active-view scoping.
597
+ Unloaded links cannot be queried, and nested links are not traversed.
598
+
599
+ Keep the full `reference` returned for each linked element: its numeric ID belongs
600
+ to the linked document and must not be used with host selection or write tools.
601
+ Different placements of one linked model can produce different host coordinates.
602
+ Optional `host_bounds` use internal feet and enclose all eight transformed box
603
+ corners; they are axis-aligned bounds, not exact geometry. A missing box is null.
604
+
605
+ Use `get_schedules` without an ID to list schedules, or with `schedule_id` to read
606
+ field definitions, width/specification metadata, sort/filter rules, and displayed
607
+ body cells. Text follows Revit's formatting and
608
+ can include grouped entries, headings, and totals; rows are not element IDs.
609
+ Hidden field definitions need not correspond to displayed columns. Follow both
610
+ `next_offset` and `next_column_offset`: finish the column pages for a row page
611
+ before moving to the next rows. List/body pages default to 50 entries (maximum
612
+ 200); column pages default to 50 (maximum 50).
613
+
614
+ `get_element_relationships` reports each requested relationship separately, with
615
+ its own count and continuation offset. It reads host-document relationships only;
616
+ logical dependents are not a complete deletion-impact prediction. In family
617
+ documents, request relationship kinds that exclude `joined`. Relationship pages
618
+ default to 100 entries (maximum 200). Link-instance pages default to 100 (maximum
619
+ 1000), and linked-element pages default to 200 (maximum 1000).
620
+
621
+ ### Reuse a script with structured inputs
622
+
623
+ `execute_csharp` accepts an optional `inputs` JSON object separately from its
624
+ source code. The script reads it through the `inputs` `JsonElement` global,
625
+ for example `inputs.GetProperty("value").GetDouble()`. Inputs default to an
626
+ empty object and are not interpolated into source text. Scripts validate their
627
+ contents; the serialized object is limited to 100,000 characters.
628
+
629
+ `manage_revit_scripts` stores reusable definitions under
630
+ `%APPDATA%\pi-revit\scripts`. Its actions are `save`, `list`, `read`, `run`, and
631
+ `history`. Saving requires a name, description, code, and `input_types`; it never
632
+ executes the script. Versions are immutable 64-character SHA-256 hashes of the
633
+ saved definition. Read a version to inspect its source, then explicitly run that
634
+ exact name/hash with current `expected_document_id` and inputs. Runs verify
635
+ saved content integrity and require a bridge with operation receipts.
636
+
637
+ Up to 40 named `input_types` can be declared as string, number, integer, boolean,
638
+ object, or array. Every declared input is required and extra inputs are rejected.
639
+ Validation covers top-level kinds only: scripts still validate nested objects,
640
+ array contents, ranges, units, and model-specific rules. The Pi skill includes
641
+ a short save/read/run example using a numeric input.
642
+
643
+ Library runs use unrestricted `execute_csharp`, with the same model, UI, file,
644
+ and external effects, synchronous execution, transaction, and timeout behavior.
645
+ There is no preview mode, scheduled execution, or automatic model saving. A saved
646
+ script is not a saved Revit model.
647
+
648
+ List/history pages default to 20 entries (maximum 100), with optional exact-name
649
+ filtering. Local history stores version, document identity, input hash,
650
+ timestamps, and receipt IDs, rather than raw inputs or results. History states
651
+ record dispatch/response progress, not a model transaction outcome. After a
652
+ timeout or interruption, inspect `get_revit_operation`; an identical retry uses
653
+ the original `_operation_id`, script version, inputs, and document identity.
654
+
655
+ ### See what a call changed
656
+
657
+ Every call that can change the model reports `model_changes`: the objects it added, modified and
658
+ deleted, and whether the call was rolled back. `observed: false` means no document change was seen.
659
+ This covers custom C# scripts too. New views list their hidden categories and elements. In a family,
660
+ the report also lists types and parameters added, removed or changed. State what a call changed from
661
+ this report, not from a screenshot.
662
+
663
+ Objects made from an existing one carry its state. Duplicating views, copying elements, changing types
664
+ and creating tags report `inherited_state`: hidden categories and elements, filters, overrides, the view
665
+ template, and carried values such as Mark and Comments. Compare it with the request and say what the
666
+ result derives from.
667
+
668
+ Objects that existed before the request are protected from silent reuse. A name already used by a view,
669
+ schedule, level, grid, type, material, filter or family type, or a sheet number already taken, is
670
+ rejected with `name_collision` and the existing object's ID. When a call writes to an object the request
671
+ names but did not create, the extension adds a scope note, and the agent must ask you or report it.
672
+ Names are compared in any writing system; there are no per-language rules.
673
+
674
+ ### Check an operation after a timeout
675
+
676
+ With a bridge that supports operation tracking, Pi automatically assigns an
677
+ operation ID to each bridge tool call and includes it in responses or request
678
+ errors. Call the native `get_revit_operation` tool with that `operation_id` to
679
+ check progress or recover the original result. This extension tool contacts the
680
+ operation's original local bridge directly, even when another instance is selected,
681
+ so it works while Revit's model thread is busy or no
682
+ document is open; the bridge must still be running.
683
+
684
+ | Receipt state | Meaning |
685
+ |---|---|
686
+ | `queued` | Waiting to begin; no tool execution has started. |
687
+ | `running` | Execution started; a client timeout or cancellation does not stop it. |
688
+ | `succeeded` | The tool returned successfully; inspect its payload for the actual model outcome. |
689
+ | `failed` | The call returned an error; this alone does not confirm rollback or absence of effects. |
690
+ | `expired_before_start` | Its queue deadline passed before execution; no tool action was performed. |
691
+ | `result_unavailable` | The tool finished, but a response could not be retained correctly; effects may already exist. |
692
+ | `unknown` | This bridge session has no receipt; the outcome is unknown. |
693
+
694
+ A successful receipt is not proof of a committed edit. For example, a completed
695
+ parameter preview can have receipt state `succeeded` while its payload reports
696
+ `preview: true`, `committed: false`, and proposed changes that were rolled back.
697
+ Inspect `committed`, validation flags, failures, and warnings in the retained
698
+ tool result.
699
+
700
+ To retry the same request, pass its exact ID as `_operation_id` on the original
701
+ tool call and preserve all original arguments. Within the same bridge session,
702
+ this waits for the existing operation or returns its cached response; it never
703
+ executes that ID a second time. Reusing the ID with a different tool or arguments
704
+ is rejected. Omitting `_operation_id` creates a new operation, so establish the
705
+ previous outcome before retrying an edit that way.
706
+
707
+ The bridge retains at most 128 completed full results, with a combined 32 MiB
708
+ limit. Older or oversized results can be evicted, but their receipt records
709
+ remain reserved for the session and report `result_available: false`. Replaying
710
+ an evicted result returns an error without repeating the action. At 10,000
711
+ receipt records, new tracked calls are rejected; existing receipts remain
712
+ queryable. A bridge restart clears all receipts and changes its identity. Old
713
+ IDs cannot be replayed in the new session. An unavailable original bridge or an
714
+ `unknown` receipt does not prove that an earlier edit never ran. Changing the
715
+ selected instance does not redirect old receipts or retries. Inspect the original
716
+ model before deciding on a new operation.
717
+
718
+ ### Read complete results
719
+
720
+ Requested rows and parameter values are included in model-visible tool content.
721
+ Results up to 12,000 characters are complete inline. For a larger result, the Pi
722
+ extension saves the complete tool payload as UTF-8 JSON and returns `result_id`,
723
+ `file_path`, `total_chars`, `complete_inline: false`, and retrieval instructions.
724
+
725
+ Call `read_revit_result` with the returned ID and `offset: 0`, then follow each
726
+ `next_offset` until `has_more` is false. A requested fragment is at most 8,000
727
+ UTF-16 code units; it may be smaller so the escaped response stays within the
728
+ message limit. Concatenate each page's `text` in order. Individual fragments are
729
+ not standalone JSON objects from the original result. Use the returned offsets,
730
+ not byte counts or a guessed increment.
731
+
732
+ Result IDs are registered in memory by the current extension instance. After an
733
+ extension reload or new Pi process, an old ID may no longer resolve; use the
734
+ original absolute `file_path` with Pi's normal `read` tool while that file remains
735
+ available. Saved results live in a unique OS temporary directory, can contain
736
+ model data, and are subject to eventual OS/user cleanup. If saving fails after
737
+ Revit completed an operation, inspect actual model state before retrying a write.
738
+
739
+ Complete payload retrieval does not expand a tool's own query page or declared
740
+ limits. Continue `get_elements`/`get_element_types` pagination separately, and
741
+ check warning-group or projection truncation indicators. A bridge-only client
742
+ must consume `details.payload` for oversized results; `read_revit_result` belongs
743
+ to the Pi extension.
744
+
745
+ Display-name parameter filters now resolve on every element, including inside a
746
+ category/class scope. Explicit built-in IDs and shared GUIDs can retain collector
747
+ optimization. In `get_element_details`, `include.parameters` and
748
+ `include.type_parameters` are independent; disabling instance parameters still
749
+ allows a type-only result.
750
+
751
+ ## Limitations — read before using on real projects
752
+
753
+ - **Writes change the open model directly.** There is no confirmation prompt.
754
+ Typed editing tools provide preview and transaction outcomes; `execute_csharp`
755
+ remains unrestricted and attempts rollback after script failure. Parameter
756
+ and type-change batches default to partial success, with optional atomic rollback.
757
+ Transforms and deletions apply the whole selection in one step. Read the
758
+ actual transaction outcome and failed lists. Script result-projection failure
759
+ can leave a successful edit committed with a `returnValueError`; filesystem and
760
+ UI effects are separate from model rollback.
761
+ - The add-in multi-targets .NET 8 (Revit 2025/2026) and .NET 10 (Revit 2027); `deploy.ps1`
762
+ auto-detects the Revit versions you have installed and builds only the matching framework(s),
763
+ so you only need the SDK for the Revit you run. Release 0.5.0 was tested live on
764
+ Revit 2025 only: a building project, a sample site model, a sample family, and requests
765
+ written in Chinese, Arabic, Japanese and Russian. Revit 2026/2027 and large-model
766
+ performance are not tested. Export API/file checks do not
767
+ establish full DWG drawing or IFC schema/geometry validation.
768
+ - Pi targets one bridge session at a time. Use `manage_revit_instances` to select
769
+ among reachable instances. Closing or restarting the selected bridge requires
770
+ explicit selection of an available identity; model calls never fall back to another.
771
+ - A tool call that outlives its timeout is abandoned client-side but may still complete inside
772
+ Revit. Check its receipt with `get_revit_operation`; when retrying the identical request,
773
+ reuse `_operation_id`. If its outcome remains unknown, inspect the model before a new write.
774
+ - Long-running scripts cannot be interrupted mid-execution (Revit's API is single-threaded);
775
+ the `execute_csharp` budget is 120s.
776
+
777
+ ## Uninstall
778
+
779
+ ### npm install
780
+
781
+ Close Revit, then in PowerShell — from any folder **outside** the installed package (Windows
782
+ cannot delete a folder your shell is standing in):
783
+
784
+ ```powershell
785
+ powershell -ExecutionPolicy Bypass -File "$env:USERPROFILE\.pi\agent\npm\node_modules\pi-revit\scripts\uninstall.ps1"
786
+ ```
787
+
788
+ ### Source install
789
+
790
+ Close Revit, then in PowerShell from the GitHub checkout:
791
+
792
+ ```powershell
793
+ powershell -ExecutionPolicy Bypass -File scripts\uninstall.ps1
794
+ ```
795
+
796
+ This removes the Revit bridge add-in (every installed Revit version), the global `pi-revit`
797
+ command, the bridge runtime folder (`%APPDATA%\RevitBridge\`), and the Pi package registration
798
+ (it tries both the npm and the source-install form, so no extra `pi remove` is needed for
799
+ either install kind). Your workspace at `Documents\pi-revit` (notes + session
800
+ history) is **preserved** — add `-RemoveWorkspace` to delete it too, or `-RevitVersion 2026` to
801
+ target a single Revit version. Pi itself is left installed; remove it with
802
+ `npm uninstall -g @earendil-works/pi-coding-agent` if you want.