diff --git a/great-docs.yml b/great-docs.yml index c6b49382..cb7f761b 100644 --- a/great-docs.yml +++ b/great-docs.yml @@ -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 @@ -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 @@ -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 @@ -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 # ------------------------ diff --git a/great_docs/_translations.py b/great_docs/_translations.py index 24568c70..e7007ce3 100644 --- a/great_docs/_translations.py +++ b/great_docs/_translations.py @@ -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", diff --git a/great_docs/_versioned_build.py b/great_docs/_versioned_build.py index c4606df9..2f102cfc 100644 --- a/great_docs/_versioned_build.py +++ b/great_docs/_versioned_build.py @@ -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()): @@ -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[^)]+)\)\{") - 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") diff --git a/great_docs/assets/great-docs.scss b/great_docs/assets/great-docs.scss index 880641be..ee726c76 100644 --- a/great_docs/assets/great-docs.scss +++ b/great_docs/assets/great-docs.scss @@ -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 @@ -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; diff --git a/great_docs/assets/post-render.py b/great_docs/assets/post-render.py index 865569af..009c072c 100644 --- a/great_docs/assets/post-render.py +++ b/great_docs/assets/post-render.py @@ -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:

- content = content.replace('

', '

') + 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('

', '

') # 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'' if cmd_name != "index": @@ -3382,12 +3385,12 @@ def process_cli_reference_pages(): f"

" ) else: - # CLI index: show "CLI / great-docs" + # CLI index: show "CLI / Index" to mirror the API reference index ("API / Index"). _cli_title_html = ( f'

' f'{_cli_label}' f'/' - f'great-docs' + f'Index' f"

" ) content = re.sub(_bc_pat, _cli_title_html, content, flags=re.DOTALL) @@ -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: @@ -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 `` whose href ends with 'reference/index.html' and its parent - `