{"slug": "ai-foundations-and-good-practices-creating-skills", "title": "AI - Foundations and good practices creating skills", "summary": "A developer has compiled a set of best practices for defining skills for AI agents, drawing on an online workshop by @mouredev and additional research. The guide covers skill directory conventions such as ./.agents/skills and ./.claude/skills, required SKILL.md files, and YAML header fields including name, description, license, version, and context. It emphasizes precise, structured skill definitions to minimize model hallucination and warns that loading skills into session context should be controlled.", "body_md": "Let's talk about skills for AI agents, and we're doing it off the back of attending an online workshop given by [@mouredev](https://dev.to/mouredev) on skills a few days ago.\n\nThe workshop was really interesting, so much so that I was left wanting more, so I went digging a bit deeper, not so much into what skills do or what they're for, but into the best practices for defining them.\n\nThe result is this post, where I try to organize the main ideas I've been gathering and putting in order. I hope they're as useful to you as they've been to me.\n\nWithout further ado... let's get started.\n\nSkills are used to automate repetitive processes whose procedure can be described with a high degree of precision.\n\nThe information inside a skill has to be perfectly structured and fine-tuned so that the model has the least possible chance of making up results.\n\nThe content of a skill can include:\n\nWhen we start up an agent, it already knows which skills are available; however, skills can be invoked manually by the developer (**explicit invocation**), or automatically by the agent itself (** implicit invocation**), once we've made it aware that they exist.\n\nEvery skill we use gets loaded into the context of the current session, so it's important to control excessive use when they're not needed.\n\nWe'll create skills inside the `./.agents/skills` directory (if they're only for the current project), or in `~/.agents/skills` (if they're global for every agent running on the machine).\n\n🔎 NOTE\n\nThis is the standard across the vast majority of agents, but just in case, it's always worth checking how a specific agent handles skills.\n\nFor example, in Claude Code CLI, if we manually create a skill in our repo, it won't be detected even after restarting the agent. Why? Because for this agent they need to be created in `./.claude/skills`. It's not a problem with the skill or its definition, but with where Claude Code looks for it.\n\nEach skill must be created inside a directory specific to that skill. Therefore, it's essential that the skill's directory name matches the name we give the skill itself, since that's the name by which the agent will invoke it.\n\nOn top of that, a `SKILL.md` file — containing the skill's definition — must **always** exist inside that directory.\n\nEvery skill must have a header that describes it. This header is identified by the opening and closing `---` characters typical of YAML.\n\n⚠️ IMPORTANT\n\nEven though the skill is defined using Markdown, the content of the skill's header must be written in YAML.\n\nThe content of that header must include the following fields:\n\n`name`\nMust be between 1 and 64 alphanumeric characters, lowercase, in `kebab-case` format.\n\nIt cannot start or end with a hyphen, and it can never contain two or more consecutive hyphens (`--`).\n\nFinally, it must match the name of the directory where the skill is defined, since that's what's used to invoke the skill (`/skill-name`).\n\nThe regex for this name is: `^[a-z0-9]+(-[a-z0-9]+)*$`.\n\n`description`\nMust be between 1 and 1024 characters.\n\nIt has to be specific enough for the agent to be able to read its content and determine whether it matches what the user is looking for.\n\nTo do that, the description should contain specific phrases, verbs, or concrete use cases, for example: `Use when the user asks to review code or optimize database queries`.\n\n`license`\nUsed to define the legal usage context of the skill or its usage permissions.\n\nWhen setting its value, it's considered good practice to follow the SPDX (`Short-form License Identifiers`) standard.\n\nThe most common values tend to be: `MIT`, `Apache-2.0`, `GLP-3.0`, and `Proprietary` or `Commercial`.\n\n`version`\nContains the string that defines the semantic versioning of the skill, for example: `1.0.0`.\n\nIt's usually used to configure pipelines, deployment updates, or tracking within certain ecosystems.\n\n`context`` inline` | `fork`\nLets us define how the skill runs in relation to the agent's main conversation thread.\n\nThis aspect matters at the level of memory orchestration configuration and environment isolation when the skill is invoked.\n\nBy default, if this field isn't defined, the skill runs in line with the ongoing conversation; however, there are values we can assign to this field to change that behavior:\n\n`inline` (default value)\n\nThe skill runs directly within the current conversation window.\n\nThe instructions, examples, and tools defined inside `SKILL.md` are added directly to the current context, and the model retains them in full for use in later parts of the conversation.\n\nThis setting is recommended when, upon starting multiple tasks that need to work together, the skill needs to know the entire history, tone, and specific references the user has made in order to operate correctly.\n\nWith all that said, if we choose this option, we need to set guardrails at the description level so the skill knows exactly when it should step back to gather information and then return to where it left off.\n\n`fork` (sub-agent or isolated sandbox mode)\n\nIn this mode, you could think of it as creating a metaphorical \"child process,\" where the agent temporarily pauses the main conversation thread, copies the relevant information from the current state, and spins up an isolated sub-agent to process exclusively the task assigned by the skill.\n\nAt the memory level, the sub-agent created doesn't read the entire memory or history of the main conversation; instead, it prevents the model from getting distracted from that conversation by avoiding drift caused by mixing in other queries.\n\nAt the token consumption level, since it doesn't take the full main conversation history but rather a small, very specific section, we can reduce the volume of input tokens and get a cheaper, faster execution.\n\nOnce the skill has completed its task, it gathers, cleans up, and distills a summary of the result and injects it into the main conversation thread before closing the \"child process\" in which it was operating.\n\nWith all that said, if we choose this option, we need to be aware that the instructions must be fully self-contained, since the skill won't have access to the complete conversation history.\n\n**Comparison table**\n\n| Feature / Behavior | `context: inline` (Default) | `context: fork` | \n|---|---|---|\n| Work area | Main conversation window | Isolated sub-agent | \n| History visibility | Full history preserved | Hidden (or heavily restricted) | \n| Token consumption | High (grows with conversation length) | Low (optimized for skill-specific data) | \n| Output obtained | Continuous as the conversation goes on | A simple, structured summary | \n| Main use case | Interactive assistant (e.g., copywriting) | Heavy background work (e.g., code review) | \n\n`compatibility`\nThis field acts as an environment check, making sure the machine where the skill or execution engine is running has the hardware capabilities, software packages, or required LLM engine requirements needed to run the instructions defined inside the skill. For example, we can define the model's minimum reasoning requirements, as well as operating system constraints, CLI packages, or required binaries.\n\nIf the system scans the skill and detects a compatibility issue, the skill is disabled or hidden to prevent runtime errors.\n\nFor example:\n\n```\n  compatibility:\n    model: \">=gpt-4o\"\n    os: \"linux, darwin\"\n    dependencies:\n      - \"python>=3.10\"\n      - \"ffmpeg\"\n```\n\n`allowed-tools`` allow-tools`)\nThis field is defined by a list of values that strictly establish or limit which tools the agent is allowed to use when the skill runs.\n\nWhen defining the content of this property, you should keep the **Principle of Least Privilege** in mind, so that if a skill needs to read local documentation, it shouldn't be allowed to execute terminal commands.\n\nWhen a skill that has this property defined is executed, the agent automatically suspends/disables any tool not specified in the list, so that even if the model hallucinated and tried to invoke one of the disallowed tools, the model's orchestrator would block it.\n\nOne of the main advantages of this field is that it prevents excessive consumption caused by the unnecessary use of expensive tools or the execution of dangerous operations (files, databases, infrastructure, etc.) that aren't clearly authorized.\n\n```\n  allowed-tools:\n    - internet_browse\n    - read_local_file\n    - parse_json\n```\n\n`disable-model-invocation`` true` | `false`\nThis field is used to configure the level of \"awareness\" the model has about whether the skill in question exists or not.\n\nWhen this field isn't defined, or is left at its default value (`false`), when the agent starts up it will have the skill in its list of available skills, and if the user says `Check my server logs`, the model will analyze the skill's description and, if it matches the goal of the request, it will start running the skill.\n\nIf this option is set to `true`, the model will completely ignore this skill, no matter how the agent is being used. The only way to use it is through explicit invocation of that skill, i.e., `/skill-name`.\n\nThis setting is particularly useful for destructive workflows, ones that apply irreversible changes, or ones that carry high risk for the system, where a misinterpretation by the model could lead to accidental loss of valuable data or unauthorized system changes.\n\n```\n  name: wipe-database-cache\n  disable-model-invocation: true\n```\n\n`metadata`\nThis field is an unstructured dictionary designed for development-specific tasks, where we can add additional information related to the skill.\n\nIt acts as a communication bridge between different platforms, code editors, and frameworks that need a way to define additional information relevant to each one, without breaking the open standard for skill definitions.\n\nOne of the main uses given to this field is to display the skill's information in a human-friendly format in applications or tools that manage skills.\n\nOn the other hand, when skills aren't public, this field can be used for tracking their development at a corporate level.\n\n```\n  metadata:\n    author: \"Platform Security Team\"\n    category: \"DevOps / SRE\"\n    icon: \"shield-alert\"\n    cost_center: \"fintech-ops-99\"\n```\n\nSome complete examples of skill headers could be the following:\n\n```\n# This example uses 'context: fork' so it runs in a secure thread, with low token consumption, to perform\n# heavy backend-level validation. Also, since it runs autonomously, it can be invoked automatically by the\n# model and can include the CLI tools needed to carry out its task.\n---\nname: kubernetes-manifest-validator\ndescription: Use this skill when the user provides Kubernetes YAML files, Helm charts, or K8s deployment manifests and requests syntax validation, security linting, or API deprecation checks.\nlicense: Apache-2.0\nversion: 2.4.1\ncontext: fork\ncompatibility:\n  model: \">=gpt-4o\"\n  os: \"linux\"\n  dependencies:\n    - \"kubeconform>=0.6.0\"\n    - \"trivy>=0.45.0\"\nallowed-tools:\n  - read_local_file\n  - execute_terminal_command\n  - write_local_file\ndisable-model-invocation: false\nmetadata:\n  team: \"SRE-Core\"\n  environment: \"staging-validation\"\n  severity-tier: \"medium\"\n---\n---\n# This example uses 'context: inline' so it runs within the agent's context window, since it needs access\n# to the full conversation history. The tool restriction lets the model run the skill during the conversation.\nname: market-competitor-analyzer\ndescription: Use this skill when the user asks for competitive intelligence, financial market trends, stock ticker comparisons, or landscape analysis regarding corporate competitors.\nlicense: MIT\nversion: 1.0.3\ncontext: inline\ncompatibility:\n  model: \">=gpt-4-mini\"\n  os: \"any\"\n  dependencies:\n    - \"python-yfinance>=0.2.0\"\nallowed-tools:\n  - web_search\n  - fetch_url_content\n  - render_data_chart\ndisable-model-invocation: false\nmetadata:\n  department: \"Product-Strategy\"\n  billing-code: \"mkt-res-2026\"\n  ux-icon: \"trending-up\"\n---\n# This skill involves the irreversible deletion of data, so it includes the 'disable-model-invocation: true'\n# setting, making it only invokable by a human. Additionally, the 'compatibility' field restricts it to the\n# specific database it can operate on.\n---\nname: production-database-purger\ndescription: Mandatorily hidden from automatic routing. This skill safely drops stale tables, truncates high-volume log schemas, and runs database vacuuming routines on production clusters during maintenance windows.\nlicense: Proprietary\nversion: 4.0.0-rc1\ncontext: fork\ncompatibility:\n  model: \">=o1-preview\"\n  os: \"linux, darwin\"\n  dependencies:\n    - \"postgresql-client-16\"\n    - \"aws-cli-v2\"\nallowed-tools:\n  - execute_sql_query\n  - fetch_vault_secret\ndisable-model-invocation: true\nmetadata:\n  compliance-required: \"SOC2-Type-II\"\n  requires-human-approval: true\n  criticality: \"high\"\n  slack-alert-channel: \"#prod-ops-logs\"\n---\n```\n\nAt this point, it's worth noting that even though these fields may belong to a standard, not every agent understands them or uses the same naming to achieve the same goal. The following table shows which agent accepts which header field:\n\n**Header fields for SKILL.md supported by agent**\n\n| Header Field | Claude Code CLI | Claude.ai (Web) | Claude API | AutoGen Studio | CrewAI Core | LangGraph Engine | OpenCode CLI | Aider CLI | CodeRabbit CLI | \n|---|---|---|---|---|---|---|---|---|---|\n| **`name`** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \n| **`description`** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \n| **`version`** | ✅ | ❌ | ✅ | ❌ | ✅ | ✅ | ✅ | ❌ | ✅ | \n| **`license`** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ | \n| **`context`** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ *(mode)* | ❌ | ✅ | \n| **`allowed-tools`** | ✅ | ❌ | ✅ | ✅ | ✅ | ✅ | ✅ *(perms)* | ✅ | ✅ | \n| **`disable-model-invocation`** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ *(disable)* | ❌ | ✅ | \n| **`compatibility`** | ✅ | ❌ | ✅ | ❌ | ❌ | ❌ | ✅ *(deps)* | ❌ | ❌ | \n| **`metadata`** | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | \n\n`T-I-P-O`\nOnce we've finished defining the skill's header, it's time for the body.\n\n⚠️ IMPORTANT\n\nUnlike the header section, the body of a skill *is* written using Markdown.\n\nWhat we define here is a compendium of semantic directives, structured in a certain way, that the model is able to internalize as operational instructions.\n\nThis block is decisive for whether the agent behaves as deterministically as possible, or starts having critical hallucinations during execution.\n\nDespite the importance of this block, since it's open source, there's no single, strict structure that universally forces us to define a skill's body in a particular way. That said, in enterprise environments there's a growing convergence toward using the pattern known as **`T-I-P-O`** (` Targets`, `Inputs`, `Procedure`, and `Outputs`), which is the de facto minimum accepted to guarantee a baseline of determinism in the model.\n\n`# Target` (or `# Objective`)\nDefines what this skill's execution is aiming for and what the expected final goal is.\n\nWhen an agent gets lost in a loop of tool calls, it re-evaluates its progress by comparing it against what's defined in this section.\n\nExample:\n\n```\n  # Target\n\n  The absolute objective of this skill is to locate deprecated API endpoints inside the repository, upgrade them to the current SDK version, and ensure the test suite passes with zero errors.\n```\n\n`# Inputs` (or `# Prerequisites`)\nDefines the exact variables, files, or data formats the agent must receive **before** starting work.\n\nThis section matters because it stops the agent from \"guessing\" or making up data; that way, if the current context doesn't contain these elements, the agent knows it should stop and ask for them.\n\n```\n  # Inputs\n\n  This skill requires two primary artifacts from the active workspace context:\n\n  1. `legacy_endpoints.json` - A manifest listing the raw endpoints.\n  2. `current_sdk_spec.yaml` - The up-to-date OpenAPI schema reference.\n```\n\n`# Procedure` (or `# Execution Steps`)\nDefines a numbered, sequential list that the agent must follow strictly, step by step.\n\nWith this, we manage to break the model's reasoning down into manageable subtasks (Chain-of-Thought) while forcing the agent to follow numbered steps, reducing possible hallucinations in workflows that use multiple tools.\n\n```\n  # Procedure\n\n  1. Parse the `legacy_endpoints.json` file using the `read_file` tool.\n  2. For each endpoint listed, locate its definition in the codebase using `grep_search`.\n  3. Replace the deprecated syntax with the new methods specified in `current_sdk_spec.yaml`.\n  4. Run the local testing pipeline using `execute_terminal_command(command=\"npm test\")`.\n```\n\n`# Outputs` (or `# Expected Output Format`)\nDefines the output contract upon completion of the skill's execution.\n\nHere we indicate whether we want a JSON, a Markdown document, a Markdown code block, etc., as well as the exact structure of the final response.\n\nThis way, the agent's outputs will be easily readable by other automated scripts or by the user, without containing filler text.\n\n```\n  # Outputs\n\n  Return exclusively a valid JSON block containing the compilation summary. Do not include conversational preambles.\n```\n\nFull example of a defined skill:\n\n```\n---\nname: api-migration-tool\ndescription: Use when the user requests an automated upgrade of legacy API endpoints.\nversion: 1.0.0\ncontext: fork\nallowed-tools:\n  - read_file\n  - write_file\n  - grep_search\n  - execute_terminal_command\ndisable-model-invocation: false\n---\n\n# Target\n\nMigrate deprecated microservice API routing schemas to the v2 standard.\n\n# Inputs\n\n- Workspace variable: `target_directory`\n- Source config file: `api_routing.conf`\n\n# Procedure\n\n1. Scan the `target_directory` for any `.conf` files.\n2. Cross-reference keys against the official version 2 documentation wrapper.\n3. Apply the structural rewrites into a new temporary branch.\n4. Validate the syntax integrity.\n\n# Outputs\n\nProvide a markdown table summarizing:\n\n- The file paths modified.\n- The original lines of code.\n- The rewritten replacement chunks.\n```\n\n🔎 NOTE\n\nAs a final note regarding the body of a skill, when we're in a section where we implement an unordered list, we sometimes find that both the hyphen (`-`) and the asterisk (`*`) are used to mark a list item.\n\nWhile it's true that, computationally, it makes no difference to the model, cleanliness and order matter here from a DevEx standpoint, so the use of the hyphen (`-`) is encouraged for unordered list items over any other character.\n\nThese are the basic sections a skill should have. Beyond these, if our application's needs require defining additional sections, we're free to do so as long as it helps fine-tune the skill's use even further.\n\nSome additional sections beyond those proposed by the `T-I-P-O` pattern are the following:\n\n`# Guardrails & Safety Constraints`\nDefines critical restrictions by listing absolute limits, forbidden behaviors, and areas the agent must never touch.\n\nThis is the main line of defense against data destruction or security breaches.\n\n```\n  # Guardrails & Safety Constraints\n\n  - **NEVER** pass raw string variables directly into bash command lines without character escaping.\n  - Do not modify or read any files inside the hidden `.git/` or `.vault/` internal directories.\n```\n\n`# User Verification Gates`\nEstablishes human approval checkpoints, explicitly defining which specific actions must mandatorily halt the agent's autonomous flow to require visual `\"Ok\"` or manual confirmation from the user in the chat.\n\n```\n  # User Verification Gates\n\n  - **Trigger:** Prior to executing any database truncation or dropping an index.\n  - **Action:** Halt the script, render the specific SQL payload to the user, and ask: \"Do you confirm the execution of this database migration? (y/n)\".\n```\n\n`# Escalation Protocols`\nThis section prevents the agent from getting stuck trying to solve problems that exceed its permission capabilities, instructing it on when to give up and hand the case off to a human user.\n\n```\n  # Escalation Protocols\n\n  - If a connection timeout error occurs more than 3 consecutive times on port 5432, halt automation.\n  - Do not attempt to guess credentials. Output: `[CRITICAL] Network isolation detected. Escalating ticket to SRE team.`\n```\n\n`# State Tracking & Memory Logging`\nWith this section we force the model to structure its thought process and internalize state changes in local variables before calling the next tool, solving memory loss in very long workflows.\n\n```\n  # State Tracking & Memory Logging\n\n  - Before modifying a file, open a `<state>` block to log the original file hash and line count.\n  - Maintain a rolling list of modified assets in your tool call parameters to avoid circular file edits.\n```\n\n`# Chain-of-Thought Auditing`\nHere, what we do is force the agent to justify each action using specific XML tags (like `<thought>`) before invoking terminal commands, which makes debugging and auditing the agent's behavior far easier.\n\n```\n  # Chain-of-Thought Auditing\n\n  - Every tool call must be preceded by a `<reasoning>` block containing:\n    1. Why this tool is necessary now.\n    2. The expected outcome of the invocation.\n```\n\n`# Performance & Cost Optimization`\nHere we can prevent the agent from uncontrollably consuming the API budget (or exhausting the context window), by regulating the amount of text it can read or write in a single iteration.\n\n```\n  # Performance & Cost Optimization\n\n  - When parsing log files, use range parameters to inspect a maximum of 150 lines per tool call.\n  - Avoid re-reading large context files if the content was already logged in the active scratchpad.\n```\n\n`# Compliance & Regulatory Standards`\nThis section matters when we're working with certain data, since it ensures that the deliverables generated by the agent (such as source code or data reports) comply with strict legal or organizational regulations for the sector (SOC2, GDPR, ISO), or from the company itself.\n\n```\n  # Compliance & Regulatory Standards\n\n  - All telemetry methods designed by this skill must completely sanitize PII (Personally Identifiable Information).\n  - Ensure encryption-in-transit configurations use TLS 1.3 as a baseline.\n```\n\n`# Workspace Clean-up & Idempotency`\nWith this property we can guarantee the hygiene of the local system, ensuring the agent deletes its temporary execution files and that, if the skill runs twice in a row, the result is identical without duplicating data.\n\n```\n  # Workspace Clean-up & Idempotency\n\n  - Upon task completion or premature failure, execute an explicit cleanup step to delete `/tmp/cache_*.json`.\n  - Design every code refactor to be completely idempotent; running the skill twice must yield zero changes on the second run.\n```\n\n`# Corporate Style & Terminology Glossaries`\nHere we can unify business terms and the agent's voice when it generates technical documentation, reports, or text responses aimed at end clients or company leadership.\n\n```\n  # Corporate Style & Terminology Glossaries\n\n  - Use the term \"Client Workspace\" instead of \"Tenant Folder\" across all markdown outputs.\n  - Keep tone formal and highly concise; eliminate words like \"obviously\", \"simply\", or conversational expressions.\n```\n\n`# Diagnostic & Telemetry Footprints`\nThis property injects digital signatures and standardized logs into Git commits or the headers of files created by the agent, to uniquely identify which changes were made by the AI and which version of the skill was used.\n\n```\n  # Diagnostic & Telemetry Footprints\n\n  - Append this precise signature at the end of every modified file header:\n    `/* Automated optimization applied via agent-skill: db-optimizer (v2.4.1) */`\n```\n\nAs we develop a skill, it can become increasingly complex, which makes our `SKILL.md` file practically unworkable due to the amount of information, instructions, examples, or similar content it can contain. The most likely result is:\n\nThe solution to this lies in a process called **`Skill Atomization`**, through which heavy logic, external resources, and referential examples are extracted from the skill, turning it into a *declarative orchestrator*, keeping the file's content under 50 or 100 lines of text. This makes the skill's initialization speed within the agent very high and increases the system's scalability through independent reference updates.\n\nThis atomization process is carried out through two actions: **`Rigorous structuring`** and **` Technical linking`**.\n\n`Rigorous structuring`\nInside the directory where we've defined the `SKILL.md` file, we start creating directories with semantically sensible names.\n\nInside each of those directories, we create the corresponding files that will hold the information we want to extract from the original skill.\n\nThe directory structure depends solely on the development team, but there is indeed a certain tendency to have certain established directories, which don't need to be implemented if our skill doesn't require them, but if we do, it's recommended to keep the same naming.\n\nAn example of rigorous structuring could be this:\n\n```\n  my-complex-agent-skill/\n    ├── SKILL.md                 # (Required   ) Main file (Orchestrator and Frontmatter)\n    ├── scripts/                 # (Optional   ) Executable code that offloads heavy logic from the LLM\n    │   └── optimize_matrix.py   #               Numerical computation/complex analysis script\n    ├── assets/                  # (Optional   ) Static data and validation schemas\n    │   └── database_schema.json #               Reference database structure\n    ├── references/              # (Optional   ) Style guides, manuals, or dense documentation\n    │   └── code_style_guide.md  #               Formatting rules the LLM only reads if needed\n    └── examples/                # (Optional   ) Few-Shot Example library (user stories)\n        ├── standard_case.md\n        └── edge_case_timeout.md\n```\n\n`Technical linking`\nNow that we've extracted the excess information from our skill into independent sections of our directory structure, we need to link that content back inside the file that's left.\n\nFor this we'll use **explicit relative paths** to the content we want to reference. Agents are able to read these paths and, through the use of internal tools, can access the files **on demand**, only when the procedure section requires it.\n\nWith this:\n\nTo do this, there are two commonly used patterns: **`Direct relative link`** and **` Footnote reference link`**.\n\n`Direct relative link`\n\nUsed for immediate dependencies the agent must always inspect **before** running a procedure.\n\n```\n# Inputs\n\nThis skill requires the project context infrastructure to match the configuration rules specified in the core [Database Architectural Reference Schema](./assets/database_schema.json).\n```\n\n`Footnote reference link`\n\nUsed for dependencies that are worth having linked but whose loading happens only in very specific situations, meaning the agent won't load these references into context unless absolutely necessary.\n\nBeyond that, they're also often used in very long procedures, pushing references to the end of the file and keeping the main text free of clutter.\n\n```\n# Procedure\n\n1. Pull the latest Docker manifest using the environment variables.\n2. Build the staging container and verify the cluster health check endpoints.\n3. In case the build triggers a pipeline schema violation, fetch the resolution steps immediately.\n\n# Error Handling & Edge Cases\n\n* If the server returns a 503 error, verify if your service mesh matches the internal corporate architecture layout.\n\n---\n\n# Resource Footnotes / Lazy-Load References: ./assets/health_check_spec.json: ./references/pipeline_troubleshooting_guide.md: ./references/corporate_network_mesh_v2.md\n```\n\nNow then, how can we start externalizing a skill? Well, we can start by laying out the following steps:\n\n**1. Move scripts out of the prompt context (`/scripts`)**\n\nSince explaining to an agent in natural language the operations a script must carry out is complex and consumes tokens unnecessarily, we can create a scripting file in a language of our choice that performs that operation.\n\nIn the skill's header, within the `allowed-tools` section, we'll grant execution permission for the `execute_terminal_command` command and invoke our script from the skill's text.\n\n```\n  ---\n  name: my-custom-skill\n  description: ...\n  allowed-tools:\n    - execute_terminal_command\n  ---\n\n  # Procedure\n\n  1. Do not compute matrix variances manually. Instead, trigger the native optimization script:\n    `execute_terminal_command(command=\"python3 ./scripts/optimize_matrix.py --path=.\")`\n```\n\n**2. Externalize the example library (`/examples`)**\n\nWe need to be very careful with how we use the linked files in this section, since, generally speaking, because they contain extensive, concrete examples, they can take up a lot of space, raising both input and output token consumption if we misuse the examples.\n\nIdeally, each example should be its own independent file, so that linking to it allows loading one or another depending on the agent's needs.\n\n```\n  # Expected Workflows\n\n  Before formatting your final response, read and analyze the corresponding execution logs inside the example library based on the current workload:\n  - For standard microservice queries, read [Standard Flow Case](./examples/standard_case.md).\n  - For database connection timeouts, read [Timeout Recovery Case](./examples/edge_case_timeout.md).\n```\n\n**3. Load documentation on demand (`/references`)**\n\nAPI documentation, procedures, etc., can saturate an agent's context very quickly, and on top of that, keeping it updated would also require modifying the skill's content.\n\nIf we extract that documentation into independent, isolated files, we can selectively load them only when needed.\n\n```\n  # Error Handling & Edge Cases\n\n  If a compilation error occurs due to typing differences, do not attempt to guess the syntax. Read the internal reference document [Type Definition Manual](./references/code_style_guide.md) before attempting a second patch rewrite.\n```\n\n`disable-model-invocation: true` on destructive or production skills (like deployments or database purges) to force the skill to only be activated via a slash command (`/`) typed by a person.\n✅ **Leverage metadata for governance**: Use the `metadata` block systematically in corporate environments to record the owning team, cost center, and compliance identifiers (e.g., `compliance: SOC2`).\n\n❌ **Duplicate skill names**: Using the same `name` field in different `SKILL.md` files within the repository, which causes collisions and makes the orchestrator ignore components at random.\n\n❌ **Create generic or ambiguous descriptions**: Writing descriptions like `description: \"An AI assistant to help you write code\"`. This causes the LLM to activate the skill constantly for common tasks, saturating the context window.\n\n❌ **Grant universal permissions out of laziness**: Declaring wildcards for tools or including terminal execution tools (`execute_terminal_command`) in skills that only need to read data.\n\n❌ **Ignore version control**: Leaving the `version` field static at `1.0.0` indefinitely, preventing CI/CD pipelines from verifying whether production agents are running the most recently validated behavior.\n\n❌ **Confuse the role of `context: fork`**: Configuring a skill as `context: inline` when it needs to process thousands of lines of server logs, causing the main chat to fill up with noise and burn through the token budget.\n\n❌ **Omit the `license` field in shared skills**: Leaving the license field empty in shared internal repositories, exposing development teams to intellectual property compliance issues.\n\n`Chain-of-Thought`).`-`) for lists of constraints, inputs, or tools. Reserve asterisks (`*`) exclusively for bold (`**`) or italics (`[Reference 1]: ./references/guide.md`), keeping the main flow free of filler text.\n✅ **State negative constraints assertively**: Dedicate an independent section to safety restrictions (`# Guardrails & Safety Constraints`) and write prohibitions in uppercase and imperative form (e.g., \"NEVER run recursive deletes\").\n\n❌ **Mix bullet styles within the same block**: Randomly combining hyphens (`-`) and asterisks (`*`) within the same list, which can break context segmentation in certain parsing engines.\n\n❌ **Write instructions as free-form narrative prose**: Writing the procedure as a long paragraph instead of a structured list. Models tend to skip secondary instructions when they're buried in dense blocks of text.\n\n❌ **Embed extensive source code within the instructions**: Pasting complete Python or Bash scripts into the prompt body. This drastically degrades the model's attention and drives up execution costs.\n\n❌ **Assume the agent knows the current environment**: Writing procedures without first defining the `# Inputs` section, causing the agent to try to guess file paths, variable names, or database environments.\n\n❌ **Use ambiguous or conditional language**: Using phrases like \"Please try to optimize the query if you think it is a good idea\". Production agents require direct, deterministic instructions (e.g., \"Analyze query latency using the EXPLAIN tool\").\n\n❌ **Overload the skill with too many secondary goals**: Trying to make a single `SKILL.md` file handle code analysis, cloud deployments, and database optimization simultaneously. If the scope grows, split it into independent skills.\n\n`# Error Handling` detailing exactly what the agent should do if a tool returns an error, times out, or returns empty data.`/scripts` and have the agent run it, processing only the output summary.`/examples` directory and link to them on demand, avoiding saturating the agent's initial context.`./assets/`, `./scripts/`, or `./references/` inside your skills actually exist physically and aren't broken.`Halt Conditions`)` 403 Unauthorized`) or persistent network failures.` Keep the skill's core under 100 configuration tokens`: Design the main `SKILL.md` as a lightweight, minimalist conductor that delegates to external resources, guaranteeing ultra-fast startups and optimal memory consumption.\n✅ **Define project-level skills according to that project's needs**: When a skill is used in isolated projects, it's not advisable to extract it for global consumption, since any agent would load it regardless of whether it needs it for that particular repository or not. We should only create global skills, or promote a local skill to global, when we're 100% certain that skill will be used by every project.\n\n❌ `Allow infinite retry loops`: Omitting contingency instructions for failures, which causes the agent to try running the same faulty tool over and over in an infinite cycle that burns through your API budget.\n\n❌ **Hide system error messages**: Instructing the model to ignore terminal failures (e.g., `2> /dev/null`). If the agent masks errors, diagnosing anomalous behavior in production environments becomes impossible.\n\n❌ **Hardcode credentials, absolute paths, or secrets**: Writing passwords, API tokens, or absolute paths like `/Users/username/project` in the body of the skill. This breaks the agent's portability across different systems and creates a critical security vulnerability.\n\n❌ **Blindly trust long-term context memory**: Designing a procedure that depends on the agent remembering a piece of data provided at the start of the general chat, especially when operating in `context: inline` configurations.\n\n❌ **Validate changes using the production environment itself**: Allowing a code refactoring skill to apply direct modifications to the main branch (`main`) without forcing the prior execution of the local unit test suite on an isolated branch.\n\n❌ **Update external scripts without updating the skill's manual**: Modifying the input parameters of an automation script in `./scripts/` but forgetting to update the corresponding tool-calling rules in the body of the `SKILL.md` file, causing the agent to invoke commands with outdated syntax.\n\nIt's clear that if there's one thing that hasn't changed with AI's arrival in the world of development, it's that best practices are more necessary now than ever, and a clear sign of that is the special care we need to take when defining our skills.\n\nI hope this content has been useful to you. If you have any questions, feel free to reach out to me. Here are my profiles on [X](https://x.com/ddialar), [LinkedIn](https://www.linkedin.com/in/ddialar), and [Github](https://github.com/ddialar).", "url": "https://wpnews.pro/news/ai-foundations-and-good-practices-creating-skills", "canonical_source": "https://dev.to/ddialar/ai-foundations-and-good-practices-creating-skills-59m3", "published_at": "2026-09-13 13:39:18+00:00", "updated_at": "2026-09-13 14:09:51.409695+00:00", "lang": "en", "topics": ["ai-agents", "ai-tools", "developer-tools", "artificial-intelligence"], "entities": ["@mouredev", "Claude Code"], "alternates": {"html": "https://wpnews.pro/news/ai-foundations-and-good-practices-creating-skills", "markdown": "https://wpnews.pro/news/ai-foundations-and-good-practices-creating-skills.md", "text": "https://wpnews.pro/news/ai-foundations-and-good-practices-creating-skills.txt", "jsonld": "https://wpnews.pro/news/ai-foundations-and-good-practices-creating-skills.jsonld"}}