Required input — ask, never invent. This skill needs a source (an API/server URL, a spec URL/file, a
HAR, or an access log). If the invocation names none, STOP and ask the user. Never invent a URL, host,
or target; for an access log also ask for the --target (the log carries no host).
-
Binary + cwd (one-time): run every command below from the repo root, or set
REPO=$(git rev-parse --show-toplevel) and prefix paths ("$REPO/bin/tmula", "$REPO/.claude/...") so
they survive a cwd reset. Build once: go build -o ./bin/tmula ./server/cmd/tmula.
-
Resolve the input → a local spec/log file + a default target. For a URL, use the tested helper
(fetches, follows an HTML Swagger-UI page to the real spec, normalizes to JSON, derives a target):
eval "$(./.claude/skills/tmula-scaffold/scripts/fetch-openapi.sh <url>)"
echo "$SPEC $TARGET"
For a local file, SPEC=<path>. For an access log there is no target in the data — you must supply one.
-
Confirm the target base URL ($TARGET, else derive):
- OpenAPI 3: first absolute
servers[].url; Swagger 2: schemes[0] + host + basePath.
- Relative server (e.g. Petstore v3
servers:[{url:"/api/v3"}]) or none → tmula init errors
"could not determine a target URL… pass --target". Derive from the Swagger URL's origin + the
relative path (.../api/v3/openapi.json + /api/v3 → https://host/api/v3).
- No server info at all → the helper falls back to the bare origin, which likely omits the basePath —
confirm/edit it. Always show the final target to the user.
-
Import to a scenario (init takes a FILE, not a URL — fetch first). init auto-detects the format,
or pass --format openapi|har|accesslog:
./bin/tmula init --from "$SPEC" --target "$TARGET" --out /tmp/scenario.yaml
- OpenAPI/Swagger → one step per path+method, in a journey-heuristic order (auth/login first,
then browse → search → view → cart → pay; unrecognized ops fall to a neutral middle, ties broken by
path depth then path then read-before-write) — not spec order, and not a real journey: mutating
ops sort in among the reads (e.g. Petstore v3 lands
POST /pet, PUT /pet near the top). Reshape it.
- HAR → one step per recorded request, in capture order (a session replay; query strings kept).
- Access log → a graph-first scenario: a branching behavior graph with transition weights, arrival
rate, and think time learned from the traffic (
--target required; init prints coverage + skipped
lines). This is the richest scaffold — keep the graph/templates/start form, don't flatten it.
-
Emit json/scenario.json (the artifact contract is JSON under json/; tmula init writes YAML, so convert):
mkdir -p json
python3 -c "import sys,yaml,json; json.dump(yaml.safe_load(open('/tmp/scenario.yaml')), open('json/scenario.json','w'), indent=2)"
sigs.k8s.io/yaml means tmula runs JSON and YAML identically; we standardize on scenario.json.
(Needs PyYAML — if import yaml fails, pip install pyyaml, or just keep scenario.yaml: tmula reads
either, and the rest of the suite accepts a .yaml scenario too.)
-
Report the scaffold honestly and hand off. State: target, # of steps/nodes, format, and that it is a
raw scaffold — path params are literal ({id}), there is no auth, and destructive ops are present.
Then: "Run tmula-enrich to substitute path params, wire auth, and filter destructive ops before
running."
-
Web console artifacts (only if you'll use the :8080 console). The console's two manual-edit fields
take separate JSON — the Scenario graph (graph) and API templates (templates) — not the
compact scenario. Produce them in the exact field format by importing the source on a running engine,
and save the raw source for the Import field:
cp "$SPEC" json/source.json
./bin/tmula --addr :8080 &
./.claude/skills/tmula-scaffold/scripts/to-console.sh http://localhost:8080 "$SPEC" auto
These come from the source via the same /api/import the console's Import button uses, so they are
the import baseline — paste them into the fields and edit there (path params, auth) just as
tmula-enrich does for the CLI json/scenario.json. (Skip this step for a CLI-only run.)