This guide walks you through creating and developing a T project — a data analysis project that uses T packages.
Package vs Project: A package is a reusable library of T functions. A project is a data analysis workspace that depends on packages.
Create a new project interactively:
$ t init --project housing-analysis
Initializing new T project...
Author [User]: Alice
License [EUPL-1.2]: EUPL-1.2
Nixpkgs date [2026-02-19]: 2026-02-19
✓ Project 'housing-analysis' created successfully!When running t init, you will be prompted to select an
AI Agent Context Level. This generates two essential
files in your project root:
These files are designed to be read by LLMs (like Antigravity, Claude, or ChatGPT) at the start of a session to give them immediate, high-fidelity context about your project and the specific version of T you are using.
This creates the following structure:
pipeline.t).Projects use Nix for reproducibility. Enter the development shell:
$ nix developThis ensures all dependencies (T, packages, R, Nix) are available at
the exact versions specified in flake.lock.
Dependencies on T packages are declared in
tproject.toml:
[project]
name = "housing-analysis"
description = "Analyzing housing data with T"
[dependencies]
my_stats = { git = "https://github.com/user/my-stats", tag = "v0.1.0" }
data_utils = { git = "https://github.com/user/data-utils", tag = "v0.2.0" }
[t]
min_version = "0.54.0"Beyond T packages, you can declare system-level tools and LaTeX packages required for your project.
Use the [additional-tools] section to add any package
from Nixpkgs (e.g., CLI utilities, compilers, or libraries):
[additional-tools]
# These will be available in your 'nix develop' shell and pipeline sandboxes
packages = ["git", "jq", "gawk", "pandoc"]If your project involves generating PDFs or reports, use the
[latex] section. T automatically provides a
texlive environment starting from
scheme-small. Just list any additional LaTeX packages you
need:
[latex]
# Standard LaTeX packages
packages = ["amsmath", "blindtext", "physics", "hyperref"]After adding or changing dependencies (including
[additional-tools] or [latex] sections),
run:
$ t update
Syncing 2 dependency(ies) from tproject.toml → flake.nix...
Running nix flake update...This regenerates flake.nix so new dependencies and tools
appear as proper flake inputs with locked versions. The tools will be
available directly in your shell and automatically provided to any
pipeline nodes during execution. When you declare runtime dependencies,
the matching tlang companion package is also exposed in the
project shell (library(tlang) for R,
import tlang for Python, and using tlang for
Julia). Then re-enter the shell:
$ nix developTo upgrade your project to the latest version of T and set the project’s nixpkgs date to today’s UTC date:
$ t upgrade
Checking for new T releases...
Upgrading project to T 0.53.0 and nixpkgs date 2026-05-08 (today's UTC date)...
Regenerating flake.nix and updating dependencies...
Running nix flake update...This updates your tproject.toml and then runs
t update automatically.
R packages can be managed via the default nixpkgs resolver or by
pointing T at an renv.lock file.
List CRAN packages from nixpkgs directly:
[r-dependencies]
packages = ["dplyr", "ggplot2", "jsonlite"]After editing, run t update to include them in
flake.nix. Packages are available in every R pipeline node
and in nix develop.
If your project already has an renv.lock file, set:
[r-dependencies]
resolver = "renv"When resolver = "renv", T automatically discovers all R
dependencies from renv.lock:
Repository or Bioconductor field and mapped to
pkgs.rPackages.*.builtins.fetchGit using the RemoteHost,
RemoteUsername, RemoteRepo, and
RemoteSha fields.Remotes field is parsed: entries
like user/repo are matched against packages in the lock
file and injected as buildInputs of the declaring git
package.api.github.com and
gitlab.com. Other sources (bitbucket, custom URLs) produce
a warning and are skipped.R, methods,
stats, etc.) are automatically filtered out.No packages list is required — renv.lock is
the single source of truth. Run t update to regenerate
flake.nix with the renv-discovered packages.
Declare R packages from remote Git repositories directly in
tproject.toml:
[r-dependencies]
packages = ["dplyr"]
my_pkg = { git = "https://github.com/user/my-pkg", rev = "abc123def456" }The rev field must be a full Git commit hash. Each git
package is injected into every R pipeline node’s
buildInputs.
When using resolver = "renv", git packages from
renv.lock are automatically merged with those declared in
tproject.toml.
Python packages can use the default nixpkgs resolver or the UV workspace resolver for PyPI-only dependencies.
List packages from the pinned nixpkgs revision:
[py-dependencies]
packages = ["pandas", "scikit-learn"]Run t update to generate a
python.withPackages environment in flake.nix.
Simple, no extra files needed.
Projects that need packages from PyPI can opt into UV explicitly:
[py-dependencies]
resolver = "uv"
workspace = "python"The version field is optional when
using the UV resolver. If omitted, T infers the Nixpkgs Python attribute
(e.g. python312) from the requires-python
field in python/pyproject.toml. The inference accepts
specifiers that constrain to a single minor version
(==3.12, ==3.12.*, ~=3.12,
>=3.12,<3.13) and errors on open-ended ranges
(>=3.12). If an explicit version conflicts
with requires-python, T prints a warning and uses the
explicit value.
The workspace directory must contain the UV project metadata and lock file:
python/
pyproject.toml
uv.lock
When resolver = "uv", do not set
[py-dependencies].packages; Python dependencies are
declared only in pyproject.toml and locked by
uv.lock. Running t update generates
uv2nix/pyproject.nix inputs in flake.nix and builds the
Python environment as a Nix virtual environment.
This walkthrough assumes you do not have
uv installed and have no existing Python
package metadata — you are starting from an empty T project.
1. Create a new T project
t project my_project
cd my_project2. Edit tproject.toml
Replace the default [py-dependencies] section (or add it
if missing):
[py-dependencies]
resolver = "uv"
workspace = "python"The version field is optional when using the UV resolver
— T infers it from requires-python in
pyproject.toml. Remove any packages key if
present — UV and packages are mutually exclusive.
3. Create the Python workspace directory and
pyproject.toml
mkdir python# python/pyproject.toml
[project]
name = "my_project_python_env"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"pandas",
]Add any PyPI packages to the dependencies list. Re-run
uv lock whenever the list changes.
4. Generate the lock file
You need uv and python3 on
PATH. If you do not have them installed, enter a temporary
Nix shell:
nix shell nixpkgs#uv nixpkgs#python3Then generate the lock file:
uv lock --project pythonType exit or press Ctrl-D to leave the Nix
shell when done, or keep it open for the next step.
5. Generate the Nix flake
t updateThis reads the UV workspace, adds pyproject-nix,
uv2nix, and pyproject-build-systems inputs to
flake.nix, and configures the Python environment to use
pySet.mkVirtualEnv instead of
pkgs.python314.withPackages.
6. Run your pipeline
nix develop
t run src/pipeline.tPython pipeline nodes work identically regardless of which resolver you chose.
Adding or removing dependencies later
dependencies in
python/pyproject.toml.uv lock --project python.t update to regenerate
flake.nix.pyproject.toml and
uv.lock.Once inside nix develop, you can use the
import statement in your T scripts to load package
functions.
import my_stats
This makes all public functions from my_stats available
in scope.
import my_stats[weighted_mean, correlation]
Only weighted_mean and correlation are
imported.
import my_stats[wmean=weighted_mean, cor=correlation]
weighted_mean is available as wmean,
correlation as cor.
All functions in a package are public by default.
Package authors can mark internal helpers as private using
@private in T-Doc comments — those functions will not be
visible to importers.
Write your analysis in src/. For example,
src/pipeline.t:
import my_stats
import data_utils[read_clean]
p = pipeline {
data = read_csv("data/housing.csv")
clean = read_clean(data)
avg_price = mean(clean.$price)
price_by_area = clean |>
group_by($area) |>
summarize(avg=mean($price), sd=sd($price))
}
build_pipeline(p)
print(price_by_area)
Run your script:
$ t run src/pipeline.tOr use the REPL for interactive exploration:
$ t replYou can add tests in tests/ following the same
conventions as packages:
-- tests/test-pipeline.t
import my_stats
result = weighted_mean([1, 2, 3], [0.5, 0.3, 0.2])
assert(result == 1.7)
Run them with:
$ t testYour project is fully reproducible through Nix:
flake.nix declares exact dependency
sources (managed by t update)flake.lock pins exact versions of all
inputstproject.toml is the human-readable
source of truth for dependenciesAnyone can reproduce your environment:
$ git clone https://github.com/user/housing-analysis
$ cd housing-analysis
$ nix develop
$ t run src/pipeline.tThe same T version, same package versions, same R packages, and same system libraries are used every time.
T projects are designed to work seamlessly with modern editors via the Language Server Protocol (LSP).
The t-lsp binary is provided automatically by your
project’s nix develop shell.
nix develop.T projects also support Atelier, a tmux-based
TUI IDE that provides a split-pane environment with an embedded editor,
REPL, file tree, variable watcher, pipeline diagram, and plot viewer. To
enable Atelier in a new project, pass --include-atelier to
t init. Once inside nix develop, simply run
atelier.
Once active, you will get real-time autocompletion for: -
Package functions: Suggestions for all imported
functions. - Local variables: Defined earlier in your
script. - DataFrame columns: Column names from your
data sources (accessible via the $ prefix).