From 5d14ac446b5bc4f247c82e7d10be22b268d0b29a Mon Sep 17 00:00:00 2001 From: Lex Christopherson Date: Wed, 25 Mar 2026 23:07:31 -0600 Subject: [PATCH] refactor: flatten syncMilestoneDir nesting with shared helper Extract syncDirFiles() helper to eliminate duplicated iterate/filter/copy/catch logic across three directory levels (milestone root, slices, tasks). Reduces maximum nesting depth from 4 to 2. Co-Authored-By: Claude Opus 4.6 (1M context) --- src/resources/extensions/gsd/auto-worktree.ts | 117 +++++++----------- 1 file changed, 46 insertions(+), 71 deletions(-) diff --git a/src/resources/extensions/gsd/auto-worktree.ts b/src/resources/extensions/gsd/auto-worktree.ts index e91c67009..471761fd1 100644 --- a/src/resources/extensions/gsd/auto-worktree.ts +++ b/src/resources/extensions/gsd/auto-worktree.ts @@ -379,6 +379,29 @@ export function syncWorktreeStateBack( * Sync a single milestone directory from worktree to main. * Copies milestone-level .md files, slice-level files, and task summaries. */ +/** Copy matching files from srcDir to dstDir (non-fatal per file). */ +function syncDirFiles( + srcDir: string, + dstDir: string, + filter: (name: string) => boolean, + synced: string[], + prefix: string, +): void { + try { + for (const entry of readdirSync(srcDir, { withFileTypes: true })) { + if (!entry.isFile() || !filter(entry.name)) continue; + try { + cpSync(join(srcDir, entry.name), join(dstDir, entry.name), { force: true }); + synced.push(`${prefix}${entry.name}`); + } catch { + /* non-fatal */ + } + } + } catch { + /* non-fatal — srcDir may not be readable */ + } +} + function syncMilestoneDir( wtGsd: string, mainGsd: string, @@ -391,84 +414,36 @@ function syncMilestoneDir( if (!existsSync(wtMilestoneDir)) return; mkdirSync(mainMilestoneDir, { recursive: true }); + const isMd = (name: string): boolean => name.endsWith(".md"); + // Sync milestone-level files (SUMMARY, VALIDATION, ROADMAP, CONTEXT) + syncDirFiles(wtMilestoneDir, mainMilestoneDir, isMd, synced, `milestones/${mid}/`); + + // Sync slice-level files (summaries, UATs) and task summaries (#1678) + const wtSlicesDir = join(wtMilestoneDir, "slices"); + const mainSlicesDir = join(mainMilestoneDir, "slices"); + if (!existsSync(wtSlicesDir)) return; + try { - for (const entry of readdirSync(wtMilestoneDir, { withFileTypes: true })) { - if (entry.isFile() && entry.name.endsWith(".md")) { - const src = join(wtMilestoneDir, entry.name); - const dst = join(mainMilestoneDir, entry.name); - try { - cpSync(src, dst, { force: true }); - synced.push(`milestones/${mid}/${entry.name}`); - } catch { - /* non-fatal */ - } + for (const sliceEntry of readdirSync(wtSlicesDir, { withFileTypes: true })) { + if (!sliceEntry.isDirectory()) continue; + const sid = sliceEntry.name; + const wtSliceDir = join(wtSlicesDir, sid); + const mainSliceDir = join(mainSlicesDir, sid); + mkdirSync(mainSliceDir, { recursive: true }); + + syncDirFiles(wtSliceDir, mainSliceDir, isMd, synced, `milestones/${mid}/slices/${sid}/`); + + const wtTasksDir = join(wtSliceDir, "tasks"); + const mainTasksDir = join(mainSliceDir, "tasks"); + if (existsSync(wtTasksDir)) { + mkdirSync(mainTasksDir, { recursive: true }); + syncDirFiles(wtTasksDir, mainTasksDir, isMd, synced, `milestones/${mid}/slices/${sid}/tasks/`); } } } catch { /* non-fatal */ } - - // Sync slice-level files (summaries, UATs) - const wtSlicesDir = join(wtMilestoneDir, "slices"); - const mainSlicesDir = join(mainMilestoneDir, "slices"); - if (existsSync(wtSlicesDir)) { - try { - for (const sliceEntry of readdirSync(wtSlicesDir, { - withFileTypes: true, - })) { - if (!sliceEntry.isDirectory()) continue; - const sid = sliceEntry.name; - const wtSliceDir = join(wtSlicesDir, sid); - const mainSliceDir = join(mainSlicesDir, sid); - mkdirSync(mainSliceDir, { recursive: true }); - - for (const fileEntry of readdirSync(wtSliceDir, { - withFileTypes: true, - })) { - if (fileEntry.isFile() && fileEntry.name.endsWith(".md")) { - const src = join(wtSliceDir, fileEntry.name); - const dst = join(mainSliceDir, fileEntry.name); - try { - cpSync(src, dst, { force: true }); - synced.push( - `milestones/${mid}/slices/${sid}/${fileEntry.name}`, - ); - } catch { - /* non-fatal */ - } - } else if (fileEntry.isDirectory() && fileEntry.name === "tasks") { - // Recurse into tasks/ subdirectory to sync task summaries (#1678). - // Without this, T01-SUMMARY.md etc. are silently dropped on - // worktree teardown because the loop only processes isFile() entries. - const wtTasksDir = join(wtSliceDir, "tasks"); - const mainTasksDir = join(mainSliceDir, "tasks"); - mkdirSync(mainTasksDir, { recursive: true }); - try { - for (const taskEntry of readdirSync(wtTasksDir, { withFileTypes: true })) { - if (taskEntry.isFile() && taskEntry.name.endsWith(".md")) { - const taskSrc = join(wtTasksDir, taskEntry.name); - const taskDst = join(mainTasksDir, taskEntry.name); - try { - cpSync(taskSrc, taskDst, { force: true }); - synced.push( - `milestones/${mid}/slices/${sid}/tasks/${taskEntry.name}`, - ); - } catch { - /* non-fatal */ - } - } - } - } catch { - /* non-fatal: tasks dir read failure */ - } - } - } - } - } catch { - /* non-fatal */ - } - } } // ─── Worktree Post-Create Hook (#597) ────────────────────────────────────────