@timurproko/a1 0.1.8-dev.0aceebe → 0.1.8-dev.112

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.
Files changed (27) hide show
  1. package/README.md +41 -31
  2. package/dist/native/darwin-arm64/manifest.json +1 -1
  3. package/dist/native/linux-x64/manifest.json +1 -1
  4. package/dist/native/win32-x64/manifest.json +2 -2
  5. package/dist/native/win32-x64/process-guardian.exe +0 -0
  6. package/dist/src/cli/dispatch.js +67 -30
  7. package/dist/src/cli/packages.d.ts +9 -1
  8. package/dist/src/cli/packages.js +24 -20
  9. package/dist/src/cli/version-stats.js +4 -4
  10. package/dist/src/foundation/pi-component-adapter/index.d.ts +1 -1
  11. package/dist/src/foundation/pi-component-adapter/index.js +1 -1
  12. package/dist/src/foundation/pi-component-adapter/shell-presenters-transcript.js +2 -1
  13. package/dist/src/foundation/pi-engine-adapter/adapter.d.ts +7 -0
  14. package/dist/src/foundation/pi-engine-adapter/adapter.js +193 -39
  15. package/dist/src/foundation/pi-owned-ui-integration/session-shell.d.ts +6 -0
  16. package/dist/src/foundation/pi-owned-ui-integration/session-shell.js +223 -13
  17. package/dist/src/foundation/release/index.d.ts +0 -1
  18. package/dist/src/foundation/release/index.js +0 -1
  19. package/dist/src/foundation/release/update.d.ts +1 -1
  20. package/dist/src/foundation/release/update.js +13 -15
  21. package/docs/architecture/toolchain.md +1 -1
  22. package/docs/ci-release-runbook.md +76 -79
  23. package/docs/features/launch-profiles.md +1 -1
  24. package/docs/manual-transparent-checkpoint.md +1 -1
  25. package/package.json +3 -1
  26. package/dist/src/foundation/release/development-preview-release.d.ts +0 -49
  27. package/dist/src/foundation/release/development-preview-release.js +0 -114
package/README.md CHANGED
@@ -9,13 +9,15 @@ npm install --global @timurproko/a1@latest
9
9
  ## Commands
10
10
 
11
11
  ```sh
12
- a1 # A1-owned UI and profile: ~/.a1/agent
13
- a1 version # show Installed, Release (latest), and Next versions
14
- a1 update # update to npm latest
15
- a1 update:next # update to npm next (or a1 update:<commit> for a specific preview)
12
+ a1 # A1-owned UI and profile: ~/.a1/agent
13
+ a1 version # show Installed, Release, and Develop versions
14
+ a1 update # update to the current release
15
+ a1 update:develop # update to the current development preview
16
+ a1 update:107 # install numbered development preview 107
17
+ a1 update --models # refresh A1's model catalogs
16
18
  ```
17
19
 
18
- Prerelease builds — what `a1 update:next` installs — add two development profiles
20
+ Prerelease builds — what `a1 update:develop` installs — add two development profiles
19
21
  for comparing against pinned Pi and for experimenting against an isolated profile.
20
22
  A release build does not carry them.
21
23
 
@@ -30,16 +32,19 @@ Pi extension packages install into A1's own profile, so bare `a1` loads them and
30
32
  `a1 pi` and `a1 sandbox` do not. Sources are Pi's: `npm:`, git, or a local path.
31
33
 
32
34
  ```sh
33
- a1 install npm:pi-mcp-adapter # install a package into ~/.a1/agent
34
- a1 remove npm:pi-mcp-adapter # remove it again (alias: a1 uninstall)
35
- a1 list # list packages installed for a1
36
- a1 update --extensions # update every installed package
37
- a1 update npm:pi-mcp-adapter # update one of them
38
- a1 update --models # refresh model catalogs
35
+ a1 pi install npm:pi-mcp-adapter # install a package into ~/.a1/agent
36
+ a1 pi remove npm:pi-mcp-adapter # remove it again (alias: a1 pi uninstall)
37
+ a1 pi list # list packages installed for a1
38
+ a1 pi update --extensions # update every installed package
39
+ a1 pi update npm:pi-mcp-adapter # update one of them
39
40
  ```
