pi-revit 0.3.1 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/AGENTS.md +167 -0
  2. package/CHANGELOG.md +114 -42
  3. package/README.md +598 -138
  4. package/bin/pi-revit.js +9 -9
  5. package/docs/architecture.md +271 -0
  6. package/docs/evaluation.md +434 -0
  7. package/docs/invariants.json +147 -0
  8. package/extensions/pi-revit/completion-monitor.ts +55 -0
  9. package/extensions/pi-revit/contracts.ts +146 -0
  10. package/extensions/pi-revit/discovery.ts +93 -0
  11. package/extensions/pi-revit/index.ts +231 -85
  12. package/extensions/pi-revit/instance-router.ts +86 -0
  13. package/extensions/pi-revit/platform-prompt.ts +40 -0
  14. package/extensions/pi-revit/scope-monitor.ts +114 -0
  15. package/extensions/pi-revit/script-library.ts +146 -0
  16. package/extensions/pi-revit/tool-catalog.ts +166 -0
  17. package/extensions/pi-revit/tool-documentation.ts +72 -0
  18. package/extensions/pi-revit/tool-schema.ts +8 -0
  19. package/package.json +65 -59
  20. package/scripts/build.ps1 +9 -9
  21. package/scripts/check-sdk.ps1 +66 -66
  22. package/scripts/check-tool-documentation.mjs +287 -0
  23. package/scripts/deploy.ps1 +16 -16
  24. package/scripts/generate-contracts.mjs +80 -0
  25. package/scripts/lib/platform.mjs +226 -0
  26. package/scripts/test-extension.mjs +15 -0
  27. package/skills/pi-revit/SKILL.md +39 -63
  28. package/skills/pi-revit/contracts.generated.json +3524 -0
  29. package/skills/pi-revit/references/execution-rules.md +41 -0
  30. package/skills/pi-revit/references/model-audit-export.md +40 -0
  31. package/skills/pi-revit/references/operation-recovery.md +33 -0
  32. package/skills/pi-revit/references/room-documentation.md +39 -0
  33. package/skills/pi-revit/references/tool-index.md +89 -0
  34. package/skills/pi-revit/references/tools/capture_view.md +62 -0
  35. package/skills/pi-revit/references/tools/change_element_types.md +65 -0
  36. package/skills/pi-revit/references/tools/create_tags.md +85 -0
  37. package/skills/pi-revit/references/tools/delete_elements.md +66 -0
  38. package/skills/pi-revit/references/tools/execute_csharp.md +81 -0
  39. package/skills/pi-revit/references/tools/export_documents.md +75 -0
  40. package/skills/pi-revit/references/tools/find_revit_tools.md +96 -0
  41. package/skills/pi-revit/references/tools/get_element_details.md +66 -0
  42. package/skills/pi-revit/references/tools/get_element_relationships.md +61 -0
  43. package/skills/pi-revit/references/tools/get_element_types.md +67 -0
  44. package/skills/pi-revit/references/tools/get_elements.md +87 -0
  45. package/skills/pi-revit/references/tools/get_linked_elements.md +79 -0
  46. package/skills/pi-revit/references/tools/get_linked_models.md +57 -0
  47. package/skills/pi-revit/references/tools/get_model_coordinates.md +64 -0
  48. package/skills/pi-revit/references/tools/get_model_health.md +53 -0
  49. package/skills/pi-revit/references/tools/get_model_overview.md +57 -0
  50. package/skills/pi-revit/references/tools/get_revit_operation.md +54 -0
  51. package/skills/pi-revit/references/tools/get_schedule_fields.md +62 -0
  52. package/skills/pi-revit/references/tools/get_schedules.md +71 -0
  53. package/skills/pi-revit/references/tools/manage_element_sets.md +92 -0
  54. package/skills/pi-revit/references/tools/manage_revit_instances.md +63 -0
  55. package/skills/pi-revit/references/tools/manage_revit_scripts.md +109 -0
  56. package/skills/pi-revit/references/tools/manage_schedules.md +90 -0
  57. package/skills/pi-revit/references/tools/manage_selection.md +66 -0
  58. package/skills/pi-revit/references/tools/manage_sheet_placements.md +82 -0
  59. package/skills/pi-revit/references/tools/manage_sheets.md +71 -0
  60. package/skills/pi-revit/references/tools/manage_views.md +95 -0
  61. package/skills/pi-revit/references/tools/measure_geometry.md +71 -0
  62. package/skills/pi-revit/references/tools/open_view.md +59 -0
  63. package/skills/pi-revit/references/tools/ping.md +41 -0
  64. package/skills/pi-revit/references/tools/query_spatial_elements.md +74 -0
  65. package/skills/pi-revit/references/tools/read_revit_result.md +53 -0
  66. package/skills/pi-revit/references/tools/search_api_docs.md +65 -0
  67. package/skills/pi-revit/references/tools/set_parameters.md +75 -0
  68. package/skills/pi-revit/references/tools/summarize_elements.md +64 -0
  69. package/skills/pi-revit/references/tools/transform_elements.md +79 -0
  70. package/skills/pi-revit/references/visual-verification.md +36 -0
  71. package/skills/pi-revit/tool-manifest.json +338 -0
  72. package/src/Revit/BridgeServer.cs +75 -19
  73. package/src/Revit/OperationStore.cs +178 -0
  74. package/src/Revit/ToolRegistry.cs +61 -8
  75. package/src/Revit/Tools/CaptureView.cs +9 -0
  76. package/src/Revit/Tools/ChangeElementTypes.cs +74 -0
  77. package/src/Revit/Tools/ChangeSet.cs +39 -0
  78. package/src/Revit/Tools/CreateTags.cs +107 -0
  79. package/src/Revit/Tools/DeleteElements.cs +53 -0
  80. package/src/Revit/Tools/DocumentGuard.cs +12 -2
  81. package/src/Revit/Tools/ElementNames.cs +103 -0
  82. package/src/Revit/Tools/ElementQueryScope.cs +27 -0
  83. package/src/Revit/Tools/ElementTraits.cs +53 -0
  84. package/src/Revit/Tools/ExecuteCsharp.cs +26 -7
  85. package/src/Revit/Tools/ExportDocuments.cs +9 -0
  86. package/src/Revit/Tools/FailureGuard.cs +26 -26
  87. package/src/Revit/Tools/GetElementDetails.cs +28 -2
  88. package/src/Revit/Tools/GetElementRelationships.cs +82 -0
  89. package/src/Revit/Tools/GetElementTypes.cs +8 -0
  90. package/src/Revit/Tools/GetElements.cs +58 -55
  91. package/src/Revit/Tools/GetLinkedElements.cs +89 -0
  92. package/src/Revit/Tools/GetLinkedModels.cs +73 -0
  93. package/src/Revit/Tools/GetModelCoordinates.cs +56 -0
  94. package/src/Revit/Tools/GetModelHealth.cs +7 -0
  95. package/src/Revit/Tools/GetModelOverview.cs +187 -160
  96. package/src/Revit/Tools/GetScheduleFields.cs +44 -0
  97. package/src/Revit/Tools/GetSchedules.cs +96 -0
  98. package/src/Revit/Tools/InheritedState.Summary.cs +57 -0
  99. package/src/Revit/Tools/InheritedState.cs +144 -0
  100. package/src/Revit/Tools/ManageElementSets.cs +114 -0
  101. package/src/Revit/Tools/ManageSchedules.cs +174 -0
  102. package/src/Revit/Tools/ManageSelection.cs +9 -0
  103. package/src/Revit/Tools/ManageSheetPlacements.cs +113 -0
  104. package/src/Revit/Tools/ManageSheets.cs +72 -0
  105. package/src/Revit/Tools/ManageViews.cs +115 -0
  106. package/src/Revit/Tools/MeasureGeometry.cs +60 -0
  107. package/src/Revit/Tools/ModelChanges.cs +154 -0
  108. package/src/Revit/Tools/ModelEditBatch.cs +105 -0
  109. package/src/Revit/Tools/ModelEditInputs.cs +49 -0
  110. package/src/Revit/Tools/OpenView.cs +8 -0
  111. package/src/Revit/Tools/ParameterResolver.cs +94 -0
  112. package/src/Revit/Tools/QuerySpatialElements.cs +70 -0
  113. package/src/Revit/Tools/SearchApiDocs.cs +72 -4
  114. package/src/Revit/Tools/SetParameters.cs +50 -119
  115. package/src/Revit/Tools/SpatialBounds.cs +30 -0
  116. package/src/Revit/Tools/SummarizeElements.cs +94 -0
  117. package/src/Revit/Tools/ToolContract.cs +48 -0
  118. package/src/Revit/Tools/ToolSupport.cs +4 -0
  119. package/src/Revit/Tools/TransformElements.cs +73 -0
  120. package/workspace/AGENTS.md +54 -48
