create-dwy 0.17.1 → 0.18.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 +19 -0
- package/bin/index.js +18 -20
- package/package.json +1 -1
- package/src/cli-help.js +5 -25
- package/src/self-upgrade.js +158 -0
- package/src/sync-all.js +155 -5
- package/src/sync-packs.js +191 -0
- package/src/sync-scene.js +169 -0
- package/src/sync.js +9 -1
- package/templates/ai-tools/CHANGELOG.md +32 -0
- package/templates/ai-tools/VERSION +1 -1
- package/templates/ai-tools/rules/Android/dwy-android-core.md +67 -1
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-dependency-freshness.md +26 -14
- package/templates/ai-tools/rules//345/274/200/345/217/221/346/265/201/347/250/213/dwy-git-commit.md +90 -22
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-acceptance/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-commercial/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-competitor/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-mvp/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-prd/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-product-launcher/references/state-and-contract.md +3 -3
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-release/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tasks/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-tdd-dev/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-validate/SKILL.md +1 -1
- package/templates/ai-tools/skills//344/272/247/345/223/2010/345/210/2601/dwy-version/SKILL.md +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/SKILL.md +39 -20
- 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 +28 -1
- 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 +31 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/aso.md +52 -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 +1 -1
- package/templates/ai-tools/skills//345/217/221/345/270/203/345/217/221/347/211/210/dwy-ios-app-store-release/references/screenshot-specs.yaml +98 -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 +62 -6
- package/templates/ai-tools/skills//345/237/272/347/241/200/345/272/223/dwy-eui/references/eui-design-guide.md +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# create-dwy
|
|
2
2
|
|
|
3
|
+
## 0.18.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- `dwy` 菜单只留两项:同步全局 skills、同步项目配置。升级只走 `dwy upgrade`。
|
|
8
|
+
- 同步项目配置里分「按场景和技术栈」与「一个一个选」。按包后直接列出已预勾子条目,可取消。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 0.18.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- `dwy` / `dwy sync` 开头选动作:同步项目配置(按包或按条目),刷新全局外部 skill,或升级 dwy。删除 `dwy skills` / `dwy skills install` / `dwy scene`。
|
|
17
|
+
- 新增 `dwy upgrade`:把全局 `create-dwy` 升到 npm `latest` 正式版(npm/pnpm/yarn/bun)。源码目录拒绝自升级。
|
|
18
|
+
- 按包勾选写入 `.dwy/sync-state.json` 的 `selectionStyle` / `packs`。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
3
22
|
## 0.17.1
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
package/bin/index.js
CHANGED
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
import { readFileSync } from 'fs'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import { dirname, resolve } from 'path'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { buildHelpText
|
|
6
|
+
import { runDwy } from '../src/sync-all.js'
|
|
7
|
+
import { selfUpgrade } from '../src/self-upgrade.js'
|
|
8
|
+
import { buildHelpText } from '../src/cli-help.js'
|
|
9
9
|
|
|
10
10
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
11
11
|
const pkg = JSON.parse(readFileSync(resolve(__dirname, '..', 'package.json'), 'utf-8'))
|
|
@@ -20,27 +20,25 @@ function printHelp() {
|
|
|
20
20
|
console.log(buildHelpText(pkg.version))
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
console.log(buildSkillsHelpText())
|
|
25
|
-
}
|
|
26
|
-
|
|
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)) {
|
|
23
|
+
if (wantsHelp(argv)) {
|
|
39
24
|
printHelp()
|
|
40
25
|
} else if (argv.includes('--version') || argv.includes('-V')) {
|
|
41
26
|
console.log(pkg.version)
|
|
27
|
+
} else if (argv[0] === 'upgrade') {
|
|
28
|
+
if (argv.length > 1) {
|
|
29
|
+
console.error(`未知命令: dwy ${argv.join(' ')}`)
|
|
30
|
+
printHelp()
|
|
31
|
+
process.exitCode = 1
|
|
32
|
+
} else {
|
|
33
|
+
try {
|
|
34
|
+
await selfUpgrade()
|
|
35
|
+
} catch (error) {
|
|
36
|
+
console.error(error.message)
|
|
37
|
+
process.exitCode = 1
|
|
38
|
+
}
|
|
39
|
+
}
|
|
42
40
|
} else if (argv.length === 0 || argv[0] === 'sync') {
|
|
43
|
-
await
|
|
41
|
+
await runDwy()
|
|
44
42
|
} else {
|
|
45
43
|
console.error(`未知命令: ${argv.join(' ')}`)
|
|
46
44
|
printHelp()
|
package/package.json
CHANGED
package/src/cli-help.js
CHANGED
|
@@ -14,31 +14,11 @@ export function buildHelpText(version) {
|
|
|
14
14
|
`dwy ${version}`,
|
|
15
15
|
'',
|
|
16
16
|
'Usage:',
|
|
17
|
-
' dwy
|
|
18
|
-
' dwy sync
|
|
19
|
-
' dwy
|
|
20
|
-
' dwy
|
|
21
|
-
' dwy --
|
|
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 显示本说明',
|
|
17
|
+
' dwy 同步全局 skills,或同步项目配置',
|
|
18
|
+
' dwy sync 同上',
|
|
19
|
+
' dwy upgrade 把 dwy 升级到 npm 最新正式版',
|
|
20
|
+
' dwy --help 显示本说明',
|
|
21
|
+
' dwy --version 显示版本',
|
|
42
22
|
'',
|
|
43
23
|
].join('\n')
|
|
44
24
|
}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dwy 自升级:把全局安装的 create-dwy 升到 npm dist-tag latest(正式版)。
|
|
3
|
+
* 不引入第三方更新器,安装命令走用户本机 npm/pnpm/yarn/bun。
|
|
4
|
+
* 源码目录 / 非全局安装拒绝执行,避免把仓库工作树覆盖成 npm 包。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { spawn } from 'node:child_process'
|
|
8
|
+
import { readFileSync } from 'node:fs'
|
|
9
|
+
import path from 'node:path'
|
|
10
|
+
import { PACKAGE_ROOT, chalk } from './utils.js'
|
|
11
|
+
|
|
12
|
+
/** npm 包名,bin 是 dwy */
|
|
13
|
+
export const PACKAGE_NAME = 'create-dwy'
|
|
14
|
+
|
|
15
|
+
const DEFAULT_REGISTRY = 'https://registry.npmjs.org/'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* 比较 a、b 两个三段版本号。a>b 返回 1,相等 0,a<b -1。
|
|
19
|
+
* 只处理正式版 MAJOR.MINOR.PATCH,不解析 beta。
|
|
20
|
+
*
|
|
21
|
+
* @param {string} a
|
|
22
|
+
* @param {string} b
|
|
23
|
+
*/
|
|
24
|
+
export function compareVersions(a, b) {
|
|
25
|
+
const parse = (v) => v.split('.').map(part => Number.parseInt(part, 10) || 0)
|
|
26
|
+
const left = parse(a)
|
|
27
|
+
const right = parse(b)
|
|
28
|
+
for (let i = 0; i < 3; i++) {
|
|
29
|
+
if (left[i] > right[i]) return 1
|
|
30
|
+
if (left[i] < right[i]) return -1
|
|
31
|
+
}
|
|
32
|
+
return 0
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 根据包安装路径判断全局包管理器。
|
|
37
|
+
* pnpm/yarn/bun 路径里也常有 node_modules/create-dwy,必须先匹配专用目录。
|
|
38
|
+
*
|
|
39
|
+
* @param {string} pkgRoot
|
|
40
|
+
* @returns {'npm' | 'pnpm' | 'yarn' | 'bun' | 'unknown'}
|
|
41
|
+
*/
|
|
42
|
+
export function detectInstaller(pkgRoot) {
|
|
43
|
+
const normalized = pkgRoot.replace(/\\/g, '/')
|
|
44
|
+
if (normalized.includes('/pnpm/global/') || normalized.includes('/.local/share/pnpm/')) return 'pnpm'
|
|
45
|
+
if (normalized.includes('/yarn/global/') || normalized.includes('/.yarn/')) return 'yarn'
|
|
46
|
+
if (normalized.includes('/.bun/install/')) return 'bun'
|
|
47
|
+
if (normalized.endsWith('/node_modules/create-dwy')) return 'npm'
|
|
48
|
+
return 'unknown'
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 构造升级命令。一律钉 @latest,不用 @next/@beta。
|
|
53
|
+
*
|
|
54
|
+
* @param {'npm' | 'pnpm' | 'yarn' | 'bun' | 'unknown'} installer
|
|
55
|
+
* @returns {[string, string[]] | null}
|
|
56
|
+
*/
|
|
57
|
+
export function buildUpgradeCommand(installer) {
|
|
58
|
+
const spec = `${PACKAGE_NAME}@latest`
|
|
59
|
+
if (installer === 'pnpm') return ['pnpm', ['add', '-g', spec]]
|
|
60
|
+
if (installer === 'yarn') return ['yarn', ['global', 'add', spec]]
|
|
61
|
+
if (installer === 'bun') return ['bun', ['add', '-g', spec]]
|
|
62
|
+
if (installer === 'npm') return ['npm', ['install', '-g', spec]]
|
|
63
|
+
return null
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* 读当前包版本。测试可注入 currentVersion,避免读盘。
|
|
68
|
+
*
|
|
69
|
+
* @param {string} [pkgRoot]
|
|
70
|
+
*/
|
|
71
|
+
function readCurrentVersion(pkgRoot = PACKAGE_ROOT) {
|
|
72
|
+
const pkg = JSON.parse(readFileSync(path.join(pkgRoot, 'package.json'), 'utf-8'))
|
|
73
|
+
return pkg.version
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 查 npm registry 的 latest。用官方 JSON,不跟浮动 tag 以外的通道。
|
|
78
|
+
*
|
|
79
|
+
* @param {typeof fetch} [fetchFn]
|
|
80
|
+
*/
|
|
81
|
+
export async function fetchLatestVersion(fetchFn = globalThis.fetch) {
|
|
82
|
+
const registry = (process.env.npm_config_registry || DEFAULT_REGISTRY).replace(/\/?$/, '/')
|
|
83
|
+
const url = `${registry}${PACKAGE_NAME}/latest`
|
|
84
|
+
const response = await fetchFn(url, { headers: { Accept: 'application/json', 'User-Agent': 'create-dwy' } })
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
throw new Error(`查询最新版本失败:${url} HTTP ${response.status}`)
|
|
87
|
+
}
|
|
88
|
+
const body = await response.json()
|
|
89
|
+
const version = body?.version
|
|
90
|
+
if (!version || typeof version !== 'string') {
|
|
91
|
+
throw new Error(`npm latest 未返回 version:${url}`)
|
|
92
|
+
}
|
|
93
|
+
return version
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* 跑包管理器命令,stdio 继承方便用户看安装进度。
|
|
98
|
+
* Windows 要 shell,才能找到 npm.cmd。
|
|
99
|
+
*
|
|
100
|
+
* @param {string} cmd
|
|
101
|
+
* @param {string[]} args
|
|
102
|
+
*/
|
|
103
|
+
function runSpawn(cmd, args) {
|
|
104
|
+
return new Promise((resolve, reject) => {
|
|
105
|
+
const child = spawn(cmd, args, {
|
|
106
|
+
stdio: 'inherit',
|
|
107
|
+
shell: process.platform === 'win32',
|
|
108
|
+
})
|
|
109
|
+
child.on('error', reject)
|
|
110
|
+
child.on('exit', (code) => {
|
|
111
|
+
if (code === 0) resolve()
|
|
112
|
+
else reject(new Error(`${cmd} ${args.join(' ')} 退出码 ${code}`))
|
|
113
|
+
})
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* 执行自升级。返回结构化结果供测试断言;失败抛错。
|
|
119
|
+
*
|
|
120
|
+
* @param {object} [opts]
|
|
121
|
+
* @param {string} [opts.currentVersion]
|
|
122
|
+
* @param {string} [opts.pkgRoot]
|
|
123
|
+
* @param {() => Promise<string>} [opts.fetchLatest]
|
|
124
|
+
* @param {(cmd: string, args: string[]) => Promise<void>} [opts.runCommand]
|
|
125
|
+
*/
|
|
126
|
+
export async function selfUpgrade({
|
|
127
|
+
currentVersion,
|
|
128
|
+
pkgRoot = PACKAGE_ROOT,
|
|
129
|
+
fetchLatest = fetchLatestVersion,
|
|
130
|
+
runCommand = runSpawn,
|
|
131
|
+
} = {}) {
|
|
132
|
+
const current = currentVersion || readCurrentVersion(pkgRoot)
|
|
133
|
+
const installer = detectInstaller(pkgRoot)
|
|
134
|
+
const command = buildUpgradeCommand(installer)
|
|
135
|
+
if (!command) {
|
|
136
|
+
throw new Error(
|
|
137
|
+
`当前不是全局安装的 ${PACKAGE_NAME}(路径 ${pkgRoot}),无法自升级。请先 npm i -g ${PACKAGE_NAME},或在源码仓 git pull。`,
|
|
138
|
+
)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const latest = await fetchLatest()
|
|
142
|
+
const cmp = compareVersions(current, latest)
|
|
143
|
+
if (cmp === 0) {
|
|
144
|
+
console.log(chalk.green(`已是最新正式版 ${current}`))
|
|
145
|
+
return { status: 'up-to-date', current, latest, installer }
|
|
146
|
+
}
|
|
147
|
+
if (cmp > 0) {
|
|
148
|
+
console.log(chalk.yellow(`当前 ${current} 新于 npm latest ${latest},不降级`))
|
|
149
|
+
return { status: 'newer-local', current, latest, installer }
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const [cmd, args] = command
|
|
153
|
+
console.log(chalk.blue(`升级 ${PACKAGE_NAME} ${current} → ${latest}`))
|
|
154
|
+
console.log(chalk.gray(` ${cmd} ${args.join(' ')}`))
|
|
155
|
+
await runCommand(cmd, args)
|
|
156
|
+
console.log(chalk.green(`已升级到 ${latest}`))
|
|
157
|
+
return { status: 'upgraded', current, latest, installer }
|
|
158
|
+
}
|
package/src/sync-all.js
CHANGED
|
@@ -33,7 +33,10 @@ import {
|
|
|
33
33
|
} from './sync-codex.js'
|
|
34
34
|
import { scanExistingCursorRules, syncCursor } from './sync-cursor.js'
|
|
35
35
|
import { syncOpenCode } from './sync-opencode.js'
|
|
36
|
-
import { ensureSkillsInstalled } from './skills-install.js'
|
|
36
|
+
import { ensureSkillsInstalled, installSkills } from './skills-install.js'
|
|
37
|
+
import { selfUpgrade } from './self-upgrade.js'
|
|
38
|
+
import { normalizePacks } from './sync-packs.js'
|
|
39
|
+
import { interactiveSelectByPacks } from './sync-scene.js'
|
|
37
40
|
|
|
38
41
|
const PLATFORM_OPTIONS = [
|
|
39
42
|
{ value: 'claude', label: 'Claude Code', description: '支持 rules / skills / commands / hooks' },
|
|
@@ -69,6 +72,19 @@ export const SYNC_MODE_ALL = 'all'
|
|
|
69
72
|
export const SYNC_MODE_SKILLS = 'skills'
|
|
70
73
|
export const DEFAULT_SYNC_MODE = SYNC_MODE_ALL
|
|
71
74
|
|
|
75
|
+
/** 按条目勾选 Skills / Rules / Hooks */
|
|
76
|
+
export const SELECTION_STYLE_ITEMS = 'items'
|
|
77
|
+
/** 按技术栈包 + 场景包勾选 */
|
|
78
|
+
export const SELECTION_STYLE_PACKS = 'packs'
|
|
79
|
+
export const DEFAULT_SELECTION_STYLE = SELECTION_STYLE_PACKS
|
|
80
|
+
|
|
81
|
+
/** 同步项目配置(skills / rules / hooks) */
|
|
82
|
+
export const ACTION_SYNC = 'sync'
|
|
83
|
+
/** 强制刷新 ~/.dwy/skills 外部 skill */
|
|
84
|
+
export const ACTION_INSTALL_SKILLS = 'install-skills'
|
|
85
|
+
/** 把全局 create-dwy 升到 npm latest */
|
|
86
|
+
export const ACTION_UPGRADE = 'upgrade'
|
|
87
|
+
|
|
72
88
|
const SKILL_ONLY_CATEGORIES = [{ key: 'skills', label: 'Skills' }]
|
|
73
89
|
|
|
74
90
|
/**
|
|
@@ -81,6 +97,28 @@ export function normalizeSyncMode(raw) {
|
|
|
81
97
|
return raw === SYNC_MODE_SKILLS ? SYNC_MODE_SKILLS : DEFAULT_SYNC_MODE
|
|
82
98
|
}
|
|
83
99
|
|
|
100
|
+
/**
|
|
101
|
+
* 规范化勾选方式。未知值回退按包,避免新用户落到 80+ 条逐项勾选。
|
|
102
|
+
*
|
|
103
|
+
* @param {unknown} raw
|
|
104
|
+
* @returns {'items' | 'packs'}
|
|
105
|
+
*/
|
|
106
|
+
export function normalizeSelectionStyle(raw) {
|
|
107
|
+
return raw === SELECTION_STYLE_ITEMS ? SELECTION_STYLE_ITEMS : DEFAULT_SELECTION_STYLE
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* 规范化入口动作。未知值回退同步,避免误进强制重装。
|
|
112
|
+
*
|
|
113
|
+
* @param {unknown} raw
|
|
114
|
+
* @returns {'sync' | 'install-skills' | 'upgrade'}
|
|
115
|
+
*/
|
|
116
|
+
export function normalizeAction(raw) {
|
|
117
|
+
if (raw === ACTION_INSTALL_SKILLS) return ACTION_INSTALL_SKILLS
|
|
118
|
+
if (raw === ACTION_UPGRADE) return ACTION_UPGRADE
|
|
119
|
+
return ACTION_SYNC
|
|
120
|
+
}
|
|
121
|
+
|
|
84
122
|
function unionSets(...sets) {
|
|
85
123
|
return new Set(sets.flatMap(set => [...set]))
|
|
86
124
|
}
|
|
@@ -170,6 +208,34 @@ export async function buildPlatformDefaultsFromLocalState(projectDir) {
|
|
|
170
208
|
return defaults.length > 0 ? defaults : ['claude', 'codex']
|
|
171
209
|
}
|
|
172
210
|
|
|
211
|
+
/**
|
|
212
|
+
* dwy 入口第一问:同步全局 skills,或同步项目配置。
|
|
213
|
+
* 自升级只走 `dwy upgrade`,不出现在这张菜单。
|
|
214
|
+
*
|
|
215
|
+
* @returns {Promise<'sync' | 'install-skills' | null>}
|
|
216
|
+
*/
|
|
217
|
+
async function promptAction() {
|
|
218
|
+
const result = await searchableSelect({
|
|
219
|
+
message: '选择要做的事',
|
|
220
|
+
options: [
|
|
221
|
+
{
|
|
222
|
+
value: ACTION_INSTALL_SKILLS,
|
|
223
|
+
label: '同步全局 skills',
|
|
224
|
+
description: '强制重装 ~/.dwy/skills(pm-skills / superpowers)',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
value: ACTION_SYNC,
|
|
228
|
+
label: '同步项目配置',
|
|
229
|
+
description: '按场景和技术栈,或一条条勾选,写入当前项目',
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
initialValue: ACTION_SYNC,
|
|
233
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
234
|
+
})
|
|
235
|
+
if (isCancel(result)) return null
|
|
236
|
+
return normalizeAction(result)
|
|
237
|
+
}
|
|
238
|
+
|
|
173
239
|
/**
|
|
174
240
|
* 开头选同步范围。仅 Skills 时后面不再问 rules / commands / hooks。
|
|
175
241
|
*
|
|
@@ -198,6 +264,34 @@ async function promptSyncMode(initialValue) {
|
|
|
198
264
|
return normalizeSyncMode(result)
|
|
199
265
|
}
|
|
200
266
|
|
|
267
|
+
/**
|
|
268
|
+
* 选勾选方式。按包是默认推荐;按条目保留原来的逐项勾选。
|
|
269
|
+
*
|
|
270
|
+
* @param {'items' | 'packs'} initialValue
|
|
271
|
+
* @returns {Promise<'items' | 'packs' | null>}
|
|
272
|
+
*/
|
|
273
|
+
async function promptSelectionStyle(initialValue) {
|
|
274
|
+
const result = await searchableSelect({
|
|
275
|
+
message: '项目配置怎么勾',
|
|
276
|
+
options: [
|
|
277
|
+
{
|
|
278
|
+
value: SELECTION_STYLE_PACKS,
|
|
279
|
+
label: '按场景和技术栈(推荐)',
|
|
280
|
+
description: '先选栈和场景包,再展示已勾子条目,可取消',
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
value: SELECTION_STYLE_ITEMS,
|
|
284
|
+
label: '一个一个选',
|
|
285
|
+
description: '一条条勾选 Skills / Rules / Hooks',
|
|
286
|
+
},
|
|
287
|
+
],
|
|
288
|
+
initialValue,
|
|
289
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
290
|
+
})
|
|
291
|
+
if (isCancel(result)) return null
|
|
292
|
+
return normalizeSelectionStyle(result)
|
|
293
|
+
}
|
|
294
|
+
|
|
201
295
|
/**
|
|
202
296
|
* 仅 Skills 模式:只写项目/全局 skill,并清理未选中的模板 skill。
|
|
203
297
|
* 禁止调用完整平台 sync,否则空 rules/commands 会删掉项目已有项。
|
|
@@ -351,6 +445,8 @@ async function loadSyncState(projectDir) {
|
|
|
351
445
|
platforms: {},
|
|
352
446
|
skillScope: normalizeSkillScope(undefined),
|
|
353
447
|
syncMode: DEFAULT_SYNC_MODE,
|
|
448
|
+
selectionStyle: DEFAULT_SELECTION_STYLE,
|
|
449
|
+
packs: normalizePacks(undefined),
|
|
354
450
|
}
|
|
355
451
|
}
|
|
356
452
|
|
|
@@ -360,6 +456,8 @@ async function loadSyncState(projectDir) {
|
|
|
360
456
|
platforms: state.platforms || {},
|
|
361
457
|
skillScope: normalizeSkillScope(state.skillScope),
|
|
362
458
|
syncMode: normalizeSyncMode(state.syncMode),
|
|
459
|
+
selectionStyle: normalizeSelectionStyle(state.selectionStyle),
|
|
460
|
+
packs: normalizePacks(state.packs),
|
|
363
461
|
}
|
|
364
462
|
}
|
|
365
463
|
|
|
@@ -719,6 +817,37 @@ async function cleanupOpenCodePlatform(projectDir, scans) {
|
|
|
719
817
|
return removedCount
|
|
720
818
|
}
|
|
721
819
|
|
|
820
|
+
/**
|
|
821
|
+
* `dwy` / `dwy sync` 入口。先选动作再分支:同步全局 skills / 同步项目配置。
|
|
822
|
+
* 自升级只走 `dwy upgrade`。测试注入 selected / action 时跳过动作菜单。
|
|
823
|
+
*
|
|
824
|
+
* @param {object} [opts]
|
|
825
|
+
*/
|
|
826
|
+
export async function runDwy(opts = {}) {
|
|
827
|
+
const skipMenu = !!(opts.selected || opts.selectedPlatforms || opts.action)
|
|
828
|
+
const action = opts.action != null
|
|
829
|
+
? normalizeAction(opts.action)
|
|
830
|
+
: (skipMenu ? ACTION_SYNC : await promptAction())
|
|
831
|
+
if (action === null) {
|
|
832
|
+
console.log(chalk.yellow('\n已取消。'))
|
|
833
|
+
return
|
|
834
|
+
}
|
|
835
|
+
if (action === ACTION_INSTALL_SKILLS) {
|
|
836
|
+
await installSkills()
|
|
837
|
+
return
|
|
838
|
+
}
|
|
839
|
+
if (action === ACTION_UPGRADE) {
|
|
840
|
+
try {
|
|
841
|
+
await selfUpgrade()
|
|
842
|
+
} catch (error) {
|
|
843
|
+
console.error(error.message)
|
|
844
|
+
process.exitCode = 1
|
|
845
|
+
}
|
|
846
|
+
return
|
|
847
|
+
}
|
|
848
|
+
await syncAll(opts)
|
|
849
|
+
}
|
|
850
|
+
|
|
722
851
|
export async function syncAll({
|
|
723
852
|
sourceDir: sourceDirOverride,
|
|
724
853
|
projectDir: projectDirOverride,
|
|
@@ -731,6 +860,8 @@ export async function syncAll({
|
|
|
731
860
|
syncMode: syncModeOverride,
|
|
732
861
|
// 全局 skill 写入的 home,测试注入假目录,禁止默认写真实 $HOME
|
|
733
862
|
homeDir,
|
|
863
|
+
// 测试注入勾选方式;交互模式在同步范围之后再问
|
|
864
|
+
selectionStyle: selectionStyleOverride,
|
|
734
865
|
} = {}) {
|
|
735
866
|
const sourceDir = sourceDirOverride || await resolveSourceDir()
|
|
736
867
|
if (!await fs.pathExists(sourceDir)) {
|
|
@@ -741,7 +872,7 @@ export async function syncAll({
|
|
|
741
872
|
|
|
742
873
|
// 开头无条件自检全局外部 skill:未装 / 清单变更(cli 加了 skill 或改了 tag)则自动更新,
|
|
743
874
|
// 已装且清单一致则零开销跳过。失败不阻塞 sync(离线/网络故障,下次再来)。
|
|
744
|
-
//
|
|
875
|
+
// 这样用户跑一次同步就把 skill 更新到位;强制重装走入口「刷新全局外部 skill」。
|
|
745
876
|
console.log(chalk.gray('检查全局外部 skill 更新...'))
|
|
746
877
|
await ensureSkillsInstalled()
|
|
747
878
|
|
|
@@ -791,9 +922,23 @@ export async function syncAll({
|
|
|
791
922
|
return
|
|
792
923
|
}
|
|
793
924
|
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
925
|
+
const selectionStyle = selectionStyleOverride
|
|
926
|
+
? normalizeSelectionStyle(selectionStyleOverride)
|
|
927
|
+
: (skipInteractive
|
|
928
|
+
? normalizeSelectionStyle(syncState.selectionStyle)
|
|
929
|
+
: await promptSelectionStyle(normalizeSelectionStyle(syncState.selectionStyle)))
|
|
930
|
+
if (selectionStyle === null) {
|
|
931
|
+
console.log(chalk.yellow('\n已取消同步。'))
|
|
932
|
+
return
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
const selected = selectedOverride || (
|
|
936
|
+
selectionStyle === SELECTION_STYLE_PACKS
|
|
937
|
+
? await interactiveSelectByPacks(scans, syncState, { skillsOnly: syncMode === SYNC_MODE_SKILLS })
|
|
938
|
+
: await interactiveSelect(scans, selectionDefaults, {
|
|
939
|
+
categories: syncMode === SYNC_MODE_SKILLS ? SKILL_ONLY_CATEGORIES : undefined,
|
|
940
|
+
})
|
|
941
|
+
)
|
|
797
942
|
if (selected === null) {
|
|
798
943
|
console.log(chalk.yellow('\n已取消同步。'))
|
|
799
944
|
return
|
|
@@ -943,6 +1088,11 @@ export async function syncAll({
|
|
|
943
1088
|
}
|
|
944
1089
|
syncState.skillScope = skillScope
|
|
945
1090
|
syncState.syncMode = syncMode
|
|
1091
|
+
syncState.selectionStyle = selectionStyle
|
|
1092
|
+
// 按条目同步不覆盖上次按包选择,方便下次再走按包
|
|
1093
|
+
if (selectionStyle === SELECTION_STYLE_PACKS) {
|
|
1094
|
+
syncState.packs = normalizePacks(selected.packs)
|
|
1095
|
+
}
|
|
946
1096
|
await saveSyncState(projectDir, syncState)
|
|
947
1097
|
|
|
948
1098
|
if (!syncedAnySupportedPlatform) {
|