@qilitt-mickey/vue3-temp-skill 1.1.25 → 1.1.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.
Files changed (42) hide show
  1. package/SKILL.md +77 -37
  2. package/package.json +15 -1
  3. package/references/auth-pages.md +217 -0
  4. package/references/base-code-dict.md +7 -0
  5. package/references/chart-echarts.md +1 -1
  6. package/references/crud-pages.md +1 -1
  7. package/references/data-compare.md +7 -0
  8. package/references/data-mapping.md +7 -0
  9. package/references/data-screen.md +7 -0
  10. package/references/data-writeback.md +7 -0
  11. package/references/directives-advanced.md +7 -0
  12. package/references/download-export.md +7 -0
  13. package/references/feedback-loading.md +7 -0
  14. package/references/feedback-ui.md +7 -0
  15. package/references/file-management.md +7 -0
  16. package/references/flowchart-g6.md +7 -0
  17. package/references/form-advanced.md +1 -1
  18. package/references/graph-relation.md +7 -0
  19. package/references/i18n-localization.md +230 -0
  20. package/references/layout-theme.md +1 -1
  21. package/references/particles.md +7 -0
  22. package/references/project-inventory.md +7 -0
  23. package/references/qrcode-barcode.md +7 -0
  24. package/references/rich-text.md +7 -0
  25. package/references/seamless-scroll.md +7 -0
  26. package/references/table-vxe.md +1 -1
  27. package/references/tree-table.md +7 -0
  28. package/references/verify-captcha.md +7 -0
  29. package/references/websocket-realtime.md +7 -0
  30. package/references/workflow-bpmn.md +7 -0
  31. package/scripts/ai-content-audit.mjs +244 -0
  32. package/scripts/ai-match-audit.mjs +348 -0
  33. package/scripts/ai-workflow-sim.mjs +393 -0
  34. package/scripts/analyze-deps.mjs +278 -0
  35. package/scripts/check.cmd +7 -0
  36. package/scripts/check.mjs +95 -0
  37. package/scripts/cli.mjs +153 -0
  38. package/scripts/generate-index.mjs +214 -0
  39. package/scripts/match.py +323 -0
  40. package/scripts/modules.json +348 -0
  41. package/scripts/scaffold-module.mjs +232 -0
  42. package/scripts/validate.mjs +256 -0
