universal-dev-standards 3.0.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/LICENSE +21 -0
- package/README.md +248 -0
- package/bin/uds.js +56 -0
- package/package.json +63 -0
- package/src/commands/check.js +149 -0
- package/src/commands/configure.js +221 -0
- package/src/commands/init.js +665 -0
- package/src/commands/list.js +100 -0
- package/src/commands/update.js +186 -0
- package/src/index.js +7 -0
- package/src/prompts/init.js +702 -0
- package/src/prompts/integrations.js +453 -0
- package/src/utils/copier.js +143 -0
- package/src/utils/detector.js +159 -0
- package/src/utils/github.js +508 -0
- package/src/utils/integration-generator.js +1694 -0
- package/src/utils/registry.js +207 -0
- package/standards-registry.json +658 -0
|
@@ -0,0 +1,702 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Prompt for AI tools being used
|
|
6
|
+
* @param {Object} detected - Detected AI tools from project
|
|
7
|
+
* @returns {Promise<string[]>} Selected AI tools
|
|
8
|
+
*/
|
|
9
|
+
export async function promptAITools(detected = {}) {
|
|
10
|
+
console.log();
|
|
11
|
+
console.log(chalk.cyan('AI Development Tools:'));
|
|
12
|
+
console.log(chalk.gray(' Select the AI coding assistants you use with this project'));
|
|
13
|
+
console.log();
|
|
14
|
+
|
|
15
|
+
const { tools } = await inquirer.prompt([
|
|
16
|
+
{
|
|
17
|
+
type: 'checkbox',
|
|
18
|
+
name: 'tools',
|
|
19
|
+
message: 'Which AI tools are you using?',
|
|
20
|
+
choices: [
|
|
21
|
+
new inquirer.Separator(chalk.gray('── Dynamic Skills ──')),
|
|
22
|
+
{
|
|
23
|
+
name: `${chalk.green('Claude Code')} ${chalk.gray('(推薦)')} - Anthropic CLI with dynamic Skills`,
|
|
24
|
+
value: 'claude-code',
|
|
25
|
+
checked: detected.claudeCode || false
|
|
26
|
+
},
|
|
27
|
+
new inquirer.Separator(chalk.gray('── Static Rule Files ──')),
|
|
28
|
+
{
|
|
29
|
+
name: `Cursor ${chalk.gray('(.cursorrules)')}`,
|
|
30
|
+
value: 'cursor',
|
|
31
|
+
checked: detected.cursor || false
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
name: `Windsurf ${chalk.gray('(.windsurfrules)')}`,
|
|
35
|
+
value: 'windsurf',
|
|
36
|
+
checked: detected.windsurf || false
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: `Cline ${chalk.gray('(.clinerules)')}`,
|
|
40
|
+
value: 'cline',
|
|
41
|
+
checked: detected.cline || false
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
name: `GitHub Copilot ${chalk.gray('(.github/copilot-instructions.md)')}`,
|
|
45
|
+
value: 'copilot',
|
|
46
|
+
checked: detected.copilot || false
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
name: `Google Antigravity ${chalk.gray('(INSTRUCTIONS.md)')} - Gemini Agent`,
|
|
50
|
+
value: 'antigravity',
|
|
51
|
+
checked: detected.antigravity || false
|
|
52
|
+
},
|
|
53
|
+
new inquirer.Separator(),
|
|
54
|
+
{
|
|
55
|
+
name: chalk.gray('None / Skip'),
|
|
56
|
+
value: 'none'
|
|
57
|
+
}
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
]);
|
|
61
|
+
|
|
62
|
+
// Filter out 'none' and separators
|
|
63
|
+
const filtered = tools.filter(t => t !== 'none' && typeof t === 'string');
|
|
64
|
+
return filtered;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Prompt for Skills installation location
|
|
69
|
+
* @returns {Promise<string>} 'user', 'project', or 'none'
|
|
70
|
+
*/
|
|
71
|
+
export async function promptSkillsInstallLocation() {
|
|
72
|
+
console.log();
|
|
73
|
+
console.log(chalk.cyan('Skills Installation:'));
|
|
74
|
+
console.log(chalk.gray(' Choose where to install Claude Code Skills'));
|
|
75
|
+
console.log();
|
|
76
|
+
|
|
77
|
+
const { location } = await inquirer.prompt([
|
|
78
|
+
{
|
|
79
|
+
type: 'list',
|
|
80
|
+
name: 'location',
|
|
81
|
+
message: 'Where should Skills be installed?',
|
|
82
|
+
choices: [
|
|
83
|
+
{
|
|
84
|
+
name: `${chalk.green('User Level')} ${chalk.gray('(推薦)')} - ~/.claude/skills/ (shared across projects)`,
|
|
85
|
+
value: 'user'
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: `${chalk.blue('Project Level')} - .claude/skills/ (project-specific)`,
|
|
89
|
+
value: 'project'
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
name: `${chalk.gray('Skip')} - Do not install Skills`,
|
|
93
|
+
value: 'none'
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
default: 'user'
|
|
97
|
+
}
|
|
98
|
+
]);
|
|
99
|
+
|
|
100
|
+
// Show explanation
|
|
101
|
+
console.log();
|
|
102
|
+
if (location === 'user') {
|
|
103
|
+
console.log(chalk.gray(' → Skills will be installed to ~/.claude/skills/'));
|
|
104
|
+
console.log(chalk.gray(' → Available across all your projects'));
|
|
105
|
+
} else if (location === 'project') {
|
|
106
|
+
console.log(chalk.gray(' → Skills will be installed to .claude/skills/'));
|
|
107
|
+
console.log(chalk.gray(' → Only available in this project'));
|
|
108
|
+
console.log(chalk.gray(' → Consider adding .claude/skills/ to .gitignore'));
|
|
109
|
+
} else {
|
|
110
|
+
console.log(chalk.gray(' → No Skills will be installed'));
|
|
111
|
+
console.log(chalk.gray(' → Full standards will be copied to .standards/'));
|
|
112
|
+
}
|
|
113
|
+
console.log();
|
|
114
|
+
|
|
115
|
+
return location;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Prompt for Skills update (dual-level check)
|
|
120
|
+
* @param {Object|null} projectInfo - Project-level Skills info
|
|
121
|
+
* @param {Object|null} userInfo - User-level Skills info
|
|
122
|
+
* @param {string} latestVersion - Latest available version
|
|
123
|
+
* @returns {Promise<Object>} Update decision { action: 'both'|'project'|'user'|'none', targets: string[] }
|
|
124
|
+
*/
|
|
125
|
+
export async function promptSkillsUpdate(projectInfo, userInfo, latestVersion) {
|
|
126
|
+
const choices = [];
|
|
127
|
+
const needsUpdate = [];
|
|
128
|
+
|
|
129
|
+
// Check project-level
|
|
130
|
+
if (projectInfo?.installed) {
|
|
131
|
+
const projectVersion = projectInfo.version || 'unknown';
|
|
132
|
+
const projectNeedsUpdate = projectVersion !== latestVersion;
|
|
133
|
+
if (projectNeedsUpdate) {
|
|
134
|
+
needsUpdate.push('project');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Check user-level
|
|
139
|
+
if (userInfo?.installed) {
|
|
140
|
+
const userVersion = userInfo.version || 'unknown';
|
|
141
|
+
const userNeedsUpdate = userVersion !== latestVersion;
|
|
142
|
+
if (userNeedsUpdate) {
|
|
143
|
+
needsUpdate.push('user');
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// If nothing needs update
|
|
148
|
+
if (needsUpdate.length === 0) {
|
|
149
|
+
console.log(chalk.green('✓ All Skills installations are up to date'));
|
|
150
|
+
return { action: 'none', targets: [] };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Build choices based on what needs updating
|
|
154
|
+
console.log();
|
|
155
|
+
console.log(chalk.cyan('Skills Update Available:'));
|
|
156
|
+
|
|
157
|
+
if (projectInfo?.installed) {
|
|
158
|
+
const pVer = projectInfo.version || 'unknown';
|
|
159
|
+
const pStatus = pVer === latestVersion
|
|
160
|
+
? chalk.green('✓ up to date')
|
|
161
|
+
: chalk.yellow(`v${pVer} → v${latestVersion}`);
|
|
162
|
+
console.log(chalk.gray(` Project level (.claude/skills/): ${pStatus}`));
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (userInfo?.installed) {
|
|
166
|
+
const uVer = userInfo.version || 'unknown';
|
|
167
|
+
const uStatus = uVer === latestVersion
|
|
168
|
+
? chalk.green('✓ up to date')
|
|
169
|
+
: chalk.yellow(`v${uVer} → v${latestVersion}`);
|
|
170
|
+
console.log(chalk.gray(` User level (~/.claude/skills/): ${uStatus}`));
|
|
171
|
+
}
|
|
172
|
+
console.log();
|
|
173
|
+
|
|
174
|
+
// Build update choices
|
|
175
|
+
if (needsUpdate.includes('project') && needsUpdate.includes('user')) {
|
|
176
|
+
choices.push({
|
|
177
|
+
name: `${chalk.green('Update Both')} - Update all Skills installations`,
|
|
178
|
+
value: 'both'
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (needsUpdate.includes('project')) {
|
|
183
|
+
choices.push({
|
|
184
|
+
name: `${chalk.blue('Update Project Level')} - Only update .claude/skills/`,
|
|
185
|
+
value: 'project'
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (needsUpdate.includes('user')) {
|
|
190
|
+
choices.push({
|
|
191
|
+
name: `${chalk.blue('Update User Level')} - Only update ~/.claude/skills/`,
|
|
192
|
+
value: 'user'
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
choices.push({
|
|
197
|
+
name: `${chalk.gray('Skip')} - Keep current versions`,
|
|
198
|
+
value: 'none'
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const { action } = await inquirer.prompt([
|
|
202
|
+
{
|
|
203
|
+
type: 'list',
|
|
204
|
+
name: 'action',
|
|
205
|
+
message: 'What would you like to do?',
|
|
206
|
+
choices,
|
|
207
|
+
default: needsUpdate.length === 2 ? 'both' : needsUpdate[0]
|
|
208
|
+
}
|
|
209
|
+
]);
|
|
210
|
+
|
|
211
|
+
// Determine targets
|
|
212
|
+
let targets = [];
|
|
213
|
+
if (action === 'both') {
|
|
214
|
+
targets = ['project', 'user'];
|
|
215
|
+
} else if (action === 'project' || action === 'user') {
|
|
216
|
+
targets = [action];
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
return { action, targets };
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Prompt for standards scope when Skills are installed
|
|
224
|
+
* @param {boolean} hasSkills - Whether Skills are installed
|
|
225
|
+
* @returns {Promise<string>} 'full' or 'minimal'
|
|
226
|
+
*/
|
|
227
|
+
export async function promptStandardsScope(hasSkills) {
|
|
228
|
+
if (!hasSkills) {
|
|
229
|
+
return 'full';
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
console.log();
|
|
233
|
+
console.log(chalk.cyan('Standards Scope:'));
|
|
234
|
+
console.log(chalk.gray(' Skills cover some standards dynamically. Choose what to install:'));
|
|
235
|
+
console.log();
|
|
236
|
+
|
|
237
|
+
const { scope } = await inquirer.prompt([
|
|
238
|
+
{
|
|
239
|
+
type: 'list',
|
|
240
|
+
name: 'scope',
|
|
241
|
+
message: 'Select standards installation scope:',
|
|
242
|
+
choices: [
|
|
243
|
+
{
|
|
244
|
+
name: `${chalk.green('Minimal')} ${chalk.gray('(推薦)')} - Only static standards (Skills cover the rest)`,
|
|
245
|
+
value: 'minimal'
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: `${chalk.blue('Full')} - Install all standards (includes Skills-covered)`,
|
|
249
|
+
value: 'full'
|
|
250
|
+
}
|
|
251
|
+
],
|
|
252
|
+
default: 'minimal'
|
|
253
|
+
}
|
|
254
|
+
]);
|
|
255
|
+
|
|
256
|
+
// Show explanation
|
|
257
|
+
console.log();
|
|
258
|
+
if (scope === 'minimal') {
|
|
259
|
+
console.log(chalk.gray(' → Only reference standards will be copied'));
|
|
260
|
+
console.log(chalk.gray(' → Skills provide dynamic guidance for covered standards'));
|
|
261
|
+
} else {
|
|
262
|
+
console.log(chalk.gray(' → All standards will be copied to .standards/'));
|
|
263
|
+
console.log(chalk.gray(' → Includes both static files and Skills-covered content'));
|
|
264
|
+
}
|
|
265
|
+
console.log();
|
|
266
|
+
|
|
267
|
+
return scope;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Prompt for output format (AI or Human-readable)
|
|
272
|
+
* @returns {Promise<string>} 'ai', 'human', or 'both'
|
|
273
|
+
*/
|
|
274
|
+
export async function promptFormat() {
|
|
275
|
+
const { format } = await inquirer.prompt([
|
|
276
|
+
{
|
|
277
|
+
type: 'list',
|
|
278
|
+
name: 'format',
|
|
279
|
+
message: 'Select standards format:',
|
|
280
|
+
choices: [
|
|
281
|
+
{
|
|
282
|
+
name: `${chalk.green('AI-Optimized')} ${chalk.gray('(推薦)')} - Token-efficient YAML for AI assistants`,
|
|
283
|
+
value: 'ai'
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
name: `${chalk.blue('Human-Readable')} - Full Markdown documentation`,
|
|
287
|
+
value: 'human'
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
name: `${chalk.yellow('Both')} - Include both formats`,
|
|
291
|
+
value: 'both'
|
|
292
|
+
}
|
|
293
|
+
],
|
|
294
|
+
default: 'ai'
|
|
295
|
+
}
|
|
296
|
+
]);
|
|
297
|
+
|
|
298
|
+
return format;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Prompt for Git workflow strategy
|
|
303
|
+
* @returns {Promise<string>} Selected workflow ID
|
|
304
|
+
*/
|
|
305
|
+
export async function promptGitWorkflow() {
|
|
306
|
+
const { workflow } = await inquirer.prompt([
|
|
307
|
+
{
|
|
308
|
+
type: 'list',
|
|
309
|
+
name: 'workflow',
|
|
310
|
+
message: 'Select Git branching strategy:',
|
|
311
|
+
choices: [
|
|
312
|
+
{
|
|
313
|
+
name: `${chalk.green('GitHub Flow')} ${chalk.gray('(推薦)')} - Simple, continuous deployment`,
|
|
314
|
+
value: 'github-flow'
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
name: `${chalk.blue('GitFlow')} - Structured releases with develop/release branches`,
|
|
318
|
+
value: 'gitflow'
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
name: `${chalk.yellow('Trunk-Based')} - Direct commits to main, feature flags`,
|
|
322
|
+
value: 'trunk-based'
|
|
323
|
+
}
|
|
324
|
+
],
|
|
325
|
+
default: 'github-flow'
|
|
326
|
+
}
|
|
327
|
+
]);
|
|
328
|
+
|
|
329
|
+
return workflow;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Prompt for merge strategy
|
|
334
|
+
* @returns {Promise<string>} Selected merge strategy ID
|
|
335
|
+
*/
|
|
336
|
+
export async function promptMergeStrategy() {
|
|
337
|
+
const { strategy } = await inquirer.prompt([
|
|
338
|
+
{
|
|
339
|
+
type: 'list',
|
|
340
|
+
name: 'strategy',
|
|
341
|
+
message: 'Select merge strategy:',
|
|
342
|
+
choices: [
|
|
343
|
+
{
|
|
344
|
+
name: `${chalk.green('Squash Merge')} ${chalk.gray('(推薦)')} - Clean history, one commit per PR`,
|
|
345
|
+
value: 'squash'
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
name: `${chalk.blue('Merge Commit')} - Preserve full branch history`,
|
|
349
|
+
value: 'merge-commit'
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
name: `${chalk.yellow('Rebase + Fast-Forward')} - Linear history, advanced`,
|
|
353
|
+
value: 'rebase-ff'
|
|
354
|
+
}
|
|
355
|
+
],
|
|
356
|
+
default: 'squash'
|
|
357
|
+
}
|
|
358
|
+
]);
|
|
359
|
+
|
|
360
|
+
return strategy;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Prompt for commit message language
|
|
365
|
+
* @returns {Promise<string>} Selected language ID
|
|
366
|
+
*/
|
|
367
|
+
export async function promptCommitLanguage() {
|
|
368
|
+
const { language } = await inquirer.prompt([
|
|
369
|
+
{
|
|
370
|
+
type: 'list',
|
|
371
|
+
name: 'language',
|
|
372
|
+
message: 'Select commit message language:',
|
|
373
|
+
choices: [
|
|
374
|
+
{
|
|
375
|
+
name: `${chalk.green('English')} ${chalk.gray('(推薦)')} - Standard international format`,
|
|
376
|
+
value: 'english'
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
name: `${chalk.blue('Traditional Chinese')} ${chalk.gray('(繁體中文)')} - For Chinese-speaking teams`,
|
|
380
|
+
value: 'traditional-chinese'
|
|
381
|
+
},
|
|
382
|
+
{
|
|
383
|
+
name: `${chalk.yellow('Bilingual')} ${chalk.gray('(雙語)')} - Both English and Chinese`,
|
|
384
|
+
value: 'bilingual'
|
|
385
|
+
}
|
|
386
|
+
],
|
|
387
|
+
default: 'english'
|
|
388
|
+
}
|
|
389
|
+
]);
|
|
390
|
+
|
|
391
|
+
return language;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Prompt for test levels to include
|
|
396
|
+
* @returns {Promise<string[]>} Selected test level IDs
|
|
397
|
+
*/
|
|
398
|
+
export async function promptTestLevels() {
|
|
399
|
+
const { levels } = await inquirer.prompt([
|
|
400
|
+
{
|
|
401
|
+
type: 'checkbox',
|
|
402
|
+
name: 'levels',
|
|
403
|
+
message: 'Select test levels to include:',
|
|
404
|
+
choices: [
|
|
405
|
+
{
|
|
406
|
+
name: `Unit Testing ${chalk.gray('(70% pyramid base)')}`,
|
|
407
|
+
value: 'unit-testing',
|
|
408
|
+
checked: true
|
|
409
|
+
},
|
|
410
|
+
{
|
|
411
|
+
name: `Integration Testing ${chalk.gray('(20%)')}`,
|
|
412
|
+
value: 'integration-testing',
|
|
413
|
+
checked: true
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: `System Testing ${chalk.gray('(7%)')}`,
|
|
417
|
+
value: 'system-testing',
|
|
418
|
+
checked: false
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
name: `E2E Testing ${chalk.gray('(3% pyramid top)')}`,
|
|
422
|
+
value: 'e2e-testing',
|
|
423
|
+
checked: false
|
|
424
|
+
}
|
|
425
|
+
]
|
|
426
|
+
}
|
|
427
|
+
]);
|
|
428
|
+
|
|
429
|
+
return levels;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Prompt for all standard options
|
|
434
|
+
* @param {number} level - Adoption level
|
|
435
|
+
* @returns {Promise<Object>} Selected options
|
|
436
|
+
*/
|
|
437
|
+
export async function promptStandardOptions(level) {
|
|
438
|
+
const options = {};
|
|
439
|
+
|
|
440
|
+
console.log();
|
|
441
|
+
console.log(chalk.cyan('Standard Options:'));
|
|
442
|
+
console.log(chalk.gray(' Configure your preferred options for each standard'));
|
|
443
|
+
console.log();
|
|
444
|
+
|
|
445
|
+
// Git workflow options (level 2+)
|
|
446
|
+
if (level >= 2) {
|
|
447
|
+
options.workflow = await promptGitWorkflow();
|
|
448
|
+
options.merge_strategy = await promptMergeStrategy();
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// Commit message options (level 1+)
|
|
452
|
+
options.commit_language = await promptCommitLanguage();
|
|
453
|
+
|
|
454
|
+
// Testing options (level 2+)
|
|
455
|
+
if (level >= 2) {
|
|
456
|
+
options.test_levels = await promptTestLevels();
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
return options;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Prompt for installation mode
|
|
464
|
+
* @returns {Promise<string>} 'skills' or 'full'
|
|
465
|
+
*/
|
|
466
|
+
export async function promptInstallMode() {
|
|
467
|
+
const { mode } = await inquirer.prompt([
|
|
468
|
+
{
|
|
469
|
+
type: 'list',
|
|
470
|
+
name: 'mode',
|
|
471
|
+
message: 'Select installation mode:',
|
|
472
|
+
choices: [
|
|
473
|
+
{
|
|
474
|
+
name: `${chalk.green('Skills Mode')} ${chalk.gray('(推薦)')} - Use Claude Code Skills`,
|
|
475
|
+
value: 'skills'
|
|
476
|
+
},
|
|
477
|
+
{
|
|
478
|
+
name: `${chalk.yellow('Full Mode')} - Install all standards without Skills`,
|
|
479
|
+
value: 'full'
|
|
480
|
+
}
|
|
481
|
+
],
|
|
482
|
+
default: 'skills'
|
|
483
|
+
}
|
|
484
|
+
]);
|
|
485
|
+
|
|
486
|
+
// Show explanation based on selection
|
|
487
|
+
console.log();
|
|
488
|
+
if (mode === 'skills') {
|
|
489
|
+
console.log(chalk.gray(' → Skills will be installed to ~/.claude/skills/'));
|
|
490
|
+
console.log(chalk.gray(' → Only static standards will be copied to .standards/'));
|
|
491
|
+
} else {
|
|
492
|
+
console.log(chalk.gray(' → All standards will be copied to .standards/'));
|
|
493
|
+
console.log(chalk.gray(' → No Skills will be installed'));
|
|
494
|
+
}
|
|
495
|
+
console.log();
|
|
496
|
+
|
|
497
|
+
return mode;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* Prompt for Skills upgrade action
|
|
502
|
+
* @param {string} installedVersion - Currently installed version (may be null)
|
|
503
|
+
* @param {string} latestVersion - Latest available version
|
|
504
|
+
* @returns {Promise<string>} 'upgrade', 'keep', or 'reinstall'
|
|
505
|
+
*/
|
|
506
|
+
export async function promptSkillsUpgrade(installedVersion, latestVersion) {
|
|
507
|
+
const versionDisplay = installedVersion
|
|
508
|
+
? `v${installedVersion} → v${latestVersion}`
|
|
509
|
+
: `unknown → v${latestVersion}`;
|
|
510
|
+
|
|
511
|
+
const { action } = await inquirer.prompt([
|
|
512
|
+
{
|
|
513
|
+
type: 'list',
|
|
514
|
+
name: 'action',
|
|
515
|
+
message: `Skills detected (${versionDisplay}). What would you like to do?`,
|
|
516
|
+
choices: [
|
|
517
|
+
{
|
|
518
|
+
name: `${chalk.green('Upgrade')} - Update to latest version`,
|
|
519
|
+
value: 'upgrade'
|
|
520
|
+
},
|
|
521
|
+
{
|
|
522
|
+
name: `${chalk.gray('Keep')} - Keep current version`,
|
|
523
|
+
value: 'keep'
|
|
524
|
+
},
|
|
525
|
+
{
|
|
526
|
+
name: `${chalk.yellow('Reinstall')} - Fresh install (overwrites existing)`,
|
|
527
|
+
value: 'reinstall'
|
|
528
|
+
}
|
|
529
|
+
],
|
|
530
|
+
default: 'upgrade'
|
|
531
|
+
}
|
|
532
|
+
]);
|
|
533
|
+
|
|
534
|
+
return action;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* Prompt for adoption level
|
|
539
|
+
* @returns {Promise<number>} Selected level
|
|
540
|
+
*/
|
|
541
|
+
export async function promptLevel() {
|
|
542
|
+
const { level } = await inquirer.prompt([
|
|
543
|
+
{
|
|
544
|
+
type: 'list',
|
|
545
|
+
name: 'level',
|
|
546
|
+
message: 'Select adoption level:',
|
|
547
|
+
choices: [
|
|
548
|
+
{
|
|
549
|
+
name: `${chalk.green('Level 1: Essential')} ${chalk.gray('(基本)')} - Minimum viable standards`,
|
|
550
|
+
value: 1
|
|
551
|
+
},
|
|
552
|
+
{
|
|
553
|
+
name: `${chalk.yellow('Level 2: Recommended')} ${chalk.gray('(推薦)')} - Professional quality`,
|
|
554
|
+
value: 2
|
|
555
|
+
},
|
|
556
|
+
{
|
|
557
|
+
name: `${chalk.blue('Level 3: Enterprise')} ${chalk.gray('(企業)')} - Comprehensive standards`,
|
|
558
|
+
value: 3
|
|
559
|
+
}
|
|
560
|
+
],
|
|
561
|
+
default: 1
|
|
562
|
+
}
|
|
563
|
+
]);
|
|
564
|
+
|
|
565
|
+
return level;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/**
|
|
569
|
+
* Prompt for language extension
|
|
570
|
+
* @param {Object} detected - Detected languages
|
|
571
|
+
* @returns {Promise<string|null>} Selected language or null
|
|
572
|
+
*/
|
|
573
|
+
export async function promptLanguage(detected) {
|
|
574
|
+
const choices = [];
|
|
575
|
+
|
|
576
|
+
if (detected.csharp) {
|
|
577
|
+
choices.push({ name: 'C# Style Guide', value: 'csharp', checked: true });
|
|
578
|
+
}
|
|
579
|
+
if (detected.php) {
|
|
580
|
+
choices.push({ name: 'PHP Style Guide (PSR-12)', value: 'php', checked: true });
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (choices.length === 0) {
|
|
584
|
+
return null;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const { languages } = await inquirer.prompt([
|
|
588
|
+
{
|
|
589
|
+
type: 'checkbox',
|
|
590
|
+
name: 'languages',
|
|
591
|
+
message: 'Detected language(s). Select style guides to include:',
|
|
592
|
+
choices
|
|
593
|
+
}
|
|
594
|
+
]);
|
|
595
|
+
|
|
596
|
+
return languages;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* Prompt for framework extension
|
|
601
|
+
* @param {Object} detected - Detected frameworks
|
|
602
|
+
* @returns {Promise<string|null>} Selected framework or null
|
|
603
|
+
*/
|
|
604
|
+
export async function promptFramework(detected) {
|
|
605
|
+
const choices = [];
|
|
606
|
+
|
|
607
|
+
if (detected['fat-free']) {
|
|
608
|
+
choices.push({ name: 'Fat-Free Framework Patterns', value: 'fat-free', checked: true });
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
if (choices.length === 0) {
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
const { frameworks } = await inquirer.prompt([
|
|
616
|
+
{
|
|
617
|
+
type: 'checkbox',
|
|
618
|
+
name: 'frameworks',
|
|
619
|
+
message: 'Detected framework(s). Select patterns to include:',
|
|
620
|
+
choices
|
|
621
|
+
}
|
|
622
|
+
]);
|
|
623
|
+
|
|
624
|
+
return frameworks;
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Prompt for locale
|
|
629
|
+
* @returns {Promise<string|null>} Selected locale or null
|
|
630
|
+
*/
|
|
631
|
+
export async function promptLocale() {
|
|
632
|
+
const { useLocale } = await inquirer.prompt([
|
|
633
|
+
{
|
|
634
|
+
type: 'confirm',
|
|
635
|
+
name: 'useLocale',
|
|
636
|
+
message: 'Use Traditional Chinese (繁體中文) locale?',
|
|
637
|
+
default: false
|
|
638
|
+
}
|
|
639
|
+
]);
|
|
640
|
+
|
|
641
|
+
return useLocale ? 'zh-tw' : null;
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* Prompt for AI tool integrations
|
|
646
|
+
* @param {Object} detected - Detected AI tools
|
|
647
|
+
* @returns {Promise<string[]>} Selected integrations
|
|
648
|
+
*/
|
|
649
|
+
export async function promptIntegrations(detected) {
|
|
650
|
+
const choices = [];
|
|
651
|
+
|
|
652
|
+
// Existing tools (checked if detected)
|
|
653
|
+
choices.push({
|
|
654
|
+
name: 'Cursor (.cursorrules)',
|
|
655
|
+
value: 'cursor',
|
|
656
|
+
checked: detected.cursor || false
|
|
657
|
+
});
|
|
658
|
+
choices.push({
|
|
659
|
+
name: 'Windsurf (.windsurfrules)',
|
|
660
|
+
value: 'windsurf',
|
|
661
|
+
checked: detected.windsurf || false
|
|
662
|
+
});
|
|
663
|
+
choices.push({
|
|
664
|
+
name: 'Cline (.clinerules)',
|
|
665
|
+
value: 'cline',
|
|
666
|
+
checked: detected.cline || false
|
|
667
|
+
});
|
|
668
|
+
choices.push({
|
|
669
|
+
name: 'GitHub Copilot (.github/copilot-instructions.md)',
|
|
670
|
+
value: 'copilot',
|
|
671
|
+
checked: detected.copilot || false
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const { integrations } = await inquirer.prompt([
|
|
675
|
+
{
|
|
676
|
+
type: 'checkbox',
|
|
677
|
+
name: 'integrations',
|
|
678
|
+
message: 'Select AI tool integrations:',
|
|
679
|
+
choices
|
|
680
|
+
}
|
|
681
|
+
]);
|
|
682
|
+
|
|
683
|
+
return integrations;
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
/**
|
|
687
|
+
* Prompt for confirmation
|
|
688
|
+
* @param {string} message - Confirmation message
|
|
689
|
+
* @returns {Promise<boolean>} True if confirmed
|
|
690
|
+
*/
|
|
691
|
+
export async function promptConfirm(message) {
|
|
692
|
+
const { confirmed } = await inquirer.prompt([
|
|
693
|
+
{
|
|
694
|
+
type: 'confirm',
|
|
695
|
+
name: 'confirmed',
|
|
696
|
+
message,
|
|
697
|
+
default: true
|
|
698
|
+
}
|
|
699
|
+
]);
|
|
700
|
+
|
|
701
|
+
return confirmed;
|
|
702
|
+
}
|