create-dwy 0.14.7 → 0.15.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 (70) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/bin/index.js +4 -0
  3. package/package.json +1 -1
  4. package/src/skills-install.js +113 -0
  5. package/src/sync-all.js +19 -0
  6. package/templates/ai-tools/CLAUDE.md +1 -1
  7. package/templates/ai-tools/hooks/Git/pre-git-commit-sensitive-check.sh +10 -2
  8. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-strict.md +221 -0
  9. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +193 -0
  10. package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +159 -0
  11. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +35 -0
  12. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +23 -0
  13. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +28 -0
  14. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +22 -0
  15. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +21 -0
  16. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +23 -0
  17. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-poc/SKILL.md +24 -0
  18. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +22 -0
  19. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +68 -0
  20. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +103 -0
  21. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +23 -0
  22. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +26 -0
  23. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +23 -0
  24. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +22 -0
  25. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +25 -0
  26. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +22 -0
  27. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +21 -0
  28. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +22 -0
  29. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +25 -0
  30. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +28 -0
  31. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +23 -0
  32. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish/SKILL.md +365 -0
  33. 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 +98 -0
  34. package/templates/ai-tools/skills//{350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel → 345/217/221/345/270/203/345/217/221/347/211/210/dwy-github-action-publish}/references/release-workflow-template.yml +3 -3
  35. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +31 -0
  36. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +31 -0
  37. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +23 -0
  38. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +37 -0
  39. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +23 -0
  40. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +27 -0
  41. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +25 -0
  42. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +32 -0
  43. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/monitor-notify.md +36 -0
  44. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/publish-config.md +65 -0
  45. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/version-sync.md +30 -0
  46. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/scripts/release_config.sh +115 -0
  47. package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/SKILL.md +31 -21
  48. package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/python-commercial-guide.md +1 -1
  49. package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/sensitive-patterns.md +1 -1
  50. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-sdk-spec/scripts/init_config.sh +91 -0
  51. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/SKILL.md +70 -0
  52. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/assets/template.html +353 -0
  53. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-sdk-spec/scripts/init_config.sh +0 -97
  54. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/SKILL.md +0 -50
  55. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/application-release.md +0 -106
  56. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/references/sdk-release.md +0 -93
  57. package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +0 -356
  58. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/files-whitelist-snippet.json" +0 -0
  59. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/js-commercial.npmignore" +0 -0
  60. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial-MANIFEST.in" +0 -0
  61. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/assets/python-commercial.gitignore" +0 -0
  62. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/js-commercial-guide.md" +0 -0
  63. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/references/publish-blacklist.md" +0 -0
  64. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_comments.py" +0 -0
  65. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_js_publish.sh" +0 -0
  66. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/check_python_publish.sh" +0 -0
  67. /package/templates/ai-tools/skills//345/{256/211/345/205/250 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-sdk-spec/scripts/detect_sdk.sh" +0 -0
  68. /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/SKILL.md" +0 -0
  69. /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/references/semver-spec.md" +0 -0
  70. /package/templates/ai-tools/skills//345/{274/200/345/217/221/346/265/201/347/250/213 → 217/221/345/270/203/345/217/221/347/211/210}/dwy-semver/scripts/bump.py" +0 -0
