create-dwy 0.17.0 → 0.18.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/CHANGELOG.md +18 -0
- package/bin/index.js +24 -22
- package/package.json +1 -1
- package/src/cli-help.js +24 -0
- package/src/self-upgrade.js +158 -0
- package/src/sync-all.js +159 -5
- package/src/sync-packs.js +191 -0
- package/src/sync-scene.js +146 -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,23 @@
|
|
|
1
1
|
# create-dwy
|
|
2
2
|
|
|
3
|
+
## 0.18.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- `dwy` / `dwy sync` 开头选动作:同步项目配置(按包或按条目),刷新全局外部 skill,或升级 dwy。删除 `dwy skills` / `dwy skills install` / `dwy scene`。
|
|
8
|
+
- 新增 `dwy upgrade`:把全局 `create-dwy` 升到 npm `latest` 正式版(npm/pnpm/yarn/bun)。源码目录拒绝自升级。
|
|
9
|
+
- 按包勾选写入 `.dwy/sync-state.json` 的 `selectionStyle` / `packs`。
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 0.17.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- `dwy --help` 正式列出 `dwy skills install` / `dwy skills --help`,不再标成隐藏命令。`dwy skills` 无参数时打印 skills 子命令说明。
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
3
21
|
## 0.17.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
package/bin/index.js
CHANGED
|
@@ -3,40 +3,42 @@
|
|
|
3
3
|
import { readFileSync } from 'fs'
|
|
4
4
|
import { fileURLToPath } from 'url'
|
|
5
5
|
import { dirname, resolve } from 'path'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { runDwy } from '../src/sync-all.js'
|
|
7
|
+
import { selfUpgrade } from '../src/self-upgrade.js'
|
|
8
|
+
import { buildHelpText } 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
|
-
`dwy ${pkg.version}`,
|
|
16
|
-
'',
|
|
17
|
-
'Usage:',
|
|
18
|
-
' dwy 交互同步:先选完整/仅 Skills,末步可选全局目录',
|
|
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))
|
|
30
21
|
}
|
|
31
22
|
|
|
32
|
-
if (
|
|
23
|
+
if (wantsHelp(argv)) {
|
|
33
24
|
printHelp()
|
|
34
25
|
} else if (argv.includes('--version') || argv.includes('-V')) {
|
|
35
26
|
console.log(pkg.version)
|
|
36
|
-
} else if (argv[0] === '
|
|
37
|
-
|
|
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
|
+
}
|
|
38
40
|
} else if (argv.length === 0 || argv[0] === 'sync') {
|
|
39
|
-
await
|
|
41
|
+
await runDwy()
|
|
40
42
|
} else {
|
|
41
43
|
console.error(`未知命令: ${argv.join(' ')}`)
|
|
42
44
|
printHelp()
|
package/package.json
CHANGED
package/src/cli-help.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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 同步项目配置,或刷新全局外部 skill',
|
|
18
|
+
' dwy sync 同上',
|
|
19
|
+
' dwy upgrade 把 dwy 升级到 npm 最新正式版',
|
|
20
|
+
' dwy --help 显示本说明',
|
|
21
|
+
' dwy --version 显示版本',
|
|
22
|
+
'',
|
|
23
|
+
].join('\n')
|
|
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,38 @@ export async function buildPlatformDefaultsFromLocalState(projectDir) {
|
|
|
170
208
|
return defaults.length > 0 ? defaults : ['claude', 'codex']
|
|
171
209
|
}
|
|
172
210
|
|
|
211
|
+
/**
|
|
212
|
+
* dwy 入口第一问:同步配置还是强制刷新全局外部 skill。
|
|
213
|
+
*
|
|
214
|
+
* @returns {Promise<'sync' | 'install-skills' | 'upgrade' | null>}
|
|
215
|
+
*/
|
|
216
|
+
async function promptAction() {
|
|
217
|
+
const result = await searchableSelect({
|
|
218
|
+
message: '选择要做的事',
|
|
219
|
+
options: [
|
|
220
|
+
{
|
|
221
|
+
value: ACTION_SYNC,
|
|
222
|
+
label: '同步项目 AI 配置',
|
|
223
|
+
description: '按包或按条目,把 skills / rules / hooks 写入当前项目',
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
value: ACTION_INSTALL_SKILLS,
|
|
227
|
+
label: '刷新全局外部 skill',
|
|
228
|
+
description: '强制重装 ~/.dwy/skills(pm-skills / superpowers)',
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
value: ACTION_UPGRADE,
|
|
232
|
+
label: '升级 dwy 到最新正式版',
|
|
233
|
+
description: '等同 dwy upgrade,把全局 create-dwy 升到 npm latest',
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
initialValue: ACTION_SYNC,
|
|
237
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
238
|
+
})
|
|
239
|
+
if (isCancel(result)) return null
|
|
240
|
+
return normalizeAction(result)
|
|
241
|
+
}
|
|
242
|
+
|
|
173
243
|
/**
|
|
174
244
|
* 开头选同步范围。仅 Skills 时后面不再问 rules / commands / hooks。
|
|
175
245
|
*
|
|
@@ -198,6 +268,34 @@ async function promptSyncMode(initialValue) {
|
|
|
198
268
|
return normalizeSyncMode(result)
|
|
199
269
|
}
|
|
200
270
|
|
|
271
|
+
/**
|
|
272
|
+
* 选勾选方式。按包是默认推荐;按条目保留原来的逐项勾选。
|
|
273
|
+
*
|
|
274
|
+
* @param {'items' | 'packs'} initialValue
|
|
275
|
+
* @returns {Promise<'items' | 'packs' | null>}
|
|
276
|
+
*/
|
|
277
|
+
async function promptSelectionStyle(initialValue) {
|
|
278
|
+
const result = await searchableSelect({
|
|
279
|
+
message: '选择勾选方式',
|
|
280
|
+
options: [
|
|
281
|
+
{
|
|
282
|
+
value: SELECTION_STYLE_PACKS,
|
|
283
|
+
label: '按技术栈 / 场景包(推荐)',
|
|
284
|
+
description: '先选 Vue/Python 等栈,再选产品0到1/自媒体等场景包',
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
value: SELECTION_STYLE_ITEMS,
|
|
288
|
+
label: '按条目',
|
|
289
|
+
description: '一条条勾选 Skills / Rules / Hooks',
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
initialValue,
|
|
293
|
+
placeholder: SEARCH_PLACEHOLDER,
|
|
294
|
+
})
|
|
295
|
+
if (isCancel(result)) return null
|
|
296
|
+
return normalizeSelectionStyle(result)
|
|
297
|
+
}
|
|
298
|
+
|
|
201
299
|
/**
|
|
202
300
|
* 仅 Skills 模式:只写项目/全局 skill,并清理未选中的模板 skill。
|
|
203
301
|
* 禁止调用完整平台 sync,否则空 rules/commands 会删掉项目已有项。
|
|
@@ -351,6 +449,8 @@ async function loadSyncState(projectDir) {
|
|
|
351
449
|
platforms: {},
|
|
352
450
|
skillScope: normalizeSkillScope(undefined),
|
|
353
451
|
syncMode: DEFAULT_SYNC_MODE,
|
|
452
|
+
selectionStyle: DEFAULT_SELECTION_STYLE,
|
|
453
|
+
packs: normalizePacks(undefined),
|
|
354
454
|
}
|
|
355
455
|
}
|
|
356
456
|
|
|
@@ -360,6 +460,8 @@ async function loadSyncState(projectDir) {
|
|
|
360
460
|
platforms: state.platforms || {},
|
|
361
461
|
skillScope: normalizeSkillScope(state.skillScope),
|
|
362
462
|
syncMode: normalizeSyncMode(state.syncMode),
|
|
463
|
+
selectionStyle: normalizeSelectionStyle(state.selectionStyle),
|
|
464
|
+
packs: normalizePacks(state.packs),
|
|
363
465
|
}
|
|
364
466
|
}
|
|
365
467
|
|
|
@@ -719,6 +821,37 @@ async function cleanupOpenCodePlatform(projectDir, scans) {
|
|
|
719
821
|
return removedCount
|
|
720
822
|
}
|
|
721
823
|
|
|
824
|
+
/**
|
|
825
|
+
* `dwy` / `dwy sync` 入口。先选动作再分支:同步 / 刷新外部 skill / 自升级。
|
|
826
|
+
* 测试注入 selected / action 时跳过动作菜单。
|
|
827
|
+
*
|
|
828
|
+
* @param {object} [opts]
|
|
829
|
+
*/
|
|
830
|
+
export async function runDwy(opts = {}) {
|
|
831
|
+
const skipMenu = !!(opts.selected || opts.selectedPlatforms || opts.action)
|
|
832
|
+
const action = opts.action != null
|
|
833
|
+
? normalizeAction(opts.action)
|
|
834
|
+
: (skipMenu ? ACTION_SYNC : await promptAction())
|
|
835
|
+
if (action === null) {
|
|
836
|
+
console.log(chalk.yellow('\n已取消。'))
|
|
837
|
+
return
|
|
838
|
+
}
|
|
839
|
+
if (action === ACTION_INSTALL_SKILLS) {
|
|
840
|
+
await installSkills()
|
|
841
|
+
return
|
|
842
|
+
}
|
|
843
|
+
if (action === ACTION_UPGRADE) {
|
|
844
|
+
try {
|
|
845
|
+
await selfUpgrade()
|
|
846
|
+
} catch (error) {
|
|
847
|
+
console.error(error.message)
|
|
848
|
+
process.exitCode = 1
|
|
849
|
+
}
|
|
850
|
+
return
|
|
851
|
+
}
|
|
852
|
+
await syncAll(opts)
|
|
853
|
+
}
|
|
854
|
+
|
|
722
855
|
export async function syncAll({
|
|
723
856
|
sourceDir: sourceDirOverride,
|
|
724
857
|
projectDir: projectDirOverride,
|
|
@@ -731,6 +864,8 @@ export async function syncAll({
|
|
|
731
864
|
syncMode: syncModeOverride,
|
|
732
865
|
// 全局 skill 写入的 home,测试注入假目录,禁止默认写真实 $HOME
|
|
733
866
|
homeDir,
|
|
867
|
+
// 测试注入勾选方式;交互模式在同步范围之后再问
|
|
868
|
+
selectionStyle: selectionStyleOverride,
|
|
734
869
|
} = {}) {
|
|
735
870
|
const sourceDir = sourceDirOverride || await resolveSourceDir()
|
|
736
871
|
if (!await fs.pathExists(sourceDir)) {
|
|
@@ -741,7 +876,7 @@ export async function syncAll({
|
|
|
741
876
|
|
|
742
877
|
// 开头无条件自检全局外部 skill:未装 / 清单变更(cli 加了 skill 或改了 tag)则自动更新,
|
|
743
878
|
// 已装且清单一致则零开销跳过。失败不阻塞 sync(离线/网络故障,下次再来)。
|
|
744
|
-
//
|
|
879
|
+
// 这样用户跑一次同步就把 skill 更新到位;强制重装走入口「刷新全局外部 skill」。
|
|
745
880
|
console.log(chalk.gray('检查全局外部 skill 更新...'))
|
|
746
881
|
await ensureSkillsInstalled()
|
|
747
882
|
|
|
@@ -791,9 +926,23 @@ export async function syncAll({
|
|
|
791
926
|
return
|
|
792
927
|
}
|
|
793
928
|
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
929
|
+
const selectionStyle = selectionStyleOverride
|
|
930
|
+
? normalizeSelectionStyle(selectionStyleOverride)
|
|
931
|
+
: (skipInteractive
|
|
932
|
+
? normalizeSelectionStyle(syncState.selectionStyle)
|
|
933
|
+
: await promptSelectionStyle(normalizeSelectionStyle(syncState.selectionStyle)))
|
|
934
|
+
if (selectionStyle === null) {
|
|
935
|
+
console.log(chalk.yellow('\n已取消同步。'))
|
|
936
|
+
return
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
const selected = selectedOverride || (
|
|
940
|
+
selectionStyle === SELECTION_STYLE_PACKS
|
|
941
|
+
? await interactiveSelectByPacks(scans, syncState, { skillsOnly: syncMode === SYNC_MODE_SKILLS })
|
|
942
|
+
: await interactiveSelect(scans, selectionDefaults, {
|
|
943
|
+
categories: syncMode === SYNC_MODE_SKILLS ? SKILL_ONLY_CATEGORIES : undefined,
|
|
944
|
+
})
|
|
945
|
+
)
|
|
797
946
|
if (selected === null) {
|
|
798
947
|
console.log(chalk.yellow('\n已取消同步。'))
|
|
799
948
|
return
|
|
@@ -943,6 +1092,11 @@ export async function syncAll({
|
|
|
943
1092
|
}
|
|
944
1093
|
syncState.skillScope = skillScope
|
|
945
1094
|
syncState.syncMode = syncMode
|
|
1095
|
+
syncState.selectionStyle = selectionStyle
|
|
1096
|
+
// 按条目同步不覆盖上次按包选择,方便下次再走按包
|
|
1097
|
+
if (selectionStyle === SELECTION_STYLE_PACKS) {
|
|
1098
|
+
syncState.packs = normalizePacks(selected.packs)
|
|
1099
|
+
}
|
|
946
1100
|
await saveSyncState(projectDir, syncState)
|
|
947
1101
|
|
|
948
1102
|
if (!syncedAnySupportedPlatform) {
|