@microi.net/cli 5.9.5 → 5.9.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.
Files changed (53) hide show
  1. package/.codebuddy-plugin/marketplace.json +2 -2
  2. package/.codebuddy-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.workbuddy-plugin/marketplace.json +2 -2
  5. package/.workbuddy-plugin/plugin.json +1 -1
  6. package/assets/build-meta.json +6 -6
  7. package/cordis.patch.yml +1 -1
  8. package/package.json +1 -1
  9. package/scripts/mcp-server.js +2 -1
  10. package/scripts/microi-cli.js +44 -49
  11. package/scripts/microi-codex-router.js +2 -1
  12. package/scripts/microi-skills.meta.json +238 -238
  13. package/skills/.microi-skills-version.json +2 -2
  14. package/skills/.progressive-disclosure-manifest.json +20 -20
  15. package/skills/ai-engine/agents/openai.yaml +4 -4
  16. package/skills/app-store/agents/openai.yaml +4 -4
  17. package/skills/datasource-engine/agents/openai.yaml +4 -4
  18. package/skills/job-engine/agents/openai.yaml +4 -4
  19. package/skills/message-notification/agents/openai.yaml +5 -5
  20. package/skills/microi-ai-app-auth.js +652 -652
  21. package/skills/microi-ai-application/agents/openai.yaml +4 -4
  22. package/skills/microi-codex-installer/SKILL.md +1 -1
  23. package/skills/microi-codex-installer/agents/openai.yaml +7 -7
  24. package/skills/microi-db-schema/agents/openai.yaml +4 -4
  25. package/skills/microi-docs-coverage/scripts/audit-doc-skill-coverage.mjs +894 -894
  26. package/skills/microi-solution-quotation/agents/openai.yaml +4 -4
  27. package/skills/microi-solution-quotation/scripts/build_solution_quote.py +296 -296
  28. package/skills/microi-system-delivery/SKILL.md +16 -4
  29. package/skills/microi-system-delivery/references/progressive-01-/346/240/207/345/207/206/345/267/245/344/275/234/346/265/201.md +39 -1
  30. 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 +3 -3
  31. package/skills/microi.v8.js +1921 -1921
  32. package/skills/module-engine/SKILL.md +26 -0
  33. package/skills/ocr-engine/agents/openai.yaml +4 -4
  34. package/skills/page-engine/SKILL.md +21 -0
  35. package/skills/page-engine/examples/compact-dashboard.json +1444 -1444
  36. package/skills/playwright-e2e/references/progressive-03-microi-helper-/346/250/241/346/235/277.md +3 -3
  37. package/skills/report-engine/SKILL.md +12 -0
  38. package/skills/report-engine/agents/openai.yaml +4 -4
  39. package/skills/scripts/optimize-progressive-disclosure.mjs +204 -204
  40. package/skills/scripts/refresh-progressive-disclosure.mjs +64 -64
  41. package/skills/scripts/sync-embedded-skills.mjs +46 -46
  42. package/skills/scripts/validate-progressive-disclosure.mjs +57 -57
  43. package/skills/search-engine/agents/openai.yaml +4 -4
  44. package/skills/translate-engine/agents/openai.yaml +4 -4
  45. package/skills/ui-design/assets/pattern-showcase/app.js +54 -54
  46. package/skills/ui-design/assets/pattern-showcase/index.html +163 -163
  47. package/skills/ui-design/assets/pattern-showcase/styles.css +311 -311
  48. package/skills/unity-integration/agents/openai.yaml +4 -4
  49. package/skills/v8-image-processing/agents/openai.yaml +4 -4
  50. package/skills/v8-mq-mqtt/scripts/check-mqtt-skill-coverage.mjs +203 -203
  51. package/skills/v8-tcp-integration/agents/openai.yaml +4 -4
  52. package/skills/vision-engine/agents/openai.yaml +4 -4
  53. package/skills/workspace-conventions/SKILL.md +2 -2
