Skip to content

Commit 5049dfd

Browse files
authored
Merge branch 'main' into automation/sync-agent-api-spec
2 parents d4882e8 + 385e81d commit 5049dfd

126 files changed

Lines changed: 535 additions & 222 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/style_lint/SKILL.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
22
name: style_lint
3-
description: Scan Warp Astro Starlight documentation for style guide violations including formatting issues (Settings path format, UI element format, header case, missing frontmatter, image alt text, standardized screenshot widths, callout syntax) and terminology issues (product name casing, Oz terms to avoid, deprecated terms). Run with --changed for PR workflows or --all for periodic audits. Optionally auto-fix high-confidence issues with --fix.
3+
description: Scan Warp Astro Starlight documentation for style guide violations including formatting issues (Settings path format, UI element format, link quality, VideoEmbed titles, header case, missing frontmatter, image alt text, standardized screenshot widths, callout syntax) and terminology issues (product name casing, Oz terms to avoid, deprecated terms). Run with --changed for PR workflows or --all for periodic audits. Optionally auto-fix high-confidence issues with --fix.
44
---
55

66
# Style Lint
@@ -45,6 +45,9 @@ python3 .agents/skills/style_lint/style_lint.py --all --fix --create-pr
4545
- **UI elements after action verbs**: `click \`X\`` → should be `click **X**`
4646
- **Header case**: Title Case in H2/H3/H4 headers (should be sentence case, with exceptions for proper feature names)
4747
- **Missing frontmatter**: Pages without YAML `description` field
48+
- **Link anchor quality**: Empty anchors, raw URL anchors, and generic anchors like "here," "this page," "learn more," or "read more"
49+
- **Link context quality**: Redundant bold prefixes that repeat the link text, and named Oz web app page links missing articles like "the"
50+
- **VideoEmbed titles**: Missing `title` props and generic or numbered titles like "Video," "GitHub Actions video," or "Codebase Context video 1"
4851
- **Image alt text**: `<img>` or `<figure>` without alt text or with generic alt text ("screenshot", "image")
4952
- **Screenshot widths**: Likely UI/product screenshots must use `<figure style={{ maxWidth: "..." }}>` with a standard width (`300px`, `350px`, `375px`, or `563px`)
5053
- **Callout syntax**: Leftover GitBook `{% hint %}` tags that should be migrated to Starlight `:::note` / `:::caution` / `:::danger` asides
@@ -62,7 +65,7 @@ python3 .agents/skills/style_lint/style_lint.py --all --fix --create-pr
6265

6366
When run with `--fix`:
6467
- **High-confidence fixes applied automatically**: Settings path format, UI element format, product name casing, external product name casing
65-
- **Low-confidence issues reported but not auto-fixed**: list format, header case (due to feature name exceptions), ambiguous terminology
68+
- **Low-confidence issues reported but not auto-fixed**: link quality, VideoEmbed title specificity, list format, header case (due to feature name exceptions), ambiguous terminology
6669

6770
## Relationship to validate_ui_refs
6871

.agents/skills/style_lint/style_lint.py

Lines changed: 210 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,40 @@
119119
"use-cases",
120120
)
121121

