@retailcrm/embed-ui-v1-components 0.9.19 → 0.9.22-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/AGENTS.md +9 -1
  2. package/README.md +5 -2
  3. package/assets/sprites/actions/add-square-outlined.svg +8 -0
  4. package/assets/sprites/actions/drag.svg +8 -0
  5. package/bin/embed-ui-v1-components.mjs +231 -46
  6. package/bin/postinstall.mjs +3 -2
  7. package/dist/host.cjs +1223 -501
  8. package/dist/host.css +637 -10
  9. package/dist/host.d.ts +152 -78
  10. package/dist/host.js +1224 -502
  11. package/dist/remote.cjs +1358 -298
  12. package/dist/remote.d.ts +805 -67
  13. package/dist/remote.js +1360 -300
  14. package/docs/AI.md +99 -17
  15. package/docs/COMPONENTS.md +58 -59
  16. package/docs/FORMAT.md +10 -10
  17. package/docs/PROFILES.md +39 -22
  18. package/docs/README.md +11 -5
  19. package/docs/profiles/components/UiAddButton.yml +134 -0
  20. package/docs/profiles/components/UiAlert.yml +105 -0
  21. package/docs/profiles/components/UiAvatar.yml +88 -0
  22. package/docs/profiles/{UiAvatarList.yml → components/UiAvatarList.yml} +26 -0
  23. package/docs/profiles/{UiButton.yml → components/UiButton.yml} +14 -8
  24. package/docs/profiles/components/UiCalendar.yml +190 -0
  25. package/docs/profiles/components/UiCheckbox.yml +111 -0
  26. package/docs/profiles/components/UiCollapse.yml +129 -0
  27. package/docs/profiles/components/UiCollapseBox.yml +143 -0
  28. package/docs/profiles/{UiCollapseGroup.yml → components/UiCollapseGroup.yml} +24 -0
  29. package/docs/profiles/components/UiCopyButton.yml +104 -0
  30. package/docs/profiles/components/UiDate.yml +84 -0
  31. package/docs/profiles/components/UiDatePicker.yml +138 -0
  32. package/docs/profiles/components/UiError.yml +123 -0
  33. package/docs/profiles/{UiField.yml → components/UiField.yml} +46 -15
  34. package/docs/profiles/components/UiInfobox.yml +130 -0
  35. package/docs/profiles/components/UiLink.yml +111 -0
  36. package/docs/profiles/components/UiLoader.yml +71 -0
  37. package/docs/profiles/components/UiMenuItem.yml +93 -0
  38. package/docs/profiles/{UiMenuItemGroup.yml → components/UiMenuItemGroup.yml} +25 -0
  39. package/docs/profiles/components/UiModalSidebar.yml +156 -0
  40. package/docs/profiles/components/UiModalWindow.yml +181 -0
  41. package/docs/profiles/{UiModalWindowSurface.yml → components/UiModalWindowSurface.yml} +30 -0
  42. package/docs/profiles/components/UiNumberStepper.yml +100 -0
  43. package/docs/profiles/{UiPageHeader.yml → components/UiPageHeader.yml} +39 -23
  44. package/docs/profiles/components/UiPopconfirm.yml +164 -0
  45. package/docs/profiles/{UiPopper.yml → components/UiPopper.yml} +66 -13
  46. package/docs/profiles/{UiPopperConnector.yml → components/UiPopperConnector.yml} +5 -25
  47. package/docs/profiles/{UiPopperTarget.yml → components/UiPopperTarget.yml} +5 -25
  48. package/docs/profiles/components/UiRadio.yml +91 -0
  49. package/docs/profiles/{UiRadioSwitch.yml → components/UiRadioSwitch.yml} +55 -32
  50. package/docs/profiles/{UiRadioSwitchOption.yml → components/UiRadioSwitchOption.yml} +7 -13
  51. package/docs/profiles/components/UiScrollBox.yml +108 -0
  52. package/docs/profiles/{UiSelect.yml → components/UiSelect.yml} +133 -44
  53. package/docs/profiles/components/UiSelectOption.yml +74 -0
  54. package/docs/profiles/components/UiSelectOptionGroup.yml +66 -0
  55. package/docs/profiles/components/UiSkeleton.yml +82 -0
  56. package/docs/profiles/components/UiSlider.yml +161 -0
  57. package/docs/profiles/components/UiSwitch.yml +123 -0
  58. package/docs/profiles/components/UiTab.yml +234 -0
  59. package/docs/profiles/{UiTabGroup.yml → components/UiTabGroup.yml} +0 -30
  60. package/docs/profiles/components/UiTable.yml +622 -0
  61. package/docs/profiles/{UiTableBodyCell.yml → components/UiTableBodyCell.yml} +26 -0
  62. package/docs/profiles/components/UiTableColumn.yml +87 -0
  63. package/docs/profiles/components/UiTableFooterButton.yml +85 -0
  64. package/docs/profiles/components/UiTableFooterSection.yml +73 -0
  65. package/docs/profiles/{UiTableHeadCell.yml → components/UiTableHeadCell.yml} +26 -0
  66. package/docs/profiles/{UiTableSorter.yml → components/UiTableSorter.yml} +27 -0
  67. package/docs/profiles/components/UiTag.yml +90 -0
  68. package/docs/profiles/{UiTextbox.yml → components/UiTextbox.yml} +20 -32
  69. package/docs/profiles/components/UiTimePicker.yml +144 -0
  70. package/docs/profiles/{UiToggleButton.yml → components/UiToggleButton.yml} +16 -11
  71. package/docs/profiles/{UiToggleGroup.yml → components/UiToggleGroup.yml} +25 -13
  72. package/docs/profiles/{UiToggleGroupOption.yml → components/UiToggleGroupOption.yml} +7 -11
  73. package/docs/profiles/components/UiToolbarButton.yml +87 -0
  74. package/docs/profiles/components/UiToolbarLink.yml +80 -0
  75. package/docs/profiles/components/UiTooltip.yml +97 -0
  76. package/docs/profiles/components/UiTransition.yml +62 -0
  77. package/docs/profiles/components/UiYandexMap.yml +104 -0
  78. package/docs/profiles/pages/CardSettingsPage.yml +58 -0
  79. package/docs/profiles/pages/CollapseBlockPage.yml +46 -0
  80. package/docs/profiles/pages/EntityListPage.yml +66 -0
  81. package/docs/profiles/pages/ModalSidebar.yml +158 -0
  82. package/docs/profiles/pages/ModalWindow.yml +55 -0
  83. package/docs/profiles/pages/MultiColumnPage.yml +27 -0
  84. package/docs/profiles/pages/PageComposition.yml +56 -0
  85. package/package.json +15 -15
  86. package/docs/profiles/UiAddButton.yml +0 -57
  87. package/docs/profiles/UiAlert.yml +0 -48
  88. package/docs/profiles/UiAvatar.yml +0 -48
  89. package/docs/profiles/UiCalendar.yml +0 -48
  90. package/docs/profiles/UiCheckbox.yml +0 -53
  91. package/docs/profiles/UiCollapse.yml +0 -40
  92. package/docs/profiles/UiCollapseBox.yml +0 -51
  93. package/docs/profiles/UiCopyButton.yml +0 -52
  94. package/docs/profiles/UiDate.yml +0 -38
  95. package/docs/profiles/UiDatePicker.yml +0 -59
  96. package/docs/profiles/UiError.yml +0 -32
  97. package/docs/profiles/UiImage.yml +0 -39
  98. package/docs/profiles/UiInfobox.yml +0 -45
  99. package/docs/profiles/UiLink.yml +0 -51
  100. package/docs/profiles/UiLoader.yml +0 -38
  101. package/docs/profiles/UiMenuItem.yml +0 -57
  102. package/docs/profiles/UiModalSidebar.yml +0 -46
  103. package/docs/profiles/UiModalWindow.yml +0 -44
  104. package/docs/profiles/UiNumberStepper.yml +0 -52
  105. package/docs/profiles/UiRadio.yml +0 -38
  106. package/docs/profiles/UiScrollBox.yml +0 -31
  107. package/docs/profiles/UiSelectOption.yml +0 -44
  108. package/docs/profiles/UiSelectOptionGroup.yml +0 -38
  109. package/docs/profiles/UiSkeleton.yml +0 -41
  110. package/docs/profiles/UiSlider.yml +0 -38
  111. package/docs/profiles/UiSwitch.yml +0 -37
  112. package/docs/profiles/UiTab.yml +0 -119
  113. package/docs/profiles/UiTable.yml +0 -152
  114. package/docs/profiles/UiTableColumn.yml +0 -50
  115. package/docs/profiles/UiTableFooterButton.yml +0 -44
  116. package/docs/profiles/UiTableFooterSection.yml +0 -38
  117. package/docs/profiles/UiTag.yml +0 -41
  118. package/docs/profiles/UiTimePicker.yml +0 -46
  119. package/docs/profiles/UiToolbarButton.yml +0 -37
  120. package/docs/profiles/UiToolbarLink.yml +0 -32
  121. package/docs/profiles/UiTooltip.yml +0 -43
  122. package/docs/profiles/UiTransition.yml +0 -27
  123. package/docs/profiles/UiYandexMap.yml +0 -29
