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.
@@ -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
+ }