@brms/ai-skills 0.1.0

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 (90) hide show
  1. package/README.md +256 -0
  2. package/bin/brms-skills.mjs +411 -0
  3. package/package.json +30 -0
  4. package/skills/brms-prototype-generator/SKILL.md +129 -0
  5. package/skills/brms-prototype-generator/agents/openai.yaml +7 -0
  6. package/skills/brms-prototype-generator/examples/few-shot-examples.md +577 -0
  7. package/skills/brms-prototype-generator/references/01-list-query.md +444 -0
  8. package/skills/brms-prototype-generator/references/02-form-entry.md +129 -0
  9. package/skills/brms-prototype-generator/references/03-detail-display.md +125 -0
  10. package/skills/brms-prototype-generator/references/04-composite-page-package.md +339 -0
  11. package/skills/brms-prototype-generator/references/05-dialog-patterns.md +113 -0
  12. package/skills/brms-prototype-generator/references/06-backend-request-patterns.md +118 -0
  13. package/skills/brms-prototype-generator/references/resource-index.md +46 -0
  14. package/skills/brms-prototype-generator/references/system-prompt.md +242 -0
  15. package/skills/brms-prototype-generator/scripts/analyze-doc.mjs +554 -0
  16. package/skills/brms-prototype-generator/scripts/check-project.mjs +228 -0
  17. package/skills/brms-prototype-generator/scripts/discover-targets.mjs +158 -0
  18. package/skills/brms-prototype-generator/scripts/install-codex.mjs +74 -0
  19. package/skills/brms-prototype-generator/scripts/plan-pages.mjs +390 -0
  20. package/skills/brms-prototype-generator/scripts/validate-generated.mjs +838 -0
  21. package/skills/brms-prototype-generator/templates/user-input-template.md +182 -0
  22. package/skills/brms-vxe-plus-developer/SKILL.md +105 -0
  23. package/skills/brms-vxe-plus-developer/agents/openai.yaml +7 -0
  24. package/skills/brms-vxe-plus-developer/references/prototype-to-real.md +54 -0
  25. package/skills/brms-vxe-plus-developer/references/real-base-development.md +110 -0
  26. package/skills/brms-vxe-plus-developer/references/resource-index.md +43 -0
  27. package/skills/brms-vxe-plus-developer/references/review-checklist.md +49 -0
  28. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/01-mental-model.md +150 -0
  29. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/02-vxe-plus-form.md +302 -0
  30. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/03-vxe-plus-table.md +253 -0
  31. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/04-example-map.md +488 -0
  32. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/05-request-and-eiinfo.md +170 -0
  33. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/90-anti-patterns.md +137 -0
  34. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/README.md +43 -0
  35. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/README.md +21 -0
  36. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/A1/P0/A1P01601.vue +483 -0
  37. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/A1/P1/A1P11011.vue +444 -0
  38. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AB/BP/ABBP0201.vue +1648 -0
  39. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AM/AF/component/AMAF0601/Bidding/formConfig.ts +228 -0
  40. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AM/AF/component/AMAF0601/Record/columns.ts +110 -0
  41. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/BMBR01.vue +130 -0
  42. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/component/BMBR01/columns.ts +94 -0
  43. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/component/BMBR01/formConfig.ts +108 -0
  44. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Change/formConfig.ts +123 -0
  45. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Change/index.vue +103 -0
  46. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Clause/columns.ts +48 -0
  47. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Clause/index.vue +202 -0
  48. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Correcte/formConfig.ts +117 -0
  49. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Correcte/index.vue +103 -0
  50. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/Payment/formConfig.ts +90 -0
  51. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/Payment/index.vue +42 -0
  52. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/columns.ts +376 -0
  53. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Pay/index.vue +619 -0
  54. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Domestic/formConfig.ts +73 -0
  55. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Domestic/index.vue +47 -0
  56. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Foreign/formConfig.ts +141 -0
  57. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/Foreign/index.vue +42 -0
  58. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/columns.ts +123 -0
  59. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/Settle/index.vue +593 -0
  60. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/index.vue +68 -0
  61. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Fee/columns.ts +150 -0
  62. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Fee/index.vue +235 -0
  63. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Files/columns.ts +63 -0
  64. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Files/index.vue +117 -0
  65. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Goods/columns.ts +327 -0
  66. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Goods/index.vue +790 -0
  67. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve/formConfig.ts +341 -0
  68. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve/index.vue +63 -0
  69. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve2/formConfig.ts +232 -0
  70. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Approve2/index.vue +27 -0
  71. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Diff/columns.ts +46 -0
  72. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/Diff/index.vue +92 -0
  73. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/formConfig.ts +979 -0
  74. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Base/index.vue +62 -0
  75. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Other/formConfig.ts +179 -0
  76. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Other/index.vue +140 -0
  77. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Sign/formConfig.ts +118 -0
  78. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/Sign/index.vue +44 -0
  79. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/index.vue +168 -0
  80. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/Major/formConfig.ts +257 -0
  81. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/Major/index.vue +47 -0
  82. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/columns.ts +256 -0
  83. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/index.vue +738 -0
  84. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Price/formConfig.ts +174 -0
  85. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Price/index.vue +51 -0
  86. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Top/index.vue +924 -0
  87. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/SM/SW/SMSW0101.vue +567 -0
  88. package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/demo/index.vue +448 -0
  89. package/skills/brms-vxe-plus-developer/scripts/check-project.mjs +259 -0
  90. package/skills/brms-vxe-plus-developer/scripts/check-vxe-plus-page.mjs +137 -0
