# How Download Links for Skill Artifacts Are Managed in the garden-skills README

> Discover how the garden-skills README automatically manages skill artifact download links using HTML comments and a script for the latest GitHub Releases.

- Repository: [ConardLi/garden-skills](https://github.com/ConardLi/garden-skills)
- Tags: how-to-guide
- Published: 2026-09-01

---

**The garden-skills repository uses HTML comment markers in the README that are automatically replaced with live download links by the `update-readme.mjs` script, ensuring users always access the latest released artifact from GitHub Releases.**

The `ConardLi/garden-skills` project distributes skill packages as zipped artifacts attached to GitHub Releases. To keep documentation synchronized with actual releases, the repository implements an automated system that generates **download links for skill artifacts** directly within the README using placeholder markers and a Node.js release script.

## Marker-Based Download Links in the README

Rather than hard-coding static URLs that quickly become stale, the README contains HTML comment markers that delimit where dynamic download links should appear. These markers follow the pattern `<!-- DOWNLOAD:<skill-id>:start -->` and `<!-- DOWNLOAD:<skill-id>:end -->`.

For example, the web-design-engineer skill embeds its link like this:

```markdown
<!-- DOWNLOAD:web-design-engineer:start -->[Download v1.3.0 .zip](https://github.com/ConardLi/garden-skills/releases/download/web-design-engineer-v1.3.0/web-design-engineer-1.3.0.zip)<!-- DOWNLOAD:web-design-engineer:end -->

```

This approach allows the automation script to perform targeted text replacements without affecting surrounding documentation content.

## The Automation Pipeline

The core logic resides in `scripts/release/update-readme.mjs`. This script orchestrates the link generation process by querying Git tags and rebuilding markdown links for every skill defined in the repository.

### Loading Skill Manifests

The script initializes by calling `loadAllManifests` from `scripts/release/lib/skills.mjs`. This function enumerates all skill directories and loads their configuration files, establishing the complete catalog of artifacts that require **download links for skill artifacts** in the README.

### Resolving Latest Versions from Git Tags

Critically, the system determines the current release version by parsing Git tags rather than reading [`manifest.json`](https://github.com/ConardLi/garden-skills/blob/main/manifest.json) files. This prevents 404 errors when developers bump manifest versions before cutting an actual release.

The `lastTagFor` function uses `parseTag`, which applies the following regex to identify valid release tags:

```js
const TAG_RE = /^([a-z0-9][a-z0-9-]*[a-z0-9])-v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)$/;

export function parseTag(tag) {
  const m = TAG_RE.exec(tag);
  if (!m) return null;
  return { skill: m[1], version: m[2] };
}

```

This regex expects tags formatted as `<skill>-v<semver>` (e.g., `gpt-image-2-v1.0.4`). By scanning the repository's tag history, the script identifies the most recent valid release for each skill, ensuring the README always links to existing assets.

### Constructing Download URLs

For each skill with a published release, the `buildBlock` function generates the final markdown link:

```js
function buildBlock(skill, version, repo, lang) {
  if (!version) return COPY[lang].unreleased;
  const tag = buildTag(skill, version);          // `<skill>-v<version>`
  const zip = zipName(skill, version);           // `<skill>-<version>.zip`
  const url = `https://github.com/${repo}/releases/download/${tag}/${zip}`;
  const label = COPY[lang].label.replace("%V", version);
  return `[${label}](${url})`;
}

```

The function assembles the GitHub Releases URL using:
- **Tag name**: Constructed by `buildTag` (e.g., `web-design-engineer-v1.3.0`)
- **Filename**: Generated by `zipName` following the pattern `${skill}-${version}.zip`
- **Repository**: The target GitHub repository identifier (e.g., `ConardLi/garden-skills`)

### Idempotent README Updates

The script performs a rewrite pass over [`README.md`](https://github.com/ConardLi/garden-skills/blob/main/README.md) and localized variants, replacing content between each `<!-- DOWNLOAD:...:start -->` and `<!-- DOWNLOAD:...:end -->` marker with the freshly generated `buildBlock` output. This operation is **idempotent**—running the script multiple times produces identical results when no new releases exist, making it safe to execute during every CI pipeline.

## Release Workflow Integration

The update process typically executes during CI/CD pipelines immediately after a new tag is pushed. When a maintainer releases version `1.3.0` of the web-design-engineer skill:

1. The Git tag `web-design-engineer-v1.3.0` is created and the ZIP artifact uploads to GitHub Releases
2. `update-readme.mjs` queries tags via `lastTagFor` and detects the new version
3. `buildBlock` generates the updated markdown URL pointing to the specific release asset
4. The script rewrites the marker block in [`README.md`](https://github.com/ConardLi/garden-skills/blob/main/README.md) with the new link
5. Changes are committed back to the repository

This workflow ensures that visitors browsing the repository always see functional **download links for skill artifacts** pointing to existing, downloadable release assets.

## Summary

- The README uses HTML comment markers (`<!-- DOWNLOAD:skill:start -->` and `<!-- DOWNLOAD:skill:end -->`) to define dynamic link regions that the script can safely rewrite
- The `scripts/release/update-readme.mjs` script automates link generation during releases by querying the Git tag history
- Version resolution relies on Git tags matching the `<skill>-v<semver>` pattern via `parseTag`, not the version field in manifest files
- The `buildBlock` function constructs GitHub Releases URLs following the template `https://github.com/${repo}/releases/download/${tag}/${zip}`
- The rewrite process is idempotent and supports multiple languages through the `COPY` configuration object passed to `buildBlock`

## Frequently Asked Questions

### Why does the script use Git tags instead of manifest.json for versioning?

Reading version information from Git tags ensures that the README only advertises releases that actually exist on GitHub. If the script read from [`manifest.json`](https://github.com/ConardLi/garden-skills/blob/main/manifest.json), it might generate links to artifacts that have not been built or uploaded yet, resulting in 404 errors for users attempting to download **skill artifacts**.

### What happens if a skill has no releases yet?

When `lastTagFor` returns null for a skill, the `buildBlock` function returns an "unreleased" placeholder string defined in the `COPY` localization object. This indicates that the skill is available in the repository source but has not yet been packaged into a downloadable ZIP file.

### Can the script handle localized README files?

Yes. The `update-readme.mjs` script processes multiple README variants (e.g., [`README.zh-CN.md`](https://github.com/ConardLi/garden-skills/blob/main/README.zh-CN.md)), using the `lang` parameter in `buildBlock` to select appropriate label text for different languages from the `COPY` configuration object.

### Is the regex pattern for tags configurable?

The `TAG_RE` constant in `scripts/release/lib/skills.mjs` is hardcoded to enforce the convention of lowercase alphanumeric skill names with hyphens followed by semantic versioning (e.g., `skill-name-v1.2.3`). Modifying this regex would require editing the source code in `skills.mjs` to support alternative tagging conventions.