@microi.net/cli 5.8.5 → 5.8.6
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/.codebuddy-plugin/marketplace.json +2 -2
- package/.codebuddy-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/.workbuddy-plugin/marketplace.json +2 -2
- package/.workbuddy-plugin/plugin.json +1 -1
- package/LICENSE +21 -21
- package/README.md +71 -71
- package/assets/build-meta.json +6 -6
- package/assets/feature-matrix.json +138 -138
- package/assets/logo.svg +4 -4
- package/cordis.patch.yml +1 -1
- package/package.json +1 -1
- package/scripts/codex-marketplace.json +20 -20
- package/scripts/mcp-codex-stdio-adapter.js +189 -189
- package/scripts/mcp-trae-windows-launcher.cmd +21 -21
- package/scripts/microi-cli-mcp.js +7 -7
- package/scripts/microi-cli.js +66 -65
- package/scripts/microi-codex-broker.js +450 -450
- package/scripts/microi-codex-router.js +618 -618
- package/scripts/microi-skills.meta.json +384 -384
- package/skills/.microi-skills-version.json +2 -2
- package/skills/.progressive-disclosure-manifest.json +3566 -3566
- package/skills/README.md +287 -287
- package/skills/ai-engine/SKILL.md +269 -265
- package/skills/ai-engine/agents/openai.yaml +4 -4
- package/skills/ai-engine/references/ai-employees.md +48 -48
- package/skills/ai-engine/references/self-hosted-digital-human.md +59 -59
- package/skills/ai-platform-governance/SKILL.md +177 -177
- package/skills/ai-platform-governance/references/progressive-01-/345/212/237/350/203/275/345/274/200/345/205/263.md +190 -190
- package/skills/app-store/SKILL.md +525 -525
- package/skills/app-store/agents/openai.yaml +4 -4
- package/skills/business-blueprint/SKILL.md +193 -193
- package/skills/datasource-engine/SKILL.md +93 -93
- package/skills/datasource-engine/agents/openai.yaml +4 -4
- package/skills/dos-orm/SKILL.md +97 -97
- package/skills/dos-orm/references/api-reference.md +229 -229
- package/skills/email-engine/SKILL.md +81 -81
- package/skills/email-engine/references/v8-email.md +34 -34
- package/skills/job-engine/SKILL.md +176 -176
- package/skills/job-engine/agents/openai.yaml +4 -4
- package/skills/message-notification/SKILL.md +156 -156
- package/skills/message-notification/agents/openai.yaml +5 -5
- package/skills/message-notification/references/contracts.md +102 -102
- package/skills/microi/SKILL.md +14 -14
- package/skills/microi-ai-app-auth.js +652 -652
- package/skills/microi-ai-application/SKILL.md +115 -115
- package/skills/microi-ai-application/agents/openai.yaml +4 -4
- package/skills/microi-ai-application/references/frontend-baseline.md +164 -164
- package/skills/microi-client-frontend/SKILL.md +244 -244
- package/skills/microi-client-frontend/references/progressive-01-3-/345/212/250/346/200/201/346/214/211/351/222/256/347/263/273/347/273/237.md +144 -144
- package/skills/microi-client-frontend/references/progressive-02-8-/350/277/220/350/241/214/346/227/266/351/253/230/351/242/221/345/235/221/345/244/215/347/233/230.md +196 -196
- package/skills/microi-client-frontend/references/progressive-03-vue3-/345/211/215/347/253/257/345/276/256/346/234/215/345/212/241/345/256/277/344/270/273/350/247/204/345/210/231.md +167 -167
- package/skills/microi-codex/SKILL.md +102 -102
- package/skills/microi-codex-installer/SKILL.md +231 -231
- package/skills/microi-codex-installer/agents/openai.yaml +7 -7
- package/skills/microi-datasource-mapping/SKILL.md +122 -122
- package/skills/microi-db-schema/SKILL.md +175 -175
- package/skills/microi-db-schema/agents/openai.yaml +4 -4
- package/skills/microi-db-schema/references/core-tables.md +695 -695
- package/skills/microi-db-schema/references/form-component-options.md +256 -256
- package/skills/microi-db-schema/references/schema-overview.md +202 -202
- package/skills/microi-db-schema/references/schema.md +646 -646
- package/skills/microi-db-schema/references/table-catalog.md +1599 -1599
- package/skills/microi-deployment/SKILL.md +221 -221
- package/skills/microi-deployment/references/deployment-matrix.md +109 -109
- package/skills/microi-docs-coverage/SKILL.md +133 -133
- package/skills/microi-docs-coverage/references/capability-map.md +91 -91
- package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
- package/skills/microi-form-engine/SKILL.md +333 -333
- package/skills/microi-form-engine/references/component-catalog.md +218 -218
- package/skills/microi-form-engine/references/data-source-events.md +124 -124
- package/skills/microi-form-layout/SKILL.md +205 -205
- package/skills/microi-form-layout/references/progressive-01-3-/344/270/211/347/247/215/345/210/206/347/273/204/347/232/204/345/255/230/345/202/250/344/270/216/351/205/215/347/275/256.md +235 -235
- package/skills/microi-frontend-sdk/SKILL.md +194 -194
- package/skills/microi-frontend-sdk/references/progressive-01-token-/345/275/223/345/211/215/347/231/273/345/275/225/347/224/250/346/210/267/344/270/216/345/275/223/345/211/215/347/273/210/347/253/257/347/231/273/345/275/225/345/215/217/350/256/256.md +176 -176
- package/skills/microi-left-right-layout/SKILL.md +141 -141
- package/skills/microi-microservice/SKILL.md +326 -324
- package/skills/microi-microservice/references/runtime-delivery.md +278 -278
- package/skills/microi-mobile-app-quality/SKILL.md +185 -185
- package/skills/microi-mobile-app-quality/references/progressive-01-4-/351/207/215/350/246/201/346/214/211/351/222/256/345/277/205/351/241/273/345/270/246/345/233/276/346/240/207.md +213 -213
- package/skills/microi-mobile-app-quality/references/progressive-02-9-/344/270/273/351/242/230/345/210/207/346/215/242/345/277/205/351/241/273/347/234/237/345/256/236/344/270/224/345/205/250/345/261/200/347/224/237/346/225/210.md +128 -128
- package/skills/microi-solution-quotation/SKILL.md +78 -78
- package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
- package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
- package/skills/microi-sso/SKILL.md +92 -92
- package/skills/microi-sso/references/acceptance.md +49 -49
- package/skills/microi-sso/references/configuration-and-security.md +53 -53
- package/skills/microi-sso/references/inbound.md +53 -53
- package/skills/microi-sso/references/outbound.md +39 -39
- package/skills/microi-system-delivery/SKILL.md +137 -137
- package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +193 -193
- package/skills/microi-system-delivery/references/progressive-02-/350/207/252/345/212/250/345/214/226/346/265/213/350/257/225/345/277/205/351/241/273/350/246/206/347/233/226/347/232/204/345/235/221.md +217 -217
- package/skills/microi-ui/SKILL.md +192 -192
- package/skills/microi-ui/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/234/272/346/231/257/350/223/235/345/233/276.md +183 -183
- package/skills/microi-uniapp-frontend/SKILL.md +193 -193
- package/skills/microi-uniapp-frontend/references/progressive-01-/347/247/273/345/212/250/347/253/257/345/210/206/347/261/273-/345/217/214/346/240/217/345/210/227/350/241/250/347/213/254/347/253/213/346/273/232/345/212/250.md +225 -225
- package/skills/microi-uniapp-frontend/references/progressive-02-/345/205/263/351/224/256/344/270/232/345/212/241/350/265/204/344/272/247/344/270/215/345/276/227/351/273/230/350/256/244/351/200/211/344/270/255.md +154 -154
- package/skills/microi.v8.js +1921 -1921
- package/skills/module-engine/SKILL.md +255 -255
- package/skills/module-engine/references/module-config.md +204 -204
- package/skills/ocr-engine/SKILL.md +113 -113
- package/skills/ocr-engine/agents/openai.yaml +4 -4
- package/skills/page-engine/SKILL.md +206 -206
- package/skills/page-engine/examples/compact-dashboard.json +1444 -1444
- package/skills/page-engine/references/progressive-01-/346/211/200/346/234/211/347/273/204/344/273/266/347/261/273/345/236/213.md +234 -234
- package/skills/page-engine/references/progressive-02-/347/211/210/346/234/254/345/216/206/345/217/262-/345/271/266/345/217/221/344/277/235/345/255/230/344/270/216/345/233/236/346/273/232.md +60 -60
- package/skills/performance-testing/SKILL.md +221 -221
- package/skills/playwright-e2e/SKILL.md +196 -196
- package/skills/playwright-e2e/references/progressive-01-/345/205/250/350/207/252/345/212/250/347/231/273/345/275/225-/345/205/215/351/252/214/350/257/201/347/240/201-/344/275/206/344/270/215/345/205/215/345/257/206/347/240/201-/345/277/205/350/257/273.md +173 -173
- package/skills/playwright-e2e/references/progressive-02-/346/226/207/345/255/227/345/257/271/346/257/224/345/272/246/344/270/216/345/217/257/350/257/273/346/200/247/350/207/252/345/212/250/345/214/226/346/243/200/346/237/245-/345/277/205/345/201/232.md +183 -183
- package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +221 -221
- package/skills/playwright-e2e/references/progressive-04-ci-/345/273/272/350/256/256.md +115 -115
- package/skills/print-engine/SKILL.md +259 -259
- package/skills/production-readonly-audit/SKILL.md +41 -41
- package/skills/report-engine/SKILL.md +71 -71
- package/skills/report-engine/agents/openai.yaml +4 -4
- package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
- package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
- package/skills/scripts/sync-embedded-skills.mjs +46 -46
- package/skills/scripts/validate-progressive-disclosure.mjs +57 -57
- package/skills/search-engine/SKILL.md +75 -75
- package/skills/search-engine/agents/openai.yaml +4 -4
- package/skills/spider-engine/SKILL.md +190 -190
- package/skills/system-observability/SKILL.md +248 -248
- package/skills/system-observability/references/memory-incident-triage.md +77 -77
- package/skills/translate-engine/SKILL.md +140 -140
- package/skills/translate-engine/agents/openai.yaml +4 -4
- package/skills/ui-design/SKILL.md +223 -223
- package/skills/ui-design/assets/pattern-showcase/app.js +54 -54
- package/skills/ui-design/assets/pattern-showcase/index.html +163 -163
- package/skills/ui-design/assets/pattern-showcase/styles.css +311 -311
- package/skills/ui-design/assets/templates/MCI-DESIGN.md +206 -206
- package/skills/ui-design/references/design-pattern-library.md +184 -184
- package/skills/ui-design/references/mci-design-contract.md +163 -163
- package/skills/ui-design/references/motion-and-media.md +78 -78
- package/skills/ui-design/references/product-flow-recipes.md +94 -94
- package/skills/ui-design/references/progressive-01-/351/242/234/350/211/262/344/275/223/347/263/273-css-variables-/346/224/257/346/214/201/344/270/273/351/242/230/345/210/207/346/215/242.md +218 -218
- package/skills/ui-design/references/progressive-02-/345/255/227/344/275/223.md +164 -164
- package/skills/ui-design/references/progressive-03-/345/212/250/346/225/210/350/247/204/350/214/203-/344/270/260/345/257/214/344/275/206/344/270/215/345/215/241.md +235 -235
- package/skills/ui-design/references/progressive-04-/347/273/204/344/273/266/351/243/216/346/240/274/351/200/237/346/237/245.md +152 -152
- package/skills/ui-design/references/progressive-05-/347/247/273/345/212/250/347/253/257/344/270/223/347/224/250/350/247/204/350/214/203.md +238 -238
- package/skills/ui-design/references/progressive-06-/344/270/273/351/242/230/345/210/207/346/215/242/345/256/236/347/216/260.md +194 -194
- package/skills/ui-design/references/progressive-07-/351/200/237/346/237/245-/344/273/216/345/244/264/346/220/255/345/273/272/344/270/200/344/270/252/347/247/273/345/212/250/347/253/257/351/241/265/351/235/242.md +207 -207
- package/skills/ui-design/references/progressive-08-/350/241/250/345/215/225/345/210/206/347/273/204/350/247/204/350/214/203-tabs-vs-collapsegroup-/345/274/272/345/210/266.md +142 -142
- package/skills/uniapp-mall-assets/SKILL.md +176 -176
- package/skills/unity-integration/SKILL.md +171 -171
- package/skills/unity-integration/agents/openai.yaml +4 -4
- package/skills/unity-integration/references/ai-app-delivery.md +119 -119
- package/skills/unity-integration/references/sdk-api.md +82 -82
- package/skills/unity-integration/references/toolbox-migration.md +66 -66
- package/skills/unity-integration/references/webgl-hosting.md +57 -57
- package/skills/v8-api-config/SKILL.md +388 -388
- package/skills/v8-cache-pattern/SKILL.md +312 -312
- package/skills/v8-crud-api/SKILL.md +178 -178
- package/skills/v8-crud-api/references/progressive-01-/346/237/245/350/257/242/345/210/227/350/241/250-/345/210/206/351/241/265.md +226 -226
- package/skills/v8-crud-api/references/progressive-02-where-/346/235/241/344/273/266/350/257/255/346/263/225/351/200/237/346/237/245.md +49 -49
- package/skills/v8-debugging/SKILL.md +284 -284
- package/skills/v8-explorer-tree/SKILL.md +228 -228
- package/skills/v8-export-import/SKILL.md +219 -219
- package/skills/v8-export-import/references/progressive-01-excellayout-/351/253/230/347/272/247/350/207/252/347/224/261/345/270/203/345/261/200.md +211 -211
- package/skills/v8-export-import/references/progressive-02-powerpoint-/345/257/274/345/207/272.md +202 -202
- package/skills/v8-export-import/references/progressive-03-/345/256/211/345/205/250-/346/200/247/350/203/275/346/263/250/346/204/217.md +42 -42
- package/skills/v8-file-upload/SKILL.md +284 -284
- package/skills/v8-file-upload/references/progressive-01-/345/205/254/346/234/211/346/241/266-vs-/347/247/201/346/234/211/346/241/266.md +263 -263
- package/skills/v8-file-upload/references/progressive-02-office-/346/226/207/344/273/266/345/234/250/347/272/277/347/274/226/350/276/221/347/211/210/346/234/254/345/217/267/350/247/204/345/210/231.md +161 -161
- package/skills/v8-formengine-http/SKILL.md +238 -238
- package/skills/v8-frontend-events/SKILL.md +180 -180
- package/skills/v8-frontend-events/references/bluetooth-print-api.md +135 -135
- package/skills/v8-frontend-events/references/bluetooth-print.md +258 -258
- package/skills/v8-frontend-events/references/progressive-01-/345/210/227/350/241/250/344/272/213/344/273/266.md +219 -219
- package/skills/v8-http-integration/SKILL.md +182 -182
- package/skills/v8-http-integration/references/progressive-01-get-/350/257/267/346/261/202.md +220 -220
- package/skills/v8-http-integration/references/progressive-02-/351/224/231/350/257/257/345/244/204/347/220/206/346/250/241/345/274/217.md +44 -44
- package/skills/v8-image-processing/SKILL.md +190 -190
- package/skills/v8-image-processing/agents/openai.yaml +4 -4
- package/skills/v8-image-processing/references/api-reference.md +623 -623
- package/skills/v8-menu-buttons/SKILL.md +186 -186
- package/skills/v8-menu-buttons/references/progressive-01-2-/346/214/211/351/222/256/345/257/271/350/261/241-schema.md +222 -222
- package/skills/v8-menu-buttons/references/progressive-02-8-/346/250/241/345/274/217-f-/345/220/216/345/217/260/344/273/273/345/212/241/346/214/211/351/222/256-/351/225/277/344/273/273/345/212/241.md +228 -228
- package/skills/v8-menu-buttons/references/progressive-03-10-/345/217/215/346/250/241/345/274/217-/351/201/277/345/205/215.md +104 -104
- package/skills/v8-mongodb/SKILL.md +200 -200
- package/skills/v8-mq-mqtt/SKILL.md +176 -176
- package/skills/v8-mq-mqtt/references/mqtt-production.md +342 -342
- package/skills/v8-mq-mqtt/references/progressive-01-v8-mqtt-iot-/347/211/251/350/201/224/347/275/221.md +181 -181
- package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
- package/skills/v8-saas-multi-tenant/SKILL.md +305 -305
- package/skills/v8-security/SKILL.md +210 -210
- package/skills/v8-security/references/progressive-01-2-/346/235/203/351/231/220/346/240/241/351/252/214.md +200 -200
- package/skills/v8-security/references/progressive-02-7-/346/227/245/345/277/227/350/256/260/345/275/225.md +160 -160
- package/skills/v8-sql-query/SKILL.md +302 -302
- package/skills/v8-table-event/SKILL.md +176 -176
- package/skills/v8-table-event/references/progressive-01-informv8-js-/350/241/250/345/215/225/346/211/223/345/274/200/344/272/213/344/273/266.md +216 -216
- package/skills/v8-table-event/references/progressive-02-/345/211/215/347/253/257/344/272/213/344/273/266/345/220/215-v8-eventname-/345/217/257/350/203/275/347/232/204/345/200/274.md +46 -46
- package/skills/v8-tcp-integration/SKILL.md +147 -147
- package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
- package/skills/v8-template-engine/SKILL.md +167 -167
- package/skills/v8-utilities/SKILL.md +104 -104
- package/skills/v8-utilities/references/client-api-index.md +143 -143
- package/skills/v8-utilities/references/platform-http-routes.md +83 -83
- package/skills/v8-utilities/references/server-api-index.md +188 -188
- package/skills/v8-workflow/SKILL.md +252 -252
- package/skills/v8-workflow/references/progressive-01-/350/212/202/347/202/271/345/274/200/345/247/213-v8-/344/272/213/344/273/266.md +180 -180
- package/skills/v8-workflow/references/workflow-configuration.md +49 -49
- package/skills/vision-engine/SKILL.md +160 -160
- package/skills/vision-engine/agents/openai.yaml +4 -4
- package/skills/vision-engine/references/architecture-and-acceptance.md +194 -194
- package/skills/workspace-conventions/SKILL.md +273 -273
- package/skills/workspace-conventions/references/progressive-01-/347/211/210/346/234/254/346/233/264/346/226/260/346/227/245/345/277/227/344/277/235/346/212/244/350/247/204/345/210/231-/345/274/272/345/210/266.md +209 -209
- package/skills/workspace-conventions/references/progressive-02-microi-net-api-/346/234/254/345/234/260/345/220/257/345/212/250/347/272/246/345/256/232.md +217 -217
- package/skills/workspace-conventions/references/progressive-03-cli-/344/270/216-ide-/346/217/222/344/273/266/351/224/231/347/211/210/345/205/261/345/255/230/347/272/246/345/256/232.md +27 -27
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: report-engine
|
|
3
|
-
description: Microi 报表引擎设计与验收规范。用于 Rpt_Report 虚拟表格、数据源、报表字段、查询/增删改接口替换、统计聚合、导出、行级权限和界面引擎图表。
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
> **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
|
|
7
|
-
|
|
8
|
-
# Microi 报表引擎
|
|
9
|
-
|
|
10
|
-
## 定位
|
|
11
|
-
|
|
12
|
-
报表引擎以 `Rpt_Report`、报表字段配置和数据源引擎组成虚拟表格。表格展示使用报表引擎;ECharts 图表和仪表盘使用界面引擎。报表字段是虚拟字段,不能假设存在同名物理表列。
|
|
13
|
-
|
|
14
|
-
## 建模顺序
|
|
15
|
-
|
|
16
|
-
1. 明确指标口径、时间粒度、维度、权限和导出上限。
|
|
17
|
-
2. 创建只读数据源(SQL/V8/JSON),使用稳定 `DataSourceKey`。
|
|
18
|
-
3. 创建报表并绑定数据源。
|
|
19
|
-
4. 配置字段 `Name/Label/Component`、查询列、排序、格式和表内编辑。
|
|
20
|
-
5. 如需写操作,为查询/新增/修改/删除分别绑定专用接口引擎。
|
|
21
|
-
6. 菜单只暴露用户需要的报表入口,配置数据范围。
|
|
22
|
-
|
|
23
|
-
## 权限与 SQL
|
|
24
|
-
|
|
25
|
-
- 报表数据源必须应用当前租户和服务端用户数据范围。菜单权限不会自动保护任意聚合 SQL。
|
|
26
|
-
- 聚合结果也可能泄露敏感信息;小样本、人员薪资、客户金额等需最小分组阈值或字段脱敏。
|
|
27
|
-
- SQL 动态值参数化,维度/排序/指标使用白名单映射,不接收原始 SQL、列名或 `GROUP BY`。
|
|
28
|
-
- 管理员专用平台表不能作为普通用户报表数据源;只读委托表只有获得真实 `Read` 授权后才能查询,`mic_page/mic_print` 继续按角色权限处理。
|
|
29
|
-
|
|
30
|
-
## 写入型报表
|
|
31
|
-
|
|
32
|
-
统计报表通常来自多表。新增、修改、删除必须调用接口引擎,在一个事务中完成校验与写入:
|
|
33
|
-
|
|
34
|
-
```js
|
|
35
|
-
var result = await V8.ApiEngine.Run('report_adjust_inventory', {
|
|
36
|
-
RowId: V8.Form.Id,
|
|
37
|
-
ExpectedVersion: V8.Form.Version,
|
|
38
|
-
Quantity: Number(V8.Form.Quantity || 0)
|
|
39
|
-
});
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
接口引擎重新校验角色、记录范围、当前版本和业务状态。前端隐藏按钮、只读字段或报表行数据不能作为授权。
|
|
43
|
-
|
|
44
|
-
## 性能
|
|
45
|
-
|
|
46
|
-
- 默认分页,限制最大页数/每页行数/导出行数。
|
|
47
|
-
- 大聚合在数据库建立合适索引或使用预聚合表;不要每次全表扫描。
|
|
48
|
-
- 缓存 Key 包含 `OsClient + ReportId + 权限主体/授权版本 + 查询参数哈希`。
|
|
49
|
-
- 导出走后台任务;生成文件放私有桶并绑定菜单、记录和字段访问上下文。
|
|
50
|
-
|
|
51
|
-
## MCP 工作流
|
|
52
|
-
|
|
53
|
-
1. `microi_get_db_schema` 读取 `Rpt_Report`、数据源、字段、菜单和业务表。
|
|
54
|
-
2. `microi_save_data_source` 保存数据源并回读。
|
|
55
|
-
3. 创建/更新报表及字段;复杂写操作先创建接口引擎。
|
|
56
|
-
4. 设置菜单与角色权限。
|
|
57
|
-
5. 用真实普通角色验收列表、筛选、汇总、详情、编辑与导出。
|
|
58
|
-
|
|
59
|
-
## 图表
|
|
60
|
-
|
|
61
|
-
界面引擎图表应调用返回稳定 `{Code, Data}` 的统计接口引擎,不在前端下载明细再聚合。统计接口设置时间范围、维度和最大点数,返回空数据时保持稳定结构。
|
|
62
|
-
|
|
63
|
-
## 验收清单
|
|
64
|
-
|
|
65
|
-
- [ ] 指标口径、时区和小数精度明确
|
|
66
|
-
- [ ] 当前租户与行级范围在数据库查询中生效
|
|
67
|
-
- [ ] 虚拟字段与物理字段没有混用
|
|
68
|
-
- [ ] 写操作走事务接口引擎并校验并发版本
|
|
69
|
-
- [ ] 分页、聚合、导出均有资源上限
|
|
70
|
-
- [ ] 缓存按租户和权限隔离
|
|
71
|
-
- [ ] 普通角色、无权限角色、跨租户负向测试通过
|
|
1
|
+
---
|
|
2
|
+
name: report-engine
|
|
3
|
+
description: Microi 报表引擎设计与验收规范。用于 Rpt_Report 虚拟表格、数据源、报表字段、查询/增删改接口替换、统计聚合、导出、行级权限和界面引擎图表。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
> **Microi吾码基础规范(强制):** 任何 AI 模型与宿主每次新建或接续吾码任务,先完整读取 `../workspace-conventions/SKILL.md`,必须执行版本播报、`@microi.net/cli` 后台自动升级、Skills/MCP 同步和进度播报。安装与诊断读取 `../microi-codex-installer/SKILL.md`;更新失败延后重试,不阻断当前工作。
|
|
7
|
+
|
|
8
|
+
# Microi 报表引擎
|
|
9
|
+
|
|
10
|
+
## 定位
|
|
11
|
+
|
|
12
|
+
报表引擎以 `Rpt_Report`、报表字段配置和数据源引擎组成虚拟表格。表格展示使用报表引擎;ECharts 图表和仪表盘使用界面引擎。报表字段是虚拟字段,不能假设存在同名物理表列。
|
|
13
|
+
|
|
14
|
+
## 建模顺序
|
|
15
|
+
|
|
16
|
+
1. 明确指标口径、时间粒度、维度、权限和导出上限。
|
|
17
|
+
2. 创建只读数据源(SQL/V8/JSON),使用稳定 `DataSourceKey`。
|
|
18
|
+
3. 创建报表并绑定数据源。
|
|
19
|
+
4. 配置字段 `Name/Label/Component`、查询列、排序、格式和表内编辑。
|
|
20
|
+
5. 如需写操作,为查询/新增/修改/删除分别绑定专用接口引擎。
|
|
21
|
+
6. 菜单只暴露用户需要的报表入口,配置数据范围。
|
|
22
|
+
|
|
23
|
+
## 权限与 SQL
|
|
24
|
+
|
|
25
|
+
- 报表数据源必须应用当前租户和服务端用户数据范围。菜单权限不会自动保护任意聚合 SQL。
|
|
26
|
+
- 聚合结果也可能泄露敏感信息;小样本、人员薪资、客户金额等需最小分组阈值或字段脱敏。
|
|
27
|
+
- SQL 动态值参数化,维度/排序/指标使用白名单映射,不接收原始 SQL、列名或 `GROUP BY`。
|
|
28
|
+
- 管理员专用平台表不能作为普通用户报表数据源;只读委托表只有获得真实 `Read` 授权后才能查询,`mic_page/mic_print` 继续按角色权限处理。
|
|
29
|
+
|
|
30
|
+
## 写入型报表
|
|
31
|
+
|
|
32
|
+
统计报表通常来自多表。新增、修改、删除必须调用接口引擎,在一个事务中完成校验与写入:
|
|
33
|
+
|
|
34
|
+
```js
|
|
35
|
+
var result = await V8.ApiEngine.Run('report_adjust_inventory', {
|
|
36
|
+
RowId: V8.Form.Id,
|
|
37
|
+
ExpectedVersion: V8.Form.Version,
|
|
38
|
+
Quantity: Number(V8.Form.Quantity || 0)
|
|
39
|
+
});
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
接口引擎重新校验角色、记录范围、当前版本和业务状态。前端隐藏按钮、只读字段或报表行数据不能作为授权。
|
|
43
|
+
|
|
44
|
+
## 性能
|
|
45
|
+
|
|
46
|
+
- 默认分页,限制最大页数/每页行数/导出行数。
|
|
47
|
+
- 大聚合在数据库建立合适索引或使用预聚合表;不要每次全表扫描。
|
|
48
|
+
- 缓存 Key 包含 `OsClient + ReportId + 权限主体/授权版本 + 查询参数哈希`。
|
|
49
|
+
- 导出走后台任务;生成文件放私有桶并绑定菜单、记录和字段访问上下文。
|
|
50
|
+
|
|
51
|
+
## MCP 工作流
|
|
52
|
+
|
|
53
|
+
1. `microi_get_db_schema` 读取 `Rpt_Report`、数据源、字段、菜单和业务表。
|
|
54
|
+
2. `microi_save_data_source` 保存数据源并回读。
|
|
55
|
+
3. 创建/更新报表及字段;复杂写操作先创建接口引擎。
|
|
56
|
+
4. 设置菜单与角色权限。
|
|
57
|
+
5. 用真实普通角色验收列表、筛选、汇总、详情、编辑与导出。
|
|
58
|
+
|
|
59
|
+
## 图表
|
|
60
|
+
|
|
61
|
+
界面引擎图表应调用返回稳定 `{Code, Data}` 的统计接口引擎,不在前端下载明细再聚合。统计接口设置时间范围、维度和最大点数,返回空数据时保持稳定结构。
|
|
62
|
+
|
|
63
|
+
## 验收清单
|
|
64
|
+
|
|
65
|
+
- [ ] 指标口径、时区和小数精度明确
|
|
66
|
+
- [ ] 当前租户与行级范围在数据库查询中生效
|
|
67
|
+
- [ ] 虚拟字段与物理字段没有混用
|
|
68
|
+
- [ ] 写操作走事务接口引擎并校验并发版本
|
|
69
|
+
- [ ] 分页、聚合、导出均有资源上限
|
|
70
|
+
- [ ] 缓存按租户和权限隔离
|
|
71
|
+
- [ ] 普通角色、无权限角色、跨租户负向测试通过
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
interface:
|
|
2
|
-
display_name: "报表引擎"
|
|
3
|
-
short_description: "设计遵守菜单与行级权限的报表、聚合接口、写操作与导出验收"
|
|
4
|
-
default_prompt: "使用 $report-engine 设计并验收当前 Microi 报表。"
|
|
1
|
+
interface:
|
|
2
|
+
display_name: "报表引擎"
|
|
3
|
+
short_description: "设计遵守菜单与行级权限的报表、聚合接口、写操作与导出验收"
|
|
4
|
+
default_prompt: "使用 $report-engine 设计并验收当前 Microi 报表。"
|
|
@@ -1,204 +1,204 @@
|
|
|
1
|
-
import crypto from 'node:crypto'
|
|
2
|
-
import fs from 'node:fs'
|
|
3
|
-
import path from 'node:path'
|
|
4
|
-
import { fileURLToPath } from 'node:url'
|
|
5
|
-
|
|
6
|
-
const skillsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
-
const manifestPath = path.join(skillsRoot, '.progressive-disclosure-manifest.json')
|
|
8
|
-
const maxEntryLines = 285
|
|
9
|
-
const keepBudgetLines = 170
|
|
10
|
-
const referenceBudgetLines = 245
|
|
11
|
-
|
|
12
|
-
const hash = (value) => crypto.createHash('sha256').update(value).digest('hex')
|
|
13
|
-
const lineCount = (value) => value.length === 0 ? 0 : value.split(/\r?\n/).length
|
|
14
|
-
const slug = (value) => value
|
|
15
|
-
.replace(/^#+\s*/, '')
|
|
16
|
-
.replace(/[`*_]/g, '')
|
|
17
|
-
.trim()
|
|
18
|
-
.toLowerCase()
|
|
19
|
-
.replace(/[^\p{L}\p{N}]+/gu, '-')
|
|
20
|
-
.replace(/^-|-$/g, '')
|
|
21
|
-
.slice(0, 48) || 'reference'
|
|
22
|
-
|
|
23
|
-
const splitAtHeading = (value, headingPattern) => {
|
|
24
|
-
const matches = [...value.matchAll(headingPattern)]
|
|
25
|
-
if (matches.length <= 1) return [value]
|
|
26
|
-
const parts = []
|
|
27
|
-
if (matches[0].index > 0) parts.push(value.slice(0, matches[0].index))
|
|
28
|
-
for (let index = 0; index < matches.length; index += 1) {
|
|
29
|
-
parts.push(value.slice(matches[index].index, matches[index + 1]?.index ?? value.length))
|
|
30
|
-
}
|
|
31
|
-
return parts.filter(Boolean)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const splitOversized = (value, eol) => {
|
|
35
|
-
if (lineCount(value) <= referenceBudgetLines) return [value]
|
|
36
|
-
let parts = splitAtHeading(value, /^###\s+.+$/gm)
|
|
37
|
-
if (parts.length === 1) parts = splitAtHeading(value, /^####\s+.+$/gm)
|
|
38
|
-
const output = []
|
|
39
|
-
for (const part of parts) {
|
|
40
|
-
if (lineCount(part) <= referenceBudgetLines) {
|
|
41
|
-
output.push(part)
|
|
42
|
-
continue
|
|
43
|
-
}
|
|
44
|
-
const lines = part.split(/\r?\n/)
|
|
45
|
-
let start = 0
|
|
46
|
-
while (start < lines.length) {
|
|
47
|
-
let end = Math.min(start + referenceBudgetLines - 8, lines.length)
|
|
48
|
-
if (end < lines.length) {
|
|
49
|
-
for (let cursor = end; cursor > start + 40; cursor -= 1) {
|
|
50
|
-
if (lines[cursor - 1].trim() === '') {
|
|
51
|
-
end = cursor
|
|
52
|
-
break
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
const hasTrailingBreak = end < lines.length || part.endsWith(eol)
|
|
57
|
-
output.push(lines.slice(start, end).join(eol) + (hasTrailingBreak ? eol : ''))
|
|
58
|
-
start = end
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
return output
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const headingOf = (value, fallback) => value.match(/^#{2,4}\s+(.+)$/m)?.[1]?.trim() || fallback
|
|
65
|
-
|
|
66
|
-
const skillDirectories = fs.readdirSync(skillsRoot, { withFileTypes: true })
|
|
67
|
-
.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(skillsRoot, entry.name, 'SKILL.md')))
|
|
68
|
-
.map((entry) => entry.name)
|
|
69
|
-
.sort()
|
|
70
|
-
|
|
71
|
-
const manifest = {
|
|
72
|
-
version: 1,
|
|
73
|
-
generatedAt: new Date().toISOString(),
|
|
74
|
-
policy: {
|
|
75
|
-
maxEntryLines,
|
|
76
|
-
referenceBudgetLines,
|
|
77
|
-
note: 'Original prefix and ordered chunks reconstruct the exact pre-split SKILL.md bytes.'
|
|
78
|
-
},
|
|
79
|
-
skills: {}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
for (const skillName of skillDirectories) {
|
|
83
|
-
const skillPath = path.join(skillsRoot, skillName, 'SKILL.md')
|
|
84
|
-
const original = fs.readFileSync(skillPath, 'utf8')
|
|
85
|
-
if (lineCount(original) <= 300) continue
|
|
86
|
-
if (original.includes('<!-- microi-progressive:begin -->')) {
|
|
87
|
-
throw new Error(`${skillName} is already optimized; validate instead of running twice`)
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const eol = original.includes('\r\n') ? '\r\n' : '\n'
|
|
91
|
-
const sectionMatches = [...original.matchAll(/^##\s+.+$/gm)]
|
|
92
|
-
if (sectionMatches.length < 2) {
|
|
93
|
-
throw new Error(`${skillName} needs at least two level-2 sections for safe semantic splitting`)
|
|
94
|
-
}
|
|
95
|
-
const prefix = original.slice(0, sectionMatches[0].index)
|
|
96
|
-
const sections = sectionMatches.map((match, index) => ({
|
|
97
|
-
heading: match[0].replace(/^##\s+/, '').trim(),
|
|
98
|
-
raw: original.slice(match.index, sectionMatches[index + 1]?.index ?? original.length),
|
|
99
|
-
}))
|
|
100
|
-
|
|
101
|
-
const kept = []
|
|
102
|
-
const moved = []
|
|
103
|
-
let keptLines = lineCount(prefix)
|
|
104
|
-
for (const section of sections) {
|
|
105
|
-
// Keep one contiguous prefix only. Picking a later short section after an
|
|
106
|
-
// earlier section was moved would reorder the original knowledge stream.
|
|
107
|
-
if (moved.length === 0 && (kept.length === 0 || keptLines + lineCount(section.raw) <= keepBudgetLines)) {
|
|
108
|
-
kept.push(section)
|
|
109
|
-
keptLines += lineCount(section.raw)
|
|
110
|
-
} else {
|
|
111
|
-
moved.push(section)
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
if (moved.length === 0) {
|
|
115
|
-
moved.push(kept.pop())
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const fragments = moved.flatMap((section) => splitOversized(section.raw, eol).map((raw, index) => ({
|
|
119
|
-
raw,
|
|
120
|
-
heading: headingOf(raw, `${section.heading} (${index + 1})`),
|
|
121
|
-
})))
|
|
122
|
-
|
|
123
|
-
const referenceGroups = []
|
|
124
|
-
let current = []
|
|
125
|
-
let currentLines = 5
|
|
126
|
-
for (const fragment of fragments) {
|
|
127
|
-
const cost = lineCount(fragment.raw) + 3
|
|
128
|
-
if (current.length && currentLines + cost > referenceBudgetLines) {
|
|
129
|
-
referenceGroups.push(current)
|
|
130
|
-
current = []
|
|
131
|
-
currentLines = 5
|
|
132
|
-
}
|
|
133
|
-
current.push(fragment)
|
|
134
|
-
currentLines += cost
|
|
135
|
-
}
|
|
136
|
-
if (current.length) referenceGroups.push(current)
|
|
137
|
-
|
|
138
|
-
const referencesDirectory = path.join(skillsRoot, skillName, 'references')
|
|
139
|
-
fs.mkdirSync(referencesDirectory, { recursive: true })
|
|
140
|
-
const chunkRecords = []
|
|
141
|
-
let ordinal = 0
|
|
142
|
-
const wrapChunk = (raw, destination) => {
|
|
143
|
-
const id = `${skillName}-${String(ordinal).padStart(3, '0')}`
|
|
144
|
-
const record = {
|
|
145
|
-
id,
|
|
146
|
-
ordinal,
|
|
147
|
-
destination: destination.replaceAll('\\', '/'),
|
|
148
|
-
sha256: hash(raw),
|
|
149
|
-
lines: lineCount(raw),
|
|
150
|
-
heading: headingOf(raw, skillName),
|
|
151
|
-
}
|
|
152
|
-
chunkRecords.push(record)
|
|
153
|
-
ordinal += 1
|
|
154
|
-
return `<!-- microi-progressive:chunk id=${id} sha256=${record.sha256} -->${eol}${raw}<!-- /microi-progressive:chunk -->${eol}`
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
const entryChunks = kept.map((section) => wrapChunk(section.raw, `${skillName}/SKILL.md`)).join('')
|
|
158
|
-
const referenceRecords = []
|
|
159
|
-
referenceGroups.forEach((group, index) => {
|
|
160
|
-
const fileName = `progressive-${String(index + 1).padStart(2, '0')}-${slug(group[0].heading)}.md`
|
|
161
|
-
const relative = `references/${fileName}`
|
|
162
|
-
const destination = `${skillName}/${relative}`
|
|
163
|
-
const body = group.map((fragment) => wrapChunk(fragment.raw, destination)).join('')
|
|
164
|
-
const title = `# ${skillName} 详细参考 ${index + 1}${eol}${eol}`
|
|
165
|
-
const note = `> 按需读取;本文件由 SKILL.md 的原章节无损拆分。${eol}${eol}`
|
|
166
|
-
fs.writeFileSync(path.join(referencesDirectory, fileName), title + note + body, 'utf8')
|
|
167
|
-
referenceRecords.push({
|
|
168
|
-
path: relative,
|
|
169
|
-
headings: group.map((fragment) => fragment.heading),
|
|
170
|
-
})
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
const routerLines = [
|
|
174
|
-
'## 详细参考路由(渐进披露)',
|
|
175
|
-
'',
|
|
176
|
-
'仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。',
|
|
177
|
-
'',
|
|
178
|
-
...referenceRecords.map((reference) =>
|
|
179
|
-
`- [${reference.path}](${reference.path}):${reference.headings.join(';')}`),
|
|
180
|
-
'',
|
|
181
|
-
].join(eol)
|
|
182
|
-
const rewritten = prefix
|
|
183
|
-
+ `<!-- microi-progressive:begin -->${eol}`
|
|
184
|
-
+ entryChunks
|
|
185
|
-
+ routerLines
|
|
186
|
-
+ `<!-- microi-progressive:end -->${eol}`
|
|
187
|
-
if (lineCount(rewritten) > maxEntryLines) {
|
|
188
|
-
throw new Error(`${skillName} entry remains ${lineCount(rewritten)} lines; lower keepBudgetLines`)
|
|
189
|
-
}
|
|
190
|
-
fs.writeFileSync(skillPath, rewritten, 'utf8')
|
|
191
|
-
|
|
192
|
-
manifest.skills[skillName] = {
|
|
193
|
-
originalSha256: hash(original),
|
|
194
|
-
originalLines: lineCount(original),
|
|
195
|
-
prefixSha256: hash(prefix),
|
|
196
|
-
prefixBytes: Buffer.byteLength(prefix),
|
|
197
|
-
entryLines: lineCount(rewritten),
|
|
198
|
-
chunks: chunkRecords,
|
|
199
|
-
references: referenceRecords,
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8')
|
|
204
|
-
console.log(`Optimized ${Object.keys(manifest.skills).length} skills; manifest: ${manifestPath}`)
|
|
1
|
+
import crypto from 'node:crypto'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import path from 'node:path'
|
|
4
|
+
import { fileURLToPath } from 'node:url'
|
|
5
|
+
|
|
6
|
+
const skillsRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
7
|
+
const manifestPath = path.join(skillsRoot, '.progressive-disclosure-manifest.json')
|
|
8
|
+
const maxEntryLines = 285
|
|
9
|
+
const keepBudgetLines = 170
|
|
10
|
+
const referenceBudgetLines = 245
|
|
11
|
+
|
|
12
|
+
const hash = (value) => crypto.createHash('sha256').update(value).digest('hex')
|
|
13
|
+
const lineCount = (value) => value.length === 0 ? 0 : value.split(/\r?\n/).length
|
|
14
|
+
const slug = (value) => value
|
|
15
|
+
.replace(/^#+\s*/, '')
|
|
16
|
+
.replace(/[`*_]/g, '')
|
|
17
|
+
.trim()
|
|
18
|
+
.toLowerCase()
|
|
19
|
+
.replace(/[^\p{L}\p{N}]+/gu, '-')
|
|
20
|
+
.replace(/^-|-$/g, '')
|
|
21
|
+
.slice(0, 48) || 'reference'
|
|
22
|
+
|
|
23
|
+
const splitAtHeading = (value, headingPattern) => {
|
|
24
|
+
const matches = [...value.matchAll(headingPattern)]
|
|
25
|
+
if (matches.length <= 1) return [value]
|
|
26
|
+
const parts = []
|
|
27
|
+
if (matches[0].index > 0) parts.push(value.slice(0, matches[0].index))
|
|
28
|
+
for (let index = 0; index < matches.length; index += 1) {
|
|
29
|
+
parts.push(value.slice(matches[index].index, matches[index + 1]?.index ?? value.length))
|
|
30
|
+
}
|
|
31
|
+
return parts.filter(Boolean)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const splitOversized = (value, eol) => {
|
|
35
|
+
if (lineCount(value) <= referenceBudgetLines) return [value]
|
|
36
|
+
let parts = splitAtHeading(value, /^###\s+.+$/gm)
|
|
37
|
+
if (parts.length === 1) parts = splitAtHeading(value, /^####\s+.+$/gm)
|
|
38
|
+
const output = []
|
|
39
|
+
for (const part of parts) {
|
|
40
|
+
if (lineCount(part) <= referenceBudgetLines) {
|
|
41
|
+
output.push(part)
|
|
42
|
+
continue
|
|
43
|
+
}
|
|
44
|
+
const lines = part.split(/\r?\n/)
|
|
45
|
+
let start = 0
|
|
46
|
+
while (start < lines.length) {
|
|
47
|
+
let end = Math.min(start + referenceBudgetLines - 8, lines.length)
|
|
48
|
+
if (end < lines.length) {
|
|
49
|
+
for (let cursor = end; cursor > start + 40; cursor -= 1) {
|
|
50
|
+
if (lines[cursor - 1].trim() === '') {
|
|
51
|
+
end = cursor
|
|
52
|
+
break
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
const hasTrailingBreak = end < lines.length || part.endsWith(eol)
|
|
57
|
+
output.push(lines.slice(start, end).join(eol) + (hasTrailingBreak ? eol : ''))
|
|
58
|
+
start = end
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return output
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const headingOf = (value, fallback) => value.match(/^#{2,4}\s+(.+)$/m)?.[1]?.trim() || fallback
|
|
65
|
+
|
|
66
|
+
const skillDirectories = fs.readdirSync(skillsRoot, { withFileTypes: true })
|
|
67
|
+
.filter((entry) => entry.isDirectory() && fs.existsSync(path.join(skillsRoot, entry.name, 'SKILL.md')))
|
|
68
|
+
.map((entry) => entry.name)
|
|
69
|
+
.sort()
|
|
70
|
+
|
|
71
|
+
const manifest = {
|
|
72
|
+
version: 1,
|
|
73
|
+
generatedAt: new Date().toISOString(),
|
|
74
|
+
policy: {
|
|
75
|
+
maxEntryLines,
|
|
76
|
+
referenceBudgetLines,
|
|
77
|
+
note: 'Original prefix and ordered chunks reconstruct the exact pre-split SKILL.md bytes.'
|
|
78
|
+
},
|
|
79
|
+
skills: {}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
for (const skillName of skillDirectories) {
|
|
83
|
+
const skillPath = path.join(skillsRoot, skillName, 'SKILL.md')
|
|
84
|
+
const original = fs.readFileSync(skillPath, 'utf8')
|
|
85
|
+
if (lineCount(original) <= 300) continue
|
|
86
|
+
if (original.includes('<!-- microi-progressive:begin -->')) {
|
|
87
|
+
throw new Error(`${skillName} is already optimized; validate instead of running twice`)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const eol = original.includes('\r\n') ? '\r\n' : '\n'
|
|
91
|
+
const sectionMatches = [...original.matchAll(/^##\s+.+$/gm)]
|
|
92
|
+
if (sectionMatches.length < 2) {
|
|
93
|
+
throw new Error(`${skillName} needs at least two level-2 sections for safe semantic splitting`)
|
|
94
|
+
}
|
|
95
|
+
const prefix = original.slice(0, sectionMatches[0].index)
|
|
96
|
+
const sections = sectionMatches.map((match, index) => ({
|
|
97
|
+
heading: match[0].replace(/^##\s+/, '').trim(),
|
|
98
|
+
raw: original.slice(match.index, sectionMatches[index + 1]?.index ?? original.length),
|
|
99
|
+
}))
|
|
100
|
+
|
|
101
|
+
const kept = []
|
|
102
|
+
const moved = []
|
|
103
|
+
let keptLines = lineCount(prefix)
|
|
104
|
+
for (const section of sections) {
|
|
105
|
+
// Keep one contiguous prefix only. Picking a later short section after an
|
|
106
|
+
// earlier section was moved would reorder the original knowledge stream.
|
|
107
|
+
if (moved.length === 0 && (kept.length === 0 || keptLines + lineCount(section.raw) <= keepBudgetLines)) {
|
|
108
|
+
kept.push(section)
|
|
109
|
+
keptLines += lineCount(section.raw)
|
|
110
|
+
} else {
|
|
111
|
+
moved.push(section)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
if (moved.length === 0) {
|
|
115
|
+
moved.push(kept.pop())
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const fragments = moved.flatMap((section) => splitOversized(section.raw, eol).map((raw, index) => ({
|
|
119
|
+
raw,
|
|
120
|
+
heading: headingOf(raw, `${section.heading} (${index + 1})`),
|
|
121
|
+
})))
|
|
122
|
+
|
|
123
|
+
const referenceGroups = []
|
|
124
|
+
let current = []
|
|
125
|
+
let currentLines = 5
|
|
126
|
+
for (const fragment of fragments) {
|
|
127
|
+
const cost = lineCount(fragment.raw) + 3
|
|
128
|
+
if (current.length && currentLines + cost > referenceBudgetLines) {
|
|
129
|
+
referenceGroups.push(current)
|
|
130
|
+
current = []
|
|
131
|
+
currentLines = 5
|
|
132
|
+
}
|
|
133
|
+
current.push(fragment)
|
|
134
|
+
currentLines += cost
|
|
135
|
+
}
|
|
136
|
+
if (current.length) referenceGroups.push(current)
|
|
137
|
+
|
|
138
|
+
const referencesDirectory = path.join(skillsRoot, skillName, 'references')
|
|
139
|
+
fs.mkdirSync(referencesDirectory, { recursive: true })
|
|
140
|
+
const chunkRecords = []
|
|
141
|
+
let ordinal = 0
|
|
142
|
+
const wrapChunk = (raw, destination) => {
|
|
143
|
+
const id = `${skillName}-${String(ordinal).padStart(3, '0')}`
|
|
144
|
+
const record = {
|
|
145
|
+
id,
|
|
146
|
+
ordinal,
|
|
147
|
+
destination: destination.replaceAll('\\', '/'),
|
|
148
|
+
sha256: hash(raw),
|
|
149
|
+
lines: lineCount(raw),
|
|
150
|
+
heading: headingOf(raw, skillName),
|
|
151
|
+
}
|
|
152
|
+
chunkRecords.push(record)
|
|
153
|
+
ordinal += 1
|
|
154
|
+
return `<!-- microi-progressive:chunk id=${id} sha256=${record.sha256} -->${eol}${raw}<!-- /microi-progressive:chunk -->${eol}`
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const entryChunks = kept.map((section) => wrapChunk(section.raw, `${skillName}/SKILL.md`)).join('')
|
|
158
|
+
const referenceRecords = []
|
|
159
|
+
referenceGroups.forEach((group, index) => {
|
|
160
|
+
const fileName = `progressive-${String(index + 1).padStart(2, '0')}-${slug(group[0].heading)}.md`
|
|
161
|
+
const relative = `references/${fileName}`
|
|
162
|
+
const destination = `${skillName}/${relative}`
|
|
163
|
+
const body = group.map((fragment) => wrapChunk(fragment.raw, destination)).join('')
|
|
164
|
+
const title = `# ${skillName} 详细参考 ${index + 1}${eol}${eol}`
|
|
165
|
+
const note = `> 按需读取;本文件由 SKILL.md 的原章节无损拆分。${eol}${eol}`
|
|
166
|
+
fs.writeFileSync(path.join(referencesDirectory, fileName), title + note + body, 'utf8')
|
|
167
|
+
referenceRecords.push({
|
|
168
|
+
path: relative,
|
|
169
|
+
headings: group.map((fragment) => fragment.heading),
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const routerLines = [
|
|
174
|
+
'## 详细参考路由(渐进披露)',
|
|
175
|
+
'',
|
|
176
|
+
'仅在当前任务涉及对应主题时读取;下列文件合计保留了原 SKILL.md 的全部详细知识。',
|
|
177
|
+
'',
|
|
178
|
+
...referenceRecords.map((reference) =>
|
|
179
|
+
`- [${reference.path}](${reference.path}):${reference.headings.join(';')}`),
|
|
180
|
+
'',
|
|
181
|
+
].join(eol)
|
|
182
|
+
const rewritten = prefix
|
|
183
|
+
+ `<!-- microi-progressive:begin -->${eol}`
|
|
184
|
+
+ entryChunks
|
|
185
|
+
+ routerLines
|
|
186
|
+
+ `<!-- microi-progressive:end -->${eol}`
|
|
187
|
+
if (lineCount(rewritten) > maxEntryLines) {
|
|
188
|
+
throw new Error(`${skillName} entry remains ${lineCount(rewritten)} lines; lower keepBudgetLines`)
|
|
189
|
+
}
|
|
190
|
+
fs.writeFileSync(skillPath, rewritten, 'utf8')
|
|
191
|
+
|
|
192
|
+
manifest.skills[skillName] = {
|
|
193
|
+
originalSha256: hash(original),
|
|
194
|
+
originalLines: lineCount(original),
|
|
195
|
+
prefixSha256: hash(prefix),
|
|
196
|
+
prefixBytes: Buffer.byteLength(prefix),
|
|
197
|
+
entryLines: lineCount(rewritten),
|
|
198
|
+
chunks: chunkRecords,
|
|
199
|
+
references: referenceRecords,
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
fs.writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 'utf8')
|
|
204
|
+
console.log(`Optimized ${Object.keys(manifest.skills).length} skills; manifest: ${manifestPath}`)
|