@@ -0,0 +1,554 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs'
3
+ import path from 'node:path'
4
+
5
+ const PAGE_HEADING_RE = /^##\s+(?:(\d+)[.、]\s*)?(.+?)\s*$/
6
+ const SUB_HEADING_RE = /^###\s+(.+?)\s*$/
7
+ const LINK_RE = /\[[^\]]*]\(([^)]+\.md(?:#[^)]+)?)\)/gi
8
+ const BARE_MD_RE = /(?:^|[\s>::((])([A-Za-z0-9_.\-\/\\]+\.md)(?=$|[\s))。,,.;;])/gi
9
+
10
+ function parseArgs(argv) {
11
+ const args = {
12
+ entry: '',
13
+ repoRoot: process.cwd(),
14
+ format: 'markdown',
15
+ }
16
+
17
+ for (let i = 0; i < argv.length; i += 1) {
18
+ const arg = argv[i]
19
+ if (arg === '--repo-root') {
20
+ args.repoRoot = argv[i + 1] || ''
21
+ i += 1
22
+ }
23
+ else if (arg === '--format') {
24
+ args.format = argv[i + 1] || ''
25
+ i += 1
26
+ }
27
+ else if (arg === '--help' || arg === '-h') {
28
+ args.help = true
29
+ }
30
+ else if (!args.entry) {
31
+ args.entry = arg
32
+ }
33
+ else {
34
+ throw new Error(`Unknown argument: ${arg}`)
35
+ }
36
+ }
37
+
38
+ if (!['json', 'markdown', 'both'].includes(args.format))
39
+ throw new Error('--format must be one of: json, markdown, both')
40
+
41
+ return args
42
+ }
43
+
44
+ function normalizePath(filePath) {
45
+ return filePath.replaceAll('\\', '/')
46
+ }
47
+
48
+ function toRepoRelative(filePath, repoRoot) {
49
+ return normalizePath(path.relative(repoRoot, filePath))
50
+ }
51
+
52
+ function stripAnchor(link) {
53
+ return link.replace(/#.*$/, '')
54
+ }
55
+
56
+ function resolveMdReference(reference, fromFile, repoRoot) {
57
+ const cleanReference = decodeURI(stripAnchor(reference.trim())).replace(/^file:\/\//, '')
58
+ if (/^[a-z]+:/i.test(cleanReference))
59
+ return null
60
+
61
+ const candidate = path.isAbsolute(cleanReference)
62
+ ? cleanReference
63
+ : path.resolve(path.dirname(fromFile), cleanReference)
64
+
65
+ if (!candidate.startsWith(repoRoot))
66
+ return null
67
+
68
+ if (fs.existsSync(candidate) && fs.statSync(candidate).isFile())
69
+ return candidate
70
+
71
+ const repoCandidate = path.resolve(repoRoot, cleanReference)
72
+ if (repoCandidate.startsWith(repoRoot) && fs.existsSync(repoCandidate) && fs.statSync(repoCandidate).isFile())
73
+ return repoCandidate
74
+
75
+ return candidate
76
+ }
77
+
78
+ function findMarkdownReferences(text) {
79
+ const references = new Set()
80
+ let match
81
+
82
+ while ((match = LINK_RE.exec(text)))
83
+ references.add(match[1])
84
+
85
+ while ((match = BARE_MD_RE.exec(text)))
86
+ references.add(match[1])
87
+
88
+ return [...references]
89
+ }
90
+
91
+ function collectDocuments(entryFile, repoRoot) {
92
+ const queue = [entryFile]
93
+ const seen = new Set()
94
+ const documents = []
95
+ const missingReferences = []
96
+
97
+ while (queue.length) {
98
+ const file = path.resolve(queue.shift())
99
+ if (seen.has(file))
100
+ continue
101
+
102
+ seen.add(file)
103
+ if (!fs.existsSync(file)) {
104
+ missingReferences.push(toRepoRelative(file, repoRoot))
105
+ continue
106
+ }
107
+
108
+ const text = fs.readFileSync(file, 'utf8')
109
+ documents.push({ file, text })
110
+
111
+ for (const ref of findMarkdownReferences(text)) {
112
+ const resolved = resolveMdReference(ref, file, repoRoot)
113
+ if (!resolved)
114
+ continue
115
+
116
+ if (!seen.has(resolved))
117
+ queue.push(resolved)
118
+ }
119
+ }
120
+
121
+ return { documents, missingReferences: [...new Set(missingReferences)] }
122
+ }
123
+
124
+ function parseMarkdownTable(lines, startIndex) {
125
+ if (!lines[startIndex]?.trim().startsWith('|'))
126
+ return null
127
+
128
+ const headerLine = lines[startIndex]
129
+ const separatorLine = lines[startIndex + 1] || ''
130
+ if (!separatorLine.trim().startsWith('|') || !separatorLine.includes('-'))
131
+ return null
132
+
133
+ const headers = splitTableRow(headerLine)
134
+ const rows = []
135
+ let endIndex = startIndex + 2
136
+
137
+ while (endIndex < lines.length && lines[endIndex].trim().startsWith('|')) {
138
+ const values = splitTableRow(lines[endIndex])
139
+ const row = {}
140
+ headers.forEach((header, index) => {
141
+ row[header] = values[index] || ''
142
+ })
143
+ rows.push(row)
144
+ endIndex += 1
145
+ }
146
+
147
+ return {
148
+ startLine: startIndex + 1,
149
+ endLine: endIndex,
150
+ headers,
151
+ rows,
152
+ }
153
+ }
154
+
155
+ function splitTableRow(line) {
156
+ return line
157
+ .trim()
158
+ .replace(/^\|/, '')
159
+ .replace(/\|$/, '')
160
+ .split('|')
161
+ .map(cell => cell.trim())
162
+ }
163
+
164
+ function isFieldTable(table, activeSubHeading) {
165
+ const headers = table.headers.join('|').toLowerCase()
166
+ return /字段|field|类型|type/.test(headers) && (/字段|field/.test(headers) || /字段清单|表结构|table/i.test(activeSubHeading))
167
+ }
168
+
169
+ function isFunctionTable(table, activeSubHeading) {
170
+ const headers = table.headers.join('|').toLowerCase()
171
+ return /功能|operation|button|action|处理逻辑/.test(headers) || /功能清单|按钮|操作/.test(activeSubHeading)
172
+ }
173
+
174
+ function hasFieldKey(headers) {
175
+ const normalized = headers.map(header => header.trim().toLowerCase())
176
+ return normalized.some(header =>
177
+ ['fieldname', 'field_name', 'field', '字段名', '字段编码', '字段key', '字段 key', '字段代码'].includes(header),
178
+ )
179
+ }
180
+
181
+ function hasDisplayName(headers) {
182
+ const normalized = headers.map(header => header.trim().toLowerCase())
183
+ return normalized.some(header =>
184
+ ['displayname', 'display_name', 'label', 'title', '显示名', '展示名', '字段', '字段名称', '名称'].includes(header),
185
+ )
186
+ }
187
+
188
+ function hasType(headers) {
189
+ const normalized = headers.map(header => header.trim().toLowerCase())
190
+ return normalized.some(header =>
191
+ ['type', 'fieldtype', 'field_type', '类型', '字段类型'].includes(header),
192
+ )
193
+ }
194
+
195
+ function summarizeFieldTable(table, lineOffset = 0) {
196
+ return {
197
+ startLine: table.startLine + lineOffset,
198
+ rows: table.rows.length,
199
+ headers: table.headers,
200
+ hasFieldKey: hasFieldKey(table.headers),
201
+ hasDisplayName: hasDisplayName(table.headers),
202
+ hasType: hasType(table.headers),
203
+ }
204
+ }
205
+
206
+ function extractPurpose(lines) {
207
+ for (const line of lines) {
208
+ const trimmed = line.trim()
209
+ if (!trimmed || trimmed.startsWith('|') || trimmed.startsWith('#') || trimmed === '---')
210
+ continue
211
+ return trimmed.replace(/^>\s*/, '')
212
+ }
213
+
214
+ return ''
215
+ }
216
+
217
+ function analyzePage(section, repoRoot) {
218
+ const lines = section.content
219
+ const fieldTables = []
220
+ const functionTables = []
221
+ let activeSubHeading = ''
222
+
223
+ for (let i = 0; i < lines.length; i += 1) {
224
+ const subHeading = lines[i].match(SUB_HEADING_RE)
225
+ if (subHeading)
226
+ activeSubHeading = subHeading[1]
227
+
228
+ const table = parseMarkdownTable(lines, i)
229
+ if (!table)
230
+ continue
231
+
232
+ if (isFieldTable(table, activeSubHeading))
233
+ fieldTables.push(summarizeFieldTable(table, section.line))
234
+ if (isFunctionTable(table, activeSubHeading))
235
+ functionTables.push({ startLine: table.startLine + section.line, rows: table.rows.length, headers: table.headers })
236
+
237
+ i = table.endLine - 1
238
+ }
239
+
240
+ const purpose = extractPurpose(lines)
241
+ const hasRenderableFields = fieldTables.some(table => table.rows > 0 && table.hasDisplayName && table.hasType)
242
+ const hasCompleteFieldDesign = fieldTables.some(table => table.rows > 0 && table.hasFieldKey && table.hasDisplayName && table.hasType)
243
+ const hasFunctions = functionTables.some(table => table.rows > 0)
244
+ const complete = Boolean(section.title && (purpose || hasFunctions) && hasRenderableFields)
245
+ const issues = []
246
+
247
+ if (!purpose && !hasFunctions)
248
+ issues.push('missing purpose or function list')
249
+ if (!fieldTables.length)
250
+ issues.push('missing field table')
251
+ if (fieldTables.length && !hasCompleteFieldDesign)
252
+ issues.push('field design missing field keys')
253
+ if (!complete)
254
+ issues.push('page incomplete')
255
+
256
+ return {
257
+ source: toRepoRelative(section.file, repoRoot),
258
+ line: section.line,
259
+ index: section.index,
260
+ title: section.title,
261
+ purpose,
262
+ fieldTables,
263
+ functionTables,
264
+ complete,
265
+ hasCompleteFieldDesign,
266
+ issues,
267
+ }
268
+ }
269
+
270
+ function extractSections(document) {
271
+ const lines = document.text.split(/\r?\n/)
272
+ const sections = []
273
+
274
+ for (let i = 0; i < lines.length; i += 1) {
275
+ const match = lines[i].match(PAGE_HEADING_RE)
276
+ if (!match)
277
+ continue
278
+
279
+ const title = match[2].trim()
280
+ if (/附录|页面间导航关系|导航关系/.test(title))
281
+ continue
282
+
283
+ let end = i + 1
284
+ while (end < lines.length && !PAGE_HEADING_RE.test(lines[end]))
285
+ end += 1
286
+
287
+ sections.push({
288
+ file: document.file,
289
+ line: i + 1,
290
+ index: match[1] ? Number(match[1]) : null,
291
+ title,
292
+ content: lines.slice(i + 1, end),
293
+ })
294
+ }
295
+
296
+ return sections
297
+ }
298
+
299
+ function extractNavigationBlocks(document, repoRoot) {
300
+ const lines = document.text.split(/\r?\n/)
301
+ const blocks = []
302
+ let inNavigation = false
303
+
304
+ for (let i = 0; i < lines.length; i += 1) {
305
+ if (/^##\s+.*(?:页面间导航关系|导航关系)/.test(lines[i])) {
306
+ inNavigation = true
307
+ continue
308
+ }
309
+
310
+ if (inNavigation && /^##\s+/.test(lines[i]))
311
+ inNavigation = false
312
+
313
+ if (!inNavigation)
314
+ continue
315
+
316
+ if (lines[i].trim().startsWith('```')) {
317
+ const start = i + 1
318
+ let end = start
319
+ while (end < lines.length && !lines[end].trim().startsWith('```'))
320
+ end += 1
321
+
322
+ blocks.push({
323
+ source: toRepoRelative(document.file, repoRoot),
324
+ line: start + 1,
325
+ text: lines.slice(start, end).join('\n').trim(),
326
+ tree: parseNavigationTree(lines.slice(start, end)),
327
+ })
328
+ i = end
329
+ }
330
+ }
331
+
332
+ return blocks
333
+ }
334
+
335
+ function parseNavigationTree(lines) {
336
+ const root = []
337
+ const stack = []
338
+
339
+ for (const rawLine of lines) {
340
+ const line = rawLine.replace(/\s+$/, '')
341
+ if (!line.trim())
342
+ continue
343
+
344
+ const branchIndex = line.search(/[├└]/)
345
+ const pipeCount = branchIndex >= 0 ? (line.slice(0, branchIndex).match(/│/g) || []).length : 0
346
+ let level = 0
347
+ if (branchIndex >= 0)
348
+ level = Math.max(pipeCount + 1, Math.max(1, Math.floor(branchIndex / 6) + 1))
349
+
350
+ const label = line
351
+ .replace(/^[\s│├└─]+/, '')
352
+ .trim()
353
+ if (!label)
354
+ continue
355
+
356
+ const noteMatch = label.match(/^(.*?)[((]([^()()]*)[))]$/)
357
+ const title = (noteMatch ? noteMatch[1] : label).trim()
358
+ const note = noteMatch ? noteMatch[2].trim() : ''
359
+ const node = {
360
+ title,
361
+ note,
362
+ level,
363
+ children: [],
364
+ }
365
+
366
+ while (stack.length && stack[stack.length - 1].level >= level)
367
+ stack.pop()
368
+
369
+ if (stack.length)
370
+ stack[stack.length - 1].node.children.push(node)
371
+ else
372
+ root.push(node)
373
+
374
+ stack.push({ level, node })
375
+ }
376
+
377
+ return root
378
+ }
379
+
380
+ function countNavigationNodes(nodes) {
381
+ return nodes.reduce((count, node) => count + 1 + countNavigationNodes(node.children || []), 0)
382
+ }
383
+
384
+ function formatNavigationTree(nodes, indent = 0) {
385
+ const lines = []
386
+ for (const node of nodes) {
387
+ const prefix = ' '.repeat(indent)
388
+ const note = node.note ? ` (${node.note})` : ''
389
+ lines.push(`${prefix}- ${node.title}${note}`)
390
+ lines.push(...formatNavigationTree(node.children || [], indent + 1))
391
+ }
392
+ return lines
393
+ }
394
+
395
+ function analyze(entry, repoRoot) {
396
+ const entryFile = path.resolve(repoRoot, entry)
397
+ if (!fs.existsSync(entryFile))
398
+ throw new Error(`Entry Markdown not found: ${entry}`)
399
+
400
+ const { documents, missingReferences } = collectDocuments(entryFile, repoRoot)
401
+ const pages = documents.flatMap(document => extractSections(document).map(section => analyzePage(section, repoRoot)))
402
+ const navigationBlocks = documents.flatMap(document => extractNavigationBlocks(document, repoRoot))
403
+ const incompletePages = pages.filter(page => !page.complete)
404
+ const pagesMissingFieldKeys = pages.filter(page => page.fieldTables.length && !page.hasCompleteFieldDesign)
405
+ const completePages = pages.filter(page => page.complete).length
406
+ const generationModes = getGenerationModes(completePages)
407
+ const navigationTreeNodes = navigationBlocks.reduce((count, block) => count + countNavigationNodes(block.tree || []), 0)
408
+
409
+ return {
410
+ entry: toRepoRelative(entryFile, repoRoot),
411
+ documents: documents.map(document => toRepoRelative(document.file, repoRoot)),
412
+ missingReferences,
413
+ summary: {
414
+ pageCount: pages.length,
415
+ completePages,
416
+ incompletePages: incompletePages.length,
417
+ pagesMissingFieldKeys: pagesMissingFieldKeys.length,
418
+ navigationBlocks: navigationBlocks.length,
419
+ navigationTreeNodes,
420
+ menuStructureDetected: navigationTreeNodes > 0,
421
+ shouldStop: completePages === 0,
422
+ shouldAskForTableDesign: pagesMissingFieldKeys.length > 0,
423
+ recommendedGenerationMode: generationModes.recommended,
424
+ },
425
+ generationModes: generationModes.modes,
426
+ pages,
427
+ navigationBlocks,
428
+ }
429
+ }
430
+
431
+ function getGenerationModes(completePages) {
432
+ const modes = [
433
+ {
434
+ id: 'single-agent-package',
435
+ name: '整包一次性生成',
436
+ recommendedWhen: '1-3 个完整页面,页面关系简单',
437
+ tradeoff: '最快,但页面多时更容易遗漏细节或规则漂移',
438
+ },
439
+ {
440
+ id: 'coordinator-sequential',
441
+ name: '总控逐页顺序生成',
442
+ recommendedWhen: '4 个以上页面,或产品希望每页更稳但不使用子智能体',
443
+ tradeoff: '最稳妥,速度较慢;总控每次只生成一个页面并做局部检查',
444
+ },
445
+ {
446
+ id: 'coordinator-subagents',
447
+ name: '总控 + 子智能体逐页生成',
448
+ recommendedWhen: '4 个以上页面,且当前环境支持子智能体/并行 agent',
449
+ tradeoff: '速度快、上下文更小;必须由总控统一共享数据、菜单和最终集成',
450
+ },
451
+ ]
452
+
453
+ let recommended = 'single-agent-package'
454
+ if (completePages >= 4)
455
+ recommended = 'coordinator-subagents'
456
+ if (completePages === 0)
457
+ recommended = 'none'
458
+
459
+ return { recommended, modes }
460
+ }
461
+
462
+ function toMarkdown(report) {
463
+ const lines = [
464
+ '# BRMS Prototype Document Analysis',
465
+ '',
466
+ `- Entry: \`${report.entry}\``,
467
+ `- Documents scanned: ${report.documents.length}`,
468
+ `- Pages detected: ${report.summary.pageCount}`,
469
+ `- Complete pages: ${report.summary.completePages}`,
470
+ `- Pages missing field keys: ${report.summary.pagesMissingFieldKeys}`,
471
+ `- Navigation blocks: ${report.summary.navigationBlocks}`,
472
+ `- Menu structure detected: ${report.summary.menuStructureDetected ? 'yes' : 'no'}`,
473
+ `- Navigation tree nodes: ${report.summary.navigationTreeNodes}`,
474
+ `- Recommended generation mode: ${report.summary.recommendedGenerationMode}`,
475
+ `- Stop required: ${report.summary.shouldStop ? 'yes' : 'no'}`,
476
+ '',
477
+ ]
478
+
479
+ if (report.missingReferences.length) {
480
+ lines.push('## Missing References', '')
481
+ report.missingReferences.forEach(ref => lines.push(`- \`${ref}\``))
482
+ lines.push('')
483
+ }
484
+
485
+ lines.push('## Generation Modes', '')
486
+ report.generationModes.forEach((mode) => {
487
+ lines.push(`- **${mode.name}** (\`${mode.id}\`): ${mode.recommendedWhen};${mode.tradeoff}`)
488
+ })
489
+ lines.push('')
490
+
491
+ lines.push('## Pages', '')
492
+ report.pages.forEach((page) => {
493
+ lines.push(`### ${page.index ? `${page.index}. ` : ''}${page.title}`)
494
+ lines.push(`- Source: \`${page.source}:${page.line}\``)
495
+ lines.push(`- Complete: ${page.complete ? 'yes' : 'no'}`)
496
+ lines.push(`- Field tables: ${page.fieldTables.length}`)
497
+ lines.push(`- Function tables: ${page.functionTables.length}`)
498
+ lines.push(`- Field design complete: ${page.hasCompleteFieldDesign ? 'yes' : 'no'}`)
499
+ if (page.issues.length)
500
+ lines.push(`- Issues: ${page.issues.join('; ')}`)
501
+ lines.push('')
502
+ })
503
+
504
+ if (report.navigationBlocks.length) {
505
+ lines.push('## Navigation Blocks', '')
506
+ report.navigationBlocks.forEach((block, index) => {
507
+ lines.push(`### Block ${index + 1} (${block.source}:${block.line})`, '')
508
+ lines.push('```text')
509
+ lines.push(block.text)
510
+ lines.push('```', '')
511
+ if (block.tree?.length) {
512
+ lines.push('Parsed menu/navigation tree:', '')
513
+ lines.push(...formatNavigationTree(block.tree))
514
+ lines.push('')
515
+ }
516
+ })
517
+ }
518
+
519
+ return lines.join('\n')
520
+ }
521
+
522
+ function main() {
523
+ const args = parseArgs(process.argv.slice(2))
524
+ if (args.help || !args.entry) {
525
+ console.log('Usage: node scripts/analyze-doc.mjs <entry-md> --repo-root <repo> --format json|markdown|both')
526
+ process.exit(args.help ? 0 : 1)
527
+ }
528
+
529
+ const repoRoot = path.resolve(args.repoRoot || process.cwd())
530
+ const report = analyze(args.entry, repoRoot)
531
+
532
+ if (args.format === 'json') {
533
+ console.log(JSON.stringify(report, null, 2))
534
+ return
535
+ }
536
+
537
+ if (args.format === 'markdown') {
538
+ console.log(toMarkdown(report))
539
+ return
540
+ }
541
+
542
+ console.log('---MARKDOWN---')
543
+ console.log(toMarkdown(report))
544
+ console.log('---JSON---')
545
+ console.log(JSON.stringify(report, null, 2))
546
+ }
547
+
548
+ try {
549
+ main()
550
+ }
551
+ catch (error) {
552
+ console.error(error instanceof Error ? error.message : String(error))
553
+ process.exit(1)
554
+ }