@@ -0,0 +1,94 @@
1
+ using System.Text.Json;
2
+ using System.Text.Json.Nodes;
3
+
4
+ namespace RevitBridge.Tools;
5
+
6
+ internal sealed class SummarizeElements : ITool
7
+ {
8
+ private const int MaxElements = 10000;
9
+ public string Name => "summarize_elements";
10
+ public IReadOnlyList<string> DocumentKinds => DocumentKind.Both;
11
+ public IReadOnlyList<string> Keywords => new[] { "statistics", "count by", "group by", "breakdown", "totals", "per level", "distribution" };
12
+ public IReadOnlyList<ToolLimit> Limits => new[]
13
+ {
14
+ new ToolLimit("Linked-model contents", "tool", "get_linked_elements"),
15
+ new ToolLimit("Scopes above 10,000 elements", "tool", "narrow the query, or get_elements with count_only for a total"),
16
+ };
17
+ public string Label => "Summarize Elements";
18
+ public string Tier => "advanced";
19
+ public string Description => "Count matching host-model elements grouped by category, type, level or one parameter value. Accepts get_elements category/class/level/type/filter scopes. At most 10,000 matches; narrow larger queries. Parameter grouping uses exact raw values, with a distinct missing-value group. Ambiguous display-name parameters are rejected. Counts cover the entire matching scope, not just the returned groups page.";
20
+ public object ParametersSchema
21
+ {
22
+ get
23
+ {
24
+ var query = ElementQueryScope.Schema();
25
+ return new
26
+ {
27
+ type = "object",
28
+ properties = new
29
+ {
30
+ query,
31
+ group_by = new { type = "string", @enum = new[] { "category", "typeName", "levelId", "parameter" } },
32
+ parameter = new { type = "string", description = "Required for group_by=parameter. Name, built-in identity or guid:<GUID>." },
33
+ type_parameter = new { type = "boolean", description = "Group by the parameter on the element type instead of the instance." },
34
+ offset = new { type = "integer", minimum = 0 },
35
+ limit = new { type = "integer", minimum = 1, maximum = 500 },
36
+ },
37
+ required = new[] { "group_by" },
38
+ };
39
+ }
40
+ }
41
+
42
+ public object Execute(JsonElement args, ToolContext context)
43
+ {
44
+ string groupBy = JsonArgs.GetString(args, "group_by") ?? "";
45
+ if (groupBy is not ("category" or "typeName" or "levelId" or "parameter")) throw new ArgumentException("Unknown group_by value.");
46
+ string? parameter = JsonArgs.GetString(args, "parameter");
47
+ if (groupBy == "parameter" && string.IsNullOrWhiteSpace(parameter)) throw new ArgumentException("parameter is required for parameter grouping.");
48
+ bool typeParameter = JsonArgs.GetBool(args, "type_parameter", false);
49
+ var query = ElementQueryScope.Parse(args);
50
+ JsonElement RunQuery() => JsonSerializer.SerializeToElement(((ToolOutput)new GetElements().Execute(JsonSerializer.SerializeToElement(query), context)!).Payload);
51
+ query["count_only"] = true;
52
+ var countResult = RunQuery();
53
+ int total = countResult.GetProperty("total_count").GetInt32();
54
+ object? warnings = countResult.TryGetProperty("warnings", out var queryWarnings) ? queryWarnings.Clone() : null;
55
+ if (total > MaxElements) throw new ArgumentException($"Query matches {total} elements; narrow the scope to at most {MaxElements}.");
56
+ query["count_only"] = false;
57
+ query["limit"] = 1000;
58
+ if (groupBy == "parameter")
59
+ {
60
+ query["parameter_names"] = new JsonArray(parameter);
61
+ query["include_type_parameters"] = typeParameter;
62
+ }
63
+ var counts = new Dictionary<string, (object? Value, bool Missing, int Count)>();
64
+ for (int position = 0; position < total; position += 1000)
65
+ {
66
+ query["offset"] = position;
67
+ foreach (var row in RunQuery().GetProperty("elements").EnumerateArray())
68
+ {
69
+ object? value = null;
70
+ bool missing = false;
71
+ if (groupBy == "parameter")
72
+ {
73
+ var projection = row.GetProperty("parameters").EnumerateArray().FirstOrDefault(p => p.GetProperty("isType").GetBoolean() == typeParameter);
74
+ missing = projection.ValueKind == JsonValueKind.Undefined || !projection.GetProperty("found").GetBoolean();
75
+ if (!missing)
76
+ {
77
+ if (projection.GetProperty("ambiguous").GetBoolean()) throw new ArgumentException($"Parameter '{parameter}' is ambiguous on element {row.GetProperty("id")}; use a built-in identity or shared GUID.");
78
+ value = projection.GetProperty("matches")[0].GetProperty("value").Clone();
79
+ }
80
+ }
81
+ else if (row.TryGetProperty(groupBy, out var field)) value = field.Clone();
82
+ string key = (missing ? "missing:" : "value:") + JsonSerializer.Serialize(value);
83
+ counts[key] = counts.TryGetValue(key, out var previous) ? (value, missing, previous.Count + 1) : (value, missing, 1);
84
+ }
85
+ }
86
+ int offset = Math.Max(0, JsonArgs.GetInt(args, "offset", 0));
87
+ int limit = Math.Clamp(JsonArgs.GetInt(args, "limit", 100), 1, 500);
88
+ var groups = counts.OrderByDescending(pair => pair.Value.Count).ThenBy(pair => pair.Key, StringComparer.Ordinal).Skip(offset).Take(limit)
89
+ .Select(pair => new { value = pair.Value.Value, missing = pair.Value.Missing, count = pair.Value.Count }).ToArray();
90
+ return new { total_elements = total, total_groups = counts.Count, group_by = groupBy, parameter, type_parameter = typeParameter,
91
+ numeric_values = "Revit internal units", warnings, offset, returned_count = groups.Length,
92
+ next_offset = offset + groups.Length < counts.Count ? (int?)(offset + groups.Length) : null, groups };
93
+ }
94
+ }
@@ -0,0 +1,48 @@
1
+ namespace RevitBridge
2
+ {
3
+ /// <summary>
4
+ /// A declared capability boundary: something the tool deliberately does not cover and
5
+ /// the route to use instead. Alternative kinds: "tool" (Ref names another public tool),
6
+ /// "api" (Ref names Revit API members to verify with search_api_docs before custom
7
+ /// execution), "user" (needs a user action or decision), or "revit_unsupported" (the
8
+ /// Revit API itself does not offer it; Ref states the evidence). A limit never ends at
9
+ /// "unsupported" without a route, so agents do not report a false impossibility.
10
+ /// Kept in its own file so every project that compiles tool sources shares one type.
11
+ /// </summary>
12
+ internal sealed record ToolLimit(string What, string Alternative, string? Ref = null)
13
+ {
14
+ public static readonly IReadOnlyList<string> AlternativeKinds = new[] { "tool", "api", "user", "revit_unsupported" };
15
+ }
16
+
17
+ /// <summary>Allowed ITool.Verification values; see ITool.Verification.</summary>
18
+ internal static class ToolVerification
19
+ {
20
+ public static readonly IReadOnlyList<string> Kinds = new[] { "reread", "capture", "inspect_output", "none" };
21
+ }
22
+
23
+ /// <summary>
24
+ /// Document kinds a tool works in (inv:document-kind-declared). Every bridge tool declares
25
+ /// ITool.DocumentKinds; the dispatcher refuses any other kind before the tool runs, with the
26
+ /// route to use instead. Revit-free, so the refusal is tested offline.
27
+ /// </summary>
28
+ internal static class DocumentKind
29
+ {
30
+ public const string Project = "project";
31
+ public const string Family = "family";
32
+ public static readonly IReadOnlyList<string> Both = new[] { Project, Family };
33
+ public static readonly IReadOnlyList<string> ProjectOnly = new[] { Project };
34
+
35
+ /// <summary>Revit API route for family types, parameters and formulas; also a declared limit alternative.</summary>
36
+ public const string FamilyApi = "FamilyManager.Types; FamilyManager.NewType; FamilyManager.Set; FamilyManager.AddParameter; FamilyManager.SetFormula";
37
+
38
+ /// <summary>Why a tool cannot run in the active document, with the alternative; null when it can.</summary>
39
+ public static string? Refusal(string tool, IReadOnlyList<string> supported, string active, string title)
40
+ {
41
+ if (supported.Count == 0 || supported.Contains(active)) return null;
42
+ string route = active == Family
43
+ ? $"A family document has no sheets, schedules, rooms, links or project coordinates. For family types, parameters and formulas use the Revit API: verify '{FamilyApi}' with search_api_docs, then use execute_csharp."
44
+ : "Open the family for editing (Document.EditFamily) or ask the user to open the family document.";
45
+ return $"{tool} works in {string.Join(" and ", supported)} documents; the active document '{title}' is a {active} document. Nothing was run. {route}";
46
+ }
47
+ }
48
+ }
@@ -191,6 +191,10 @@ namespace RevitBridge.Tools
191
191
  _ => null,
