@juanibiapina/pi-powerbar 0.9.0 → 0.9.1

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAA6B,MAAM,+BAA+B,CAAC;AAoB7F,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAkF9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAA6B,MAAM,+BAA+B,CAAC;AA+B7F,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAqF9D"}
@@ -6,6 +6,14 @@
6
6
  */
7
7
  import { renderBar } from "./render.js";
8
8
  import { loadSettings, registerSettings } from "./settings.js";
9
+ function segmentEquals(left, right) {
10
+ return (left?.text === right.text &&
11
+ left.suffix === right.suffix &&
12
+ left.icon === right.icon &&
13
+ left.color === right.color &&
14
+ left.bar === right.bar &&
15
+ left.barSegments === right.barSegments);
16
+ }
9
17
  export default function createExtension(pi) {
10
18
  const segments = new Map();
11
19
  const segmentCatalog = new Map();
@@ -41,10 +49,12 @@ export default function createExtension(pi) {
41
49
  if (!payload?.id)
42
50
  return;
43
51
  if (!payload.text && payload.bar === undefined) {
44
- segments.delete(payload.id);
52
+ const changed = segments.delete(payload.id);
53
+ if (!changed)
54
+ return;
45
55
  }
46
56
  else {
47
- segments.set(payload.id, {
57
+ const nextSegment = {
48
58
  id: payload.id,
49
59
  text: payload.text ?? "",
50
60
  suffix: payload.suffix,
@@ -52,7 +62,10 @@ export default function createExtension(pi) {
52
62
  color: payload.color,
53
63
  bar: payload.bar,
54
64
  barSegments: payload.barSegments,
55
- });
65
+ };
66
+ if (segmentEquals(segments.get(payload.id), nextSegment))
67
+ return;
68
+ segments.set(payload.id, nextSegment);
56
69
  }
57
70
  refresh();
58
71
  });
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Core Extension\n *\n * Listens for \"powerbar:update\" events from producer extensions,\n * maintains a segment store, and renders a powerline-style widget.\n */\n\nimport type { OrderedListOption } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI, ExtensionUIContext, Theme } from \"@mariozechner/pi-coding-agent\";\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { renderBar, type Segment } from \"./render.js\";\nimport { loadSettings, type PowerbarSettings, registerSettings } from \"./settings.js\";\n\ninterface PowerbarUpdatePayload {\n\tid: string;\n\ttext?: string;\n\tsuffix?: string;\n\ticon?: string;\n\tcolor?: string;\n\tbar?: number;\n\tbarSegments?: number;\n}\n\ninterface SegmentRegistration {\n\tid: string;\n\tlabel: string;\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tconst segments: Map<string, Segment> = new Map();\n\tconst segmentCatalog: Map<string, OrderedListOption> = new Map();\n\tlet settings: PowerbarSettings;\n\tlet currentCtx: { ui: { setWidget: (...args: any[]) => void }; hasUI: boolean } | undefined;\n\n\t// Register settings with empty options initially (no segments known yet)\n\tregisterSettings(pi, []);\n\n\t// Listen for segment registrations from producer extensions\n\tpi.events.on(\"powerbar:register-segment\", (data: unknown) => {\n\t\tconst { id, label } = data as SegmentRegistration;\n\t\tsegmentCatalog.set(id, { id, label });\n\t\t// Re-register settings with updated segment options\n\t\tregisterSettings(pi, Array.from(segmentCatalog.values()));\n\t});\n\n\tfunction refresh(): void {\n\t\tif (!currentCtx?.hasUI) return;\n\n\t\tcurrentCtx.ui.setWidget(\n\t\t\t\"powerbar\",\n\t\t\t(_tui: TUI, theme: Theme): Component & { dispose?(): void } => {\n\t\t\t\treturn {\n\t\t\t\t\trender(width: number): string[] {\n\t\t\t\t\t\tconst line = renderBar(segments, settings, theme, width);\n\t\t\t\t\t\treturn [line];\n\t\t\t\t\t},\n\t\t\t\t\tinvalidate(): void {\n\t\t\t\t\t\t// No cached state to clear\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\t{ placement: settings.placement },\n\t\t);\n\t}\n\n\t// Listen for segment updates from any extension\n\tpi.events.on(\"powerbar:update\", (data: unknown) => {\n\t\tconst payload = data as PowerbarUpdatePayload;\n\t\tif (!payload?.id) return;\n\n\t\tif (!payload.text && payload.bar === undefined) {\n\t\t\tsegments.delete(payload.id);\n\t\t} else {\n\t\t\tsegments.set(payload.id, {\n\t\t\t\tid: payload.id,\n\t\t\t\ttext: payload.text ?? \"\",\n\t\t\t\tsuffix: payload.suffix,\n\t\t\t\ticon: payload.icon,\n\t\t\t\tcolor: payload.color,\n\t\t\t\tbar: payload.bar,\n\t\t\t\tbarSegments: payload.barSegments,\n\t\t\t});\n\t\t}\n\n\t\trefresh();\n\t});\n\n\tfunction hideFooter(ctx: { ui: ExtensionUIContext; hasUI: boolean }): void {\n\t\tif (!ctx.hasUI) return;\n\t\tctx.ui.setFooter((_tui, _theme, _footerData) => ({\n\t\t\trender(): string[] {\n\t\t\t\treturn [];\n\t\t\t},\n\t\t\tinvalidate(): void {},\n\t\t}));\n\t}\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsettings = loadSettings();\n\t\tcurrentCtx = ctx;\n\t\thideFooter(ctx);\n\t\trefresh();\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setWidget(\"powerbar\", undefined);\n\t\t}\n\t\tcurrentCtx = undefined;\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Core Extension\n *\n * Listens for \"powerbar:update\" events from producer extensions,\n * maintains a segment store, and renders a powerline-style widget.\n */\n\nimport type { OrderedListOption } from \"@juanibiapina/pi-extension-settings\";\nimport type { ExtensionAPI, ExtensionUIContext, Theme } from \"@mariozechner/pi-coding-agent\";\nimport type { Component, TUI } from \"@mariozechner/pi-tui\";\nimport { renderBar, type Segment } from \"./render.js\";\nimport { loadSettings, type PowerbarSettings, registerSettings } from \"./settings.js\";\n\ninterface PowerbarUpdatePayload {\n\tid: string;\n\ttext?: string;\n\tsuffix?: string;\n\ticon?: string;\n\tcolor?: string;\n\tbar?: number;\n\tbarSegments?: number;\n}\n\ninterface SegmentRegistration {\n\tid: string;\n\tlabel: string;\n}\n\nfunction segmentEquals(left: Segment | undefined, right: Segment): boolean {\n\treturn (\n\t\tleft?.text === right.text &&\n\t\tleft.suffix === right.suffix &&\n\t\tleft.icon === right.icon &&\n\t\tleft.color === right.color &&\n\t\tleft.bar === right.bar &&\n\t\tleft.barSegments === right.barSegments\n\t);\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tconst segments: Map<string, Segment> = new Map();\n\tconst segmentCatalog: Map<string, OrderedListOption> = new Map();\n\tlet settings: PowerbarSettings;\n\tlet currentCtx: { ui: { setWidget: (...args: any[]) => void }; hasUI: boolean } | undefined;\n\n\t// Register settings with empty options initially (no segments known yet)\n\tregisterSettings(pi, []);\n\n\t// Listen for segment registrations from producer extensions\n\tpi.events.on(\"powerbar:register-segment\", (data: unknown) => {\n\t\tconst { id, label } = data as SegmentRegistration;\n\t\tsegmentCatalog.set(id, { id, label });\n\t\t// Re-register settings with updated segment options\n\t\tregisterSettings(pi, Array.from(segmentCatalog.values()));\n\t});\n\n\tfunction refresh(): void {\n\t\tif (!currentCtx?.hasUI) return;\n\n\t\tcurrentCtx.ui.setWidget(\n\t\t\t\"powerbar\",\n\t\t\t(_tui: TUI, theme: Theme): Component & { dispose?(): void } => {\n\t\t\t\treturn {\n\t\t\t\t\trender(width: number): string[] {\n\t\t\t\t\t\tconst line = renderBar(segments, settings, theme, width);\n\t\t\t\t\t\treturn [line];\n\t\t\t\t\t},\n\t\t\t\t\tinvalidate(): void {\n\t\t\t\t\t\t// No cached state to clear\n\t\t\t\t\t},\n\t\t\t\t};\n\t\t\t},\n\t\t\t{ placement: settings.placement },\n\t\t);\n\t}\n\n\t// Listen for segment updates from any extension\n\tpi.events.on(\"powerbar:update\", (data: unknown) => {\n\t\tconst payload = data as PowerbarUpdatePayload;\n\t\tif (!payload?.id) return;\n\n\t\tif (!payload.text && payload.bar === undefined) {\n\t\t\tconst changed = segments.delete(payload.id);\n\t\t\tif (!changed) return;\n\t\t} else {\n\t\t\tconst nextSegment: Segment = {\n\t\t\t\tid: payload.id,\n\t\t\t\ttext: payload.text ?? \"\",\n\t\t\t\tsuffix: payload.suffix,\n\t\t\t\ticon: payload.icon,\n\t\t\t\tcolor: payload.color,\n\t\t\t\tbar: payload.bar,\n\t\t\t\tbarSegments: payload.barSegments,\n\t\t\t};\n\t\t\tif (segmentEquals(segments.get(payload.id), nextSegment)) return;\n\t\t\tsegments.set(payload.id, nextSegment);\n\t\t}\n\n\t\trefresh();\n\t});\n\n\tfunction hideFooter(ctx: { ui: ExtensionUIContext; hasUI: boolean }): void {\n\t\tif (!ctx.hasUI) return;\n\t\tctx.ui.setFooter((_tui, _theme, _footerData) => ({\n\t\t\trender(): string[] {\n\t\t\t\treturn [];\n\t\t\t},\n\t\t\tinvalidate(): void {},\n\t\t}));\n\t}\n\n\tpi.on(\"session_start\", async (_event, ctx) => {\n\t\tsettings = loadSettings();\n\t\tcurrentCtx = ctx;\n\t\thideFooter(ctx);\n\t\trefresh();\n\t});\n\n\tpi.on(\"session_shutdown\", async (_event, ctx) => {\n\t\tif (ctx.hasUI) {\n\t\t\tctx.ui.setWidget(\"powerbar\", undefined);\n\t\t}\n\t\tcurrentCtx = undefined;\n\t});\n}\n"]}
@@ -3,7 +3,14 @@
3
3
  *
