agentsclimarketplace

Jinja expert

Skill air-gapped/skills/.claude/skills/jinja-expert

Claude Code plugin marketplace — 58 installable reference skills across vLLM/SGLang inference, Kubernetes & Harvester, GPU host bring-up, observability, security, and agent workflows.

Install
npx -y skills add air-gapped/skills --skill jinja-expert

Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.

One thing to look at

  • 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.

What its author says it does

Copied from the file, not written here

Author, read, and debug Jinja2 templates across the three places Jinja lives in 2026 — HuggingFace `chat_template.jinja` (rendered by `apply_chat_template` for vLLM / sglang), Ansible playbooks + `.j2` files, and Jinja-adjacent Kubernetes workflows (`values.yaml.j2`, `kubernetes.core.k8s + template`, Helm post-renderers). Companion to the `helm` skill — Helm charts are Go `text/template` + Sprig, not Jinja, and this skill makes that disambiguation explicit.

SKILL.md

11.5 KB, as published. Nobody here has run it

Jinja Expert

Boundary with transformers-config-tokenizers-expert (same dev plugin). Both skills touch chat_template.jinja, from opposite sides. This one owns the template as Jinja — syntax, scoping, filters, whitespace, why the render is wrong. That one owns which file wins and what the loader does with it: tokenizer_config.json vs chat_template.jinja precedence, structural tokens, which class actually instantiates. "My template renders the wrong text" is here; "my template isn't the one being used" is there.

Jinja2 shows up in three mostly-disjoint ecosystems in 2026. Each has its own dialect — different sandbox, different extensions, different filters, different failure modes. Using chat-template idioms in an Ansible playbook will silently misbehave. Using Ansible !unsafe in a chat template is a syntax error. This skill is the map.


Which dialect am I in?

Pick the row that matches the file under review. The rightmost column points to the next file to read.

SignalDialectGo to
File named chat_template.jinja; apply_chat_template, tokenizer_config.json, vLLM/sglang in play; messages/tools/add_generation_prompt in scopeHF chat template (sandboxed, loopcontrols, tojson ensure_ascii=False)references/chat-template/
File ends in .j2; inside an Ansible playbook/role/collection; {{ ansible_managed }}, hostvars, lookup(...), `default(omit); when:` bare expressionsAnsible Jinja (recursive templating, native-types flag, !unsafe)
values.yaml.j2 / deployment.yaml.j2 feeding Helm or kubectl; jinja2-cli; community.kubernetes.helm / kubernetes.core.k8s; ArgoCD config management pluginHelm-adjacent Jinja (Jinja around Helm, not inside)references/helm-and-jinja.md
File inside a Helm chart (templates/*.yaml, _helpers.tpl, Chart.yaml); {{ .Values.x }}, {{ toYaml ... }}, {{ include "mychart.name" . }}, Sprig functionsGo text/template + Sprignot JinjaReach for the helm skill instead
Syntax looks like {{ }} but something else — .gotmpl, helmfile.yaml, Flask, Saltstack state, dbt model, Home Assistant configNot covered here; use the core-language cheatsheetreferences/language-surface.md + upstream docs

When the dialect is uncertain, start at references/language-surface.md — dialect-neutral, covers the statements, expressions, filters, tests, globals, and whitespace rules shared by every Jinja deployment.


Quick decision guide (chat-template default)

Chat templates are the dialect most likely to bring someone here, so the body below stays biased toward them. When the task is Ansible or Helm-adjacent, branch to the matching reference and ignore the rest.

TaskGo to
Look up a statement, filter, test, or globalreferences/language-surface.md
Understand the exact env apply_chat_template buildsreferences/chat-template/transformers-dialect.md
Diagnose a specific category of chat-template bugreferences/chat-template/seven-failure-categories.md
Write a new chat template (or patch an existing one)references/chat-template/authoring-patterns.md
Find a "clean" chat template to study, or a broken one to learn fromreferences/chat-template/exemplars-and-antiexemplars.md
Debug a chat template or set up coverage/parity testingreferences/chat-template/debugging-and-testing.md
Write Ansible playbooks or .j2 files — filters, lookups, !unsafe, native types, k8s manifestsreferences/ansible-dialect.md
Jinja around Helm — values.yaml.j2, raw manifests, post-renderers, the Helm-is-Go disambiguationreferences/helm-and-jinja.md
Author a Helm chart (Go templates, _helpers.tpl, Sprig)cross to the helm skill

The chat-template dialect at a glance

Every chat template in the HuggingFace ecosystem is rendered through an ImmutableSandboxedEnvironment (trim_blocks=True, lstrip_blocks=True, loopcontrols extension loaded, tojson filter forced to ensure_ascii=False, raise_exception + strftime_now globals). vLLM and sglang both delegate to tokenizer.apply_chat_template, so the sandbox is the whole surface. .append / .pop / .update raise SecurityError; ext.do / ext.i18n / ext.debug are NOT loaded. Variables in scope: messages, tools, documents, add_generation_prompt, all special_tokens_map entries, date_string, plus caller chat_template_kwargs.

Full env config, sandbox rules, and canonical skeleton → references/chat-template/transformers-dialect.md (env) and references/chat-template/authoring-patterns.md (skeleton).


The seven chat-template failure categories

Roughly 90% of real chat-template bugs. Templates written against these defensive idioms survive the next model release.

#CategorySymptomDefensive idiom
1Content-format bifurcationTemplate assumes content is a string; caller passes a list of parts. Python repr of the list lands in the prompt.Branch is string → else is sequence and is not string → else is mapping → catch-all. Never isinstance.
2Loop-scope escape{% set flag = true %} inside {% for %} doesn't persist after the loop.{% set ns = namespace(flag=false) %} then {% set ns.flag = true %}.
3add_generation_prompt bimodalTemplate hardcodes the assistant primer, breaking training; or reads the flag via **kwargs, so vLLM's AST detection can't see it.Explicit {% if add_generation_prompt %} at the tail; render both modes in tests and diff.
4Tool-call shape variancetool_call.function.arguments is sometimes a JSON string, sometimes a dict. tool_call.id is optional. Unguarded access crashes.`{{ args if args is string else args
5Whitespace controlStray \n before `<im_start
6Special-token doublingBOS emitted by tokenizer (add_bos_token=True) AND by template ({{ bos_token }}) → double BOS.Emit BOS in exactly one place. Check tokenizer_config.json before adding {{ bos_token }}.
7Sandbox mutation attempts.append, .pop, .update raise SecurityError. {% do x.append(...) %} doesn't parse (ext.do not loaded).namespace() for accumulators; filter chains (selectattr, map, join) for transformations.

Full examples, before/after code, canonical skeleton, and real-world incidents → references/chat-template/seven-failure-categories.md + references/chat-template/authoring-patterns.md.


The Ansible dialect at a glance

Ansible is the largest Jinja-in-production deployment outside web frameworks. It adds a long list of things to stock Jinja — recursive templating, lazy variable resolution, the !unsafe tag, native-types mode, hostvars, conditional expressions without delimiters (when:, failed_when:, changed_when:, until:), custom filter/test/lookup plugins, and a 22-level variable precedence system. None of it lives in Pallets Jinja.

Four rules carry most of the distance:

  1. when: takes a bare expression, not a Jinja template. when: "{{ foo }}" is always wrong; write when: foo. Same for failed_when:, changed_when:, until:.
  2. !unsafe is all-or-nothing. It disables templating for the whole value — including lookups. For mixed literal + templated content, use {% raw %}...{% endraw %}.
  3. Recursive templating amplifies errors. A variable whose value is itself Jinja-looking renders again. An undefined reference in round-two produces an error message that mentions neither round.
  4. | default(omit) removes a task parameter entirely. The idiomatic way to make a parameter conditional without duplicating task definitions.

Everything else (filters, lookups, kubernetes.core.k8s + template, custom plugins, ansible_managed, debugging with ansible-playbook --check --diff, j2lint, ten gotchas) → references/ansible-dialect.md.


Helm and Jinja — what really runs where

Helm charts do not use Jinja. They use Go's text/template + Sprig. Any {{ ... }} inside a chart is Go syntax. The two languages look identical for the first ten seconds and diverge hard after that — . semantics, pipe argument shape (default 3 vs default(3)), Sprig's toYaml / required / lookup, range rebinding. Chart authoring is owned by the helm skill, not this one.

Where Jinja does show up in Kubernetes workflows:

  1. Ansible wraps Helmvalues.yaml.j2 rendered by Ansible, installed via community.kubernetes.helm. Most common pattern.
  2. jinja2-cli preprocessor — same shape without Ansible. Makefile or CI step renders values.yaml.j2values.yaml, then helm upgrade --install.
  3. Raw Jinja manifests, no Helm — small apps or air-gapped sites. Ansible kubernetes.core.k8s with template: path/to.j2.
  4. Helm post-renderer chained with Jinja — niche; kustomize is usually the better fit here.
  5. kluctl — actually Jinja-native for Kubernetes GitOps. One of the few 2026 tools that isn't Go underneath.
  6. helm-jinja plugins — historically tried to swap Helm's engine; never thrived. Don't adopt in 2026. Use patterns 1/2/3 instead.

Full patterns, decision matrix, and the ten Go-vs-Jinja gotchas (default arity, . scope, toYaml, required vs mandatory, lookup, CRD ordering, genCA / genSignedCert, whitespace trim, double-templating) → references/helm-and-jinja.md.


Out of scope

Generic Flask web templating, custom ext.Extension authoring, async Jinja (enable_async), bytecode caching, custom loaders, and Helm chart authoring itself (Go text/template + Sprig — use the helm skill). For anything in this list, the Pallets docs at https://jinja.palletsprojects.com/ are authoritative for the core language.

Keep looking

Skills are one crate of 328,083. Ordering is by how many stacks a row turns up in, so the top of any crate is what has actually been picked rather than what has the most stars.