@wirecat/cli-core 0.347.0 → 0.348.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/cli/bot/copy.d.ts +1 -1
  3. package/dist/cli/bot/copy.js +1 -1
  4. package/dist/cli/embedding-choice.js +2 -1
  5. package/dist/cli/envelope.d.ts +4 -0
  6. package/dist/cli/envelope.js +4 -3
  7. package/dist/cli/guide.js +4 -0
  8. package/dist/cli/help/first-screen.js +13 -1
  9. package/dist/cli/help/sections.d.ts +4 -3
  10. package/dist/cli/help/sections.js +5 -10
  11. package/dist/cli/index.d.ts +1 -0
  12. package/dist/cli/index.js +1 -0
  13. package/dist/cli/messenger/backfill-command.js +6 -6
  14. package/dist/cli/messenger/context.d.ts +3 -3
  15. package/dist/cli/messenger/context.js +44 -17
  16. package/dist/cli/messenger/doctor-command.js +3 -3
  17. package/dist/cli/messenger/flooded.js +1 -1
  18. package/dist/cli/messenger/mcp-command.js +1 -1
  19. package/dist/cli/messenger/messages-delete-command.js +2 -1
  20. package/dist/cli/messenger/observed.js +21 -7
  21. package/dist/cli/messenger/schedule-command.js +4 -3
  22. package/dist/cli/messenger/serve-relay.js +1 -0
  23. package/dist/cli/messenger/store-maintenance-command.js +3 -3
  24. package/dist/cli/messenger/stored.js +36 -30
  25. package/dist/cli/program.js +10 -9
  26. package/dist/cli/runs/command.js +3 -2
  27. package/dist/cli/steps.d.ts +4 -0
  28. package/dist/cli/steps.js +9 -0
  29. package/dist/cli/store-settings.js +4 -3
  30. package/dist/domain/models.d.ts +11 -1
  31. package/dist/kit/contract.d.ts +2 -2
  32. package/dist/kit/contract.js +25 -0
  33. package/dist/kit/fake.d.ts +1 -0
  34. package/dist/kit/fake.js +15 -16
  35. package/dist/mcp/meeting-preparation.d.ts +1 -0
  36. package/dist/mcp/meeting-project-candidates.d.ts +1 -0
  37. package/dist/mcp/meeting-series-inventory.d.ts +1 -0
  38. package/dist/mcp/tool.js +2 -0
  39. package/dist/mcp/tools/messages-photo.js +2 -1
  40. package/dist/meetings/mcp/passage.d.ts +1 -0
  41. package/dist/meetings/mcp/project-evidence.d.ts +1 -0
  42. package/dist/meetings/mcp/search.d.ts +1 -0
  43. package/dist/meetings/reads.d.ts +1 -0
  44. package/dist/ports/messenger.d.ts +10 -2
  45. package/dist/runs/recording.js +1 -0
  46. package/dist/runs/scope.d.ts +7 -0
  47. package/dist/runs/scope.js +5 -0
  48. package/dist/services/archive.js +2 -1
  49. package/dist/services/chats.js +3 -2
  50. package/dist/services/conversations.js +2 -1
  51. package/dist/services/deps.js +2 -1
  52. package/dist/services/embeddings-maintenance.js +3 -2
  53. package/dist/services/index.d.ts +1 -1
  54. package/dist/services/search-all-meetings.d.ts +3 -4
  55. package/dist/services/search-all.d.ts +8 -1
  56. package/dist/services/search-all.js +1 -1
  57. package/dist/speech/transcribe.js +2 -1
  58. package/dist/store/chats.d.ts +2 -2
  59. package/dist/store/sqlite/chats.d.ts +2 -2
  60. package/dist/store/sqlite/chats.js +7 -7
  61. package/dist/store/types.d.ts +2 -2
  62. package/package.json +3 -3
@@ -4,6 +4,7 @@ import { botCheckService } from "../botcheck/service.js";
4
4
  import { threadIdOf } from "../domain/thread.js";
5
5
  import { capability } from "../ports/messenger.js";
6
6
  import { syncsOneChat } from "../ports/traits.js";
