feat: build CLI core framework with Commander.js

Add CLI entry point with Commander.js, config management (~/.mcpctl/config.json
with Zod validation), output formatters (table/json/yaml), config and status
commands with dependency injection for testing. Fix sanitizeString regex ordering.
67 tests passing.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Michal
2026-02-21 04:17:31 +00:00
parent 981585a943
commit 1b66e235fc
21 changed files with 922 additions and 72 deletions

182
pnpm-lock.yaml generated
View File

@@ -16,7 +16,7 @@ importers:
version: 8.56.0(eslint@10.0.1(jiti@2.6.1))(typescript@5.9.3)
'@vitest/coverage-v8':
specifier: ^4.0.18
version: 4.0.18(vitest@4.0.18(jiti@2.6.1)(tsx@4.21.0))
version: 4.0.18(vitest@4.0.18(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0))
eslint:
specifier: ^10.0.1
version: 10.0.1(jiti@2.6.1)
@@ -34,7 +34,7 @@ importers:
version: 5.9.3
vitest:
specifier: ^4.0.18
version: 4.0.18(jiti@2.6.1)(tsx@4.21.0)
version: 4.0.18(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)
src/cli:
dependencies:
@@ -52,10 +52,20 @@ importers:
version: 13.1.0
inquirer:
specifier: ^12.0.0
version: 12.11.1
version: 12.11.1(@types/node@25.3.0)
js-yaml:
specifier: ^4.1.0
version: 4.1.1
zod:
specifier: ^3.24.0
version: 3.25.76
devDependencies:
'@types/js-yaml':
specifier: ^4.0.9
version: 4.0.9
'@types/node':
specifier: ^25.3.0
version: 25.3.0
src/db:
dependencies:
@@ -698,9 +708,15 @@ packages:
'@types/estree@1.0.8':
resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==}
'@types/js-yaml@4.0.9':
resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==}
'@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/node@25.3.0':
resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==}
'@typescript-eslint/eslint-plugin@8.56.0':
resolution: {integrity: sha512-lRyPDLzNCuae71A3t9NEINBiTn7swyOhvUj3MyUOxb8x6g6vPEFoOU+ZRmGMusNC3X3YMhqMIX7i8ShqhT74Pw==}
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
@@ -1795,6 +1811,9 @@ packages:
engines: {node: '>=14.17'}
hasBin: true
undici-types@7.18.2:
resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==}
unpipe@1.0.0:
resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
engines: {node: '>= 0.8'}
@@ -2098,100 +2117,128 @@ snapshots:
'@inquirer/ansi@1.0.2': {}
'@inquirer/checkbox@4.3.2':
'@inquirer/checkbox@4.3.2(@types/node@25.3.0)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10
'@inquirer/type': 3.0.10(@types/node@25.3.0)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/confirm@5.1.21':
'@inquirer/confirm@5.1.21(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/core@10.3.2':
'@inquirer/core@10.3.2(@types/node@25.3.0)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10
'@inquirer/type': 3.0.10(@types/node@25.3.0)
cli-width: 4.1.0
mute-stream: 2.0.0
signal-exit: 4.1.0
wrap-ansi: 6.2.0
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/editor@4.2.23':
'@inquirer/editor@4.2.23(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/external-editor': 1.0.3
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/external-editor': 1.0.3(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/expand@4.0.23':
'@inquirer/expand@4.0.23(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/external-editor@1.0.3':
'@inquirer/external-editor@1.0.3(@types/node@25.3.0)':
dependencies:
chardet: 2.1.1
iconv-lite: 0.7.2
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/figures@1.0.15': {}
'@inquirer/input@4.3.1':
'@inquirer/input@4.3.1(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/number@3.0.23':
'@inquirer/number@3.0.23(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/password@4.0.23':
'@inquirer/password@4.0.23(@types/node@25.3.0)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/prompts@7.10.1':
'@inquirer/prompts@7.10.1(@types/node@25.3.0)':
dependencies:
'@inquirer/checkbox': 4.3.2
'@inquirer/confirm': 5.1.21
'@inquirer/editor': 4.2.23
'@inquirer/expand': 4.0.23
'@inquirer/input': 4.3.1
'@inquirer/number': 3.0.23
'@inquirer/password': 4.0.23
'@inquirer/rawlist': 4.1.11
'@inquirer/search': 3.2.2
'@inquirer/select': 4.4.2
'@inquirer/checkbox': 4.3.2(@types/node@25.3.0)
'@inquirer/confirm': 5.1.21(@types/node@25.3.0)
'@inquirer/editor': 4.2.23(@types/node@25.3.0)
'@inquirer/expand': 4.0.23(@types/node@25.3.0)
'@inquirer/input': 4.3.1(@types/node@25.3.0)
'@inquirer/number': 3.0.23(@types/node@25.3.0)
'@inquirer/password': 4.0.23(@types/node@25.3.0)
'@inquirer/rawlist': 4.1.11(@types/node@25.3.0)
'@inquirer/search': 3.2.2(@types/node@25.3.0)
'@inquirer/select': 4.4.2(@types/node@25.3.0)
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/rawlist@4.1.11':
'@inquirer/rawlist@4.1.11(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/search@3.2.2':
'@inquirer/search@3.2.2(@types/node@25.3.0)':
dependencies:
'@inquirer/core': 10.3.2
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10
'@inquirer/type': 3.0.10(@types/node@25.3.0)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/select@4.4.2':
'@inquirer/select@4.4.2(@types/node@25.3.0)':
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/figures': 1.0.15
'@inquirer/type': 3.0.10
'@inquirer/type': 3.0.10(@types/node@25.3.0)
yoctocolors-cjs: 2.1.3
optionalDependencies:
'@types/node': 25.3.0
'@inquirer/type@3.0.10': {}
'@inquirer/type@3.0.10(@types/node@25.3.0)':
optionalDependencies:
'@types/node': 25.3.0
'@jridgewell/resolve-uri@3.1.2': {}
@@ -2351,8 +2398,14 @@ snapshots:
'@types/estree@1.0.8': {}
'@types/js-yaml@4.0.9': {}
'@types/json-schema@7.0.15': {}
'@types/node@25.3.0':
dependencies:
undici-types: 7.18.2
'@typescript-eslint/eslint-plugin@8.56.0(@typescript-eslint/parser@8.56.0(eslint@10.0.1(jiti@2.6.1))(typescript@5.9.3))(eslint@10.0.1(jiti@2.6.1))(typescript@5.9.3)':
dependencies:
'@eslint-community/regexpp': 4.12.2
@@ -2444,7 +2497,7 @@ snapshots:
'@typescript-eslint/types': 8.56.0
eslint-visitor-keys: 5.0.1
'@vitest/coverage-v8@4.0.18(vitest@4.0.18(jiti@2.6.1)(tsx@4.21.0))':
'@vitest/coverage-v8@4.0.18(vitest@4.0.18(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0))':
dependencies:
'@bcoe/v8-coverage': 1.0.2
'@vitest/utils': 4.0.18
@@ -2456,7 +2509,7 @@ snapshots:
obug: 2.1.1
std-env: 3.10.0
tinyrainbow: 3.0.3
vitest: 4.0.18(jiti@2.6.1)(tsx@4.21.0)
vitest: 4.0.18(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)
'@vitest/expect@4.0.18':
dependencies:
@@ -2467,13 +2520,13 @@ snapshots:
chai: 6.2.2
tinyrainbow: 3.0.3
'@vitest/mocker@4.0.18(vite@7.3.1(jiti@2.6.1)(tsx@4.21.0))':
'@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0))':
dependencies:
'@vitest/spy': 4.0.18
estree-walker: 3.0.3
magic-string: 0.30.21
optionalDependencies:
vite: 7.3.1(jiti@2.6.1)(tsx@4.21.0)
vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)
'@vitest/pretty-format@4.0.18':
dependencies:
@@ -3033,15 +3086,17 @@ snapshots:
inherits@2.0.4: {}
inquirer@12.11.1:
inquirer@12.11.1(@types/node@25.3.0):
dependencies:
'@inquirer/ansi': 1.0.2
'@inquirer/core': 10.3.2
'@inquirer/prompts': 7.10.1
'@inquirer/type': 3.0.10
'@inquirer/core': 10.3.2(@types/node@25.3.0)
'@inquirer/prompts': 7.10.1(@types/node@25.3.0)
'@inquirer/type': 3.0.10(@types/node@25.3.0)
mute-stream: 2.0.0
run-async: 4.0.6
rxjs: 7.8.2
optionalDependencies:
'@types/node': 25.3.0
ip-address@10.0.1: {}
@@ -3532,6 +3587,8 @@ snapshots:
typescript@5.9.3: {}
undici-types@7.18.2: {}
unpipe@1.0.0: {}
uri-js@4.4.1:
@@ -3540,7 +3597,7 @@ snapshots:
vary@1.1.2: {}
vite@7.3.1(jiti@2.6.1)(tsx@4.21.0):
vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0):
dependencies:
esbuild: 0.27.3
fdir: 6.5.0(picomatch@4.0.3)
@@ -3549,14 +3606,15 @@ snapshots:
rollup: 4.58.0
tinyglobby: 0.2.15
optionalDependencies:
'@types/node': 25.3.0
fsevents: 2.3.3
jiti: 2.6.1
tsx: 4.21.0
vitest@4.0.18(jiti@2.6.1)(tsx@4.21.0):
vitest@4.0.18(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0):
dependencies:
'@vitest/expect': 4.0.18
'@vitest/mocker': 4.0.18(vite@7.3.1(jiti@2.6.1)(tsx@4.21.0))
'@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0))
'@vitest/pretty-format': 4.0.18
'@vitest/runner': 4.0.18
'@vitest/snapshot': 4.0.18
@@ -3573,8 +3631,10 @@ snapshots:
tinyexec: 1.0.2
tinyglobby: 0.2.15
tinyrainbow: 3.0.3
vite: 7.3.1(jiti@2.6.1)(tsx@4.21.0)
vite: 7.3.1(@types/node@25.3.0)(jiti@2.6.1)(tsx@4.21.0)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 25.3.0
transitivePeerDependencies:
- jiti
- less