This is an automated email from the ASF dual-hosted git repository. jamesfredley pushed a commit to branch docs/publish-grails-skillsjars in repository https://gitbox.apache.org/repos/asf/grails-core.git
commit 5ed1a0f83e6ae7eaeac5af8fe98e47da52e0289e Author: James Fredley <[email protected]> AuthorDate: Wed Jul 1 10:25:30 2026 -0400 Publish Grails skills through SkillsJars Expose the canonical .agents/skills directory through a root skills symlink so SkillsJars can discover the existing skill definitions without duplicating files. Add a verification task and publishing notes for submitting the skills from skillsjars.com. Assisted-by: Hephaestus:openai/gpt-5.5 codex-review --- .agents/skills/README.md | 26 +++++++++++++++++++++++++ build.gradle | 44 ++++++++++++++++++++++++++++++++++++++++++- gradle/rat-root-config.gradle | 3 ++- skills | 1 + 4 files changed, 72 insertions(+), 2 deletions(-) diff --git a/.agents/skills/README.md b/.agents/skills/README.md new file mode 100644 index 0000000000..99ed315af9 --- /dev/null +++ b/.agents/skills/README.md @@ -0,0 +1,26 @@ +<!-- +SPDX-License-Identifier: Apache-2.0 + +Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements; and to You under the Apache License, Version 2.0. +--> + +# Grails Agent Skills + +The canonical Grails agent skills live in this directory. + +The repository root `skills` path is intentionally a symbolic link to this directory so [SkillsJars](https://www.skillsjars.com/) can discover the skills without maintaining duplicate files. SkillsJars deploys from public GitHub repositories by scanning `skills/**/SKILL.md` and then publishes one Maven Central artifact per skill under `com.skillsjars`. + +Run this check before publishing or updating a skill: + +```bash +./gradlew verifySkillsJarsSources +``` + +After changes are merged to the public branch, publish them from SkillsJars by submitting: + +| Field | Value | +|-------|-------| +| GitHub Org | `apache` | +| GitHub Repo | `grails-core` | + +Expected coordinates use the `apache__grails-core__<skill-name>` artifact pattern, for example `com.skillsjars:apache__grails-core__grails-developer:<date>-<commit>`. diff --git a/build.gradle b/build.gradle index 433eba3b5a..b41269402d 100644 --- a/build.gradle +++ b/build.gradle @@ -119,6 +119,48 @@ tasks.register('publishAllToMavenLocal') { } } +tasks.register('verifySkillsJarsSources') { + description = 'Verifies the SkillsJars publication path points at the agent-local skill sources.' + group = 'verification' + + def agentSkillsDir = layout.projectDirectory.dir('.agents/skills') + def skillsJarsLink = layout.projectDirectory.file('skills') + + inputs.dir(agentSkillsDir) + inputs.files(skillsJarsLink) + + doLast { + def expectedTarget = '.agents/skills' + def skillsPath = skillsJarsLink.asFile.toPath() + def actualTarget = null + + if (java.nio.file.Files.isSymbolicLink(skillsPath)) { + actualTarget = java.nio.file.Files.readSymbolicLink(skillsPath).toString().replace('\\', '/') + } + else if (skillsJarsLink.asFile.isFile()) { + actualTarget = skillsJarsLink.asFile.getText('UTF-8').trim().replace('\\', '/') + } + + if (actualTarget != expectedTarget) { + throw new GradleException("The SkillsJars publication path must be a symlink to ${expectedTarget}, but found ${actualTarget ?: 'no link target'}.") + } + + def gitMode = providers.exec { + commandLine 'git', 'ls-files', '-s', 'skills' + }.standardOutput.asText.get().trim().split(' ')[0] + if (gitMode != '120000') { + throw new GradleException("The SkillsJars publication path must be committed as a symlink with git mode 120000, but found mode ${gitMode ?: 'none'}.") + } + + def skillMarkers = fileTree(agentSkillsDir).matching { + include '**/SKILL.md' + }.files + if (skillMarkers.empty) { + throw new GradleException("No SkillsJars skill marker files found under ${expectedTarget}.") + } + } +} + apply { from layout.projectDirectory.file('gradle/publish-root-config.gradle') from layout.projectDirectory.file('gradle/rat-root-config.gradle') @@ -133,4 +175,4 @@ apply { // logger.lifecycle("\t- ${dep}") // } // } -//} \ No newline at end of file +//} diff --git a/gradle/rat-root-config.gradle b/gradle/rat-root-config.gradle index acd52b23db..f3e81bded6 100644 --- a/gradle/rat-root-config.gradle +++ b/gradle/rat-root-config.gradle @@ -26,6 +26,7 @@ tasks.named('rat') { 'CHECKSUMS', // build artifact for storing checksums for easy verification 'PUBLISHED_ARTIFACTS', // build artifact for storing published artifacts coordinates & paths 'licenses/**', // licenses directory excluded + 'skills', // SkillsJars discovery symlink; licensed skill sources live under .agents/skills '.git-blame-ignore-revs', // git configuration isn't code 'grails-web-common/src/main/groovy/org/grails/web/json/JSONObject.java', 'grails-web-common/src/main/groovy/org/grails/web/json/JSONArray.java', @@ -138,4 +139,4 @@ tasks.named('rat') { excludes = allExcludes // never cache license audits it.outputs.upToDateWhen { false } -} \ No newline at end of file +} diff --git a/skills b/skills new file mode 120000 index 0000000000..5018230063 --- /dev/null +++ b/skills @@ -0,0 +1 @@ +.agents/skills \ No newline at end of file
