| id | 78 |
|---|---|
| title | Query subcommand for front-matter filtering |
| status | 🔲 |
| summary | Add mdsmith query to select files by CUE expression on front matter |
The plan/ directory accumulates completed plans. Today
the only cleanup path is manual grep-and-rm. mdsmith
already parses front matter and evaluates CUE constraints
(in requiredstructure), so it can drive file selection
natively.
A general query subcommand prints matching file paths
to stdout. Callers compose it with Unix tools:
mdsmith query 'status: "✅"' "plan/[0-9]*.md" \
| xargs git rmCUE is the query language. mdsmith already depends on
cuelang.org/go/cue for front-matter validation. A CUE
expression unifies with parsed YAML front matter — the
same mechanism validateFrontMatterCUE uses.
Add mdsmith query to print paths of Markdown files
whose front matter satisfies a CUE expression, one path
per line on stdout.
mdsmith query [flags] <cue-expr> [files...]Flags:
-0— NUL-delimit output (forxargs -0)-v, --verbose— print skipped files and reasons on stderr
The CUE expression is a struct literal body. It is compiled and unified with each file's front matter. Files whose front matter satisfies the expression are printed.
Examples:
# List completed plans
mdsmith query 'status: "✅"' "plan/[0-9]*.md"
# Delete them
mdsmith query 'status: "✅"' "plan/[0-9]*.md" \
| xargs git rm
# NUL-delimited for paths with spaces
mdsmith query -0 'status: "✅"' plan/ | xargs -0 rm
# Compound condition
mdsmith query 'status: "✅", id: >50' plan/
# All plans not yet started
mdsmith query 'status: "🔲"' plan/Output goes to stdout (one path per line). Status messages and errors go to stderr.
Exit codes follow grep convention, not the linter
convention (check/fix use 1 for "issues found").
This is intentional — query is a filter, not a
linter:
- 0 — at least one file matched
- 1 — no files matched
- 2 — invalid CUE expression or runtime error
A malformed CUE expression prints the compile error to stderr and exits 2, so typos are never silent.
Files without front matter or where unification fails
are silently skipped (shown with --verbose).
Proto/template files are naturally excluded because their
front-matter values are CUE schema strings (e.g.
'"🔲" | "🔳" | "✅"'), not concrete values — CUE
unification rejects non-concrete data.
The core logic mirrors validateFrontMatterCUE in
internal/rules/requiredstructure/rule.go:
func matchFrontMatter(schema cue.Value,
fm map[string]any) bool {
data, _ := json.Marshal(fm)
val := schema.Context().CompileBytes(data)
merged := schema.Unify(val)
return merged.Validate(cue.Concrete(true)) == nil
}The caller compiles the CUE expression once and exits
2 on compile error. It then passes the compiled value
to matchFrontMatter for each file.
Reuses lint.ResolveFilesWithOpts for file discovery.
Reuses lint.StripFrontMatter plus YAML unmarshal
for front matter. Unmarshals into map[string]any to
keep numeric types so id: >50 works.
- Extract
readFrontMatterRaw(path) map[string]anyinto a shared internal package (or keep a small local copy) so both catalog and query can use it - Add
internal/query/query.gowithMatch(expr string, fm map[string]any) boolwrapping CUE compile-unify-validate - Add
runQueryincmd/mdsmith/main.gowith flag parsing (-0,--verbose), file resolution, and stdout output loop - Register
queryin the subcommand dispatcher and updateusageText - Add
queryto the Commands table inREADME.mdanddocs/reference/cli.md - Write unit tests for
internal/query: matching expression passes, non-matching fails, missing field fails, absent front matter fails, schema-string front matter (proto) fails, compound expressions - Write an e2e test: matches printed to stdout, no
matches yields exit 1,
-0uses NUL delimiter - Run
mdsmith check .andgo test ./...
-
mdsmith query 'status: "✅"' plan/prints paths of completed plans, one per line - Piping into
xargs git rmdeletes them - Exit 0 on match, exit 1 on no match
-
-0outputs NUL-delimited paths - Files without front matter are silently skipped
-
proto.mdis never matched (non-concrete CUE values fail validation) - All tests pass:
go test ./... -
README.mdCommands table includesquery -
docs/reference/cli.mdCommands table includesquery - Invalid CUE expression prints error to stderr and exits 2
-
go tool golangci-lint runreports no issues