Skip to content

generate integrations reference from catalog#2563

Open
DyanGalih wants to merge 33 commits into
github:mainfrom
DyanGalih:002-generate-integrations-docs
Open

generate integrations reference from catalog#2563
DyanGalih wants to merge 33 commits into
github:mainfrom
DyanGalih:002-generate-integrations-docs

Conversation

@DyanGalih
Copy link
Copy Markdown
Contributor

@DyanGalih DyanGalih commented May 14, 2026

What changed

  • Added a small generator for the integrations reference table, backed by INTEGRATION_REGISTRY plus per-key URL and notes maps in catalog_docs.py.
  • Updated docs/reference/integrations.md so the supported agent table is generated from the integration registry (not hand-maintained).
  • Added a regression test that checks the committed doc stays in sync with the rendered table output.

Why

The integrations reference had been hand-maintained, which made it easy for the docs to drift from the runtime registry. This change makes the doc a checked artifact and reduces maintenance overhead.

User impact

  • Maintainers can update the integration registry and regenerate the docs with specify integration search --markdown.
  • Readers get a more reliable integrations reference with less chance of stale entries.

Validation

  • specify integration search --markdown
  • pytest tests/test_catalog_docs.py -q

Copilot AI review requested due to automatic review settings May 14, 2026 16:35
@DyanGalih DyanGalih marked this pull request as ready for review May 14, 2026 16:36
@DyanGalih DyanGalih requested a review from mnriem as a code owner May 14, 2026 16:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adds catalog-backed generation and validation for the integrations reference documentation, along with a CLI script and tests to keep the generated markdown in sync.

Changes:

  • Introduce specify_cli.catalog_docs helpers to render/update the generated integrations table from integrations/catalog.json.
  • Add scripts/generate_integrations_reference.py with --check/--write modes for CI and local updates.
  • Regenerate docs/reference/integrations.md with generated-table markers and add tests to enforce consistency.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

File Description
tests/test_catalog_docs.py Adds tests asserting the committed docs match the generator and that registry metadata is reflected.
src/specify_cli/catalog_docs.py Implements catalog loading, table rendering, and marker-based replacement for the docs page.
scripts/generate_integrations_reference.py Provides a CLI entrypoint to check or rewrite the generated integrations reference file.
docs/reference/integrations.md Converts the integrations table into a generated block and updates surrounding instructions.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread scripts/generate_integrations_reference.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
@DyanGalih DyanGalih changed the title [codex] generate integrations reference from catalog generate integrations reference from catalog May 14, 2026
@DyanGalih DyanGalih requested a review from Copilot May 14, 2026 16:53
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

Comment thread tests/test_catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py
Comment thread tests/test_catalog_docs.py Outdated
Copy link
Copy Markdown
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love it, but can we integrate it into specify integration search --markdown? Specifically can we keep it simpler than this and just render out the table. On the project end we will take care of integrating it into the docs. I do not want to burden the CLI with that part of the process

@DyanGalih
Copy link
Copy Markdown
Contributor Author

Thanks for the direction! I've refactored based on your feedback:

What changed:

  • Removed scripts/generate_integrations_reference.py (standalone script gone)
  • Stripped all doc-injection machinery from catalog_docs.py — it now only contains the table-rendering helpers (list_integrations_for_docs, render_integrations_table)
  • Wired render_integrations_table() into the existing specify integration search --markdown flag — it now outputs the rich Agent/Key/Notes table (with proper column alignment and pipe/CR escaping) instead of the old simple Name/ID/Version/Description/Author table
  • Removed the HTML marker comments from docs/reference/integrations.md and updated the note to reference specify integration search --markdown
  • Simplified tests/test_catalog_docs.py to just verify table rendering and registry metadata — no subprocess or doc-path tests

Usage:

specify integration search --markdown

Prints the full integrations reference table to stdout. The docs team can paste it wherever needed.

@DyanGalih DyanGalih requested review from Copilot and mnriem May 14, 2026 22:49
@DyanGalih DyanGalih force-pushed the 002-generate-integrations-docs branch from 1bdc359 to 59c134c Compare May 14, 2026 23:07
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated 5 comments.

Comment thread src/specify_cli/__init__.py Outdated
Comment thread docs/reference/integrations.md Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 4/4 changed files
  • Comments generated: 3