7
+ import { typedCommand } from "../runs/scope.js";
7
8
  import { codeOf, guardedWrite } from "../sends/guarded.js";
8
9
  import { newOperationId } from "../sends/send-id.js";
9
10
  import { CHAT_COLUMNS, queryFilter, } from "../store/store.js";
@@ -147,7 +148,7 @@ export const chatsService = (deps) => ({
147
148
  const counted = counts.length > 0 ? { ...known, memberCounts: counts } : known;
148
149
  return completeness.state === "complete" || !syncsOneChat(deps.messenger)
149
150
  ? counted
150
- : { ...counted, fetch: `${deps.messenger.app.command} messages sync ${chatId}` };
151
+ : { ...counted, fetch: typedCommand(deps.messenger.app.command, `messages sync ${chatId}`) };
151
152
  },
152
153
  sendAs: async (chat) => {
153
154
  if (deps.offline) {
@@ -180,7 +181,7 @@ export const chatsService = (deps) => ({
180
181
  unknown,
181
182
  ...(stored?.completeness ? { completeness: stored.completeness } : {}),
182
183
  ...(stored?.completeness && stored.completeness.state !== "complete" && syncsOneChat(deps.messenger)
183
- ? { fetch: `${deps.messenger.app.command} messages sync ${id}` }
184
+ ? { fetch: typedCommand(deps.messenger.app.command, `messages sync ${id}`) }
184
185
  : {}),
185
186
  items,
186
187
  };
@@ -5,6 +5,7 @@ import { linkMessages, RULES_VERSION } from "../conversations/link.js";
5
5
  import { formatLocator } from "../domain/locator.js";
6
6
  import { formatReference } from "../domain/references.js";
7
7
  import { conversation } from "../entities/conversation.js";
8
+ import { typedCommand } from "../runs/scope.js";
8
9
  import { CONVERSATION_COLUMNS, queryFilter, } from "../store/store.js";
9
10
  import { accountStore } from "./deps.js";
10
11
  import { chatIdIn } from "./message-resolution.js";
@@ -38,7 +39,7 @@ export const conversationsService = (deps) => {
38
39
  const store = await accountStore(deps);
39
40
  return { store, chatId: await chatIdIn(deps.messenger, chat, store) };
40
41
  };
41
- const notBuilt = (chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${deps.messenger.app.command} conversations build --chat ${chatId}\``);
42
+ const notBuilt = (chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations build --chat ${chatId}`)}\``);
42
43
  return {
43
44
  build: async (chat, { maxMessages, check } = {}) => {
44
45
  check?.();
@@ -1,5 +1,6 @@
1
1
  import { CliError } from "@wirecat/cli-base";
2
2
  import { isCliFailure } from "../errors/failures.js";
3
+ import { typedCommand } from "../runs/scope.js";
3
4
  /** The store with the running account bound: the default for a service's reads and writes. */
4
5
  export const accountStore = async (deps) => (await deps.store()).forAccount(await deps.account());
5
6
  /**
@@ -49,7 +50,7 @@ export const storeIfOpen = async (deps) => {
49
50
  /** Whether `chats`, `messages list|context` and `contacts` answer from the store instead of the messenger. */
50
51
  export const fromStore = (deps) => deps.offline || deps.reads === "store";
51
52
  /** What a store-mode read answers for a chat the store holds nothing of. */
52
- export const nothingStored = (messenger) => `nothing stored for this chat yet — keep \`${messenger.app.command} serve\` running`;
53
+ export const nothingStored = (messenger) => `nothing stored for this chat yet — keep \`${typedCommand(messenger.app.command, "serve")}\` running`;
53
54
  /** Where a store-mode messenger's history comes from, for what it cannot do. */
54
55
  export const PUSHED = "this messenger's history is read from the local store";
55
56
  export const OFFLINE = "--offline answers only from what is kept locally: `chats list|show`, `messages list|show|context` and `contacts list|show`";
@@ -6,6 +6,7 @@ import { DEFAULT_TEXT_MODEL, textModel, vectorModelKey } from "../embeddings/mod
6
6
  import { remoteKey } from "../embeddings/remote.js";
7
7
  import { CONTEXT_MESSAGES, CONTEXT_MINUTES } from "../images/context/index.js";
8
8
  import { registeredImageProviders } from "../images/registry.js";
9
+ import { typedCommand } from "../runs/scope.js";
9
10
  import { conversationsService } from "./conversations.js";
10
11
  import { storedChatId } from "./message-resolution.js";
11
12
  /** How much one run without `--chat` does at most, by default. */
@@ -16,11 +17,11 @@ const PER_SESSION = 8;
16
17
  const PER_REQUEST = 256;
17
18
  /** e5's tokenizer reads ~3.7 characters a token in Russian; 3 keeps the bound above the real count. */
18
19
  const CHARS_PER_TOKEN_AT_LEAST = 3;
19
- const notDownloaded = (deps, id) => new CliError("not_found", `${id} is not downloaded — \`${deps.messenger.app.command} models text download ${id}\``, {
20
+ const notDownloaded = (deps, id) => new CliError("not_found", `${id} is not downloaded — \`${typedCommand(deps.messenger.app.command, `models text download ${id}`)}\``, {
20
21
  reason: "model.not-downloaded",
21
22
  model: id,
22
23
  });
23
- export const notBuiltError = (deps, chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${deps.messenger.app.command} conversations build --chat ${chatId}\``);
24
+ export const notBuiltError = (deps, chatId) => new CliError("not_found", `chat ${chatId} has no conversations yet — \`${typedCommand(deps.messenger.app.command, `conversations build --chat ${chatId}`)}\``);
24
25
  export const embeddingTarget = (deps, choice = DEFAULT_TEXT_MODEL) => {
25
26
  if (typeof choice !== "string") {
26
27
  const { remote, apiKey, concurrency = 4 } = choice;
@@ -156,7 +156,7 @@ export { privatePeopleService } from "./private-people.js";
156
156
  export { approveAsTask, type ProposalTaskStore, proposalSource } from "./proposal-tasks.js";
157
157
  export type { RetentionQuery, RetentionService } from "./retention.js";
158
158
  export { retentionService } from "./retention.js";
159
- export { RESOURCES_SEARCHED, type SearchAllFound, type SearchAllItem, type SearchAllRequest, type SearchedResource, searchAll, } from "./search-all.js";
159
+ export { RESOURCES_SEARCHED, type SearchAllFound, type SearchAllItem, type SearchAllMessenger, type SearchAllRequest, type SearchedResource, searchAll, } from "./search-all.js";
160
160
  export { type MeetingSearchCoverage, type MeetingSearchCursor, type MeetingSearchItem, RESOURCES_SEARCHED_WITH_MEETINGS, type ResourceWithMeetings, type SearchAllIncludingMeetingsFound, type SearchAllIncludingMeetingsRequest, type SearchAllWithMeetingsRequest, searchAllIncludingMeetings, searchAllWithMeetings, } from "./search-all-meetings.js";
161
161
  export { CATCH_UP_BOUNDS, type CatchUpOptions, type CatchUpResult, catchUpSearch, validateCatchUp, } from "./search-catchup.js";
162
162
  export { accountsOfKind, listContextOf, type SearchKind } from "./search-kind.js";
@@ -1,8 +1,7 @@
1
1
  import type { SearchHit } from "../meetings/index.js";
2
- import type { MessengerTraits } from "../ports/traits.js";
3
2
  import type { AccountKey, MessageStore } from "../store/store.js";
4
3
  import type { SearchFound, SearchQuery } from "./messages.js";
5
- import { type SearchAllFound, type SearchAllItem, type SearchAllRequest, type Skipped } from "./search-all.js";
4
+ import { type SearchAllFound, type SearchAllItem, type SearchAllMessenger, type SearchAllRequest, type Skipped } from "./search-all.js";
6
5
  export declare const RESOURCES_SEARCHED_WITH_MEETINGS: readonly ["messages", "mail", "notes", "meetings"];
7
6
  export type ResourceWithMeetings = (typeof RESOURCES_SEARCHED_WITH_MEETINGS)[number];
8
7
  export interface MeetingSearchItem {
@@ -55,7 +54,7 @@ export interface SearchAllIncludingMeetingsFound extends Omit<SearchAllFound, "i
55
54
  meetings?: MeetingSearchCoverage;
56
55
  }
57
56
  /** Opt-in fusion of the existing unified ranking and one explicitly scoped meeting archive. */
58
- export declare const searchAllIncludingMeetings: (store: MessageStore, account: AccountKey, request: SearchAllIncludingMeetingsRequest, messenger?: Partial<Pick<MessengerTraits, "savedChatId" | "app">>, searchMessages?: (query: SearchQuery) => Promise<SearchFound>) => Promise<SearchAllIncludingMeetingsFound>;
57
+ export declare const searchAllIncludingMeetings: (store: MessageStore, account: AccountKey, request: SearchAllIncludingMeetingsRequest, messenger?: SearchAllMessenger, searchMessages?: (query: SearchQuery) => Promise<SearchFound>) => Promise<SearchAllIncludingMeetingsFound>;
59
58
  export interface SearchAllWithMeetingsRequest extends Omit<SearchAllIncludingMeetingsRequest, "meetingAccount"> {
60
59
  /**
61
60
  * `--source`: each kind reads the accounts the list names of it and a kind it names none of is skipped.
@@ -66,4 +65,4 @@ export interface SearchAllWithMeetingsRequest extends Omit<SearchAllIncludingMee
66
65
  /** Meetings have no index to search every account at once, so they are read only when named. */
67
66
  export declare const MEETINGS_UNNAMED = "meetings are searched only when --source names a meeting account, zoom or meetings";
68
67
  /** `search all`: every kind but meetings, unless `--source` names a meeting account. */
69
- export declare const searchAllWithMeetings: (store: MessageStore, account: AccountKey, request: SearchAllWithMeetingsRequest, messenger?: Partial<Pick<MessengerTraits, "savedChatId" | "app">>, searchMessages?: (query: SearchQuery) => Promise<SearchFound>) => Promise<SearchAllIncludingMeetingsFound>;
68
+ export declare const searchAllWithMeetings: (store: MessageStore, account: AccountKey, request: SearchAllWithMeetingsRequest, messenger?: SearchAllMessenger, searchMessages?: (query: SearchQuery) => Promise<SearchFound>) => Promise<SearchAllIncludingMeetingsFound>;
@@ -81,9 +81,16 @@ export interface SearchAllRequest {
81
81
  /** The accounts `--source` chose, per kind; a kind left out reads every account of its own. */
82
82
  scope?: Partial<Record<SearchedResource, AccountKey[]>>;
83
83
  }
84
+ export type SearchAllMessenger = Partial<Pick<MessengerTraits, "savedChatId" | "app">> & {
85
+ /**
86
+ * The tool named in a model-download hint, when it is not `app.command`: memo passes tg's identity for `group:`
87
+ * lists but has no `models` command. `null` names the tools that have one.
88
+ */
89
+ hintCommand?: string | null;
90
+ };
84
91
  /**
85
92
  * `search all`: messages, mail and notes from the local store — messages also from the messenger's
86
93
  * server, as `search messages` asks it — each list best first, merged by reciprocal rank so no
87
94
  * resource's own scores have to be compared with another's.
88
95
  */
89
- export declare const searchAll: (store: MessageStore, account: AccountKey, request: SearchAllRequest, messenger?: Partial<Pick<MessengerTraits, "savedChatId" | "app">>, searchMessages?: (query: SearchQuery) => Promise<SearchFound>) => Promise<SearchAllFound>;
96
+ export declare const searchAll: (store: MessageStore, account: AccountKey, request: SearchAllRequest, messenger?: SearchAllMessenger, searchMessages?: (query: SearchQuery) => Promise<SearchFound>) => Promise<SearchAllFound>;
@@ -21,7 +21,7 @@ export const searchAll = async (store, account, request, messenger = {}, searchM
21
21
  // Each resource is read deep enough for the merged page, then the merge is cut.
22
22
  const window = offset + request.limit;
23
23
  const only = request.only ?? RESOURCES_SEARCHED;
24
- const command = messenger.app?.command;
24
+ const command = messenger.hintCommand === undefined ? messenger.app?.command : (messenger.hintCommand ?? undefined);
25
25
  const lists = [];
26
26
  const searched = [];
27
27
  const accounts = [];
@@ -1,6 +1,7 @@
1
1
  import { CliError, isCliError } from "@wirecat/cli-base";
2
2
  import { fromFile } from "../cli/settings.js";
3
3
  import { capability } from "../ports/messenger.js";
4
+ import { typedCommand } from "../runs/scope.js";
4
5
  import { installedBytes, isInstalled, megabytes, modelPath, modelsDirectory, vadPath } from "./install.js";
5
6
  import { orderedModels, speechModel } from "./models.js";
6
7
  /** `messenger`: its own recognition (Telegram Premium). `local`: a model on this machine. `auto`: the first, then the second. */
@@ -20,7 +21,7 @@ export const choose = (messenger, settings, flags, env = process.env) => {
20
21
  };
21
22
  /** Never a download: a model is hundreds of MB, and whether to fetch one is the owner's call. */
22
23
  export const notDownloaded = (messenger, model) => new CliError("not_found", `the speech model ${model.id} is not on this machine — the owner downloads it once with ` +
23
- `\`${messenger.app.command} models audio download ${model.id}\` (${megabytes(installedBytes(model))})`, { reason: "model.not-downloaded", model: model.id });
24
+ `\`${typedCommand(messenger.app.command, `models audio download ${model.id}`)}\` (${megabytes(installedBytes(model))})`, { reason: "model.not-downloaded", model: model.id });
24
25
  /**
25
26
  * Over an open connection: the messenger's own transcript, or the recording's bytes for a local model.
26
27
  * The model runs after the connection closes — a minute of speech should not hold a socket open.
@@ -1,5 +1,5 @@
1
1
  import type { ChannelTagMatch } from "../domain/channel-tags.js";
2
- import type { Chat, ChatKind, Id, Page } from "../domain/models.js";
2
+ import type { Chat, ChatKind, ChatUpdate, Id, Page } from "../domain/models.js";
3
3
  import { type ChatRekey } from "./sqlite/chat-rekey.js";
4
4
  import type { ChatMetadata } from "./sqlite/chats.js";
5
5
  import type { ChatCompleteness } from "./sqlite/completeness.js";
@@ -7,7 +7,7 @@ import type { StoreContext } from "./sqlite/open.js";
7
7
  import type { Where } from "./sqlite/query-filter.js";
8
8
  import type { AccountKey, ChatActivity, ChatStats, StoredChatFilter } from "./types.js";
9
9
  export interface ChatsStore {
10
- saveChats(key: AccountKey, chats: Chat[]): Promise<void>;
10
+ saveChats(key: AccountKey, chats: ChatUpdate[]): Promise<void>;
11
11
  /** Newest first. `query` matches three letters or more of a title; `unread` keeps chats with unread messages. */
12
12
  chats(key: AccountKey, window: {
13
13
  limit?: number;
@@ -1,5 +1,5 @@
1
1
  import type { ChannelTagMatch } from "../../domain/channel-tags.js";
2
- import type { Chat, Id, Member, Page } from "../../domain/models.js";
2
+ import type { Chat, ChatUpdate, Id, Member, Page } from "../../domain/models.js";
3
3
  import type { AccountKey, StoredChatFilter } from "../types.js";
4
4
  import type { StoreContext } from "./open.js";
5
5
  /** The chat's own id, when the store key is a chat of this account. */
@@ -7,7 +7,7 @@ export declare const chatOf: ({ orm }: StoreContext, accountKey: number, chatKey
7
7
  export declare const findChatPk: ({ orm }: StoreContext, accountKey: number, chatId: Id) => number | undefined;
8
8
  /** A chat known only from its messages, until the chat list names it. */
9
9
  export declare const chatPkFor: (context: StoreContext, accountKey: number, chatId: Id) => number;
10
- export declare const upsertChat: (context: StoreContext, accountKey: number, chat: Chat) => void;
10
+ export declare const upsertChat: (context: StoreContext, accountKey: number, chat: ChatUpdate) => void;
11
11
  /** Replaces who is in the chat, whole. */
12
12
  export declare const writeMembers: (context: StoreContext, key: AccountKey, accountKey: number, chatId: Id, memberIds: Id[]) => void;
13
13
  export declare const members: ({ orm }: StoreContext, chatKey: number) => Member[];
@@ -45,10 +45,10 @@ export const upsertChat = (context, accountKey, chat) => {
45
45
  kind: chat.kind,
46
46
  parentChatId: parent,
47
47
  scope: chat.scope ?? null,
48
- title: chat.title,
49
- unreadCount: chat.unreadCount,
48
+ title: chat.title ?? null,
49
+ unreadCount: chat.unreadCount ?? null,
50
50
  lastMessageAt: toMs(chat.lastMessageAt),
51
- participantsCount: chat.participantsCount,
51
+ participantsCount: chat.participantsCount ?? null,
52
52
  metadata: mergedMetadata(stored?.metadata ?? null, chat.providerMetadata),
53
53
  membershipState: chat.membershipState ?? null,
54
54
  createdAt: now(),
@@ -60,10 +60,10 @@ export const upsertChat = (context, accountKey, chat) => {
60
60
  kind: sql `excluded.kind`,
61
61
  ...(chat.parentChatId === undefined ? {} : { parentChatId: sql `excluded.parent_chat_id` }),
62
62
  scope: sql `coalesce(excluded.scope, ${chats.scope})`,
63
- title: sql `excluded.title`,
64
- unreadCount: sql `excluded.unread_count`,
65
- lastMessageAt: sql `excluded.last_message_at`,
66
- participantsCount: sql `excluded.participants_count`,
63
+ ...(chat.title === undefined ? {} : { title: sql `excluded.title` }),
64
+ ...(chat.unreadCount === undefined ? {} : { unreadCount: sql `excluded.unread_count` }),
65
+ ...(chat.lastMessageAt === undefined ? {} : { lastMessageAt: sql `excluded.last_message_at` }),
66
+ ...(chat.participantsCount === undefined ? {} : { participantsCount: sql `excluded.participants_count` }),
67
67
  metadata: sql `excluded.metadata`,
68
68
  // A chat the list names again has been rejoined; any other state it had is kept.
69
69
  membershipState: sql `CASE WHEN excluded.membership_state IS NOT NULL THEN excluded.membership_state
@@ -1,6 +1,6 @@
1
1
  import type { TextRange } from "../conversations/chunks.js";
2
2
  import type { Link } from "../conversations/link.js";
3
- import type { Chat, ChatKind, Id, MessageHit, Provider } from "../domain/models.js";
3
+ import type { Chat, ChatKind, ChatUpdate, Id, MessageHit, Provider } from "../domain/models.js";
4
4
  import type * as imageChunks from "./sqlite/image-chunks.js";
5
5
  import type { Where } from "./sqlite/query-filter.js";
6
6
  export type PictureInput = imageChunks.PictureInput;
@@ -82,7 +82,7 @@ export type MessageFilter = AccountMessageFilter & {
82
82
  chatId?: undefined;
83
83
  });
84
84
  export interface Delta {
85
- chats?: Chat[];
85
+ chats?: ChatUpdate[];
86
86
  people?: PersonFacts[];
87
87
  members?: Map<Id, Id[]>;
88
88
  state?: Record<string, string>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wirecat/cli-core",
3
- "version": "0.347.0",
3
+ "version": "0.348.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",
@@ -202,7 +202,7 @@
202
202
  "@types/node": "^22.20.4",
203
203
  "@types/pngjs": "6.0.5",
204
204
  "@vitest/coverage-v8": "^5.0.2",
205
- "@wirecat/cli-base": "0.46.0",
205
+ "@wirecat/cli-base": "0.47.0",
206
206
  "commander": "15.0.0",
207
207
  "cspell": "10.3.6",
208
208
  "drizzle-kit": "1.0.0-rc.4",
@@ -222,7 +222,7 @@
222
222
  },
223
223
  "peerDependencies": {
224
224
  "@napi-rs/canvas": ">=0.1.69 <2",
225
- "@wirecat/cli-base": ">=0.45.0 <0.47.0",
225
+ "@wirecat/cli-base": ">=0.47.0 <0.48.0",
226
226
  "commander": ">=15"
227
227
  },
228
228
  "bin": {