192
192
  };
193
193
  }
194
+ // Shared projection: special or system-owned objects are flagged for every tool
195
+ // that lists elements (inv:special-objects-flagged). Ordinary elements add nothing.
196
+ if (ElementTraits.For(element) is { } traits)
197
+ row["traits"] = traits;
194
198
  return row;
195
199
  }
196
200
  }
@@ -0,0 +1,73 @@
1
+ using System.Text.Json;
2
+ using Autodesk.Revit.DB;
3
+
4
+ namespace RevitBridge.Tools;
5
+
6
+ internal sealed class TransformElements : ITool
7
+ {
8
+ public string Name => "transform_elements";
9
+ public IReadOnlyList<string> DocumentKinds => DocumentKind.Both;
10
+ public IReadOnlyList<string> Keywords => new[] { "move", "copy", "rotate", "shift", "offset", "relocate" };
11
+ public IReadOnlyList<ToolLimit> Limits => new[]
12
+ {
13
+ new ToolLimit("Mirroring", "api", "ElementTransformUtils.MirrorElements"),
14
+ new ToolLimit("Pinned elements (move and rotate reject them)", "user", "Confirm unpinning (Element.Pinned) first"),
15
+ new ToolLimit("Elements inside linked models", "user", "Edit the linked model itself"),
16
+ };
17
+ public string? Verification => "reread";
18
+ public string Label => "Transform Elements";
19
+ public string Tier => "advanced";
20
+ public bool Write => true;
21
+ public string Description => "Move, copy or rotate 1–200 elements together in the active document. Coordinates use the document internal origin and axes; unit is required. Rotation uses a right-handed axis and angle_degrees. The whole selection succeeds or rolls back as one step; pinned elements are never automatically unpinned. Revit may move constrained/hosted dependents too. A copy's result reports inherited_state: values and traits the copies carried over, such as Mark, Comments, group or design-option membership. preview=true validates commit then rolls back; any created IDs in a preview are temporary and must not be reused. Snapshots describe requested elements, not a complete dependent-change audit.";
22
+ public object ParametersSchema => new
23
+ {
24
+ type = "object", properties = new
25
+ {
26
+ action = new { type = "string", @enum = new[] { "move", "copy", "rotate" } },
27
+ element_ids = ModelEditInputs.IdsSchema,
28
+ unit = ModelEditInputs.LengthUnitSchema,
29
+ translation = ModelEditInputs.VectorSchema("Move/copy displacement [x,y,z] in unit."),
30
+ axis_origin = ModelEditInputs.VectorSchema("Rotation axis origin [x,y,z] in unit, relative to internal origin."),
31
+ axis_direction = ModelEditInputs.VectorSchema("Nonzero rotation axis direction [x,y,z], dimensionless."),
32
+ angle_degrees = new { type = "number", description = "Signed right-hand rotation angle in degrees." },
33
+ preview = ModelEditInputs.PreviewSchema,
34
+ }, required = new[] { "action", "element_ids", "unit" },
35
+ };
36
+
37
+ public object Execute(JsonElement args, ToolContext context)
38
+ {
39
+ var doc = context.Document ?? throw new NoActiveDocumentException();
40
+ var ids = ModelEditInputs.Ids(args);
41
+ string action = JsonArgs.GetString(args, "action") ?? "";
42
+ if (action is not ("move" or "copy" or "rotate")) throw new ArgumentException("action must be move, copy or rotate.");
43
+ double scale = ModelEditInputs.LengthScale(args);
44
+ XYZ? translation = action != "rotate" ? ModelEditInputs.Vector(args, "translation").Multiply(scale) : null;
45
+ XYZ? origin = action == "rotate" ? ModelEditInputs.Vector(args, "axis_origin").Multiply(scale) : null;
46
+ XYZ? direction = action == "rotate" ? ModelEditInputs.Vector(args, "axis_direction") : null;
47
+ double angle = action == "rotate" ? ModelEditInputs.Number(args, "angle_degrees") * Math.PI / 180 : 0;
48
+ if (direction != null && direction.GetLength() < 1e-12) throw new ArgumentException("axis_direction must be nonzero.");
49
+ var result = ModelEditBatch.Run(doc, Name, args, new[] { new ModelEditBatch.Step(
50
+ new() { ["action"] = action, ["element_ids"] = ids.Select(x => x.Value).ToArray() }, () =>
51
+ {
52
+ var elements = ids.Select(id => doc.GetElement(id) ?? throw new ArgumentException($"Element {id.Value} not found.")).ToArray();
53
+ if (action != "copy" && elements.Any(e => e.Pinned)) throw new ArgumentException("Selection contains pinned elements; no elements were unpinned.");
54
+ var before = elements.Select(ModelEditInputs.Snapshot).ToArray();
55
+ ICollection<ElementId> outputIds = ids;
56
+ if (action == "move") ElementTransformUtils.MoveElements(doc, ids, translation!);
57
+ else if (action == "copy") outputIds = ElementTransformUtils.CopyElements(doc, ids, translation!);
58
+ else ElementTransformUtils.RotateElements(doc, ids, Line.CreateUnbound(origin!, direction!.Normalize()), angle);
59
+ doc.Regenerate();
60
+ var result = new Dictionary<string, object?> { ["before"] = before, ["after"] = outputIds.Select(doc.GetElement).Where(e => e != null).Select(ModelEditInputs.Snapshot).ToArray(),
61
+ ["created_ids"] = action == "copy" ? outputIds.Select(id => id.Value).ToArray() : Array.Empty<long>(),
62
+ ["coordinate_system"] = "document_internal", ["snapshot_unit"] = "feet",
63
+ ["created_ids_are_temporary"] = action == "copy" && JsonArgs.GetBool(args, "preview", false) };
64
+ // Copies carry their sources' values and traits (inv:derived-state-reported). CopyElements
65
+ // returns copies in no guaranteed order, so each copy is reported without pairing it to one source.
66
+ if (action == "copy")
67
+ result["inherited_state"] = outputIds.Take(InheritedState.ListCap).Select(doc.GetElement).Where(e => e != null)
68
+ .Select(copy => new { id = copy!.Id.Value, state = InheritedState.OfElement(copy, null) }).Where(row => row.state != null).ToArray();
69
+ return result;
70
+ }) });
71
+ return result.Payload;
72
+ }
73
+ }
@@ -1,72 +1,78 @@
1
1
  # pi-revit workspace
