Tsk tsk-config
Use this skill when the user wants to set up or configure tsk Docker container images, customize their tsk.toml for Docker builds, configure stack/agent/project layers, or troubleshoot tsk container build issues.
git clone https://github.com/dtormoen/tsk-tsk
T=$(mktemp -d) && git clone --depth=1 https://github.com/dtormoen/tsk-tsk "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/tsk-config/skills/tsk-config" ~/.claude/skills/dtormoen-tsk-tsk-config && rm -rf "$T"
skills/tsk-config/skills/tsk-config/SKILL.mdtsk
Docker Configuration Guide
tskYou are helping a user configure
tsk Docker container images for their project. Follow these steps in order.
Step 1: Gather Project Information
Detect the project's technology stack by checking for these files in the project root. These are
tsk's built-in stacks with auto-detection — custom stacks for any language can be defined via stack_config in tsk.toml (covered in Step 5).
| File | Stack |
|---|---|
| rust |
| go |
| node |
, , | python |
, , | java |
, , | lua |
| None of the above | default |
Also determine the project name from the current directory name.
Tell the user what you detected and ask them to confirm or override. If their stack isn't listed above, let them know they can define a custom stack.
Step 2: Check for Deprecated Dockerfiles
Check if
.tsk/dockerfiles/ exists. If it does, warn the user:
Deprecated:
no longer supports filesystem-based dockerfiles intsk. Docker customization is now done via.tsk/dockerfiles/fields insetup. This guide will help you migrate to the new format.tsk.toml
List any files found in
.tsk/dockerfiles/ and note their contents — you will use them to populate the new config.
Step 3: Choose Configuration Location
Ask the user where to put the configuration:
- Project-level (
): Checked into version control, shared with the team. Best for project-specific dependencies that all contributors need..tsk/tsk.toml - User-level (
): Personal settings, not shared. Best for machine-specific paths, personal preferences, or settings across multiple projects.~/.config/tsk/tsk.toml
If the user picks user-level, config goes under
[project.<project-name>] in ~/.config/tsk/tsk.toml. If project-level, config goes at the top level of .tsk/tsk.toml.
Step 4: View the Current Docker Build
Run this command and show the output to the user:
tsk docker build --dry-run
Explain to the user: this shows the complete Dockerfile that
tsk generates with all layers resolved. Look for comments like # Stack layer, # Project layer, and # Agent layer to see where each setup field injects content. The next step will help them add the right customizations.
Step 5: Populate Configuration
Based on the project analysis, write the
tsk.toml configuration. Use the layer reference below to decide what goes where.
tsk
Docker Layer Architecture
tsktsk builds container images using 4 layers, assembled in this order:
1. Base layer — Ubuntu 25.10, git, build-essential, ripgrep, Python 3, uv, podman 2. Stack layer — Language runtime and tools (e.g., Go, Rust, Node.js) 3. Project layer — Project-specific system dependencies 4. Agent layer — AI agent installation (Claude, Codex)
You customize layers 2-4 via
tsk.toml:
| Config field | Layer | Purpose |
|---|---|---|
| Stack (2) | Language tooling, compilers, package managers |
| Project (3) | Project-specific apt packages, system libraries, custom tools |
| Agent (4) | Agent-specific setup (rarely needed) |
Each
setup field contains raw Dockerfile commands (RUN, ENV, COPY, etc.) that get injected into the generated Dockerfile at the corresponding layer position. Setup commands run as the agent user by default. Use USER root to switch to root for operations that require it (e.g., apt-get), and always switch back to USER agent afterwards.
Built-in Stacks (What's Already Included)
tsk has built-in stack layers. You only need stack_config if the built-in is insufficient.
rust: Rust stable via rustup,
CARGO_TARGET_DIR=/home/agent/.cargo/target
go: Go 1.25.0,
GOPATH=/home/agent/gopath, CGO_ENABLED=0
node: Node.js LTS, npm, pnpm, yarn, typescript, ts-node, nodemon, eslint, prettier, jest, npm-check-updates.
NODE_ENV=development
python: uv venv at
/home/agent/.venv, pytest, pip, black, ruff, ty, mypy, poetry
java: OpenJDK 17, Maven, Gradle. Maven
settings.xml and Gradle gradle.properties are pre-configured to route through the tsk proxy
lua: LuaJIT, Lua 5.1 dev libs, Neovim, stylua, LuaRocks with luacheck/busted/luassert/luafilesystem/nlua
default: Empty (base layer only)
Base Layer (Always Present)
Every container includes: Ubuntu 25.10, git, git-lfs, build-essential, curl, jq, just, ripgrep, sudo, Python 3, uv, podman (for DIND), and an
agent user (UID 1000).
Configuration Format
Project-level (
.tsk/tsk.toml):
# Project-specific dependencies (injected at the project layer) setup = ''' USER root RUN apt-get update && apt-get install -y libssl-dev pkg-config cmake USER agent ''' # Override or extend the stack layer [stack_config.rust] setup = ''' RUN cargo install cargo-nextest sccache ENV RUSTC_WRAPPER=sccache '''
User-level (
~/.config/tsk/tsk.toml):
# Default settings for all projects [defaults] memory_gb = 16.0 cpu = 8 # Per-project overrides [project.my-project] stack = "rust" setup = ''' USER root RUN apt-get update && apt-get install -y libssl-dev pkg-config USER agent ''' [project.my-project.stack_config.rust] setup = ''' RUN cargo install cargo-nextest '''
Common Examples
Rust project needing system libraries:
setup = ''' USER root RUN apt-get update && apt-get install -y \ libssl-dev pkg-config cmake protobuf-compiler USER agent '''
Python project with native dependencies:
setup = ''' USER root RUN apt-get update && apt-get install -y \ libpq-dev libffi-dev USER agent '''
Node.js project needing native build tools:
setup = ''' USER root RUN apt-get update && apt-get install -y \ libcairo2-dev libjpeg-dev libpango1.0-dev libgif-dev USER agent '''
Go project with CGO and protocol buffers:
[stack_config.go] setup = ''' USER root ENV CGO_ENABLED=1 RUN apt-get update && apt-get install -y protobuf-compiler USER agent RUN go install google.golang.org/protobuf/cmd/protoc-gen-go@latest '''
Java project with a specific JDK version:
[stack_config.java] setup = ''' USER root RUN apt-get update && apt-get install -y openjdk-21-jdk USER agent ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 '''
Adding a stack not built into
(e.g., Scala):tsk
stack = "scala" [stack_config.scala] setup = ''' USER root RUN apt-get update && apt-get install -y openjdk-21-jdk RUN curl -fL https://github.com/coursier/coursier/releases/latest/download/cs-x86_64-pc-linux.gz | gzip -d > /usr/local/bin/cs \ && chmod +x /usr/local/bin/cs \ && cs setup --yes USER agent ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 '''
Writing Good Setup Commands
- Setup commands run as the
user (UID 1000) by default. Useagent
when you need elevated privileges (e.g.,USER root
), and always switch back toapt-get install
afterwardsUSER agent - Always combine
withapt-get update
in the sameapt-get install
to avoid stale cachesRUN - Use
flag for non-interactive installs-y - Combine related commands with
to reduce layers&& - Environment variables set with
persist into the running containerENV - The project layer runs BEFORE the agent layer, so project setup cannot depend on agent tools
Deciding What Goes Where
Ask yourself:
- Is it a language runtime or dev tool? →
stack_config.<stack>.setup - Is it a system library or project dependency? →
(project layer)setup - Does it modify how the AI agent works? →
(rare)agent_config.<agent>.setup - Would it be useful for other projects with the same stack? →
in user-levelstack_config[defaults] - Is it specific to this project? →
insetup.tsk/tsk.toml
Write the configuration file based on the project's needs. If there were deprecated dockerfiles found in step 2, migrate their contents to the appropriate setup fields.
If the user does not have specific customization needs yet, write a minimal config with just the
stack field set and an empty setup with a comment explaining where to add dependencies.
Step 6: Test the Configuration
Run this command to verify the Docker image builds successfully and tasks work:
tsk run -t ack
This creates a minimal task that just says "ack" and exits. If it completes successfully, the Docker configuration is working. If it fails, examine the error output:
- Build failures: Usually a bad
command in a setup field. Check the Dockerfile output for the failing line.RUN - Missing dependencies at runtime: The setup command succeeded but the tool isn't available. Check
andPATH
settings.ENV - Permission errors: May need
before install commands.USER root
If the build fails, help the user fix the configuration and re-run
tsk run -t ack.
If the user is stuck and cannot get their project working after multiple attempts, ask if they'd like to file a bug report against
tsk. If they agree:
- Capture the output of
tsk docker build --dry-run - Capture the error message from the failing build or run
- Ask the user to briefly describe what aspect of their project is causing problems (e.g., specific system libraries, unusual toolchain, network requirements)
- File the issue using
:gh
<error output> ```gh issue create --repo dtormoen/tsk-tsk \ --title "Docker build failure: <short description>" \ --body "$(cat <<'EOF' ## Problem <user's description of what aspect of their project causes problems> ## Error
Generated Dockerfile (tsk docker build --dry-run
)
tsk docker build --dry-run<dry-run output>
EOF )"
## Additional Configuration Options After the basic setup works, mention these optional settings the user might want: ```toml # Forward host ports to containers (e.g., local databases) host_ports = [5432, 6379] # Mount volumes for build caches volumes = [ { host = "~/.cache/go-mod", container = "/go/pkg/mod" }, { name = "build-cache", container = "/home/agent/.cache" }, ] # Pass environment variables to containers env = [ { name = "DB_PORT", value = "5432" }, ] # Container resources memory_gb = 16.0 cpu = 8 # Enable Docker-in-Docker dind = true # Enable passwordless sudo inside containers sudo = false # Run containers in privileged mode (disables security restrictions) privileged = false # Expose host devices to containers (supports glob patterns) devices = ["/dev/video0", "/dev/ttyUSB*"]
Note: To connect to host services via forwarded ports, use the
TSK_PROXY_HOST environment variable (set automatically by tsk) as the hostname, not localhost.