Skip to main content

The .daintree Directory

The version-controlled .daintree/ directory: team-shared project config, recipes, lifecycle scripts, and what stays machine-local.

Updated
Reviewed

The .daintree/ Directory

Daintree supports an optional .daintree/ directory at the root of your repository, similar to .vscode/ or .devcontainer/, for storing project configuration alongside your code. When present, Daintree reads these files on project open, so any team member who clones the repo gets the same project identity, settings, and recipes automatically. Environment variables and secrets are never written to these files; they have to be entered locally on each machine.

It is the single home for everything you want to share about a project through version control: its identity (name, emoji, color), the shareable subset of its settings, per-project recipes, and worktree lifecycle scripts. Two parts of it are populated automatically once you enable in-repository settings; the config.json lifecycle file is the one piece you create by hand.

Enabling In-Repository Settings

To start writing settings into .daintree/, open Project Settings > General (right-click the project name in the sidebar, or use the gear icon in the project toolbar) and scroll to the In-Repository Settings section. Toggling on Store settings in repository expands a confirmation panel listing the files Daintree will create. Click Confirm and enable to proceed.

Once enabled, Daintree writes .daintree/project.json and .daintree/settings.json immediately and keeps them in sync as you change settings. Existing project recipes are synced to .daintree/recipes/. These files are separate from the settings.json Daintree maintains in its app data directory; the in-repo files hold only the shareable subset of your project configuration.

Note

Disabling in-repository settings is non-destructive. Daintree stops writing to .daintree/, but the existing files stay in the repo. A teammate cloning the repo still gets the identity and settings applied on open.

Files Overview

FileContainsSafe to commit?
.daintree/project.jsonProject name, emoji, and colorYes
.daintree/settings.jsonShareable project settings (run commands, worktree path pattern, terminal settings, etc.)Yes
.daintree/recipes/Per-project recipes as individual JSON files (env var values are redacted; keys preserved). Synced from local recipe storage when in-repo mode is active.Yes
.daintree/config.jsonWorktree lifecycle scripts (setup and teardown commands)Yes
.daintree/*.local.jsonMachine-local overrides (reserved for future use)No, add to .gitignore

What's Shared vs. Machine-Local

When Daintree writes .daintree/settings.json, it leaves out machine-specific values on purpose. These fields are never written to the in-repo file:

  • Environment variables and secrets: project-scoped env vars are stored in plaintext on your machine only, never in the in-repo file (the local file is kept out of shared settings, not encrypted)
  • Shell executable path: your local shell binary is not shared
  • Dev server auto-detection state: a per-machine flag
  • Project icon (SVG): stored locally, not in the repo
  • Notification overrides: per-machine preferences

Everything else is written to settings.json and shared: run commands, dev server command and timeout, CopyTree settings, excluded paths, worktree path pattern, and terminal settings (shell args, default working directory, scrollback lines).

.gitignore Guidance

The enable confirmation panel shows a snippet you can copy into your .gitignore. Daintree never modifies .gitignore on its own. The snippet has two parts:

  • Safe-to-commit files (project.json, settings.json): listed for reference. Do not add these lines to your .gitignore if you want to commit them; the whole point of enabling in-repository settings is to track these files in git.
  • Machine-local files (*.local.json): these should always be in .gitignore.

The one line you have to add to .gitignore is:

.daintree/*.local.json

.daintree/recipes/ is also safe to commit. Recipe env var values are redacted before writing, so no secrets land in those files.

Worktree Lifecycle Scripts

The .daintree/config.json file lets you define shell commands that run automatically when worktrees are created or deleted. Use it to run npm install, seed a database, or tear down containers as part of your worktree workflow. Lifecycle scripts run for every worktree with no user prompt. To launch specific agents or panels conditionally, use Recipes instead.

Create .daintree/config.json manually in your repo. This file is independent of the in-repository settings toggle:

{"setup": [
  "npm install",
  "cp .env.example .env"
],
"teardown": [
  "docker compose down"
]}

Setup commands run asynchronously after a new worktree is created, with the worktree root as the working directory. You can switch to and use the worktree right away, but tools that depend on setup completing (like npm-installed CLIs) won't be available until the spinner clears. A spinner sits on the worktree card while commands run, showing the name of the active command. Commands run in sequence, and if one fails the rest are skipped. Teardown commands run before git worktree remove. Normal deletion allows up to 120 seconds for teardown to complete; force-delete shortens that to 15 seconds. Teardown failures are logged but never block deletion.

Note

Script output is not visible in the Daintree UI. While running, the worktree card shows the active command name. On failure, the card shows a red "Setup failed" or "Setup timed out" label. To capture output for debugging, redirect it to a log file in your commands:

npm install >> .daintree-setup.log 2>&1

For teardown scripts, log to a path outside the worktree (for example, $DAINTREE_PROJECT_ROOT/.daintree-teardown.log) since the worktree directory is removed once teardown completes.

Script Priority Order

Daintree checks three locations for config.json and uses the first one it finds:

  1. ~/.daintree/projects/<sanitized-root>/config.json: user-level override (machine-local, not in repo). <sanitized-root> is the absolute project path with filesystem-illegal characters replaced by underscores.
  2. <worktree-path>/.daintree/config.json: worktree-level config
  3. <project-root>/.daintree/config.json: main repo config (shared with team)

The user-level location is useful when you need machine-specific setup steps (activating a local virtualenv, say) without touching the shared repo config.

Injected Environment Variables

Daintree injects these variables into every lifecycle script command:

VariableValue
DAINTREE_WORKTREE_PATHAbsolute path to the worktree directory
DAINTREE_PROJECT_ROOTAbsolute path to the main repository root
DAINTREE_WORKTREE_NAMEThe branch/worktree name
CItrue
NONINTERACTIVE1
GIT_TERMINAL_PROMPT0
DEBIAN_FRONTENDnoninteractive

Your project's environment variables are not injected into lifecycle scripts.

Timeouts and PATH

Config Validation

If config.json contains invalid JSON or doesn't match the expected schema, Daintree logs a warning and skips that config file, then tries the next location in the priority chain. The recognized top-level keys are setup and teardown (string arrays), plus resource or resources for Remote Compute. If all three levels are invalid or missing, no lifecycle scripts run.

When scripts don't run as expected, check Daintree's log file (Settings > Troubleshooting > Open Log File) for config parse warnings.

New Worktrees Inherit .daintree/

When Daintree creates a new worktree, it copies the .daintree/ directory from the main repo into the new worktree directory. This seeds the worktree with the shared config so lifecycle scripts and identity are available right away. Existing files in the destination are never overwritten; if a worktree already has a .daintree/config.json, it is kept as a worktree-level override.

One consequence: once a worktree exists, later changes to the main repo's config.json won't apply to it automatically, since the copied worktree-level file takes priority per the order above. Delete the worktree-level .daintree/config.json if you want the worktree to pick up updated repo-level scripts.