40
41
 
41
42
  A running session loads a newly installed package after a restart. Pi's own
42
- profile at `~/.pi/agent` is managed by Pi itself.
43
+ profile at `~/.pi/agent` is managed by Pi itself. Extension configuration is
44
+ isolated too: for example, MCP configuration for bare `a1` belongs under
45
+ `~/.a1/agent` (or the project), so `~/.pi/agent/mcp.json` is not read. Run
46
+ `/mcp setup` inside bare `a1` to configure it; the MCP footer status appears
47
+ when that A1 configuration contains a server.
43
48
 
44
49
  ## Develop
45
50
 
@@ -59,24 +64,30 @@ npm run test:full # complete non-physical suite
59
64
  Two channels, both published by CI from the exact bytes it validated. Nothing is
60
65
  ever published from a workstation.
61
66
 
62
- ### Previews — npm `next`
67
+ ### Development previews
63
68
 
64
- Automatic. Every push to `develop` publishes one, versioned
65
- `<major.minor.patch>-dev.<short commit>` the base from whatever `package.json`
66
- declares, the suffix naming the commit it was built from. It is stamped at publish
67
- time and never committed, so previews cost no commits and need no command, and an
68
- installed preview says exactly which source produced it.
69
+ Ordinary pushes to `develop` do not publish. The nightly run at `03:17 UTC` always
70
+ performs complete verification of current `origin/develop`; it publishes only when
71
+ that source's preview is absent. A maintainer can request the same authoritative
72
+ GitHub Actions path and wait for its result:
69
73
 
70
74
  ```sh
71
- a1 update:next # install the newest preview
72
- a1 update:7eabe9e # install the preview built from that commit
75
+ npm run develop
73
76
  ```
74
77
 
75
- Naming a commit is what the version suffix is for: read it off `a1 version`, a
76
- pull request, or a commit list, and install exactly that build you never need to
77
- know which version it went out under. A commit that was never published is refused
78
- rather than guessed at. A full preview version works in the same place, so a string
79
- pasted back from `a1 version` is understood too.
78
+ A preview is `<major.minor.patch>-dev.<pull-request number>`, stamped only into the
79
+ package. For example, the source GitHub presents as `develop (#107)` produces
80
+ `0.1.8-dev.107`. Repeating a request for immutable version 107 succeeds without
81
+ building or publishing again.
82
+
83
+ ```sh
84
+ a1 update:develop # install the current development preview
85
+ a1 update:107 # install preview 107
86
+ a1 update:0.1.8-dev.107 # install that exact full preview version
87
+ ```
88
+
89
+ The published version list is authoritative: an unpublished number is refused
90
+ rather than guessed. npm's `next` dist-tag remains an internal registry detail.
80
91
 
81
92
  ### Stable — npm `latest`
82
93
 
@@ -89,12 +100,11 @@ npm run release -- major # 0.1.1 -> 1.0.0
89
100
  npm run release -- 0.4.0 # an exact version
90
101
  ```
91
102
 
92
- It lands the version on `develop` through a pull request that merges itself.
93
- **Landing it is what publishes** the workflow builds, validates the packed release
94
- on Windows, Linux, and macOS, publishes to npm `latest` with provenance, and only
95
- then writes the `v<version>` tag and records the GitHub Release. The command waits
96
- for that to succeed, then opens the next `-dev` line so previews resume
97
- immediately.
103
+ It lands the version on `develop` through a pull request that merges itself, then
104
+ explicitly dispatches publication for that exact commit. GitHub Actions builds,
105
+ validates the packed release on Windows, Linux, and macOS, publishes to npm
106
+ `latest` with provenance, and only then writes the `v<version>` tag and records the
107
+ GitHub Release. The command waits for success before opening the next `-dev` line.
98
108
 
99
109
  ```sh
100
110
  a1 update # install the newest stable release
@@ -5,7 +5,7 @@
5
5
  "platform": "darwin",
6
6
  "architecture": "arm64",
7
7
  "capability": "unsupported",
8
- "builtAt": "2026-08-24T07:04:20.048Z",
8
+ "builtAt": "2026-08-25T09:42:56.359Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "7524bf568992517f50ed53196c861c5edeba0d7275633bb4735ab45bd5963a28",
@@ -5,7 +5,7 @@
5
5
  "platform": "linux",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-24T07:04:23.438Z",
8
+ "builtAt": "2026-08-25T09:43:23.793Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian",
11
11
  "sha256": "29e22fe29de2828982bc67ef418c4adcaab1490281477b7bea592ec6fe621bcd",
@@ -5,10 +5,10 @@
5
5
  "platform": "win32",
6
6
  "architecture": "x64",
7
7
  "capability": "supported",
8
- "builtAt": "2026-08-24T07:04:56.702Z",
8
+ "builtAt": "2026-08-25T09:44:35.937Z",
9
9
  "artifact": {
10
10
  "filename": "process-guardian.exe",
11
- "sha256": "856c57a950c47109893dd631bf5ee4fbe467b98433d9b8d3cc7ffaf4124f15a9",
11
+ "sha256": "0e2919843f1cdc824ce48203dace68df43f8ebb4b24b190ff7baf6c13d4846b1",
12
12
  "size": 172544
13
13
  },
14
14
  "provenance": {
@@ -5,12 +5,13 @@ export function cliUsage(capabilities) {
5
5
  "",
6
6
  ...(capabilities.developmentProfiles ? ["pi", "sandbox"] : []),
7
7
  "version",
8
- "update [self|<source>|--extensions|--models]",
9
- "update:next",
10
- "update:<commit>",
11
- "install <source>",
12
- "remove <source>",
13
- "list",
8
+ "update [self|--models]",
9
+ "update:develop",
10
+ "update:<number>",
11
+ "pi install <source>",
12
+ "pi remove <source>",
13
+ "pi list",
14
+ "pi update [--extensions|<source>]",
14
15
  ]);
15
16
  }
16
17
  const PROFILE_WORDS = new Set(["pi", "sandbox"]);
