AI/ML Skills Engineering

Building a Custom Agent Skill: The Semantic Git-Commit Assistant


We recently unpacked the architectural blueprints of Anthropic's guide on Agent Skills. The takeaway was clear: stop dumping massive, monolithic prompts into your LLM contexts. Instead, design lightweight, modular skills that use progressive disclosure to load instruction layers on demand.

In this post, we'll build a real-world, local developer agent skill from scratch: The Semantic Git-Commit Assistant. We'll structure it using the recommended three-layered architecture (Metadata, Playbook, and Depth) to show how an agent can read code diffs, verify commits against strict styling guidelines, and run local validation scripts to self-correct.


The Target Architecture

Following progressive disclosure, we segregate our custom skill into a clean folder structure inside the agent's environment (e.g. .agents/skills/git-commit/):

flowchart TD A[git-commit/ Folder] --> B[SKILL.md] B --> B1[YAML Frontmatter: Router Layer] B --> B2[Markdown Instructions: Playbook Layer] A --> C[references/commit_standards.md: Depth Layer - Style Guide] A --> D[scripts/verify_commit.py: Depth Layer - Code Gate]

Layer 1: The Metadata Router

The router sits at the very top of our SKILL.md. It determines whether our agent should load this specific skill when processing a query. We define clear positive boundaries (like requesting a git commit or review of staged diffs) and negative boundaries (preventing triggering on general git commands like push/pull).

Create the entry file .agents/skills/git-commit/SKILL.md and insert the YAML frontmatter:

---
name: semantic-git-commit
description: Helps standardize local git commits. Trigger when the user requests a commit message generator, a PR log draft, or asks to commit staged changes. Do NOT trigger for general git questions, push/pull commands, or general coding support.
---

Layer 2: The Playbook Instructions

Below the frontmatter in the same SKILL.md, we write the playbook body. These are the natural language guidelines that lead the agent step-by-step through execution: locating the staged changes, reading our formatting standards, generating the commit draft, and running a script-based check.

Append the playbook instructions directly after the YAML block in SKILL.md:

# Playbook: Semantic Git-Commit Generator

You are tasked with generating a clear, standardized semantic commit message for the user's staged changes.

## Step-by-Step Execution Loop:

1. **Retrieve the Staged Diff**:
   - Run the terminal command `git diff --cached` to inspect the code changes waiting to be committed.
   - If no files are staged, inform the user they must run `git add` first.

2. **Read Style Standards**:
   - Load and read the dynamic reference file: `.agents/skills/git-commit/references/commit_standards.md`.
   - Ensure the generated subject line and body strictly follow the rules defined in that document.

3. **Draft the Commit Message**:
   - Generate a draft message containing a subject prefix (e.g. `feat:`, `fix:`), a concise description, and an optional bulleted body detailing non-obvious code deletions or alterations.

4. **Verify the Draft (The Quality Gate)**:
   - Write the draft commit message to a temporary file (e.g. `commit_draft.txt`).
   - Run the local validation script passing the draft file path to ensure format compliance:
     `python .agents/skills/git-commit/scripts/verify_commit.py commit_draft.txt`
   - If the script exits with an error status code or output details:
     - Read the script output logs.
     - Refactor the commit message, update the draft file, and re-run this check step until the validation passes cleanly.

5. **Commit the Changes**:
   - Once validated, prompt the user for confirmation, or execute `git commit -F commit_draft.txt` if they authorized auto-commit.

Layer 3: The Depth Elements

This layer provides the strict details that the model shouldn't carry in its primary brain. We define the styling template in a reference document and the execution constraints in a Python script.

1. The Style Sheet (references/commit_standards.md)

Create a references/commit_standards.md file in your skill folder to store target conventions:

# Commit Conventions

* **Format**: `<type>(<scope>): <subject>` followed by a blank line and a descriptive body.
* **Allowed Types**:
  - `feat`: A new feature addition.
  - `fix`: A bug fix.
  - `docs`: Documentation updates.
  - `refactor`: Structural code changes that do not alter features.
  - `chore`: Maintenance, updates, or build setup.
* **Rules**:
  - The subject line must not exceed 50 characters.
  - The subject line must not end with a period.
  - Write the subject line in the imperative mood (e.g. "Add validation" instead of "Added validation").

2. The Code Gate (scripts/verify_commit.py)

Rather than relying on the LLM to count characters, we write a lightweight Python script. This acts as our validation boundary, giving the model concrete error messages to self-correct against.

Create scripts/verify_commit.py:

import argparse
import sys
import re

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument("msg_file", help="Path to the file containing the commit message")
    args = parser.parse_args()

    try:
        with open(args.msg_file, "r", encoding="utf-8") as f:
            msg = f.read()
    except Exception as e:
        print(f"ERROR: Could not read message file: {e}")
        sys.exit(1)

    lines = msg.strip().splitlines()
    if not lines:
        print("ERROR: Commit message is empty.")
        sys.exit(1)

    subject = lines[0].strip()
    
    # Check length
    if len(subject) > 50:
        print(f"ERROR: Subject line is too long ({len(subject)}/50 chars).")
        sys.exit(1)

    # Check period
    if subject.endswith("."):
        print("ERROR: Subject line must not end with a period.")
        sys.exit(1)

    # Check prefix
    pattern = r"^(feat|fix|docs|refactor|chore)(\([A-Za-z0-9_\-]+\))?: .+"
    if not re.match(pattern, subject):
        print("ERROR: Subject line must start with a valid semantic prefix (e.g., 'feat: ', 'fix: ').")
        sys.exit(1)

    print("SUCCESS: Commit message passes validation.")
    sys.exit(0)

if __name__ == "__main__":
    main()

Watching the Agent Self-Correct

When this skill activates, the agent executes the playbook loop. If the model drafts an incorrect message, the script steps in to enforce quality:

Agent drafts message and writes to commit_draft.txt:
"feat: Added the network configuration interface, fixing process sockets."

Agent runs check:
$ python .agents/skills/git-commit/scripts/verify_commit.py commit_draft.txt
> ERROR: Subject line is too long (72/50 chars).

Agent catches error:
"The validation script failed because the subject line is 72 characters. 
 I will refactor to shorten it and use the imperative verb."

Agent refactors and updates commit_draft.txt:
"feat: add network configuration interface"

Agent runs check:
$ python .agents/skills/git-commit/scripts/verify_commit.py commit_draft.txt
> SUCCESS: Commit message passes validation.

By defining a local validation boundary, we completely remove the "vibe checks" from prompt engineering. The agent uses natural language instructions to drive the workflow, but relies on standard system scripts to lock in formatting constraints.

References