GitHub - Isaac12x/seed-cli: Terraform + Make + rsync semantics for directory trees.

8 min read Original article ↗

seed-cli

seed is a Terraform-inspired, spec-driven filesystem orchestration tool.

It captures directory trees, plans changes, applies them safely, syncs drift, scaffolds projects from reusable templates, and can also execute manifest-driven repository or service maintenance.

Think Terraform for directory trees, plus template scaffolding and workspace maintenance.

ci-cd

Highlights

  • Multiple spec inputs: .tree, .seed, YAML, JSON, DOT, image OCR, and stdin
  • Deterministic planning with exportable plans: seed plan spec.seed --out plan.json
  • Safe execution of immutable plans: seed apply plan.json
  • Drift workflows: diff, sync, match, snapshots, and spec history
  • Template variables in paths and content: <varname>/ and {{var}}
  • Project-local template registration under .seed/templates/
  • Reusable template registry with versions, locking, content sources, and built-in templates
  • Copier-style template config support: questions, defaults, answers files, excludes, skip-if-exists, and gated tasks
  • Manifest-driven repository/service/system maintenance with seed maintain
  • Structure locking, watch mode, state locks, hooks, Graphviz export, and shell completion

Install

pip install seed-cli
pip install "seed-cli[image]"   # OCR/image parsing
pip install "seed-cli[ui]"      # rich terminal output

Python >=3.10 is required.

Quick Start

Capture an existing directory, preview a plan, then apply it:

seed capture --out project.tree
seed plan project.tree --out plan.json
seed apply plan.json

For direct spec execution:

seed register project.tree
seed apply project.tree
seed diff project.tree

For repository or service automation:

seed maintain maintenance.yml
seed maintain maintenance.yml --execute

Commands

seed --help groups commands by workflow so related tasks are easier to scan.

Group Commands Description
Plan & Apply plan, diff, apply, sync, match Preview, compare, and apply filesystem changes
Templates register, create, templates (template) Register, instantiate, and manage reusable specs
State & History capture, revert, specs, lock Capture state, recover snapshots, inspect history, and enforce structure versions
Maintenance doctor, maintain, hooks Lint specs, run repository/service maintenance, and install hooks
Export & Utilities export, utils Export trees/plans/DOT output and run helper tools

Core Workflow

Immutable Plans

seed plan dir_structure.tree --out plan.json
seed apply plan.json

seed apply also accepts a spec directly:

seed apply dir_structure.tree

When you apply a spec with template placeholders such as <name>/ or features/<name>.ts, seed apply first runs seed register semantics: it writes the supporting files under .seed/templates/ and .seed/templates/project/, then removes any stale literal placeholder paths like features/<name>/ left behind by older runs.

Drift Detection

seed diff dir_structure.tree
seed sync dir_structure.tree --dangerous
seed match dir_structure.tree --dangerous

sync deletes extras not in the spec. match also creates missing paths while respecting directories marked with ....

Partial Plans

seed plan dir_structure.tree --target scripts/
seed plan dir_structure.tree --targets "services/*"
seed plan dir_structure.tree --target-mode exact

Spec Syntax

Use .tree for simple filesystem specs, or .seed when you want the same tree-shaped format plus richer inline metadata such as kinds, tags, and URLs.

Basic Example

@include base.tree

scripts/
├── build.py        @generated
├── notes.txt       @manual
├── cache/          ...
└── docs/           ?

Markers

  • @include file.tree: include another spec
  • @generated: generated file
  • @manual: manually maintained file
  • ?: optional file or directory
  • ...: allow extras inside a directory
  • <varname>: template placeholder in a path segment or filename
  • {{var}}: variable interpolation in file contents

.seed also supports inline metadata markers:

  • !kind: semantic kind marker such as !service, !doc, or !template
  • +tag: repeatable tags such as +remote +shared
  • -> URL: attach a metadata URL to a node; on directory nodes this can be used as a template content source

Example:

vendor/
└── api/ !service +remote -> https://github.com/acme/repo.git

Structured YAML and JSON specs can also carry metadata with either a metadata object or top-level kind, tags, and url fields.

Variable Usage

seed plan spec.tree --vars project_name=myapp
seed apply spec.tree --vars project_name=myapp
seed create spec.tree project_name=myapp

Templates

Template Directories

Define repeating structures with template variables:

files/
├── <version_id>/
│   ├── data.json
│   └── meta/
└── ...

Placeholders can also appear in path-per-line specs or filenames, and templates can include more than one placeholder:

features/<domain>/<name>/route.ts
features/<name>.ts

Create instances:

seed create releases.tree version_id=v3
seed create component.tree domain=billing name=invoices
seed create releases.tree version_id=v3 --dry-run

Project-Local Templates

Use seed register to mirror any .tree or .seed spec into the project-level .seed/templates/ directory. When the spec contains placeholders anywhere in a path, it also extracts the outermost placeholder subtree into .seed/templates/project/. seed apply <spec> runs the same registration step automatically before execution.

seed register releases.tree
seed apply releases.tree

That lets you create from either a path-based project template:

seed create --template .seed/templates/releases.tree version_id=v3

or a registered project template name:

seed create --project version_id version_id=v3

Template Registry

Manage reusable templates stored under ~/.seed/templates/ by default, or under $SEED_HOME/templates/ when SEED_HOME is set.

seed templates list
seed templates add ./template.tree --name my-template
seed templates add ./template.seed --name service-template
seed templates show my-template
seed templates use my-template target-folder
seed templates versions my-template --add ./updated.tree --name v2
seed templates lock my-template
seed templates update my-template
seed templates remove my-template

