Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 24 additions & 5 deletions great-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,6 @@ reference:
contents:
- name: GreatDocs
members: false

- title: GreatDocs Methods
desc: >-
Methods for installing dependencies, building the documentation site, launching a live
Expand All @@ -316,7 +315,6 @@ reference:
- GreatDocs.build
- GreatDocs.preview
- GreatDocs.check_links

- title: Table Preview/Explorer
desc: >-
Generate styled HTML table previews from any tabular data source with `tbl_preview()`, and
Expand All @@ -326,10 +324,8 @@ reference:
- enable_tbl_preview
- disable_tbl_preview
- tbl_explorer

- title: Skills
desc: >-
Install, check, and list Agent Skills from the Python API.
desc: Install, check, and list Agent Skills from the Python API.
contents:
- install_skill
- check_skill
Expand All @@ -341,6 +337,29 @@ cli:
enabled: true
module: great_docs.cli
name: cli
desc: >-
The `great-docs` command-line interface drives the full documentation
workflow: from initializing a project and building the site to checking
links, recording terminal sessions, and publishing to GitHub Pages.
sections:
- title: Project setup
desc: Initialize a project, manage its configuration, and remove Great Docs.
contents: [init, config, uninstall]
- title: Building & previewing
desc: Build the documentation site, preview it locally, and freeze executable content.
contents: [build, preview, freeze]
- title: Analysis & quality
desc: Inspect the project, validate links, and check SEO, prose, and style.
contents: [scan, timings, check-links, seo, lint, proofread]
- title: API tracking
desc: Snapshot the public API and diff it across versions.
contents: [api-snapshot, api-diff]
- title: Publishing & versions
desc: Set up GitHub Pages, manage versioned builds, and generate the changelog.
contents: [setup-github-pages, versions, changelog]
- title: Command groups
desc: Grouped commands for Agent Skills and terminal session recordings.
contents: [skill, termshow]

