You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
perf(init): tighten list-dir hot loop + emit POSIX paths (#815)
## Summary
Focused code-audit cleanup of `src/lib/init/tools/list-dir.ts`.
No behavior changes on POSIX hosts; Windows hosts now get a
consistent wire contract.
## What changed
### Correctness: emit POSIX-separator paths
`path.relative(cwd, ...)` returns native separators, so Windows
hosts were sending paths like \`src\\app.ts\` on the Mastra wire.
Two downstream callers had \`replaceAll(\"\\\\\", \"/\")\` patches
to cope — one (\`workflow-inputs.ts::preReadCommonFiles\`, the
direct list-dir consumer) is cleaned up here; the other
(\`formatters.ts\`, normalizing data from a different source) stays
as-is.
The existing \`filesystem-tools.test.ts\` already asserted
\`src/app.ts\` (POSIX) and would have failed on Windows pre-fix.
### Perf: prune the hot loop
Per-entry changes in \`walkDirectory\`:
1. \`dir + NATIVE_SEP + entry.name\` instead of \`path.join(...)\` —
manual concat is ~10× faster in V8 since inputs are already clean.
2. \`abs.slice(state.cwdPrefixLen)\` instead of \`path.relative(...)\` —
O(1) slice instead of a parse-and-allocate call, since \`dir\` always
starts with \`cwd + sep\`.
3. \`cwdPrefixLen\` cached once at walk entry rather than recomputed per
dirent.
4. Dropped the redundant mid-loop \`reachedWalkLimit\` call (the
function-entry guard handles \`depth\`; an inline \`state.entries.length
>= state.maxEntries\` check handles the cap).
5. Same manual-concat fix applied to the recursion call site.
Microbench on a 500-entry recursive listing (30 runs, 3 warmup):
| Version | p50 |
|---|---:|
| Before | 1.705 ms |
| After | **1.130 ms** |
−34% at p50. Absolute numbers are small in both cases because
list-dir isn't on a hot path (called once per wizard step), but
cleanup stacked on top of the correctness fix.
## New test coverage
Added \`test/lib/init/tools/list-dir.test.ts\` with 11 dedicated tests.
Pre-PR, list-dir was covered by a single smoke test in
\`filesystem-tools.test.ts\`.
## Test plan
- [x] \`bunx tsc --noEmit\` — clean
- [x] \`bun run lint\` — clean (1 pre-existing markdown.ts warning,
unrelated)
- [x] \`bun test test/lib/init/\` — **135 pass, 0 fail** (+11 new)
- [x] \`bun test --timeout 15000 test/lib test/commands test/types\` —
**5664 pass, 0 fail**
- [x] \`bun test test/isolated\` — 138 pass
- [x] Microbench before/after — −34% p50 on 500-entry fixture
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* **mapFilesConcurrent skips null but not empty arrays — callers must return null for no-op**: Scan/formatter off-by-ones: (1) \`mapFilesConcurrent\` in \`src/lib/scan/concurrent.ts\` filters \`null\` but NOT empty arrays — fires \`onResult\` per empty result. Callers (e.g. \`processEntry\` in \`dsn/code-scanner.ts\`) must return \`null\` (not \`\[]\`) for no-op files (~99% of 10k-file walks). Stream variant filters both. (2) \`collectGlob\`/\`collectGrep\` must NOT forward \`maxResults\` to underlying iterator — collector drains uncapped, sets \`truncated=true\` on overshoot; forwarding stops iterator at exactly N without \`truncated\`. (3) \`filterFields\` in \`formatters/json.ts\` uses dot-notation — property tests must use \`\[a-zA-Z0-9\_]\` charset to avoid ambiguous keys with dots.
0 commit comments