@hupan56/wlkj 2.7.12 → 3.1.2
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/bin/cli.js +1435 -1107
- package/package.json +29 -29
- package/templates/qoder/agents/insight-planning.md +1 -1
- package/templates/qoder/agents/insight-research.md +28 -15
- package/templates/qoder/commands/optional/wl-insight.md +161 -161
- package/templates/qoder/commands/optional/wl-report.md +20 -39
- package/templates/qoder/commands/optional/wl-status.md +4 -4
- package/templates/qoder/commands/wl-code.md +2 -2
- package/templates/qoder/commands/wl-design.md +6 -6
- package/templates/qoder/commands/wl-init.md +3 -3
- package/templates/qoder/commands/wl-prd.md +26 -13
- package/templates/qoder/commands/wl-req.md +43 -0
- package/templates/qoder/commands/wl-search.md +78 -47
- package/templates/qoder/commands/wl-task.md +343 -40
- package/templates/qoder/commands/wl-test.md +43 -17
- package/templates/qoder/config.yaml +52 -14
- package/templates/qoder/hooks/post-tool-use.py +181 -0
- package/templates/qoder/hooks/session-start.py +94 -23
- package/templates/qoder/hooks/stop-eval.py +207 -0
- package/templates/qoder/hooks/user-prompt-submit.py +139 -0
- package/templates/qoder/rules/wl-pipeline.md +85 -49
- package/templates/qoder/scripts/README.md +102 -0
- package/templates/qoder/scripts/capability/__init__.py +26 -0
- package/templates/qoder/scripts/capability/__main__.py +72 -0
- package/templates/qoder/scripts/capability/adapters/__init__.py +29 -0
- package/templates/qoder/scripts/capability/adapters/cli.py +316 -0
- package/templates/qoder/scripts/capability/adapters/mcp.py +334 -0
- package/templates/qoder/scripts/capability/adapters/qw.py +271 -0
- package/templates/qoder/scripts/capability/caps/__init__.py +27 -0
- package/templates/qoder/scripts/capability/caps/context.py +36 -0
- package/templates/qoder/scripts/capability/caps/cron.py +50 -0
- package/templates/qoder/scripts/capability/caps/identity.py +43 -0
- package/templates/qoder/scripts/capability/caps/memory.py +71 -0
- package/templates/qoder/scripts/capability/caps/notify.py +41 -0
- package/templates/qoder/scripts/capability/caps/present.py +48 -0
- package/templates/qoder/scripts/capability/caps/repo.py +31 -0
- package/templates/qoder/scripts/capability/caps/sandbox.py +43 -0
- package/templates/qoder/scripts/capability/chain.py +92 -0
- package/templates/qoder/scripts/capability/memory_chain.py +41 -0
- package/templates/qoder/scripts/capability/registry.py +246 -0
- package/templates/qoder/scripts/capability/registry_mcp.py +250 -0
- package/templates/qoder/scripts/capability/smoke_test.py +211 -0
- package/templates/qoder/scripts/capability/smoke_test_report.json +94 -0
- package/templates/qoder/scripts/deployment/setup/__init__.py +11 -0
- package/templates/qoder/scripts/deployment/setup/carriers.py +669 -0
- package/templates/qoder/scripts/deployment/setup/carriers_verify.py +148 -0
- package/templates/qoder/scripts/{init_doctor.py → deployment/setup/init_doctor.py} +100 -39
- package/templates/qoder/scripts/{install_qoderwork.py → deployment/setup/install_qoderwork.py} +48 -30
- package/templates/qoder/scripts/{platform_doctor.py → deployment/setup/platform_doctor.py} +271 -259
- package/templates/qoder/scripts/{repo_root.py → deployment/setup/repo_root.py} +9 -2
- package/templates/qoder/scripts/{setup.py → deployment/setup/setup.py} +143 -14
- package/templates/qoder/scripts/{setup_lanhu.py → deployment/setup/setup_lanhu.py} +979 -963
- package/templates/qoder/scripts/domain/design/__init__.py +0 -0
- package/templates/qoder/scripts/{fill_prototype.py → domain/design/fill_prototype.py} +17 -5
- package/templates/qoder/scripts/{gen_design_doc.py → domain/design/gen_design_doc.py} +406 -394
- package/templates/qoder/scripts/domain/kg/__init__.py +11 -0
- package/templates/qoder/scripts/domain/kg/build/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/build/build_entity_registry.py +201 -0
- package/templates/qoder/scripts/domain/kg/build/build_relations.py +132 -0
- package/templates/qoder/scripts/{build_style_index.py → domain/kg/build/build_style_index.py} +50 -15
- package/templates/qoder/scripts/domain/kg/build/build_workflows.py +149 -0
- package/templates/qoder/scripts/{kg_build.py → domain/kg/build/kg_build.py} +685 -612
- package/templates/qoder/scripts/{kg_build_db.py → domain/kg/build/kg_build_db.py} +338 -327
- package/templates/qoder/scripts/{kg_incremental.py → domain/kg/build/kg_incremental.py} +425 -393
- package/templates/qoder/scripts/{learn_aggregate.py → domain/kg/build/learn_aggregate.py} +212 -201
- package/templates/qoder/scripts/domain/kg/extract/__init__.py +0 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/extract.py +430 -419
- package/templates/qoder/scripts/domain/kg/extract/test_extract.py +115 -0
- package/templates/qoder/scripts/{common → domain/kg/extract}/ts_extract.py +614 -536
- package/templates/qoder/scripts/domain/kg/graph/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_link_db.py → domain/kg/graph/kg_link_db.py} +235 -224
- package/templates/qoder/scripts/domain/kg/graph/kg_semantic.py +683 -0
- package/templates/qoder/scripts/{kg.py → domain/kg/kg.py} +871 -708
- package/templates/qoder/scripts/domain/kg/kg_capabilities.py +182 -0
- package/templates/qoder/scripts/domain/kg/search/__init__.py +0 -0
- package/templates/qoder/scripts/{context_pack.py → domain/kg/search/context_pack.py} +45 -17
- package/templates/qoder/scripts/{enrich_prompt.py → domain/kg/search/enrich_prompt.py} +238 -226
- package/templates/qoder/scripts/domain/kg/search/prefetch.py +384 -0
- package/templates/qoder/scripts/{common → domain/kg/search}/search_engine.py +207 -205
- package/templates/qoder/scripts/{search_index.py → domain/kg/search/search_index.py} +210 -88
- package/templates/qoder/scripts/domain/kg/server/__init__.py +0 -0
- package/templates/qoder/scripts/domain/kg/server/kgd.py +549 -0
- package/templates/qoder/scripts/domain/kg/server/perf_bench.py +197 -0
- package/templates/qoder/scripts/domain/kg/storage/__init__.py +0 -0
- package/templates/qoder/scripts/{kg_duckdb.py → domain/kg/storage/kg_duckdb.py} +70 -45
- package/templates/qoder/scripts/domain/learning/__init__.py +0 -0
- package/templates/qoder/scripts/{learn.py → domain/learning/learn.py} +157 -146
- package/templates/qoder/scripts/domain/report/__init__.py +11 -0
- package/templates/qoder/scripts/{add_session.py → domain/report/add_session.py} +256 -244
- package/templates/qoder/scripts/{export.py → domain/report/export.py} +72 -4
- package/templates/qoder/scripts/{report.py → domain/report/report.py} +292 -281
- package/templates/qoder/scripts/domain/report/report_snapshot.py +261 -0
- package/templates/qoder/scripts/domain/report/role.py +39 -0
- package/templates/qoder/scripts/{status.py → domain/report/status.py} +737 -628
- package/templates/qoder/scripts/domain/report/status_snapshot.py +191 -0
- package/templates/qoder/scripts/domain/requirement/__init__.py +0 -0
- package/templates/qoder/scripts/{archive_prd.py → domain/requirement/archive_prd.py} +389 -377
- package/templates/qoder/scripts/domain/requirement/req.py +228 -0
- package/templates/qoder/scripts/domain/task/__init__.py +11 -0
- package/templates/qoder/scripts/{git_sync.py → domain/task/git_sync.py} +90 -50
- package/templates/qoder/scripts/{syncgate.py → domain/task/syncgate.py} +18 -7
- package/templates/qoder/scripts/domain/task/task.py +229 -0
- package/templates/qoder/scripts/domain/task/task_lifecycle.py +606 -0
- package/templates/qoder/scripts/domain/task/task_query.py +162 -0
- package/templates/qoder/scripts/domain/task/task_relations.py +425 -0
- package/templates/qoder/scripts/{team_sync.py → domain/task/team_sync.py} +101 -23
- package/templates/qoder/scripts/domain/task/zentao_sync.py +370 -0
- package/templates/qoder/scripts/foundation/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/bootstrap.py +145 -0
- package/templates/qoder/scripts/foundation/core/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/core/cmd_registry.py +112 -0
- package/templates/qoder/scripts/foundation/core/config.py +187 -0
- package/templates/qoder/scripts/{common → foundation/core}/paths.py +706 -400
- package/templates/qoder/scripts/foundation/data/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/data/contract.py +317 -0
- package/templates/qoder/scripts/{common → foundation/data}/events.py +2 -2
- package/templates/qoder/scripts/foundation/data/result.py +223 -0
- package/templates/qoder/scripts/{common → foundation/data}/task_utils.py +427 -392
- package/templates/qoder/scripts/foundation/identity/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/identity/check_publish.py +103 -0
- package/templates/qoder/scripts/{common → foundation/identity}/developer.py +239 -238
- package/templates/qoder/scripts/foundation/identity/guard.py +159 -0
- package/templates/qoder/scripts/{common → foundation/identity}/identity.py +132 -9
- package/templates/qoder/scripts/foundation/identity/roles.py +60 -0
- package/templates/qoder/scripts/foundation/integrations/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/integrations}/active_task.py +30 -15
- package/templates/qoder/scripts/{common → foundation/integrations}/feishu.py +11 -10
- package/templates/qoder/scripts/{common → foundation/integrations}/terms.py +3 -3
- package/templates/qoder/scripts/foundation/integrations/zentao_client.py +220 -0
- package/templates/qoder/scripts/foundation/io/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/io}/filelock.py +1 -1
- package/templates/qoder/scripts/{common → foundation/io}/reqid.py +1 -1
- package/templates/qoder/scripts/foundation/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/foundation/protocol/duckdb_conn.py +39 -0
- package/templates/qoder/scripts/foundation/protocol/mcp_base.py +268 -0
- package/templates/qoder/scripts/foundation/utils/__init__.py +0 -0
- package/templates/qoder/scripts/{common → foundation/utils}/eval_api.py +12 -1
- package/templates/qoder/scripts/{common → foundation/utils}/pip_install.py +23 -6
- package/templates/qoder/scripts/{common → foundation/utils}/platform_guard.py +1 -1
- package/templates/qoder/scripts/orchestration/__init__.py +0 -0
- package/templates/qoder/scripts/orchestration/wlkj.py +185 -0
- package/templates/qoder/scripts/protocol/__init__.py +0 -0
- package/templates/qoder/scripts/protocol/browser/README.md +207 -0
- package/templates/qoder/scripts/protocol/browser/SKILL.md +265 -0
- package/templates/qoder/scripts/protocol/browser/config.env +9 -0
- package/templates/qoder/scripts/protocol/browser/references/cdp-api.md +110 -0
- package/templates/qoder/scripts/protocol/browser/references/migration-2.5.3.md +72 -0
- package/templates/qoder/scripts/protocol/browser/references/site-patterns/.gitkeep +0 -0
- package/templates/qoder/scripts/protocol/browser/scripts/browser-discovery.mjs +138 -0
- package/templates/qoder/scripts/protocol/browser/scripts/cdp-proxy.mjs +672 -0
- package/templates/qoder/scripts/protocol/browser/scripts/check-deps.mjs +206 -0
- package/templates/qoder/scripts/protocol/browser/scripts/find-url.mjs +253 -0
- package/templates/qoder/scripts/protocol/browser/scripts/match-site.mjs +46 -0
- package/templates/qoder/scripts/protocol/browser/templates/config.env.template +9 -0
- package/templates/qoder/scripts/protocol/mcp/__init__.py +11 -0
- package/templates/qoder/scripts/protocol/mcp/kg_mcp_server.py +478 -0
- package/templates/qoder/scripts/{lanhu_stdio_wrapper.py → protocol/mcp/lanhu_stdio_wrapper.py} +131 -119
- package/templates/qoder/scripts/protocol/mcp/mcp_doctor.py +548 -0
- package/templates/qoder/scripts/protocol/mcp/mcp_launcher.py +600 -0
- package/templates/qoder/scripts/protocol/mcp/mysql_mcp_server.py +461 -0
- package/templates/qoder/scripts/protocol/mcp/zentao_mcp_server.py +1920 -0
- package/templates/qoder/scripts/protocol/transports/__init__.py +56 -0
- package/templates/qoder/scripts/protocol/transports/base.py +87 -0
- package/templates/qoder/scripts/protocol/transports/cli.py +132 -0
- package/templates/qoder/scripts/protocol/transports/http.py +141 -0
- package/templates/qoder/scripts/protocol/transports/stdio.py +293 -0
- package/templates/qoder/scripts/run_weekly_update.bat +27 -18
- package/templates/qoder/scripts/run_weekly_update.sh +22 -13
- package/templates/qoder/scripts/validation/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/__init__.py +0 -0
- package/templates/qoder/scripts/validation/eval/perf-report-2026-06-24.md +139 -0
- package/templates/qoder/scripts/validation/eval/qwork_harness.py +617 -0
- package/templates/qoder/scripts/validation/eval/report-commands.md +220 -0
- package/templates/qoder/scripts/validation/eval/transcript_timing.py +386 -0
- package/templates/qoder/scripts/validation/metrics/__init__.py +0 -0
- package/templates/qoder/scripts/{eval_prd.py → validation/metrics/eval_prd.py} +84 -15
- package/templates/qoder/scripts/validation/metrics/usability_score.py +750 -0
- package/templates/qoder/scripts/validation/test/__init__.py +11 -0
- package/templates/qoder/scripts/validation/test/assertion_gen.py +551 -0
- package/templates/qoder/scripts/{autotest.py → validation/test/autotest.py} +1234 -1751
- package/templates/qoder/scripts/validation/test/autotest_auth.py +109 -0
- package/templates/qoder/scripts/{autotest_batch.py → validation/test/autotest_batch.py} +255 -224
- package/templates/qoder/scripts/validation/test/autotest_data.py +680 -0
- package/templates/qoder/scripts/validation/test/autotest_opencli.py +646 -0
- package/templates/qoder/scripts/{autotest_run.py → validation/test/autotest_run.py} +323 -297
- package/templates/qoder/scripts/validation/test/autotest_webaccess.py +571 -0
- package/templates/qoder/scripts/{benchmark.py → validation/test/benchmark.py} +17 -5
- package/templates/qoder/scripts/{kg_auto_login.py → validation/test/kg_auto_login.py} +208 -196
- package/templates/qoder/scripts/{kg_test_runner.py → validation/test/kg_test_runner.py} +13 -2
- package/templates/qoder/scripts/{page_probe.py → validation/test/page_probe.py} +470 -459
- package/templates/qoder/scripts/validation/test/smoke_all_commands.py +170 -0
- package/templates/qoder/settings.json +23 -2
- package/templates/qoder/skills/design-import/SKILL.md +235 -226
- package/templates/qoder/skills/design-review/SKILL.md +91 -82
- package/templates/qoder/skills/prd-generator/SKILL.md +38 -24
- package/templates/qoder/skills/prd-review/SKILL.md +8 -6
- package/templates/qoder/skills/prototype-generator/SKILL.md +296 -247
- package/templates/qoder/skills/spec-coder/SKILL.md +24 -15
- package/templates/qoder/skills/spec-generator/SKILL.md +24 -12
- package/templates/qoder/skills/test-generator/SKILL.md +9 -7
- package/templates/qoder/skills/wl-code/SKILL.md +25 -13
- package/templates/qoder/skills/wl-commit/SKILL.md +10 -9
- package/templates/qoder/skills/wl-design/SKILL.md +7 -5
- package/templates/qoder/skills/wl-init/SKILL.md +8 -8
- package/templates/qoder/skills/wl-insight/SKILL.md +25 -12
- package/templates/qoder/skills/wl-prd-full/SKILL.md +59 -8
- package/templates/qoder/skills/wl-prd-quick/SKILL.md +7 -7
- package/templates/qoder/skills/wl-prd-review/SKILL.md +18 -6
- package/templates/qoder/skills/wl-report/SKILL.md +27 -25
- package/templates/qoder/skills/wl-search/SKILL.md +151 -80
- package/templates/qoder/skills/wl-spec/SKILL.md +9 -7
- package/templates/qoder/skills/wl-status/SKILL.md +50 -18
- package/templates/qoder/skills/wl-task/SKILL.md +94 -12
- package/templates/qoder/skills/wl-test/SKILL.md +154 -45
- package/templates/qoder/templates/prd-full-template.md +7 -0
- package/templates/root/AGENTS.md +275 -237
- package/templates/root/requirements.txt +3 -0
- package/templates/root//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
- package/templates/root//346/226/260/346/211/213/346/214/207/345/215/227.md +2 -2
- package/templates/qoder/hooks/inject-workflow-state.py +0 -169
- package/templates/qoder/scripts/__pycache__/check_mcp_launch.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/install_qoderwork.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/mcp_launcher.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/__pycache__/platform_doctor.cpython-39.pyc +0 -0
- package/templates/qoder/scripts/check_carriers.py +0 -238
- package/templates/qoder/scripts/check_mcp.py +0 -298
- package/templates/qoder/scripts/check_mcp_launch.py +0 -183
- package/templates/qoder/scripts/check_qoderwork_consistency.py +0 -166
- package/templates/qoder/scripts/collect_prds.py +0 -31
- package/templates/qoder/scripts/common/mentions.py +0 -134
- package/templates/qoder/scripts/common/utf8.py +0 -38
- package/templates/qoder/scripts/extract_api_params.py +0 -246
- package/templates/qoder/scripts/extract_routes.py +0 -54
- package/templates/qoder/scripts/extract_routes_tree.py +0 -78
- package/templates/qoder/scripts/handoff.py +0 -22
- package/templates/qoder/scripts/init_developer.py +0 -76
- package/templates/qoder/scripts/kg_mcp_server.py +0 -801
- package/templates/qoder/scripts/kg_semantic.py +0 -150
- package/templates/qoder/scripts/mcp_launcher.py +0 -414
- package/templates/qoder/scripts/mysql_mcp_server.py +0 -396
- package/templates/qoder/scripts/parse_prds.py +0 -33
- package/templates/qoder/scripts/role.py +0 -51
- package/templates/qoder/scripts/sync_carriers.py +0 -259
- package/templates/qoder/scripts/task.py +0 -1261
- package/templates/qoder/scripts/workspace_init.py +0 -102
- package/templates/qoder/scripts/zentao_mcp_server.py +0 -424
- package/templates/qoder/skills/prompt-enrich/SKILL.md +0 -90
- /package/templates/qoder/scripts/{common → deployment}/__init__.py +0 -0
- /package/templates/qoder/{skills/prototype-generator/SKILL.md.zcode-79180-2af4721f-f9a6-412c-88db-c0af680d211b.tmp → scripts/domain/__init__.py} +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/graph}/graph_traverse.py +0 -0
- /package/templates/qoder/scripts/{common → domain/kg/server}/repowiki.py +0 -0
- /package/templates/qoder/scripts/{common → foundation/io}/atomicio.py +0 -0
- /package/templates/qoder/scripts/{secure-ls.js → validation/test/secure-ls.js} +0 -0
|
@@ -1,394 +1,406 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# -*- coding: utf-8 -*-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
lines.append('
|
|
72
|
-
lines.append('')
|
|
73
|
-
for
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
lines.append('')
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
lines.append('')
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
lines.append('')
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
lines.append('
|
|
149
|
-
lines.append('
|
|
150
|
-
lines.append('
|
|
151
|
-
lines.append('
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
lines.append('
|
|
163
|
-
lines.append('')
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
lines.append('
|
|
175
|
-
lines.append('')
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
'
|
|
181
|
-
'
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
#
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
lines.append(f' -
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
lines.append('
|
|
243
|
-
lines.append('')
|
|
244
|
-
lines.append('
|
|
245
|
-
lines.append('')
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
lines.append('')
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
lines.append('
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
lines.append('
|
|
326
|
-
lines.append('
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
lines.append('
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
# ==========
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
lines.append('
|
|
360
|
-
lines.append('')
|
|
361
|
-
lines.append('
|
|
362
|
-
lines.append('
|
|
363
|
-
lines.append('
|
|
364
|
-
lines.append('')
|
|
365
|
-
lines.append('
|
|
366
|
-
lines.append('
|
|
367
|
-
lines.append('
|
|
368
|
-
lines.append('
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# v3.0 路径自举: 引导到 common/bootstrap, 统一 sys.path 逻辑
|
|
4
|
+
import os as _o, sys as _s
|
|
5
|
+
_f = _o.path.abspath(__file__)
|
|
6
|
+
for _ in range(10):
|
|
7
|
+
_f = _o.path.dirname(_f)
|
|
8
|
+
_cp = _o.path.join(_f, 'foundation')
|
|
9
|
+
if _o.path.isfile(_o.path.join(_cp, 'bootstrap.py')):
|
|
10
|
+
break
|
|
11
|
+
if _cp not in _s.path: _s.path.insert(0, _cp)
|
|
12
|
+
from bootstrap import setup; setup()
|
|
13
|
+
from foundation.core.paths import get_repo_root
|
|
14
|
+
|
|
15
|
+
"""
|
|
16
|
+
gen_design_doc.py - 自动生成 DESIGN.md (给 AI 的设计系统说明书)
|
|
17
|
+
|
|
18
|
+
从 ui-style.json + ref-vben-style.json + ref-icon.json 等索引数据
|
|
19
|
+
组装成一个单一文件,AI 出原型前必须先读这个文件。
|
|
20
|
+
|
|
21
|
+
解决了业界最佳实践的核心思路:
|
|
22
|
+
- v0.dev: AI 读 Registry → 用正确 token 生成
|
|
23
|
+
- design.md (GitHub): 从 URL 提取 → DESIGN.md → AI 读
|
|
24
|
+
- 我们: 从真实代码索引 → DESIGN.md → AI 读 → 不再猜风格
|
|
25
|
+
|
|
26
|
+
Usage:
|
|
27
|
+
python gen_design_doc.py # 生成到 data/design/DESIGN.md
|
|
28
|
+
python gen_design_doc.py --stdout # 输出到 stdout (给 AI 直接读)
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
import os
|
|
32
|
+
import sys
|
|
33
|
+
import json
|
|
34
|
+
import re
|
|
35
|
+
import time
|
|
36
|
+
|
|
37
|
+
# UTF-8 stdio
|
|
38
|
+
try:
|
|
39
|
+
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
|
40
|
+
except (AttributeError, TypeError, OSError, IOError):
|
|
41
|
+
try:
|
|
42
|
+
sys.stdout.reconfigure(encoding='utf-8')
|
|
43
|
+
except Exception:
|
|
44
|
+
pass
|
|
45
|
+
|
|
46
|
+
BASE = get_repo_root()
|
|
47
|
+
INDEX_DIR = os.path.join(BASE, 'data', 'index')
|
|
48
|
+
OUTPUT_DIR = os.path.join(BASE, 'data', 'design')
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def load_json(filename):
|
|
52
|
+
path = os.path.join(INDEX_DIR, filename)
|
|
53
|
+
if not os.path.isfile(path):
|
|
54
|
+
return {}
|
|
55
|
+
try:
|
|
56
|
+
with open(path, encoding='utf-8') as f:
|
|
57
|
+
return json.load(f)
|
|
58
|
+
except Exception:
|
|
59
|
+
return {}
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def gen_design_doc():
|
|
63
|
+
"""生成 DESIGN.md 内容。返回字符串。"""
|
|
64
|
+
# 加载数据源
|
|
65
|
+
ui_style = load_json('ui-style.json')
|
|
66
|
+
vben = load_json('ref-vben-style.json')
|
|
67
|
+
icon = load_json('ref-icon.json')
|
|
68
|
+
chart_style = load_json('ref-chart-style.json')
|
|
69
|
+
|
|
70
|
+
lines = []
|
|
71
|
+
lines.append('# DESIGN.md - AI Prototype Design System Reference')
|
|
72
|
+
lines.append('')
|
|
73
|
+
lines.append('> **This file is the SINGLE SOURCE OF TRUTH for AI when generating prototypes.')
|
|
74
|
+
lines.append('> AI MUST read this file before generating ANY prototype. Do NOT invent styles.**')
|
|
75
|
+
lines.append('')
|
|
76
|
+
lines.append('> Auto-generated by gen_design_doc.py from real code indexes.')
|
|
77
|
+
lines.append(f'> Generated: {time.strftime("%Y-%m-%d %H:%M")}')
|
|
78
|
+
lines.append('')
|
|
79
|
+
lines.append('---')
|
|
80
|
+
lines.append('')
|
|
81
|
+
|
|
82
|
+
# ========== 1. Layout Fingerprint ==========
|
|
83
|
+
lines.append('## 1. Layout Fingerprint (from real pages)')
|
|
84
|
+
lines.append('')
|
|
85
|
+
for proj_name, pdata in (ui_style.get('projects') or {}).items():
|
|
86
|
+
fp = pdata.get('layout_fingerprint')
|
|
87
|
+
if not fp:
|
|
88
|
+
continue
|
|
89
|
+
lines.append(f'### {proj_name}')
|
|
90
|
+
lines.append('')
|
|
91
|
+
sidebar_w = fp.get('sidebar_width') or '?'
|
|
92
|
+
radius_v = fp.get('radius') or '?'
|
|
93
|
+
lines.append(f'- **Layout mode**: {fp.get("layout", "unknown")} (determines sidebar/header structure)')
|
|
94
|
+
lines.append(f'- **Stack**: {fp.get("stack", "unknown")}')
|
|
95
|
+
lines.append(f'- **Theme**: {fp.get("theme_mode", "light")}')
|
|
96
|
+
lines.append(f'- **Sidebar**: width={sidebar_w}px, semi_dark={fp.get("semi_dark_sidebar", False)}')
|
|
97
|
+
lines.append(f'- **Topbar**: dark header with logo + tools')
|
|
98
|
+
lines.append(f'- **Access mode**: {fp.get("access_mode", "unknown")} (menus come from backend API)')
|
|
99
|
+
lines.append(f'- **Border radius**: {radius_v}rem')
|
|
100
|
+
|
|
101
|
+
if fp.get('layout') == 'mixed-nav':
|
|
102
|
+
lines.append('')
|
|
103
|
+
lines.append('**mixed-nav layout means:**')
|
|
104
|
+
lines.append('- Top bar: logo + notification + avatar (dark background)')
|
|
105
|
+
lines.append('- Left sidebar: module-level menus (with icons)')
|
|
106
|
+
lines.append('- Content area: breadcrumb + page header + content')
|
|
107
|
+
lines.append('- Menu depth: 3-4 levels supported (ParentView nesting)')
|
|
108
|
+
lines.append('')
|
|
109
|
+
|
|
110
|
+
# ========== 2. Design Tokens ==========
|
|
111
|
+
lines.append('## 2. Design Tokens (CSS Variables)')
|
|
112
|
+
lines.append('')
|
|
113
|
+
lines.append('**MUST use these exact values. NEVER hardcode #1890ff (Ant Design default).**')
|
|
114
|
+
lines.append('')
|
|
115
|
+
|
|
116
|
+
# From vben reference
|
|
117
|
+
dt = vben.get('design_tokens', {})
|
|
118
|
+
if dt:
|
|
119
|
+
lines.append('### Color Tokens (from vben-style-reference)')
|
|
120
|
+
lines.append('')
|
|
121
|
+
color_tokens = {k: v for k, v in dt.items()
|
|
122
|
+
if 'color' in k.lower() or 'background' in k.lower()
|
|
123
|
+
or 'foreground' in k.lower() or 'primary' in k.lower()
|
|
124
|
+
or 'destructive' in k.lower() or 'success' in k.lower()
|
|
125
|
+
or 'warning' in k.lower() or 'border' in k.lower()
|
|
126
|
+
or 'input' in k.lower() or 'muted' in k.lower()
|
|
127
|
+
or 'card' in k.lower() or 'popover' in k.lower()
|
|
128
|
+
or 'radius' in k.lower() or 'font' in k.lower()}
|
|
129
|
+
for k, v in color_tokens.items():
|
|
130
|
+
lines.append(f' {k}: {v}')
|
|
131
|
+
lines.append('')
|
|
132
|
+
|
|
133
|
+
# From layout fingerprint (authoritative)
|
|
134
|
+
fp_primary = None
|
|
135
|
+
fp_destructive = None
|
|
136
|
+
fp_success = None
|
|
137
|
+
fp_warning = None
|
|
138
|
+
# 优先取有 color_primary 的指纹 (fywl-ui 有完整颜色, Carmg-H5 可能没有)
|
|
139
|
+
for proj_name, pdata in (ui_style.get('projects') or {}).items():
|
|
140
|
+
fp = pdata.get('layout_fingerprint', {})
|
|
141
|
+
if fp and fp.get('color_primary'):
|
|
142
|
+
fp_primary = fp.get('color_primary')
|
|
143
|
+
fp_destructive = fp.get('color_destructive')
|
|
144
|
+
fp_success = fp.get('color_success')
|
|
145
|
+
fp_warning = fp.get('color_warning')
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
lines.append('### Authoritative Colors (from layout_fingerprint - HIGHEST priority)')
|
|
149
|
+
lines.append('')
|
|
150
|
+
lines.append('```css')
|
|
151
|
+
lines.append(':root {')
|
|
152
|
+
if fp_primary:
|
|
153
|
+
lines.append(f' --primary: {fp_primary}')
|
|
154
|
+
if fp_destructive:
|
|
155
|
+
lines.append(f' --destructive: {fp_destructive}')
|
|
156
|
+
if fp_success:
|
|
157
|
+
lines.append(f' --success: {fp_success}')
|
|
158
|
+
if fp_warning:
|
|
159
|
+
lines.append(f' --warning: {fp_warning}')
|
|
160
|
+
lines.append(' --background-deep: hsl(216, 20.11%, 95.47%)')
|
|
161
|
+
lines.append(' --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif')
|
|
162
|
+
lines.append(' --radius: 0.25rem')
|
|
163
|
+
lines.append('}')
|
|
164
|
+
lines.append('```')
|
|
165
|
+
lines.append('')
|
|
166
|
+
|
|
167
|
+
lines.append('### FORBIDDEN Colors (NEVER use)')
|
|
168
|
+
lines.append('')
|
|
169
|
+
lines.append('- `#1890ff` - Ant Design default, NOT this project\'s primary')
|
|
170
|
+
lines.append('- Any color not listed above without citing a source file')
|
|
171
|
+
lines.append('')
|
|
172
|
+
|
|
173
|
+
# ========== 3. Page Types ==========
|
|
174
|
+
lines.append('## 3. Page Types (how this system structures pages)')
|
|
175
|
+
lines.append('')
|
|
176
|
+
for proj_name, pdata in (ui_style.get('projects') or {}).items():
|
|
177
|
+
pt = pdata.get('page_types', {})
|
|
178
|
+
if not pt:
|
|
179
|
+
continue
|
|
180
|
+
lines.append(f'### {proj_name} page type distribution')
|
|
181
|
+
lines.append('')
|
|
182
|
+
for page_type, count in sorted(pt.items(), key=lambda x: -x[1]):
|
|
183
|
+
lines.append(f'- **{page_type}**: {count} pages')
|
|
184
|
+
lines.append('')
|
|
185
|
+
|
|
186
|
+
lines.append('### Common page patterns (extracted from real pages)')
|
|
187
|
+
lines.append('')
|
|
188
|
+
# 从 page_examples 动态提取每种页面类型的真实布局模式
|
|
189
|
+
_page_type_labels = {
|
|
190
|
+
'table-page': 'Table page (search + grid + pagination)',
|
|
191
|
+
'form-page': 'Form page (structured input, NOT a fixed template)',
|
|
192
|
+
'detail-page': 'Detail page (read-only data display)',
|
|
193
|
+
'modal': 'Modal/Drawer (popup form or detail)',
|
|
194
|
+
'dashboard': 'Dashboard (summary cards + charts)',
|
|
195
|
+
}
|
|
196
|
+
# 统计每种类型的真实布局模式
|
|
197
|
+
for proj_name, pdata in (ui_style.get('projects') or {}).items():
|
|
198
|
+
pe = pdata.get('page_examples') or {}
|
|
199
|
+
for pt_key in ['table-page', 'form-page', 'detail-page', 'modal', 'dashboard']:
|
|
200
|
+
examples = pe.get(pt_key, [])
|
|
201
|
+
real_examples = [e for e in examples if isinstance(e, dict) and (e.get('layout') or e.get('form_fields') or e.get('table_columns') or e.get('behaviors') or e.get('form_components'))]
|
|
202
|
+
if not real_examples:
|
|
203
|
+
continue
|
|
204
|
+
label = _page_type_labels.get(pt_key, pt_key)
|
|
205
|
+
lines.append(f'- **{label}** ({proj_name}, {len(real_examples)} examples)')
|
|
206
|
+
# 提取布局模式
|
|
207
|
+
ex0 = real_examples[0]
|
|
208
|
+
layout = ex0.get('layout', {})
|
|
209
|
+
if layout:
|
|
210
|
+
if layout.get('grid_cols'):
|
|
211
|
+
lines.append(f' - Grid columns: {layout["grid_cols"]} (real pattern from {ex0["file"].split("/")[-1]})')
|
|
212
|
+
if layout.get('label_width'):
|
|
213
|
+
lines.append(f' - Label width: {layout["label_width"]}px')
|
|
214
|
+
# 表单组件
|
|
215
|
+
fc = ex0.get('form_components', [])
|
|
216
|
+
if fc:
|
|
217
|
+
lines.append(f' - Form components: {", ".join(fc[:6])}')
|
|
218
|
+
# 交互行为 (侧边栏折叠/表格分页/表单模式)
|
|
219
|
+
behaviors = ex0.get('behaviors', {})
|
|
220
|
+
if behaviors:
|
|
221
|
+
bh_strs = []
|
|
222
|
+
for bk, bv in behaviors.items():
|
|
223
|
+
bh_strs.append(f'{bk}={bv}')
|
|
224
|
+
lines.append(f' - Behaviors: {", ".join(bh_strs)}')
|
|
225
|
+
# 表单字段数
|
|
226
|
+
ff_count = len(ex0.get('form_fields', []))
|
|
227
|
+
if ff_count:
|
|
228
|
+
lines.append(f' - Field count: {ff_count} fields (typical)')
|
|
229
|
+
sample_fields = [f.get('label', f.get('field', '')) for f in ex0.get('form_fields', [])[:5]]
|
|
230
|
+
lines.append(f' - Sample fields: {", ".join(sample_fields)}')
|
|
231
|
+
# 表格列数
|
|
232
|
+
tc_count = len(ex0.get('table_columns', []))
|
|
233
|
+
if tc_count:
|
|
234
|
+
lines.append(f' - Column count: {tc_count} columns (typical)')
|
|
235
|
+
sample_cols = [c.get('title', c.get('field', '')) for c in ex0.get('table_columns', [])[:5]]
|
|
236
|
+
lines.append(f' - Sample columns: {", ".join(sample_cols)}')
|
|
237
|
+
lines.append(f' - Reference file: {ex0.get("file", "?")}')
|
|
238
|
+
lines.append('')
|
|
239
|
+
break # 只取第一个有数据的项目 (通常是 fywl-ui)
|
|
240
|
+
|
|
241
|
+
lines.append('### Form page behavior modes (NOT a fixed template - choose by requirement)')
|
|
242
|
+
lines.append('')
|
|
243
|
+
lines.append('Form pages vary by section count. DO NOT use one fixed template. Match the mode to the requirement:')
|
|
244
|
+
lines.append('- **Single-section form**: one form, 2-3 grid columns, label 80-140px, bottom save/reset buttons')
|
|
245
|
+
lines.append('- **Multi-section form**: tabs or accordion separating sections, each section 2-3 columns')
|
|
246
|
+
lines.append('- **Step wizard form**: horizontal step indicator + per-step form + back/next buttons')
|
|
247
|
+
lines.append('- **Drawer form**: right-side drawer popup, single column, compact layout')
|
|
248
|
+
lines.append('')
|
|
249
|
+
lines.append('> fill_prototype.py extracts real grid_cols/label_width from page_examples.')
|
|
250
|
+
lines.append('> The form layout adapts to the matched example, NOT a hardcoded shell.')
|
|
251
|
+
lines.append('')
|
|
252
|
+
|
|
253
|
+
# ========== 4. Top Fields ==========
|
|
254
|
+
lines.append('## 4. Top Used Fields (real field names -> Chinese labels)')
|
|
255
|
+
lines.append('')
|
|
256
|
+
lines.append('These are the most common fields across all pages. Use them for new pages.')
|
|
257
|
+
lines.append('')
|
|
258
|
+
fm = ui_style.get('field_map') or {}
|
|
259
|
+
top_fields = sorted(fm.items(), key=lambda x: len(x[1].get('titles', set()) if isinstance(x[1].get('titles'), set) else x[1].get('titles', [])), reverse=True)[:30]
|
|
260
|
+
for field_name, info in top_fields:
|
|
261
|
+
titles = info.get('titles', set())
|
|
262
|
+
if isinstance(titles, set):
|
|
263
|
+
title = next(iter(titles), field_name)
|
|
264
|
+
elif isinstance(titles, list) and titles:
|
|
265
|
+
title = titles[0]
|
|
266
|
+
else:
|
|
267
|
+
title = field_name
|
|
268
|
+
count = info.get('count', 0)
|
|
269
|
+
lines.append(f'- `{field_name}` -> {title} (used {count}x)')
|
|
270
|
+
lines.append('')
|
|
271
|
+
|
|
272
|
+
# ========== 4.5 Feature Modules (from prd-features.json) ==========
|
|
273
|
+
pf = load_json('prd-features.json')
|
|
274
|
+
features = pf.get('features', {}) if isinstance(pf, dict) else {}
|
|
275
|
+
if features:
|
|
276
|
+
lines.append('## 4.5 Feature Modules (系统有哪些功能模块)')
|
|
277
|
+
lines.append('')
|
|
278
|
+
lines.append('出原型时, 先确定需求属于哪个模块, 参考该模块的现有页面。')
|
|
279
|
+
lines.append('')
|
|
280
|
+
for feat_key, feat_info in list(features.items())[:12]:
|
|
281
|
+
cn = feat_info.get('cn', feat_key)
|
|
282
|
+
page_count = feat_info.get('page_count', 0)
|
|
283
|
+
api_count = feat_info.get('api_count', 0)
|
|
284
|
+
pages = feat_info.get('pages', [])
|
|
285
|
+
sample_pages = ', '.join(pages[:2]) if pages else ''
|
|
286
|
+
lines.append(f'- **{cn}** ({feat_key}): {page_count} pages, {api_count} APIs')
|
|
287
|
+
if sample_pages:
|
|
288
|
+
lines.append(f' - 标杆页面: {sample_pages}')
|
|
289
|
+
lines.append('')
|
|
290
|
+
|
|
291
|
+
# ========== 4.6 Real Buttons (from entity-registry.json) ==========
|
|
292
|
+
er = load_json('entity-registry.json')
|
|
293
|
+
entities = er.get('entities', {}) if isinstance(er, dict) else {}
|
|
294
|
+
button_samples = []
|
|
295
|
+
for eid, info in list(entities.items())[:500]:
|
|
296
|
+
if not eid.startswith('B:'):
|
|
297
|
+
continue
|
|
298
|
+
props = info.get('properties', {}) if isinstance(info, dict) else {}
|
|
299
|
+
btn_text = props.get('button_text', '')
|
|
300
|
+
if btn_text and btn_text not in [b['text'] for b in button_samples]:
|
|
301
|
+
button_samples.append({'text': btn_text, 'endpoint': props.get('endpoint', '')})
|
|
302
|
+
if len(button_samples) >= 15:
|
|
303
|
+
break
|
|
304
|
+
if button_samples:
|
|
305
|
+
lines.append('## 4.6 Real Button Texts (系统里真实的按钮文案)')
|
|
306
|
+
lines.append('')
|
|
307
|
+
lines.append('出原型时, 按钮文案用这些真实的, 不要编"新增/编辑"。')
|
|
308
|
+
lines.append('')
|
|
309
|
+
for b in button_samples:
|
|
310
|
+
ep = ' -> ' + b['endpoint'] if b['endpoint'] else ''
|
|
311
|
+
lines.append(f'- {b["text"]}{ep}')
|
|
312
|
+
lines.append('')
|
|
313
|
+
|
|
314
|
+
# ========== 5. Components ==========
|
|
315
|
+
lines.append('## 5. UI Components (from real code)')
|
|
316
|
+
lines.append('')
|
|
317
|
+
cc = ui_style.get('common_components') or {}
|
|
318
|
+
lines.append('### Most used Ant Design Vue components')
|
|
319
|
+
lines.append('')
|
|
320
|
+
for comp, count in list(cc.items())[:15]:
|
|
321
|
+
lines.append(f'- {comp}: {count}')
|
|
322
|
+
lines.append('')
|
|
323
|
+
|
|
324
|
+
fc = ui_style.get('form_components') or {}
|
|
325
|
+
lines.append('### Form components')
|
|
326
|
+
lines.append('')
|
|
327
|
+
for comp, count in list(fc.items())[:10]:
|
|
328
|
+
lines.append(f'- {comp}: {count}')
|
|
329
|
+
lines.append('')
|
|
330
|
+
|
|
331
|
+
# ========== 6. Icons ==========
|
|
332
|
+
lines.append('## 6. Icons')
|
|
333
|
+
lines.append('')
|
|
334
|
+
lines.append('**MUST use real icon system. NEVER use emoji.**')
|
|
335
|
+
lines.append('')
|
|
336
|
+
lines.append('- **Web (fywl-ui)**: Ant Design inline SVG icons')
|
|
337
|
+
lines.append(' - Source: `data/index/ref-icon.json`')
|
|
338
|
+
lines.append(' - Usage: `<svg viewBox="0 0 1024 1024" fill="currentColor">...</svg>`')
|
|
339
|
+
lines.append(' - Size: 14-16px for menu items, 18-20px for actions')
|
|
340
|
+
lines.append('- **APP (Carmg-H5)**: Vant font icons')
|
|
341
|
+
lines.append(' - CDN: https://cdn.jsdelivr.net/npm/vant@4/lib/index.css')
|
|
342
|
+
lines.append(' - Usage: `<i class="van-icon van-icon-xxx"></i>`')
|
|
343
|
+
lines.append('')
|
|
344
|
+
|
|
345
|
+
# ========== 7. Dashboard / Chart styles ==========
|
|
346
|
+
chart_data = chart_style
|
|
347
|
+
if chart_data:
|
|
348
|
+
lines.append('## 7. Dashboard / Chart Styles')
|
|
349
|
+
lines.append('')
|
|
350
|
+
lines.append('Two in-system chart idioms:')
|
|
351
|
+
lines.append('- **Dark screen** (monitor): background `#011845`, accent `#53CFF0`/`#FFE646`')
|
|
352
|
+
lines.append('- **Light card** (workbench): white cards, blue accent `#2E61E0`')
|
|
353
|
+
lines.append('')
|
|
354
|
+
lines.append('Source: `data/index/ref-chart-style.json`')
|
|
355
|
+
lines.append('')
|
|
356
|
+
|
|
357
|
+
# ========== 8. Prototype Rules ==========
|
|
358
|
+
lines.append('## 8. Prototype Rules (AI MUST follow)')
|
|
359
|
+
lines.append('')
|
|
360
|
+
lines.append('1. **Token priority**: layout_fingerprint > vben-style-reference > code > PDF')
|
|
361
|
+
lines.append('2. **Only change the diff**: everything else identical to existing page')
|
|
362
|
+
lines.append('3. **Single file**: one HTML with all CSS and JS inline')
|
|
363
|
+
lines.append('4. **Interactive**: buttons click, forms fill, tables scroll')
|
|
364
|
+
lines.append('5. **Icons**: real SVG/Webfont only, NEVER emoji')
|
|
365
|
+
lines.append('6. **Colors**: from this file only, NEVER invent')
|
|
366
|
+
lines.append('7. **Layout**: sidebar_width/topbar_height from layout_fingerprint')
|
|
367
|
+
lines.append('8. **Source citation**: header comment MUST cite which files provided the style')
|
|
368
|
+
lines.append('')
|
|
369
|
+
|
|
370
|
+
# ========== 9. How to generate ==========
|
|
371
|
+
lines.append('## 9. How to Generate a Prototype')
|
|
372
|
+
lines.append('')
|
|
373
|
+
lines.append('```bash')
|
|
374
|
+
lines.append('# Step 1: Run fill_prototype (generates 80% draft with real data)')
|
|
375
|
+
lines.append('python .qoder/scripts/fill_prototype.py <keyword> --platform <web|app>')
|
|
376
|
+
lines.append('')
|
|
377
|
+
lines.append('# Step 2: AI reads the draft + this DESIGN.md, does 20% adjustment')
|
|
378
|
+
lines.append('# - Confirm table columns match requirement')
|
|
379
|
+
lines.append('# - Add interaction details (sidebar collapse, modal, tab switch)')
|
|
380
|
+
lines.append('# - Do NOT change tokens/colors/layout parameters')
|
|
381
|
+
lines.append('```')
|
|
382
|
+
lines.append('')
|
|
383
|
+
|
|
384
|
+
return '\n'.join(lines)
|
|
385
|
+
|
|
386
|
+
|
|
387
|
+
def main():
|
|
388
|
+
stdout_mode = '--stdout' in sys.argv
|
|
389
|
+
content = gen_design_doc()
|
|
390
|
+
|
|
391
|
+
if stdout_mode:
|
|
392
|
+
print(content)
|
|
393
|
+
return 0
|
|
394
|
+
|
|
395
|
+
# 写到 data/design/DESIGN.md
|
|
396
|
+
os.makedirs(OUTPUT_DIR, exist_ok=True)
|
|
397
|
+
output_path = os.path.join(OUTPUT_DIR, 'DESIGN.md')
|
|
398
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
399
|
+
f.write(content)
|
|
400
|
+
print(f'DESIGN.md generated: {output_path}')
|
|
401
|
+
print(f' Size: {len(content)} chars, {content.count(chr(10))} lines')
|
|
402
|
+
return 0
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
if __name__ == '__main__':
|
|
406
|
+
sys.exit(main())
|