@retailcrm/embed-ui-v1-contexts 0.9.24 → 0.9.26

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/README.md CHANGED
@@ -60,6 +60,16 @@ npx @retailcrm/embed-ui-v1-contexts init-agents
60
60
  такого блока там ещё нет. С `--force` можно обновить уже существующий блок
61
61
  пакета.
62
62
 
63
+ Для project-level skills можно создать `.agents/skills/embed-ui-v1-contexts-usage/SKILL.md`:
64
+
65
+ ```bash
66
+ npx @retailcrm/embed-ui-v1-contexts init-skills
67
+ ```
68
+
69
+ Skill описывает повторяемый workflow применения контекстов в коде: проверку доступности
70
+ контекста в target, выбор public import, readonly/writable семантику, actions и fallback
71
+ на generated YAML profiles, если MCP недоступен.
72
+
63
73
  ## Инициализация MCP-конфига
64
74
 
65
75
  Пакет также может сам добавить project-level MCP-настройки в целевой проект:
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
+ import { fileURLToPath } from 'node:url'
3
4
  import fs from 'node:fs'
4
5
  import path from 'node:path'
5
6
  import process from 'node:process'
@@ -12,6 +13,8 @@ const AGENTS_SECTION_END = '<!-- embed-ui-agents:@retailcrm/embed-ui-v1-contexts
12
13
  const README_MCP_SECTION_HEADER = '## MCP For AI Assistants: @retailcrm/embed-ui-v1-contexts'
13
14
  const README_MCP_MARKER = 'embed-ui-v1-contexts://contexts'
14
15
  const MCP_SERVER_NAME = 'retailcrm-embed-ui-v1-contexts'
16
+ const SKILL_NAME = 'embed-ui-v1-contexts-usage'
17
+ const SKILL_TEMPLATE_PATH = `templates/skills/${SKILL_NAME}/SKILL.md.txt`
15
18
  const MCP_BIN_NAME = process.platform === 'win32' ? 'embed-ui-v1-contexts-mcp.cmd' : 'embed-ui-v1-contexts-mcp'
16
19
  const RELATIVE_MCP_BIN_PATH = `./node_modules/.bin/${MCP_BIN_NAME}`
17
20
  const CLAUDE_PROJECT_MCP_BIN_PATH = `\${CLAUDE_PROJECT_DIR:-.}/node_modules/.bin/${MCP_BIN_NAME}`
@@ -48,6 +51,7 @@ const MCP_CLIENT_CONFIGS = {
48
51
  const HELP_TEXT = `Usage:
49
52
  npx ${PACKAGE_NAME} init-agents [target] [options]
50
53
  npx ${PACKAGE_NAME} init-config [target] [options]
54
+ npx ${PACKAGE_NAME} init-skills [target] [options]
51
55
 
52
56
  Options:
53
57
  -f, --force Replace existing managed sections and MCP server entries
@@ -57,6 +61,7 @@ Options:
57
61
 
58
62
  Examples:
59
63
  npx ${PACKAGE_NAME} init-agents
64
+ npx ${PACKAGE_NAME} init-skills
60
65
  npx ${PACKAGE_NAME} init-agents ./my-project
61
66
  npx ${PACKAGE_NAME} init-agents --force
62
67
  npx ${PACKAGE_NAME} init-config ./my-project
@@ -74,6 +79,10 @@ const printMcpNotice = (message) => {
74
79
  console.log(`MCP: ${message}`)
75
80
  }
76
81
 
82
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
83
+
84
+ const createSkill = () => fs.readFileSync(path.join(packageRoot, SKILL_TEMPLATE_PATH), 'utf8')
85
+
77
86
  const parseArgs = (argv) => {
78
87
  const options = {
79
88
  command: null,
@@ -459,6 +468,35 @@ const initAgents = (target, force) => {
459
468
  console.log(`The ${PACKAGE_NAME} instructions were appended to the end of the file.`)
460
469
  }
461
470
 
471
+ const initSkills = (target, options) => {
472
+ if (!fs.existsSync(target)) {
473
+ throw new Error(`Target path does not exist: ${target}`)
474
+ }
475
+
476
+ const stat = fs.statSync(target)
477
+
478
+ if (!stat.isDirectory()) {
479
+ throw new Error(`Target path is not a directory: ${target}`)
480
+ }
481
+
482
+ const skillPath = path.join(target, '.agents', 'skills', SKILL_NAME, 'SKILL.md')
483
+ const fileExists = fs.existsSync(skillPath)
484
+
485
+ if (fileExists && !options.force) {
486
+ console.log(`${skillPath} already exists`)
487
+ console.log('Nothing was changed. Re-run with --force to refresh that skill.')
488
+ return
489
+ }
490
+
491
+ if (!options.dryRun) {
492
+ fs.mkdirSync(path.dirname(skillPath), { recursive: true })
493
+ fs.writeFileSync(skillPath, createSkill(), 'utf8')
494
+ }
495
+
496
+ const action = fileExists ? 'updated' : 'created'
497
+ console.log(`SKILL: ${options.dryRun ? `would ${action}` : action} ${skillPath}`)
498
+ }
499
+
462
500
  const writeMcpServerConfig = (target, relativePath, rootField, options, serverConfig) => {
463
501
  const filePath = path.join(target, relativePath)
464
502
  const fileExists = fs.existsSync(filePath)
@@ -575,6 +613,11 @@ const main = () => {
575
613
  return
576
614
  }
577
615
 
616
+ if (options.command === 'init-skills') {
617
+ initSkills(options.target, options)
618
+ return
619
+ }
620
+
578
621
  throw new Error(`Unknown command: ${options.command}`)
579
622
  } catch (error) {
580
623
  console.error(error instanceof Error ? error.message : String(error))
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  },
7
7
  "description": "Reactive contexts for RetailCRM JS API",
8
8
  "type": "module",
9
- "version": "0.9.24",
9
+ "version": "0.9.26",
10
10
  "license": "MIT",
11
11
  "author": "RetailDriverLLC <integration@retailcrm.ru>",
12
12
  "repository": {
@@ -104,7 +104,8 @@
104
104
  "dist",
105
105
  "docs",
106
106
  "types",
107
- "README.md"
107
+ "README.md",
108
+ "templates"
108
109
  ],
109
110
  "scripts": {
110
111
  "build": "yarn prepare && yarn build:docs && yarn build:code && yarn build:mcp && yarn build:meta",
@@ -123,11 +124,11 @@
123
124
  "dependencies": {
124
125
  "@modelcontextprotocol/sdk": "^1.29.0",
125
126
  "@omnicajs/symfony-router": "^1.0.0",
126
- "@retailcrm/embed-ui-v1-types": "^0.9.24"
127
+ "@retailcrm/embed-ui-v1-types": "^0.9.26"
127
128
  },
128
129
  "devDependencies": {
129
130
  "@remote-ui/rpc": "^1.4.7",
130
- "@retailcrm/embed-ui-v1-testing": "^0.9.24",
131
+ "@retailcrm/embed-ui-v1-testing": "^0.9.26",
131
132
  "tsx": "^4.21.0",
132
133
  "typescript": "^5.9.3",
133
134
  "vite": "^7.3.2",
@@ -0,0 +1,46 @@
1
+ ---
2
+ name: embed-ui-v1-contexts-usage
3
+ description: Use when applying RetailCRM JS API contexts in extension code with @retailcrm/embed-ui-v1-contexts. Covers target-to-context selection, public imports, readonly versus writable fields, actions, custom contexts, dictionaries, and MCP/profile fallback.
4
+ ---
5
+
6
+ # Embed UI v1 Contexts Usage
7
+
8
+ Use this skill before changing code that reads CRM page data, writes context fields, calls context actions, or works with custom fields through `@retailcrm/embed-ui-v1-contexts`.
9
+
10
+ ## Reading order
11
+
12
+ 1. Read project `AGENTS.md` if it exists.
13
+ 2. Read `./node_modules/@retailcrm/embed-ui-v1-contexts/README.md`.
14
+ 3. Read `./node_modules/@retailcrm/embed-ui-v1-contexts/docs/ru/CONCEPT.md`.
15
+ 4. Read `./node_modules/@retailcrm/embed-ui-v1-contexts/docs/ru/CUSTOM.md` if custom fields or custom dictionaries are involved.
16
+ 5. If the task is tied to a widget target and endpoint MCP is available, read that target profile first; it is the source of truth for available `contexts`, `custom_contexts`, and `action_scopes`.
17
+ 6. If contexts MCP resources are available, start from:
18
+ - `embed-ui-v1-contexts://contexts`
19
+ - `embed-ui-v1-contexts://actions`
20
+ - `embed-ui-v1-contexts://custom-contexts`
21
+ 7. Then read the specific profile before coding:
22
+ - `embed-ui-v1-contexts://contexts/<encoded-context>`
23
+ - `embed-ui-v1-contexts://actions/<encoded-scope>`
24
+ - `embed-ui-v1-contexts://custom-contexts/<encoded-entity>`
25
+ 8. If MCP is not available, use generated YAML profiles from `./node_modules/@retailcrm/embed-ui-v1-contexts/docs/contexts/*.yml`, `docs/actions/*.yml`, and `docs/custom-contexts/*.yml`.
26
+
27
+ ## Workflow
28
+
29
+ 1. Identify where the code runs: CRM page, widget target, page runner, or shared remote module.
30
+ 2. Confirm that the needed context, custom context, or action scope is available there; do not infer availability from page names or target ids.
31
+ 3. Pick the exact public entrypoint from the profile `public_import` or docs, such as `@retailcrm/embed-ui-v1-contexts/remote/order/card`.
32
+ 4. Read context fields through `useContext()` and keep UI state derived from reactive store values instead of copying context data into parallel state.
33
+ 5. For writes, check the field `readonly` flag and `mutation` notes. Assign only writable fields directly.
34
+ 6. For readonly aggregates or business operations, use the documented `useActions()` method and payload shape instead of mutating nested data.
35
+ 7. For custom contexts, call `initialize()` before relying on schema-backed fields, handle rejection, and use `useCustomField()` with the expected `kind`.
36
+ 8. For custom dictionary fields, resolve the dictionary code from the schema and load options through `useDictionary()`.
37
+
38
+ ## High-signal checks
39
+
40
+ - Do not assume all contexts are available on all targets.
41
+ - Do not import from `@retailcrm/embed-ui-v1-contexts/dist/*`, source files, or repository-only paths.
42
+ - Do not infer mutability from TypeScript shape alone; read the generated profile action and mutation notes.
43
+ - Use current item identifiers from the context, such as `OrderItem.index`, when action notes require them.
44
+ - Preserve host-mediated behavior: let CRM perform action validation, mutation, and synchronization.
45
+ - If a field description is vague or absent, state that any business meaning is an inference.
46
+ - Keep context usage aligned with endpoint target profiles when `@retailcrm/embed-ui-v1-endpoint` is also installed.