@@ -0,0 +1,230 @@
1
+ ---
2
+ skill: i18n-localization
3
+ description: vue-i18n 国际化、locales 语言包、ReLanguageSwitch 语言切换、store/modules/lang
4
+ scope: project
5
+ tags: [国际化, 多语言, i18n, vue-i18n, locale, 语言切换, 语言包, $t, useI18n, ReLanguageSwitch]
6
+ ---
7
+
8
+ # 国际化(i18n)
9
+
10
+ > 使用 `vue-i18n` + `src/locales/` + `ReLanguageSwitch`,勿另起一套 i18n 方案。
11
+
12
+ ## 架构
13
+
14
+ ```
15
+ src/
16
+ ├── locales/
17
+ │ ├── index.ts # i18n 实例创建,合并 Element Plus / Vant / 自定义语言包
18
+ │ ├── en-US.json # 英文翻译(按模块分组)
19
+ │ └── zh-Cn.json # 中文翻译(按模块分组)
20
+ ├── utils/
21
+ │ └── i18n.ts # getLanguage():localStorage + Cookie + navigator.language 回退
22
+ ├── store/modules/
23
+ │ └── lang.ts # useLangStore:语言状态管理 + 持久化
24
+ ├── components/
25
+ │ └── ReLanguageSwitch/ # 语言切换下拉组件
26
+ └── main.ts # app.use(i18n) + $t 全局注入
27
+ ```
28
+
29
+ ## 创建 i18n 实例
30
+
31
+ ```typescript
32
+ // src/locales/index.ts
33
+ import elenLocale from 'element-plus/es/locale/lang/en'
34
+ import elzhLocale from 'element-plus/es/locale/lang/zh-cn'
35
+ import venLocale from 'vant/es/locale/lang/en-US'
36
+ import vzhLocale from 'vant/es/locale/lang/zh-CN'
37
+
38
+ import { createI18n } from 'vue-i18n'
39
+ import { getLanguage } from '@/utils/i18n'
40
+ import enLocale from './en-US.json'
41
+ import zhLocale from './zh-Cn.json'
42
+
43
+ const messages = {
44
+ en: {
45
+ ...elenLocale,
46
+ ...venLocale,
47
+ ...enLocale,
48
+ },
49
+ zh: {
50
+ ...elzhLocale,
51
+ ...vzhLocale,
52
+ ...zhLocale,
53
+ },
54
+ }
55
+
56
+ export const i18n = createI18n({
57
+ legacy: false,
58
+ locale: getLanguage(),
59
+ fallbackLocale: 'zh',
60
+ messages,
61
+ globalInjection: true,
62
+ })
63
+
64
+ export const $t: typeof i18n.global.t = i18n.global.t
65
+ export default i18n
66
+ ```
67
+
68
+ ## 语言检测
69
+
70
+ ```typescript
71
+ // src/utils/i18n.ts
72
+ import { storageLocal } from '@pureadmin/utils'
73
+ import Cookies from 'js-cookie'
74
+
75
+ export function getLanguage() {
76
+ const lang = storageLocal().getItem<string>('lang') || Cookies.get('lang')
77
+ if (!lang) {
78
+ return navigator.language.startsWith('en') ? 'en' : 'zh'
79
+ }
80
+ return lang
81
+ }
82
+ ```
83
+
84
+ 优先级:`localStorage('lang')` > `Cookie('lang')` > `navigator.language` 回退。
85
+
86
+ ## 语言状态管理
87
+
88
+ ```typescript
89
+ // src/store/modules/lang.ts
90
+ import { storageLocal } from '@pureadmin/utils'
91
+ import Cookies from 'js-cookie'
92
+ import i18n from '@/locales/index'
93
+ import { store } from '@/store'
94
+ import { getLanguage } from '@/utils/i18n'
95
+
96
+ export const useLangStore = defineStore('Lang', {
97
+ state: () => ({
98
+ lang: getLanguage(),
99
+ }),
100
+ actions: {
101
+ setLang(lang) {
102
+ this.lang = lang
103
+ storageLocal().setItem('lang', lang)
104
+ i18n.global.locale.value = lang
105
+ Cookies.set('lang', lang)
106
+ },
107
+ },
108
+ })
109
+
110
+ export function useLangStoreHook() {
111
+ return useLangStore(store)
112
+ }
113
+ ```
114
+
115
+ ## 语言切换组件
116
+
117
+ ```vue
118
+ <!-- src/components/ReLanguageSwitch/src/index.vue -->
119
+ <script setup lang="ts">
120
+ import { useGlobal } from '@pureadmin/utils'
121
+ import { ElDropdown, ElDropdownItem, ElDropdownMenu } from 'element-plus'
122
+ import { useLangStore } from '@/store/modules/lang'
123
+
124
+ defineOptions({ name: 'LanguageSwitch' })
125
+
126
+ const langStore = useLangStore()
127
+ const { $t } = useGlobal<GlobalPropertiesApi>()
128
+
129
+ const langSelectList = reactive([
130
+ { command: 'en', name: 'English', disabled: langStore.lang === 'en' },
131
+ { command: 'zh', name: $t('中文'), disabled: langStore.lang === 'zh' },
132
+ ])
133
+
134
+ function handleSetLang(lang) {
135
+ langStore.setLang(lang)
136
+ window.location.reload()
137
+ }
138
+ </script>
139
+
140
+ <template>
141
+ <ElDropdown @command="handleSetLang">
142
+ <div class="language-trigger">
143
+ <div class="i-ri:translate-2 language-icon" />
144
+ </div>
145
+ <template #dropdown>
146
+ <ElDropdownMenu>
147
+ <ElDropdownItem
148
+ v-for="{ command, name } in langSelectList"
149
+ :key="command"
150
+ :command="command"
151
+ :disabled="langStore.lang === command"
152
+ >
153
+ {{ name }}
154
+ </ElDropdownItem>
155
+ </ElDropdownMenu>
156
+ </template>
157
+ </ElDropdown>
158
+ </template>
159
+ ```
160
+
161
+ ## main.ts 注册
162
+
163
+ ```typescript
164
+ import i18n from '@/locales'
165
+
166
+ const app = createApp(App)
167
+ app.use(i18n)
168
+ app.config.globalProperties.$t = i18n.global.t
169
+ ```
170
+
171
+ ## 语言包结构
172
+
173
+ ```json
174
+ // src/locales/zh-Cn.json
175
+ {
176
+ "请填写正确的验证码": "请填写正确的验证码",
177
+ "错误提示": "错误提示",
178
+ "确定": "确定",
179
+ "取消": "取消",
180
+ "登录": "登录",
181
+ "退出登录": "退出登录"
182
+ }
183
+ ```
184
+
185
+ ```json
186
+ // src/locales/en-US.json
187
+ {
188
+ "请填写正确的验证码": "Please enter the correct verification code",
189
+ "错误提示": "Error",
190
+ "确定": "OK",
191
+ "取消": "Cancel",
192
+ "登录": "Login",
193
+ "退出登录": "Log Out"
194
+ }
195
+ ```
196
+
197
+ ## 使用方式
198
+
199
+ ### 模板中使用
200
+
201
+ ```vue
202
+ <template>
203
+ <span>{{ $t('登录') }}</span>
204
+ <ElButton>{{ $t('确定') }}</ElButton>
205
+ </template>
206
+ ```
207
+
208
+ ### 脚本中使用
209
+
210
+ ```typescript
211
+ import { useGlobal } from '@pureadmin/utils'
212
+ const { $t } = useGlobal<GlobalPropertiesApi>()
213
+ const message = $t('请填写正确的验证码')
214
+ ```
215
+
216
+ ### 新增语言
217
+
218
+ 1. 在 `src/locales/` 下新建语言包文件(如 `ja-JP.json`)
219
+ 2. 在 `src/locales/index.ts` 中 import 并合并到 `messages`
220
+ 3. 在 `ReLanguageSwitch` 的 `langSelectList` 中添加选项
221
+ 4. 在 `store/modules/lang.ts` 的 `setLang` 中同步持久化
222
+
223
+ ## Must-do 清单
224
+
225
+ 1. **勿用中文硬编码**:所有用户可见文案用 `$t('中文 key')` 包裹
226
+ 2. **语言包 key 用中文**:以中文原文作为 key,`$t('请填写正确的验证码')` 而非 `$t('enterCorrectCode')`
227
+ 3. **Element Plus / Vant 语言包合并**:`createI18n` 时须合并三方库自带语言包,确保组件内置文案同步切换
228
+ 4. **切换后 reload**:`langStore.setLang(lang)` 后调 `window.location.reload()`,确保所有组件重新渲染
229
+ 5. **持久化双写**:`localStorage` + `Cookie` 同时写入,兼容服务端读取
230
+ 6. **回退策略**:`fallbackLocale: 'zh'`,未翻译的 key 回退到中文
@@ -2,7 +2,7 @@
2
2
  skill: layout-theme