# MCP Server Documentation
# ------------------------
Expand Down
54 changes: 54 additions & 0 deletions great_docs/_translations.py
Original file line number Diff line number Diff line change
Expand Up @@ -1600,6 +1600,60 @@
"ar": "فهرس API",
"he": "אינדקס API",
},
"cli_index": {
"en": "CLI Index",
"fr": "Index CLI",
"de": "CLI-Index",
"es": "Índice CLI",
"pt": "Índice da CLI",
"it": "Indice CLI",
"nl": "CLI-index",
"ja": "CLIインデックス",
"ko": "CLI 색인",
"zh-Hans": "CLI 索引",
"zh-Hant": "CLI 索引",
"ru": "Указатель CLI",
"pl": "Indeks CLI",
"tr": "CLI Dizini",
"sv": "CLI-index",
"da": "CLI-indeks",
"nb": "CLI-indeks",
"is": "CLI-vísir",
"fi": "CLI-hakemisto",
"cs": "CLI index",
"ro": "Index CLI",
"el": "Ευρετήριο CLI",
"hi": "CLI अनुक्रमणिका",
"ar": "فهرس CLI",
"he": "אינדקס CLI",
},
"cli_commands": {
"en": "Commands",
"fr": "Commandes",
"de": "Befehle",
"es": "Comandos",
"pt": "Comandos",
"it": "Comandi",
"nl": "Opdrachten",
"ja": "コマンド",
"ko": "명령어",
"zh-Hans": "命令",
"zh-Hant": "命令",
"ru": "Команды",
"pl": "Polecenia",
"tr": "Komutlar",
"sv": "Kommandon",
"da": "Kommandoer",
"nb": "Kommandoer",
"is": "Skipanir",
"fi": "Komennot",
"cs": "Příkazy",
"ro": "Comenzi",
"el": "Εντολές",
"hi": "कमांड",
"ar": "الأوامر",
"he": "פקודות",
},
"cli": {
"en": "CLI",
"fr": "CLI",
Expand Down
75 changes: 41 additions & 34 deletions great_docs/_versioned_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,14 +184,15 @@ def _prune_cli_pages(dest_dir: Path, snap: object) -> None:
return

# Build the set of valid command file stems from the snapshot.
# CliCommandInfo.subcommands holds the actual commands; the group itself
# maps to `index.qmd`.
# CliCommandInfo.subcommands holds the actual commands; the index page maps to `index.qmd`
# and the root command's own page maps to the entry point's safe name.
valid_stems: set[str] = {"index"}
valid_names: set[str] = set()
entry_name = getattr(cli_commands, "name", "") or ""
if entry_name:
valid_stems.add(entry_name.replace("-", "_"))
for sub in getattr(cli_commands, "subcommands", []):
# Click command names use hyphens; file stems use underscores
valid_stems.add(sub.name.replace("-", "_"))
valid_names.add(sub.name)

# Remove QMD files for commands not present at this version
for qmd_file in list(cli_ref_dir.iterdir()):
Expand All @@ -200,47 +201,53 @@ def _prune_cli_pages(dest_dir: Path, snap: object) -> None:
if qmd_file.stem not in valid_stems:
qmd_file.unlink()

# Rewrite the index.qmd to remove stale commands from the help text
# Rewrite the index.qmd to remove stale commands from the listing
index_qmd = cli_ref_dir / "index.qmd"
if index_qmd.exists() and valid_names:
_rewrite_cli_index(index_qmd, valid_names)
if index_qmd.exists():
_rewrite_cli_index(index_qmd, valid_stems)

# Prune the CLI sidebar in _quarto.yml
_prune_quarto_cli_sidebar(dest_dir, valid_stems)


def _rewrite_cli_index(index_qmd: Path, valid_names: set[str]) -> None:
"""Remove lines for non-existent commands from the CLI index help block."""
def _rewrite_cli_index(index_qmd: Path, valid_stems: set[str]) -> None:
"""Remove command entries for non-existent commands from the CLI index listing.

The index is a definition list of ``[name](href){...}`` entries. An entry is kept when the
first path component of its href (e.g. ``init`` from ``init.qmd``, or ``skill`` from
``skill/install.qmd``) is a valid command stem at this version. Dropping an entry also drops
its ``: description`` line and the trailing blank line.
"""
content = index_qmd.read_text(encoding="utf-8")
lines = content.split("\n")
new_lines: list[str] = []
in_commands_block = False
entry_re = re.compile(r"^\[[^\]]+\]\((?P<href>[^)]+)\)\{")

for line in lines:
stripped = line.strip()

# Detect the "Commands:" header in the help text
if stripped == "Commands:":
in_commands_block = True
new_lines.append(line)
continue

if in_commands_block:
# End of commands block: blank line or closing fence
if not stripped or stripped.startswith("```") or stripped.startswith(":::"):
in_commands_block = False
new_lines.append(line)
continue

# Each command line looks like " command-name Description text..."
# Extract the command name (first non-whitespace token)
tokens = stripped.split()
if tokens and tokens[0] in valid_names:
new_lines.append(line)
# Skip lines for commands not in valid_names
new_lines: list[str] = []
i = 0
n = len(lines)
while i < n:
match = entry_re.match(lines[i].strip())
if match:
stem = match.group("href").split("/")[0]
if stem.endswith(".qmd"):
stem = stem[:-4]
elif stem.endswith(".md"):
stem = stem[:-3]

if stem in valid_stems:
new_lines.append(lines[i])
i += 1
else:
# Skip the link line, its ": description" line, and one trailing blank line.
i += 1
if i < n and lines[i].lstrip().startswith(":"):
i += 1
if i < n and not lines[i].strip():
i += 1
continue

new_lines.append(line)
new_lines.append(lines[i])
i += 1

index_qmd.write_text("\n".join(new_lines), encoding="utf-8")

Expand Down
37 changes: 36 additions & 1 deletion great_docs/assets/great-docs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -1419,6 +1419,41 @@ a.sidebar-item-text.sidebar-link.text-start > span {
padding-bottom: 8px;
}

/* CLI reference: present command groups (e.g. SKILL, TERMSHOW) as small uppercase
pills so they read as command groups, distinct from leaf commands. Scoped to the
CLI reference pages via the doc-cli-reference body class. */
body.doc-cli-reference #quarto-sidebar .sidebar-item-section
> .sidebar-item-container
> .sidebar-item-text
.menu-text {
display: inline-block;
text-transform: uppercase;
font-family: $font-family-monospace;
font-size: 0.7rem;
letter-spacing: 0.04em;
line-height: 1.2;
padding: 0.1rem 0.4rem;
border-radius: 0.25rem;
border: 1px solid $label-cli-group-color;
color: $label-cli-group-color;
background-color: tint-color($label-cli-group-color, 90%);
}

/* Give each CLI command group a little breathing room above its pill. */
body.doc-cli-reference #quarto-sidebar .sidebar-item-section {
margin-top: 0.4rem;
}

/* The first command group follows a plain leaf command (no section
padding-bottom above it), so it needs extra top margin to match the gap that
later groups (e.g. TERMSHOW) get from the preceding group's padding-bottom. */
body.doc-cli-reference
#quarto-sidebar
li.sidebar-item:not(.sidebar-item-section)
+ li.sidebar-item-section {
margin-top: 0.9rem;
}

