pi-revit 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +388 -351
- package/README.md +426 -22
- package/extensions/pi-revit/index.ts +246 -187
- package/extensions/pi-revit/instance-router.ts +86 -0
- package/extensions/pi-revit/script-library.ts +146 -0
- package/extensions/pi-revit/tool-catalog.ts +67 -0
- package/package.json +59 -59
- package/skills/pi-revit/SKILL.md +196 -32
- package/skills/pi-revit/references/model-audit-export.md +29 -0
- package/skills/pi-revit/references/room-documentation.md +28 -0
- package/src/Revit/BridgeServer.cs +87 -37
- package/src/Revit/OperationStore.cs +178 -0
- package/src/Revit/ToolRegistry.cs +57 -35
- package/src/Revit/Tools/CaptureView.cs +2 -1
- package/src/Revit/Tools/ChangeElementTypes.cs +60 -0
- package/src/Revit/Tools/CreateTags.cs +95 -0
- package/src/Revit/Tools/DeleteElements.cs +44 -0
- package/src/Revit/Tools/DocumentGuard.cs +64 -64
- package/src/Revit/Tools/ElementQueryScope.cs +27 -0
- package/src/Revit/Tools/ExecuteCsharp.cs +45 -35
- package/src/Revit/Tools/ExportDocuments.cs +121 -120
- package/src/Revit/Tools/FailureGuard.cs +26 -26
- package/src/Revit/Tools/GetElementDetails.cs +42 -12
- package/src/Revit/Tools/GetElementRelationships.cs +76 -0
- package/src/Revit/Tools/GetElements.cs +37 -23
- package/src/Revit/Tools/GetLinkedElements.cs +82 -0
- package/src/Revit/Tools/GetLinkedModels.cs +66 -0
- package/src/Revit/Tools/GetModelCoordinates.cs +49 -0
- package/src/Revit/Tools/GetModelOverview.cs +2 -2
- package/src/Revit/Tools/GetScheduleFields.cs +37 -0
- package/src/Revit/Tools/GetSchedules.cs +89 -0
- package/src/Revit/Tools/ManageElementSets.cs +106 -0
- package/src/Revit/Tools/ManageSchedules.cs +164 -0
- package/src/Revit/Tools/ManageSelection.cs +37 -36
- package/src/Revit/Tools/ManageSheetPlacements.cs +97 -0
- package/src/Revit/Tools/ManageSheets.cs +63 -0
- package/src/Revit/Tools/ManageViews.cs +100 -0
- package/src/Revit/Tools/MeasureGeometry.cs +54 -0
- package/src/Revit/Tools/ModelEditBatch.cs +102 -0
- package/src/Revit/Tools/ModelEditInputs.cs +49 -0
- package/src/Revit/Tools/OpenView.cs +2 -1
- package/src/Revit/Tools/QuerySpatialElements.cs +63 -0
- package/src/Revit/Tools/SetParameters.cs +43 -93
- package/src/Revit/Tools/SpatialBounds.cs +30 -0
- package/src/Revit/Tools/SummarizeElements.cs +87 -0
- package/src/Revit/Tools/ToolSupport.cs +1 -1
- package/src/Revit/Tools/TransformElements.cs +58 -0
- package/workspace/AGENTS.md +45 -45
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
import { mkdir, readdir, readFile, writeFile, rename } from "node:fs/promises";
|
|
4
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
|
|
8
|
+
type InputKind = "string" | "number" | "integer" | "boolean" | "object" | "array";
|
|
9
|
+
interface Definition { name: string; description: string; code: string; input_types: Record<string, InputKind>; }
|
|
10
|
+
interface Version extends Definition { version: string; created_at: string; }
|
|
11
|
+
interface Receipt { operation_id: string; bridge_id: string; }
|
|
12
|
+
type Result = { content: { type: "text"; text: string }[]; details?: unknown };
|
|
13
|
+
type Runner = (args: Record<string, unknown>, signal: AbortSignal | undefined, prepared: (receipt: Receipt) => Promise<void>) => Promise<Result>;
|
|
14
|
+
const kinds = ["string", "number", "integer", "boolean", "object", "array"] as const;
|
|
15
|
+
const object = (value: unknown): value is Record<string, unknown> => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16
|
+
const hash = (value: unknown) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
17
|
+
const scriptName = (name: unknown) => {
|
|
18
|
+
if (typeof name !== "string" || !/^[a-z][a-z0-9_-]{0,63}$/.test(name)) throw new Error("name must be 1–64 lowercase letters, digits, underscores or hyphens, starting with a letter.");
|
|
19
|
+
return name;
|
|
20
|
+
};
|
|
21
|
+
const versionId = (version: unknown) => {
|
|
22
|
+
if (typeof version !== "string" || !/^[a-f0-9]{64}$/.test(version)) throw new Error("version must be the exact saved 64-character hash.");
|
|
23
|
+
return version;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function createScriptLibrary(root: string) {
|
|
27
|
+
const versions = path.join(root, "versions"), history = path.join(root, "history");
|
|
28
|
+
const definition = (value: Record<string, unknown>): Definition => {
|
|
29
|
+
const name = scriptName(value.name);
|
|
30
|
+
if (typeof value.code !== "string" || !value.code.trim() || value.code.length > 100000) throw new Error("code must contain 1–100,000 characters.");
|
|
31
|
+
if (typeof value.description !== "string" || value.description.length > 2000) throw new Error("description must be a string of at most 2,000 characters.");
|
|
32
|
+
if (!object(value.input_types) || Object.keys(value.input_types).length > 40) throw new Error("input_types must declare at most 40 named inputs.");
|
|
33
|
+
const input_types: Record<string, InputKind> = Object.create(null);
|
|
34
|
+
for (const key of Object.keys(value.input_types).sort()) {
|
|
35
|
+
if (!/^[A-Za-z][A-Za-z0-9_]{0,63}$/.test(key) || !kinds.includes(value.input_types[key] as InputKind)) throw new Error(`Invalid input declaration: ${key}.`);
|
|
36
|
+
input_types[key] = value.input_types[key] as InputKind;
|
|
37
|
+
}
|
|
38
|
+
return { name, description: value.description, code: value.code, input_types };
|
|
39
|
+
};
|
|
40
|
+
async function files(directory: string) {
|
|
41
|
+
try { return (await readdir(directory)).filter(file => file.endsWith(".json")); }
|
|
42
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code === "ENOENT") return []; throw error; }
|
|
43
|
+
}
|
|
44
|
+
async function read(name: unknown, version: unknown): Promise<Version> {
|
|
45
|
+
const value = JSON.parse(await readFile(path.join(versions, `${scriptName(name)}--${versionId(version)}.json`), "utf8"));
|
|
46
|
+
const content = definition(value);
|
|
47
|
+
if (content.name !== name || hash(content) !== version || value.version !== version) throw new Error("Saved script integrity check failed; no code was executed.");
|
|
48
|
+
return { ...content, version: value.version, created_at: value.created_at };
|
|
49
|
+
}
|
|
50
|
+
async function save(args: Record<string, unknown>) {
|
|
51
|
+
const content = definition(args), version = hash(content);
|
|
52
|
+
await mkdir(versions, { recursive: true });
|
|
53
|
+
const file = path.join(versions, `${content.name}--${version}.json`);
|
|
54
|
+
const value = { ...content, version, created_at: new Date().toISOString() };
|
|
55
|
+
try { await writeFile(file, JSON.stringify(value, null, 2), { encoding: "utf8", flag: "wx" }); }
|
|
56
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error; }
|
|
57
|
+
const saved = await read(content.name, version);
|
|
58
|
+
return { name: saved.name, version, created_at: saved.created_at, file_path: file, executed: false };
|
|
59
|
+
}
|
|
60
|
+
function validateInputs(saved: Version, input: unknown) {
|
|
61
|
+
if (!object(input)) throw new Error("inputs must be a JSON object.");
|
|
62
|
+
if (JSON.stringify(input).length > 100000) throw new Error("inputs exceed 100,000 JSON characters.");
|
|
63
|
+
for (const name of Object.keys(input)) if (!Object.hasOwn(saved.input_types, name)) throw new Error(`Undeclared input: ${name}.`);
|
|
64
|
+
for (const [name, kind] of Object.entries(saved.input_types)) {
|
|
65
|
+
const value = input[name];
|
|
66
|
+
const valid = Object.hasOwn(input, name) && (kind === "array" ? Array.isArray(value) : kind === "object" ? object(value)
|
|
67
|
+
: kind === "integer" ? Number.isSafeInteger(value) : kind === "number" ? typeof value === "number" && Number.isFinite(value) : typeof value === kind);
|
|
68
|
+
if (!valid) throw new Error(`Input '${name}' is required and must be ${kind}.`);
|
|
69
|
+
}
|
|
70
|
+
return input;
|
|
71
|
+
}
|
|
72
|
+
async function list(name: unknown, offset: number, limit: number) {
|
|
73
|
+
const filter = name === undefined ? undefined : scriptName(name);
|
|
74
|
+
const names = (await files(versions)).filter(file => /^[a-z][a-z0-9_-]{0,63}--[a-f0-9]{64}\.json$/.test(file) && (!filter || file.slice(0, file.lastIndexOf("--")) === filter)).sort();
|
|
75
|
+
const entries = [];
|
|
76
|
+
for (const file of names.slice(offset, offset + limit)) {
|
|
77
|
+
const split = file.lastIndexOf("--");
|
|
78
|
+
const value = await read(file.slice(0, split), file.slice(split + 2, -5));
|
|
79
|
+
entries.push({ name: value.name, version: value.version, description: value.description, input_types: value.input_types, created_at: value.created_at });
|
|
80
|
+
}
|
|
81
|
+
return { total_count: names.length, offset, entries, next_offset: offset + entries.length < names.length ? offset + entries.length : null };
|
|
82
|
+
}
|
|
83
|
+
async function writeHistory(record: Record<string, unknown>) {
|
|
84
|
+
await mkdir(history, { recursive: true });
|
|
85
|
+
const file = path.join(history, `${record.run_id}.json`), temp = `${file}.${randomUUID()}.tmp`;
|
|
86
|
+
await writeFile(temp, JSON.stringify(record, null, 2), { encoding: "utf8", flag: "wx" });
|
|
87
|
+
await rename(temp, file);
|
|
88
|
+
}
|
|
89
|
+
async function readHistory(name: unknown, offset: number, limit: number) {
|
|
90
|
+
const filter = name === undefined ? undefined : scriptName(name);
|
|
91
|
+
const entries = [];
|
|
92
|
+
for (const file of await files(history)) {
|
|
93
|
+
if (!/^[a-f0-9-]{36}\.json$/.test(file)) continue;
|
|
94
|
+
const value = JSON.parse(await readFile(path.join(history, file), "utf8"));
|
|
95
|
+
if (!filter || value.name === filter) entries.push(value);
|
|
96
|
+
}
|
|
97
|
+
entries.sort((a, b) => b.started_at.localeCompare(a.started_at) || a.run_id.localeCompare(b.run_id));
|
|
98
|
+
return { total_count: entries.length, offset, entries: entries.slice(offset, offset + limit), next_offset: offset + limit < entries.length ? offset + limit : null };
|
|
99
|
+
}
|
|
100
|
+
return { root, read, save, list, validateInputs, writeHistory, readHistory };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function registerScriptLibrary(pi: ExtensionAPI, run: Runner, render: (value: unknown) => Promise<Result>) {
|
|
104
|
+
const library = createScriptLibrary(path.join(process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming"), "pi-revit", "scripts"));
|
|
105
|
+
pi.registerTool({
|
|
106
|
+
name: "manage_revit_scripts", label: "Manage Revit Scripts",
|
|
107
|
+
description: "Save, list, read, run and inspect local reusable C# scripts. Saving never executes code. Versions are immutable content hashes; read a version before explicitly running that exact hash. Every declared input is required; extra inputs are rejected. input_types validates top-level JSON kinds only; scripts validate nested contents and domain rules. Runs use execute_csharp with separate inputs JsonElement, exact expected_document_id and operation receipts. Scripts have the same unrestricted model/UI/file/external effects and transaction behavior as execute_csharp. Local history records version, document, input hash and receipt, not input values or results. After interruption, inspect get_revit_operation before retrying with the same _operation_id and identical inputs. No automatic runs or model saving.",
|
|
108
|
+
parameters: Type.Object({
|
|
109
|
+
action: Type.Union(["list", "save", "read", "run", "history"].map(value => Type.Literal(value))),
|
|
110
|
+
name: Type.Optional(Type.String()), version: Type.Optional(Type.String()), description: Type.Optional(Type.String({ maxLength: 2000 })),
|
|
111
|
+
code: Type.Optional(Type.String({ minLength: 1, maxLength: 100000 })),
|
|
112
|
+
input_types: Type.Optional(Type.Record(Type.String(), Type.Union(kinds.map(value => Type.Literal(value))))),
|
|
113
|
+
inputs: Type.Optional(Type.Record(Type.String(), Type.Unknown())), expected_document_id: Type.Optional(Type.String()), _operation_id: Type.Optional(Type.String()),
|
|
114
|
+
offset: Type.Optional(Type.Integer({ minimum: 0 })), limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 100 })),
|
|
115
|
+
}),
|
|
116
|
+
executionMode: "sequential",
|
|
117
|
+
async execute(_id, args, signal) {
|
|
118
|
+
const offset = Math.max(0, args.offset ?? 0), limit = Math.max(1, Math.min(100, args.limit ?? 20));
|
|
119
|
+
if (args.action === "save") return render(await library.save(args));
|
|
120
|
+
if (args.action === "list") return render(await library.list(args.name, offset, limit));
|
|
121
|
+
if (args.action === "history") return render(await library.readHistory(args.name, offset, limit));
|
|
122
|
+
if (args.action !== "read" && args.action !== "run") throw new Error("Unknown script library action.");
|
|
123
|
+
const saved = await library.read(args.name, args.version);
|
|
124
|
+
if (args.action === "read") return render(saved);
|
|
125
|
+
if (typeof args.expected_document_id !== "string" || !args.expected_document_id) throw new Error("Run requires expected_document_id from get_model_overview.");
|
|
126
|
+
const inputs = library.validateInputs(saved, args.inputs ?? {});
|
|
127
|
+
const record: Record<string, unknown> = { run_id: randomUUID(), name: saved.name, version: saved.version, started_at: new Date().toISOString(), expected_document_id: args.expected_document_id, inputs_hash: hash(inputs), state: "prepared" };
|
|
128
|
+
let dispatched = false;
|
|
129
|
+
try {
|
|
130
|
+
const result = await run({ code: saved.code, inputs, expected_document_id: args.expected_document_id, ...(args._operation_id !== undefined ? { _operation_id: args._operation_id } : {}) }, signal, async receipt => {
|
|
131
|
+
Object.assign(record, receipt); await library.writeHistory(record); dispatched = true;
|
|
132
|
+
});
|
|
133
|
+
Object.assign(record, { state: "response_received", completed_at: new Date().toISOString() });
|
|
134
|
+
try { await library.writeHistory(record); }
|
|
135
|
+
catch { result.content.push({ type: "text", text: "The script response was received, but the final local history update failed. Use the operation receipt for its outcome." }); }
|
|
136
|
+
return result;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
if (dispatched) {
|
|
139
|
+
Object.assign(record, { state: "outcome_unconfirmed", completed_at: new Date().toISOString() });
|
|
140
|
+
try { await library.writeHistory(record); } catch { /* Preserve the original error and operation ID. */ }
|
|
141
|
+
}
|
|
142
|
+
throw error;
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
|
|
4
|
+
export interface BridgeToolDescriptor {
|
|
5
|
+
name: string;
|
|
6
|
+
label?: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
category?: string;
|
|
9
|
+
tier?: string;
|
|
10
|
+
parameters?: unknown;
|
|
11
|
+
executionMode?: string;
|
|
12
|
+
write?: boolean;
|
|
13
|
+
effects?: string[];
|
|
14
|
+
requiresDocument?: boolean;
|
|
15
|
+
promptSnippet?: string | null;
|
|
16
|
+
promptGuidelines?: string[] | null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The bridge registry is the source of truth; discovery never duplicates its schemas. */
|
|
20
|
+
export function createToolCatalog(pi: ExtensionAPI, discover: () => Promise<boolean>) {
|
|
21
|
+
const entries = new Map<string, BridgeToolDescriptor>();
|
|
22
|
+
function hideAdvanced(names: string[] = [...entries.keys()]) {
|
|
23
|
+
const advanced = new Set(names.filter(name => entries.get(name)?.tier === "advanced"));
|
|
24
|
+
pi.setActiveTools(pi.getActiveTools().filter(name => !advanced.has(name)));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
pi.registerTool({
|
|
28
|
+
name: "find_revit_tools",
|
|
29
|
+
label: "Find Revit Tools",
|
|
30
|
+
description: "Search the installed Revit tool catalogue and activate matching tools for this Pi session. Search words match tool names and descriptions. Supply exact names to activate known tools. With no query or names, list tools without activating them. Activation is additive and does not run any Revit operation. Read the returned schemas on the next model turn before calling a newly activated tool.",
|
|
31
|
+
promptSnippet: "Find and activate specialist Revit tools for links, schedules, relationships, inspection and editing.",
|
|
32
|
+
promptGuidelines: ["Use find_revit_tools to discover and activate specialist Revit tools before using execute_csharp for a task that may already have a dedicated tool."],
|
|
33
|
+
parameters: Type.Object({
|
|
34
|
+
query: Type.Optional(Type.String({ description: "Search words, such as linked elements or schedules." })),
|
|
35
|
+
names: Type.Optional(Type.Array(Type.String(), { maxItems: 20, description: "Exact tool names; takes precedence over query." })),
|
|
36
|
+
activate: Type.Optional(Type.Boolean({ description: "Activate returned matches. Default true for a query or exact names; false for browsing." })),
|
|
37
|
+
offset: Type.Optional(Type.Integer({ minimum: 0 })),
|
|
38
|
+
limit: Type.Optional(Type.Integer({ minimum: 1, maximum: 20 })),
|
|
39
|
+
}),
|
|
40
|
+
executionMode: "sequential",
|
|
41
|
+
async execute(_id, args) {
|
|
42
|
+
if (!entries.size && !(await discover())) throw new Error("The Revit tool catalogue is unavailable. Start Revit with the bridge loaded and retry.");
|
|
43
|
+
const names = args.names?.length ? [...new Set(args.names)] : undefined;
|
|
44
|
+
if (names?.some(name => !entries.has(name))) throw new Error(`Unknown Revit tool names: ${names.filter(name => !entries.has(name)).join(", ")}`);
|
|
45
|
+
const words = (args.query ?? "").trim().toLowerCase().split(/[\s_]+/).filter(Boolean);
|
|
46
|
+
const matches = [...entries.values()].filter(entry => names ? names.includes(entry.name)
|
|
47
|
+
: words.every(word => `${entry.name.replaceAll("_", " ")} ${entry.description ?? ""}`.toLowerCase().includes(word)))
|
|
48
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
|
+
const offset = Math.max(0, args.offset ?? 0);
|
|
50
|
+
const page = matches.slice(offset, offset + Math.max(1, Math.min(20, args.limit ?? 10)));
|
|
51
|
+
const activate = args.activate ?? Boolean(names || words.length);
|
|
52
|
+
if (activate && page.length) pi.setActiveTools([...new Set([...pi.getActiveTools(), ...page.map(entry => entry.name)])]);
|
|
53
|
+
const active = new Set(pi.getActiveTools());
|
|
54
|
+
const result = {
|
|
55
|
+
total_count: matches.length, offset, returned_count: page.length,
|
|
56
|
+
next_offset: offset + page.length < matches.length ? offset + page.length : null,
|
|
57
|
+
tools: page.map(entry => ({ name: entry.name, description: entry.description, tier: entry.tier ?? "core", effects: entry.effects ?? (entry.write ? ["model"] : []), active: active.has(entry.name) })),
|
|
58
|
+
};
|
|
59
|
+
return { content: [{ type: "text" as const, text: JSON.stringify(result) }], details: result };
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
function reset() {
|
|
63
|
+
pi.setActiveTools(pi.getActiveTools().filter(name => !entries.has(name)));
|
|
64
|
+
entries.clear();
|
|
65
|
+
}
|
|
66
|
+
return { add: (entry: BridgeToolDescriptor) => entries.set(entry.name, entry), hideAdvanced, reset };
|
|
67
|
+
}
|
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "pi-revit",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Native Pi connector for Autodesk Revit. Run npx.cmd -y pi-revit for the full Windows install.",
|
|
5
|
-
"author": "Ahmad Altahlawi",
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "git+https://github.com/Triavision-ai/pi-revit.git"
|
|
10
|
-
},
|
|
11
|
-
"bugs": {
|
|
12
|
-
"url": "https://github.com/Triavision-ai/pi-revit/issues"
|
|
13
|
-
},
|
|
14
|
-
"homepage": "https://github.com/Triavision-ai/pi-revit#readme",
|
|
15
|
-
"keywords": [
|
|
16
|
-
"pi-package",
|
|
17
|
-
"pi-extension",
|
|
18
|
-
"revit",
|
|
19
|
-
"bim",
|
|
20
|
-
"autodesk",
|
|
21
|
-
"revit-api"
|
|
22
|
-
],
|
|
23
|
-
"bin": {
|
|
24
|
-
"pi-revit": "bin/pi-revit.js"
|
|
25
|
-
},
|
|
26
|
-
"scripts": {
|
|
27
|
-
"deploy": "powershell -ExecutionPolicy Bypass -File scripts/deploy.ps1",
|
|
28
|
-
"setup": "powershell -ExecutionPolicy Bypass -File scripts/setup-workspace.ps1",
|
|
29
|
-
"uninstall:revit": "powershell -ExecutionPolicy Bypass -File scripts/uninstall.ps1",
|
|
30
|
-
"test:search": "dotnet run --project tests/search-engine",
|
|
31
|
-
"test:installer": "node --test tests/installer/*.test.cjs",
|
|
32
|
-
"test:sdk": "powershell -NoProfile -ExecutionPolicy Bypass -File tests/installer/sdk.test.ps1"
|
|
33
|
-
},
|
|
34
|
-
"files": [
|
|
35
|
-
"bin/",
|
|
36
|
-
"extensions/",
|
|
37
|
-
"skills/",
|
|
38
|
-
"scripts/",
|
|
39
|
-
"src/",
|
|
40
|
-
"workspace/",
|
|
41
|
-
"README.md",
|
|
42
|
-
"CHANGELOG.md"
|
|
43
|
-
],
|
|
44
|
-
"pi": {
|
|
45
|
-
"extensions": [
|
|
46
|
-
"./extensions/pi-revit/index.ts"
|
|
47
|
-
],
|
|
48
|
-
"skills": [
|
|
49
|
-
"./skills"
|
|
50
|
-
]
|
|
51
|
-
},
|
|
52
|
-
"engines": {
|
|
53
|
-
"node": ">=20.3"
|
|
54
|
-
},
|
|
55
|
-
"peerDependencies": {
|
|
56
|
-
"@earendil-works/pi-coding-agent": "*",
|
|
57
|
-
"typebox": "*"
|
|
58
|
-
}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "pi-revit",
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "Native Pi connector for Autodesk Revit. Run npx.cmd -y pi-revit for the full Windows install.",
|
|
5
|
+
"author": "Ahmad Altahlawi",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/Triavision-ai/pi-revit.git"
|
|
10
|
+
},
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/Triavision-ai/pi-revit/issues"
|
|
13
|
+
},
|
|
14
|
+
"homepage": "https://github.com/Triavision-ai/pi-revit#readme",
|
|
15
|
+
"keywords": [
|
|
16
|
+
"pi-package",
|
|
17
|
+
"pi-extension",
|
|
18
|
+
"revit",
|
|
19
|
+
"bim",
|
|
20
|
+
"autodesk",
|
|
21
|
+
"revit-api"
|
|
22
|
+
],
|
|
23
|
+
"bin": {
|
|
24
|
+
"pi-revit": "bin/pi-revit.js"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"deploy": "powershell -ExecutionPolicy Bypass -File scripts/deploy.ps1",
|
|
28
|
+
"setup": "powershell -ExecutionPolicy Bypass -File scripts/setup-workspace.ps1",
|
|
29
|
+
"uninstall:revit": "powershell -ExecutionPolicy Bypass -File scripts/uninstall.ps1",
|
|
30
|
+
"test:search": "dotnet run --project tests/search-engine",
|
|
31
|
+
"test:installer": "node --test tests/installer/*.test.cjs",
|
|
32
|
+
"test:sdk": "powershell -NoProfile -ExecutionPolicy Bypass -File tests/installer/sdk.test.ps1"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin/",
|
|
36
|
+
"extensions/",
|
|
37
|
+
"skills/",
|
|
38
|
+
"scripts/",
|
|
39
|
+
"src/",
|
|
40
|
+
"workspace/",
|
|
41
|
+
"README.md",
|
|
42
|
+
"CHANGELOG.md"
|
|
43
|
+
],
|
|
44
|
+
"pi": {
|
|
45
|
+
"extensions": [
|
|
46
|
+
"./extensions/pi-revit/index.ts"
|
|
47
|
+
],
|
|
48
|
+
"skills": [
|
|
49
|
+
"./skills"
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
"engines": {
|
|
53
|
+
"node": ">=20.3"
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
57
|
+
"typebox": "*"
|
|
58
|
+
}
|
|
59
|
+
}
|