Architecture updates to support the plugin's I/O and configuration layer: - TaskSource interface abstracts task loading from I/O, making future sources (API, database, test) swappable without operation changes - FileSource implements v1: Bun.Glob for directory scanning, Bun.file for reading, parseFrontmatter for parsing (single-pass I/O) - SourceResult provides raw file content (for show) and per-file error detail (for validate) that parseTaskDirectory couldn't offer - Config schema uses TypeBox (already a dep via taskgraph) for compile-time types, runtime validation, and JSON Schema export - ADR-005: TaskSource abstraction rationale - ADR-006: Bun.Glob over parseTaskDirectory rationale - Performance benchmark added (43 tasks full pipeline: ~150ms) - AGENTS.md updated with config section and source structure
2.8 KiB
status, last_updated
| status | last_updated |
|---|---|
| draft | 2026-04-28 |
ADR-006: Bun.Glob Over parseTaskDirectory
Context
@alkdev/taskgraph provides parseTaskDirectory(dirPath) — a convenience function that recursively scans a directory for .md files and returns TaskInput[]. It uses node:fs/promises.readdir for directory traversal and silently skips files with invalid frontmatter.
The plugin needs more than what parseTaskDirectory provides:
- Raw file content — the
showoperation returns full markdown body (frontmatter + description + acceptance criteria + notes).parseTaskDirectoryonly returns parsed frontmatter. - Error detail by filename — the
validateoperation reports which file failed and why.parseTaskDirectorysilently skips invalid files with no error reporting. - Bun-native runtime — the plugin targets Bun.
Bun.GlobandBun.file()are native APIs with no Node compat overhead. - Single-pass I/O — read each file once.
parseTaskDirectory+ separate file reads forshowwould be two passes.
Decision
Use Bun.Glob("**/*.md") for directory scanning, Bun.file().text() for reading, and parseFrontmatter() (singular, from @alkdev/taskgraph) for parsing. The FileSource class orchestrates this into a SourceResult.
We still use parseFrontmatter() for the YAML/schema validation — we just don't use parseTaskDirectory or parseTaskFile (which does the same thing but with node:fs/promises.readFile).
Consequences
Positive:
- Single I/O pass per operation call — glob scan, read all files, parse in memory
rawFilesMap gives full content forshowwithout a second readerrorsarray gives per-file error detail forvalidate- Bun-native APIs (
Bun.Glob,Bun.file()) — no Node compat layer - Consistent with the TaskSource abstraction (see ADR-005)
Negative:
- Not using
parseTaskDirectorymeans reimplementing directory scanning — butBun.Globis ~2 lines and more flexible - Not using
parseTaskFilemeans we callparseFrontmatter()directly after reading the file ourselves — same outcome, slightly more code - The
rawFilesMap keeps all file content in memory — acceptable for typical task sets (≤50 files, ≤100KB total)
Benchmark
43 task files, all analysis functions, Bun runtime:
Bun.Globscan: ~1ms- File read +
parseFrontmatter(43 files): ~140ms TaskGraph.fromTasks: ~5ms- All 6 analysis functions: ~17ms
- Total: ~150ms
The Rust CLI is faster on raw I/O/parsing (native binary), but the plugin eliminates subprocess overhead and plain-text parsing by the LLM. Overall tool call latency favors the plugin.
References
@alkdev/taskgraphfrontmatter/file-io.ts—parseTaskFileandparseTaskDirectoryimplementations- Bun API docs:
Bun.Glob,Bun.file()