seed templates list also shows project-local templates discovered from .seed/templates/project/, before global registry templates. seed templates use <name> <folder> resolves a visible project template first, then falls back to the global registry. The singular alias seed template use ... is also accepted.

Built-in templates include fastapi, python-package, and node-typescript.

Template Content Sources

Templates can point at a local directory, a GitHub tree URL, or a git repository URL so seed can fetch real file contents alongside the structure spec. Repository sources are cloned without their .git metadata.

seed templates add ./fastapi --name fastapi \
  --content-url https://github.com/tiangolo/full-stack-fastapi-template/tree/master/backend/app

seed templates add ./service.seed --name service \
  --content-url https://github.com/acme/service-skeleton.git

seed templates update fastapi
seed templates update --all
seed templates update fastapi --content-url /path/to/local/files

Templates that include a source.json file with {"content_url": "..."} are fetched automatically when installed. .seed directory nodes can also declare their own content sources inline:

vendor/
└── api/ !service +remote -> https://github.com/acme/api-client.git

Copier-Style Scaffolding

seed templates use supports template config files named copier.yml, copier.yaml, .seed-template.yml, or .seed-template.yaml.

Supported workflow features include:

  • promptable questions and defaults
  • --data-file for JSON/YAML answers
  • --defaults and --non-interactive
  • --answers-file or _answers_file
  • _exclude and _skip_if_exists
  • _tasks, which only execute with --unsafe
  • --overwrite for existing files

Example:

seed templates use python-package \
  --base ./myapp \
  --data-file answers.yml \
  --defaults \
  --answers-file .seed/answers.yml \
  --overwrite

If a template defines _tasks, they are shown but skipped unless you opt into execution:

seed templates use python-package --unsafe

Repository & System Maintenance

seed maintain orchestrates repository, service, system, and project upkeep from YAML or JSON manifests.

Built-in maintenance goals include:

  • repositories: ensure_path, git_fetch, git_status, git_pull_ff_only
  • services: ensure_paths, compose_pull, compose_up, launchctl_restart
  • custom actions with tool, args, cwd, env, or shell commands

You can point seed maintain at a manifest file or a directory containing maintenance.yml, project.yml, or service.yml.

Workspace Manifest

targets:
  - name: seed-cli
    kind: repository
    path: ./repos/seed-cli
    goals:
      - ensure_path
      - git_fetch
      - git_status

  - name: notes-api
    kind: service
    path: ./systems/services/notes-api
    config_dir: ./systems/services/notes-api/config
    data_dir: ./local/services/notes-api
    compose_file: compose.yml
    deploy_engine: docker-compose
    launch_agent: user/com.example.notes-api
    goals:
      - ensure_paths
      - compose_pull
      - compose_up
      - launchctl_restart

project.yml

name: product-x
type: project
path: ~/work/projects/active/product-x
maintenance:
  goals:
    - git_fetch
    - git_status
repos:
  - name: web-app
    path: repos/web-app
  - name: api
    path: repos/api

service.yml

name: notes-api
type: service
path: ~/systems/services/notes-api
config_dir: ~/systems/services/notes-api/config
data_dir: ~/local/services/notes-api
compose_file: compose.yml
deploy_engine: docker-compose
launch_agent: user/com.example.notes-api
maintenance:
  goals:
    - ensure_paths
    - compose_pull
    - compose_up
    - launchctl_restart
  actions:
    - tool: python
      args: ["scripts/rebuild_index.py"]
      cwd: "{{path}}"

Run the planner first, then execute:

seed maintain ./workspace
seed maintain ./workspace --execute

Snapshots, Spec History, and Locks

Snapshots are created automatically before apply, sync, and match:

seed revert --list
seed revert
seed revert abc123 --dry-run

Applied structures are also captured as versioned specs:

seed specs list
seed specs show
seed specs diff v1 v3
seed specs watch

seed specs watch polls the workspace and writes a new .seed/specs/vN.tree whenever the filesystem structure changes, so manual file creation after an initial apply advances the internal reference automatically.

Lock a filesystem structure and watch it for drift:

seed lock set spec.tree
seed lock list
seed lock status
seed lock watch
seed lock upgrade v2 --dry-run
seed lock downgrade v1 --dangerous

Export, Hooks, and Utilities

Export current state or a plan:

seed export tree --out structure.tree
seed export json --out structure.json
seed export dot --out structure.dot
seed plan spec.tree --dot > plan.dot

Install git hooks:

seed hooks install
seed hooks install --hook pre-push

Utilities:

seed utils extract-tree screenshot.png --out spec.tree
seed utils state-lock
seed utils state-lock --force-unlock

Shell Autocomplete

Click provides shell completion for Bash, Zsh, and Fish. Generate the completion script from the installed seed entry point:

# zsh
eval "$(_SEED_COMPLETE=zsh_source seed)"

# bash
eval "$(_SEED_COMPLETE=bash_source seed)"

# fish
_SEED_COMPLETE=fish_source seed | source

Then reload your shell and use tab completion:

seed <TAB>
seed templates <TAB>
seed lock <TAB>

Safety Model

seed is designed to be safe by default:

  • destructive workflows require explicit dangerous flags
  • execution state is lock-protected with heartbeat renewal
  • plans are validated before writes and deletes
  • template tasks require explicit --unsafe
  • answers files and execution targets are constrained to the base directory
  • git maintenance refuses git pull --ff-only on dirty worktrees

Philosophy

seed-cli is:

  • Declarative
  • Deterministic
  • Auditable
  • Safe by default

License

Modified MIT. See LICENSE.md.