Skip to content

yusing/shadowtree

Repository files navigation

Shadowtree

Project-local recipes for repeatable development workflows.

shadowtree resolves commands from .shadowtree.toml, prints inspectable execution plans, and runs recipes in a disposable workspace by default. Use it for checks, builds, generation, cleanup, install steps, and other project commands that should be easy to discover, complete in a shell, and run without accidentally mutating the host checkout.

TL;DR: use shadowtree recipes to see what a project exposes, use shadowtree --print <recipe> before unfamiliar work, use shadowtree <recipe> to run it in a sandbox, and use --sync-out or sandboxed = false only when the workflow is meant to change the checkout.

Quick Start

# 1. Install the CLI
go install github.com/yusing/shadowtree/cmd/shadowtree@latest

# 2. Create a project config
shadowtree init

# 3. Inspect and run recipes
shadowtree recipes
shadowtree --print test
shadowtree test

go install writes to $(go env GOPATH)/bin by default; make sure that directory is on PATH.

Without a config file, Shadowtree can detect nearby Go or Node project markers and expose profile built-ins. A discovered config controls the recipe set exactly unless it opts into a profile with profile = "go" or profile = "node".

Everyday Workflows

Workflow Command Output
List available recipes shadowtree recipes Resolved recipe names and help text
Inspect a recipe shadowtree help test Recipe help, args, requirements, stages, and sync-out paths
Print a plan shadowtree --print test Resolved plan without running commands
Print expanded details shadowtree --print --expanded test Expanded scripts, args, vars, env, logs, and sync-out paths
Validate a recipe shadowtree --check test Resolution checks without command execution
Parse expanded shell shadowtree --check --shell test Resolution checks plus sh/bash parsing
Run a recipe shadowtree test Sandboxed execution by default
Run a one-off command shadowtree exec -- go test ./... Sandboxed one-off command execution
Copy selected outputs back shadowtree --sync-out dist build-assets Successful sandbox run plus selected host updates
Install completion shadowtree completion bash|fish|zsh Shell completion script

Global flags come before the command or recipe name:

shadowtree --verbose test ./...

Arguments after the recipe name are recipe arguments or are forwarded to the main command, depending on the recipe.

Why Shadowtree?

Project scripts tend to sprawl across Makefile, package scripts, shell helpers, ad hoc README snippets, and CI-only commands. That makes it hard to answer basic questions before running them: what will execute, where will it write, which arguments are accepted, and whether generated files will touch the checkout.

Shadowtree puts those workflows behind one recipe interface:

  1. It discovers or loads project-local TOML configuration.
  2. It resolves profiles, includes, recipe references, typed arguments, vars, env, lifecycle stages, and sync-out rules into an execution plan.
  3. It lets you inspect or validate that plan before running it.
  4. It runs sandboxed by default, keeping ordinary command writes out of the host checkout.
  5. It copies results back only through explicit sync-out or recipes that opt out of sandboxing.

What It Provides

Surface What it does
Recipe config .shadowtree.toml recipes with pre, cmd, post, for_each, workdir, env, vars, requirements, logs, and sync-out
Sandboxed runs Overlayfs in a user and mount namespace on Linux, with a copied-workspace fallback when overlayfs is unavailable
Explicit checkout writes Recipe sync_out, CLI --sync-out, --sync-out-all, or sandboxed = false for workflows that intentionally edit the host checkout
Typed arguments Positional and named recipe inputs with defaults, validation, value providers, presets, and completion
Recipe references @recipe and @path:recipe references for composing workflows without a second task language
Profiles Built-in Go and Node recipe sets, selected explicitly, by config, or by marker detection when no config is loaded
CLI inspection Help, recipe listing, plan printing, expanded printing, dry checks, shell parsing, and verbose execution boundaries
Completion Dynamic Bash, Fish, and Zsh completion from resolved recipes and argument values
Editor support JSON Schema, VS Code schema binding, Zed language support, and shadowtree-lsp

Configuration

Shadowtree discovers config upward from the current directory until the Git root or filesystem root:

.shadowtree.toml

Start with:

shadowtree init

A minimal project config looks like this:

profile = "go"
shell = "sh"

[recipes.generate]
help = "Regenerate checked-in files."
cmd = "go generate ./..."
sync_out = ["internal/generated"]

[recipes.install]
help = "Install project tools."
sandboxed = false
cmd = "go install ./cmd/tool"

Recipes are sandboxed unless they set sandboxed = false or inherit behavior from a built-in profile. Use sync_out when a sandboxed recipe should copy specific generated paths back after a successful run. Prefer narrow sync-out paths over --sync-out-all.

Includes, vars, env, typed arguments, command requirements, logging, lifecycle stages, and recipe references are documented in the manual.

CLI Reference

Common commands:

shadowtree [flags] <recipe> [args...]
shadowtree [flags] exec -- <cmd> [args...]
shadowtree help [recipe [color=false]]
shadowtree recipes
shadowtree config
shadowtree init [path]
shadowtree completion bash|fish|zsh

Global flags:

Flag Purpose
--config <path> Use an explicit config file
--profile go|node Select built-in profile recipes
--sync-out <path> Copy selected paths back after a successful sandboxed run
--sync-out-all Copy the whole sandbox workspace back after success
--print Print the resolved plan without running
--expanded With --print, include expanded scripts, values, env, logs, and sync-out paths
--check Validate the resolved recipe without running commands
--shell With --check, parse expanded sh and bash scripts
--verbose Show workspace details and compact stage boundaries
--help Show basic CLI help
--version Print the version

See shadowtree help, shadowtree help <recipe>, and the CLI inspection guide for exact output.

Profiles

Profiles provide built-in recipes for common projects.

Go projects expose recipes such as test, test-race, vet, check, build, generate, lint, fmt, fix, tidy, and run. Module-wide built-ins fan out across discovered Go modules. Built-in fix, fmt, and tidy are unsandboxed because they are meant to update the checkout.

Node projects expose recipes such as install, dev, build, start, test, lint, fmt, typecheck, and check. Package manager, script, framework, test, lint, formatter, and typechecker inference comes from package.json, lockfiles, installed dependencies, and common config markers. Node built-ins are unsandboxed by default because package-manager and framework commands commonly mutate project state.

Profile selection precedence:

  1. explicit --profile
  2. config profile
  3. marker detection only when no config is loaded

How It Works

flowchart TD
    Start["shadowtree command"] --> Config["Discover or load config"]
    Config --> Profile["Apply profile and includes"]
    Profile --> Resolve["Resolve recipe, args, vars, env, and references"]
    Resolve --> Inspect["Print, expand, or check plan"]
    Resolve --> Sandbox["Prepare sandboxed workspace"]
    Sandbox --> Run["Run pre, cmd, and post stages"]
    Run --> Sync["Optional sync-out to host checkout"]
    Resolve --> Direct["Unsandboxed recipe"]
    Direct --> Run
Loading

For sandboxed runs, Shadowtree skips .git, .shadowtree, and .shadowtree.* while preparing workspaces. Go build recipes that require stable behavior should use -buildvcs=false when they run without .git.

Documentation

Development

Use the local CLI before installing a binary:

go run ./cmd/shadowtree recipes
go run ./cmd/shadowtree test
go run ./cmd/shadowtree check
go run ./cmd/shadowtree build
go run ./cmd/shadowtree fmt
go run ./cmd/shadowtree tidy

The repository's own .shadowtree.toml uses the Go profile and adds project recipes such as build, ci-test, install, and install-skill.

About

Shadowtree runs development recipes in a disposable copy of the current project.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages