@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.
- package/README.md +256 -0
- package/bin/brms-skills.mjs +411 -0
- package/package.json +30 -0
- package/skills/brms-prototype-generator/SKILL.md +129 -0
- package/skills/brms-prototype-generator/agents/openai.yaml +7 -0
- package/skills/brms-prototype-generator/examples/few-shot-examples.md +577 -0
- package/skills/brms-prototype-generator/references/01-list-query.md +444 -0
- package/skills/brms-prototype-generator/references/02-form-entry.md +129 -0
- package/skills/brms-prototype-generator/references/03-detail-display.md +125 -0
- package/skills/brms-prototype-generator/references/04-composite-page-package.md +339 -0
- package/skills/brms-prototype-generator/references/05-dialog-patterns.md +113 -0
- package/skills/brms-prototype-generator/references/06-backend-request-patterns.md +118 -0
- package/skills/brms-prototype-generator/references/resource-index.md +46 -0
- package/skills/brms-prototype-generator/references/system-prompt.md +242 -0
- package/skills/brms-prototype-generator/scripts/analyze-doc.mjs +554 -0
- package/skills/brms-prototype-generator/scripts/check-project.mjs +228 -0
- package/skills/brms-prototype-generator/scripts/discover-targets.mjs +158 -0
- package/skills/brms-prototype-generator/scripts/install-codex.mjs +74 -0
- package/skills/brms-prototype-generator/scripts/plan-pages.mjs +390 -0
- package/skills/brms-prototype-generator/scripts/validate-generated.mjs +838 -0
- package/skills/brms-prototype-generator/templates/user-input-template.md +182 -0
- package/skills/brms-vxe-plus-developer/SKILL.md +105 -0
- package/skills/brms-vxe-plus-developer/agents/openai.yaml +7 -0
- package/skills/brms-vxe-plus-developer/references/prototype-to-real.md +54 -0
- package/skills/brms-vxe-plus-developer/references/real-base-development.md +110 -0
- package/skills/brms-vxe-plus-developer/references/resource-index.md +43 -0
- package/skills/brms-vxe-plus-developer/references/review-checklist.md +49 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/01-mental-model.md +150 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/02-vxe-plus-form.md +302 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/03-vxe-plus-table.md +253 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/04-example-map.md +488 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/05-request-and-eiinfo.md +170 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/90-anti-patterns.md +137 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/README.md +43 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/README.md +21 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/A1/P0/A1P01601.vue +483 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/A1/P1/A1P11011.vue +444 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AB/BP/ABBP0201.vue +1648 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AM/AF/component/AMAF0601/Bidding/formConfig.ts +228 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/AM/AF/component/AMAF0601/Record/columns.ts +110 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/BMBR01.vue +130 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/component/BMBR01/columns.ts +94 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/BM/BR/component/BMBR01/formConfig.ts +108 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Change/formConfig.ts +123 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Change/index.vue +103 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Clause/columns.ts +48 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Clause/index.vue +202 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Correcte/formConfig.ts +117 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Correcte/index.vue +103 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Explain/index.vue +68 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Fee/columns.ts +150 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Fee/index.vue +235 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Files/columns.ts +63 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Files/index.vue +117 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Goods/columns.ts +327 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Goods/index.vue +790 -0
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Main/index.vue +168 -0
- 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
- 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
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/columns.ts +256 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Party/index.vue +738 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Price/formConfig.ts +174 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Price/index.vue +51 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/PM/PC/component/PMPC0101/Top/index.vue +924 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/SM/SW/SMSW0101.vue +567 -0
- package/skills/brms-vxe-plus-developer/references/vxe-plus-knowledge/sources/project/base/src/views/demo/index.vue +448 -0
- package/skills/brms-vxe-plus-developer/scripts/check-project.mjs +259 -0
- package/skills/brms-vxe-plus-developer/scripts/check-vxe-plus-page.mjs +137 -0
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFileSync } from 'node:child_process'
|
|
3
|
+
import fs from 'node:fs'
|
|
4
|
+
import path from 'node:path'
|
|
5
|
+
|
|
6
|
+
function usage() {
|
|
7
|
+
console.log('Usage: node scripts/plan-pages.mjs <entry-md> --repo-root <repo> --target-project <project/name-or-path> --page-prefix <PAGE_PREFIX_4_PLUS> [--format json|markdown|both] [--out <file>]')
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function parseArgs(argv) {
|
|
11
|
+
const args = {
|
|
12
|
+
repoRoot: process.cwd(),
|
|
13
|
+
format: 'markdown',
|
|
14
|
+
pagePrefix: '',
|
|
15
|
+
out: '',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
19
|
+
const arg = argv[i]
|
|
20
|
+
if (arg === '--repo-root') {
|
|
21
|
+
args.repoRoot = argv[i + 1] || ''
|
|
22
|
+
i += 1
|
|
23
|
+
}
|
|
24
|
+
else if (arg === '--target-project') {
|
|
25
|
+
args.targetProject = argv[i + 1] || ''
|
|
26
|
+
i += 1
|
|
27
|
+
}
|
|
28
|
+
else if (arg === '--page-prefix') {
|
|
29
|
+
args.pagePrefix = (argv[i + 1] || '').toUpperCase()
|
|
30
|
+
i += 1
|
|
31
|
+
}
|
|
32
|
+
else if (arg === '--format') {
|
|
33
|
+
args.format = argv[i + 1] || ''
|
|
34
|
+
i += 1
|
|
35
|
+
}
|
|
36
|
+
else if (arg === '--out') {
|
|
37
|
+
args.out = argv[i + 1] || ''
|
|
38
|
+
i += 1
|
|
39
|
+
}
|
|
40
|
+
else if (arg === '--help' || arg === '-h') {
|
|
41
|
+
args.help = true
|
|
42
|
+
}
|
|
43
|
+
else if (!args.entry) {
|
|
44
|
+
args.entry = arg
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
throw new Error(`Unknown argument: ${arg}`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!['json', 'markdown', 'both'].includes(args.format))
|
|
52
|
+
throw new Error('--format must be one of: json, markdown, both')
|
|
53
|
+
if (!args.pagePrefix)
|
|
54
|
+
throw new Error('--page-prefix is required before page-package generation. Use the 4+ character page prefix, for example DMDA -> DMDA01.')
|
|
55
|
+
if (args.pagePrefix && !/^[A-Z0-9]{4,}$/.test(args.pagePrefix))
|
|
56
|
+
throw new Error('--page-prefix must be a 4+ character uppercase page prefix, for example DMDA -> DMDA01. Do not use a 2-character module prefix such as KQ.')
|
|
57
|
+
|
|
58
|
+
return args
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function toRepoRelative(filePath, repoRoot) {
|
|
62
|
+
return path.relative(repoRoot, filePath).replaceAll(path.sep, '/')
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeProjectPath(targetProject) {
|
|
66
|
+
const clean = targetProject.replaceAll('\\', '/').replace(/^\/+|\/+$/g, '')
|
|
67
|
+
if (!clean)
|
|
68
|
+
return ''
|
|
69
|
+
if (clean.startsWith('project/'))
|
|
70
|
+
return clean
|
|
71
|
+
return `project/${clean}`
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function discoverTarget(repoRoot, targetProject) {
|
|
75
|
+
const projectDir = normalizeProjectPath(targetProject)
|
|
76
|
+
if (!projectDir)
|
|
77
|
+
throw new Error('--target-project is required after target confirmation')
|
|
78
|
+
|
|
79
|
+
const absProjectDir = path.resolve(repoRoot, projectDir)
|
|
80
|
+
const srcDir = path.join(absProjectDir, 'src')
|
|
81
|
+
const viewsDir = path.join(srcDir, 'views')
|
|
82
|
+
const routeConfigDir = path.join(srcDir, 'routers', 'config')
|
|
83
|
+
const routeConfigIndex = path.join(routeConfigDir, 'index.ts')
|
|
84
|
+
const packageJson = path.join(absProjectDir, 'package.json')
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
projectDir: toRepoRelative(absProjectDir, repoRoot),
|
|
88
|
+
packageName: fs.existsSync(packageJson) ? JSON.parse(fs.readFileSync(packageJson, 'utf8')).name || '' : '',
|
|
89
|
+
viewsDir: fs.existsSync(viewsDir) ? toRepoRelative(viewsDir, repoRoot) : '',
|
|
90
|
+
routeConfigDir: fs.existsSync(routeConfigDir) ? toRepoRelative(routeConfigDir, repoRoot) : '',
|
|
91
|
+
routeConfigIndex: fs.existsSync(routeConfigIndex) ? toRepoRelative(routeConfigIndex, repoRoot) : '',
|
|
92
|
+
canRegisterStaticRoute: fs.existsSync(routeConfigDir) && fs.existsSync(routeConfigIndex),
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function runAnalyzer(entry, repoRoot) {
|
|
97
|
+
const script = path.join(repoRoot, '.claude', 'skills', 'brms-prototype-generator', 'scripts', 'analyze-doc.mjs')
|
|
98
|
+
const output = execFileSync(process.execPath, [script, entry, '--repo-root', repoRoot, '--format', 'json'], {
|
|
99
|
+
cwd: repoRoot,
|
|
100
|
+
encoding: 'utf8',
|
|
101
|
+
maxBuffer: 20 * 1024 * 1024,
|
|
102
|
+
})
|
|
103
|
+
return JSON.parse(output)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function pad2(value) {
|
|
107
|
+
return String(value).padStart(2, '0')
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function inferPageType(page) {
|
|
111
|
+
const text = `${page.title} ${page.purpose || ''}`.toLowerCase()
|
|
112
|
+
if (/附件|上传|资料|attachment|upload/.test(text))
|
|
113
|
+
return 'attachment-management'
|
|
114
|
+
if (/任务|提醒|通知|task/.test(text))
|
|
115
|
+
return 'task-management'
|
|
116
|
+
if (/详情|detail/.test(text))
|
|
117
|
+
return 'detail-display'
|
|
118
|
+
if (/新增|录入|填报|申请|entry|form/.test(text))
|
|
119
|
+
return 'form-entry'
|
|
120
|
+
if (/结构|层级|上下级|主从|master/.test(text))
|
|
121
|
+
return 'master-detail'
|
|
122
|
+
if (/查询|综合|query|search/.test(text))
|
|
123
|
+
return 'list-query'
|
|
124
|
+
return 'list-maintenance'
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function requiredResources(type, page) {
|
|
128
|
+
const resources = new Set(['references/resource-index.md'])
|
|
129
|
+
if (['list-query', 'list-maintenance', 'attachment-management', 'task-management', 'master-detail'].includes(type))
|
|
130
|
+
resources.add('references/01-list-query.md')
|
|
131
|
+
if (type === 'form-entry')
|
|
132
|
+
resources.add('references/02-form-entry.md')
|
|
133
|
+
if (type === 'detail-display')
|
|
134
|
+
resources.add('references/03-detail-display.md')
|
|
135
|
+
if (type === 'master-detail')
|
|
136
|
+
resources.add('references/04-composite-page-package.md')
|
|
137
|
+
if (['list-maintenance', 'master-detail', 'attachment-management', 'task-management'].includes(type))
|
|
138
|
+
resources.add('references/05-dialog-patterns.md')
|
|
139
|
+
|
|
140
|
+
const text = `${page.title} ${page.purpose || ''} ${page.functionTables?.map(table => table.headers.join(' ')).join(' ') || ''}`
|
|
141
|
+
if (/新增|编辑|详情|变更|弹窗|抽屉|上传|附件|modal|dialog|drawer|upload|attachment/i.test(text))
|
|
142
|
+
resources.add('references/05-dialog-patterns.md')
|
|
143
|
+
|
|
144
|
+
return Array.from(resources)
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function buildPageCode(pagePrefix, index) {
|
|
148
|
+
if (!pagePrefix)
|
|
149
|
+
return `CONFIRM_CODE_${pad2(index)}`
|
|
150
|
+
return `${pagePrefix}${pad2(index)}`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function pageTargetFile(target, pageCode) {
|
|
154
|
+
if (!target.viewsDir || /^CONFIRM_CODE_/.test(pageCode))
|
|
155
|
+
return ''
|
|
156
|
+
const module = pageCode.slice(0, 2)
|
|
157
|
+
const submodule = pageCode.slice(2, 4)
|
|
158
|
+
return `${target.viewsDir}/${module}/${submodule}/${pageCode}.vue`
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function collectNavigationTitles(nodes, result = []) {
|
|
162
|
+
for (const node of nodes || []) {
|
|
163
|
+
result.push(node.note ? `${node.title} (${node.note})` : node.title)
|
|
164
|
+
collectNavigationTitles(node.children || [], result)
|
|
165
|
+
}
|
|
166
|
+
return result
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function collectNavigationEdges(nodes, parent = null, result = []) {
|
|
170
|
+
for (const node of nodes || []) {
|
|
171
|
+
if (parent) {
|
|
172
|
+
result.push({
|
|
173
|
+
fromTitle: parent.title,
|
|
174
|
+
fromNote: parent.note || '',
|
|
175
|
+
toTitle: node.title,
|
|
176
|
+
toNote: node.note || '',
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
collectNavigationEdges(node.children || [], node, result)
|
|
180
|
+
}
|
|
181
|
+
return result
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeTitle(value) {
|
|
185
|
+
return String(value || '').replace(/[()()\s]/g, '')
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function buildTitleToPage(pages) {
|
|
189
|
+
const exact = new Map()
|
|
190
|
+
for (const page of pages)
|
|
191
|
+
exact.set(normalizeTitle(page.title), page)
|
|
192
|
+
return exact
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function attachNavigationContracts(pages, report) {
|
|
196
|
+
const titleToPage = buildTitleToPage(pages)
|
|
197
|
+
const edges = report.navigationBlocks.flatMap(block => collectNavigationEdges(block.tree || []))
|
|
198
|
+
const unresolved = []
|
|
199
|
+
|
|
200
|
+
for (const edge of edges) {
|
|
201
|
+
const fromPage = titleToPage.get(normalizeTitle(edge.fromTitle))
|
|
202
|
+
const toPage = titleToPage.get(normalizeTitle(edge.toTitle))
|
|
203
|
+
const contract = {
|
|
204
|
+
fromTitle: edge.fromTitle,
|
|
205
|
+
toTitle: edge.toTitle,
|
|
206
|
+
note: edge.toNote || edge.fromNote || '',
|
|
207
|
+
requiredSourceEntry: 'visible button, business-field click, card, tab, or row/cell click',
|
|
208
|
+
requiredPush: toPage ? `router.push({ path: '/web/${toPage.pageCode}', query: {...} })` : 'resolve target page code before generating',
|
|
209
|
+
requiredTargetRead: 'target page reads route.query as default filter or context',
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (fromPage && toPage) {
|
|
213
|
+
fromPage.outboundNavigation.push(contract)
|
|
214
|
+
toPage.inboundNavigation.push(contract)
|
|
215
|
+
}
|
|
216
|
+
else if (!fromPage && toPage) {
|
|
217
|
+
toPage.menuEntries.push({
|
|
218
|
+
fromTitle: edge.fromTitle,
|
|
219
|
+
note: edge.toNote || edge.fromNote || '',
|
|
220
|
+
})
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
unresolved.push(contract)
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return unresolved
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function createPlan(report, target, pagePrefix) {
|
|
231
|
+
const completePages = report.pages.filter(page => page.complete)
|
|
232
|
+
const pages = completePages.map((page, index) => {
|
|
233
|
+
const pageCode = buildPageCode(pagePrefix, index + 1)
|
|
234
|
+
const type = inferPageType(page)
|
|
235
|
+
return {
|
|
236
|
+
index: index + 1,
|
|
237
|
+
title: page.title,
|
|
238
|
+
pageCode,
|
|
239
|
+
type,
|
|
240
|
+
source: `${page.source}:${page.line}`,
|
|
241
|
+
targetFile: pageTargetFile(target, pageCode),
|
|
242
|
+
dataSource: 'mock',
|
|
243
|
+
fieldDesignComplete: page.hasCompleteFieldDesign,
|
|
244
|
+
menuEntries: [],
|
|
245
|
+
inboundNavigation: [],
|
|
246
|
+
outboundNavigation: [],
|
|
247
|
+
issues: page.issues || [],
|
|
248
|
+
requiredResources: requiredResources(type, page),
|
|
249
|
+
forbiddenFiles: [
|
|
250
|
+
`${target.projectDir}/src/routers/config/localMenu.ts`,
|
|
251
|
+
'package.json',
|
|
252
|
+
'pnpm-lock.yaml',
|
|
253
|
+
],
|
|
254
|
+
workerScope: pageTargetFile(target, pageCode) || '<confirm target file after page code is assigned>',
|
|
255
|
+
validationCommand: pageTargetFile(target, pageCode)
|
|
256
|
+
? `node .claude/skills/brms-prototype-generator/scripts/validate-generated.mjs ${pageTargetFile(target, pageCode)} --repo-root .`
|
|
257
|
+
: 'assign page code first, then run validate-generated.mjs on the generated file',
|
|
258
|
+
}
|
|
259
|
+
})
|
|
260
|
+
const unresolvedNavigation = attachNavigationContracts(pages, report)
|
|
261
|
+
|
|
262
|
+
return {
|
|
263
|
+
entry: report.entry,
|
|
264
|
+
target,
|
|
265
|
+
summary: {
|
|
266
|
+
pageCount: report.summary.pageCount,
|
|
267
|
+
completePages: report.summary.completePages,
|
|
268
|
+
plannedPages: pages.length,
|
|
269
|
+
pagesMissingFieldKeys: report.summary.pagesMissingFieldKeys,
|
|
270
|
+
menuStructureDetected: report.summary.menuStructureDetected,
|
|
271
|
+
recommendedGenerationMode: report.summary.completePages >= 4 ? 'coordinator-subagents' : 'single-agent-package',
|
|
272
|
+
},
|
|
273
|
+
navigation: {
|
|
274
|
+
blocks: report.navigationBlocks.length,
|
|
275
|
+
titles: report.navigationBlocks.flatMap(block => collectNavigationTitles(block.tree || [])),
|
|
276
|
+
unresolved: unresolvedNavigation,
|
|
277
|
+
},
|
|
278
|
+
globalRules: [
|
|
279
|
+
'Do not reread the full product Markdown for each page; use this plan and the assigned page packet.',
|
|
280
|
+
'Page codes must be 6+ uppercase letters/digits and use a 4+ page prefix, for example DMDA01 maps to src/views/DM/DA/DMDA01.vue.',
|
|
281
|
+
'Default to mock-backed UI unless explicit serviceName and methodName are documented.',
|
|
282
|
+
'Every documented cross-page navigation edge must have a source UI entry with router.push({ path: "/web/<PAGE_CODE>", query }) and a target page that reads route.query.',
|
|
283
|
+
'Unresolved navigation nodes in this plan are blocking questions; map them to a generated page or ask the user before dropping the jump.',
|
|
284
|
+
'Coordinator owns shared data, route config, final validation, and handoff summary.',
|
|
285
|
+
'Workers may edit only their assigned page file.',
|
|
286
|
+
],
|
|
287
|
+
pages,
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
function toMarkdown(plan) {
|
|
292
|
+
const lines = [
|
|
293
|
+
'# BRMS Prototype Generation Plan',
|
|
294
|
+
'',
|
|
295
|
+
`- Entry: \`${plan.entry}\``,
|
|
296
|
+
`- Target project: \`${plan.target.projectDir}\``,
|
|
297
|
+
`- Package: \`${plan.target.packageName || 'unknown'}\``,
|
|
298
|
+
`- Static route config: ${plan.target.canRegisterStaticRoute ? `yes (\`${plan.target.routeConfigDir}\`)` : 'no'}`,
|
|
299
|
+
`- Planned pages: ${plan.summary.plannedPages}`,
|
|
300
|
+
`- Recommended mode: \`${plan.summary.recommendedGenerationMode}\``,
|
|
301
|
+
`- Pages missing field keys: ${plan.summary.pagesMissingFieldKeys}`,
|
|
302
|
+
`- Menu structure detected: ${plan.summary.menuStructureDetected ? 'yes' : 'no'}`,
|
|
303
|
+
'',
|
|
304
|
+
'## Global Rules',
|
|
305
|
+
'',
|
|
306
|
+
...plan.globalRules.map(rule => `- ${rule}`),
|
|
307
|
+
'',
|
|
308
|
+
]
|
|
309
|
+
|
|
310
|
+
if (plan.navigation.titles.length) {
|
|
311
|
+
lines.push('## Navigation Titles', '')
|
|
312
|
+
plan.navigation.titles.forEach(title => lines.push(`- ${title}`))
|
|
313
|
+
lines.push('')
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
if (plan.navigation.unresolved.length) {
|
|
317
|
+
lines.push('## Unresolved Navigation', '')
|
|
318
|
+
plan.navigation.unresolved.forEach((item) => {
|
|
319
|
+
lines.push(`- ${item.fromTitle} -> ${item.toTitle}${item.note ? ` (${item.note})` : ''}: map to a generated page or ask before dropping this jump.`)
|
|
320
|
+
})
|
|
321
|
+
lines.push('')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
lines.push('## Page Packets', '')
|
|
325
|
+
plan.pages.forEach((page) => {
|
|
326
|
+
lines.push(`### ${page.index}. ${page.title}`)
|
|
327
|
+
lines.push(`- Page code: \`${page.pageCode}\``)
|
|
328
|
+
lines.push(`- Type: \`${page.type}\``)
|
|
329
|
+
lines.push(`- Source: \`${page.source}\``)
|
|
330
|
+
lines.push(`- Target file: ${page.targetFile ? `\`${page.targetFile}\`` : '`assign after page code confirmation`'}`)
|
|
331
|
+
lines.push(`- Data source: \`${page.dataSource}\``)
|
|
332
|
+
lines.push(`- Field design complete: ${page.fieldDesignComplete ? 'yes' : 'no'}`)
|
|
333
|
+
if (page.issues.length)
|
|
334
|
+
lines.push(`- Issues: ${page.issues.join('; ')}`)
|
|
335
|
+
if (page.menuEntries.length) {
|
|
336
|
+
lines.push('- Menu/navigation entries:')
|
|
337
|
+
page.menuEntries.forEach(item => lines.push(` - From ${item.fromTitle}${item.note ? ` (${item.note})` : ''}`))
|
|
338
|
+
}
|
|
339
|
+
if (page.inboundNavigation.length) {
|
|
340
|
+
lines.push('- Inbound navigation:')
|
|
341
|
+
page.inboundNavigation.forEach(item => lines.push(` - ${item.fromTitle} -> ${item.toTitle}: ${item.requiredTargetRead}`))
|
|
342
|
+
}
|
|
343
|
+
if (page.outboundNavigation.length) {
|
|
344
|
+
lines.push('- Outbound navigation:')
|
|
345
|
+
page.outboundNavigation.forEach(item => lines.push(` - ${item.fromTitle} -> ${item.toTitle}: ${item.requiredPush}; ${item.requiredSourceEntry}`))
|
|
346
|
+
}
|
|
347
|
+
lines.push(`- Required resources: ${page.requiredResources.map(resource => `\`${resource}\``).join(', ')}`)
|
|
348
|
+
lines.push(`- Worker scope: \`${page.workerScope}\``)
|
|
349
|
+
lines.push(`- Validation: \`${page.validationCommand}\``)
|
|
350
|
+
lines.push('')
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
return lines.join('\n')
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
function main() {
|
|
357
|
+
const args = parseArgs(process.argv.slice(2))
|
|
358
|
+
if (args.help || !args.entry) {
|
|
359
|
+
usage()
|
|
360
|
+
process.exit(args.help ? 0 : 1)
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const repoRoot = path.resolve(args.repoRoot || process.cwd())
|
|
364
|
+
const target = discoverTarget(repoRoot, args.targetProject)
|
|
365
|
+
const report = runAnalyzer(args.entry, repoRoot)
|
|
366
|
+
const plan = createPlan(report, target, args.pagePrefix)
|
|
367
|
+
const json = JSON.stringify(plan, null, 2)
|
|
368
|
+
const markdown = toMarkdown(plan)
|
|
369
|
+
|
|
370
|
+
if (args.out) {
|
|
371
|
+
const outFile = path.resolve(repoRoot, args.out)
|
|
372
|
+
fs.mkdirSync(path.dirname(outFile), { recursive: true })
|
|
373
|
+
fs.writeFileSync(outFile, args.out.endsWith('.json') ? json : markdown, 'utf8')
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (args.format === 'json' || args.format === 'both')
|
|
377
|
+
console.log(json)
|
|
378
|
+
if (args.format === 'both')
|
|
379
|
+
console.log('\n---\n')
|
|
380
|
+
if (args.format === 'markdown' || args.format === 'both')
|
|
381
|
+
console.log(markdown)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
try {
|
|
385
|
+
main()
|
|
386
|
+
}
|
|
387
|
+
catch (error) {
|
|
388
|
+
console.error(error instanceof Error ? error.message : String(error))
|
|
389
|
+
process.exit(1)
|
|
390
|
+
}
|