create-dwy 0.5.0 → 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.
- package/bin/index.js +1 -1
- package/package.json +2 -2
- package/src/create.js +61 -37
- package/src/sync.js +213 -53
- package/templates/claude-global/CLAUDE.md +92 -0
- package/templates/claude-global/rules/dwy-android-core.md +610 -0
- package/templates/claude-global/rules/dwy-api-design.md +110 -0
- package/templates/claude-global/rules/dwy-db-migration.md +46 -0
- package/templates/claude-global/rules/dwy-flutter-core.md +564 -0
- package/templates/claude-global/rules/dwy-flutter-network.md +222 -0
- package/templates/claude-global/rules/dwy-flutter-router.md +97 -0
- package/templates/claude-global/rules/dwy-flutter-testing.md +94 -0
- package/templates/claude-global/rules/dwy-git-commit-convention.md +82 -0
- package/templates/claude-global/rules/dwy-git-security.md +53 -0
- package/templates/claude-global/rules/dwy-payload-limits.md +178 -0
- package/templates/claude-global/rules/{prefer-open-source.md → dwy-prefer-open-source.md} +4 -0
- package/templates/claude-global/rules/dwy-project-structure.md +181 -0
- package/templates/claude-global/rules/{python-code-style.md → dwy-python-core.md} +314 -357
- package/templates/claude-global/rules/dwy-python-fastapi.md +272 -0
- package/templates/claude-global/rules/dwy-python-orm.md +61 -0
- package/templates/claude-global/rules/{backend-security.md → dwy-python-security.md} +116 -79
- package/templates/claude-global/rules/dwy-python-testing.md +53 -0
- package/templates/claude-global/rules/{server-security.md → dwy-server-security.md} +1 -2
- package/templates/claude-global/rules/dwy-tdd-development.md +70 -0
- package/templates/claude-global/rules/dwy-tech-stack.md +60 -0
- package/templates/claude-global/rules/dwy-vue-api.md +146 -0
- package/templates/claude-global/rules/{vue-code-style.md → dwy-vue-core.md} +361 -152
- package/templates/claude-global/rules/dwy-vue-pinia.md +75 -0
- package/templates/claude-global/rules/dwy-vue-testing.md +102 -0
- package/templates/claude-global/settings.json +20 -0
- package/templates/claude-global/skills/dwy-deploy-audit/SKILL.md +481 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-data.md +117 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-network.md +74 -0
- package/templates/claude-global/skills/dwy-deploy-audit/references/checks-runtime.md +184 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_base.sh +39 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_capacity.sh +278 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_db.sh +122 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_docker.sh +249 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_env.sh +64 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_https.sh +60 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_logs.sh +209 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_nginx.sh +92 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_resilience.sh +157 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_secrets.sh +327 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_services.sh +77 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/check_ssh.sh +56 -0
- package/templates/claude-global/skills/dwy-deploy-audit/scripts/run_all.sh +105 -0
- package/templates/claude-global/skills/dwy-docker/SKILL.md +187 -0
- package/templates/claude-global/skills/dwy-docker/references/ask-templates.md +142 -0
- package/templates/claude-global/skills/dwy-docker/references/templates.md +479 -0
- package/templates/claude-global/skills/dwy-docker/references/version-rules.md +76 -0
- package/templates/claude-global/skills/dwy-docker/scripts/query_dockerhub.py +204 -0
- package/templates/claude-global/skills/dwy-docker/scripts/scan_images.sh +185 -0
- package/templates/claude-global/skills/dwy-eapi/SKILL.md +433 -0
- package/templates/claude-global/skills/dwy-eapi/references/tasks-integration-guide.md +387 -0
- package/templates/claude-global/skills/dwy-ekit/SKILL.md +85 -0
- package/templates/claude-global/skills/dwy-ekit/references/data.md +135 -0
- package/templates/claude-global/skills/dwy-ekit/references/form.md +68 -0
- package/templates/claude-global/skills/dwy-ekit/references/request.md +183 -0
- package/templates/claude-global/skills/dwy-ekit/references/ui.md +109 -0
- package/templates/claude-global/skills/{dwy-frontend-eui → dwy-eui}/SKILL.md +129 -15
- package/templates/claude-global/skills/dwy-eui/references/eui-design-guide.md +257 -0
- package/templates/claude-global/skills/dwy-eui/references/eui-integration-guide.md +302 -0
- package/templates/claude-global/skills/dwy-eui/references/eui-landing-design-guide.md +437 -0
- package/templates/claude-global/skills/dwy-fullstack-scaffold/SKILL.md +214 -0
- package/templates/claude-global/skills/dwy-mirror-source/SKILL.md +275 -0
- package/templates/claude-global/skills/dwy-mirror-source/references/dockerfile-snippets.md +228 -0
- package/templates/claude-global/skills/dwy-mirror-source/references/mirror-providers.md +150 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/__pycache__/check_mirrors.cpython-313.pyc +0 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/apply_mirrors.py +666 -0
- package/templates/claude-global/skills/dwy-mirror-source/scripts/check_mirrors.py +868 -0
- package/templates/claude-global/skills/dwy-pentest/SKILL.md +344 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__init__.py +1 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/__init__.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/config.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/executor.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/severity.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/__pycache__/validator.cpython-311.pyc +0 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/config.py +109 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/executor.py +157 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/severity.py +96 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/lib/validator.py +113 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_auth.py +289 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_recon.py +217 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_report.py +343 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_scan.py +205 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_ssl.py +208 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_vuln.py +205 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pentest_web.py +330 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/pyproject.toml +14 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/setup_tools.sh +97 -0
- package/templates/claude-global/skills/dwy-pentest/scripts/uv.lock +415 -0
- package/templates/claude-global/skills/dwy-publish/SKILL.md +135 -0
- package/templates/claude-global/skills/dwy-vue-core/SKILL.md +890 -0
- package/templates/project/backend/.dwy.hbs +5 -0
- package/templates/project/backend/.python-version +1 -0
- package/templates/project/backend/CLAUDE.md.hbs +40 -0
- package/templates/project/{web/backend → backend}/alembic/script.py.mako +2 -0
- package/templates/project/{web/backend → backend}/alembic.ini.hbs +0 -1
- package/templates/project/backend/deploy.sh.hbs +18 -0
- package/templates/project/{web → backend}/docker-compose.prod.yml.hbs +1 -1
- package/templates/project/{web/backend → backend}/pyproject.toml.hbs +4 -5
- package/templates/project/{web/backend → backend}/src/app/config.py.hbs +1 -1
- package/templates/project/backend/src/app/database.py +10 -0
- package/templates/project/backend/src/app/dependencies.py +5 -0
- package/templates/project/{web/backend → backend}/src/app/main.py.hbs +3 -3
- package/templates/project/backend/src/app/models/base.py +5 -0
- package/templates/project/web/CLAUDE.md.hbs +29 -36
- package/templates/project/web/{frontend → apps/web}/index.html.hbs +1 -0
- package/templates/project/web/{frontend → apps/web}/package.json.hbs +4 -3
- package/templates/project/web/apps/web/src/App.vue +9 -0
- package/templates/project/web/apps/web/src/api/client.ts +5 -0
- package/templates/project/web/apps/web/src/composables/.gitkeep +0 -0
- package/templates/project/web/apps/web/src/layouts/DefaultLayout.vue +8 -0
- package/templates/project/web/{frontend → apps/web}/src/router/index.ts +1 -1
- package/templates/project/web/{frontend → apps/web}/src/stores/auth.ts +4 -2
- package/templates/project/web/apps/web/src/style.css +2 -0
- package/templates/project/web/apps/web/src/types/router.d.ts +8 -0
- package/templates/project/web/apps/web/src/views/HomeView.vue +8 -0
- package/templates/project/web/apps/web/tsconfig.app.json +18 -0
- package/templates/project/web/apps/web/tsconfig.json +6 -0
- package/templates/project/web/{frontend → apps/web}/vite.config.ts.hbs +7 -1
- package/templates/project/web/package.json.hbs +16 -0
- package/templates/project/web/packages/.gitkeep +0 -0
- package/templates/project/web/pnpm-workspace.yaml +3 -0
- package/templates/claude-global/rules/agent-team-execution.md +0 -138
- package/templates/claude-global/rules/agent-team-patterns.md +0 -157
- package/templates/claude-global/rules/git-security.md +0 -144
- package/templates/claude-global/skills/dwy-auto-sync/SKILL.md +0 -52
- package/templates/claude-global/skills/dwy-backend-base/SKILL.md +0 -279
- package/templates/claude-global/skills/dwy-frontend-core/SKILL.md +0 -181
- package/templates/project/web/.claude/commands/deploy.md +0 -4
- package/templates/project/web/backend/src/app/models/base.py +0 -5
- package/templates/project/web/deploy.sh.hbs +0 -113
- package/templates/project/web/frontend/src/App.vue +0 -3
- package/templates/project/web/frontend/src/api/client.ts +0 -13
- package/templates/project/web/frontend/src/style.css +0 -1
- package/templates/project/web/frontend/src/views/HomeView.vue +0 -5
- package/templates/project/web/frontend/tsconfig.app.json +0 -14
- package/templates/project/web/frontend/tsconfig.json +0 -4
- /package/templates/{project/web/backend → claude-global/skills/dwy-pentest/scripts}/.python-version +0 -0
- /package/templates/project/{web/backend → backend}/Dockerfile +0 -0
- /package/templates/project/{web/backend → backend}/alembic/env.py.hbs +0 -0
- /package/templates/{claude-global/skills → project/backend/alembic/versions}/.gitkeep +0 -0
- /package/templates/project/{web → backend}/docker-compose.dev.yml.hbs +0 -0
- /package/templates/project/{web/backend → backend}/src/app/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/models/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/routers/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/routers/health.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/schemas/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/src/app/services/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/tests/__init__.py +0 -0
- /package/templates/project/{web/backend → backend}/tests/conftest.py.hbs +0 -0
- /package/templates/project/{web/backend/alembic/versions → mobile}/.gitkeep +0 -0
- /package/templates/project/web/{frontend → apps/web}/src/components/.gitkeep +0 -0
- /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.
|
|
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": "
|
|
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
|
-
|
|
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
|
|
29
|
-
{ name: '
|
|
30
|
-
{ name: '
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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,83 +1,243 @@
|
|
|
1
1
|
import fs from 'fs-extra'
|
|
2
2
|
import path from 'path'
|
|
3
|
-
import
|
|
3
|
+
import inquirer from 'inquirer'
|
|
4
|
+
import { ensureRepoCache, copyDir, chalk, PACKAGE_ROOT } from './utils.js'
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
18
|
-
|
|
43
|
+
return skills.sort((a, b) => a.name.localeCompare(b.name))
|
|
44
|
+
}
|
|
19
45
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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(
|
|
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
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
|
154
|
+
}
|
|
52
155
|
}
|
|
53
|
-
|
|
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'))
|
|
54
161
|
}
|
|
55
162
|
|
|
56
|
-
|
|
163
|
+
return 1
|
|
57
164
|
}
|
|
58
165
|
|
|
59
|
-
|
|
60
|
-
|
|
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
|
|
61
172
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
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
|
|
67
186
|
}
|
|
187
|
+
const repoDir = await ensureRepoCache()
|
|
188
|
+
return path.join(repoDir, 'claude-cli', 'templates', 'claude-global')
|
|
189
|
+
}
|
|
68
190
|
|
|
69
|
-
|
|
70
|
-
|
|
191
|
+
/**
|
|
192
|
+
* 主同步函数:交互式选择 + 同步到当前目录 .claude/
|
|
193
|
+
*/
|
|
194
|
+
export async function syncClaude() {
|
|
195
|
+
console.log(chalk.blue('\nScanning available Claude configuration...\n'))
|
|
71
196
|
|
|
72
|
-
const
|
|
73
|
-
const sourceDir = path.join(repoDir, 'claude-cli', 'templates', 'project', template, '.claude')
|
|
197
|
+
const sourceDir = await resolveSourceDir()
|
|
74
198
|
const targetDir = path.join(process.cwd(), '.claude')
|
|
75
199
|
|
|
76
200
|
if (!await fs.pathExists(sourceDir)) {
|
|
77
|
-
console.error(chalk.red(
|
|
201
|
+
console.error(chalk.red('Error: claude-global templates not found in repo'))
|
|
78
202
|
process.exit(1)
|
|
79
203
|
}
|
|
80
204
|
|
|
81
|
-
|
|
82
|
-
|
|
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()
|
|
83
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
|
+
- 用户明确要求详细解释时
|