@xynogen/pix-core 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/README.md CHANGED
@@ -2,7 +2,11 @@
2
2
 
3
3
  Pi coding agent extension — core UI/UX meta-package.
4
4
 
5
- Installing `pix-core` pulls in all of the packages below as npm dependencies — no source code of its own.
5
+ Installing `pix-core` pulls in all of the packages below as npm dependencies **and activates them**. A single `pi install npm:@xynogen/pix-core` boots every core extension you do not need to install the members individually.
6
+
7
+ ## How it works
8
+
9
+ Pi activates extensions per installed package via each package's `pi.extensions` manifest; it does not walk npm dependencies. So `pix-core` ships a thin aggregator (`src/extension.ts`) that imports each member's extension factory and invokes it against the same host. Each member also carries a `globalThis` idempotency guard, so installing `pix-core` **and** a member standalone activates that member only once.
6
10
 
7
11
  ## What's included
8
12
 
@@ -15,7 +19,7 @@ Installing `pix-core` pulls in all of the packages below as npm dependencies —
15
19
  | `pix-commands` | `/diff` and `/clear` slash commands |
16
20
  | `pix-diagnostics` | Compact LSP diagnostic widget |
17
21
  | `pix-prompts` | System-prompt injection (AGENTS.md + repo directive files) |
18
- | `pix-skills` | Agent skill loader (`read_skills` tool + 21 bundled skills) |
22
+ | `pix-skills` | Agent skill loader (`read_skills` tool + 23 bundled skills) |
19
23
  | `pix-nudge` | Tool + capability nudge hooks |
20
24
 
21
25
  ## Install
@@ -24,7 +28,7 @@ Installing `pix-core` pulls in all of the packages below as npm dependencies —
24
28
  pi install npm:@xynogen/pix-core
25
29
  ```
26
30
 
27
- > Includes the core pix UI/UX packages and installs their dependencies.
31
+ > Installs and activates the core pix UI/UX extensions in one command. Members are deduped if also installed directly.
28
32
 
29
33
  ## Full distro
30
34
 
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@xynogen/pix-core",
3
- "version": "0.3.1",
4
- "description": "Pi extension bundle — installs all core pix-* packages",
3
+ "version": "0.4.0",
4
+ "description": "Pi extension bundle — installs and activates all core pix-* extensions",
5
5
  "type": "module",
6
+ "main": "src/extension.ts",
7
+ "pi": {
8
+ "extensions": [
9
+ "src/extension.ts"
10
+ ]
11
+ },
6
12
  "scripts": {
7
13
  "test": "bun test"
8
14
  },
9
15
  "files": [
16
+ "src",
10
17
  "README.md",
11
18
  "LICENSE"
12
19
  ],
@@ -42,6 +49,7 @@
42
49
  "@xynogen/pix-nudge": "*"
43
50
  },
44
51
  "peerDependencies": {
45
- "@earendil-works/pi-coding-agent": "*"
52
+ "@earendil-works/pi-coding-agent": "*",
53
+ "@earendil-works/pi-tui": "*"
46
54
  }
47
55
  }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * pix-core — aggregator extension.
3
+ *
4
+ * Pi activates extensions per installed package via its `pi.extensions`
5
+ * manifest; it does NOT walk npm dependencies. So a meta-package can only
6
+ * activate its members by importing each one's extension factory and invoking
7
+ * it against the same `pi` host.
8
+ *
9
+ * Every member ships a default-exported `(pi) => void` factory. We resolve them
10
+ * by subpath import (members have no `exports` map, so the full package dir is
11
+ * importable). One `pi install npm:@xynogen/pix-core` then boots all of them.
12
+ */
13
+
14
+ import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
15
+ import registerCommands from "@xynogen/pix-commands/src/extension.ts";
16
+ import registerDiagnostics from "@xynogen/pix-diagnostics/src/extension.ts";
17
+ import registerFooter from "@xynogen/pix-footer/src/extension.ts";
18
+ import registerModels from "@xynogen/pix-models/src/extension.ts";
19
+ import registerNudge from "@xynogen/pix-nudge/src/extension.ts";
20
+ import registerPrompts from "@xynogen/pix-prompts/src/extension.ts";
21
+ import registerSkills from "@xynogen/pix-skills/src/index.ts";
22
+ import registerUpdate from "@xynogen/pix-update/src/extension.ts";
23
+ import registerWelcome from "@xynogen/pix-welcome/src/extension.ts";
24
+
25
+ type Factory = (pi: ExtensionAPI) => void;
26
+
27
+ const MEMBERS: Factory[] = [
28
+ registerWelcome,
29
+ registerFooter,
30
+ registerModels,
31
+ registerUpdate,
32
+ registerCommands,
33
+ registerNudge,
34
+ registerDiagnostics,
35
+ registerPrompts,
36
+ registerSkills,
37
+ ];
38
+
39
+ export default function (pi: ExtensionAPI): void {
40
+ for (const register of MEMBERS) {
41
+ register(pi);
42
+ }
43
+ }
@@ -0,0 +1,62 @@
1
+ import { afterEach, describe, expect, it } from "bun:test";
2
+
3
+ // Mirror of the per-member guard. pix-core does not own once.ts (each member
4
+ // duplicates it to stay cross-dep-free), so we re-declare the contract here and
5
+ // assert the dedupe semantics that the aggregator relies on.
6
+ function once(key: string, fn: () => void): void {
7
+ const g = globalThis as { __pixLoaded?: Set<string> };
8
+ const loaded = (g.__pixLoaded ??= new Set<string>());
9
+ if (loaded.has(key)) return;
10
+ loaded.add(key);
11
+ fn();
12
+ }
13
+
14
+ afterEach(() => {
15
+ delete (globalThis as { __pixLoaded?: Set<string> }).__pixLoaded;
16
+ });
17
+
18
+ describe("once", () => {
19
+ it("runs the factory on first invocation", () => {
20
+ let calls = 0;
21
+ once("pix-footer", () => {
22
+ calls++;
23
+ });
24
+ expect(calls).toBe(1);
25
+ });
26
+
27
+ it("skips repeated invocations of the same key", () => {
28
+ let calls = 0;
29
+ const reg = () => {
30
+ calls++;
31
+ };
32
+ once("pix-footer", reg);
33
+ once("pix-footer", reg);
34
+ once("pix-footer", reg);
35
+ expect(calls).toBe(1);
36
+ });
37
+
38
+ it("isolates distinct keys", () => {
39
+ const seen: string[] = [];
40
+ once("pix-footer", () => seen.push("footer"));
41
+ once("pix-welcome", () => seen.push("welcome"));
42
+ expect(seen).toEqual(["footer", "welcome"]);
43
+ });
44
+
45
+ it("shares the registry across calls via globalThis", () => {
46
+ let calls = 0;
47
+ once("pix-skills", () => {
48
+ calls++;
49
+ });
50
+ // A second loader pass (e.g. standalone install after pix-core) reuses
51
+ // the same globalThis registry and must not re-run.
52
+ once("pix-skills", () => {
53
+ calls++;
54
+ });
55
+ expect(calls).toBe(1);
56
+ expect(
57
+ (globalThis as { __pixLoaded?: Set<string> }).__pixLoaded?.has(
58
+ "pix-skills",
59
+ ),
60
+ ).toBe(true);
61
+ });
62
+ });