3
3
  description: 布局、主题色、暗黑模式、侧栏配色、Design Tokens、UnoCSS、响应式。修改布局/主题/颜色时加载。上游设计产出落地见 design-handoff / design-system。
4
4
  scope: project
5
- tags: [vue3, layout, theme, dark-mode, css-variables, design-tokens, element-plus, vant, dual-ui, unocss, chroma-js]
5
+ tags: [vue3, layout, theme, dark-mode, css-variables, design-tokens, element-plus, vant, dual-ui, unocss, chroma-js, 标签页, 面包屑, tabs, breadcrumb]
6
6
  ---
7
7
 
8
8
  # 设计系统、布局与主题适配
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: particles
3
+ description: tsParticles 登录背景粒子、@tsparticles/slim、particlesOptions
4
+ scope: project
5
+ tags: [粒子, tsparticles, 登录背景粒子, 粒子动画]
6
+ ---
7
+
1
8
  # 粒子特效(tsParticles)
2
9
 
3
10
  > 按本文件示例编写,复用项目已有 `@tsparticles/*` 注册与 `src/utils/particles.ts` 配置,勿另起一套粒子实现,勿引入 `particles.js`。
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: project-inventory
3
+ description: 项目资产清单:三方库、Re* 组件、依赖安装表
4
+ scope: project
5
+ tags: [查Re, 版本, 安装, 依赖清单, 库版本]
6
+ ---
7
+
1
8
  # 项目资产清单(Project Inventory)
