Skip to content

Custom Instructions

Load an instruction file when you want to test a project's conventions with Copilot. Pass its content to CopilotEval.instructions, then assert on the output those conventions require.

This tests explicitly supplied instructions. It does not reproduce an editor's automatic file matching or prove that ambient instructions were loaded. SDK configuration discovery is disabled by default.

Loading a Single Instruction File

For example, create .github/instructions/python-style.instructions.md:

---
applyTo: "**/*.py"
description: Python function conventions
---

Use snake_case function names.
Annotate every function parameter and return value.
Give every public function a docstring.

Load and check it in a test:

from __future__ import annotations

import ast
import re

from pytest_skill_engineering import load_instruction_file
from pytest_skill_engineering.copilot import CopilotEval


async def test_function_conventions(copilot_eval, tmp_path):
    instruction = load_instruction_file(".github/instructions/python-style.instructions.md")
    agent = CopilotEval(
        name="python-style",
        model="gpt-5.6-sol",
        instructions=instruction["content"],
        working_directory=str(tmp_path),
    )
    result = await copilot_eval(
        agent, "Create arithmetic.py with a public function that adds two numbers."
    )

    assert result.success, result.error
    tree = ast.parse((tmp_path / "arithmetic.py").read_text(encoding="utf-8"))
    functions = [node for node in tree.body if isinstance(node, ast.FunctionDef)]
    assert functions
    for function in functions:
        assert re.fullmatch(r"_?[a-z][a-z0-9]*(?:_[a-z0-9]+)*", function.name)
        assert ast.get_docstring(function)
        parameters = function.args.posonlyargs + function.args.args + function.args.kwonlyargs
        if function.args.vararg is not None:
            parameters.append(function.args.vararg)
        if function.args.kwarg is not None:
            parameters.append(function.args.kwarg)
        assert all(parameter.annotation is not None for parameter in parameters)
        assert function.returns is not None

These checks inspect syntax, annotations, docstrings, and the shown snake_case rule. They do not establish that addition works. Add your project's independent behavior tests for a complete check. Run generated code only in an environment approved for untrusted code.

load_instruction_file() returns:

Key Value
name Filename-derived name, with the instruction suffix removed
content Markdown body, with frontmatter removed
apply_to Frontmatter applyTo, or ""
description Frontmatter description, or ""
metadata Full parsed frontmatter mapping
path Supplied source path as a string

Instruction File Formats

The same loader accepts .instructions.md, .github/copilot-instructions.md, AGENTS.md, and CLAUDE.md. Plain Markdown needs no frontmatter.

.instructions.md (VS Code)

applyTo describes editor file matching. The loader returns it as apply_to; passing content to an eval does not apply that glob or save the file metadata automatically.

.github/copilot-instructions.md (VS Code, project-wide)

Use this file for repository-wide conventions. To load it with project custom agent definitions, use CopilotEval.from_copilot_config(project_root).

AGENTS.md / CLAUDE.md (plain markdown)

You can explicitly load either file and use its body as instructions. Doing so does not switch the execution to another coding-agent product.

Testing loaded instructions

There is no CopilotEval.from_instruction_files() factory. Load the files explicitly or use the appropriate project loader.

Combining Multiple Instruction Files

This configuration fragment concatenates files in the chosen order:

from pytest_skill_engineering import load_instruction_file
from pytest_skill_engineering.copilot import CopilotEval

paths = [
    ".github/copilot-instructions.md",
    ".github/instructions/python-style.instructions.md",
]
instructions = [load_instruction_file(path) for path in paths]
agent = CopilotEval(
    name="combined-conventions",
    instructions="\n\n".join(item["content"] for item in instructions),
)

All concatenated instructions apply to this session, regardless of their applyTo values. Resolve conflicting rules deliberately.

Loading All Instruction Files from a Directory

load_instruction_files() loads immediate *.instructions.md files and well-known instruction filenames. Filter by filename-derived name:

from pytest_skill_engineering import load_instruction_files

instructions = load_instruction_files(
    ".github/instructions",
    include={"python-style", "testing-conventions"},
    exclude={"experimental"},
)

Use pytest parametrization when each file should have its own task and outcome. Do not apply unrelated files to one generic task and call session completion evidence of adherence.

Instruction file evidence

Name evals and pytest parameters after their variants. JSON captures the configured system prompt, not automatically the source filenames or per-file results. Record safe filenames with record_property if you need them.

See System prompts, Prompt files, and Custom agents for the corresponding configuration boundaries.