package/AGENTS.md CHANGED
@@ -77,7 +77,7 @@ Commonly used exports from `remote` include:
77
77
 
78
78
  - Do not import from `@retailcrm/embed-ui-v1-components/host` in normal extension code.
79
79
  - Do not rely on internal file paths from this repository.
80
- - Do not treat Storybook examples or internal source layout as stable runtime API.
80
+ - Do not treat examples or internal source layout as stable runtime API.
81
81
  - Do not assume every host-side component is available to extension authors.
82
82
 
83
83
  ## Example
@@ -122,5 +122,13 @@ const save = () => {}
122
122
  [`./docs/README.md`](./docs/README.md)
123
123
  - Machine-oriented package summary:
124
124
  [`./docs/AI.md`](./docs/AI.md)
125
+ - Page composition guidelines:
126
+ [`./docs/profiles/pages`](./docs/profiles/pages)
125
127
  - Component profiles:
126
128
  [`./docs/PROFILES.md`](./docs/PROFILES.md)
129
+
130
+ For table, catalog, registry, journal, or search-result screens, read [`./docs/AI.md`](./docs/AI.md)
131
+ and [`./docs/profiles/pages/EntityListPage.yml`](./docs/profiles/pages/EntityListPage.yml), then check the
132
+ `UiTable` and `UiLink` profiles before generating code. Put filters above the table, persist
133
+ filters and pagination in GET query parameters when routing exists, and set `size="small"` on
134
+ `UiLink` inside table cells by default.
package/README.md CHANGED
@@ -46,15 +46,18 @@ import { UiButton } from '@retailcrm/embed-ui-v1-components/remote'
46
46
 