2
2
 
3
- This folder tree is the working area for Pi + Revit sessions. The pi-revit tools talk to the
4
- Revit bridge add-in, targeting Revit 2025, 2026, and 2027. The 0.3.0 changes were
5
- tested live on Revit 2025. Bridge document tools require a project open; `ping` and
6
- `search_api_docs` work without a document. `read_revit_result` reads saved local
7
- results without contacting Revit.
3
+ This folder tree is the working area for Pi + Revit sessions. The pi-revit tools talk to the
4
+ Revit bridge add-in, targeting Revit 2025, 2026, and 2027. This file owns workspace
5
+ and output conventions. The `pi-revit` skill and focused tool manuals own current
6
+ operating guidance. `find_revit_tools` with `scope: "documentation"` returns manual
7
+ paths even while Revit is closed. Most bridge tools require a project open; `ping`
8
+ and `search_api_docs` need the bridge but no document. `read_revit_result` reads
9
+ saved local results without contacting Revit.
8
10
 
9
11
  ## File rules — where every file goes
10
12
 
11
13
  Work sorts by model, automatically. The export tool files its output under the model it came
12
- from — never a decision for you or the user to make:
14
+ from; it is not a decision for you or the user to make:
13
15
 
14
16
  ```text
15
17
  Documents\pi-revit\
16
18
  ├─ AGENTS.md <- this file
17
19
  ├─ pi-revit.cmd <- double-click launcher
18
20
  └─ Models\
19
- └─ <model title>--<identity hash>\ <- selected automatically for the exported document
20
- ├─ model.txt <- document identity/path marker written by the add-in
21
+ └─ <model title>--<identity hash>\ <- selected automatically for the exported document
22
+ ├─ model.txt <- document identity/path marker written by the add-in
21
23
  ├─ exports\ <- export_documents output (its default)
22
24
  ├─ captures\ <- view snapshots worth keeping
23
25
  └─ scripts\ <- generated scripts and analysis for that model
24
26
  ```