4
4
  * Shows subscription usage from pi-sub-core.
5
5
  * Sub-core is loaded by pi as a sibling extension (declared in package.json pi.extensions).
6
- * This producer just listens to sub-core events and emits powerbar segments.
6
+ *
7
+ * We listen only to `sub-core:ready` and `sub-core:update-current`. We intentionally
8
+ * skip `sub-core:update-all`: its entries list filters providers by cache TTL, so the
9
+ * current provider can be missing whenever its `fetchedAt` drifts past the refresh
10
+ * interval (e.g. during anthropic 429s, see https://github.com/marckrenn/pi-sub/issues/58).
11
+ * `update-current` is authoritative for the current provider's usage in every case,
12
+ * including when other pi instances refresh the shared cache (sub-core re-emits
13
+ * `update-current` from its `onCacheUpdate` listener).
7
14
  *
8
15
  * Segment IDs: "sub-hourly", "sub-weekly"
9
16
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA2ClE,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAuB9D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA4ClE,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,EAAE,EAAE,YAAY,GAAG,IAAI,CAW9D"}
@@ -3,7 +3,14 @@
3
3
  *
4
4
  * Shows subscription usage from pi-sub-core.
5
5
  * Sub-core is loaded by pi as a sibling extension (declared in package.json pi.extensions).
6
- * This producer just listens to sub-core events and emits powerbar segments.
6
+ *
7
+ * We listen only to `sub-core:ready` and `sub-core:update-current`. We intentionally
8
+ * skip `sub-core:update-all`: its entries list filters providers by cache TTL, so the
9
+ * current provider can be missing whenever its `fetchedAt` drifts past the refresh
10
+ * interval (e.g. during anthropic 429s, see https://github.com/marckrenn/pi-sub/issues/58).
11
+ * `update-current` is authoritative for the current provider's usage in every case,
12
+ * including when other pi instances refresh the shared cache (sub-core re-emits
13
+ * `update-current` from its `onCacheUpdate` listener).
7
14
  *
8
15
  * Segment IDs: "sub-hourly", "sub-weekly"
9
16
  */
@@ -36,7 +43,8 @@ function emitWindow(pi, segmentId, window, barSegments) {
36
43
  color: getColor(pct),
37
44
  });
38
45
  }