47
47
  Дополнительные материалы по пакету находятся в `docs/`:
48
48
 
49
+ - [`AGENTS.md`](./AGENTS.md) — инструкции для AI-агентов, использующих пакет в целевом проекте.
49
50
  - [`docs/README.md`](./docs/README.md) — обзор пакета и правил использования.
50
51
  - [`docs/COMPONENTS.md`](./docs/COMPONENTS.md) — карта публичных компонентов.
51
52
  - [`docs/AI.md`](./docs/AI.md) — контекст для ИИ и автоматизаций.
52
- - [`docs/PROFILES.md`](./docs/PROFILES.md) — AI-friendly YAML-профили компонентов.
53
+ - [`docs/PROFILES.md`](./docs/PROFILES.md) — AI-friendly YAML-профили компонентов и страниц.
53
54
  - [`docs/FORMAT.md`](./docs/FORMAT.md) — формат описания компонента для AI-агентов.
55
+ - [`docs/profiles/pages`](./docs/profiles/pages) — YAML-профили страниц, модалок, шторок, фильтров и таблиц.
54
56
 
55
57
  ## AI и инициализация `AGENTS.md`
56
58
 
57
- После установки пакет показывает подсказку, что внутри есть AI-заметки и YAML-профили компонентов.
59
+ После установки пакет показывает подсказку, что внутри есть `README.md`, `AGENTS.md`,
60
+ AI-заметки и YAML-профили компонентов и страниц.
58
61
 
