From release-automation
Detect project type and configuration for generic application releases
npx claudepluginhub jayteealao/agent-skills --plugin release-automationThis skill uses the workspace's default tool permissions.
Analyzes the project directory to determine the project type (Node.js, Python, Rust, Go, Java, generic, Claude Code plugin, or monorepo) and loads or generates appropriate release configuration. This is the foundation for generic release automation that works with any application.
Verifies tests pass on completed feature branch, presents options to merge locally, create GitHub PR, keep as-is or discard; executes choice and cleans up worktree.
Guides root cause investigation for bugs, test failures, unexpected behavior, performance issues, and build failures before proposing fixes.
Writes implementation plans from specs for multi-step tasks, mapping files and breaking into TDD bite-sized steps before coding.
Analyzes the project directory to determine the project type (Node.js, Python, Rust, Go, Java, generic, Claude Code plugin, or monorepo) and loads or generates appropriate release configuration. This is the foundation for generic release automation that works with any application.
The skill expects to be invoked in a project root directory. It examines:
.release-config.json, .releaserc.json)First, check if user has provided explicit configuration:
# Check for config files in order of precedence
if [ -f ".release-config.json" ]; then
config_file=".release-config.json"
elif [ -f ".releaserc.json" ]; then
config_file=".releaserc.json"
elif [ -f ".releaserc" ]; then
config_file=".releaserc"
else
config_file=""
fi
If config file exists:
config_source: "explicit"See Configuration Reference for schema details.
If no configuration file, detect project type from filesystem markers:
# Detection logic (in priority order)
project_type="unknown"
# Check for monorepo first (multiple package.json or project files in subdirs)
if [ $(find packages -name "package.json" 2>/dev/null | wc -l) -gt 1 ] || \
[ $(find apps -name "package.json" 2>/dev/null | wc -l) -gt 1 ]; then
project_type="monorepo"
# Node.js project
elif [ -f "package.json" ]; then
project_type="nodejs"
# Python project
elif [ -f "pyproject.toml" ]; then
project_type="python"
# Rust project
elif [ -f "Cargo.toml" ]; then
project_type="rust"
# Go project
elif [ -f "go.mod" ]; then
project_type="go"
# Java/Gradle project
elif [ -f "build.gradle" ] || [ -f "gradle.properties" ]; then
project_type="java"
# Maven project
elif [ -f "pom.xml" ]; then
project_type="java"
# Claude Code plugin
elif [ -f ".claude-plugin/plugin.json" ]; then
project_type="claude-plugin"
# Claude Code marketplace
elif [ -f ".claude-plugin/marketplace.json" ]; then
project_type="claude-marketplace"
# Generic project with VERSION file
elif [ -f "VERSION" ] || [ -f "version.txt" ] || [ -f ".version" ]; then
project_type="generic"
# Legacy Python (setup.py)
elif [ -f "setup.py" ]; then
project_type="python"
else
project_type="unknown"
fi
Based on detected project type, determine version file locations:
Node.js:
version_files=("package.json")
adapter="json"
field="version"
Python:
# Check for multiple version sources
version_files=()
if [ -f "pyproject.toml" ]; then
version_files+=("pyproject.toml")
fi
# Look for __version__.py files
if [ -d "src" ]; then
# Find __version__.py in src directory
version_file=$(find src -name "__version__.py" | head -1)
if [ -n "$version_file" ]; then
version_files+=("$version_file")
fi
fi
# Legacy setup.py
if [ -f "setup.py" ]; then
version_files+=("setup.py")
fi
Rust:
version_files=("Cargo.toml")
Go:
# Go uses git tags for versions, no version file
version_files=()
version_via_tags=true
Java/Gradle:
if [ -f "gradle.properties" ]; then
version_files=("gradle.properties")
elif [ -f "build.gradle" ]; then
version_files=("build.gradle")
fi
Maven:
version_files=("pom.xml")
Claude Code Plugin:
version_files=(".claude-plugin/plugin.json")
Generic:
# Find version file by name
if [ -f "VERSION" ]; then
version_files=("VERSION")
elif [ -f "version.txt" ]; then
version_files=("version.txt")
elif [ -f ".version" ]; then
version_files=(".version")
fi
# Check for existing changelog in common formats
if [ -f "CHANGELOG.md" ]; then
changelog_file="CHANGELOG.md"
elif [ -f "HISTORY.md" ]; then
changelog_file="HISTORY.md"
elif [ -f "CHANGES.md" ]; then
changelog_file="CHANGES.md"
elif [ -f "NEWS.md" ]; then
changelog_file="NEWS.md"
elif [ -f "CHANGES.rst" ]; then
changelog_file="CHANGES.rst"
else
# Will be created
changelog_file="CHANGELOG.md"
fi
# Project-specific tag patterns
case "$project_type" in
"nodejs"|"python"|"rust"|"generic")
tag_pattern="v{version}"
;;
"go")
tag_pattern="v{version}" # Go convention
;;
"java")
tag_pattern="v{version}"
;;
"claude-plugin")
# Use plugin name from plugin.json
plugin_name=$(jq -r '.name' .claude-plugin/plugin.json)
tag_pattern="${plugin_name}-v{version}"
;;
"claude-marketplace")
tag_pattern="marketplace-v{version}"
;;
"monorepo")
tag_pattern="{package}-v{version}"
;;
esac
For monorepo projects, scan for packages:
monorepo_packages=()
# Check common monorepo patterns
for pattern in "packages/*" "apps/*" "libs/*"; do
for dir in $pattern; do
if [ -d "$dir" ]; then
# Check if directory contains a project marker
if [ -f "$dir/package.json" ] || \
[ -f "$dir/Cargo.toml" ] || \
[ -f "$dir/pyproject.toml" ]; then
monorepo_packages+=("$dir")
fi
fi
done
done
documentation_files=("README.md")
# Add common doc patterns
if [ -d "docs" ]; then
documentation_files+=("docs/**/*.md")
fi
if [ -d "website/docs" ]; then
documentation_files+=("website/docs/**/*.md")
fi
Validate the detected configuration:
Required validations:
Warnings:
Return structured configuration:
{
"project_type": "nodejs",
"config_source": "auto-detected",
"version_files": [
{
"path": "package.json",
"adapter": "json",
"field": "version",
"exists": true
}
],
"changelog_file": "CHANGELOG.md",
"changelog_format": "keep-a-changelog",
"tag_pattern": "v{version}",
"tag_message": "Release v{version}",
"conventional_commits": true,
"documentation_files": [
"README.md",
"docs/**/*.md"
],
"monorepo": {
"enabled": false,
"packages": []
},
"validations": {
"errors": [],
"warnings": [
"No CHANGELOG.md found, will be created"
]
}
}
Project structure:
/
├── package.json
├── README.md
└── src/
Detection result:
{
"project_type": "nodejs",
"config_source": "auto-detected",
"version_files": [
{
"path": "package.json",
"adapter": "json",
"field": "version"
}
],
"changelog_file": "CHANGELOG.md",
"tag_pattern": "v{version}",
"conventional_commits": true
}
Project structure:
/
├── pyproject.toml
├── src/
│ └── mypackage/
│ └── __version__.py
└── README.md
Detection result:
{
"project_type": "python",
"config_source": "auto-detected",
"version_files": [
{
"path": "pyproject.toml",
"adapter": "toml",
"section": "project"
},
{
"path": "src/mypackage/__version__.py",
"adapter": "python-file"
}
],
"changelog_file": "CHANGELOG.md",
"tag_pattern": "v{version}"
}
Project structure:
/
├── Cargo.toml
├── src/
└── README.md
Detection result:
{
"project_type": "rust",
"config_source": "auto-detected",
"version_files": [
{
"path": "Cargo.toml",
"adapter": "toml",
"section": "package"
}
],
"changelog_file": "CHANGELOG.md",
"tag_pattern": "v{version}"
}
Project structure:
/
├── go.mod
├── main.go
└── README.md
Detection result:
{
"project_type": "go",
"config_source": "auto-detected",
"version_files": [],
"version_via_tags": true,
"changelog_file": "CHANGELOG.md",
"tag_pattern": "v{version}"
}
Project structure:
/
├── packages/
│ ├── lib-a/
│ │ └── package.json
│ └── lib-b/
│ └── package.json
└── README.md
Detection result:
{
"project_type": "monorepo",
"config_source": "auto-detected",
"monorepo": {
"enabled": true,
"packages": [
"packages/lib-a",
"packages/lib-b"
]
},
"tag_pattern": "{package}-v{version}",
"changelog_file": "{package}/CHANGELOG.md"
}
Project structure:
/
├── .claude-plugin/
│ └── plugin.json
├── skills/
└── README.md
Detection result:
{
"project_type": "claude-plugin",
"config_source": "auto-detected",
"version_files": [
{
"path": ".claude-plugin/plugin.json",
"adapter": "json",
"field": "version"
}
],
"tag_pattern": "my-plugin-v{version}",
"changelog_file": "CHANGELOG.md"
}
Project structure:
/
├── .release-config.json
├── VERSION
└── README.md
.release-config.json:
{
"projectType": "generic",
"versionFiles": ["VERSION"],
"tagPattern": "release-{version}"
}
Detection result:
{
"project_type": "generic",
"config_source": "explicit",
"version_files": [
{
"path": "VERSION",
"adapter": "text"
}
],
"tag_pattern": "release-{version}",
"changelog_file": "CHANGELOG.md"
}
Unknown project type:
{
"project_type": "unknown",
"validations": {
"errors": [
"Could not detect project type. Please create .release-config.json with explicit configuration."
]
}
}
Invalid configuration:
{
"config_source": "explicit",
"validations": {
"errors": [
"Configuration file .release-config.json contains invalid JSON",
"versionFiles[0]: 'missing.json' does not exist"
]
}
}
No version files found:
{
"project_type": "generic",
"validations": {
"errors": [
"No version files found. Expected one of: VERSION, version.txt, package.json"
]
}
}
This skill is invoked by the /release command in Phase 1. The command will:
--config argument if needed