From 4391cf7d2b80290b6c2d394740c441f020942856 Mon Sep 17 00:00:00 2001 From: "contextbotai[bot]" Date: Wed, 17 Jun 2026 16:22:49 +0000 Subject: [PATCH] refactor: replace readdirSync directory walks with Bun.Glob The Bun-native APIs rule flags hand-rolled readdirSync directory walks as the most consistently corrected Node carry-over in PR review, calling for Bun.Glob instead. Convert the three Bun-only production/script sites that walk a directory to enumerate its subdirectories: - packages/skills/src/skills.ts (loadAllFrom) - packages/skills/scripts/check.ts (findOrphans) - packages/storage/src/db/loadMigrations.macro.ts (loadStorageMigrations) Each scans for the marker file (SKILL.md / migration.sql) and derives the directory name, matching the existing Bun.Glob pattern in packages/skills/src/handlebars.ts. Behavior is preserved: same set of directories, same alphabetical ordering, same underscore exclusion in the skills loader. readFileSync calls in tight synchronous loops are left as-is per the rule's explicit carve-out. --- packages/skills/scripts/check.ts | 9 +++------ packages/skills/src/skills.ts | 13 +++++++------ packages/storage/src/db/loadMigrations.macro.ts | 7 +++---- 3 files changed, 13 insertions(+), 16 deletions(-) diff --git a/packages/skills/scripts/check.ts b/packages/skills/scripts/check.ts index a168ab8..78335fb 100644 --- a/packages/skills/scripts/check.ts +++ b/packages/skills/scripts/check.ts @@ -1,4 +1,4 @@ -import { readFileSync, readdirSync } from 'node:fs'; +import { readFileSync } from 'node:fs'; import { dirname, join, relative } from 'node:path'; import type { BaseContext } from '@contextbridge/context'; import { SKILL_RENDERABLE_HARNESSES } from '@contextbridge/harness'; @@ -8,7 +8,6 @@ import { createScriptContext } from './context.ts'; import { REPO_ROOT, type RenderTarget, SOURCES_DIR, outDirFor, targetsForAll } from './renderTargets.ts'; const safeReadFile = fromThrowable((path: string) => readFileSync(path, 'utf8')); -const safeReaddir = fromThrowable((dir: string) => readdirSync(dir, { withFileTypes: true })); async function main(ctx: BaseContext): Promise { const { logger } = ctx; @@ -51,10 +50,8 @@ function checkDrift({ path, body, harness }: RenderTarget): Result } function findOrphans(parent: string, expectedDirs: Set): string[] { - return safeReaddir(parent) - .unwrapOr([]) - .filter((entry) => entry.isDirectory()) - .map((entry) => join(parent, entry.name)) + return Array.from(new Bun.Glob('*/SKILL.md').scanSync(parent)) + .map((relPath) => join(parent, dirname(relPath))) .filter((dir) => !expectedDirs.has(dir)); } diff --git a/packages/skills/src/skills.ts b/packages/skills/src/skills.ts index 47a0f58..c3b711d 100644 --- a/packages/skills/src/skills.ts +++ b/packages/skills/src/skills.ts @@ -1,5 +1,5 @@ -import { readFileSync, readdirSync } from 'node:fs'; -import { join } from 'node:path'; +import { readFileSync } from 'node:fs'; +import { dirname, join } from 'node:path'; import fm from 'front-matter'; import { z } from 'zod'; @@ -35,10 +35,11 @@ export function parseSkill(source: string): Skill { } export function loadAllFrom(sourcesDir: string): Skill[] { - return readdirSync(sourcesDir, { withFileTypes: true }) - .filter((entry) => entry.isDirectory() && !entry.name.startsWith('_')) - .sort((a, b) => a.name.localeCompare(b.name)) - .map((entry) => loadOne(sourcesDir, entry.name)); + return Array.from(new Bun.Glob('*/SKILL.md').scanSync(sourcesDir)) + .map((relPath) => dirname(relPath)) + .filter((dirName) => !dirName.startsWith('_')) + .sort((a, b) => a.localeCompare(b)) + .map((dirName) => loadOne(sourcesDir, dirName)); } function loadOne(sourcesDir: string, dirName: string): Skill { diff --git a/packages/storage/src/db/loadMigrations.macro.ts b/packages/storage/src/db/loadMigrations.macro.ts index ac162bf..131098e 100644 --- a/packages/storage/src/db/loadMigrations.macro.ts +++ b/packages/storage/src/db/loadMigrations.macro.ts @@ -1,4 +1,4 @@ -import { readFileSync, readdirSync } from 'node:fs'; +import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { formatToMillis } from 'drizzle-orm/migrator.utils'; @@ -13,9 +13,8 @@ export function loadStorageMigrations(): StorageMigrationEntry[] { const here = dirname(fileURLToPath(import.meta.url)); const drizzleDir = join(here, '..', '..', 'generated', 'drizzle'); - return readdirSync(drizzleDir, { withFileTypes: true }) - .filter((entry) => entry.isDirectory()) - .map((entry) => entry.name) + return Array.from(new Bun.Glob('*/migration.sql').scanSync(drizzleDir)) + .map((relPath) => dirname(relPath)) .sort() .map((name) => { const timestamp = formatToMillis(name.slice(0, 14));