Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Use when the user wants SEO growth, AI SEO, GEO/AEO, Google visibility, ChatGPT or Perplexity citations, schema, structured data, llms.txt, programmatic SEO, directory submissions, Product Hunt, G2/Capterra, backlinks, or a site audit. Trigger on 'SEO audit', 'AI search visibility', 'AI citations', 'schema markup', 'pSEO', 'submit to directories', or 'backlink plan'.
SkillScore68/100
Test results 68
3/3 safety checks passed
50%68%of test cases passed +18 pts
Passed 15 of 22 test cases with the skill, and 11 of 22 without it.
5 fixed10 passed either way6 still failing1 broke
This benchmark measured an earlier version of the skill.
One test taskWe are updating our clinic details across local directories. Is it fine to use '123 Main Street, Suite 400' on Google Business Profile, '123 Main St, Ste 400' on our website footer, and '123 Main St #400' in LocalBusiness schema?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add minhnv0807-32-seo-growth-global
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install minhnv0807-32-seo-growth-globalThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/minhnv0807-32-seo-growth-global/SKILL.md https://app.decimal.ai/s/minhnv0807-32-seo-growth-global/SKILL.md.claude/skills/minhnv0807-32-seo-growth-global/SKILL.mdThis gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add minhnv0807-32-seo-growth-globalimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add minhnv0807-32-seo-growth-globalfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add minhnv0807-32-seo-growth-globalimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add minhnv0807-32-seo-growth-globalimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add minhnv0807-32-seo-growth-globalfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add minhnv0807-32-seo-growth-globalfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 3 other files are not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add minhnv0807-32-seo-growth-globalimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add minhnv0807-32-seo-growth-globalfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 3 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add minhnv0807-32-seo-growth-globalfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 3 other files are not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add minhnv0807-32-seo-growth-globalimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 3 other files are not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add minhnv0807-32-seo-growth-globalimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 3 other files are not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add minhnv0807-32-seo-growth-globalfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("minhnv0807-32-seo-growth-global")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/minhnv0807-32-seo-growth-global/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 3 other files are not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add minhnv0807-32-seo-growth-globalNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/minhnv0807-32-seo-growth-global/SKILL.md https://app.decimal.ai/s/minhnv0807-32-seo-growth-global/SKILL.md.agents/skills/minhnv0807-32-seo-growth-global/SKILL.mdThis gets SKILL.md only. The skill's 3 other files are not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 3 other files are not included.
Raw files: /s/minhnv0807-32-seo-growth-global/SKILL.md · /s/minhnv0807-32-seo-growth-global.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/minhnv0807-32-seo-growth-global/SKILL.mdcurl -fsSL https://app.decimal.ai/s/minhnv0807-32-seo-growth-global.jsonThis gets SKILL.md only. The skill's 3 other files are not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
Operating rules:
skills/en/references/operating-rules-global.md(esp. rules 2, 3, 7).
This skill covers 6 practical SEO layers: foundation audit, local SEO, search-intent content, AI search visibility, schema/machine-readable files, and backlink/distribution. The goal is qualified demand, not publishing volume.
Read .agents/product-marketing-context-global.md if available. Ask up to 4 questions: website URL, target market, main product/service, and 90-day SEO goal. Use Search Console, GA4, Ahrefs, Semrush or similar exports when available.
Three companion references:
references/seo-growth-playbook.md — 90-day roadmap, pSEO, comparison and alternative pages, site architecture.references/schema-json-ld.md — 12 copy-paste JSON-LD blocks with current rich-result status.references/ai-visibility-measurement.md — prompt portfolio, answer audit, AI referral traffic, crawler log check, fix loop.| User says | Mode |
|---|---|
| "Traffic dropped", "site is not ranking" | SEO audit |
| "We don't show up on Maps / in the local pack" | Local SEO |
| "Get cited by ChatGPT/Perplexity" | AI SEO / GEO |
| "Add schema", "rich results" | Schema |
| "Create many SEO pages" | Programmatic SEO |
| "Build alternative / vs pages" | Comparison pages |
| "Submit to Product Hunt/G2/directories" | Directory + backlink |
Run in order — a failure at a lower layer nullifies everything above it.
| Check | Passing threshold | Tool |
|---|---|---|
| robots.txt does not block money pages | 0 revenue pages disallowed | Search Console > robots.txt report |
| XML sitemap exists, declared in robots.txt | Present, auto-updating | Search Console > Sitemaps |
| Indexed pages / pages you want indexed | >90% | Search Console > Pages |
| Redirect chains | 1 hop max, no loops | Screaming Frog, Ahrefs Site Audit |
| Soft 404s | 0 | Search Console > Pages > "Soft 404" |
| Canonicals point at the right page | 100% self-canonical or pointing at the true original | Any crawler |
Leftover noindex from staging | 0 pages | Grep noindex site-wide |
Two issues account for most "the site just won't rank" cases: site-wide canonicals pointing at the homepage (misconfigured theme or plugin) and a noindex left over from staging after go-live.
| Element | Threshold | Note |
|---|---|---|
| Title | 50-60 characters | Longer gets truncated; primary term near the front |
| Meta description | 150-160 characters | Not a ranking factor, but drives CTR |
| H1 | Exactly one per page | Same topic as the title, not a copy of it |
| H2/H3 | Phrased the way people type queries | This is the scaffolding AI engines extract passages from |
| Images | Descriptive alt, meaningful filenames | WebP; no 4MB originals |
| Internal links | Every page has at least one inbound link | See site architecture in the playbook |
| Metric | Good | Needs improvement | Poor |
|---|---|---|---|
| LCP (Largest Contentful Paint) | < 2.5s | 2.5-4.0s | > 4.0s |
| INP (Interaction to Next Paint) | < 200ms | 200-500ms | > 500ms |
| CLS (Cumulative Layout Shift) | < 0.1 | 0.1-0.25 | > 0.25 |
Read field data first (Search Console > Core Web Vitals, CrUX), lab data second (PageSpeed Insights). A 100 desktop score with a 5s mobile field LCP is common — mobile is what matters.
Usual LCP offenders: uncompressed hero images, auto-playing sliders above the fold, fonts loaded from multiple origins, and synchronous chat or popup scripts.
When two of your own pages compete for the same query, both underperform. How to detect it:
site:domain.com [keyword] — several near-identical pages means consolidate.Fix in this order: merge into the strongest page and 301 the rest → or split the search intent cleanly across pages → canonical only as a last resort.
Applies to any business with a physical location or a defined service area: clinics, restaurants, gyms, studios, home services, multi-location retail.
First-time Google Business Profile setup: see
11-channel-setup-global. This section covers optimization once the profile exists.
NAP = Name, Address, Phone. These must be byte-identical everywhere:
| Where it appears | Check |
|---|---|
| Google Business Profile | The source of truth — everything else copies from here |
| Website footer | Character for character |
| Contact / location pages | Character for character |
LocalBusiness schema | Character for character |
| Social profiles | Character for character |
| Citations and directories (Apple Business Connect, Bing Places, Yelp, industry directories) | Character for character |
"Suite 200" vs "Ste. 200" vs "#200" counts as three different addresses to an aggregator. Pick one string and enforce it.
| Item | Target | Cadence |
|---|---|---|
| Primary category | The most specific accurate category, never a generic one | Review quarterly |
| Secondary categories | 2-5 genuinely relevant | Quarterly |
| Photos | Exterior, interior, team, product | 3-5 per month |
| Posts | Offers, events, updates | 2-4 per month |
| Q&A | Seed and answer the questions customers actually ask | Monthly review |
| Hours | Update holiday hours two weeks ahead | Per holiday calendar |
| Services / menu | Complete, with prices | Quarterly |
One page per location, never all of them crammed onto the contact page:
/locations/[city] or /[service]-[city].LocalBusiness block per location (block 9 in references/schema-json-ld.md).| Funnel | Keyword type | Page type |
|---|---|---|
| TOFU | problem, how-to, checklist | blog, guide, free tool |
| MOFU | comparison, template, use case, "how much does X cost" | comparison, use-case page, pricing |
| BOFU | alternatives, review, demo, pricing | landing page, pricing, case study |
Local businesses add a geographic layer: [service] [city], [service] near me, [service] [city] cost.
Drop the phrase "rank on ChatGPT" — nobody controls that. Report on four rungs instead, because each is earned differently:
| Rung | Meaning | What governs it | How you see it |
|---|---|---|---|
| 1. Retrieved | The model read your content while composing an answer | Crawlability, parseable structure, query relevance | Server logs; largely invisible |
| 2. Cited | Your page appears as a source in the answer | Content structure, statistics, clarity, freshness | Prompt-tracking tools, AI Overview source lists |
| 3. Mentioned | Your brand is named in the answer text | Entity recognition plus how the web describes you | Prompt-tracking tools |
| 4. Recommended | Your product is on the shortlist the buyer considers | Aggregate web consensus — reviews, communities, analysts, press, video | The framing around the mention, not just its presence |
Rungs 1-3 are what on-site optimization earns you. Rung 4 mostly is not on your site at all. Reporting a single "AI visibility" number hides that gap. A rising citation count with a flat recommendation rate is a specific, diagnosable problem: the rest of the web does not corroborate you yet.
There is also a negative rung: recommended against. On requirements-heavy prompts, models increasingly name products a buyer should avoid, with sources. So track the sentiment around each mention (favorable / neutral / hedged / negative), not just the count.
Publishing your own "Top 10 [category] tools" guide and ranking yourself first is the most widely recommended AEO tactic. It does not work the way it is sold.
The model treats your guide as a source about the category. It extracts the competitor names, evaluation criteria and pricing you compiled — then makes its recommendation from web-wide consensus, where established players dominate. The common outcome: your page earns the citation while the recommendation goes to a competitor. You did the research that helped the model describe them.
It splits by market position:
The test before funding another self-ranked listicle: if a model ignored everything on our domain, would the rest of the web still put us on the shortlist? If not, that gap is the priority.
User-agent: GPTBot
Allow: /
User-agent: OAI-SearchBot
Allow: /
User-agent: ChatGPT-User
Allow: /
User-agent: PerplexityBot
Allow: /
User-agent: ClaudeBot
Allow: /
User-agent: anthropic-ai
Allow: /
User-agent: Google-Extended
Allow: /
User-agent: Bingbot
Allow: /
User-agent: CCBot
Disallow: /
| Bot | Operator | Role |
|---|---|---|
| GPTBot | OpenAI | Crawls for model training; obeys robots.txt. Independent of search: allowing OAI-SearchBot while disallowing GPTBot is a valid combination |
| OAI-SearchBot | OpenAI | Decides whether the site can appear in ChatGPT search answers; obeys robots.txt, changes take about 24h to apply. Opted out means not shown in those answers |
| ChatGPT-User | OpenAI | Fetches a page when a user acts in ChatGPT (e.g. opens a link); robots.txt rules may not apply. Not used to decide search inclusion |
| OAI-AdsBot | OpenAI | Checks landing pages submitted as ChatGPT ads; does not train models. Only relevant if you run those ads |
| PerplexityBot | Perplexity | Perplexity always cites sources, making it the most measurable channel |
| ClaudeBot / anthropic-ai | Anthropic | anthropic-ai is the legacy token, ClaudeBot the current one |
| Google-Extended | Not a crawler. See the note below | |
| Bingbot | Microsoft | Feeds both Bing and Copilot |
| CCBot | Common Crawl | Training-only corpus; produces no citations, safe to block if you prefer |
OpenAI bot roles [checked 2026-10, developers.openai.com/api/docs/bots]. Re-check that page on the planning date: bot lists change, so do not copy user-agent strings or version numbers from memory. Confirm the bots really fetch your key pages with the crawler log check in references/ai-visibility-measurement.md.
What most guides get wrong: Google-Extended does not control whether you appear in AI Overviews. AI Overviews are built on the regular Search index. Blocking Google-Extended only affects use of your content in Google's other generative products. To control snippet usage in AI Overviews you use nosnippet, max-snippet or data-nosnippet — which also costs you ordinary snippets.
Three additional checks:
llms.txt. Served at /llms.txt, listing your most important pages in markdown with short descriptions. It is a community-proposed convention with no formal commitment from any major platform. Optional: effect unproven, no vendor commitment. Cheap enough to ship, but do not oversell it or bill it as a major line item.For a fixed prompt portfolio, per-answer audit columns, metrics, AI referral traffic and a fix loop, use references/ai-visibility-measurement.md. Quick version: test 10-20 of your highest-value queries and log the results:
| Question | Platform (ChatGPT / Perplexity / AI Overview) | Are we cited? | Which competitor is cited instead | Action |
|---|---|---|---|---|
| [query 1] | Yes / No | |||
| [query 2] | Yes / No |
Query types to cover:
When a competitor is cited and you are not, check five things: is their content more extractable, do they cite statistics and sources, is it more recently updated, do they have schema you lack, and are they referenced by third parties (press, communities, review sites).
AI systems extract passages, not pages. Every block below must stand alone without the paragraph before it. Weave these into normal content — do not write a separate "AI version" of the page, which risks scaled-content policy problems.
| Block | Query it serves | Structure |
|---|---|---|
| Definition | "what is X" | Sentence 1 defines it. Sentences 2-3 expand. Final sentence says why it matters |
| Step-by-step | "how to X" | One intro line plus a numbered list, one concrete action per step |
| Comparison table | "X vs Y" | Criteria column, a "Best for" row, and a 1-2 sentence bottom line |
| Pros and cons | "is X worth it" | Pros, cons, then a conditional verdict |
| FAQ | related questions | Questions phrased as users ask them; 50-100 word answers |
| Listicle | "best X" | Read section 4.2 before building these |
| Statistic citation | evidence-seeking queries | Specific number, source, year, link |
| Self-contained answer | all types | 40-80 words, no "it", "this", or "as mentioned above" |
| Pricing | "how much does X cost" | Real range, the variables that move it, one worked example |
| Who it is and is not for | evaluation queries | Explicitly name who should not buy — this is the differentiator |
| Freshness and authorship | all types | Updated date plus a real author with stated expertise |
Four formatting rules: lead each section with the answer instead of burying it; write H2/H3 the way users phrase queries; use tables over prose for comparisons; use numbered lists over paragraphs for procedures.
| Page | Schema |
|---|---|
| Company | Organization, WebSite |
| Blog | Article / BlogPosting |
| Product | Product + Offer (+ AggregateRating when real reviews are on the page) |
| SaaS / app | SoftwareApplication |
| FAQ | FAQPage — see caveat |
| Ordered procedure | HowTo — see caveat |
| Breadcrumb | BreadcrumbList |
| Location page | LocalBusiness (use the specific subtype: Dentist, Restaurant, ProfessionalService...) |
| Event, webinar | Event |
| Course | Course |
Two things to state plainly to the client:
reported: Search Central notice dated 2026-05-08, doc not fetched). Search Console FAQ report removal is reported for June 2026 and API removal for August 2026. The markup is still valid schema.org and harmless: keep it when the FAQ is genuinely on the page, but promise no rich result and do not sell it as extra SERP real estate.Full code for 12 JSON-LD blocks, testing tools and common failure modes: references/schema-json-ld.md.
Never conclude "this site has no schema" from a single fetch. Simple fetch tools do not execute JavaScript, and a large share of sites inject JSON-LD through GTM or client-side rendering. Verify with the Rich Results Test or DevTools before it goes into an audit — including when auditing competitors.
Prioritize sources that can also produce leads, not raw link counts:
Avoid: bulk link packages, PBNs, comment spam. Beyond the penalty risk, they generate none of the web consensus AI engines actually weigh.
# SEO Growth Plan — [Brand]
## 1. Diagnosis
| Area | Status | Evidence | Priority |
## 2. Technical baseline
| Metric | Current | Threshold | Fix | Owner |
## 3. 90-day SEO strategy
- Market:
- ICP:
- Main search intent:
- Primary KPI:
## 4. Page roadmap
| Page | Funnel | Keyword intent | CTA | Owner |
## 5. Local SEO (if applicable)
| Item | Current | Action | Cadence |
## 6. AI SEO / GEO actions
| Question | Platform | Cited? | Competitor cited | Action |
Current ladder rung: [retrieved / cited / mentioned / recommended]
Mention sentiment: [favorable / neutral / hedged / negative]
## 7. Schema + technical checklist
| Page | Schema/file | Action | Validation |
## 8. Backlink/distribution plan
| Destination | Asset needed | Submission copy | Deadline |
## 9. Measurement
| Metric | Baseline | Target | Tool |
08-competitor-research-global: SEO competitors, content gaps, and the input data for comparison pages.11-channel-setup-global: first-time Google Business Profile setup.12-landing-page-brief-global: BOFU landing pages and comparison pages.13-data-analysis-global: GA4/Search Console/export analysis.15-social-listening-global: real community questions for FAQ and TOFU content.36-content-brief-global: turn the page roadmap into writing briefs.aggregateRating on pages without reviews.Published skills whose descriptions closely match this one, with their headline benchmark lift.
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.