universal-dev-standards 3.5.1-beta.15 → 3.5.1-beta.17
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/bin/uds.js +2 -0
- package/bundled/core/checkin-standards.md +39 -2
- package/bundled/locales/zh-CN/core/checkin-standards.md +42 -5
- package/bundled/locales/zh-TW/core/checkin-standards.md +42 -5
- package/bundled/skills/claude-code/commands/check.md +52 -1
- package/bundled/skills/claude-code/commands/config.md +14 -0
- package/bundled/skills/claude-code/commands/update.md +23 -7
- package/package.json +2 -1
- package/src/commands/check.js +455 -28
- package/src/commands/configure.js +64 -5
- package/src/commands/init.js +29 -3
- package/src/commands/update.js +227 -49
- package/src/i18n/messages.js +54 -6
- package/src/utils/hasher.js +193 -0
- package/src/utils/integration-generator.js +11 -1
- package/src/utils/skills-installer.js +39 -4
- package/standards-registry.json +3 -3
package/src/commands/init.js
CHANGED
|
@@ -537,7 +537,11 @@ export async function initCommand(options) {
|
|
|
537
537
|
integrations: [],
|
|
538
538
|
skills: [],
|
|
539
539
|
commands: [],
|
|
540
|
-
errors: []
|
|
540
|
+
errors: [],
|
|
541
|
+
// New: hash tracking for enhanced file integrity
|
|
542
|
+
skillHashes: {},
|
|
543
|
+
commandHashes: {},
|
|
544
|
+
integrationBlockHashes: {}
|
|
541
545
|
};
|
|
542
546
|
|
|
543
547
|
// Get standards for the selected level
|
|
@@ -702,6 +706,14 @@ export async function initCommand(options) {
|
|
|
702
706
|
if (result.success) {
|
|
703
707
|
results.integrations.push(result.path);
|
|
704
708
|
generatedFiles.add(targetFile);
|
|
709
|
+
|
|
710
|
+
// Capture integration block hash for tracking UDS content
|
|
711
|
+
if (result.blockHashInfo) {
|
|
712
|
+
results.integrationBlockHashes[result.path] = {
|
|
713
|
+
...result.blockHashInfo,
|
|
714
|
+
installedAt: new Date().toISOString()
|
|
715
|
+
};
|
|
716
|
+
}
|
|
705
717
|
} else {
|
|
706
718
|
// Fall back to legacy static file copy
|
|
707
719
|
const mapping = INTEGRATION_MAPPINGS[tool];
|
|
@@ -776,6 +788,11 @@ export async function initCommand(options) {
|
|
|
776
788
|
}
|
|
777
789
|
}
|
|
778
790
|
|
|
791
|
+
// Collect skill file hashes for integrity tracking
|
|
792
|
+
if (installResult.allFileHashes) {
|
|
793
|
+
Object.assign(results.skillHashes, installResult.allFileHashes);
|
|
794
|
+
}
|
|
795
|
+
|
|
779
796
|
// Build location summary for display
|
|
780
797
|
const targetLocations = skillsConfig.skillsInstallations.map(inst => {
|
|
781
798
|
const displayName = getAgentDisplayName(inst.agent);
|
|
@@ -867,6 +884,11 @@ export async function initCommand(options) {
|
|
|
867
884
|
}
|
|
868
885
|
}
|
|
869
886
|
|
|
887
|
+
// Collect command file hashes for integrity tracking
|
|
888
|
+
if (cmdResult.allFileHashes) {
|
|
889
|
+
Object.assign(results.commandHashes, cmdResult.allFileHashes);
|
|
890
|
+
}
|
|
891
|
+
|
|
870
892
|
// Build location summary
|
|
871
893
|
const cmdLocations = skillsConfig.commandsInstallations.map(agent => {
|
|
872
894
|
const displayName = getAgentDisplayName(agent);
|
|
@@ -983,7 +1005,7 @@ export async function initCommand(options) {
|
|
|
983
1005
|
}
|
|
984
1006
|
|
|
985
1007
|
const manifest = {
|
|
986
|
-
version: '3.
|
|
1008
|
+
version: '3.3.0', // Updated for enhanced file tracking
|
|
987
1009
|
upstream: {
|
|
988
1010
|
repo: 'AsiaOstrich/universal-dev-standards',
|
|
989
1011
|
version: repoInfo.standards.version,
|
|
@@ -1021,7 +1043,11 @@ export async function initCommand(options) {
|
|
|
1021
1043
|
skipLimit: 3
|
|
1022
1044
|
}
|
|
1023
1045
|
} : null,
|
|
1024
|
-
fileHashes
|
|
1046
|
+
fileHashes,
|
|
1047
|
+
// New: enhanced file tracking for Skills, Commands, and Integration blocks
|
|
1048
|
+
skillHashes: results.skillHashes || {},
|
|
1049
|
+
commandHashes: results.commandHashes || {},
|
|
1050
|
+
integrationBlockHashes: results.integrationBlockHashes || {}
|
|
1025
1051
|
};
|
|
1026
1052
|
|
|
1027
1053
|
writeManifest(manifest, projectPath);
|
package/src/commands/update.js
CHANGED
|
@@ -336,6 +336,15 @@ export async function updateCommand(options) {
|
|
|
336
336
|
if (result.success) {
|
|
337
337
|
results.integrations.push(result.path);
|
|
338
338
|
generatedFiles.add(targetFile);
|
|
339
|
+
|
|
340
|
+
// Track integration block hash for UDS content integrity
|
|
341
|
+
if (result.blockHashInfo) {
|
|
342
|
+
if (!manifest.integrationBlockHashes) manifest.integrationBlockHashes = {};
|
|
343
|
+
manifest.integrationBlockHashes[result.path] = {
|
|
344
|
+
...result.blockHashInfo,
|
|
345
|
+
installedAt: new Date().toISOString()
|
|
346
|
+
};
|
|
347
|
+
}
|
|
339
348
|
} else {
|
|
340
349
|
results.errors.push(`${tool}: ${result.error}`);
|
|
341
350
|
}
|
|
@@ -384,7 +393,7 @@ export async function updateCommand(options) {
|
|
|
384
393
|
}
|
|
385
394
|
|
|
386
395
|
// Update manifest
|
|
387
|
-
manifest.version = '3.
|
|
396
|
+
manifest.version = '3.3.0';
|
|
388
397
|
manifest.upstream.version = latestVersion;
|
|
389
398
|
manifest.upstream.installed = new Date().toISOString().split('T')[0];
|
|
390
399
|
writeManifest(manifest, projectPath);
|
|
@@ -405,15 +414,17 @@ export async function updateCommand(options) {
|
|
|
405
414
|
}
|
|
406
415
|
}
|
|
407
416
|
|
|
408
|
-
// Check for new features (Skills/Commands) not yet installed
|
|
417
|
+
// Check for new features (Skills/Commands) not yet installed or outdated
|
|
409
418
|
if (!options.standardsOnly) {
|
|
410
|
-
const
|
|
419
|
+
const latestSkillsVersion = repoInfo.skills.version;
|
|
420
|
+
const { missingSkills, outdatedSkills, missingCommands } = checkNewFeatures(projectPath, manifest, latestSkillsVersion);
|
|
411
421
|
|
|
412
|
-
if (missingSkills.length > 0 || missingCommands.length > 0) {
|
|
422
|
+
if (missingSkills.length > 0 || outdatedSkills.length > 0 || missingCommands.length > 0) {
|
|
413
423
|
if (!options.yes) {
|
|
414
|
-
// Interactive mode: prompt user to install
|
|
415
|
-
const { installSkills, installCommands } = await promptNewFeatureInstallation(
|
|
424
|
+
// Interactive mode: prompt user to install/update
|
|
425
|
+
const { installSkills, updateSkills, installCommands } = await promptNewFeatureInstallation(
|
|
416
426
|
missingSkills,
|
|
427
|
+
outdatedSkills,
|
|
417
428
|
missingCommands
|
|
418
429
|
);
|
|
419
430
|
|
|
@@ -431,6 +442,12 @@ export async function updateCommand(options) {
|
|
|
431
442
|
...installSkills
|
|
432
443
|
];
|
|
433
444
|
|
|
445
|
+
// Update skill hashes for integrity tracking
|
|
446
|
+
if (skillResult.allFileHashes) {
|
|
447
|
+
if (!manifest.skillHashes) manifest.skillHashes = {};
|
|
448
|
+
Object.assign(manifest.skillHashes, skillResult.allFileHashes);
|
|
449
|
+
}
|
|
450
|
+
|
|
434
451
|
if (skillResult.totalErrors === 0) {
|
|
435
452
|
skillSpinner.succeed((msg.newSkillsInstalled || 'Installed Skills for {count} AI tools')
|
|
436
453
|
.replace('{count}', installSkills.length));
|
|
@@ -440,6 +457,30 @@ export async function updateCommand(options) {
|
|
|
440
457
|
}
|
|
441
458
|
}
|
|
442
459
|
|
|
460
|
+
// Update outdated Skills if user agreed
|
|
461
|
+
if (updateSkills.length > 0) {
|
|
462
|
+
const updateSpinner = ora(msg.updatingSkills || 'Updating Skills...').start();
|
|
463
|
+
const updateResult = await installSkillsToMultipleAgents(updateSkills, null, projectPath);
|
|
464
|
+
|
|
465
|
+
// Update manifest version
|
|
466
|
+
if (!manifest.skills) manifest.skills = {};
|
|
467
|
+
manifest.skills.version = repoInfo.skills.version;
|
|
468
|
+
|
|
469
|
+
// Update skill hashes for integrity tracking
|
|
470
|
+
if (updateResult.allFileHashes) {
|
|
471
|
+
if (!manifest.skillHashes) manifest.skillHashes = {};
|
|
472
|
+
Object.assign(manifest.skillHashes, updateResult.allFileHashes);
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (updateResult.totalErrors === 0) {
|
|
476
|
+
updateSpinner.succeed((msg.skillsUpdated || 'Updated Skills for {count} AI tools')
|
|
477
|
+
.replace('{count}', updateSkills.length));
|
|
478
|
+
} else {
|
|
479
|
+
updateSpinner.warn((msg.skillsUpdatedWithErrors || 'Updated Skills with {errors} errors')
|
|
480
|
+
.replace('{errors}', updateResult.totalErrors));
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
|
|
443
484
|
// Install Commands if user agreed
|
|
444
485
|
if (installCommands.length > 0) {
|
|
445
486
|
const cmdSpinner = ora(msg.installingNewCommands || 'Installing commands...').start();
|
|
@@ -453,6 +494,12 @@ export async function updateCommand(options) {
|
|
|
453
494
|
...installCommands
|
|
454
495
|
];
|
|
455
496
|
|
|
497
|
+
// Update command hashes for integrity tracking
|
|
498
|
+
if (cmdResult.allFileHashes) {
|
|
499
|
+
if (!manifest.commandHashes) manifest.commandHashes = {};
|
|
500
|
+
Object.assign(manifest.commandHashes, cmdResult.allFileHashes);
|
|
501
|
+
}
|
|
502
|
+
|
|
456
503
|
if (cmdResult.totalErrors === 0) {
|
|
457
504
|
cmdSpinner.succeed((msg.newCommandsInstalled || 'Installed commands for {count} AI tools')
|
|
458
505
|
.replace('{count}', installCommands.length));
|
|
@@ -462,8 +509,8 @@ export async function updateCommand(options) {
|
|
|
462
509
|
}
|
|
463
510
|
}
|
|
464
511
|
|
|
465
|
-
// Write updated manifest if anything was installed
|
|
466
|
-
if (installSkills.length > 0 || installCommands.length > 0) {
|
|
512
|
+
// Write updated manifest if anything was installed or updated
|
|
513
|
+
if (installSkills.length > 0 || updateSkills.length > 0 || installCommands.length > 0) {
|
|
467
514
|
writeManifest(manifest, projectPath);
|
|
468
515
|
}
|
|
469
516
|
} else {
|
|
@@ -474,6 +521,10 @@ export async function updateCommand(options) {
|
|
|
474
521
|
const toolNames = missingSkills.map(s => s.displayName).join(', ');
|
|
475
522
|
console.log(chalk.gray(` • Skills (${toolNames}): run "uds update --skills" or "uds init" to install`));
|
|
476
523
|
}
|
|
524
|
+
if (outdatedSkills.length > 0) {
|
|
525
|
+
const toolNames = outdatedSkills.map(s => `${s.displayName} (${s.currentVersion} → ${s.latestVersion})`).join(', ');
|
|
526
|
+
console.log(chalk.gray(` • Skills update (${toolNames}): run "uds update" interactively to update`));
|
|
527
|
+
}
|
|
477
528
|
if (missingCommands.length > 0) {
|
|
478
529
|
const toolNames = missingCommands.map(c => c.displayName).join(', ');
|
|
479
530
|
console.log(chalk.gray(` • Commands (${toolNames}): run "uds update --commands" or "uds init" to install`));
|
|
@@ -527,26 +578,19 @@ export async function updateCommand(options) {
|
|
|
527
578
|
}
|
|
528
579
|
|
|
529
580
|
/**
|
|
530
|
-
*
|
|
581
|
+
* Regenerate integration files for all configured AI tools
|
|
582
|
+
* Reusable core logic that can be called from both updateIntegrationsOnly and configureCommand
|
|
531
583
|
* @param {string} projectPath - Project path
|
|
532
|
-
* @param {Object} manifest - Manifest object
|
|
584
|
+
* @param {Object} manifest - Manifest object (will be mutated with updated hashes)
|
|
585
|
+
* @returns {{success: boolean, updated: string[], errors: string[]}}
|
|
533
586
|
*/
|
|
534
|
-
|
|
535
|
-
const msg = t().commands.update;
|
|
536
|
-
|
|
537
|
-
console.log(chalk.cyan(msg.updatingIntegrationsOnly));
|
|
538
|
-
console.log();
|
|
539
|
-
|
|
587
|
+
export function regenerateIntegrations(projectPath, manifest) {
|
|
540
588
|
const aiTools = manifest.aiTools || [];
|
|
589
|
+
|
|
541
590
|
if (aiTools.length === 0) {
|
|
542
|
-
|
|
543
|
-
console.log(chalk.gray(` ${msg.runConfigure}`));
|
|
544
|
-
console.log();
|
|
545
|
-
return;
|
|
591
|
+
return { success: true, updated: [], errors: [] };
|
|
546
592
|
}
|
|
547
593
|
|
|
548
|
-
const spinner = ora(msg.regeneratingIntegrations).start();
|
|
549
|
-
|
|
550
594
|
// Build installed standards list
|
|
551
595
|
const installedStandardsList = manifest.standards?.map(s => basename(s)) || [];
|
|
552
596
|
|
|
@@ -596,15 +640,55 @@ async function updateIntegrationsOnly(projectPath, manifest) {
|
|
|
596
640
|
}
|
|
597
641
|
manifest.fileHashes[result.path] = { ...hashInfo, installedAt: now };
|
|
598
642
|
}
|
|
643
|
+
|
|
644
|
+
// Track integration block hash for UDS content integrity
|
|
645
|
+
if (result.blockHashInfo) {
|
|
646
|
+
if (!manifest.integrationBlockHashes) manifest.integrationBlockHashes = {};
|
|
647
|
+
manifest.integrationBlockHashes[result.path] = {
|
|
648
|
+
...result.blockHashInfo,
|
|
649
|
+
installedAt: now
|
|
650
|
+
};
|
|
651
|
+
}
|
|
599
652
|
} else {
|
|
600
653
|
results.errors.push(`${tool}: ${result.error}`);
|
|
601
654
|
}
|
|
602
655
|
}
|
|
603
656
|
|
|
657
|
+
return {
|
|
658
|
+
success: results.errors.length === 0,
|
|
659
|
+
updated: results.updated,
|
|
660
|
+
errors: results.errors
|
|
661
|
+
};
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Update integration files only (without updating standards)
|
|
666
|
+
* @param {string} projectPath - Project path
|
|
667
|
+
* @param {Object} manifest - Manifest object
|
|
668
|
+
*/
|
|
669
|
+
async function updateIntegrationsOnly(projectPath, manifest) {
|
|
670
|
+
const msg = t().commands.update;
|
|
671
|
+
|
|
672
|
+
console.log(chalk.cyan(msg.updatingIntegrationsOnly));
|
|
673
|
+
console.log();
|
|
674
|
+
|
|
675
|
+
const aiTools = manifest.aiTools || [];
|
|
676
|
+
if (aiTools.length === 0) {
|
|
677
|
+
console.log(chalk.yellow(msg.noAiToolsConfigured));
|
|
678
|
+
console.log(chalk.gray(` ${msg.runConfigure}`));
|
|
679
|
+
console.log();
|
|
680
|
+
return;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
const spinner = ora(msg.regeneratingIntegrations).start();
|
|
684
|
+
|
|
685
|
+
// Use reusable regeneration function
|
|
686
|
+
const results = regenerateIntegrations(projectPath, manifest);
|
|
687
|
+
|
|
604
688
|
spinner.succeed(msg.regeneratedIntegrations.replace('{count}', results.updated.length));
|
|
605
689
|
|
|
606
690
|
// Update manifest
|
|
607
|
-
manifest.version = '3.
|
|
691
|
+
manifest.version = '3.3.0';
|
|
608
692
|
writeManifest(manifest, projectPath);
|
|
609
693
|
|
|
610
694
|
// Summary
|
|
@@ -715,6 +799,15 @@ async function syncIntegrationReferences(projectPath, manifest) {
|
|
|
715
799
|
manifest.fileHashes[integrationPath] = { ...hashInfo, installedAt: now };
|
|
716
800
|
}
|
|
717
801
|
|
|
802
|
+
// Track integration block hash for UDS content integrity
|
|
803
|
+
if (result.blockHashInfo) {
|
|
804
|
+
if (!manifest.integrationBlockHashes) manifest.integrationBlockHashes = {};
|
|
805
|
+
manifest.integrationBlockHashes[integrationPath] = {
|
|
806
|
+
...result.blockHashInfo,
|
|
807
|
+
installedAt: now
|
|
808
|
+
};
|
|
809
|
+
}
|
|
810
|
+
|
|
718
811
|
updatedCount++;
|
|
719
812
|
} else {
|
|
720
813
|
console.log(chalk.red(` ${msg.failedToUpdate.replace('{path}', integrationPath).replace('{error}', result.error)}`));
|
|
@@ -723,7 +816,7 @@ async function syncIntegrationReferences(projectPath, manifest) {
|
|
|
723
816
|
|
|
724
817
|
// Update manifest version and save
|
|
725
818
|
if (updatedCount > 0) {
|
|
726
|
-
manifest.version = '3.
|
|
819
|
+
manifest.version = '3.3.0';
|
|
727
820
|
writeManifest(manifest, projectPath);
|
|
728
821
|
}
|
|
729
822
|
|
|
@@ -840,6 +933,13 @@ async function updateSkillsOnly(projectPath, manifest) {
|
|
|
840
933
|
// Update manifest
|
|
841
934
|
manifest.skills.version = latestVersion;
|
|
842
935
|
manifest.skills.installations = skillsInstallations;
|
|
936
|
+
|
|
937
|
+
// Update skill hashes for integrity tracking
|
|
938
|
+
if (result.allFileHashes) {
|
|
939
|
+
if (!manifest.skillHashes) manifest.skillHashes = {};
|
|
940
|
+
Object.assign(manifest.skillHashes, result.allFileHashes);
|
|
941
|
+
}
|
|
942
|
+
|
|
843
943
|
writeManifest(manifest, projectPath);
|
|
844
944
|
|
|
845
945
|
console.log();
|
|
@@ -908,6 +1008,13 @@ async function updateCommandsOnly(projectPath, manifest) {
|
|
|
908
1008
|
manifest.commands = manifest.commands || {};
|
|
909
1009
|
manifest.commands.installed = true;
|
|
910
1010
|
manifest.commands.installations = commandsInstallations;
|
|
1011
|
+
|
|
1012
|
+
// Update command hashes for integrity tracking
|
|
1013
|
+
if (result.allFileHashes) {
|
|
1014
|
+
if (!manifest.commandHashes) manifest.commandHashes = {};
|
|
1015
|
+
Object.assign(manifest.commandHashes, result.allFileHashes);
|
|
1016
|
+
}
|
|
1017
|
+
|
|
911
1018
|
writeManifest(manifest, projectPath);
|
|
912
1019
|
|
|
913
1020
|
console.log();
|
|
@@ -915,19 +1022,21 @@ async function updateCommandsOnly(projectPath, manifest) {
|
|
|
915
1022
|
}
|
|
916
1023
|
|
|
917
1024
|
/**
|
|
918
|
-
* Check manifest.aiTools and detect missing Skills/Commands
|
|
1025
|
+
* Check manifest.aiTools and detect missing/outdated Skills/Commands
|
|
919
1026
|
* @param {string} projectPath - Project path
|
|
920
1027
|
* @param {Object} manifest - Manifest object
|
|
921
|
-
* @
|
|
1028
|
+
* @param {string} latestSkillsVersion - Latest skills version from repository
|
|
1029
|
+
* @returns {{missingSkills: Array, outdatedSkills: Array, missingCommands: Array}}
|
|
922
1030
|
*/
|
|
923
|
-
function checkNewFeatures(projectPath, manifest) {
|
|
1031
|
+
function checkNewFeatures(projectPath, manifest, latestSkillsVersion) {
|
|
924
1032
|
const aiTools = manifest.aiTools || [];
|
|
925
1033
|
|
|
926
1034
|
if (aiTools.length === 0) {
|
|
927
|
-
return { missingSkills: [], missingCommands: [] };
|
|
1035
|
+
return { missingSkills: [], outdatedSkills: [], missingCommands: [] };
|
|
928
1036
|
}
|
|
929
1037
|
|
|
930
1038
|
const missingSkills = [];
|
|
1039
|
+
const outdatedSkills = [];
|
|
931
1040
|
const missingCommands = [];
|
|
932
1041
|
|
|
933
1042
|
for (const tool of aiTools) {
|
|
@@ -936,20 +1045,16 @@ function checkNewFeatures(projectPath, manifest) {
|
|
|
936
1045
|
|
|
937
1046
|
// Check Skills support
|
|
938
1047
|
if (config.supportsSkills && config.skills) {
|
|
939
|
-
// Check if skills are installed for this agent
|
|
1048
|
+
// Check if skills are actually installed for this agent (file-based check)
|
|
940
1049
|
const projectInfo = getInstalledSkillsInfoForAgent(tool, 'project', projectPath);
|
|
941
1050
|
const userInfo = getInstalledSkillsInfoForAgent(tool, 'user');
|
|
942
1051
|
|
|
943
|
-
// Check if
|
|
944
|
-
const existingInstallations = manifest.skills?.installations || [];
|
|
945
|
-
const isInManifest = existingInstallations.some(
|
|
946
|
-
inst => inst.agent === tool || (inst.agent === 'claude-code' && tool === 'claude-code')
|
|
947
|
-
);
|
|
948
|
-
|
|
949
|
-
// Check if using marketplace (Claude Code only)
|
|
1052
|
+
// Check if using marketplace (Claude Code only) - marketplace auto-updates
|
|
950
1053
|
const usingMarketplace = manifest.skills?.location === 'marketplace' && tool === 'claude-code';
|
|
951
1054
|
|
|
952
|
-
|
|
1055
|
+
// Only trust actual file existence, not manifest records
|
|
1056
|
+
// (manifest records can be stale if user deleted the directory)
|
|
1057
|
+
const hasSkills = projectInfo?.installed || userInfo?.installed || usingMarketplace;
|
|
953
1058
|
|
|
954
1059
|
if (!hasSkills) {
|
|
955
1060
|
missingSkills.push({
|
|
@@ -957,6 +1062,23 @@ function checkNewFeatures(projectPath, manifest) {
|
|
|
957
1062
|
displayName: getAgentDisplayName(tool),
|
|
958
1063
|
paths: config.skills
|
|
959
1064
|
});
|
|
1065
|
+
} else if (latestSkillsVersion) {
|
|
1066
|
+
// Check if installed Skills are outdated
|
|
1067
|
+
const installedInfo = userInfo || projectInfo;
|
|
1068
|
+
const installedVersion = installedInfo?.version;
|
|
1069
|
+
|
|
1070
|
+
// Skip marketplace (auto-updates) and unknown versions
|
|
1071
|
+
if (!usingMarketplace && installedVersion && installedVersion !== latestSkillsVersion) {
|
|
1072
|
+
outdatedSkills.push({
|
|
1073
|
+
agent: tool,
|
|
1074
|
+
displayName: getAgentDisplayName(tool),
|
|
1075
|
+
paths: config.skills,
|
|
1076
|
+
currentVersion: installedVersion,
|
|
1077
|
+
latestVersion: latestSkillsVersion,
|
|
1078
|
+
level: userInfo?.installed ? 'user' : 'project',
|
|
1079
|
+
path: installedInfo?.path
|
|
1080
|
+
});
|
|
1081
|
+
}
|
|
960
1082
|
}
|
|
961
1083
|
}
|
|
962
1084
|
|
|
@@ -964,11 +1086,8 @@ function checkNewFeatures(projectPath, manifest) {
|
|
|
964
1086
|
if (config.commands !== null && config.commands) {
|
|
965
1087
|
const cmdInfo = getInstalledCommandsForAgent(tool, projectPath);
|
|
966
1088
|
|
|
967
|
-
//
|
|
968
|
-
const
|
|
969
|
-
const isInCmdManifest = existingCmdInstallations.includes(tool);
|
|
970
|
-
|
|
971
|
-
const hasCommands = cmdInfo?.installed || isInCmdManifest;
|
|
1089
|
+
// Only trust actual file existence, not manifest records
|
|
1090
|
+
const hasCommands = cmdInfo?.installed;
|
|
972
1091
|
|
|
973
1092
|
if (!hasCommands) {
|
|
974
1093
|
missingCommands.push({
|
|
@@ -980,21 +1099,22 @@ function checkNewFeatures(projectPath, manifest) {
|
|
|
980
1099
|
}
|
|
981
1100
|
}
|
|
982
1101
|
|
|
983
|
-
return { missingSkills, missingCommands };
|
|
1102
|
+
return { missingSkills, outdatedSkills, missingCommands };
|
|
984
1103
|
}
|
|
985
1104
|
|
|
986
1105
|
/**
|
|
987
1106
|
* Prompt user to install new features discovered during update
|
|
988
1107
|
* @param {Array} missingSkills - Array of {agent, displayName, paths}
|
|
1108
|
+
* @param {Array} outdatedSkills - Array of {agent, displayName, paths, currentVersion, latestVersion, level, path}
|
|
989
1109
|
* @param {Array} missingCommands - Array of {agent, displayName, path}
|
|
990
|
-
* @returns {Promise<{installSkills: Array, installCommands: Array}>}
|
|
1110
|
+
* @returns {Promise<{installSkills: Array, updateSkills: Array, installCommands: Array}>}
|
|
991
1111
|
*/
|
|
992
|
-
async function promptNewFeatureInstallation(missingSkills, missingCommands) {
|
|
1112
|
+
async function promptNewFeatureInstallation(missingSkills, outdatedSkills, missingCommands) {
|
|
993
1113
|
const msg = t().commands.update;
|
|
994
1114
|
|
|
995
|
-
// If nothing
|
|
996
|
-
if (missingSkills.length === 0 && missingCommands.length === 0) {
|
|
997
|
-
return { installSkills: [], installCommands: [] };
|
|
1115
|
+
// If nothing to do, return empty
|
|
1116
|
+
if (missingSkills.length === 0 && outdatedSkills.length === 0 && missingCommands.length === 0) {
|
|
1117
|
+
return { installSkills: [], updateSkills: [], installCommands: [] };
|
|
998
1118
|
}
|
|
999
1119
|
|
|
1000
1120
|
console.log();
|
|
@@ -1003,7 +1123,7 @@ async function promptNewFeatureInstallation(missingSkills, missingCommands) {
|
|
|
1003
1123
|
console.log(chalk.cyan('━'.repeat(50)));
|
|
1004
1124
|
console.log();
|
|
1005
1125
|
|
|
1006
|
-
const result = { installSkills: [], installCommands: [] };
|
|
1126
|
+
const result = { installSkills: [], updateSkills: [], installCommands: [] };
|
|
1007
1127
|
|
|
1008
1128
|
// Handle missing Skills with checkbox selection
|
|
1009
1129
|
if (missingSkills.length > 0) {
|
|
@@ -1065,6 +1185,64 @@ async function promptNewFeatureInstallation(missingSkills, missingCommands) {
|
|
|
1065
1185
|
console.log();
|
|
1066
1186
|
}
|
|
1067
1187
|
|
|
1188
|
+
// Handle outdated Skills with checkbox selection
|
|
1189
|
+
if (outdatedSkills.length > 0) {
|
|
1190
|
+
console.log(chalk.yellow(msg.skillsOutdatedFor || 'Skills updates available for these AI tools:'));
|
|
1191
|
+
for (const skill of outdatedSkills) {
|
|
1192
|
+
const levelLabel = skill.level === 'user' ? 'user' : 'project';
|
|
1193
|
+
const pathDisplay = skill.path || getSkillsDirForAgent(skill.agent, skill.level);
|
|
1194
|
+
console.log(chalk.gray(` • ${skill.displayName} (${levelLabel}: ${pathDisplay})`));
|
|
1195
|
+
console.log(chalk.gray(` ${skill.currentVersion} → ${skill.latestVersion}`));
|
|
1196
|
+
}
|
|
1197
|
+
console.log();
|
|
1198
|
+
|
|
1199
|
+
// Build checkbox choices with path info
|
|
1200
|
+
const updateChoices = outdatedSkills.map(skill => {
|
|
1201
|
+
const levelLabel = skill.level === 'user' ? 'user' : 'project';
|
|
1202
|
+
const pathDisplay = skill.path || getSkillsDirForAgent(skill.agent, skill.level);
|
|
1203
|
+
return {
|
|
1204
|
+
name: `${skill.displayName} ${chalk.gray(`(${levelLabel}: ${pathDisplay}) ${skill.currentVersion} → ${skill.latestVersion}`)}`,
|
|
1205
|
+
value: skill.agent,
|
|
1206
|
+
checked: true // Default checked for opt-out behavior
|
|
1207
|
+
};
|
|
1208
|
+
});
|
|
1209
|
+
|
|
1210
|
+
// Add skip option
|
|
1211
|
+
updateChoices.push(new inquirer.Separator());
|
|
1212
|
+
updateChoices.push({
|
|
1213
|
+
name: chalk.gray(msg.skipSkillsUpdate || 'Skip Skills update'),
|
|
1214
|
+
value: '__skip__'
|
|
1215
|
+
});
|
|
1216
|
+
|
|
1217
|
+
const { selectedUpdateAgents } = await inquirer.prompt([{
|
|
1218
|
+
type: 'checkbox',
|
|
1219
|
+
name: 'selectedUpdateAgents',
|
|
1220
|
+
message: msg.selectSkillsToUpdate || 'Select AI tools to update Skills for:',
|
|
1221
|
+
choices: updateChoices,
|
|
1222
|
+
validate: (answer) => {
|
|
1223
|
+
if (answer.includes('__skip__') && answer.length > 1) {
|
|
1224
|
+
return msg.skipValidationError || 'Cannot select Skip with other options';
|
|
1225
|
+
}
|
|
1226
|
+
return true;
|
|
1227
|
+
}
|
|
1228
|
+
}]);
|
|
1229
|
+
|
|
1230
|
+
// Filter out skip and map to update info
|
|
1231
|
+
const filteredUpdateAgents = selectedUpdateAgents.filter(a => a !== '__skip__');
|
|
1232
|
+
|
|
1233
|
+
if (filteredUpdateAgents.length > 0) {
|
|
1234
|
+
result.updateSkills = filteredUpdateAgents.map(agent => {
|
|
1235
|
+
const skillInfo = outdatedSkills.find(s => s.agent === agent);
|
|
1236
|
+
return {
|
|
1237
|
+
agent,
|
|
1238
|
+
level: skillInfo.level,
|
|
1239
|
+
path: skillInfo.path
|
|
1240
|
+
};
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
console.log();
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1068
1246
|
// Handle missing Commands with checkbox selection
|
|
1069
1247
|
if (missingCommands.length > 0) {
|
|
1070
1248
|
console.log(chalk.yellow(msg.commandsNotInstalledFor || 'Slash commands not yet installed for these AI tools:'));
|
package/src/i18n/messages.js
CHANGED
|
@@ -604,7 +604,18 @@ export const messages = {
|
|
|
604
604
|
skillsInstalledSuccess: 'Installed Skills for {count} AI tools',
|
|
605
605
|
commandsInstalledSuccess: 'Installed commands for {count} AI tools',
|
|
606
606
|
// Read-only hint
|
|
607
|
-
missingSkillsHint: 'Tip: Run `uds update` to install missing Skills/Commands'
|
|
607
|
+
missingSkillsHint: 'Tip: Run `uds update` to install missing Skills/Commands',
|
|
608
|
+
// Summary mode (--summary)
|
|
609
|
+
summary_mode: {
|
|
610
|
+
title: 'UDS Status Summary',
|
|
611
|
+
notInitialized: 'Not initialized',
|
|
612
|
+
manifestError: 'Manifest error',
|
|
613
|
+
version: 'Version',
|
|
614
|
+
level: 'Level',
|
|
615
|
+
files: 'Files',
|
|
616
|
+
skills: 'Skills',
|
|
617
|
+
commands: 'Commands'
|
|
618
|
+
}
|
|
608
619
|
},
|
|
609
620
|
|
|
610
621
|
// init command (commands/init.js)
|
|
@@ -843,7 +854,12 @@ export const messages = {
|
|
|
843
854
|
errors: 'Errors',
|
|
844
855
|
noCommandSupportedTools: 'No AI tools with command support configured',
|
|
845
856
|
commandSupportedList: 'Tools that support commands: OpenCode, Copilot, Roo Code, Gemini CLI',
|
|
846
|
-
commandsInstalled: 'commands'
|
|
857
|
+
commandsInstalled: 'commands',
|
|
858
|
+
// Smart apply
|
|
859
|
+
applyChangesNow: 'Apply changes now? (regenerate integration files)',
|
|
860
|
+
runUpdateLater: 'Run `uds update --integrations-only` later to apply changes',
|
|
861
|
+
applyingChanges: 'Applying changes...',
|
|
862
|
+
changesApplied: 'Changes applied successfully'
|
|
847
863
|
}
|
|
848
864
|
}
|
|
849
865
|
},
|
|
@@ -1444,7 +1460,18 @@ export const messages = {
|
|
|
1444
1460
|
skillsInstalledSuccess: '已為 {count} 個 AI 工具安裝 Skills',
|
|
1445
1461
|
commandsInstalledSuccess: '已為 {count} 個 AI 工具安裝斜線命令',
|
|
1446
1462
|
// Read-only hint
|
|
1447
|
-
missingSkillsHint: '提示:執行 `uds update` 安裝缺少的 Skills/斜線命令'
|
|
1463
|
+
missingSkillsHint: '提示:執行 `uds update` 安裝缺少的 Skills/斜線命令',
|
|
1464
|
+
// Summary mode (--summary)
|
|
1465
|
+
summary_mode: {
|
|
1466
|
+
title: 'UDS 狀態摘要',
|
|
1467
|
+
notInitialized: '尚未初始化',
|
|
1468
|
+
manifestError: 'Manifest 錯誤',
|
|
1469
|
+
version: '版本',
|
|
1470
|
+
level: '等級',
|
|
1471
|
+
files: '檔案',
|
|
1472
|
+
skills: 'Skills',
|
|
1473
|
+
commands: '斜線命令'
|
|
1474
|
+
}
|
|
1448
1475
|
},
|
|
1449
1476
|
|
|
1450
1477
|
// init command (commands/init.js)
|
|
@@ -1683,7 +1710,12 @@ export const messages = {
|
|
|
1683
1710
|
errors: '錯誤',
|
|
1684
1711
|
noCommandSupportedTools: '未設定支援命令的 AI 工具',
|
|
1685
1712
|
commandSupportedList: '支援命令的工具:OpenCode、Copilot、Roo Code、Gemini CLI',
|
|
1686
|
-
commandsInstalled: '個命令'
|
|
1713
|
+
commandsInstalled: '個命令',
|
|
1714
|
+
// Smart apply
|
|
1715
|
+
applyChangesNow: '是否立即套用變更?(重新產生整合檔案)',
|
|
1716
|
+
runUpdateLater: '稍後執行 `uds update --integrations-only` 以套用變更',
|
|
1717
|
+
applyingChanges: '正在套用變更...',
|
|
1718
|
+
changesApplied: '變更套用成功'
|
|
1687
1719
|
}
|
|
1688
1720
|
}
|
|
1689
1721
|
},
|
|
@@ -2187,7 +2219,18 @@ export const messages = {
|
|
|
2187
2219
|
skillsInstalledSuccess: '已为 {count} 个 AI 工具安装 Skills',
|
|
2188
2220
|
commandsInstalledSuccess: '已为 {count} 个 AI 工具安装斜线命令',
|
|
2189
2221
|
// Read-only hint
|
|
2190
|
-
missingSkillsHint: '提示:执行 `uds update` 安装缺少的 Skills/斜线命令'
|
|
2222
|
+
missingSkillsHint: '提示:执行 `uds update` 安装缺少的 Skills/斜线命令',
|
|
2223
|
+
// Summary mode (--summary)
|
|
2224
|
+
summary_mode: {
|
|
2225
|
+
title: 'UDS 状态摘要',
|
|
2226
|
+
notInitialized: '尚未初始化',
|
|
2227
|
+
manifestError: 'Manifest 错误',
|
|
2228
|
+
version: '版本',
|
|
2229
|
+
level: '等级',
|
|
2230
|
+
files: '文件',
|
|
2231
|
+
skills: 'Skills',
|
|
2232
|
+
commands: '斜线命令'
|
|
2233
|
+
}
|
|
2191
2234
|
},
|
|
2192
2235
|
|
|
2193
2236
|
// update command
|
|
@@ -2355,7 +2398,12 @@ export const messages = {
|
|
|
2355
2398
|
errors: '错误',
|
|
2356
2399
|
noCommandSupportedTools: '未配置支持命令的 AI 工具',
|
|
2357
2400
|
commandSupportedList: '支持命令的工具:OpenCode、Copilot、Roo Code、Gemini CLI',
|
|
2358
|
-
commandsInstalled: '个命令'
|
|
2401
|
+
commandsInstalled: '个命令',
|
|
2402
|
+
// Smart apply
|
|
2403
|
+
applyChangesNow: '是否立即应用变更?(重新生成集成文件)',
|
|
2404
|
+
runUpdateLater: '稍后执行 `uds update --integrations-only` 以应用变更',
|
|
2405
|
+
applyingChanges: '正在应用变更...',
|
|
2406
|
+
changesApplied: '变更应用成功'
|
|
2359
2407
|
}
|
|
2360
2408
|
}
|
|
2361
2409
|
}
|