@retailcrm/embed-ui 0.9.23 → 0.9.25
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 +13 -0
- package/README.md +22 -4
- package/bin/embed-ui.mjs +219 -23
- package/package.json +6 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [0.9.25](https://github.com/retailcrm/embed-ui/compare/v0.9.24...v0.9.25) (2026-05-28)
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* Project skill initialization added ([f04caef](https://github.com/retailcrm/embed-ui/commit/f04caef7e858b3f2089cb80c9ef26daafce6c2b5))
|
|
9
|
+
* **v1-components:** Project skill installer added ([8b7c61c](https://github.com/retailcrm/embed-ui/commit/8b7c61cac3704ad80582a44609e5aabba31757df))
|
|
10
|
+
* **v1-contexts:** Context usage skill installer added ([b9545d6](https://github.com/retailcrm/embed-ui/commit/b9545d624e6062c136ea55c16c9220ab9d6e1b8a))
|
|
11
|
+
* **v1-endpoint:** Runtime skill installer added ([f426dac](https://github.com/retailcrm/embed-ui/commit/f426dac22766002dc3df01188608f7a0f1fe6689))
|
|
12
|
+
## [0.9.24](https://github.com/retailcrm/embed-ui/compare/v0.9.23...v0.9.24) (2026-05-26)
|
|
13
|
+
|
|
14
|
+
### Bug Fixes
|
|
15
|
+
|
|
16
|
+
* Page manifest descriptors generated for publish ([6c6685e](https://github.com/retailcrm/embed-ui/commit/6c6685e9c52a3437febddf4fdd32b13cb01de5af))
|
|
4
17
|
## [0.9.23](https://github.com/retailcrm/embed-ui/compare/v0.9.23-alpha.3...v0.9.23) (2026-05-23)
|
|
5
18
|
## [0.9.23-alpha.3](https://github.com/retailcrm/embed-ui/compare/v0.9.23-alpha.2...v0.9.23-alpha.3) (2026-05-23)
|
|
6
19
|
|
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ npx @retailcrm/embed-ui --help
|
|
|
41
41
|
- создает или дополняет `.gitignore` типовыми правилами для `node_modules`, `dist`, `coverage`, `.env` и логов;
|
|
42
42
|
- создает стартовые файлы во frontend-каталоге: endpoint worker, страницу настроек, виджет заказа, i18n JSON-сообщения;
|
|
43
43
|
- добавляет `extensionrc.json` и `scripts/publish-extension.mjs`;
|
|
44
|
-
- добавляет `AGENTS.md
|
|
44
|
+
- добавляет `AGENTS.md`, project-level skills и MCP-настройки пакетов, если они не отключены флагами;
|
|
45
45
|
- может инициализировать Git-репозиторий, если включить `--git` или выбрать это действие в интерактивном режиме.
|
|
46
46
|
|
|
47
47
|
```bash
|
|
@@ -60,6 +60,7 @@ npx @retailcrm/embed-ui init ./web --package-manager yarn
|
|
|
60
60
|
- шаблон — создается стартовая конфигурация для страницы настроек и виджета `order/card:common.after`;
|
|
61
61
|
- каталоги — создаются `endpoint`, `pages`, `widgets`, `shared`, `i18n` и `i18n/locales` внутри frontend-каталога;
|
|
62
62
|
- `AGENTS.md` — создается или дополняется инструкциями корневого пакета и выбранных пакетов;
|
|
63
|
+
- project-level skills — создаются в `.agents/skills/*` для выбранных пакетов;
|
|
63
64
|
- MCP — добавляется `.mcp.json` для выбранных пакетов с MCP (`v1-contexts` и `v1-endpoint`);
|
|
64
65
|
- project-level client configs для Codex CLI, Cursor, Junie и VS Code — не создаются без `--mcp-client-configs`;
|
|
65
66
|
- Git — в неинтерактивном режиме не инициализируется без `--git`, а в интерактивном режиме предлагается, если корень проекта не является Git-репозиторием;
|
|
@@ -88,6 +89,9 @@ npx @retailcrm/embed-ui init ./web --cwd ./my-project --package-manager npm
|
|
|
88
89
|
- `--no-configs` — не создавать `tsconfig.json`, `vite.config.ts`, `eslint.config.js` и `env.d.ts`.
|
|
89
90
|
- `--no-template` — не создавать стартовые Vue-файлы и `extensionrc.json`.
|
|
90
91
|
- `--no-agents` — не создавать и не дополнять `AGENTS.md`.
|
|
92
|
+
- `--no-skills` — не создавать и не обновлять project-level skills в `.agents/skills/*`.
|
|
93
|
+
- `--force-skills` — обновить уже существующие управляемые project-level skills.
|
|
94
|
+
- `--skills-only` — установить только project-level skills, без изменения зависимостей, шаблонов и `AGENTS.md`.
|
|
91
95
|
- `--no-mcp` — не добавлять MCP-настройки пакетов.
|
|
92
96
|
- `--mcp-client-configs codex,cursor,junie,vscode` — дополнительно создать project-level конфиги поддерживаемых AI-клиентов.
|
|
93
97
|
- `--git` — выполнить `git init` в корне проекта, если каталог еще не является Git-репозиторием.
|
|
@@ -99,14 +103,28 @@ npx @retailcrm/embed-ui init ./web --cwd ./my-project --package-manager npm
|
|
|
99
103
|
npx @retailcrm/embed-ui init ./web --force --no-install --no-template
|
|
100
104
|
```
|
|
101
105
|
|
|
102
|
-
### AGENTS.md И MCP
|
|
106
|
+
### AGENTS.md, Skills И MCP
|
|
103
107
|
|
|
104
|
-
При `init` CLI добавляет общий раздел в `AGENTS.md`,
|
|
108
|
+
При `init` CLI добавляет общий раздел в `AGENTS.md`, project-level skills в `.agents/skills/*`, а пакеты могут
|
|
109
|
+
добавить свои инструкции. Сейчас это используют:
|
|
105
110
|
|
|
106
111
|
- `@retailcrm/embed-ui-v1-components` — добавляет порядок чтения README, AI-документации и YAML-профилей компонентов.
|
|
107
|
-
- `@retailcrm/embed-ui-v1-contexts` — добавляет
|
|
112
|
+
- `@retailcrm/embed-ui-v1-contexts` — добавляет workflow применения контекстов, actions, custom contexts и MCP-ресурсов.
|
|
108
113
|
- `@retailcrm/embed-ui-v1-endpoint` — добавляет инструкции по целям виджетов и MCP-ресурсам.
|
|
109
114
|
|
|
115
|
+
Project-level skills дополняют `AGENTS.md`: `AGENTS.md` задает базовые правила проекта, а skills описывают
|
|
116
|
+
повторяемые процедуры для конкретных задач. Чтобы установить или обновить только skills, используйте:
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npx @retailcrm/embed-ui init-skills
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Или в рамках init:
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
npx @retailcrm/embed-ui init ./web --skills-only
|
|
126
|
+
```
|
|
127
|
+
|
|
110
128
|
Для `v1-contexts` и `v1-endpoint`, если эти пакеты выбраны для установки, также создается `.mcp.json` с серверами
|
|
111
129
|
`retailcrm-embed-ui-v1-contexts` и `retailcrm-embed-ui-v1-endpoint`. Этот файл рассчитан на Claude Code project scope
|
|
112
130
|
и использует `${CLAUDE_PROJECT_DIR:-.}/node_modules/.bin/...`, чтобы серверы резолвились относительно открытого проекта.
|
package/bin/embed-ui.mjs
CHANGED
|
@@ -16,6 +16,7 @@ const PACKAGE_MANAGERS = ["yarn", "npm", "pnpm", "bun"];
|
|
|
16
16
|
const HELP_TEXT = `Usage:
|
|
17
17
|
npx @retailcrm/embed-ui [target] [version] [options]
|
|
18
18
|
npx @retailcrm/embed-ui init [target] [options]
|
|
19
|
+
npx @retailcrm/embed-ui init-skills [target] [options]
|
|
19
20
|
|
|
20
21
|
Options:
|
|
21
22
|
-t, --target <path> Target path (default: current directory)
|
|
@@ -33,6 +34,8 @@ Options:
|
|
|
33
34
|
--force-deps Replace incompatible existing init dependencies
|
|
34
35
|
--fix-sections Move init dependencies to expected package.json sections
|
|
35
36
|
--no-agents Do not create or update AGENTS.md in init mode
|
|
37
|
+
--no-skills Do not create or update .agents/skills in init mode
|
|
38
|
+
--skills-only Install only project-level skills
|
|
36
39
|
--no-mcp Do not add package MCP instructions in init mode
|
|
37
40
|
--mcp-client-configs Comma-separated project-level MCP client configs to create (codex,cursor,junie,vscode)
|
|
38
41
|
--git Initialize Git repository in init mode when cwd is not a Git work tree
|
|
@@ -47,6 +50,7 @@ Examples:
|
|
|
47
50
|
npx @retailcrm/embed-ui --add --packages components,contexts
|
|
48
51
|
npx @retailcrm/embed-ui init ./web --package-manager yarn
|
|
49
52
|
npx @retailcrm/embed-ui init --interactive
|
|
53
|
+
npx @retailcrm/embed-ui init-skills
|
|
50
54
|
`;
|
|
51
55
|
const isSemverLike = (value) => /^v?\d+\.\d+\.\d+/.test(value);
|
|
52
56
|
const stripLeadingV = (value) => value.replace(/^v/, "");
|
|
@@ -111,7 +115,7 @@ const parseInitArgs = (argv) => {
|
|
|
111
115
|
type: "string",
|
|
112
116
|
default: "order/card:common.after",
|
|
113
117
|
describe: "Starter widget target"
|
|
114
|
-
}).option("dry-run", { type: "boolean", default: false }).option("exact", { type: "boolean", default: false }).option("interactive", { type: "boolean", default: false }).option("verbose", { type: "boolean", default: false }).option("install", { type: "boolean", default: true }).option("force", { type: "boolean", default: false }).option("force-deps", { type: "boolean", default: false }).option("fix-sections", { type: "boolean", default: false }).option("force-files", { type: "boolean", default: false }).option("configs", { type: "boolean", default: true }).option("dirs-enabled", { type: "boolean", default: true }).option("template-enabled", { type: "boolean", default: true }).option("agents", { type: "boolean", default: true }).option("force-agents", { type: "boolean", default: false }).option("agents-only", { type: "boolean", default: false }).option("mcp", { type: "boolean", default: true }).option("force-mcp", { type: "boolean", default: false }).option("mcp-client-configs", {
|
|
118
|
+
}).option("dry-run", { type: "boolean", default: false }).option("exact", { type: "boolean", default: false }).option("interactive", { type: "boolean", default: false }).option("verbose", { type: "boolean", default: false }).option("install", { type: "boolean", default: true }).option("force", { type: "boolean", default: false }).option("force-deps", { type: "boolean", default: false }).option("fix-sections", { type: "boolean", default: false }).option("force-files", { type: "boolean", default: false }).option("configs", { type: "boolean", default: true }).option("dirs-enabled", { type: "boolean", default: true }).option("template-enabled", { type: "boolean", default: true }).option("agents", { type: "boolean", default: true }).option("force-agents", { type: "boolean", default: false }).option("agents-only", { type: "boolean", default: false }).option("skills", { type: "boolean", default: true }).option("force-skills", { type: "boolean", default: false }).option("skills-only", { type: "boolean", default: false }).option("mcp", { type: "boolean", default: true }).option("force-mcp", { type: "boolean", default: false }).option("mcp-client-configs", {
|
|
115
119
|
type: "string",
|
|
116
120
|
coerce: parsePackageList,
|
|
117
121
|
describe: "Comma-separated MCP client config ids"
|
|
@@ -156,10 +160,13 @@ const parseInitArgs = (argv) => {
|
|
|
156
160
|
template: parsed.template,
|
|
157
161
|
pageCode: parsed.pageCode,
|
|
158
162
|
widgetTarget: parsed.widgetTarget,
|
|
159
|
-
noAgents: !parsed.agents,
|
|
163
|
+
noAgents: parsed.skillsOnly ? true : !parsed.agents,
|
|
160
164
|
forceAgents: parsed.forceAgents,
|
|
161
165
|
agentsOnly: parsed.agentsOnly,
|
|
162
|
-
|
|
166
|
+
noSkills: parsed.agentsOnly ? true : !parsed.skills,
|
|
167
|
+
forceSkills: parsed.forceSkills,
|
|
168
|
+
skillsOnly: parsed.skillsOnly,
|
|
169
|
+
noMcp: parsed.skillsOnly ? true : !parsed.mcp,
|
|
163
170
|
forceMcp: parsed.forceMcp,
|
|
164
171
|
mcpClientConfigs: parsed.mcpClientConfigs ?? null,
|
|
165
172
|
initGit: parsed.git
|
|
@@ -169,6 +176,23 @@ const parseArgs = (argv) => {
|
|
|
169
176
|
if (argv[0] === "init") {
|
|
170
177
|
return parseInitArgs(argv.slice(1));
|
|
171
178
|
}
|
|
179
|
+
if (argv[0] === "init-skills") {
|
|
180
|
+
const options2 = parseInitArgs(argv.slice(1));
|
|
181
|
+
const hasExplicitCwd = argv.slice(1).some((argument) => argument === "--cwd" || argument.startsWith("--cwd="));
|
|
182
|
+
return {
|
|
183
|
+
...options2,
|
|
184
|
+
cwd: options2.target && !hasExplicitCwd ? path.resolve(process$2.cwd(), options2.target) : options2.cwd,
|
|
185
|
+
target: null,
|
|
186
|
+
noInstall: true,
|
|
187
|
+
noConfigs: true,
|
|
188
|
+
noDirs: true,
|
|
189
|
+
noTemplate: true,
|
|
190
|
+
noAgents: true,
|
|
191
|
+
noSkills: false,
|
|
192
|
+
skillsOnly: true,
|
|
193
|
+
noMcp: true
|
|
194
|
+
};
|
|
195
|
+
}
|
|
172
196
|
const parsed = yargs(argv).scriptName("embed-ui").usage("Usage: $0 [target] [version] [options]").help(false).version(false).exitProcess(false).strictOptions().option("target", {
|
|
173
197
|
alias: "t",
|
|
174
198
|
type: "string",
|
|
@@ -246,6 +270,12 @@ const INSTALLABLE_PACKAGES = [
|
|
|
246
270
|
binName: "embed-ui-v1-components",
|
|
247
271
|
command: "init-agents",
|
|
248
272
|
failureMode: "advisory"
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
type: "skills",
|
|
276
|
+
binName: "embed-ui-v1-components",
|
|
277
|
+
command: "init-skills",
|
|
278
|
+
failureMode: "advisory"
|
|
249
279
|
}
|
|
250
280
|
]
|
|
251
281
|
},
|
|
@@ -262,6 +292,12 @@ const INSTALLABLE_PACKAGES = [
|
|
|
262
292
|
failureMode: "advisory",
|
|
263
293
|
requiresMcp: true
|
|
264
294
|
},
|
|
295
|
+
{
|
|
296
|
+
type: "skills",
|
|
297
|
+
binName: "embed-ui-v1-contexts",
|
|
298
|
+
command: "init-skills",
|
|
299
|
+
failureMode: "advisory"
|
|
300
|
+
},
|
|
265
301
|
{
|
|
266
302
|
type: "config",
|
|
267
303
|
binName: "embed-ui-v1-contexts",
|
|
@@ -296,6 +332,12 @@ const INSTALLABLE_PACKAGES = [
|
|
|
296
332
|
failureMode: "advisory",
|
|
297
333
|
requiresMcp: true
|
|
298
334
|
},
|
|
335
|
+
{
|
|
336
|
+
type: "skills",
|
|
337
|
+
binName: "embed-ui-v1-endpoint",
|
|
338
|
+
command: "init-skills",
|
|
339
|
+
failureMode: "advisory"
|
|
340
|
+
},
|
|
299
341
|
{
|
|
300
342
|
type: "config",
|
|
301
343
|
binName: "embed-ui-v1-endpoint",
|
|
@@ -815,6 +857,9 @@ const appendMcpHookNotices = (output, changes) => {
|
|
|
815
857
|
if (trimmedLine.startsWith("MCP: ")) {
|
|
816
858
|
changes.mcp.push(trimmedLine.slice("MCP: ".length));
|
|
817
859
|
}
|
|
860
|
+
if (trimmedLine.startsWith("SKILL: ")) {
|
|
861
|
+
changes.skills.push(trimmedLine.slice("SKILL: ".length));
|
|
862
|
+
}
|
|
818
863
|
}
|
|
819
864
|
};
|
|
820
865
|
const runPackageHookCommand = async (cwd, packageName, binName, packageManager, args, failureMode, options, changes) => {
|
|
@@ -925,6 +970,9 @@ const applyInitAgents = async (cwd, selectedPackages, packageManager, options, c
|
|
|
925
970
|
}
|
|
926
971
|
};
|
|
927
972
|
const applyInitPackageConfigHooks = async (cwd, selectedPackages, packageManager, options, changes) => {
|
|
973
|
+
if (options.agentsOnly || options.skillsOnly) {
|
|
974
|
+
return;
|
|
975
|
+
}
|
|
928
976
|
for (const selectedPackage of selectedPackages) {
|
|
929
977
|
for (const hook of selectedPackage.hooks ?? []) {
|
|
930
978
|
if (hook.type !== "config") {
|
|
@@ -1205,8 +1253,8 @@ const analyzeTemplateFileSkips = (cwd, sourceRoot, options, changes) => {
|
|
|
1205
1253
|
}
|
|
1206
1254
|
};
|
|
1207
1255
|
const applyInitPreflight = (cwd, sourceRoot, packageManager, selectedPackages, version, options, changes) => {
|
|
1208
|
-
if (options.agentsOnly) {
|
|
1209
|
-
changes.preflight.push("agents-only mode: package.json, configs, and template files are skipped
|
|
1256
|
+
if (options.agentsOnly || options.skillsOnly) {
|
|
1257
|
+
changes.preflight.push(`${options.skillsOnly ? "skills-only" : "agents-only"} mode: package.json, configs, and template files are skipped`);
|
|
1210
1258
|
return;
|
|
1211
1259
|
}
|
|
1212
1260
|
changes.preflight.push(`source root: ${path.relative(cwd, sourceRoot) || "."}`);
|
|
@@ -1252,6 +1300,43 @@ const applyInitPreflight = (cwd, sourceRoot, packageManager, selectedPackages, v
|
|
|
1252
1300
|
analyzePackageJson(packageJson, selectedPackages, version, options, changes);
|
|
1253
1301
|
}
|
|
1254
1302
|
};
|
|
1303
|
+
const runInitSkillsHook = async (packageName, binName, cwd, packageManager, failureMode, options, changes) => {
|
|
1304
|
+
const args = ["init-skills", cwd];
|
|
1305
|
+
if (options.force || options.forceSkills) {
|
|
1306
|
+
args.push("--force");
|
|
1307
|
+
}
|
|
1308
|
+
await runPackageHookCommand(
|
|
1309
|
+
cwd,
|
|
1310
|
+
packageName,
|
|
1311
|
+
binName,
|
|
1312
|
+
packageManager,
|
|
1313
|
+
args,
|
|
1314
|
+
failureMode,
|
|
1315
|
+
options,
|
|
1316
|
+
changes
|
|
1317
|
+
);
|
|
1318
|
+
};
|
|
1319
|
+
const applyInitSkills = async (cwd, selectedPackages, packageManager, options, changes) => {
|
|
1320
|
+
if (options.noSkills) {
|
|
1321
|
+
return;
|
|
1322
|
+
}
|
|
1323
|
+
for (const selectedPackage of selectedPackages) {
|
|
1324
|
+
for (const hook of selectedPackage.hooks ?? []) {
|
|
1325
|
+
if (hook.type !== "skills") {
|
|
1326
|
+
continue;
|
|
1327
|
+
}
|
|
1328
|
+
await runInitSkillsHook(
|
|
1329
|
+
selectedPackage.name,
|
|
1330
|
+
hook.binName,
|
|
1331
|
+
cwd,
|
|
1332
|
+
packageManager,
|
|
1333
|
+
hook.failureMode,
|
|
1334
|
+
options,
|
|
1335
|
+
changes
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
};
|
|
1255
1340
|
const SKIP_DIRECTORIES = /* @__PURE__ */ new Set([
|
|
1256
1341
|
".git",
|
|
1257
1342
|
".hg",
|
|
@@ -1696,6 +1781,86 @@ const assertStringArray = (value, field) => {
|
|
|
1696
1781
|
return value
|
|
1697
1782
|
}
|
|
1698
1783
|
|
|
1784
|
+
const assertOptionalNonEmptyString = (value, field) => {
|
|
1785
|
+
if (value === undefined || value === null) {
|
|
1786
|
+
return null
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
return assertNonEmptyString(value, field)
|
|
1790
|
+
}
|
|
1791
|
+
|
|
1792
|
+
const assertOptionalNumber = (value, field) => {
|
|
1793
|
+
if (value === undefined || value === null) {
|
|
1794
|
+
return null
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1797
|
+
if (typeof value !== 'number' || !Number.isFinite(value)) {
|
|
1798
|
+
throw new Error('Field "' + field + '" must be a finite number')
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
return value
|
|
1802
|
+
}
|
|
1803
|
+
|
|
1804
|
+
const assertOptionalBoolean = (value, field) => {
|
|
1805
|
+
if (value === undefined || value === null) {
|
|
1806
|
+
return false
|
|
1807
|
+
}
|
|
1808
|
+
|
|
1809
|
+
if (typeof value !== 'boolean') {
|
|
1810
|
+
throw new Error('Field "' + field + '" must be a boolean')
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
return value
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
const assertTranslation = (value, field, { nullable = false } = {}) => {
|
|
1817
|
+
if (value === undefined || value === null) {
|
|
1818
|
+
return nullable ? null : undefined
|
|
1819
|
+
}
|
|
1820
|
+
|
|
1821
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
1822
|
+
throw new Error('Field "' + field + '" must be an object with ru, en and es strings')
|
|
1823
|
+
}
|
|
1824
|
+
|
|
1825
|
+
return {
|
|
1826
|
+
ru: assertNonEmptyString(value.ru, field + '.ru'),
|
|
1827
|
+
en: assertNonEmptyString(value.en, field + '.en'),
|
|
1828
|
+
es: assertNonEmptyString(value.es, field + '.es'),
|
|
1829
|
+
}
|
|
1830
|
+
}
|
|
1831
|
+
|
|
1832
|
+
const normalizePages = (value) => {
|
|
1833
|
+
if (value === undefined) {
|
|
1834
|
+
return []
|
|
1835
|
+
}
|
|
1836
|
+
|
|
1837
|
+
if (!Array.isArray(value)) {
|
|
1838
|
+
throw new Error('Field "pages" must be an array of page descriptor objects')
|
|
1839
|
+
}
|
|
1840
|
+
|
|
1841
|
+
return value.map((page, index) => {
|
|
1842
|
+
const field = 'pages[' + index + ']'
|
|
1843
|
+
|
|
1844
|
+
if (typeof page === 'string') {
|
|
1845
|
+
throw new Error('Field "' + field + '" uses deprecated string page form. Use { "code": "' + page + '", "menu": "...", "menuItemTitle": { "ru": "...", "en": "...", "es": "..." } }')
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
if (!page || typeof page !== 'object' || Array.isArray(page)) {
|
|
1849
|
+
throw new Error('Field "' + field + '" must be a page descriptor object')
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
return {
|
|
1853
|
+
code: assertNonEmptyString(page.code, field + '.code'),
|
|
1854
|
+
menu: assertNonEmptyString(page.menu, field + '.menu'),
|
|
1855
|
+
parentMenuItemCode: assertOptionalNonEmptyString(page.parentMenuItemCode, field + '.parentMenuItemCode'),
|
|
1856
|
+
menuItemOrdering: assertOptionalNumber(page.menuItemOrdering, field + '.menuItemOrdering'),
|
|
1857
|
+
menuItemTitle: assertTranslation(page.menuItemTitle, field + '.menuItemTitle', { nullable: true }) ?? null,
|
|
1858
|
+
pageHelpLink: assertTranslation(page.pageHelpLink, field + '.pageHelpLink', { nullable: true }),
|
|
1859
|
+
isSettingsMainPage: assertOptionalBoolean(page.isSettingsMainPage, field + '.isSettingsMainPage'),
|
|
1860
|
+
}
|
|
1861
|
+
})
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1699
1864
|
const listFiles = (directoryPath, basePath = directoryPath) => {
|
|
1700
1865
|
const result = []
|
|
1701
1866
|
|
|
@@ -1803,7 +1968,7 @@ try {
|
|
|
1803
1968
|
const uuid = assertNonEmptyString(config.uuid, 'uuid')
|
|
1804
1969
|
const version = assertNonEmptyString(config.version, 'version')
|
|
1805
1970
|
const targets = assertStringArray(config.targets, 'targets')
|
|
1806
|
-
const pages =
|
|
1971
|
+
const pages = normalizePages(config.pages)
|
|
1807
1972
|
const runner = config.runner ?? 'worker'
|
|
1808
1973
|
|
|
1809
1974
|
if (targets.length === 0 && pages.length === 0) {
|
|
@@ -1918,9 +2083,9 @@ try {
|
|
|
1918
2083
|
process.exit(1)
|
|
1919
2084
|
}
|
|
1920
2085
|
`;
|
|
1921
|
-
const readmeEnGBTemplate = "# RetailCRM extension frontend\n\nThis project was generated by `embed-ui init`.\n\n## What Was Added\n\n- `package.json` with scripts for Vite build, ESLint, and extension publishing.\n- `extensionrc.json` with the generated extension manifest source.\n- `__SOURCE_ROOT__/endpoint/endpoint.worker.ts` with `defineRunner`, one page runner, and one widget runner.\n- `__SOURCE_ROOT__/pages/SettingsPage.vue` as a starter settings page.\n- `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue` as a starter order widget.\n- `__SOURCE_ROOT__/i18n/` with shared JSON message files.\n- `scripts/publish-extension.mjs` for creating `dist/extension.zip` and publishing the integration module through RetailCRM API.\n- `AGENTS.md` when agent instructions were enabled during init.\n\n## Replace Generic Values\n\nReview these generated placeholders before using the project in a real integration:\n\n- Extension code in `extensionrc.json`: `retailcrm-extension-frontend`.\n- Extension name in `extensionrc.json`: `RetailCRM Extension Frontend`.\n- Page code: `__PAGE_CODE__`.\n- Widget target: `__WIDGET_TARGET__`.\n- Sample controls and fake data in `__SOURCE_ROOT__/pages/SettingsPage.vue`.\n- Sample toolbar actions and fake order data in `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue`.\n- Shared messages in `__SOURCE_ROOT__/i18n/locales/*.json`.\n\nThe generated page and widget are intentionally generic. Keep the structure you need, but replace the sample labels, fields, and fake data with real product behavior.\n\n## Vue File Names\n\n`SettingsPage.vue` and `OrderCommonAfterWidget.vue` are generic starter names. In product code, rename Vue files after the role they play in the extension, and update imports in `__SOURCE_ROOT__/endpoint/endpoint.worker.ts`.\n\nExamples from RetailCRM extension examples:\n\n- `ReturnsPage.vue` is a full returns-management page.\n- `TasksPage.vue` is a task list/workspace page.\n- `SummaryPage.vue` is a summary dashboard page.\n- `RecordToCalendlyWidget.vue` is a focused widget for one scenario.\n\nUse the same idea for your code: `LoyaltySettingsPage.vue`, `OrderNotesWidget.vue`, `PaymentStatusSidebar.vue`, or another name that describes the real scenario.\n\n## Commands\n\n```bash\n__PACKAGE_MANAGER__ install\n__PACKAGE_MANAGER_RUN__ eslint\n__PACKAGE_MANAGER_RUN__ build\n__PACKAGE_MANAGER_RUN__ publish-extension -- --archive-only\n```\n\n## Publishing\n\nCreate `.env` in the project root when you want `publish-extension` to update RetailCRM:\n\n```dotenv\nCRM_API_HOST=https://example.retailcrm.pro\nCRM_API_KEY=your-api-key\nMODULE_URL=https://example.com\n```\n\nRun `__PACKAGE_MANAGER_RUN__ build` before publishing. The archive-only mode creates `dist/extension.zip` without sending API requests.\n";
|
|
1922
|
-
const readmeEsESTemplate = "# Frontend de extension RetailCRM\n\nEste proyecto fue generado por `embed-ui init`.\n\n## Que Se Agrego\n\n- `package.json` con scripts para Vite build, ESLint y publicacion de la extension.\n- `extensionrc.json` con la fuente del manifiesto de la extension.\n- `__SOURCE_ROOT__/endpoint/endpoint.worker.ts` con `defineRunner`, un runner de pagina y un runner de widget.\n- `__SOURCE_ROOT__/pages/SettingsPage.vue` como pagina inicial de configuracion.\n- `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue` como widget inicial del pedido.\n- `__SOURCE_ROOT__/i18n/` con archivos JSON de mensajes compartidos.\n- `scripts/publish-extension.mjs` para crear `dist/extension.zip` y publicar el modulo de integracion por RetailCRM API.\n- `AGENTS.md` si las instrucciones para agentes estaban activadas durante init.\n\n## Sustituya Los Valores Genericos\n\nRevise estos valores generados antes de usar el proyecto en una integracion real:\n\n- Codigo de extension en `extensionrc.json`: `retailcrm-extension-frontend`.\n- Nombre de extension en `extensionrc.json`: `RetailCRM Extension Frontend`.\n- Codigo de pagina: `__PAGE_CODE__`.\n- Target del widget: `__WIDGET_TARGET__`.\n- Controles de ejemplo y datos ficticios en `__SOURCE_ROOT__/pages/SettingsPage.vue`.\n- Acciones toolbar de ejemplo y datos ficticios del pedido en `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue`.\n- Mensajes compartidos en `__SOURCE_ROOT__/i18n/locales/*.json`.\n\nLa pagina y el widget generados son intencionalmente genericos. Mantenga la estructura que necesite, pero sustituya etiquetas, campos y datos ficticios por comportamiento real del producto.\n\n## Nombres De Archivos Vue\n\n`SettingsPage.vue` y `OrderCommonAfterWidget.vue` son nombres iniciales genericos. En codigo de producto, renombre los archivos Vue segun la funcion que cumplen en la extension y actualice los imports en `__SOURCE_ROOT__/endpoint/endpoint.worker.ts`.\n\nEjemplos del repositorio de extensiones RetailCRM:\n\n- `ReturnsPage.vue` es una pagina completa de gestion de devoluciones.\n- `TasksPage.vue` es una pagina de lista o espacio de trabajo de tareas.\n- `SummaryPage.vue` es una pagina de resumen o dashboard.\n- `RecordToCalendlyWidget.vue` es un widget enfocado en un escenario.\n\nUse la misma idea para su codigo: `LoyaltySettingsPage.vue`, `OrderNotesWidget.vue`, `PaymentStatusSidebar.vue` u otro nombre que describa el escenario real.\n\n## Comandos\n\n```bash\n__PACKAGE_MANAGER__ install\n__PACKAGE_MANAGER_RUN__ eslint\n__PACKAGE_MANAGER_RUN__ build\n__PACKAGE_MANAGER_RUN__ publish-extension -- --archive-only\n```\n\n## Publicacion\n\nCree `.env` en la raiz del proyecto cuando quiera que `publish-extension` actualice RetailCRM:\n\n```dotenv\nCRM_API_HOST=https://example.retailcrm.pro\nCRM_API_KEY=your-api-key\nMODULE_URL=https://example.com\n```\n\nEjecute `__PACKAGE_MANAGER_RUN__ build` antes de publicar. El modo archive-only crea `dist/extension.zip` sin enviar peticiones API.\n";
|
|
1923
|
-
const readmeRuRUTemplate = "# Фронтенд расширения RetailCRM\n\nПроект создан командой `embed-ui init`.\n\n## Что Добавлено\n\n- `package.json` со скриптами для сборки Vite, ESLint и публикации расширения.\n- `extensionrc.json` с исходным описанием манифеста расширения.\n- `__SOURCE_ROOT__/endpoint/endpoint.worker.ts` с `defineRunner`, одним runner страницы и одним runner виджета.\n- `__SOURCE_ROOT__/pages/SettingsPage.vue` как стартовая страница настроек.\n- `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue` как стартовый виджет заказа.\n- `__SOURCE_ROOT__/i18n/` с общими JSON-файлами переводов.\n- `scripts/publish-extension.mjs` для создания `dist/extension.zip` и публикации интеграционного модуля через RetailCRM API.\n- `AGENTS.md`, если при инициализации были включены инструкции для агентов.\n\n## Замените Generic Значения\n\nПеред использованием проекта в реальной интеграции проверьте сгенерированные общие значения:\n\n- Код расширения в `extensionrc.json`: `retailcrm-extension-frontend`.\n- Название расширения в `extensionrc.json`: `RetailCRM Extension Frontend`.\n- Код страницы: `__PAGE_CODE__`.\n- Цель виджета: `__WIDGET_TARGET__`.\n- Демонстрационные контролы и ненастоящие данные в `__SOURCE_ROOT__/pages/SettingsPage.vue`.\n- Демонстрационные toolbar-действия и ненастоящие данные заказа в `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue`.\n- Общие сообщения в `__SOURCE_ROOT__/i18n/locales/*.json`.\n\nСгенерированные страница и виджет намеренно сделаны универсальными. Оставьте нужную структуру, но замените примерные подписи, поля и ненастоящие данные на реальное поведение продукта.\n\n## Имена Vue-Файлов\n\n`SettingsPage.vue` и `OrderCommonAfterWidget.vue` — универсальные стартовые имена. В продуктовом коде переименуйте Vue-файлы по роли, которую они выполняют в расширении, и обновите импорты в `__SOURCE_ROOT__/endpoint/endpoint.worker.ts`.\n\nПримеры из репозитория расширений RetailCRM:\n\n- `ReturnsPage.vue` — полноценная страница управления возвратами.\n- `TasksPage.vue` — страница списка задач или рабочей области задач.\n- `SummaryPage.vue` — страница сводки или дашборда.\n- `RecordToCalendlyWidget.vue` — сфокусированный виджет под один сценарий.\n\nИспользуйте тот же принцип: `LoyaltySettingsPage.vue`, `OrderNotesWidget.vue`, `PaymentStatusSidebar.vue` или другое имя, которое описывает реальный сценарий.\n\n## Команды\n\n```bash\n__PACKAGE_MANAGER__ install\n__PACKAGE_MANAGER_RUN__ eslint\n__PACKAGE_MANAGER_RUN__ build\n__PACKAGE_MANAGER_RUN__ publish-extension -- --archive-only\n```\n\n## Публикация\n\nСоздайте `.env` в корне проекта, когда потребуется обновлять RetailCRM через `publish-extension`:\n\n```dotenv\nCRM_API_HOST=https://example.retailcrm.pro\nCRM_API_KEY=your-api-key\nMODULE_URL=https://example.com\n```\n\nПеред публикацией выполните `__PACKAGE_MANAGER_RUN__ build`. Режим archive-only создает `dist/extension.zip` без API-запросов.\n";
|
|
2086
|
+
const readmeEnGBTemplate = "# RetailCRM extension frontend\n\nThis project was generated by `embed-ui init`.\n\n## What Was Added\n\n- `package.json` with scripts for Vite build, ESLint, and extension publishing.\n- `extensionrc.json` with the generated extension manifest source.\n- `__SOURCE_ROOT__/endpoint/endpoint.worker.ts` with `defineRunner`, one page runner, and one widget runner.\n- `__SOURCE_ROOT__/pages/SettingsPage.vue` as a starter settings page.\n- `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue` as a starter order widget.\n- `__SOURCE_ROOT__/i18n/` with shared JSON message files.\n- `scripts/publish-extension.mjs` for creating `dist/extension.zip` and publishing the integration module through RetailCRM API.\n- `AGENTS.md` when agent instructions were enabled during init.\n\n## Replace Generic Values\n\nReview these generated placeholders before using the project in a real integration:\n\n- Extension code in `extensionrc.json`: `retailcrm-extension-frontend`.\n- Extension name in `extensionrc.json`: `RetailCRM Extension Frontend`.\n- Page code: `__PAGE_CODE__`.\n- Page descriptor in `extensionrc.json`: keep `pages[]` as an object with `code`, `menu`, and `menuItemTitle`; string page form is not valid for RetailCRM API publishing.\n- Widget target: `__WIDGET_TARGET__`.\n- Sample controls and fake data in `__SOURCE_ROOT__/pages/SettingsPage.vue`.\n- Sample toolbar actions and fake order data in `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue`.\n- Shared messages in `__SOURCE_ROOT__/i18n/locales/*.json`.\n\nThe generated page and widget are intentionally generic. Keep the structure you need, but replace the sample labels, fields, and fake data with real product behavior.\n\n## Vue File Names\n\n`SettingsPage.vue` and `OrderCommonAfterWidget.vue` are generic starter names. In product code, rename Vue files after the role they play in the extension, and update imports in `__SOURCE_ROOT__/endpoint/endpoint.worker.ts`.\n\nExamples from RetailCRM extension examples:\n\n- `ReturnsPage.vue` is a full returns-management page.\n- `TasksPage.vue` is a task list/workspace page.\n- `SummaryPage.vue` is a summary dashboard page.\n- `RecordToCalendlyWidget.vue` is a focused widget for one scenario.\n\nUse the same idea for your code: `LoyaltySettingsPage.vue`, `OrderNotesWidget.vue`, `PaymentStatusSidebar.vue`, or another name that describes the real scenario.\n\n## Commands\n\n```bash\n__PACKAGE_MANAGER__ install\n__PACKAGE_MANAGER_RUN__ eslint\n__PACKAGE_MANAGER_RUN__ build\n__PACKAGE_MANAGER_RUN__ publish-extension -- --archive-only\n```\n\n## Publishing\n\nCreate `.env` in the project root when you want `publish-extension` to update RetailCRM:\n\n```dotenv\nCRM_API_HOST=https://example.retailcrm.pro\nCRM_API_KEY=your-api-key\nMODULE_URL=https://example.com\n```\n\nRun `__PACKAGE_MANAGER_RUN__ build` before publishing. The archive-only mode creates `dist/extension.zip` without sending API requests.\n\nFor local CRM checks, `MODULE_URL` must point to a dev/static server that serves\n`/extension/<uuid>/script` and `/extension/<uuid>/stylesheet`. `publish-extension` registers these URLs in CRM,\nbut it does not start that server.\n";
|
|
2087
|
+
const readmeEsESTemplate = "# Frontend de extension RetailCRM\n\nEste proyecto fue generado por `embed-ui init`.\n\n## Que Se Agrego\n\n- `package.json` con scripts para Vite build, ESLint y publicacion de la extension.\n- `extensionrc.json` con la fuente del manifiesto de la extension.\n- `__SOURCE_ROOT__/endpoint/endpoint.worker.ts` con `defineRunner`, un runner de pagina y un runner de widget.\n- `__SOURCE_ROOT__/pages/SettingsPage.vue` como pagina inicial de configuracion.\n- `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue` como widget inicial del pedido.\n- `__SOURCE_ROOT__/i18n/` con archivos JSON de mensajes compartidos.\n- `scripts/publish-extension.mjs` para crear `dist/extension.zip` y publicar el modulo de integracion por RetailCRM API.\n- `AGENTS.md` si las instrucciones para agentes estaban activadas durante init.\n\n## Sustituya Los Valores Genericos\n\nRevise estos valores generados antes de usar el proyecto en una integracion real:\n\n- Codigo de extension en `extensionrc.json`: `retailcrm-extension-frontend`.\n- Nombre de extension en `extensionrc.json`: `RetailCRM Extension Frontend`.\n- Codigo de pagina: `__PAGE_CODE__`.\n- Descriptor de pagina en `extensionrc.json`: mantenga `pages[]` como objeto con `code`, `menu` y `menuItemTitle`; la forma de cadena no sirve para publicar por la API RetailCRM.\n- Target del widget: `__WIDGET_TARGET__`.\n- Controles de ejemplo y datos ficticios en `__SOURCE_ROOT__/pages/SettingsPage.vue`.\n- Acciones toolbar de ejemplo y datos ficticios del pedido en `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue`.\n- Mensajes compartidos en `__SOURCE_ROOT__/i18n/locales/*.json`.\n\nLa pagina y el widget generados son intencionalmente genericos. Mantenga la estructura que necesite, pero sustituya etiquetas, campos y datos ficticios por comportamiento real del producto.\n\n## Nombres De Archivos Vue\n\n`SettingsPage.vue` y `OrderCommonAfterWidget.vue` son nombres iniciales genericos. En codigo de producto, renombre los archivos Vue segun la funcion que cumplen en la extension y actualice los imports en `__SOURCE_ROOT__/endpoint/endpoint.worker.ts`.\n\nEjemplos del repositorio de extensiones RetailCRM:\n\n- `ReturnsPage.vue` es una pagina completa de gestion de devoluciones.\n- `TasksPage.vue` es una pagina de lista o espacio de trabajo de tareas.\n- `SummaryPage.vue` es una pagina de resumen o dashboard.\n- `RecordToCalendlyWidget.vue` es un widget enfocado en un escenario.\n\nUse la misma idea para su codigo: `LoyaltySettingsPage.vue`, `OrderNotesWidget.vue`, `PaymentStatusSidebar.vue` u otro nombre que describa el escenario real.\n\n## Comandos\n\n```bash\n__PACKAGE_MANAGER__ install\n__PACKAGE_MANAGER_RUN__ eslint\n__PACKAGE_MANAGER_RUN__ build\n__PACKAGE_MANAGER_RUN__ publish-extension -- --archive-only\n```\n\n## Publicacion\n\nCree `.env` en la raiz del proyecto cuando quiera que `publish-extension` actualice RetailCRM:\n\n```dotenv\nCRM_API_HOST=https://example.retailcrm.pro\nCRM_API_KEY=your-api-key\nMODULE_URL=https://example.com\n```\n\nEjecute `__PACKAGE_MANAGER_RUN__ build` antes de publicar. El modo archive-only crea `dist/extension.zip` sin enviar peticiones API.\n\nPara comprobar localmente en CRM, `MODULE_URL` debe apuntar a un dev/static server que sirva\n`/extension/<uuid>/script` y `/extension/<uuid>/stylesheet`. `publish-extension` registra estas URL en CRM,\npero no arranca ese servidor.\n";
|
|
2088
|
+
const readmeRuRUTemplate = "# Фронтенд расширения RetailCRM\n\nПроект создан командой `embed-ui init`.\n\n## Что Добавлено\n\n- `package.json` со скриптами для сборки Vite, ESLint и публикации расширения.\n- `extensionrc.json` с исходным описанием манифеста расширения.\n- `__SOURCE_ROOT__/endpoint/endpoint.worker.ts` с `defineRunner`, одним runner страницы и одним runner виджета.\n- `__SOURCE_ROOT__/pages/SettingsPage.vue` как стартовая страница настроек.\n- `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue` как стартовый виджет заказа.\n- `__SOURCE_ROOT__/i18n/` с общими JSON-файлами переводов.\n- `scripts/publish-extension.mjs` для создания `dist/extension.zip` и публикации интеграционного модуля через RetailCRM API.\n- `AGENTS.md`, если при инициализации были включены инструкции для агентов.\n\n## Замените Generic Значения\n\nПеред использованием проекта в реальной интеграции проверьте сгенерированные общие значения:\n\n- Код расширения в `extensionrc.json`: `retailcrm-extension-frontend`.\n- Название расширения в `extensionrc.json`: `RetailCRM Extension Frontend`.\n- Код страницы: `__PAGE_CODE__`.\n- Описание страницы в `extensionrc.json`: `pages[]` должен оставаться объектом с `code`, `menu` и `menuItemTitle`; строковая форма не подходит для публикации через RetailCRM API.\n- Цель виджета: `__WIDGET_TARGET__`.\n- Демонстрационные контролы и ненастоящие данные в `__SOURCE_ROOT__/pages/SettingsPage.vue`.\n- Демонстрационные toolbar-действия и ненастоящие данные заказа в `__SOURCE_ROOT__/widgets/OrderCommonAfterWidget.vue`.\n- Общие сообщения в `__SOURCE_ROOT__/i18n/locales/*.json`.\n\nСгенерированные страница и виджет намеренно сделаны универсальными. Оставьте нужную структуру, но замените примерные подписи, поля и ненастоящие данные на реальное поведение продукта.\n\n## Имена Vue-Файлов\n\n`SettingsPage.vue` и `OrderCommonAfterWidget.vue` — универсальные стартовые имена. В продуктовом коде переименуйте Vue-файлы по роли, которую они выполняют в расширении, и обновите импорты в `__SOURCE_ROOT__/endpoint/endpoint.worker.ts`.\n\nПримеры из репозитория расширений RetailCRM:\n\n- `ReturnsPage.vue` — полноценная страница управления возвратами.\n- `TasksPage.vue` — страница списка задач или рабочей области задач.\n- `SummaryPage.vue` — страница сводки или дашборда.\n- `RecordToCalendlyWidget.vue` — сфокусированный виджет под один сценарий.\n\nИспользуйте тот же принцип: `LoyaltySettingsPage.vue`, `OrderNotesWidget.vue`, `PaymentStatusSidebar.vue` или другое имя, которое описывает реальный сценарий.\n\n## Команды\n\n```bash\n__PACKAGE_MANAGER__ install\n__PACKAGE_MANAGER_RUN__ eslint\n__PACKAGE_MANAGER_RUN__ build\n__PACKAGE_MANAGER_RUN__ publish-extension -- --archive-only\n```\n\n## Публикация\n\nСоздайте `.env` в корне проекта, когда потребуется обновлять RetailCRM через `publish-extension`:\n\n```dotenv\nCRM_API_HOST=https://example.retailcrm.pro\nCRM_API_KEY=your-api-key\nMODULE_URL=https://example.com\n```\n\nПеред публикацией выполните `__PACKAGE_MANAGER_RUN__ build`. Режим archive-only создает `dist/extension.zip` без API-запросов.\n\nДля локальной проверки в CRM `MODULE_URL` должен указывать на dev/static server, который отдаёт\n`/extension/<uuid>/script` и `/extension/<uuid>/stylesheet`. `publish-extension` регистрирует эти URL в CRM,\nно сам dev-server не запускает.\n";
|
|
1924
2089
|
const settingsPageTemplate = `<template>
|
|
1925
2090
|
<main :class="$style['settings-page']">
|
|
1926
2091
|
<UiPageHeader :value="t('title')">
|
|
@@ -2322,7 +2487,18 @@ const createExtensionConfig = (options) => `${JSON.stringify({
|
|
|
2322
2487
|
uuid: randomUUID(),
|
|
2323
2488
|
version: "1.0.0",
|
|
2324
2489
|
targets: [options.widgetTarget],
|
|
2325
|
-
pages: [
|
|
2490
|
+
pages: [{
|
|
2491
|
+
code: options.pageCode,
|
|
2492
|
+
menu: "private_main_menu",
|
|
2493
|
+
parentMenuItemCode: "settings",
|
|
2494
|
+
menuItemOrdering: 100,
|
|
2495
|
+
menuItemTitle: {
|
|
2496
|
+
ru: "Настройки",
|
|
2497
|
+
en: "Settings",
|
|
2498
|
+
es: "Configuración"
|
|
2499
|
+
},
|
|
2500
|
+
pageHelpLink: null
|
|
2501
|
+
}],
|
|
2326
2502
|
stylesheet: true,
|
|
2327
2503
|
entrypointType: "script",
|
|
2328
2504
|
runner: "worker"
|
|
@@ -2345,6 +2521,7 @@ const createInitChanges = () => ({
|
|
|
2345
2521
|
directories: [],
|
|
2346
2522
|
files: [],
|
|
2347
2523
|
agents: [],
|
|
2524
|
+
skills: [],
|
|
2348
2525
|
mcp: [],
|
|
2349
2526
|
git: [],
|
|
2350
2527
|
hooks: [],
|
|
@@ -2408,6 +2585,13 @@ const printInitReport = (cwd, sourceRoot, version, packageManager, changes, opti
|
|
|
2408
2585
|
console.log(` ${agentChange}`);
|
|
2409
2586
|
}
|
|
2410
2587
|
}
|
|
2588
|
+
if (changes.skills.length > 0) {
|
|
2589
|
+
console.log("");
|
|
2590
|
+
console.log("skills");
|
|
2591
|
+
for (const skillChange of changes.skills) {
|
|
2592
|
+
console.log(` ${skillChange}`);
|
|
2593
|
+
}
|
|
2594
|
+
}
|
|
2411
2595
|
if (changes.mcp.length > 0) {
|
|
2412
2596
|
console.log("");
|
|
2413
2597
|
console.log("MCP");
|
|
@@ -2463,6 +2647,7 @@ const printInitSummary = (cwd, sourceRoot, version, packageManager, changes) =>
|
|
|
2463
2647
|
changes.directories.length ? `directories created: ${changes.directories.length}` : null,
|
|
2464
2648
|
changes.files.length ? `files changed: ${changes.files.length}` : null,
|
|
2465
2649
|
changes.agents.length ? "AGENTS.md updated" : null,
|
|
2650
|
+
changes.skills.length ? `skills installed: ${changes.skills.length}` : null,
|
|
2466
2651
|
changes.mcp.length ? "MCP config updated" : null,
|
|
2467
2652
|
changes.git.length ? `git: ${changes.git.join(", ")}` : null,
|
|
2468
2653
|
changes.hooks.length ? `package hooks ran: ${changes.hooks.length}` : null,
|
|
@@ -4525,6 +4710,7 @@ const INIT_ACTION_LABELS = {
|
|
|
4525
4710
|
configs: "Создать базовые конфиги",
|
|
4526
4711
|
template: "Создать стартовый шаблон",
|
|
4527
4712
|
agents: "Обновить AGENTS.md",
|
|
4713
|
+
skills: "Установить project-level skills",
|
|
4528
4714
|
mcp: "Добавить MCP-настройки",
|
|
4529
4715
|
git: "Инициализировать Git",
|
|
4530
4716
|
install: "Запустить установку зависимостей"
|
|
@@ -4533,6 +4719,7 @@ const INIT_ACTION_DESCRIPTIONS = {
|
|
|
4533
4719
|
configs: "tsconfig.json, vite.config.ts, eslint.config.js и env.d.ts",
|
|
4534
4720
|
template: "Vue-точка входа, страница настроек, виджет заказа, i18n и publish script",
|
|
4535
4721
|
agents: "Общие и пакетные инструкции для AI-агентов",
|
|
4722
|
+
skills: ".agents/skills/* с пакетными процедурами для AI-агентов",
|
|
4536
4723
|
mcp: ".mcp.json и MCP-инструкции пакетов",
|
|
4537
4724
|
git: "git init в каталоге проекта, если Git еще не настроен",
|
|
4538
4725
|
install: "Запуск выбранного package manager после изменения package.json"
|
|
@@ -4593,22 +4780,25 @@ const resolvePromptedPackages = async (options) => {
|
|
|
4593
4780
|
};
|
|
4594
4781
|
const resolveAvailableActions = (options) => {
|
|
4595
4782
|
const actions = [];
|
|
4596
|
-
if (!options.agentsOnly && !options.noConfigs) {
|
|
4783
|
+
if (!options.agentsOnly && !options.skillsOnly && !options.noConfigs) {
|
|
4597
4784
|
actions.push("configs");
|
|
4598
4785
|
}
|
|
4599
|
-
if (!options.agentsOnly && !options.noTemplate) {
|
|
4786
|
+
if (!options.agentsOnly && !options.skillsOnly && !options.noTemplate) {
|
|
4600
4787
|
actions.push("template");
|
|
4601
4788
|
}
|
|
4602
4789
|
if (!options.noAgents) {
|
|
4603
4790
|
actions.push("agents");
|
|
4604
4791
|
}
|
|
4792
|
+
if (!options.noSkills) {
|
|
4793
|
+
actions.push("skills");
|
|
4794
|
+
}
|
|
4605
4795
|
if (!options.noMcp) {
|
|
4606
4796
|
actions.push("mcp");
|
|
4607
4797
|
}
|
|
4608
|
-
if (!options.agentsOnly && !isGitWorkTree$1(options.cwd)) {
|
|
4798
|
+
if (!options.agentsOnly && !options.skillsOnly && !isGitWorkTree$1(options.cwd)) {
|
|
4609
4799
|
actions.push("git");
|
|
4610
4800
|
}
|
|
4611
|
-
if (!options.agentsOnly && !options.noInstall) {
|
|
4801
|
+
if (!options.agentsOnly && !options.skillsOnly && !options.noInstall) {
|
|
4612
4802
|
actions.push("install");
|
|
4613
4803
|
}
|
|
4614
4804
|
return actions;
|
|
@@ -4618,6 +4808,7 @@ const applyPromptedActions = (options, selectedActions) => {
|
|
|
4618
4808
|
options.noConfigs = options.noConfigs || !selectedActionSet.has("configs");
|
|
4619
4809
|
options.noTemplate = options.noTemplate || !selectedActionSet.has("template");
|
|
4620
4810
|
options.noAgents = options.noAgents || !selectedActionSet.has("agents");
|
|
4811
|
+
options.noSkills = options.noSkills || !selectedActionSet.has("skills");
|
|
4621
4812
|
options.noMcp = options.noMcp || !selectedActionSet.has("mcp");
|
|
4622
4813
|
options.initGit = options.initGit || selectedActionSet.has("git");
|
|
4623
4814
|
options.noInstall = options.noInstall || !selectedActionSet.has("install");
|
|
@@ -4679,7 +4870,7 @@ const resolveInteractiveInitOptions = async (cwd, options, detectedPackageManage
|
|
|
4679
4870
|
throw new Error("Interactive init mode requires a TTY. Use explicit flags or omit --interactive.");
|
|
4680
4871
|
}
|
|
4681
4872
|
const nextOptions = { ...options };
|
|
4682
|
-
if (!nextOptions.agentsOnly) {
|
|
4873
|
+
if (!nextOptions.agentsOnly && !nextOptions.skillsOnly) {
|
|
4683
4874
|
const defaultSourceRoot = resolveDefaultSourceRoot(cwd, nextOptions);
|
|
4684
4875
|
const sourceRoot = await input({
|
|
4685
4876
|
message: "Frontend source root",
|
|
@@ -4808,11 +4999,15 @@ const hasEnabledPackageHook = (selectedPackages, options) => selectedPackages.so
|
|
|
4808
4999
|
return !options.noAgents && (!hook.requiresMcp || !options.noMcp);
|
|
4809
5000
|
}
|
|
4810
5001
|
if (hook.type === "config") {
|
|
4811
|
-
return !options.agentsOnly && (!hook.requiresMcp || !options.noMcp);
|
|
5002
|
+
return !options.agentsOnly && !options.skillsOnly && (!hook.requiresMcp || !options.noMcp);
|
|
5003
|
+
}
|
|
5004
|
+
if (hook.type === "skills") {
|
|
5005
|
+
return !options.noSkills;
|
|
4812
5006
|
}
|
|
4813
5007
|
return false;
|
|
4814
5008
|
}) ?? false);
|
|
4815
5009
|
const shouldRequirePackageManagerBinary = (selectedPackages, options) => !options.dryRun && (!options.noInstall || hasEnabledPackageHook(selectedPackages, options));
|
|
5010
|
+
const isGuidanceOnlyInit = (options) => options.agentsOnly || options.skillsOnly;
|
|
4816
5011
|
const resolveInitCwd = (options) => {
|
|
4817
5012
|
const cwd = path.resolve(options.cwd);
|
|
4818
5013
|
if (!fs.existsSync(cwd)) {
|
|
@@ -4961,7 +5156,7 @@ const applyInitPackageJson = (cwd, selectedPackages, version, packageManager, op
|
|
|
4961
5156
|
return packageJsonPath;
|
|
4962
5157
|
};
|
|
4963
5158
|
const applyInitDirectories = (sourceRoot, options, changes) => {
|
|
4964
|
-
if (options.noDirs || options
|
|
5159
|
+
if (options.noDirs || isGuidanceOnlyInit(options)) {
|
|
4965
5160
|
return;
|
|
4966
5161
|
}
|
|
4967
5162
|
const dirs = options.dirs ?? DEFAULT_INIT_DIRS;
|
|
@@ -4981,7 +5176,7 @@ const applyInitDirectories = (sourceRoot, options, changes) => {
|
|
|
4981
5176
|
}
|
|
4982
5177
|
};
|
|
4983
5178
|
const applyInitConfigs = (cwd, sourceRoot, options, changes) => {
|
|
4984
|
-
if (options.noConfigs || options
|
|
5179
|
+
if (options.noConfigs || isGuidanceOnlyInit(options)) {
|
|
4985
5180
|
return;
|
|
4986
5181
|
}
|
|
4987
5182
|
writeFileIfAllowed(path.join(cwd, "tsconfig.json"), createTsConfig(cwd, sourceRoot), options, changes);
|
|
@@ -4990,7 +5185,7 @@ const applyInitConfigs = (cwd, sourceRoot, options, changes) => {
|
|
|
4990
5185
|
writeFileIfAllowed(path.join(cwd, "eslint.config.js"), createEslintConfig(cwd, sourceRoot), options, changes);
|
|
4991
5186
|
};
|
|
4992
5187
|
const applyInitTemplate = (cwd, sourceRoot, packageManager, options, changes) => {
|
|
4993
|
-
if (options.noTemplate || options
|
|
5188
|
+
if (options.noTemplate || isGuidanceOnlyInit(options)) {
|
|
4994
5189
|
return;
|
|
4995
5190
|
}
|
|
4996
5191
|
if (options.template !== "order-card") {
|
|
@@ -5014,7 +5209,7 @@ const applyInitTemplate = (cwd, sourceRoot, packageManager, options, changes) =>
|
|
|
5014
5209
|
writeFileIfAllowed(path.join(cwd, "README.md"), createReadme(cwd, sourceRoot, options, packageManager), options, changes);
|
|
5015
5210
|
};
|
|
5016
5211
|
const runInstall = async (cwd, packageManager, options, changes, packageJsonChanged) => {
|
|
5017
|
-
if (options.noInstall || options
|
|
5212
|
+
if (options.noInstall || isGuidanceOnlyInit(options)) {
|
|
5018
5213
|
return;
|
|
5019
5214
|
}
|
|
5020
5215
|
if (!packageJsonChanged && !options.force) {
|
|
@@ -5092,9 +5287,9 @@ const runInit = async (options) => {
|
|
|
5092
5287
|
throw new Error(`Target path is not a directory: ${sourceRoot}`);
|
|
5093
5288
|
}
|
|
5094
5289
|
const selectedPackages = resolveInitPackages(interactiveOptions.packages, interactiveOptions.with);
|
|
5095
|
-
const version = interactiveOptions
|
|
5290
|
+
const version = isGuidanceOnlyInit(interactiveOptions) ? interactiveOptions.version ?? "not used" : interactiveOptions.version ?? resolveDefaultInitVersion();
|
|
5096
5291
|
const resolvedOptions = version === "not used" ? interactiveOptions : { ...interactiveOptions, version };
|
|
5097
|
-
const packageManager = interactiveOptions
|
|
5292
|
+
const packageManager = isGuidanceOnlyInit(interactiveOptions) ? interactiveOptions.packageManager ?? detectPackageManagerByLockfile(cwd) ?? "npm" : await resolvePackageManager(cwd, interactiveOptions.packageManager);
|
|
5098
5293
|
const changes = createInitChanges();
|
|
5099
5294
|
if (shouldRequirePackageManagerBinary(selectedPackages, resolvedOptions)) {
|
|
5100
5295
|
assertPackageManagerAvailable(packageManager);
|
|
@@ -5102,7 +5297,7 @@ const runInit = async (options) => {
|
|
|
5102
5297
|
applyInitPreflight(cwd, sourceRoot, packageManager, selectedPackages, version, resolvedOptions, changes);
|
|
5103
5298
|
await applyInitGit(cwd, resolvedOptions, changes);
|
|
5104
5299
|
let packageJsonPath = null;
|
|
5105
|
-
if (!resolvedOptions
|
|
5300
|
+
if (!isGuidanceOnlyInit(resolvedOptions)) {
|
|
5106
5301
|
packageJsonPath = applyInitPackageJson(cwd, selectedPackages, version, packageManager, resolvedOptions, changes);
|
|
5107
5302
|
updateGitignore(cwd, resolvedOptions, changes);
|
|
5108
5303
|
applyInitDirectories(sourceRoot, resolvedOptions, changes);
|
|
@@ -5111,6 +5306,7 @@ const runInit = async (options) => {
|
|
|
5111
5306
|
}
|
|
5112
5307
|
await runInstall(cwd, packageManager, resolvedOptions, changes, Boolean(packageJsonPath && changes.packageJson.length > 0));
|
|
5113
5308
|
await applyInitPackageConfigHooks(cwd, selectedPackages, packageManager, resolvedOptions, changes);
|
|
5309
|
+
await applyInitSkills(cwd, selectedPackages, packageManager, resolvedOptions, changes);
|
|
5114
5310
|
await applyInitAgents(cwd, selectedPackages, packageManager, resolvedOptions, changes);
|
|
5115
5311
|
printInitReport(cwd, sourceRoot, version, packageManager, changes, resolvedOptions);
|
|
5116
5312
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retailcrm/embed-ui",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.9.
|
|
4
|
+
"version": "0.9.25",
|
|
5
5
|
"description": "API and components for creating RetailCRM UI extensions",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -57,10 +57,10 @@
|
|
|
57
57
|
"@omnicajs/symfony-router": "^1.0.0",
|
|
58
58
|
"@omnicajs/vue-remote": "^0.2.24",
|
|
59
59
|
"@remote-ui/rpc": "^1.4.5",
|
|
60
|
-
"@retailcrm/embed-ui-v1-components": "^0.9.
|
|
61
|
-
"@retailcrm/embed-ui-v1-contexts": "^0.9.
|
|
62
|
-
"@retailcrm/embed-ui-v1-endpoint": "^0.9.
|
|
63
|
-
"@retailcrm/embed-ui-v1-types": "^0.9.
|
|
60
|
+
"@retailcrm/embed-ui-v1-components": "^0.9.25",
|
|
61
|
+
"@retailcrm/embed-ui-v1-contexts": "^0.9.25",
|
|
62
|
+
"@retailcrm/embed-ui-v1-endpoint": "^0.9.25",
|
|
63
|
+
"@retailcrm/embed-ui-v1-types": "^0.9.25",
|
|
64
64
|
"yargs": "^17.7.2"
|
|
65
65
|
},
|
|
66
66
|
"devDependencies": {
|
|
@@ -69,7 +69,7 @@
|
|
|
69
69
|
"@modulify/git-toolkit": "^0.0.2",
|
|
70
70
|
"@modulify/pkg": "^1.0.1",
|
|
71
71
|
"@omnicajs/eslint-plugin-dependencies": "^0.0.2",
|
|
72
|
-
"@retailcrm/embed-ui-v1-testing": "^0.9.
|
|
72
|
+
"@retailcrm/embed-ui-v1-testing": "^0.9.25",
|
|
73
73
|
"@types/git-semver-tags": "^7.0.0",
|
|
74
74
|
"@types/node": "^22.19.2",
|
|
75
75
|
"@types/semver": "^7.7.1",
|