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 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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-revit",
3
- "version": "0.2.6",
3
+ "version": "0.2.7",
4
4
  "description": "Native Pi connector for Autodesk Revit. Run npx.cmd -y pi-revit for the full Windows install.",
5
5
  "author": "Ahmad Altahlawi",
6
6
  "license": "MIT",