@xynogen/pix-welcome 0.1.2 → 0.1.3

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xynogen/pix-welcome",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "description": "Pi extension — welcome banner with startup health checks",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -7,6 +7,7 @@ import {
7
7
  renderCheck,
8
8
  shortCwd,
9
9
  statusIcon,
10
+ summariseSkills,
10
11
  summariseTools,
11
12
  type Theme,
12
13
  } from "./welcome.ts";
@@ -117,6 +118,35 @@ describe("summariseTools", () => {
117
118
  });
118
119
  });
119
120
 
121
+ describe("summariseSkills", () => {
122
+ it("warns when no skills loaded", () => {
123
+ const r = summariseSkills([]);
124
+ expect(r.status).toBe("warn");
125
+ expect(r.detail).toBe("none loaded");
126
+ });
127
+
128
+ it("reports count when skills present", () => {
129
+ const r = summariseSkills([{}, {}, {}]);
130
+ expect(r.status).toBe("ok");
131
+ expect(r.detail).toBe("3 loaded");
132
+ });
133
+
134
+ it("excludes skills with disableModelInvocation", () => {
135
+ const r = summariseSkills([{}, { disableModelInvocation: true }, {}]);
136
+ expect(r.status).toBe("ok");
137
+ expect(r.detail).toBe("2 loaded");
138
+ });
139
+
140
+ it("warns when all skills are disabled", () => {
141
+ const r = summariseSkills([
142
+ { disableModelInvocation: true },
143
+ { disableModelInvocation: true },
144
+ ]);
145
+ expect(r.status).toBe("warn");
146
+ expect(r.detail).toBe("none loaded");
147
+ });
148
+ });
149
+
120
150
  describe("PI_IGNORE_RULES", () => {
121
151
  it("includes both rules", () => {
122
152
  expect(PI_IGNORE_RULES).toEqual([".pi/", ".pi-lens/"]);
package/src/welcome.ts CHANGED
@@ -170,6 +170,17 @@ async function checkPiIgnore(
170
170
  }
171
171
  }
172
172
 
173
+ interface SkillInfo {
174
+ disableModelInvocation?: boolean;
175
+ }
176
+
177
+ export function summariseSkills(skills: SkillInfo[]): CheckResult {
178
+ const count = skills.filter((s) => !s.disableModelInvocation).length;
179
+ return count > 0
180
+ ? { label: "Skills", status: "ok", detail: `${count} loaded` }
181
+ : { label: "Skills", status: "warn", detail: "none loaded" };
182
+ }
183
+
173
184
  interface ToolInfo {
174
185
  sourceInfo?: { source?: string };
175
186
  }
@@ -285,6 +296,7 @@ function buildCheckLines(theme: Theme, checks: CheckResult[]): string[] {
285
296
  export default function (pi: ExtensionAPI) {
286
297
  let dismissed = false;
287
298
  let requestRender: (() => void) | null = null;
299
+ let _updateSkills: ((r: CheckResult) => void) | null = null;
288
300
 
289
301
  const dismiss = (ctx: ExtensionContext) => {
290
302
  if (dismissed) return;
@@ -306,6 +318,7 @@ export default function (pi: ExtensionAPI) {
306
318
  { label: "Auth", status: "pending" },
307
319
  { label: "Models", status: "pending" },
308
320
  { label: "Tools", status: "pending" },
321
+ { label: "Skills", status: "pending" },
309
322
  { label: "Ignore", status: "pending" },
310
323
  ];
311
324
 
@@ -344,8 +357,11 @@ export default function (pi: ExtensionAPI) {
344
357
  requestRender?.();
345
358
  };
346
359
 
360
+ // Expose skills updater so the before_agent_start handler can fill it in.
361
+ _updateSkills = (r: CheckResult) => update(4, r);
362
+
347
363
  void checkPiVersion(pi).then((r) => update(0, r));
348
- void checkPiIgnore(pi, ctx.cwd).then((r) => update(4, r));
364
+ void checkPiIgnore(pi, ctx.cwd).then((r) => update(5, r));
349
365
  // auth already filled synchronously above; no async needed
350
366
 
351
367
  // Tools register during session_start (incl. other extensions); read on
@@ -360,6 +376,23 @@ export default function (pi: ExtensionAPI) {
360
376
  );
361
377
  });
362
378
 
379
+ // Skills count is only available once resources_discover has run, which
380
+ // happens after session_start. Grab it from the first before_agent_start
381
+ // (guaranteed to have skills in systemPromptOptions) and update the banner.
382
+ let skillsChecked = false;
383
+ pi.on("before_agent_start", (event) => {
384
+ if (skillsChecked) return;
385
+ skillsChecked = true;
386
+ const skills = event.systemPromptOptions?.skills ?? [];
387
+ const result = summariseSkills(skills);
388
+ // Banner may already be dismissed by the time the first prompt fires —
389
+ // update anyway so it's accurate if still visible.
390
+ requestRender?.();
391
+ // Reach into CHECKS via the closure captured above per session.
392
+ // We store the updater so each session's banner is independent.
393
+ _updateSkills?.(result);
394
+ });
395
+
363
396
  pi.on("turn_start", (_event, ctx) => {
364
397
  dismiss(ctx);
365
398
  });