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,665 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import ora from 'ora';
|
|
3
|
+
import {
|
|
4
|
+
getStandardsByLevel,
|
|
5
|
+
getRepositoryInfo,
|
|
6
|
+
getSkillFiles,
|
|
7
|
+
getStandardSource,
|
|
8
|
+
getOptionSource,
|
|
9
|
+
findOption
|
|
10
|
+
} from '../utils/registry.js';
|
|
11
|
+
import { detectAll } from '../utils/detector.js';
|
|
12
|
+
import {
|
|
13
|
+
copyStandard,
|
|
14
|
+
copyIntegration,
|
|
15
|
+
writeManifest,
|
|
16
|
+
isInitialized
|
|
17
|
+
} from '../utils/copier.js';
|
|
18
|
+
import {
|
|
19
|
+
downloadSkillToLocation,
|
|
20
|
+
getInstalledSkillsInfo,
|
|
21
|
+
getProjectInstalledSkillsInfo,
|
|
22
|
+
writeSkillsManifest,
|
|
23
|
+
getSkillsDir,
|
|
24
|
+
getProjectSkillsDir
|
|
25
|
+
} from '../utils/github.js';
|
|
26
|
+
import {
|
|
27
|
+
promptAITools,
|
|
28
|
+
promptSkillsInstallLocation,
|
|
29
|
+
promptSkillsUpdate,
|
|
30
|
+
promptStandardsScope,
|
|
31
|
+
promptLevel,
|
|
32
|
+
promptLanguage,
|
|
33
|
+
promptFramework,
|
|
34
|
+
promptLocale,
|
|
35
|
+
promptConfirm,
|
|
36
|
+
promptFormat,
|
|
37
|
+
promptStandardOptions
|
|
38
|
+
} from '../prompts/init.js';
|
|
39
|
+
import {
|
|
40
|
+
promptIntegrationConfig
|
|
41
|
+
} from '../prompts/integrations.js';
|
|
42
|
+
import {
|
|
43
|
+
writeIntegrationFile,
|
|
44
|
+
integrationFileExists
|
|
45
|
+
} from '../utils/integration-generator.js';
|
|
46
|
+
|
|
47
|
+
// Integration file mappings (legacy - for fallback)
|
|
48
|
+
const INTEGRATION_MAPPINGS = {
|
|
49
|
+
cursor: {
|
|
50
|
+
source: 'integrations/cursor/.cursorrules',
|
|
51
|
+
target: '.cursorrules'
|
|
52
|
+
},
|
|
53
|
+
windsurf: {
|
|
54
|
+
source: 'integrations/windsurf/.windsurfrules',
|
|
55
|
+
target: '.windsurfrules'
|
|
56
|
+
},
|
|
57
|
+
cline: {
|
|
58
|
+
source: 'integrations/cline/.clinerules',
|
|
59
|
+
target: '.clinerules'
|
|
60
|
+
},
|
|
61
|
+
copilot: {
|
|
62
|
+
source: 'integrations/github-copilot/copilot-instructions.md',
|
|
63
|
+
target: '.github/copilot-instructions.md'
|
|
64
|
+
},
|
|
65
|
+
antigravity: {
|
|
66
|
+
source: 'integrations/google-antigravity/INSTRUCTIONS.md',
|
|
67
|
+
target: 'INSTRUCTIONS.md'
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// Extension file mappings
|
|
72
|
+
const EXTENSION_MAPPINGS = {
|
|
73
|
+
csharp: 'extensions/languages/csharp-style.md',
|
|
74
|
+
php: 'extensions/languages/php-style.md',
|
|
75
|
+
'fat-free': 'extensions/frameworks/fat-free-patterns.md',
|
|
76
|
+
'zh-tw': 'extensions/locales/zh-tw.md'
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Init command - initialize standards in current project
|
|
81
|
+
* @param {Object} options - Command options
|
|
82
|
+
*/
|
|
83
|
+
export async function initCommand(options) {
|
|
84
|
+
const projectPath = process.cwd();
|
|
85
|
+
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(chalk.bold('Universal Development Standards - Initialize'));
|
|
88
|
+
console.log(chalk.gray('─'.repeat(50)));
|
|
89
|
+
|
|
90
|
+
// STEP 1: Check if already initialized
|
|
91
|
+
if (isInitialized(projectPath)) {
|
|
92
|
+
console.log(chalk.yellow('⚠ Standards already initialized in this project.'));
|
|
93
|
+
console.log(chalk.gray(' Use `uds update` to update, or delete .standards/ to reinitialize.'));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// STEP 2: Detect project characteristics
|
|
98
|
+
const spinner = ora('Detecting project characteristics...').start();
|
|
99
|
+
const detected = detectAll(projectPath);
|
|
100
|
+
spinner.succeed('Project analysis complete');
|
|
101
|
+
|
|
102
|
+
// Show detected info
|
|
103
|
+
const detectedLangs = Object.entries(detected.languages)
|
|
104
|
+
.filter(([, v]) => v)
|
|
105
|
+
.map(([k]) => k);
|
|
106
|
+
const detectedFrameworks = Object.entries(detected.frameworks)
|
|
107
|
+
.filter(([, v]) => v)
|
|
108
|
+
.map(([k]) => k);
|
|
109
|
+
const detectedTools = Object.entries(detected.aiTools)
|
|
110
|
+
.filter(([, v]) => v)
|
|
111
|
+
.map(([k]) => k);
|
|
112
|
+
|
|
113
|
+
if (detectedLangs.length > 0) {
|
|
114
|
+
console.log(chalk.gray(` Languages: ${detectedLangs.join(', ')}`));
|
|
115
|
+
}
|
|
116
|
+
if (detectedFrameworks.length > 0) {
|
|
117
|
+
console.log(chalk.gray(` Frameworks: ${detectedFrameworks.join(', ')}`));
|
|
118
|
+
}
|
|
119
|
+
if (detectedTools.length > 0) {
|
|
120
|
+
console.log(chalk.gray(` AI Tools: ${detectedTools.join(', ')}`));
|
|
121
|
+
}
|
|
122
|
+
console.log();
|
|
123
|
+
|
|
124
|
+
// Initialize configuration variables
|
|
125
|
+
let level = options.level ? parseInt(options.level, 10) : null;
|
|
126
|
+
let languages = options.lang ? [options.lang] : null;
|
|
127
|
+
let frameworks = options.framework ? [options.framework] : null;
|
|
128
|
+
let locale = options.locale || null;
|
|
129
|
+
let format = options.format || null;
|
|
130
|
+
let standardOptions = {};
|
|
131
|
+
|
|
132
|
+
// Skills configuration
|
|
133
|
+
let skillsConfig = {
|
|
134
|
+
installed: false,
|
|
135
|
+
location: null,
|
|
136
|
+
needsInstall: false,
|
|
137
|
+
updateTargets: []
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
// AI tools configuration
|
|
141
|
+
let aiTools = [];
|
|
142
|
+
let integrations = [];
|
|
143
|
+
|
|
144
|
+
if (!options.yes) {
|
|
145
|
+
// ===== Interactive mode =====
|
|
146
|
+
|
|
147
|
+
// STEP 3: Ask for AI tools
|
|
148
|
+
aiTools = await promptAITools({
|
|
149
|
+
claudeCode: detected.aiTools.claudeCode || false,
|
|
150
|
+
cursor: detected.aiTools.cursor || false,
|
|
151
|
+
windsurf: detected.aiTools.windsurf || false,
|
|
152
|
+
cline: detected.aiTools.cline || false,
|
|
153
|
+
copilot: detected.aiTools.copilot || false
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
const useClaudeCode = aiTools.includes('claude-code');
|
|
157
|
+
|
|
158
|
+
// STEP 4: Skills handling (only if Claude Code is selected)
|
|
159
|
+
if (useClaudeCode) {
|
|
160
|
+
const projectSkillsInfo = getProjectInstalledSkillsInfo(projectPath);
|
|
161
|
+
const userSkillsInfo = getInstalledSkillsInfo();
|
|
162
|
+
const repoInfo = getRepositoryInfo();
|
|
163
|
+
const latestVersion = repoInfo.skills.version;
|
|
164
|
+
|
|
165
|
+
const hasProjectSkills = projectSkillsInfo?.installed;
|
|
166
|
+
const hasUserSkills = userSkillsInfo?.installed;
|
|
167
|
+
|
|
168
|
+
if (hasProjectSkills && hasUserSkills) {
|
|
169
|
+
// Case D: Both levels installed
|
|
170
|
+
console.log();
|
|
171
|
+
console.log(chalk.cyan('Skills Status:'));
|
|
172
|
+
console.log(chalk.gray(` Project level: v${projectSkillsInfo.version || 'unknown'}`));
|
|
173
|
+
console.log(chalk.gray(` User level: v${userSkillsInfo.version || 'unknown'}`));
|
|
174
|
+
|
|
175
|
+
const updateResult = await promptSkillsUpdate(projectSkillsInfo, userSkillsInfo, latestVersion);
|
|
176
|
+
skillsConfig = {
|
|
177
|
+
installed: true,
|
|
178
|
+
location: 'both',
|
|
179
|
+
needsInstall: updateResult.action !== 'none',
|
|
180
|
+
updateTargets: updateResult.targets
|
|
181
|
+
};
|
|
182
|
+
} else if (hasProjectSkills) {
|
|
183
|
+
// Case C: Only project level installed
|
|
184
|
+
console.log();
|
|
185
|
+
console.log(chalk.cyan('Skills Status:'));
|
|
186
|
+
console.log(chalk.gray(` Project level: v${projectSkillsInfo.version || 'unknown'}`));
|
|
187
|
+
console.log(chalk.gray(' User level: not installed'));
|
|
188
|
+
|
|
189
|
+
const updateResult = await promptSkillsUpdate(projectSkillsInfo, null, latestVersion);
|
|
190
|
+
skillsConfig = {
|
|
191
|
+
installed: true,
|
|
192
|
+
location: 'project',
|
|
193
|
+
needsInstall: updateResult.action !== 'none',
|
|
194
|
+
updateTargets: updateResult.targets
|
|
195
|
+
};
|
|
196
|
+
} else if (hasUserSkills) {
|
|
197
|
+
// Case B: Only user level installed
|
|
198
|
+
console.log();
|
|
199
|
+
console.log(chalk.cyan('Skills Status:'));
|
|
200
|
+
console.log(chalk.gray(' Project level: not installed'));
|
|
201
|
+
console.log(chalk.gray(` User level: v${userSkillsInfo.version || 'unknown'}`));
|
|
202
|
+
|
|
203
|
+
const updateResult = await promptSkillsUpdate(null, userSkillsInfo, latestVersion);
|
|
204
|
+
skillsConfig = {
|
|
205
|
+
installed: true,
|
|
206
|
+
location: 'user',
|
|
207
|
+
needsInstall: updateResult.action !== 'none',
|
|
208
|
+
updateTargets: updateResult.targets
|
|
209
|
+
};
|
|
210
|
+
} else {
|
|
211
|
+
// Case A: Neither installed
|
|
212
|
+
console.log();
|
|
213
|
+
console.log(chalk.cyan('Skills Status:'));
|
|
214
|
+
console.log(chalk.gray(' No Skills installation detected'));
|
|
215
|
+
|
|
216
|
+
const location = await promptSkillsInstallLocation();
|
|
217
|
+
if (location !== 'none') {
|
|
218
|
+
skillsConfig = {
|
|
219
|
+
installed: true,
|
|
220
|
+
location,
|
|
221
|
+
needsInstall: true,
|
|
222
|
+
updateTargets: [location]
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// STEP 5: Standards scope (if Skills are installed)
|
|
229
|
+
const standardsScope = await promptStandardsScope(skillsConfig.installed);
|
|
230
|
+
|
|
231
|
+
// STEP 6: Adoption level
|
|
232
|
+
if (!level) {
|
|
233
|
+
level = await promptLevel();
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// STEP 7: Standards format
|
|
237
|
+
if (!format) {
|
|
238
|
+
format = await promptFormat();
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// STEP 8: Standard options
|
|
242
|
+
standardOptions = await promptStandardOptions(level);
|
|
243
|
+
|
|
244
|
+
// STEP 9: Language extensions
|
|
245
|
+
if (!languages) {
|
|
246
|
+
languages = await promptLanguage(detected.languages) || [];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// STEP 10: Framework extensions
|
|
250
|
+
if (!frameworks) {
|
|
251
|
+
frameworks = await promptFramework(detected.frameworks) || [];
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// STEP 11: Locale
|
|
255
|
+
if (!locale) {
|
|
256
|
+
locale = await promptLocale();
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Determine integrations from AI tools (excluding claude-code)
|
|
260
|
+
integrations = aiTools.filter(t => t !== 'claude-code');
|
|
261
|
+
|
|
262
|
+
// STEP 12: Integration configuration for non-Claude tools
|
|
263
|
+
// All tools share the same configuration since they have identical functionality
|
|
264
|
+
const integrationConfigs = {};
|
|
265
|
+
if (integrations.length > 0) {
|
|
266
|
+
console.log();
|
|
267
|
+
console.log(chalk.cyan('Integration Configuration:'));
|
|
268
|
+
|
|
269
|
+
if (integrations.length > 1) {
|
|
270
|
+
console.log(chalk.gray(' All selected tools will share the same rule configuration.'));
|
|
271
|
+
console.log();
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Check if any existing files
|
|
275
|
+
const existingFiles = {};
|
|
276
|
+
for (const tool of integrations) {
|
|
277
|
+
existingFiles[tool] = integrationFileExists(tool, projectPath);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const hasAnyExisting = Object.values(existingFiles).some(v => v);
|
|
281
|
+
|
|
282
|
+
// Prompt configuration once for all tools
|
|
283
|
+
// Use first tool as representative, but mention all tools
|
|
284
|
+
const toolNames = integrations.length === 1
|
|
285
|
+
? integrations[0]
|
|
286
|
+
: `${integrations.slice(0, -1).join(', ')} & ${integrations[integrations.length - 1]}`;
|
|
287
|
+
|
|
288
|
+
const sharedConfig = await promptIntegrationConfig(toolNames, detected, hasAnyExisting);
|
|
289
|
+
|
|
290
|
+
// Apply shared config to all tools (unless user chose to keep existing)
|
|
291
|
+
if (sharedConfig.mergeStrategy !== 'keep') {
|
|
292
|
+
for (const tool of integrations) {
|
|
293
|
+
// Each tool gets the same config but with its specific tool name
|
|
294
|
+
integrationConfigs[tool] = { ...sharedConfig, tool };
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// Store integration configs for later use
|
|
300
|
+
skillsConfig.integrationConfigs = integrationConfigs;
|
|
301
|
+
|
|
302
|
+
// Store standards scope for later use
|
|
303
|
+
skillsConfig.standardsScope = standardsScope;
|
|
304
|
+
|
|
305
|
+
} else {
|
|
306
|
+
// ===== Non-interactive mode =====
|
|
307
|
+
level = level || 2;
|
|
308
|
+
format = format || 'ai';
|
|
309
|
+
languages = languages || Object.keys(detected.languages).filter(k => detected.languages[k]);
|
|
310
|
+
frameworks = frameworks || Object.keys(detected.frameworks).filter(k => detected.frameworks[k]);
|
|
311
|
+
integrations = Object.keys(detected.aiTools).filter(k => detected.aiTools[k] && k !== 'claudeCode');
|
|
312
|
+
|
|
313
|
+
// Default standard options
|
|
314
|
+
standardOptions = {
|
|
315
|
+
workflow: options.workflow || 'github-flow',
|
|
316
|
+
merge_strategy: options.mergeStrategy || 'squash',
|
|
317
|
+
commit_language: options.commitLang || 'english',
|
|
318
|
+
test_levels: options.testLevels ? options.testLevels.split(',') : ['unit-testing', 'integration-testing']
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// Auto-detect Skills status
|
|
322
|
+
const userSkillsInfo = getInstalledSkillsInfo();
|
|
323
|
+
if (userSkillsInfo?.installed) {
|
|
324
|
+
skillsConfig = {
|
|
325
|
+
installed: true,
|
|
326
|
+
location: 'user',
|
|
327
|
+
needsInstall: false,
|
|
328
|
+
updateTargets: [],
|
|
329
|
+
standardsScope: 'minimal'
|
|
330
|
+
};
|
|
331
|
+
} else {
|
|
332
|
+
// Default to installing Skills in non-interactive mode
|
|
333
|
+
skillsConfig = {
|
|
334
|
+
installed: true,
|
|
335
|
+
location: 'user',
|
|
336
|
+
needsInstall: true,
|
|
337
|
+
updateTargets: ['user'],
|
|
338
|
+
standardsScope: 'minimal'
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Configuration summary
|
|
344
|
+
console.log();
|
|
345
|
+
console.log(chalk.cyan('Configuration Summary:'));
|
|
346
|
+
console.log(chalk.gray(` Level: ${level}`));
|
|
347
|
+
console.log(chalk.gray(` Format: ${format === 'ai' ? 'AI-Optimized' : format === 'human' ? 'Human-Readable' : 'Both'}`));
|
|
348
|
+
console.log(chalk.gray(` Standards Scope: ${skillsConfig.standardsScope === 'minimal' ? 'Minimal (Skills cover the rest)' : 'Full'}`));
|
|
349
|
+
console.log(chalk.gray(` Languages: ${languages.length > 0 ? languages.join(', ') : 'none'}`));
|
|
350
|
+
console.log(chalk.gray(` Frameworks: ${frameworks.length > 0 ? frameworks.join(', ') : 'none'}`));
|
|
351
|
+
console.log(chalk.gray(` Locale: ${locale || 'default (English)'}`));
|
|
352
|
+
console.log(chalk.gray(` AI Tools: ${aiTools.length > 0 ? aiTools.join(', ') : 'none'}`));
|
|
353
|
+
console.log(chalk.gray(` Integrations: ${integrations.length > 0 ? integrations.join(', ') : 'none'}`));
|
|
354
|
+
|
|
355
|
+
if (skillsConfig.installed) {
|
|
356
|
+
const skillsStatus = skillsConfig.needsInstall
|
|
357
|
+
? `install/update to ${skillsConfig.location}`
|
|
358
|
+
: `using existing (${skillsConfig.location})`;
|
|
359
|
+
console.log(chalk.gray(` Skills: ${skillsStatus}`));
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Show selected standard options
|
|
363
|
+
if (standardOptions.workflow) {
|
|
364
|
+
console.log(chalk.gray(` Git Workflow: ${standardOptions.workflow}`));
|
|
365
|
+
}
|
|
366
|
+
if (standardOptions.merge_strategy) {
|
|
367
|
+
console.log(chalk.gray(` Merge Strategy: ${standardOptions.merge_strategy}`));
|
|
368
|
+
}
|
|
369
|
+
if (standardOptions.commit_language) {
|
|
370
|
+
console.log(chalk.gray(` Commit Language: ${standardOptions.commit_language}`));
|
|
371
|
+
}
|
|
372
|
+
if (standardOptions.test_levels && standardOptions.test_levels.length > 0) {
|
|
373
|
+
console.log(chalk.gray(` Test Levels: ${standardOptions.test_levels.join(', ')}`));
|
|
374
|
+
}
|
|
375
|
+
console.log();
|
|
376
|
+
|
|
377
|
+
if (!options.yes) {
|
|
378
|
+
const confirmed = await promptConfirm('Proceed with installation?');
|
|
379
|
+
if (!confirmed) {
|
|
380
|
+
console.log(chalk.yellow('Installation cancelled.'));
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// ===== Start installation =====
|
|
386
|
+
console.log();
|
|
387
|
+
const copySpinner = ora('Copying standards...').start();
|
|
388
|
+
|
|
389
|
+
const results = {
|
|
390
|
+
standards: [],
|
|
391
|
+
extensions: [],
|
|
392
|
+
integrations: [],
|
|
393
|
+
skills: [],
|
|
394
|
+
errors: []
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
// Get standards for the selected level
|
|
398
|
+
const standards = getStandardsByLevel(level);
|
|
399
|
+
|
|
400
|
+
// Determine which standards to copy based on scope
|
|
401
|
+
const standardsToCopy = skillsConfig.standardsScope === 'minimal'
|
|
402
|
+
? standards.filter(s => s.category === 'reference')
|
|
403
|
+
: standards.filter(s => s.category === 'reference' || s.category === 'skill');
|
|
404
|
+
|
|
405
|
+
// Helper to copy standard with format awareness
|
|
406
|
+
const copyStandardWithFormat = async (std, targetFormat) => {
|
|
407
|
+
const sourcePath = getStandardSource(std, targetFormat);
|
|
408
|
+
const result = await copyStandard(sourcePath, '.standards', projectPath);
|
|
409
|
+
return { ...result, sourcePath };
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
// Helper to copy option files
|
|
413
|
+
const copyOptionFiles = async (std, optionCategory, selectedOptionIds, targetFormat) => {
|
|
414
|
+
const copiedOptions = [];
|
|
415
|
+
if (!std.options || !std.options[optionCategory]) return copiedOptions;
|
|
416
|
+
|
|
417
|
+
const optionIds = Array.isArray(selectedOptionIds) ? selectedOptionIds : [selectedOptionIds];
|
|
418
|
+
for (const optionId of optionIds) {
|
|
419
|
+
const option = findOption(std, optionCategory, optionId);
|
|
420
|
+
if (option) {
|
|
421
|
+
const sourcePath = getOptionSource(option, targetFormat);
|
|
422
|
+
const result = await copyStandard(sourcePath, '.standards/options', projectPath);
|
|
423
|
+
if (result.success) {
|
|
424
|
+
copiedOptions.push(sourcePath);
|
|
425
|
+
} else {
|
|
426
|
+
results.errors.push(`${sourcePath}: ${result.error}`);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
return copiedOptions;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// Copy standards based on format
|
|
434
|
+
const formatsToUse = format === 'both' ? ['ai', 'human'] : [format];
|
|
435
|
+
|
|
436
|
+
for (const std of standardsToCopy) {
|
|
437
|
+
for (const targetFormat of formatsToUse) {
|
|
438
|
+
const { success, sourcePath, error } = await copyStandardWithFormat(std, targetFormat);
|
|
439
|
+
if (success) {
|
|
440
|
+
results.standards.push(sourcePath);
|
|
441
|
+
} else {
|
|
442
|
+
results.errors.push(`${sourcePath}: ${error}`);
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// Copy selected options for this standard
|
|
447
|
+
if (std.options) {
|
|
448
|
+
for (const targetFormat of formatsToUse) {
|
|
449
|
+
// Git workflow options
|
|
450
|
+
if (std.id === 'git-workflow') {
|
|
451
|
+
if (standardOptions.workflow) {
|
|
452
|
+
const copied = await copyOptionFiles(std, 'workflow', standardOptions.workflow, targetFormat);
|
|
453
|
+
results.standards.push(...copied);
|
|
454
|
+
}
|
|
455
|
+
if (standardOptions.merge_strategy) {
|
|
456
|
+
const copied = await copyOptionFiles(std, 'merge_strategy', standardOptions.merge_strategy, targetFormat);
|
|
457
|
+
results.standards.push(...copied);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
// Commit message options
|
|
461
|
+
if (std.id === 'commit-message' && standardOptions.commit_language) {
|
|
462
|
+
const copied = await copyOptionFiles(std, 'commit_language', standardOptions.commit_language, targetFormat);
|
|
463
|
+
results.standards.push(...copied);
|
|
464
|
+
}
|
|
465
|
+
// Testing options
|
|
466
|
+
if (std.id === 'testing' && standardOptions.test_levels) {
|
|
467
|
+
const copied = await copyOptionFiles(std, 'test_level', standardOptions.test_levels, targetFormat);
|
|
468
|
+
results.standards.push(...copied);
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
copySpinner.succeed(`Copied ${results.standards.length} standard files`);
|
|
475
|
+
|
|
476
|
+
// Copy extensions
|
|
477
|
+
if (languages.length > 0 || frameworks.length > 0 || locale) {
|
|
478
|
+
const extSpinner = ora('Copying extensions...').start();
|
|
479
|
+
|
|
480
|
+
for (const lang of languages) {
|
|
481
|
+
if (EXTENSION_MAPPINGS[lang]) {
|
|
482
|
+
const result = await copyStandard(EXTENSION_MAPPINGS[lang], '.standards', projectPath);
|
|
483
|
+
if (result.success) {
|
|
484
|
+
results.extensions.push(EXTENSION_MAPPINGS[lang]);
|
|
485
|
+
} else {
|
|
486
|
+
results.errors.push(`${EXTENSION_MAPPINGS[lang]}: ${result.error}`);
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
for (const fw of frameworks) {
|
|
492
|
+
if (EXTENSION_MAPPINGS[fw]) {
|
|
493
|
+
const result = await copyStandard(EXTENSION_MAPPINGS[fw], '.standards', projectPath);
|
|
494
|
+
if (result.success) {
|
|
495
|
+
results.extensions.push(EXTENSION_MAPPINGS[fw]);
|
|
496
|
+
} else {
|
|
497
|
+
results.errors.push(`${EXTENSION_MAPPINGS[fw]}: ${result.error}`);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (locale && EXTENSION_MAPPINGS[locale]) {
|
|
503
|
+
const result = await copyStandard(EXTENSION_MAPPINGS[locale], '.standards', projectPath);
|
|
504
|
+
if (result.success) {
|
|
505
|
+
results.extensions.push(EXTENSION_MAPPINGS[locale]);
|
|
506
|
+
} else {
|
|
507
|
+
results.errors.push(`${EXTENSION_MAPPINGS[locale]}: ${result.error}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
extSpinner.succeed(`Copied ${results.extensions.length} extension files`);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Generate and write integrations
|
|
515
|
+
if (integrations.length > 0) {
|
|
516
|
+
const intSpinner = ora('Generating integration files...').start();
|
|
517
|
+
const integrationConfigs = skillsConfig.integrationConfigs || {};
|
|
518
|
+
|
|
519
|
+
for (const tool of integrations) {
|
|
520
|
+
// Check if we have a custom config for this tool
|
|
521
|
+
if (integrationConfigs[tool]) {
|
|
522
|
+
// Use dynamic generator with custom config
|
|
523
|
+
const result = writeIntegrationFile(tool, integrationConfigs[tool], projectPath);
|
|
524
|
+
if (result.success) {
|
|
525
|
+
results.integrations.push(result.path);
|
|
526
|
+
} else {
|
|
527
|
+
results.errors.push(`${tool}: ${result.error}`);
|
|
528
|
+
}
|
|
529
|
+
} else {
|
|
530
|
+
// Fall back to legacy static file copy
|
|
531
|
+
const mapping = INTEGRATION_MAPPINGS[tool];
|
|
532
|
+
if (mapping) {
|
|
533
|
+
const result = await copyIntegration(mapping.source, mapping.target, projectPath);
|
|
534
|
+
if (result.success) {
|
|
535
|
+
results.integrations.push(mapping.target);
|
|
536
|
+
} else {
|
|
537
|
+
results.errors.push(`${mapping.source}: ${result.error}`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
intSpinner.succeed(`Generated ${results.integrations.length} integration files`);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// Install Skills if needed
|
|
547
|
+
if (skillsConfig.needsInstall && skillsConfig.updateTargets.length > 0) {
|
|
548
|
+
const skillSpinner = ora('Installing Claude Code Skills...').start();
|
|
549
|
+
|
|
550
|
+
const skillFiles = getSkillFiles();
|
|
551
|
+
const repoInfo = getRepositoryInfo();
|
|
552
|
+
let successCount = 0;
|
|
553
|
+
let errorCount = 0;
|
|
554
|
+
|
|
555
|
+
for (const target of skillsConfig.updateTargets) {
|
|
556
|
+
for (const [skillName, files] of Object.entries(skillFiles)) {
|
|
557
|
+
const result = await downloadSkillToLocation(
|
|
558
|
+
skillName,
|
|
559
|
+
files,
|
|
560
|
+
target,
|
|
561
|
+
target === 'project' ? projectPath : null
|
|
562
|
+
);
|
|
563
|
+
|
|
564
|
+
if (result.success) {
|
|
565
|
+
successCount++;
|
|
566
|
+
if (!results.skills.includes(skillName)) {
|
|
567
|
+
results.skills.push(skillName);
|
|
568
|
+
}
|
|
569
|
+
} else {
|
|
570
|
+
errorCount++;
|
|
571
|
+
const failedFiles = result.files.filter(f => !f.success).map(f => f.file).join(', ');
|
|
572
|
+
results.errors.push(`Skill ${skillName} (${target}): failed to install ${failedFiles}`);
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Write manifest for each target location
|
|
577
|
+
const targetDir = target === 'project'
|
|
578
|
+
? getProjectSkillsDir(projectPath)
|
|
579
|
+
: getSkillsDir();
|
|
580
|
+
writeSkillsManifest(repoInfo.skills.version, targetDir);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
const targetLocations = skillsConfig.updateTargets.map(t =>
|
|
584
|
+
t === 'project' ? getProjectSkillsDir(projectPath) : getSkillsDir()
|
|
585
|
+
).join(', ');
|
|
586
|
+
|
|
587
|
+
if (errorCount === 0) {
|
|
588
|
+
skillSpinner.succeed(`Installed ${successCount} Skills to ${targetLocations}`);
|
|
589
|
+
} else {
|
|
590
|
+
skillSpinner.warn(`Installed ${successCount} Skills with ${errorCount} errors`);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
// Create manifest
|
|
595
|
+
const repoInfo = getRepositoryInfo();
|
|
596
|
+
const manifest = {
|
|
597
|
+
version: '3.0.0',
|
|
598
|
+
upstream: {
|
|
599
|
+
repo: 'AsiaOstrich/universal-dev-standards',
|
|
600
|
+
version: repoInfo.standards.version,
|
|
601
|
+
installed: new Date().toISOString().split('T')[0]
|
|
602
|
+
},
|
|
603
|
+
level,
|
|
604
|
+
format,
|
|
605
|
+
standardsScope: skillsConfig.standardsScope || 'full',
|
|
606
|
+
standards: results.standards,
|
|
607
|
+
extensions: results.extensions,
|
|
608
|
+
integrations: results.integrations,
|
|
609
|
+
options: {
|
|
610
|
+
workflow: standardOptions.workflow || null,
|
|
611
|
+
merge_strategy: standardOptions.merge_strategy || null,
|
|
612
|
+
commit_language: standardOptions.commit_language || null,
|
|
613
|
+
test_levels: standardOptions.test_levels || []
|
|
614
|
+
},
|
|
615
|
+
aiTools,
|
|
616
|
+
skills: {
|
|
617
|
+
installed: skillsConfig.installed,
|
|
618
|
+
location: skillsConfig.location,
|
|
619
|
+
names: results.skills,
|
|
620
|
+
version: skillsConfig.installed ? repoInfo.skills.version : null
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
writeManifest(manifest, projectPath);
|
|
625
|
+
|
|
626
|
+
// Summary
|
|
627
|
+
console.log();
|
|
628
|
+
console.log(chalk.green('✓ Standards initialized successfully!'));
|
|
629
|
+
console.log();
|
|
630
|
+
|
|
631
|
+
const totalFiles = results.standards.length + results.extensions.length + results.integrations.length;
|
|
632
|
+
console.log(chalk.gray(` ${totalFiles} files copied to project`));
|
|
633
|
+
|
|
634
|
+
if (results.skills.length > 0) {
|
|
635
|
+
const skillLocations = [];
|
|
636
|
+
if (skillsConfig.updateTargets.includes('user')) {
|
|
637
|
+
skillLocations.push('~/.claude/skills/');
|
|
638
|
+
}
|
|
639
|
+
if (skillsConfig.updateTargets.includes('project')) {
|
|
640
|
+
skillLocations.push('.claude/skills/');
|
|
641
|
+
}
|
|
642
|
+
console.log(chalk.gray(` ${results.skills.length} Skills installed to ${skillLocations.join(' and ')}`));
|
|
643
|
+
}
|
|
644
|
+
console.log(chalk.gray(' Manifest created at .standards/manifest.json'));
|
|
645
|
+
|
|
646
|
+
if (results.errors.length > 0) {
|
|
647
|
+
console.log();
|
|
648
|
+
console.log(chalk.yellow(`⚠ ${results.errors.length} error(s) occurred:`));
|
|
649
|
+
for (const err of results.errors) {
|
|
650
|
+
console.log(chalk.gray(` ${err}`));
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
console.log();
|
|
655
|
+
console.log(chalk.gray('Next steps:'));
|
|
656
|
+
console.log(chalk.gray(' 1. Review .standards/ directory'));
|
|
657
|
+
console.log(chalk.gray(' 2. Add .standards/ to version control'));
|
|
658
|
+
if (skillsConfig.installed) {
|
|
659
|
+
console.log(chalk.gray(' 3. Restart Claude Code to load new Skills'));
|
|
660
|
+
console.log(chalk.gray(' 4. Run `uds check` to verify adoption status'));
|
|
661
|
+
} else {
|
|
662
|
+
console.log(chalk.gray(' 3. Run `uds check` to verify adoption status'));
|
|
663
|
+
}
|
|
664
|
+
console.log();
|
|
665
|
+
}
|