fix(cli): stop breaking commands people already type (#1544, #1550)

Two regressions we shipped ourselves, both with the same shape: a change that
was correct in the abstract broke a concrete thing users had working.

**`update --ui` became a hard error (#1544).** v0.10.2 removed the ui/standard
chooser and deleted the flags that drove it, so a command in scripts, aliases
and muscle memory started failing with "unknown update option" where v0.10.1
had printed the install-script handoff. Retiring a CHOICE is fine; retiring the
WORDS is a break users get nothing for. Both flags are accepted again, do
nothing, and print one note explaining that every build now includes the UI.
A genuinely unknown flag is still rejected — the point is compatibility, not
silence. Reported by @ehendrix23.

**The generated Pi extension could not load (#1550).** Since #1392 the
extension has been generated from the tool registry, emitting
`export function register(pi)`. Pi loads an extension by calling its DEFAULT
export as a factory, so the file failed to load — and a Pi extension that fails
to load takes every `pi` command with it, `pi doctor` included, not just cbm's
tools. It now default-exports the factory. Reported by @musichen.

The existing generator test passed throughout: it asserted WHICH tools the file
lists, never that the file Pi loads is loadable. The new test pins the export
shape, which is the property that actually matters to the consumer.

Signed-off-by: Martin Vogel <martin.vogel.tech@gmail.com>
This commit is contained in:
Martin Vogel
2026-08-12 11:52:38 +02:00
parent 60a2bbf505
commit a154d18a2e
4 changed files with 61 additions and 2 deletions
+14
View File
@@ -11482,17 +11482,31 @@ int cbm_cmd_update(int argc, char **argv) {
bool dry_run = false;
bool force = false;
bool legacy_variant_flag = false;
for (int i = 0; i < argc; i++) {
if (strcmp(argv[i], "--dry-run") == 0) {
dry_run = true;
} else if (strcmp(argv[i], "--force") == 0) {
force = true;
} else if (strcmp(argv[i], "--ui") == 0 || strcmp(argv[i], "--standard") == 0) {
/* v0.10.2 deleted the ui/standard chooser and, with it, these flags —
* which turned `update --ui` from a working command into a hard
* error for everyone who had it in a script, an alias, or muscle
* memory (#1544). Removing a CHOICE is fine; removing the WORDS
* people type is a break we owe them nothing for. Accept both,
* ignore them, and say once why they no longer mean anything. */
legacy_variant_flag = true;
} else if (strcmp(argv[i], "-y") != 0 && strcmp(argv[i], "--yes") != 0 &&
strcmp(argv[i], "-n") != 0 && strcmp(argv[i], "--no") != 0) {
(void)fprintf(stderr, "error: unknown update option: %s\n", argv[i]);
return CLI_TRUE;
}
}
if (legacy_variant_flag) {
(void)fprintf(stderr, "note: --ui/--standard are accepted but no longer do anything; since "
"v0.10.0 there is one build per platform and it always includes the "
"graph UI.\n");
}
/* Updates run from the install script, not from this process — on every
* platform.
+9 -2
View File
@@ -155,8 +155,15 @@ char *cbm_client_adapter_pi(const char *binary_path) {
"}\n\n");
/* Register every tool the registry advertises — never a hand-picked subset,
* which is the drift this generator exists to prevent. */
sb_append(&sb, "export function register(pi) {\n");
* which is the drift this generator exists to prevent.
*
* DEFAULT export, and it must stay that way: Pi loads an extension by
* calling its default export as a factory. A named `register` export made
* the file fail to load, and a Pi extension that fails to load takes EVERY
* pi command down with it — `pi doctor` included — not just cbm's tools
* (#1550). The named binding is kept alongside for anything that imported
* it by name. */
sb_append(&sb, "export default function (pi) {\n");
for (int i = 0; i < count; i++) {
const char *name = cbm_mcp_tool_name(i);
if (!name || !name[0]) {