VSC extension: workspace & multi-project support for all commands#586
VSC extension: workspace & multi-project support for all commands#586chiaramooney wants to merge 9 commits into
Conversation
When no project is found at the workspace root, the extension now searches for compatible app projects (Tauri, Electron, Flutter, .NET, Rust, C++) and presents them in a QuickPick for the user to select. Mirrors the CLI's ProjectDetectionService behavior (PR #530): - 0 projects: QuickPick with current directory fallback - 1 project at root: proceeds directly to SDK mode selection - 1 project nested: QuickPick with project + current directory - 2+ projects: QuickPick with all projects + current directory - 10+ projects: adds note that search stopped at 10 entries Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Commands that need a project context (restore, update, pack, run, create-debug-identity, manifest generate, manifest update-assets, cert generate, get-winapp-path, manifest add-alias, unregister) now use a shared resolveProjectDirectory() helper that: 1. If workspace root contains a project, uses it directly (no prompt) 2. Otherwise searches for projects in the workspace 3. Single project found: auto-selects it 4. Multiple projects found: shows QuickPick to choose 5. No projects found: falls back to workspace root Commands that take explicit file paths (sign, cert install, cert info) are unchanged. Resolves: #492 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Adds: debug, release, .vscode, artifacts, TestResults, .gradle, .nuget, .cargo Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1. Symlinks/junctions: use Dirent.isSymbolicLink() which on Windows correctly reports both symlinks and junctions (reparse points), matching the CLI's FileAttributes.ReparsePoint check. 2. csproj detection: use regex to extract <OutputType> and <IsTestProject> element values (case-insensitive) instead of naive string.includes() checks. Mirrors the CLI's XDocument-based parsing logic. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When configured in .vscode/settings.json, commands use the listed directories directly instead of scanning the workspace. Single entry auto-selects; multiple entries show a QuickPick. This is useful for users who know their project locations and want to avoid the scan on every command invocation. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
25 tests covering: - detectProjectAt: all project types (.NET, Electron, Flutter, Rust, C++, Tauri), test project exclusion, library exclusion, priority ordering, display path formatting - detectProjects: BFS traversal, maxProjects limit, ignored directory skipping (node_modules, bin, obj, etc.), hidden dir skipping, no recursion into detected projects - getDisplayFilePath / getProjectLabel: formatting helpers Run with: npm run test:unit Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR adds workspace-aware, multi-project support to the WinApp VS Code extension (src/winapp-VSC), so commands can target an app project that is not at the workspace root (monorepos, multi-app repos, nested projects). It introduces a new TypeScript project-detection module that mirrors the CLI's ProjectDetectionService, a shared resolveProjectDirectory() helper used by all project-context commands, a smarter winapp init flow, and a new winapp.appDirectories setting that takes precedence over auto-detection. It resolves issue #492 and aligns extension behavior with CLI PR #530.
Changes:
- New
project-detection.ts(BFS detection of Tauri/Electron/Flutter/.NET/Rust/C++, skip-list, symlink handling) plus 25 mocha unit tests and atest:unitscript. extension.ts:resolveProjectDirectory()(setting → root project → workspace scan with QuickPick) wired intorestore,update,pack,run,create-debug-identity,manifest *,cert generate,get-winapp-path,unregister;initgains its own detection/QuickPick flow.- New
winapp.appDirectoriesconfiguration inpackage.json, README docs, andtsconfig.jsonmocha types.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
src/winapp-VSC/src/project-detection.ts |
New detection module mirroring the CLI service (BFS, skip-list, csproj heuristic). |
src/winapp-VSC/src/extension.ts |
Adds resolveProjectDirectory(), multi-project init flow, and routes all project-context commands through resolved dirs. |
src/winapp-VSC/src/test/project-detection.test.ts |
New mocha unit tests covering detection logic. |
src/winapp-VSC/package.json |
Adds winapp.appDirectories setting and test:unit script. |
src/winapp-VSC/tsconfig.json |
Adds mocha to global types for tests. |
src/winapp-VSC/README.md |
Documents workspace/multi-project support and updated init behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…xplicit mocha dep - Make detectProjects() async with fs/promises and periodic yielding so withProgress UI stays responsive during large workspace scans - Use case-insensitive comparison for SKIP_DIRS (matches CLI behavior) - Replace fragile .replace() pattern with getDisplayFilePath() import - Add mocha as explicit devDependency (was only transitive) - Update isExecutableCsproj docstring to reflect simplified heuristic Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Here's what my agent picked up on this. A lot of these are related to having project detected implemented twice, not sure if the structure of the extension would allow us to share the logic, but may be worth looking into.
I would definitely take a look at H2 to confirm that's a real security risk.
Summary
Critical: 0 High: 2 Medium: 9 Low: 3
Coverage
security ⚠ 2 findings
correctness ⚠ 2 findings
cli-ux ⚠ 1 finding
alternative-solution ⚠ 5 findings
test-coverage ⚠ 5 findings
docs-and-samples ⚠ 1 finding
packaging ✓ clean
multi-model ✓ 2/2 high confirmed (1 specialist "high" downgraded to medium)
Findings
H1 src/winapp-VSC/package.json:197-200 test-coverage New mocha unit tests are not wired into any CI workflow
H2 src/winapp-VSC/src/extension.ts:547-552 security PowerShell command injection via detected project folder name in init
M1 src/winapp-VSC/README.md:54-65 docs-and-samples README says init "auto-selects" single project; code shows a confirm QuickPick
M2 src/winapp-VSC/src/extension.ts:116-177 test-coverage resolveProjectDirectory branching logic has no tests
M3 src/winapp-VSC/src/extension.ts:121-130 security winapp.appDirectories accepts absolute/.. paths outside workspace
M4 src/winapp-VSC/src/extension.ts:464-476 cli-ux init command bypasses the winapp.appDirectories setting
M5 src/winapp-VSC/src/extension.ts:464-552 test-coverage init bespoke picker flow is untested
M6 src/winapp-VSC/src/project-detection.ts:44-48 alternative-solution TS detector duplicates C# ProjectDetectionService (drift risk)
M7 src/winapp-VSC/src/project-detection.ts:91-130 correctness "async" BFS calls synchronous fs APIs; blocks extension host
M8 src/winapp-VSC/src/project-detection.ts:106-129 test-coverage Symlink/error/yield/malformed-file branches untested
M9 src/winapp-VSC/src/project-detection.ts:766-784 correctness csproj regex parsing diverges from CLI's XML parsing
L1 src/winapp-VSC/src/extension.ts:5 alternative-solution getProjectLabel imported but unused in extension.ts
L2 src/winapp-VSC/src/extension.ts:464-539 alternative-solution init duplicates ~60 lines of resolveProjectDirectory picker logic
L3 src/winapp-VSC/src/project-detection.ts:44-48 test-coverage No test guards TS↔C# detection drift
Details
H1 src/winapp-VSC/package.json:197-200
- Severity: high
- Confidence: high
- Domain: test-coverage, packaging
- Multi-model: confirmed
- Finding: The new
test:unitmocha tests never run in CI, so the detection logic is unguarded on PRs. - Evidence: package.json adds
"test:unit": "tsc -p ./ && mocha out/test/**/*.test.js", but.github/workflows/build-package.yml:59only runsbuild-cli.ps1 -SkipDocsandscripts/package-vsc.ps1:159only runsnpm run compile. No workflow invokesnpm run test:unit. - Recommendation: Add a CI step (in build-package.yml or package-vsc.ps1) that runs
npm ci+npm run test:unitinsrc/winapp-VSCbefore packaging.
H2 src/winapp-VSC/src/extension.ts:547-552
- Severity: high
- Confidence: high
- Domain: security
- Multi-model: confirmed
- Finding: A detected project directory name is interpolated into a PowerShell command string, allowing code execution during
winapp.init. - Evidence:
runWinappCommandsends raw text to a powershell.exe terminal:terminal.sendText(& "${cliPath}" ${command})(extension.ts:66). The init path builds`init "${selectedPath}" --use-defaults`(line 547), whereselectedPathderives from on-disk folder names viapath.relative(...). A folder named with PowerShell expansion syntax (e.g.$(...)) is evaluated by PowerShell. (Other commands route the directory throughcreateTerminal({ cwd }), which is not injectable — only the init command interpolates it into the command string.) - Recommendation: Don't compose a PowerShell command string from paths — pass arguments via a non-shell spawn/argv array, or PowerShell-escape every interpolated value centrally.
M1 src/winapp-VSC/README.md:54-65
- Severity: medium
- Confidence: high
- Domain: docs-and-samples
- Finding: README's init scenario table claims a single discovered project is auto-selected; the code shows a confirmation QuickPick instead.
- Evidence: README:
| No project at root, 1 project found elsewhere | Auto-selects that project |. But extension.ts:286-300 shows a QuickPick offering the project + "Current directory" and awaits the user's choice. - Recommendation: Reword to "offered for selection/confirmation (with current-directory alternative)", not "auto-selects".
M2 src/winapp-VSC/src/extension.ts:116-177
- Severity: medium
- Confidence: high
- Domain: test-coverage
- Finding: resolveProjectDirectory has substantial branching (appDirectories single/multiple, root vs scan, 0/1/many results, cancel) with no tests.
- Evidence: Only test file imports
project-detection; no test touches extension.ts. - Recommendation: Extract the resolver behind injectable VS Code/detection facades (or a pure helper) and unit-test each branch.
M3 src/winapp-VSC/src/extension.ts:121-130
- Severity: medium
- Confidence: high
- Domain: security
- Finding:
winapp.appDirectoriesis documented as relative butpath.resolveaccepts absolute paths and.., letting workspace settings retarget commands outside the workspace. - Evidence:
return path.resolve(workspacePath, appDirs[0]);/directory: path.resolve(workspacePath, dir)with no containment check. - Recommendation: Canonicalize and reject entries that are absolute or whose real path escapes the workspace root.
M4 src/winapp-VSC/src/extension.ts:464-476
- Severity: medium
- Confidence: high
- Domain: cli-ux
- Finding: The init command (the primary reason users configure appDirectories) ignores the setting — it goes straight to root-detect + scan.
- Evidence: init uses
detectProjectAt/detectProjectsdirectly and never readsconfig.get('appDirectories'), unlike resolveProjectDirectory (lines 117-136). - Recommendation: Route init through shared resolution that honors appDirectories first, then layers the extra "Current directory" option.
M5 src/winapp-VSC/src/extension.ts:464-552
- Severity: medium
- Confidence: high
- Domain: test-coverage
- Finding: The init picker flow (root / 0 / 1 / many projects, cancel, relative-path conversion, command building) is untested.
- Evidence: extension.ts:464-552 adds many branches culminating in
init "${selectedPath}"; no covering tests. - Recommendation: Extract an init-selection/command-building helper and test each path, or add VS Code-API-mocked tests.
M6 src/winapp-VSC/src/project-detection.ts:44-48
- Severity: medium
- Confidence: high
- Domain: alternative-solution
- Finding: The extension re-implements the CLI's project detection in TS rather than reusing the existing source of truth, creating long-term drift risk.
- Evidence: Comments state it "Mirrors ProjectDetectionService.DetectProject from WinApp.Cli"; the repo already has ProjectDetectionService.cs with the same rules. (See M9 for an already-present divergence.)
- Recommendation: Consider a CLI JSON detection entry point reusing IProjectDetectionService that the extension spawns; the extra process is acceptable since detection already does filesystem scanning.
M7 src/winapp-VSC/src/project-detection.ts:91-130
- Severity: medium
- Confidence: high
- Domain: correctness, alternative-solution
- Finding: detectProjects is
asyncand claims to "keep the UI responsive", but each node calls synchronous fs (readdirSync/readFileSync/existsSync) via detectProjectAt, blocking the extension host on large trees. - Evidence:
await fsp.readdiris used for queue expansion, butdetectProjectAt(current, root)(line 98) uses fs.readdirSync (line 705), fs.readFileSync (line 730), fs.existsSync throughout. - Recommendation: Make detection consistently async with fs/promises, or run the sync version off the UI-sensitive path.
M8 src/winapp-VSC/src/project-detection.ts:106-129
- Severity: medium
- Confidence: high
- Domain: test-coverage
- Finding: Safety/error branches lack coverage: symlink/junction skipping, stat/read failures, the periodic-yield path, malformed package.json/csproj, and Electron-in-devDependencies.
- Evidence: Tests cover ignored/hidden dirs and maxProjects but none of the above try/catch or symlink branches.
- Recommendation: Add mocha cases for symlink skip, inaccessible dirs, malformed files, devDependencies.electron, and >50 dirs to hit the yield branch.
M9 src/winapp-VSC/src/project-detection.ts:766-784
- Severity: medium (specialist rated high; multi-model downgraded — real divergence but not a guaranteed break for common projects)
- Confidence: high
- Domain: correctness, alternative-solution
- Finding: csproj classification uses first-match regex on /, while the CLI uses XDocument XML parsing over all PropertyGroups — so VSC can accept/reject .csproj projects differently than the CLI.
- Evidence: project-detection.ts:768/778 use
content.match(...); ProjectDetectionService.cs:265-285 usesXDocument.Load+Descendants(). Comments/conditional/multiple PropertyGroups produce mismatches. - Recommendation: Parse csproj as XML and mirror the C# "all property groups, comments ignored, LocalName match" behavior (ideally via M6's shared detection).
L1 src/winapp-VSC/src/extension.ts:5
- Severity: low
- Confidence: high
- Domain: alternative-solution
- Finding:
getProjectLabelis imported but never used in extension.ts (only its definition + tests reference it). - Recommendation: Remove the unused import (or use it for QuickPick labels).
L2 src/winapp-VSC/src/extension.ts:464-539
- Severity: low
- Confidence: high
- Domain: alternative-solution
- Finding: init rebuilds the same scan + project QuickPick that resolveProjectDirectory already implements (~60 duplicated lines).
- Recommendation: Add options to resolveProjectDirectory (e.g. includeCurrentDirectoryFallback, confirmSingleProject) and have init reuse it.
L3 src/winapp-VSC/src/project-detection.ts:44-48
- Severity: low
- Confidence: high
- Domain: test-coverage
- Finding: The TS detector mirrors C# but nothing guards cross-language drift; each side has independent fixtures.
- Recommendation: Add shared golden fixtures or a comparison test so TS and C# detection agree.
Coverage notes
packaging: Inspected package.json (v0.1.1), package-lock.json (mocha + new peer markers; npm ci ran clean),
.vscodeignore (excludes src/, out/, test TS, lockfile), tsconfig mocha types, package-vsc.ps1, and the
VSIX workflow path. npm run package/esbuild succeeded. No version-bump or lockfile-drift blocker found.
(Note: M-severity overlaps — the missing CI hookup for test:unit is reported under H1.)
Security: - H2: Add escapePowerShellArg() to prevent command injection via folder names - M3: Validate appDirectories entries stay within workspace (reject ../ and absolute paths) Correctness: - M7: Make detectProjectAt and all helpers fully async (fs/promises) so the extension host is never blocked during detection - M4/L2: Refactor init command to reuse resolveProjectDirectory, eliminating ~60 lines of duplicated picker logic and honoring appDirectories setting Cleanup: - L1: Remove unused getProjectLabel/DetectedProject imports from extension.ts - M1: Update README init description to reflect unified resolution behavior Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
Adds project detection and multi-project support to the VS Code extension, allowing users to run WinApp commands from the root of a monorepo or multi-app workspace without needing to open VS Code at the project directory. Resolves #492.
Changes
New: Project detection module (
src/winapp-VSC/src/project-detection.ts)ProjectDetectionService(PR Make init smarter as far as detecting projects, placing files, and showing warnings for incompatible directories #530)node_modules,bin,obj,target, etc.)Updated:
winapp initcommandUpdated: All other project-context commands
Commands now use a shared
resolveProjectDirectory()helper:restore,update,pack,run,create-debug-identitymanifest generate,manifest update-assets,manifest add-aliascert generate,get-winapp-path,unregisterBehavior: root project auto-selects, otherwise searches and prompts.
Commands that take explicit file paths (
sign,cert install,cert info) are unchanged.New:
winapp.appDirectoriessettingjsonc // .vscode/settings.json { "winapp.appDirectories": ["apps/my-app", "apps/shell"] }When configured, commands use these paths directly (no scanning). Single entry auto-selects; multiple entries show a QuickPick. Takes priority over auto-detection.
Tests
npm run test:unitfromsrc/winapp-VSCDocumentation
src/winapp-VSC/README.mdwith full workspace & multi-project support sectionResolves