One of the things I love about Howzit is how it bridges the gap between simple task lists and full automation. You can write quick (or complex) scripts right in your build notes and, with the latest updates, they can communicate back to Howzit in useful ways. The latest updates make this even easier with automatically-injected helper scripts and some powerful new directives.

The first thing I did was a major overhaul of the way Topics are processed. It now “streams” the topics in a sequential order, reevaluating conditions and variable substitutions after each executable directive or block is run. That means that the variables you pass from inside of run blocks can be used to control if/else logic in the rest of the topic being run, and it makes variables immediately available in the next task in the topic. Previously you would have had to incorporate multiple topics to use the variables effectively. Now they work the way you would expect in any scripting environment. (Yes, I’ll apply this idea to Bunch soon, too).

Here are couple of features made possible by this change:

Automatic Helper Script Injection

When you write a run block (those fenced code blocks with run as the language), Howzit automatically detects your script’s interpreter from the hashbang and injects helper functions. No setup required — just start using them.

Here’s what happens behind the scenes: Howzit sees your hashbang, figures out you’re using bash (or Ruby, Python, Fish, etc.), and automatically loads the appropriate helper script. The helpers are installed in ~/.config/howzit/support/ and made available via the HOWZIT_SUPPORT_DIR environment variable.

By the way, Howzit now runs out of ~/.config instead of ~/.local/share. It will automatically migrate files as necessary, with confirmation. You can force a migration with howzit --migrate.

For bash scripts, you get functions like log and set_var:

```run Build and Test
#!/bin/bash
log info "Starting build process..."
set_var BUILD_STATUS "in_progress"

# Do your build stuff
make build

if [ $? -eq 0 ]; then
  set_var BUILD_STATUS "success"
  log info "Build completed successfully"
else
  set_var BUILD_STATUS "failed"
  log error "Build failed"
fi
```

The same helpers work in Ruby, Python, Fish, and other languages—just with syntax that matches each language. In Ruby, it’s Howzit.logger.info() and Howzit.set_var(). In Python, it’s howzit.Howzit.logger.info() and howzit.Howzit.set_var(). You get the idea.

Controlling Log Output with @log_level

Sometimes you want verbose output during development, but quieter logs in production. The @log_level directive lets you control which messages from your scripts actually get displayed.

Log levels work like you’d expect: debug shows everything, info shows informational messages and above, warn shows warnings and errors, and error only shows errors. You can change the log level multiple times throughout a topic:

@log_level(debug)
@run(./verbose-script.sh) Development build with all messages

@log_level(warn)
@run(./production-script.sh) Production build, warnings only

This is especially useful when you have scripts that output a lot of debug information. Set the log level to warn or error and you’ll only see what matters.

Setting Variables with @set_var

Variables are super useful for making your build notes dynamic. You can set them in scripts using set_var, but now you can also set them directly in your build notes with the @set_var directive.

The basic syntax is straightforward:

@set_var(VERSION, "1.2.3")
@set_var(STATUS, "success")

Variables set this way work exactly like variables set in scripts — they’re available as ${VAR} in subsequent tasks and can be used in conditional blocks.

Command Substitution

Here’s where it gets interesting: @set_var supports command substitution. You can execute a command and use its output as the variable value:

@set_var(VERSION, `rake vver`)
@set_var(BUILD_DATE, $(date +%Y-%m-%d))

Both backticks and $() syntax work. The command output (with whitespace stripped) becomes your variable value.

Even better, you can reference other variables in your commands:

@set_var(PREV_VERSION, "1.2.2")
@set_var(NEXT_VERSION, `echo ${PREV_VERSION} | awk -F. '{print $1"."$2"."$3+1}'`)

The ${PREV_VERSION} gets substituted before the command runs, so you can build up complex values from simpler ones.

Putting It All Together

Here’s a practical example that combines everything:

## Deploy

@set_var(VERSION, `git describe --tags`)
@set_var(BRANCH, `git rev-parse --abbrev-ref HEAD`)

@log_level(info)
@if ${BRANCH} == "main"
  @run(./deploy-prod.sh ${VERSION}) Deploy to production
@else
  @run(./deploy-staging.sh ${VERSION}) Deploy to staging
@end

@log_level(debug)
```run Verify Deployment
#!/bin/bash
log debug "Checking deployment status..."
# More verbose logging here
```

The @set_var directives set up your variables from git commands, @log_level controls verbosity, and the conditional uses those variables to decide what to deploy. The run block at the end uses the helper functions to send messages back to Howzit.

Why This Matters

These features make Howzit build notes feel more like a real scripting environment while keeping the simplicity of markdown, and now scripts within a topic can communicate with each other, and topics can cascade variables between them. You can write complex automation without leaving your build notes file, and the helper scripts mean you don’t have to remember communication file formats or write boilerplate code.

The automatic injection means less friction — just write your script and use the helpers. The @log_level directive gives you control over noise. And @set_var with command substitution lets you build dynamic workflows that adapt to your project’s current state.

This latest release, along with the recent conditional blocks features, makes Howzit even more useful for me. Hopefully you’ll find it useful, too. See the project page for an overview, and check out the wiki for full documentation.