59
62
  Если в целевом проекте еще нет `AGENTS.md`, можно сгенерировать стартовый файл командой:
60
63
 
@@ -0,0 +1,8 @@
1
+ <svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path
3
+ fill-rule="evenodd"
4
+ clip-rule="evenodd"
5
+ fill="currentColor"
6
+ d="M19 3H5C3.89543 3 3 3.89543 3 5V19C3 20.1046 3.89543 21 5 21H19C20.1046 21 21 20.1046 21 19V5C21 3.89543 20.1046 3 19 3ZM19 19H5V5H19V19ZM11 13H8.5C8.22386 13 8 12.7761 8 12.5V11.5C8 11.2239 8.22386 11 8.5 11H11V8.5C11 8.22386 11.2239 8 11.5 8H12.5C12.7761 8 13 8.22386 13 8.5V11H15.5C15.7761 11 16 11.2239 16 11.5V12.5C16 12.7761 15.7761 13 15.5 13H13V15.5C13 15.7761 12.7761 16 12.5 16H11.5C11.2239 16 11 15.7761 11 15.5V13Z"
7
+ />
8
+ </svg>
@@ -0,0 +1,8 @@
1
+ <svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg" >
2
+ <path
3
+ d="M7.4987 6.66683C8.41917 6.66683 9.16536 5.92064 9.16536 5.00016C9.16536 4.07969 8.41917 3.3335 7.4987 3.3335C6.57822 3.3335 5.83203 4.07969 5.83203 5.00016C5.83203 5.92064 6.57822 6.66683 7.4987 6.66683ZM5.83203 10.0002C5.83203 9.07969 6.57822 8.3335 7.4987 8.3335C8.41917 8.3335 9.16536 9.07969 9.16536 10.0002C9.16536 10.9206 8.41917 11.6668 7.4987 11.6668C6.57822 11.6668 5.83203 10.9206 5.83203 10.0002ZM7.4987 13.3335C6.57822 13.3335 5.83203 14.0797 5.83203 15.0002C5.83203 15.9206 6.57822 16.6668 7.4987 16.6668C8.41917 16.6668 9.16536 15.9206 9.16536 15.0002C9.16536 14.0797 8.41917 13.3335 7.4987 13.3335ZM14.1654 5.00016C14.1654 5.92064 13.4192 6.66683 12.4987 6.66683C11.5782 6.66683 10.832 5.92064 10.832 5.00016C10.832 4.07969 11.5782 3.3335 12.4987 3.3335C13.4192 3.3335 14.1654 4.07969 14.1654 5.00016ZM12.4987 8.3335C11.5782 8.3335 10.832 9.07969 10.832 10.0002C10.832 10.9206 11.5782 11.6668 12.4987 11.6668C13.4192 11.6668 14.1654 10.9206 14.1654 10.0002C14.1654 9.07969 13.4192 8.3335 12.4987 8.3335ZM10.832 15.0002C10.832 14.0797 11.5782 13.3335 12.4987 13.3335C13.4192 13.3335 14.1654 14.0797 14.1654 15.0002C14.1654 15.9206 13.4192 16.6668 12.4987 16.6668C11.5782 16.6668 10.832 15.9206 10.832 15.0002Z"
4
+ fill="currentColor"
5
+ fill-rule="evenodd"
6
+ clip-rule="evenodd"
7
+ />
8
+ </svg>
@@ -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'
@@ -7,6 +8,8 @@ import process from 'node:process'
7
8
  const PACKAGE_NAME = '@retailcrm/embed-ui-v1-components'
8
9
  const DEFAULT_NEWLINE = '\n'
9
10
  const AGENTS_SECTION_HEADER = '## @retailcrm/embed-ui-v1-components'
