|
| 1 | +# Configure AI Tools — Multi-Platform Redesign Spec |
| 2 | + |
| 3 | +## Goal |
| 4 | + |
| 5 | +Revise the accordion panel to support installing skills to multiple platforms simultaneously. Replace the single-select IDE tab control with a multi-select "Install for" platform checklist placed *below* the skills list. Add Universal (`.agents/skills/`) and Windsurf as install targets; retire the Cursor-specific `.cursor/rules/` path in favour of Universal. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Platform Definitions |
| 10 | + |
| 11 | +| ID | Display name | Skills path | Notes | |
| 12 | +|----|-------------|-------------|-------| |
| 13 | +| `universal` | Universal | `.agents/skills/{dirName}/SKILL.md` | Covers Cursor, Codex, Amp, Warp, Antigravity, Gemini CLI + more | |
| 14 | +| `claude-code` | Claude Code | `.claude/skills/{dirName}/SKILL.md` | | |
| 15 | +| `vscode-copilot` | VS Code (Copilot) | `.github/copilot-instructions.md` | Appends `## {name}` section | |
| 16 | +| `windsurf` | Windsurf | `.windsurf/skills/{dirName}/SKILL.md` | | |
| 17 | + |
| 18 | +Cursor's old `.cursor/rules/{dirName}.mdc` install path is **retired** from the webview flow. The QuickPick command palette fallback may keep it for now but the accordion uses Universal for Cursor users. |
| 19 | + |
| 20 | +### Universal sub-label |
| 21 | + |
| 22 | +The `universal` platform row shows a muted sub-label: **"Cursor, Codex, Amp, Warp + more"** to make it obvious that checking this covers Cursor-based workflows. |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## Panel Layout (Ready state) |
| 27 | + |
| 28 | +``` |
| 29 | + Skills |
| 30 | + ──────────────────────────── |
| 31 | + ☑ cloudinary-docs installed |
| 32 | + ☑ cloudinary-react partial |
| 33 | + ☑ cloudinary-transforms — |
| 34 | +
|
| 35 | + Install for |
| 36 | + ──────────────────────────── |
| 37 | + ☑ Universal |
| 38 | + Cursor, Codex, Amp, Warp + more |
| 39 | + ☑ Claude Code |
| 40 | + ☐ VS Code (Copilot) |
| 41 | + ☐ Windsurf |
| 42 | +
|
| 43 | + MCP Servers |
| 44 | + ──────────────────────────── |
| 45 | + ☐ Asset Management configured |
| 46 | + ☑ MediaFlows — |
| 47 | +
|
| 48 | + [ Apply ] |
| 49 | +``` |
| 50 | + |
| 51 | +Skills checklist is above the platform selector. Platform selector is always visible (not collapsible). |
| 52 | + |
| 53 | +--- |
| 54 | + |
| 55 | +## Skill Status Logic |
| 56 | + |
| 57 | +Status is computed relative to the **currently checked platforms**: |
| 58 | + |
| 59 | +| Condition | Label | Default checked? | |
| 60 | +|-----------|-------|-----------------| |
| 61 | +| Installed on **all** checked platforms | `installed` | No | |
| 62 | +| Installed on **some** checked platforms | `partial` | Yes (fills the gaps) | |
| 63 | +| Installed on **none** of the checked platforms | `—` | Yes | |
| 64 | + |
| 65 | +Status label is recomputed live whenever the user toggles a platform checkbox. No round-trip to the extension needed — all `installedByPlatform` data is cached in the client from the initial `aiToolsData` message. |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +## Default Platform Selection |
| 70 | + |
| 71 | +On accordion open, the extension computes `activePlatforms` — the set of platforms that should be pre-checked: |
| 72 | + |
| 73 | +1. **Detected IDE** → mapped to platform ID: |
| 74 | + - `detectEditor()` returns `"unknown"` or `"cursor"` → `universal` |
| 75 | + - `"claude-code"` / `"antigravity"` → `claude-code` … wait, antigravity → `universal` |
| 76 | + - Full mapping: `cursor` → `universal`, `windsurf` → `windsurf`, `vscode` → `vscode-copilot`, everything else (claude-code, unknown, antigravity) → `claude-code` for claude-code, `universal` for others |
| 77 | + |
| 78 | + Simplified: `windsurf` → `windsurf`; `vscode` → `vscode-copilot`; `cursor` or `antigravity` → `universal`; default (claude-code, unknown) → `claude-code`. |
| 79 | + |
| 80 | +2. **Already-installed platforms** → any platform where at least one skill dir is found on disk is also pre-checked (user has an existing setup there). |
| 81 | + |
| 82 | +`activePlatforms` = union of detected-IDE platform + all platforms with existing installs. |
| 83 | + |
| 84 | +--- |
| 85 | + |
| 86 | +## Data Flow |
| 87 | + |
| 88 | +### Open accordion |
| 89 | + |
| 90 | +Extension computes in parallel: |
| 91 | +- `fetchSkillList()` (cached after first open) |
| 92 | +- `readInstalledSkillDirNames(rootUri, platformId, skills)` for all 4 platforms |
| 93 | +- `readActivePlatforms(rootUri)` — checks which platform dirs exist on disk |
| 94 | +- `readConfiguredMcpServerKeys(rootUri, ...)` |
| 95 | + |
| 96 | +Posts `aiToolsData`: |
| 97 | + |
| 98 | +```json |
| 99 | +{ |
| 100 | + "command": "aiToolsData", |
| 101 | + "skills": [ |
| 102 | + { "name": "cloudinary-docs", "dirName": "cloudinary-docs", "description": "..." } |
| 103 | + ], |
| 104 | + "installedByPlatform": { |
| 105 | + "universal": ["cloudinary-docs"], |
| 106 | + "claude-code": ["cloudinary-docs"], |
| 107 | + "vscode-copilot": [], |
| 108 | + "windsurf": [] |
| 109 | + }, |
| 110 | + "activePlatforms": ["universal", "claude-code"], |
| 111 | + "mcpServers": [ |
| 112 | + { "key": "cloudinary-asset-mgmt", "label": "Asset Management", "description": "..." } |
| 113 | + ], |
| 114 | + "configuredMcpKeys": ["cloudinary-asset-mgmt"] |
| 115 | +} |
| 116 | +``` |
| 117 | + |
| 118 | +Note: `detectedIde` is gone; replaced by `activePlatforms`. |
| 119 | + |
| 120 | +### Apply |
| 121 | + |
| 122 | +Webview posts: |
| 123 | + |
| 124 | +```json |
| 125 | +{ |
| 126 | + "command": "installAiTools", |
| 127 | + "skills": ["cloudinary-react"], |
| 128 | + "platforms": ["universal", "claude-code"], |
| 129 | + "mcpServers": ["mediaflows"] |
| 130 | +} |
| 131 | +``` |
| 132 | + |
| 133 | +Extension installs each skill to each platform in sequence. Progress posted per skill (one event covers all platforms for that skill — error if any platform fails): |
| 134 | + |
| 135 | +```json |
| 136 | +{ "command": "aiToolsProgress", "item": "cloudinary-react", "status": "done" } |
| 137 | +{ "command": "aiToolsProgress", "item": "mediaflows", "status": "done" } |
| 138 | +``` |
| 139 | + |
| 140 | +Final result: |
| 141 | + |
| 142 | +```json |
| 143 | +{ "command": "aiToolsResult", "errors": [] } |
| 144 | +``` |
| 145 | + |
| 146 | +--- |
| 147 | + |
| 148 | +## New / Changed Service Functions (`src/aiToolsService.ts`) |
| 149 | + |
| 150 | +### New types |
| 151 | + |
| 152 | +```typescript |
| 153 | +export type PlatformId = 'universal' | 'claude-code' | 'vscode-copilot' | 'windsurf'; |
| 154 | + |
| 155 | +export type PlatformDef = { |
| 156 | + id: PlatformId; |
| 157 | + label: string; |
| 158 | + sublabel?: string; |
| 159 | +}; |
| 160 | +``` |
| 161 | + |
| 162 | +### New constant |
| 163 | + |
| 164 | +```typescript |
| 165 | +export const PLATFORMS: PlatformDef[] = [ |
| 166 | + { id: 'universal', label: 'Universal', sublabel: 'Cursor, Codex, Amp, Warp + more' }, |
| 167 | + { id: 'claude-code', label: 'Claude Code' }, |
| 168 | + { id: 'vscode-copilot', label: 'VS Code (Copilot)' }, |
| 169 | + { id: 'windsurf', label: 'Windsurf' }, |
| 170 | +]; |
| 171 | +``` |
| 172 | + |
| 173 | +### New install functions |
| 174 | + |
| 175 | +`installForUniversal(rootUri, skillName, content, createdFiles, errors)` — writes SKILL.md to `.agents/skills/{skillName}/SKILL.md`. Same structure as `installForClaudeCode` but with `.agents/skills/` prefix. |
| 176 | + |
| 177 | +`installForWindsurf(rootUri, skillName, content, createdFiles, errors)` — writes SKILL.md to `.windsurf/skills/{skillName}/SKILL.md`. Same structure. |
| 178 | + |
| 179 | +### Updated `readInstalledSkillDirNames` |
| 180 | + |
| 181 | +Signature changes to accept `PlatformId` instead of `ideTargetLabel: string`: |
| 182 | + |
| 183 | +```typescript |
| 184 | +export async function readInstalledSkillDirNames( |
| 185 | + rootUri: vscode.Uri, |
| 186 | + platform: PlatformId, |
| 187 | + skills: SkillInfo[] |
| 188 | +): Promise<Set<string>> |
| 189 | +``` |
| 190 | + |
| 191 | +Routing: |
| 192 | +- `universal` → stat `.agents/skills/{dirName}/SKILL.md` |
| 193 | +- `claude-code` → stat `.claude/skills/{dirName}/SKILL.md` |
| 194 | +- `vscode-copilot` → read `.github/copilot-instructions.md`, check for `## {name}` sections |
| 195 | +- `windsurf` → stat `.windsurf/skills/{dirName}/SKILL.md` |
| 196 | + |
| 197 | +### New `detectActivePlatforms` |
| 198 | + |
| 199 | +```typescript |
| 200 | +export async function detectActivePlatforms( |
| 201 | + rootUri: vscode.Uri |
| 202 | +): Promise<PlatformId[]> |
| 203 | +``` |
| 204 | + |
| 205 | +Checks for existence of each platform's skills directory (or instructions file). Returns IDs of platforms that have any install present. Used to pre-check platforms that have existing setups. |
| 206 | + |
| 207 | +### `detectEditorPlatform` |
| 208 | + |
| 209 | +```typescript |
| 210 | +export function detectEditorPlatform(): PlatformId |
| 211 | +``` |
| 212 | + |
| 213 | +Maps `detectEditor()` result to a `PlatformId`: |
| 214 | +- `windsurf` → `windsurf` |
| 215 | +- `vscode` → `vscode-copilot` |
| 216 | +- `cursor` | `antigravity` → `universal` |
| 217 | +- everything else (claude-code, unknown) → `claude-code` |
| 218 | + |
| 219 | +--- |
| 220 | + |
| 221 | +## File Map |
| 222 | + |
| 223 | +| Action | File | What changes | |
| 224 | +|--------|------|-------------| |
| 225 | +| Modify | `src/aiToolsService.ts` | Add `PlatformId`, `PlatformDef`, `PLATFORMS`; add `installForUniversal`, `installForWindsurf`; update `readInstalledSkillDirNames` to use `PlatformId`; add `detectActivePlatforms`, `detectEditorPlatform` | |
| 226 | +| Modify | `src/webview/homescreenView.ts` | Replace IDE segmented control HTML with platform checkbox section; update `_handleAiToolsExpanded` (new message shape); update `_handleInstallAiTools` (accepts `platforms[]`) | |
| 227 | +| Modify | `src/webview/client/homescreen.ts` | Replace IDE selector UI with platform multi-select; update skill status logic; update Apply payload | |
| 228 | + |
| 229 | +--- |
| 230 | + |
| 231 | +## Styling |
| 232 | + |
| 233 | +- Platform section uses the same `.hs-ai-item` / `.hs-ai-cb` pattern as skills and MCP rows |
| 234 | +- Sub-label ("Cursor, Codex, Amp…") uses `--vscode-descriptionForeground`, font-size ~9.5px, displayed on its own line below the platform name |
| 235 | +- No segmented control / pill animation needed — plain checkboxes |
| 236 | +- Platform section header uses `.hs-ai-section-head` pattern |
| 237 | + |
| 238 | +--- |
| 239 | + |
| 240 | +## Out of Scope |
| 241 | + |
| 242 | +- `installForCursor` (old `.cursor/rules/` path) is kept in `aiToolsService.ts` for the QuickPick command palette flow but is not used by the accordion |
| 243 | +- No per-platform progress ticks in the done state (one tick per skill covers all platforms) |
| 244 | +- No "install globally" (to `~/.claude/skills` etc.) — workspace-level only |
0 commit comments