@@ -32,7 +33,13 @@ export function parseCliCommand(arguments_, capabilities) {
32
33
  if (arguments_.length === 0)
33
34
  return { kind: "launch", profileId: "a1" };
34
35
  const [command, ...rest] = arguments_;
35
- if (capabilities.developmentProfiles && (command === "pi" || command === "sandbox")) {
36
+ if (command === "pi") {
37
+ if (rest.length > 0)
38
+ return parsePiPackageCommand(rest);
39
+ if (capabilities.developmentProfiles)
40
+ return { kind: "launch", profileId: "pi" };
41
+ }
42
+ if (capabilities.developmentProfiles && command === "sandbox") {
36
43
  return withoutArguments(rest, { kind: "launch", profileId: command });
37
44
  }
38
45
  if (command === "version")
@@ -41,11 +48,9 @@ export function parseCliCommand(arguments_, capabilities) {
41
48
  return parseColonUpdate(command.slice("update:".length), rest);
42
49
  if (command === "update")
43
50
  return parseUpdate(rest);
44
- if (command === "install" || command === "remove" || command === "uninstall") {
45
- return parseSourceCommand(command === "install" ? "install" : "remove", rest);
51
+ if (command === "install" || command === "remove" || command === "uninstall" || command === "list") {
52
+ return packageNamespaceRejection(command, rest.join(" ") || undefined);
46
53
  }
47
- if (command === "list")
48
- return withoutArguments(rest, { kind: "packages", request: { verb: "list", source: null } });
49
54
  if (command === "ui")
50
55
  return { kind: "error", message: `The ui subcommand was removed; run bare ${PRODUCT_TEXT.commandName} for the owned UI.` };
51
56
  if (command === "agent")
@@ -53,28 +58,29 @@ export function parseCliCommand(arguments_, capabilities) {
53
58
  return { kind: "error", message: PRODUCT_TEXT.diagnostic(`received an unknown command: ${command ?? ""}`) };
54
59
  }
55
60
  /**
56
- * What follows the colon says which build to move to. `next` is the newest
57
- * preview; anything else names one outright, by the commit it was built from or by
58
- * its full version — a preview is published as `<version>-dev.<commit>`, so the
59
- * commit alone is enough to find it.
61
+ * What follows the colon says which development build to move to. `develop`
62
+ * selects the channel head; a positive decimal or a full numbered preview names
63
+ * one immutable publication.
60
64
  */
61
65
  function parseColonUpdate(suffix, rest) {
62
66
  if (rest.length > 0)
63
67
  return { kind: "error", message: PRODUCT_TEXT.diagnostic("update takes what to move to after the colon, and nothing else.") };
64
- if (suffix === "next")
68
+ if (suffix === "next") {
69
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`renamed its development channel; run ${PRODUCT_TEXT.commandName} update:develop.`) };
70
+ }
71
+ if (suffix === "develop")
65
72
  return { kind: "update", channel: "next" };
66
73
  if (suffix.length === 0)
67
- return { kind: "error", message: PRODUCT_TEXT.diagnostic(`update: needs a preview after the colon, as in ${PRODUCT_TEXT.commandName} update:next.`) };
68
- if (!/^[0-9a-z][0-9a-z.+-]*$/i.test(suffix)) {
69
- return { kind: "error", message: PRODUCT_TEXT.diagnostic(`received an unusable preview: ${suffix}`) };
74
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`update: needs a preview after the colon, as in ${PRODUCT_TEXT.commandName} update:develop.`) };
75
+ if (!/^[1-9]\d*$/.test(suffix) && !/^\d+\.\d+\.\d+-dev\.[1-9]\d*$/.test(suffix)) {
76
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`received an unusable numbered preview: ${suffix}`) };
70
77
  }
71
78
  return { kind: "update", channel: "next", target: suffix };
72
79
  }
73
80
  /**
74
- * `update` carries both meanings pinned Pi gives it: itself by default, and the
75
- * profile's packages when a target says so. Pi is refused as a target because A1
76
- * certifies each release against one pinned Pi, so moving Pi underneath it would
77
- * invalidate what was certified.
81
+ * Top-level `update` owns A1 itself and A1's model catalogs. Extension package
82
+ * maintenance lives under the `pi` compatibility namespace, while updating the
83
+ * pinned Pi runtime remains impossible.
78
84
  */
79
85
  function parseUpdate(rest) {
80
86
  if (rest.length === 0)
@@ -90,18 +96,42 @@ function parseUpdate(rest) {
90
96
  message: PRODUCT_TEXT.diagnostic(`pins the Pi version it was certified against; run ${PRODUCT_TEXT.commandName} update to move ${PRODUCT_TEXT.displayName} itself.`),
91
97
  };
92
98
  }
