agentleFS
MicrosoftKnown publishermicrosoft/vscode-python-environments/.github/skills/settings-precedence/SKILL.md

settings-precedence

Skill146 starsChanged 9 months agoMITView on GitHub

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.

We split this skill into the rules it gives an agent

3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 1

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Very specific:

    When you need workspaceFolderValue from inspect()

    line 38
  2. Distinctive:

    Cause: Not checking for existing values before writing

    line 165
  3. Distinctive:

    Cause: Not including workspace identifier in settings

    line 180

≈ “Common” is an estimate: at least 1 in 5 of the 308,232 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

VS Code Settings Precedence

Settings precedence bugs corrupt user configurations. This skill documents the correct patterns.

Precedence Order (Highest to Lowest)

  1. Workspace folder value - Per-folder in multi-root workspace
  2. Workspace value - .vscode/settings.json or .code-workspace
  3. User/global value - User settings.json
  4. Default value - From extension's package.json (⚠️ may come from other extensions!)

Core Rules

Rule 1: Always Pass Scope to getConfiguration()

// ❌ 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:

  • Reading per-resource settings (scope: "resource" in package.json)
  • Any multi-workspace scenario
  • When you need workspaceFolderValue from inspect()

Rule 2: Use inspect() to Check Explicit Values

// ❌ 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;
}

Rule 3: Don't Overwrite User's Explicit Values

// ❌ 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);
}

Rule 4: Update at the Correct Scope

// 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);

Multi-Root Workspace Handling

The workspace Property

For 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.

Getting the Right Workspace Folder

// ❌ 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);

Common Issues

Issue: workspaceFolderValue is undefined

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!

Issue: defaultValue from other extensions

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 || ...) { }

Issue: Settings overwritten on reload

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);
}

Issue: Settings mixed up in multi-root

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
};

Complete Example: Safe Settings Read/Write

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);
}

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

Reviews can't be read right now.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.