Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Turn an object or character reference image into a quality-gated, animation-ready procedural Three.js model built in code.
SkillScore73/100
Test results 73
3/3 safety checks passed
45%73%of test cases passed +27 pts
Passed 16 of 22 test cases with the skill, and 10 of 22 without it.
7 fixed9 passed either way5 still failing1 broke
One test taskWhen writing helper processing scripts for procedural 3D model specification pipeline tools, what external package dependencies can be imported?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add anbeime-img2threejs
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("anbeime-img2threejs")
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 anbeime-img2threejsThe 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/anbeime-img2threejs/SKILL.md https://app.decimal.ai/s/anbeime-img2threejs/SKILL.md.claude/skills/anbeime-img2threejs/SKILL.mdThis gets SKILL.md only. The skill's 5 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 anbeime-img2threejsimport 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsfrom 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsimport 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsimport 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsfrom 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsfrom 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsimport 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("anbeime-img2threejs")
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/anbeime-img2threejs/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 5 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 anbeime-img2threejsfrom 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsfrom 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsimport 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsimport 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsfrom 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("anbeime-img2threejs")
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/anbeime-img2threejs/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/anbeime-img2threejs/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 5 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 anbeime-img2threejsNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/anbeime-img2threejs/SKILL.md https://app.decimal.ai/s/anbeime-img2threejs/SKILL.md.agents/skills/anbeime-img2threejs/SKILL.mdThis gets SKILL.md only. The skill's 5 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 5 other files are not included.
Raw files: /s/anbeime-img2threejs/SKILL.md · /s/anbeime-img2threejs.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/anbeime-img2threejs/SKILL.mdcurl -fsSL https://app.decimal.ai/s/anbeime-img2threejs.jsonThis gets SKILL.md only. The skill's 5 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.
Rebuild the object visible in a reference image as a code-only procedural Three.js model, gated by a staged sculpting pipeline and an AI-vision self-correction loop. This is reconstruction-by-code, not photogrammetry, mesh extraction, or downloaded art packs.
Agent-agnostic: works under Claude Code, Codex, or OpenCode. Wherever this doc says "agent vision" or "agent browser tool", use whatever the host provides — native image reading, a browser MCP (playwright/chrome-devtools), the project preview, or a user-supplied screenshot.
This file is the always-loaded router: it holds the order of operations and every hard rule as one
line. The full contract behind each rule lives in the grimoire/ or docs/ file that rule names —
read the named file at the moment you reach that stage, not before.
Keep one checkout of this repository and let every host enter it through a symlink, so Claude and Codex execute the same code instead of drifting apart:
~/.claude/skills/img2threejs -> <your checkout>
~/.codex/skills/img2threejs -> <your checkout>
The user attaches/points to an object image and wants a procedural Three.js model, a reconstruction/animation/destruction plan, a sculpt spec, or code. Also for material studies, action-ready props, game objects, botanical/mechanical parts, and stylized reconstructions.
Sculpt from a photo, in order — never one-shot a mesh:
python3 forge/next.py --state .img2threejs/state.json [<spec>] first, at every start,
resume, and before every correction iteration. It reports the ordered checklist, exact next
command, evidence status, and bounded correction-loop status; it never replaces the spec/pass
gates. Obey a hard stop; never continue from memory.grimoire/intake/validation_rubric.md).qualityContract before any code.State explicitly when output is approximate/stylized/low-poly. A single image cannot reveal hidden sides or guarantee exact geometry — say so instead of faking confidence.
Conversation context is disposable; .img2threejs/state.json is the local checklist authority.
Initialize once per reconstruction, then gate every step through it:
python3 forge/state.py init --state .img2threejs/state.json --reference <img> --profile <generic|cs2|character|animated-character> --spec object-sculpt-spec.json
python3 forge/next.py --state .img2threejs/state.json [object-sculpt-spec.json]
python3 forge/state.py mark <step-id> --state .img2threejs/state.json --evidence <path>
next.py prints the current step, pass, incomplete mandatory steps, exact next command, and
loop/max. Exit code 3 or status=stopped is a hard stop: report the reason and request input.
Never bypass it by reconstructing progress from chat history.skipped only with --reason —
silent omission is forbidden. Loop counts derive from reviewHistory actions
(refine-spec/refine-code), not agent memory. Defaults: 3 corrections per pass, 6 total.cs2 requires classification,
manifest, and a machine-readable CS2 review before AI review; character requires the character
contracts and landmark evidence; animated-character adds all of character plus the nine Stage R
steps (grimoire/readiness/animation_contract.md). Pick it whenever the rig must MOVE — on
character the Stage R gates are absent and the build completes without ever running them, which
is how animation used to ship broken. Its order is load-bearing: repair the mesh, freeze it, bind
additively, then verify parity. Every profile records suitability, projection applicability, and
material-evidence applicability. The state file is a resumability index, not visual evidence:
renders, specs, review history, and deterministic gates remain the authoritative artifacts.Run scripts from the skill root (forge/...). Pure Python 3.10+ stdlib, no pip installs.
Full flags: grimoire/scripts.md. Never let a script score visuals — that is the agent's job.
grimoire/intake/image_analysis.md — identify/classify, decompose macro→meso→micro,
map part relationships, name materials in PBR terms, list identity-defining features, and flag
what the single view hides. Observation before inference; controlled 3D vocabulary; 3D
object-space not 2D image-space. Then probe local images:
forge/stage1_intake/probe_image.py <image> (metadata only, not a visual check).
1a. Local Spec Search — after image analysis, before writing or refining a spec, pull local
domain evidence (anatomy/PBR/wear/geometry/runtime/physics) rather than inventing it:
python3 forge/stage2_spec/new_pre_spec_assessment.py "Name" --image <img> --out assessment.json
(auto-runs BM25, auto-picks cs2/core_3d collection, writes a localSpecSearch bundle that
new_sculpt_spec.py --assessment carries into the spec). Full query-expansion recipe
(bilingual terms, focused search_specs.py retrieval, cache rules):
grimoire/intake/local_spec_search.md. MUST read it before retrying an incomplete or
domain-specific query.
1b. CS2 intake manifest — for a CS2 request, create and validate cs2-intake.json before
pre-spec authoring (admission, heuristic signal, classification, family/route resolution).
MUST read grimoire/intake/cs2_intake_contract.md completely before creating the manifest or
running pre-spec assessment.
1c. Optional fidelity evidence adapters — only when they improve an observed weak point; the
stdlib core remains authoritative. Thin/complex masks → local SAM2; character face/pose →
MediaPipe; weak front/back cues → Depth Anything V2
(forge/stage1_intake/run_vision_adapter.py <segment|landmarks|depth> ...; every adapter emits
provenance; monocular depth is relative only). MCP-only scene mutations never count as
implementation — write the proven change back to the spec or TypeScript, rebuild, recapture.
Full adapter + MCP routing and authority boundaries:
docs/integrations/reference_fidelity_tooling.md.forge/stage2_spec/new_pre_spec_assessment.py "Name" --image <img> --complexity <simple|moderate|complex|ultra-complex> --out assessment.json. Rules: grimoire/intake/quality_contract.md.
Set objectClass.primaryDomain (object | character | hybrid) and fill the seeded
detailInventory (its targetMinDetails scales with complexity). Supported CS2 knife skins
and Glock-18 assets: always pass --cs2, which defaults the complexity tier to ultra-complex
(targetMinDetails 16, floor 9) — the finish/wear/hardware is the item, so CS2 is held to the
top fidelity bar. Author procedural GEOMETRY but route the FINISH through the projection path in
step 2c — a procedural finish for a patterned skin (Doppler/Gamma/Marble/Fade) reads visibly
wrong against the reference. Finish routes + rulebook: grimoire/build/cs2_finishes.md;
optional exact-texture acquisition: grimoire/intake/cs2_texture_acquisition.md.
2b. Detail inventory (do not skip for detailed subjects) — scan zones and enumerate every
identity-defining small detail (gloss, bevel, fasteners, linework, contours, stains):
forge/stage1_intake/build_detail_inventory.py <image> --mode grid-3x3 --out-dir <dir> --out di.json.
Each detail MUST map to a component.localFeatures or material.localOverrides entry — never
prose only. Taxonomy + 3D-term recipes: grimoire/intake/detail_inventory.md.
2c. Projection-first fidelity (characters AND reference-matched surfaces — supported CS2 skins,
decals, painted patterns) — when the goal is matching a specific reference's surface, put the
photo's own pixels on the mesh instead of approximating them procedurally. This is the single
biggest fidelity lever; a procedural material for a patterned surface is the #1 reconstruction
failure. Recipe (grimoire/character/likeness_maximization.md — its two levers generalize past
characters): solve the camera (stage1_intake/solve_camera_pose.py → referenceCamera),
de-light the reference (stage1_intake/delight_albedo.py, hard requirement — de-lighting is
what makes projection safe), then project the de-lit crop and bake it into UVs
(stage3_build/bake_projected_texture.py --mesh-id <id>). For a CS2 skin the projected de-lit
crop IS the finish — no procedural Doppler material. For characters, first capture landmarks
(stage1_intake/extract_landmarks.py --out anatomy.json), fill preSpecAssessment.anatomy,
route grimoire/character/reconstruction.md. A single view cannot show hidden sides — report
per-region confidence and request more views when it matters.
Character sub-routes, in order — decide what parts exist before shaping any, and shape the head
before the hair that sits on it:
grimoire/character/structure_decomposition.mdgrimoire/character/head_construction.md (what the likeness gate reads against)grimoire/character/stylized_hair_threejs.md + parameter contract in
grimoire/character/threejs_hair_parameter_contract.json. Lock topology only after the
silhouette review passes: material tuning cannot repair wrong lock topology.
2d. Reference-free humanoid — a generic figure with no reference image has nothing to measure,
so fill anatomy from public canon:
forge/stage2_spec/humanoid_proportions.py <spec> --style-heads 8 --in-place. It writes
anatomy.source: "canon-table" so canon is never mistaken for measurement, refuses to run when
the spec names a reference image, and names anything the corpus does not supply rather than
interpolating it.forge/stage2_spec/new_sculpt_spec.py "Name" --image <img> --assessment assessment.json --manifest cs2-intake.json --out object-sculpt-spec.json.
Replace generic starter featureReviewTargets with the object's real identity-defining
systems (≤5 critical, ≤3 important per pass); for characters add anatomy-proportion,
face-landmark-placement, pose-silhouette, outfit-and-palette. Use 3D-graphics terms only
(grimoire/glossary/3d_vocabulary.md), never "nice/smooth/shiny". Classify every component's
topologyClass/topologyRationale per grimoire/intake/surface_topology.md before picking a
primitive — this is what prevents a continuous organic form from being picked as a box.forge/stage1_intake/analyze_texture.py <crop> --spec spec.json --material-id <id> --in-place
classifies the finish, extracts the gradient palette, and writes doc-grounded
MeshPhysicalMaterial scalars onto the material. Recipes + Three.js texture/PBR rules:
grimoire/build/threejs_texture_reference.md. Rule of thumb: solid albedo for flat paint,
real reference crop for patterned finishes.forge/stage1_intake/extract_pbr_evidence.py <crop> --out-dir <dir> --material-id <id> --target-threshold 0.7.
Confidence < 0.7 is a stop/refine-input signal, not a pass. It is inference, not inverse rendering.forge/stage1_intake/material_region_analysis.py --manifest regions.json --out-dir material-evidence --out material-analysis.json,
resolve each assignment from docs/materials/material-reference.json, wire it in with
forge/stage2_spec/apply_material_analysis.py.forge/stage4_review/material_views.py),
compare visible-footprint crops (material_comparator.py), apply only bounded material-scoped
corrections (material_feedback.py), and record the blocking result (material_gate.py).forge/stage2_spec/validate_sculpt_spec.py object-sculpt-spec.json then --strict-quality.
Strict blocks shallow specs (a complex object with one root, no repetition systems, no
local overrides, no micro groups is NOT implementation-ready even if JSON validates).forge/stage3_build/orchestrate_passes.py status object-sculpt-spec.json
forge/stage3_build/generate_threejs_factory.py object-sculpt-spec.json --out src/createObjectModel.ts
The generator is fail-closed: strict-quality must pass before it writes any factory, and a
future --pass-id fails until prior passes are reviewed continue. If blocked, preserve the
BLOCKED artifact and refine the subject-specific spec; do not substitute a generic template.
The local state adds --force only for a new pass or refine-spec; refine-code edits the
current artifact without regenerating it. Before overwriting, carry valid hand refinement back
into the spec; generated code must not be the only copy of reconstruction decisions.
6a. Hitting a triangle budget. performanceBudget.targetTriangles selects a tessellation tier
for every primitive with segment counts (low ≤6k, standard ≤60k, else hero) and caps
implicit-surface sampling grids. Where a tier is not precise enough, add
geometryDescriptor.decimate: {"targetRatio": 0.4} to that component — a quadric collapse in
the generated factory, run before skin binding so weights are computed on surviving
vertices. It keeps position only (normals recomputed), so it is refused on an
authored/unwrapped uvStrategy. Offline LOD tiers:
forge/stage3_build/decimate.py <mesh.json> --ratio <r> --json.forge/stage4_review/turntable_gate.py --capture 0=front.png --capture 90=right.png --capture 180=rear.png --capture 270=left.png --json
node runtime/scripts/export_mesh_geometry.mjs --url <preview> --out meshes.json then
forge/stage4_review/self_intersection.py meshes.json --json
forge/stage4_review/attachment_anchor.py object-sculpt-spec.json --measured measured.json --json
All three exit 0 clean / 1 gate failure / 2 error. A failure blocks continue even when
the global fidelity score passes. Read sampledVertexCount / unmeasuredAttachments /
missingAzimuths before believing a clean verdict: each names what the gate did not look at.grimoire/review/gates_reference.md and grimoire/review/self_correction.md completely. Run
forge/stage4_review/diagnose_render.py and record the passing Tier 1 result with
--spec object-sculpt-spec.json --pass-id <pass> --in-place; for non-planar forms also run
forge/stage4_review/diagnose_render_multi_angle.py with the fixed view and at least two
meaningful orbit views. Then run
forge/stage3_build/orchestrate_passes.py check object-sculpt-spec.json --pass-id <pass>.forge/stage4_review/make_comparison_sheet.py --reference <img> --render <shot> --out cmp.png --json.forge/stage4_review/append_review.py object-sculpt-spec.json --pass-id <pass> --fidelity <0-1> --action <continue|refine-spec|refine-code|request-input|stop> --summary "..." --render-screenshot <shot> --comparison-image cmp.png --ai-vision-score <0-1> --layer-scores-json '{...}' --feature-reviews-json <f.json> --in-place.
For the CS2 family path, produce the versioned report first with
forge/stage4_review/cs2_review.py --manifest cs2-intake.json --metrics cs2-review-inputs.json --scene forge/tests/fixtures/knife_review_scene.json --out cs2-review.json
and attach it with --cs2-review-json cs2-review.json --review-scene-json forge/tests/fixtures/knife_review_scene.json.
A failed family, painted-region, projection-coverage, critical-detail, or orbit gate blocks
continue even when the global score passes. See docs/cs2/review-gates.md.forge/stage3_build/orchestrate_passes.py sync object-sculpt-spec.json --in-place
python3 forge/next.py --state .img2threejs/state.json object-sculpt-spec.json.forge/stage4_review/check_part_coverage.py --spec object-sculpt-spec.json --manifest parts.json
and verify the action-ready hierarchy. Mark part-coverage and action-ready only with evidence.When the user supplies a GLB as an intermediate reference, the browser-rendered GLB is the
structural and visual baseline for an independently authored procedural factory. The raw GLB is
never pixel evidence and its topology/materials are never copied into the factory. Before any
factory edit — full contract in grimoire/build/python_threejs_render_bridge.md, machine-readable
schema in docs/specs/render-profile.v2.schema.json (+ example; fail-closed validation):
forge/stage1_intake/probe_glb.py first. A merged one-node/one-mesh asset is insufficient
for semantic labels; request a multipart GLB or a browser semantic-ID pass before claiming
exact regions.render-profile.v2 (forge/stage4_review/validate_render_profile.py) used by
both the GLB and procedural routes. Region IDs are subject-specific, never inherited from the
example profile; declare the required set in extensions.requiredSemanticRegions so omission is
a hard validation error.beauty, alpha-silhouette, semantic-id, depth,
normal, roughness-material-id); score with forge/stage4_review/compare_region_passes.py.
Missing semantic-ID data blocks per-region confidence rather than falling back to whole-image
scores.camera → silhouette → face → clothing → accessory → materials → lighting; recapture the full pass set after each group and record the changed
group, hashes and score. Never combine groups when diagnosing improvement.Before any visual review or continue decision, MUST read the full gate-by-gate contract in
grimoire/review/gates_reference.md (Divine Eye, VLM rescue, multi-angle, interior difference,
chirality, hair, CS2 review, bounded correction, Divine Eye fitting, screenshot feedback, assembly,
attachment, material, detail inventory, rig payload, character track). In short:
grimoire/intake/validation_rubric.md, check_reference_admission.py).divine_eye.py is deterministic-first; the VLM (vlm_gate.py) is a gated last layer, never
consulted on a hard-gate failure.diagnose_render_multi_angle.py).interior_difference.py). Silhouette IoU reads
~11% of figure cells: a model with its face deleted scored the same 0.8803 as the finished face.-l/-r pair is a MIRROR, not a rotation — hard at spec time (validate_chirality). A pair
wrong the same way on both sides still passes, and needs medial_lateral_bias vs a reference.scalp_exposure.py is HARD and runs on geometry before any render; hair_gate.py
is soft and subordinate to it. A coverage shortfall never authorises widening the masses.vertex_region_gate.py. Never a
texture — this pipeline emits code; the shape predicates live in _shared/vertex_paint.py.swept_arc_gate.py: silhouette IoU
passes a straight cone occupying roughly the right cells.stage5_rig/validate_rig_payload.py) before binding a
THREE.Skeleton; it proves payload integrity only, never pose stress or likeness.grimoire/readiness/animation_contract.md,
stage5_rig/rig_gates.py). A clip that exists is not a clip that plays: only G1
(maxSampledBindingDelta <= 2^-23) separates the two, and a gate whose input is missing reports
unevaluated, never a pass. Bind at IDENTITY in attached mode and take the display offset from
the mesh bounds alone; loop is decided by poseReturn, never by travel.cs2_review.py against the versioned scene fixture.correction_loop.py may stop earlier on repeated defects, oscillation, or plateau.continue requires a render + comparison sheet + AI-vision score ≥ threshold, every critical
feature ≥ its own threshold (grimoire/feedback/render_capture.md).check_part_coverage.py, grimoire/build/geometry_patterns.md).grimoire/readiness/action_rigging.md, grimoire/readiness/joint_attachment.md,
grimoire/feedback/shading_realism.md, grimoire/intake/quality_contract.md,
grimoire/intake/validation_rubric.md.After every pass, decide exactly one: continue | refine-spec | refine-code | request-input | stop.
refine-spec fixes a wrong/missing/shallow spec (re-validate, don't patch code around it);
refine-code fixes geometry/material/lighting that doesn't match a sound spec. Before making the
decision, MUST read the root-cause guide + fidelity scale in grimoire/review/self_correction.md,
record the decision, and re-run the local state gate.
Small features need a different instrument. Divine Eye's SSIM/tonal/edge signals run on a 64×64
luma grid, so a detail a few pixels wide is absent before any comparison happens. When fidelity
depends on individual tears, spars, fangs or eyes, use the four-tier microscope:
grimoire/review/divine_eye_microscope.md. Two empirically established rules from it: measure
fidelity on a component's visible footprint (full frame minus a component-hidden frame), never
on an isolation render; and never colour-gate a concave feature, where a dark ratio captures
cavity shading rather than material.
Report what changed each pass with evidence (exact values/coordinates), name what still doesn't
match, and never claim "done" when only "improved". A passing gate is not proof of 3D realism.
Full rule + examples: grimoire/review/self_correction.md.
A left/right pair is a reflection, never a rotation: negate the lateral axis and nothing else,
(x, y, z) → (-x, y, z). With forward: +Z, Y up and a right-handed frame, the character's own
left is +X. The convention lives as code in forge/_shared/chirality.py
(CHARACTER_LEFT_SIGN), with two different gates for the two defects that shipped from getting it
wrong: validate_chirality catches a rotation-mistaken-for-reflection at spec time, and
medial_lateral_bias vs a reference catches a pair that is wrong the same way on both sides.
Reflecting also inverts triangle winding — flip it back on the mirrored side or flatShading
lights the limb as though lit from behind. Full write-up with the measured defects:
grimoire/scripts.md ("Left and right").
Hair has its own subsystem because it has failure modes no other gate can see. Full contract,
measurements and non-goals: docs/HAIR_PIPELINE.md. The hard rules:
(u, v), never absolute positions (hard validation error).standProud is enforced by the generator, not advisory.scalp_exposure.py is a HARD gate on geometry before any render; a coverage shortfall never on
its own authorises widening the masses.shell, not locks; strand impression comes from faceting and
material (hair.human.code-only), since this skill emits no textures.plane-card is rejected for hair (needs an alpha texture this skill cannot emit).For a CS2 item, the target is observable agreement between the supplied image and the rendered item: silhouette, proportions, edge profile, hardware layout, coating colour, pattern placement, wear, roughness response, and camera framing. Every decision must be traceable to evidence or be labelled as an approximation.
The initial CS2 family boundary covers supported knife subtypes and the Glock-18 pistol
adapter. Rifle, SMG, sniper, heavy, glove, unsupported pistol, and unknown knife subtypes must stop
with unsupported-family or unsupported-subtype; they must not receive another family's component
tree as a generic fallback.
The full layer contract (what each layer owns, must emit, and must never decide alone), the CS2
intake order, and the surface/review rule live in grimoire/intake/cs2_intake_contract.md — step
1b already requires reading it completely before intake state can advance. The canonical hand-off
is cs2-intake.json (schemaVersion: 1, states proceed | request-input | fallback | rejected | unsupported-family | unsupported-subtype); write it atomically, preserve unknown provider fields
under extensions, and never let a fallback erase prior evidence.
Subdivision runtime tests compile generated TypeScript against the showcase checkout. Set
IMG2THREEJS_SHOWCASE_ROOT to that checkout; without it, local runtime-only tests skip with an
actionable message while static contracts still run. CI should set IMG2THREEJS_REQUIRE_SHOWCASE=1
to turn a missing showcase checkout into a test failure.
IMG2THREEJS_SHOWCASE_ROOT=/path/to/img2threejs-showcase python3 forge/tests/test_subdivision.py
IMG2THREEJS_SHOWCASE_ROOT=/path/to/img2threejs-showcase python3 -m unittest discover -s forge/tests
IMG2THREEJS_SHOWCASE_ROOT=/path/to/img2threejs-showcase python3 forge/tests/test_showcase_tsc_smoke.py
TypeScript + plain Three.js unless the project uses a wrapper. Group factory
createObjectNameModel(spec, options), reconstruction data kept separate from renderer objects,
deterministic seeds for all procedural noise. Prefer primitives / Shape extrude / curve+tube /
instancing / displacement / generated canvas textures before any external art. Full geometry &
material recipes + hard-won failure patterns: grimoire/build/geometry_patterns.md.
When Python is requested for character rendering, use it as a deterministic job/evidence layer
around the browser Three.js runtime: camera-batch manifests, source/output hashes, readiness and
settle checks, screenshot persistence, masks, diagnostics, and comparison packaging. The target
Three.js browser route remains the rendering authority. Do not silently replace the procedural
TypeScript factory with Blender/VRM/GLB output. Full routing, manifest fields, and failure rules:
grimoire/build/python_threejs_render_bridge.md.
Use grimoire/readiness/standard_character_pipeline.md for character work. Beta owns the
strict sculpt/build/review gates; alpha owns deterministic camera manifests, browser screenshot
evidence and UniRig-shaped rig validation. CharacterGen, Tripo, VRM and other neural/asset
systems are opt-in adapters with source, checkpoint, license, coordinate conversion and output
hashes. They never silently replace the procedural TypeScript factory. Image-to-mesh systems emit a
static mesh with no skeleton, so their output is never animation-ready however good it looks.
Executable entry points: forge/stage4_review/render_bridge.py and
scripts/capture_threejs_playwright.py (init → browser capture → validate → diagnose; capture
must operate on the real showcase/browser route and leave readable PNGs in the workspace).
Automatically, when a task matches. Use for image-to-3D reconstruction, detail-accurate object rebuilds, stylized/likeness-maximized human characters, sculpt specs, and staged code generation.
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.