create-dwy 0.14.6 → 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 (74) hide show
  1. package/CHANGELOG.md +18 -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 +47 -3
  6. package/templates/ai-tools/CLAUDE.md +18 -1
  7. package/templates/ai-tools/hooks/Git/pre-git-commit-sensitive-check.sh +10 -2
  8. package/templates/ai-tools/rules/Android/dwy-android-core.md +8 -0
  9. package/templates/ai-tools/rules/Flutter/dwy-flutter-core.md +8 -0
  10. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-strict.md +221 -0
  11. package/templates/ai-tools/rules/HarmonyOS/dwy-arkts-ui-build.md +193 -0
  12. package/templates/ai-tools/rules/iOS/dwy-ios-pitfalls.md +159 -0
  13. package/templates/ai-tools/rules/iOS/dwy-swift-core.md +35 -0
  14. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +23 -0
  15. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-architecture/SKILL.md +28 -0
  16. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +22 -0
  17. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-ideate/SKILL.md +21 -0
  18. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +23 -0
  19. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-poc/SKILL.md +24 -0
  20. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +22 -0
  21. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +68 -0
  22. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +103 -0
  23. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prototype/SKILL.md +23 -0
  24. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +26 -0
  25. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-design/SKILL.md +23 -0
  26. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-dev/SKILL.md +22 -0
  27. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-launch/SKILL.md +25 -0
  28. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-prd/SKILL.md +22 -0
  29. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-stage-ship/SKILL.md +21 -0
  30. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +22 -0
  31. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +25 -0
  32. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +28 -0
  33. package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +23 -0
  34. 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
  35. 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
  36. 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
  37. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +31 -0
  38. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +31 -0
  39. 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
  40. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +37 -0
  41. 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
  42. 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
  43. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +25 -0
  44. package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +32 -0
  45. 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
  46. 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
  47. 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
  48. 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
  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/SKILL.md +31 -21
  50. 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
  51. 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
  52. 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
  53. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-whiteboard-prototype/SKILL.md +70 -0
  54. 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
  55. package/templates/ai-tools/rules/Global/dwy-common-code-baseline.md +0 -27
  56. package/templates/ai-tools/skills//345/256/211/345/205/250/dwy-sdk-spec/scripts/init_config.sh +0 -97
  57. package/templates/ai-tools/skills//345/274/200/345/217/221/346/265/201/347/250/213/dwy-publish/SKILL.md +0 -50
  58. 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
  59. 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
  60. package/templates/ai-tools/skills//350/277/220/347/273/264/345/217/221/345/270/203/dwy-cibuildwheel/SKILL.md +0 -356
  61. /package/templates/ai-tools/rules/{Global → Docker}/dwy-docker.md +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/assets/files-whitelist-snippet.json" +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/assets/js-commercial.npmignore" +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/assets/python-commercial-MANIFEST.in" +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/assets/python-commercial.gitignore" +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/references/js-commercial-guide.md" +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/references/publish-blacklist.md" +0 -0
  68. /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
  69. /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
  70. /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
  71. /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
  72. /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
  73. /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
  74. /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
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # create-dwy
2
2
 
