create-dwy 0.16.1 → 0.17.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/CHANGELOG.md +17 -0
- package/bin/index.js +23 -19
- package/package.json +1 -1
- package/src/cli-help.js +44 -0
- package/src/sync-all.js +303 -59
- package/src/sync-global-skills.js +167 -0
- package/src/sync.js +21 -11
- package/templates/ai-tools/CHANGELOG.md +134 -0
- package/templates/ai-tools/VERSION +1 -1
- package/templates/ai-tools/rules/Android/dwy-android-core.md +65 -1
- package/templates/ai-tools/rules/Python/dwy-python-core.md +47 -0
- package/templates/ai-tools/rules/Vue/dwy-vue-core.md +55 -0
- package/templates/ai-tools/rules/iOS/dwy-swift-core.md +46 -0
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-oss-commercial-license.md +128 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +65 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/assets/appstore-submission.yaml +43 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/api-workflow.md +17 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/in-app-purchase-review.md +16 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/screenshot-workflow.md +16 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/scripts/appstore_config.sh +140 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/SKILL.md +11 -8
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/android.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/backend-deploy.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/changelog.md +24 -4
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/frontend-deploy.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/generic-release.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/git-tag.md +82 -0
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/harmony.md +2 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/ios.md +3 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-publish/references/monitor-notify.md +2 -2
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-semver/SKILL.md +1 -1
- package/templates/ai-tools/skills//345/237/272/347/241/200/345/272/223/dwy-eapi/references/tasks-integration-guide.md +2 -2
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/README.md +57 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/SKILL.md +147 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/api.md +43 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/references/config.example.yaml +33 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/global_config.py +89 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/setup_config.py +175 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/dwy-doubao-tts/scripts/synthesize.py +339 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/SKILL.md +127 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/assets/cover-style-master-v1.png +0 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-copy-examples.md +37 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/media-platform-packaging/references/platform-posting-times.md +193 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0005-ideation/SKILL.md +42 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0005-ideation/templates/topic-backlog.template.md +50 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0005-ideation/templates//351/200/211/351/242/230/346/261/240.template.md +16 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0010-product/SKILL.md +106 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0010-product/templates/channel-profile.template.md +52 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0010-product/templates//344/272/247/345/223/201/345/215/241.template.md +132 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/SKILL.md +60 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/scripts/build_facts_html.py +430 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/scripts/generate_html.py +1287 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/scripts/generate_report.py +189 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0030-facts/templates//344/272/213/345/256/236/350/265/204/346/226/231/345/272/223.template.md +55 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/SKILL.md +36 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates/README.md +10 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//344/272/213/345/256/236.template.md +82 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//345/215/225/351/233/206/346/226/207/346/241/243.template.md +175 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//350/204/232/346/234/254.template.md +69 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0040-script/templates//351/252/250/346/236/266-v1.1.md +174 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0050-design/SKILL.md +55 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0060-assets/SKILL.md +29 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package/SKILL.md +42 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package/scripts/validate_shots.py +207 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package/shots.schema.json +186 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package//344/272/272/345/256/241/345/214/205.template.md +1 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package//344/272/272/345/256/241/350/257/264/346/230/216.md +15 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0070-package//346/210/220/347/211/207/345/214/205.template.md +49 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-0080-factory/SKILL.md +58 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-comic-kit/SKILL.md +424 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-comic-kit/evals.md +41 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-flow/CONTRACT.md +123 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-flow/SKILL.md +81 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-flow//346/211/247/350/241/214/346/270/205/345/215/225.template.md +60 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/SKILL.md +78 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/agents/openai.yaml +4 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/knowledge-town-paper-film-v1.json +80 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/AudioTracks.tsx +20 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/CaptionLayer.tsx +18 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/CueTimeline.ts +24 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/assets/remotion/KnowledgeTownComposition.tsx +21 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/references/cinematic-direction.md +73 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/references/sticker-asset-production.md +30 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/references/style-guide.md +43 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/scripts/build_cue_timeline.py +75 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/scripts/validate_cue_timeline.py +68 -0
- package/templates/ai-tools/skills//350/207/252/345/252/222/344/275/223/uc-media-knowledge-town/scripts/validate_style_preset.py +22 -0
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/SKILL.md +0 -511
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-data.md +0 -126
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-network.md +0 -88
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/checks-runtime.md +0 -195
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/references/security-doc-search.md +0 -179
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_base.sh +0 -39
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_capacity.sh +0 -278
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_db.sh +0 -147
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_docker.sh +0 -230
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_env.sh +0 -64
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_https.sh +0 -60
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_logs.sh +0 -209
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_nginx.sh +0 -100
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_resilience.sh +0 -157
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_secrets.sh +0 -265
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_services.sh +0 -94
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/check_ssh.sh +0 -56
- package/templates/ai-tools/skills/unitcircle/uc-deploy-audit/scripts/run_all.sh +0 -105
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# create-dwy
|
|
2
2
|
|
|
3
|
+
## 0.17.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `dwy --help` 正式列出 `dwy skills install` / `dwy skills --help`,不再标成隐藏命令。`dwy skills` 无参数时打印 skills 子命令说明。
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## 0.17.0
|
|
12
|
+
|
|
13
|
+
### Minor Changes
|
|
14
|
+
|
|
15
|
+
- `dwy` 交互同步开头增加「同步范围」:完整同步(默认)或仅 Skills。仅 Skills 不写、不删 Rules / Commands / Hooks。
|
|
16
|
+
- 最后增加「Skill 同步位置」:默认仅项目目录;可另选 Claude / Grok / Codex / Cursor / OpenCode 全局目录,并勾选要额外拷贝的 skill。只覆盖点名 skill,不删用户 home 里其它个人 skill。选择写入 `/.dwy/sync-state.json` 的 `skillScope` / `syncMode`。
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
3
20
|
## 0.16.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/bin/index.js
CHANGED
|
@@ -5,36 +5,40 @@ import { fileURLToPath } from 'url'
|
|
|
5
5
|
import { dirname, resolve } from 'path'
|
|
6
6
|
import { syncAll } from '../src/sync-all.js'
|
|
7
7
|
import { installSkills } from '../src/skills-install.js'
|
|
8
|
+
import { buildHelpText, buildSkillsHelpText } from '../src/cli-help.js'
|
|
8
9
|
|
|
9
10
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
11
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'))
|
|
11
12
|
const argv = process.argv.slice(2)
|
|
12
13
|
|
|
14
|
+
/** 是否请求帮助(含 -h / --help / help) */
|
|
15
|
+
function wantsHelp(args) {
|
|
16
|
+
return args.includes('--help') || args.includes('-h') || args[0] === 'help'
|
|
17
|
+
}
|
|
18
|
+
|
|
13
19
|
function printHelp() {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
' dwy --help 显示本说明',
|
|
20
|
-
' dwy -h 显示本说明',
|
|
21
|
-
' dwy --version 显示当前版本',
|
|
22
|
-
' dwy -V 显示当前版本',
|
|
23
|
-
'',
|
|
24
|
-
'以下为隐藏命令(向后兼容保留,日常无需手动调用):',
|
|
25
|
-
' dwy sync 同上的别名',
|
|
26
|
-
' dwy skills install 强制刷新全局外部 skill',
|
|
27
|
-
'',
|
|
28
|
-
]
|
|
29
|
-
console.log(lines.join('\n'))
|
|
20
|
+
console.log(buildHelpText(pkg.version))
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function printSkillsHelp() {
|
|
24
|
+
console.log(buildSkillsHelpText())
|
|
30
25
|
}
|
|
31
26
|
|
|
32
|
-
if (argv
|
|
27
|
+
if (argv[0] === 'skills') {
|
|
28
|
+
const rest = argv.slice(1)
|
|
29
|
+
if (rest.length === 0 || wantsHelp(rest) || rest[0] === 'help') {
|
|
30
|
+
printSkillsHelp()
|
|
31
|
+
} else if (rest[0] === 'install') {
|
|
32
|
+
await installSkills()
|
|
33
|
+
} else {
|
|
34
|
+
console.error(`未知命令: dwy skills ${rest.join(' ')}`)
|
|
35
|
+
printSkillsHelp()
|
|
36
|
+
process.exitCode = 1
|
|
37
|
+
}
|
|
38
|
+
} else if (wantsHelp(argv)) {
|
|
33
39
|
printHelp()
|
|
34
40
|
} else if (argv.includes('--version') || argv.includes('-V')) {
|
|
35
41
|
console.log(pkg.version)
|
|
36
|
-
} else if (argv[0] === 'skills' && argv[1] === 'install') {
|
|
37
|
-
await installSkills()
|
|
38
42
|
} else if (argv.length === 0 || argv[0] === 'sync') {
|
|
39
43
|
await syncAll()
|
|
40
44
|
} else {
|
package/package.json
CHANGED
package/src/cli-help.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dwy 命令行帮助文案。
|
|
3
|
+
* 已实现的入口必须出现在这里;禁止把可用命令藏进「隐藏」段。
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 主帮助:`dwy --help`。
|
|
8
|
+
* version 来自 package.json,调用方传入,避免本模块读盘。
|
|
9
|
+
*
|
|
10
|
+
* @param {string} version
|
|
11
|
+
*/
|
|
12
|
+
export function buildHelpText(version) {
|
|
13
|
+
return [
|
|
14
|
+
`dwy ${version}`,
|
|
15
|
+
'',
|
|
16
|
+
'Usage:',
|
|
17
|
+
' dwy 交互同步:选范围(完整/仅 Skills)、平台、skill 落点',
|
|
18
|
+
' dwy sync 同上',
|
|
19
|
+
' dwy skills install 强制刷新全局外部 skill 到 ~/.dwy/skills',
|
|
20
|
+
' dwy skills --help 显示 skills 子命令',
|
|
21
|
+
' dwy --help 显示本说明',
|
|
22
|
+
' dwy -h 显示本说明',
|
|
23
|
+
' dwy --version 显示当前版本',
|
|
24
|
+
' dwy -V 显示当前版本',
|
|
25
|
+
'',
|
|
26
|
+
'Skills:',
|
|
27
|
+
' dwy skills install 按清单安装/覆盖更新 pm-skills、superpowers(一台机一份)',
|
|
28
|
+
'',
|
|
29
|
+
].join('\n')
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* skills 子命令帮助:`dwy skills` / `dwy skills --help`。
|
|
34
|
+
*/
|
|
35
|
+
export function buildSkillsHelpText() {
|
|
36
|
+
return [
|
|
37
|
+
'dwy skills',
|
|
38
|
+
'',
|
|
39
|
+
'Usage:',
|
|
40
|
+
' dwy skills install 强制刷新全局外部 skill 到 ~/.dwy/skills',
|
|
41
|
+
' dwy skills --help 显示本说明',
|
|
42
|
+
'',
|
|
43
|
+
].join('\n')
|
|
44
|
+
}
|
package/src/sync-all.js
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
import fs from 'fs-extra'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import { confirm, isCancel } from '@clack/prompts'
|
|
4
|
-
import { SEARCH_PLACEHOLDER, searchableMultiselect } from './searchable-select.js'
|
|
4
|
+
import { SEARCH_PLACEHOLDER, searchableMultiselect, searchableSelect } from './searchable-select.js'
|
|
5
|
+
import {
|
|
6
|
+
DEFAULT_SKILL_DESTINATIONS,
|
|
7
|
+
copySkillsToGlobalDirs,
|
|
8
|
+
copySkillsToProjectPlatforms,
|
|
9
|
+
listSkillDestOptions,
|
|
10
|
+
normalizeSkillScope,
|
|
11
|
+
resolveProjectSkillDir,
|
|
12
|
+
} from './sync-global-skills.js'
|
|
5
13
|
import { chalk } from './utils.js'
|
|
6
14
|
import {
|
|
7
15
|
interactiveSelect,
|
|
@@ -55,6 +63,24 @@ const TYPE_LABELS = {
|
|
|
55
63
|
hooks: 'hooks',
|
|
56
64
|
}
|
|
57
65
|
|
|
66
|
+
/** 完整同步:skills / rules / commands / hooks 都走平台写入 */
|
|
67
|
+
export const SYNC_MODE_ALL = 'all'
|
|
68
|
+
/** 仅 Skills:不写、不删 rules / commands / hooks */
|
|
69
|
+
export const SYNC_MODE_SKILLS = 'skills'
|
|
70
|
+
export const DEFAULT_SYNC_MODE = SYNC_MODE_ALL
|
|
71
|
+
|
|
72
|
+
const SKILL_ONLY_CATEGORIES = [{ key: 'skills', label: 'Skills' }]
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* 规范化同步范围。未知值回退完整同步,避免误进仅 Skills 删不该删的东西。
|
|
76
|
+
*
|
|
77
|
+
* @param {unknown} raw
|
|
78
|
+
* @returns {'all' | 'skills'}
|
|
79
|
+
*/
|
|
80
|
+
export function normalizeSyncMode(raw) {
|
|
81
|
+
return raw === SYNC_MODE_SKILLS ? SYNC_MODE_SKILLS : DEFAULT_SYNC_MODE
|
|
82
|
+
}
|
|
83
|
+
|
|
58
84
|
function unionSets(...sets) {
|
|
59
85
|
return new Set(sets.flatMap(set => [...set]))
|
|
60
86
|
}
|
|
@@ -144,6 +170,132 @@ export async function buildPlatformDefaultsFromLocalState(projectDir) {
|
|
|
144
170
|
return defaults.length > 0 ? defaults : ['claude', 'codex']
|
|
145
171
|
}
|
|
146
172
|
|
|
173
|
+
/**
|
|
174
|
+
* 开头选同步范围。仅 Skills 时后面不再问 rules / commands / hooks。
|
|
175
|
+
*
|
|
176
|
+
* @param {'all' | 'skills'} initialValue
|
|
177
|
+
* @returns {Promise<'all' | 'skills' | null>}
|
|
178
|
+
*/
|
|
179
|
+
async function promptSyncMode(initialValue) {
|
|
180
|
+
const result = await searchableSelect({
|
|
181
|
+
message: '选择同步范围',
|
|
182
|
+
options: [
|
|
183
|
+
{
|
|
184
|
+
value: SYNC_MODE_ALL,
|
|
185
|
+
label: '完整同步',
|
|
186
|
+
description: '同步 Skills / Rules / Commands / Hooks',
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
value: SYNC_MODE_SKILLS,
|
|
190
|
+
label: '仅 Skills',
|
|
191
|
+
description: '只处理 skill,不改动已有 Rules / Commands / Hooks',
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
initialValue,
|
|
195
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
196
|
+
})
|
|
197
|
+
if (isCancel(result)) return null
|
|
198
|
+
return normalizeSyncMode(result)
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 仅 Skills 模式:只写项目/全局 skill,并清理未选中的模板 skill。
|
|
203
|
+
* 禁止调用完整平台 sync,否则空 rules/commands 会删掉项目已有项。
|
|
204
|
+
*
|
|
205
|
+
* @param {{ projectDir: string, selectedPlatforms: string[], skills: Array<{ name: string, sourcePath: string }>, templateSkillNames: Set<string>, staleSkillNames: Set<string>, skillScope: { destinations: string[] } }} opts
|
|
206
|
+
*/
|
|
207
|
+
async function syncSkillsOnly({
|
|
208
|
+
projectDir,
|
|
209
|
+
selectedPlatforms,
|
|
210
|
+
skills,
|
|
211
|
+
templateSkillNames,
|
|
212
|
+
staleSkillNames,
|
|
213
|
+
skillScope,
|
|
214
|
+
}) {
|
|
215
|
+
const writeProject = skillScope.destinations.includes('project')
|
|
216
|
+
if (!writeProject) return
|
|
217
|
+
|
|
218
|
+
const skillPlatforms = selectedPlatforms.filter(platform => resolveProjectSkillDir(projectDir, platform))
|
|
219
|
+
if (skillPlatforms.length === 0) {
|
|
220
|
+
console.log(chalk.yellow('已选平台不支持项目内 skills,跳过项目写入。'))
|
|
221
|
+
return
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
console.log(chalk.blue('\nSyncing skills only (rules / commands / hooks untouched)...\n'))
|
|
225
|
+
await copySkillsToProjectPlatforms({
|
|
226
|
+
projectDir,
|
|
227
|
+
platforms: skillPlatforms,
|
|
228
|
+
skills,
|
|
229
|
+
})
|
|
230
|
+
|
|
231
|
+
const selectedNames = new Set(skills.map(item => item.name))
|
|
232
|
+
const toRemove = new Set(
|
|
233
|
+
[...templateSkillNames, ...staleSkillNames].filter(name => !selectedNames.has(name)),
|
|
234
|
+
)
|
|
235
|
+
for (const platform of skillPlatforms) {
|
|
236
|
+
const destRoot = resolveProjectSkillDir(projectDir, platform)
|
|
237
|
+
const existingNames = await scanExisting(path.dirname(destRoot), 'skills')
|
|
238
|
+
await removeManagedItems(
|
|
239
|
+
destRoot,
|
|
240
|
+
existingNames,
|
|
241
|
+
toRemove,
|
|
242
|
+
path.relative(projectDir, destRoot),
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* 交互最后一步:选 skill 同步位置,默认仅项目。
|
|
249
|
+
* 勾了全局 dest 后再问哪些 skill 额外拷到全局;不删用户 home 里其它 skill。
|
|
250
|
+
*
|
|
251
|
+
* @param {{ selectedSkills: Array<{ name: string }>, syncState: { skillScope?: object } }} args
|
|
252
|
+
* @returns {Promise<{ destinations: string[], globalSkills: string[] } | null>}
|
|
253
|
+
*/
|
|
254
|
+
async function promptSkillScope({ selectedSkills, syncState }) {
|
|
255
|
+
const previous = normalizeSkillScope(syncState.skillScope)
|
|
256
|
+
const destInitial = previous.destinations.length > 0
|
|
257
|
+
? previous.destinations
|
|
258
|
+
: [...DEFAULT_SKILL_DESTINATIONS]
|
|
259
|
+
|
|
260
|
+
console.log(chalk.gray('\nSkill 默认同步到项目目录。勾选全局目录可把部分 skill 额外拷到本机 home。'))
|
|
261
|
+
|
|
262
|
+
const destResult = await searchableMultiselect({
|
|
263
|
+
message: '选择 skill 同步位置(默认项目)',
|
|
264
|
+
options: listSkillDestOptions(),
|
|
265
|
+
initialValues: destInitial,
|
|
266
|
+
required: true,
|
|
267
|
+
maxItems: 8,
|
|
268
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
269
|
+
})
|
|
270
|
+
if (isCancel(destResult)) return null
|
|
271
|
+
|
|
272
|
+
const destinations = destResult
|
|
273
|
+
const globalDests = destinations.filter(id => id !== 'project')
|
|
274
|
+
if (globalDests.length === 0 || selectedSkills.length === 0) {
|
|
275
|
+
return { destinations, globalSkills: [] }
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const selectedNames = selectedSkills.map(item => item.name)
|
|
279
|
+
const globalInitial = previous.globalSkills.filter(name => selectedNames.includes(name))
|
|
280
|
+
const globalResult = await searchableMultiselect({
|
|
281
|
+
message: '哪些 skill 额外同步到全局?',
|
|
282
|
+
options: selectedSkills.map(item => ({
|
|
283
|
+
value: item.name,
|
|
284
|
+
label: item.name,
|
|
285
|
+
description: item.description,
|
|
286
|
+
})),
|
|
287
|
+
initialValues: globalInitial,
|
|
288
|
+
required: false,
|
|
289
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
290
|
+
})
|
|
291
|
+
if (isCancel(globalResult)) return null
|
|
292
|
+
|
|
293
|
+
return {
|
|
294
|
+
destinations,
|
|
295
|
+
globalSkills: globalResult,
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
147
299
|
async function selectPlatforms(projectDir) {
|
|
148
300
|
const initialValues = await buildPlatformDefaultsFromLocalState(projectDir)
|
|
149
301
|
|
|
@@ -197,6 +349,8 @@ async function loadSyncState(projectDir) {
|
|
|
197
349
|
return {
|
|
198
350
|
version: 1,
|
|
199
351
|
platforms: {},
|
|
352
|
+
skillScope: normalizeSkillScope(undefined),
|
|
353
|
+
syncMode: DEFAULT_SYNC_MODE,
|
|
200
354
|
}
|
|
201
355
|
}
|
|
202
356
|
|
|
@@ -204,6 +358,8 @@ async function loadSyncState(projectDir) {
|
|
|
204
358
|
return {
|
|
205
359
|
version: 1,
|
|
206
360
|
platforms: state.platforms || {},
|
|
361
|
+
skillScope: normalizeSkillScope(state.skillScope),
|
|
362
|
+
syncMode: normalizeSyncMode(state.syncMode),
|
|
207
363
|
}
|
|
208
364
|
}
|
|
209
365
|
|
|
@@ -328,6 +484,19 @@ async function promptStaleRemovals(staleEntries) {
|
|
|
328
484
|
return approvals
|
|
329
485
|
}
|
|
330
486
|
|
|
487
|
+
/**
|
|
488
|
+
* 仅 Skills 模式:陈旧项提示只保留 skills,避免问 rules/commands/hooks。
|
|
489
|
+
*
|
|
490
|
+
* @param {Record<string, Record<string, string[]>>} staleEntries
|
|
491
|
+
*/
|
|
492
|
+
function retainSkillStaleEntries(staleEntries) {
|
|
493
|
+
const next = createEmptyRemovalState()
|
|
494
|
+
for (const [platform, types] of Object.entries(staleEntries)) {
|
|
495
|
+
if (types.skills?.length) next[platform] = { skills: types.skills }
|
|
496
|
+
}
|
|
497
|
+
return next
|
|
498
|
+
}
|
|
499
|
+
|
|
331
500
|
function getPreservedStaleNames(staleEntries, approvedRemovals, platform, type) {
|
|
332
501
|
const staleNames = staleEntries[platform]?.[type] || []
|
|
333
502
|
const approvedNames = approvedRemovals[platform]?.[type] || new Set()
|
|
@@ -556,6 +725,12 @@ export async function syncAll({
|
|
|
556
725
|
selected: selectedOverride,
|
|
557
726
|
selectedPlatforms: selectedPlatformsOverride,
|
|
558
727
|
staleRemovals: staleRemovalsOverride,
|
|
728
|
+
// 测试/脚本可注入;交互模式在平台选择之后再问
|
|
729
|
+
skillScope: skillScopeOverride,
|
|
730
|
+
// all = 完整同步;skills = 只写 skill,测试/脚本可注入
|
|
731
|
+
syncMode: syncModeOverride,
|
|
732
|
+
// 全局 skill 写入的 home,测试注入假目录,禁止默认写真实 $HOME
|
|
733
|
+
homeDir,
|
|
559
734
|
} = {}) {
|
|
560
735
|
const sourceDir = sourceDirOverride || await resolveSourceDir()
|
|
561
736
|
if (!await fs.pathExists(sourceDir)) {
|
|
@@ -605,7 +780,20 @@ export async function syncAll({
|
|
|
605
780
|
console.log(chalk.gray('首次同步,进入交互式选择...\n'))
|
|
606
781
|
}
|
|
607
782
|
|
|
608
|
-
const
|
|
783
|
+
const skipInteractive = !!(selectedOverride || selectedPlatformsOverride)
|
|
784
|
+
const syncMode = syncModeOverride
|
|
785
|
+
? normalizeSyncMode(syncModeOverride)
|
|
786
|
+
: (skipInteractive
|
|
787
|
+
? normalizeSyncMode(syncState.syncMode)
|
|
788
|
+
: await promptSyncMode(normalizeSyncMode(syncState.syncMode)))
|
|
789
|
+
if (syncMode === null) {
|
|
790
|
+
console.log(chalk.yellow('\n已取消同步。'))
|
|
791
|
+
return
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const selected = selectedOverride || await interactiveSelect(scans, selectionDefaults, {
|
|
795
|
+
categories: syncMode === SYNC_MODE_SKILLS ? SKILL_ONLY_CATEGORIES : undefined,
|
|
796
|
+
})
|
|
609
797
|
if (selected === null) {
|
|
610
798
|
console.log(chalk.yellow('\n已取消同步。'))
|
|
611
799
|
return
|
|
@@ -617,7 +805,10 @@ export async function syncAll({
|
|
|
617
805
|
return
|
|
618
806
|
}
|
|
619
807
|
|
|
620
|
-
const
|
|
808
|
+
const staleEntriesRaw = await collectStaleEntries(projectDir, scans, syncState, selectedPlatforms)
|
|
809
|
+
const staleEntries = syncMode === SYNC_MODE_SKILLS
|
|
810
|
+
? retainSkillStaleEntries(staleEntriesRaw)
|
|
811
|
+
: staleEntriesRaw
|
|
621
812
|
const approvedStaleRemovals = staleRemovalsOverride
|
|
622
813
|
? normalizeRemovalInput(staleRemovalsOverride)
|
|
623
814
|
: await promptStaleRemovals(staleEntries)
|
|
@@ -626,79 +817,132 @@ export async function syncAll({
|
|
|
626
817
|
return
|
|
627
818
|
}
|
|
628
819
|
|
|
820
|
+
const skillScope = skillScopeOverride
|
|
821
|
+
? normalizeSkillScope(skillScopeOverride)
|
|
822
|
+
: (selectedOverride
|
|
823
|
+
? normalizeSkillScope(syncState.skillScope)
|
|
824
|
+
: await promptSkillScope({ selectedSkills: selected.skills || [], syncState }))
|
|
825
|
+
if (skillScope === null) {
|
|
826
|
+
console.log(chalk.yellow('\n已取消同步。'))
|
|
827
|
+
return
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
const selectedSkillNames = new Set((selected.skills || []).map(item => item.name))
|
|
831
|
+
const skillsToWrite = scans.skills.filter(item => selectedSkillNames.has(item.name))
|
|
629
832
|
let syncedAnySupportedPlatform = false
|
|
630
833
|
|
|
631
|
-
if (
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
834
|
+
if (syncMode === SYNC_MODE_SKILLS) {
|
|
835
|
+
const staleSkillNames = new Set()
|
|
836
|
+
for (const platform of selectedPlatforms) {
|
|
837
|
+
for (const name of approvedStaleRemovals[platform]?.skills || []) staleSkillNames.add(name)
|
|
838
|
+
}
|
|
839
|
+
await syncSkillsOnly({
|
|
636
840
|
projectDir,
|
|
637
|
-
|
|
638
|
-
|
|
841
|
+
selectedPlatforms,
|
|
842
|
+
skills: skillsToWrite,
|
|
843
|
+
templateSkillNames: new Set(scans.skills.map(item => item.name)),
|
|
844
|
+
staleSkillNames,
|
|
845
|
+
skillScope,
|
|
639
846
|
})
|
|
640
|
-
|
|
847
|
+
syncedAnySupportedPlatform = selectedPlatforms.some(platform => resolveProjectSkillDir(projectDir, platform))
|
|
848
|
+
|| skillScope.destinations.some(id => id !== 'project')
|
|
849
|
+
} else {
|
|
850
|
+
if (selectedPlatforms.includes('claude')) {
|
|
851
|
+
syncedAnySupportedPlatform = true
|
|
852
|
+
console.log(chalk.blue('\nSyncing Claude Code configuration...\n'))
|
|
853
|
+
await syncClaude({
|
|
854
|
+
sourceDir,
|
|
855
|
+
projectDir,
|
|
856
|
+
selected,
|
|
857
|
+
staleRemovals: approvedStaleRemovals.claude,
|
|
858
|
+
})
|
|
859
|
+
}
|
|
641
860
|
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
861
|
+
if (selectedPlatforms.includes('codex')) {
|
|
862
|
+
syncedAnySupportedPlatform = true
|
|
863
|
+
console.log(chalk.blue('\nSyncing Codex configuration...\n'))
|
|
864
|
+
await syncCodex({
|
|
865
|
+
sourceDir,
|
|
866
|
+
projectDir,
|
|
867
|
+
selected,
|
|
868
|
+
staleRemovals: approvedStaleRemovals.codex,
|
|
869
|
+
preserveMissingRules: getPreservedStaleNames(staleEntries, approvedStaleRemovals, 'codex', 'rules'),
|
|
870
|
+
})
|
|
871
|
+
}
|
|
653
872
|
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
873
|
+
if (selectedPlatforms.includes('cursor')) {
|
|
874
|
+
syncedAnySupportedPlatform = true
|
|
875
|
+
await syncCursor({
|
|
876
|
+
sourceDir,
|
|
877
|
+
projectDir,
|
|
878
|
+
selected,
|
|
879
|
+
staleRemovals: approvedStaleRemovals.cursor,
|
|
880
|
+
})
|
|
881
|
+
}
|
|
663
882
|
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
883
|
+
if (selectedPlatforms.includes('opencode')) {
|
|
884
|
+
syncedAnySupportedPlatform = true
|
|
885
|
+
await syncOpenCode({
|
|
886
|
+
sourceDir,
|
|
887
|
+
projectDir,
|
|
888
|
+
selected,
|
|
889
|
+
staleRemovals: approvedStaleRemovals.opencode,
|
|
890
|
+
preserveMissingRules: getPreservedStaleNames(staleEntries, approvedStaleRemovals, 'opencode', 'rules'),
|
|
891
|
+
})
|
|
892
|
+
}
|
|
674
893
|
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
894
|
+
if (!selectedPlatforms.includes('codex') && !selectedPlatforms.includes('opencode')) {
|
|
895
|
+
await clearProjectAgentsMd(projectDir)
|
|
896
|
+
}
|
|
678
897
|
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
898
|
+
if (!selectedPlatforms.includes('claude')) {
|
|
899
|
+
await cleanupClaudePlatform(projectDir, scans, claudeManagedHookNames, baseline)
|
|
900
|
+
}
|
|
682
901
|
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
902
|
+
if (!selectedPlatforms.includes('cursor')) {
|
|
903
|
+
await cleanupCursorPlatform(projectDir, scans, baseline)
|
|
904
|
+
}
|
|
686
905
|
|
|
687
|
-
|
|
688
|
-
|
|
906
|
+
if (!selectedPlatforms.includes('codex')) {
|
|
907
|
+
await cleanupCodexPlatform(projectDir, scans, codexManagedHookNames)
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
if (!selectedPlatforms.includes('opencode')) {
|
|
911
|
+
await cleanupOpenCodePlatform(projectDir, scans)
|
|
912
|
+
}
|
|
689
913
|
}
|
|
690
914
|
|
|
691
|
-
|
|
692
|
-
|
|
915
|
+
const globalSkillNameSet = new Set(skillScope.globalSkills)
|
|
916
|
+
const globalSkills = scans.skills.filter(item => globalSkillNameSet.has(item.name))
|
|
917
|
+
if (globalSkills.length > 0 && skillScope.destinations.some(id => id !== 'project')) {
|
|
918
|
+
console.log(chalk.blue('\nSyncing selected skills to global directories...\n'))
|
|
919
|
+
await copySkillsToGlobalDirs({
|
|
920
|
+
skills: globalSkills,
|
|
921
|
+
destIds: skillScope.destinations,
|
|
922
|
+
homeDir,
|
|
923
|
+
})
|
|
693
924
|
}
|
|
694
925
|
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
syncState.platforms[platform]
|
|
698
|
-
|
|
699
|
-
syncState.platforms[platform] =
|
|
926
|
+
if (syncMode === SYNC_MODE_SKILLS) {
|
|
927
|
+
for (const platform of selectedPlatforms) {
|
|
928
|
+
const previous = syncState.platforms[platform] || createEmptyPlatformState()
|
|
929
|
+
const skillsOnlySelected = { skills: selected.skills || [], rules: [], commands: [], hooks: [] }
|
|
930
|
+
syncState.platforms[platform] = {
|
|
931
|
+
...previous,
|
|
932
|
+
skills: buildNextPlatformState(skillsOnlySelected, staleEntries, approvedStaleRemovals, platform).skills,
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
} else {
|
|
936
|
+
for (const platform of Object.keys(PLATFORM_SYNC_TYPES)) {
|
|
937
|
+
if (selectedPlatforms.includes(platform)) {
|
|
938
|
+
syncState.platforms[platform] = buildNextPlatformState(selected, staleEntries, approvedStaleRemovals, platform)
|
|
939
|
+
} else {
|
|
940
|
+
syncState.platforms[platform] = createEmptyPlatformState()
|
|
941
|
+
}
|
|
700
942
|
}
|
|
701
943
|
}
|
|
944
|
+
syncState.skillScope = skillScope
|
|
945
|
+
syncState.syncMode = syncMode
|
|
702
946
|
await saveSyncState(projectDir, syncState)
|
|
703
947
|
|
|
704
948
|
if (!syncedAnySupportedPlatform) {
|