.github/skills/settings-precedence/SKILL.md
VS Code settings precedence rules and common pitfalls. Essential for any code that reads or writes settings. Covers getConfiguration scope, inspect() vs get(), and multi-workspace handling.
npx skillsauth add microsoft/vscode-python-environments settings-precedenceInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Settings precedence bugs corrupt user configurations. This skill documents the correct patterns.
.vscode/settings.json or .code-workspacesettings.jsonpackage.json (⚠️ may come from other extensions!)// ❌ WRONG: Missing scope
const config = vscode.workspace.getConfiguration('python-envs');
const value = config.get('pythonProjects');
// workspaceFolderValue will be UNDEFINED because VS Code doesn't know which folder!
// ✅ RIGHT: Pass scope (workspace folder or document URI)
const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder);
const value = config.get('pythonProjects');
When to pass scope:
scope: "resource" in package.json)workspaceFolderValue from inspect()// ❌ WRONG: get() returns defaultValue even from other extensions!
const config = vscode.workspace.getConfiguration('python');
if (config.get('useEnvironmentsExtension')) {
// May return true from another extension's package.json default!
}
// ✅ RIGHT: Use inspect() and check explicit values only
const config = vscode.workspace.getConfiguration('python', scope);
const inspected = config.inspect('useEnvironmentsExtension');
const hasExplicitValue =
inspected?.globalValue !== undefined ||
inspected?.workspaceValue !== undefined ||
inspected?.workspaceFolderValue !== undefined;
if (hasExplicitValue) {
// User explicitly set this value
const effectiveValue = inspected?.workspaceFolderValue ?? inspected?.workspaceValue ?? inspected?.globalValue;
}
// ❌ WRONG: Unconditionally writing to settings
await config.update('pythonPath', detectedPath, ConfigurationTarget.Workspace);
// Overwrites user's explicit choice!
// ✅ RIGHT: Check for existing explicit values first
const inspected = config.inspect('pythonPath');
const hasUserValue = inspected?.workspaceValue !== undefined;
if (!hasUserValue) {
// Only set if user hasn't explicitly chosen
await config.update('pythonPath', detectedPath, ConfigurationTarget.Workspace);
}
// Configuration targets (least to most specific)
ConfigurationTarget.Global; // User settings.json
ConfigurationTarget.Workspace; // .vscode/settings.json or .code-workspace
ConfigurationTarget.WorkspaceFolder; // Per-folder in multi-root
// To remove a setting, update with undefined
await config.update('pythonPath', undefined, ConfigurationTarget.Workspace);
workspace PropertyFor multi-root workspaces, pythonProjects settings need a workspace property:
{
"python-envs.pythonProjects": [
{
"path": ".",
"workspace": "/path/to/workspace-folder",
"envManager": "ms-python.python:venv"
}
]
}
Without the workspace property, settings get mixed up between folders.
// ❌ WRONG: Always using first workspace folder
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
// ✅ RIGHT: Get folder for specific document/file
const workspaceFolder = vscode.workspace.getWorkspaceFolder(documentUri) ?? vscode.workspace.workspaceFolders?.[0];
// When you have a path but not a URI
const uri = vscode.Uri.file(filePath);
const workspaceFolder = vscode.workspace.getWorkspaceFolder(uri);
Cause: Missing scope parameter in getConfiguration()
// This returns undefined for workspaceFolderValue!
const config = vscode.workspace.getConfiguration('python-envs');
const inspected = config.inspect('pythonProjects');
console.log(inspected?.workspaceFolderValue); // undefined!
// Fix: Pass scope
const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder.uri);
const inspected = config.inspect('pythonProjects');
console.log(inspected?.workspaceFolderValue); // Now works!
Cause: Using get() instead of inspect() for boolean checks
The defaultValue in inspect() may come from ANY extension's package.json, not just yours:
// Another extension might have in their package.json:
// "python.useEnvironmentsExtension": { "default": true }
// Your check will be wrong:
config.get('python.useEnvironmentsExtension') // true from other extension!
// Fix: Only check explicit values
const inspected = config.inspect('python.useEnvironmentsExtension');
if (inspected?.globalValue === true || ...) { }
Cause: Not checking for existing values before writing
// During extension activation, this overwrites user's config!
await config.update('defaultEnvManager', 'venv', ConfigurationTarget.Global);
// Fix: Only write defaults if no value exists
const current = config.inspect('defaultEnvManager');
if (current?.globalValue === undefined && current?.workspaceValue === undefined) {
await config.update('defaultEnvManager', 'venv', ConfigurationTarget.Global);
}
Cause: Not including workspace identifier in settings
// Without workspace identifier, can't tell which folder this belongs to
{
"python-envs.pythonProjects": [
{ "path": ".", "envManager": "venv" } // Which workspace?
]
}
// Fix: Always include workspace when saving
const project = {
path: projectPath,
workspace: workspaceFolder.uri.fsPath,
envManager: selectedManager
};
import * as vscode from 'vscode';
async function getProjectConfig(projectUri: vscode.Uri): Promise<ProjectConfig | undefined> {
const workspaceFolder = vscode.workspace.getWorkspaceFolder(projectUri);
if (!workspaceFolder) {
return undefined;
}
// Always pass scope!
const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder.uri);
// Use inspect() to understand where values come from
const inspected = config.inspect<ProjectConfig[]>('pythonProjects');
// Prefer most specific value
const projects = inspected?.workspaceFolderValue ?? inspected?.workspaceValue ?? inspected?.globalValue ?? []; // Don't use defaultValue!
// Find project matching the URI
return projects.find((p) => path.resolve(workspaceFolder.uri.fsPath, p.path) === projectUri.fsPath);
}
async function saveProjectConfig(projectUri: vscode.Uri, projectConfig: ProjectConfig): Promise<void> {
const workspaceFolder = vscode.workspace.getWorkspaceFolder(projectUri);
if (!workspaceFolder) {
return;
}
const config = vscode.workspace.getConfiguration('python-envs', workspaceFolder.uri);
const inspected = config.inspect<ProjectConfig[]>('pythonProjects');
// Get existing projects (not including defaults!)
const existingProjects = inspected?.workspaceFolderValue ?? inspected?.workspaceValue ?? [];
// Ensure workspace property for multi-root
const configToSave: ProjectConfig = {
...projectConfig,
workspace: workspaceFolder.uri.fsPath,
};
// Update or add
const projectIndex = existingProjects.findIndex(
(p) => path.resolve(workspaceFolder.uri.fsPath, p.path) === projectUri.fsPath,
);
const updatedProjects = [...existingProjects];
if (projectIndex >= 0) {
updatedProjects[projectIndex] = configToSave;
} else {
updatedProjects.push(configToSave);
}
// Write to workspace folder scope in multi-root
const target =
vscode.workspace.workspaceFolders?.length > 1
? vscode.ConfigurationTarget.WorkspaceFolder
: vscode.ConfigurationTarget.Workspace;
await config.update('pythonProjects', updatedProjects, target);
}
tools
Run smoke tests to verify extension functionality in a real VS Code environment. Use this when checking if basic features work after changes.
development
Run the mandatory pre-commit checks before committing code. Includes lint, type checking, and unit tests. MUST be run before every commit.
tools
Run integration tests to verify that extension components work together correctly. Use this after modifying component interactions or event handling.
testing
Run E2E tests to verify complete user workflows like environment discovery, creation, and selection. Use this before releases or after major changes.