create-dwy 0.19.1 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/package.json +1 -1
  3. package/src/cli-help.js +31 -2
  4. package/src/sync-all.js +168 -577
  5. package/src/sync-codex.js +42 -28
  6. package/src/sync-cursor.js +0 -25
  7. package/src/sync-global-skills.js +2 -2
  8. package/src/sync-opencode.js +7 -33
  9. package/src/sync.js +59 -226
  10. package/src/tab-multiselect.js +15 -53
  11. package/src/utils.js +0 -1
  12. package/templates/ai-tools/AGENTS.md +223 -0
  13. package/templates/ai-tools/CHANGELOG.md +195 -0
  14. package/templates/ai-tools/VERSION +1 -1
  15. package/templates/ai-tools/hooks/Git/pre-git-commit-ai-signature-check.sh +5 -4
  16. package/templates/ai-tools/rules/Android/dwy-android-core.md +147 -13
  17. package/templates/ai-tools/rules/Android/dwy-android-layering.md +142 -0
  18. package/templates/ai-tools/rules/Android/dwy-android-mvp.md +55 -0
  19. package/templates/ai-tools/rules/Android/dwy-android-support-only.md +2 -0
  20. package/templates/ai-tools/rules/Docker/dwy-docker.md +23 -1
  21. package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +2 -0
  22. package/templates/ai-tools/rules/Flutter/dwy-flutter-testing.md +1 -1
  23. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +12 -8
  24. package/templates/ai-tools/rules/Python/dwy-python-backend.md +4 -43
  25. package/templates/ai-tools/rules/Python/dwy-python-layering.md +169 -0
  26. package/templates/ai-tools/rules/Vue/dwy-vue-core.md +11 -49
  27. package/templates/ai-tools/rules/Vue/dwy-vue-layering.md +153 -0
  28. package/templates/ai-tools/rules/iOS/dwy-apple-layering.md +133 -0
  29. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +2 -0
  30. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-code-craft.md +83 -0
  31. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +27 -66
  32. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +8 -219
  33. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +27 -123
  34. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-prod-branch.md +31 -0
  35. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-postgres.md +17 -10
  36. package/templates/ai-tools/rules//346/225/260/346/215/256/345/272/223/dwy-redis.md +6 -3
  37. package/templates/ai-tools/skills/Docker/dwy-deploy-first/SKILL.md +14 -14
  38. package/templates/ai-tools/skills/Docker/dwy-deploy-first/references/chapter-01-mirrors.md +2 -2
  39. package/templates/ai-tools/skills/Docker/dwy-docker/SKILL.md +21 -13
  40. package/templates/ai-tools/skills/Docker/dwy-docker/references/ask-templates.md +13 -12
  41. package/templates/ai-tools/skills/Docker/dwy-docker/references/templates.md +3 -1
  42. package/templates/ai-tools/skills/Docker/dwy-docker/references/version-rules.md +10 -6
  43. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/query_dockerhub.py +21 -6
  44. package/templates/ai-tools/skills/Docker/dwy-docker/scripts/scan_images.sh +2 -2
  45. package/templates/ai-tools/skills/Docker/dwy-mirror-source/SKILL.md +44 -30
  46. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/dockerfile-snippets.md +2 -0
  47. package/templates/ai-tools/skills/Docker/dwy-mirror-source/references/mirror-providers.md +8 -8
  48. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/apply_mirrors.py +14 -1
  49. package/templates/ai-tools/skills/Docker/dwy-mirror-source/scripts/check_mirrors.py +4 -0
  50. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/SKILL.md +8 -6
  51. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/python-sdk.md +3 -3
  52. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/digest/stream-engine.md +2 -2
  53. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/anti-patterns.md +3 -1
  54. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/change-checklist.md +4 -4
  55. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/lethal-violations.md +5 -5
  56. package/templates/ai-tools/skills/DolphinDB/dwy-dolphindb/references/review-rules/perf-baselines.md +5 -5
  57. package/templates/ai-tools/skills/Python/dwy-eapi/SKILL.md +2 -2
  58. package/templates/ai-tools/skills/Python/dwy-eapi/references/tasks-integration-guide.md +4 -4
  59. package/templates/ai-tools/skills/Python/dwy-eapi/v0/breaking.md +3 -0
  60. package/templates/ai-tools/skills/Python/dwy-eapi/v0/email-canonical.md +28 -3
  61. package/templates/ai-tools/skills/Python/dwy-eapi/v0/index.md +2 -2
  62. package/templates/ai-tools/skills/Python/dwy-eapi/v0/providers-email.md +12 -2
  63. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-design-guide.md +1 -1
  64. package/templates/ai-tools/skills/Vue/dwy-eui/references/eui-integration-guide.md +12 -0
  65. package/templates/ai-tools/skills/Vue/dwy-eui/shared/config-theme.md +2 -0
  66. package/templates/ai-tools/skills/Vue/dwy-eui/shared/form.md +10 -0
  67. package/templates/ai-tools/skills/Vue/dwy-eui/v2/index.md +5 -0
  68. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +11 -14
  69. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +1 -1
  70. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +1 -1
  71. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +1 -1
  72. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +32 -15
  73. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/electron-release-workflow-template.yml +3 -1
  74. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/references/release-workflow-template.yml +8 -5
  75. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +1 -1
  76. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/assets/appstore-submission.yaml +1 -1
  77. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/aso.md +2 -0
  78. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/in-app-purchase-review.md +1 -1
  79. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +4 -3
  80. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +1 -1
  81. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +1 -1
  82. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +45 -27
  83. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +1 -1
  84. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +1 -1
  85. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +1 -1
  86. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +1 -1
  87. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +1 -1
  88. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +8 -0
  89. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/SKILL.md +7 -7
  90. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/references/checks-runtime.md +63 -59
  91. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_base.sh +10 -1
  92. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_capacity.sh +21 -23
  93. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_db.sh +9 -1
  94. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_docker.sh +20 -24
  95. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-deploy-audit/scripts/check_logs.sh +3 -3
  96. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +4 -2
  97. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +1 -1
  98. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +1 -0
  99. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +7 -5
  100. package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +5 -3
  101. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-dependency-choice/SKILL.md +140 -0
  102. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-git-commit/SKILL.md +113 -0
  103. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-shared/SKILL.md +17 -14
  104. package/templates/ai-tools/skills//351/200/232/347/224/250/dwy-whiteboard-prototype/SKILL.md +1 -1
  105. package/templates/project/fullstack-monorepo/{CLAUDE.md.hbs → AGENTS.md.hbs} +1 -1
  106. package/src/sync-packs.js +0 -223
  107. package/src/sync-scene.js +0 -178
  108. package/templates/ai-tools/CLAUDE.md +0 -210
  109. package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-tdd-development.md +0 -115
  110. /package/templates/project/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  111. /package/templates/project/fullstack-monorepo/backend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  112. /package/templates/project/fullstack-monorepo/frontend/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
  113. /package/templates/project/web/{CLAUDE.md.hbs → AGENTS.md.hbs} +0 -0