11
+ const AGENTS_SECTION_START = '<!-- embed-ui-agents:start -->'
12
+ const AGENTS_SECTION_END = '<!-- embed-ui-agents:end -->'
10
13
 
11
14
  const HELP_TEXT = `Usage:
12
15
  npx ${PACKAGE_NAME} init-agents [target] [options]
@@ -21,6 +24,136 @@ Examples:
21
24
  npx ${PACKAGE_NAME} init-agents --force
22
25
  `
23
26
 
27
+ const toPosixPath = (value) => value.split(path.sep).join('/')
28
+
29
+ const withDotPrefix = (value) => {
30
+ if (!value || value === '.') {
31
+ return '.'
32
+ }
33
+
34
+ return value.startsWith('.') ? value : `./${value}`
35
+ }
36
+
37
+ const isPackageRoot = (directory) => {
38
+ const packageJsonPath = path.join(directory, 'package.json')
39
+
40
+ if (!fs.existsSync(packageJsonPath)) {
41
+ return false
42
+ }
43
+
44
+ try {
45
+ const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'))
46
+
47
+ return packageJson.name === PACKAGE_NAME
48
+ } catch {
49
+ return false
50
+ }
51
+ }
52
+
53
+ const isInsideDirectory = (parent, child) => {
54
+ const relativePath = path.relative(parent, child)
55
+
56
+ return relativePath === '' || (!relativePath.startsWith('..') && !path.isAbsolute(relativePath))
57
+ }
58
+
59
+ const addNestedPackageCandidates = (start, addCandidate) => {
60
+ const packagePath = path.join('node_modules', ...PACKAGE_NAME.split('/'))
61
+ const ignoredDirectories = new Set(['.git', '.yarn', 'dist', 'node_modules'])
62
+ const queue = [path.resolve(start)]
63
+ const seen = new Set(queue)
64
+
65
+ for (let index = 0; index < queue.length; index++) {
66
+ const current = queue[index]
67
+
68
+ addCandidate(path.join(current, packagePath))
69
+
70
+ let entries = []
71
+
72
+ try {
73
+ entries = fs.readdirSync(current, { withFileTypes: true })
74
+ } catch {
75
+ continue
76
+ }
77
+
78
+ for (const entry of entries) {
79
+ if (!entry.isDirectory() || entry.name.startsWith('.') || ignoredDirectories.has(entry.name)) {
80
+ continue
81
+ }
82
+
83
+ const next = path.join(current, entry.name)
84
+
85
+ if (!seen.has(next)) {
86
+ seen.add(next)
87
+ queue.push(next)
88
+ }
89
+ }
90
+ }
91
+ }
92
+
93
+ const getCurrentPackageRoot = () => {
94
+ const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
95
+
96
+ return isPackageRoot(packageRoot) ? packageRoot : null
97
+ }
98
+
99
+ const findPackageRoot = (target) => {
100
+ const candidates = []
101
+ const seen = new Set()
102
+
103
+ const addCandidate = (candidate) => {
104
+ const resolved = path.resolve(candidate)
105
+
106
+ if (!seen.has(resolved)) {
107
+ seen.add(resolved)
108
+ candidates.push(resolved)
109
+ }
110
+ }
111
+
112
+ const addNodeModulesCandidates = (start) => {
113
+ const packagePath = path.join('node_modules', ...PACKAGE_NAME.split('/'))
114
+ let current = path.resolve(start)
115
+
116
+ while (true) {
117
+ addCandidate(path.join(current, packagePath))
118
+
119
+ const parent = path.dirname(current)
120
+
121
+ if (parent === current) {
122
+ break
123
+ }
124
+
125
+ current = parent
126
+ }
127
+ }
128
+
129
+ addCandidate(target)
130
+ addNodeModulesCandidates(target)
131
+ addNestedPackageCandidates(target, addCandidate)
132
+
133
+ const currentPackageRoot = getCurrentPackageRoot()
134
+
135
+ if (currentPackageRoot && isInsideDirectory(target, currentPackageRoot)) {
136
+ addCandidate(currentPackageRoot)
137
+ }
138
+
139
+ const packageRoot = candidates.find(isPackageRoot)
140
+
141
+ if (!packageRoot) {
142
+ throw new Error(
143
+ `Cannot find local ${PACKAGE_NAME}. Install it in this project or run init-agents from the package workspace.`
144
+ )
145
+ }
146
+
147
+ return packageRoot
148
+ }
149
+
150
+ const createPackageDocsPath = (target) => {
151
+ const packageRoot = findPackageRoot(target)
152
+ const relativePath = path.relative(target, packageRoot)
153
+
154
+ return withDotPrefix(toPosixPath(relativePath))
155
+ }
156
+
24
157
  const parseArgs = (argv) => {
25
158
  const options = {
26
159
  command: null,
@@ -67,83 +200,134 @@ const parseArgs = (argv) => {
67
200
  return options
68
201
  }
69
202
 
70
- const createAgentsTemplate = () => {
203
+ const createAgentsTemplate = (packageDocsPath) => {
71
204
  return `# AGENTS.md
72
205
 
206
+ ${createAgentsSection(packageDocsPath)}`
207
+ }
208
+
209
+ const createAgentsSection = (packageDocsPath) => {
210
+ return `${AGENTS_SECTION_START}
73
211
  ${AGENTS_SECTION_HEADER}
74
212
 
75
213
  When working with \`${PACKAGE_NAME}\` in this project:
76
214
 
77
- 1. Read \`./node_modules/${PACKAGE_NAME}/docs/AI.md\`.
78
- 2. Then read \`./node_modules/${PACKAGE_NAME}/docs/COMPONENTS.md\`.
79
- 3. Then open the relevant profile from \`./node_modules/${PACKAGE_NAME}/docs/profiles/<Component>.yml\`.
80
- 4. Prefer those docs and profiles over guessing from internal implementation files.
81
- 5. Import only from documented public entrypoints:
215
+ 1. Read \`${packageDocsPath}/README.md\`.
216
+ 2. Then read \`${packageDocsPath}/AGENTS.md\`.
217
+ 3. Then read \`${packageDocsPath}/docs/AI.md\`.
218
+ 4. Then read \`${packageDocsPath}/docs/COMPONENTS.md\`.
219
+ 5. Then read \`${packageDocsPath}/docs/PROFILES.md\`.
220
+ 6. Then open relevant component profiles from \`${packageDocsPath}/docs/profiles/components/*.yml\`.
221
+ 7. For complete pages, modals, sidebars, filters, tables, or settings layouts, open the relevant
222
+ page profile from \`${packageDocsPath}/docs/profiles/pages/*.yml\`.
223
+ 8. Prefer those docs and profiles over guessing from internal implementation files.
224
+ 9. Import only from documented public entrypoints:
82
225
  - \`${PACKAGE_NAME}/remote\`
83
226
  - \`${PACKAGE_NAME}/host\`
84
227
  - \`${PACKAGE_NAME}/assets/...\`
85
- 6. Prefer \`${PACKAGE_NAME}/remote\` for extension UI code.
86
- 7. Do not import from package-internal files such as \`dist/*\`, repository-only paths, or source internals.
228
+ 10. Prefer \`${PACKAGE_NAME}/remote\` for extension UI code.
229
+ 11. Do not import from package-internal files such as \`dist/*\`, repository-only paths, or source internals.
87
230
 
88
231
  ## Suggested Reading Order
89
232
 
90
- 1. \`docs/AI.md\`
91
- 2. \`docs/COMPONENTS.md\`
92
- 3. The relevant profile from \`docs/profiles/*.yml\`
93
- 4. \`docs/FORMAT.md\` if you need to understand profile structure
94
- 5. Storybook and public types only when no profile exists yet
95
- ` + DEFAULT_NEWLINE
233
+ 1. \`README.md\`
234
+ 2. \`AGENTS.md\`
235
+ 3. \`docs/AI.md\`
236
+ 4. \`docs/COMPONENTS.md\`
237
+ 5. \`docs/PROFILES.md\`
238
+ 6. The relevant component profile from \`docs/profiles/components/*.yml\`
239
+ 7. The relevant page profile from \`docs/profiles/pages/*.yml\` for full-screen or overlay composition
240
+ 8. \`docs/FORMAT.md\` if you need to understand profile structure
241
+ 9. Public type declarations only when no profile exists yet
242
+ ${AGENTS_SECTION_END}
243
+ `
96
244
  }
97
245
 
98
- const createAgentsSection = () => {
99
- return `${AGENTS_SECTION_HEADER}
246
+ const findMarkedSectionRange = (content) => {
247
+ const start = content.indexOf(AGENTS_SECTION_START)
248
+ const end = content.indexOf(AGENTS_SECTION_END, start + AGENTS_SECTION_START.length)
100
249
 
101
- When working with \`${PACKAGE_NAME}\` in this project:
250
+ if (start === -1 && end === -1) {
251
+ return null
252
+ }
102
253
 
103
- 1. Read \`./node_modules/${PACKAGE_NAME}/docs/AI.md\`.
104
- 2. Then read \`./node_modules/${PACKAGE_NAME}/docs/COMPONENTS.md\`.
105
- 3. Then open the relevant profile from \`./node_modules/${PACKAGE_NAME}/docs/profiles/<Component>.yml\`.
106
- 4. Prefer those docs and profiles over guessing from internal implementation files.
107
- 5. Import only from documented public entrypoints:
108
- - \`${PACKAGE_NAME}/remote\`
109
- - \`${PACKAGE_NAME}/host\`
110
- - \`${PACKAGE_NAME}/assets/...\`
111
- 6. Prefer \`${PACKAGE_NAME}/remote\` for extension UI code.
112
- 7. Do not import from package-internal files such as \`dist/*\`, repository-only paths, or source internals.
254
+ if (start === -1 || end === -1 || end < start) {
255
+ throw new Error(`AGENTS.md contains incomplete ${PACKAGE_NAME} section markers`)
256
+ }
113
257
 
114
- ## Suggested Reading Order
258
+ return {
259
+ start,
260
+ end: end + AGENTS_SECTION_END.length,
261
+ }
262
+ }
115
263
 
116
- 1. \`docs/AI.md\`
117
- 2. \`docs/COMPONENTS.md\`
118
- 3. The relevant profile from \`docs/profiles/*.yml\`
119
- 4. \`docs/FORMAT.md\` if you need to understand profile structure
120
- 5. Storybook and public types only when no profile exists yet
121
- `
264
+ const findLegacySectionRange = (content) => {
265
+ const escapedHeader = AGENTS_SECTION_HEADER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
266
+ const headerPattern = new RegExp(`(^|\\n)${escapedHeader}(?=\\n|$)`, 'u')
267
+ const match = headerPattern.exec(content)
268
+
269
+ if (!match) {
270
+ return null
271
+ }
272
+
273
+ const start = match.index + match[1].length
274
+ const afterHeader = content.slice(start + AGENTS_SECTION_HEADER.length)
275
+ const nextExternalHeading = /\n## (?!Suggested Reading Order\b)[^\n]*/u.exec(afterHeader)
276
+
277
+ return {
278
+ start,
279
+ end: nextExternalHeading
280
+ ? start + AGENTS_SECTION_HEADER.length + nextExternalHeading.index
281
+ : content.length,
282
+ }
122
283
  }
123
284
 
124
- const hasPackageSection = (content) => content.includes(AGENTS_SECTION_HEADER)
285
+ const hasPackageSection = (content) => {
286
+ return Boolean(findMarkedSectionRange(content) || findLegacySectionRange(content))
287
+ }
125
288
 
126
289
  const appendSection = (content, section) => {
127
290
  const trimmed = content.replace(/\s+$/u, '')
128
291
 
129
292
  if (!trimmed.length) {
130
- return `${section}${DEFAULT_NEWLINE}`
293
+ return `${section.trimEnd()}${DEFAULT_NEWLINE}`
294
+ }
295
+
296
+ return `${trimmed}${DEFAULT_NEWLINE}${DEFAULT_NEWLINE}${section.trimEnd()}${DEFAULT_NEWLINE}`
297
+ }
298
+
299
+ const replaceRange = (content, range, section) => {
300
+ const before = content.slice(0, range.start).replace(/\s+$/u, '')
301
+ const after = content.slice(range.end).replace(/^\s+/u, '')
302
+ const parts = []
303
+
304
+ if (before) {
305
+ parts.push(before)
131
306
  }
132
307
 
133
- return `${trimmed}${DEFAULT_NEWLINE}${DEFAULT_NEWLINE}${section}${DEFAULT_NEWLINE}`
308
+ parts.push(section.trimEnd())
309
+
310
+ if (after) {
311
+ parts.push(after)
312
+ }
313
+
314
+ return `${parts.join(`${DEFAULT_NEWLINE}${DEFAULT_NEWLINE}`)}${DEFAULT_NEWLINE}`
134
315
  }
135
316
 
136
317
  const replaceSection = (content, section) => {
137
- const escapedHeader = AGENTS_SECTION_HEADER.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
138
- const sectionPattern = new RegExp(`${escapedHeader}[\\s\\S]*?(?=\\n##\\s|$)`, 'u')
318
+ const markedRange = findMarkedSectionRange(content)
319
+
320
+ if (markedRange) {
321
+ return replaceRange(content, markedRange, section)
322
+ }
323
+
324
+ const legacyRange = findLegacySectionRange(content)
139
325
 
140
- if (!sectionPattern.test(content)) {
141
- return appendSection(content, section)
326
+ if (legacyRange) {
327
+ return replaceRange(content, legacyRange, section)
142
328
  }
143
329
 
144
- return content
145
- .replace(sectionPattern, section.trimEnd())
146
- .replace(/\s+$/u, '') + DEFAULT_NEWLINE
330
+ return appendSection(content, section)
147
331
  }
148
332
 
149
333
  const initAgents = (target, force) => {
@@ -158,10 +342,11 @@ const initAgents = (target, force) => {
158
342
  }
159
343
 
160
344
  const agentsPath = path.join(target, 'AGENTS.md')
161
- const section = createAgentsSection()
345
+ const packageDocsPath = createPackageDocsPath(target)
346
+ const section = createAgentsSection(packageDocsPath)
162
347
 
163
348
  if (!fs.existsSync(agentsPath)) {
164
- fs.writeFileSync(agentsPath, createAgentsTemplate(), 'utf8')
349
+ fs.writeFileSync(agentsPath, createAgentsTemplate(packageDocsPath), 'utf8')
165
350
 
166
351
  console.log(`AGENTS.md was created at ${agentsPath}`)
167
352
  console.log('Next step: review it and adjust project-specific rules if needed.')
@@ -26,8 +26,9 @@ const agentsPath = path.join(targetRoot, 'AGENTS.md')
26
26
  const hasAgentsFile = fs.existsSync(agentsPath)
27
27
 
28
28
  console.log('')
29
- console.log(`[${PACKAGE_NAME}] AI docs are available in node_modules/${PACKAGE_NAME}/docs`)
30
- console.log(`[${PACKAGE_NAME}] Start with docs/AI.md, docs/COMPONENTS.md, and docs/profiles/*.yml`)
29
+ console.log(`[${PACKAGE_NAME}] AI docs are available in node_modules/${PACKAGE_NAME}/README.md, AGENTS.md, and docs`)
30
+ console.log(`[${PACKAGE_NAME}] Start with README.md, AGENTS.md, docs/AI.md, docs/COMPONENTS.md, and docs/PROFILES.md`)
31
+ console.log(`[${PACKAGE_NAME}] Component profiles live in docs/profiles/components, page profiles live in docs/profiles/pages`)
31
32
 
32
33
  if (!hasAgentsFile) {
33
34
  console.log(`[${PACKAGE_NAME}] To scaffold AGENTS.md for this project, run:`)