diff --git a/anton/core/llm/prompt_builder.py b/anton/core/llm/prompt_builder.py
index d7340fe..734bcc5 100644
--- a/anton/core/llm/prompt_builder.py
+++ b/anton/core/llm/prompt_builder.py
@@ -61,6 +61,18 @@ def _build_tool_prompts_section(self, tool_defs: list["ToolDef"] | None) -> str:
return "\n\n".join(chunks)
+ # Built-in skills that are always available regardless of ~/.anton/skills/.
+ # Each entry must have "label" and "when_to_use".
+ _BUILTIN_SKILL_SUMMARIES: list[dict] = [
+ {
+ "label": "generate_dashboard_html",
+ "when_to_use": (
+ "when the user asks to build a dashboard, chart, report, "
+ "presentation, or any data visualization as a standalone HTML page"
+ ),
+ },
+ ]
+
def _build_procedural_memory_section(
self, skill_store: "SkillStore | None"
) -> str:
@@ -71,12 +83,17 @@ def _build_procedural_memory_section(
the full procedure. Returns an empty string if no store is wired
or no skills are saved — the caller skips the section entirely.
"""
- if skill_store is None:
- return ""
- try:
- summaries = skill_store.list_summaries()
- except Exception:
- return ""
+ summaries: list[dict] = list(self._BUILTIN_SKILL_SUMMARIES)
+ if skill_store is not None:
+ try:
+ user_summaries = skill_store.list_summaries()
+ except Exception:
+ user_summaries = []
+ # User skills override builtins with the same label.
+ builtin_labels = {s["label"] for s in summaries}
+ for s in user_summaries:
+ if s.get("label") not in builtin_labels:
+ summaries.append(s)
if not summaries:
return ""
diff --git a/anton/core/llm/prompts.py b/anton/core/llm/prompts.py
index 73f57da..42e2e1e 100644
--- a/anton/core/llm/prompts.py
+++ b/anton/core/llm/prompts.py
@@ -236,124 +236,358 @@
VISUALIZATIONS_HTML_OUTPUT_FORMAT_PROMPT = """\
-LIST THE INSIGHTS (terse — one line each, not an essay):
-Before coding, list the insights you want to present/convey/highlight as `1 -