create-dwy 0.14.5 → 0.14.7

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/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # create-dwy
2
2
 
3
+ ## 0.14.7
4
+
5
+ ### Patch Changes
6
+
7
+ - 平台选择交互默认按当前项目本地目录回填:存在 `.claude`、`.codex` / `.agents` / `AGENTS.md`、`.cursor`、`.opencode` 时,自动勾选对应平台;无本地目录时退回默认 `Claude Code + Codex`。
8
+ - 将跨语言通用基础约束并入 `CLAUDE.md`,删除重复的 `rules/Global/dwy-common-code-baseline.md`,减少全局规则分散。
9
+ - 将 Docker 规则从 `rules/Global/dwy-docker.md` 调整到 `rules/Docker/dwy-docker.md`,按技术域归类。
10
+ - 为 Android 与 Flutter 规则补充全面屏、刘海屏、挖孔屏适配约束,统一要求基于安全区域与响应式布局实现。
11
+
12
+ ## 0.14.6
13
+
14
+ ### Patch Changes
15
+
16
+ - `dwy` 交互同步时,默认勾选优先读取项目目录下 `/.dwy/sync-state.json` 的上次托管选择结果,减少每次重新勾选 `rules`、`skills`、`commands`、`hooks`。
17
+ - 新增 iOS 适配开发规则 `dwy-ios-layout-adaptation.md`,统一原型基准为 `393×852 pt`,补充 `375 / 393 / 430 pt` 验证矩阵与 Safe Area 约束。
18
+ - 补充 iOS 与 Android 应用图标安全边距规则:iOS 图标源稿使用 `1024x1024` PNG,Android 启动图标源稿使用 `mipmap-xxxhdpi` `192x192`,主体默认预留 `10%` 安全边距。
19
+
3
20
  ## 0.14.5
4
21
 
5
22
  ### Patch Changes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.14.5",
3
+ "version": "0.14.7",
4
4
  "description": "项目脚手架与多平台 AI 配置同步工具",
5
5
  "type": "module",
6
6
  "bin": {
package/src/sync-all.js CHANGED
@@ -57,6 +57,29 @@ function unionSets(...sets) {
57
57
  return new Set(sets.flatMap(set => [...set]))
58
58
  }
59
59
 
60
+ function hasSetItems(setsByType) {
61
+ return setsByType.skills.size + setsByType.rules.size + setsByType.commands.size + setsByType.hooks.size > 0
62
+ }
63
+
64
+ export function buildSelectionDefaultsFromSyncState(existing, syncState) {
65
+ const stateDefaults = {
66
+ skills: new Set(),
67
+ rules: new Set(),
68
+ commands: new Set(),
69
+ hooks: new Set(),
70
+ }
71
+
72
+ for (const platformState of Object.values(syncState?.platforms || {})) {
73
+ for (const type of ['skills', 'rules', 'commands', 'hooks']) {
74
+ for (const name of platformState?.[type] || []) {
75
+ stateDefaults[type].add(name)
76
+ }
77
+ }
78
+ }
79
+
80
+ return hasSetItems(stateDefaults) ? stateDefaults : existing
81
+ }
82
+
60
83
  async function scanCombinedExisting(projectDir) {
61
84
  const claudeDir = path.join(projectDir, '.claude')
62
85
  const agentsMdPath = path.join(projectDir, 'AGENTS.md')
@@ -80,7 +103,7 @@ async function scanCombinedExisting(projectDir) {
80
103
  }
81
104
 
82
105
  async function shouldInitialize(projectDir, existing) {
83
- const hasAnyExisting = existing.skills.size + existing.rules.size + existing.commands.size + existing.hooks.size > 0
106
+ const hasAnyExisting = hasSetItems(existing)
84
107
  if (hasAnyExisting) return true
85
108
 
86
109
  const hasConfigDir = await fs.pathExists(path.join(projectDir, '.claude'))
@@ -96,7 +119,32 @@ async function shouldInitialize(projectDir, existing) {
96
119
  return !isCancel(ok) && ok
97
120
  }
98
121
 
99
- async function selectPlatforms() {
122
+ export async function buildPlatformDefaultsFromLocalState(projectDir) {
123
+ const defaults = []
124
+
125
+ if (await fs.pathExists(path.join(projectDir, '.claude'))) {
126
+ defaults.push('claude')
127
+ }
128
+ if (
129
+ await fs.pathExists(path.join(projectDir, '.codex'))
130
+ || await fs.pathExists(path.join(projectDir, '.agents'))
131
+ || await fs.pathExists(path.join(projectDir, 'AGENTS.md'))
132
+ ) {
133
+ defaults.push('codex')
134
+ }
135
+ if (await fs.pathExists(path.join(projectDir, '.cursor'))) {
136
+ defaults.push('cursor')
137
+ }
138
+ if (await fs.pathExists(path.join(projectDir, '.opencode'))) {
139
+ defaults.push('opencode')
140
+ }
141
+
142
+ return defaults.length > 0 ? defaults : ['claude', 'codex']
143
+ }
144
+
145
+ async function selectPlatforms(projectDir) {
146
+ const initialValues = await buildPlatformDefaultsFromLocalState(projectDir)
147
+
100
148
  console.log(chalk.gray('\n平台能力预览:'))
101
149
  console.log(chalk.gray(' Claude Code → rules / skills / commands / hooks'))
102
150
  console.log(chalk.gray(' Codex → rules / skills / hooks'))
@@ -106,7 +154,7 @@ async function selectPlatforms() {
106
154
  const result = await multiselect({
107
155
  message: '选择要同步的平台',
108
156
  options: PLATFORM_OPTIONS,
109
- initialValues: ['claude', 'codex'],
157
+ initialValues,
110
158
  required: true,
111
159
  maxItems: 6,
112
160
  })
@@ -533,30 +581,31 @@ export async function syncAll({
533
581
  console.log(chalk.yellow(`Found ${scans.skills.length} skills, ${scans.rules.length} rules, ${scans.commands.length} commands, ${scans.hooks.length} hooks\n`))
534
582
 
535
583
  const existing = await scanCombinedExisting(projectDir)
584
+ const syncState = await loadSyncState(projectDir)
585
+ const selectionDefaults = buildSelectionDefaultsFromSyncState(existing, syncState)
536
586
  const skipInitPrompt = !!(selectedOverride || selectedPlatformsOverride)
537
587
  if (!skipInitPrompt && !await shouldInitialize(projectDir, existing)) {
538
588
  console.log(chalk.yellow('\n已取消同步。'))
539
589
  return
540
590
  }
541
591
 
542
- const hasAnyExisting = existing.skills.size + existing.rules.size + existing.commands.size + existing.hooks.size > 0
592
+ const hasAnyExisting = hasSetItems(existing)
543
593
  if (!hasAnyExisting && !selectedOverride) {
544
594
  console.log(chalk.gray('首次同步,进入交互式选择...\n'))
545
595
  }
546
596
 
547
- const selected = selectedOverride || await interactiveSelect(scans, existing)
597
+ const selected = selectedOverride || await interactiveSelect(scans, selectionDefaults)
548
598
  if (selected === null) {
549
599
  console.log(chalk.yellow('\n已取消同步。'))
550
600
  return
551
601
  }
552
602
 
553
- const selectedPlatforms = selectedPlatformsOverride || await selectPlatforms()
603
+ const selectedPlatforms = selectedPlatformsOverride || await selectPlatforms(projectDir)
554
604
  if (selectedPlatforms === null) {
555
605
  console.log(chalk.yellow('\n已取消同步。'))
556
606
  return
557
607
  }
558
608
 
559
- const syncState = await loadSyncState(projectDir)
560
609
  const staleEntries = await collectStaleEntries(projectDir, scans, syncState, selectedPlatforms)
561
610
  const approvedStaleRemovals = staleRemovalsOverride
562
611
  ? normalizeRemovalInput(staleRemovalsOverride)
@@ -10,6 +10,23 @@
10
10
 
11
11
  任务执行必须使用多 Agent 或 agent team 并行提高效率。
12
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
+
13
30
  ## 减少常见 LLM 编码错误的行为指南
14
31
 
15
32
  与项目特定指令合并使用。
@@ -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
 
@@ -97,6 +105,8 @@ res/drawable/
97
105
  #### mipmap / 9-patch / 模块前缀
98
106
 
99
107
  - **mipmap 只放启动图标**(`ic_launcher` 系列),其余图片一律放 `drawable` —— mipmap 会跨密度保留资源,普通图放 mipmap 白白增大包体
108
+ - 应用图标设计稿只提供 `mipmap-xxxhdpi` 目录下的 `192x192` 启动图标源图,其余密度版本交给系统或构建流程派生。
109
+ - 图标主体必须预留至少 `10%` 安全边距,避免启动器缩放、遮罩或厂商裁切时贴边缺角。
100
110
  - **9-patch 图保留 `.9` 后缀**:`bg_chat_bubble.9.png`
101
111
  - **模块前缀(可选)**:多模块 / 大型项目推荐资源加业务模块前缀,便于检索、避免跨模块重名 —— `user_activity_login.xml`、id `user_tv_name`;单模块 / 老项目可省略,保持本规范其余示例的无前缀写法
102
112
 
@@ -783,4 +793,4 @@ val result = calculate(x)
783
793
  | 21 | Gradle 脚本(Groovy / kts):锁版本 / `exclude` / 自定义 task / 变体 / 关键魔法值有 why 注释,无复述式、无注释掉的配置块 | ✓ |
784
794
 
785
795
 
786
- **不执行自检就提交代码 = 违规。**
796
+ **不执行自检就提交代码 = 违规。**
@@ -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,47 @@
1
+ ---
2
+ description: iOS 适配开发与原型基准规范(393pt 主基准、安全区、自适应布局)
3
+ paths:
4
+ - "**/*.swift"
5
+ ---
6
+
7
+ ## iOS 适配基线
8
+
9
+ - iOS 布局统一使用 `pt`,不要按 `px` 做分辨率换算。
10
+ - iOS 适配依赖 SwiftUI 自适应布局 + Safe Area,不为每个机型单独写一套尺寸。
11
+ - 原型图与开发都以同一套布局意图为准,原型不是按像素缩放给不同机型。
12
+
13
+ ## 原型基准尺寸
14
+
15
+ - 做 iOS 原型图时,统一基于 `393×852 pt` 制作,对齐 `iPhone 15 / iPhone 16` 主流尺寸。
16
+ - `393×852 pt` 是视觉基准,不是要求开发写死页面宽高。
17
+ - 切图资源按系统倍率处理,布局本身仍按 `pt` 思考。
18
+
19
+ ## 适配验证矩阵
20
+
21
+ - 小屏下限按 `375pt` 宽验证,不允许关键内容挤压、截断、横向溢出。
22
+ - 主基准按 `393pt` 宽设计与开发。
23
+ - 大屏上限按 `430pt` 宽验证,要求布局自然放宽,不靠写死间距撑版。
24
+
25
+ ## 布局规则
26
+
27
+ - 页面容器、列表、卡片宽度优先 `maxWidth: .infinity` 或等价自适应写法。
28
+ - 间距优先用 `Spacer`、stack、grid、`safeAreaInset` 等布局系统能力,不用绝对 `offset` 拼版。
29
+ - 禁止用 `UIScreen.main.bounds` 为页面主布局做硬编码换算,优先 `GeometryReader` 或容器相对布局。
30
+ - 具有固定视觉比例的实体控件可以使用固定 `pt` 尺寸,但外层容器必须自适应。
31
+
32
+ ## 安全区
33
+
34
+ - 前景内容默认在安全区内布局,不进入刘海、灵动岛、Home 指示条区域。
35
+ - 全屏背景、遮罩可用 `ignoresSafeArea()`,但不能让主要交互内容越过安全区。
36
+ - 禁止写死状态栏、刘海、底部 Home 条高度,统一依赖系统安全区。
37
+
38
+ ## 字体与可访问性
39
+
40
+ - 字号优先语义化管理,避免页面散落裸值。
41
+ - 关键文案默认兼容 Dynamic Type,系统字体放大后不能破版。
42
+
43
+ ## 开发验证
44
+
45
+ - 至少验证三档宽度:`375pt`、`393pt`、`430pt`。
46
+ - 检查内容是否被刘海、灵动岛、Home 指示条遮挡。
47
+ - 检查滚动页、底部固定栏、弹层在小屏和大屏下都无错位或裁切。
@@ -53,6 +53,8 @@ private let motionSpring = Animation.spring(response: 0.5, dampingFraction: 0.75
53
53
  ## 图标与文案
54
54
 
55
55
  - 图标优先 `Image(systemName:)`,无符号则用成熟 SVG。
56
+ - iOS 应用图标设计稿只提供一张 `1024x1024` 的 PNG 源图,由系统生成各尺寸适配版本。
57
+ - 图标主体必须预留至少 `10%` 安全边距,避免系统缩放、圆角遮罩或视觉放大时被切边。
56
58
  - 文案禁止 emoji,禁止硬编码,统一 `loc("key")`。
57
59
  - 禁止使用 emoji 作为功能说明、状态、提示。
58
60
 
@@ -1,27 +0,0 @@
1
- ---
2
- description: 跨语言通用代码基础规则(注释、图标、全面屏适配、发布与打包)
3
- ---
4
-
5
- # 跨语言通用代码基础规则
6
-
7
- ## 1. 代码注释
8
-
9
- - 所有代码都必须有注释。
10
- - 类、接口、私有方法、函数、重要参数与重要字段都必须写明用途、约束与边界。
11
- - 每个类顶部要有简短功能说明。
12
- - 注释重点写“为什么”和“约束”,避免仅复述代码实现。
13
-
14
- ## 2. 图标规范
15
-
16
- - 图标优先使用成熟稳定的图标库,不使用 emoji 作为功能性图标。
17
- - 优先选择项目已有成熟依赖;无可用依赖时再补充稳定方案。
18
-
19
- ## 3. iOS / Android 全面屏适配
20
-
21
- - iOS 与 Android 端默认按全面屏/刘海屏适配实现,不使用固定上下左右边距的“硬编码”方案。
22
- - 优先使用安全区域(Safe Area / Window Insets)与响应式布局约束。
23
-
24
- ## 4. 发布与打包基础准则
25
-
26
- - PyPI 与 npm 的发布必须走 GitHub Actions,使用 OIDC 完成鉴权,禁止使用长期静态发布凭据。
27
- - 多平台打包必须走 GitHub Actions 工作流执行,不使用本地手工打包作为唯一流程。