本文へ移動
cccskills
無料GitHub で公開

vs-code

How to programmatically configure, modify, and verify VSCode settings, keybindings, extensions, and workspace files using Python. For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gymスター 224(GitHub 224 + cccskills 0)

実行スコア 未検証

インストール方法を見る

含まれるファイル(1)

  • SKILL.md13.4 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

VSCode — Python Manipulation Guide

This skill teaches setup-gen (create/modify configs) and reward-gen (read/verify configs) how to work with VSCode configuration files using pure Python code.

  • Libraries: json, os, subprocess, shutil
  • Config root: ~/.config/Code/User/ (Linux)

0. GUI Startup on VM (for setup-gen)

After preparing workspace/settings, setup-gen should open VSCode with the target folder or file for the GUI agent.

CRITICAL VM LIMIT: GUI launches must set DISPLAY=:0.

import os
import shlex
import subprocess
import time

def launch_gui(command: str, delay_sec: float = 1.0):
    env = os.environ.copy()
    env["DISPLAY"] = ":0"
    subprocess.Popen(
        shlex.split(command),
        stdout=subprocess.DEVNULL,
        stderr=subprocess.DEVNULL,
        env=env,
    )
    time.sleep(delay_sec)

# Open workspace/folder
launch_gui('code "/home/user/workspace"', delay_sec=2.0)

# Or open a specific file
# launch_gui('code "/home/user/workspace/main.py"', delay_sec=2.0)

Guidelines:

  • Use non-blocking launch (Popen) and short delays.
  • For multi-app tasks, coordinate launch order explicitly.

1. Creating & Writing Files (setup-gen)

Key File Paths

import os, json, shutil, subprocess

HOME = os.path.expanduser("~")
VSCODE_USER = os.path.join(HOME, ".config", "Code", "User")
SETTINGS_PATH = os.path.join(VSCODE_USER, "settings.json")
KEYBINDINGS_PATH = os.path.join(VSCODE_USER, "keybindings.json")
SNIPPETS_DIR = os.path.join(VSCODE_USER, "snippets")

Settings (settings.json)

# Read existing settings (or start empty)
def load_settings():
    try:
        with open(SETTINGS_PATH, "r") as f:
            return json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        return {}

# Write settings — merges with existing
def update_settings(updates: dict):
    settings = load_settings()
    settings.update(updates)
    os.makedirs(os.path.dirname(SETTINGS_PATH), exist_ok=True)
    with open(SETTINGS_PATH, "w") as f:
        json.dump(settings, f, indent=4)

# Examples
update_settings({
    "editor.fontSize": 16,
    "editor.wordWrap": "on",
    "workbench.colorTheme": "Visual Studio Dark",
    "debug.focusEditorOnBreak": False,
    "python.analysis.diagnosticSeverityOverrides": {
        "reportMissingImports": "none"
    }
})

Nested Settings

def update_nested_setting(key_path: list, value):
    """Set a deeply nested key like ["python", "analysis", "typeCheckingMode"]."""
    settings = load_settings()
    d = settings
    for key in key_path[:-1]:
        d = d.setdefault(key, {})
    d[key_path[-1]] = value
    with open(SETTINGS_PATH, "w") as f:
        json.dump(settings, f, indent=4)

Keybindings (keybindings.json)

def load_keybindings():
    """Load keybindings, handling the optional comment prefix line."""
    try:
        with open(KEYBINDINGS_PATH, "r") as f:
            content = f.read()
        try:
            return json.loads(content)
        except json.JSONDecodeError:
            # Skip first line (comment like "// Place your key bindings...")
            lines = content.split("\n", 1)
            if len(lines) > 1:
                return json.loads(lines[1])
            return []
    except FileNotFoundError:
        return []

def add_keybinding(key: str, command: str, when: str = None):
    bindings = load_keybindings()
    entry = {"key": key, "command": command}
    if when:
        entry["when"] = when
    bindings.append(entry)
    os.makedirs(os.path.dirname(KEYBINDINGS_PATH), exist_ok=True)
    with open(KEYBINDINGS_PATH, "w") as f:
        json.dump(bindings, f, indent=4)

# Examples
add_keybinding("ctrl+shift+t", "workbench.action.terminal.toggleTerminal")
add_keybinding("ctrl+k ctrl+c", "editor.action.addCommentLine", when="editorTextFocus")

Workspace Files (.code-workspace)

def create_workspace(workspace_path: str, folders: list, settings: dict = None):
    """Create a .code-workspace file."""
    workspace = {
        "folders": [{"path": p} for p in folders],
    }
    if settings:
        workspace["settings"] = settings
    with open(workspace_path, "w") as f:
        json.dump(workspace, f, indent=4)

# Example
create_workspace(
    "/home/user/project.code-workspace",
    ["/home/user/src", "/home/user/data1", "/home/user/data2"],
    settings={"editor.tabSize": 2}
)