@@ -0,0 +1,159 @@
1
+ ---
2
+ description: iOS / Swift / SwiftUI 开发避坑清单(XcodeGen / 签名 / 真机部署 / SwiftUI 代码踩坑)
3
+ paths:
4
+ - "**/*.swift"
5
+ - "**/project.yml"
6
+ - "**/Package.swift"
7
+ ---
8
+
9
+ # iOS 开发坑清单
10
+
11
+ > 动手前先对照本清单避开已知坑;踩到新坑随手按四栏格式(现象 / 根因 / 正确做法 / 适用)追加到对应分类,没有合适分类就新建一个。
12
+
13
+ ## 分类索引
14
+
15
+ - 工程与构建(XcodeGen / SPM / Xcode 工程)
16
+ - 签名与真机部署
17
+ - 证书 / Keychain / mTLS
18
+ - 排查方法论
19
+ - Swift / SwiftUI 代码
20
+ - 其他分类遇到再加:StoreKit / 内购、Info.plist / 权限 / Capability、上架审核
21
+
22
+ ---
23
+
24
+ ## 工程与构建
25
+
26
+ ### XcodeGen 工程要打开 .xcodeproj,不能直接打开项目文件夹
27
+
28
+ - **现象**:Xcode 里左侧根节点是文件夹名、右边只显示 `Package.swift`,点项目没有 General / Signing & Capabilities 标签页;选不到真机、装不上真机。
29
+ - **根因**:用 Xcode 直接打开了**项目文件夹**(或 `Package.swift`),Xcode 把它当 Swift Package(纯库)加载,没有 app target,自然没有签名页、也不能跑真机。
30
+ - **正确做法**:先 `xcodegen generate` 生成 `<项目>.xcodeproj`,再用 Xcode 打开这个 `.xcodeproj`(Finder 双击或 File → Open),**不要打开文件夹 / Package.swift**。打开对了,左侧根节点是蓝色 app 图标、底下有 TARGETS。
31
+ - **适用**:用 XcodeGen 且 `.xcodeproj` 不入库(被 .gitignore)的工程。SPM-only 的纯库项目本就没 app target,不适用。
32
+
33
+ ### .xcodeproj 是生成物,Xcode 里手改设置会被 xcodegen 覆盖
34
+
35
+ - **现象**:在 Xcode 里改了签名 / Build Settings / bundle id,下次 `xcodegen generate` 之后改动全没了。
36
+ - **根因**:`.xcodeproj` 由 `project.yml` 生成、通常不入库;`xcodegen generate` 整体重建工程,覆盖一切手改。
37
+ - **正确做法**:需要持久化的工程设置(签名、bundle id、Build Settings、Info.plist 键)一律改 `project.yml` 再 `xcodegen generate`。Xcode 里手改只用于临时试验。
38
+ - **适用**:XcodeGen 工程。
39
+
40
+ ### Package.swift 的 .macOS 平台声明,别为「只支持手机」删掉
41
+
42
+ - **现象**:想让 app 只支持 iPhone,看到 `Package.swift` 里 `platforms: [.iOS(.v17), .macOS(.v14)]` 就想删掉 `.macOS`。
43
+ - **根因**:这是**纯逻辑库**的平台声明,作用是让命令行 `swift test` 能在 mac(host)上跑核心逻辑测试,和 iOS app 支持哪些设备**无关**。app 的设备类型由 app target 的 `TARGETED_DEVICE_FAMILY` 控制。删掉 `.macOS` 会让 `swift test` 在 mac 上跑不起来。
44
+ - **正确做法**:保留 `.macOS(.v14)`。要 app 只支持手机,设 app target `TARGETED_DEVICE_FAMILY: "1"`(1=iPhone,2=iPad)。库的 platforms 是「最低支持平台」,不影响 app。
45
+ - **适用**:两层结构(SPM 纯逻辑库 + iOS app)、靠命令行 `swift test` 验证核心逻辑的工程。
46
+
47
+ ### XcodeGen 加 String Catalog,要靠 CFBundleLocalizations 才生成多语言 .lproj
48
+
49
+ - **现象**:加了 `Localizable.xcstrings` 并翻译了多国语言,运行时却只显示开发语言(如中文),系统切语言 / app 内切语言都不生效;`xcodegen generate` 出来的 `.xcodeproj` 里 `knownRegions` 只有 `Base, en`。
50
+ - **根因**:XcodeGen 不会扫 `.xcstrings` 内容来推断本地化区域。pbxproj 的 `knownRegions` 决定 Xcode 编译哪些 `.lproj`;某语言不在其中,String Catalog 里该语言就不会编进 app bundle,自建的 `loc()` / 系统本地化都只能回退开发语言。纯 `.xcodeproj` 工程不暴露此坑,因为 `knownRegions` 是手维护的(参考项目 niannian 即如此)。
51
+ - **正确做法**:在 `project.yml` 的 app target `info.properties` 下显式声明 `CFBundleLocalizations` 数组,列出全部目标区域(如 `zh-Hans, zh-Hant, en, ja, ko, de, fr, es`)。XcodeGen 会据此把这些区域写进 pbxproj 的 `knownRegions`,Xcode 才会把 `.xcstrings` 编成各语言 `.lproj`。生成后用 `grep -A12 knownRegions TVRemote.xcodeproj/project.pbxproj` 核对区域齐全。
52
+ - **适用**:XcodeGen + String Catalog(`.xcstrings`)且 `.xcodeproj` 不入库的工程。
53
+
54
+ ### Info.plist 文案本地化要用固定文件名 InfoPlist.xcstrings
55
+
56
+ - **现象**:`CFBundleDisplayName`(app 显示名)、各 `NS*UsageDescription`(权限弹窗文案)硬编码成一种语言,想随界面语言本地化,但放进 `Localizable.xcstrings` 不生效。
57
+ - **根因**:Info.plist 的面向用户字段不归 `Localizable.xcstrings` 管,Xcode 用一个专门的 String Catalog —— 文件名必须是 `InfoPlist.xcstrings`(大小写敏感),Xcode 自动识别它来本地化 Info.plist 各键。
58
+ - **正确做法**:在资源目录建 `InfoPlist.xcstrings`,key 用 Info.plist 的键名(`CFBundleDisplayName` / `NSLocalNetworkUsageDescription` / `NSPhotoLibraryAddUsageDescription`…),各 key 补齐目标语言。XcodeGen 工程把它放进 target 的 sources 目录即自动纳入 resources(`lastKnownFileType = text.json.xcstrings`);仍需配 `CFBundleLocalizations` 注册区域(见上一条)。`project.yml` 里 `info.properties` 的原值作为开发语言默认保留即可,xcstrings 提供各语言覆盖。
59
+ - **适用**:iOS app 本地化 Info.plist 文案,Xcode 15+ String Catalog。
60
+
61
+ ---
62
+
63
+ ## 签名与真机部署
64
+
65
+ ### 命令行/CI 用的 CODE_SIGNING_ALLOWED=NO + 空 DEVELOPMENT_TEAM,装真机前要改
66
+
67
+ - **现象**:工程命令行编译、模拟器运行都正常,但装不上真机 / 没有可用签名。
68
+ - **根因**:`project.yml` 为命令行、CI、模拟器验证配了 `CODE_SIGNING_ALLOWED: "NO"` 和 `DEVELOPMENT_TEAM: ""`;真机安装必须代码签名,这套配置把签名直接关了。
69
+ - **正确做法**:真机部署在 `project.yml` 的 app target settings 配 `DEVELOPMENT_TEAM: "<teamID>"` + `CODE_SIGN_STYLE: Automatic`,删掉 `CODE_SIGNING_ALLOWED: "NO"`,再 `xcodegen generate`。命令行 / CI 仍可在调用时用 `xcodebuild ... CODE_SIGNING_ALLOWED=NO` 覆盖,不必把它写死进工程。teamID 在 Xcode → Settings → Accounts 看。
70
+ - **适用**:为命令行 / 模拟器验证而关签名的工程,首次要装真机时。
71
+
72
+ ### personal team(免费账号)签名,bundle id 不能用 com.example 等保留前缀
73
+
74
+ - **现象**:用免费 personal team 自动签名失败 / 注册 App ID 被拒。
75
+ - **根因**:`com.example.*`(及 `com.apple.*` 等)是保留 / 示例域名,注册 App ID 会被拒。
76
+ - **正确做法**:换成自有反向域名,如 `com.<你的标识>.<app>`;同时改 `project.yml` 的 `bundleIdPrefix` 和 `PRODUCT_BUNDLE_IDENTIFIER`,再 `xcodegen generate`。
77
+ - **适用**:免费 personal team 真机调试。付费账号用已注册的 App ID。
78
+
79
+ ### personal team 签的 app 7 天过期、首次需在设备「信任开发者」
80
+
81
+ - **现象**:免费账号装的 app 几天后打不开;或刚装上点击提示「不受信任的开发者」。
82
+ - **根因**:personal team 签名证书有效期仅 7 天;且首次安装的开发者证书需手动信任。
83
+ - **正确做法**:过期重新 `Cmd+R` 安装即可;首次安装后到 iPhone → 设置 → 通用 → VPN与设备管理 → 信任你的开发者证书。
84
+ - **适用**:免费 personal team。付费账号无 7 天限制。
85
+
86
+ ### devicectl 的设备 UUID 和 xcodebuild 的 -destination id 不是同一套
87
+
88
+ - **现象**:`xcrun devicectl list devices` 拿到设备 Identifier(如 `5C5E6FAB-D288-559D-9425-3C2754AA401C`),直接拿去 `xcodebuild -destination 'id=...'`,报 `Unable to find a device matching the provided destination specifier` / `no available devices matched`。
89
+ - **根因**:两套工具用两套设备标识。`devicectl` 列的是它自己的 CoreDevice UUID;`xcodebuild` 的 `-destination id=` 要的是硬件 ECID 形式的 id(如 `00008101-001645E21406001E`)。同一台真机两个 id 完全不同,不能混用。
90
+ - **正确做法**:编译阶段从 `xcodebuild` 报错末尾的「Available destinations for the scheme」里抄那台机器的 `id=`(ECID 形式)传给 `-destination`;安装阶段 `xcrun devicectl device install app --device <UUID>` 用 `devicectl list devices` 的 UUID。即:build 用 xcodebuild 的 id,install 用 devicectl 的 UUID,按名字对上同一台机器。
91
+ - **适用**:命令行 `xcodebuild` 编译 + `xcrun devicectl device install` 安装真机的流程(Xcode 15+ / iOS 17+ CoreDevice)。
92
+
93
+ ---
94
+
95
+ ## 排查方法论
96
+
97
+ ### 别把 deploymentTarget 下限误读成「不支持高版本」
98
+
99
+ - **现象**:看到工程部署目标 iOS 17.0,以为「不支持 iOS 26」,甚至想抬高部署目标来「支持新系统」。
100
+ - **根因**:deploymentTarget 是**最低**支持版本(下限),不是上限。iOS 17.0 表示 17 及以上(含 26)全部支持。真机装不上几乎从不是因为这个。
101
+ - **正确做法**:真机装不上,按「打开方式 → 签名配置 → Xcode 是否支持该 iOS 大版本」排查,别先动部署目标。另:iOS 新大版本真机需要配套新 Xcode(如 iOS 26 真机需 Xcode 26),但那报的是设备支持 / 架构类错误,也不是改部署目标能解决的。
102
+ - **适用**:通用。
103
+
104
+ ### 真机装不上时,拿「同机能跑的另一个项目」做对照
105
+
106
+ - **现象**:一个项目装不上真机、报错含糊(如 unsupported architecture),方向难定。
107
+ - **根因**:同一台 Mac、同一个 Xcode 下,若另一个项目能装真机,说明 Xcode 版本、设备、账号都没问题,差异一定在**这个项目的工程配置**(打开方式 / 签名 / bundle id)。
108
+ - **正确做法**:找一个同机能装真机的项目当 working example,逐项对比 `project.yml`、打开方式、签名设置,差异处就是根因——比凭空猜快得多。
109
+ - **适用**:手头有可对照项目时的真机部署排查。
110
+
111
+ ---
112
+
113
+ ## 证书 / Keychain / mTLS
114
+
115
+ ### SecIdentityCreateWithCertificate 在 iOS 不存在,组装 SecIdentity 要走 keychain 查询
116
+
117
+ - **现象**:用 swift-certificates 生成自签证书后想组装 `SecIdentity`,命令行 `swift test`(host 是 macOS)能过,但一编译 iOS(模拟器/真机)就报 `cannot find 'SecIdentityCreateWithCertificate' in scope`,阻断整个 app build。
118
+ - **根因**:`SecIdentityCreateWithCertificate` 是 **macOS-only** API,iOS SDK 里没有。只在 macOS 跑 `swift test` 不会暴露——因为测试 host 就是 macOS,能解析到该符号。
119
+ - **正确做法**:iOS 上把私钥(`SecItemAdd` + `kSecClassKey`)和证书(`SecItemAdd` + `kSecClassCertificate`)分别入 keychain,再用 `kSecClassIdentity` + `SecItemCopyMatching` 查询——系统会自动把同一公钥的 key+cert 组装成 `SecIdentity` 返回。此写法 iOS / macOS 通用。
120
+ - **适用**:iOS 客户端 mTLS / 自签证书 / SecIdentity 组装。两层工程(SPM 纯逻辑库 + iOS app)只靠 `swift test` 验证逻辑时,**务必再补一次 iOS 编译**,否则 macOS-only 的 Security API 会从 `swift test` 漏过去。
121
+
122
+ ### swift-crypto 的 Crypto 产物在 Xcode 测试目标链接报「framework 缺二进制」
123
+
124
+ - **现象**:给 SPM Core 加了 swift-crypto / swift-certificates 后,app target 能 build,但单元测试目标 `build-for-testing` 报 `clang: error: no such file or directory: .../Crypto_<hash>_PackageProduct.framework/Crypto_<hash>_PackageProduct`。
125
+ - **根因**:swift-crypto 的 `Crypto` 产物在 Apple 平台会桥接系统 CryptoKit,其 `PackageProduct.framework` 没有可链接的二进制。测试目标若**同时**依赖 app target 和 `package: <Core>`,就会去直接链这个空 framework 而报缺文件;app target 自身的链接逻辑会跳过它,所以 app build 不受影响、只有测试目标炸。
126
+ - **正确做法**:单元测试目标**只依赖 app 宿主 target**(`- target: <App>`),删掉冗余的 `- package: <Core>`。Core 及其传递框架由 app 链接并嵌入,测试经宿主仍可 `import <Core>`(含 `@testable import <App>`)。改 `project.yml` 后 `xcodegen generate` 重生工程。
127
+ - **适用**:XcodeGen + SPM,且 unit-test 目标依赖了含 swift-crypto / swift-certificates 这类「Apple 平台会桥接系统库」的包时。
128
+
129
+ ---
130
+
131
+ ## Swift / SwiftUI 代码
132
+
133
+ ### @AppStorage 驱动 .preferredColorScheme 切换后不即时生效(须重启)
134
+
135
+ - **现象**:设置页切「浅色/深色/跟随系统」,`.preferredColorScheme` 由 `@AppStorage("themeMode")` 驱动,改后界面不变,杀进程重启才生效。换 sheet→push、枚举存值改存 `String` rawValue、调整视图层级都**无效**——全是猜,方向错。
136
+ - **根因**:SwiftUI 框架级已知问题——`.preferredColorScheme` 由 `@AppStorage` 驱动时不即时刷新,尤其 `.system` 档对应的 `nil` 不触发更新(iOS 17/18 多人复现)。与「枚举 vs String 的 KVO」无关,换存储类型治不了本。
137
+ - **正确做法**:别用 `@AppStorage` 驱动 `.preferredColorScheme`。改用单一 `@MainActor @Observable` 单例(如 `ThemeManager.shared`,持久化自己写 UserDefaults),`@State private var theme = ThemeManager.shared` 在根视图持有,`.preferredColorScheme(theme.mode.colorScheme)` 读它;`@Observable` 属性变化可靠驱动重算,绕开框架 bug。这正是同项目语言切换 `LocalizationManager.shared` 已验证的免重启 pattern——设置类全局开关一律照此办,别走 `@AppStorage` + `App` struct。范例:`Sources/Foundation/DesignKit/ThemeManager.swift` + `TVRemote/App/RootView.swift`。仍命中 nil 不刷新时,system 档读 `@Environment(\.colorScheme)` 当前值兜底代替 nil。
138
+ - **适用**:iOS 17+ SwiftUI,运行时切换 `.preferredColorScheme` 需即时生效。推广:任何「设置改后须即时全局生效」的状态,优先 `@Observable` 单例 + `@State` 持有,而非 `@AppStorage` 跨视图/跨 App-struct 同步。
139
+
140
+ ### 隐藏系统导航栏后边缘滑动返回手势失灵
141
+
142
+ - **现象**:用 NavigationStack 做主路由,页面自绘深色 UI 故 `.toolbar(.hidden, for: .navigationBar)` 隐藏系统导航栏。隐藏后 iOS 左缘右滑返回手势(interactivePopGestureRecognizer)不响应,只能点自绘返回按钮。
143
+ - **根因**:`interactivePopGestureRecognizer` 的 `delegate` 默认指向系统导航栏。导航栏被隐藏后,delegate 据「栏不可见」拒绝识别手势,于是边缘返回整体失效。这是 SwiftUI/UIKit 十年老坑,不是 NavigationStack 的 bug。
144
+ - **正确做法**:用 `UIViewControllerRepresentable` 在 `didMove(toParent:)` 拿 `navigationController?.interactivePopGestureRecognizer`,置 `isEnabled = true` 并把 delegate 换成**自定义带守卫的 delegate**(`gestureRecognizerShouldBegin` 仅在 `viewControllers.count > 1` 时返回 true)。封装成 `.swipeBackEnabled()` View 扩展。范例见 `TVRemote/App/SwipeBackEnabler.swift`。**切忌把 delegate 置 nil**——见下条。
145
+ - **适用**:iOS 16+ NavigationStack(或 UIKit push 栈)+ 隐藏系统导航栏自绘顶栏的场景。手感须真机验证。
146
+
147
+ ### interactivePopGestureRecognizer.delegate 置 nil 致根页崩溃/卡死
148
+
149
+ - **现象**:为恢复边缘手势把 `interactivePopGestureRecognizer.delegate = nil`。结果:在首页(栈根)边缘右滑后首页卡死、点击崩溃;甚至点返回按钮程序化 pop 也崩溃,app 退到桌面。
150
+ - **根因**:UIKit 默认的手势 delegate 含「栈深 ≤ 1 不识别」的守卫,防止 pop 根控制器。置 nil 移除这道守卫,根页边缘滑动会试图 pop 根控制器,UINavigationController 内部状态损坏 → 卡死/崩溃;损坏后任何后续 pop(含程序化 `path = []`)都可能崩。
151
+ - **正确做法**:不要 `delegate = nil`。装自定义 `UIGestureRecognizerDelegate`,`gestureRecognizerShouldBegin` 返回 `navigationController.viewControllers.count > 1`,根页(count==1)不放行。`shouldRecognizeSimultaneouslyWith` 返回 false 避免与滚动冲突。delegate 弱引用 navigationController 防循环。
152
+ - **适用**:iOS 16+ 自管 interactivePopGestureRecognizer 的场景。本坑由上一条「隐藏 navbar 恢复手势」的错误修法引出,二者要一起记。
153
+
154
+ ### TabView 切页保活视图态,别用 switch 渲染 tab 内容
155
+
156
+ - **现象**:底部 tab 用 `@ViewBuilder { switch tab { case .a: AViewView() ... } }` 渲染,切 tab 再切回,页面的滚动位置、已加载数据、嵌套导航栈全丢——「重复刷新」。
157
+ - **根因**:`switch` 在视图树里每次只保留当前 case 的子树,切 tab 时 SwiftUI **销毁**旧 tab 视图、**重建**新 tab 视图,`@State` 随之重置。它不是缓存容器。
158
+ - **正确做法**:用系统 `TabView(selection:)` + 每页 `.tag(...)`。TabView 保活所有 tab 页,切换只是显隐,状态天然保留。隐藏系统 tab bar(`.toolbar(.hidden, for: .tabBar)`)后用自绘底栏,`selection` 与自绘栏共用同一 `@State`。注意别用 `.tabViewStyle(.page)`(会引入左右滑动切页,与边缘返回手势冲突)。
159
+ - **适用**:iOS 16+ SwiftUI 多 tab 且需保留各 tab 页内状态时。
@@ -42,6 +42,32 @@ final class TaskListViewModel {}
42
42
  - 全屏背景、遮罩、弹层可用 `ignoresSafeArea()`。