Comment thread tests/test_catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py
Comment thread docs/reference/integrations.md Outdated
Copy link
Copy Markdown
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback. Please revert the change to integrations.md as we will setup a separate GitHub actions job for that. Thanks for the great work

Copilot AI review requested due to automatic review settings May 15, 2026 13:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread src/specify_cli/catalog_docs.py
Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
@DyanGalih
Copy link
Copy Markdown
Contributor Author

Done! Both tasks completed:\n\n- Copilot feedback addressed — all review threads from the latest rounds have been fixed and replied to (removed dead INTEGRATIONS_REFERENCE_PATH constant, dropped URL-length padding from table renderer, updated integration_search docstring to reflect dual behavior, removed unreachable FileNotFoundError from the except clause, fixed naming from "catalog-backed" to registry-backed, updated PR description validation steps).\n- docs/reference/integrations.md reverted — restored to the upstream/main state and the in-process sync test was removed from the pytest suite (the GH Actions job will handle that check separately)."

@DyanGalih DyanGalih requested a review from Copilot May 15, 2026 14:08
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread src/specify_cli/__init__.py Outdated
Comment thread src/specify_cli/__init__.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
@DyanGalih DyanGalih requested a review from Copilot May 15, 2026 14:55
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread src/specify_cli/catalog_docs.py
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
@DyanGalih DyanGalih requested a review from Copilot May 15, 2026 18:11
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread tests/test_catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
Copilot AI review requested due to automatic review settings May 15, 2026 18:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 4 comments.

Comment thread tests/test_catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread tests/test_catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
@DyanGalih DyanGalih requested a review from Copilot May 15, 2026 20:00
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

Comment thread tests/test_catalog_docs.py Outdated
Comment thread src/specify_cli/catalog_docs.py Outdated
Comment thread src/specify_cli/__init__.py
…r message, validate test rows, prevent double newline
Copy link
Copy Markdown
Contributor Author

@DyanGalih DyanGalih left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed all 4 feedback items in latest commit (60fc926): (1) Made escape function public to avoid private API coupling, (2) Updated error message to be CLI-agnostic, (3) Test now fails on malformed rows with error message, (4) Added nl=False to prevent double newline in CLI output.

from pathlib import Path
from typing import Any

from .catalog_docs import escape_url_for_markdown_link, render_cell
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Renamed _escape_url_for_markdown_link() to escape_url_for_markdown_link() (now public) in catalog_docs.py, and updated the import in community_catalog_docs.py to use the public name. This removes the "private API" coupling concern.

if not path.exists():
raise FileNotFoundError(
f"Community catalog not found at {path}. "
"Ensure the repository checkout includes the extensions/ directory."
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Updated the error message to describe the actual requirement instead of referencing the CLI flag: "Community catalog not found at {path}. Ensure the repository checkout includes the extensions/ directory." This is now accurate for both CLI and programmatic usage.

Comment thread tests/test_catalog_docs.py Outdated

# Validate we have 3 columns
assert (
len(parts) == 3
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Verified - all test assertions in test_catalog_docs.py are now comprehensive. The parser validation is explicit with error messages when rows don't match expected column count.

)
from .catalog_docs import render_integrations_table
try:
typer.echo(render_integrations_table(), nl=False)
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed. Updated the typer.echo() call to use nl=False, which prevents adding an extra newline since render_integrations_table() already returns a string with a trailing newline. This keeps the CLI output clean and consistent.

@DyanGalih DyanGalih requested a review from Copilot May 18, 2026 21:36
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment on lines +10 to +15
from specify_cli.catalog_docs import (
_escape_url_for_markdown_link,
render_cell,
list_integrations_for_docs,
render_integrations_table,
)
Comment thread tests/test_community_catalog_docs.py Outdated
# ---------------------------------------------------------------------------

def test_missing_catalog_file(tmp_path: Path) -> None:
with pytest.raises(FileNotFoundError, match="spec-kit source checkout"):
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed the assertion to match the current FileNotFoundError message: Ensure the repository checkout includes the extensions/ directory.

Comment thread tests/test_catalog_docs.py Outdated
}
fake_label_overrides = {}
fake_notes = {"copilot": "Test note"}

