create-dwy 0.18.0 → 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 +9 -0
- package/package.json +1 -1
- package/src/cli-help.js +1 -1
- package/src/sync-all.js +13 -17
- package/src/sync-scene.js +72 -49
package/CHANGELOG.md
CHANGED
package/package.json
CHANGED
package/src/cli-help.js
CHANGED
package/src/sync-all.js
CHANGED
|
@@ -209,28 +209,24 @@ export async function buildPlatformDefaultsFromLocalState(projectDir) {
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
/**
|
|
212
|
-
* dwy
|
|
212
|
+
* dwy 入口第一问:同步全局 skills,或同步项目配置。
|
|
213
|
+
* 自升级只走 `dwy upgrade`,不出现在这张菜单。
|
|
213
214
|
*
|
|
214
|
-
* @returns {Promise<'sync' | 'install-skills' |
|
|
215
|
+
* @returns {Promise<'sync' | 'install-skills' | null>}
|
|
215
216
|
*/
|
|
216
217
|
async function promptAction() {
|
|
217
218
|
const result = await searchableSelect({
|
|
218
219
|
message: '选择要做的事',
|
|
219
220
|
options: [
|
|
220
|
-
{
|
|
221
|
-
value: ACTION_SYNC,
|
|
222
|
-
label: '同步项目 AI 配置',
|
|
223
|
-
description: '按包或按条目,把 skills / rules / hooks 写入当前项目',
|
|
224
|
-
},
|
|
225
221
|
{
|
|
226
222
|
value: ACTION_INSTALL_SKILLS,
|
|
227
|
-
label: '
|
|
223
|
+
label: '同步全局 skills',
|
|
228
224
|
description: '强制重装 ~/.dwy/skills(pm-skills / superpowers)',
|
|
229
225
|
},
|
|
230
226
|
{
|
|
231
|
-
value:
|
|
232
|
-
label: '
|
|
233
|
-
description: '
|
|
227
|
+
value: ACTION_SYNC,
|
|
228
|
+
label: '同步项目配置',
|
|
229
|
+
description: '按场景和技术栈,或一条条勾选,写入当前项目',
|
|
234
230
|
},
|
|
235
231
|
],
|
|
236
232
|
initialValue: ACTION_SYNC,
|
|
@@ -276,16 +272,16 @@ async function promptSyncMode(initialValue) {
|
|
|
276
272
|
*/
|
|
277
273
|
async function promptSelectionStyle(initialValue) {
|
|
278
274
|
const result = await searchableSelect({
|
|
279
|
-
message: '
|
|
275
|
+
message: '项目配置怎么勾',
|
|
280
276
|
options: [
|
|
281
277
|
{
|
|
282
278
|
value: SELECTION_STYLE_PACKS,
|
|
283
|
-
label: '
|
|
284
|
-
description: '
|
|
279
|
+
label: '按场景和技术栈(推荐)',
|
|
280
|
+
description: '先选栈和场景包,再展示已勾子条目,可取消',
|
|
285
281
|
},
|
|
286
282
|
{
|
|
287
283
|
value: SELECTION_STYLE_ITEMS,
|
|
288
|
-
label: '
|
|
284
|
+
label: '一个一个选',
|
|
289
285
|
description: '一条条勾选 Skills / Rules / Hooks',
|
|
290
286
|
},
|
|
291
287
|
],
|
|
@@ -822,8 +818,8 @@ async function cleanupOpenCodePlatform(projectDir, scans) {
|
|
|
822
818
|
}
|
|
823
819
|
|
|
824
820
|
/**
|
|
825
|
-
* `dwy` / `dwy sync`
|
|
826
|
-
*
|
|
821
|
+
* `dwy` / `dwy sync` 入口。先选动作再分支:同步全局 skills / 同步项目配置。
|
|
822
|
+
* 自升级只走 `dwy upgrade`。测试注入 selected / action 时跳过动作菜单。
|
|
827
823
|
*
|
|
828
824
|
* @param {object} [opts]
|
|
829
825
|
*/
|
package/src/sync-scene.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 按包勾选交互:技术栈包 → 场景包 →
|
|
3
|
-
* 由 dwy / dwy sync
|
|
2
|
+
* 按包勾选交互:技术栈包 → 场景包 → 展示已预勾的子条目(可取消)。
|
|
3
|
+
* 由 dwy / dwy sync 在「按场景和技术栈」模式下调用。
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import { isCancel } from '@clack/prompts'
|
|
7
7
|
import { SEARCH_PLACEHOLDER, searchableMultiselect, searchableSelect } from './searchable-select.js'
|
|
8
8
|
import { SCENE_PACKS, STACK_PACKS, expandPackSelection, normalizePacks } from './sync-packs.js'
|
|
9
|
-
import { promptSelection } from './sync.js'
|
|
10
9
|
import { chalk } from './utils.js'
|
|
11
10
|
|
|
11
|
+
const TYPE_LABELS = {
|
|
12
|
+
skills: 'Skills',
|
|
13
|
+
rules: 'Rules',
|
|
14
|
+
hooks: 'Hooks',
|
|
15
|
+
}
|
|
16
|
+
|
|
12
17
|
/**
|
|
13
18
|
* 把包目录转成可搜索选项。
|
|
14
19
|
*
|
|
@@ -23,7 +28,52 @@ function packOptions(packs) {
|
|
|
23
28
|
}
|
|
24
29
|
|
|
25
30
|
/**
|
|
26
|
-
*
|
|
31
|
+
* 子条目在多选里的稳定 key。类型前缀避免 skill/rule 重名撞车。
|
|
32
|
+
*
|
|
33
|
+
* @param {'skills' | 'rules' | 'hooks'} type
|
|
34
|
+
* @param {string} name
|
|
35
|
+
*/
|
|
36
|
+
export function packItemKey(type, name) {
|
|
37
|
+
return `${type}:${name}`
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 把包展开结果打成一条可搜索列表。label 含类型 + 分类 + 名字。
|
|
42
|
+
*
|
|
43
|
+
* @param {{ skills: object[], rules: object[], hooks: object[] }} selected
|
|
44
|
+
*/
|
|
45
|
+
export function buildPackItemOptions(selected) {
|
|
46
|
+
const rows = []
|
|
47
|
+
for (const type of ['skills', 'rules', 'hooks']) {
|
|
48
|
+
for (const item of selected[type] || []) {
|
|
49
|
+
rows.push({
|
|
50
|
+
value: packItemKey(type, item.name),
|
|
51
|
+
label: `${TYPE_LABELS[type]} / ${item.category || '其他'} / ${item.name}`,
|
|
52
|
+
description: item.description,
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return rows
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* 按用户勾选的 key 从展开结果里筛子条目。未勾的丢掉。
|
|
61
|
+
*
|
|
62
|
+
* @param {{ skills: object[], rules: object[], hooks: object[] }} expanded
|
|
63
|
+
* @param {string[]} keys
|
|
64
|
+
*/
|
|
65
|
+
export function splitPackItemKeys(expanded, keys) {
|
|
66
|
+
const set = new Set(keys)
|
|
67
|
+
return {
|
|
68
|
+
skills: expanded.skills.filter(item => set.has(packItemKey('skills', item.name))),
|
|
69
|
+
rules: expanded.rules.filter(item => set.has(packItemKey('rules', item.name))),
|
|
70
|
+
hooks: expanded.hooks.filter(item => set.has(packItemKey('hooks', item.name))),
|
|
71
|
+
commands: [],
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 打印包展开条数,随后立刻列出预勾子条目。
|
|
27
77
|
*
|
|
28
78
|
* @param {string[]} stacks
|
|
29
79
|
* @param {string[]} scenes
|
|
@@ -39,7 +89,7 @@ function printPackSummary(stacks, scenes, selected) {
|
|
|
39
89
|
}
|
|
40
90
|
|
|
41
91
|
/**
|
|
42
|
-
*
|
|
92
|
+
* 按包选择条目。栈/场景选完后展示聚合子条目,默认全勾,用户可取消。
|
|
43
93
|
* 返回 { skills, rules, commands, hooks, packs } 或 null(取消)。
|
|
44
94
|
*
|
|
45
95
|
* @param {object} scans
|
|
@@ -51,8 +101,6 @@ export async function interactiveSelectByPacks(scans, syncState, { skillsOnly =
|
|
|
51
101
|
let stacks = previous.stacks.length > 0 ? previous.stacks : ['common']
|
|
52
102
|
let scenes = previous.scenes
|
|
53
103
|
let step = 'stacks'
|
|
54
|
-
/** 微调后的展开结果;返回改包时丢弃,重新 expand。 */
|
|
55
|
-
let selected = null
|
|
56
104
|
|
|
57
105
|
while (true) {
|
|
58
106
|
if (step === 'stacks') {
|
|
@@ -66,7 +114,6 @@ export async function interactiveSelectByPacks(scans, syncState, { skillsOnly =
|
|
|
66
114
|
})
|
|
67
115
|
if (isCancel(result)) return null
|
|
68
116
|
stacks = result
|
|
69
|
-
selected = null
|
|
70
117
|
|
|
71
118
|
const nav = await searchableSelect({
|
|
72
119
|
message: `已选 ${stacks.length} 个技术栈。下一步:`,
|
|
@@ -94,53 +141,29 @@ export async function interactiveSelectByPacks(scans, syncState, { skillsOnly =
|
|
|
94
141
|
})
|
|
95
142
|
if (isCancel(result)) return null
|
|
96
143
|
scenes = result
|
|
97
|
-
|
|
98
|
-
step = 'confirm'
|
|
144
|
+
step = 'items'
|
|
99
145
|
continue
|
|
100
146
|
}
|
|
101
147
|
|
|
102
|
-
|
|
103
|
-
printPackSummary(stacks, scenes,
|
|
148
|
+
const expanded = expandPackSelection(scans, { stacks, scenes, skillsOnly })
|
|
149
|
+
printPackSummary(stacks, scenes, expanded)
|
|
150
|
+
const options = buildPackItemOptions(expanded)
|
|
151
|
+
if (options.length === 0) {
|
|
152
|
+
return { skills: [], rules: [], commands: [], hooks: [], packs: { stacks, scenes } }
|
|
153
|
+
}
|
|
104
154
|
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
] : []),
|
|
112
|
-
{ label: '返回改技术栈', value: 'stacks' },
|
|
113
|
-
{ label: '返回改场景包', value: 'scenes' },
|
|
114
|
-
{ label: '取消同步', value: 'cancel' },
|
|
115
|
-
]
|
|
116
|
-
const final = await searchableSelect({
|
|
117
|
-
message: '包已帮你勾好。确认提交,或微调加减条目:',
|
|
118
|
-
options: confirmOptions,
|
|
119
|
-
initialValue: 'confirm',
|
|
155
|
+
const keys = await searchableMultiselect({
|
|
156
|
+
message: '包已预勾子条目,取消不需要的即可',
|
|
157
|
+
options,
|
|
158
|
+
initialValues: options.map(option => option.value),
|
|
159
|
+
required: false,
|
|
160
|
+
maxItems: 15,
|
|
120
161
|
placeholder: SEARCH_PLACEHOLDER,
|
|
121
162
|
})
|
|
122
|
-
if (isCancel(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
commands: [],
|
|
127
|
-
packs: { stacks, scenes },
|
|
128
|
-
}
|
|
163
|
+
if (isCancel(keys)) return null
|
|
164
|
+
return {
|
|
165
|
+
...splitPackItemKeys(expanded, keys),
|
|
166
|
+
packs: { stacks, scenes },
|
|
129
167
|
}
|
|
130
|
-
if (final === 'stacks' || final === 'scenes') {
|
|
131
|
-
step = final
|
|
132
|
-
selected = null
|
|
133
|
-
continue
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// 完整清单 + 包内项预勾:可减包内项,也可加包外项
|
|
137
|
-
const catalog = scans[final] || []
|
|
138
|
-
const tuned = await promptSelection(
|
|
139
|
-
catalog,
|
|
140
|
-
final === 'skills' ? 'Skills' : final === 'rules' ? 'Rules' : 'Hooks',
|
|
141
|
-
new Set(selected[final].map(item => item.name)),
|
|
142
|
-
)
|
|
143
|
-
if (tuned === null) return null
|
|
144
|
-
selected = { ...selected, [final]: tuned }
|
|
145
168
|
}
|
|
146
169
|
}
|