@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 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` и MCP-настройки пакетов, если они не отключены флагами;
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` — добавляет инструкции по контекстам, actions, custom contexts и MCP-ресурсам.
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
- noMcp: !parsed.mcp,
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 = assertStringArray(config.pages, '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: [options.pageCode],
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.agentsOnly) {
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.agentsOnly) {
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.agentsOnly) {
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.agentsOnly) {
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.agentsOnly ? interactiveOptions.version ?? "not used" : interactiveOptions.version ?? resolveDefaultInitVersion();
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.agentsOnly ? interactiveOptions.packageManager ?? detectPackageManagerByLockfile(cwd) ?? "npm" : await resolvePackageManager(cwd, interactiveOptions.packageManager);
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.agentsOnly) {
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.23",
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.23",
61
- "@retailcrm/embed-ui-v1-contexts": "^0.9.23",
62
- "@retailcrm/embed-ui-v1-endpoint": "^0.9.23",
63
- "@retailcrm/embed-ui-v1-types": "^0.9.23",
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.23",
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",