Skip to content

Synthesize

Synthesize is a tool for managing long-lived development workflows that involve multiple tools executing concurrently, each of which might have bespoke conditions around when and how it needs to be run or re-run.

In Synthesize, a flow is a graph (potentially disjoint) of nodes, each of which runs a recipe whenever one of that node's triggers activates. Synthesize has a wide variety of triggers:

  • Recipe B should run after recipe A runs.
  • Recipe W should run every time file F changes.
  • Recipe R should be restarted if it ever exits.
  • Recipe O should run once when the flow starts.

These can all coexist as part of same flow, and can even be combined for a single recipe, allowing for complex nodes like "restart recipe W if it exits or if file F changes".

Features

  • Recipe and trigger definitions can be factored out and shared across multiple nodes and flows.
  • Recipes are just shell commands, so you can use any tools you'd like. Synthesize works with your existing tools, it doesn't replace them.
  • Recipes can be parameterized with arguments (each recipe is actually a Jinja template) and environment variables. Arguments and environment variables can also be provided at the flow and recipe levels (most specific wins).
  • Nodes can have multiple triggers, allowing you to express complex triggering conditions.
  • All command output is combined in a single output stream, with each node's output prefixed with a timestamp and its name.
  • The current time and the status of each node is displayed at the bottom of your terminal.
  • You can generate Mermaid diagrams of your flows for debugging and documentation.

Examples

As an example, here is Synthesize's own synth.yaml configuration file:

flows:
  default:
    nodes:
      tests:
        recipe: tests
        triggers:
        - code-changes
      types:
        recipe: types
        triggers:
        - code-changes
      docs:
        recipe: docs
        triggers:
        - delay: 1
        - watch: ["docs/hooks/"]

recipes:
  tests:
    commands: |
      pytest -vv --cov

  types:
    commands: |
      mypy

  docs:
    commands: |
      mkdocs serve --strict

triggers:
  code-changes:
    watch:
    - src/
    - tests/
    - docs/examples/
    - docs/hooks/
    - pyproject.toml
    - .coveragerc
flowchart TD
  tests(tests)
  w_bb19edcd7b45bfcfbfcab4096798a339f9610617[("src/
tests/
docs/examples/
docs/hooks/
pyproject.toml
.coveragerc")]
  w_bb19edcd7b45bfcfbfcab4096798a339f9610617 -->|👁| tests
  types(types)
  w_bb19edcd7b45bfcfbfcab4096798a339f9610617 -->|👁| types
  docs(docs)
  docs -->|∞ 1s| docs
  w_f8192994ea74c4d2311ffd6eb936c7c30e96617f[("docs/hooks/")]
  w_f8192994ea74c4d2311ffd6eb936c7c30e96617f -->|👁| docs

Installation

Synthesize is available on PyPI.

We recommend installing Synthesize as a uv tool:

uv tool install synthesize

Then run synth --help to get started.

Or to try it out without a permanent install using uvx:

uvx --from synthesize synth --help

Or to add it as a development dependency in a uv project:

uv add --dev synthesize

Then use uv run synth to run it within the project environment.

Synthesize does not work on Windows

We recommend using the Windows Subsystem for Linux (WSL) to run Synthesize on Windows.

Inspirations