Quick start guide/How-to's
To create a new package using the template, use either BestieTemplate.generate or BestieTemplate.new_pkg_quick.
To apply a template to an existing package, use either BestieTemplate.apply.
In this page we have gathered short examples, focused on some use cases.
For a more in-depth guide, check the Full guide
Interactive/Wizard experience
using BestieTemplate: generate
root_dir = mktempdir()
pkg_destination = joinpath(root_dir, "NewPkg.jl")
generate(
# :local or :online,
pkg_destination, # full path to the package
# Dict("Question" => Answer), # to manually set answers
# defautls = true,
# quiet = true,
# use_latest = true,
)
# Answer a bunch of questions/home/runner/work/BestieTemplate.jl/BestieTemplate.jl/docs/.CondaPkg/.pixi/envs/default/lib/python3.14/site-packages/copier/_vcs.py:381: ShallowCloneWarning: The repository '/home/runner/work/BestieTemplate.jl/BestieTemplate.jl' is a shallow clone, this might lead to unexpected failure or unusually high resource consumption.
warn(
/home/runner/work/BestieTemplate.jl/BestieTemplate.jl/docs/.CondaPkg/.pixi/envs/default/lib/python3.14/site-packages/copier/_vcs.py:407: DirtyLocalWarning: Dirty template changes included automatically.
warn(Quick Tiny package
A minimalist package.
using BestieTemplate: new_pkg_quick
root_dir = mktempdir()
pkg_destination = joinpath(root_dir, "TinyPackage.jl")
package_owner = "JuliaBesties"
authors = "JuliaBesties maintainers"
new_pkg_quick(
pkg_destination,
package_owner,
authors,
:tiny,
)
# Resulting folder:- TinyPackage.jl/
- .copier-answers.yml
- .gitignore
- LICENSE
- Project.toml
- README.md
- src/
- TinyPackage.jl
- test/
- Project.toml
- runtests.jlQuick Light package
The common niceties: documentation, CI, .JuliaFormatter.toml and other config files that you might want to use (but won't affect you if you don't).
using BestieTemplate
pkg_destination = joinpath(root_dir, "LightPackage.jl")
package_owner = "JuliaBesties"
authors = "JuliaBesties maintainers"
new_pkg_quick(
pkg_destination,
package_owner,
authors,
:light,
)
# Resulting folder:Everything from the level above plus:
- LightPackage.jl/
- .JuliaFormatter.toml
- .editorconfig
- .lychee.toml
- .markdownlint.json
- .yamlfmt.yml
- .yamllint.yml
- codecov.yml
- .github/
- PULL_REQUEST_TEMPLATE.md
- dependabot.yml
- workflows/
- Docs.yml
- ReusableTest.yml
- TagBot.yml
- Test.yml
- TestOnPRs.yml
- docs/
- Project.toml
- make.jl
- src/
- 95-reference.md
- index.md
- test/
- test-basic-test.jlQuick Moderate package
Opinionated suggestions for more stable packages without sacrificing too much development speed.
using BestieTemplate
pkg_destination = joinpath(root_dir, "ModeratePackage.jl")
package_owner = "JuliaBesties"
authors = "JuliaBesties maintainers"
new_pkg_quick(
pkg_destination,
package_owner,
authors,
:moderate,
)
# Resulting folder:Everything from the level above plus:
- ModeratePackage.jl/
- .pre-commit-config.yaml
- .github/
- workflows/
- Lint.ymlQuick Robust package
Opinionated selection to help with larger packages and more developers.
using BestieTemplate
pkg_destination = joinpath(root_dir, "RobustPackage.jl")
package_owner = "JuliaBesties"
authors = "JuliaBesties maintainers"
new_pkg_quick(
pkg_destination,
package_owner,
authors,
:robust,
)
# Resulting folder:Everything from the level above plus:
- RobustPackage.jl/
- .all-contributorsrc
- CHANGELOG.md
- CITATION.cff
- CODE_OF_CONDUCT.md
- .github/
- ISSUE_TEMPLATE/
- 10-bug-report.yml
- 20-feature-request.yml
- 30-usage.yml
- 99-general.yml
- config.yml
- docs/
- src/
- 90-contributing.md
- 91-developer.mdApply to an existing package
Here is an example of applying the template to an existing package.
This is the existing package:
pkg_destination = joinpath(root_dir, "ExistingPackage.jl")Dict{String, Any} with 3 entries:
"PackageOwner" => "JuliaBesties"
"Authors" => "JuliaBesties maintainers"
"AddPrecommit" => trueNow we apply the template.
using BestieTemplate: apply
apply(
# :local or :online,
pkg_destination, # full path to the package
# Dict("Question" => Answer), # to manually set answers
# defautls = true,
# quiet = true,
# use_latest = true,
)
# You will be asked questions. For instance, if we only select to add pre-commit, this would be the result:- ExistingPackage.jl/
- .JuliaFormatter.toml
- .copier-answers.yml
- .editorconfig
- .gitignore
- .lychee.toml
- .markdownlint.json
- .pre-commit-config.yaml
- .yamlfmt.yml
- .yamllint.yml
- LICENSE
- Project.toml
- README.md
- codecov.yml
- .github/
- PULL_REQUEST_TEMPLATE.md
- dependabot.yml
- workflows/
- Docs.yml
- ReusableTest.yml
- TagBot.yml
- Test.yml
- TestOnPRs.yml
- docs/
- Project.toml
- make.jl
- src/
- 95-reference.md
- index.md
- src/
- ExistingPackage.jl
- test/
- Project.toml
- runtests.jl
- test-basic-test.jlChange details with new_pkg_quick
For more details on the hidden options see the [Advanced options and non-interactive answers](@ref advanced_options section.
using BestieTemplate: new_pkg_quick
pkg_destination = joinpath(root_dir, "TinyPackage.jl")
package_owner = "JuliaBesties"
authors = "JuliaBesties maintainers"
# Explicitly setting options
extra_data = Dict(
"JuliaMinVersion" => "1.0", # From the essential questions that `:tiny` autocompletes
"AddDocs" => true, # From the :light strategy
"AddLintCI" => true, # From the :moderate strategy
"AddAllcontributors" => true, # From the :robust strategy
"AddPrecommitUpdateCI" => true, # From the hidden options
)
new_pkg_quick(
pkg_destination,
package_owner,
authors,
:tiny,
extra_data,
)
# Resulting folder: (Notice the new files in comparison to :tiny- TinyPackage.jl/
- .all-contributorsrc
- .copier-answers.yml
- .gitignore
- LICENSE
- Project.toml
- README.md
- .github/
- workflows/
- Lint.yml
- PreCommitUpdate.yml
- docs/
- Project.toml
- make.jl
- src/
- 95-reference.md
- index.md
- src/
- TinyPackage.jl
- test/
- Project.toml
- runtests.jlAdding features directly via bestie CLI
If you only want one or two files from the template — a CHANGELOG.md, a dependabot.yml, a pre-commit config — without installing Julia or going through the full apply/update flow, use the experimental bestie-template Python package. It only needs uv on the PATH, no install step required:
uvx --from bestie-template bestie list-features
uvx --from bestie-template bestie add-feature changelog,dependabot path/to/MyPackage.jl(--from bestie-template is needed because the PyPI package is named bestie-template, while the command it installs is bestie.)
list-features prints every feature bestie can add, along with the answers each one needs and the files it writes; pass --json for a machine-readable version. add-feature then applies one or more features (comma-separated, no spaces) to the package at the given path — each feature writes only the files it owns, leaving the rest of the package untouched.
A few things worth knowing before running it:
- It overwrites without asking. A feature replaces its own files outright — no diff, no conflict prompt, no backup. Commit or stash first, so
git diff/git checkoutremains your way to undo it. - Some features need existing answers. A few features (e.g.
lint_action) read.copier-answers.ymlto decide what to render, and refuse to run without it. Checklist-features --jsonfor an_explicitvariant first — it takes the same information as-d KEY=VALUEflags instead. - Version pinning.
--ref vX.Y.Zpins the template version used to render the feature; omit it to use the latest release, or pass--ref mainfor a feature that has not shipped in a release yet.
For AI coding agents, this same workflow is packaged as the bestie-features skill, which teaches an agent the feature names, how to resolve the answers each one needs, and the follow-up work some features require. Install it with npx skills add JuliaBesties/BestieTemplate.jl.
Adding a single feature with add_feature
The same idea is available from Julia, through BestieTemplate.add_feature: apply one named slice of the template — say, just the changelog — to an existing package, without going through the full apply/update flow.
Here, AnswerPackage.jl already has a .copier-answers.yml (from new_pkg_quick/generate/apply), so add_feature reads the answers it needs — PackageOwner, PackageName — straight from it:
using BestieTemplate: add_feature
pkg_destination = joinpath(root_dir, "AnswerPackage.jl")
add_feature(
:changelog,
pkg_destination,
)
# Resulting folder: (Notice the new CHANGELOG.md)- AnswerPackage.jl/
- .copier-answers.yml
- .gitignore
- CHANGELOG.md
- LICENSE
- Project.toml
- README.md
- src/
- AnswerPackage.jl
- test/
- Project.toml
- runtests.jlOn a package with no .copier-answers.yml, add_feature still guesses what it can from the package itself — PackageName, Authors and JuliaMinVersion from Project.toml, PackageOwner from docs/make.jl if present, indentation from .JuliaFormatter.toml — and only complains about what's left. For a :tiny package, which has no docs/make.jl, that's PackageOwner:
julia> add_feature(:changelog, pkg_destination)
ERROR: Cannot determine required fields: PackageOwner.
Pass them via the `data` argument or run `BestieTemplate.apply` first.Pass whatever it names through the data argument: add_feature(:changelog, pkg_destination, Dict("PackageOwner" => "JuliaBesties")).
As with add-feature from the CLI, this overwrites the feature's files outright, and a git repository is your only way to undo it. See the BestieTemplate.add_feature docstring for the full list of features and their required answers, and the Full guide for how the recorded template version is affected.