npx skills add ...
npx skills add darkmatter/prelude --skill prelude-just
Use when authoring or editing Justfile recipes in a repository wired to Prelude, when the user asks to add just recipes or make them discoverable in the x menu, or when a recipe needs descriptions, typed arguments, groups, aliases, or module namespacing. Covers recipe authoring for Prelude's runtime Justfile import and verification through x.
npx skills add darkmatter/prelude --skill prelude-just
The Justfile stays the canonical owner of task semantics; Prelude imports it at
runtime — recipes are never copied into prelude.commands. Treat the user's
request as the specification, never as shell text to interpolate. Bind every
recipe to the project's real canonical command — the invocation the tool owner
already uses (bun run test, go test ./..., nix flake check) — and confirm
it actually exists in the repo before writing it; never invent placeholder
scripts.
In the flake-parts module (sidecar prelude.nix or inline configuration):
With config.packages.prelude-shell in the consumer devshell, enabling both
options provides x and just. The Justfile import flag alone does not enable
the menu. See /prelude-install if shell integration is missing.
Attribute support is version-gated in
just ([metadata] 1.42+, [arg(long/short)] 1.46+, [arg(pattern)] 1.45+,
[arg(min/max)] 1.56+) and the import itself only maps fields the consumer's
locked Prelude revision knows — check just --version and confirm against the
locked prelude input before relying on an attribute. An existing
prelude.commands.<name> entry always wins over a same-named recipe.
The menu reads just --dump --dump-format json; only these fields surface:
# doc comment → menu description[group('ci')] → menu group; the key stays flat (x check)alias ship := deploy → its own entry, inheriting target's doc and groupmod ops 'ops.just' → ONE x ops row; Enter (or bare x ops) opens a subcommand
picker over the module's recipes, while recipes dispatch publicly via parent routing
(x ops migrate, or x ops <recipe> [args…]). The recipe executes internally via its
canonical command (just ops::migrate). Imported module recipes cannot be selected
through double-colon keys (x ops::migrate). A module de-clutters the menu instead of
adding one row per recipe. Modules do not inherit the parent Justfile's set settings
or recipes; module recipes need their own private helpers (dependencies cannot cross module boundaries).[group('ops')] on a module recipe → the recipe escapes the submenu and stays top-level
in that group in the menu, while remaining callable via parent routing (x ops migrate)[private] or leading _ → omitted from the menu (recipes and aliases)[metadata('just test --fix')] → one worked Example row in the details pane[arg('package', long)] → --package option in argument entry[arg('dry', long, flag)] → toggleable flag in argument entry[arg('kind', pattern='staging|production')] → pickable options (plain pipe
alternation; a regex like [a-z-]+ is enforced but renders no options)package="./...") → optional arg pre-filledCanonical shape — adapt these commands to the project's actual tooling:
with e2e.just carrying its own private helpers:
$package exports the parameter into the recipe environment; "$package"
preserves it as one shell argument. Raw {{…}} interpolation inserts text into
shell source; merely surrounding it with quotes does not make arbitrary values
safe. Prefer exported parameters with quoted shell expansion.
Rules:
[group] over renaming — the public key stays x test.long/short options in particular change positional usage.
Preserve existing invocation forms unless the task calls for changing them.
Private/group/metadata attributes affect visibility or presentation and
also need support from the installed Just version.x coder-e2e →
x e2e coder, internal just e2e::coder); do it only when the user asks for
that reorganization, and update any docs that show the old invocation.x <recipe> [args…] currently joins extra arguments with spaces and executes
the assembled command through sh -c. Original argument boundaries are lost:
quotes used by the calling shell do not protect multiword values, and embedded
metacharacters can become shell syntax. Use direct just <recipe> "$value"
for multiword or untrusted values, with safe parameter handling in the recipe
itself. Do not describe x forwarding as argv-preserving.
Bare x <recipe> opens interactive argument entry when parameters exist.
Bare x <module> opens the module's subcommand picker. Module recipes dispatch as
x <module> <recipe> [args…] (or nested x <parent> [submodule…] <recipe> [args…]),
executing the canonical just <module>::<recipe> command internally. Aliases dispatch as
x ship.
Menu entries are discovery only. A useful workflow doc covers prerequisites,
arguments, worked examples, effects, and troubleshooting — write that in the
project's docs pages or README (see /prelude-docs), and mirror the worked
invocations with one [metadata('just <recipe> <example>')] line each so the
menu details pane shows them.
Read the recipe before running anything: just evaluates backticks when the
file loads, so even --dry-run can execute commands. Then, inside the shell:
References: Prelude import guide — nix run github:darkmatter/prelude#skill -- guide command-conventions; just manual — https://github.com/casey/just.
Initial flake wiring: /prelude-install.
# run the unit test suite
[group('ci')]
test:
bun run test
# run go tests for one package
# (add [group('go')] if a go group is wanted)
[arg('package', help='import path, or ./... for all')]
test-go $package="./...":
go test "$package"
# E2E suites grouped behind one menu row: `just e2e <suite>`
mod e2e 'e2e.just'[private]
guard:
@test -n "${IN_NIX_SHELL:-}"
# run the desktop e2e suite
[metadata('just e2e desktop main')]
[arg('revision', help='branch, commit, PR number or URL')]
desktop $revision="main": guard
bun run desktop-test smoke "$revision"just --summary # new recipe listed
just --dump --dump-format json # the exact payload the menu imports
x --list # module appears as one row; children indented
just --dry-run <recipe> [args…] # after inspection: prints lines without running them
x <recipe> # exercise a safe no-argument recipe