Extension Management

# Install extension via CLI
def install_extension(extension_id: str):
    subprocess.run(["code", "--install-extension", extension_id], check=True)

# Uninstall extension
def uninstall_extension(extension_id: str):
    subprocess.run(["code", "--uninstall-extension", extension_id], check=True)

# List installed extensions
def list_extensions() -> str:
    result = subprocess.run(["code", "--list-extensions"], capture_output=True, text=True)
    return result.stdout

# Examples
install_extension("ms-python.python")
install_extension("esbenp.prettier-vscode")

Text File Creation/Modification

# Create a file for VSCode to open
def create_text_file(path: str, content: str):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, "w") as f:
        f.write(content)

# Find and replace in a file
def find_replace_in_file(path: str, old: str, new: str):
    with open(path, "r") as f:
        content = f.read()
    content = content.replace(old, new)
    with open(path, "w") as f:
        f.write(content)

# Golden file: copy then modify
shutil.copy("/home/user/original.py", "/home/user/golden.py")
find_replace_in_file("/home/user/golden.py", "text", "test")

Launching VSCode

# Open VSCode with a file
subprocess.Popen(["code", "/home/user/document.txt"])

# Open VSCode with a folder
subprocess.Popen(["code", "/home/user/project/"])

# Open VSCode with a workspace
subprocess.Popen(["code", "/home/user/project.code-workspace"])

Snippet Files

def create_snippet(language: str, name: str, prefix: str, body: list, description: str = ""):
    snippet_path = os.path.join(SNIPPETS_DIR, f"{language}.json")
    os.makedirs(SNIPPETS_DIR, exist_ok=True)
    try:
        with open(snippet_path, "r") as f:
            snippets = json.load(f)
    except (FileNotFoundError, json.JSONDecodeError):
        snippets = {}
    snippets[name] = {"prefix": prefix, "body": body, "description": description}
    with open(snippet_path, "w") as f:
        json.dump(snippets, f, indent=4)

2. Reading & Verifying Files (reward-gen)

Verifying Settings (Subset Check)

def verify_settings_subset(expected: dict) -> bool:
    """Check that all expected key-value pairs exist in settings.json."""
    settings = load_settings()
    return _is_subset(expected, settings)

def _is_subset(expected, actual) -> bool:
    if isinstance(expected, dict):
        if not isinstance(actual, dict):
            return False
        return all(k in actual and _is_subset(v, actual[k]) for k, v in expected.items())
    if isinstance(expected, list):
        return expected == actual
    return expected == actual

# Example: check theme was changed
assert verify_settings_subset({"workbench.colorTheme": "Visual Studio Dark"})
# Nested check
assert verify_settings_subset({
    "python.analysis.diagnosticSeverityOverrides": {"reportMissingImports": "none"}
})

Verifying Keybindings

def verify_keybinding(expected_entry: dict) -> bool:
    """Check that expected keybinding exists in keybindings.json."""
    bindings = load_keybindings()
    return expected_entry in bindings

# Example
assert verify_keybinding({"key": "ctrl+shift+t", "command": "workbench.action.terminal.toggleTerminal"})

Verifying Config with Containment

def verify_config_containment(file_path: str, expected_json_str: str) -> bool:
    """Check JSON file contains expected structure (subset match)."""
    try:
        with open(file_path, "r") as f:
            actual = json.load(f)
        expected = json.loads(expected_json_str)
        return _is_subset(expected, actual)
    except (FileNotFoundError, json.JSONDecodeError):
        return False

Verifying Text Files

import re

def verify_text_file(actual_path: str, expected_path: str,
                     ignore_blanks=False, ignore_case=False) -> bool:
    with open(actual_path) as f:
        actual = f.read()
    with open(expected_path) as f:
        expected = f.read()
    if ignore_blanks:
        actual = re.sub(r'[\t\n]', ' ', actual).strip()
        actual = re.sub(r'\s+', ' ', actual)
        expected = re.sub(r'[\t\n]', ' ', expected).strip()
        expected = re.sub(r'\s+', ' ', expected)
    if ignore_case:
        actual = actual.lower()
        expected = expected.lower()
    return actual == expected

Verifying Extensions

def verify_extension_installed(extension_id: str) -> bool:
    result = subprocess.run(["code", "--list-extensions"], capture_output=True, text=True)
    return extension_id in result.stdout

def verify_extension_not_installed(extension_id: str) -> bool:
    return not verify_extension_installed(extension_id)

Verifying Workspace Files

def verify_workspace_folders(workspace_path: str, expected_folders: list) -> bool:
    with open(workspace_path, "r") as f:
        ws = json.load(f)
    actual_paths = [folder["path"] for folder in ws.get("folders", [])]
    return set(expected_folders) == set(actual_paths)