39
- function emitUsage(pi, usage) {
46
+ function emitUsage(pi, state) {
47
+ const usage = state?.usage;
40
48
  if (!usage || usage.windows.length === 0) {
41
49
  pi.events.emit("powerbar:update", { id: "sub-hourly", text: undefined });
42
50
  pi.events.emit("powerbar:update", { id: "sub-weekly", text: undefined });
@@ -49,21 +57,10 @@ export default function createExtension(pi) {
49
57
  pi.events.emit("powerbar:register-segment", { id: "sub-hourly", label: "Sub Hourly" });
50
58
  pi.events.emit("powerbar:register-segment", { id: "sub-weekly", label: "Sub Weekly" });
51
59
  pi.events.on("sub-core:ready", (payload) => {
52
- const data = payload;
53
- emitUsage(pi, data.state?.usage);
60
+ emitUsage(pi, payload.state);
54
61
  });
55
62
  pi.events.on("sub-core:update-current", (payload) => {
56
- const data = payload;
57
- emitUsage(pi, data.state?.usage);
58
- });
59
- pi.events.on("sub-core:update-all", (payload) => {
60
- const data = payload;
61
- // Find the entry matching the current provider, or fall back to first entry
62
- const currentProvider = data.state?.provider;
63
- const entry = currentProvider
64
- ? data.state?.entries?.find((e) => e.provider === currentProvider)
65
- : data.state?.entries?.[0];
66
- emitUsage(pi, entry?.usage);
63
+ emitUsage(pi, payload.state);
67
64
  });
68
65
  }
69
66
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Sub Producer\n *\n * Shows subscription usage from pi-sub-core.\n * Sub-core is loaded by pi as a sibling extension (declared in package.json pi.extensions).\n * This producer just listens to sub-core events and emits powerbar segments.\n *\n * Segment IDs: \"sub-hourly\", \"sub-weekly\"\n */\n\nimport type { RateWindow, SubCoreAllState, SubCoreState, UsageSnapshot } from \"@marckrenn/pi-sub-shared\";\nimport type { ExtensionAPI } from \"@mariozechner/pi-coding-agent\";\n\nfunction getColor(pct: number): string {\n\tif (pct > 80) return \"error\";\n\tif (pct > 60) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction emitWindow(pi: ExtensionAPI, segmentId: string, window: RateWindow | undefined, barSegments: number): void {\n\tif (!window) {\n\t\tpi.events.emit(\"powerbar:update\", { id: segmentId, text: undefined });\n\t\treturn;\n\t}\n\n\tconst pct = Math.round(window.usedPercent);\n\tconst label = window.label || \"\";\n\tconst reset = window.resetDescription || \"\";\n\n\tconst textParts: string[] = [];\n\tif (label) textParts.push(label);\n\tif (reset) textParts.push(reset);\n\n\tpi.events.emit(\"powerbar:update\", {\n\t\tid: segmentId,\n\t\ttext: textParts.join(\" \"),\n\t\tsuffix: `${pct}%`,\n\t\tbar: pct,\n\t\tbarSegments,\n\t\tcolor: getColor(pct),\n\t});\n}\n\nfunction emitUsage(pi: ExtensionAPI, usage: UsageSnapshot | undefined): void {\n\tif (!usage || usage.windows.length === 0) {\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-hourly\", text: undefined });\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-weekly\", text: undefined });\n\t\treturn;\n\t}\n\n\temitWindow(pi, \"sub-hourly\", usage.windows[0], 5);\n\temitWindow(pi, \"sub-weekly\", usage.windows[1], 7);\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-hourly\", label: \"Sub Hourly\" });\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-weekly\", label: \"Sub Weekly\" });\n\n\tpi.events.on(\"sub-core:ready\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreState };\n\t\temitUsage(pi, data.state?.usage);\n\t});\n\n\tpi.events.on(\"sub-core:update-current\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreState };\n\t\temitUsage(pi, data.state?.usage);\n\t});\n\n\tpi.events.on(\"sub-core:update-all\", (payload: unknown) => {\n\t\tconst data = payload as { state?: SubCoreAllState };\n\t\t// Find the entry matching the current provider, or fall back to first entry\n\t\tconst currentProvider = data.state?.provider;\n\t\tconst entry = currentProvider\n\t\t\t? data.state?.entries?.find((e) => e.provider === currentProvider)\n\t\t\t: data.state?.entries?.[0];\n\t\temitUsage(pi, entry?.usage);\n\t});\n}\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/powerbar-sub/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Powerbar Sub Producer\n *\n * Shows subscription usage from pi-sub-core.\n * Sub-core is loaded by pi as a sibling extension (declared in package.json pi.extensions).\n *\n * We listen only to `sub-core:ready` and `sub-core:update-current`. We intentionally\n * skip `sub-core:update-all`: its entries list filters providers by cache TTL, so the\n * current provider can be missing whenever its `fetchedAt` drifts past the refresh\n * interval (e.g. during anthropic 429s, see https://github.com/marckrenn/pi-sub/issues/58).\n * `update-current` is authoritative for the current provider's usage in every case,\n * including when other pi instances refresh the shared cache (sub-core re-emits\n * `update-current` from its `onCacheUpdate` listener).\n *\n * Segment IDs: \"sub-hourly\", \"sub-weekly\"\n */\n\nimport type { RateWindow, SubCoreState } from \"@marckrenn/pi-sub-shared\";\nimport type { ExtensionAPI } from \"@mariozechner/pi-coding-agent\";\n\nfunction getColor(pct: number): string {\n\tif (pct > 80) return \"error\";\n\tif (pct > 60) return \"warning\";\n\treturn \"muted\";\n}\n\nfunction emitWindow(pi: ExtensionAPI, segmentId: string, window: RateWindow | undefined, barSegments: number): void {\n\tif (!window) {\n\t\tpi.events.emit(\"powerbar:update\", { id: segmentId, text: undefined });\n\t\treturn;\n\t}\n\n\tconst pct = Math.round(window.usedPercent);\n\tconst label = window.label || \"\";\n\tconst reset = window.resetDescription || \"\";\n\n\tconst textParts: string[] = [];\n\tif (label) textParts.push(label);\n\tif (reset) textParts.push(reset);\n\n\tpi.events.emit(\"powerbar:update\", {\n\t\tid: segmentId,\n\t\ttext: textParts.join(\" \"),\n\t\tsuffix: `${pct}%`,\n\t\tbar: pct,\n\t\tbarSegments,\n\t\tcolor: getColor(pct),\n\t});\n}\n\nfunction emitUsage(pi: ExtensionAPI, state: SubCoreState | undefined): void {\n\tconst usage = state?.usage;\n\tif (!usage || usage.windows.length === 0) {\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-hourly\", text: undefined });\n\t\tpi.events.emit(\"powerbar:update\", { id: \"sub-weekly\", text: undefined });\n\t\treturn;\n\t}\n\n\temitWindow(pi, \"sub-hourly\", usage.windows[0], 5);\n\temitWindow(pi, \"sub-weekly\", usage.windows[1], 7);\n}\n\nexport default function createExtension(pi: ExtensionAPI): void {\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-hourly\", label: \"Sub Hourly\" });\n\tpi.events.emit(\"powerbar:register-segment\", { id: \"sub-weekly\", label: \"Sub Weekly\" });\n\n\tpi.events.on(\"sub-core:ready\", (payload: unknown) => {\n\t\temitUsage(pi, (payload as { state?: SubCoreState }).state);\n\t});\n\n\tpi.events.on(\"sub-core:update-current\", (payload: unknown) => {\n\t\temitUsage(pi, (payload as { state?: SubCoreState }).state);\n\t});\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juanibiapina/pi-powerbar",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Pi extension that renders a persistent powerline status bar with left/right segments updated via events",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,6 +9,7 @@
9
9
  "clean": "rm -rf dist",
10
10
  "build": "tsc -p tsconfig.build.json",
11
11
  "dev": "tsc -p tsconfig.build.json --watch",
12
+ "test": "npm run build && node --test test/*.test.js",
12
13
  "check": "biome check --write --error-on-warnings . && tsc --noEmit",
13
14
  "prepublishOnly": "npm run clean && npm run build && npm run check"
14
15
  },
@@ -44,13 +45,13 @@
44
45
  },
45
46
  "dependencies": {
46
47
  "@juanibiapina/pi-extension-settings": "^0.6.1",
47
- "@marckrenn/pi-sub-core": "^1.3.0"
48
+ "@marckrenn/pi-sub-core": "^1.5.0"
48
49
  },
49
50
  "devDependencies": {
50
- "@biomejs/biome": "2.4.12",
51
- "@marckrenn/pi-sub-shared": "^1.3.0",
52
- "@mariozechner/pi-coding-agent": "^0.67.3",
53
- "@types/node": "^25.2.3",
54
- "typescript": "^6.0.2"
51
+ "@biomejs/biome": "2.4.13",
52
+ "@marckrenn/pi-sub-shared": "^1.5.0",
53
+ "@mariozechner/pi-coding-agent": "^0.70.2",
54
+ "@types/node": "^25.6.0",
55
+ "typescript": "^6.0.3"
55
56
  }
56
57
  }