Use the `untaped recipe` command to apply reusable file recipes (templated files, YAML edits, copies, removals) across many directories or repos with a preview, backups and a CI drift check, and to author and test recipe packs. Use when the user mentions recipes, recipe packs, codemods, bulk or fleet-wide file changes, applying the same change to many repos, or drift checks.
Resources
1Install
npx skillscat add alexisbeaulieu97/untaped/untaped-recipe Install via the SkillsCat registry.
Untaped Recipe
Use this skill when applying reusable local file recipes across one or more
plain directories, or when authoring and testing recipe packs. The engine is
VCS-agnostic: it plans every change in memory, previews it, and only writes
after confirmation. Planning is the only execution — there are no shell steps,
no control flow in recipes, and no state or inventory.
Command tree: recipe verbs sit at untaped recipe <verb> (apply, list,get, edit, init, validate, test); packs, hooks, and backups have
their own nouns: recipe packs add|sync|list|get|edit|remove|init,recipe hooks list|get|edit|init|run, and recipe backups list|get|restore|prune.
Details that do not fit here ship next to this file:
| File | Read it when |
|---|---|
| references/apply.md | preview shapes, --parallel, input precedence and prompts, sensitive inputs, piped records, record kinds |
| references/library.md | installing, syncing, inspecting or removing packs, validate, golden test cases, the trust model, backups and restores |
| references/authoring.md | scaffolding packs, writing recipe YAML and hooks, hooks run, the built-in yaml_edit hook |
Applying recipes
untaped recipe apply <recipe> <dir>...plans, previews on stderr, confirms,
backs up, then writes. The recipe argument is a bare name (unique across
installed packs), apack/reciperef, an explicit path to arecipe.yml, or
a local pack path plus--recipe <name>. An installed pack name or a local
pack path with exactly one recipe selects that recipe; with several, the
error lists them. A value is a path only when it is.or.., starts with./,../,/, or~, or ends in.yml/.yaml— anything else is a library ref, never probed on disk.
The same target directory given twice (in any spelling) is planned once.- Pass
--yes/-yfor non-interactive applies. Backups are on by default;
use--no-backuponly when the target tree is protected another way. --dry-runplans and previews without writing or creating backups. Pack
hooks still execute for real during--dry-run(and--check): they
compute the planned changes, so a dry run is not a way to inspect an
untrusted pack.--checkis the CI/drift mode: writes nothing, creates no backups, prompts
for nothing, exits 3 when any target would change, and exits 1 when any
target fails. Rows carryaction: planned(orunchanged).- Declining the confirmation exits 1 with
cancelled; no changes made; rows
for the targets that would have changed carryaction: cancelled. --quiet/-qmutes post-run success chatter only — never preview detail,
warnings, errors, or confirmation prompts.- Failures are per-target: a target that fails to plan or write is reported and
writes nothing, while other targets proceed. Within a target, writes are
transactional and roll back on failure.
Inputs
Provide fixed values with repeated
--var KEY=VALUEand repeated--vars-file file.ymlYAML mappings: a later file wins over an earlier one,
and--varwins over every file (the same flags and precedence asawx test). Unknown input names are rejected.--vars-filevalues are YAML
(3.10→3.1,on→true): quote version-like strings.For inputs declared
listordict,--varparses the value as YAML first:--var 'cols=[name, owner]',--var 'labels={team: platform}'. Scalar
inputs keep literal-string semantics.--vars-filefiles may hold native lists
and mappings.Agents pass every required input with
--var/--vars-file, or add--non-interactive: without a terminal a missing required input fails
instead of prompting.
Pipes
apply --stdinreads targets from stdin instead of positional dirs (never
both). Lines are bare paths or untaped pipe records; lines that parse as JSON
scalars (a directory named2024) are still paths — only JSON objects with
the untaped envelope marker are records.- With
--stdin, the confirmation reads the controlling terminal; input
prompts never run. Without a terminal, apply refuses before planning (exit- unless
--yes,--dry-run, or--checkis given.
- unless
- Input
fromexpressions can read the per-target piperecord, so upstream
tool output can drive both target selection and input values. - Prefer
--format jsonfor machine-readable summaries and--format pipe
(NDJSON envelope{"untaped":"1","kind":...,"record":...}) when chaining
into other untaped tools.--columns/-cnarrows row fields.--format
and--columnsaffect stdout rows only, never the stderr preview. recipe.apply_outcomerows carry absolutetarget_path,action,files_changed,warnings(a list: accumulatedhelpers.warn(...)
messages, skipped optional transforms, a skip reason),detail(the failure
message;nullunless failed; a failed row addserror:category,system,retryable,message,hint), resolvedinputs, andrecipe(canonicalpack/recipe
ref). Actions:planned(--dry-run/--checkwould change),applied,unchanged(plan produced no writes),skipped(validate hook returnedhelpers.skip(...); not applicable, never a failure),cancelled
(confirmation declined), andfailed. Skips are success (all-skip runs
exit 0, no backup); a skip is not--checkdrift. Summary lines gain aN skippedcount.
Safety
- Installing a pack is installing code: its hooks run as the user, with full
file access, even during--dry-runand--check. Inspect an unfamiliar
pack before installing it; see references/library.md. - Exit codes: 0 success, 1 failure (a bad recipe, pack or input, a missing
name, a failing hook) or declined confirmation, 2 usage error, 3--check
drift, 4 fix the environment (uvmissing,$EDITOR, settings), 5
temporary (a git fetch timeout; retry later), 130 interrupted; a run exits
with its most severe failure. With--format jsonstderr is JSON Lines
whose errors carrycategory,system,retryableandhint.