/* ==========================================================================
Floating Sidebar Navigation (desktop only)
Transforms #quarto-sidebar into a pinned, independently-scrollable panel
Expand Down Expand Up @@ -2872,7 +2907,7 @@ html.quarto-dark .gd-badge-deprecated,
margin-left: 0;
margin-right: 0.6rem;
margin-bottom: 0.2rem;
padding-top: 1.25rem;
padding-top: 0.5rem;
padding-bottom: 0.6rem;
border-bottom: 1px solid rgba(0, 0, 0, 0.06);
background: transparent;
Expand Down
36 changes: 20 additions & 16 deletions great_docs/assets/post-render.py
Original file line number Diff line number Diff line change
Expand Up @@ -3362,12 +3362,15 @@ def process_cli_reference_pages():
with open(html_file, "r", encoding="utf-8") as file:
content = file.read()

# Add 'cli-title' class to h1.title elements
# This matches the pattern: <h1 class="title">
content = content.replace('<h1 class="title">', '<h1 class="title cli-title">')
cmd_name = os.path.basename(html_file).replace(".html", "")

# Add 'cli-title' class to h1.title elements so command-page titles match the monospaced
# API object-page style. The CLI index is a listing page (like the API reference index),
# so its plain "CLI Reference" title is left unstyled to match the API index title.
if cmd_name != "index":
content = content.replace('<h1 class="title">', '<h1 class="title cli-title">')

# Replace breadcrumb with a "CLI / great-docs cmd" title bar label
cmd_name = os.path.basename(html_file).replace(".html", "")
_cli_label = _t("cli", "CLI")
_bc_pat = r'<nav class="quarto-page-breadcrumbs[^"]*"[^>]*>.*?</nav>'
if cmd_name != "index":
Expand All @@ -3382,12 +3385,12 @@ def process_cli_reference_pages():
f"</h1>"
)
else:
# CLI index: show "CLI / great-docs"
# CLI index: show "CLI / Index" to mirror the API reference index ("API / Index").
_cli_title_html = (
f'<h1 class="quarto-secondary-nav-title no-breadcrumbs gd-ref-title">'
f'<span class="gd-ref-title-prefix">{_cli_label}</span>'
f'<span class="gd-ref-title-sep">/</span>'
f'<span class="gd-ref-title-name">great-docs</span>'
f'<span class="gd-ref-title-name">Index</span>'
f"</h1>"
)
content = re.sub(_bc_pat, _cli_title_html, content, flags=re.DOTALL)
Expand Down Expand Up @@ -3715,6 +3718,7 @@ def fix_script_paths():
for _idx_label, _idx_path in [
("homepage", os.path.join("_site", "index.html")),
("reference index", os.path.join("_site", "reference", "index.html")),
("CLI reference index", os.path.join("_site", "reference", "cli", "index.html")),
]:
if os.path.isfile(_idx_path):
with open(_idx_path, "r", encoding="utf-8") as f:
Expand Down Expand Up @@ -3764,20 +3768,22 @@ def inject_sidebar_body_classes():

def style_api_index_sidebar_item():
"""
Apply inline styles to the 'API Index' sidebar link so it visually separates from the monospace
reference entries.
Apply inline styles to the 'API Index' / 'CLI Index' sidebar links so they visually separate
from the monospace reference entries.

Targets the `<a>` whose href ends with 'reference/index.html' and its parent
`<div class="sidebar-item-container">`.
Targets the `<a>` whose href ends with 'reference/index.html' (API index) or
'reference/cli/index.html' (CLI index) and its parent `<div class="sidebar-item-container">`.
"""
import re

print("Styling API Index sidebar item...")
print("Styling reference index sidebar items...")
count = 0
font = (
"&quot;Open Sans&quot;, -apple-system, BlinkMacSystemFont, "
"&quot;Segoe UI&quot;, Roboto, &quot;Helvetica Neue&quot;, Arial, sans-serif"
)
# Match the API index link, or the CLI index link (the extra '/cli' segment is optional).
href_pat = r'href="[^"]*reference/(?:cli/)?index\.html"'

for html_file in all_html_files:
rel_path = os.path.relpath(html_file, "_site")
Expand All @@ -3787,11 +3793,9 @@ def style_api_index_sidebar_item():
with open(html_file, "r", encoding="utf-8") as f:
content = f.read()

# Find the sidebar-item-container div immediately followed by the
# API Index link (href ending in reference/index.html)
# Find the sidebar-item-container div immediately followed by the index link.
match = re.search(
r'(<div class="sidebar-item-container")(>\s*'
r'<a )([^>]*href="[^"]*reference/index\.html"[^>]*>)',
r'(<div class="sidebar-item-container")(>\s*<a )([^>]*' + href_pat + r"[^>]*>)",
content,
)
if not match:
Expand All @@ -3818,7 +3822,7 @@ def style_api_index_sidebar_item():
f.write(new_content)
count += 1

print(f"Styled API Index sidebar item in {count} reference HTML files")
print(f"Styled reference index sidebar items in {count} reference HTML files")


style_api_index_sidebar_item()
Expand Down
Loading
Loading