#Searching tasks
lazy search looks across tasks, prompts, turns, commits, notes, follow-ups,
captured conversations, and shared memory records. The builder and task agents
reach the same engine through the lazy_search MCP tool — both surfaces share
one parser, one evaluator, and one fuzzy content loader, so a query that works
in one works in the other, and all three matching modes see the same content.
lazy search --help is the authoritative reference for flags; this page explains
the query language and the behavior that isn't obvious from a flag list.
#Three matching modes
| Mode | When it is used | What it matches |
|---|---|---|
| Structured | The query contains a boolean operator, a field: term, or a bare #tag |
Parsed query, evaluated per field |
| Regex | Plain text with none of the above | Case-insensitive regex over content |
| Fuzzy | --fuzzy / fuzzy=true |
Typo-tolerant match; never parses field: syntax |
Mode selection is automatic — there is no flag to force structured search. Note
that --fuzzy bypasses the query language: lazy search 'tag:launch' --fuzzy
looks for the literal text tag:launch, not for tasks tagged launch.
#Quote the whole query
Always wrap the query in single quotes:
lazy search 'tag:launch AND status:blocked'
Two shell behaviors bite otherwise. # starts a comment in most shells, so an
unquoted #onboarding never reaches lazy. And an unquoted multi-word value is
split into separate argv entries before lazy sees it.
#Query language
Boolean operators are case-sensitive (AND, not and). AND binds tighter
than OR. Two adjacent terms with no operator are an implicit AND.
AND Both conditions must match
OR Either condition matches
NOT Negation
(A OR B) AND C Parentheses group
Field filters:
status:<value> Task status (working, blocked, backlog, complete, ...)
goal:<text> Match against the task goal
code:<value> Match the task code
tag:<value> Tasks carrying this tag
#<value> Shorthand for tag:<value> (see Tags below)
in:turns <text> Within turn content
in:commits <text> Within commit messages
in:comments <text> Within comments
in:followups <text> Within follow-ups
in:conversations <text> Within captured builder conversations
in:memories <text> Within shared memory records
has:commits Task has commits
has:turns Task has turns
has:comments Task has comments
has:followups Task has follow-ups
created:>YYYY-MM-DD Created after / before (also created:<)
updated:>YYYY-MM-DD Last updated after / before (also updated:<)
#Tags
Tags are normalized on write and on query, identically: lowercased, with
every run of non-alphanumeric characters collapsed to a single hyphen and
leading/trailing hyphens stripped. All four of these find the task tagged
my-feature:
lazy search 'tag:my-feature'
lazy search 'tag:MY-FEATURE'
lazy search 'tag:My_Feature!' # punctuation collapses to a hyphen
lazy search 'tag:#my-feature' # a leading '#' is stripped
Normalization applies to one token. It does not join words across a space — see the multi-word caveat below.
A tag value that normalizes to nothing (tag:"!!!") is a parse error, not a
silent zero-result — the write side rejects the same input, so the query side
does too.
##name is the spelling lazy prints
lazy tag and lazy show print tags with a leading # ("Tags: #launch"), so
that spelling has to work when pasted back into a search. A bare #launch term
is a superset, not a redirect — it matches tag:launch or the literal text
#launch. That keeps #1234 issue references in commit messages findable.
lazy search '#launch' # tag 'launch', OR the text '#launch' anywhere
#Quote a multi-word tag
This is the one real trap. tag: consumes a single token, and adjacent tokens
are an implicit AND:
lazy search 'tag:"My Feature Work"' # matches the tag 'my-feature-work'
lazy search 'tag:My Feature Work' # tag:my AND text "Feature" AND text "Work"
The second form is valid syntax that usually matches nothing. Lazy does not
guess between the two — tag:x some text is a legitimate query — so instead it
explains the empty result (below).
#Empty tag results explain themselves
A structured, non-fuzzy tag query that returns nothing prints a hint naming what is actually missing, rather than a bare "No matches found":
$ lazy search 'tag:onbording'
No matches found.
No task is tagged #onbording — did you mean #onboarding?
Known tags: #infra #launch #onboarding
Tags are normalized to lowercase alphanumerics and hyphens. Quote a multi-word tag: tag:"My Feature Work"
Details worth knowing:
- Suggestions come from tags that actually exist, ranked by prefix/substring
relation first and edit distance second — the ranking that surfaces
my-feature-workwhen an unquoted multi-word tag queried onlymy. - If every queried tag exists, there is no hint: the empty result came from the rest of the query (a status, a date range, a text term), not the tag.
NOT tag:xnever triggers a hint — it says nothing about whetherxexists.- With
--json, the hint appears as a top-levelhintfield. The MCPlazy_searchtool returns the same field.
#From a hit to the thing it hit
Result excerpts are truncated (~500 chars over MCP) on purpose: search locates,
show reads. So a hit that lives inside a task's own list — a turn, commit,
comment or follow-up — carries a locator you can hand straight to show.
CLI. Turn hits print as
turn #12in the TYPE column, solazy show <task>tells you which turn to look at without counting. With--json, each hit'scontextcarriesturn_seq(the turn number) andindex(its 0-based position).MCP. Each such hit carries
index, and turn hits also carryturnSequence. For turn, commit and comment hitsindexis the entity's position in the very listlazy_showpages over, so it works as anoffsetagainst that one section:lazy_search(query="reconciler") -> {type: "turn", index: 7, turnSequence: 8, ...} lazy_show(task_id, sections=["turns"], offset=7, limit=1) # that exact turn, in fullsections=["commits"]andsections=["comments"]work the same way.
index and turnSequence are deliberately separate. index is a pagination
offset; turnSequence is the turn's identity in rendered output. They coincide
only when a session's sequences start at 0 and skip nothing.
Follow-up hits are the exception: there is no follow_ups value in lazy_show's
sections, and none is needed — lazy_show always returns every follow-up in
full as follow_ups, unpaged, because a task's follow-ups are its triage queue
at review. So a follow-up hit's index is a position to read off in that array,
not an offset to page to. Asking for sections=["follow_ups"] is a schema error,
by design.
Hits with no position in a per-task list — task, prompt, conversation, memory —
carry neither field, rather than a misleading index: 0.
#Examples
lazy search 'auth' # regex, everywhere
lazy search 'catchup' --fuzzy # typo-tolerant
lazy search 'status:blocked AND in:turns "reconciler"'
lazy search 'has:commits AND NOT in:commits "wip"'
lazy search 'created:>2026-02-15 AND status:working'
lazy search 'tag:onboarding'
lazy search '#onboarding' # the printed spelling
lazy search 'tag:"My Feature Work"'
lazy search 'tag:launch AND status:blocked'
lazy search 'in:memories "credentials"'
lazy search 'auth' --json # structured output
#Related
- Shared memory — memory records and
in:memories - lazy-agent design — how agents reach search over MCP rather than by shelling out