kitlangton / kitlangton/effect-solutions

Distribute effect-solutions docs as an Agent Skill

Open
#40 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
442
Forks
22
PR merge metrics
No merged PRs in 30d

Description

## Why

Effect Solutions already targets two surfaces (humans via the website, agents via the CLI). A third surface — [Agent Skills](https://agentskills.io/) — is now a first-class distribution channel via [skills.sh](https://skills.sh) and [gh skill](https://cli.github.com/manual/gh_skill). Shipping the docs as a skill means:

- **Auto-discovery**: the skill's `description` is injected into the agent's context at startup, so the agent decides on its own when to consult Effect Solutions. No need for users to maintain an `AGENTS.md` / `CLAUDE.md` blurb explaining the CLI or asking the agent to use it.
- **Human trigger**: users can invoke it explicitly via the `/effect-solutions` slash command instead of remembering CLI flags.
- **Additional distribution**: via skills.sh auto-indexing + `gh skill install kitlangton/effect-solutions`.

The proposal follows the recommended **progressive disclosure** pattern (one umbrella `SKILL.md` index, per-topic files loaded on demand) — same shape as [vercel-react-best-practices](https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices).

## Proposal

1. **Move** `packages/website/docs/*.md` → `skills/effect-solutions/topics/*.md`. Single source of truth; both website and CLI read from the new path. Filenames keep `NN-slug.md` numeric prefixes.
2. **Generate** `skills/effect-solutions/SKILL.md` (committed, drift-checked) — frontmatter + curated index of all topics grouped by `Setup` / `Core Patterns` / `Ecosystem`. No body transforms; topic files served as-is.
3. **Publish** on every tag: extend `release.yml` to run `gh skill publish` after `changeset publish`, reusing the existing `effect-solutions@X.Y.Z` tag.

## Design decisions

- **Single umbrella skill**, not a bundle.
- **Topic files are not generated** — they live at `skills/effect-solutions/topics/NN-slug.md`. Hide markers / link rewriting / H1 stripping remain render-time concerns of each consumer (website MDX, CLI `generate-manifest.ts`).
- **Only `SKILL.md` is generated.** It's a metadata index, same category as the existing `packages/cli/src/docs-manifest.ts`.
- **No `AGENTS.md` full bundle** — rely on per-topic progressive disclosure.
- **Versioning**: mirror `packages/cli/package.json` version into `metadata.version`. One git tag covers everything.
- **Description format**: ` Topics: , , ...` — ≤1024 chars; on overflow, drop trailing slugs and append `, ...`.

## Implementation plan (PR-deliverable)

1. **Move docs**: `git mv packages/website/docs/*.md skills/effect-solutions/topics/`.
2. **Update path constants** (table below). All single-line edits.
3. **Verify website dev**: `bun run dev` — doc routes render, HMR works across the workspace boundary.
4. **Verify CLI**: `bun run dev:cli -- list` and `... show services-and-layers` resolve from new path.
5. **Verify typecheck + tests**: `bun run typecheck:docs`, `bun run test`.
6. **Generator** `scripts/generate-skill.ts`: walks topics, parses frontmatter, writes `SKILL.md`. Reuses parse/sort/group logic from `generate-manifest.ts`. **Must `import type { DocGroup, DocMetadata } from "../packages/website/src/lib/mdx"`** and extend `DocMetadata` (`Topic extends DocMetadata`) rather than redeclaring the shape — `mdx.ts` is the single source of truth for doc frontmatter types, and a parallel definition will silently drift out of sync the first time a field is added there. (No "DO NOT EDIT" header in the output: SKILL.md is the distributed artifact loaded into agent context, so the header from the `docs-manifest.ts` analogy doesn't apply — that file is bundled into a compiled binary and never user-visible. Contributors are warned via CLAUDE.md instead.)
7. **Wire scripts**: add `bun run generate:skill`; chain into `check` (local pre-commit affordance) and `release` (regen before tag, mirroring how `docs-manifest.ts` and OG images are handled).
8. **Generator + SKILL.md tests** (`tests/skill-generation.test.ts`, vitest):
- End-to-end run produces parseable output (gray-matter round-trip).
- Frontmatter: `name === "effect-solutions"`, length ≤64, matches `^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$` ([spec](https://agentskills.io/specification#skill-md-format)); `description` is string with `1 ≤ length ≤ 1024` ([spec](https://agentskills.io/specification#skill-md-format)); `metadata.version` matches `packages/cli/package.json`.
- Body has a pointer for every non-draft topic; every pointer's target file exists.
- Drafts excluded.
- Topics grouped by `group`, ordered by `order` within group.
- `buildDescription` truncates with `, ...` on overflow; unchanged when fits.
- Idempotent (`generate → diff === ""`).
9. **No PR-time drift gate.** Release is the gate: `scripts/release.ts` already regenerates SKILL.md and auto-commits any drift before push/tag (existing logic at lines 74-85 categorizes generated changes and commits them). End users never see stale SKILL.md because `bun release` always regenerates before publishing. This mirrors how `packages/cli/src/docs-manifest.ts` and `packages/website/public/og/*.png` are kept in sync — neither has a PR drift gate either. The repo also has no pre-commit hook infrastructure (`.husky` / `lint-staged` / `simple-git-hooks` are absent), so introducing one for SKILL.md alone would be an inconsistent new convention. Contributors run `bun run check` locally before committing topic frontmatter changes; it regenerates SKILL.md as part of the chain.
10. **README + docs update**: short section in `README.md` covering install via `npx skills add` and `gh skill install`. Update `CLAUDE.md` doc location + add-new-docs flow, including: "When editing topic frontmatter, run `bun run check` before committing — it regenerates `SKILL.md` and any generator-tracked artifacts." Also fix the misleading comment at `CLAUDE.md:28` from `# Format website/docs` to `# Format website TS/CSS/JSON` — see note below.
**Format script note (out of scope, comment fix only):** `bun --cwd packages/website run format` runs biome 2.4.7, which has no markdown formatter — verified against biome's official [language support matrix](https://biomejs.dev/internals/language-support/) (markdown parsing + formatting both ⌛ "in progress"), tracking issue [biomejs/biome#3718](https://github.com/biomejs/biome/issues/3718) (state: open), and biome's `format --help` flags (json/css/graphql/html only — no `markdown-formatter-`*). The script never formatted `.md` files when they lived under `packages/website/docs/`; verified empirically by restoring a topic to the old path and re-running format — file count stays at 77 (TS/TSX/JSON/CSS in `packages/website/`), and explicit `biome format docs/00-quick-start.md --files-ignore-unknown=false` reports `These paths were provided but ignored`. The comment `# Format website/docs` is misleading.
11. **Workflow patch** in `.github/workflows/release.yml` — insert two steps between `changeset publish` and `Create GitHub release`, gated on the same tag prefix as the existing release step:
```yaml
- name: Ensure gh CLI has skill subcommand
if: startsWith(github.ref, 'refs/tags/effect-solutions@')
run: brew upgrade gh || true

- name: Publish agent skill
if: startsWith(github.ref, 'refs/tags/effect-solutions@')
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG_NAME: ${{ github.ref_name }}
run: gh skill publish --tag "$TAG_NAME" || true
```
`gh skill` is a built-in subcommand of the GitHub CLI (post April 2026 GA), not an extension. The `brew upgrade gh` step is belt-and-suspenders against a stale runner image; fine on `macos-latest` (the existing job runner). Uses existing `contents: write` permission. Ordering matters: skill-publish runs **before** `gh release create` so it can create the GitHub Release for the tag — the existing step's `if gh release view` guard then no-ops. `|| true` keeps a transient skill-publish failure from breaking the npm publish that already succeeded above. No path arg: `gh skill publish` discovers `skills/*/SKILL.md` from the repo root; passing `skills/effect-solutions` makes the CLI treat that path as the scan root and fail validation (`name "effect-solutions" does not match directory name "."`). Cannot be exercised end-to-end from a fork PR (`release.yml` triggers on `push: tags`, not PRs; fork PRs also can't read repo secrets); validate with local `gh skill publish --dry-run` (matching the workflow invocation) before opening the PR and paste output in the PR description.
12. **Changeset**: `bun scripts/changeset-named.ts "distribute docs as an agent skill"` — `minor` on `effect-solutions`. The `@effect-best-practices/website` package is in the `fixed` group with it (`.changeset/config.json`), so it bumps in lockstep automatically. Without a changeset, `bun release` aborts at `hasChangesets` (`scripts/release.ts:55-60`).

## Path-constant updates

| File | Change |
| --------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
| `packages/website/src/lib/mdx.ts:6` | `docsDirectory` → `path.join(process.cwd(), "../../skills/effect-solutions/topics")` |
| `packages/website/scripts/content-watcher.ts` | retarget watch path |
| `packages/cli/scripts/generate-manifest.ts:6,17` | `DOCS_DIR` + Bun text-import path |
| `packages/cli/tsconfig.json` | `rootDirs` + `include` |
| `scripts/typecheck-docs.ts:12` | `DOCS_DIR` |
| `.github/workflows/validate-docs.yml:33` | error-message path string |
| `CLAUDE.md` "Documentation Structure" | path + add-new-docs instructions |
| `CONTRIBUTING.md` (lines 31, 40, 101, 139, 142) | replace `packages/website/docs/` with `skills/effect-solutions/topics/` |
| `packages/website/docs/13-cli.md` (now `skills/.../topics/13-cli.md`) | self-references if any |

## Acceptance criteria

- `skills/effect-solutions/SKILL.md` exists, committed, frontmatter valid per the [Agent Skills spec](https://agentskills.io/specification#skill-md-format):
- `name: effect-solutions` — ≤64 chars; matches `^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$` (lowercase alphanumeric + hyphens; no leading/trailing hyphen).
- `description` non-empty, ≤1024 chars.
- `metadata.version` matches `packages/cli/package.json` version.
- If `allowed-tools` is ever added, it must be a space-separated **string**, not an array — per `[gh skill publish` validation]([https://cli.github.com/manual/gh_skill_publish](https://cli.github.com/manual/gh_skill_publish)).
- `skills/effect-solutions/topics/` contains all 15 (currently) topic files; `packages/website/docs/` removed.
- Website dev + build serve docs from the new path; HMR works.
- `bun run dev:cli -- list` and `bun run dev:cli -- show ` work unchanged.
- `bun run typecheck:docs`, `bun run test`, `bun run check` pass.
- `bun run generate:skill` is idempotent (re-running produces no diff).
- `tests/skill-generation.test.ts` covers all assertions listed in step 8.
- Local `gh skill publish --dry-run` succeeds (output pasted in PR description).

## Maintainer-only follow-ups (post-merge)

1. **Repo topic** (one-time): `gh repo edit kitlangton/effect-solutions --add-topic agent-skills`. Required for `gh skill publish` and skills.sh indexing.
2. **First release**: cut `effect-solutions@X.Y.Z` via `bun release`. Merged `release.yml` runs `gh skill publish` on the same tag.
3. **Smoke test (`gh skill`)**: `gh skill install kitlangton/effect-solutions`; verify the skill installs into the local agent skills directory and skill is envocable in coding agents.
4. **Smoke test (`*skills.sh` CLI)**: `npx skills add kitlangton/effect-solutions`; verify the skill installs into the local agent skills directory and skill is envocable in coding agents.

## Possible caveats

1. **Tag reuse**: proposal is to reuse `effect-solutions@X.Y.Z` for the skill release — single source of version truth, single tag covers npm + skill, history stays linked. Theoretical risk: `gh skill publish --tag ` may conflict with the GitHub Release that the existing `release.yml` already creates for that tag. Mitigated by ordering — run the skill-publish step before the existing `gh release create` step (which already self-skips via `if gh release view`), so `gh skill publish` creates the release and the later step is a no-op. Most likely no problem in practice; fallback if it does reject outright is a dedicated `effect-solutions-skill@X.Y.Z` tag.
2. `**gh skill` availability on hosted runners**: shipped as a built-in subcommand of the GitHub CLI in the April 2026 GA, not as an extension. Risk is that the `macos-latest` runner image lags behind a release-day cutoff and bundles a `gh` version that predates `skill`. Mitigated by a `brew upgrade gh || true` step before publish, and a `|| true` on `gh skill publish` itself so a transient failure doesn't break the npm publish that already succeeded earlier in the job. Treat the publish step as non-blocking until verified stable across a few releases.

## References

**Agent Skills (open standard)**

- Spec home: [https://agentskills.io/](https://agentskills.io/)
- Open spec discussion (manifest/dependencies): [https://github.com/agentskills/agentskills/issues/210](https://github.com/agentskills/agentskills/issues/210)
- Claude's implementation doc: [https://code.claude.com/docs/en/skills](https://code.claude.com/docs/en/skills)
- Reference monorepo (multi-skill, single tag): [https://github.com/anthropics/skills](https://github.com/anthropics/skills)

**skills.sh**

- Docs: [https://skills.sh/docs](https://skills.sh/docs)
- CLI: [https://skills.sh/docs/cli](https://skills.sh/docs/cli)
- API: [https://skills.sh/docs/api](https://skills.sh/docs/api)
- FAQ: [https://skills.sh/docs/faq](https://skills.sh/docs/faq)
- Official directory: [https://skills.sh/official](https://skills.sh/official)

**gh skill**

- Launch announcement: [https://github.blog/changelog/2026-04-16-manage-agent-skills-with-github-cli/](https://github.blog/changelog/2026-04-16-manage-agent-skills-with-github-cli/)
- Manual: [https://cli.github.com/manual/gh_skill](https://cli.github.com/manual/gh_skill)
- Publish: [https://cli.github.com/manual/gh_skill_publish](https://cli.github.com/manual/gh_skill_publish)

**Reference skill (umbrella + `rules/*.md` pattern)**

- [https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices](https://skills.sh/vercel-labs/agent-skills/vercel-react-best-practices)

**This repo (load-bearing files)**

- CLI source: `packages/cli/src/cli.ts`
- Existing manifest generator (template): `packages/cli/scripts/generate-manifest.ts`
- Website doc loader: `packages/website/src/lib/mdx.ts:6`
- Doc typecheck: `scripts/typecheck-docs.ts:12`
- Release workflow: `.github/workflows/release.yml`
- LLM setup instructions (description copy reference): `packages/website/src/lib/llm-instructions.ts`

Contributor guide

Open the contributing guide

Research direction

Start with packages/website/src/lib/mdx.ts and packages/cli/scripts/generate-manifest.ts, then trace the documented path constants and release workflow changes. Implement the skills/effect-solutions/topics move, scripts/generate-skill.ts, workflow wiring, and tests/skill-generation.test.ts. Run the listed Bun checks and gh skill publish --dry-run; done means the acceptance criteria pass and generated SKILL.md is idempotent.

Written by the indexing model from the issue text.

Assessment

Tech stack
bun, github, github-actions, typescript
Domain
ci-cd, documentation, tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.