diff --git a/.agents/skills/validate_ui_refs/validate_ui_refs.py b/.agents/skills/validate_ui_refs/validate_ui_refs.py
index 0eeb42a1..eb4b6a47 100644
--- a/.agents/skills/validate_ui_refs/validate_ui_refs.py
+++ b/.agents/skills/validate_ui_refs/validate_ui_refs.py
@@ -260,6 +260,27 @@ def extract_ui_paths(file_path: Path) -> List[Dict[str, Any]]:
re.IGNORECASE,
)
+# Opening/self-closing HTML or JSX tag on a single line, e.g.
+#
+#
+# Quoted strings *inside* such a tag are component props or CSS values, never
+# Command Palette commands. Matching the tag span (rather than sniffing for a
+# `word=` prefix) keeps legitimate prose like `Palette: "Open theme picker"`
+# from being suppressed.
+_RE_HTML_JSX_TAG = re.compile(r"<[A-Za-z][^<>]*>")
+
+# Markdown fenced code block delimiter. Fenced blocks hold prompt and CLI
+# examples (e.g. an agent prompt that happens to quote a UI label), which are
+# illustrative text rather than live references to Warp's Command Palette.
+_RE_CODE_FENCE = re.compile(r"^\s*(?:```|~~~)")
+
+
+def _is_inside_jsx_tag(line: str, index: int) -> bool:
+ """Return True if `index` falls within an HTML/JSX tag on `line`."""
+ return any(
+ m.start() <= index < m.end() for m in _RE_HTML_JSX_TAG.finditer(line)
+ )
+
def _is_plausible_command_name(name: str) -> bool:
"""Filter false positives for command palette names."""
@@ -339,7 +360,16 @@ def extract_command_palette_refs(file_path: Path) -> List[Dict[str, Any]]:
return results
lines = text.splitlines()
+ in_code_fence = False
for line_num, line in enumerate(lines, start=1):
+ # Skip fenced code blocks — they contain prompt/CLI examples, not
+ # live UI references.
+ if _RE_CODE_FENCE.match(line):
+ in_code_fence = not in_code_fence
+ continue
+ if in_code_fence:
+ continue
+
# Check if "Command Palette" is mentioned nearby (within 2 lines)
context_start = max(0, line_num - 3)
context_end = min(len(lines), line_num + 1)
@@ -368,6 +398,10 @@ def extract_command_palette_refs(file_path: Path) -> List[Dict[str, Any]]:
prefix = line[:match.start()]
if _RE_UI_LABEL_PREFIX.search(prefix):
continue
+ # Skip component props and CSS values inside JSX/HTML tags
+ # (e.g. `label="..."`, `title="..."`, `maxWidth: "375px"`)
+ if _is_inside_jsx_tag(line, match.start()):
+ continue
# Skip if already captured by arrow pattern
if not any(
r["line"] == line_num and r["name"] == name
@@ -1976,6 +2010,50 @@ def _run_self_test(valid_paths_path: Path) -> int:
else:
os.environ[key] = value
+ # --- 5. Command Palette extraction ignores JSX attributes and code fences
+ with tempfile.TemporaryDirectory() as td:
+ sample = Path(td) / "sample.mdx"
+ sample.write_text(textwrap.dedent("""\
+ Open the Command Palette and search for "Open theme picker".
+
+
+
+
+
+ Example prompt for the command palette:
+ ```text
+ Walk through the entire "New run" creation flow end to end.
+ ```
+
+ In the Command Palette, search for "Warpify SSH Session".
+ """))
+
+ found = {r["name"] for r in extract_command_palette_refs(sample)}
+
+ # JSX component props and CSS values must not be treated as commands.
+ for bogus in (
+ "Block Divider Demo",
+ "Command Palette Demo",
+ "375px",
+ ):
+ if bogus in found:
+ failures.append(
+ f"extract_command_palette_refs() captured JSX attribute {bogus!r}"
+ )
+
+ # Quoted labels inside fenced code blocks are examples, not references.
+ if "New run" in found:
+ failures.append(
+ "extract_command_palette_refs() captured a name inside a code fence"
+ )
+
+ # Genuine prose references must still be captured.
+ for expected in ("Open theme picker", "Warpify SSH Session"):
+ if expected not in found:
+ failures.append(
+ f"extract_command_palette_refs() missed prose reference {expected!r}"
+ )
+
if failures:
print("SELF-TEST FAILED:")
for f in failures: