Skip to content

Authoring Rules

When you ask your coding agent to write a rule, Taskless doesn’t jump straight to its service. It routes the request local-first: it stays on your machine whenever it reasonably can, and only calls the Taskless service when a rule genuinely needs it.

Routing makes one decision, which recipe authors this rule, and it makes that from evidence rather than from preference.

Start from the evidence, not the destination

Section titled “Start from the evidence, not the destination”

The order matters, and getting it backwards is the common failure. Naming a destination first and justifying it afterwards is how a rule about prose ends up in sg, and how a question that spans files ends up in vale.

So the question to answer first is not “which engine?” but “what would you have to look at to decide this?”

Routing starts with a scan:

Terminal window
npx @taskless/cli detect --json

detect is offline and deterministic. It reads your repository and reports what it finds, with no network call: the linters you already use, the languages present, and any existing rule styles. In a monorepo the evidence carries the sub-package path, so a linter configured in one workspace is reported as such. This is the ground truth for routing rather than a guess about your tooling.

{
"success": true,
"linters": [{ "name": "eslint", "evidence": [".eslintrc.json"] }],
"languages": ["JavaScript", "TypeScript"],
"ruleStyles": [
{
"source": ".taskless/rules/sg",
"description": "Existing Taskless ast-grep rules."
}
]
}

Login state is read at the same time, because it changes which destinations exist. It is an input to the decision, not a requirement for making it. Routing itself needs no account.

The rule is decided by… Recipe Login
a tool the repo already runs, in that tool’s dialect create-legacy-rule no
one file’s syntax tree: a call, an import, a JSX attribute, a type annotation create-sg-rule no
a document’s words: docs, README files, comments, commit bodies create-vale-rule no
more than one file, or something outside the files create-runtime-rule / create-remote-rule yes

Run the decision rather than making it yourself:

Terminal window
npx @taskless/cli agent route

Two distinctions catch people out:

Correlating two things inside one file is still sg. A useEffect whose dependency array omits a value used in its body compares two places, but both are in one file, so a single-file engine can decide it.

Consistency across documents is not vale. Vale sees one document at a time. “No page may use this word” is Vale; “these twelve pages must agree on a term” needs evidence outside the file being checked, which makes it a runtime question.

If your repository already uses a linter that can express the rule, Taskless writes it in that tool’s own dialect, matching your existing config and conventions. Taskless doesn’t maintain a catalog of linter rules; it sources the pattern from your repo first and the tool’s current docs second, then writes it where that tool expects.

This path is author-only: your own toolchain runs the rule. taskless check does not execute external linters. You run the rule with your existing lint script, the same way you run every other rule in that tool.

When the rule is decided by one file’s structure, Taskless authors an sg rule locally, with no login, into .taskless/rules/sg/<id>/, then validates it with verify and test in a feedback loop until it passes. These files are exactly what the Taskless service would have written, so check and later improvement treat them identically.

When the rule is decided by a document’s words, Taskless authors a vale rule into .taskless/rules/vale/<id>/, also locally and with no login. Vale rules need both a style file and a .vale.ini scope, and an incomplete one fails silently, so verify is part of the loop rather than an optional extra. See Vale.

The runtime engine, or the Taskless service

Section titled “The runtime engine, or the Taskless service”

Some rules aren’t reasonably solvable from a single file. They need to hold two files at once, read git metadata, resolve an alias, or follow a config chain. Those become runtime rules, or are generated by the Taskless service when local authoring can’t express them.

Both require an account. Service generation also consumes a generation, so it’s reserved for what can’t be done locally. If a local attempt is tried first and can’t capture your cases, Taskless tells you before spending one.

The routing decision always leans toward staying on your machine:

  • A rule your own linter can express stays in your linter.
  • A rule one file can decide is authored locally, whether that’s sg or vale.
  • Only rules that genuinely need an account go there, and never without your knowledge.

Three of the four engines need no login at all.

Each destination has a recipe your coding agent can fetch:

Terminal window
npx @taskless/cli agent route
npx @taskless/cli agent create-legacy-rule
npx @taskless/cli agent create-sg-rule
npx @taskless/cli agent create-vale-rule
npx @taskless/cli agent create-runtime-rule
npx @taskless/cli agent create-remote-rule

To create a rule directly, see the Overview. To run your rules, see Checking rules locally. For what each engine is and where it stores rules, see Engines.