Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .jules/bolt.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## 2024-06-06 - [Performance Optimization] Filesystem Traversal

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

문서 날짜를 실제 변경 시점으로 맞춰 주세요.

Line 1의 2024-06-06은 현재 PR 시점(2026-06-06)과 불일치합니다. 히스토리 추적 혼선을 막기 위해 날짜를 실제 작성일로 정정하는 게 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.jules/bolt.md at line 1, Update the markdown header "## 2024-06-06 -
[Performance Optimization] Filesystem Traversal" so the date matches the actual
write/PR date; replace the "2024-06-06" token with the correct date (e.g.,
"2026-06-06" or the true authoring date) to keep the document history
consistent.

**Learning:** In local-first CLI applications that extensively traverse the filesystem (like vspec), using `fs.readdirSync` with `fs.statSync` for each file causes a significant performance bottleneck due to excessive I/O operations.
**Action:** Always use `fs.readdirSync(..., { withFileTypes: true })` instead. It directly returns `fs.Dirent` objects containing type information, saving an I/O system call per file and dramatically speeding up directory traversals.
7 changes: 3 additions & 4 deletions src/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,9 @@ export function projectKey(start = process.cwd()): string | null {
export function walkFiles(root: string, predicate: (path: string) => boolean): string[] {
if (!existsSync(root)) return [];
const files: string[] = [];
for (const entry of readdirSync(root)) {
const path = join(root, entry);
const stat = statSync(path);
if (stat.isDirectory()) files.push(...walkFiles(path, predicate));
for (const entry of readdirSync(root, { withFileTypes: true })) {
const path = join(root, entry.name);
if (entry.isDirectory()) files.push(...walkFiles(path, predicate));
else if (predicate(path)) files.push(path);
Comment on lines +30 to 33

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# 검증 목적:
# 1) walkFiles 관련 테스트에 symlink 디렉터리 케이스가 있는지 확인
# 2) 이전 구현과 현재 구현의 디렉터리 판별 차이를 코드 레벨로 확인

set -euo pipefail

echo "== walkFiles 호출/테스트 맥락 검색 =="
rg -n -C3 '\bwalkFiles\s*\(' --type=ts

echo
echo "== symlink 관련 테스트/정책 흔적 검색 =="
rg -n -C3 'symlink|symbolic|isSymbolicLink|ln -s' --type=ts --type=md

echo
echo "== 직전 커밋의 walkFiles 구현 확인 =="
git show HEAD~1:src/files.ts | sed -n '1,140p'

Repository: Seongho-Bae/vooster-v2-mvp

Length of output: 6109


Dirent.isDirectory() 전환으로 symlink 디렉터리 순회 계약이 바뀔 수 있어 정책/테스트 추가 필요

src/files.ts 30-33에서 재귀 순회를 entry.isDirectory()로만 판단하면, 기존의 statSync(path).isDirectory() 기반 동작과 달리 symlink 디렉터리 하위가 누락(또는 포함)될 수 있습니다. walkFilesspecs/**.md를 폭넓게 수집하는 데 쓰이므로(예: src/usecase-commands.ts, src/validate/doctor.ts, src/keys.ts) 결과 집합 변화 위험이 큽니다. 저장소 내에 symlink 처리 정책/명시가 확인되지 않으니, 의도(포함/제외)를 문서화하고 symlink 디렉터리 케이스 회귀 테스트를 추가하세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/files.ts` around lines 30 - 33, The recursive file-walk behavior changed
by relying solely on Dirent.isDirectory() in walkFiles; restore the prior
symlink-directory handling (or make it explicit) by checking for symlinks and
using fs.statSync(path).isDirectory() to decide whether to recurse into a
directory symlink, or clearly document and enforce the new policy
(include/exclude symlink dirs). Update walkFiles to handle
entry.isSymbolicLink() and call statSync(path).isDirectory() for accurate
decisions, and add a regression test that creates a symlinked directory with .md
files to assert the intended include/exclude behavior; also add a brief note in
repository docs describing the chosen symlink policy.

}
return files.sort();
Expand Down