package/src/sync-scene.js DELETED
@@ -1,178 +0,0 @@
1
- /**
2
- * 按包勾选交互:技术栈包 → 场景包。
3
- * 每包一个 Tab,Tab 下列 checkbox;←/→ 切包,Space 勾子项,a 全选本包。
4
- * 由 dwy / dwy sync 在「按场景和技术栈」模式下调用。
5
- */
6
-
7
- import { isCancel } from '@clack/prompts'
8
- import { tabMultiselect } from './tab-multiselect.js'
9
- import {
10
- SCENE_PACKS,
11
- STACK_PACKS,
12
- listPackItems,
13
- normalizePacks,
14
- } from './sync-packs.js'
15
- import { chalk } from './utils.js'
16
-
17
- /** 子 value 三段分隔:packId / type / name。packId 不含冒号。 */
18
- const PACK_CHILD_RE = /^([^:]+):(skills|rules|hooks):(.+)$/
19
-
20
- /**
21
- * 子条目在 Tab 多选里的稳定 value。
22
- * 带 packId:同一 skill 可同时出现在 Vue 与 Python,取消一边不能误伤另一边。
23
- *
24
- * @param {string} packId
25
- * @param {'skills' | 'rules' | 'hooks'} type
26
- * @param {string} name
27
- */
28
- export function packChildKey(packId, type, name) {
29
- return `${packId}:${type}:${name}`
30
- }
31
-
32
- /**
33
- * 解析 packChildKey。对不上的丢掉,避免脏 initialValues 撑进结果。
34
- *
35
- * @param {unknown} key
36
- * @returns {{ packId: string, type: 'skills' | 'rules' | 'hooks', name: string } | null}
37
- */
38
- export function parsePackChildKey(key) {
39
- const match = String(key).match(PACK_CHILD_RE)
40
- if (!match) return null
41
- return { packId: match[1], type: match[2], name: match[3] }
42
- }
43
-
44
- /**
45
- * 每个包一个 Tab。空包跳过,避免 Tab 下面没有可选项。
46
- *
47
- * @param {object[]} packs
48
- * @param {object} scans
49
- * @param {{ skillsOnly?: boolean, group?: string }} [opts]
50
- * @returns {Array<{ id: string, label: string, group?: string, options: Array<{ value: string, label: string, type: string, description?: string }> }>}
51
- */
52
- export function packTabs(packs, scans, { skillsOnly = false, group } = {}) {
53
- const tabs = []
54
- for (const pack of packs) {
55
- const items = listPackItems(pack, scans, { skillsOnly })
56
- if (items.length === 0) continue
57
- tabs.push({
58
- id: pack.id,
59
- label: pack.label,
60
- ...(group ? { group } : {}),
61
- options: items.map(item => ({
62
- value: packChildKey(pack.id, item.type, item.name),
63
- label: item.name,
64
- type: item.type,
65
- description: item.description || pack.description,
66
- })),
67
- })
68
- }
69
- return tabs
70
- }
71
-
72
- /**
73
- * 已选包 → 其下全部子 value,给 tabMultiselect initialValues。
74
- * 打开时该包 Tab 显示为全勾,用户可再取消。
75
- *
76
- * @param {object[]} packs
77
- * @param {object} scans
78
- * @param {string[]} selectedPackIds
79
- * @param {{ skillsOnly?: boolean }} [opts]
80
- */
81
- export function initialChildValues(packs, scans, selectedPackIds, { skillsOnly = false } = {}) {
82
- const selected = new Set(selectedPackIds)
83
- const values = []
84
- for (const pack of packs) {
85
- if (!selected.has(pack.id)) continue
86
- for (const item of listPackItems(pack, scans, { skillsOnly })) {
87
- values.push(packChildKey(pack.id, item.type, item.name))
88
- }
89
- }
90
- return values
91
- }
92
-
93
- /**
94
- * 把勾上的子 value 还原成条目 + 所属包。
95
- * 只认扫描结果里真实存在的名字,防止 key 伪造。
96
- *
97
- * @param {string[]} keys
98
- * @param {object} scans
99
- * @param {{ skillsOnly?: boolean }} [opts]
100
- */
101
- export function itemsFromChildKeys(keys, scans, { skillsOnly = false } = {}) {
102
- const parsed = keys.map(parsePackChildKey).filter(Boolean)
103
- const packIds = [...new Set(parsed.map(item => item.packId))]
104
- const names = { skills: new Set(), rules: new Set(), hooks: new Set() }
105
- for (const item of parsed) names[item.type].add(item.name)
106
- const pick = (type) => (scans[type] || []).filter(item => names[type].has(item.name))
107
- if (skillsOnly) {
108
- return { packIds, skills: pick('skills'), rules: [], commands: [], hooks: [] }
109
- }
110
- return {
111
- packIds,
112
- skills: pick('skills'),
113
- rules: pick('rules'),
114
- commands: [],
115
- hooks: pick('hooks'),
116
- }
117
- }
118
-
119
- /**
120
- * 打印本次按包勾选的包名与条数。
121
- *
122
- * @param {string[]} stacks
123
- * @param {string[]} scenes
124
- * @param {{ skills: object[], rules: object[], hooks: object[] }} selected
125
- */
126
- function printPackSummary(stacks, scenes, selected) {
127
- const stackLabels = STACK_PACKS.filter(pack => stacks.includes(pack.id)).map(pack => pack.label)
128
- const sceneLabels = SCENE_PACKS.filter(pack => scenes.includes(pack.id)).map(pack => pack.label)
129
- const packText = [...stackLabels, ...sceneLabels].join(' + ') || '(未选包)'
130
- console.log(chalk.gray('\n包展开:'))
131
- console.log(chalk.gray(` ${packText}`))
132
- console.log(chalk.gray(` Skills ${selected.skills.length} Rules ${selected.rules.length} Hooks ${selected.hooks.length}`))
133
- }
134
-
135
- /**
136
- * 按包选择条目。技术栈 Tab 在左、场景 Tab 在右,同一屏勾选。
137
- * 返回 { skills, rules, commands, hooks, packs } 或 null(取消)。
138
- *
139
- * @param {object} scans
140
- * @param {{ packs?: { stacks?: string[], scenes?: string[] } }} syncState
141
- * @param {{ skillsOnly?: boolean }} [opts]
142
- */
143
- export async function interactiveSelectByPacks(scans, syncState, { skillsOnly = false } = {}) {
144
- const previous = normalizePacks(syncState?.packs)
145
- const packOpts = { skillsOnly }
146
- const defaultStacks = previous.stacks.length > 0 ? previous.stacks : ['common']
147
- const tabs = [
148
- ...packTabs(STACK_PACKS, scans, { ...packOpts, group: 'stack' }),
149
- ...packTabs(SCENE_PACKS, scans, { ...packOpts, group: 'scene' }),
150
- ]
151
- const keys = await tabMultiselect({
152
- message: '选择技术栈和场景包',
153
- tabs,
154
- initialValues: [
155
- ...initialChildValues(STACK_PACKS, scans, defaultStacks, packOpts),
156
- ...initialChildValues(SCENE_PACKS, scans, previous.scenes, packOpts),
157
- ],
158
- required: true,
159
- maxItems: 12,
160
- })
161
- if (isCancel(keys)) return null
162
-
163
- const stacks = STACK_PACKS.map(pack => pack.id).filter(id =>
164
- keys.some(key => parsePackChildKey(key)?.packId === id),
165
- )
166
- const scenes = SCENE_PACKS.map(pack => pack.id).filter(id =>
167
- keys.some(key => parsePackChildKey(key)?.packId === id),
168
- )
169
- const selected = itemsFromChildKeys(keys, scans, packOpts)
170
- printPackSummary(stacks, scenes, selected)
171
- return {
172
- skills: selected.skills,
173
- rules: selected.rules,
174
- commands: selected.commands,
175
- hooks: selected.hooks,
176
- packs: { stacks, scenes },
177
- }
178
- }
@@ -1,210 +0,0 @@
1
- 所有回答必须使用中文。
2
-
3
- ## 输出完整性要求
4
-
5
- - **文件路径**:必须输出完整的绝对路径(如 `/Users/chances/WebstormProjects/dwy-shared/frontend/eui/src/components/input/EInput.vue`),禁止省略为相对路径或只写文件名
6
- - **服务地址**:必须输出完整的 URL(如 `http://localhost:8000/api/users`),禁止省略协议、端口或路径
7
- - **数据库连接串**:必须输出完整连接字符串(如 `postgresql+asyncpg://user:pass@localhost:5432/mydb`),禁止只写主机名或库名
8
-
9
- ## 团队执行任务
10
-
11
- 任务执行前必须先分析 block;无 block 的任务使用多 Agent 并行执行以提高效率。
12
-
13
- ## 代码注释
14
-
15
- - 所有代码都必须有注释。
16
- - 类、接口、私有方法、函数、重要参数与重要字段都必须写明用途、约束与边界。
17
- - 每个类顶部要有简短功能说明。
18
- - 注释重点写“为什么”和“约束”,避免仅复述代码实现。
19
-
20
- ## 图标规范
21
-
22
- - 图标优先使用成熟稳定的图标库,不使用 emoji 作为功能性图标。
23
- - 优先选择项目已有成熟依赖;无可用依赖时再补充稳定方案。
24
-
25
- ## 发布与打包基础准则
26
-
27
- - PyPI 与 npm 的发布必须走 GitHub Actions,使用 OIDC 完成鉴权,禁止使用长期静态发布凭据。
28
- - 多平台打包必须走 GitHub Actions 工作流执行,不使用本地手工打包作为唯一流程。
29
-
30
- ## 减少常见 LLM 编码错误的行为指南
31
-
32
- 与项目特定指令合并使用。
33
-
34
- 权衡:这些指南偏向谨慎而非速度。对于琐碎任务,自行判断。
35
-
36
- ### 1. 先思考再编码
37
-
38
- 不要假设。不要隐藏困惑。把权衡摊开。
39
-
40
- 在实现之前:
41
- - 明确陈述你的假设。如果不确定,就问。
42
- - 如果存在多种解读,提出来——不要默默选择。
43
- - 如果有更简单的方案,说出来。在必要时提出异议。
44
- - 如果有什么不清晰,停下来。说出困惑是什么。然后提问。
45
-
46
- ### 2. 简单优先
47
-
48
- 解决问题的最小代码。没有推测性代码。
49
-
50
- - 不做需求之外的功能。
51
- - 不为一次性使用的代码做抽象。
52
- - 不做未被要求的"灵活性"或"可配置性"。
53
- - 不为不可能发生的场景做错误处理。
54
- - 如果你写了 200 行但可以写成 50 行,重写它。
55
- - 问自己:"一个资深工程师会说这过于复杂吗?"如果是,简化。
56
-
57
- ### 写代码前走决策阶梯
58
-
59
- 写任何新代码前,从上往下逐级问,命中即停:
60
-
61
- 1. 这任务真需要存在吗?(YAGNI——能不做就不做)
62
- 2. 代码库里已有现成的吗?
63
- 3. 标准库覆盖吗?
64
- 4. 平台 / 框架原生特性有吗?
65
- 5. 已安装的依赖能做吗?
66
- 6. 能一行解决吗?
67
- 7. 以上都不行 → 才写最小代码。
68
-
69
- 刻意的简化用注释标注:跳过了什么、何时该加回。
70
- 红线——以下永不简化掉:输入校验、错误处理、安全、可访问性。先理解问题,再谈优化。
71
-
72
- ### 3. 精准改动
73
-
74
- 只动必须动的代码。只清理自己造成的混乱。
75
-
76
- 在编辑现有代码时:
77
- - 不要"改进"相邻的代码、注释或格式。
78
- - 不要重构没有问题的东西。
79
- - 匹配现有的风格,即使你会用不同的写法。
80
- - 如果发现不相关的死代码,提一下——但不要删除。
81
-
82
- 当你的改动产生孤儿代码时:
83
- - 移除你的改动导致的未使用的 import/变量/函数。
84
- - 除非被要求,不要移除已有的死代码。
85
-
86
- 检验标准:每一行改动的代码都应能直接追溯到用户的需求。
87
-
88
- ### 4. 目标驱动执行
89
-
90
- 定义成功标准。循环直到验证通过。
91
-
92
- 将任务转化为可验证的目标:
93
- - "添加校验" → "编写无效输入的测试,然后让它们通过"
94
- - "修复 bug" → "编写重现 bug 的测试,然后让它通过"
95
- - "重构 X" → "确保测试在重构前后都通过"
96
-
97
- 对于多步骤任务,陈述一个简要计划:
98
- 1. [步骤] → 验证:[检查项]
99
- 2. [步骤] → 验证:[检查项]
100
- 3. [步骤] → 验证:[检查项]
101
-
102
- 强成功标准让你能独立循环。弱标准("让它工作")需要不断澄清。
103
-
104
- 这些指南在生效的标志是:diff 中不必要的改动变少,因过度复杂而重写的情况减少,澄清性问题出现在实现之前而非错误之后。
105
-
106
- ## 输出精简规则
107
-
108
- 输出保持精简,去掉冗余,保留技术精确性。
109
-
110
- ### 核心规则
111
-
112
- - **去掉填充词**:不用"了/的/吧/吗/呢"等语气助词,不用"我认为/可能/也许/似乎"等模糊词
113
- - **不用客套话**:不用"你好/请/谢谢/不客气"等社交用语,直入主题
114
- - **允许碎片句**:不用强制完整主谓宾,短语表达即可
115
- - **短优先**:能用短词就不用长词,能用一句话就不用两句话
116
- - **技术术语精确**:变量名、函数名、错误信息、代码块等保持原样,不缩写
117
- - **代码块完整**:代码必须完整可运行,不受精简规则影响
118
-
119
- ### 例外场景(恢复完整语气)
120
-
121
- 以下场景不得精简,必须清晰明确:
122
- - 安全警告和风险提示
123
- - 不可逆操作确认(删除、强制推送等)
124
- - 需要用户决策的复杂多步骤流程
125
- - 用户明确要求详细解释时
126
-
127
- ## 必须使用成熟开源方案
128
-
129
- 开发新功能时,先给出 3 到 5 个成熟开源方案,用户确认后再实现。默认不手写已有成熟方案。
130
-
131
- ### 决策流程(必须执行)
132
-
133
- 1. 用户需求触发:确认功能范围与技术栈约束。
134
- 2. 候选检索:给出 3 到 5 个成熟开源项目(按 npm/PyPI/GitHub stars、维护活跃度、许可证兼容性排序)并说明选型理由。
135
- 3. 用户选择:先不改代码,向用户返回“候选列表 + 选型对比 + 推荐项”,等待用户明确选择。
136
- 4. 选型确认后:仅对确认方案进行适配实现;未确认方案不落地。
137
- 5. 跨语言备选:若当前技术栈无合适方案,允许并必须先列出其他语言生态(如 Kotlin/Compose、Jetpack、Flutter、React Native、Web 生态)中可借鉴且成熟的 3 到 5 个替代方案,由用户确认后再继续落地;禁止直接从 0 开始造轮子。
138
-
139
- ### 强制规则
140
-
141
- - **禁止**手写已有主流开源方案的功能,一律使用社区主流库
142
- - 选用开源库优先级:项目已在用的依赖 > 社区主流方案 > 小众但适合的方案
143
- - 当候选不足 3 个且无可用替代时,说明原因并请求用户确认是否允许自研。
144
-
145
- ## 代码简约原则
146
-
147
- 只写**必要的逻辑**,不写"以防万一"的冗余代码。信任内部代码和框架保证,只在系统**外边界**(用户输入、外部 API、文件 / 网络读入)做校验。
148
-
149
- ### 禁止的冗余模式
150
-
151
- - **多余 fallback**:值已有明确来源时,不再 `value or default` / `value ?? default`
152
- - **多余类型转换**:上游已是目标类型时,不再 `int(x)` / `Number(x)` / `String(x)`
153
- - **多余 else**:上面已 return,下面不再写 else 分支(用 early return)
154
- - **多余默认值**:调用方都传值时,不写形参默认值 + 空值检查
155
- - **多余异常兜底**:`try { ... } catch { /* swallow */ }` 掩盖 bug;只捕获**预期**的具体异常,其余让它自然抛出
156
- - **多余空集合检查**:能直接 `for ... in collection` 时,不要 `if len(x) > 0` 再循环
157
-
158
- ### 判断标准
159
-
160
- 写每一行防御代码前问自己:**这个情况在当前上下文真的会发生吗?**
161
-
162
- - 会 → 写防御,注释说明触发条件
163
- - 不会 → 不写,信任上游
164
- - 不确定 → 查调用链确认,不要"以防万一"
165
-
166
- | 位置 | 是否校验 |
167
- |---|---|
168
- | 用户输入、外部 API 响应、文件 / 网络读入 | 必须校验 |
169
- | 内部模块调用(已有类型签名) | 信任,不重复校验 |
170
-
171
- ## 用户输入长度限制
172
-
173
- 所有用户输入必须限长 / 限大小,**前端 + 后端双层**:
174
-
175
- - 前端:即时反馈,避免无效输入浪费带宽
176
- - 后端:防绕过、防 DoS、防内存耗尽(前端可被绕过,后端必须再校验一次)
177
-
178
- 具体数字按业务语义推断(用户名几十字符、描述几百、正文几千、图片几 MB),无须硬编码统一表。
179
-
180
- ### 前端
181
-
182
- 凡接收用户输入的控件(`input` / `textarea` / 富文本 / 文件选择 / 批量项)→ 都必须有长度 / 大小上限,并配 schema 层校验兜底(防粘贴绕过原生 maxlength)。
183
-
184
- ### 后端
185
-
186
- 凡接收用户数据的字段 / Query / Path / Header → 必须显式声明长度上限。
187
-
188
- - 分页 `page_size` 必须有上限
189
- - 禁止用「松散字典 / map / any 类型」直接接收 body,必须有 schema
190
- - 图片上传额外校验像素尺寸,防 decompression bomb
191
-
192
- ### 禁止
193
-
194
- - 任一层无限制上线
195
- - 仅前端限而后端不校验
196
- - 后端用 dict / map / any 接收 body
197
- - 分页 `page_size` 无上限
198
- - 图片只校验文件大小不校验像素
199
-
200
- ## 自成长项目规则(project-rules)
201
-
202
- 写项目要自成长:同一类问题反复踩坑,主动沉淀成可复用规则,下次自动生效,不靠人事后总结。
203
-
204
- - **触发**:写代码 / 排查问题时,同一类问题踩坑 **≥2 次**(重复的报错根因、踩同一个坑、被反复纠正同一处写法)。
205
- - **动作**:把教训沉淀成一条可复用规则(写清「现象 / 根因 / 怎么避免」),追加到 `project-rules.md`;已有同类规则补充进去,不重复立条。
206
- - **落盘位置**(按当前工具自动选):
207
- - **Claude Code** → 项目 `.claude/rules/project-rules.md`(放进去即自动加载,无需注册)。
208
- - **Codex** → 项目根 `AGENTS.md`,写在 `<!-- DWY-RULES:START/END -->` 托管块**之外**的 `## [自成长] 项目规则` 节下。托管块内会被 `dwy codex sync` 整块覆盖,自成长内容**必落块外**。
209
- - **方式**:自动沉淀,事后输出一行告知「已沉淀 <规则> 到 project-rules」,不打断当前任务、不逐条问用户。
210
- - **边界**:只沉淀**可复用、跨任务**的教训;一次性 / 本任务特有的问题不立规则,避免噪音堆积。
@@ -1,115 +0,0 @@
1
- ---
2
- description: TDD 开发流程(新功能/bug 修复/重构的测试先行与回归策略)与测试-源码分隔(测试工程独立 tests/ 目录、镜像源码结构、各栈目录约定)
3
- ---
4
-
5
- # TDD 开发规则
6
-
7
- 所有代码变更必须遵循测试驱动开发流程。测试不是事后补的,是开发的起点。
8
-
9
- ## 开发流程
10
-
11
- ### 新功能
12
-
13
- 1. 先写测试 — 定义预期行为,测试此时应失败
14
- 2. 再写实现 — 用最简代码让测试通过
15
- 3. 重构优化 — 在测试保护下重构,确保始终通过
16
-
17
- ### Bug 修复
18
-
19
- 1. 先写复现测试 — 写一个能暴露 bug 的测试用例,确认它失败
20
- 2. 再修代码 — 修复 bug,确认测试通过
21
- 3. 跑回归 — 确认没有引入新问题
22
-
23
- ### 重构
24
-
25
- 1. 先跑现有测试 — 确认当前全部通过
26
- 2. 再重构 — 修改代码
27
- 3. 再跑测试 — 确认行为没变
28
-
29
- ## 测试与源码分隔(硬约束)
30
-
31
- 测试工程必须与源码分隔,不混在一起。
32
-
33
- ### 核心规则
34
-
35
- - 测试代码**禁止**与源码同目录混放;测试集中在项目根 / 包根的独立 `tests/` 目录。
36
- - `tests/` **镜像源码结构**,测试文件与被测源码一一对应、可反查:
37
- - `src/foo/bar.ts` → `tests/foo/bar.test.ts`
38
- - `src/services/user.py` → `tests/test_user.py`
39
- - 源码目录(`src/`)保持纯净,**只放产品代码**;打包 / 发布产物天然不含测试,CI 用一个目录通配即可圈定全部测试。
40
- - 测试用的 fixture / mock 数据 / 测试工具,放 `tests/` 内(如 `tests/fixtures/`、`tests/conftest.py`),不散落进源码目录。
41
-
42
- ### 为什么(约束的根因)
43
-
44
- 测试混入源码目录会引发一连串问题,分隔后一次性收口:
45
-
46
- - **被打包进发布产物**:测试随源码一起发到 npm / PyPI,污染产物、撑大体积。
47
- - **source map / 内部细节泄露**:测试常含内部实现假设、mock 的真实数据,泄露到产物有安全风险。
48
- - **覆盖率 / 构建统计污染**:测试文件被计入源码统计,覆盖率、行数失真。
49
- - **CI 难圈定范围**:源码与测试交织,CI 无法用一条目录规则排除测试。
50
- - **AI / 工具难定位**:找不到「源码↔测试」对应关系,反复把测试误写进 `src/`。
51
-
52
- ### 各栈目录与命令速查表
53
-
54
- | 栈 | 测试目录 | 命名约定 | 运行命令 |
55
- |----|---------|---------|---------|
56
- | Vue / Vitest | `tests/` 镜像 `src/` | `*.test.ts` | `pnpm vitest run [路径]` |
57
- | Python / pytest | `tests/` | `test_*.py` | `pytest tests/ -v` / `uv run pytest` |
58
- | Android | `src/test/`(单元)+ `src/androidTest/`(仪器) | `*Test.kt` | `./gradlew test` / `./gradlew connectedAndroidTest` |
59
- | Node / CLI | `tests/` 镜像 `src/` | `*.test.ts` | 对应 runner(vitest / jest) |
60
-
61
- 注:Android 的 `src/test` / `src/androidTest` 是 Gradle 生态**强约定**的独立测试源集(test source set),与 `src/main` 源码完全分隔,符合本规范"测试与源码分隔"理念,按其生态默认即可,**属合规分隔**,不视为"测试写进 src"。
62
-
63
- **通用准则(表中未列的栈)**:遵循「独立测试目录 + 镜像源码结构 + 该生态主流 runner 约定」三条原则自行判断,不硬套上表布局。
64
-
65
- ## 回归测试
66
-
67
- ### 开发中:只跑受影响的测试
68
-
69
- 每次代码改动后,只运行受影响的测试文件,提高开发效率:
70
-
71
- ```bash
72
- # 后端:只跑改动涉及的测试文件
73
- uv run pytest tests/test_factor.py -v # 改了 factor 相关代码
74
- uv run pytest tests/test_tick.py tests/test_upload.py -v # 改了多个模块
75
-
76
- # 前端:只跑对应目录
77
- pnpm vitest run tests/stores/ # 改了 store
78
- pnpm vitest run tests/utils/format.test.ts # 改了单个工具函数
79
- ```
80
-
81
- 判断"受影响的测试"的规则:
82
- - 改了 `services/xxx.py` → 跑 `tests/test_xxx.py`
83
- - 改了 `routers/xxx.py` → 跑 `tests/test_xxx.py`
84
- - 改了 `schemas/` 或 `models/` → 跑引用了它们的测试
85
- - 改了 `conftest.py` 或公共依赖 → 全量回归
86
- - 不确定影响范围 → 全量回归
87
-
88
- ### 提交前:跑受影响的测试即可
89
-
90
- 提交前确保受影响的测试通过即可,不要求全量回归。全量测试留给 CI 或手动触发。
91
-
92
- ## TEST_CASES.md
93
-
94
- 每个包/项目维护一份 `TEST_CASES.md`,记录所有测试用例的清单。**`TEST_CASES.md` 跟测试目录走,不放项目根**:
95
-
96
- - 有 `tests/` → 放 `tests/TEST_CASES.md`
97
- - 测试源集分散(如 Android `src/test/`)→ 放对应测试根(`src/test/TEST_CASES.md`),与该模块测试同处
98
- - 理念:测试清单是测试工程的一部分,归测试目录,与「测试与源码分隔」一致
99
-
100
- 内容要求:
101
-
102
- - 人类可读,方便审查和手动编辑
103
- - 新增/修改/删除功能时必须同步更新
104
- - 内容与实际测试文件保持一致
105
- - 作为版本回测基准
106
-
107
- ## 禁止事项
108
-
109
- - **禁止**不写测试就提交功能代码
110
- - **禁止**写完代码再补测试(先测试后代码)
111
- - **禁止**测试不通过就提交
112
- - **禁止** TEST_CASES.md 与实际测试不同步
113
- - **禁止**测试文件写进 `src/` 源码目录(Android `src/test`、`src/androidTest` 例外,是 Gradle 标准测试源集)
114
- - **禁止**测试目录结构与源码脱节(找不到「源码↔测试」对应关系)
115
- - **禁止**把测试 fixture / mock 数据散落进源码目录