2
9
 
3
10
  ## 何时读本文件
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: qrcode-barcode
3
+ description: 二维码、条形码相关 Re 组件与依赖
4
+ scope: project
5
+ tags: [二维码, 条形码, qrcode, barcode, 扫码]
6
+ ---
7
+
1
8
  # 二维码与条形码
2
9
 
3
10
  > 按本文件示例编写,复用项目已有 `ReQrcode` / `ReBarcode` 等封装,勿另起一套码生成实现。
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: rich-text
3
+ description: '@wangeditor/editor + ReRichText 富文本使用方式、图片/视频上传'
4
+ scope: project
5
+ tags: [富文本, wangeditor, ReRichText, 编辑器, Markdown]
6
+ ---
7
+
1
8
  # 富文本编辑器
2
9
 
3
10
  > 按本文件示例编写;富文本使用选型表中的 `@wangeditor/editor` + `ReRichText`,勿改用 Quill / TinyMCE 等;新依赖须待确认。
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: seamless-scroll
3
+ description: ReSeamlessScroll 无缝滚动、classOption、关键滚动约定
4
+ scope: project
5
+ tags: [无缝滚动, 跑马灯, marquee, 滚动公告, 滚动通知]
6
+ ---
7
+
1
8
  # 无缝滚动
2
9
 
3
10
  > 按本文件示例编写,复用项目已有 `ReSeamlessScroll` 等封装,勿另起一套滚动实现。
@@ -2,7 +2,7 @@
2
2
  skill: table-vxe
3
3
  description: vxe-table / vxe-pc-ui 高性能表格规范。大数据量或复杂网格编辑时加载。自带完整示例。
4
4
  scope: project
5
- tags: [vxe-table, vxe-pc-ui, grid, performance, table]
5
+ tags: [vxe-table, vxe-pc-ui, grid, performance, table, 单元格编辑, 可编辑表格]
6
6
  ---
7
7
 
8
8
  # VXE 高性能表格
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: tree-table
3
+ description: handleTree/buildHierarchyTree 和 el-table 树形表格、树与表格联动
4
+ scope: project
5
+ tags: [树表, ReTreeLine, handleTree, 树形, 树状, 层级表格, 组织架构, 部门树, 树形列表, 树状表格]
6
+ ---
7
+
1
8
  # 树形表格与树结构
2
9
 
3
10
  > 使用 `handleTree` / `ReTreeLine` + `el-table` tree,按本示例编写。
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: verify-captcha
3
+ description: 滑块拼图 ReVerify、拖拽验证 ReDragVerify、图片验证码、短信验证码
4
+ scope: project
5
+ tags: [验证码, 滑块, 短信验证, captcha, 拖拽验证, 图片验证码]
6
+ ---
7
+
1
8
  # 验证码与校验组件
2
9
 
3
10
  > 按本文件示例编写,复用项目已有验证码相关组件与封装,勿另起一套校验交互。
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: websocket-realtime
3
+ description: WebSocket 实时通信架构 utils/websocket + useWebSocket
4
+ scope: project
5
+ tags: [websocket, ws, 实时, 消息推送, 实时推送, 在线状态]
6
+ ---
7
+
1
8
  # WebSocket 实时通信
2
9
 
3
10
  > 使用 `utils/websocket` + `useWebSocket`,勿另起一套 WS 管理器。
@@ -1,3 +1,10 @@
1
+ ---
2
+ skill: workflow-bpmn
3
+ description: bpmn-js 只读 Viewer 使用方式,明确不用 Modeler
4
+ scope: project
5
+ tags: [bpmn, 流程审批, 审批流, BPMN流程, 工作流, 审批流程]
6
+ ---
7
+
1
8
  # BPMN 工作流查看器
2
9
 
3
10
  > 按本文件示例编写;必须使用选型表中的 `bpmn-js` Viewer(见 project-inventory),勿改用同类其他流程引擎;新依赖须待确认。