93
- // A release channel is spelled with the colon. Taking the bare word as a package
94
- // source would turn a near miss into a confident search for a package nobody has.
95
- if (target === "next" || target === "stable") {
96
- const form = target === "next" ? `${PRODUCT_TEXT.commandName} update:next` : `${PRODUCT_TEXT.commandName} update`;
99
+ if (target === "next" || target === "develop" || target === "stable") {
100
+ const form = target === "stable" ? `${PRODUCT_TEXT.commandName} update` : `${PRODUCT_TEXT.commandName} update:develop`;
97
101
  return { kind: "error", message: PRODUCT_TEXT.diagnostic(`selects a release channel with a colon; run ${form}.`) };
98
102
  }
99
- if (target === "--extensions")
100
- return { kind: "packages", request: { verb: "update", source: null } };
101
103
  if (target === "--models")
102
104
  return { kind: "packages", request: { verb: "refresh-models", source: null } };
105
+ if (target === "--extensions" || (target !== undefined && !target.startsWith("-"))) {
106
+ return packageNamespaceRejection("update", target === "--extensions" ? "--extensions" : target);
107
+ }
108
+ return unknownOption(target ?? "", "update");
109
+ }
110
+ function parsePiPackageCommand(arguments_) {
111
+ const [verb, ...rest] = arguments_;
112
+ if (verb === "install" || verb === "remove" || verb === "uninstall") {
113
+ return parseSourceCommand(verb === "install" ? "install" : "remove", rest);
114
+ }
115
+ if (verb === "list")
116
+ return withoutArguments(rest, { kind: "packages", request: { verb: "list", source: null } });
117
+ if (verb === "update")
118
+ return parsePiPackageUpdate(rest);
119
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`received an unknown pi package command: ${verb ?? ""}`) };
120
+ }
121
+ function parsePiPackageUpdate(rest) {
122
+ if (rest.length === 0) {
123
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`pi update needs --extensions or a package source.`) };
124
+ }
125
+ if (rest.length > 1)
126
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic("pi update accepts one target.") };
127
+ const [target] = rest;
128
+ if (target === "--extensions")
129
+ return { kind: "packages", request: { verb: "update", source: null } };
130
+ if (target === "--models") {
131
+ return { kind: "error", message: PRODUCT_TEXT.diagnostic(`refreshes its model catalogs at the top level; run ${PRODUCT_TEXT.commandName} update --models.`) };
132
+ }
103
133
  if (target === undefined || target.startsWith("-"))
104
- return unknownOption(target ?? "", "update");
134
+ return unknownOption(target ?? "", "pi update");
105
135
  if (PROFILE_WORDS.has(target))
106
136
  return profileRejection("update");
107
137
  return { kind: "packages", request: { verb: "update", source: target } };
@@ -129,6 +159,13 @@ function withoutArguments(rest, command) {
129
159
  return profileRejection("list");
130
160
  return { kind: "error", message: PRODUCT_TEXT.diagnostic("commands do not accept additional arguments.") };
131
161
  }
