summaryrefslogtreecommitdiff
path: root/common/templates
diff options
context:
space:
mode:
authorbrustybee <bee@4d2.org>2026-03-19 23:43:21 +0530
committerbrustybee <bee@4d2.org>2026-03-19 23:43:21 +0530
commit9d2256adddbb830d33119a5e01e3fad6b4b0652b (patch)
tree958e1de443a6d59a9b09716ba5815c52bfb7bfb3 /common/templates
parentf9142a5822a4f212a3031b932cbf51b2d50344a8 (diff)
add flake templates
Diffstat (limited to 'common/templates')
-rw-r--r--common/templates/clang/.envrc1
-rw-r--r--common/templates/clang/.gitignore58
-rw-r--r--common/templates/clang/build.zig66
-rw-r--r--common/templates/clang/flake.nix46
-rw-r--r--common/templates/clang/src/main.c51
-rw-r--r--common/templates/clang/src/test.zig37
-rw-r--r--common/templates/default.nix17
-rw-r--r--common/templates/go/.envrc1
-rw-r--r--common/templates/go/.gitignore36
-rw-r--r--common/templates/go/flake.nix49
-rw-r--r--common/templates/go/go.mod4
-rw-r--r--common/templates/go/main.go43
-rw-r--r--common/templates/python/.envrc1
-rw-r--r--common/templates/python/.gitignore219
-rw-r--r--common/templates/python/flake.nix98
-rw-r--r--common/templates/python/pyproject.toml19
-rw-r--r--common/templates/python/src/hello_world/__init__.py3
-rw-r--r--common/templates/python/src/hello_world/main.py6
-rw-r--r--common/templates/python/uv.lock8
-rw-r--r--common/templates/rust/.envrc1
-rw-r--r--common/templates/rust/.gitignore29
-rw-r--r--common/templates/rust/Cargo.lock7
-rw-r--r--common/templates/rust/Cargo.toml14
-rw-r--r--common/templates/rust/flake.nix50
-rw-r--r--common/templates/rust/src/lib.rs33
-rw-r--r--common/templates/rust/src/main.rs6
-rw-r--r--common/templates/zig/.envrc1
-rw-r--r--common/templates/zig/.gitignore6
-rw-r--r--common/templates/zig/flake.nix46
-rw-r--r--common/templates/zig/src/main.zig5
30 files changed, 961 insertions, 0 deletions
diff --git a/common/templates/clang/.envrc b/common/templates/clang/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/common/templates/clang/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/common/templates/clang/.gitignore b/common/templates/clang/.gitignore
new file mode 100644
index 0000000..030089a
--- /dev/null
+++ b/common/templates/clang/.gitignore
@@ -0,0 +1,58 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf
+
+# debug information files
+*.dwo
+
+.direnv
+result
diff --git a/common/templates/clang/build.zig b/common/templates/clang/build.zig
new file mode 100644
index 0000000..2953ab7
--- /dev/null
+++ b/common/templates/clang/build.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const exe = b.addExecutable(.{
+ .name = "hello-c",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+
+ // 1. Link the C standard library
+ exe.linkLibC();
+
+ // 2. Add your C source files
+ exe.addCSourceFile(.{
+ .file = b.path("src/main.c"),
+ .flags = &.{ "-Wall", "-Wextra", "-O2" },
+ });
+
+ // 3. Declare intent for the executable to be installed
+ b.installArtifact(exe);
+
+ // 4. Create the 'run' step
+ const run_step = b.step("run", "Run the app");
+ const run_cmd = b.addRunArtifact(exe);
+
+ run_step.dependOn(&run_cmd.step);
+ run_cmd.step.dependOn(b.getInstallStep());
+
+ if (b.args) |args| {
+ run_cmd.addArgs(args);
+ }
+
+ // --- 5. Support Testing ---
+ // This creates an executable that runs 'test' blocks in src/test.zig
+ const exe_unit_tests = b.addTest(.{
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("src/test.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+
+ // This exposes our Zig exported functions to the Linux
+ // dynamic symbol table so dlsym() can actually find them.
+ exe_unit_tests.rdynamic = true;
+
+ exe_unit_tests.addCSourceFile(.{
+ .file = b.path("src/main.c"),
+ .flags = &.{ "-Wall", "-Wextra", "-pedantic", "-Dmain=nomain" },
+ });
+
+ // Allow tests to link against C and find your headers
+ exe_unit_tests.linkLibC();
+ exe_unit_tests.addIncludePath(b.path("src"));
+
+ const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests);
+
+ const test_step = b.step("test", "Run unit tests");
+ test_step.dependOn(&run_exe_unit_tests.step);
+}
+
diff --git a/common/templates/clang/flake.nix b/common/templates/clang/flake.nix
new file mode 100644
index 0000000..a3dfd01
--- /dev/null
+++ b/common/templates/clang/flake.nix
@@ -0,0 +1,46 @@
+{
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
+ outputs =
+ { self, nixpkgs, ... }:
+ let
+ inherit (nixpkgs) lib;
+ forAllSystems = lib.genAttrs lib.systems.flakeExposed;
+ pname = "hello-c";
+ in
+ {
+ packages = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.stdenv.mkDerivation {
+ inherit pname;
+ version = "0.1.0";
+ src = self;
+
+ # The zig hook handles the build AND the install automatically
+ nativeBuildInputs = [ pkgs.zig.hook ];
+ doCheck = true;
+ };
+ }
+ );
+
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.mkShellNoCC {
+ packages = [
+ pkgs.zig
+ pkgs.zls
+ pkgs.clang-tools
+ ];
+ };
+ }
+ );
+ };
+}
diff --git a/common/templates/clang/src/main.c b/common/templates/clang/src/main.c
new file mode 100644
index 0000000..5bda38b
--- /dev/null
+++ b/common/templates/clang/src/main.c
@@ -0,0 +1,51 @@
+#define _GNU_SOURCE // Required to unlock RTLD_DEFAULT so we can search all loaded memory
+#include <ctype.h>
+#include <dlfcn.h> // Gives us dlsym() to look up memory addresses by string names
+#include <stddef.h>
+
+void hello_world(const char* action) {
+ // __func__ is secretly injected by the compiler and holds the string "hello_world".
+ // We use raw pointers here (src and dst) instead of array indices because
+ // stepping the memory address forward directly (*dst++) generates faster machine code.
+ const char* src = __func__;
+ char result[64];
+ char* dst = result;
+ int capitalize = 1;
+
+ // We walk the 'src' pointer forward until we hit the null terminator.
+ // The (dst - result) < 60 check guarantees we never write past the end of
+ // our 64-byte array, preventing stack smashing if the function name is unusually long.
+ while (*src && (dst - result) < 60) {
+ if (*src == '_') {
+ *dst++ = ' ';
+ capitalize = 1;
+ } else {
+ *dst++ = capitalize ? toupper(*src) : *src;
+ capitalize = 0;
+ }
+ src++;
+ }
+
+ // Cap off the string with a bang and the required null terminator
+ *dst++ = '!';
+ *dst++ = '\n';
+ *dst = '\0';
+
+ // Declare a function pointer that takes a format string and variadic arguments
+ int (*dynamic_eval)(const char*, ...);
+
+ // This looks unhinged, but it is the official POSIX-compliant way to use dlsym.
+ // ISO C strictly forbids casting a raw data pointer (void*) directly to a
+ // function pointer. To bypass the compiler warning, we take the address of our
+ // function pointer, cast THAT to a void**, and dereference it to write the address.
+ *(void **)(&dynamic_eval) = dlsym(RTLD_DEFAULT, action);
+
+ if (dynamic_eval) {
+ dynamic_eval("%s", result);
+ }
+}
+
+int main(void) {
+ hello_world("printf");
+ return 0;
+}
diff --git a/common/templates/clang/src/test.zig b/common/templates/clang/src/test.zig
new file mode 100644
index 0000000..49ff4d0
--- /dev/null
+++ b/common/templates/clang/src/test.zig
@@ -0,0 +1,37 @@
+const std = @import("std");
+
+extern fn hello_world(action: [*c]const u8) void;
+
+// We use global static memory for our trap.
+// Because this test runs in a single thread and we know the string is tiny,
+var captured_output: [64]u8 = undefined;
+var captured_len: usize = 0;
+
+// Exported to the dynamic symbol table so dlsym() can find it.
+// Signature must accept two pointers to perfectly map to the
+// System V ABI hardware registers used by: dynamic_eval("%s", result);
+// CPU registers (RDI and RSI) that the C code will use when calling this via variadic arguments.
+export fn test_capture_sink(fmt: [*c]const u8, msg: [*c]const u8) void {
+ // We intentionally ignore the format string ("%s") sitting in the first register
+ _ = fmt;
+
+ // std.mem.span walks the raw C pointer until it finds the \0 null terminator.
+ // It doesn't allocate memory; it just calculates the length so we have a safe Zig slice.
+ const slice = std.mem.span(msg);
+
+ // Copy the raw bytes directly from the C memory space into our static Zig buffer.
+ @memcpy(captured_output[0..slice.len], slice);
+ captured_len = slice.len;
+}
+
+test "catfish dlsym eval" {
+ // We hand our C code the name of our exported Zig function.
+ // The C code will parse its own __func__, ask the OS to find "test_capture_sink",
+ // and execute it, throwing the parsed string right back into our global variables.
+ hello_world("test_capture_sink");
+
+ // Reconstruct a strict Zig string from the exact number of bytes we captured
+ const result = captured_output[0..captured_len];
+
+ try std.testing.expectEqualStrings("Hello World!\n", result);
+}
diff --git a/common/templates/default.nix b/common/templates/default.nix
new file mode 100644
index 0000000..ce06868
--- /dev/null
+++ b/common/templates/default.nix
@@ -0,0 +1,17 @@
+inputs: {
+ c = {
+ path = ./clang;
+ };
+ rust = {
+ path = ./rust;
+ };
+ zig = {
+ path = ./zig;
+ };
+ go = {
+ path = ./go;
+ };
+ python = {
+ path = ./python;
+ };
+}
diff --git a/common/templates/go/.envrc b/common/templates/go/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/common/templates/go/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/common/templates/go/.gitignore b/common/templates/go/.gitignore
new file mode 100644
index 0000000..9c1605b
--- /dev/null
+++ b/common/templates/go/.gitignore
@@ -0,0 +1,36 @@
+# If you prefer the allow list template instead of the deny list, see community template:
+# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
+#
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Code coverage profiles and other test artifacts
+*.out
+coverage.*
+*.coverprofile
+profile.cov
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+# Go workspace file
+go.work
+go.work.sum
+
+# env file
+.env
+.direnv
+
+#nix
+result
+
+# Editor/IDE
+# .idea/
+# .vscode/
diff --git a/common/templates/go/flake.nix b/common/templates/go/flake.nix
new file mode 100644
index 0000000..3f8e7d5
--- /dev/null
+++ b/common/templates/go/flake.nix
@@ -0,0 +1,49 @@
+{
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
+ outputs =
+ {
+ self,
+ nixpkgs,
+ ...
+ }:
+ let
+ inherit (nixpkgs) lib;
+ forAllSystems = lib.genAttrs lib.systems.flakeExposed;
+ in
+ {
+ packages = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.buildGoModule {
+ pname = "hello_go";
+ version = "0.0.1";
+ src = self;
+
+ # The "Trust but Verify" hash.
+ vendorHash = null;
+ };
+ }
+ );
+
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.mkShellNoCC {
+ packages = with pkgs; [
+ go
+ gopls
+ gotools
+ go-tools
+ ];
+ };
+ }
+ );
+ };
+}
diff --git a/common/templates/go/go.mod b/common/templates/go/go.mod
new file mode 100644
index 0000000..24673a6
--- /dev/null
+++ b/common/templates/go/go.mod
@@ -0,0 +1,4 @@
+module hello_go
+
+go 1.25.5
+
diff --git a/common/templates/go/main.go b/common/templates/go/main.go
new file mode 100644
index 0000000..d895205
--- /dev/null
+++ b/common/templates/go/main.go
@@ -0,0 +1,43 @@
+package main
+
+import (
+ "fmt"
+ "runtime"
+ "strings"
+ "unicode"
+)
+
+func hello_world(action string) {
+ // 1. Get the Program Counter (PC) of the current function
+ pc, _, _, _ := runtime.Caller(0)
+
+ // 2. Get the full function name (e.g., "main.hello_world")
+ fullFuncName := runtime.FuncForPC(pc).Name()
+
+ // 3. Strip the package path to get just "hello_world"
+ // Splits "main.hello_world" and takes the last part
+ parts := strings.Split(fullFuncName, ".")
+ funcName := parts[len(parts)-1]
+
+ // 4. Split into words: "hello", "world"
+ words := strings.Split(funcName, "_")
+
+ // 5. Capitalize each word: "Hello", "World"
+ for i, w := range words {
+ runes := []rune(w)
+ runes[0] = unicode.ToUpper(runes[0])
+ words[i] = string(runes)
+ }
+
+ // 6. Join with ", " and add "!" -> "Hello, World!"
+ finalString := strings.Join(words, ", ") + "!"
+
+ // 7. Execute the action
+ if action == "print" {
+ fmt.Println(finalString)
+ }
+}
+
+func main() {
+ hello_world("print")
+}
diff --git a/common/templates/python/.envrc b/common/templates/python/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/common/templates/python/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/common/templates/python/.gitignore b/common/templates/python/.gitignore
new file mode 100644
index 0000000..f51634e
--- /dev/null
+++ b/common/templates/python/.gitignore
@@ -0,0 +1,219 @@
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[codz]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+# Usually these files are written by a python script from a template
+# before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py.cover
+.hypothesis/
+.pytest_cache/
+cover/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+.pybuilder/
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+# For a library or package, you might want to ignore these files since the code is
+# intended to run in multiple environments; otherwise, check them in:
+# .python-version
+
+# pipenv
+# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+# However, in case of collaboration, if having platform-specific dependencies or dependencies
+# having no cross-platform support, pipenv may install dependencies that don't work, or not
+# install all needed dependencies.
+# Pipfile.lock
+
+# UV
+# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# uv.lock
+
+# poetry
+# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
+# This is especially recommended for binary packages to ensure reproducibility, and is more
+# commonly ignored for libraries.
+# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
+# poetry.lock
+# poetry.toml
+
+# pdm
+# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
+# pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
+# https://pdm-project.org/en/latest/usage/project/#working-with-version-control
+# pdm.lock
+# pdm.toml
+.pdm-python
+.pdm-build/
+
+# pixi
+# Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
+# pixi.lock
+# Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
+# in the .venv directory. It is recommended not to include this directory in version control.
+.pixi
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# Redis
+*.rdb
+*.aof
+*.pid
+
+# RabbitMQ
+mnesia/
+rabbitmq/
+rabbitmq-data/
+
+# ActiveMQ
+activemq-data/
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pytype static type analyzer
+.pytype/
+
+# Cython debug symbols
+cython_debug/
+
+# PyCharm
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+# .idea/
+
+# Abstra
+# Abstra is an AI-powered process automation framework.
+# Ignore directories containing user credentials, local state, and settings.
+# Learn more at https://abstra.io/docs
+.abstra/
+
+# Visual Studio Code
+# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
+# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
+# and can be added to the global gitignore or merged into this file. However, if you prefer,
+# you could uncomment the following to ignore the entire vscode folder
+# .vscode/
+
+# Ruff stuff:
+.ruff_cache/
+
+# PyPI configuration file
+.pypirc
+
+# Marimo
+marimo/_static/
+marimo/_lsp/
+__marimo__/
+
+# Streamlit
+.streamlit/secrets.toml
+
+#nix
+.direnv
+result
diff --git a/common/templates/python/flake.nix b/common/templates/python/flake.nix
new file mode 100644
index 0000000..e773850
--- /dev/null
+++ b/common/templates/python/flake.nix
@@ -0,0 +1,98 @@
+{
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+
+ pyproject-nix = {
+ url = "github:pyproject-nix/pyproject.nix";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+
+ uv2nix = {
+ url = "github:pyproject-nix/uv2nix";
+ inputs.pyproject-nix.follows = "pyproject-nix";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+
+ pyproject-build-systems = {
+ url = "github:pyproject-nix/build-system-pkgs";
+ inputs.pyproject-nix.follows = "pyproject-nix";
+ inputs.uv2nix.follows = "uv2nix";
+ inputs.nixpkgs.follows = "nixpkgs";
+ };
+ };
+
+ outputs =
+ {
+ nixpkgs,
+ pyproject-nix,
+ uv2nix,
+ pyproject-build-systems,
+ ...
+ }:
+ let
+ inherit (nixpkgs) lib;
+ forAllSystems = lib.genAttrs lib.systems.flakeExposed;
+
+ workspace = uv2nix.lib.workspace.loadWorkspace { workspaceRoot = ./.; };
+
+ overlay = workspace.mkPyprojectOverlay {
+ sourcePreference = "wheel";
+ };
+
+ editableOverlay = workspace.mkEditablePyprojectOverlay {
+ root = "$REPO_ROOT";
+ };
+ packageName = "hello_world";
+
+ pythonSets = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ python = pkgs.python3;
+ in
+ (pkgs.callPackage pyproject-nix.build.packages {
+ inherit python;
+ }).overrideScope
+ (
+ lib.composeManyExtensions [
+ pyproject-build-systems.overlays.wheel
+ overlay
+ ]
+ )
+ );
+
+ in
+ {
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ pythonSet = pythonSets.${system}.overrideScope editableOverlay;
+ virtualenv = pythonSet.mkVirtualEnv "${packageName}-dev-env" workspace.deps.all;
+ in
+ {
+ default = pkgs.mkShellNoCC {
+ packages = [
+ virtualenv
+ pkgs.uv
+ pkgs.pyrefly
+ pkgs.ruff
+ ];
+ env = {
+ UV_NO_SYNC = "1";
+ UV_PYTHON = pythonSet.python.interpreter;
+ UV_PYTHON_DOWNLOADS = "never";
+ };
+ shellHook = ''
+ unset PYTHONPATH
+ export REPO_ROOT=$(git rev-parse --show-toplevel)
+ '';
+ };
+ }
+ );
+
+ packages = forAllSystems (system: {
+ default = pythonSets.${system}.mkVirtualEnv packageName workspace.deps.default;
+ });
+ };
+}
diff --git a/common/templates/python/pyproject.toml b/common/templates/python/pyproject.toml
new file mode 100644
index 0000000..5892765
--- /dev/null
+++ b/common/templates/python/pyproject.toml
@@ -0,0 +1,19 @@
+[project]
+name = "hello-world"
+version = "0.1.0"
+description = "Add your description here"
+requires-python = ">=3.12"
+dependencies = []
+
+[project.scripts]
+hello_world = "hello_world.main:main"
+
+[build-system]
+requires = ["uv_build>=0.10.0,<0.11.0"]
+build-backend = "uv_build"
+
+[tool.pyrefly]
+project-includes = [
+ "**/*.py*",
+ "**/*.ipynb",
+]
diff --git a/common/templates/python/src/hello_world/__init__.py b/common/templates/python/src/hello_world/__init__.py
new file mode 100644
index 0000000..402c976
--- /dev/null
+++ b/common/templates/python/src/hello_world/__init__.py
@@ -0,0 +1,3 @@
+def hello_world(action: str):
+ result = [*hello_world.__name__.split("_")]
+ eval(action)(" ".join(result).title() + "!\n")
diff --git a/common/templates/python/src/hello_world/main.py b/common/templates/python/src/hello_world/main.py
new file mode 100644
index 0000000..71ad102
--- /dev/null
+++ b/common/templates/python/src/hello_world/main.py
@@ -0,0 +1,6 @@
+from hello_world import hello_world
+
+
+def main():
+ hello_world("print")
+
diff --git a/common/templates/python/uv.lock b/common/templates/python/uv.lock
new file mode 100644
index 0000000..6fa00de
--- /dev/null
+++ b/common/templates/python/uv.lock
@@ -0,0 +1,8 @@
+version = 1
+revision = 3
+requires-python = ">=3.12"
+
+[[package]]
+name = "hello-world"
+version = "0.1.0"
+source = { editable = "." }
diff --git a/common/templates/rust/.envrc b/common/templates/rust/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/common/templates/rust/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/common/templates/rust/.gitignore b/common/templates/rust/.gitignore
new file mode 100644
index 0000000..e8cf9a0
--- /dev/null
+++ b/common/templates/rust/.gitignore
@@ -0,0 +1,29 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug
+target
+
+.direnv
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# MSVC Windows builds of rustc generate these, which store debugging information
+*.pdb
+
+# Generated by cargo mutants
+# Contains mutation testing data
+**/mutants.out*/
+
+# RustRover
+# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
+# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
+# and can be added to the global gitignore or merged into this file. For a more nuclear
+# option (not recommended) you can uncomment the following to ignore the entire idea folder.
+#.idea/
+
+
+# Added by cargo
+
+/target
+
diff --git a/common/templates/rust/Cargo.lock b/common/templates/rust/Cargo.lock
new file mode 100644
index 0000000..c87dd44
--- /dev/null
+++ b/common/templates/rust/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "rust-project"
+version = "0.1.0"
diff --git a/common/templates/rust/Cargo.toml b/common/templates/rust/Cargo.toml
new file mode 100644
index 0000000..5ab6a6c
--- /dev/null
+++ b/common/templates/rust/Cargo.toml
@@ -0,0 +1,14 @@
+[package]
+name = "rust-project"
+version = "0.1.0"
+edition = "2024"
+description = """
+my rust project
+"""
+repository = ""
+license = ""
+
+[workspace]
+members = []
+
+[dependencies]
diff --git a/common/templates/rust/flake.nix b/common/templates/rust/flake.nix
new file mode 100644
index 0000000..afe2601
--- /dev/null
+++ b/common/templates/rust/flake.nix
@@ -0,0 +1,50 @@
+{
+ inputs = {
+ nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+ naersk.url = "github:nix-community/naersk";
+ naersk.inputs.nixpkgs.follows = "nixpkgs";
+ };
+
+ outputs =
+ {
+ self,
+ nixpkgs,
+ naersk,
+ ...
+ }:
+ let
+ inherit (nixpkgs) lib;
+ forAllSystems = lib.genAttrs lib.systems.flakeExposed;
+ in
+ {
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.mkShell {
+ buildInputs = with pkgs; [
+ cargo
+ clippy
+ rustc
+ rustfmt
+ rust-analyzer
+ ];
+ RUST_SRC_PATH = pkgs.rustPlatform.rustLibSrc;
+ };
+ }
+ );
+
+ packages = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ naersk-lib = pkgs.callPackage naersk { };
+ in
+ {
+ default = naersk-lib.buildPackage self;
+ }
+ );
+ };
+}
diff --git a/common/templates/rust/src/lib.rs b/common/templates/rust/src/lib.rs
new file mode 100644
index 0000000..ee5d017
--- /dev/null
+++ b/common/templates/rust/src/lib.rs
@@ -0,0 +1,33 @@
+use std::any::type_name;
+
+pub fn hello_world(action: &str) {
+ // Every function in Rust has a unique, unnameable type.
+ // we define a tiny helper to extract that type's name.
+ fn get_type_name<T>(_: T) -> &'static str {
+ type_name::<T>()
+ }
+
+ // This gives us "current_crate::hello_world"
+ let full_path = get_type_name(hello_world);
+
+ // We transform the string entirely through a lazy iterator pipeline.
+ let formatted: String = full_path
+ .split("::")
+ .last()
+ .unwrap_or("")
+ .split('_')
+ .map(|word| {
+ // Capitalize the first letter and chain the rest
+ let mut chars = word.chars();
+ chars
+ .next()
+ .map(|f| f.to_uppercase().collect::<String>() + chars.as_str())
+ .unwrap_or_default()
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ + "!";
+
+ println!("{}", formatted)
+}
+
diff --git a/common/templates/rust/src/main.rs b/common/templates/rust/src/main.rs
new file mode 100644
index 0000000..629b4b1
--- /dev/null
+++ b/common/templates/rust/src/main.rs
@@ -0,0 +1,6 @@
+use rust_project::hello_world;
+
+fn main() {
+ hello_world("print");
+}
+
diff --git a/common/templates/zig/.envrc b/common/templates/zig/.envrc
new file mode 100644
index 0000000..3550a30
--- /dev/null
+++ b/common/templates/zig/.envrc
@@ -0,0 +1 @@
+use flake
diff --git a/common/templates/zig/.gitignore b/common/templates/zig/.gitignore
new file mode 100644
index 0000000..f55821a
--- /dev/null
+++ b/common/templates/zig/.gitignore
@@ -0,0 +1,6 @@
+.direnv
+.zig-cache/
+zig-out/
+result
+*.o
+
diff --git a/common/templates/zig/flake.nix b/common/templates/zig/flake.nix
new file mode 100644
index 0000000..26bb8f1
--- /dev/null
+++ b/common/templates/zig/flake.nix
@@ -0,0 +1,46 @@
+{
+ inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
+
+ outputs =
+ {
+ self,
+ nixpkgs,
+ ...
+ }:
+ let
+ inherit (nixpkgs) lib;
+ forAllSystems = lib.genAttrs lib.systems.flakeExposed;
+ in
+ {
+ packages = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.stdenv.mkDerivation {
+ pname = "hello_zig";
+ version = "0.1.0";
+ src = self;
+
+ nativeBuildInputs = [ pkgs.zig.hook ];
+ };
+ }
+ );
+
+ devShells = forAllSystems (
+ system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ default = pkgs.mkShellNoCC {
+ packages = [
+ pkgs.zig
+ pkgs.zls
+ ];
+ };
+ }
+ );
+ };
+}
diff --git a/common/templates/zig/src/main.zig b/common/templates/zig/src/main.zig
new file mode 100644
index 0000000..3b2b910
--- /dev/null
+++ b/common/templates/zig/src/main.zig
@@ -0,0 +1,5 @@
+const std = @import("std");
+
+pub fn main() !void {
+ try std.fs.File.stdout().writeAll("Hello, World!\n");
+}