@@ -0,0 +1,244 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * 参考文件 AI 可用性审计
5
+ *
6
+ * 从 AI 代码生成角度审计每个 references/*.md 文件:
7
+ * 1. 标准样板 — 是否有完整可抄的代码块(AI 最依赖的"整段照抄")
8
+ * 2. Must-do 清单 — 是否有 AI 可逐项核对的检查项
9
+ * 3. 完整性守则 — 是否声明了"不能只抄部分"的约束
10
+ * 4. 依赖信息 — 是否说明了需要什么包/组件/Hook
11
+ * 5. 交叉引用 — 是否标注了何时同时加载其他模块
12
+ * 6. 代码块完备性 — 代码块是否完整(有开始标签和结束标签)
13
+ * 7. 可替换标记 — 样板中是否有明确的占位符(如 Customer、customer)
14
+ *
15
+ * 核心思路:AI 生成代码时是"照抄样板 + 改名",如果样板不完整或缺约束,
16
+ * AI 生成的代码就会出问题。本脚本检测这些"AI 生成质量风险点"。
17
+ *
18
+ * 用法:
19
+ * node scripts/ai-content-audit.mjs # 全量审计
20
+ * node scripts/ai-content-audit.mjs crud-pages # 审计单个模块
21
+ * node scripts/ai-content-audit.mjs --json # JSON 输出
22
+ */
23
+
24
+ import fs from "fs";
25
+ import path from "path";
26
+ import { fileURLToPath } from "url";
27
+
28
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
29
+ const ROOT = path.resolve(__dirname, "..");
30
+ const REFS_DIR = path.join(ROOT, "references");
31
+
32
+ const c = {
33
+ red: (s) => `\x1b[31m${s}\x1b[0m`,
34
+ green: (s) => `\x1b[32m${s}\x1b[0m`,
35
+ yellow: (s) => `\x1b[33m${s}\x1b[0m`,
36
+ cyan: (s) => `\x1b[36m${s}\x1b[0m`,
37
+ bold: (s) => `\x1b[1m${s}\x1b[0m`,
38
+ dim: (s) => `\x1b[2m${s}\x1b[0m`,
39
+ };
40
+
41
+ // ── 审计单个文件 ─────────────────────────────────────────
42
+ function auditFile(id, content) {
43
+ const report = {
44
+ moduleId: id,
45
+ size: content.length,
46
+ checks: {},
47
+ risks: [],
48
+ score: 0,
49
+ };
50
+
51
+ // 1. 标准样板检测
52
+ const hasTemplate =
53
+ /标准样板|强制样板|完整示例/.test(content) ||
54
+ /照抄|整段/.test(content);
55
+ const codeBlocks = content.match(/```[\s\S]*?```/g) || [];
56
+ const vueBlocks = codeBlocks.filter((b) => b.startsWith("```vue"));
57
+ const tsBlocks = codeBlocks.filter((b) => b.startsWith("```typescript") || b.startsWith("```ts"));
58
+ const hasCompleteCode = codeBlocks.length > 0;
59
+ report.checks.template = {
60
+ hasStandardTemplate: hasTemplate,
61
+ codeBlockCount: codeBlocks.length,
62
+ vueBlockCount: vueBlocks.length,
63
+ tsBlockCount: tsBlocks.length,
64
+ };
65
+ if (!hasCompleteCode) report.risks.push("无代码块 — AI 无样板可抄");
66
+ if (hasTemplate && codeBlocks.length === 0) report.risks.push("声明了「标准样板」但无代码块");
67
+
68
+ // 2. Must-do 清单检测
69
+ const hasMustDo = /Must-do|必须|清单/.test(content);
70
+ const checklistItems = (content.match(/^- \[ \]/gm) || []).length +
71
+ (content.match(/^\d+\.\s/gm) || []).length;
72
+ report.checks.mustDo = {
73
+ hasMustDoSection: hasMustDo,
74
+ checklistItemCount: checklistItems,
75
+ };
76
+ if (!hasMustDo) report.risks.push("无 Must-do 清单 — AI 生成后无法逐项核对");
77
+
78
+ // 3. 完整性守则检测
79
+ const hasCompletenessRule = /完整性守则|完整性|整段|禁止只/.test(content);
80
+ report.checks.completeness = {
81
+ hasCompletenessRule,
82
+ };
83
+ if (!hasCompletenessRule) report.risks.push("无完整性守则 — AI 可能只抄 API 签名丢掉配套段落");
84
+
85
+ // 4. 依赖信息检测
86
+ const hasDeps = /依赖|pnpm add|package\.json|缺则先装/.test(content);
87
+ const depMatches = content.match(/`([\w@/-]+)@([\d.]+)`/g) || [];
88
+ report.checks.dependencies = {
89
+ hasDepInfo: hasDeps,
90
+ pinnedVersions: depMatches.length,
91
+ };
92
+
93
+ // 5. 交叉引用检测
94
+ const crossRefPattern = /→\s*`?([a-z][a-z0-9-]+)`?|见\s*`?([a-z][a-z0-9-]+)`?/g;
95
+ let crossRefCount = 0;
96
+ let m;
97
+ while ((m = crossRefPattern.exec(content)) !== null) {
98
+ crossRefCount++;
99
+ }
100
+ const hasCoLoad = /同读|同批|同时加载|同读|一起/.test(content);
101
+ report.checks.crossRefs = {
102
+ crossRefCount,
103
+ hasCoLoadInstruction: hasCoLoad,
104
+ };
105
+ if (crossRefCount === 0) report.risks.push("无交叉引用 — AI 不知道何时还需加载其他模块");
106
+
107
+ // 6. 代码块完备性检测
108
+ const unclosedBlocks = (content.match(/```/g) || []).length % 2;
109
+ report.checks.codeBlocks = {
110
+ totalFences: (content.match(/```/g) || []).length,
111
+ isBalanced: unclosedBlocks === 0,
112
+ };
113
+ if (unclosedBlocks !== 0) report.risks.push("代码块未闭合 — AI 可能截断生成");
114
+
115
+ // 7. 可替换标记检测(样板中是否有占位符暗示 AI 需要改名)
116
+ const placeholders = content.match(/[A-Z][a-z]+(?:List|Detail|Edit|View|Page)/g) || [];
117
+ const hasPlaceholder = placeholders.length > 0;
118
+ report.checks.placeholders = {
119
+ placeholderCount: placeholders.length,
120
+ examples: [...new Set(placeholders)].slice(0, 5),
121
+ };
122
+
123
+ // 8. AI 可执行性检测(是否有 step-by-step 指引)
124
+ const hasSteps = /步骤|Step|第一步|1\./.test(content);
125
+ const hasCodeToCopy = codeBlocks.length >= 2;
126
+ report.checks.executability = {
127
+ hasSteps,
128
+ hasEnoughCode: hasCodeToCopy,
129
+ };
130
+
131
+ // 评分
132
+ let score = 0;
133
+ if (hasCompleteCode) score += 20;
134
+ if (hasTemplate) score += 15;
135
+ if (hasMustDo) score += 15;
136
+ if (hasCompletenessRule) score += 15;
137
+ if (hasDeps) score += 10;
138
+ if (crossRefCount > 0) score += 10;
139
+ if (unclosedBlocks === 0) score += 10;
140
+ if (hasCoLoad) score += 5;
141
+ report.score = score;
142
+
143
+ return report;
144
+ }
145
+
146
+ // ── 主审计逻辑 ───────────────────────────────────────────
147
+ function auditAll(targetModule) {
148
+ const files = fs
149
+ .readdirSync(REFS_DIR)
150
+ .filter((f) => f.endsWith(".md"))
151
+ .sort();
152
+
153
+ const reports = [];
154
+ for (const file of files) {
155
+ const id = file.replace(".md", "");
156
+ if (targetModule && id !== targetModule) continue;
157
+ const content = fs.readFileSync(path.join(REFS_DIR, file), "utf-8");
158
+ reports.push(auditFile(id, content));
159
+ }
160
+
161
+ return reports;
162
+ }
163
+
164
+ // ── 文本输出 ─────────────────────────────────────────────
165
+ function printReport(report) {
166
+ const { moduleId, checks, risks, score } = report;
167
+
168
+ console.log(c.bold(`\n${c.cyan(moduleId)} ${c.dim(`(score: ${score}/100)`)}\n`));
169
+
170
+ console.log(c.dim(" 检查项:"));
171
+ const items = [
172
+ ["标准样板", checks.template?.hasStandardTemplate, `${checks.template?.codeBlockCount || 0} 代码块`],
173
+ ["Must-do 清单", checks.mustDo?.hasMustDoSection, `${checks.mustDo?.checklistItemCount || 0} 项`],
174
+ ["完整性守则", checks.completeness?.hasCompletenessRule, ""],
175
+ ["依赖信息", checks.dependencies?.hasDepInfo, `${checks.dependencies?.pinnedVersions || 0} 精确版本`],
176
+ ["交叉引用", checks.crossRefs?.crossRefCount > 0, `${checks.crossRefs?.crossRefCount || 0} 处引用`],
177
+ ["同批加载指引", checks.crossRefs?.hasCoLoadInstruction, ""],
178
+ ["代码块闭合", checks.codeBlocks?.isBalanced, `${checks.codeBlocks?.totalFences || 0} 围栏`],
179
+ ["可替换标记", checks.placeholders?.placeholderCount > 0, checks.placeholders?.examples?.join(", ") || ""],
180
+ ["步骤指引", checks.executability?.hasSteps, ""],
181
+ ["充足代码量", checks.executability?.hasEnoughCode, ""],
182
+ ];
183
+
184
+ for (const [name, passed, detail] of items) {
185
+ const mark = passed ? c.green("✓") : c.red("✗");
186
+ console.log(` ${mark} ${name.padEnd(16)} ${c.dim(detail)}`);
187
+ }
188
+
189
+ if (risks.length > 0) {
190
+ console.log(c.yellow(`\n AI 生成风险 (${risks.length}):`));
191
+ for (const r of risks) {
192
+ console.log(` ${c.yellow("!")} ${r}`);
193
+ }
194
+ }
195
+ }
196
+
197
+ function printSummary(reports) {
198
+ console.log(c.bold(`\n${c.cyan("AI 可用性审计汇总")} — ${reports.length} 个模块\n`));
199
+
200
+ // 按评分排序
201
+ const sorted = [...reports].sort((a, b) => b.score - a.score);
202
+
203
+ for (const r of sorted) {
204
+ const bar = "█".repeat(Math.floor(r.score / 5)) +
205
+ c.dim("░".repeat(20 - Math.floor(r.score / 5)));
206
+ const level = r.score >= 70 ? c.green("优") : r.score >= 40 ? c.yellow("中") : c.red("差");
207
+ const riskStr = r.risks.length > 0 ? c.red(` (${r.risks.length} 风险)`) : "";
208
+ console.log(` ${bar} ${r.score.toString().padStart(3)} ${c.bold(r.moduleId.padEnd(22))} ${level}${riskStr}`);
209
+ }
210
+
211
+ // 统计
212
+ const totalRisks = reports.reduce((sum, r) => sum + r.risks.length, 0);
213
+ const avgScore = Math.round(reports.reduce((sum, r) => sum + r.score, 0) / reports.length);
214
+ const excellent = reports.filter((r) => r.score >= 70).length;
215
+ const poor = reports.filter((r) => r.score < 40).length;
216
+
217
+ console.log(c.dim(`\n 平均分: ${avgScore}/100 | 优: ${excellent} | 中: ${reports.length - excellent - poor} | 差: ${poor} | 总风险: ${totalRisks}\n`));
218
+
219
+ // 高风险模块
220
+ const highRisk = reports.filter((r) => r.risks.length >= 2);
221
+ if (highRisk.length > 0) {
222
+ console.log(c.bold(`高风险模块(≥2 个 AI 生成风险):\n`));
223
+ for (const r of highRisk) {
224
+ console.log(` ${c.red("!")} ${r.moduleId} ${c.dim(`(score: ${r.score})`)}`);
225
+ for (const risk of r.risks) {
226
+ console.log(` ${c.yellow("!")} ${risk}`);
227
+ }
228
+ }
229
+ }
230
+ }
231
+
232
+ // ── CLI 入口 ────────────────────────────────────────────────
233
+ const args = process.argv.slice(2);
234
+ const targetModule = args.find((a) => !a.startsWith("--"));
235
+ const reports = auditAll(targetModule);
236
+
237
+ if (args.includes("--json")) {
238
+ console.log(JSON.stringify(reports, null, 2));
239
+ } else if (targetModule) {
240
+ printReport(reports[0]);
241
+ console.log("");
242
+ } else {
243
+ printSummary(reports);
244
+ }