create-dwy 0.5.1 → 0.6.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 (156) hide show
  1. package/bin/index.js +1 -1
  2. package/package.json +2 -2
  3. package/src/create.js +61 -37
  4. package/src/sync.js +212 -61
  5. package/templates/claude-global/CLAUDE.md +92 -0
  6. package/templates/claude-global/rules/dwy-android-core.md +610 -0
  7. package/templates/claude-global/rules/dwy-api-design.md +110 -0
  8. package/templates/claude-global/rules/dwy-db-migration.md +46 -0
  9. package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
  10. package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
  11. package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
  12. package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
  13. package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
  14. package/templates/claude-global/rules/dwy-git-security.md +53 -0
  15. package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
  16. package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
  17. package/templates/claude-global/rules/dwy-project-structure.md +181 -0
  18. package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +292 -358
  19. package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
  20. package/templates/claude-global/rules/dwy-python-orm.md +61 -0
  21. package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +99 -72
  22. package/templates/claude-global/rules/dwy-python-testing.md +53 -0
  23. package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
  24. package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
  25. package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
  26. package/templates/claude-global/rules/dwy-vue-api.md +146 -0
  27. package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +338 -152
  28. package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
  29. package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
  30. package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
  31. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
  32. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
  33. package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
  34. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
  35. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
  36. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
  37. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
  38. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
  39. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
  40. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
  41. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
  42. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
  43. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
  44. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
  45. package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
  46. package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
  47. package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
  48. package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
  49. package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
  50. package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
  51. package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
  52. package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
  53. package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
  54. package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
  55. package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
  56. package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
  57. package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
  58. package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
  59. package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
  60. package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
  61. package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
  62. package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
  63. package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
  64. package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
  65. package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
  66. package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
  67. package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
  68. package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
  69. package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
  70. package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
  71. package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
  72. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
  73. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
  74. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
  75. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
  76. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
  77. package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
  78. package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
  79. package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
  80. package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
  81. package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
  82. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
  83. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
  84. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
  85. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
  86. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
  87. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
  88. package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
  89. package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
  90. package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
  91. package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
  92. package/templates/claude-global/skills/{dwy-release → dwy-publish}/SKILL.md +42 -18
  93. package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
  94. package/templates/project/backend/.dwy.hbs +5 -0
  95. package/templates/project/backend/.python-version +1 -0
  96. package/templates/project/backend/CLAUDE.md.hbs +40 -0
  97. package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
  98. package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
  99. package/templates/project/backend/deploy.sh.hbs +18 -0
  100. package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
  101. package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
  102. package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
  103. package/templates/project/backend/src/app/database.py +10 -0
  104. package/templates/project/backend/src/app/dependencies.py +5 -0
  105. package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
  106. package/templates/project/backend/src/app/models/base.py +5 -0
  107. package/templates/project/web/CLAUDE.md.hbs +29 -36
  108. package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
  109. package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
  110. package/templates/project/web/apps/web/src/App.vue +9 -0
  111. package/templates/project/web/apps/web/src/api/client.ts +5 -0
  112. package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
  113. package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
  114. package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
  115. package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
  116. package/templates/project/web/apps/web/src/style.css +2 -0
  117. package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
  118. package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
  119. package/templates/project/web/apps/web/tsconfig.app.json +18 -0
  120. package/templates/project/web/apps/web/tsconfig.json +6 -0
  121. package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
  122. package/templates/project/web/package.json.hbs +16 -0
  123. package/templates/project/web/packages/.gitkeep +0 -0
  124. package/templates/project/web/pnpm-workspace.yaml +3 -0
  125. package/templates/claude-global/hooks/post-sync-claude-global.sh +0 -72
  126. package/templates/claude-global/rules/agent-team-execution.md +0 -138
  127. package/templates/claude-global/rules/agent-team-patterns.md +0 -157
  128. package/templates/claude-global/rules/git-commit-convention.md +0 -106
  129. package/templates/claude-global/rules/git-security.md +0 -144
  130. package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
  131. package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
  132. package/templates/project/web/.claude/commands/deploy.md +0 -4
  133. package/templates/project/web/backend/src/app/models/base.py +0 -5
  134. package/templates/project/web/deploy.sh.hbs +0 -113
  135. package/templates/project/web/frontend/src/App.vue +0 -3
  136. package/templates/project/web/frontend/src/api/client.ts +0 -13
  137. package/templates/project/web/frontend/src/style.css +0 -1
  138. package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
  139. package/templates/project/web/frontend/tsconfig.app.json +0 -14
  140. package/templates/project/web/frontend/tsconfig.json +0 -4
  141. /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
  142. /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
  143. /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
  144. /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
  145. /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
  146. /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
  147. /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
  148. /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
  149. /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
  150. /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
  151. /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
  152. /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
  153. /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
  154. /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
  155. /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
  156. /package/templates/project/web/{frontend → apps/web}/src/main.ts +0 -0
