@pecot/tools-mcp 1.0.1 → 1.1.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 (39) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/cli/dist/cli.d.ts +1 -2
  3. package/cli/dist/cli.js +9 -13
  4. package/cli/dist/diagnostics.d.ts +38 -0
  5. package/cli/dist/diagnostics.js +46 -0
  6. package/cli/dist/domain/arguments.d.ts +2 -15
  7. package/cli/dist/domain/arguments.js +38 -99
  8. package/cli/dist/domain/athena-query.d.ts +2 -8
  9. package/cli/dist/domain/athena-query.js +10 -35
  10. package/cli/dist/domain/s3-input.d.ts +2 -9
  11. package/cli/dist/domain/s3-input.js +8 -21
  12. package/cli/dist/infra/awscli.d.ts +8 -36
  13. package/cli/dist/infra/awscli.js +15 -55
  14. package/cli/dist/infra/sso/login-registry.d.ts +4 -21
  15. package/cli/dist/infra/sso/login-registry.js +13 -56
  16. package/cli/dist/mcp/serve.d.ts +6 -28
  17. package/cli/dist/mcp/serve.js +20 -64
  18. package/cli/dist/mcp/server.d.ts +4 -20
  19. package/cli/dist/mcp/server.js +9 -35
  20. package/cli/dist/mcp/tools/_shared.d.ts +6 -27
  21. package/cli/dist/mcp/tools/_shared.js +36 -65
  22. package/cli/dist/mcp/tools/athena.js +17 -27
  23. package/cli/dist/mcp/tools/cloudwatch.js +13 -15
  24. package/cli/dist/mcp/tools/ec2.js +7 -10
  25. package/cli/dist/mcp/tools/ecr.js +10 -16
  26. package/cli/dist/mcp/tools/index.js +1 -5
  27. package/cli/dist/mcp/tools/lambda.js +10 -10
  28. package/cli/dist/mcp/tools/logs.js +22 -22
  29. package/cli/dist/mcp/tools/rds.js +15 -18
  30. package/cli/dist/mcp/tools/s3.js +19 -29
  31. package/cli/dist/mcp/tools/scheduler.js +10 -13
  32. package/cli/dist/mcp/tools/sqs.js +8 -8
  33. package/cli/dist/mcp/tools/sso.d.ts +1 -5
  34. package/cli/dist/mcp/tools/sso.js +33 -59
  35. package/cli/dist/mcp/tools/sts.js +2 -2
  36. package/cli/dist/node-runtime.d.ts +6 -1
  37. package/cli/dist/node-runtime.js +16 -8
  38. package/npm-shrinkwrap.json +3 -3
  39. package/package.json +2 -2
@@ -3,27 +3,27 @@ import { z } from 'zod';
3
3
  import { SQS_GET_QUEUE_ATTRIBUTES, SQS_LIST_QUEUES } from '../../domain/allowlist.js';
4
4
  import { allowlistedTool, COMMON_INPUT_SHAPE, MAX_ITEMS_PAGINATION_SHAPE, } from './_shared.js';
5
5
  /**
6
- 取得する属性名。既定は全属性。
6
+ Attribute names to get. Defaults to all attributes。
7
7
  */
8
- const ATTRIBUTE_NAMES_INPUT = z.array(z.string()).default(['All']).describe('取得する属性名。既定は全属性');
8
+ const ATTRIBUTE_NAMES_INPUT = z.array(z.string()).default(['All']).describe('Attribute names to get. Defaults to all attributes');
9
9
  export function sqsTools(options = {}) {
10
10
  return [
11
11
  allowlistedTool({
12
12
  entry: SQS_LIST_QUEUES,
13
- title: 'キューの一覧',
14
- description: 'SQS のキュー URL を一覧する。queue_name_prefix でキュー名の前方一致に絞れる',
13
+ title: 'List queues',
14
+ description: 'Lists SQS queue URLs. Narrow by name prefix with queue_name_prefix',
15
15
  inputSchema: z.object({
16
- queue_name_prefix: z.string().optional().describe('キュー名の前方一致'),
16
+ queue_name_prefix: z.string().optional().describe('Queue name prefix'),
17
17
  ...MAX_ITEMS_PAGINATION_SHAPE,
18
18
  ...COMMON_INPUT_SHAPE,
19
19
  }),
20
20
  }, options),
21
21
  allowlistedTool({
22
22
  entry: SQS_GET_QUEUE_ATTRIBUTES,
23
- title: 'キュー属性の取得',
24
- description: 'キュー 1 本の属性(滞留件数・可視性タイムアウト・デッドレターキュー設定等)を返す',
23
+ title: 'Get queue attributes',
24
+ description: 'Returns the attributes of one queue (backlog counts, visibility timeout, dead-letter queue settings, etc.)',
25
25
  inputSchema: z.object({
26
- queue_url: z.string().describe('キューの URL(https:// で始まる)'),
26
+ queue_url: z.string().describe('Queue URL (starts with https://)'),
27
27
  attribute_names: ATTRIBUTE_NAMES_INPUT,
28
28
  ...COMMON_INPUT_SHAPE,
29
29
  }),
@@ -1,11 +1,7 @@
1
1
  import type { ToolDefinition } from '../server.js';
2
2
  import { type ToolOptions } from './_shared.js';
3
3
  /**
4
- SSO の 2 件を組み立てる。
5
-
6
- ハンドラは ToolCallContext(signal)を使わない。tools/call のキャンセルで子を落とすと、
7
- 「開始だけして後で確認する」という 2 段構成の趣旨に反する。
8
- クライアントの接続断は serve.ts の stdin end の処理が子を片付けるため、孤児は残らない。
4
+ SSO の 2 件を組み立てる。signal を使わないのは、キャンセルで子を落とすと 2 段構成の趣旨に反するため(接続断は serve.ts が片付ける)。
9
5
  */
10
6
  export declare function ssoTools(options?: Readonly<ToolOptions>): readonly ToolDefinition[];
11
7
  //# sourceMappingURL=sso.d.ts.map
@@ -1,45 +1,30 @@
1
- // SSO 認証の 2 段構成(contracts/mcp-tools.md §SSO 認証 / research R5 / FR-007)。
2
- //
3
- // `aws sso login` は認証が終わるまで戻らないため、開始(URL とコードを即時に返す)と
4
- // 完了確認を別のツールに分ける。子プロセスの保持と状態遷移は infra/sso/login-registry.ts が持ち、
5
- // ここは応答の組み立て(ヒント・上限・annotations)だけを持つ。
6
- //
7
- // この 2 件は許可リスト(domain/allowlist.ts)に入れない。許可リストは「awscli を 1 回起動して
8
- // 出力を返す」形のコマンド表で、tests/integration/real-awscli.test.ts が全件を実 awscli へ通す。
9
- // そのため、入れると実行のたびに本物のデバイス認証が発行される。
1
+ // SSO 認証の 2 段構成(contracts/mcp-tools.md §SSO 認証 / research R5)。開始と完了確認を別ツールに分け、子の保持は login-registry.ts が持つ。
2
+ // 許可リストに入れない。実 awscli テストが全件を通すため、入れると実行のたびに本物のデバイス認証が発行される。
10
3
  import { z } from 'zod';
11
4
  import { ensureAwscli } from '../../infra/awscli.js';
12
5
  import { loginRegistry as sharedLoginRegistry, } from '../../infra/sso/login-registry.js';
13
6
  import { limitOutput, toCallToolResult } from '../result.js';
14
7
  import { PRE_EXECUTION_EXIT_CODE, PROFILE_INPUT_SHAPE, resolveExitCode, TIMEOUT_EXIT_CODE, TOOL_OUTPUT_SCHEMA, UNKNOWN_FAILURE_EXIT_CODE, } from './_shared.js';
15
8
  /**
16
- SSO 専用の失敗種別ごとの再試行方法。
17
-
18
- 共通表(_shared.ts の FAILURE_HINTS)は auth-missing / auth-expired で、
19
- 「aws_sso_login を実行してください」と案内する。SSO 自身の失敗に使うと、案内が循環する。
9
+ SSO 専用の失敗種別ごとの再試行方法。共通表は「aws_sso_login を実行」と案内するので、SSO 自身の失敗に使うと循環する。
20
10
  */
21
11
  const SSO_FAILURE_HINTS = {
22
- 'not-allowed': '許可されていない操作です。公開しているツールの範囲で実行してください',
23
- 'invalid-input': 'profile を修正して再実行してください',
24
- 'awscli-missing': 'awscli v2 (2.22 以上) を導入して PATH を通してください',
25
- 'auth-missing': 'profile に SSO の設定がありません。端末で aws configure sso を実行してください',
26
- 'auth-expired': 'トークンのキャッシュを読めませんでした。profile の SSO 設定を確認してください',
27
- 'permission-denied': 'SSO の許可セットに対象アカウントが含まれているか確認してください',
28
- 'resource-not-found': 'profile の sso_start_url / sso_region を確認してください',
29
- 'timeout': '制限時間内に認証が完了しませんでした。aws_sso_login を再度呼び出してください',
30
- 'other': 'awscli が失敗しました。stderr を確認してください',
12
+ 'not-allowed': 'Operation not allowed. Use only the exposed tools',
13
+ 'invalid-input': 'Fix the profile and run again',
14
+ 'awscli-missing': 'Install awscli v2 (2.22 or later) and add it to PATH',
15
+ 'auth-missing': 'The profile has no SSO configuration. Run aws configure sso in a terminal',
16
+ 'auth-expired': 'Could not read the token cache. Check the SSO settings of the profile',
17
+ 'permission-denied': 'Check that the SSO permission set includes the target account',
18
+ 'resource-not-found': 'Check sso_start_url / sso_region of the profile',
19
+ 'timeout': 'Authentication did not complete within the time limit. Call aws_sso_login again',
20
+ 'other': 'awscli failed. Check stderr',
31
21
  };
32
22
  /**
33
23
  出力が上限で切り詰められたときに message へ足す注記。
34
24
  */
35
- const OUTPUT_TRUNCATION_NOTE = '(出力は上限を超えたため先頭のみ載せています)';
25
+ const OUTPUT_TRUNCATION_NOTE = '(Output exceeded the limit, so only the beginning is included)';
36
26
  /**
37
- annotations(判断の根拠は契約書の共通規約)。
38
-
39
- `aws_sso_login` は AWS 側に新しいトークンを発行させるので readOnlyHint は false。
40
- `aws_sso_login_status` はレジストリを読むだけなので true。壊す対象を持たないため
41
- destructiveHint は 2 件とも false。openWorldHint は共通規約に合わせて 2 件とも true にする
42
- (status は awscli を起動しないが、返す内容は外界での承認の進み具合に依存する)。
27
+ annotations。status は awscli を起動しないが、返す内容は外界の承認の進み具合に依存するので openWorldHint は 2 件とも true。
43
28
  */
44
29
  const LOGIN_ANNOTATIONS = { readOnlyHint: false, destructiveHint: false, openWorldHint: true };
45
30
  const STATUS_ANNOTATIONS = { readOnlyHint: true, destructiveHint: false, openWorldHint: true };
@@ -58,19 +43,14 @@ function promptData(session) {
58
43
  };
59
44
  }
60
45
  /**
61
- pending 応答の案内文。
62
-
63
- `promptData` は未確定の項目をキーごと data から落とす。
64
- autofill URL(verificationUrlComplete)が未確定のときに、それを第一候補として案内すると、
65
- 応答に無い項目を案内することになる。
66
- autofill 行は URL・コードより後の chunk で届くことがあり、揃うまでの間はこの分岐に入る。
46
+ pending 応答の案内文。autofill URL は後の chunk で届くことがあり、未確定の間は応答に無い項目を案内しない。
67
47
  */
68
48
  function pendingMessage(session) {
69
49
  return (session.verificationUrlComplete === undefined
70
- ? 'デバイス認証を開始しました。verificationUrl をブラウザで開き、userCode を入力してください。'
71
- : 'デバイス認証を開始しました。verificationUrlComplete をブラウザで開けば'
72
- + 'コード入力は要りません(verificationUrl を開いた場合は userCode を入力します)。')
73
- + '承認後に aws_sso_login_status で完了を確認してください';
50
+ ? 'Device authorization started. Open verificationUrl in a browser and enter userCode. '
51
+ : 'Device authorization started. Open verificationUrlComplete in a browser; '
52
+ + 'no code entry is needed (if you open verificationUrl, enter userCode). ')
53
+ + 'After approval, check completion with aws_sso_login_status.';
74
54
  }
75
55
  function hintOf(category) {
76
56
  // eslint-disable-next-line security/detect-object-injection -- category は FailureCategory の列挙値
@@ -83,9 +63,7 @@ function failureResult(session, data) {
83
63
  /* c8 ignore next -- レジストリは failed に必ず category を付ける。既定値は型を閉じるための防御網 */
84
64
  const category = session.category ?? 'other';
85
65
  const limited = limitOutput(session.output);
86
- // awscli の終了コードをそのまま返す(許可リストのツールと同じ規約。SSO 設定なしなら 253)。
87
- // シグナル終了・未終了は resolveExitCode が 1 へ畳む。0 以下は ok: false と同居させられないので、
88
- // ここでも 1 にする。
66
+ // awscli の終了コードをそのまま返す(SSO 設定なしなら 253)。ok: false と同居できないので、0 以下とシグナル終了は 1 にする。
89
67
  const resolved = resolveExitCode(category, session.exitCode);
90
68
  return {
91
69
  ok: false,
@@ -96,7 +74,7 @@ function failureResult(session, data) {
96
74
  category,
97
75
  data: { ...data },
98
76
  artifacts: [],
99
- message: `SSO 認証が失敗しました (${category})。${hintOf(category)}`
77
+ message: `SSO authentication failed (${category}). ${hintOf(category)}`
100
78
  + (limited.truncated ? OUTPUT_TRUNCATION_NOTE : ''),
101
79
  stderr: limited.text,
102
80
  };
@@ -111,7 +89,7 @@ function timedOutResult(data) {
111
89
  category: 'timeout',
112
90
  data: { ...data },
113
91
  artifacts: [],
114
- message: `SSO 認証が時間切れになりました。${hintOf('timeout')}`,
92
+ message: `SSO authentication timed out. ${hintOf('timeout')}`,
115
93
  };
116
94
  }
117
95
  async function startLogin(arguments_, registry) {
@@ -146,7 +124,7 @@ async function startLogin(arguments_, registry) {
146
124
  exitCode: 0,
147
125
  data: { ...promptData(session), ...base },
148
126
  artifacts: [],
149
- message: 'デバイス認証を始める前から認証済みでした',
127
+ message: 'Already authenticated before device authorization started',
150
128
  };
151
129
  }
152
130
  case 'timed-out': {
@@ -166,7 +144,7 @@ function loginStatus(arguments_, registry) {
166
144
  category: 'invalid-input',
167
145
  data: {},
168
146
  artifacts: [],
169
- message: '開始済みの SSO ログインがありません。aws_sso_login を先に呼んでください',
147
+ message: 'No SSO login in progress. Call aws_sso_login first',
170
148
  };
171
149
  }
172
150
  const base = { state: session.state };
@@ -177,7 +155,7 @@ function loginStatus(arguments_, registry) {
177
155
  exitCode: 0,
178
156
  data: { ...promptData(session), ...base },
179
157
  artifacts: [],
180
- message: 'まだ承認されていません。案内した URL での承認後に再度呼び出してください',
158
+ message: 'Not approved yet. Approve at the given URL, then call again',
181
159
  };
182
160
  }
183
161
  case 'completed': {
@@ -187,7 +165,7 @@ function loginStatus(arguments_, registry) {
187
165
  exitCode: 0,
188
166
  data: { ...promptData(session), ...base, output: limited.text },
189
167
  artifacts: [],
190
- message: 'SSO 認証が完了しました'
168
+ message: 'SSO authentication completed'
191
169
  + (limited.truncated ? OUTPUT_TRUNCATION_NOTE : ''),
192
170
  };
193
171
  }
@@ -200,11 +178,7 @@ function loginStatus(arguments_, registry) {
200
178
  }
201
179
  }
202
180
  /**
203
- SSO の 2 件を組み立てる。
204
-
205
- ハンドラは ToolCallContext(signal)を使わない。tools/call のキャンセルで子を落とすと、
206
- 「開始だけして後で確認する」という 2 段構成の趣旨に反する。
207
- クライアントの接続断は serve.ts の stdin end の処理が子を片付けるため、孤児は残らない。
181
+ SSO の 2 件を組み立てる。signal を使わないのは、キャンセルで子を落とすと 2 段構成の趣旨に反するため(接続断は serve.ts が片付ける)。
208
182
  */
209
183
  export function ssoTools(options = {}) {
210
184
  // 既定の解決はここで済ませる。ハンドラ内で遅延解決すると、テストが注入したレジストリと
@@ -214,9 +188,9 @@ export function ssoTools(options = {}) {
214
188
  {
215
189
  name: 'aws_sso_login',
216
190
  config: {
217
- title: 'SSO ログインの開始',
218
- description: 'AWS SSO のデバイス認証を開始し、認証 URL とコードを即時に返す。完了は待たないため、'
219
- + '利用者の承認後に aws_sso_login_status で確認する',
191
+ title: 'Start SSO login',
192
+ description: 'Starts AWS SSO device authorization and returns the verification URL and code immediately. It does not wait for completion, so '
193
+ + 'check with aws_sso_login_status after the user approves',
220
194
  inputSchema: z.object({ ...PROFILE_INPUT_SHAPE }),
221
195
  outputSchema: TOOL_OUTPUT_SCHEMA,
222
196
  annotations: LOGIN_ANNOTATIONS,
@@ -226,9 +200,9 @@ export function ssoTools(options = {}) {
226
200
  {
227
201
  name: 'aws_sso_login_status',
228
202
  config: {
229
- title: 'SSO ログインの完了確認',
230
- description: 'aws_sso_login で開始した認証の状態(pending / completed / failed / timed-out)を返す。'
231
- + 'awscli は起動しない',
203
+ title: 'Check SSO login status',
204
+ description: 'Returns the state (pending / completed / failed / timed-out) of the authorization started by aws_sso_login. '
205
+ + 'Does not run awscli',
232
206
  inputSchema: z.object({ ...PROFILE_INPUT_SHAPE }),
233
207
  outputSchema: TOOL_OUTPUT_SCHEMA,
234
208
  annotations: STATUS_ANNOTATIONS,
@@ -6,8 +6,8 @@ export function stsTools(options = {}) {
6
6
  return [
7
7
  allowlistedTool({
8
8
  entry: STS_GET_CALLER_IDENTITY,
9
- title: '呼び出し元 ID の確認',
10
- description: '現在の認証情報が示すアカウント ID・ユーザ ID・ARN を返す。認証が通っているかの確認に使う',
9
+ title: 'Get caller identity',
10
+ description: 'Returns the account ID, user ID, and ARN of the current credentials. Use it to check that authentication works',
11
11
  inputSchema: z.object({ ...COMMON_INPUT_SHAPE }),
12
12
  }, options),
13
13
  ];
@@ -12,9 +12,14 @@ export declare const MINIMUM_NODE_VERSIONS: Readonly<Record<number, string>>;
12
12
  */
13
13
  export declare function suggestedNodeVersion(): string;
14
14
  /**
15
- 起動してよい版なら undefined、止めるべき版なら利用者向けの案内文を返す。
15
+ 起動してよい版なら undefined、止めるべき版なら字下げ無しの複数行の案内文を返す。
16
+ CLI は診断行の共通の関数に渡し、続きの行の字下げはそちらが付ける。
16
17
  表に無い major は、表の最大 major より新しければ修正後のリリースとみなして許可し、
17
18
  それ以外(24 未満・サポート終了の 25 系)は止める。
18
19
  */
20
+ export declare function nodeRuntimeGuidance(version: string): string | undefined;
21
+ /**
22
+ launcher と hook がそのまま書く形。続きの行の先頭に空白 2 つを付けた案内文を返す。
23
+ */
19
24
  export declare function checkNodeRuntime(version: string): string | undefined;
20
25
  //# sourceMappingURL=node-runtime.d.ts.map
@@ -6,7 +6,8 @@
6
6
  // enum・namespace・パラメータプロパティを使わず、他モジュールを import しない。
7
7
  // ランチャーと hook が node から直接読み込んで checkNodeRuntime を呼ぶため。
8
8
  //
9
- // 判定は cli.ts の main() で行う。静的 import の評価後に動くため、import の段階で落ちる
9
+ // 判定は cli.ts の main() で行う。CLI は nodeRuntimeGuidance の結果を診断行の共通の関数に渡す
10
+ // (続きの行の字下げは共通の関数が付ける)。静的 import の評価後に動くため、import の段階で落ちる
10
11
  // 古い major(23 以前)では案内を出せないことがある。判定だけのエントリモジュールには分けない。
11
12
  /**
12
13
  系列(major)ごとの最低版。2026-10-21 の Node.js セキュリティリリース
@@ -44,8 +45,8 @@ function isAtLeast(actual, minimum) {
44
45
  }
45
46
  function requiredVersionsText() {
46
47
  return Object.entries(MINIMUM_NODE_VERSIONS)
47
- .map(([major, minimum]) => `${major} 系は ${minimum} 以上`)
48
- .join('、');
48
+ .map(([major, minimum]) => `${major}.x: ${minimum} or later`)
49
+ .join(', ');
49
50
  }
50
51
  /**
51
52
  案内に載せる更新先の版。表の最小 major の最低版。
@@ -57,17 +58,18 @@ export function suggestedNodeVersion() {
57
58
  }
58
59
  function guidance(version) {
59
60
  return [
60
- `Node.js ${version} は既知の脆弱性の修正を含まないため起動しません。`,
61
- ` 必要な版: ${requiredVersionsText()}`,
62
- ` Node.js を更新して再実行してください(nvm の例: nvm install ${suggestedNodeVersion()})。`,
61
+ `Node.js ${version} does not include fixes for known vulnerabilities; not starting`,
62
+ `required: ${requiredVersionsText()}`,
63
+ `(fix: update Node.js and run again; with nvm: nvm install ${suggestedNodeVersion()})`,
63
64
  ].join('\n');
64
65
  }
65
66
  /**
66
- 起動してよい版なら undefined、止めるべき版なら利用者向けの案内文を返す。
67
+ 起動してよい版なら undefined、止めるべき版なら字下げ無しの複数行の案内文を返す。
68
+ CLI は診断行の共通の関数に渡し、続きの行の字下げはそちらが付ける。
67
69
  表に無い major は、表の最大 major より新しければ修正後のリリースとみなして許可し、
68
70
  それ以外(24 未満・サポート終了の 25 系)は止める。
69
71
  */
70
- export function checkNodeRuntime(version) {
72
+ export function nodeRuntimeGuidance(version) {
71
73
  const actual = parseVersion(version);
72
74
  if (actual === undefined)
73
75
  return guidance(version);
@@ -81,4 +83,10 @@ export function checkNodeRuntime(version) {
81
83
  return guidance(version);
82
84
  return isAtLeast(actual, minimumVersion.tuple) ? undefined : guidance(version);
83
85
  }
86
+ /**
87
+ launcher と hook がそのまま書く形。続きの行の先頭に空白 2 つを付けた案内文を返す。
88
+ */
89
+ export function checkNodeRuntime(version) {
90
+ return nodeRuntimeGuidance(version)?.split('\n').join('\n ');
91
+ }
84
92
  //# sourceMappingURL=node-runtime.js.map
@@ -1,16 +1,16 @@
1
1
  {
2
2
  "name": "@pecot/tools-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@pecot/tools-mcp",
9
- "version": "1.0.1",
9
+ "version": "1.1.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@modelcontextprotocol/server": "^2.4.0",
13
- "commander": "^15.0.0",
13
+ "commander": "^15.1.0",
14
14
  "zod": "^4.6.7"
15
15
  },
16
16
  "bin": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pecot/tools-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "pecot-tools MCP server (standalone distribution)",
6
6
  "license": "MIT",
@@ -17,7 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@modelcontextprotocol/server": "^2.4.0",
20
- "commander": "^15.0.0",
20
+ "commander": "^15.1.0",
21
21
  "zod": "^4.6.7"
22
22
  }
23
23
  }