Yalla Nkardaz’s personal CLI toolkit
Project description
YAL
YAL is a command-line utility for project initialization and updates based on templates from git repositories.
It also supports project-local commands (similar to Makefile targets or scripts
in package.json). Define them in .yal/project.yml and invoke them with
yal <command>.
Installation
pip install yal-cmd
Requires Python >= 3.12
Dependencies:
- requests >=2.34.2
- ruamel.yaml >=0.19.1
- tomli-w >=1.2.0
Quick Start
# Create a project from the built-in book template
yal new book
YAL downloads the template (once — it’s cached afterward), then walks you
through a few interactive prompts (defined by the template’s
.yal/template.yml): project name, author, license, and so on. Answer them, and
a new folder appears — named after your project, or book-<version> if the
template doesn’t define a custom name.
cd book-1.7.1 # or whatever you named it
That’s the whole loop. The new folder contains a .yal/project.yml recording
which template and version it came from.
Don’t have a template? Use any repo as one
You don’t need a .yal/template.yml to get started — any git repository works,
even an empty one:
yal add demo:my-app from <user>/my-repo
yal new demo:my-app
Without a .yal/template.yml, YAL just copies the repo as-is and creates a
default .yal/project.yml for you. Add field prompts and file templating later,
once you actually need them — see .yal/template.yml below.
Running project commands
If the resulting .yal/project.yml has [[command]] entries (your own, or ones
shipped by the template), run them the same way you ran yal new:
yal <command-name>
See .yal/project.yml for how to define them.
Commands
yal new <kind>[:<name>][@<ref>] [--commit]— uses a template to create a new project; if name is not specified, thedefaulttemplate will be usedyal update <kind>[:<name>] [--commit]— downloads and caches the latest version of a templateyal add <kind>:<name>[@<ref>] from <repository URL | user/repo> [--commit]— registers an external template from GitHub with the specified nameyal remove <kind>[:<name>[@<version>]]— removes a template from the local storage; if name is not specified, all templates of the given kind will be removed
--commit (on new, update, add) skips release lookup entirely and
resolves against the latest matching commit instead — useful when a repository
has tagged releases but you specifically want unreleased work.
Create a project
Downloads the template (if not cached locally) and initiates the configuration process.
# Basic usage — uses the “default” template, latest version
yal new book
# Specify a template by name
yal new book:my-theme
# Specify a version (release tag or commit hash)
yal new book@1.7.1
yal new book@c651f7d
# Combined
yal new book:my-theme@1.7.1
# Skip releases, use the latest commit instead
yal new book --commit
If the requested version is already cached locally, YAL skips the download. If
no version is specified, YAL uses the most recent locally cached version; if
none is cached, it downloads the latest release (or the latest commit if the
repository has no releases). Pass --commit to skip the release lookup entirely
and resolve straight to the latest matching commit, even when releases exist.
Update a template
Downloads and caches the latest version of a template without creating a project. Useful for pre-caching before working offline.
yal update book
yal update book:my-theme
# Skip releases, update to the latest commit instead
yal update book --commit
Add an external template
Registers a GitHub repository as a named template under a given kind, then downloads it.
# Register and download the latest release (or commit)
yal add book:my-theme from https://github.com/user/my-book-template
# shortcut for GitHub
yal add book:my-theme from user/my-book-template
# or
yal add book:my-theme <other git service/local repo>
# Register a specific version
yal add book:my-theme@1.2.0 from user/my-book-template
# Skip releases, register and download the latest commit instead
yal add book:my-theme from user/my-book-template --commit
<kind>:<name> must not collide with a built-in template —
yal add book:default from ... is rejected, since book:default already ships
with YAL. Register under a different name instead (e.g. book:my-theme).
After registration the template is available like any built-in one:
yal new book:my-theme
yal update book:my-theme
Registered templates are stored in ~/.yal/user-templates.toml. Downloaded
files go to ~/.yal/user-templates/<kind>/<name>/<version>/.
Git
You can use any Git hosting service or a local repository. Built-in shortcuts are available for a number of services:
user/repo # Github
gitlab:user/repo
codeberg:user/repo
bitbucket:user/repo
git.gay:user/repo
gitverse:user/repo
sourceforge:project/repo
sourceforge:user@project/repo
yal add my-sf:default from sourceforge:my-project/code
Local repository:
yal add my-local-repos:default from /path/to/local/repo
Remove a template
Removes cached template files from local storage. When all versions of a user-registered template are removed, its registry entry is also deleted.
# Remove all cached versions of all “book” templates
yal remove book
# Remove all cached versions of a specific template
yal remove book:default
yal remove book:my-theme
# Remove a specific version only
yal remove book:default@1.7.1
yal remove book:my-theme@c651f7d
.yal/project.yml
A configuration file placed in the root of the created project. It stores metadata about the template used and lets you define local commands for the project.
origin:
template: book
template-version: '9670322' # commit hash, or a release tag like "1.7.1"
created-at: '2026-05-31T03:16:33Z'
yal-version: '0.1.1'
command:
- name: make
script: /build/build.py # leading "/" = project root, not filesystem root
exec: python3
arguments: # {} means any value is accepted
--mode: []
# yal make → python3 build/build.py
# yal make --mode=print → python3 build/build.py --mode=print
- name: make print
macros: make --mode=print
# yal make print → expands to → yal make --mode=print
Inline scripts
script doesn’t have to be a path — it can contain the code itself as a
multi-line string. In that case exec is required (there’s no file extension to
infer the interpreter from), and the code is passed directly to the interpreter
via its own "run code" flag (-c/-e/-r) — nothing is written to disk.
command:
- name: hello
exec: python3
script: |
def greet():
print("Hello from an inline script!")
greet()
Supported exec values for inline scripts: python3/python,
node/ts-node, ruby, perl, php, bash/zsh/fish/sh, os-bash,
lua. Arguments (arguments = {...}) work the same as with file scripts — with
one exception: lua doesn’t support passing extra arguments to inline code (its
CLI always treats the first extra argument as a script file to run), so
.yal/project.yml rejects arguments on lua inline scripts rather than
silently dropping them.
If .yal/project.yml is not present in the template, it will be created
automatically with a default [origin] section filled in on project creation.
Commands
Each [[command]] entry supports:
| Field | Description |
|---|---|
name |
Command name as typed on the CLI (spaces allowed for multi-token names) |
script |
Path to the script file. Leading / is relative to the project root |
exec |
Interpreter: python3, node, bash, os-bash, etc. Auto-detected from file extension if omitted |
arguments |
Allowed flags and their accepted values. Empty list [] accepts any value |
macros |
Expands to another command instead of running a script |
exec = "os-bash" uses bash -c on Unix and cmd /c on Windows.
Arguments are passed as --key=value or --key value:
yal make --mode=print
yal make --mode print
.yal/template.yml
A configuration file placed in the root of a template repository. When present,
YAL uses it to interactively prompt the user for values during yal new, then
applies them to target files in the new project.
meta:
yal-min-version: '0.1.3'
exclude: ['examples/'] # paths not copied into the project
actions:
pre:
- cmd: echo "Preparing project..."
- cmd: mkdir -p .temp
post:
- cmd: git init --initial-branch=main
- cmd: npm install
if: use-npm
- cmd: code "{project_path}"
if: open-in-code
os: windows
fields:
- id: project-name
type: text
required: true
is-folder-name: true # this value becomes the output folder name
- id: author
type: text
default: Anonymous
- id: license
type: select
options: ['MIT', 'Apache-2.0', 'GPL-3.0']
default: MIT
- id: use-npm
type: boolean
default: false
- id: open-in-code
type: boolean
default: false
messages:
project-name:
prompt: Project name
author:
prompt: Author name
placeholder: Your Name
license:
prompt: License
use-npm:
prompt: Install npm dependencies?
open-in-code:
prompt: Open project in VS Code?
ru:
project-name:
prompt: Название проекта
author:
prompt: Имя автора
use-npm:
prompt: Установить npm зависимости?
open-in-code:
prompt: Открыть проект в VS Code?
Actions
The actions section defines commands to run before and after project creation.
Commands support conditional execution and OS-specific filtering.
Structure:
actions:
pre:
- cmd: echo "Starting..."
- cmd: mkdir -p temp
post:
- cmd: git init --initial-branch=main
- cmd: npm install
if: use-npm
- cmd: code "{project_path}"
if: open-in-code
os: windows
Fields:
| Field | Description |
|---|---|
pre |
Commands executed before interactive field collection |
post |
Commands executed after project creation |
cmd |
The command to run (supports {field} interpolation) |
if |
Condition expression (same syntax as show-if) |
os |
Restrict command to specific OS: windows, linux, or macos |
Conditional commands:
actions:
post:
- cmd: npm install
if: use-npm
- cmd: pip install -r requirements.txt
if: use-python
- cmd: code "{project_path}"
if: open-in-code and project-name != ""
OS-specific commands:
actions:
post:
- cmd: timeout /t 1 /nobreak > nul && code "{project_path}"
os: windows
if: open-in-code
- cmd: sleep 1 && code "{project_path}"
os: linux
if: open-in-code
- cmd: sleep 1 && code "{project_path}"
os: macos
if: open-in-code
Special variables:
{project_path}— absolute path to the created project folder
Fields
Each [[fields]] entry supports:
| Field | Description |
|---|---|
id |
Unique identifier, used as key in mappings and message lookups |
type |
Input type: text, number, list, select, multi-select, or boolean (see Field types below) |
required |
If true, the user must provide a non-empty value (for multi-select, at least one item) |
default |
Default value used if the user presses Enter without typing. Use "{placeholder}" to mirror the placeholder. For boolean/multi-select, a native TOML true/false or array also works |
options |
The list of choices — required for select and multi-select |
is-folder-name |
If true, the field’s value becomes the name of the created project folder (only meaningful for text) |
min |
Minimum value for number fields (inclusive) |
max |
Maximum value for number fields (inclusive) |
pattern |
Regular expression for text field validation (Python regex syntax, uses re.fullmatch) |
allow-custom |
If true, allows entering custom values in select and multi-select fields |
min-cols |
Minimum number of columns in interactive picker (default: 1). Only effective when terminal is wide enough |
show-if |
Conditional expression to show/hide the field based on other fields (see Conditional fields below) |
Field types
text — free-form input. options, if given, is just a hint and isn’t
enforced.
number — an integer or float.
list — one or more values, entered one per line (press Enter after each, then an empty line to finish).
fields:
- id: keywords
type: list
default: ['typst', 'book', 'publishing']
select — exactly one value from options. In an interactive terminal this
renders as an arrow-key picker (↑/↓ to move, Enter to confirm); when stdin
isn’t a real terminal (pipes, CI), it falls back to typed input validated
against options.
fields:
- id: license
type: select
options: ['MIT', 'Apache-2.0', 'GPL-3.0']
default: MIT
multi-select — zero or more values from options, picked with Space to
toggle and Enter to confirm (or comma-separated typed input as a fallback).
The collected value is a list and is written natively to YAML/JSON/TOML targets;
for .env targets it’s joined with commas.
fields:
- id: features
type: multi-select
options: ['auth', 'billing', 'search']
default: ['auth', 'search'] # or default: "auth,search"
boolean — a yes/no prompt ([y/N]/[Y/n] depending on default).
Written as a native true/false to YAML/JSON/TOML, and as the string
"true"/"false" to .env.
fields:
- id: use-ci
type: boolean
default: true
An unknown type, or a select/multi-select without options, falls back to
plain text input with a warning — it won’t crash project creation.
Conditional fields
The show-if attribute lets you conditionally show or hide fields based on
values of previously answered fields. It uses a simple expression language with
logical operators.
Supported operators:
- Comparison:
==,!=,<,<=,>,>= - Membership:
in,not in - Logical:
and,or,not - Grouping: parentheses
( ) - Truthiness:
fieldalone checks for truthiness
Examples:
fields:
- id: use-ci
type: boolean
default: false
- id: ci-provider
type: select
options: ['github-actions', 'gitlab-ci', 'circle-ci']
show-if: use-ci
# Only shown if use-ci is true
- id: enterprise-plan
type: select
options: ['basic', 'pro', 'enterprise']
show-if: features in ['enterprise']
# Only shown if 'enterprise' is selected in the 'features' multi-select field
- id: deployment-type
type: select
options: ['dev', 'staging', 'production']
show-if: ci-provider == 'github-actions' and use-ci
# Only shown for GitHub Actions CI
Targets
After collecting field values, YAL writes them into files specified under
[[targets]]. Supported formats: yaml, json, toml, env.
targets:
- file: /config/meta.yaml
format: yaml
fields:
- key: project.name # dot-separated path
field: project-name # use value from fields id="project-name"
- key: project.created
value: ${DATE} # built-in generator
- key: project.uuid
value: ${UUID}
- key: meta.copyright
value: '© {author}, ${YEAR}' # interpolation + generator
- key: app.port
value: ${NULL} # sets YAML key to ~ (null), JSON to null
Path syntax for nested keys:
- project.name → sets project.name in the target file
- app[0].url → sets the first element’s url field
- database[0] → sets the first element of the array
Field mapping options:
- field — use value from a [[fields]] entry by its id
- value — use a literal value or generator expression (e.g., "${DATE}", "© {author}")
- fallback — fallback value if the primary field or value resolves to empty ("", [], or None)
.env file support
Target format env handles .env files:
targets:
- file: /.env
format: env
fields:
- key: APP_NAME
field: project-name
- key: APP_PORT
field: port
YAL preserves comments and empty lines, updates existing variables in place, and adds new ones at the end. Values with spaces or special characters are automatically quoted.
Built-in generators
| Syntax | Output |
|---|---|
${UUID} |
Random UUID4 |
${DATE} |
Current date YYYY-MM-DD (UTC) |
${YEAR} |
Current year |
${MONTH} |
Current month |
${DAY} |
Current day |
${TIMESTAMP} |
Unix timestamp (seconds) |
${RANDOM} |
Random integer 0 – 2³¹−1 |
${NULL} |
Sets the YAML key to ~ (null) |
Field values can be interpolated with {field-id} syntax and combined freely
with generators: "© {author}, ${YEAR}".
Option localization and labels
For select and multi-select fields, you can localize option display names
and add descriptions:
messages:
genre:
prompt: Select book genre
option:
Fantasy: Fantasy
Sci-Fi: Science Fiction
Mystery: Mystery
label:
Fantasy: Magic, mythical creatures, imaginary worlds
Sci-Fi: Future, technology, space exploration
Mystery: Crime, detective work, suspense
ru:
genre:
prompt: Выберите жанр книги
option:
Fantasy: Фэнтези
Sci-Fi: Научная фантастика
Mystery: Детектив
label:
Fantasy: Магия, мифические существа, вымышленные миры
Sci-Fi: Будущее, технологии, космические путешествия
Mystery: Преступления, расследования, саспенс
When displayed in the picker, options show both the display name and description:
Fantasy — Magic, mythical creatures, imaginary worlds
Science Fiction — Future, technology, space exploration
Mystery — Crime, detective work, suspense
The stored value remains the original option key ("Fantasy", "Sci-Fi",
"Mystery"), not the display name.
Localization
Messages support per-language overrides. YAL detects the system locale automatically and falls back to the base (top-level) messages if no translation is found for the current language.
[messages]
project-name.prompt = "Project name" # base (fallback)
[messages.ru]
project-name.prompt = "Название проекта"
Built-in templates
<Book> Typst Book Template
- Repo: https://github.com/DemerNkardaz/Typst-Book-Template
- Name:
default - Use:
yal new book
A template for creating a book using Typst, including a prebuilt structure, plugins, styles, settings, and a build system combining Typst and Python tools.
Requirements: Typst compiler and the following Python packages:
pip install pyyaml pikepdf pillow
Custom templates
Any public repository can be registered as a template. The repository does not
need to contain a .yal/template.yml — YAL will copy it as-is and create a
default .yal/project.yml in the resulting project.
# Register under a new kind "vue", name "default"
yal add vue:default from <user>/my-vue-template
# Create a project from it
yal new vue
You can register multiple named templates under the same kind:
yal add vue:tailwind from <user>/vue-tailwind-template
yal add vue:minimal from <user>/vue-minimal-template
yal new vue:tailwind
yal new vue:minimal
Full example with .yal/template.yml
meta:
yal-min-version: '0.1.3'
actions:
pre:
- cmd: echo "Initializing project..."
post:
- cmd: git init --initial-branch=main
- cmd: pip install -r requirements.txt
if: use-python
- cmd: npm install
if: use-npm
- cmd: code "{project_path}"
if: open-in-code
os: windows
- cmd: sleep 1 && code "{project_path}"
if: open-in-code
os: linux
- cmd: sleep 1 && code "{project_path}"
if: open-in-code
os: macos
fields:
- id: book-title
type: text
required: true
is-folder-name: true
- id: author
type: text
default: '{placeholder}'
- id: genre
type: select
options:
- fantasy
- scifi
- mystery
- romance
default: fantasy
- id: features
type: multi-select
options:
- glossary
- illustrations
- index
- bibliography
default:
- glossary
- bibliography
- id: use-typst
type: boolean
default: true
- id: use-python
type: boolean
default: false
- id: use-npm
type: boolean
default: false
- id: open-in-code
type: boolean
default: false
- id: output-format
type: select
options: ['pdf', 'html', 'epub']
show-if: use-typst
default: pdf
targets:
- file: /book.yaml
format: yaml
fields:
- key: book.title
field: book-title
- key: book.author
field: author
- key: book.genre
field: genre
- key: book.features
field: features
- key: book.created
value: ${DATE}
- file: /.env
format: env
fields:
- key: BOOK_TITLE
field: book-title
- key: BOOK_AUTHOR
field: author
- key: OUTPUT_FORMAT
field: output-format
fallback: pdf
messages:
book-title:
prompt: Book title
author:
prompt: Author
placeholder: Your Name
genre:
prompt: Select genre
option:
fantasy: Fantasy
scifi: Science Fiction
mystery: Mystery
romance: Romance
label:
fantasy: Magic, mythical creatures, imaginary worlds
scifi: Future, technology, space exploration
mystery: Crime, detective work, suspense
romance: Love stories, relationships, emotions
features:
prompt: Select book features
option:
glossary: Glossary
illustrations: Illustrations
index: Index
bibliography: Bibliography
label:
glossary: Terms and definitions
illustrations: Images and diagrams
index: Keyword index
bibliography: References and sources
use-typst:
prompt: Use Typst for typesetting
use-python:
prompt: Use Python dependencies?
use-npm:
prompt: Use npm dependencies?
open-in-code:
prompt: Open project in VS Code?
output-format:
prompt: Output format
ru:
book-title:
prompt: Название книги
author:
prompt: Автор
placeholder: Ваше имя
genre:
prompt: Выберите жанр
option:
fantasy: Фэнтези
scifi: Научная фантастика
mystery: Детектив
romance: Романтика
label:
fantasy: Магия, мифические существа, вымышленные миры
scifi: Будущее, технологии, космические путешествия
mystery: Преступления, расследования, саспенс
romance: Любовные истории, отношения, эмоции
features:
prompt: Выберите элементы книги
option:
glossary: Глоссарий
illustrations: Иллюстрации
index: Индекс
bibliography: Библиография
label:
glossary: Термины и определения
illustrations: Изображения и диаграммы
index: Ключевые слова
bibliography: Ссылки и источники
use-typst:
prompt: Использовать Typst для вёрстки
use-python:
prompt: Использовать Python зависимости?
use-npm:
prompt: Использовать npm зависимости?
open-in-code:
prompt: Открыть проект в VS Code?
output-format:
prompt: Формат вывода
Local storage layout
~/.yal/
templates/ # built-in template cache
<kind>/
<name>/
<version>/
yal-meta.json
...template files...
user-templates/ # user-registered template cache
<kind>/
<name>/
<version>/
yal-meta.json
...template files...
user-templates.toml # user registry (managed by yal add / yal remove)
yal-meta.json stores the kind, name, version, source type (release or
commit), repository URL, release date, and install date for each cached
template.
Authentication
YAL supports using personal access tokens to access private repositories and to reduce API rate limits.
-
For provider HTTP APIs, YAL recognizes these environment variables:
GITHUB_TOKENorGH_TOKEN— GitHub APIGITLAB_TOKENorGL_TOKEN— GitLab APICODEBERG_TOKENorFORGEJO_TOKEN— Codeberg / Forgejo API
-
For Git operations (
git ls-remote,git clone) YAL will, when a token is available for the host, inject it into HTTPS URLs as a practical fallback. Supported variables for HTTPS auth include:BITBUCKET_TOKEN— Bitbucket HTTPS accessSOURCEFORGE_TOKEN— SourceForge HTTPS accessGITVERSE_TOKEN— gitverse.ru HTTPS accessGITGAY_TOKEN— git.gay HTTPS access
Example exports (bash):
# GitHub (API)
export GITHUB_TOKEN=ghp_...
# GitLab (API)
export GITLAB_TOKEN=glpat-...
# Codeberg / Forgejo (API)
export CODEBERG_TOKEN=...
# Bitbucket / SourceForge / gitverse (clone via HTTPS)
export BITBUCKET_TOKEN=...
export SOURCEFORGE_TOKEN=...
export GITVERSE_TOKEN=...
Note: token injection into URLs is only performed for HTTPS remotes and only
when the corresponding environment variable is set. SSH remotes (git@...) are
left unchanged and use SSH keys.
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yal_cmd-0.1.3.tar.gz.
File metadata
- Download URL: yal_cmd-0.1.3.tar.gz
- Upload date:
- Size: 71.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d2d2552b19999e3e8882fbbb5c35dc9c14f41fe1bd717c17209ef876102574a8
|
|
| MD5 |
16a2894f3e44b0df2f7fdf4fbcacaba9
|
|
| BLAKE2b-256 |
031b71b249fdab0ab809380c5079f925ec26ea7388d4994ffe901fe073faa435
|
File details
Details for the file yal_cmd-0.1.3-py3-none-any.whl.
File metadata
- Download URL: yal_cmd-0.1.3-py3-none-any.whl
- Upload date:
- Size: 70.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b83cb033b61aca7e4edcdb338e987d5736fe70e0813b98d941f55ca187da27a
|
|
| MD5 |
e78e356a4fdbbaf835dc946c3f65000a
|
|
| BLAKE2b-256 |
490e69abf5ed3747fb5805e5171939a25f11b2ecbf28556e805483d28e2f7be6
|