162
+ function packageNamespaceRejection(verb, target) {
163
+ const suffix = target === undefined ? "" : ` ${target}`;
164
+ return {
165
+ kind: "error",
166
+ message: PRODUCT_TEXT.diagnostic(`manages extension packages under its pi namespace; run ${PRODUCT_TEXT.commandName} pi ${verb}${suffix}.`),
167
+ };
168
+ }
132
169
  function profileRejection(verb) {
133
170
  return {
134
171
  kind: "error",
@@ -11,6 +11,12 @@ export interface PackageCommandRequest {
11
11
  readonly verb: PackageCommandVerb;
12
12
  readonly source: string | null;
13
13
  }
14
+ export interface PackageCommandStyle {
15
+ readonly dim: (message: string) => string;
16
+ readonly bold: (message: string) => string;
17
+ readonly green: (message: string) => string;
18
+ readonly red: (message: string) => string;
19
+ }
14
20
  export interface PackageCommandEnvironment {
15
21
  readonly createPort: (input: AgentPackagesPortInput) => AgentPackagesPort;
16
22
  readonly cwd?: string;
@@ -18,6 +24,8 @@ export interface PackageCommandEnvironment {
18
24
  readonly stdout?: (message: string) => void;
19
25
  readonly stderr?: (message: string) => void;
20
26
  readonly initializeProfile?: typeof initializeProductProfile;
27
+ /** Defaults to Chalk's terminal-aware styles; injectable for transcript tests. */
28
+ readonly style?: PackageCommandStyle;
21
29
  }
22
30
  export declare function runPackageCommand(request: PackageCommandRequest, environment: PackageCommandEnvironment): Promise<number>;
23
- export declare function renderPackageOutcome(outcome: AgentPackageOutcome, profileRoot: string): string;
31
+ export declare function renderPackageOutcome(outcome: AgentPackageOutcome, profileRoot: string, style?: PackageCommandStyle): string;
@@ -1,3 +1,4 @@
1
+ import chalk from "chalk";
1
2
  import { configurationRootForProfile, initializeProductProfile, resolveLaunchProfilePaths } from "../features/launch/index.js";
2
3
  import { PRODUCT_TEXT } from "../product-identity.js";
3
4
  export async function runPackageCommand(request, environment) {
@@ -5,6 +6,7 @@ export async function runPackageCommand(request, environment) {
5
6
  const stderr = environment.stderr ?? (message => process.stderr.write(message));
6
7
  const cwd = environment.cwd ?? process.cwd();
7
8
  const processEnvironment = environment.environment ?? process.env;
9
+ const style = environment.style ?? chalk;
8
10
  const paths = resolveLaunchProfilePaths({ environment: processEnvironment });
9
11
  const profileRoot = configurationRootForProfile("a1", paths);
10
12
  if (profileRoot === null)
@@ -19,10 +21,10 @@ export async function runPackageCommand(request, environment) {
19
21
  const port = environment.createPort({
20
22
  profileRoot,
21
23
  cwd,
22
- onProgress: progress => stdout(`${progress.message}\n`),
24
+ onProgress: progress => stdout(`${style.dim(progress.message)}\n`),
23
25
  });
24
26
  const outcome = await runVerb(port, request);
25
- const rendered = renderPackageOutcome(outcome, profileRoot);
27
+ const rendered = renderPackageOutcome(outcome, profileRoot, style);
26
28
  (outcome.status === "completed" ? stdout : stderr)(rendered);
27
29
  return outcome.status === "completed" ? 0 : 1;
28
30
  }
@@ -37,38 +39,40 @@ async function runVerb(port, request) {
37
39
  throw new Error(PRODUCT_TEXT.diagnostic(`requires a source for ${request.verb}`));
38
40
  return request.verb === "install" ? await port.install(request.source) : await port.remove(request.source);
39
41
  }
40
- export function renderPackageOutcome(outcome, profileRoot) {
41
- const name = PRODUCT_TEXT.displayName;
42
+ export function renderPackageOutcome(outcome, profileRoot, style = chalk) {
43
+ // Model refresh remains an A1 top-level command. The `a1 pi` compatibility
44
+ // transcript applies to package operations only.
45
+ if (outcome.operation === "refresh-models") {
46
+ if (outcome.status === "failed") {
47
+ return `${PRODUCT_TEXT.diagnostic(`could not ${describeOperation(outcome.operation)}: ${outcome.detail ?? "unknown failure"}`)}\n`;
48
+ }
49
+ return `${PRODUCT_TEXT.displayName} refreshed the model catalogs in ${profileRoot}.\n`;
50
+ }
42
51
  if (outcome.status === "failed") {
43
- return `${PRODUCT_TEXT.diagnostic(`could not ${describeOperation(outcome.operation)}: ${outcome.detail ?? "unknown failure"}`)}\n`;
52
+ return `${style.red(`Error: ${outcome.detail ?? "Unknown package command error"}`)}\n`;
44
53
  }
45
54
  if (outcome.status === "not-found") {
46
- return `${PRODUCT_TEXT.diagnostic(`found no package matching ${outcome.source ?? "that source"} in ${profileRoot}.`)}\n`;
55
+ return `${style.red(`No matching package found for ${outcome.source ?? "that source"}`)}\n`;
47
56
  }
48
57
  switch (outcome.operation) {
49
58
  case "install":
50
- return `${name} installed ${outcome.source} into ${profileRoot}.\n`
51
- + `Restart ${PRODUCT_TEXT.commandName} for a running session to load it.\n`;
59
+ return `${style.green(`Installed ${outcome.source}`)}\n`;
52
60
  case "remove":
53
- return `${name} removed ${outcome.source} from ${profileRoot}.\n`;
61
+ return `${style.green(`Removed ${outcome.source}`)}\n`;
54
62
  case "update":
55
- return outcome.source === null
56
- ? `${name} updated the packages in ${profileRoot}.\n`
57
- : `${name} updated ${outcome.source}.\n`;
58
- case "refresh-models":
59
- return `${name} refreshed the model catalogs in ${profileRoot}.\n`;
63
+ return `${style.green(outcome.source === null ? "Updated packages" : `Updated ${outcome.source}`)}\n`;
60
64
  case "list":
61
- return renderPackageList(outcome, profileRoot);
65
+ return renderPackageList(outcome, style);
62
66
  }
63
67
  }
64
- function renderPackageList(outcome, profileRoot) {
68
+ function renderPackageList(outcome, style) {
65
69
  if (outcome.packages.length === 0)
66
- return `${PRODUCT_TEXT.displayName} has no packages installed in ${profileRoot}.\n`;
67
- const lines = [`Packages installed for ${PRODUCT_TEXT.commandName} in ${profileRoot}:`];
70
+ return `${style.dim("No packages installed.")}\n`;
71
+ const lines = [style.bold("User packages:")];
68
72
  for (const entry of outcome.packages) {
69
- lines.push(` ${entry.source}${entry.filtered ? " (partly enabled)" : ""}`);
73
+ lines.push(` ${entry.source}${entry.filtered ? " (filtered)" : ""}`);
70
74
  if (entry.installedPath !== null)
71
- lines.push(` ${entry.installedPath}`);
75
+ lines.push(style.dim(` ${entry.installedPath}`));
72
76
  }
73
77
  return `${lines.join("\n")}\n`;
74
78
  }
@@ -21,7 +21,7 @@ export async function runVersionStats(options) {
21
21
  return 1;
22
22
  }
23
23
  const remote = await queryDistTags(runner);
24
- output.stdout(`Installed: ${installed}\nRelease: ${remote.release ?? "unavailable"}\nNext: ${remote.next ?? "unavailable"}\n`);
24
+ output.stdout(`Installed: ${installed}\nRelease: ${remote.release ?? "unavailable"}\nDevelop: ${remote.develop ?? "unavailable"}\n`);
25
25
  if (remote.error)
26
26
  output.stderr(`${PRODUCT_TEXT.diagnostic(`could not resolve npm dist-tags: ${remote.error}`)}\n`);
27
27
  return 0;
@@ -42,15 +42,15 @@ async function queryDistTags(runner) {
42
42
  throw new TypeError("npm returned a non-object dist-tags value");
43
43
  const tags = metadata;
44
44
  const release = parseVersion(tags.latest, "npm latest");
45
- const next = tags.next === undefined ? null : parseVersion(tags.next, "npm next");
46
- return { release, next, error: null };
45
+ const develop = tags.next === undefined ? null : parseVersion(tags.next, "npm development channel");
46
+ return { release, develop, error: null };
47
47
  }
48
48
  catch (error) {
49
49
  return unavailable(message(error));
50
50
  }
51
51
  }
52
52
  function unavailable(error) {
53
- return { release: null, next: null, error };
53
+ return { release: null, develop: null, error };
54
54
  }
55
55
  function createVersionProcessRunner() {
56
56
  return async (command, arguments_) => await new Promise((resolvePromise, rejectPromise) => {
@@ -3,7 +3,7 @@ export * from "./conformance.js";
3
3
  export * from "./shell-components.js";
4
4
  export * from "./theme.js";
5
5
  export * from "./upstream/theme/theme-controller.js";
6
- export { applyConfiguredPiTheme, getAvailablePiThemes } from "./upstream/theme/theme.js";
6
+ export { applyConfiguredPiTheme, getAvailablePiThemes, onPiThemeChange } from "./upstream/theme/theme.js";
7
7
  export * from "./upstream/components/countdown-timer.js";
8
8
  export * from "./upstream/components/extension-editor.js";
9
9
  export * from "./upstream/components/session-selector.js";
@@ -3,7 +3,7 @@ export * from "./conformance.js";
3
3
  export * from "./shell-components.js";
4
4
  export * from "./theme.js";
5
5
  export * from "./upstream/theme/theme-controller.js";
6
- export { applyConfiguredPiTheme, getAvailablePiThemes } from "./upstream/theme/theme.js";
6
+ export { applyConfiguredPiTheme, getAvailablePiThemes, onPiThemeChange } from "./upstream/theme/theme.js";
7
7
  export * from "./upstream/components/countdown-timer.js";
8
8
  export * from "./upstream/components/extension-editor.js";
9
9
  export * from "./upstream/components/session-selector.js";
@@ -1,6 +1,7 @@
1
1
  import { AssistantMessageComponent, BashExecutionComponent, CompactionSummaryMessageComponent, CustomMessageComponent, DynamicBorder, getMarkdownTheme, parseSkillBlock, ToolExecutionComponent, UserMessageComponent, } from "@earendil-works/pi-coding-agent";
2
2
  import { SkillInvocationMessageComponent, } from "./upstream/components/skill-invocation-message.js";
3
3
  import { Container, Markdown, Spacer, Text, } from "#pi-tui";
4
+ import { PRODUCT_TEXT } from "../../product-identity.js";
4
5
  import { KeybindingsManager, } from "./upstream/adjacent/core/keybindings.js";
5
6
  import { PINNED_PI_LAYOUT, piTheme, } from "./theme.js";
6
7
  import { componentPort, createTuiFacade, ensureTheme, formatSessionTokens, isRecord, } from "./shell-shared-facade.js";
@@ -168,7 +169,7 @@ export function renderPiShellPackageUpdateNotice(packages, width) {
168
169
  container.addChild(new Spacer(1));
169
170
  container.addChild(new DynamicBorder(text => theme.fg("warning", text)));
170
171
  container.addChild(new Text(`${theme.bold(theme.fg("warning", "Package Updates Available"))}\n`
171
- + `${theme.fg("muted", "Package updates are available. Run ")}${theme.fg("accent", "pi update --extensions")}\n`
172
+ + `${theme.fg("muted", "Package updates are available. Run ")}${theme.fg("accent", `${PRODUCT_TEXT.commandName} pi update --extensions`)}\n`
172
173
  + `${theme.fg("muted", "Packages:")}\n`
173
174
  + packages.map(name => `- ${name}`).join("\n"), 1, 0));
174
175
  container.addChild(new DynamicBorder(text => theme.fg("warning", text)));
@@ -60,11 +60,18 @@ export interface OwnedPiResourceSummary {
60
60
  readonly sourcePath: string | null;
61
61
  readonly diagnostic: string | null;
62
62
  }
63
+ export interface OwnedPiExtensionSourceSummary {
64
+ readonly source: string;
65
+ readonly scope: "user" | "project" | "temporary";
66
+ readonly origin: "package" | "top-level";
67
+ readonly baseDir: string | null;
68
+ }
63
69
  export interface OwnedPiExtensionResourceSummary {
64
70
  readonly kind: "extension";
65
71
  readonly id: string;
66
72
  readonly sourcePath: string | null;
67
73
  readonly resolvedPath: string | null;
74
+ readonly sourceInfo: OwnedPiExtensionSourceSummary | null;
68
75
  readonly loaded: boolean;
69
76
  readonly hidden: boolean;
70
77
  readonly diagnostic: string | null;