3
+ ## 0.15.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 新增「产品 0→1」三层 skill 体系(总控 `dwy-product-launcher` + 5 个可独立触发的阶段 skill + 13 个产出物级原子 skill):从模糊想法渐进式推进到上线产品,立项阶段三道闸门(需求市场验证为总闸门 → 技术 POC → MVP≤7),廉价验证在前、昂贵投入在后。
8
+ - 新增 `dwy skills install` 命令:把 pm-skills / superpowers 的外部 skill 安装到全局 `~/.dwy/skills/`(一台机一份、全项目共享),含完整 `scripts/` 与 LICENSE;再次运行即覆盖更新。`dwy claude sync` 选中相关 skill 时自动自检安装。
9
+ - 外部 skill 进全局纯数据目录而非 `.claude/skills/`,避免被工具当 skill 加载、与 `dwy-*` 触发冲突。
10
+ - 改造 `dwy-whiteboard-prototype`:配色按产品定位推荐(保留 `:root` 语义变量结构)、尺寸按目标端推荐、图标不再用 emoji。
11
+
12
+ ## 0.14.7
13
+
14
+ ### Patch Changes
15
+
16
+ - 平台选择交互默认按当前项目本地目录回填:存在 `.claude`、`.codex` / `.agents` / `AGENTS.md`、`.cursor`、`.opencode` 时,自动勾选对应平台;无本地目录时退回默认 `Claude Code + Codex`。
17
+ - 将跨语言通用基础约束并入 `CLAUDE.md`,删除重复的 `rules/Global/dwy-common-code-baseline.md`,减少全局规则分散。
18
+ - 将 Docker 规则从 `rules/Global/dwy-docker.md` 调整到 `rules/Docker/dwy-docker.md`,按技术域归类。
19
+ - 为 Android 与 Flutter 规则补充全面屏、刘海屏、挖孔屏适配约束,统一要求基于安全区域与响应式布局实现。
20
+
3
21
  ## 0.14.6
4
22
 
5
23
  ### Patch Changes
package/bin/index.js CHANGED
@@ -4,6 +4,7 @@ import { readFileSync } from 'fs'
4
4
  import { fileURLToPath } from 'url'
5
5
  import { dirname, resolve } from 'path'
6
6
  import { syncAll } from '../src/sync-all.js'
7
+ import { installSkills } from '../src/skills-install.js'
7
8
 
8
9
  const __dirname = dirname(fileURLToPath(import.meta.url))
9
10
  const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'))
@@ -16,6 +17,7 @@ function printHelp() {
16
17
  'Usage:',
17
18
  ' dwy 运行交互式同步',
18
19
  ' dwy sync 运行交互式同步',
20
+ ' dwy skills install 安装/更新全局外部 skill 到 ~/.dwy/skills/',
19
21
  ' dwy --help 显示本说明',
20
22
  ' dwy -h 显示本说明',
21
23
  ' dwy --version 显示当前版本',
@@ -29,6 +31,8 @@ if (argv.includes('--help') || argv.includes('-h') || argv[0] === 'help') {
29
31
  printHelp()
30
32
  } else if (argv.includes('--version') || argv.includes('-V')) {
31
33
  console.log(pkg.version)
34
+ } else if (argv[0] === 'skills' && argv[1] === 'install') {
35
+ await installSkills()
32
36
  } else if (argv.length === 0 || argv[0] === 'sync') {
33
37
  await syncAll()
34
38
  } else {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.14.6",
3
+ "version": "0.15.0",
4
4
  "description": "项目脚手架与多平台 AI 配置同步工具",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,113 @@
1
+ import fs from 'fs-extra'
2
+ import path from 'path'
3
+ import os from 'node:os'
4
+ import { getDwyHomeDir, runGit, chalk } from './utils.js'
5
+
6
+ function logAction(label) {
7
+ console.log(chalk.green(` ✓ ${label}`))
8
+ }
9
+
10
+ // 产品 0→1 流程依赖的外部 skill 清单。
11
+ // 每项:缓存目录名(= 原子 skill 引用的 ~/.dwy/skills/<name>)→ 来源 repo / stable tag / repo 内目录。
12
+ // 只用正式 release tag,不用 main HEAD(定版可复现)。更新版本时改这里的 tag。
13
+ const SKILL_SOURCES = {
14
+ 'pm-skills': {
15
+ repo: 'https://github.com/phuryn/pm-skills.git',
16
+ tag: 'v2.0.0',
17
+ licenseFile: 'LICENSE',
18
+ licenseDest: 'LICENSE-pm-skills',
19
+ skills: {
20
+ 'competitor-analysis': 'pm-market-research/skills/competitor-analysis',
21
+ 'market-sizing': 'pm-market-research/skills/market-sizing',
22
+ 'sentiment-analysis': 'pm-market-research/skills/sentiment-analysis',
23
+ 'interview-script': 'pm-product-discovery/skills/interview-script',
24
+ 'prioritize-features': 'pm-product-discovery/skills/prioritize-features',
25
+ 'opportunity-solution-tree': 'pm-product-discovery/skills/opportunity-solution-tree',
26
+ 'metrics-dashboard': 'pm-product-discovery/skills/metrics-dashboard',
27
+ 'create-prd': 'pm-execution/skills/create-prd',
28
+ 'outcome-roadmap': 'pm-execution/skills/outcome-roadmap',
29
+ 'test-scenarios': 'pm-execution/skills/test-scenarios',
30
+ 'release-notes': 'pm-execution/skills/release-notes',
31
+ 'gtm-strategy': 'pm-go-to-market/skills/gtm-strategy',
32
+ 'intended-vs-implemented': 'pm-ai-shipping/skills/intended-vs-implemented',
33
+ 'north-star-metric': 'pm-marketing-growth/skills/north-star-metric',
34
+ },
35
+ },
36
+ superpowers: {
37
+ repo: 'https://github.com/obra/superpowers.git',
38
+ tag: 'v6.0.3',
39
+ licenseFile: 'LICENSE',
40
+ licenseDest: 'LICENSE-superpowers',
41
+ skills: {
42
+ brainstorming: 'skills/brainstorming',
43
+ 'writing-plans': 'skills/writing-plans',
44
+ 'test-driven-development': 'skills/test-driven-development',
45
+ 'systematic-debugging': 'skills/systematic-debugging',
46
+ },
47
+ },
48
+ }
49
+
50
+ // 全局外部 skill 安装目录:~/.dwy/skills/
51
+ function getGlobalSkillsDir() {
52
+ return path.join(getDwyHomeDir(), 'skills')
53
+ }
54
+
55
+ // 浅克隆指定 tag 到临时目录(--depth 1 + --branch <tag> 只取定版,省带宽)。
56
+ async function shallowCloneTag(repoUrl, tag, destDir) {
57
+ await runGit(['clone', '--depth', '1', '--branch', tag, repoUrl, destDir])
58
+ }
59
+
60
+ // 安装/更新全部外部 skill 到 ~/.dwy/skills/。再次运行即覆盖式更新。
61
+ export async function installSkills() {
62
+ const targetDir = getGlobalSkillsDir()
63
+ await fs.ensureDir(targetDir)
64
+
65
+ const tmpRoot = path.join(os.tmpdir(), `dwy-skills-${process.pid}`)
66
+ await fs.ensureDir(tmpRoot)
67
+
68
+ const versions = {}
69
+ try {
70
+ for (const [sourceName, src] of Object.entries(SKILL_SOURCES)) {
71
+ console.log(chalk.gray(`拉取 ${sourceName} @ ${src.tag} ...`))
72
+ const repoDir = path.join(tmpRoot, sourceName)
73
+ await shallowCloneTag(src.repo, src.tag, repoDir)
74
+
75
+ // 搬 LICENSE(MIT 要求保留原版权声明)
76
+ const licenseSrc = path.join(repoDir, src.licenseFile)
77
+ if (await fs.pathExists(licenseSrc)) {
78
+ await fs.copy(licenseSrc, path.join(targetDir, src.licenseDest), { overwrite: true })
79
+ }
80
+
81
+ // 搬每个 skill 整目录(含 scripts/、配套 .md)
82
+ for (const [skillName, repoPath] of Object.entries(src.skills)) {
83
+ const from = path.join(repoDir, repoPath)
84
+ const to = path.join(targetDir, skillName)
85
+ if (!await fs.pathExists(from)) {
86
+ throw new Error(`${sourceName} @ ${src.tag} 内未找到 ${repoPath}(仓库结构可能变了)`)
87
+ }
88
+ await fs.remove(to)
89
+ await fs.copy(from, to, { overwrite: true })
90
+ logAction(`skills/${skillName}`)
91
+ versions[skillName] = { repo: src.repo, tag: src.tag }
92
+ }
93
+ }
94
+
95
+ // 写版本清单,供查看与后续更新比对
96
+ await fs.writeJson(path.join(targetDir, 'VERSIONS.json'), versions, { spaces: 2 })
97
+ console.log(chalk.green(`\n✓ 外部 skill 已安装到 ${targetDir}`))
98
+ } finally {
99
+ await fs.remove(tmpRoot)
100
+ }
101
+ }
102
+
103
+ // 全局 skill 是否已就绪(供 sync 自检:缺则自动装)。
104
+ export async function skillsInstalled() {
105
+ return fs.pathExists(path.join(getGlobalSkillsDir(), 'VERSIONS.json'))
106
+ }
107
+
108
+ export async function ensureSkillsInstalled() {
109
+ if (await skillsInstalled()) return false
110
+ console.log(chalk.gray('未检测到全局外部 skill,自动安装...'))
111
+ await installSkills()
112
+ return true
113
+ }
package/src/sync-all.js CHANGED
@@ -24,6 +24,14 @@ import {
24
24
  } from './sync-codex.js'
25
25
  import { scanExistingCursorRules, syncCursor } from './sync-cursor.js'
26
26
  import { syncOpenCode } from './sync-opencode.js'
27
+ import { ensureSkillsInstalled } from './skills-install.js'
28
+
29
+ // 产品 0→1 流程里依赖全局外部 skill 的包装型原子 skill。
30
+ // 同步选中其中任一个时,触发全局外部 skill 自检安装。
31
+ const WRAPPER_SKILL_NAMES = new Set([
32
+ 'dwy-ideate', 'dwy-competitor', 'dwy-validate', 'dwy-mvp', 'dwy-prd',
33
+ 'dwy-version', 'dwy-tasks', 'dwy-tdd-dev', 'dwy-acceptance', 'dwy-release',
34
+ ])
27
35
 
28
36
  const PLATFORM_OPTIONS = [
29
37
  { value: 'claude', label: 'Claude Code', hint: '支持 rules / skills / commands / hooks' },
@@ -119,7 +127,32 @@ async function shouldInitialize(projectDir, existing) {
119
127
  return !isCancel(ok) && ok
120
128
  }
121
129
 
122
- async function selectPlatforms() {
130
+ export async function buildPlatformDefaultsFromLocalState(projectDir) {
131
+ const defaults = []
132
+
133
+ if (await fs.pathExists(path.join(projectDir, '.claude'))) {
134
+ defaults.push('claude')
135
+ }
136
+ if (
137
+ await fs.pathExists(path.join(projectDir, '.codex'))
138
+ || await fs.pathExists(path.join(projectDir, '.agents'))
139
+ || await fs.pathExists(path.join(projectDir, 'AGENTS.md'))
140
+ ) {
141
+ defaults.push('codex')
142
+ }
143
+ if (await fs.pathExists(path.join(projectDir, '.cursor'))) {
144
+ defaults.push('cursor')
145
+ }
146
+ if (await fs.pathExists(path.join(projectDir, '.opencode'))) {
147
+ defaults.push('opencode')
148
+ }
149
+
150
+ return defaults.length > 0 ? defaults : ['claude', 'codex']
151
+ }
152
+
153
+ async function selectPlatforms(projectDir) {
154
+ const initialValues = await buildPlatformDefaultsFromLocalState(projectDir)
155
+
123
156
  console.log(chalk.gray('\n平台能力预览:'))
124
157
  console.log(chalk.gray(' Claude Code → rules / skills / commands / hooks'))
125
158
  console.log(chalk.gray(' Codex → rules / skills / hooks'))
@@ -129,7 +162,7 @@ async function selectPlatforms() {
129
162
  const result = await multiselect({
130
163
  message: '选择要同步的平台',
131
164
  options: PLATFORM_OPTIONS,
132
- initialValues: ['claude', 'codex'],
165
+ initialValues,
133
166
  required: true,
134
167
  maxItems: 6,
135
168
  })
@@ -575,7 +608,7 @@ export async function syncAll({
575
608
  return
576
609
  }
577
610
 
578
- const selectedPlatforms = selectedPlatformsOverride || await selectPlatforms()
611
+ const selectedPlatforms = selectedPlatformsOverride || await selectPlatforms(projectDir)
579
612
  if (selectedPlatforms === null) {
580
613
  console.log(chalk.yellow('\n已取消同步。'))
581
614
  return
@@ -665,6 +698,17 @@ export async function syncAll({
665
698
  }
666
699
  await saveSyncState(projectDir, syncState)
667
700
 
701
+ // 产品 0→1 流程的包装型原子 skill 依赖全局外部 skill(~/.dwy/skills/)。
702
+ // 若本次同步选中了这些 skill 且全局未安装,自动装一次(缺则装、有则跳过)。
703
+ if (selected.skills?.some((s) => WRAPPER_SKILL_NAMES.has(s.name))) {
704
+ try {
705
+ await ensureSkillsInstalled()
706
+ } catch (error) {
707
+ console.log(chalk.yellow(`\n外部 skill 自动安装失败:${error.message}`))
708
+ console.log(chalk.gray('可稍后手动运行 `dwy skills install` 重试。'))
709
+ }
710
+ }
711
+
668
712
  if (!syncedAnySupportedPlatform) {
669
713
  console.log(chalk.yellow('未选择已接入平台,本次未执行同步。'))
670
714
  }
@@ -8,7 +8,24 @@
8
8
 
9
9
  ## 团队执行任务
10
10
 
11
- 任务执行必须使用多 Agent 或 agent team 并行提高效率。
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 工作流执行,不使用本地手工打包作为唯一流程。
12
29
 
13
30
  ## 减少常见 LLM 编码错误的行为指南
14
31
 
@@ -37,8 +37,16 @@ PATTERNS=(
37
37
  # 拼接为正则
38
38
  REGEX=$(IFS='|'; echo "${PATTERNS[*]}")
39
39
 
40
- # 扫描暂存内容
41
- RESULT=$(git diff --cached -U0 2>/dev/null | grep -inE "$REGEX" 2>/dev/null)
40
+ # 只扫「新增行」(+ 开头,排除 +++ 文件头),删除的代码不可能是正在引入的凭证。
41
+ # 跳过文档/规则类文件(.md/.mdc/规则/hook 本身)——它们写的是「如何检测凭证」的模式说明,非真凭证。
42
+ SCAN_FILES=$(printf '%s\n' "$STAGED" | grep -viE '\.(md|mdc|txt)$|rules/|/hooks/|sensitive-check')
43
+ RESULT=""
44
+ for f in $SCAN_FILES; do
45
+ HIT=$(git diff --cached -U0 -- "$f" 2>/dev/null \
46
+ | grep -E '^\+' | grep -vE '^\+\+\+' \
47
+ | grep -inE "$REGEX" 2>/dev/null)
48
+ [ -n "$HIT" ] && RESULT="${RESULT}${f}:\n${HIT}\n"
49
+ done
42
50
 
43
51
  if [ -n "$RESULT" ]; then
44
52
  echo "========================================"
@@ -15,6 +15,14 @@ paths:
15
15
 
16
16
  不限制构建工具、UI 框架、架构模式、SDK 版本、依赖库选型 —— 与老项目兼容。
17
17
 
18
+ ## 全面屏 / 刘海屏适配
19
+
20
+ - Android 端默认按全面屏、刘海屏、挖孔屏适配实现,不要使用固定上下左右边距硬编码内容区。
21
+ - 优先使用 `WindowInsets`、系统栏 inset、约束布局和响应式容器处理状态栏、导航栏、手势区域占位。
22
+ - 顶部标题栏、底部操作栏、输入区、悬浮按钮必须显式考虑状态栏、导航栏和手势返回区域,避免被遮挡或贴边误触。
23
+ - 全屏背景、沉浸式遮罩可以延伸到系统栏区域,但前景可交互内容默认留在安全显示区内。
24
+ - 禁止写死状态栏高度、导航栏高度、刘海高度,统一通过系统 inset 获取。
25
+
18
26
  ## 一、命名规范
19
27
 
20
28
 
@@ -8,6 +8,14 @@ paths:
8
8
 
9
9
  # Flutter + Dart 基础风格规范
10
10
 
11
+ ## 全面屏 / 刘海屏适配
12
+
13
+ - Flutter 应用默认按 iOS 与 Android 全面屏、刘海屏、挖孔屏适配实现,不要依赖固定上下左右边距硬编码页面内容区。
14
+ - 优先使用 `SafeArea`、`MediaQuery.padding`、`MediaQuery.viewPadding`、`MediaQuery.viewInsets` 与响应式布局约束处理状态栏、底部手势区、键盘遮挡。
15
+ - 页面主体、底部操作栏、表单输入区、弹层内容必须显式处理安全区域,避免被刘海、Home Indicator、导航栏或软键盘遮挡。
16
+ - 全屏背景、沉浸式插画、遮罩层可以铺满屏幕,但前景可交互内容默认留在安全区域内。
17
+ - 禁止写死 iOS 状态栏高度、Android 导航栏高度或机型刘海尺寸,统一通过系统安全区域与 inset 信息计算。
18
+
11
19
  ## 一、项目结构
12
20
 
13
21
  ### 1.1 标准目录
@@ -0,0 +1,221 @@
1
+ ---
2
+ description: HarmonyOS ArkTS 严格性避坑(相对 TS/JS 的禁用语法,编译期 arkts-no-* 报错)
3
+ paths:
4
+ - "**/*.ets"
5
+ - "**/*.ts"
6
+ ---
7
+
8
+ # ArkTS 严格性避坑
9
+
10
+ ArkTS 是 TypeScript 的**受限子集**,禁掉大量 TS/JS 动态写法以换取性能与静态可分析性。
11
+ 从 TS/JS(尤其 iOS/前端转鸿蒙)照搬代码必触发 `arkts-no-*` 编译错误。
12
+ **写 `.ets` 前先过一遍本清单**,别用下面列出的禁用写法。每条配真实报错码 + 错误/正确写法。
13
+
14
+ ## 1. 禁止解构声明与解构赋值(arkts-no-destruct-decls / arkts-no-destruct-assignment)
15
+
16
+ ```typescript
17
+ // 错:解构声明
18
+ let [oldR, r] = [a % m, m];
19
+ // 错:解构赋值
20
+ [oldR, r] = [r, oldR - q * r];
21
+ const { value, next } = readVarint(bytes, i);
22
+
23
+ // 对:拆成单变量 + 临时变量交换
24
+ let oldR = a % m;
25
+ let r = m;
26
+ const tmpR = oldR - q * r;
27
+ oldR = r;
28
+ r = tmpR;
29
+ const res = readVarint(bytes, i);
30
+ const value = res.value;
31
+ const next = res.next;
32
+ ```
33
+
34
+ ## 2. 对象字面量必须对应显式 class/interface(arkts-no-untyped-obj-literals / arkts-no-obj-literals-as-types)
35
+
36
+ ArkTS 不允许「匿名对象字面量当类型」,也不允许「无类型标注的对象字面量」。
37
+ **两种触发场景都要先声明 interface**:
38
+
39
+ ### 2.1 函数返回类型不能写匿名对象
40
+
41
+ ```typescript
42
+ // 错:返回类型是匿名对象字面量
43
+ private static readVarint(bytes: Uint8Array, i: number): { value: number; next: number } { ... }
44
+
45
+ // 对:先声明 interface
46
+ interface VarintResult {
47
+ value: number;
48
+ next: number;
49
+ }
50
+ private static readVarint(bytes: Uint8Array, i: number): VarintResult { ... }
51
+ ```
52
+
53
+ ### 2.2 返回 / 赋值的对象字面量必须有类型来源
54
+
55
+ ```typescript
56
+ // 错:裸对象字面量(编译器无法对应到具体类型)
57
+ return { command: c, arg0: a, arg1: b, dataLength: d };
58
+ const out = { header, payload: payload.slice() };
59
+
60
+ // 对:显式标注类型(interface 已声明)
61
+ const header: AdbHeader = { command: c, arg0: a, arg1: b, dataLength: d };
62
+ return header;
63
+ const out: AdbMessageResult = { header, payload: payload.slice() };
64
+ ```
65
+
66
+ ### 2.3 给 SDK API 传对象参数也要带类型
67
+
68
+ SDK(@ohos.net.socket / cryptoFramework 等)的参数即使是已声明类型,裸字面量仍报错,
69
+ 必须用「声明类型的变量」传入:
70
+
71
+ ```typescript
72
+ // 错:裸字面量传参
73
+ await tcp.connect({ address: { address: host, port: 5555, family: 1 }, timeout: 15000 });
74
+ await tcp.send({ data: buf });
75
+ const out = await signer.sign({ data: token });
76
+
77
+ // 对:拆成带 SDK 类型标注的变量
78
+ const netAddr: socket.NetAddress = { address: host, port: 5555, family: 1 };
79
+ const opts: socket.TCPConnectOptions = { address: netAddr, timeout: 15000 };
80
+ await tcp.connect(opts);
81
+
82
+ const sendOpts: socket.TCPSendOptions = { data: buf };
83
+ await tcp.send(sendOpts);
84
+
85
+ const input: cryptoFramework.DataBlob = { data: token };
86
+ const out = await signer.sign(input);
87
+ ```
88
+
89
+ ## 3. 禁止用下标访问 string 取字符(arkts-no-props-by-index)
90
+
91
+ string 不能 `s[i]` 取字符(被判为「字段索引访问」)。用 `charAt`:
92
+
93
+ ```typescript
94
+ // 错
95
+ const table = 'ABCD...';
96
+ out += table[(n >> 18) & 63];
97
+
98
+ // 对
99
+ out += table.charAt((n >> 18) & 63);
100
+ ```
101
+
102
+ > 注意:`Uint8Array[i]` / `Array[i]` 下标访问是允许的,只有 **string 下标取字符** 被禁。
103
+
104
+ ## 4. 禁止 String() / Number() / Boolean() 当转换函数调用
105
+
106
+ ArkTS 禁止把内置构造器当函数转换值。catch 错误转文案、数字转字符串都要换写法:
107
+
108
+ ```typescript
109
+ // 错
110
+ const msg = e instanceof Error ? e.message : String(e);
111
+ const s = String(num);
112
+
113
+ // 对:catch 变量在 ArkTS 里就是 Error(含 BusinessError extends Error),直接取 message
114
+ function errMsg(e: Error): string {
115
+ return e.message;
116
+ }
117
+ // 数字转字符串用 .toString() 或模板串
118
+ const s = num.toString();
119
+ const s2 = `${num}`;
120
+ ```
121
+
122
+ ## 5. Promise executor / 回调形参类型:简单形参标,executor 的 resolve/reject 不标
123
+
124
+ 箭头函数当回调,形参不能省类型;executor 里别把函数直接当回调透传:
125
+
126
+ ```typescript
127
+ // 错:resolve 无类型 + 直接把 resolve 透传给 setTimeout
128
+ return new Promise<void>((resolve) => setTimeout(resolve, ms));
129
+
130
+ // 对:形参标类型 + 包一层箭头
131
+ return new Promise<void>((resolve: () => void) => {
132
+ setTimeout(() => {
133
+ resolve();
134
+ }, ms);
135
+ });
136
+ ```
137
+
138
+ **⚠️ 例外(实测踩坑):给 `Promise` executor 的 resolve/reject 标注「函数类型」反而触发
139
+ `arkts-no-structural-typing`**。executor 的两个形参类型由 `Promise<T>` 自身约束,显式标
140
+ `(reject: (e: Error) => void)` 会与 SDK 内置的 reject 签名做结构比对而报错。这种情况**去掉
141
+ executor 形参的类型注解,让 ArkTS 推断**(reject 内部传 `Error` 子类实例完全正常):
142
+
143
+ ```typescript
144
+ // 错:executor 形参标函数类型 → arkts-no-structural-typing
145
+ return new Promise<Uint8Array>(
146
+ (resolve: (v: Uint8Array) => void, reject: (e: Error) => void) => { ... });
147
+
148
+ // 对:executor 形参不标,推断
149
+ return new Promise<Uint8Array>((resolve, reject) => {
150
+ reject(new MyError(...)); // MyError extends Error,正常
151
+ });
152
+ ```
153
+
154
+ 判据:**普通回调(如自己定义的 `onError: (e: Error) => void`)形参要标;`new Promise(...)`
155
+ 的 executor 形参不标**。另:跨文件传回调时,形参类型用基类(`Error`)而非具体子类
156
+ (`MyGrpcError`),避免「子类型 lambda 赋给基类形参」触发同一规则。
157
+
158
+ ## 6. 页面路由路径不带 `ets/` 前缀
159
+
160
+ `main_pages.json` 的 `src` 与 `windowStage.loadContent(...)` 的路径都相对 `src/main/ets/` 根,
161
+ **不要带 `ets/` 前缀**,否则编译报 `Page '.../ets/ets/xxx' does not exist`:
162
+
163
+ ```json
164
+ // 错:resources/base/profile/main_pages.json
165
+ { "src": ["ets/spike/SpikePage"] }
166
+ // 对
167
+ { "src": ["spike/SpikePage"] }
168
+ ```
169
+
170
+ ```typescript
171
+ // 错
172
+ windowStage.loadContent('ets/spike/SpikePage', cb);
173
+ // 对
174
+ windowStage.loadContent('spike/SpikePage', cb);
175
+ ```
176
+
177
+ ## 7. 大整数运算用原生 bigint(无需第三方库)
178
+
179
+ ArkTS 原生支持 ES11 `bigint`(`10n` / `BigInt(x)` / `<< >> % & | ** /`)。
180
+ 但无 `.inverse(m)` / `.power(_,modulus:)` / `.serialize()` 这类方法(那是 Swift BigInt 库的),
181
+ 模逆/模幂/序列化要**手写**:
182
+
183
+ ```typescript
184
+ // 模幂(快速幂)
185
+ function modPow(base: bigint, exp: bigint, m: bigint): bigint {
186
+ let result = 1n;
187
+ let b = base % m;
188
+ let e = exp;
189
+ while (e > 0n) {
190
+ if ((e & 1n) === 1n) { result = (result * b) % m; }
191
+ e >>= 1n;
192
+ b = (b * b) % m;
193
+ }
194
+ return result;
195
+ }
196
+ // 模逆(扩展欧几里得,见本仓鸿蒙工程 AndroidPubkey.ets)
197
+ ```
198
+
199
+ ## 8. 自检清单(写完 .ets 提交前 grep 一遍)
200
+
201
+ ```bash
202
+ grep -rn 'let \[\|const \[' . # 解构声明
203
+ grep -rn '): {' . # 匿名对象当返回类型
204
+ grep -rn 'return {' . # 裸对象字面量返回(确认有类型标注)
205
+ grep -rn 'String(\|Number(\|Boolean(' . # 构造器当转换函数(排除 String.fromCharCode)
206
+ grep -rn 'new Promise<.*>((.*:.*=>' . # executor 形参标了函数类型(见 §5 例外,应去注解)
207
+ # string 下标取字符需人工核对(table[i] 形式)
208
+ ```
209
+
210
+ ## 9. 仍需真机/真实 SDK 确认的(编译期过不代表运行对)
211
+
212
+ ArkTS 编译通过只保证语法合规;以下 SDK 行为编译期无法验证,需真机/模拟器实测:
213
+ - cryptoFramework 的 `createSign(规格串)` 是否支持目标算法组合(如 `RSA2048|PKCS1|NoHash`)
214
+ - `getEncoded()` 返回的公钥 DER 是 PKCS#1 还是 X.509 SPKI(影响解析)
215
+ - `@ohos.net.socket.TCPSocket` 局域网连通性(需 `ohos.permission.INTERNET`,normal 级安装即授予)
216
+
217
+ ---
218
+
219
+ > 本文件只管**语言语法层**。ArkUI 组件、V2 状态管理、图标资源、工程构建、运行时容错的坑
220
+ > 见同目录 **`dwy-arkts-ui-build.md`**(图标名映射 / 自定义组件链属性 / @BuilderParam 默认值 /
221
+ > @Param 外部初始化 / async 状态快照 / bootstrap 分步容错 / DevEco 命令行构建)。