Repository navigation
Description
Goal
Extend the gh docs index command to generate a useful page for every matching repository, rendered from a user-authored Markdown template, and generate a Starlight sidebar module linking to those pages.
The feature is intended for a GitHub-backed Astro/Starlight documentation site, such as the astro section of mindovermachine-dev.github.io. The pages provide the actual repository index; the sidebar provides navigation to those pages.
Keep YAML, JSON, and HTML as single-file exports for users who need data or a simple index in other contexts. Change the existing .mjs/sidebar output mode: it no longer means one sidebar-only listing. It means a paired output consisting of a directory of generated pages and one .mjs sidebar file.
User experience
gh docs index --owner mindovermachine-dev \
--format sidebar \
--file docs/sidebar/repositories.mjs \
--pages-dir astro/src/content/docs/repositories \
--template astro/repository-template.mdIn sidebar mode, the command:
- Discovers repositories using the existing owner, visibility, topic, and limit behavior.
- Renders one Markdown page per matching repository into the configured pages directory.
- Writes the
.mjssidebar file with local links to those generated pages.
The page set and sidebar must be generated from the same collected repository data in one invocation. Missing output directories are created. Report both output locations on success.
Output modes
| Format | Output | Behavior |
|---|---|---|
yaml |
One .yml or .yaml file |
Serialize the repository list as YAML; unchanged |
json |
One .json file |
Serialize the repository list as JSON; unchanged |
html |
One .html file |
Render the existing simple HTML index; unchanged |
sidebar |
One .mjs file plus a pages directory |
Render a Markdown page per repository and a local-link Starlight sidebar module |
An output path ending in .mjs continues to infer the sidebar format. --format sidebar and an .mjs --file path both select the paired behavior. YAML, JSON, and HTML modes do not generate pages or require a template.
Keep the current precedence and mutual-exclusion rules for --file and --format unless a small change is necessary to support this behavior. Do not rename unrelated flags as part of this feature.
Page and template configuration
Add settings under the existing index: configuration node and corresponding CLI flags:
pages-dir/--pages-dir: directory for generated Markdown pages; defaultdocs/content/repo.template/--template: path to a Markdown template; insidebarmode, use the maintained Starlight starter template when omitted.--starlight: copy the maintained Starlight starter template to a file and exit without querying GitHub. Use--templateas the destination path; default torepository-template.md.
Flags override .docs.yml; .docs.yml overrides built-in defaults. Relative paths are resolved from the working directory. These settings are meaningful only in sidebar mode; reject or clearly ignore them in other formats, consistently with existing validation conventions.
Example configuration:
index:
owner: mindovermachine-dev
visibility: public
topic: documentation
format: sidebar
file: docs/sidebar/repositories.mjs
pages-dir: astro/src/content/docs/repositories
template: astro/repository-template.md
limit: 1000The user-authored template is a normal Markdown file (for example, repository-template.md) so editors retain Markdown support. Use Go text/template with custom delimiters [% and %], and do not HTML-escape the rendered Markdown. The delimiters are intentionally gh-docs-specific, not a generic template standard: Go's default {{ }} overlaps with Liquid/Jekyll syntax and can be mistaken for template syntax in other tools. Configure them with Go's Template.Delims before parsing. Provide a yaml template function for safely serializing values in YAML front matter.
Starlight starter template
Maintain the default page template as a standalone source file at internal/index/templates/starlight.md. It must demonstrate every supported keyword below using the [% ... %] delimiters. Embed or otherwise package that file with the CLI, then have --starlight copy its contents to the requested destination; do not construct the Markdown template in Go code. This keeps the example easy to edit with Markdown tooling and makes the checked-in file the single source of truth.
Example:
gh docs index --starlight --template astro/repository-template.mdIf --template is omitted with --starlight, write repository-template.md in the current directory. Do not overwrite an existing destination; return a clear error and leave it untouched. --starlight is a scaffold-only mode: it must not discover repositories or generate pages/sidebar output. The copied file can then be edited and supplied to a normal --format sidebar invocation.
Page generation
- Generate one
.mdfile per repository. - Use a stable, filesystem-safe slug based on the repository name. The owner is fixed for one invocation, so repository name is sufficient; detect collisions and fail with a useful error rather than overwriting another repository's page.
- Write each page to
<pages-dir>/<slug>.md. - For Starlight links, derive the local page target from the generated path relative to the configured content root. Document the default relationship between
docs/content/repoand its sidebar URL; allow abase-urlsetting if the filesystem layout alone cannot determine the public route. - Sort repositories before rendering and sidebar generation so repeated runs produce stable output.
- Overwrite generated pages on later runs. Do not delete pages for repositories that are no longer selected; document that stale files require manual cleanup.
Template contract
Render the template once per repository with a Repo value and the configured owner. Use explicit field names with Go template syntax wrapped in [% ... %]. The template must be able to include optional sections with if and iterate over topics.
Available fields:
| Template expression | Meaning |
|---|---|
[% .Owner %] |
GitHub owner or organization |
[% .Name %] |
Repository name |
[% .Title %] |
Display title; defaults to repository name |
[% .Description %] |
Repository description; empty if absent |
[% .Slug %] |
Generated page slug |
[% .RepoURL %] |
Canonical GitHub repository URL |
[% .HomepageURL %] |
Configured documentation/homepage URL; empty if absent |
[% .Topics %] |
Repository topics |
[% .BadgeURL %] |
Configured status badge URL; empty if not configured |
[% .DefaultBranch %] |
Default branch; empty if unavailable |
[% .LatestCommit.SHA %] |
Short SHA of latest default-branch commit; empty if unavailable |
[% .LatestCommit.Date %] |
Latest commit date in RFC 3339 format; empty if unavailable |
[% .LatestCommit.Message %] |
First line of latest commit message; empty if unavailable |
[% .LatestVersion %] |
Highest stable SemVer release/tag; empty if unavailable |
[% .Stars %] |
Star count, when collected |
[% .License %] |
SPDX license identifier; empty if unavailable |
[% .UpdatedAt %] |
Repository update timestamp in RFC 3339 format, when available |
Optional values must be safe to test with if; absent metadata must not make template execution fail. A parse or execution error must identify the template and repository. Unknown struct fields should produce a clear template error.
The maintained starter example is internal/index/templates/starlight.md. Keep that file in sync with the documented template contract; do not duplicate its body in Go code or maintain a second copy of the example in this spec.
Metadata collection
Keep the current gh repo list discovery and topic filtering. Extend the collected data only as needed for the template contract.
- Fetch commit and version details only in
sidebarmode, not for YAML, JSON, or HTML exports. - Select the highest stable SemVer tag/release by SemVer precedence, not lexical order. Ignore prereleases.
- Missing optional metadata is not an error. A per-repository lookup failure should warn and leave that field empty while allowing other pages to be generated.
- Failure to discover repositories, render a page, or write either required output is fatal.
- Keep GitHub authentication delegated to the
ghCLI. Do not accept, log, or persist credentials. - If a badge is supported, require it to be configured; do not guess a workflow filename.
Use bounded concurrency for extra GitHub API calls if needed, and retain deterministic repository ordering regardless of response order.
Sidebar module
The .mjs file must export a Starlight-compatible sidebar value with one group and one item per generated page. Use repository title/name for labels and local links to the generated pages, never external repository or homepage URLs. Use JSON-safe encoding for generated labels and links.
Keep the existing group label or make it configurable under index:. Do not rewrite site configuration. Document how the site imports the generated module and combines it with any existing sidebar groups.
Safety and output integrity
- Sanitize slugs derived from remote repository data and verify each resolved page path stays inside
pages-dir. - Do not execute arbitrary code from templates.
text/templateis limited to the data and explicitly registered template functions. - Validate the template and output paths before writing. Prepare all page and sidebar content before replacing output files so a rendering failure does not leave a partial generation.
- Create directories with appropriate permissions and write files with ordinary non-executable permissions.
Documentation
Update the command help and docs/ pages to describe:
- YAML/JSON/HTML as single-file formats and
.mjs/sidebaras the paired pages-plus-sidebar mode. --pages-dir,--template, their defaults, and.docs.ymlsettings/precedence.- The
--starlightscaffold mode, its destination behavior, and how to use the copied file for page generation. - The template keywords, optional values,
yamlhelper, and a complete Markdown page example. - The custom
[% ... %]template delimiters and their distinction from generic Astro/Liquid/Jekyll syntax. - How to import the generated sidebar module into Starlight and how page paths map to local links.
- The fact that stale generated pages are not deleted automatically.
Tests and acceptance criteria
- Preserve existing YAML, JSON, and HTML output behavior and golden files.
- Test that
.mjspath inference and--format sidebarboth select paired output generation. - Test default and configured pages directories, template selection, config/flag precedence, and invalid combinations.
- Test
--starlightcopies the checked-in template file, uses the default destination, refuses to overwrite, and exits without invokinggh. - Test custom and built-in templates with the custom delimiters, all documented keywords, optional metadata, YAML quoting, and clear template errors.
- Test stable safe slugs, collisions, deterministic output, and path traversal rejection.
- Test that the generated
.mjscontains valid Starlight entries linking to the pages written in the same run. - Test SemVer selection and that one repository's metadata failure does not prevent other pages from being generated.
- Verify YAML/JSON/HTML modes do not create pages or make extra GitHub API calls.
- Run
make test,make lint, andmake build.
Activity
lakruzz commentedon Nov 5, 2026
Follow-up requirements
gh docs indextogh docs template --starlight.template --outputoption for the destination. With the default output folderdocs, createdocs/repository-template.md.--starlightand--templatefromindex, including its config/help documentation. Sidebar indexing should use the maintained template atdocs/repository-template.md.index --format sidebarwith an embedded template. Ifdocs/repository-template.mdis missing, fail with an actionable message directing the user to create it withgh docs template --starlight --output docs..HomepageURL) and one to the GitHub repository (.RepoURL). Omit the documentation action when no homepage URL is available.Acceptance checks
gh docs template --starlightwritesdocs/repository-template.mdby default, using the checked-in starter template.template --outputlocation is honored and existing files are not overwritten without explicit force behavior.gh docs index --format sidebaruses the docs-folder template and exits with a clear scaffold instruction if it is absent; it does not silently use an embedded fallback..HomepageURL.--starlightor--template.Related to #19
lakruzz commentedon Nov 5, 2026
Commit summary:
79d4b67docs/repository-template.mdxalongsidedocs/repository-template.mdfor sidebar page generation..mdtemplate is selected first..mdor.mdx).Linting #19
Generate repository pages and Starlight sidebar from templates - reso…
github-actions commentedon Nov 5, 2026
Closed by Ready action after PR #20 was delivered to 'main'.