package/bin/index.js CHANGED
@@ -22,7 +22,7 @@ program
22
22
 
23
23
  program
24
24
  .command('sync <target>')
25
- .description('Sync configuration (claude | project-claude)')
25
+ .description('Sync Claude configuration (claude | project-claude)')
26
26
  .action(async (target) => {
27
27
  if (target === 'claude') {
28
28
  await syncClaude()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-dwy",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Project scaffolding and Claude Code config sync CLI",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  ],
14
14
  "keywords": ["cli", "scaffold", "fastapi", "vue"],
15
15
  "license": "MIT",
16
- "author": "danweiyuan",
16
+ "author": "dwydev",
17
17
  "dependencies": {
18
18
  "commander": "^12.0.0",
19
19
  "inquirer": "^9.0.0",
package/src/create.js CHANGED
@@ -6,7 +6,8 @@ import { renderTemplate, chalk, PACKAGE_ROOT } from './utils.js'
6
6
  export async function createProject(name) {
7
7
  console.log(chalk.blue('Creating a new project...\n'))
8
8
 
9
- const answers = await inquirer.prompt([
9
+ // Step 1: common questions
10
+ const common = await inquirer.prompt([
10
11
  {
11
12
  type: 'input',
12
13
  name: 'projectName',
@@ -25,43 +26,58 @@ export async function createProject(name) {
25
26
  name: 'template',
26
27
  message: '选择项目模板:',
27
28
  choices: [
28
- { name: 'web — Vue + FastAPI', value: 'web' },
29
- { name: 'mobile — 移动端 + FastAPI (预留)', value: 'mobile', disabled: '即将推出' },
30
- { name: 'backend — 纯 FastAPI 服务 (预留)', value: 'backend', disabled: '即将推出' },
29
+ { name: 'web — Vue 3 前端 (pnpm monorepo, eui + ekit + Tailwind)', value: 'web' },
30
+ { name: 'backend — FastAPI 后端 (eapi + PostgreSQL + Redis + Docker)', value: 'backend' },
31
+ { name: 'mobile — 移动端 (暂未实现)', value: 'mobile', disabled: '即将推出' },
31
32
  ],
32
33
  },
33
- {
34
- type: 'confirm',
35
- name: 'includeDolphindb',
36
- message: '需要 DolphinDB 吗?',
37
- default: false,
38
- },
39
- {
40
- type: 'input',
41
- name: 'sshAlias',
42
- message: 'SSH 部署别名:',
43
- default: (a) => `${a.projectName}-server`,
44
- },
45
- {
46
- type: 'input',
47
- name: 'devDbPort',
48
- message: '本地开发 DB 端口:',
49
- default: '5432',
50
- },
51
- {
52
- type: 'input',
53
- name: 'devRedisPort',
54
- message: '本地开发 Redis 端口:',
55
- default: '6379',
56
- },
57
- {
58
- type: 'input',
59
- name: 'backendPort',
60
- message: '生产 Backend 端口:',
61
- default: '8001',
62
- },
63
34
  ])
64
35
 
36
+ // Step 2: template-specific questions
37
+ let extra = {}
38
+
39
+ if (common.template === 'backend') {
40
+ extra = await inquirer.prompt([
41
+ {
42
+ type: 'input',
43
+ name: 'sshAlias',
44
+ message: 'SSH 部署别名:',
45
+ default: `${common.projectName}-server`,
46
+ },
47
+ {
48
+ type: 'input',
49
+ name: 'devDbPort',
50
+ message: '本地开发 DB 端口:',
51
+ default: '5432',
52
+ },
53
+ {
54
+ type: 'input',
55
+ name: 'devRedisPort',
56
+ message: '本地开发 Redis 端口:',
57
+ default: '6379',
58
+ },
59
+ {
60
+ type: 'input',
61
+ name: 'backendPort',
62
+ message: '生产 Backend 端口:',
63
+ default: '8001',
64
+ },
65
+ ])
66
+ }
67
+
68
+ if (common.template === 'web') {
69
+ extra = await inquirer.prompt([
70
+ {
71
+ type: 'input',
72
+ name: 'apiProxy',
73
+ message: 'API 代理地址 (后端):',
74
+ default: 'http://127.0.0.1:8000',
75
+ },
76
+ ])
77
+ }
78
+
79
+ const answers = { ...common, ...extra }
80
+
65
81
  const context = {
66
82
  ...answers,
67
83
  dbName: answers.projectName.replace(/-/g, '_'),
@@ -87,7 +103,15 @@ export async function createProject(name) {
87
103
  console.log(chalk.gray('Next steps:'))
88
104
  console.log(chalk.gray(` cd ${context.projectName}`))
89
105
  console.log(chalk.gray(' git init && git add . && git commit -m "init"'))
90
- console.log(chalk.gray(' docker compose -f docker-compose.dev.yml up -d'))
91
- console.log(chalk.gray(' cd backend && uv venv && uv pip install -e ".[dev]"'))
92
- console.log(chalk.gray(' cd frontend && pnpm install && pnpm dev'))
106
+
107
+ if (context.template === 'backend') {
108
+ console.log(chalk.gray(' docker compose -f docker-compose.dev.yml up -d'))
109
+ console.log(chalk.gray(' uv venv && uv pip install -e ".[dev]"'))
110
+ console.log(chalk.gray(' uvicorn app.main:app --reload'))
111
+ }
112
+
113
+ if (context.template === 'web') {
114
+ console.log(chalk.gray(' pnpm install'))
115
+ console.log(chalk.gray(' pnpm dev'))
116
+ }
93
117
  }
package/src/sync.js CHANGED
@@ -1,92 +1,243 @@
1
1
  import fs from 'fs-extra'
2
2
  import path from 'path'
3
- import { ensureRepoCache, copyDir, chalk } from './utils.js'
3
+ import inquirer from 'inquirer'
4
+ import { ensureRepoCache, copyDir, chalk, PACKAGE_ROOT } from './utils.js'
4
5
 
5
- export async function syncClaude() {
6
- console.log(chalk.blue('Syncing global Claude configuration...'))
6
+ /**
7
+ * 从 YAML frontmatter 中提取 description 字段
8
+ */
9
+ function extractDescription(content) {
10
+ const match = content.match(/^---\s*\n([\s\S]*?)\n---/)
11
+ if (!match) return ''
12
+ const frontmatter = match[1]
13
+ const descMatch = frontmatter.match(/description:\s*(.+)/)
14
+ return descMatch ? descMatch[1].trim().replace(/^["']|["']$/g, '') : ''
15
+ }
7
16
 
8
- const repoDir = await ensureRepoCache()
9
- const sourceDir = path.join(repoDir, 'claude-cli', 'templates', 'claude-global')
10
- const targetDir = path.join(process.env.HOME, '.claude')
17
+ /**
18
+ * 扫描 skills 目录,返回 { name, description, sourcePath } 列表
19
+ */
20
+ async function scanSkills(sourceDir) {
21
+ const skillsDir = path.join(sourceDir, 'skills')
22
+ if (!await fs.pathExists(skillsDir)) return []
11
23
 
12
- if (!await fs.pathExists(sourceDir)) {
13
- console.error(chalk.red('Error: claude-global templates not found in repo'))
14
- process.exit(1)
24
+ const entries = await fs.readdir(skillsDir, { withFileTypes: true })
25
+ const skills = []
26
+
27
+ for (const entry of entries) {
28
+ if (!entry.isDirectory()) continue
29
+ const skillPath = path.join(skillsDir, entry.name)
30
+ const skillMd = path.join(skillPath, 'SKILL.md')
31
+ if (!await fs.pathExists(skillMd)) continue
32
+
33
+ const content = await fs.readFile(skillMd, 'utf-8')
34
+ const description = extractDescription(content)
35
+ skills.push({
36
+ name: entry.name,
37
+ description: description || '(无描述)',
38
+ sourcePath: skillPath,
39
+ type: 'skill',
40
+ })
15
41
  }
16
42
 
17
- const subdirs = ['rules', 'skills', 'commands', 'hooks']
18
- let syncedCount = 0
43
+ return skills.sort((a, b) => a.name.localeCompare(b.name))
44
+ }
19
45
 
20
- for (const subdir of subdirs) {
21
- const src = path.join(sourceDir, subdir)
22
- if (await fs.pathExists(src)) {
23
- const dest = path.join(targetDir, subdir)
24
- await copyDir(src, dest)
25
- const files = await fs.readdir(src).catch(() => [])
26
- syncedCount += files.length
27
- console.log(chalk.green(` ✓ ${subdir}/ — ${files.length} files`))
28
- }
46
+ /**
47
+ * 扫描 rules 目录,返回 { name, description, sourcePath } 列表
48
+ */
49
+ async function scanRules(sourceDir) {
50
+ const rulesDir = path.join(sourceDir, 'rules')
51
+ if (!await fs.pathExists(rulesDir)) return []
52
+
53
+ const entries = await fs.readdir(rulesDir, { withFileTypes: true })
54
+ const rules = []
55
+
56
+ for (const entry of entries) {
57
+ if (!entry.isFile() || !entry.name.endsWith('.md')) continue
58
+ const rulePath = path.join(rulesDir, entry.name)
59
+ const content = await fs.readFile(rulePath, 'utf-8')
60
+ const description = extractDescription(content)
61
+ rules.push({
62
+ name: entry.name.replace(/\.md$/, ''),
63
+ description: description || '(无描述)',
64
+ sourcePath: rulePath,
65
+ type: 'rule',
66
+ })
29
67
  }
30
68
 
31
- // Sync CLAUDE.md
32
- const claudeMdSrc = path.join(sourceDir, 'CLAUDE.md')
33
- if (await fs.pathExists(claudeMdSrc)) {
34
- await fs.copy(claudeMdSrc, path.join(targetDir, 'CLAUDE.md'))
69
+ return rules.sort((a, b) => a.name.localeCompare(b.name))
70
+ }
71
+
72
+ /**
73
+ * 扫描 commands 目录
74
+ */
75
+ async function scanCommands(sourceDir) {
76
+ const commandsDir = path.join(sourceDir, 'commands')
77
+ if (!await fs.pathExists(commandsDir)) return []
78
+
79
+ const entries = await fs.readdir(commandsDir, { withFileTypes: true })
80
+ const commands = []
81
+
82
+ for (const entry of entries) {
83
+ if (entry.name === '.gitkeep') continue
84
+ commands.push({
85
+ name: entry.name,
86
+ description: entry.isDirectory() ? '命令目录' : '命令文件',
87
+ sourcePath: path.join(commandsDir, entry.name),
88
+ type: 'command',
89
+ })
90
+ }
91
+
92
+ return commands.sort((a, b) => a.name.localeCompare(b.name))
93
+ }
94
+
95
+ /**
96
+ * 用 inquirer 做交互式多选
97
+ */
98
+ async function promptSelection(items, category) {
99
+ if (items.length === 0) return []
100
+
101
+ const { selected } = await inquirer.prompt([
102
+ {
103
+ type: 'checkbox',
104
+ name: 'selected',
105
+ message: `选择要同步的 ${category}(空格勾选,回车确认):`,
106
+ choices: items.map(item => ({
107
+ name: `${chalk.cyan(item.name)} ${chalk.gray('- ' + item.description.slice(0, 60))}${item.description.length > 60 ? '...' : ''}`,
108
+ value: item,
109
+ short: item.name,
110
+ })),
111
+ pageSize: 15,
112
+ },
113
+ ])
114
+
115
+ return selected
116
+ }
117
+
118
+ /**
119
+ * 同步选中的文件到目标目录
120
+ */
121
+ async function syncSelectedItems(items, targetDir) {
122
+ let syncedCount = 0
123
+
124
+ for (const item of items) {
125
+ const dest = path.join(targetDir, item.type + 's', item.name)
126
+ await fs.ensureDir(path.dirname(dest))
127
+ await fs.copy(item.sourcePath, dest, { overwrite: true })
35
128
  syncedCount++
36
- console.log(chalk.green(' ✓ CLAUDE.md'))
129
+ console.log(chalk.green(` ✓ ${item.type}s/${item.name}`))
37
130
  }
38
131
 
132
+ return syncedCount
133
+ }
134
+
135
+ /**
136
+ * 同步 settings.json(智能合并)
137
+ */
138
+ async function syncSettings(sourceDir, targetDir) {
39
139
  const settingsSrc = path.join(sourceDir, 'settings.json')
40
- if (await fs.pathExists(settingsSrc)) {
41
- const settingsDest = path.join(targetDir, 'settings.json')
42
- const newSettings = await fs.readJson(settingsSrc)
43
-
44
- if (await fs.pathExists(settingsDest)) {
45
- const existing = await fs.readJson(settingsDest)
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
- }
140
+ if (!await fs.pathExists(settingsSrc)) return 0
141
+
142
+ const settingsDest = path.join(targetDir, 'settings.json')
143
+ const newSettings = await fs.readJson(settingsSrc)
144
+
145
+ if (await fs.pathExists(settingsDest)) {
146
+ const existing = await fs.readJson(settingsDest)
147
+ const merged = { ...existing }
148
+ for (const [key, value] of Object.entries(newSettings)) {
149
+ if (key in merged && typeof merged[key] === 'object' && !Array.isArray(merged[key])
150
+ && typeof value === 'object' && !Array.isArray(value)) {
151
+ merged[key] = { ...merged[key], ...value }
152
+ } else {
153
+ merged[key] = value
55
154
  }
56
- await fs.writeJson(settingsDest, merged, { spaces: 2 })
57
- console.log(chalk.green(' ✓ settings.json — merged'))
58
- } else {
59
- await fs.writeJson(settingsDest, newSettings, { spaces: 2 })
60
- console.log(chalk.green(' ✓ settings.json — created'))
61
155
  }
62
- syncedCount++
156
+ await fs.writeJson(settingsDest, merged, { spaces: 2 })
157
+ console.log(chalk.green(' ✓ settings.json — merged'))
158
+ } else {
159
+ await fs.writeJson(settingsDest, newSettings, { spaces: 2 })
160
+ console.log(chalk.green(' ✓ settings.json — created'))
63
161
  }
64
162
 
65
- console.log(chalk.blue(`\nDone! Synced ${syncedCount} items to ~/.claude/`))
163
+ return 1
66
164
  }
67
165
 
68
- export async function syncProjectClaude() {
69
- console.log(chalk.blue('Syncing project Claude configuration...'))
166
+ /**
167
+ * 同步 CLAUDE.md
168
+ */
169
+ async function syncClaudeMd(sourceDir, targetDir) {
170
+ const claudeMdSrc = path.join(sourceDir, 'CLAUDE.md')
171
+ if (!await fs.pathExists(claudeMdSrc)) return 0
70
172
 
71
- const dwyFile = path.join(process.cwd(), '.dwy')
72
- if (!await fs.pathExists(dwyFile)) {
73
- console.error(chalk.red('Error: .dwy marker file not found. Is this a dwy project?'))
74
- console.error(chalk.gray('Run "dwy create" to create a new project, or create .dwy manually.'))
75
- process.exit(1)
173
+ await fs.copy(claudeMdSrc, path.join(targetDir, 'CLAUDE.md'), { overwrite: true })
174
+ console.log(chalk.green(' ✓ CLAUDE.md'))
175
+ return 1
176
+ }
177
+
178
+ /**
179
+ * 检测本地模板目录:如果当前是 dwy-shared monorepo,直接用本地模板
180
+ */
181
+ async function resolveSourceDir() {
182
+ const localTemplate = path.join(process.cwd(), 'claude-cli', 'templates', 'claude-global')
183
+ if (await fs.pathExists(localTemplate)) {
184
+ console.log(chalk.gray('Using local templates from current project...'))
185
+ return localTemplate
76
186
  }
187
+ const repoDir = await ensureRepoCache()
188
+ return path.join(repoDir, 'claude-cli', 'templates', 'claude-global')
189
+ }
77
190
 
78
- const marker = await fs.readJson(dwyFile)
79
- const template = marker.template || 'web'
191
+ /**
192
+ * 主同步函数:交互式选择 + 同步到当前目录 .claude/
193
+ */
194
+ export async function syncClaude() {
195
+ console.log(chalk.blue('\nScanning available Claude configuration...\n'))
80
196
 
81
- const repoDir = await ensureRepoCache()
82
- const sourceDir = path.join(repoDir, 'claude-cli', 'templates', 'project', template, '.claude')
197
+ const sourceDir = await resolveSourceDir()
83
198
  const targetDir = path.join(process.cwd(), '.claude')
84
199
 
85
200
  if (!await fs.pathExists(sourceDir)) {
86
- console.error(chalk.red(`Error: template "${template}" not found in repo`))
201
+ console.error(chalk.red('Error: claude-global templates not found in repo'))
87
202
  process.exit(1)
88
203
  }
89
204
 
90
- await copyDir(sourceDir, targetDir)
91
- console.log(chalk.blue('Done! Synced .claude/ for current project.'))
205
+ // 扫描可用资源
206
+ const skills = await scanSkills(sourceDir)
207
+ const rules = await scanRules(sourceDir)
208
+ const commands = await scanCommands(sourceDir)
209
+
210
+ console.log(chalk.yellow(`Found ${skills.length} skills, ${rules.length} rules, ${commands.length} commands\n`))
211
+
212
+ // 交互式选择
213
+ const selectedSkills = await promptSelection(skills, 'Skills')
214
+ const selectedRules = await promptSelection(rules, 'Rules')
215
+ const selectedCommands = await promptSelection(commands, 'Commands')
216
+
217
+ const totalSelected = selectedSkills.length + selectedRules.length + selectedCommands.length
218
+ if (totalSelected === 0) {
219
+ console.log(chalk.yellow('\nNo items selected. Nothing to sync.'))
220
+ return
221
+ }
222
+
223
+ console.log(chalk.blue(`\nSyncing ${totalSelected} items to ${targetDir}...\n`))
224
+
225
+ await fs.ensureDir(targetDir)
226
+
227
+ let syncedCount = 0
228
+ syncedCount += await syncSelectedItems(selectedSkills, targetDir)
229
+ syncedCount += await syncSelectedItems(selectedRules, targetDir)
230
+ syncedCount += await syncSelectedItems(selectedCommands, targetDir)
231
+ syncedCount += await syncClaudeMd(sourceDir, targetDir)
232
+ syncedCount += await syncSettings(sourceDir, targetDir)
233
+
234
+ console.log(chalk.blue(`\nDone! Synced ${syncedCount} items to ${targetDir}`))
235
+ }
236
+
237
+ /**
238
+ * 保留旧的 project-claude 同步逻辑
239
+ */
240
+ export async function syncProjectClaude() {
241
+ console.log(chalk.yellow('"dwy sync project-claude" is deprecated. Use "dwy sync claude" instead.'))
242
+ await syncClaude()
92
243
  }
@@ -1 +1,93 @@
1
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
+ ## Agent 团队执行
10
+
11
+ 所有任务默认启用 agent team 模式执行:任务可拆分时优先并行派遣 Agent,具体派遣策略、并行度、worktree 隔离、review 闭环等由 Claude 自行判断,不在此处硬编码规则。
12
+
13
+ ## Behavioral Guidelines — 减少常见 LLM 编码错误的行为指南
14
+
15
+ 与项目特定指令合并使用。
16
+
17
+ 权衡:这些指南偏向谨慎而非速度。对于琐碎任务,自行判断。
18
+
19
+ ### 1. 先思考再编码(Think Before Coding)
20
+
21
+ 不要假设。不要隐藏困惑。把权衡摊开。
22
+
23
+ 在实现之前:
24
+ - 明确陈述你的假设。如果不确定,就问。
25
+ - 如果存在多种解读,提出来——不要默默选择。
26
+ - 如果有更简单的方案,说出来。在必要时提出异议。
27
+ - 如果有什么不清晰,停下来。说出困惑是什么。然后提问。
28
+
29
+ ### 2. 简单优先(Simplicity First)
30
+
31
+ 解决问题的最小代码。没有推测性代码。
32
+
33
+ - 不做需求之外的功能。
34
+ - 不为一次性使用的代码做抽象。
35
+ - 不做未被要求的"灵活性"或"可配置性"。
36
+ - 不为不可能发生的场景做错误处理。
37
+ - 如果你写了 200 行但可以写成 50 行,重写它。
38
+ - 问自己:"一个资深工程师会说这过于复杂吗?"如果是,简化。
39
+
40
+ ### 3. 精准改动(Surgical Changes)
41
+
42
+ 只动必须动的代码。只清理自己造成的混乱。
43
+
44
+ 在编辑现有代码时:
45
+ - 不要"改进"相邻的代码、注释或格式。
46
+ - 不要重构没有问题的东西。
47
+ - 匹配现有的风格,即使你会用不同的写法。
48
+ - 如果发现不相关的死代码,提一下——但不要删除。
49
+
50
+ 当你的改动产生孤儿代码时:
51
+ - 移除你的改动导致的未使用的 import/变量/函数。
52
+ - 除非被要求,不要移除已有的死代码。
53
+
54
+ 检验标准:每一行改动的代码都应能直接追溯到用户的需求。
55
+
56
+ ### 4. 目标驱动执行(Goal-Driven Execution)
57
+
58
+ 定义成功标准。循环直到验证通过。
59
+
60
+ 将任务转化为可验证的目标:
61
+ - "添加校验" → "编写无效输入的测试,然后让它们通过"
62
+ - "修复 bug" → "编写重现 bug 的测试,然后让它通过"
63
+ - "重构 X" → "确保测试在重构前后都通过"
64
+
65
+ 对于多步骤任务,陈述一个简要计划:
66
+ 1. [步骤] → 验证:[检查项]
67
+ 2. [步骤] → 验证:[检查项]
68
+ 3. [步骤] → 验证:[检查项]
69
+
70
+ 强成功标准让你能独立循环。弱标准("让它工作")需要不断澄清。
71
+
72
+ 这些指南在生效的标志是:diff 中不必要的改动变少,因过度复杂而重写的情况减少,澄清性问题出现在实现之前而非错误之后。
73
+
74
+ ## 输出精简规则
75
+
76
+ 输出保持精简,去掉冗余,保留技术精确性。
77
+
78
+ ### 核心规则
79
+
80
+ - **去掉填充词**:不用"了/的/吧/吗/呢"等语气助词,不用"我认为/可能/也许/似乎"等模糊词
81
+ - **不用客套话**:不用"你好/请/谢谢/不客气"等社交用语,直入主题
82
+ - **允许碎片句**:不用强制完整主谓宾,短语表达即可
83
+ - **短优先**:能用短词就不用长词,能用一句话就不用两句话
84
+ - **技术术语精确**:变量名、函数名、错误信息、代码块等保持原样,不缩写
85
+ - **代码块完整**:代码必须完整可运行,不受精简规则影响
86
+
87
+ ### 例外场景(恢复完整语气)
88
+
89
+ 以下场景不得精简,必须清晰明确:
90
+ - 安全警告和风险提示
91
+ - 不可逆操作确认(删除、强制推送等)
92
+ - 需要用户决策的复杂多步骤流程
93
+ - 用户明确要求详细解释时