122+
GENERIC_LINK_ANCHORS = {
123+
"click here",
124+
"documentation",
125+
"docs",
126+
"here",
127+
"learn more",
128+
"link",
129+
"more",
130+
"page",
131+
"read more",
132+
"this article",
133+
"this documentation",
134+
"this guide",
135+
"this link",
136+
"this page",
137+
"website",
138+
}
139+
140+
GENERIC_VIDEO_TITLES = {
141+
"demo",
142+
"demo video",
143+
"overview video",
144+
"tutorial video",
145+
"video",
146+
"walkthrough video",
147+
}
148+
149+
RAW_URL_ANCHOR = re.compile(
150+
r"^(?:https?://|www\.|(?:[a-z0-9-]+\.)+(?:ai|app|co|com|dev|edu|gov|io|net|org)(?:/|$))",
151+
re.IGNORECASE,
152+
)
153+
MARKDOWN_LINK = re.compile(r"\[([^\]]*)\]\(([^)]+)\)")
154+
VIDEO_EMBED_TITLE = re.compile(r"\btitle\s*=\s*([\"'])(.*?)\1", re.DOTALL)
155+
122156
# Common bolded words that are NOT product terms (false positive suppression)
123157
COMMON_BOLD_WORDS = {
124158
# General emphasis words
@@ -194,7 +228,7 @@ def find_changed_md_files() -> List[Path]:
194228
for line in result.stdout.strip().split("\n"):
195229
if (line.endswith(".md") or line.endswith(".mdx")) and os.path.exists(line):
196230
p = Path(line)
197-
if not any(part in EXCLUDED_DIRS for part in p.parts):
231+
if not any(part in EXCLUDED_DIRS for part in p.parts) and not p.is_relative_to(CHANGELOG_DIR):
198232
files.append(p)
199233
return sorted(files)
200234
except subprocess.CalledProcessError:
@@ -222,6 +256,104 @@ def check_frontmatter(content: str, filepath: str) -> List[Issue]:
222256
return issues
223257

224258

259+
def _strip_markdown_formatting(text: str) -> str:
260+
"""Remove lightweight Markdown/HTML formatting from link text."""
261+
text = re.sub(r"`([^`]*)`", r"\1", text)
262+
text = re.sub(r"\*\*([^*]*)\*\*", r"\1", text)
263+
text = re.sub(r"__([^_]*)__", r"\1", text)
264+
text = re.sub(r"\*([^*]*)\*", r"\1", text)
265+
text = re.sub(r"_([^_]*)_", r"\1", text)
266+
text = re.sub(r"<[^>]+>", "", text)
267+
return text
268+
269+
270+
def _normalize_link_text(text: str) -> str:
271+
"""Normalize link text for generic-anchor comparisons."""
272+
text = _strip_markdown_formatting(text)
273+
text = re.sub(r"\s+", " ", text).strip().lower()
274+
return text.strip(" \t\n\r.,:;!?()[]{}\"'")
275+
276+
277+
def _meaningful_words(text: str) -> List[str]:
278+
"""Return lowercase words that carry semantic meaning for comparisons."""
279+
stopwords = {"a", "an", "and", "for", "in", "of", "on", "the", "to", "with", "x"}
280+
words = re.findall(r"[a-z0-9]+", text.lower())
281+
return [word for word in words if len(word) > 2 and word not in stopwords]
282+
283+
284+
def check_link_quality(lines: List[str], filepath: str) -> List[Issue]:
285+
"""Check Markdown links for descriptive, contextual anchor text."""
286+
issues = []
287+
in_code_block = False
288+
article_pattern = re.compile(
289+
r"\b(go to|open|visit|navigate to|view)\s+\[((?:Runs|Integrations|Schedules|Environments|Secrets) page in the Oz web app)\]",
290+
re.IGNORECASE,
291+
)
292+
redundant_prefix = re.compile(
293+
r"^\s*[-*]\s+\*\*([^*]+)\*\*\s*[:—-]\s*\[([^\]]+)\]\([^)]+\)\.?\s*$"
294+
)
295+
296+
for i, line in enumerate(lines, 1):
297+
stripped = line.strip()
298+
if stripped.startswith("```"):
299+
in_code_block = not in_code_block
300+
continue
301+
if in_code_block:
302+
continue
303+
304+
for m in MARKDOWN_LINK.finditer(line):
305+
# Skip Markdown images.
306+
if m.start() > 0 and line[m.start() - 1] == "!":
307+
continue
308+
309+
anchor = m.group(1).strip()
310+
normalized = _normalize_link_text(anchor)
311+
if not normalized:
312+
issues.append(Issue(
313+
filepath, i, "link-anchor",
314+
"Markdown link has empty anchor text; use descriptive link text that explains the destination",
315+
"error",
316+
))
317+
continue
318+
319+
if RAW_URL_ANCHOR.match(normalized):
320+
issues.append(Issue(
321+
filepath, i, "link-anchor",
322+
f"Raw URL used as link text: \"{anchor}\". Name the destination instead.",
323+
"warning",
324+
))
325+
elif normalized in GENERIC_LINK_ANCHORS:
326+
issues.append(Issue(
327+
filepath, i, "link-anchor",
328+
f"Generic link text: \"{anchor}\". Use descriptive anchor text that explains what users will find.",
329+
"warning",
330+
))
331+
332+
for m in article_pattern.finditer(line):
333+
action = m.group(1)
334+
page_name = m.group(2)
335+
issues.append(Issue(
336+
filepath, i, "link-context",
337+
f"Add \"the\" before named destination page: \"{action} the [{page_name}]\"",
338+
"warning",
339+
))
340+
341+
m = redundant_prefix.match(line)
342+
if m:
343+
prefix = _normalize_link_text(m.group(1))
344+
anchor = _normalize_link_text(m.group(2))
345+
prefix_words = _meaningful_words(prefix)
346+
anchor_words = set(_meaningful_words(anchor))
347+
if prefix_words and all(word in anchor_words for word in prefix_words):
348+
issues.append(Issue(
349+
filepath, i, "link-context",
350+
f"Link text repeats the bold prefix \"{m.group(1)}\". Remove the prefix or add distinct context.",
351+
"warning",
352+
))
353+
354+
return issues
355+
356+
225357
def check_settings_paths(lines: List[str], filepath: str) -> List[Issue]:
226358
"""Detect backtick-wrapped Settings paths that should be bold per-segment."""
227359
issues = []
@@ -438,6 +570,81 @@ def check_screenshot_widths(lines: List[str], filepath: str) -> List[Issue]:
438570
return issues
439571

440572

573+
def _iter_video_embed_tags(lines: List[str]) -> List[Tuple[int, str]]:
574+
"""Return (line_number, tag_text) for VideoEmbed components."""
575+
tags: List[Tuple[int, str]] = []
576+
in_code_block = False
577+
collecting = False
578+
start_line = 0
579+
tag_parts: List[str] = []
580+
581+
for i, line in enumerate(lines, 1):
582+
stripped = line.strip()
583+
if stripped.startswith("```"):
584+
in_code_block = not in_code_block
585+
continue
586+
if in_code_block:
587+
continue
588+
589+
if not collecting and "<VideoEmbed" in line:
590+
collecting = True
591+
start_line = i
592+
tag_parts = [line]
593+
if ">" in line:
594+
tags.append((start_line, "\n".join(tag_parts)))
595+
collecting = False
596+
tag_parts = []
597+
continue
598+
599+
if collecting:
600+
tag_parts.append(line)
601+
if ">" in line:
602+
tags.append((start_line, "\n".join(tag_parts)))
603+
collecting = False
604+
tag_parts = []
605+
606+
if collecting and tag_parts:
607+
tags.append((start_line, "\n".join(tag_parts)))
608+
return tags
609+
610+
611+
def _is_generic_video_title(title: str) -> bool:
612+
"""Return True when a VideoEmbed title is too generic for SEO/accessibility."""
613+
normalized = re.sub(r"\s+", " ", title).strip().lower()
614+
normalized = normalized.strip(" \t\n\r.,:;!?()[]{}\"'")
615+
if normalized in GENERIC_VIDEO_TITLES:
616+
return True
617+
if re.search(r"\b(?:video|demo)\s+\d+\b", normalized):
618+
return True
619+
words = re.findall(r"[a-z0-9]+", normalized)
620+
if normalized.endswith(" video") and len(words) <= 3:
621+
return True
622+
return False
623+
624+
625+
def check_video_embed_titles(lines: List[str], filepath: str) -> List[Issue]:
626+
"""Check VideoEmbed components for specific title props."""
627+
issues = []
628+
for line_number, tag in _iter_video_embed_tags(lines):
629+
title_match = VIDEO_EMBED_TITLE.search(tag)
630+
if not title_match or not title_match.group(2).strip():
631+
issues.append(Issue(
632+
filepath, line_number, "video-title",
633+
"VideoEmbed missing title prop. Add a specific title that describes the integration, workflow, feature, or task shown.",
634+
"error",
635+
))
636+
continue
637+
638+
title = title_match.group(2).strip()
639+
if _is_generic_video_title(title):
640+
issues.append(Issue(
641+
filepath, line_number, "video-title",
642+
f"Generic VideoEmbed title: \"{title}\". Use a specific title that describes what the video shows.",
643+
"warning",
644+
))
645+
return issues
646+
647+
441648
def check_callout_syntax(lines: List[str], filepath: str) -> List[Issue]:
442649
"""Check for malformed hint/callout syntax."""
443650
issues = []
@@ -664,9 +871,11 @@ def run_all_checks(filepath: Path) -> List[Issue]:
664871
issues.extend(check_frontmatter(content, str(filepath)))
665872
issues.extend(check_settings_paths(lines, str(filepath)))
666873
issues.extend(check_ui_element_backticks(lines, str(filepath)))
874+
issues.extend(check_link_quality(lines, str(filepath)))
667875
issues.extend(check_header_case(lines, str(filepath)))
668876
issues.extend(check_image_alt_text(lines, str(filepath)))
669877
issues.extend(check_screenshot_widths(lines, str(filepath)))
878+
issues.extend(check_video_embed_titles(lines, str(filepath)))
670879
issues.extend(check_callout_syntax(lines, str(filepath)))
671880
issues.extend(check_product_casing(lines, str(filepath)))
672881
issues.extend(check_oz_terms(lines, str(filepath)))
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
name: triage-issue-local
3+
specializes: triage-issue
4+
description: Repo-specific triage guidance for the Warp docs repo. Only the categories declared overridable by the core triage-issue skill may be specialized here.
5+
---
6+
7+
# Repo-specific triage guidance for `docs`
8+
9+
This file is a companion to the core `triage-issue` skill. It does not
10+
redefine the triage output schema, safety rules, or follow-up-question
11+
contract. It only specializes the override categories the core skill
12+
marks as overridable.
13+
14+
## Heuristics
15+
16+
- `docs` is the public Warp documentation repository, built with Astro Starlight. Content lives in `src/content/docs/` as MDX files. Treat public issue reports as potentially incomplete.
17+
- Distinguish between **site bugs** (the docs platform is broken — search, navigation, rendering, styling, build errors) and **content issues** (documentation is incorrect, outdated, missing, unclear, has typos, or has formatting problems). Most issues will be content issues.
18+
- When the reporter provides a `docs.warp.dev` URL, map it to the source file: `docs.warp.dev/agent-platform/capabilities/skills``src/content/docs/agent-platform/capabilities/skills.mdx`.
19+
- When an issue claims documentation is wrong about a feature's behavior, verify against the source repos (`warp-internal` for client/Rust, `warp-server` for server/Go) before concluding the docs are incorrect. Docs are the primary source of truth for user-facing content, but source code is essential for validating accuracy when disputed.
20+
- Check the docs style guide (`AGENTS.md`) and terminology glossary (`.warp/references/terminology.md`) to validate that issue reports reference features by their correct names and that any proposed fixes would align with current terminology.
21+
- If the report is a support question (e.g., "How do I do X?") rather than an issue with the docs themselves, direct the reporter to the [Warp community Slack](https://go.warp.dev/join-preview) and the [docs site](https://docs.warp.dev).
22+
23+
## Follow-up question limit
24+
25+
Ask **at most 2 follow-up questions** per triage response. Each question must be high-value: it should meaningfully change the label assignment or reproduction confidence if answered. Do not ask questions whose answers can be inferred from the issue body, linked URLs, or screenshots.
26+
27+
## Label taxonomy
28+
29+
Use the following labels when triaging docs issues:
30+
31+
**Priority labels** (always apply exactly one):
32+
- `priority/high` — Factually incorrect information, broken page, security-related content, or docs that cause users to take a wrong action
33+
- `priority/medium` — Outdated content, confusing instructions, incomplete coverage, or misleading screenshots
34+
- `priority/low` — Typos, minor formatting issues, small clarifications, nice-to-have improvements
35+
36+
**Status labels:**
37+
- `triage` — Always apply on new issues. Signals the issue needs human review.
38+
- `ready-to-implement` — Reserved for human maintainers. Do not apply automatically; mention implementation readiness in the triage analysis instead.
39+
40+
**Existing template labels** (applied automatically by issue templates — do not remove):
41+
- `bug` — Applied by the "Docs site bug" template
42+
- `improve or update documentation` — Applied by the "Docs content issue" template
43+
44+
Do not invent new labels.
45+
46+
## Information to check before asking follow-up questions
47+
48+
Before asking the reporter for more information, check the issue body, comments, and attachments for:
49+
50+
- The specific page URL(s) or topic area affected
51+
- What is incorrect, outdated, missing, or unclear (for content issues)
52+
- Browser and OS (for site bugs — search, rendering, navigation issues)
53+
- Screenshots or recordings showing the problem
54+
- Whether the reporter has already suggested a fix or correction
55+
- Whether the affected page exists in `src/content/docs/` and what it currently says
56+
57+
## Recurring follow-up patterns
58+
59+
- Content issue with no page URL: ask which page or topic is affected.
60+
- "Docs are wrong" with no source: ask for the expected behavior or a reference (release notes, CLI help output, changelog) that contradicts the current docs.
61+
- Site bug with no reproduction details: ask for browser, OS, and whether the issue persists in incognito/private browsing.
62+
63+
## Content structure reference
64+
65+
Documentation lives in `src/content/docs/` with these sections:
66+
- `terminal/` — Warp Terminal features (blocks, editor, sessions, appearance)
67+
- `agent-platform/` — Agent Platform (local agents, cloud agents, capabilities, integrations)
68+
- `code/` — Code editor, code review, git worktrees
69+
- `getting-started/` — Installation, setup, quickstart
70+
- `reference/` — CLI and API/SDK reference
71+
- `guides/` — Guides and tutorials
72+
- `knowledge-and-collaboration/` — Warp Drive, teams, Admin Panel
73+
- `support-and-community/` — Troubleshooting, billing, privacy
74+
- `enterprise/` — Enterprise features
75+
- `changelog/` — Release changelog
76+
77+
The sidebar configuration is in `astro.config.mjs` at the repo root.

0 commit comments

Comments
 (0)