create-dwy 0.5.0 → 0.5.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.5.0",
3
+ "version": "0.5.1",
4
4
  "description": "Project scaffolding and Claude Code config sync CLI",
5
5
  "type": "module",
6
6
  "bin": {
package/src/sync.js CHANGED
@@ -43,7 +43,16 @@ export async function syncClaude() {
43
43
 
44
44
  if (await fs.pathExists(settingsDest)) {
45
45
  const existing = await fs.readJson(settingsDest)
46
- const merged = { ...existing, ...newSettings }
46
+ // 两层深合并:顶层 key merge + dict value 二级 merge
47
+ const merged = { ...existing }
48
+ for (const [key, value] of Object.entries(newSettings)) {
49
+ if (key in merged && typeof merged[key] === 'object' && !Array.isArray(merged[key])
50
+ && typeof value === 'object' && !Array.isArray(value)) {
51
+ merged[key] = { ...merged[key], ...value }
52
+ } else {
53
+ merged[key] = value
54
+ }
55
+ }
47
56
  await fs.writeJson(settingsDest, merged, { spaces: 2 })
48
57
  console.log(chalk.green(' ✓ settings.json — merged'))
49
58
  } else {
@@ -0,0 +1,72 @@
1
+ #!/bin/bash
2
+ # Claude Code PostToolUse Hook
3
+ # 当 Edit/Write 修改了 claude-global 模板文件时,自动同步到 ~/.claude/
4
+
5
+ # 快速路径检查:不含模板目录标记则直接退出
6
+ TEMPLATE_MARKER="claude-cli/templates/claude-global/"
7
+ echo "$TOOL_INPUT" | grep -q "$TEMPLATE_MARKER" || exit 0
8
+
9
+ # 用 python3 提取 file_path 并执行同步
10
+ python3 -c "
11
+ import json, sys, os, shutil
12
+
13
+ tool_input = os.environ.get('TOOL_INPUT', '')
14
+ if not tool_input:
15
+ sys.exit(0)
16
+
17
+ try:
18
+ data = json.loads(tool_input)
19
+ except (json.JSONDecodeError, TypeError):
20
+ sys.exit(0)
21
+
22
+ file_path = data.get('file_path', '')
23
+ marker = '$TEMPLATE_MARKER'
24
+
25
+ if marker not in file_path:
26
+ sys.exit(0)
27
+
28
+ # 提取相对路径
29
+ rel_path = file_path.split(marker, 1)[1]
30
+ if not rel_path or '..' in rel_path:
31
+ sys.exit(0)
32
+
33
+ target_dir = os.path.expanduser('~/.claude')
34
+ dest_path = os.path.join(target_dir, rel_path)
35
+
36
+ # settings.json: 两层深合并
37
+ if rel_path == 'settings.json':
38
+ try:
39
+ with open(file_path) as f:
40
+ new_settings = json.load(f)
41
+ except Exception:
42
+ sys.exit(0)
43
+
44
+ if os.path.exists(dest_path):
45
+ try:
46
+ with open(dest_path) as f:
47
+ existing = json.load(f)
48
+ except Exception:
49
+ existing = {}
50
+ # 两层深合并:顶层 merge + dict value 二级 merge
51
+ merged = dict(existing)
52
+ for key, value in new_settings.items():
53
+ if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
54
+ merged[key] = {**merged[key], **value}
55
+ else:
56
+ merged[key] = value
57
+ else:
58
+ os.makedirs(target_dir, exist_ok=True)
59
+ merged = new_settings
60
+
61
+ with open(dest_path, 'w') as f:
62
+ json.dump(merged, f, indent=2, ensure_ascii=False)
63
+ f.write('\n')
64
+ print(f'[sync] Merged settings.json -> {dest_path}')
65
+ else:
66
+ # 其他文件:直接复制
67
+ os.makedirs(os.path.dirname(dest_path), exist_ok=True)
68
+ shutil.copy2(file_path, dest_path) # copy2 保留权限和时间戳
69
+ print(f'[sync] Copied {rel_path} -> {dest_path}')
70
+ "
71
+
72
+ exit 0
@@ -127,7 +127,7 @@ Agent-2 (worktree) → 前端页面 + API 调用
127
127
  | 多 Agent 改同一文件 | 冲突、覆盖 | 串行修改或拆分文件职责 |
128
128
  | 无上下文的 Agent | 产出质量差 | prompt 必须自包含 |
129
129
  | 链式依赖硬并行 | B 依赖 A 的输出却并行 | 识别依赖,串行执行 |
130
- | 10+ Agent 同时派出 | 难以管理、context 爆炸 | 单批最多 5 个 Agent |
130
+ | 超过 5 个 Agent 同时派出 | 难以管理、context 爆炸 | 单批最多 5 个 Agent |
131
131
  | 跳过 review 直接提交 | 质量无保障 | 遵循 review 闭环 |
132
132
 
133
133
  ## 四、Agent 数量指南
@@ -581,19 +581,29 @@ async def export_users(request: Request): ...
581
581
 
582
582
  ## 十五、违规检测清单
583
583
 
584
- AI 在编写或审查代码时,必须检查以下违规模式:
584
+ **AI 在编写或审查代码时,必须检查以下违规模式。检测到违规时按严重程度执行对应动作:**
585
+
586
+ ### 严重程度与动作
587
+
588
+ | 严重程度 | 动作 |
589
+ |---------|------|
590
+ | **致命** | **立即 STOP,不得继续编写或提交代码。** 必须修正后重新检查,修正前不执行任何其他操作。 |
591
+ | **高** | 必须修正后才能继续。向用户说明违规点和修正方案。 |
592
+ | **中** | 提示用户存在风险,建议修正。用户确认后可继续。 |
593
+
594
+ ### 检查清单
585
595
 
586
596
  | 检查项 | 违规模式 | 严重程度 |
587
597
  |--------|---------|---------|
598
+ | 密码明文 | `password` 字段出现在 Response Schema | **致命 → STOP** |
599
+ | 密码弱存储 | 使用 MD5/SHA 而非 bcrypt | **致命 → STOP** |
600
+ | SQL 拼接 | f-string 拼接 SQL 语句 | **致命 → STOP** |
601
+ | 命令注入 | 直接执行 shell 命令或 `shell=True` | **致命 → STOP** |
602
+ | DolphinDB 拼接 | 用户输入直接进 DolphinDB 脚本 | **致命 → STOP** |
588
603
  | 自增 ID 暴露 | 路由参数为 `int` 类型的 ID | 高 |
589
- | 密码明文 | `password` 字段出现在 Response Schema | 致命 |
590
- | 密码弱存储 | 使用 MD5/SHA 而非 bcrypt | 致命 |
591
604
  | 无认证接口 | 数据接口无 `Depends(get_current_user)` | 高 |
592
- | SQL 拼接 | `f"SELECT ... {user_input}"` | 致命 |
593
- | 命令注入 | `os.system()` / `subprocess(shell=True)` | 致命 |
594
605
  | 全量返回 | 列表接口无分页限制 | 高 |
595
606
  | 错误回显 | `str(exception)` 返回给客户端 | 高 |
607
+ | 文件名不安全 | 使用用户原始文件名做存储路径 | 高 |
596
608
  | 物理删除 | `session.delete()` 用于业务数据 | 中 |
597
609
  | Redis 无 TTL | `redis.set()` 没有 `ex` 参数 | 中 |
598
- | 文件名不安全 | 使用用户原始文件名做存储路径 | 高 |
599
- | DolphinDB 拼接 | 用户输入直接进 DolphinDB 脚本 | 致命 |
@@ -0,0 +1,106 @@
1
+ # Git Commit 强制规范
2
+
3
+ **每次 `git commit` 前必须完整执行以下检查流程,不可跳过任何步骤。**
4
+
5
+ ## 提交前强制检查流程
6
+
7
+ 执行 `git commit` 前,按顺序完成以下 4 步。任何一步未通过则 **STOP,不得提交**。
8
+
9
+ ### Step 1: 分析变更内容
10
+
11
+ 运行 `git diff --cached` 查看暂存区,回答以下问题:
12
+
13
+ - 这次变更做了几件事?
14
+ - 涉及哪些模块/目录?
15
+
16
+ **如果做了多件事** → STOP,必须拆成多个 commit。判断标准:能否用一个 ≤ 72 字符的 subject 清晰描述?不能就必须拆。
17
+
18
+ ### Step 2: 确定 scope
19
+
20
+ 读取当前项目 CLAUDE.md,查找 `## Git Commit Scope` 段落:
21
+
22
+ - **找到了** → scope 必须从其中定义的枚举值中选取,不得自造
23
+ - **没找到** → 提醒用户:「当前项目 CLAUDE.md 未定义 Git Commit Scope,建议添加。需要我帮你加吗?」,然后 scope 可省略
24
+
25
+ scope 使用规则:
26
+ - 变更仅涉及单个模块 → **必须**带 scope
27
+ - 变更跨多个模块 → 省略 scope
28
+
29
+ ### Step 3: 生成 commit message
30
+
31
+ 格式必须严格匹配:
32
+
33
+ ```
34
+ <type>(<scope>): <subject>
35
+ ```
36
+
37
+ 或(无 scope 时):
38
+
39
+ ```
40
+ <type>: <subject>
41
+ ```
42
+
43
+ **type 只能从以下枚举中选取:**
44
+
45
+ | type | 用途 |
46
+ |------|------|
47
+ | feat | 新功能 |
48
+ | fix | 修复 bug |
49
+ | refactor | 重构(不改变外部行为) |
50
+ | chore | 构建/依赖/配置/发版等杂务 |
51
+ | docs | 文档变更 |
52
+ | test | 测试新增或修改 |
53
+ | perf | 性能优化 |
54
+ | style | 代码格式调整(不影响逻辑) |
55
+ | ci | CI/CD 配置变更 |
56
+
57
+ **subject 强制要求:**
58
+
59
+ - **中文**描述
60
+ - 不加句号
61
+ - ≤ 72 字符
62
+ - 用简洁的动宾短语:`添加`、`修复`、`移除`、`重构`、`升级`
63
+
64
+ **body(可选):**
65
+
66
+ - 空一行后写
67
+ - 中文,只写 why(动机、背景),不复述 what
68
+ - 破坏性变更加 `BREAKING CHANGE:` 前缀
69
+
70
+ ### Step 4: 自检验证
71
+
72
+ 生成 commit message 后,逐条验证:
73
+
74
+ - [ ] type 在枚举表中?
75
+ - [ ] scope 符合项目 CLAUDE.md 定义(或正确省略)?
76
+ - [ ] subject 是中文、无句号、≤ 72 字符、简洁动宾短语?
77
+ - [ ] 一个 commit 只做一件事,没有混合不同 type 的变更?
78
+ - [ ] 没有混合不同 scope 的独立变更?
79
+
80
+ **全部通过 → 执行 commit。任何一条未通过 → 修正后重新验证。**
81
+
82
+ ## 常见错误(必须避免)
83
+
84
+ | 错误 | 正确 |
85
+ |------|------|
86
+ | `feat: add user login`(英文) | `feat: 添加用户登录功能`(中文) |
87
+ | `fix(eui): 修复了按钮样式。` | `fix(eui): 修复按钮样式`(无句号、不用「了」) |
88
+ | `feat: 添加登录功能并修复登出 bug` | 拆成两个 commit |
89
+ | `update: 修改配置` | `update` 不在 type 枚举中,应为 `chore` |
90
+ | `feat(auth): 添加 JWT 刷新`(项目 scope 枚举无 auth) | 使用项目定义的 scope 值 |
91
+
92
+ ## 正确示例
93
+
94
+ ```
95
+ feat(eui): 添加 Image 组件,支持懒加载
96
+
97
+ fix: 修复 token 刷新竞态条件
98
+
99
+ refactor(backend): 提取分页逻辑为共享工具
100
+
101
+ chore: 升级 Vite 至 8.x
102
+
103
+ feat(eui)!: 重命名 EDialog open 属性为 v-model:open
104
+
105
+ BREAKING CHANGE: EDialog 不再接受 `visible` 属性,请改用 `v-model:open`。
106
+ ```
@@ -845,6 +845,29 @@ user = cache.get_user(user_id)
845
845
  async def create_user(body: UserCreate) -> UserResponse:
846
846
  """根据提交的信息创建新用户,邮箱不可重复。"""
847
847
  ...
848
+
849
+ ## 代码自检(写代码时强制执行)
850
+
851
+ **每次生成或修改 Python 代码后,必须逐条验证以下清单。任何一条未通过 → STOP,立即修正后再继续。**
852
+
853
+ | # | 检查项 | 违规即 STOP |
854
+ |---|--------|------------|
855
+ | 1 | 所有公开函数有参数 + 返回值类型标注 | ✓ |
856
+ | 2 | 无 `Any` 类型(除非有注释说明原因) | ✓ |
857
+ | 3 | 使用 `list[int]` / `str \| None` 等现代语法,不用 `typing.List` / `Optional` | ✓ |
858
+ | 4 | service 层不抛 `HTTPException`,只抛业务异常 | ✓ |
859
+ | 5 | 无裸 `except:` 或 `except Exception: pass` | ✓ |
860
+ | 6 | 路由函数只做参数接收 → 调用 service → 返回结果,无业务逻辑 | ✓ |
861
+ | 7 | HTTP 请求用 `httpx`,不用 `requests` | ✓ |
862
+ | 8 | 日志用 `%s` 占位符或 structlog,不用 f-string | ✓ |
863
+ | 9 | 生命周期用 `lifespan`,不用 `@app.on_event` | ✓ |
864
+ | 10 | 无可变默认参数 `def f(items=[])` | ✓ |
865
+ | 11 | Pydantic Schema 请求/响应分离,不复用 | ✓ |
866
+ | 12 | 嵌套不超过 3 层,函数体不超过 50 行 | ✓ |
867
+ | 13 | 所有模块/类/函数有 docstring(Google 风格) | ✓ |
868
+ | 14 | 依赖通过 `Depends()` 注入,不在路由中直接创建 | ✓ |
869
+
870
+ **不执行自检就提交代码 = 违规。**
848
871
  ```
849
872
 
850
873
  ## 十九、安全编码
@@ -713,3 +713,26 @@ import { UserInfo, getUsers } from '@/api/user'
713
713
  | `reactive()` 用于简单值 | `ref()` 用于所有场景 |
714
714
  | 解构 store 不用 `storeToRefs` | 响应式值必须用 `storeToRefs` |
715
715
  | 手写 CSS 为主 | Tailwind 工具类为主 |
716
+
717
+ ## 代码自检(写代码时强制执行)
718
+
719
+ **每次生成或修改 Vue/TypeScript 代码后,必须逐条验证以下清单。任何一条未通过 → STOP,立即修正后再继续。**
720
+
721
+ | # | 检查项 | 违规即 STOP |
722
+ |---|--------|------------|
723
+ | 1 | 使用 `<script setup lang="ts">`,不是 Options API | ✓ |
724
+ | 2 | Props 用 `defineProps<{}>()`,Emits 用 `defineEmits<{}>()` | ✓ |
725
+ | 3 | 无 `any` 类型(除非有注释说明原因) | ✓ |
726
+ | 4 | `ref()` 复杂类型有泛型标注(`ref<T>` 而非 `ref(null)`) | ✓ |
727
+ | 5 | Pinia 使用 Setup Store,不是 Options Store | ✓ |
728
+ | 6 | Store 解构用 `storeToRefs`(响应式值),方法直接解构 | ✓ |
729
+ | 7 | 类型导入用 `import type { ... }` | ✓ |
730
+ | 8 | 路由组件使用 `() => import()` 懒加载 | ✓ |
731
+ | 9 | API 调用通过 `api/` 模块,未在组件中直接 `axios.get` | ✓ |
732
+ | 10 | 布尔变量有 `is/has/can/should` 前缀 | ✓ |
733
+ | 11 | 样式以 Tailwind 为主,非必要不写 scoped CSS | ✓ |
734
+ | 12 | 无 `v-if` + `v-for` 同元素 | ✓ |
735
+ | 13 | 模板无复杂表达式,已提取为 `computed` | ✓ |
736
+ | 14 | 枚举用联合类型 `type X = 'a' | 'b'`,不用 `enum` | ✓ |
737
+
738
+ **不执行自检就提交代码 = 违规。**
@@ -10,6 +10,26 @@
10
10
  }
11
11
  ]
12
12
  }
13
+ ],
14
+ "PostToolUse": [
15
+ {
16
+ "matcher": "Edit",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "bash ~/.claude/hooks/post-sync-claude-global.sh"
21
+ }
22
+ ]
23
+ },
24
+ {
25
+ "matcher": "Write",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "bash ~/.claude/hooks/post-sync-claude-global.sh"
30
+ }
31
+ ]
32
+ }
13
33
  ]
14
34
  }
15
35
  }
@@ -0,0 +1,111 @@
1
+ ---
2
+ name: dwy-release
3
+ description: "标准化发版流程:bump version → changelog → build → publish → git tag。触发条件:用户说'发版'、'发布'、'release'、'bump version' 时。"
4
+ ---
5
+
6
+ # 标准化发版流程
7
+
8
+ 通用的发版流程框架,项目特定的包名、路径、命令由 CLAUDE.md 的 `## Release` 段落定义。
9
+
10
+ ## 触发条件
11
+
12
+ 用户要求发布包时("发版"、"发布"、"release"、"bump version")。
13
+
14
+ ## 第 0 步:检查项目 Release 配置
15
+
16
+ **在执行任何操作前**,检查当前项目的 CLAUDE.md 是否包含 `## Release` 段落:
17
+
18
+ - **有** → 读取其中的包列表、版本文件路径、构建命令、发布命令、依赖顺序等配置,按配置执行
19
+ - **没有** → **停止发版流程**,提醒用户:
20
+
21
+ > 「当前项目 CLAUDE.md 未定义 `## Release` 段落,无法执行发版流程。需要我帮你加吗?」
22
+
23
+ 参考格式(提供给用户):
24
+
25
+ ```markdown
26
+ ## Release
27
+
28
+ ### 包列表
29
+
30
+ | 包名 | scope | 版本文件 | 构建命令 | 发布命令 | 验证命令 |
31
+ |------|-------|---------|---------|---------|---------|
32
+ | @scope/pkg-a | pkg-a | packages/a/package.json | pnpm build:a | pnpm publish:a | npm view @scope/pkg-a version |
33
+
34
+ ### 依赖顺序
35
+
36
+ 多包发版时按此顺序执行:pkg-a → pkg-b → pkg-c
37
+
38
+ ### Tag 命名
39
+
40
+ - 单包:`@scope/pkg-a@1.0.0`
41
+ - 仅一个包时可简化为:`v1.0.0`
42
+
43
+ ### CHANGELOG
44
+
45
+ - 命令:`pnpm changelog`
46
+ - 工具:changelogen
47
+ ```
48
+
49
+ ## 发版前确认
50
+
51
+ 询问用户:
52
+
53
+ 1. **发哪个包?**(从 Release 配置的包列表中选,可多选)
54
+ 2. **版本号?** patch / minor / major,或指定具体版本号
55
+
56
+ ## 发版流程
57
+
58
+ ### 1. Bump Version
59
+
60
+ 根据 Release 配置中的版本文件路径,直接编辑 version 字段。
61
+
62
+ ```bash
63
+ # 读取当前版本(根据配置的版本文件)
64
+ # 计算新版本:patch=x.y.z+1, minor=x.y+1.0, major=x+1.0.0
65
+ ```
66
+
67
+ 不使用 `npm version`(避免自动 commit)。Python 项目编辑 `pyproject.toml` 中的 `version` 字段。
68
+
69
+ ### 2. Generate CHANGELOG
70
+
71
+ 执行 Release 配置中定义的 changelog 命令。检查生成内容,确认无误后继续。
72
+
73
+ ### 3. Build
74
+
75
+ 执行 Release 配置中对应包的构建命令。**构建必须成功才能继续。**
76
+
77
+ ### 4. Commit + Tag
78
+
79
+ ```bash
80
+ # 暂存变更(version + changelog)
81
+ git add <changed-files>
82
+
83
+ # 提交(遵循 git-commit-convention)
84
+ git commit -m "chore(<scope>): release v<version>"
85
+
86
+ # 打 tag(按 Release 配置的命名规则)
87
+ git tag <tag-name>
88
+
89
+ # 推送
90
+ git push origin <branch> --tags
91
+ ```
92
+
93
+ ### 5. Publish
94
+
95
+ 执行 Release 配置中对应包的发布命令。
96
+
97
+ ### 6. 发版后验证
98
+
99
+ 执行 Release 配置中对应包的验证命令,确认新版本已发布。
100
+
101
+ ## 多包同时发版
102
+
103
+ 按 Release 配置中定义的依赖顺序,每个包独立走完 bump → build → publish,最后统一 commit + tag + push。
104
+
105
+ ## 禁止事项
106
+
107
+ - **禁止**跳过 build 直接 publish
108
+ - **禁止**忘记打 tag
109
+ - **禁止**publish 失败后不回滚 version 变更
110
+ - **禁止**在 CHANGELOG 未生成时发版
111
+ - **禁止**在无 Release 配置的情况下猜测发版命令
@@ -1,52 +0,0 @@
1
- ---
2
- name: dwy-auto-sync
3
- description: "create-dwy 发版后自动更新本地 CLI 并同步 Claude Code 配置。触发条件:执行了 npm publish create-dwy、pnpm publish:cli、或用户要求同步 dwy 配置时。"
4
- ---
5
-
6
- # DWY 发版后自动同步
7
-
8
- 当 `create-dwy` 发布新版本后,必须执行以下步骤确保本地 Claude Code 配置与最新版本同步。
9
-
10
- ## 触发时机
11
-
12
- 以下任一情况发生时,执行同步流程:
13
-
14
- 1. 执行了 `npm publish`(在 claude-cli 目录下)
15
- 2. 执行了 `pnpm publish:cli`
16
- 3. 用户要求"同步 dwy"或"更新配置"
17
-
18
- ## 同步流程
19
-
20
- 发版成功后,按顺序执行:
21
-
22
- ```bash
23
- # 1. 更新全局 CLI 到最新版
24
- npm i -g create-dwy@latest
25
-
26
- # 2. 验证版本
27
- dwy --version
28
-
29
- # 3. 同步 Claude Code 配置(skills/rules/hooks/commands/settings)
30
- dwy sync claude
31
-
32
- # 4. 验证同步结果
33
- ls ~/.claude/skills/
34
- ```
35
-
36
- ## 同步了什么
37
-
38
- `dwy sync claude` 会将以下内容从 CLI 模板同步到 `~/.claude/`:
39
-
40
- | 目录 | 内容 | 同步方式 |
41
- |------|------|---------|
42
- | `~/.claude/skills/` | AI 速查文档 (dwy-frontend-eui, dwy-frontend-core, dwy-backend-base) | 覆盖同名,保留已有 |
43
- | `~/.claude/rules/` | 编码规范 (python-code-style, vue-code-style, 安全规则等) | 覆盖同名,保留已有 |
44
- | `~/.claude/hooks/` | 自动化脚本 (pre-git-commit 检查) | 覆盖同名,保留已有 |
45
- | `~/.claude/commands/` | 自定义命令 | 覆盖同名,保留已有 |
46
- | `~/.claude/settings.json` | Claude Code 设置 | 浅合并(新 key 加入,同名覆盖,已有独有 key 保留) |
47
-
48
- ## 注意事项
49
-
50
- - npm 注册表有传播延迟(~1 分钟),如果 `npm i -g create-dwy@latest` 安装的不是刚发布的版本,等待后重试
51
- - `dwy sync claude` 从 Gitee 仓库拉取模板,确保代码已 push 到 Gitee 后再执行
52
- - 同步不会删除用户手动添加的 skills/rules,只覆盖同名文件