43
43
  - 禁止使用固定状态栏高度常量。
44
44
 
45
+ ## 软键盘
46
+
47
+ - 任何调起系统软键盘的输入控件,必须给用户提供主动关闭键盘的途径,禁止只能靠输入框自身失焦。
48
+ - 至少同时满足以下两种退出方式:
49
+ - 点击键盘外空白区域收起:根容器加 `.onTapGesture` 把 `@FocusState` 置 `false`。
50
+ - 滚动视图内滚动时收起:`ScrollView` 加 `.scrollDismissesKeyboard(.interactively)`。
51
+ - 多行输入或无明显空白可点的页面,额外在键盘上方放「完成」按钮收起。
52
+ - 焦点统一用 `@FocusState` 管理,不散落手写 UIKit `resignFirstResponder`。
53
+
54
+ ```swift
55
+ @FocusState private var focused: Bool
56
+
57
+ ScrollView {
58
+ TextField(loc("input_placeholder"), text: $text)
59
+ .focused($focused)
60
+ }
61
+ .scrollDismissesKeyboard(.interactively)
62
+ .onTapGesture { focused = false }
63
+ .toolbar {
64
+ ToolbarItemGroup(placement: .keyboard) {
65
+ Spacer()
66
+ Button(loc("done")) { focused = false }
67
+ }
68
+ }
69
+ ```
70
+
45
71
  ## 动效
