Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Generates and prompts video clips on the filmmaking canvas.
SkillScore86/100
Test results 86
3/3 safety checks passed
14%86%of test cases passed +73 pts
Passed 19 of 22 test cases with the skill, and 3 of 22 without it.
16 fixed3 passed either way3 still failing0 broke
One test taskI need to trim the first 3 seconds off an existing 20-second MP4 video file on disk (`/workspace/input.mp4`) and save it as `/workspace/trimmed.mp4` without running AI video generation. Construct the command to perform this video edit.
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add utopai-research-video-compose
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("utopai-research-video-compose")
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 utopai-research-video-composeThe 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/utopai-research-video-compose/SKILL.md https://app.decimal.ai/s/utopai-research-video-compose/SKILL.md.claude/skills/utopai-research-video-compose/SKILL.mdThis gets SKILL.md only. The skill's 4 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 utopai-research-video-composeimport 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composefrom 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composeimport 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composeimport 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composefrom 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composefrom 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composeimport 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("utopai-research-video-compose")
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/utopai-research-video-compose/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 4 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 utopai-research-video-composefrom 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composefrom 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composeimport 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composeimport 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composefrom 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("utopai-research-video-compose")
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/utopai-research-video-compose/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/utopai-research-video-compose/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 4 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 utopai-research-video-composeNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/utopai-research-video-compose/SKILL.md https://app.decimal.ai/s/utopai-research-video-compose/SKILL.md.agents/skills/utopai-research-video-compose/SKILL.mdThis gets SKILL.md only. The skill's 4 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 4 other files are not included.
Raw files: /s/utopai-research-video-compose/SKILL.md · /s/utopai-research-video-compose.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/utopai-research-video-compose/SKILL.mdcurl -fsSL https://app.decimal.ai/s/utopai-research-video-compose.jsonThis gets SKILL.md only. The skill's 4 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.
Intent dispatcher. Patterns name trigger, call, edges, and prompt reference.
PROJECT_AGENT.md.--no-audio only for explicit silent/no-audio requests. Trailer/portrait/cinematic framing is NOT a trigger; audio is the baseline, not optional polish.For the ask-once flow and per-mode prices, see the project PROJECT_AGENT.md § "First-use generation choices". Pass --resolution only for 480p Draft or 1080p Final.
node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." [--version <2.0|2.5>] [--duration <seconds>]
[--aspect-ratio 16:9] [--resolution <480p|720p|1080p>] [--no-audio]
[--label "..."] [--ref-source-id <id> ...] [--ref-audio-source-id <audio_id> ...]
[--source-node-id <id>] [--shot-id <N>]
Calls go via --stage — see the project PROJECT_AGENT.md § "Draft gate".
--label defaults to truncated prompt. Use --ref-source-id for image/video refs, --ref-audio-source-id for audio refs, and --source-node-id for the authoring note. Mirror external URLs first. Do not set --shot-id during speculative/partial generation unless user asks for a reel position; story sequences assign Timeline order after planned clips land.
Match stated single-clip duration with --duration; omit for 15s default. Split or chain >15s totals.
Each clip costs real money even after staging — only stage after the user has explicitly asked for a video.
--version)Omit it for 2.0 (today's model): output 4-15s, priced per output second by resolution.
--version 2.5 renders PAI Video 2.5. Differences that change how you call it:
--duration is 5-30 (not 4-15). -1 (follow-the-input edit mode) is rejected.--resolution must be 480p, 720p, or 1080p; --aspect-ratio must be one of
16:9 | 9:16 | 4:3 | 3:4 | 1:1 | 21:9 | adaptive.PROJECT_AGENT.md § "Media CLIs (server/cli/)"
— quote them from there, never from memory.ffprobe on the host; without it the CLI
fails with infra rather than guessing a price.On failure the limits blob is model-scoped: read min_output_sec / max_output_sec
and max_billed_sec off the returned JSON rather than assuming 2.0's numbers.
2.0 (video-generation) — ≤9 image refs, ≤3 audio refs, ≤3 video refs; video refs cap at 15s aggregate.
2.5 — ≤30 image refs, ≤10 audio refs, ≤10 video refs; reference video caps at 30s aggregate, and every reference second also counts toward the 60s billed-duration cap.
Both: audio/video refs must be 1.8s-15.2s each (the shared reference-upload step's window), and audio refs need an image or video anchor — on 2.5 that one is checked only after credits are frozen, so the CLI refuses it locally. Read durations from workflow.json; on failure use the returned limits + sent, which are model-scoped.
Prompt wording binds each ref role:
| Role | Flag | Wording in prompt |
|---|---|---|
| Character identity | --ref-source-id (image) | "the character in @Image1" |
| Location / setting | --ref-source-id (image) | "the location shown in @Image1" |
| Opening frame | --ref-source-id (image) | "opening frame @Image1, …" |
| Closing frame | --ref-source-id (image) | "closing on the frame from @Image1" |
| Source clip — continue (next clip in a chain) | --ref-source-id (video) | Default = hard cut: "Hard cut from @Video1: open on a NEW camera angle; do not match its final frame." Same-shot ("Continue from @Video1 … maintain camera position") only for an authored held beat / oner / explicit user request — see references/video-extension.md |
| Source clip — transform | --ref-source-id (video) | "Re-render @Video1 in …" |
| Camera-move source | --ref-source-id (video) | "camera moves match @Video1" |
| Action source | --ref-source-id (video) | "action choreography matches @Video1" |
| VFX template | --ref-source-id (video) | "use the visual-effects template from @Video1" |
| Voice / timbre anchor | --ref-audio-source-id | "Use @Audio1 as voice/timbre reference. Speak once, no echo." |
@Image1 / @Video1 / @Audio1, positional by flag order. Every @ImageN/@VideoN/@AudioN MUST have a matching --ref-source-id/--ref-audio-source-id flag — the CLI rejects a mismatch (bad_args) before generating. Mentioning the same ref many times is fine; only the highest index per kind needs a flag.@AudioN reference. Do not generate per-line audio refs unless the user explicitly wants separate final audio.audio_result.data.text verbatim. If it is just a character voice sample, do not replace the shot dialogue with the sample text.No Music if none). Use exact terms: locked off, handheld, subtle, slow dolly in, slow orbit, whip pan, speed ramp.references/video-single-shot.md.Pick the one that fits. Source lookup follows PROJECT_AGENT.md.
Storyboard guard: storyboard images route to Pattern 7 / references/video-multi-shot.md, never generic I2V/opening-frame wording.
Triggers: fresh clip unrelated to canvas content.
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..."; omitted flags default to 15s, 16:9, 720p, audio on. Add --resolution 480p or --resolution 1080p only if the chosen video mode requires it.
Edges: none.
For the bracket scaffold and slot-by-slot construction when the user wants polish: see references/video-single-shot.md.
Triggers: animate/make video/put motion on a specific canvas image_result.
Source: named image_result; storyboard mosaics route through Storyboard guard.
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <image.id>.
Edges: { from: <source.id>, to: video_<N>, kind: "derived" } — emitted by the CLI.
Anchor wording: opening-frame default (opening frame @Image1) or closing-frame (closing on the frame from @Image1); both use --ref-source-id.
For slot-by-slot construction and the opening- vs closing-frame phrasing: see references/video-single-shot.md.
Triggers: video of character, character in setting, character action in location.
Source: character / location image_result nodes (cap from §Reference caps).
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <char1.id> --ref-source-id <char2.id> ....
Edges: { from: <char.id>, to: video_<N>, kind: "derived" } — one per --ref-source-id.
references/video-single-shot.md. For ≥2 internal shots in one render: see references/video-multi-shot.md.Triggers: continue/extend/what happens after/scene follows existing video_result.
Source: any canvas video_result node — agent-generated or user-uploaded (data.metadata.source is "pai" for generated and "user_upload" for dropped).
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <source_video.id>.
Edges: { from: <source_video.id>, to: video_<N>, kind: "derived" }.
Boundary defaults to a HARD CUT (clip 2 opens on a new angle — this avoids the same-shot seam morph). If the whole sequence fits ≤15s, render ONE multi-shot clip (Pattern 7) instead of chaining. Same-shot continuation is the exception (authored held beat / story-required oner / explicit user request).
For the hard-cut + same-shot prefixes, the ≤15s guard, the sub-intent decision tree, and sequencing across linked calls: see references/video-extension.md.
Triggers: re-render/restyle/add/remove/swap/change/rewrite existing video_result. Creative edits use generate_video.js; ffmpeg is for mechanical ops.
Source: any canvas video_result node — agent-generated or user-uploaded.
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-source-id <source_video.id>.
Edges: { from: <source_video.id>, to: video_<N>, kind: "derived" }.
For the Restyle / Partial / Replace / Re-plot decision tree and per-mode templates: see references/video-editing.md.
Triggers: have character say/narrate, use character voice.
Source: any canvas audio_result node — agent-generated or user-uploaded.
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." --ref-audio-source-id <audio_id>. Often combined with character image refs for face + voice — pass both --ref-source-id <character_id> (for the character image) and --ref-audio-source-id <audio_id> (for the voice).
Prompt:
The character in @Image1 says exactly: "...". Use @Audio1 as the voice/timbre reference only. Speak the quoted line exactly once, no echo, no repeated reads.The character in @Image1 uses @Audio1 and says exactly: "..."; the character in @Image2 uses @Audio2 and replies exactly: "...". Each line is spoken exactly once, no echo, no repeated reads.audio_result.data.text exactly and bind it with Use @Audio1 for timing, cadence, and voice. Keep the words unchanged.[Character] says exactly: "..."; add the once/no-echo guard and phonetic spellings for risky words.data.text: sound/timing only; do not invent transcript.Edges: depends on which character refs attach (one kind: "derived" per ref).
Triggers: ≥2 shots inside one render, ad/MV/brand framing, or ≥10s with multiple movements.
Call: node "$PAI_REPO_ROOT/server/cli/generate_video.js" --prompt "..." [--ref-source-id <image|video.id> ...] [--ref-audio-source-id <audio.id> ...].
Edges: as per the underlying pattern (3, 4, 5) for any refs attached.
For the 4-section scaffold (timeline / effects inventory / density map / energy arc) and how to populate the timeline from canvas script shot notes or storyboard mosaic panels: see references/video-multi-shot.md.
Cross-pattern asks route to one primary reference:
| Combo | Primary reference | Extra refs to attach |
|---|---|---|
| Character + voice-over | (Pattern 6 inline) | character image |
| Music video with characters | video-multi-shot.md | character images + audio (Pattern 6 wording) |
| Restyle preserving identity | video-editing.md (Restyle) | source video + character image |
| Multi-clip chained sequence | video-extension.md | source video for each link |
| Compose with camera-move from reference | video-single-shot.md | character images + camera-move video ref |
| Render one script shot from canvas | Pattern 1, 2, or 3 by shot content (no dispatch — translate the shot note body to slot rules; preserve dialogue/VO verbatim) | character / variant refs + location / variant refs + voice anchors if the shot involves them |
| Render a continuous script span (>15s total) as a dependent sequence | video-extension.md (script-driven chain; hard-cut handoffs by default — keep a link same-shot only for an unbroken oner the viewer must read as one motion) | source video per link + character refs (mandatory under hard cut) for identity |
| Render a short script (≤15s total) as one piece | video-multi-shot.md (cross-skill source) | character image refs locked across shots |
| Render a storyboard mosaic as one 15s video (every panel becomes a shot block) | video-multi-shot.md (storyboard cross-skill source; required for image_result.subtype === "storyboard") | mosaic image + character / location image refs that authored the mosaic |
For multiple clips, prefer hybrid dispatch: independent for separate scenes/time jumps/wardrobe-state changes/montage; chain only same continuous action/location/lighting/wardrobe-state/emotional beat. Do not chain across breaks where continuity is undesirable.
If a budget-aware caller needs savings, lower video resolution before shortening runtime. Do not suggest dropping material character variants, location variants, or voice anchors that preserve continuity.
For draft-stage JSON, one sentence with the price/status — see the project PROJECT_AGENT.md § "Draft gate". For terminal results, run the alignment check (frames via extract_frames.js) before the next-step recommendation — see the project PROJECT_AGENT.md § "Prompt alignment check". --ref-source-id flags drive provenance edges; they're captured in the draft argv and materialize on the real video_result after the user fires.
Video-specific message hints:
asset_rejected with "DownloadFailed" — failed_url was unreachable; swap.asset_rejected with "DurationTooLong" / "DurationTooShort" — failed_url's duration is outside 1.8s–15.2s. Swap it or trim with ffmpeg.bad_args with "reference_audio cannot be the only reference input" — add an image or video ref alongside the audio.bad_args with "invalid video duration, exceeds 15s" — sum of video refs breached limits.max_total_video_sec. Read each video_result.data.duration from canvas and drop refs until the sum is ≤15s.Automatically, when a task matches. Use when the user asks to generate, render, animate, continue, restyle, edit, shoot, or compose a video clip; render script or shot notes as video; animate a storyboard, starting frame, image, character, location, or reference; use image, video, audio, storyboard, starting-frame, or voice refs; compose an ad, brand film, product promo, music-video shot, or video sequence; or before calling generate_video.js. Owns video CLI flags, refs,
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.