You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@grails.apache.org by ja...@apache.org on 2026/07/01 14:26:54 UTC

(grails-core) branch docs/publish-grails-skillsjars created (now 5ed1a0f83e)

This is an automated email from the ASF dual-hosted git repository.

jamesfredley pushed a change to branch docs/publish-grails-skillsjars
in repository https://gitbox.apache.org/repos/asf/grails-core.git


      at 5ed1a0f83e Publish Grails skills through SkillsJars

This branch includes the following new commits:

     new 5ed1a0f83e Publish Grails skills through SkillsJars

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(grails-core) 01/01: Publish Grails skills through SkillsJars

Posted by ja...@apache.org.
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 <ja...@users.noreply.github.com>
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