universal-dev-standards 3.5.1-beta.17 → 3.5.1-beta.19

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.
@@ -77,7 +77,7 @@ Always include **Skip** option: Don't update at this time.
77
77
  - Option 1: "更新至 Beta (建議)" - "更新標準至 3.5.1-beta.15 版本(🟡 功能大致完成)"
78
78
  - Option 2: "暫時跳過" - "目前不進行更新,維持現有版本"
79
79
 
80
- ### Step 3: Execute | 步驟 3:執行
80
+ ### Step 3: Execute Update | 步驟 3:執行更新
81
81
 
82
82
  **If Update Now selected:**
83
83
  ```bash
@@ -89,39 +89,93 @@ uds update --yes
89
89
  uds update --beta --yes
90
90
  ```
91
91
 
92
- ### Step 4: Install Skills/Commands | 步驟 4:安裝 Skills/Commands
92
+ ### Step 4: Check Skills/Commands Status | 步驟 4:檢查 Skills/Commands 狀態
93
93
 
94
- After update completes, check if Skills/Commands need installation.
94
+ After update completes, the CLI automatically detects missing or outdated Skills/Commands.
95
95
 
96
- 更新完成後,檢查是否需要安裝 Skills/Commands。
96
+ 更新完成後,CLI 會自動偵測缺少或過時的 Skills/Commands。
97
97
 
98
- **Check installation status:**
98
+ **Important:** The CLI uses file-based detection (`getInstalledSkillsInfoForAgent`) to check actual installation status, not just manifest records.
99
99
 
100
- 1. Read `.standards/manifest.json` to get `aiTools` list and `skills.installed` status
101
- 2. Check if Skills are installed for each configured AI tool
102
- 3. Check if Commands are installed for tools that support them (opencode, copilot, gemini-cli, roo-code)
100
+ **重要:** CLI 使用檔案實際存在檢測,而非僅讀取 manifest 記錄。
103
101
 
104
- **If missing Skills/Commands detected**, use AskUserQuestion:
102
+ #### Handling Missing Skills | 處理缺少的 Skills
103
+
104
+ If missing Skills are detected for configured AI tools, CLI shows checkbox selection:
105
+
106
+ 如果偵測到已配置 AI 工具缺少 Skills,CLI 會顯示多選介面:
107
+
108
+ ```
109
+ Skills not yet installed for these AI tools:
110
+ • Claude Code
111
+ • OpenCode
112
+
113
+ ? Select AI tools to install Skills for: (Press <space> to select)
114
+ ❯ ◯ Claude Code
115
+ ◯ OpenCode
116
+ ──────────────
117
+ ◯ Skip Skills installation
118
+ ```
119
+
120
+ **After tool selection, prompt for installation level:**
105
121
 
106
122
  | Option | Description |
107
123
  |--------|-------------|
108
- | **Install All (Recommended)** | Install Skills + Commands for all configured tools |
109
- | **Skills Only** | Install only Skills |
110
- | **Commands Only** | Install only Commands |
111
- | **Skip** | Don't install at this time |
124
+ | **Project level** | Install to `.claude/skills/`, `.opencode/skill/`, etc. |
125
+ | **User level** | Install to `~/.claude/skills/`, `~/.opencode/skill/`, etc. |
126
+
127
+ #### Handling Outdated Skills | 處理過時的 Skills
128
+
129
+ If installed Skills have older version than latest, CLI shows update prompt:
130
+
131
+ 如果已安裝的 Skills 版本比最新版本舊,CLI 會顯示更新提示:
132
+
133
+ ```
134
+ Skills updates available for these AI tools:
135
+ • Claude Code (project: .claude/skills/)
136
+ 3.4.0 → 3.5.1
137
+
138
+ ? Select AI tools to update Skills for: (Press <space> to select)
139
+ ❯ ◉ Claude Code (project) 3.4.0 → 3.5.1
140
+ ──────────────
141
+ ◯ Skip Skills update
142
+ ```
143
+
144
+ #### Handling Missing Commands | 處理缺少的 Commands
145
+
146
+ Similar checkbox selection for Commands:
147
+
148
+ ```
149
+ Slash commands not yet installed for these AI tools:
150
+ • OpenCode → .opencode/commands/
151
+ • GitHub Copilot → .github/commands/
152
+
153
+ ? Select AI tools to install Commands for: (Press <space> to select)
154
+ ❯ ◉ OpenCode (.opencode/commands/)
155
+ ◉ GitHub Copilot (.github/commands/)
156
+ ──────────────
157
+ ◯ Skip Commands installation
158
+ ```
112
159
 
