@wirecat/cli-base 0.74.0 → 0.75.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.
package/CHANGELOG.md CHANGED
@@ -8,6 +8,35 @@ Versions before 0.8.0 are in the [git tags](https://github.com/WireCatLabs/cli-b
8
8
  Every entry says what changed as a caller sees it, why, and what to watch for — the rules are
9
9
  [`docs/dev/CONVENTIONS.md`](docs/dev/CONVENTIONS.md#the-changelog).
10
10
 
11
+ ## 0.75.1 — 02.11.2026
12
+
13
+ ### Fixed
14
+
15
+ - **A list's help and MCP `query` description no longer offer `word*` or ranges that none of its fields takes.**
16
+ `fieldsHelp`'s `Syntax:` line on a `backend: "sql"` list now lists the phrase, `word*` and `[a TO b] · > >= < <=`
17
+ forms only when one of its shown fields takes them, as a native list's line already did; `AND OR NOT` and `( )`
18
+ stay. A list of whole values only (enum, bool) said it took both, and refused them. `queryDescription` drops
19
+ `[a TO b], > <` the same way. A list whose fields take every form prints the same line as before.
20
+
21
+ ## 0.75.0 — 02.11.2026
22
+
23
+ ### Fixed
24
+
25
+ - **A list whose provider filters it no longer advertises `OR` in its help.** `fieldsHelp`'s `Syntax:` line on a
26
+ `backend: "native"` list now says only what such a list takes: `field:value` terms joined by AND, and the phrase,
27
+ `word*` and range forms only when one of its fields takes them. `OR`, `NOT` and `( )` were listed before, and the
28
+ list refused them. A negatable field keeps its `Leave out:` line. The MCP `query` description already said this
29
+ (0.73.0). A `sql` list's line is unchanged.
30
+
31
+ - **`<cli> upgrade` no longer fails with `MODULE_NOT_FOUND` after the install.** pnpm deletes the folder the old
32
+ version runs from, and a chunk the command imported after the install was gone. The bundle's loader now loads every
33
+ chunk before an upgrade starts, as it does for a server.
34
+
35
+ ### Added
36
+
37
+ - **`--full-history`**, a mode switch: keep a chat's whole history from now on, past a messenger's history limits
38
+ (cli-core's `messages sync`).
39
+
11
40
  ## 0.74.0 — 02.11.2026
12
41
 
13
42
  ### Added
@@ -26,6 +26,11 @@ export declare const LOADER_MARK = "// cli-dev bundle loader\n";
26
26
  * written into the loader as source, so it uses nothing from outside.
27
27
  */
28
28
  export declare const isServer: (words: readonly string[]) => boolean;
29
+ /**
30
+ * `upgrade`: the package manager replaces the files this process runs from, and pnpm deletes the old folder, so a chunk
31
+ * the command imports after the install is gone (MODULE_NOT_FOUND). It loads every chunk before it starts.
32
+ */
33
+ export declare const isUpgrade: (words: readonly string[]) => boolean;
29
34
  /** The bin that replaces the compiled one: `name` is the bundle's entry, `chunks` what a server preloads. */
30
35
  export declare const loaderSource: (name: string, version: string, chunks: readonly string[]) => string;
31
36
  export declare const bundle: (options: BundleOptions) => Promise<BundleResult>;
@@ -177,6 +177,14 @@ export const isServer = (words) => {
177
177
  .slice(0, 4)
178
178
  .some((word, at) => word === "serve" || word === "watch" || (word === "mcp" && plain[at + 1] === undefined));
179
179
  };
180
+ /**
181
+ * `upgrade`: the package manager replaces the files this process runs from, and pnpm deletes the old folder, so a chunk
182
+ * the command imports after the install is gone (MODULE_NOT_FOUND). It loads every chunk before it starts.
183
+ */
184
+ export const isUpgrade = (words) => words
185
+ .filter((word) => !word.startsWith("-"))
186
+ .slice(0, 2)
187
+ .includes("upgrade");
180
188
  /** The bin that replaces the compiled one: `name` is the bundle's entry, `chunks` what a server preloads. */
181
189
  export const loaderSource = (name, version, chunks) => `#!/usr/bin/env node
182
190
  ${LOADER_MARK}import * as module from "node:module"
@@ -202,8 +210,9 @@ if (
202
210
  : next(specifier, context),
203
211
  })
204
212
  }
205
- const running = import(${JSON.stringify(`../bundle/${name}.js`)})
206
213
  const chunks = ${JSON.stringify(chunks)}
214
+ if ((${isUpgrade.toString()})(words)) await Promise.allSettled(chunks.map((chunk) => import(chunk)))
215
+ const running = import(${JSON.stringify(`../bundle/${name}.js`)})
207
216
  // After the server answers its first request, so it starts no later.
208
217
  if (serves) setTimeout(() => void Promise.allSettled(chunks.map((chunk) => import(chunk))), 2000).unref()
209
218
  await running
@@ -1,6 +1,15 @@
1
1
  export const QUERY_SYNTAX = 'field:value · "a phrase" · word* · AND OR NOT · ( ) · [a TO b] · > >= < <=';
2
2
  const shownOf = (registry) => registry.backend === "native" ? registry.fields.filter((field) => field.native) : registry.fields;
3
3
  const leftOut = (fields) => fields.filter((field) => field.negatable).map((field) => field.name);
4
+ const has = (fields, operator) => fields.some((field) => field.operators.includes(operator));
5
+ // Each form is offered only when a shown field takes it, and a provider takes no OR and no grouping.
6
+ const syntaxOf = (shown, backend) => [
7
+ "field:value",
8
+ ...(has(shown, "phrase") ? ['"a phrase"'] : []),
9
+ ...(has(shown, "wildcard") ? ["word*"] : []),
10
+ ...(backend === "native" ? ["terms joined by AND"] : ["AND OR NOT", "( )"]),
11
+ ...(has(shown, "range") ? ["[a TO b] · > >= < <="] : []),
12
+ ].join(" · ");
4
13
  const kindOf = (field) => field.values ? `${field.type}: ${field.values.join("|")}` : `${field.type}: ${field.operators.join(", ")}`;
5
14
  /** The Fields section of a list's help: one line per field it filters by, its defaults, the syntax and the guide. */
6
15
  export const fieldsHelp = (registry, guide) => {
@@ -17,7 +26,7 @@ export const fieldsHelp = (registry, guide) => {
17
26
  ...(negatable.length > 0
18
27
  ? [`Leave out: -${negatable[0]}:value or NOT ${negatable[0]}:value, for ${negatable.join(", ")}`]
19
28
  : []),
20
- `Syntax: ${QUERY_SYNTAX}`,
29
+ `Syntax: ${syntaxOf(shown, registry.backend)}`,
21
30
  ...(guide ? [`Guide: ${guide}`] : []),
22
31
  ].join("\n");
23
32
  };
@@ -34,7 +43,7 @@ export const queryDescription = (registry) => {
34
43
  ? `field:value terms joined by AND that narrow the list${negatable.length > 0
35
44
  ? `; -field:value or NOT field:value leaves out what matches, for ${negatable.join(", ")} only`
36
45
  : ""}`
37
- : "field:value terms that narrow the list (Lucene: AND OR NOT, -field:value, [a TO b], > <)";
46
+ : `field:value terms that narrow the list (Lucene: AND OR NOT, -field:value${has(shown, "range") ? ", [a TO b], > <" : ""})`;
38
47
  const fields = shown.map((field) => (field.values ? `${field.name} (${field.values.join("|")})` : field.name));
39
48
  const defaults = registry.defaults?.length ? `; unless named: ${registry.defaults.join(" ")}` : "";
40
49
  return `${terms}; fields: ${fields.join(", ")}; e.g. ${shown[0]?.example ?? ""}${defaults}`;
@@ -282,6 +282,7 @@ export const HOW_FLAGS = [
282
282
  meaning: "refuse if what it names has changed since it was read",
283
283
  replaces: ["--expected"],
284
284
  },
285
+ { name: "--full-history", role: "mode", type: "switch", meaning: "keep a chat's whole history, past its limits" },
285
286
  {
286
287
  name: "--for-everyone",
287
288
  role: "mode",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-base",
3
- "version": "0.74.0",
3
+ "version": "0.75.1",
4
4
  "description": "The parts every command line tool needs: output modes, terminal rendering, error model, exit codes, credentials, clocks",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Viacheslav Ptsarev",