/linkyee-plugin-builder
Use when the user wants to add dynamic data (GitHub stars, latest blog posts, weather, follower counts, repo activity, anything fetched from a URL) to their linkyee site by writing a build-time plugin. Triggers on phrases like "add a plugin", "show my latest Medium post on the
$ npx -y skills add ZhgChgLi/linkyee --skill linkyee-plugin-builder --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/linkyee-plugin-builder
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use when the user wants to add dynamic data (GitHub stars, latest blog posts, weather, follower counts, repo activity, anything fetched from a URL) to their linkyee site by writing a build-time plugin. Triggers on phrases like "add a plugin", "show my latest Medium post on the
SKILL.md
linkyee-plugin-builder.SKILL.mdname: linkyee-plugin-builder
description: Use when the user wants to add dynamic data (GitHub stars, latest blog posts, weather, follower counts, repo activity, anything fetched from a URL) to their linkyee site by writing a build-time plugin. Triggers on phrases like "add a plugin", "show my latest Medium post on the page", "fetch X and display it", "make linkyee pull data from Y", "inject a value into the page". Generates `plugins/<PluginName>.rb`, wires it up in `config.yml`, references it in the right Liquid spot, and runs the build to verify.
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
linkyee Plugin Builder
You help users add **build-time plugins** to **linkyee** — a Ruby/Liquid static-site generator. A plugin is a small Ruby class that fetches some data at build time and exposes it to the templates as `vars.<PluginName>`.
The canonical reference for the plugin contract lives at [`plugins/README.md`](../../../plugins/README.md). Read it before you write code; it contains examples, helpers, and conventions that this skill expects you to follow. Do not duplicate that information here — this skill is the workflow, the wiki is the spec.
Mental model in one paragraph
`config.yml` lists enabled plugins. At build time `scaffold.rb` instantiates `<PluginName>.new(yaml_values)`, calls `execute()`, and stores the return value at `settings["vars"][<PluginName>]`. Liquid then renders `{{ vars.<PluginName> }}` (and friends) inside `config.yml` strings and the theme's `index.html`. If a plugin raises, scaffold logs the error and sets the value to `nil` — the build still completes.
Workflow
Follow these steps **in order**. Don't skip steps; the verification step in particular catches most mistakes.
1. Understand the request
Before writing anything, confirm:
- **What data** does the user want? (a star count, a list of posts, a
weather temp, a follower count, the latest commit, …)
- **From where?** (specific URL, RSS feed, JSON API, scraped HTML)
- **Where on the page** should it appear? (footer, a link's text, a new
link, a new section in `index.html`)
If anything is ambiguous, ask **one** focused question. Don't ask three clarifying questions at once.
2. Check whether a built-in plugin already covers it
Read `plugins/README.md` § "Built-in plugins". If `GithubRepoStarsCountPlugin`, `GithubLastCommitPlugin`, `GithubProfilePlugin`, or `RSSFeedPlugin` already does the job, **don't write a new one** — just enable and reference it. Tell the user.
3. Read the existing config
Run `Read` on `config.yml` to see how the user already structures their links and what's already in `vars`. This affects naming and where to inject the value.
4. Write the plugin
Create `plugins/<PluginName>.rb`:
- Name the class to match the file. Use a descriptive `*Plugin` suffix
(e.g. `WeatherPlugin`, `MediumLatestPostsPlugin`).
- `require_relative 'Plugin'` at the top.
- Inherit from `Plugin`.
- Use the helpers (`http_get`, `http_get_json`, `args`, `params`, `cache`,
`log`) — do **not** call `Net::HTTP` or `URI` directly. They're already wrapped with redirect following, timeouts, and error handling.
- Be defensive. Return a sensible default (`0`, `""`, `{}`, `[]`) on
failure. Plugins must not raise.
- Use **String** keys in returned hashes (Liquid can't look up symbol
keys).
- Match the data shape on success and failure (don't return a Hash on
success and `nil` on failure — Liquid breaks).
Look at the existing built-in plugins as templates — they're short and demonstrate the patterns.
5. Wire it up in config.yml
Add to `plugins:` using the YAML style that matches the plugin's arguments (list-style for `args`, hash-style for `params`). Then reference `{{ vars.<PluginName>… }}` in the appropriate `links`/`socials`/`title`/`tagline`/`footer` field, **or** edit the theme's `index.html` if the user wants a loop / conditional.
6. Verify the build
Run:
bundle exec ruby ./scaffold.rb
Then check:
- Build exits 0
- No `[<PluginName>]` or `[scaffold] Plugin '<PluginName>' failed` lines
in stderr
- The injected value appears in `_output/index.html` (grep for it)
If the value is missing, **read the stderr output** — the new `scaffold.rb` swallows plugin failures but logs them clearly. Do not guess; fix the underlying error.
7. Secrets — non-negotiable
If the plugin needs an API key, personal access token, OAuth token, or any other credential:
- **Never** put the secret value in `config.yml`. That file is committed
to git and rendered into the public site at build time. Anything in it is public, forever, including in old commits.
- **Never** hardcode the secret in the plugin's `.rb` file. Same problem.
- **Always** read it from `ENV`:
token = ENV["MEDIUM_TOKEN"]
return [] if token.nil? || token.empty?
http_get_json(url, headers: { "Authorization" => "Bearer #{token}" })- **Always** document the secret name in the plugin's header comment so
the user knows what to set.
- **Always** tell the user how to wire it through, in three steps:
1. **Repo secret**: GitHub → repo → *Settings → Secrets and variables → Actions → New repository secret*. Name it (e.g. `MEDIUM_TOKEN`), paste value. 2. **Workflow env**: edit `.github/workflows/build.yml`, add an `env:` block to the `Deploy` step:
- name: Deploy
env:
MEDIUM_TOKEN: ${{ secrets.MEDIUM_TOKEN }}
run: bash deploy.sh3. **Local dev**: same name, exported in shell: `export MEDIUM_TOKEN=xxx && bundle exec ruby ./scaffold.rb`
If the user asks you to "just paste the token in config.yml for now", **refuse and explain why**. The secret will end up in git history and on the deployed gh-pages branch — no easy way to revoke once leaked.
8. Report back
Tell the user:
- What plugin was created
- What got added to `config.yml`
- Where in the rendered page the value now appears
- How to extend
Read more
name: linkyee-plugin-builder description: Use when the user wants to add dynamic data (GitHub stars, latest blog posts, weather, follower counts, repo activity, anything fetched from a URL) to their linkyee site by writing a build-time plugin. Triggers on phrases like "add a plugin", "show my latest Medium post on the page", "fetch X and display it", "make linkyee pull data from Y", "inject a value into the page". Generates `plugins/<PluginName>.rb`, wires it up in `config.yml`, references it in the right Liquid spot, and runs the build to verify. allowed-tools: Read, Write, Edit, Bash, Glob, Grep
linkyee Plugin Builder
You help users add **build-time plugins** to **linkyee** — a Ruby/Liquid static-site generator. A plugin is a small Ruby class that fetches some data at build time and exposes it to the templates as `vars.<PluginName>`.
The canonical reference for the plugin contract lives at [`plugins/README.md`](../../../plugins/README.md). Read it before you write code; it contains examples, helpers, and conventions that this skill expects you to follow. Do not duplicate that information here — this skill is the workflow, the wiki is the spec.
Mental model in one paragraph
`config.yml` lists enabled plugins. At build time `scaffold.rb` instantiates `<PluginName>.new(yaml_values)`, calls `execute()`, and stores the return value at `settings["vars"][<PluginName>]`. Liquid then renders `{{ vars.<PluginName> }}` (and friends) inside `config.yml` strings and the theme's `index.html`. If a plugin raises, scaffold logs the error and sets the value to `nil` — the build still completes.
Workflow
Follow these steps **in order**. Don't skip steps; the verification step in particular catches most mistakes.
1. Understand the request
Before writing anything, confirm:
- **What data** does the user want? (a star count, a list of posts, a
weather temp, a follower count, the latest commit, …)
- **From where?** (specific URL, RSS feed, JSON API, scraped HTML)
- **Where on the page** should it appear? (footer, a link's text, a new
link, a new section in `index.html`)
If anything is ambiguous, ask **one** focused question. Don't ask three clarifying questions at once.
2. Check whether a built-in plugin already covers it
Read `plugins/README.md` § "Built-in plugins". If `GithubRepoStarsCountPlugin`, `GithubLastCommitPlugin`, `GithubProfilePlugin`, or `RSSFeedPlugin` already does the job, **don't write a new one** — just enable and reference it. Tell the user.
3. Read the existing config
Run `Read` on `config.yml` to see how the user already structures their links and what's already in `vars`. This affects naming and where to inject the value.
4. Write the plugin
Create `plugins/<PluginName>.rb`:
- Name the class to match the file. Use a descriptive `*Plugin` suffix
(e.g. `WeatherPlugin`, `MediumLatestPostsPlugin`).
- `require_relative 'Plugin'` at the top.
- Inherit from `Plugin`.
- Use the helpers (`http_get`, `http_get_json`, `args`, `params`, `cache`,
`log`) — do **not** call `Net::HTTP` or `URI` directly. They're already wrapped with redirect following, timeouts, and error handling.
- Be defensive. Return a sensible default (`0`, `""`, `{}`, `[]`) on
failure. Plugins must not raise.
- Use **String** keys in returned hashes (Liquid can't look up symbol
keys).
- Match the data shape on success and failure (don't return a Hash on
success and `nil` on failure — Liquid breaks).
Look at the existing built-in plugins as templates — they're short and demonstrate the patterns.
5. Wire it up in config.yml
Add to `plugins:` using the YAML style that matches the plugin's arguments (list-style for `args`, hash-style for `params`). Then reference `{{ vars.<PluginName>… }}` in the appropriate `links`/`socials`/`title`/`tagline`/`footer` field, **or** edit the theme's `index.html` if the user wants a loop / conditional.
6. Verify the build
Run:
bundle exec ruby ./scaffold.rb
Then check:
- Build exits 0
- No `[<PluginName>]` or `[scaffold] Plugin '<PluginName>' failed` lines
in stderr
- The injected value appears in `_output/index.html` (grep for it)
If the value is missing, **read the stderr output** — the new `scaffold.rb` swallows plugin failures but logs them clearly. Do not guess; fix the underlying error.
7. Secrets — non-negotiable
If the plugin needs an API key, personal access token, OAuth token, or any other credential:
- **Never** put the secret value in `config.yml`. That file is committed
to git and rendered into the public site at build time. Anything in it is public, forever, including in old commits.
- **Never** hardcode the secret in the plugin's `.rb` file. Same problem.
- **Always** read it from `ENV`:
token = ENV["MEDIUM_TOKEN"]
return [] if token.nil? || token.empty?
http_get_json(url, headers: { "Authorization" => "Bearer #{token}" })- **Always** document the secret name in the plugin's header comment so
the user knows what to set.
- **Always** tell the user how to wire it through, in three steps:
1. **Repo secret**: GitHub → repo → *Settings → Secrets and variables → Actions → New repository secret*. Name it (e.g. `MEDIUM_TOKEN`), paste value. 2. **Workflow env**: edit `.github/workflows/build.yml`, add an `env:` block to the `Deploy` step:
- name: Deploy
env:
MEDIUM_TOKEN: ${{ secrets.MEDIUM_TOKEN }}
run: bash deploy.sh3. **Local dev**: same name, exported in shell: `export MEDIUM_TOKEN=xxx && bundle exec ruby ./scaffold.rb`
If the user asks you to "just paste the token in config.yml for now", **refuse and explain why**. The secret will end up in git history and on the deployed gh-pages branch — no easy way to revoke once leaked.
8. Report back
Tell the user:
- What plugin was created
- What got added to `config.yml`
- Where in the rendered page the value now appears
- How to extend
A fully customized, 100% free, open-source LinkTree alternative — deployed straight to GitHub Pages. Inspired by Jekyllrb and LinkTree.
Repo: ZhgChgLi/linkyee