113
- **Based on user selection, execute:**
160
+ #### Declined Features Handling | 拒絕功能處理
114
161
 
115
- | Selection | Command |
116
- |-----------|---------|
117
- | Install All | `uds configure --type skills --ai-tool <tool>` for each tool, then `uds configure --type commands --ai-tool <tool>` |
118
- | Skills Only | `uds configure --type skills --ai-tool <tool>` for each tool |
119
- | Commands Only | `uds configure --type commands --ai-tool <tool>` for each tool |
120
- | Skip | No action needed |
162
+ **Important:** The CLI tracks user's declined choices in `manifest.declinedFeatures`.
121
163
 
122
- **Note**: The `--ai-tool` option allows non-interactive installation for specific tools.
164
+ **重要:** CLI 會在 `manifest.declinedFeatures` 中追蹤用戶拒絕的選項。
123
165
 
124
- Explain the results and any next steps to the user.
166
+ - Tools that user previously declined will NOT be shown in subsequent prompts
167
+ - Users can reinstall declined features via `/config skills` or `/config commands`
168
+ - Declining is remembered per-tool (e.g., declining Skills for OpenCode doesn't affect Claude Code)
169
+
170
+ 用戶之前拒絕的工具不會在後續提示中顯示。可透過 `/config skills` 或 `/config commands` 重新安裝。
171
+
172
+ ### Step 5: Explain Results | 步驟 5:說明結果
173
+
174
+ After all operations complete, explain:
175
+ 1. What was updated (standards version, file count)
176
+ 2. Skills/Commands installation results
177
+ 3. Any errors encountered
178
+ 4. Next steps (restart AI tool if Skills were installed)
125
179
 
126
180
  ## Quick Mode | 快速模式
127
181
 
@@ -131,8 +185,12 @@ When invoked with `--yes` or specific options, skip interactive questions:
131
185
  /update --yes # Update without confirmation
132
186
  /update --beta --yes # Update to beta version
133
187
  /update --offline # Skip npm registry check
188
+ /update --skills # Update Skills only
189
+ /update --commands # Update Commands only
134
190
  ```
135
191
 
192
+ **Note:** In `--yes` mode, CLI shows hints about available Skills/Commands but does NOT auto-install them (conservative behavior).
193
+
136
194
  ## Options Reference | 選項參考
137
195
 
138
196
  | Option | Description | 說明 |
@@ -140,12 +198,17 @@ When invoked with `--yes` or specific options, skip interactive questions:
140
198
  | `--yes`, `-y` | Skip confirmation prompt | 跳過確認提示 |
141
199
  | `--offline` | Skip npm registry check | 跳過 npm registry 檢查 |
142
200
  | `--beta` | Check for beta version updates | 檢查 beta 版本更新 |
201
+ | `--skills` | Update Skills only | 僅更新 Skills |
202
+ | `--commands` | Update Commands only | 僅更新 Commands |
203
+ | `--integrations-only` | Regenerate integration files only | 僅重新產生整合檔案 |
204
+ | `--sync-refs` | Sync integration file references | 同步整合檔案參考 |
205
+ | `--standards-only` | Update standards without integrations | 僅更新標準,不更新整合 |
143
206
 
144
207
  ## What Gets Updated | 更新內容
145
208
 
146
209
  - Standard files in `.standards/` directory
147
210
  - Extension files (language, framework, locale)
148
- - Integration files (`.cursorrules`, etc.)
211
+ - Integration files (`.cursorrules`, `CLAUDE.md`, etc.)
149
212
  - Version info in `manifest.json`
150
213
 
151
214
  ## Skills Update | Skills 更新
@@ -166,7 +229,11 @@ Skills are managed separately:
166
229
  **"Already up to date"**
167
230
  - No action needed; standards are current
168
231
 
232
+ **"Skills previously declined"**
233
+ - Run `/config skills` to reinstall declined Skills
234
+
169
235
  ## Reference | 參考
170
236
 
171
237
  - CLI documentation: `uds update --help`
172
238
  - Check command: [/check](./check.md)
239
+ - Config command: [/config](./config.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "universal-dev-standards",
3
- "version": "3.5.1-beta.17",
3
+ "version": "3.5.1-beta.19",
4
4
  "description": "CLI tool for adopting Universal Development Standards",
5
5
  "keywords": [
6
6
  "documentation",
@@ -693,18 +693,21 @@ function displaySkillsStatus(manifest, projectPath, msg) {
693
693
  }
694
694
 
695
695
  // Check for Marketplace installation (Claude Code specific)
696
+ // Dynamically detect marketplace installation regardless of manifest
696
697
  const hasClaudeCode = aiTools.includes('claude-code');
698
+ const marketplaceInfo = getMarketplaceSkillsInfo();
699
+ const hasMarketplaceSkills = marketplaceInfo?.installed;
700
+
697
701
  const location = manifest.skills?.location || '';
698
- const isMarketplace = location === 'marketplace' ||
702
+ const isMarketplaceInManifest = location === 'marketplace' ||
699
703
  location.includes('plugins/cache') ||
700
704
  location.includes('plugins\\cache');
701
705
 
702
- if (isMarketplace && hasClaudeCode) {
706
+ // Show marketplace status if actually installed (not just manifest)
707
+ if (hasMarketplaceSkills && hasClaudeCode) {
703
708
  console.log(chalk.green(` ${msg.skillsViaMarketplace}`));
704
709
 
705
- // Try to get actual version from marketplace
706
- const marketplaceInfo = getMarketplaceSkillsInfo();
707
- if (marketplaceInfo && marketplaceInfo.version && marketplaceInfo.version !== 'unknown') {
710
+ if (marketplaceInfo.version && marketplaceInfo.version !== 'unknown') {
708
711
  console.log(chalk.gray(` ${t().commands.common.version}: ${marketplaceInfo.version}`));
709
712
  if (marketplaceInfo.lastUpdated) {
710
713
  const updateDate = marketplaceInfo.lastUpdated.split('T')[0];
@@ -732,7 +735,7 @@ function displaySkillsStatus(manifest, projectPath, msg) {
732
735
  const userSkillsInfo = getInstalledSkillsInfoForAgent(tool, 'user', projectPath);
733
736
 
734
737
  // Check if using marketplace for Claude Code
735
- const usingMarketplace = isMarketplace && tool === 'claude-code';
738
+ const usingMarketplace = (hasMarketplaceSkills || isMarketplaceInManifest) && tool === 'claude-code';
736
739
 
737
740
  if (projectSkillsInfo?.installed || userSkillsInfo?.installed || usingMarketplace) {
738
741
  console.log(chalk.green(` ✓ Skills ${msg.installed || 'installed'}:`));
@@ -1425,8 +1428,12 @@ function getSkillsStatusSummary(manifest, projectPath) {
1425
1428
  const parts = [];
1426
1429
 
1427
1430
  // Check for Marketplace installation (Claude Code specific)
1431
+ // Dynamically detect marketplace installation regardless of manifest
1432
+ const marketplaceInfo = getMarketplaceSkillsInfo();
1433
+ const hasMarketplaceSkills = marketplaceInfo?.installed;
1434
+
1428
1435
  const location = manifest.skills?.location || '';
1429
- const isMarketplace = location === 'marketplace' ||
1436
+ const isMarketplaceInManifest = location === 'marketplace' ||
1430
1437
  location.includes('plugins/cache') ||
1431
1438
  location.includes('plugins\\cache');
1432
1439
 
@@ -1435,7 +1442,7 @@ function getSkillsStatusSummary(manifest, projectPath) {
1435
1442
  if (!config || !config.supportsSkills) continue;
1436
1443
 
1437
1444
  const displayName = getAgentDisplayName(tool);
1438
- const usingMarketplace = isMarketplace && tool === 'claude-code';
1445
+ const usingMarketplace = (hasMarketplaceSkills || isMarketplaceInManifest) && tool === 'claude-code';
1439
1446
 
1440
1447
  if (usingMarketplace) {
1441
1448
  parts.push(chalk.green(`${displayName} ✓`));
@@ -44,6 +44,7 @@ import {
44
44
  writeIntegrationFile,
45
45
  getToolFilePath
46
46
  } from '../utils/integration-generator.js';
47
+ import { getMarketplaceSkillsInfo } from '../utils/github.js';
47
48
  import { t } from '../i18n/messages.js';
48
49
  import { regenerateIntegrations } from './update.js';
49
50
 
@@ -562,8 +563,21 @@ async function handleSkillsConfiguration(manifest, projectPath, msg, common, spe
562
563
  return;
563
564
  }
564
565
 
566
+ // Get declined skills from manifest
567
+ const declinedSkills = manifest.declinedFeatures?.skills || [];
568
+
569
+ // Check if Skills are installed via marketplace (Claude Code only)
570
+ const marketplaceInfo = getMarketplaceSkillsInfo();
571
+ const hasMarketplaceSkills = marketplaceInfo?.installed;
572
+
565
573
  // Show current Skills status
566
574
  console.log(chalk.cyan(msg.currentSkillsStatus || 'Current Skills status:'));
575
+
576
+ // Show marketplace status if applicable
577
+ if (hasMarketplaceSkills && aiTools.includes('claude-code')) {
578
+ console.log(chalk.green(` ✓ ${msg.viaMarketplace || 'Via Marketplace'}: ${marketplaceInfo.version || 'installed'}`));
579
+ }
580
+
567
581
  for (const tool of aiTools) {
568
582
  const config = getAgentConfig(tool);
569
583
  if (!config?.supportsSkills) continue;
@@ -580,23 +594,48 @@ async function handleSkillsConfiguration(manifest, projectPath, msg, common, spe
580
594
  if (projectInfo?.installed) {
581
595
  console.log(chalk.gray(` - Project: ${projectInfo.version || 'installed'}`));
582
596
  }
597
+ } else if (declinedSkills.includes(tool)) {
598
+ console.log(chalk.yellow(` ⊘ ${displayName}: ${msg.previouslyDeclined || 'Previously declined'}`));
599
+ } else if (hasMarketplaceSkills && tool === 'claude-code') {
600
+ // Claude Code has marketplace skills but no file-based installation
601
+ console.log(chalk.cyan(` ◎ ${displayName}: ${msg.marketplaceOnly || 'Marketplace only (no local files)'}`));
583
602
  } else {
584
603
  console.log(chalk.gray(` ○ ${displayName}: ${msg.notInstalled || 'Not installed'}`));
585
604
  }
586
605
  }
606
+
607
+ // Show marketplace coexistence note if user might want to install local files
608
+ if (hasMarketplaceSkills && aiTools.includes('claude-code')) {
609
+ console.log();
610
+ console.log(chalk.cyan(` ℹ ${msg.marketplaceCoexistNote || 'Note: File-based installation will coexist with Marketplace version'}`));
611
+ }
587
612
  console.log();
588
613
 
614
+ // Build menu choices
615
+ const menuChoices = [
616
+ { name: msg.installSkills || 'Install/Update Skills', value: 'install' }
617
+ ];
618
+
619
+ // Add reinstall declined option if there are declined skills
620
+ if (declinedSkills.length > 0) {
621
+ menuChoices.push({
622
+ name: msg.reinstallDeclinedSkills || 'Reinstall declined Skills',
623
+ value: 'reinstall_declined'
624
+ });
625
+ }
626
+
627
+ menuChoices.push(
628
+ { name: msg.viewStatus || 'View status only', value: 'view' },
629
+ { name: common.cancel || 'Cancel', value: 'cancel' }
630
+ );
631
+
589
632
  // Ask what action to take
590
633
  const { action } = await inquirer.default.prompt([
591
634
  {
592
635
  type: 'list',
593
636
  name: 'action',
594
637
  message: msg.skillsAction || 'What would you like to do?',
595
- choices: [
596
- { name: msg.installSkills || 'Install/Update Skills', value: 'install' },
597
- { name: msg.viewStatus || 'View status only', value: 'view' },
598
- { name: common.cancel || 'Cancel', value: 'cancel' }
599
- ]
638
+ choices: menuChoices
600
639
  }
601
640
  ]);
602
641
 
@@ -605,6 +644,63 @@ async function handleSkillsConfiguration(manifest, projectPath, msg, common, spe
605
644
  return;
606
645
  }
607
646
 
647
+ // Handle reinstall declined action
648
+ if (action === 'reinstall_declined') {
649
+ // Get only the declined tools that support skills
650
+ const declinedToolsWithSupport = declinedSkills.filter(tool => {
651
+ const config = getAgentConfig(tool);
652
+ return config?.supportsSkills;
653
+ });
654
+
655
+ if (declinedToolsWithSupport.length === 0) {
656
+ console.log(chalk.gray(msg.noChanges || 'No changes made'));
657
+ return;
658
+ }
659
+
660
+ // Prompt for installation level
661
+ const { skillsLevel } = await inquirer.default.prompt([{
662
+ type: 'list',
663
+ name: 'skillsLevel',
664
+ message: msg.skillsLevelQuestion || 'Where should Skills be installed?',
665
+ choices: [
666
+ { name: `${msg.projectLevel || 'Project level'} (.claude/skills/, etc.)`, value: 'project' },
667
+ { name: `${msg.userLevel || 'User level'} (~/.claude/skills/, etc.)`, value: 'user' }
668
+ ],
669
+ default: 'project'
670
+ }]);
671
+
672
+ const installations = declinedToolsWithSupport.map(agent => ({
673
+ agent,
674
+ location: skillsLevel
675
+ }));
676
+
677
+ // Install Skills
678
+ const spinner = ora(msg.installingSkills || 'Installing Skills...').start();
679
+ const result = await installSkillsToMultipleAgents(installations, null, projectPath);
680
+ spinner.stop();
681
+
682
+ if (result.success) {
683
+ console.log(chalk.green(msg.skillsInstallSuccess || 'Skills installed successfully'));
684
+ console.log(chalk.gray(` ${msg.totalInstalled || 'Total installed'}: ${result.totalInstalled}`));
685
+ } else {
686
+ console.log(chalk.yellow(msg.skillsInstallPartial || 'Skills installed with some issues'));
687
+ if (result.totalErrors > 0) {
688
+ console.log(chalk.red(` ${msg.errors || 'Errors'}: ${result.totalErrors}`));
689
+ }
690
+ }
691
+
692
+ // Update manifest - clear declined status for installed tools
693
+ manifest.skills = manifest.skills || {};
694
+ manifest.skills.installations = installations;
695
+ if (manifest.declinedFeatures?.skills) {
696
+ manifest.declinedFeatures.skills = manifest.declinedFeatures.skills.filter(
697
+ tool => !declinedToolsWithSupport.includes(tool)
698
+ );
699
+ }
700
+ writeManifest(manifest, projectPath);
701
+ return;
702
+ }
703
+
608
704
  // Use unified installation prompt
609
705
  const installations = await promptSkillsInstallLocation(aiTools);
610
706
  if (installations.length === 0) {
@@ -692,6 +788,9 @@ async function handleCommandsConfiguration(manifest, projectPath, msg, common, s
692
788
  return;
693
789
  }
694
790
 
791
+ // Get declined commands from manifest
792
+ const declinedCommands = manifest.declinedFeatures?.commands || [];
793
+
695
794
  // Show current Commands status
696
795
  console.log(chalk.cyan(msg.currentCommandsStatus || 'Current Commands status:'));
697
796
  for (const tool of commandSupportedTools) {
@@ -700,23 +799,42 @@ async function handleCommandsConfiguration(manifest, projectPath, msg, common, s
700
799
 
701
800
  if (commandsInfo?.installed) {
702
801
  console.log(chalk.green(` ✓ ${displayName}: ${commandsInfo.count} ${msg.commandsInstalled || 'commands'}`));
802
+ } else if (declinedCommands.includes(tool)) {
803
+ console.log(chalk.yellow(` ⊘ ${displayName}: ${msg.previouslyDeclined || 'Previously declined'}`));
703
804
  } else {
704
805
  console.log(chalk.gray(` ○ ${displayName}: ${msg.notInstalled || 'Not installed'}`));
705
806
  }
706
807
  }
707
808
  console.log();
708
809
 
810
+ // Build menu choices
811
+ const menuChoices = [
812
+ { name: msg.installCommands || 'Install/Update Commands', value: 'install' }
813
+ ];
814
+
815
+ // Add reinstall declined option if there are declined commands
816
+ const declinedCommandsWithSupport = declinedCommands.filter(tool =>
817
+ commandSupportedTools.includes(tool)
818
+ );
819
+ if (declinedCommandsWithSupport.length > 0) {
820
+ menuChoices.push({
821
+ name: msg.reinstallDeclinedCommands || 'Reinstall declined Commands',
822
+ value: 'reinstall_declined'
823
+ });
824
+ }
825
+
826
+ menuChoices.push(
827
+ { name: msg.viewStatus || 'View status only', value: 'view' },
828
+ { name: common.cancel || 'Cancel', value: 'cancel' }
829
+ );
830
+
709
831
  // Ask what action to take
710
832
  const { action } = await inquirer.default.prompt([
711
833
  {
712
834
  type: 'list',
713
835
  name: 'action',
714
836
  message: msg.commandsAction || 'What would you like to do?',
715
- choices: [
716
- { name: msg.installCommands || 'Install/Update Commands', value: 'install' },
717
- { name: msg.viewStatus || 'View status only', value: 'view' },
718
- { name: common.cancel || 'Cancel', value: 'cancel' }
719
- ]
837
+ choices: menuChoices
720
838
  }
721
839
  ]);
722
840
 
@@ -725,6 +843,40 @@ async function handleCommandsConfiguration(manifest, projectPath, msg, common, s
725
843
  return;
726
844
  }
727
845
 
846
+ // Handle reinstall declined action
847
+ if (action === 'reinstall_declined') {
848
+ if (declinedCommandsWithSupport.length === 0) {
849
+ console.log(chalk.gray(msg.noChanges || 'No changes made'));
850
+ return;
851
+ }
852
+
853
+ // Install Commands
854
+ const spinner = ora(msg.installingCommands || 'Installing Commands...').start();
855
+ const result = await installCommandsToMultipleAgents(declinedCommandsWithSupport, null, projectPath);
856
+ spinner.stop();
857
+
858
+ if (result.success) {
859
+ console.log(chalk.green(msg.commandsInstallSuccess || 'Commands installed successfully'));
860
+ console.log(chalk.gray(` ${msg.totalInstalled || 'Total installed'}: ${result.totalInstalled}`));
861
+ } else {
862
+ console.log(chalk.yellow(msg.commandsInstallPartial || 'Commands installed with some issues'));
863
+ if (result.totalErrors > 0) {
864
+ console.log(chalk.red(` ${msg.errors || 'Errors'}: ${result.totalErrors}`));
865
+ }
866
+ }
867
+
868
+ // Update manifest - clear declined status for installed tools
869
+ manifest.commands = manifest.commands || {};
870
+ manifest.commands.installations = declinedCommandsWithSupport;
871
+ if (manifest.declinedFeatures?.commands) {
872
+ manifest.declinedFeatures.commands = manifest.declinedFeatures.commands.filter(
873
+ tool => !declinedCommandsWithSupport.includes(tool)
874
+ );
875
+ }
876
+ writeManifest(manifest, projectPath);
877
+ return;
878
+ }
879
+
728
880
  // Use unified installation prompt
729
881
  const selectedAgents = await promptCommandsInstallation(commandSupportedTools);
730
882
  if (selectedAgents.length === 0) {
@@ -30,6 +30,7 @@ import {
30
30
  getSkillsDirForAgent,
31
31
  getCommandsDirForAgent
32
32
  } from '../config/ai-agent-paths.js';
33
+ import { getMarketplaceSkillsInfo } from '../utils/github.js';
33
34
 
34
35
  /**
35
36
  * Compare two semantic versions
@@ -422,7 +423,7 @@ export async function updateCommand(options) {
422
423
  if (missingSkills.length > 0 || outdatedSkills.length > 0 || missingCommands.length > 0) {
423
424
  if (!options.yes) {
424
425
  // Interactive mode: prompt user to install/update
425
- const { installSkills, updateSkills, installCommands } = await promptNewFeatureInstallation(
426
+ const { installSkills, updateSkills, installCommands, declinedSkills, declinedCommands } = await promptNewFeatureInstallation(
426
427
  missingSkills,
427
428
  outdatedSkills,
428
429
  missingCommands
@@ -509,8 +510,39 @@ export async function updateCommand(options) {
509
510
  }
510
511
  }
511
512
 
512
- // Write updated manifest if anything was installed or updated
513
- if (installSkills.length > 0 || updateSkills.length > 0 || installCommands.length > 0) {
513
+ // Update declined features in manifest
514
+ if (declinedSkills.length > 0 || declinedCommands.length > 0) {
515
+ if (!manifest.declinedFeatures) manifest.declinedFeatures = {};
516
+
517
+ // Merge with existing declined items (avoid duplicates)
518
+ if (declinedSkills.length > 0) {
519
+ const existing = manifest.declinedFeatures.skills || [];
520
+ manifest.declinedFeatures.skills = [...new Set([...existing, ...declinedSkills])];
521
+ }
522
+
523
+ if (declinedCommands.length > 0) {
524
+ const existing = manifest.declinedFeatures.commands || [];
525
+ manifest.declinedFeatures.commands = [...new Set([...existing, ...declinedCommands])];
526
+ }
527
+ }
528
+
529
+ // Remove from declined list if user decided to install this time
530
+ if (installSkills.length > 0 && manifest.declinedFeatures?.skills) {
531
+ const installedAgents = installSkills.map(s => s.agent);
532
+ manifest.declinedFeatures.skills = manifest.declinedFeatures.skills.filter(
533
+ agent => !installedAgents.includes(agent)
534
+ );
535
+ }
536
+ if (installCommands.length > 0 && manifest.declinedFeatures?.commands) {
537
+ manifest.declinedFeatures.commands = manifest.declinedFeatures.commands.filter(
538
+ agent => !installCommands.includes(agent)
539
+ );
540
+ }
541
+
542
+ // Write updated manifest if anything was installed, updated, or declined
543
+ const hasChanges = installSkills.length > 0 || updateSkills.length > 0 ||
544
+ installCommands.length > 0 || declinedSkills.length > 0 || declinedCommands.length > 0;
545
+ if (hasChanges) {
514
546
  writeManifest(manifest, projectPath);
515
547
  }
516
548
  } else {
@@ -1035,6 +1067,10 @@ function checkNewFeatures(projectPath, manifest, latestSkillsVersion) {
1035
1067
  return { missingSkills: [], outdatedSkills: [], missingCommands: [] };
1036
1068
  }
1037
1069
 
1070
+ // Get declined features from manifest (to exclude from prompts)
1071
+ const declinedSkills = manifest.declinedFeatures?.skills || [];
1072
+ const declinedCommands = manifest.declinedFeatures?.commands || [];
1073
+
1038
1074
  const missingSkills = [];
1039
1075
  const outdatedSkills = [];
1040
1076
  const missingCommands = [];
@@ -1056,7 +1092,8 @@ function checkNewFeatures(projectPath, manifest, latestSkillsVersion) {
1056
1092
  // (manifest records can be stale if user deleted the directory)
1057
1093
  const hasSkills = projectInfo?.installed || userInfo?.installed || usingMarketplace;
1058
1094
 
1059
- if (!hasSkills) {
1095
+ // Skip if user previously declined this tool's skills
1096
+ if (!hasSkills && !declinedSkills.includes(tool)) {
1060
1097
  missingSkills.push({
1061
1098
  agent: tool,
1062
1099
  displayName: getAgentDisplayName(tool),
@@ -1089,7 +1126,8 @@ function checkNewFeatures(projectPath, manifest, latestSkillsVersion) {
1089
1126
  // Only trust actual file existence, not manifest records
1090
1127
  const hasCommands = cmdInfo?.installed;
1091
1128
 
1092
- if (!hasCommands) {
1129
+ // Skip if user previously declined this tool's commands
1130
+ if (!hasCommands && !declinedCommands.includes(tool)) {
1093
1131
  missingCommands.push({
1094
1132
  agent: tool,
1095
1133
  displayName: getAgentDisplayName(tool),
@@ -1107,14 +1145,14 @@ function checkNewFeatures(projectPath, manifest, latestSkillsVersion) {
1107
1145
  * @param {Array} missingSkills - Array of {agent, displayName, paths}
1108
1146
  * @param {Array} outdatedSkills - Array of {agent, displayName, paths, currentVersion, latestVersion, level, path}
1109
1147
  * @param {Array} missingCommands - Array of {agent, displayName, path}
1110
- * @returns {Promise<{installSkills: Array, updateSkills: Array, installCommands: Array}>}
1148
+ * @returns {Promise<{installSkills: Array, updateSkills: Array, installCommands: Array, declinedSkills: Array, declinedCommands: Array}>}
1111
1149
  */
1112
1150
  async function promptNewFeatureInstallation(missingSkills, outdatedSkills, missingCommands) {
1113
1151
  const msg = t().commands.update;
1114
1152
 
1115
1153
  // If nothing to do, return empty
1116
1154
  if (missingSkills.length === 0 && outdatedSkills.length === 0 && missingCommands.length === 0) {
1117
- return { installSkills: [], updateSkills: [], installCommands: [] };
1155
+ return { installSkills: [], updateSkills: [], installCommands: [], declinedSkills: [], declinedCommands: [] };
1118
1156
  }
1119
1157
 
1120
1158
  console.log();
@@ -1123,22 +1161,42 @@ async function promptNewFeatureInstallation(missingSkills, outdatedSkills, missi
1123
1161
  console.log(chalk.cyan('━'.repeat(50)));
1124
1162
  console.log();
1125
1163
 
1126
- const result = { installSkills: [], updateSkills: [], installCommands: [] };
1164
+ const result = { installSkills: [], updateSkills: [], installCommands: [], declinedSkills: [], declinedCommands: [] };
1127
1165
 
1128
1166
  // Handle missing Skills with checkbox selection
1129
1167
  if (missingSkills.length > 0) {
1168
+ // Check if Skills are already installed via marketplace (Claude Code only)
1169
+ const marketplaceInfo = getMarketplaceSkillsInfo();
1170
+ const hasMarketplaceSkills = marketplaceInfo?.installed;
1171
+
1130
1172
  console.log(chalk.yellow(msg.skillsNotInstalledFor || 'Skills not yet installed for these AI tools:'));
1131
1173
  for (const skill of missingSkills) {
1132
- console.log(chalk.gray(` • ${skill.displayName}`));
1174
+ // Show marketplace hint for Claude Code if applicable
1175
+ if (hasMarketplaceSkills && skill.agent === 'claude-code') {
1176
+ console.log(chalk.gray(` • ${skill.displayName} ${chalk.cyan(`(${msg.alreadyViaMarketplace || 'already via Marketplace'})`)}`));
1177
+ } else {
1178
+ console.log(chalk.gray(` • ${skill.displayName}`));
1179
+ }
1180
+ }
1181
+
1182
+ // Show marketplace coexistence warning if applicable
1183
+ if (hasMarketplaceSkills && missingSkills.some(s => s.agent === 'claude-code')) {
1184
+ console.log();
1185
+ console.log(chalk.cyan(` ℹ ${msg.marketplaceCoexistNote || 'Note: File-based installation will coexist with Marketplace version'}`));
1133
1186
  }
1134
1187
  console.log();
1135
1188
 
1136
1189
  // Build checkbox choices
1137
- const skillChoices = missingSkills.map(skill => ({
1138
- name: skill.displayName,
1139
- value: skill.agent,
1140
- checked: true // Default checked for opt-out behavior
1141
- }));
1190
+ const skillChoices = missingSkills.map(skill => {
1191
+ const isClaudeWithMarketplace = hasMarketplaceSkills && skill.agent === 'claude-code';
1192
+ return {
1193
+ name: isClaudeWithMarketplace
1194
+ ? `${skill.displayName} ${chalk.cyan(`(${msg.alreadyViaMarketplace || 'already via Marketplace'})`)}`
1195
+ : skill.displayName,
1196
+ value: skill.agent,
1197
+ checked: !isClaudeWithMarketplace // Default unchecked if already via marketplace
1198
+ };
1199
+ });
1142
1200
 
1143
1201
  // Add skip option
1144
1202
  skillChoices.push(new inquirer.Separator());
@@ -1164,6 +1222,12 @@ async function promptNewFeatureInstallation(missingSkills, outdatedSkills, missi
1164
1222
  // Filter out skip and handle selection
1165
1223
  const filteredSkillAgents = selectedSkillAgents.filter(a => a !== '__skip__');
1166
1224
 
1225
+ // Track declined skills (user explicitly skipped or deselected)
1226
+ const skippedSkillAgents = missingSkills
1227
+ .map(s => s.agent)
1228
+ .filter(agent => !filteredSkillAgents.includes(agent));
1229
+ result.declinedSkills = skippedSkillAgents;
1230
+
1167
1231
  if (filteredSkillAgents.length > 0) {
1168
1232
  // Prompt for installation level
1169
1233
  const { skillsLevel } = await inquirer.prompt([{
@@ -1279,7 +1343,14 @@ async function promptNewFeatureInstallation(missingSkills, outdatedSkills, missi
1279
1343
  }]);
1280
1344
 
1281
1345
  // Filter out skip
1282
- result.installCommands = selectedCommandAgents.filter(a => a !== '__skip__');
1346
+ const filteredCommandAgents = selectedCommandAgents.filter(a => a !== '__skip__');
1347
+ result.installCommands = filteredCommandAgents;
1348
+
1349
+ // Track declined commands (user explicitly skipped or deselected)
1350
+ const skippedCommandAgents = missingCommands
1351
+ .map(c => c.agent)
1352
+ .filter(agent => !filteredCommandAgents.includes(agent));
1353
+ result.declinedCommands = skippedCommandAgents;
1283
1354
  }
1284
1355
 
1285
1356
  return result;