25
27
 
26
28
  1. **Let exports sort themselves**: call `export_documents` without `output_dir` — files land
27
- in an identity-derived model folder automatically. Treat the returned `outputDir`
28
- and file paths as authoritative; do not derive the hash from the model title or
29
- opaque `project.documentId`. Save As can change the destination. Existing title-only
30
- folders remain untouched. Pass
29
+ in an identity-derived model folder automatically. Treat the returned `outputDir`
30
+ and file paths as authoritative; do not derive the hash from the model title or
31
+ opaque `project.documentId`. Save As can change the destination. Existing title-only
32
+ folders remain untouched. Pass
31
33
  `output_dir` only when the user names a different target.
32
34
  2. **Anything else you produce about a model goes into that model's folder**: view captures the
33
- user wants to keep in that verified model folder's `captures` subfolder
34
- (`capture_view` writes to temp), and scripts and analysis in its `scripts`
35
- subfolder. For a default export, the model folder is the parent of returned
36
- `outputDir`. If no folder has been established, identify it from existing model
37
- markers or choose an explicit destination for the task; do not trigger an
38
- unnecessary export or guess a title-only folder. Create subfolders on first use.
39
- 3. **Never create files loose in the workspace root.** The root holds `AGENTS.md`,
35
+ user wants to keep in that verified model folder's `captures` subfolder
36
+ (`capture_view` writes to temp), and scripts and analysis in its `scripts`
37
+ subfolder. For a default export, the model folder is the parent of returned
38
+ `outputDir`. If no folder has been established, identify it from existing model
39
+ markers or choose an explicit destination for the task; do not trigger an
40
+ unnecessary export or guess a title-only folder. Create subfolders on first use.
41
+ 3. **Never create files loose in the workspace root.** <!-- inv:workspace-root-clean --> The root holds `AGENTS.md`,
40
42
  `pi-revit.cmd`, `Models\`, and Pi's own session data — nothing else, ever.
41
43
 
42
44
  ## Tool habits
43
45
 
44
- - Start unfamiliar models with `get_model_overview`; use `get_elements` for any listing or
45
- counting; read parameter values with `get_element_details`.
46
- - Copy the intended model's `project.documentId` unchanged into `expected_document_id`
47
- for `set_parameters`, `execute_csharp`, `export_documents`, `open_view`, and
48
- selection/zoom changes. Any `manage_selection` call with `isolate_in_view: true`
49
- also requires it, including action `get`. Pure reads may omit it; supplied IDs
50
- are checked. Legacy `expected_document` titles alone do not satisfy the guard.
51
- After a rejection, verify the intended model before refreshing its ID. Closing
52
- and reopening a model or restarting Revit invalidates earlier IDs.
53
- - Before `execute_csharp`, verify unfamiliar API signatures with `search_api_docs`.
54
- - Write tools (`set_parameters`, `execute_csharp`) change the real model — state clearly what
55
- was changed. `set_parameters` commits partial successes: always check its `failed` list.
56
- Inspect `commitWarnings` and the actual transaction outcome. A failed
57
- `execute_csharp` attempts rollback; do not assume rollback succeeded. A
58
- `returnValueError` can follow a committed edit. Earlier UI actions and created
59
- files can persist after failure. Read reported effects before retrying.
60
- - When a result returns `result_id`, use `read_revit_result` from offset zero and
61
- follow `next_offset` until `has_more` is false. Join its `text` fragments in order;
62
- offsets count UTF-16 code units. The returned absolute file path remains a
63
- fallback for `read` after extension reload while the file exists. Tool query
64
- pagination and limits still apply separately.
65
-
66
- ## Upgrade notes
67
-
68
- After installing 0.3.0, deploy the matching add-in with Revit closed, restart Revit,
69
- and start a fresh Pi session to load the new tool schemas. Obtain a new overview
70
- before changing a document. Setup preserves existing `AGENTS.md`; older workspaces
71
- need these exact-ID, saved-result, and identity-derived folder rules merged into
72
- their existing instructions.
46
+ - Distinguish explanations, inspections, and requested changes. General questions
47
+ need not connect to a model. An audit alone does not authorize repairs or exports.
48
+ - For model work, use the skill's execution rules, select the intended session,
49
+ and start unfamiliar models with `get_model_overview`. Discover dedicated tools
50
+ and read their returned manual paths and current schemas before using custom code.
51
+ - Copy the intended model's `project.documentId` unchanged into `expected_document_id`
52
+ whenever required by the schema/runtime, including writes, previews, export,
53
+ view/selection changes and every `manage_sheet_placements` action. Selection
54
+ isolation also requires it with action `get`. Supplied IDs on reads are checked.
55
+ A title alone is insufficient. On rejection verify the intended target before
56
+ refreshing the ID; reopening a document or restarting Revit invalidates old IDs.
57
+ - Before `execute_csharp`, verify unfamiliar API signatures with `search_api_docs`.
58
+ - Report actual retained changes. `set_parameters` defaults to partial success;
59
+ previews roll back, and atomic failures roll back the batch. Read the manual and
60
+ distinguish `succeeded`, `proposed`, and `failed`; discard rolled-back IDs.
61
+ Inspect `commitWarnings` and the actual transaction outcome. A failed
62
+ `execute_csharp` attempts rollback; do not assume rollback succeeded. A
63
+ `returnValueError` can follow a committed edit. Earlier UI actions and created
64
+ files can persist after failure. Read reported effects before retrying.
65
+ - When a result returns `result_id`, use `read_revit_result` from offset zero and
66
+ follow `next_offset` until `has_more` is false. Join its `text` fragments in order;
67
+ offsets count UTF-16 code units. The returned absolute file path remains a
68
+ fallback for `read` after extension reload while the file exists. Tool query
69
+ pagination and limits still apply separately.
70
+
71
+ ## Upgrade notes
72
+
73
+ After a package/add-in upgrade, deploy the matching add-in with Revit closed, restart Revit,
74
+ and start a fresh Pi session to load the new tool schemas. Obtain a new overview
75
+ before changing a document. Setup preserves existing `AGENTS.md`; older workspaces
76
+ need updated exact-ID, saved-result, identity-derived folder and guidance-routing
77
+ rules merged into their existing instructions. These are upgrade steps, not
78
+ actions to perform during an ordinary model question or documentation check.