# Remote -> Many Multi-File (Files Only) Implementation Plan > **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Allow Transfers -> `Remote -> Many` to select multiple source files (files only) and transfer them to many targets. **Architecture:** Frontend-only change. Expand `sourcePaths x targetConnectionIds` into a queue of items; each item uses existing backend `/sftp/transfer-remote/tasks` and SSE progress tracking. **Tech Stack:** Vue 3 + TypeScript (Vite), Pinia, Spring Boot backend unchanged. --- ## File Map Modify: - `frontend/src/views/TransfersView.vue` (Remote -> Many UI + state) - `frontend/src/components/SftpFilePickerModal.vue` (add multi-select mode) - `frontend/src/stores/transfers.ts` (support `sourcePaths[]`, `targetMode`, improve SSE unsubscription) No backend changes required. --- ### Task 1: Add multi-select mode to SftpFilePickerModal **Files:** - Modify: `frontend/src/components/SftpFilePickerModal.vue` - [ ] **Step 1: Update component API (props + emits)** - Add optional prop: `multiple?: boolean` (default `false`). - Keep existing event: `select` for single selection. - Add new event: `select-many` for multi selection (payload: `string[]`). - [ ] **Step 2: Add selection state and deterministic ordering** - Track selected paths in selection-time order (array) + a set for O(1) membership. - File click behavior: - Directory: navigate into directory. - File: - if `multiple`: toggle selection. - else: emit `select(path)` and close (keep existing). - Persist selection across directory navigation within the modal session. - If a file is toggled off and later toggled on again, re-add it at the end of the array (selection-time order). - [ ] **Step 2.1: Add a visible selection indicator** - When `multiple`, render a checkbox in each file row (checked = selected). - Directory rows never render a checkbox. - [ ] **Step 3: Add modal footer controls** - Add footer actions when `multiple`: - `Confirm (N)` (disabled when `N=0`) emits `select-many(paths)` then closes. - `Cancel` closes without emitting. - Add a "Selected (N)" compact summary area with remove (`x`) for each selected file. - [ ] **Step 4: Manual verification** - Run: `npm --prefix frontend run build` - Expected: build succeeds. - Manual: - Open picker, select multiple files across different directories, ensure selections persist. - Remove items in the "Selected" summary. - Confirm emits all paths in selection-time order. - Verify search still filters entries correctly. - Verify hidden-files toggle still works as before. - [ ] **Step 5: Commit (optional)** - If doing commits: - Run: `git add frontend/src/components/SftpFilePickerModal.vue` - Run: `git commit -m "feat: add multi-select mode to SFTP file picker"` --- ### Task 2: Update TransfersView Remote -> Many UI for multi-file selection **Files:** - Modify: `frontend/src/views/TransfersView.vue` - [ ] **Step 1: Update remote source model** - Replace `remoteSourcePath: string` with `remoteSourcePaths: string[]`. - UI: - Show count + list of selected paths (basename + full path tooltip). - Provide remove per item + clear all. - [ ] **Step 1.1: Add client-side validation for typed paths** - Before starting Remote -> Many: - validate each `remoteSourcePaths[i]` is non-empty - validate it does not end with `/` (heuristic directory indicator) - If invalid: show inline error and block Start. - [ ] **Step 2: Wire picker in multi mode** - Call ``. - Listen for `select-many` and merge paths into `remoteSourcePaths`: - append + de-duplicate by full path while preserving first-seen order. - [ ] **Step 3: Add target path mode toggle** - Add UI control: `Directory` (default) vs `Exact Path`. - Behavior: - `Directory` treats input as directory; normalize to end with `/`; append source filename. - `Exact Path` uses raw input as full file path (only allowed when exactly one source file is selected). - Disable `Exact Path` when `remoteSourcePaths.length > 1` (force directory mode). - [ ] **Step 3.1: Update Start button enablement** - Require `remoteSourceConnectionId != null`. - Require `remoteSourcePaths.length > 0`. - Require `remoteSelectedTargets.length > 0`. - Also require validation in Step 1.1 passes. - [ ] **Step 4: Add basename collision warning** - When `Directory` mode and `remoteSourcePaths` contains duplicate basenames: - show a warning dialog/banner before starting (acknowledge to proceed). - message mentions overwrite will happen on each selected target connection. - [ ] **Step 5: Source connection change behavior** - When `remoteSourceConnectionId` changes: - clear `remoteSourcePaths` - remove source id from `remoteSelectedTargets` if present - [ ] **Step 6: Manual verification** - Run: `npm --prefix frontend run build` - Manual: - Pick 2 files, choose 2 targets -> queue shows 4 items. - Toggle Directory/Exact Path: - multi-file forces Directory - single-file allows Exact Path - Change source connection -> selected files cleared. - [ ] **Step 7: Commit (optional)** - If doing commits: - Run: `git add frontend/src/views/TransfersView.vue frontend/src/components/SftpFilePickerModal.vue` - Run: `git commit -m "feat: allow selecting multiple remote source files"` --- ### Task 3: Update transfers store to support multi-file Remote -> Many + safe SSE unsubscription **Files:** - Modify: `frontend/src/stores/transfers.ts` - [ ] **Step 1: Update store API and types** - Change `startRemoteToMany` params: - from: `sourcePath: string` - to: `sourcePaths: string[]` - Add param: `targetMode: 'dir' | 'path'`. - [ ] **Step 2: Implement target path resolution in store** - For each `sourcePath`: - compute `filename` as basename. - if `targetMode === 'dir'`: normalize `targetDirOrPath` to end with `/` and append filename. - if `targetMode === 'path'`: require `sourcePaths.length === 1` and use raw `targetDirOrPath`. - [ ] **Step 3: Expand items as `sourcePaths x targets`** - Create a `TransferItem` per pair. - Label includes both source path and target connection. - Concurrency limiter applies across the full list. - [ ] **Step 4: Fix SSE subscription cleanup** - In `waitForRemoteTransfer` and upload-wait logic: - ensure the returned unsubscribe is called on terminal states (success/error/cancel). - still store unsubscribe in the run controller so `cancelRun` can close any active subscriptions. - [ ] **Step 5: Manual verification** - Run: `npm --prefix frontend run build` - Manual: - Start a small run and ensure completion does not leave SSE connections open (observe via browser network if needed). - Cancel a run and confirm subscriptions close promptly. - [ ] **Step 6: Commit (optional)** - If doing commits: - Run: `git add frontend/src/stores/transfers.ts` - Run: `git commit -m "fix: close SSE subscriptions for transfer tasks"` --- ### Task 4: End-to-end verification **Files:** - Verify: `frontend/src/views/TransfersView.vue` - Verify: `frontend/src/components/SftpFilePickerModal.vue` - Verify: `frontend/src/stores/transfers.ts` - [ ] **Step 1: Frontend build** - Run: `npm --prefix frontend run build` - Expected: success. - [ ] **Step 2: Smoke test (manual)** - Start app (if needed): - Backend: `mvn -f backend/pom.xml spring-boot:run` - Frontend: `npm --prefix frontend run dev` - If deps aren't installed: - Frontend: `npm --prefix frontend install` - Use Transfers: - Remote -> Many: pick 2-3 files, 2 targets, Directory mode. - Verify files arrive at target directory with correct names. - Verify errors are per-item, run continues for other items. - [ ] **Step 3: Commit (optional)** - If doing a final commit: - Run: `git add frontend/src/views/TransfersView.vue frontend/src/components/SftpFilePickerModal.vue frontend/src/stores/transfers.ts` - Run: `git commit -m "feat: support multi-file Remote -> Many transfers"`