From graft
Jenkins UI and frontend development — Jelly views, Design Library components, form controls, help files, JavaScript integration, and XSS prevention. Use this skill when writing config.jelly files, implementing Jenkins UI views, working with form validation, or applying Design Library patterns. Covers the full frontend stack: Jelly templating, Stapler form binding, CSS classes, dialog/notification JS APIs, and layout patterns. Make sure to use this skill whenever the user mentions Jenkins forms, Jelly files, Jenkins configuration pages, help files for Jenkins, Jenkins frontend, or Design Library — even if they just say "create the config form." Triggers on: config.jelly, global.jelly, Jelly view, Jenkins form, f:entry, f:textbox, f:select, Design Library, help-*.html, Jenkins UI, Jenkins frontend, Jenkins config page, Jelly XSS, Jenkins JavaScript, validateButton.
How this skill is triggered — by the user, by Claude, or both
Slash command
/graft:jenkins-uiThis skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
Jenkins UI uses Apache Jelly (XML templating) with the Jenkins Design Library for consistent
Jenkins UI uses Apache Jelly (XML templating) with the Jenkins Design Library for consistent
components. Views live alongside Java classes in src/main/resources/<package>/<ClassName>/.
src/main/resources/com/example/MyBuilder/
config.jelly # Job/step configuration form
help.html # Step-level help text
help-fieldName.html # Per-field inline help (auto-linked via field= attribute)
help-fieldName_fr.html # Localized help variant
src/main/resources/com/example/MyConfig/
config.jelly # Global configuration form (Manage Jenkins)
Every Jelly file must start with XSS protection:
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core"
xmlns:f="/lib/form"
xmlns:l="/lib/layout"
xmlns:st="jelly:stapler"
xmlns:t="/lib/hudson">
<!-- content -->
</j:jelly>
Namespaces:
| Prefix | URI | Purpose |
|---|---|---|
j: | jelly:core | Control flow: j:if, j:choose, j:forEach, j:set, j:out |
f: | /lib/form | Form controls: f:entry, f:textbox, f:select, etc. |
l: | /lib/layout | Layout: l:layout, l:main-panel, l:side-panel, l:icon, l:card |
t: | /lib/hudson | Jenkins components: t:progressBar, t:help, t:setIconSize |
st: | jelly:stapler | Stapler: st:bind (JS proxy), st:adjunct (CSS/JS), st:include |
JEXL expressions:
${it.name} — current object's properties${app} — Jenkins singleton${h} — Functions helper class${%messageKey} — localized message lookup from Messages.propertiesJob/step config (config.jelly):
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:entry title="${%Server URL}" field="serverUrl">
<f:textbox placeholder="https://example.com" />
</f:entry>
<f:entry title="${%Credentials}" field="credentialsId">
<f:select />
</f:entry>
<f:advanced>
<f:entry title="${%Timeout}" field="timeout">
<f:number default="30" min="1" />
</f:entry>
</f:advanced>
<f:validateButton title="${%Test Connection}" progress="${%Testing...}"
method="testConnection" with="serverUrl,credentialsId"
checkMethod="post" />
</j:jelly>
Global config (config.jelly on GlobalConfiguration class):
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form">
<f:section title="${%My Plugin}">
<f:entry title="${%Default Server}" field="serverUrl">
<f:textbox />
</f:entry>
<f:entry title="${%Cache Manager}" field="cacheManager">
<f:dropdownDescriptorSelector />
</f:entry>
</f:section>
</j:jelly>
| Tag | Purpose | Java backing |
|---|---|---|
<f:entry title="..." field="..."> | Wrapper with label + help icon | Getter/setter matching field name |
<f:textbox /> | Text input | String property |
<f:number min="1" max="99" default="5"/> | Numeric input | int/Integer property |
<f:password /> | Secret input | Secret type (never plain String) |
<f:textarea /> | Multi-line text | String property |
<f:textarea codemirror-mode="shell"/> | Code editor | String with syntax highlighting |
<f:checkbox title="..." /> | Checkbox | boolean property |
<f:booleanRadio /> | Yes/No radio pair | boolean property |
<f:select /> | Dropdown | doFillFieldItems() returns ListBoxModel |
<f:enum default="SECONDS">${it.description}</f:enum> | Enum dropdown | Enum property |
<f:combobox /> | Searchable dropdown | doFillFieldItems() returns ComboBoxModel |
<f:toggleSwitch title="Disabled" checkedTitle="Enabled"/> | Toggle switch | boolean property |
<f:optionalBlock> | Checkbox-gated section | boolean + nested properties |
<f:advanced> | Collapsible "Advanced..." | Contains optional f:entry elements |
<f:section title="..."> | Form grouping | Visual section header |
<f:repeatableProperty field="items"/> | Repeatable list | List property |
<f:repeatableHeteroProperty /> | Heterogeneous repeatable | DescribableList property |
<f:dropdownDescriptorSelector /> | Extension-point dropdown | ExtensionPoint property |
<f:validateButton method="test" with="a,b"/> | Server-side validation | doTest() on Descriptor |
Place help-fieldName.html next to config.jelly. Auto-linked when field="fieldName" is used:
<div>
Enter the server URL including protocol (e.g., <code>https://example.com</code>).
This is used for API calls during the build.
</div>
help-fieldName_de.html, help-fieldName_fr.html<f:entry help="/plugin/my-plugin/help/custom.html"><?jelly escape-by-default='true'?> as first line<script> blocks:
<!-- UNSAFE -->
<script>var x = "${value}";</script>
<!-- SAFE — use data attributes -->
<div id="x" data-value="${value}"></div>
<script>
var x = document.querySelector('#x').getAttribute('data-value');
</script>
${...} in PCDATA, NOT attributes (handled by XML engine)Util.xmlEscape() and Functions.htmlAttributeEscape() in Java when building HTMLJS proxy for AJAX calls to Java:
@JavaScriptMethod
public int increment(int n) { return count += n; }
<st:bind var="myObj" value="${it}"/>
<script>myObj.increment(1, function(t) { /* t.responseObject() */ });</script>
Adjuncts (load JS/CSS bundles):
<st:adjunct includes="io.jenkins.plugins.myPlugin.myScript" />
Loads src/main/resources/io/jenkins/plugins/myPlugin/myScript.js.
Dialog API (modern Jenkins):
dialog.alert("Title", { message: "Body" });
dialog.confirm("Delete?").then(() => { /* confirmed */ });
dialog.prompt("Name?").then(value => { /* use value */ });
Toast notifications:
notificationBar.show("Saved!", notificationBar.SUCCESS);
For the full Design Library component catalog and form validation patterns, read:
references/design-components.md — layout, buttons, cards, tables, alerts, icons, CSS classesreferences/form-validation.md — doCheck, doFill, validateButton, security in validationnpx claudepluginhub aneveux/claude-garden --plugin graftGenerates declarative, scripted Jenkinsfiles and shared libraries for CI/CD pipelines with Docker/K8s agents, parallel stages, approvals, and security scans.
Configures and optimizes GitLab CI/CD pipelines in .gitlab-ci.yml with caching, Docker-in-Docker, CI Steps composition, and gitlab-ci-local testing. Authors GLFM READMEs/Wikis using alerts, Mermaid, and references.