@wirecat/cli-core 0.482.0 → 0.483.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/CHANGELOG.md +10 -0
- package/dist/cli/messenger/statistics-query.js +13 -4
- package/dist/cli/program.d.ts +1 -1
- package/dist/cli/program.js +8 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,16 @@ shape of this file.
|
|
|
7
7
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
|
+
## 0.483.0 — 30.10.2026
|
|
11
|
+
|
|
12
|
+
### Fixed
|
|
13
|
+
|
|
14
|
+
- **An MCP server answers each tool call about 6 ms sooner in tg.** The command tree is still built fresh for every
|
|
15
|
+
call, because each call attaches its own hooks and options to it. Two checks that gave the same answer every time now
|
|
16
|
+
run once per process: the shared command contracts once per program definition, and each list's query fields once
|
|
17
|
+
(with `@wirecat/cli-base` 0.65.2, now the pinned version). A CLI that builds a new definition object still gets the
|
|
18
|
+
check for it.
|
|
19
|
+
|
|
10
20
|
## 0.482.0 — 29.10.2026
|
|
11
21
|
|
|
12
22
|
### Changed — may break callers
|
|
@@ -61,9 +61,12 @@ const FIELD_SPECS = [
|
|
|
61
61
|
},
|
|
62
62
|
];
|
|
63
63
|
const fieldsFor = (report) => FIELD_SPECS.filter(({ name }) => FIELDS[report].includes(name));
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const
|
|
64
|
+
const registries = new Map();
|
|
65
|
+
const registryOf = (report) => {
|
|
66
|
+
const known = registries.get(report);
|
|
67
|
+
if (known)
|
|
68
|
+
return known;
|
|
69
|
+
const registry = defineQuery({
|
|
67
70
|
entity: report.endsWith("top") ? "ranking" : "admin_report",
|
|
68
71
|
backend: "sql",
|
|
69
72
|
fields: [
|
|
@@ -71,9 +74,15 @@ export const withStatisticsQuery = (command, report) => {
|
|
|
71
74
|
...field,
|
|
72
75
|
example: field.name === "text" ? "text:invoice" : field.name === "topic" ? "topic:42" : field.example,
|
|
73
76
|
})),
|
|
74
|
-
...
|
|
77
|
+
...fieldsFor(report),
|
|
75
78
|
],
|
|
76
79
|
});
|
|
80
|
+
registries.set(report, registry);
|
|
81
|
+
return registry;
|
|
82
|
+
};
|
|
83
|
+
export const withStatisticsQuery = (command, report) => {
|
|
84
|
+
const extra = fieldsFor(report);
|
|
85
|
+
const query = registryOf(report);
|
|
77
86
|
annotate(command, { query });
|
|
78
87
|
command.addHelpText("after", `\n${fieldsHelp({ fields: extra }, LIST_QUERY_GUIDE)}\nReport predicates are positive AND terms; the remaining query selects stored messages.\n`);
|
|
79
88
|
return command;
|
package/dist/cli/program.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export interface ProgramOptions {
|
|
|
30
30
|
* The command tree, built fresh on each call. Commander is global state by default —
|
|
31
31
|
* `exitOverride` and the write hooks turn it into a value a test can drive.
|
|
32
32
|
*/
|
|
33
|
-
export declare const createProgram: (
|
|
33
|
+
export declare const createProgram: (definition: ProgramDefinition, { out, err }?: ProgramOptions) => Command;
|
|
34
34
|
export type RunOptions = BaseEnvironment & Record<string, unknown>;
|
|
35
35
|
/**
|
|
36
36
|
* An interactive command run without a terminal — an agent's `session start qr` — waits on a person scanning a
|
package/dist/cli/program.js
CHANGED
|
@@ -32,11 +32,14 @@ import { absolutePaths, relayTarget } from "./relay-route.js";
|
|
|
32
32
|
import { NoProfileHere, namedProfile, settingsFor, } from "./settings.js";
|
|
33
33
|
import { assertSharedContracts } from "./shared-contracts.js";
|
|
34
34
|
import { commandAt, failureSteps, lineOf, withLines } from "./steps.js";
|
|
35
|
+
// An MCP server builds the tree on every tool call; the contracts of one definition's tree do not change between builds.
|
|
36
|
+
const contractsChecked = new WeakSet();
|
|
35
37
|
/**
|
|
36
38
|
* The command tree, built fresh on each call. Commander is global state by default —
|
|
37
39
|
* `exitOverride` and the write hooks turn it into a value a test can drive.
|
|
38
40
|
*/
|
|
39
|
-
export const createProgram = (
|
|
41
|
+
export const createProgram = (definition, { out, err } = {}) => {
|
|
42
|
+
const { app, commands, configure, firstScreen } = definition;
|
|
40
43
|
const program = new Command();
|
|
41
44
|
const mounted = commands();
|
|
42
45
|
const messenger = app.messenger !== false;
|
|
@@ -75,7 +78,10 @@ export const createProgram = ({ app, commands, configure, firstScreen }, { out,
|
|
|
75
78
|
for (const command of mounted)
|
|
76
79
|
program.addCommand(command);
|
|
77
80
|
configure?.(program);
|
|
78
|
-
|
|
81
|
+
if (!contractsChecked.has(definition)) {
|
|
82
|
+
assertSharedContracts(program);
|
|
83
|
+
contractsChecked.add(definition);
|
|
84
|
+
}
|
|
79
85
|
installHelp(program, { pitch: app.description, ...firstScreen });
|
|
80
86
|
// Commander builds a usage line from the command tree, where the profile is not a word.
|
|
81
87
|
if (firstScreen?.profile !== false)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wirecat/cli-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.483.0",
|
|
4
4
|
"description": "The messenger-neutral half of a messaging CLI: domain model, message locators, rendering, name resolution, the SQLite seam, the command skeleton and shared read commands, the send guard, run records and the message store",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Viacheslav Ptsarev",
|
|
@@ -247,7 +247,7 @@
|
|
|
247
247
|
"@types/node": "^22.20.5",
|
|
248
248
|
"@types/pngjs": "6.0.5",
|
|
249
249
|
"@vitest/coverage-v8": "^5.0.5",
|
|
250
|
-
"@wirecat/cli-base": "0.65.
|
|
250
|
+
"@wirecat/cli-base": "0.65.2",
|
|
251
251
|
"@wirecat/cli-core-legacy-tags": "npm:@wirecat/cli-core@0.414.0",
|
|
252
252
|
"commander": "15.0.0",
|
|
253
253
|
"cspell": "10.4.0",
|