@@ -1,4 +1,4 @@
1
- interface:
2
- display_name: "Microi吾码解决方案与报价"
3
- short_description: "生成紧凑专业的吾码解决方案、周期与总价报价 Word"
4
- default_prompt: "使用 $microi-solution-quotation 生成一份紧凑的一页 Microi吾码系统解决方案与报价 Word。"
1
+ interface:
2
+ display_name: "Microi吾码解决方案与报价"
3
+ short_description: "生成紧凑专业的吾码解决方案、周期与总价报价 Word"
4
+ default_prompt: "使用 $microi-solution-quotation 生成一份紧凑的一页 Microi吾码系统解决方案与报价 Word。"
@@ -1,296 +1,296 @@
1
- import argparse
2
- import json
3
- from datetime import date
4
- from pathlib import Path
5
-
6
- from docx import Document
7
- from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
8
- from docx.enum.text import WD_ALIGN_PARAGRAPH
9
- from docx.oxml import OxmlElement
10
- from docx.oxml.ns import qn
11
- from docx.shared import Inches, Pt, RGBColor
12
-
13
-
14
- NAVY = "17365D"
15
- BLUE = "2F75B5"
16
- PALE_BLUE = "EAF3F9"
17
- PALE_GRAY = "F4F6F8"
18
- PALE_GOLD = "FFF3D6"
19
- GOLD = "B45309"
20
- INK = "1F2937"
21
- MUTED = "64748B"
22
- WHITE = "FFFFFF"
23
- BORDER = "CBD5E1"
24
- FONT = "Microsoft YaHei"
25
- PAGE_WIDTH_DXA = 11909
26
- MARGIN_DXA = 720
27
- TABLE_INDENT_DXA = 120
28
- TABLE_WIDTH_DXA = PAGE_WIDTH_DXA - MARGIN_DXA * 2 - TABLE_INDENT_DXA
29
-
30
-
31
- def set_run_font(run, size=9.2, bold=False, color=INK):
32
- run.font.name = FONT
33
- rfonts = run._element.get_or_add_rPr().get_or_add_rFonts()
34
- for key in ("w:eastAsia", "w:ascii", "w:hAnsi"):
35
- rfonts.set(qn(key), FONT)
36
- run.font.size = Pt(size)
37
- run.font.bold = bold
38
- run.font.color.rgb = RGBColor.from_string(color)
39
-
40
-
41
- def format_paragraph(paragraph, before=0, after=0, line=1.05, align=None):
42
- fmt = paragraph.paragraph_format
43
- fmt.space_before = Pt(before)
44
- fmt.space_after = Pt(after)
45
- fmt.line_spacing = line
46
- if align is not None:
47
- paragraph.alignment = align
48
-
49
-
50
- def add_paragraph(doc, text, size=9.2, bold=False, color=INK, before=0, after=2, line=1.05, align=None):
51
- paragraph = doc.add_paragraph()
52
- format_paragraph(paragraph, before, after, line, align)
53
- set_run_font(paragraph.add_run(text), size, bold, color)
54
- return paragraph
55
-
56
-
57
- def set_cell_shading(cell, fill):
58
- tc_pr = cell._tc.get_or_add_tcPr()
59
- shd = tc_pr.find(qn("w:shd"))
60
- if shd is None:
61
- shd = OxmlElement("w:shd")
62
- tc_pr.append(shd)
63
- shd.set(qn("w:fill"), fill)
64
-
65
-
66
- def set_cell_margins(cell, top=70, start=110, bottom=70, end=110):
67
- tc_pr = cell._tc.get_or_add_tcPr()
68
- tc_mar = tc_pr.first_child_found_in("w:tcMar")
69
- if tc_mar is None:
70
- tc_mar = OxmlElement("w:tcMar")
71
- tc_pr.append(tc_mar)
72
- for name, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
73
- element = tc_mar.find(qn(f"w:{name}"))
74
- if element is None:
75
- element = OxmlElement(f"w:{name}")
76
- tc_mar.append(element)
77
- element.set(qn("w:w"), str(value))
78
- element.set(qn("w:type"), "dxa")
79
-
80
-
81
- def set_cell_width(cell, width):
82
- tc_pr = cell._tc.get_or_add_tcPr()
83
- tc_w = tc_pr.find(qn("w:tcW"))
84
- if tc_w is None:
85
- tc_w = OxmlElement("w:tcW")
86
- tc_pr.append(tc_w)
87
- tc_w.set(qn("w:w"), str(width))
88
- tc_w.set(qn("w:type"), "dxa")
89
-
90
-
91
- def set_table_geometry(table, widths, indent=TABLE_INDENT_DXA):
92
- total = sum(widths)
93
- table.autofit = False
94
- table.alignment = WD_TABLE_ALIGNMENT.LEFT
95
- tbl_pr = table._tbl.tblPr
96
- tbl_w = tbl_pr.find(qn("w:tblW"))
97
- if tbl_w is None:
98
- tbl_w = OxmlElement("w:tblW")
99
- tbl_pr.append(tbl_w)
100
- tbl_w.set(qn("w:w"), str(total))
101
- tbl_w.set(qn("w:type"), "dxa")
102
- tbl_ind = tbl_pr.find(qn("w:tblInd"))
103
- if tbl_ind is None:
104
- tbl_ind = OxmlElement("w:tblInd")
105
- tbl_pr.append(tbl_ind)
106
- tbl_ind.set(qn("w:w"), str(indent))
107
- tbl_ind.set(qn("w:type"), "dxa")
108
- grid = table._tbl.tblGrid
109
- for child in list(grid):
110
- grid.remove(child)
111
- for width in widths:
112
- col = OxmlElement("w:gridCol")
113
- col.set(qn("w:w"), str(width))
114
- grid.append(col)
115
- for row in table.rows:
116
- for index, cell in enumerate(row.cells):
117
- set_cell_width(cell, widths[index])
118
- set_cell_margins(cell)
119
- cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
120
-
121
-
122
- def set_table_borders(table, color=BORDER, size="6"):
123
- tbl_pr = table._tbl.tblPr
124
- borders = tbl_pr.first_child_found_in("w:tblBorders")
125
- if borders is None:
126
- borders = OxmlElement("w:tblBorders")
127
- tbl_pr.append(borders)
128
- for edge in ("top", "left", "bottom", "right", "insideH", "insideV"):
129
- tag = f"w:{edge}"
130
- element = borders.find(qn(tag))
131
- if element is None:
132
- element = OxmlElement(tag)
133
- borders.append(element)
134
- element.set(qn("w:val"), "single")
135
- element.set(qn("w:sz"), size)
136
- element.set(qn("w:space"), "0")
137
- element.set(qn("w:color"), color)
138
-
139
-
140
- def add_section_heading(doc, text):
141
- return add_paragraph(doc, text, size=10.8, bold=True, color=BLUE, before=4, after=1, line=1.0)
142
-
143
-
144
- def add_bullet(doc, label, detail):
145
- paragraph = doc.add_paragraph(style="List Bullet")
146
- format_paragraph(paragraph, before=0, after=1, line=1.03)
147
- set_run_font(paragraph.add_run(f"{label}:"), 8.8, True, NAVY)
148
- set_run_font(paragraph.add_run(detail), 8.8, False, INK)
149
-
150
-
151
- def add_metadata_table(doc, config):
152
- table = doc.add_table(rows=1, cols=4)
153
- widths = [TABLE_WIDTH_DXA // 4] * 4
154
- widths[-1] += TABLE_WIDTH_DXA - sum(widths)
155
- values = [
156
- ("版本", config["version"]),
157
- ("日期", config["date"]),
158
- ("开发周期", config["development_cycle"]),
159
- ("报价有效期", f'{config["validity_days"]} 天'),
160
- ]
161
- for index, (label, value) in enumerate(values):
162
- cell = table.cell(0, index)
163
- set_cell_shading(cell, PALE_GRAY)
164
- paragraph = cell.paragraphs[0]
165
- format_paragraph(paragraph, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
166
- set_run_font(paragraph.add_run(f"{label}:"), 8.0, True, MUTED)
167
- set_run_font(paragraph.add_run(str(value)), 8.2, True, NAVY)
168
- set_table_geometry(table, widths)
169
- set_table_borders(table, "E2E8F0", "4")
170
-
171
-
172
- def add_callout(doc, label, detail, fill=PALE_BLUE):
173
- table = doc.add_table(rows=1, cols=1)
174
- set_table_geometry(table, [TABLE_WIDTH_DXA])
175
- set_table_borders(table, "9CC2E5", "7")
176
- cell = table.cell(0, 0)
177
- set_cell_shading(cell, fill)
178
- paragraph = cell.paragraphs[0]
179
- format_paragraph(paragraph, after=0, line=1.03)
180
- set_run_font(paragraph.add_run(f"{label}:"), 8.8, True, NAVY)
181
- set_run_font(paragraph.add_run(detail), 8.8, False, INK)
182
-
183
-
184
- def add_quote_table(doc, config):
185
- table = doc.add_table(rows=2, cols=3)
186
- widths = [TABLE_WIDTH_DXA // 3] * 3
187
- widths[-1] += TABLE_WIDTH_DXA - sum(widths)
188
- headers = ["市场预估价(总价)", "吾码优惠报价(总价)", "开发周期"]
189
- values = [config["market_price"], config["preferential_price"], config["development_cycle"]]
190
- for index, header in enumerate(headers):
191
- cell = table.cell(0, index)
192
- set_cell_shading(cell, NAVY)
193
- paragraph = cell.paragraphs[0]
194
- format_paragraph(paragraph, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
195
- set_run_font(paragraph.add_run(header), 8.7, True, WHITE)
196
- for index, value in enumerate(values):
197
- cell = table.cell(1, index)
198
- set_cell_shading(cell, PALE_GOLD if index == 1 else PALE_GRAY)
199
- paragraph = cell.paragraphs[0]
200
- format_paragraph(paragraph, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
201
- set_run_font(paragraph.add_run(str(value)), 12.0 if index == 1 else 10.5, True, GOLD if index == 1 else NAVY)
202
- set_table_geometry(table, widths)
203
- set_table_borders(table)
204
-
205
-
206
- def normalize_config(raw):
207
- required = [
208
- "system_name", "project_goal", "solution_items", "technical_route",
209
- "deliverables", "market_price", "preferential_price",
210
- "development_cycle", "boundary_note",
211
- ]
212
- missing = [name for name in required if not raw.get(name)]
213
- if missing:
214
- raise ValueError("Missing required fields: " + ", ".join(missing))
215
- if not isinstance(raw["solution_items"], list) or not raw["solution_items"]:
216
- raise ValueError("solution_items must be a non-empty list")
217
- raw.setdefault("version", "v1.0.0")
218
- raw.setdefault("date", date.today().isoformat())
219
- raw.setdefault("validity_days", 15)
220
- raw.setdefault("subtitle", "AI 深度融合 V8引擎 的低代码平台,官网:https://microi.net")
221
- raw.setdefault("project_scope", "")
222
- return raw
223
-
224
-
225
- def build_document(config, output):
226
- doc = Document()
227
- section = doc.sections[0]
228
- section.page_width = Inches(8.27)
229
- section.page_height = Inches(11.69)
230
- section.top_margin = Inches(0.5)
231
- section.bottom_margin = Inches(0.5)
232
- section.left_margin = Inches(0.5)
233
- section.right_margin = Inches(0.5)
234
- section.header_distance = Inches(0.2)
235
- section.footer_distance = Inches(0.2)
236
-
237
- normal = doc.styles["Normal"]
238
- normal.font.name = FONT
239
- normal._element.rPr.rFonts.set(qn("w:eastAsia"), FONT)
240
- normal.font.size = Pt(9.2)
241
- normal.font.color.rgb = RGBColor.from_string(INK)
242
-
243
- title = f'Microi吾码 {config["system_name"]} 解决方案与报价'
244
- add_paragraph(doc, title, size=20.0, bold=True, color=NAVY, after=1, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
245
- add_paragraph(doc, config["subtitle"], size=9.3, color=MUTED, after=4, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
246
- add_metadata_table(doc, config)
247
-
248
- add_section_heading(doc, "一、项目目标")
249
- add_callout(doc, "目标", config["project_goal"])
250
-
251
- add_section_heading(doc, "二、解决方案")
252
- for item in config["solution_items"]:
253
- if isinstance(item, str):
254
- add_bullet(doc, "方案", item)
255
- else:
256
- add_bullet(doc, item.get("label", "方案"), item.get("detail", ""))
257
-
258
- add_section_heading(doc, "三、核心技术路线")
259
- add_callout(doc, "链路", config["technical_route"], fill=PALE_GRAY)
260
-
261
- add_section_heading(doc, "四、交付范围")
262
- add_paragraph(doc, config["deliverables"], size=8.7, after=1, line=1.03)
263
- if config["project_scope"]:
264
- add_paragraph(doc, config["project_scope"], size=8.3, color=MUTED, after=1, line=1.0)
265
-
266
- add_section_heading(doc, "五、项目总价与周期")
267
- add_quote_table(doc, config)
268
-
269
- add_section_heading(doc, "六、说明与边界")
270
- add_paragraph(doc, config["boundary_note"], size=8.1, color=MUTED, after=0, line=1.0)
271
-
272
- footer = section.footer.paragraphs[0]
273
- format_paragraph(footer, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.RIGHT)
274
- set_run_font(footer.add_run(f'Microi吾码 | {config["version"]} | {config["date"]} | https://microi.net'), 7.2, False, MUTED)
275
-
276
- doc.core_properties.title = title
277
- doc.core_properties.subject = "Microi吾码系统解决方案与报价"
278
- doc.core_properties.author = "Microi吾码"
279
- output.parent.mkdir(parents=True, exist_ok=True)
280
- doc.save(output)
281
-
282
-
283
- def main():
284
- parser = argparse.ArgumentParser(description="Build a compact Microi solution quotation DOCX")
285
- parser.add_argument("--input", required=True, help="UTF-8 JSON configuration file")
286
- parser.add_argument("--output", required=True, help="Output DOCX path")
287
- args = parser.parse_args()
288
- with open(args.input, "r", encoding="utf-8") as handle:
289
- config = normalize_config(json.load(handle))
290
- output = Path(args.output).resolve()
291
- build_document(config, output)
292
- print(output)
293
-
294
-
295
- if __name__ == "__main__":
296
- main()
1
+ import argparse
2
+ import json
3
+ from datetime import date
4
+ from pathlib import Path
5
+
6
+ from docx import Document
7
+ from docx.enum.table import WD_CELL_VERTICAL_ALIGNMENT, WD_TABLE_ALIGNMENT
8
+ from docx.enum.text import WD_ALIGN_PARAGRAPH
9
+ from docx.oxml import OxmlElement
10
+ from docx.oxml.ns import qn
11
+ from docx.shared import Inches, Pt, RGBColor
12
+
13
+
14
+ NAVY = "17365D"
15
+ BLUE = "2F75B5"
16
+ PALE_BLUE = "EAF3F9"
17
+ PALE_GRAY = "F4F6F8"
18
+ PALE_GOLD = "FFF3D6"
19
+ GOLD = "B45309"
20
+ INK = "1F2937"
21
+ MUTED = "64748B"
22
+ WHITE = "FFFFFF"
23
+ BORDER = "CBD5E1"
24
+ FONT = "Microsoft YaHei"
25
+ PAGE_WIDTH_DXA = 11909
26
+ MARGIN_DXA = 720
27
+ TABLE_INDENT_DXA = 120
28
+ TABLE_WIDTH_DXA = PAGE_WIDTH_DXA - MARGIN_DXA * 2 - TABLE_INDENT_DXA
29
+
30
+
31
+ def set_run_font(run, size=9.2, bold=False, color=INK):
32
+ run.font.name = FONT
33
+ rfonts = run._element.get_or_add_rPr().get_or_add_rFonts()
34
+ for key in ("w:eastAsia", "w:ascii", "w:hAnsi"):
35
+ rfonts.set(qn(key), FONT)
36
+ run.font.size = Pt(size)
37
+ run.font.bold = bold
38
+ run.font.color.rgb = RGBColor.from_string(color)
39
+
40
+
41
+ def format_paragraph(paragraph, before=0, after=0, line=1.05, align=None):
42
+ fmt = paragraph.paragraph_format
43
+ fmt.space_before = Pt(before)
44
+ fmt.space_after = Pt(after)
45
+ fmt.line_spacing = line
46
+ if align is not None:
47
+ paragraph.alignment = align
48
+
49
+
50
+ def add_paragraph(doc, text, size=9.2, bold=False, color=INK, before=0, after=2, line=1.05, align=None):
51
+ paragraph = doc.add_paragraph()
52
+ format_paragraph(paragraph, before, after, line, align)
53
+ set_run_font(paragraph.add_run(text), size, bold, color)
54
+ return paragraph
55
+
56
+
57
+ def set_cell_shading(cell, fill):
58
+ tc_pr = cell._tc.get_or_add_tcPr()
59
+ shd = tc_pr.find(qn("w:shd"))
60
+ if shd is None:
61
+ shd = OxmlElement("w:shd")
62
+ tc_pr.append(shd)
63
+ shd.set(qn("w:fill"), fill)
64
+
65
+
66
+ def set_cell_margins(cell, top=70, start=110, bottom=70, end=110):
67
+ tc_pr = cell._tc.get_or_add_tcPr()
68
+ tc_mar = tc_pr.first_child_found_in("w:tcMar")
69
+ if tc_mar is None:
70
+ tc_mar = OxmlElement("w:tcMar")
71
+ tc_pr.append(tc_mar)
72
+ for name, value in (("top", top), ("start", start), ("bottom", bottom), ("end", end)):
73
+ element = tc_mar.find(qn(f"w:{name}"))
74
+ if element is None:
75
+ element = OxmlElement(f"w:{name}")
76
+ tc_mar.append(element)
77
+ element.set(qn("w:w"), str(value))
78
+ element.set(qn("w:type"), "dxa")
79
+
80
+
81
+ def set_cell_width(cell, width):
82
+ tc_pr = cell._tc.get_or_add_tcPr()
83
+ tc_w = tc_pr.find(qn("w:tcW"))
84
+ if tc_w is None:
85
+ tc_w = OxmlElement("w:tcW")
86
+ tc_pr.append(tc_w)
87
+ tc_w.set(qn("w:w"), str(width))
88
+ tc_w.set(qn("w:type"), "dxa")
89
+
90
+
91
+ def set_table_geometry(table, widths, indent=TABLE_INDENT_DXA):
92
+ total = sum(widths)
93
+ table.autofit = False
94
+ table.alignment = WD_TABLE_ALIGNMENT.LEFT
95
+ tbl_pr = table._tbl.tblPr
96
+ tbl_w = tbl_pr.find(qn("w:tblW"))
97
+ if tbl_w is None:
98
+ tbl_w = OxmlElement("w:tblW")
99
+ tbl_pr.append(tbl_w)
100
+ tbl_w.set(qn("w:w"), str(total))
101
+ tbl_w.set(qn("w:type"), "dxa")
102
+ tbl_ind = tbl_pr.find(qn("w:tblInd"))
103
+ if tbl_ind is None:
104
+ tbl_ind = OxmlElement("w:tblInd")
105
+ tbl_pr.append(tbl_ind)
106
+ tbl_ind.set(qn("w:w"), str(indent))
107
+ tbl_ind.set(qn("w:type"), "dxa")
108
+ grid = table._tbl.tblGrid
109
+ for child in list(grid):
110
+ grid.remove(child)
111
+ for width in widths:
112
+ col = OxmlElement("w:gridCol")
113
+ col.set(qn("w:w"), str(width))
114
+ grid.append(col)
115
+ for row in table.rows:
116
+ for index, cell in enumerate(row.cells):
117
+ set_cell_width(cell, widths[index])
118
+ set_cell_margins(cell)
119
+ cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.CENTER
120
+
121
+
122
+ def set_table_borders(table, color=BORDER, size="6"):
123
+ tbl_pr = table._tbl.tblPr
124
+ borders = tbl_pr.first_child_found_in("w:tblBorders")
125
+ if borders is None:
126
+ borders = OxmlElement("w:tblBorders")
127
+ tbl_pr.append(borders)
128
+ for edge in ("top", "left", "bottom", "right", "insideH", "insideV"):
129
+ tag = f"w:{edge}"
130
+ element = borders.find(qn(tag))
131
+ if element is None:
132
+ element = OxmlElement(tag)
133
+ borders.append(element)
134
+ element.set(qn("w:val"), "single")
135
+ element.set(qn("w:sz"), size)
136
+ element.set(qn("w:space"), "0")
137
+ element.set(qn("w:color"), color)
138
+
139
+
140
+ def add_section_heading(doc, text):
141
+ return add_paragraph(doc, text, size=10.8, bold=True, color=BLUE, before=4, after=1, line=1.0)
142
+
143
+
144
+ def add_bullet(doc, label, detail):
145
+ paragraph = doc.add_paragraph(style="List Bullet")
146
+ format_paragraph(paragraph, before=0, after=1, line=1.03)
147
+ set_run_font(paragraph.add_run(f"{label}:"), 8.8, True, NAVY)
148
+ set_run_font(paragraph.add_run(detail), 8.8, False, INK)
149
+
150
+
151
+ def add_metadata_table(doc, config):
152
+ table = doc.add_table(rows=1, cols=4)
153
+ widths = [TABLE_WIDTH_DXA // 4] * 4
154
+ widths[-1] += TABLE_WIDTH_DXA - sum(widths)
155
+ values = [
156
+ ("版本", config["version"]),
157
+ ("日期", config["date"]),
158
+ ("开发周期", config["development_cycle"]),
159
+ ("报价有效期", f'{config["validity_days"]} 天'),
160
+ ]
161
+ for index, (label, value) in enumerate(values):
162
+ cell = table.cell(0, index)
163
+ set_cell_shading(cell, PALE_GRAY)
164
+ paragraph = cell.paragraphs[0]
165
+ format_paragraph(paragraph, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
166
+ set_run_font(paragraph.add_run(f"{label}:"), 8.0, True, MUTED)
167
+ set_run_font(paragraph.add_run(str(value)), 8.2, True, NAVY)
168
+ set_table_geometry(table, widths)
169
+ set_table_borders(table, "E2E8F0", "4")
170
+
171
+
172
+ def add_callout(doc, label, detail, fill=PALE_BLUE):
173
+ table = doc.add_table(rows=1, cols=1)
174
+ set_table_geometry(table, [TABLE_WIDTH_DXA])
175
+ set_table_borders(table, "9CC2E5", "7")
176
+ cell = table.cell(0, 0)
177
+ set_cell_shading(cell, fill)
178
+ paragraph = cell.paragraphs[0]
179
+ format_paragraph(paragraph, after=0, line=1.03)
180
+ set_run_font(paragraph.add_run(f"{label}:"), 8.8, True, NAVY)
181
+ set_run_font(paragraph.add_run(detail), 8.8, False, INK)
182
+
183
+
184
+ def add_quote_table(doc, config):
185
+ table = doc.add_table(rows=2, cols=3)
186
+ widths = [TABLE_WIDTH_DXA // 3] * 3
187
+ widths[-1] += TABLE_WIDTH_DXA - sum(widths)
188
+ headers = ["市场预估价(总价)", "吾码优惠报价(总价)", "开发周期"]
189
+ values = [config["market_price"], config["preferential_price"], config["development_cycle"]]
190
+ for index, header in enumerate(headers):
191
+ cell = table.cell(0, index)
192
+ set_cell_shading(cell, NAVY)
193
+ paragraph = cell.paragraphs[0]
194
+ format_paragraph(paragraph, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
195
+ set_run_font(paragraph.add_run(header), 8.7, True, WHITE)
196
+ for index, value in enumerate(values):
197
+ cell = table.cell(1, index)
198
+ set_cell_shading(cell, PALE_GOLD if index == 1 else PALE_GRAY)
199
+ paragraph = cell.paragraphs[0]
200
+ format_paragraph(paragraph, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
201
+ set_run_font(paragraph.add_run(str(value)), 12.0 if index == 1 else 10.5, True, GOLD if index == 1 else NAVY)
202
+ set_table_geometry(table, widths)
203
+ set_table_borders(table)
204
+
205
+
206
+ def normalize_config(raw):
207
+ required = [
208
+ "system_name", "project_goal", "solution_items", "technical_route",
209
+ "deliverables", "market_price", "preferential_price",
210
+ "development_cycle", "boundary_note",
211
+ ]
212
+ missing = [name for name in required if not raw.get(name)]
213
+ if missing:
214
+ raise ValueError("Missing required fields: " + ", ".join(missing))
215
+ if not isinstance(raw["solution_items"], list) or not raw["solution_items"]:
216
+ raise ValueError("solution_items must be a non-empty list")
217
+ raw.setdefault("version", "v1.0.0")
218
+ raw.setdefault("date", date.today().isoformat())
219
+ raw.setdefault("validity_days", 15)
220
+ raw.setdefault("subtitle", "AI 深度融合 V8引擎 的低代码平台,官网:https://microi.net")
221
+ raw.setdefault("project_scope", "")
222
+ return raw
223
+
224
+
225
+ def build_document(config, output):
226
+ doc = Document()
227
+ section = doc.sections[0]
228
+ section.page_width = Inches(8.27)
229
+ section.page_height = Inches(11.69)
230
+ section.top_margin = Inches(0.5)
231
+ section.bottom_margin = Inches(0.5)
232
+ section.left_margin = Inches(0.5)
233
+ section.right_margin = Inches(0.5)
234
+ section.header_distance = Inches(0.2)
235
+ section.footer_distance = Inches(0.2)
236
+
237
+ normal = doc.styles["Normal"]
238
+ normal.font.name = FONT
239
+ normal._element.rPr.rFonts.set(qn("w:eastAsia"), FONT)
240
+ normal.font.size = Pt(9.2)
241
+ normal.font.color.rgb = RGBColor.from_string(INK)
242
+
243
+ title = f'Microi吾码 {config["system_name"]} 解决方案与报价'
244
+ add_paragraph(doc, title, size=20.0, bold=True, color=NAVY, after=1, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
245
+ add_paragraph(doc, config["subtitle"], size=9.3, color=MUTED, after=4, line=1.0, align=WD_ALIGN_PARAGRAPH.CENTER)
246
+ add_metadata_table(doc, config)
247
+
248
+ add_section_heading(doc, "一、项目目标")
249
+ add_callout(doc, "目标", config["project_goal"])
250
+
251
+ add_section_heading(doc, "二、解决方案")
252
+ for item in config["solution_items"]:
253
+ if isinstance(item, str):
254
+ add_bullet(doc, "方案", item)
255
+ else:
256
+ add_bullet(doc, item.get("label", "方案"), item.get("detail", ""))
257
+
258
+ add_section_heading(doc, "三、核心技术路线")
259
+ add_callout(doc, "链路", config["technical_route"], fill=PALE_GRAY)
260
+
261
+ add_section_heading(doc, "四、交付范围")
262
+ add_paragraph(doc, config["deliverables"], size=8.7, after=1, line=1.03)
263
+ if config["project_scope"]:
264
+ add_paragraph(doc, config["project_scope"], size=8.3, color=MUTED, after=1, line=1.0)
265
+
266
+ add_section_heading(doc, "五、项目总价与周期")
267
+ add_quote_table(doc, config)
268
+
269
+ add_section_heading(doc, "六、说明与边界")
270
+ add_paragraph(doc, config["boundary_note"], size=8.1, color=MUTED, after=0, line=1.0)
271
+
272
+ footer = section.footer.paragraphs[0]
273
+ format_paragraph(footer, after=0, line=1.0, align=WD_ALIGN_PARAGRAPH.RIGHT)
274
+ set_run_font(footer.add_run(f'Microi吾码 | {config["version"]} | {config["date"]} | https://microi.net'), 7.2, False, MUTED)
275
+
276
+ doc.core_properties.title = title
277
+ doc.core_properties.subject = "Microi吾码系统解决方案与报价"
278
+ doc.core_properties.author = "Microi吾码"
279
+ output.parent.mkdir(parents=True, exist_ok=True)
280
+ doc.save(output)
281
+
282
+
283
+ def main():
284
+ parser = argparse.ArgumentParser(description="Build a compact Microi solution quotation DOCX")
285
+ parser.add_argument("--input", required=True, help="UTF-8 JSON configuration file")
286
+ parser.add_argument("--output", required=True, help="Output DOCX path")
287
+ args = parser.parse_args()
288
+ with open(args.input, "r", encoding="utf-8") as handle:
289
+ config = normalize_config(json.load(handle))
290
+ output = Path(args.output).resolve()
291
+ build_document(config, output)
292
+ print(output)
293
+
294
+
295
+ if __name__ == "__main__":
296
+ main()
@@ -11,9 +11,10 @@ description: 已初始化吾码工作区的默认 AI 开发总控入口,用户
11
11
 
12
12
  本 Skill 是已初始化吾码工作区的默认开发总控入口。用户直接描述需求即可,无需指定 Skill 名称;
13
13
  目标 MCP 连接可沿用当前项目或唯一已登录连接,需求文件/验收地址按需提供。
14
- 任何吾码开发任务先读本入口与八项清单,再按下表读取相关专项 Skill。
14
+ 任何吾码开发任务先读本入口、八项清单及其增补要求,再按下表读取相关专项 Skill。
15
15
  收到“完整开发/交付系统”请求时,必须先读取
16
16
  [完整系统开发默认交付清单(原推荐提示词 1–8)](references/progressive-01-标准工作流.md#完整系统开发默认交付清单原推荐提示词-18),
17
+ 并完整读取同一参考文件的[菜单先行与经营报表增补要求](references/progressive-01-标准工作流.md#菜单先行与经营报表增补要求强制),
17
18
  逐项执行并记录覆盖结果;不能只交方案、示例或待办。局部修改、只读分析按用户实际范围执行,
18
19
  这份默认清单不额外授权生产写入或客户部署。
19
20
 
@@ -23,11 +24,20 @@ description: 已初始化吾码工作区的默认 AI 开发总控入口,用户
23
24
  |---|---|
24
25
  | 需求文档、成熟数据模型、表/字段/索引 | `business-blueprint`、`microi-db-schema`、`microi-form-engine` |
25
26
  | 表单宽度、分组、整行控件与视觉 | `microi-form-layout`、`ui-design` |
26
- | 菜单、PageTabs、统计角标、模块指标、复合列、移动卡片 | `module-engine`、`v8-menu-buttons` |
27
+ | 菜单骨架、AI 开发中占位、逐菜单切换、PageTabs、模块展示 | `module-engine`、`v8-menu-buttons` |
27
28
  | 选项 Key/Label、数据源、状态模板 | `microi-datasource-mapping`、`datasource-engine`、`v8-template-engine` |
28
- | 业务接口、表单事件、看板与报表 | `v8-crud-api`、`v8-table-event`、`page-engine`、`report-engine` |
29
+ | 业务接口、表单事件 | `v8-crud-api`、`v8-table-event` |
30
+ | 业务统计、老板经营、领导管理看板及明细报表 | `page-engine`、`report-engine`、`datasource-engine` |
29
31
  | 真实业务、截图、账号和跨端验收 | `playwright-e2e`;有独立前端时再读相应前端 Skill |
30
32
 
33
+ 完整开发按“需求与现状分析 → 菜单和报表规划 → 独立骨架 Manifest 检查与 dry-run →
34
+ 授权落地菜单骨架 → 分阶段实现最终资源并独立预验收 → 保持菜单身份切换 → 原入口最终验收”推进。
35
+ 仅用户可见的新业务/报表叶菜单先指向共享 AI 开发中组件(`IsMicroiService=0`),
36
+ 保留独立业务 URL、标题和菜单身份;隐藏数据/授权载体维持原绑定,骨架不预绑定不存在资源。
37
+ 候选预验收不依赖仍占位的原 URL,切换失败精确恢复本菜单原绑定并列为未完成;
38
+ 详情按增补要求及 `module-engine` 执行。经营、领导和业务统计看板默认基于界面引擎,
39
+ 不能以列表角标或占位页替代。局部改动只处理范围内菜单/报表,只读分析不创建占位资源。
40
+
31
41
  每张由 AI/MCP 创建的业务表都必须同时设计默认表单 Banner,不能只建字段和菜单。完整
32
42
  Manifest 使用 `tables[].formBanner`;未显式配置时仍按字段类型选择业务编号/名称标题、
33
43
  客户/项目副标题、首个 `ImgUpload`、状态/类型标签和真实数值指标,并写入 `diy_table`
@@ -132,7 +142,7 @@ AI 零代码交付不能只验证管理员帐号和页面能打开。任何涉
132
142
  - 订单状态角标只给需要处理的状态,不给“全部/已完成/已取消”等静态分类堆数字。
133
143
 
134
144
  <!-- /microi-progressive:chunk -->
135
- <!-- microi-progressive:chunk id=microi-system-delivery-004 sha256=9a226a70ef240ce83151c140d002253dccbf7efa63653f61c8825f2b69c1c7d3 -->
145
+ <!-- microi-progressive:chunk id=microi-system-delivery-004 sha256=cc8ef54619f2f7a1dc3ba4268d02cbe678c855ba17e2d9e9ed3251ac0dc35e18 -->
136
146
  ## 交付完成定义
137
147
 
138
148
  一次 Microi 全系统交付只有同时满足以下条件才算完成:
@@ -145,6 +155,8 @@ AI 零代码交付不能只验证管理员帐号和页面能打开。任何涉
145
155
  6. 后端和前端服务可自动启动。
146
156
  7. Playwright 账号登录、接口契约、网络守卫、业务闭环、截图复核均通过。
147
157
  8. 生成或修改的通用经验已写回 microi.skills,且不能包含项目专属措辞;不能只写本地 memory、聊天记录或项目临时文档。
158
+ 9. 菜单树包含范围内全部业务与报表入口;每个本次新增可见业务/报表叶菜单已从开发中占位切换到验收通过的真实页面,菜单 Id、业务 URL、层级、排序与权限关联保持正确。仍有占位或阻塞的菜单逐项标为未完成。
159
+ 10. 基于界面引擎的业务统计、老板经营与领导管理报表已接通当前租户真实业务数据;指标口径、筛选/时间、权限、穿透、空/失败状态与明细对账均有证据。
148
160
 
149
161
  <!-- /microi-progressive:chunk -->
150
162
  ## 详细参考路由(渐进披露)