diff --git a/.github/ISSUE_TEMPLATE/preset_submission.yml b/.github/ISSUE_TEMPLATE/preset_submission.yml index 19244e6651..02c72c9eea 100644 --- a/.github/ISSUE_TEMPLATE/preset_submission.yml +++ b/.github/ISSUE_TEMPLATE/preset_submission.yml @@ -77,6 +77,18 @@ body: validations: required: true + - type: input + id: documentation + attributes: + label: Documentation URL + description: | + Link to the README that explains how to use **this preset** (not a general product/framework pitch). + Prefer the preset-scoped README (e.g. `presets//README.md` in a monorepo) over the repository root README. + It must contain at least one valid `specify preset add ...` install command (ideally `specify preset add --from `) matching the Download URL above. + placeholder: "https://github.com/your-org/spec-kit-presets/blob/main/presets/your-preset/README.md" + validations: + required: true + - type: input id: license attributes: @@ -175,7 +187,7 @@ body: options: - label: Valid `preset.yml` manifest included required: true - - label: README.md with description and usage instructions + - label: Linked README (Documentation URL) explains how to use this preset and includes a valid `specify preset add ...` command matching the download URL required: true - label: LICENSE file included required: true diff --git a/.github/workflows/add-community-preset.lock.yml b/.github/workflows/add-community-preset.lock.yml index 9aec9914f1..c4b19298f6 100644 --- a/.github/workflows/add-community-preset.lock.yml +++ b/.github/workflows/add-community-preset.lock.yml @@ -1,4 +1,4 @@ -# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"392ace500b7cb9b0aa6b020d150841de398bcbcfe54dbad729f0d860d698bde2","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} +# gh-aw-metadata: {"schema_version":"v4","frontmatter_hash":"b4ba1db5fdec754fa825cc3160879924118bc454a781eed70ef6c90beab83a95","body_hash":"3952bd62908616e275f8485b4ef1dbb1eb64b688c4c98735138deb2eb91dcd55","compiler_version":"v0.79.8","strict":true,"agent_id":"copilot","engine_versions":{"copilot":"1.0.60"}} # gh-aw-manifest: {"version":1,"secrets":["COPILOT_GITHUB_TOKEN","GH_AW_CI_TRIGGER_TOKEN","GH_AW_GITHUB_MCP_SERVER_TOKEN","GH_AW_GITHUB_TOKEN","GITHUB_TOKEN"],"actions":[{"repo":"actions/checkout","sha":"df4cb1c069e1874edd31b4311f1884172cec0e10","version":"v6.0.3"},{"repo":"actions/download-artifact","sha":"3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c","version":"v8.0.1"},{"repo":"actions/github-script","sha":"3a2844b7e9c422d3c10d287c895573f7108da1b3","version":"v9.0.0"},{"repo":"actions/setup-node","sha":"48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e","version":"v6.4.0"},{"repo":"actions/upload-artifact","sha":"043fb46d1a93c77aae656e7c1c64a875d1fc6a0a","version":"v7.0.1"},{"repo":"github/gh-aw-actions/setup","sha":"c0338fef4749d08c21f8f975fb0e37efa17dda47","version":"v0.79.8"}],"containers":[{"image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2","digest":"sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6","pinned_image":"ghcr.io/github/gh-aw-firewall/agent:0.27.2@sha256:f88e5b17b6b7a600117bc121114d6ce2155c88c983c0c939c5df884f730fa1d6"},{"image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2","digest":"sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4","pinned_image":"ghcr.io/github/gh-aw-firewall/api-proxy:0.27.2@sha256:ee39841d980878ebbb87592903b06d31a1af500c71525c9616f7e8e2a27041a4"},{"image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2","digest":"sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591","pinned_image":"ghcr.io/github/gh-aw-firewall/squid:0.27.2@sha256:2e3a717e5f19a654cd9a2263beb52012b56bcb68562ec5ae2e42f9d156b49591"},{"image":"ghcr.io/github/gh-aw-mcpg:v0.3.25","digest":"sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa","pinned_image":"ghcr.io/github/gh-aw-mcpg:v0.3.25@sha256:c10331ad17668ef89f38f5e356678788a40b0cd5fef96e8f92e1d9c1de47cbaa"},{"image":"ghcr.io/github/github-mcp-server:v1.1.2","digest":"sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c","pinned_image":"ghcr.io/github/github-mcp-server:v1.1.2@sha256:30197479d8036c7811892bc07e06f9a05c9ef3cdd79bc59f256d50647f95788c"}]} # This file was automatically generated by gh-aw (v0.79.8). DO NOT EDIT. To debug this workflow, load the skill at https://github.com/github/gh-aw/blob/main/debug.md # diff --git a/.github/workflows/add-community-preset.md b/.github/workflows/add-community-preset.md index bc2a8115e1..b46ed3895d 100644 --- a/.github/workflows/add-community-preset.md +++ b/.github/workflows/add-community-preset.md @@ -73,6 +73,7 @@ fields): | Author | `author` | Yes | | Repository URL | `repository` | Yes | | Download URL | `download-url` | Yes | +| Documentation URL | `documentation` | Yes | | License | `license` | Yes | | Required Spec Kit Version | `speckit-version` | Yes | | Required Extensions | `required-extensions` | No | @@ -100,17 +101,62 @@ deciding pass/fail: ### 2c. Repository validation - Fetch the repository URL — confirm it exists and is publicly accessible - Confirm the repository contains a `preset.yml` file -- Confirm the repository contains a `README.md` file - Confirm the repository contains a `LICENSE` file -### 2d. Release and download URL validation +> The README requirement is enforced once, in **Step 2d**, against the specific file the +> `documentation` field points to — not a generic repository-root `README.md`. This avoids +> the monorepo false-positive where a root README exists but isn't the preset-usage doc. + +### 2d. Documentation README validation + +The `documentation` field must point to the README that explains **how to use this +preset** — not just any file named `README.md`, and not a product/framework pitch. + +- **Restrict the URL to GitHub before fetching.** The `documentation` value is + user-provided input. Only accept GitHub-hosted README URLs: + - `https://github.com///blob//` + - `https://github.com///raw//` + - `https://raw.githubusercontent.com////` + + If the URL points anywhere else (or isn't a URL), **fail this check** and do not fetch it. +- Fetch the **exact URL** in the `documentation` field. First strip any fragment (`#...`) + or query string (`?...`) — these are common when copying from the browser UI and must be + ignored so the fetch target is deterministic. Then convert GitHub `blob`/`raw` web URLs to + their `raw.githubusercontent.com` equivalent before fetching, and confirm it resolves to a + readable Markdown file. +- **Validate that the README contains a valid Spec Kit CLI install command.** The fetched + README must contain at least one `specify preset add ...` invocation. The strongest + signal is the catalog-install form whose URL matches the submitted **Download URL**: + - `specify preset add --from ` (preferred), or + - `specify preset add `, or + - `specify preset add --dev ` + + A `specify preset add --from ` command only counts when its `` **matches the + submitted Download URL exactly**. A `--from` command pointing at a *different* URL does + **not** satisfy the install-command requirement (treat it as if absent) — but the README + may still pass on one of the other accepted forms (`specify preset add ` or + `specify preset add --dev `). + + If **no** accepted `specify preset add ...` command is present, the README is treated as a + generic description/pitch rather than preset-usage documentation — **fail this check** and + tell the submitter to add a valid install command (ideally + `specify preset add --from `). +- **Prefer a preset-scoped README in monorepos.** If `documentation` resolves to a generic + repository-root README in a monorepo (the preset lives in a subdirectory such as + `presets//` and a preset-scoped README exists there), **flag it** in your comment and + recommend the submitter point `documentation` at the preset-scoped README + (e.g. `presets//README.md`) so the catalog surfaces usage instead of marketing. Treat + this as a flag rather than a hard failure **only if** the root README still contains a valid + `specify preset add ...` command for this preset; otherwise it fails check 2d above. + +### 2e. Release and download URL validation - The download URL should follow the pattern `https://github.com///archive/refs/tags/v.zip` or `https://github.com///releases/download//.zip` - Verify a GitHub release exists matching the submitted version -### 2e. Submission checklists +### 2f. Submission checklists - Confirm that all required checkboxes in the Testing Checklist and Submission Requirements sections are checked (`[x]`) @@ -154,7 +200,7 @@ Insert the entry in **alphabetical order by preset ID** within the "repository": "", "download_url": "", "homepage": "", - "documentation": "", + "documentation": "", "license": "", "requires": { "speckit_version": "" diff --git a/presets/PUBLISHING.md b/presets/PUBLISHING.md index 661614e5c0..a18f334051 100644 --- a/presets/PUBLISHING.md +++ b/presets/PUBLISHING.md @@ -19,7 +19,7 @@ Before publishing a preset, ensure you have: 1. **Valid Preset**: A working preset with a valid `preset.yml` manifest 2. **Git Repository**: Preset hosted on GitHub (or other public git hosting) -3. **Documentation**: README.md with description and usage instructions +3. **Documentation**: A preset-scoped README.md that explains how to use **this preset**, including a valid `specify preset add ...` install command (see [Usage README requirements](#6-usage-readme-requirements)) 4. **License**: Open source license file (MIT, Apache 2.0, etc.) 5. **Versioning**: Semantic versioning (e.g., 1.0.0) 6. **Testing**: Preset tested on real projects with `specify preset add --dev` @@ -147,6 +147,45 @@ https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0 specify preset add --from https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip ``` +### 6. Usage README Requirements + +The catalog `documentation` field must point at a README that explains how to use +**this preset** — not a product pitch for a broader framework or a separate CLI. + +The submission workflow **mechanically enforces** two of these requirements: the linked +README must resolve to a readable file, and it must contain at least one valid +`specify preset add ...` command. The remaining items (preferring a preset-scoped README, +covering the minimum structure) are expectations a human reviewer checks — follow them so +your submission isn't sent back for changes. + +- **Point `documentation` at the preset-scoped README.** In a monorepo where the preset + lives in a subdirectory (e.g. `presets//`), link the README inside that directory + (`presets//README.md`) rather than the repository-root README. The root README is + often a marketing/overview page; the catalog should surface preset usage instead. (Keep + this usage README **outside** the release ZIP — it should be discoverable *before* a user + downloads the artifact.) +- **Include a valid Spec Kit CLI install command** *(enforced)*. The linked README must + contain at least one `specify preset add ...` invocation. Preferably use the + catalog-install form whose URL matches your Download URL: + + ```bash + # is the same URL you submit as the catalog Download URL — + # either the tag archive or a release asset, e.g.: + specify preset add --from https://github.com///archive/refs/tags/vX.Y.Z.zip + specify preset add --from https://github.com///releases/download/vX.Y.Z/-X.Y.Z.zip + ``` + + `specify preset add ` and `specify preset add --dev ` are also accepted, but the + `--from ` form is the clearest signal that the README documents this exact + preset release. +- **Cover the minimum structure** so a reader can decide whether the preset fits: + - What the preset does / what it provides + - The install command using Spec Kit CLI syntax (above) + - When to use it / when not to use it + +A submission whose linked README lacks a valid `specify preset add ...` command **fails +validation** (workflow check 2d) and will not be added until corrected. + --- ## Submit to Catalog @@ -181,11 +220,13 @@ Edit `presets/catalog.community.json` and add your preset. "presets": { "your-preset": { "name": "Your Preset Name", + "id": "your-preset", "description": "Brief description of what your preset provides", "author": "Your Name", "version": "1.0.0", "download_url": "https://github.com/your-org/spec-kit-preset-your-preset/archive/refs/tags/v1.0.0.zip", "repository": "https://github.com/your-org/spec-kit-preset-your-preset", + "documentation": "https://github.com/your-org/spec-kit-preset-your-preset/blob/main/README.md", "license": "MIT", "requires": { "speckit_version": ">=0.1.0" @@ -242,7 +283,7 @@ git push origin add-your-preset ### Checklist - [ ] Valid preset.yml manifest -- [ ] README.md with description and usage +- [ ] Preset-scoped README with a valid `specify preset add ...` command, linked from `documentation` - [ ] LICENSE file included - [ ] GitHub release created - [ ] Preset tested with `specify preset add --dev` @@ -263,7 +304,13 @@ After submission, maintainers will review: 2. **Template quality** — templates are useful and well-structured 3. **Command coherence** — commands reference sections that exist in templates 4. **Security** — no malicious content, safe file operations -5. **Documentation** — clear README explaining what the preset does +5. **Documentation** — the README linked from `documentation` explains how to use *this* preset and contains a valid `specify preset add ...` command + +> **Reviewer note:** the workflow can mechanically check *structure* (the linked README +> resolves, contains a valid `specify preset add ...` snippet, and the download URL matches), +> but whether the README genuinely documents *this* preset is partly a content judgment. A +> human reviewer should still confirm the linked doc isn't just a funnel to a separate +> product or CLI before approving. Once verified, `verified: true` is set and the preset appears in `specify preset search`.