@retailcrm/embed-ui-v1-endpoint 0.9.21 → 0.9.22-alpha.2

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 (46) hide show
  1. package/README.md +70 -11
  2. package/bin/embed-ui-v1-endpoint-mcp.mjs +10 -0
  3. package/bin/embed-ui-v1-endpoint.mjs +416 -0
  4. package/dist/common/targets.cjs +61 -130
  5. package/dist/common/targets.d.ts +38 -27
  6. package/dist/common/targets.documentation.d.ts +302 -0
  7. package/dist/common/targets.js +61 -130
  8. package/dist/mcp/server.cjs +115 -0
  9. package/dist/mcp/server.d.ts +3 -0
  10. package/dist/mcp/server.js +97 -0
  11. package/dist/meta.json +618 -0
  12. package/docs/README.md +8 -6
  13. package/docs/create-endpoint.md +8 -10
  14. package/docs/define-page-runner.md +3 -3
  15. package/docs/define-widget-runner.md +2 -2
  16. package/docs/layout.md +14 -37
  17. package/docs/menu-placements.md +11 -13
  18. package/docs/page-routes.md +12 -14
  19. package/docs/run-endpoint.md +4 -4
  20. package/docs/targets/customer-card-communications-after.yml +48 -0
  21. package/docs/targets/customer-card-inwork-after.yml +48 -0
  22. package/docs/targets/customer-card-inwork-before.yml +48 -0
  23. package/docs/targets/customer-card-phone.yml +49 -0
  24. package/docs/targets/order-card-comment-manager-before.yml +51 -0
  25. package/docs/targets/order-card-common-after.yml +51 -0
  26. package/docs/targets/order-card-common-before.yml +51 -0
  27. package/docs/targets/order-card-customer-after.yml +51 -0
  28. package/docs/targets/order-card-customer-before.yml +51 -0
  29. package/docs/targets/order-card-customer-email.yml +51 -0
  30. package/docs/targets/order-card-customer-phone.yml +51 -0
  31. package/docs/targets/order-card-delivery-address.yml +51 -0
  32. package/docs/targets/order-card-delivery-after.yml +51 -0
  33. package/docs/targets/order-card-delivery-before.yml +51 -0
  34. package/docs/targets/order-card-dimensions-before.yml +51 -0
  35. package/docs/targets/order-card-list-after.yml +51 -0
  36. package/docs/targets/order-card-list-before.yml +51 -0
  37. package/docs/targets/order-card-payment-before.yml +51 -0
  38. package/docs/targets/order-card-store-before.yml +51 -0
  39. package/docs/targets/order-mg-delivery-after.yml +51 -0
  40. package/docs/targets/order-mg-delivery-before.yml +51 -0
  41. package/docs/targets/order-mg-list-after.yml +51 -0
  42. package/docs/targets/order-mg-list-before.yml +51 -0
  43. package/docs/targets/order-mg-payment-after.yml +51 -0
  44. package/docs/targets/order-mg-payment-before.yml +51 -0
  45. package/docs/targets.md +42 -17
  46. package/package.json +27 -7
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  [![npm version](https://img.shields.io/npm/v/@retailcrm/embed-ui-v1-endpoint.svg)](https://www.npmjs.com/package/@retailcrm/embed-ui-v1-endpoint)
4
4
 
5
- `@retailcrm/embed-ui-v1-endpoint` помогает поднять endpoint для удалённых
6
- виджетов и страниц в RetailCRM: принять вызовы host-части, смонтировать Vue
7
- приложение в удалённый root и корректно освобождать ресурсы.
5
+ `@retailcrm/embed-ui-v1-endpoint` предоставляет endpoint API для встраиваемых
6
+ виджетов и страниц в RetailCRM: обработку вызовов host-части, монтирование Vue-приложения
7
+ в endpoint root и освобождение ресурсов.
8
8
 
9
9
  Пакет покрывает два сценария:
10
10
 
@@ -28,19 +28,78 @@ const runner = defineRunner({
28
28
  runEndpoint(runner)
29
29
  ```
30
30
 
31
- Для продакшен-использования обычно достаточно `runEndpoint(...)` в worker entry.
31
+ Для продакшен-использования обычно достаточно `runEndpoint(...)` в точке входа веб-воркера.
32
32
  Если нужен более низкоуровневый контроль транспорта — используйте `createEndpoint(...)`.
33
33
 
34
- ## Продвинутые гайды
34
+ ## Документация
35
35
 
36
36
  Подробная документация по методам находится в каталоге [`docs/`](./docs/README.md):
37
37
 
38
38
  - [`defineRunner`](./docs/define-runner.md) — как объединить page и widget runners в один endpoint runner.
39
- - [`definePageRunner`](./docs/define-page-runner.md) — как запускать remote-страницы по `code`.
40
- - [`defineWidgetRunner`](./docs/define-widget-runner.md) — как запускать remote-виджеты по `target`.
39
+ - [`definePageRunner`](./docs/define-page-runner.md) — как запускать встраиваемые страницы по `code`.
40
+ - [`defineWidgetRunner`](./docs/define-widget-runner.md) — как запускать встраиваемые виджеты по `target`.
41
41
  - [`createEndpoint`](./docs/create-endpoint.md) — как вручную создать endpoint с transport и messenger.
42
- - [`runEndpoint`](./docs/run-endpoint.md) — как поднять endpoint в worker entry одной строкой.
42
+ - [`runEndpoint`](./docs/run-endpoint.md) — как поднять endpoint в точке входа веб-воркера одной строкой.
43
43
  - [`targets` и `defineTarget`](./docs/targets.md) — как типизировать цели виджетов и маршрутизировать их по target.
44
- - [`menu-placements`](./docs/menu-placements.md) — как описывать меню и пункты навигации для remote-страниц.
45
- - [`page-routes`](./docs/page-routes.md) — как связывать page `code`, CRM route и `definePageRunner`.
46
- - [`layout`](./docs/layout.md) — как выбирать layout-паттерны страниц, `modal sidebar` и `modal window`, и из каких `v1-components` их собирать.
44
+ - [`menu-placements`](./docs/menu-placements.md) — как описывать меню и пункты навигации для встраиваемых страниц.
45
+ - [`page-routes`](./docs/page-routes.md) — как связывать page `code`, CRM-маршрут и `definePageRunner`.
46
+ - [`layout`](./docs/layout.md) — как выбирать паттерны компоновки страниц, `modal sidebar` и `modal window`, и из каких `v1-components` их собирать.
47
+
48
+ ## MCP для AI-ассистентов
49
+
50
+ Пакет поставляет MCP-сервер с AI-friendly описаниями встроенных widget targets.
51
+ Сервер читает сгенерированные YAML-профили из `docs/targets/*.yml` и отдаёт их как MCP resources.
52
+
53
+ Запуск через опубликованный пакет:
54
+
55
+ ```bash
56
+ npx -p @retailcrm/embed-ui-v1-endpoint embed-ui-v1-endpoint-mcp
57
+ ```
58
+
59
+ Пример stdio-конфига для MCP-клиента:
60
+
61
+ ```json
62
+ {
63
+ "command": "npx",
64
+ "args": ["-y", "-p", "@retailcrm/embed-ui-v1-endpoint", "embed-ui-v1-endpoint-mcp"]
65
+ }
66
+ ```
67
+
68
+ Базовые resources:
69
+
70
+ - `embed-ui-v1-endpoint://targets` — JSON-индекс всех target profiles.
71
+ - `embed-ui-v1-endpoint://targets/<encoded-target>` — YAML-профиль конкретного target.
72
+
73
+ ## AI и инициализация `AGENTS.md`
74
+
75
+ Чтобы агент понимал, когда использовать MCP-сервер пакета, можно добавить в
76
+ целевой проект секцию с инструкциями:
77
+
78
+ ```bash
79
+ npx @retailcrm/embed-ui-v1-endpoint init-agents
80
+ ```
81
+
82
+ Если `AGENTS.md` ещё нет, команда создаст файл. Если файл уже есть, команда
83
+ допишет в конец английский блок для `@retailcrm/embed-ui-v1-endpoint`, если
84
+ такого блока там ещё нет. С `--force` можно обновить уже существующий блок
85
+ пакета.
86
+
87
+ ## Инициализация MCP-конфига
88
+
89
+ Пакет также может сам добавить project-level MCP-настройки в целевой проект:
90
+
91
+ ```bash
92
+ npx @retailcrm/embed-ui-v1-endpoint init-config
93
+ ```
94
+
95
+ Команда создаёт или дополняет корневой `.mcp.json`, добавляет заметку в
96
+ `README.md` и не дублирует уже существующую настройку. Клиентские project-level
97
+ конфиги создаются только явно:
98
+
99
+ ```bash
100
+ npx @retailcrm/embed-ui-v1-endpoint init-config --mcp-client-configs cursor,junie,vscode
101
+ ```
102
+
103
+ С `--force` можно обновить уже существующие управляемые записи. Команда
104
+ обновляет только запись `retailcrm-embed-ui-v1-endpoint`, а остальные серверы и
105
+ пользовательские настройки клиентских конфигов оставляет без изменений.
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { runEndpointMcpServer } from '../dist/mcp/server.js'
4
+
5
+ try {
6
+ await runEndpointMcpServer()
7
+ } catch (error) {
8
+ console.error(error)
9
+ process.exit(1)
10
+ }
@@ -0,0 +1,416 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+ import process from 'node:process'
6
+
7
+ const PACKAGE_NAME = '@retailcrm/embed-ui-v1-endpoint'
8
+ const DEFAULT_NEWLINE = '\n'
9
+ const AGENTS_SECTION_HEADER = '## @retailcrm/embed-ui-v1-endpoint'
10
+ const README_MCP_SECTION_HEADER = '## MCP For AI Assistants'
11
+ const README_MCP_MARKER = 'embed-ui-v1-endpoint://targets'
12
+ const MCP_SERVER_NAME = 'retailcrm-embed-ui-v1-endpoint'
13
+ const MCP_SERVER_CONFIG = {
14
+ command: 'npx',
15
+ args: ['-y', '-p', PACKAGE_NAME, 'embed-ui-v1-endpoint-mcp'],
16
+ }
17
+ const MCP_CLIENT_CONFIGS = {
18
+ cursor: {
19
+ filePath: '.cursor/mcp.json',
20
+ rootField: 'mcpServers',
21
+ },
22
+ junie: {
23
+ filePath: '.junie/mcp/mcp.json',
24
+ rootField: 'mcpServers',
25
+ },
26
+ vscode: {
27
+ filePath: '.vscode/mcp.json',
28
+ rootField: 'servers',
29
+ },
30
+ }
31
+
32
+ const HELP_TEXT = `Usage:
33
+ npx ${PACKAGE_NAME} init-agents [target] [options]
34
+ npx ${PACKAGE_NAME} init-config [target] [options]
35
+
36
+ Options:
37
+ -f, --force Replace existing managed sections and MCP server entries
38
+ --mcp-client-configs Comma-separated MCP client configs to create (cursor,junie,vscode)
39
+ --dry-run Print planned config changes without writing files
40
+ -h, --help Show this help
41
+
42
+ Examples:
43
+ npx ${PACKAGE_NAME} init-agents
44
+ npx ${PACKAGE_NAME} init-agents ./my-project
45
+ npx ${PACKAGE_NAME} init-agents --force
46
+ npx ${PACKAGE_NAME} init-config ./my-project
47
+ npx ${PACKAGE_NAME} init-config ./my-project --mcp-client-configs cursor,junie,vscode
48
+ `
49
+
50
+ const parseArgs = (argv) => {
51
+ const options = {
52
+ command: null,
53
+ target: process.cwd(),
54
+ force: false,
55
+ dryRun: false,
56
+ mcpClientConfigs: [],
57
+ }
58
+
59
+ const positionals = []
60
+
61
+ for (let index = 0; index < argv.length; index++) {
62
+ const argument = argv[index]
63
+
64
+ if (argument === '-h' || argument === '--help') {
65
+ console.log(HELP_TEXT)
66
+ process.exit(0)
67
+ }
68
+
69
+ if (argument === '-f' || argument === '--force') {
70
+ options.force = true
71
+ continue
72
+ }
73
+
74
+ if (argument === '--dry-run') {
75
+ options.dryRun = true
76
+ continue
77
+ }
78
+
79
+ if (argument === '--mcp-client-configs') {
80
+ const value = argv[index + 1]
81
+ if (!value || value.startsWith('-')) {
82
+ throw new Error('--mcp-client-configs requires a comma-separated value')
83
+ }
84
+
85
+ options.mcpClientConfigs = value
86
+ .split(',')
87
+ .map((entry) => entry.trim())
88
+ .filter(Boolean)
89
+ index++
90
+ continue
91
+ }
92
+
93
+ if (argument.startsWith('-')) {
94
+ throw new Error(`Unknown option: ${argument}`)
95
+ }
96
+
97
+ positionals.push(argument)
98
+ }
99
+
100
+ if (!positionals.length) {
101
+ throw new Error('Command is required')
102
+ }
103
+
104
+ options.command = positionals[0]
105
+
106
+ if (positionals.length >= 2) {
107
+ options.target = path.resolve(process.cwd(), positionals[1])
108
+ }
109
+
110
+ if (positionals.length > 2) {
111
+ throw new Error('Too many positional arguments')
112
+ }
113
+
114
+ return options
115
+ }
116
+
117
+ const createAgentsSection = () => {
118
+ return `${AGENTS_SECTION_HEADER}
119
+
120
+ When working with \`${PACKAGE_NAME}\` in this project:
121
+
122
+ 1. Read \`./node_modules/${PACKAGE_NAME}/README.md\`.
123
+ 2. Then read the relevant guide from \`./node_modules/${PACKAGE_NAME}/docs/README.md\`.
124
+ 3. Use documented public entrypoints instead of package internals:
125
+ - \`${PACKAGE_NAME}/remote\`
126
+ - \`${PACKAGE_NAME}/common/targets\`
127
+ 4. Do not import from \`${PACKAGE_NAME}/dist/*\`, source files, or repository-only paths.
128
+ 5. When the task involves widget targets, target placement, target contexts, target metadata, or choosing a target, use the package MCP server if it is available.
129
+ 6. First read \`embed-ui-v1-endpoint://targets\` to discover available target profiles.
130
+ 7. Then read the relevant \`embed-ui-v1-endpoint://targets/<encoded-target>\` resource before answering or changing code related to that target.
131
+ 8. If MCP resources are not available, use the generated YAML profiles from \`./node_modules/${PACKAGE_NAME}/docs/targets/*.yml\` as the fallback source.
132
+ 9. Prefer target profiles over guessing target placement, contexts, or semantic intent from names alone.
133
+
134
+ Suggested MCP stdio server configuration:
135
+
136
+ \`\`\`json
137
+ {
138
+ "command": "npx",
139
+ "args": ["-y", "-p", "${PACKAGE_NAME}", "embed-ui-v1-endpoint-mcp"]
140
+ }
141
+ \`\`\`
142
+ `
143
+ }
144
+
145
+ const createMcpReadmeSection = (clientConfigs) => {
146
+ const clientConfigText = clientConfigs.length
147
+ ? `Client MCP configs were also requested: ${clientConfigs.map((clientConfig) => `\`${clientConfig}\``).join(', ')}. Review the generated files and restart the AI client if it is already open.`
148
+ : 'Client MCP configs are not created by default. For supported project-level configs, rerun init with `--mcp-client-configs cursor,junie,vscode`.'
149
+
150
+ return `${README_MCP_SECTION_HEADER}
151
+
152
+ The project has an MCP server configuration for \`${PACKAGE_NAME}\`.
153
+ It exposes AI-friendly widget target descriptions as MCP resources.
154
+
155
+ Basic check:
156
+
157
+ \`\`\`bash
158
+ npx -p ${PACKAGE_NAME} embed-ui-v1-endpoint-mcp
159
+ \`\`\`
160
+
161
+ Primary resources:
162
+
163
+ - \`${README_MCP_MARKER}\` is the widget target index.
164
+ - \`embed-ui-v1-endpoint://targets/<encoded-target>\` is a YAML profile for one target.
165
+
166
+ ${clientConfigText}
167
+
168
+ ### User-Level MCP Clients
169
+
170
+ Some clients store MCP servers in a user-level config outside this repository. Init does not edit
171
+ those files. Add the same server manually and restart the client.
172
+
173
+ Claude Desktop config paths:
174
+
175
+ - macOS: \`~/Library/Application Support/Claude/claude_desktop_config.json\`
176
+ - Windows: \`%APPDATA%\\Claude\\claude_desktop_config.json\`
177
+
178
+ Config snippet:
179
+
180
+ \`\`\`json
181
+ {
182
+ "mcpServers": {
183
+ "${MCP_SERVER_NAME}": {
184
+ "command": "npx",
185
+ "args": ["-y", "-p", "${PACKAGE_NAME}", "embed-ui-v1-endpoint-mcp"]
186
+ }
187
+ }
188
+ }
189
+ \`\`\`
190
+ `
191
+ }
192
+
193
+ const createAgentsTemplate = () => {
194
+ return `# AGENTS.md
195
+
196
+ ${createAgentsSection()}` + DEFAULT_NEWLINE
197
+ }
198
+
199
+ const hasPackageSection = (content) => content.includes(AGENTS_SECTION_HEADER)
200
+
201
+ const appendSection = (content, section) => {
202
+ const trimmed = content.replace(/\s+$/u, '')
203
+
204
+ if (!trimmed.length) {
205
+ return `${section}${DEFAULT_NEWLINE}`
206
+ }
207
+
208
+ return `${trimmed}${DEFAULT_NEWLINE}${DEFAULT_NEWLINE}${section}${DEFAULT_NEWLINE}`
209
+ }
210
+
211
+ const replaceSection = (content, section) => {
212
+ const escapedHeader = AGENTS_SECTION_HEADER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
213
+ const sectionPattern = new RegExp(`${escapedHeader}[\\s\\S]*?(?=\\n##\\s|$)`, 'u')
214
+
215
+ if (!sectionPattern.test(content)) {
216
+ return appendSection(content, section)
217
+ }
218
+
219
+ return content
220
+ .replace(sectionPattern, section.trimEnd())
221
+ .replace(/\s+$/u, '') + DEFAULT_NEWLINE
222
+ }
223
+
224
+ const replaceReadmeMcpSection = (content, section) => {
225
+ const escapedHeader = README_MCP_SECTION_HEADER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
226
+ const sectionPattern = new RegExp(`${escapedHeader}[\\s\\S]*?(?=\\n##\\s|$)`, 'u')
227
+
228
+ if (!sectionPattern.test(content)) {
229
+ return appendSection(content, section)
230
+ }
231
+
232
+ return content
233
+ .replace(sectionPattern, section.trimEnd())
234
+ .replace(/\s+$/u, '') + DEFAULT_NEWLINE
235
+ }
236
+
237
+ const readJsonObject = (filePath) => {
238
+ if (!fs.existsSync(filePath)) {
239
+ return {}
240
+ }
241
+
242
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf8'))
243
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
244
+ throw new Error(`${filePath} must contain a JSON object`)
245
+ }
246
+
247
+ return parsed
248
+ }
249
+
250
+ const ensureObjectField = (object, field, filePath) => {
251
+ const value = object[field]
252
+
253
+ if (!value) {
254
+ object[field] = {}
255
+ return object[field]
256
+ }
257
+
258
+ if (typeof value !== 'object' || Array.isArray(value)) {
259
+ throw new Error(`${filePath} field "${field}" must be a JSON object`)
260
+ }
261
+
262
+ return value
263
+ }
264
+
265
+ const writeJson = (filePath, value, dryRun) => {
266
+ if (dryRun) {
267
+ return
268
+ }
269
+
270
+ fs.mkdirSync(path.dirname(filePath), { recursive: true })
271
+ fs.writeFileSync(filePath, `${JSON.stringify(value, null, 2)}${DEFAULT_NEWLINE}`, 'utf8')
272
+ }
273
+
274
+ const initAgents = (target, force) => {
275
+ if (!fs.existsSync(target)) {
276
+ throw new Error(`Target path does not exist: ${target}`)
277
+ }
278
+
279
+ const stat = fs.statSync(target)
280
+
281
+ if (!stat.isDirectory()) {
282
+ throw new Error(`Target path is not a directory: ${target}`)
283
+ }
284
+
285
+ const agentsPath = path.join(target, 'AGENTS.md')
286
+ const section = createAgentsSection()
287
+
288
+ if (!fs.existsSync(agentsPath)) {
289
+ fs.writeFileSync(agentsPath, createAgentsTemplate(), 'utf8')
290
+
291
+ console.log(`AGENTS.md was created at ${agentsPath}`)
292
+ console.log('Next step: review it and adjust project-specific rules if needed.')
293
+ return
294
+ }
295
+
296
+ const currentContent = fs.readFileSync(agentsPath, 'utf8')
297
+
298
+ if (force) {
299
+ fs.writeFileSync(agentsPath, replaceSection(currentContent, section), 'utf8')
300
+ console.log(`AGENTS.md was updated at ${agentsPath}`)
301
+ console.log(`The ${PACKAGE_NAME} section was refreshed.`)
302
+ return
303
+ }
304
+
305
+ if (hasPackageSection(currentContent)) {
306
+ console.log(`AGENTS.md already contains a ${PACKAGE_NAME} section at ${agentsPath}`)
307
+ console.log('Nothing was changed. Re-run with --force to refresh that section.')
308
+ return
309
+ }
310
+
311
+ fs.writeFileSync(agentsPath, appendSection(currentContent, section), 'utf8')
312
+
313
+ console.log(`AGENTS.md was updated at ${agentsPath}`)
314
+ console.log(`The ${PACKAGE_NAME} instructions were appended to the end of the file.`)
315
+ }
316
+
317
+ const writeMcpServerConfig = (target, relativePath, rootField, options) => {
318
+ const filePath = path.join(target, relativePath)
319
+ const fileExists = fs.existsSync(filePath)
320
+ const config = readJsonObject(filePath)
321
+ const servers = ensureObjectField(config, rootField, filePath)
322
+
323
+ if (servers[MCP_SERVER_NAME] && !options.force) {
324
+ console.log(`${relativePath} already contains ${MCP_SERVER_NAME}`)
325
+ console.log('Nothing was changed. Re-run with --force to refresh that server entry.')
326
+ return
327
+ }
328
+
329
+ servers[MCP_SERVER_NAME] = MCP_SERVER_CONFIG
330
+ writeJson(filePath, config, options.dryRun)
331
+
332
+ const action = fileExists ? 'updated' : 'created'
333
+ console.log(`${relativePath} ${options.dryRun ? `would be ${action}` : `was ${action}`}`)
334
+ }
335
+
336
+ const resolveMcpClientConfigs = (tokens) => {
337
+ for (const token of tokens) {
338
+ if (!(token in MCP_CLIENT_CONFIGS)) {
339
+ throw new Error(`Unknown MCP client config: ${token}`)
340
+ }
341
+ }
342
+
343
+ return tokens
344
+ }
345
+
346
+ const updateMcpReadmeNotes = (target, clientConfigs, options) => {
347
+ const readmePath = path.join(target, 'README.md')
348
+ const fileExists = fs.existsSync(readmePath)
349
+ const currentContent = fileExists
350
+ ? fs.readFileSync(readmePath, 'utf8')
351
+ : '# README.md\n'
352
+ const section = createMcpReadmeSection(clientConfigs)
353
+
354
+ if (currentContent.includes(README_MCP_MARKER) && !options.force) {
355
+ console.log(`README.md already contains MCP setup notes at ${readmePath}`)
356
+ console.log('Nothing was changed. Re-run with --force to refresh that section.')
357
+ return
358
+ }
359
+
360
+ const nextContent = replaceReadmeMcpSection(currentContent, section)
361
+
362
+ if (!options.dryRun) {
363
+ fs.writeFileSync(readmePath, nextContent, 'utf8')
364
+ }
365
+
366
+ const action = fileExists ? 'updated' : 'created'
367
+ console.log(`README.md ${options.dryRun ? `would be ${action}` : `was ${action}`} with MCP setup notes`)
368
+ }
369
+
370
+ const initConfig = (target, options) => {
371
+ if (!fs.existsSync(target)) {
372
+ throw new Error(`Target path does not exist: ${target}`)
373
+ }
374
+
375
+ const stat = fs.statSync(target)
376
+
377
+ if (!stat.isDirectory()) {
378
+ throw new Error(`Target path is not a directory: ${target}`)
379
+ }
380
+
381
+ const clientConfigs = resolveMcpClientConfigs(options.mcpClientConfigs)
382
+
383
+ writeMcpServerConfig(target, '.mcp.json', 'mcpServers', options)
384
+
385
+ for (const clientConfig of clientConfigs) {
386
+ const config = MCP_CLIENT_CONFIGS[clientConfig]
387
+ writeMcpServerConfig(target, config.filePath, config.rootField, options)
388
+ }
389
+
390
+ updateMcpReadmeNotes(target, clientConfigs, options)
391
+ }
392
+
393
+ const main = () => {
394
+ try {
395
+ const options = parseArgs(process.argv.slice(2))
396
+
397
+ if (options.command === 'init-agents') {
398
+ initAgents(options.target, options.force)
399
+ return
400
+ }
401
+
402
+ if (options.command === 'init-config') {
403
+ initConfig(options.target, options)
404
+ return
405
+ }
406
+
407
+ throw new Error(`Unknown command: ${options.command}`)
408
+ } catch (error) {
409
+ console.error(error instanceof Error ? error.message : String(error))
410
+ console.error('')
411
+ console.error(HELP_TEXT)
412
+ process.exit(1)
413
+ }
414
+ }
415
+
416
+ main()