We are live on DevHunt: tool of the week contest

Check it out

Taskless v0.11.x: Vale, Markdown, and More

Jakob Heuser

Taskless CLI 0.11 is out. Going from 0.10 to 0.11: Vale ships as a second engine, check runs all three engines at once, and taskless agent route works out where a rule belongs so you don't have to.

What changed

Until now, a Taskless rule was an ast-grep pattern. That covers most of what goes wrong in code. It doesn't cover the line in your docs that says "log in" when your product says "sign in," and it doesn't cover the convention that lives in one senior engineer's review comments.

Markdown is a suggestion dressed up like a rule. 0.11 is where we stopped adding suggestions.

You describe the constraint. Taskless decides whether that's an ast-grep pattern, a Vale rule, an executable check, or something that belongs in the linter you're already running.

Vale, for the words

Vale is a prose linter. It's good, it's fast, and teams have been using it for years to enforce style guides. It's now a first-class Taskless engine, which means a correction about language becomes a rule the same way a correction about code does.

Banned terms. Product names spelled the way your brand guide spells them. The phrase your support team hates. Docs that have to say "sign in" every time.

Vale rules are static, so they work just like the ast-grep rules you're already writing: no login, no signing, no server round trip. A Vale rule can't reach your machine either, and that's Vale's design rather than a promise from us. Its scripting sandbox exposes text, math and fmt. We checked the rest. os, io and exec all come back "module not found."

Three engines, one run

check dispatches by engine and runs everything at once, then merges the findings into a single result set.

sg ast-grep. Code structure, matched as a pattern.
vale Prose and docs. Terms, phrases, style.
runtime An executable check.ts, for rules that read across files.

An engine that isn't available says so and the others still report. An engine that times out or rejects its config fails the check. A green run means every engine ran.

The router is the part that makes this feel like one tool instead of three. taskless agent route reads what your repo already has, picks the lightest thing that does the job, and names the authoring procedure to follow. Your existing linter when it fits. An sg rule when the correction is structural. A Vale rule when it's about language. A runtime rule only when the check has to open more than one file.

An agent goes from a correction to create-vale-rule without anyone stopping to decide which engine this is.

Markdown takes both engines

A markdown document is two things at once. There's the structure, headings and fenced blocks and list items, and there's the prose living inside it. Before 0.11, Taskless could only address the first, because ast-grep was the only engine and it reads syntax trees.

0.11 closes it from both sides. The vendored ast-grep goes to 0.45.2, which adds Markdown and Dart to the languages an sg rule can target, so heading levels, fenced code blocks, list items and paragraphs all become addressable. Vale arrives to read the words, which is everything inside a line: link text, banned terms, the product name spelled the way your brand guide spells it.

That split is most of why Vale is here. ast-grep exposes tree-sitter-markdown's block half, so it can tell you a fenced block carries no language tag. The inline half, links and emphasis and the actual sentences, is Vale's. Between them the document is covered, and neither engine gets there alone.

So "every fenced block declares a language" is an sg rule and "link text must not read click here" is a Vale rule. Since .md is the first extension both static engines claim, route tells you which question each one answers rather than leaving it to the file extension.

Two things to know when you write the sg half. Reaching for an inline kind like link is a config error that exits 8 and takes the whole scan with it, so stay in the block kinds. And "every doc has exactly one h1" belongs to neither static engine, because ast-grep has no count assertion and cannot see absence. That one is a runtime rule.

Every rule is a directory

.taskless/rules/<engine>/<id>/
rule.yml
.vale.ini
.tests/

Writing a rule means making a directory. Deleting one means rm -rf. Nothing outside it changes either way, so two people adding rules on the same afternoon don't collide on a shared file.

Vale rules carry their own .vale.ini saying which files they apply to. The config Vale reads gets assembled from those on every run and regenerated, so there's no shared file to hand-edit and nothing to resolve in a merge.

Projects on the old layout migrate on the next command.

Breaking changes

taskless help <topic> is now taskless agent <topic>. The command is named for who reads it. Agents fetching a procedure aren't asking for help. There's no alias, so an old command line stops working.

Topics are a single token now:

rule create -> create-sg-rule / create-remote-rule
rule improve -> improve-rule
rule delete -> delete-rule
rule verify -> verify-rule
rule meta -> rule-meta
static -> create-sg-rule
existing -> create-legacy-rule
engine-selection -> route

engine-selection is gone rather than renamed. route applies the reasoning itself and names a concrete create-*-rule topic, so the criterion is stated once.

rule verify is replaced by two commands addressed by path. verify <path> checks that a rule has what its engine needs and doesn't require tests, so it works while you're still writing. test <path> runs the tests after verifying. Both take a rule directory, an engine directory, or nothing at all for the whole project. Think of verify as confirming the rule is written right, and test as confirming the rule checks what you wanted.

A rewriters: entry in an sg rule now requires a fix:. It was optional in ast-grep 0.41.0. We can't migrate this one for you, because fix is replacement text and a tool can find every affected rewriter without being able to write one. verify names the offending rewriter directly. Nothing we ship uses rewriters:, so this only reaches rules you wrote yourself.

If you consume @taskless/cli/prompts, TOPICS changed and engine-selection is no longer exported.

Versions

ast-grep 0.45.2 and Vale 3.20.0, both pinned and both checked by contract tests that run the actual binaries. A version bump that adds or drops a language fails those tests instead of quietly narrowing what the router thinks it can build.

Getting it

npx @taskless/cli

The Vale binary comes down as a per-platform package alongside the CLI. No install script, nothing fetched at runtime, and only your platform's binary lands. Hosts we don't publish for install cleanly and fall back to a vale on your PATH.

If you've got a style guide sitting in a doc somewhere that nothing enforces, that's the first rule to write.