Verifying Python Files by Test Suite

import importlib.util, sys, uuid

def run_test_on_file(test_file: str, test_func_name: str = "test") -> float:
    """Load a test file and run its test function. Returns 0.0-1.0."""
    original_cwd = os.getcwd()
    original_path = sys.path.copy()
    module_name = f"test_{uuid.uuid4().hex[:8]}"
    try:
        test_dir = os.path.dirname(os.path.abspath(test_file))
        os.chdir(test_dir)
        if test_dir not in sys.path:
            sys.path.insert(0, test_dir)
        spec = importlib.util.spec_from_file_location(module_name, test_file)
        module = importlib.util.module_from_spec(spec)
        sys.modules[module_name] = module
        spec.loader.exec_module(module)
        result = getattr(module, test_func_name)()
        if isinstance(result, bool):
            return 1.0 if result else 0.0
        return max(0.0, min(1.0, float(result)))
    except Exception:
        return 0.0
    finally:
        if module_name in sys.modules:
            del sys.modules[module_name]
        os.chdir(original_cwd)
        sys.path[:] = original_path

Verifying ZIP/Archive Files

import zipfile

def verify_zip_files(actual_zip: str, expected_zip: str) -> bool:
    with zipfile.ZipFile(actual_zip) as z1, zipfile.ZipFile(expected_zip) as z2:
        if set(z1.namelist()) != set(z2.namelist()):
            return False
        return all(z1.read(name) == z2.read(name) for name in z1.namelist())

3. Bitter Lessons

  1. keybindings.json may have a comment prefix line. VSCode writes // Place your key bindings in this file... as the first line. json.load() fails. Always try direct load first, then skip the first line as fallback.

  2. settings.json uses update not replace. Always load existing settings and merge. Overwriting the entire file destroys user settings that other setup steps depend on.

  3. _is_subset is the correct comparison for settings. The evaluator checks that expected keys exist with correct values, NOT that settings.json is an exact match. Extra keys are fine.

  4. Workspace file is JSON, not settings.json. .code-workspace files have a different schema (folders, settings, extensions). Don't confuse with ~/.config/Code/User/settings.json.

  5. Extension IDs are case-insensitive but must be exact. ms-python.python works; Python alone does not. Use code --list-extensions to get canonical IDs.

  6. code CLI may not be in PATH. In headless/VM environments, VSCode may need to be launched via full path (/usr/bin/code) or the code symlink may not exist. Check with which code first.

  7. Text file comparison: ignore_blanks collapses tabs and newlines to spaces. It replaces [\t\n] with space, then collapses multiple spaces. This means paragraph structure is lost. Use ignore_blanks=False when line structure matters.

  8. JSON settings with // comments are NOT valid JSON. VSCode's settings.json supports JSONC (JSON with Comments). Python's json module cannot parse comments. Use regex to strip them: re.sub(r'//.*$', '', content, flags=re.MULTILINE) before parsing.

  9. compare_config defaults to containment mode. The evaluator's containment_ok=True (default) means the expected JSON only needs to be a subset of the actual. Set containment_ok=False for strict equality.

  10. Python test suite runs in the test file's directory. The evaluator cds to the test file's parent directory before running. Relative paths in the test file resolve from there.

  11. Extension installation is async. code --install-extension returns before the extension is fully activated. If subsequent steps depend on the extension, add a delay or restart VSCode.

  12. Workspace folders use exact paths. {"path": "/home/user/data1"} is NOT equal to {"path": "~/data1"}. Always use fully expanded absolute paths when comparing.

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

blender

無料

How to programmatically create, modify, and verify Blender 3D scenes using Python bpy (headless background mode, .blend files, render output). For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gym2242026年8月13日 更新

chrome

無料

How to programmatically set up, control, and verify Google Chrome browser state using Python (Playwright CDP, SQLite, JSON configs). For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gym2242026年8月13日 更新

drawio

無料

How to programmatically set up, manipulate, and verify draw.io diagram state using Python (Playwright CDP, XML injection via Edit Diagram menu, DOM verification). For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gym2242026年8月13日 更新

How to programmatically set up, manipulate, and verify Excalidraw whiteboard state using Python (Playwright CDP, localStorage JSON, scene elements). For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gym2242026年8月13日 更新

gimp

無料

How to programmatically create, modify, and verify images using Python Pillow, NumPy, OpenCV, and scikit-image. For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gym2242026年8月13日 更新

grafana

無料

How to set up and manipulate Grafana dashboard, alerting, and admin state via HTTP APIs for CUA-Gym tasks. For setup-gen and reward-gen agents.

日本語の概要は準備中です。原文の説明を表示しています。

xlang-ai/CUA-Gym2242026年8月13日 更新

xlang-ai のスキルをすべて見る

このスキルの問題を報告する