pi-revit 0.2.6 → 0.2.7
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 +11 -0
- package/extensions/pi-revit/index.ts +70 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ Format follows [Keep a Changelog](https://keepachangelog.com/); version headers
|
|
|
7
7
|
Every published version gets an entry with **Added** / **Changed** / **Fixed** sections
|
|
8
8
|
describing what the user will notice — not internal refactors.
|
|
9
9
|
|
|
10
|
+
## [0.2.7] - 2026-07-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Update announcements: after a pi-revit update, the next pi session shows a one-time
|
|
14
|
+
"What's new in pi-revit" note listing the changelog entries for every version since the
|
|
15
|
+
last one announced — the same mechanism pi uses for its own updates. The last-announced
|
|
16
|
+
version is remembered in `%APPDATA%\pi-revit\state.json` (kept outside the package
|
|
17
|
+
folder so npm updates can't erase it). Fresh installs stay silent.
|
|
18
|
+
|
|
19
|
+
Extension-only change: no Revit add-in redeploy or Revit restart needed.
|
|
20
|
+
|
|
10
21
|
## [0.2.6] - 2026-07-20
|
|
11
22
|
|
|
12
23
|
### Fixed
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import { Type, type TSchema } from "typebox";
|
|
3
|
-
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
4
4
|
import os from "node:os";
|
|
5
5
|
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
6
7
|
import { version as packageVersion } from "../../package.json";
|
|
7
8
|
|
|
8
9
|
interface BridgeInfo {
|
|
@@ -207,6 +208,73 @@ function versionMismatch(addinVersion: unknown): string | null {
|
|
|
207
208
|
return `pi-revit ${packageVersion} is installed, but the Revit add-in ${state} — the update is incomplete. Close Revit and run: npx.cmd -y pi-revit (or ask the agent to run scripts\\deploy.ps1 from the installed package, then restart Revit).`;
|
|
208
209
|
}
|
|
209
210
|
|
|
211
|
+
// ------------------------------------------------------------- what's new
|
|
212
|
+
|
|
213
|
+
/** Stable per-user state file. Deliberately NOT under node_modules: npm wipes
|
|
214
|
+
* the package folder on every update, which is exactly when the last-announced
|
|
215
|
+
* version must survive. */
|
|
216
|
+
function announcerStatePath(): string {
|
|
217
|
+
const appData = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
|
|
218
|
+
return path.join(appData, "pi-revit", "state.json");
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function compareVersions(a: string, b: string): number {
|
|
222
|
+
const pa = a.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
223
|
+
const pb = b.split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
224
|
+
for (let i = 0; i < 3; i++) {
|
|
225
|
+
if ((pa[i] ?? 0) !== (pb[i] ?? 0)) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
226
|
+
}
|
|
227
|
+
return 0;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** Parses `## [x.y.z]` entries from the packaged CHANGELOG.md (the same header
|
|
231
|
+
* format pi's own changelog parser reads) and returns the ones newer than
|
|
232
|
+
* sinceVersion, newest first — so a 0.2.5 -> 0.2.9 jump shows all four. */
|
|
233
|
+
async function newChangelogEntries(sinceVersion: string): Promise<{ version: string; body: string }[]> {
|
|
234
|
+
const changelogPath = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "CHANGELOG.md");
|
|
235
|
+
const markdown = await readFile(changelogPath, "utf8");
|
|
236
|
+
const headers = [...markdown.matchAll(/^##\s+\[?(\d+\.\d+\.\d+)\]?[^\n]*$/gm)];
|
|
237
|
+
const entries: { version: string; body: string }[] = [];
|
|
238
|
+
for (let i = 0; i < headers.length; i++) {
|
|
239
|
+
const version = headers[i][1];
|
|
240
|
+
if (compareVersions(version, sinceVersion) <= 0) continue;
|
|
241
|
+
const start = (headers[i].index ?? 0) + headers[i][0].length;
|
|
242
|
+
const end = i + 1 < headers.length ? headers[i + 1].index : markdown.length;
|
|
243
|
+
entries.push({ version, body: markdown.slice(start, end).trim() });
|
|
244
|
+
}
|
|
245
|
+
return entries;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/** Shows the changelog entries between the last announced version and the
|
|
249
|
+
* current one, once per update, then records the current version. A fresh
|
|
250
|
+
* install records silently (nothing is "new" yet). Every failure is swallowed:
|
|
251
|
+
* the announcer must never break a session. */
|
|
252
|
+
async function announceUpdateOnce(notify: (message: string, level: "info") => void): Promise<void> {
|
|
253
|
+
try {
|
|
254
|
+
const statePath = announcerStatePath();
|
|
255
|
+
let lastVersion: string | null = null;
|
|
256
|
+
try {
|
|
257
|
+
const state = JSON.parse(await readFile(statePath, "utf8")) as { lastAnnouncedVersion?: string };
|
|
258
|
+
if (typeof state.lastAnnouncedVersion === "string") lastVersion = state.lastAnnouncedVersion;
|
|
259
|
+
} catch {
|
|
260
|
+
// First run: no state yet.
|
|
261
|
+
}
|
|
262
|
+
if (lastVersion === packageVersion) return;
|
|
263
|
+
|
|
264
|
+
if (lastVersion) {
|
|
265
|
+
const entries = await newChangelogEntries(lastVersion);
|
|
266
|
+
if (entries.length > 0) {
|
|
267
|
+
const text = entries.map((entry) => `pi-revit ${entry.version}\n${entry.body}`).join("\n\n");
|
|
268
|
+
notify(`What's new in pi-revit (updated from ${lastVersion}):\n\n${text}`, "info");
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
await mkdir(path.dirname(statePath), { recursive: true });
|
|
272
|
+
await writeFile(statePath, JSON.stringify({ lastAnnouncedVersion: packageVersion }, null, 2), "utf8");
|
|
273
|
+
} catch {
|
|
274
|
+
// Never let the announcer break a session.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
210
278
|
function registerPing(pi: ExtensionAPI) {
|
|
211
279
|
pi.registerTool({
|
|
212
280
|
name: "ping",
|
|
@@ -236,6 +304,7 @@ export default async function revitConnector(pi: ExtensionAPI) {
|
|
|
236
304
|
// where the user lands after running `pi update --extensions`. Bridge down at
|
|
237
305
|
// session start is the normal Revit-closed case: stay quiet.
|
|
238
306
|
pi.on("session_start", async (_event, ctx) => {
|
|
307
|
+
await announceUpdateOnce((message, level) => ctx.ui.notify(message, level));
|
|
239
308
|
try {
|
|
240
309
|
const payload = (await bridgeRequest("/ping", { method: "GET" }, undefined, 3_000)) as { addinVersion?: string };
|
|
241
310
|
const warning = versionMismatch(payload.addinVersion);
|