46
72
 
47
73
  - 默认优先 `spring`,用同一组参数避免风格撕裂。
@@ -54,6 +80,7 @@ private let motionSpring = Animation.spring(response: 0.5, dampingFraction: 0.75
54
80
 
55
81
  - 图标优先 `Image(systemName:)`,无符号则用成熟 SVG。
56
82
  - iOS 应用图标设计稿只提供一张 `1024x1024` 的 PNG 源图,由系统生成各尺寸适配版本。
83
+ - App Icon 源图必须是直角完整方形,背景填满整张画布,禁止预裁圆角、圆角背景、透明圆角或模拟系统圆角遮罩。
57
84
  - 图标主体必须预留至少 `10%` 安全边距,避免系统缩放、圆角遮罩或视觉放大时被切边。
58
85
  - 文案禁止 emoji,禁止硬编码,统一 `loc("key")`。
59
86
  - 禁止使用 emoji 作为功能说明、状态、提示。
@@ -75,3 +102,11 @@ Text(loc("home_title"))
75
102
 
76
103
  - 颜色、字号走 token,不用裸值。
77
104
  - 时间来源通过注入参数或测试可控入口,不直接散落 `Date()`。
105
+
106
+ ## 打包与版本号
107
+
108
+ - **build 号用时间戳**:`CFBundleVersion` 每次打包设为 `date +%Y%m%d%H%M`(如 `202606231415`)。时间戳天然唯一递增,满足 TestFlight / App Store「build 号唯一递增」要求,免去人工记号撞号。
109
+ - marketing version `CFBundleShortVersionString`(如 `1.0`)按发布节奏手动改,不随每次打包变。
110
+ - 用 XcodeGen 的工程,`xcodegen generate` 会按 `project.yml` 重写 `Info.plist` 把 build 号覆盖回默认值。顺序必须 **先 `xcodegen generate`、再 `PlistBuddy -c "Set CFBundleVersion <时间戳>"` 设号、最后 archive**,别反。
111
+ - 归档产物放工程内固定目录(如 `build/`)并加 gitignore,不用 `$TMPDIR` 临时目录。
112
+ - changelog 维护在 iOS 端目录下(如 `ios/CHANGELOG.md`),发版前按「新增 / 变更 / 修复」记本次相对上版的用户可感变化。
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: dwy-acceptance
3
+ description: "【dwy·验收测试】产品 0 到 1 上架迭代阶段。触发场景:用户说『验收 / 验收测试 / 跑集成测试 / E2E 测试 / 上线前测试 / acceptance』,要在上架前做集成/E2E/兼容/合规验收并查文档与代码实际差距时。不重复单元测试。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 集成/E2E/兼容/合规/沙盒验收 + 文档 vs 代码实际差距分析。**不重复单元测试**(单测归 dwy-tdd-dev)。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.dev_progress`、`confirmed.prd`、`confirmed.architecture`
11
+ - 项目源码 + `.dwy/prod/[项目]/02-需求规划/PRD.md`
12
+ - `.dwy/prod/[项目]/03-设计与架构/接口契约.md`
13
+
14
+ ## 实现
15
+ [包装型] 用全局本地 skill(位于 `~/.dwy/skills/<外部skill名>/`):
16
+ 读其 `SKILL.md`(及同目录 scripts/、配套 .md),按它的方式与用户交互/提问,产出按下方「产出契约」落地。
17
+ - 本 skill 依赖的外部 skill:`test-scenarios`、`intended-vs-implemented`(各对应 `~/.dwy/skills/<名>/`)
18
+ - 若 `~/.dwy/skills/<名>/` 不存在:提示用户先跑 `dwy skills install`(或 `dwy claude sync` 会自动装),不要自己用内置能力顶替。
19
+
20
+ ## 产出契约(硬约束)
21
+ - 落到:`.dwy/prod/[项目]/04-上线交付/验收报告.md`
22
+ - 固定章节:集成/E2E 场景结果 / 兼容性 / 合规与沙盒 / 文档vs代码差距 / 验收结论
23
+ - 回写 state.json:`confirmed.acceptance`(验收结论 / 是否达上线标准)
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: dwy-architecture
3
+ description: "【dwy·技术架构】产品 0 到 1 设计与架构阶段。触发场景:用户说『设计架构 / 出技术方案 / 设计库表 / 定接口契约 / 技术架构 / architecture』,要在开发前定下技术架构、数据库设计和接口契约时。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 按项目 rules 产出技术架构 + 数据库库表设计 + 接口契约。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.prd`、`confirmed.tasks`、`confirmed.prototype`
11
+ - `.dwy/prod/[项目]/02-需求规划/PRD.md`、`开发任务拆解.md`
12
+ - 项目 `.claude/rules/` 下的技术栈与编码规则
13
+
14
+ ## 实现
15
+ [自写型]
16
+ 1. 读项目 `.claude/rules/`,按项目既定技术栈与规范产出架构
17
+ 2. 技术架构:分层、模块边界、技术选型理由
18
+ 3. 数据库设计:库表、字段、索引、关系
19
+ 4. 接口契约:路由、请求/响应 schema(遵循项目响应格式约定)
20
+ 5. **走 rules 土建**,不引入项目规范外的技术栈
21
+
22
+ ## 产出契约(硬约束)
23
+ - 落到:`.dwy/prod/[项目]/03-设计与架构/技术架构.md`、`数据库设计.md`、`接口契约.md`
24
+ - 固定章节:
25
+ - 技术架构.md:分层架构 / 模块划分 / 技术选型
26
+ - 数据库设计.md:库表清单 / 字段定义 / 关系与索引
27
+ - 接口契约.md:接口清单 / 请求响应 schema / 错误码
28
+ - 回写 state.json:`confirmed.architecture`
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: dwy-competitor
3
+ description: "【dwy·竞品分析】产品 0 到 1 立项阶段。触发场景:用户说『分析竞品 / 看看市面上有啥 / 竞品调研 / 对标分析 / 市场上谁在做 / competitor』,想知道已有玩家、市场规模和差异化切入点时。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 盘点已有竞品 + 估算市场规模,找出差异化切入点。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.idea`
11
+ - `.dwy/prod/[项目]/01-立项/想法收敛.md`
12
+
13
+ ## 实现
14
+ [包装型] 用全局本地 skill(位于 `~/.dwy/skills/<外部skill名>/`):
15
+ 读其 `SKILL.md`(及同目录 scripts/、配套 .md),按它的方式与用户交互/提问,产出按下方「产出契约」落地。
16
+ - 本 skill 依赖的外部 skill:`competitor-analysis`、`market-sizing`(各对应 `~/.dwy/skills/<名>/`)
17
+ - 若 `~/.dwy/skills/<名>/` 不存在:提示用户先跑 `dwy skills install`(或 `dwy claude sync` 会自动装),不要自己用内置能力顶替。
18
+
19
+ ## 产出契约(硬约束)
20
+ - 落到:`.dwy/prod/[项目]/01-立项/竞品分析.md`
21
+ - 固定章节:竞品清单 / 各竞品优劣 / 市场规模(TAM/SAM/SOM)/ 差异化切入点
22
+ - 回写 state.json:`confirmed.competitors`
@@ -0,0 +1,21 @@
1
+ ---
2
+ name: dwy-ideate
3
+ description: "【dwy·想法收敛】产品 0 到 1 立项第一步。触发场景:用户说『我有个想法 / 帮我把这个点子理清楚 / 想做个 XX / 收敛想法 / 立项 / ideate』,要把模糊念头通过苏格拉底式反问发散+收敛成清晰可执行的产品想法时。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 把用户模糊的初始念头,通过苏格拉底式反问发散+收敛,产出一份清晰的产品想法定义。
8
+
9
+ ## 输入(从哪读)
10
+ - 用户口述的初始想法(首个原子,无上游产出)
11
+
12
+ ## 实现
13
+ [包装型] 用全局本地 skill(位于 `~/.dwy/skills/<外部skill名>/`):
14
+ 读其 `SKILL.md`(及同目录 scripts/、配套 .md),按它的方式与用户交互/提问,产出按下方「产出契约」落地。
15
+ - 本 skill 依赖的外部 skill:`brainstorming`(对应 `~/.dwy/skills/brainstorming/`)
16
+ - 若 `~/.dwy/skills/brainstorming/` 不存在:提示用户先跑 `dwy skills install`(或 `dwy claude sync` 会自动装),不要自己用内置能力顶替。
17
+
18
+ ## 产出契约(硬约束)
19
+ - 落到:`.dwy/prod/[项目]/01-立项/想法收敛.md`
20
+ - 固定章节:核心问题 / 目标用户 / 价值主张 / 关键假设 / 边界与不做什么
21
+ - 回写 state.json:`confirmed.idea`
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: dwy-mvp
3
+ description: "【dwy·MVP定义】产品 0 到 1 立项阶段(闸3)。触发场景:用户说『定义 MVP / 最小可行产品 / 先做哪些功能 / 砍功能 / 第一版做什么 / mvp』,要从想法收敛出第一版必须做的核心功能清单时。MVP 功能硬上限 ≤7 个。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 从想法+验证结果,收敛出第一版 MVP 功能清单。**功能硬上限 ≤7 个**,超出的进后续版本。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.idea`、`confirmed.validation`、`confirmed.poc`
11
+ - `.dwy/prod/[项目]/01-立项/想法收敛.md`、`需求市场验证.md`、`技术验证.md`
12
+
13
+ ## 实现
14
+ [包装型] 用全局本地 skill(位于 `~/.dwy/skills/<外部skill名>/`):
15
+ 读其 `SKILL.md`(及同目录 scripts/、配套 .md),按它的方式与用户交互/提问,产出按下方「产出契约」落地。
16
+ - 本 skill 依赖的外部 skill:`prioritize-features`、`opportunity-solution-tree`(各对应 `~/.dwy/skills/<名>/`)
17
+ - 若 `~/.dwy/skills/<名>/` 不存在:提示用户先跑 `dwy skills install`(或 `dwy claude sync` 会自动装),不要自己用内置能力顶替。
18
+
19
+ ## 产出契约(硬约束)
20
+ - 落到:`.dwy/prod/[项目]/01-立项/MVP清单.md`
21
+ - 固定章节:MVP 功能清单(≤7 个)/ 优先级排序 / 砍到后续版本的功能
22
+ - **功能数超 7 个直接拒绝,强制收敛**
23
+ - 回写 state.json:`confirmed.mvp_features`
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: dwy-poc
3
+ description: "【dwy·技术验证】产品 0 到 1 立项阶段(闸2)。触发场景:用户说『技术验证 / 这技术能不能成 / 跑个 POC / 验证可行性 / 核心技术死穴 / poc』,要在写 PRD 前用最小可跑代码实测核心技术假设时。仅在需求市场验证通过后执行。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 识别 1 个核心技术死穴假设,写最小可跑 POC 实测能不能成。失败→建议砍方向/换技术栈,不进 PRD。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.validation`(仅当 `pass=true` 才执行)
11
+ - `.dwy/prod/[项目]/01-立项/需求市场验证.md`、`想法收敛.md`
12
+
13
+ ## 实现
14
+ [自写型]
15
+ 1. 校验 `confirmed.validation.pass=true`,否则直接退出
16
+ 2. 从想法中识别**唯一一个**最致命的技术假设(最可能让方案不成立的那个)
17
+ 3. 写最小可跑 POC 代码实测,代码放 `.dwy/prod/[项目]/01-立项/poc/`
18
+ 4. 实测结果定性:成立 / 不成立
19
+ 5. 不成立 → 给出砍方向或换技术栈建议,**不进 PRD**
20
+
21
+ ## 产出契约(硬约束)
22
+ - 落到:`.dwy/prod/[项目]/01-立项/技术验证.md`,POC 代码放 `01-立项/poc/`
23
+ - 固定章节:核心技术假设 / POC 方案 / 实测结果 / 结论与建议
24
+ - 回写 state.json:`confirmed.poc`(含 `pass` 布尔字段)
@@ -0,0 +1,22 @@
1
+ ---
2
+ name: dwy-prd
3
+ description: "【dwy·PRD生成】产品 0 到 1 需求规划阶段。触发场景:用户说『写 PRD / 出需求文档 / 产品需求文档 / prd』,要把 MVP 清单细化成可开发的产品需求文档时。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 把 MVP 功能清单细化成完整可开发的 PRD。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.mvp_features`、`confirmed.idea`
11
+ - `.dwy/prod/[项目]/01-立项/MVP清单.md`
12
+
13
+ ## 实现
14
+ [包装型] 用全局本地 skill(位于 `~/.dwy/skills/<外部skill名>/`):
15
+ 读其 `SKILL.md`(及同目录 scripts/、配套 .md),按它的方式与用户交互/提问,产出按下方「产出契约」落地。
16
+ - 本 skill 依赖的外部 skill:`create-prd`(对应 `~/.dwy/skills/create-prd/`)
17
+ - 若 `~/.dwy/skills/create-prd/` 不存在:提示用户先跑 `dwy skills install`(或 `dwy claude sync` 会自动装),不要自己用内置能力顶替。
18
+
19
+ ## 产出契约(硬约束)
20
+ - 落到:`.dwy/prod/[项目]/02-需求规划/PRD.md`
21
+ - 固定章节:背景与目标 / 用户场景 / 功能需求(逐条)/ 非功能需求 / 验收标准
22
+ - 回写 state.json:`confirmed.prd`
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: dwy-product-launcher
3
+ description: "【dwy·全流程总控】从模糊想法到上线产品的唯一入口。触发场景:用户说『我想做个产品 / 从 0 到 1 把这个做出来 / 走完整流程 / 帮我把想法变成产品 / 一条龙做产品 / product launcher』,要把一个模糊念头逐步推进时。**渐进式:从立项第一步直接开始,每阶段完成后问继不继续,不预设终点、用户随时可停。** 只想做其中某一阶段,也可直接触发对应 dwy-stage-* 阶段 skill。"
4
+ ---
5
+
6
+ ## 职责(总控·编排层)
7
+ 从模糊想法到产品的唯一入口。按顺序串 5 个阶段 skill,维护 state.json(进度 + 已确认结论),并做多版本子 Agent 编排。**不掺产出内容**,产出由阶段内原子 skill 落地。
8
+
9
+ ## 渐进式推进(核心交互)
10
+ **不在入口预问"走到哪个阶段"。** 直接从立项第一步开始往下走,一步一步推进,每个阶段准出后问一句「继续下一步 / 到此为止」,由用户当场决定走不走。终点不预设——走一步看一步,用户随时可喊停。
11
+
12
+ ```
13
+ 开始 → dwy-stage-launch(立项·三闸门)
14
+ → 阶段完成,问:继续做需求版本? → 用户选 继续 / 停
15
+ 继续 → dwy-stage-prd(需求版本)
16
+ → 阶段完成,问:继续做设计架构? → 继续 / 停
17
+ 继续 → dwy-stage-design(设计架构)
18
+ → 问:继续 TDD 开发? → 继续 / 停
19
+ 继续 → dwy-stage-dev(TDD 开发)
20
+ → 问:继续上架迭代? → 继续 / 停
21
+ 继续 → dwy-stage-ship(上架迭代)→ 完
22
+ ```
23
+
24
+ 规则:
25
+ - **从第一步直接开始**,不让用户提前选终点
26
+ - 每阶段准出后**只问"继不继续"**,不问"要走到哪"
27
+ - 闸门内(立项的需求验证/POC)若没过,自动停在当前阶段,不往下走
28
+ - 用户喊停时,已完成阶段的产出和 state.json 都已落盘,下次可断点续跑
29
+
30
+ 按序的五阶段:
31
+ 1. **dwy-stage-launch** — 立项(三闸门:需求验证 → POC → MVP)
32
+ 2. **dwy-stage-prd** — 需求版本
33
+ 3. **dwy-stage-design** — 设计架构
34
+ 4. **dwy-stage-dev** — TDD 开发
35
+ 5. **dwy-stage-ship** — 上架迭代
36
+
37
+ 进度以 `state.json` 的 `current_stage` 为准;可从任意阶段断点续跑。
38
+
39
+ ## 首次初始化(只建根,不建阶段目录)
40
+ 新项目首次进入时,**只**做两件最小事:
41
+ - 建项目根目录 `.dwy/prod/[项目]/`(不建 01~04 任何阶段子目录)
42
+ - 初始化 `state.json`
43
+
44
+ 产品产出 `.dwy/prod/[项目]/` 入库,无需改 `.gitignore`(外部 skill 在全局 `~/.dwy/skills/`,不落项目)。若包装型原子 skill 触发时 `~/.dwy/skills/<name>/` 不存在,提示用户先跑 `dwy skills install`。
45
+
46
+ **阶段目录渐进式按需建**:`01-立项/` 进立项时才建,`02-需求规划/` 进需求阶段才建……不要一次把四个阶段目录全建出来(那不是渐进式)。state schema、各 skill 的 `confirmed.*` 字段契约、外部 skill 全局安装见 `references/state-and-contract.md`。
47
+
48
+ ## state.json(进度 + 结论)
49
+ 落 `.dwy/prod/[项目]/state.json`,含:
50
+ - `current_stage` — 当前阶段
51
+ - `current_version` — 当前版本(如 `V1.0`)
52
+ - `confirmed.*` — 各原子的已确认结论引用(idea / competitors / validation / poc / mvp_features / prd / version_plan / tasks / prototype / architecture / dev_progress / acceptance / release)
53
+ - `versions` — 各版本状态(路线图 + 完成情况)
54
+
55
+ ## 多版本编排逻辑
56
+
57
+ ### 版本间:串行
58
+ - V1.1 依赖 V1.0,版本一个接一个做
59
+ - **每个版本起独立子 Agent**(独立上下文窗口),读版本路线图 + 上一版代码
60
+ - 产出写回 `.dwy/prod/` 和源码,**只向主窗口返回成果摘要**(不返回全量代码,省上下文)
61
+
62
+ ### 版本内:多模块并发
63
+ - 互不依赖的模块用多个子 Agent **并发**
64
+ - 改同一批文件时各自独立 **worktree**,避免互相覆盖
65
+
66
+ ## 准出条件(硬约束)
67
+ - 全部规划版本上线完成
68
+ - `state.json` 各版本标记完成,`confirmed.*` 齐全
@@ -0,0 +1,103 @@
1
+ # 产品 0→1 流程 · 状态与产出契约(配套参考)
2
+
3
+ `dwy-product-launcher` 总控和各原子/阶段 skill 统一遵循本文件。
4
+
5
+ ## 一、产出与缓存目录(最终全貌,**渐进式按需创建**)
6
+
7
+ 下面是流程全部走完后的**目标结构**,不是初始化时一次建好的。**渐进式原则:每个阶段真正开始时,才创建它自己那一级目录和文件**——初始化只建项目根 + state.json,`01-立项/` 进立项才建,`02-需求规划/` 进需求阶段才建,以此类推。不要上来就把 01~04 四个阶段目录全建出来。
8
+
9
+ ```
10
+ .dwy/prod/
11
+ [项目名]/ # 产品产出,入库(git 跟踪)
12
+ 01-立项/
13
+ 想法收敛.md
14
+ 竞品分析.md
15
+ 需求市场验证.md # 闸1·总闸门
16
+ 技术验证.md # 闸2
17
+ poc/ # 最小 POC 代码
18
+ MVP清单.md # 闸3·≤7
19
+ 02-需求规划/
20
+ PRD.md
21
+ 版本路线图.md
22
+ 开发任务拆解.md
23
+ 03-设计与架构/
24
+ prototypes/ # dwy-whiteboard-prototype 白板 HTML
25
+ 技术架构.md
26
+ 数据库设计.md
27
+ 接口契约.md
28
+ 04-上线交付/
29
+ 验收报告.md
30
+ 上架物料.md
31
+ 埋点与指标.md
32
+ 下一版迭代规划.md
33
+ 09-开发日志.md
34
+ state.json # 进度 + 已确认结论,入库
35
+ ```
36
+
37
+ **外部 skill 不在项目内**:pm-skills / superpowers 的副本装在**全局** `~/.dwy/skills/<name>/`(一台机一份、全项目共享),由 `dwy skills install` 维护,`dwy claude sync` 自检缺失时自动装。包装型原子 skill 运行时直接读这个全局路径,不在项目里缓存。
38
+
39
+ ## 二、.gitignore 规则(总控初始化项目时写入)
40
+
41
+ 产品产出 `.dwy/prod/[项目]/` **入库**(git 跟踪),无需任何忽略——外部 skill 在全局 `~/.dwy/skills/`,不落项目,没有要忽略的缓存。
42
+
43
+ 注意:**不要**写整行 `.dwy/`(那会把产品文档也忽略)。本流程不需要在项目 `.gitignore` 加任何条目。
44
+
45
+ ## 三、state.json schema
46
+
47
+ 线性单向,只记进度 + 已确认结论,无回退状态机。
48
+
49
+ ```json
50
+ {
51
+ "version": "1.0",
52
+ "project_name": "...",
53
+ "current_stage": 1,
54
+ "current_round": "stage1_round1",
55
+ "confirmed": {
56
+ "idea": {},
57
+ "competitors": {},
58
+ "validation": { "pass": true, "evidence": "...", "method": "..." },
59
+ "poc": { "pass": true, "deadlock": "...", "result": "..." },
60
+ "mvp_features": [],
61
+ "prd": {},
62
+ "version_plan": {},
63
+ "tasks": {},
64
+ "prototype": {},
65
+ "architecture": {},
66
+ "dev_progress": { "<module>": "todo | done" },
67
+ "acceptance": {},
68
+ "release": {}
69
+ }
70
+ }
71
+ ```
72
+
73
+ 字段名是各 skill 的对外契约,**回写方与读取方必须用同一名字**:
74
+
75
+ | 原子 skill | 回写字段 | 下游读取方 |
76
+ |---|---|---|
77
+ | dwy-ideate | confirmed.idea | dwy-competitor |
78
+ | dwy-competitor | confirmed.competitors | dwy-validate |
79
+ | dwy-validate | confirmed.validation | dwy-poc(仅 pass=true 才执行) |
80
+ | dwy-poc | confirmed.poc | dwy-mvp |
81
+ | dwy-mvp | confirmed.mvp_features | dwy-stage-prd |
82
+ | dwy-prd | confirmed.prd | dwy-version |
83
+ | dwy-version | confirmed.version_plan | dwy-tasks / dwy-stage-design |
84
+ | dwy-tasks | confirmed.tasks | dwy-stage-dev |
85
+ | dwy-prototype | confirmed.prototype | — |
86
+ | dwy-architecture | confirmed.architecture | dwy-stage-dev |
87
+ | dwy-tdd-dev | confirmed.dev_progress | dwy-stage-ship |
88
+ | dwy-acceptance | confirmed.acceptance | dwy-release |
89
+ | dwy-release | confirmed.release | 迭代回到阶段二 |
90
+
91
+ ## 四、外部 skill 全局安装 `~/.dwy/skills/`
92
+
93
+ 包装型原子 skill(dwy-ideate / dwy-competitor / dwy-validate / dwy-mvp / dwy-prd / dwy-version / dwy-tasks / dwy-tdd-dev / dwy-acceptance / dwy-release)依赖 pm-skills / superpowers 的外部 skill,装在**全局** `~/.dwy/skills/<name>/`:
94
+
95
+ - 每个外部 skill 是**整个目录**(含 scripts/ 与配套 .md,不止 SKILL.md)
96
+ - 由 cli 命令 `dwy skills install` 安装/更新(clone 两仓库的指定 stable tag → 搬整目录 + LICENSE → 写 `VERSIONS.json`)
97
+ - `dwy claude sync` 自检:`~/.dwy/skills/` 缺失时自动装一次
98
+ - 当前 stable:pm-skills **v2.0.0** | superpowers **v6.0.3**(升级改 cli 的 `skills-install.js` 清单 tag)
99
+ - 原子 skill 运行时**只读** `~/.dwy/skills/<name>/`,缺失则提示用户先 `dwy skills install`,不自己拉、不降级
100
+
101
+ ## 五、断点续跑
102
+
103
+ 会话中断后,总控读 `state.json` 的 `current_stage` + `current_round`,从中断处续跑。已确认字段不重做。
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: dwy-prototype
3
+ description: "【dwy·UI原型】产品 0 到 1 设计与架构阶段。触发场景:用户说『画原型 / 出原型 / 做 UI 原型 / 把页面画出来 / prototype』,要在写 UI 代码前把页面与交互流程画成白板原型时。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 把 PRD 的页面与交互画成白板 HTML 原型。**不重复造**,直接调用现有的 `dwy-whiteboard-prototype` skill。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.prd`、`confirmed.tasks`
11
+ - `.dwy/prod/[项目]/02-需求规划/PRD.md`
12
+
13
+ ## 实现
14
+ [自写型]
15
+ 1. 直接调用现有 skill `dwy-whiteboard-prototype`(位于 `skills/开发流程/dwy-whiteboard-prototype/`)
16
+ 2. 按其规范画白板:每页一个 HTML,状态全、跳转清
17
+ 3. **不另起炉灶手搓白板引擎**,复用其 `assets/template.html`
18
+ 4. 产出目录改为本阶段约定路径(见产出契约)
19
+
20
+ ## 产出契约(硬约束)
21
+ - 落到:`.dwy/prod/[项目]/03-设计与架构/prototypes/`,每页一个 `<页面>-spec.html` + `index-spec.html` 总览
22
+ - 固定要求:覆盖 PRD 所有页面、状态全、跨白板跳转清
23
+ - 回写 state.json:`confirmed.prototype`
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: dwy-release
3
+ description: "【dwy·上架迭代】产品 0 到 1 上架迭代阶段(终点)。触发场景:用户说『上架 / 发布上线 / 出上架物料 / 配埋点 / 规划下一版 / release』,要准备上架物料、埋点指标并规划下一版迭代时。"
4
+ ---
5
+
6
+ ## 职责(单一)
7
+ 产出上架物料 + 埋点指标设计 + 下一版迭代规划。
8
+
9
+ ## 输入(从哪读)
10
+ - state.json 的 `confirmed.acceptance`、`confirmed.prd`、`confirmed.version_plan`
11
+ - `.dwy/prod/[项目]/04-上线交付/验收报告.md`
12
+ - `.dwy/prod/[项目]/02-需求规划/版本路线图.md`
13
+
14
+ ## 实现
15
+ [包装型] 用全局本地 skill(位于 `~/.dwy/skills/<外部skill名>/`):
16
+ 读其 `SKILL.md`(及同目录 scripts/、配套 .md),按它的方式与用户交互/提问,产出按下方「产出契约」落地。
17
+ - 本 skill 依赖的外部 skill:`release-notes`、`gtm-strategy`、`metrics-dashboard`、`north-star-metric`(各对应 `~/.dwy/skills/<名>/`)
18
+ - 若 `~/.dwy/skills/<名>/` 不存在:提示用户先跑 `dwy skills install`(或 `dwy claude sync` 会自动装),不要自己用内置能力顶替。
19
+
20
+ ## 产出契约(硬约束)
21
+ - 落到:`.dwy/prod/[项目]/04-上线交付/上架物料.md`、`埋点与指标.md`、`下一版迭代规划.md`
22
+ - 固定章节:
23
+ - 上架物料.md:发布说明 / GTM 策略 / 渠道文案
24
+ - 埋点与指标.md:北极星指标 / 埋点清单 / 指标看板设计
25
+ - 下一版迭代规划.md:数据反馈方向 / 下一版功能候选 / 优先级
26
+ - 回写 state.json:`confirmed.release`(上架物料就绪 + 下一版迭代方向)