Comment thread tests/test_catalog_docs.py Outdated

# Validate we have 3 columns
assert (
len(parts) == 3
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Verified - all test assertions in test_catalog_docs.py are now comprehensive. The parser validation is explicit with error messages when rows don't match expected column count.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment thread tests/test_catalog_docs.py Outdated
Comment on lines +214 to +235
def split_markdown_table_row(line: str) -> list[str]:
parts = []
current = ""
backslash_run = 0
for char in line:
if char == "\\":
backslash_run += 1
current += char
continue
if char == "|" and backslash_run % 2 == 0:
parts.append(current.strip())
current = ""
else:
current += char
backslash_run = 0
parts.append(current.strip())
if parts and parts[0] == "":
parts = parts[1:]
if parts and parts[-1] == "":
parts = parts[:-1]
return parts

Comment thread tests/test_catalog_docs.py Outdated
Comment on lines +188 to +190
import pytest
from pathlib import Path

Comment thread tests/test_catalog_docs.py Outdated
Comment on lines +313 to +323
# Assert they are in perfect sync
diff_missing = generated_rows - committed_rows
diff_extra = committed_rows - generated_rows

error_msg = (
"The committed integrations.md table is out of sync with the registry.\n"
f"Missing from docs: {diff_missing}\n"
f"Extra in docs: {diff_extra}\n"
"To update the docs table, run: specify integration search --markdown"
)
assert not diff_missing and not diff_extra, error_msg
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 5/5 changed files
  • Comments generated: 3

Comment on lines +1 to +16
"""Tests for the integration registry documentation generation."""

from __future__ import annotations

from contextlib import ExitStack, contextmanager
from unittest.mock import MagicMock, patch

from typer.testing import CliRunner

from specify_cli.catalog_docs import (
escape_url_for_markdown_link,
render_cell,
list_integrations_for_docs,
render_integrations_table,
)
from specify_cli import app
Comment on lines +94 to +123
# Create a minimal fake registry with two known integrations
fake_registry = {
"copilot": MagicMock(config={"name": "GitHub Copilot"}),
"codex": MagicMock(config={"name": "Codex CLI"}),
}

# Mock the doc maps to only contain entries for the fake registry
fake_doc_urls = {
"copilot": "https://code.visualstudio.com/",
"codex": "https://github.com/openai/codex",
}
fake_label_overrides = {}
fake_notes = {}

patch_registry = patch(
"specify_cli.catalog_docs._get_integration_registry",
return_value=fake_registry,
)
patch_urls = patch(
"specify_cli.catalog_docs.INTEGRATION_DOC_URLS", fake_doc_urls
)
patch_labels = patch(
"specify_cli.catalog_docs.INTEGRATION_LABEL_OVERRIDES",
fake_label_overrides,
)
patch_notes = patch(
"specify_cli.catalog_docs.INTEGRATION_NOTES", fake_notes
)

with patch_registry, patch_urls, patch_labels, patch_notes:
Comment thread src/specify_cli/catalog_docs.py Outdated

def escape_url_for_markdown_link(url: str) -> str:
"""Escape characters that can break Markdown link syntax.

Copy link
Copy Markdown
Collaborator

@mnriem mnriem left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please address Copilot feedback

@DyanGalih DyanGalih requested a review from Copilot May 22, 2026 07:01
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Comment thread src/specify_cli/community_catalog_docs.py Outdated
Comment on lines +74 to +78
link = (
f"[{safe_name}]({safe_repo})"
if row["repository"]
else safe_name
)
Comment on lines +185 to +192
safe_label = render_cell(label)
safe_notes = render_cell(notes)
safe_url = escape_url_for_markdown_link(url) if url else None
agent = (
f"[{safe_label}]({safe_url})"
if safe_url
else safe_label
)
Comment thread src/specify_cli/catalog_docs.py Outdated

def escape_url_for_markdown_link(url: str) -> str:
"""Escape characters that can break Markdown link syntax.

from .catalog_docs import render_integrations_table
try:
typer.echo(render_integrations_table(), nl=False)
except Exception as exc:
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 22, 2026 07:05
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Comment on lines +67 to +74
def test_integrations_reference_doc_matches_renderer():
doc_text = INTEGRATIONS_REFERENCE_PATH.read_text(encoding="utf-8")
start_marker = "## Supported AI Coding Agents\n\n"
end_marker = "\n## List Available Integrations\n"
start = doc_text.index(start_marker) + len(start_marker)
end = doc_text.index(end_marker)
committed_table = doc_text[start:end].rstrip("\n")
rendered_table = render_integrations_table().rstrip("\n")
Comment thread src/specify_cli/__init__.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 22, 2026 07:16
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.

Comment on lines +153 to +162
stale_keys = sorted(
set(extra_in_urls + extra_in_labels + extra_in_notes)
)
warnings.warn(
f"Stale key(s) found in doc maps (no longer in registry): "
f"{stale_keys}. Consider removing them from "
"INTEGRATION_DOC_URLS, INTEGRATION_LABEL_OVERRIDES, and "
"INTEGRATION_NOTES.",
stacklevel=2
)
Comment on lines +9 to +10
ROOT_DIR = Path(__file__).resolve().parents[2]
INTEGRATIONS_REFERENCE_PATH = ROOT_DIR / "docs" / "reference" / "integrations.md"
Comment thread src/specify_cli/catalog_docs.py Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 22, 2026 07:26
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Comment thread src/specify_cli/community_catalog_docs.py Outdated
Comment on lines +67 to +74
def test_integrations_reference_doc_matches_renderer():
doc_text = INTEGRATIONS_REFERENCE_PATH.read_text(encoding="utf-8")
start_marker = "## Supported AI Coding Agents\n\n"
end_marker = "\n## List Available Integrations\n"
start = doc_text.index(start_marker) + len(start_marker)
end = doc_text.index(end_marker)
committed_table = doc_text[start:end].rstrip("\n")
rendered_table = render_integrations_table().rstrip("\n")
Comment on lines +122 to +153
def test_integrations_docs_label_and_url_sources():
"""Test using mocked registry/doc maps to avoid test brittleness."""
# Create a minimal fake registry with two known integrations
fake_registry = {
"copilot": MagicMock(config={"name": "GitHub Copilot"}),
"codex": MagicMock(config={"name": "Codex CLI"}),
}

# Mock the doc maps to only contain entries for the fake registry
fake_doc_urls = {
"copilot": "https://code.visualstudio.com/",
"codex": "https://github.com/openai/codex",
}
fake_label_overrides = {}
fake_notes = {}

patch_registry = patch(
"specify_cli.catalog_docs._get_integration_registry",
return_value=fake_registry,
)
patch_urls = patch(
"specify_cli.catalog_docs.INTEGRATION_DOC_URLS", fake_doc_urls
)
patch_labels = patch(
"specify_cli.catalog_docs.INTEGRATION_LABEL_OVERRIDES",
fake_label_overrides,
)
patch_notes = patch(
"specify_cli.catalog_docs.INTEGRATION_NOTES", fake_notes
)

with patch_registry, patch_urls, patch_labels, patch_notes:
),
):
"""Search for integrations in the active catalog stack."""
"""Search for integrations in the active catalog stack, or output the built-in reference table with --markdown."""
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 22, 2026 07:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Comment on lines +68 to +73
doc_text = INTEGRATIONS_REFERENCE_PATH.read_text(encoding="utf-8")
start_marker = "## Supported AI Coding Agents\n\n"
end_marker = "\n## List Available Integrations\n"
start = doc_text.index(start_marker) + len(start_marker)
end = doc_text.index(end_marker)
committed_table = doc_text[start:end].rstrip("\n")
Comment on lines +76 to +86
def parse_table(table: str) -> list[list[str]]:
rows: list[list[str]] = []
for line in table.splitlines():
if not line.startswith("| "):
continue
parts = [part.strip() for part in line.strip("|").split("|")]
if parts and set(parts[0]) == {"-"}:
continue
if len(parts) == 3:
rows.append(parts)
return rows
"--markdown",
help=(
"Output the full built-in integrations table as markdown "
"(ignores filters)"
Comment on lines +80 to +85
safe_repo = escape_url_for_markdown_link(row["repository"])
link = (
f"[{safe_name}]({safe_repo})"
if row["repository"]
else safe_name
)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants