Your First Trailmap¶
This page walks one tool — myapp_login — from an empty workspace to a recorded trail
that replays it. It uses the typed scripted-tool surface (per-tool
reference) — TypeScript interfaces describe inputs,
TSDoc on the exported const becomes the LLM-facing description, and a single .ts
file is everything you write per tool. No per-tool YAML, no package.json, no
tsconfig.json to hand-author.
This is the third step of the adoption ladder outlined on the
Introduction: you’ve driven a device with the
CLI, saved and replayed a session, and now you’re giving your agent first-class
commands like login or addToCart that record, replay, and type-check the same as
tap or inputText.
For the trailmap-manifest schema, per-suffix file conventions, and dep-graph discovery rules, this page links out to Trailmaps.
Prerequisites¶
- The
trailblazeCLI on yourPATH. If you don’t have it, follow Getting Started first. - A device or browser the CLI can drive — an Android emulator, an iOS simulator, or a
local browser via the Playwright driver. The CLI’s
trailblaze device listconfirms what’s reachable. - For the smoke trail at the end you’ll need a real connected device that runs
MyApp(or substitute your own target’s package id when copying).
What you’ll create¶
my-workspace/trails/config/
├── trailblaze.yaml # workspace anchor — names the trailmap
└── trailmaps/
└── myapp/
├── trailmap.yaml # the manifest
└── tools/
└── myapp_login.ts # typed tool source — the only file you write per tool
Plus a smoke trail somewhere under your workspace:
my-workspace/trails/myapp/login/android.trail.yaml
That’s the whole footprint. trailblaze check materializes the workspace SDK, the
per-trailmap typed bindings, and the framework-managed tsconfig.json for you.
Step 1 — Anchor the workspace¶
Drop a trailblaze.yaml anywhere in your project. Its parent directory becomes the
workspace anchor:
# trails/config/trailblaze.yaml
targets:
- myapp
targets: names the trailmap ids you want to make runnable. The framework finds each
named trailmap under <anchor>/trailmaps/<id>/trailmap.yaml (workspace) or on the
classpath (framework-shipped). See Trailmaps → Discovery and precedence
for the full rule.
Step 2 — Write the trailmap manifest¶
# trails/config/trailmaps/myapp/trailmap.yaml
id: myapp
dependencies:
- trailblaze # pulls in core_interaction, navigation, …
target:
display_name: MyApp
tools:
- myapp_login # the name your .ts exports, see Step 3
platforms:
android:
app_ids: [com.example.myapp]
tool_sets:
- core_interaction
- verification
Three things to notice:
dependencies: [trailblaze]brings the framework’s standard tool sets and defaults into scope. Without it you get a minimal target with no built-in interaction tools.target.tools:lists tool names (not file paths). The loader auto-discovers everytools/*.tsthat exports atrailblaze.tool(...)declaration and resolves the names here into the runtime tool list.tool_sets:names framework-shipped bundles of Kotlin tools to expose.core_interactionbrings in primitives liketapOnElement,inputText,swipe;verificationbrings inassertVisible/assertNotVisible. See Trailmaps → Discovery for the full catalog and howdependencies:controls what’s available.
Step 3 — Write the typed .ts source¶
// trails/config/trailmaps/myapp/tools/myapp_login.ts
import { trailblaze } from "@trailblaze/scripting";
export interface MyappLoginArgs {
/** Email to type into the login form. */
email: string;
/** Password to type into the login form. */
password: string;
}
/**
* Sign into MyApp with the supplied credentials. Use this whenever the task is
* to log in, sign in, authenticate, or otherwise reach a signed-in state.
*/
export const myapp_login = trailblaze.tool<MyappLoginArgs>(
{ supportedPlatforms: ["android"], requiresContext: true },
async (input, ctx) => {
await ctx.tools.tapOnElement({ ref: "Email" });
await ctx.tools.inputText({ text: input.email });
await ctx.tools.tapOnElement({ ref: "Password" });
await ctx.tools.inputText({ text: input.password });
await ctx.tools.tapOnElement({ ref: "Sign In" });
return `Signed in as ${input.email}.`;
},
);
The ref: value ("Email", "Password", "Sign In") is what tapOnElement matches
against on the live view hierarchy — accessibility label on iOS/Android, ARIA name on
the web. The agent resolves it against the device’s native hierarchy each call; you
don’t have to compute coordinates.
What the typed surface gives you:
MyappLoginArgsbecomes the tool’s JSON Schema. Per-field TSDoc becomes each parameter’s LLM-facing description.- The TSDoc on the exported
constbecomes the tool’s description — what the agent reads when deciding whether to call this tool. - The spec object (
{ supportedPlatforms, requiresContext }) becomes the runtime’s registration gates and metadata hints — no separate_meta:YAML required. ctx.tools.<name>(args)is the typed composition surface. Every framework tool, every sibling trailmap-local tool, and every tool inherited via a dep’sexports:is a method on it. Unknown names aretscerrors.
The export name (myapp_login) is the load-bearing identifier — the manifest’s
target.tools: names this export and the runtime registers a tool by exactly that name.
See Scripted Tools (TypeScript) — The shape
for the full reference.
Step 4 — Materialize the workspace SDK¶
trailblaze check
This single command:
- Resolves the trailmap graph and emits
dist/targets/<id>.yaml. - Vendors the
@trailblaze/scripting.d.tsinto<workspace>/.trailblaze/sdk/dist/. - Emits per-trailmap typed bindings at
trailmaps/myapp/tools/trailblaze-client.d.ts. - Writes a framework-managed
trailmaps/myapp/tools/tsconfig.json(plus a.gitignorefor derived files).
After this, your IDE has full typing on ctx.tools.<name>(args) and on any
@trailblaze/scripting imports. The daemon re-runs the pipeline automatically on every
aware command; the output is idempotent.
Step 5 — See the tool in the agent’s toolbox¶
trailblaze toolbox -d android --target myapp --search myapp_login
--device (-d) is required for toolbox unless you’re asking about a single tool by
--name. The target’s platforms.<p> map decides which tools are applicable to which
device, so the listing always resolves against a real platform.
Your tool appears in the listing alongside the framework primitives that dependencies:
[trailblaze] brought in. The agent sees the same description and parameter docs your
TSDoc wrote.
Step 6 — Smoke trail¶
Write a one-step trail that calls the new tool:
# trails/myapp/login/android.trail.yaml
- config:
id: myapp/login
target: myapp
platform: android
- prompts:
- step: Sign in to MyApp
recording:
tools:
- myapp_login:
email: test@example.com
password: Password123!
- verify: The home screen is visible
Three things to know about the trail format:
- The
recording:block under a step pins which tool to dispatch (and with what args) when the trail runs in replay mode. With arecording:block, replay is deterministic — no LLM in the loop. The step’s prose (Sign in to MyApp) is preserved so a future repair flow can re-derive the recording if the UI drifts. - A bare
step:(withoutrecording:) means “ask the agent to figure this out against the live device” — the LLM picks tools and resolves selectors at runtime. Use bare steps when authoring, recordings when you want CI determinism. - The
verify:entry is Trailblaze’s vision-based assertion — an LLM judges a screenshot of the post-state against the prose claim (“The home screen is visible”). No selector to write, and it covers cases (icons, charts, layout) that DOM/a11y selectors can’t reach.
Run it:
trailblaze run trails/myapp/login/android.trail.yaml -d android
That’s the loop: edit .ts → trailblaze check → trailblaze run. Repeat until the
tool’s behavior matches what your agent should see at the top of toolbox.
Where to go next¶
- Compose multiple tools. Sibling tools call each other through
ctx.tools.<name>(args)— see thecontacts_ios_searchAndVerifyworked example. - Read a full worked target trailmap. The iOS Contacts and Wikipedia examples each ship a complete trailmap (9 tools + system prompt + trails). Copy either one as the starting shape for your own target.
- Test your tools without a device. See Scripted Tools (TypeScript) — Testing your tool for the mock client + context helpers.
- Ship the trailmap to other teams. See Publishing a Trailmap for the distribution tiers.