From 21a9ab2bcf2bdac1d2ec78eda757096d7b74ee36 Mon Sep 17 00:00:00 2001 From: Jeremy McSpadden Date: Fri, 20 Mar 2026 09:28:53 -0500 Subject: [PATCH] fix: break remaining shared/mod.js barrel imports in report generation chain (#1588) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR #1527 fixed metrics.ts but missed several other paths that still reach shared/mod.js → ui.js → @gsd/pi-tui during report generation via native dynamic import() (which bypasses jiti alias resolution). Remaining chains fixed: - preferences.ts, preferences-validation.ts, export.ts, forensics.ts, migrate/parsers.ts: import from shared/format-utils.js directly - state.ts, visualizer-data.ts, files.ts: import from milestone-ids.js instead of guided-flow.js (which pulls in shared/mod.js) - files.ts: import checkExistingEnvKeys from new env-utils.ts instead of get-secrets-from-user.ts (which imports @gsd/pi-tui) New file: env-utils.ts extracts the pure checkExistingEnvKeys function. Co-authored-by: Claude Opus 4.6 (1M context) --- src/resources/extensions/env-utils.ts | 31 +++++++++++++++++++ .../extensions/get-secrets-from-user.ts | 29 +++-------------- src/resources/extensions/gsd/export.ts | 2 +- src/resources/extensions/gsd/files.ts | 4 +-- src/resources/extensions/gsd/forensics.ts | 2 +- .../extensions/gsd/migrate/parsers.ts | 2 +- .../extensions/gsd/preferences-validation.ts | 2 +- src/resources/extensions/gsd/preferences.ts | 2 +- src/resources/extensions/gsd/state.ts | 2 +- .../extensions/gsd/visualizer-data.ts | 2 +- 10 files changed, 45 insertions(+), 33 deletions(-) create mode 100644 src/resources/extensions/env-utils.ts diff --git a/src/resources/extensions/env-utils.ts b/src/resources/extensions/env-utils.ts new file mode 100644 index 000000000..d5400fd7d --- /dev/null +++ b/src/resources/extensions/env-utils.ts @@ -0,0 +1,31 @@ +// GSD Extension — Environment variable utilities +// Copyright (c) 2026 Jeremy McSpadden +// +// Pure utility for checking existing env keys in .env files and process.env. +// Extracted from get-secrets-from-user.ts to avoid pulling in @gsd/pi-tui +// when only env-checking is needed (e.g. from files.ts during report generation). + +import { readFile } from "node:fs/promises"; + +/** + * Check which keys already exist in a .env file or process.env. + * Returns the subset of `keys` that are already set. + */ +export async function checkExistingEnvKeys(keys: string[], envFilePath: string): Promise { + let fileContent = ""; + try { + fileContent = await readFile(envFilePath, "utf8"); + } catch { + // ENOENT or other read error — proceed with empty content + } + + const existing: string[] = []; + for (const key of keys) { + const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + const regex = new RegExp(`^${escaped}\\s*=`, "m"); + if (regex.test(fileContent) || key in process.env) { + existing.push(key); + } + } + return existing; +} diff --git a/src/resources/extensions/get-secrets-from-user.ts b/src/resources/extensions/get-secrets-from-user.ts index f8fccdee7..58906c7d3 100644 --- a/src/resources/extensions/get-secrets-from-user.ts +++ b/src/resources/extensions/get-secrets-from-user.ts @@ -67,30 +67,11 @@ async function writeEnvKey(filePath: string, key: string, value: string): Promis // ─── Exported utilities ─────────────────────────────────────────────────────── -/** - * Check which keys already exist in the .env file or process.env. - * Returns the subset of `keys` that are already set. - * Handles ENOENT gracefully (still checks process.env). - * Empty-string values count as existing. - */ -export async function checkExistingEnvKeys(keys: string[], envFilePath: string): Promise { - let fileContent = ""; - try { - fileContent = await readFile(envFilePath, "utf8"); - } catch { - // ENOENT or other read error — proceed with empty content - } - - const existing: string[] = []; - for (const key of keys) { - const escaped = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); - const regex = new RegExp(`^${escaped}\\s*=`, "m"); - if (regex.test(fileContent) || key in process.env) { - existing.push(key); - } - } - return existing; -} +// Re-export from env-utils.ts so existing consumers still work. +// The implementation lives in env-utils.ts to avoid pulling @gsd/pi-tui +// into modules that only need env-checking (e.g. files.ts during reports). +import { checkExistingEnvKeys } from "./env-utils.js"; +export { checkExistingEnvKeys }; /** * Detect the write destination based on project files in basePath. diff --git a/src/resources/extensions/gsd/export.ts b/src/resources/extensions/gsd/export.ts index 7bec6ae17..009f63659 100644 --- a/src/resources/extensions/gsd/export.ts +++ b/src/resources/extensions/gsd/export.ts @@ -11,7 +11,7 @@ import { } from "./metrics.js"; import type { UnitMetrics } from "./metrics.js"; import { gsdRoot } from "./paths.js"; -import { formatDuration, fileLink } from "../shared/mod.js"; +import { formatDuration, fileLink } from "../shared/format-utils.js"; import { getErrorMessage } from "./error-utils.js"; /** diff --git a/src/resources/extensions/gsd/files.ts b/src/resources/extensions/gsd/files.ts index 529379332..a2c939279 100644 --- a/src/resources/extensions/gsd/files.ts +++ b/src/resources/extensions/gsd/files.ts @@ -7,7 +7,7 @@ import { promises as fs } from 'node:fs'; import { resolve } from 'node:path'; import { atomicWriteAsync } from './atomic-write.js'; import { resolveMilestoneFile, relMilestoneFile, resolveGsdRootFile } from './paths.js'; -import { milestoneIdSort, findMilestoneIds } from './guided-flow.js'; +import { milestoneIdSort, findMilestoneIds } from './milestone-ids.js'; import type { Roadmap, BoundaryMapEntry, @@ -20,7 +20,7 @@ import type { ManifestStatus, } from './types.js'; -import { checkExistingEnvKeys } from '../get-secrets-from-user.js'; +import { checkExistingEnvKeys } from '../env-utils.js'; import { parseRoadmapSlices } from './roadmap-slices.js'; import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativeParseSummaryFile, NATIVE_UNAVAILABLE } from './native-parser-bridge.js'; import { debugTime, debugCount } from './debug-logger.js'; diff --git a/src/resources/extensions/gsd/forensics.ts b/src/resources/extensions/gsd/forensics.ts index 2b76b2d5a..2dcda6549 100644 --- a/src/resources/extensions/gsd/forensics.ts +++ b/src/resources/extensions/gsd/forensics.ts @@ -27,7 +27,7 @@ import { deriveState } from "./state.js"; import { isAutoActive } from "./auto.js"; import { loadPrompt } from "./prompt-loader.js"; import { gsdRoot } from "./paths.js"; -import { formatDuration } from "../shared/mod.js"; +import { formatDuration } from "../shared/format-utils.js"; import { getAutoWorktreePath } from "./auto-worktree.js"; // ─── Types ──────────────────────────────────────────────────────────────────── diff --git a/src/resources/extensions/gsd/migrate/parsers.ts b/src/resources/extensions/gsd/migrate/parsers.ts index 4241b0079..708f72a8f 100644 --- a/src/resources/extensions/gsd/migrate/parsers.ts +++ b/src/resources/extensions/gsd/migrate/parsers.ts @@ -3,7 +3,7 @@ // Zero Pi dependencies — uses only exported helpers from files.ts. import { splitFrontmatter, parseFrontmatterMap, extractBoldField } from '../files.js'; -import { normalizeStringArray } from '../../shared/mod.js'; +import { normalizeStringArray } from '../../shared/format-utils.js'; import type { PlanningRoadmap, diff --git a/src/resources/extensions/gsd/preferences-validation.ts b/src/resources/extensions/gsd/preferences-validation.ts index 57643b5fb..1bdc7a2d6 100644 --- a/src/resources/extensions/gsd/preferences-validation.ts +++ b/src/resources/extensions/gsd/preferences-validation.ts @@ -10,7 +10,7 @@ import type { GitPreferences } from "./git-service.js"; import type { PostUnitHookConfig, PreDispatchHookConfig, TokenProfile, PhaseSkipPreferences } from "./types.js"; import type { DynamicRoutingConfig } from "./model-router.js"; import { VALID_BRANCH_NAME } from "./git-service.js"; -import { normalizeStringArray } from "../shared/mod.js"; +import { normalizeStringArray } from "../shared/format-utils.js"; import { KNOWN_PREFERENCE_KEYS, diff --git a/src/resources/extensions/gsd/preferences.ts b/src/resources/extensions/gsd/preferences.ts index ef748b3eb..8e48888a9 100644 --- a/src/resources/extensions/gsd/preferences.ts +++ b/src/resources/extensions/gsd/preferences.ts @@ -17,7 +17,7 @@ import { gsdRoot } from "./paths.js"; import { parse as parseYaml } from "yaml"; import type { PostUnitHookConfig, PreDispatchHookConfig, TokenProfile } from "./types.js"; import type { DynamicRoutingConfig } from "./model-router.js"; -import { normalizeStringArray } from "../shared/mod.js"; +import { normalizeStringArray } from "../shared/format-utils.js"; import { resolveProfileDefaults as _resolveProfileDefaults } from "./preferences-models.js"; import { diff --git a/src/resources/extensions/gsd/state.ts b/src/resources/extensions/gsd/state.ts index d97f320a3..5a3046c1b 100644 --- a/src/resources/extensions/gsd/state.ts +++ b/src/resources/extensions/gsd/state.ts @@ -31,7 +31,7 @@ import { gsdRoot, } from './paths.js'; -import { milestoneIdSort, findMilestoneIds } from './guided-flow.js'; +import { milestoneIdSort, findMilestoneIds } from './milestone-ids.js'; import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js'; import { join, resolve } from 'path'; diff --git a/src/resources/extensions/gsd/visualizer-data.ts b/src/resources/extensions/gsd/visualizer-data.ts index 587a3d4a6..b06fe92d2 100644 --- a/src/resources/extensions/gsd/visualizer-data.ts +++ b/src/resources/extensions/gsd/visualizer-data.ts @@ -3,7 +3,7 @@ import { existsSync, readFileSync, statSync } from 'node:fs'; import { deriveState } from './state.js'; import { parseRoadmap, parsePlan, parseSummary, loadFile } from './files.js'; -import { findMilestoneIds } from './guided-flow.js'; +import { findMilestoneIds } from './milestone-ids.js'; import { resolveMilestoneFile, resolveSliceFile, resolveGsdRootFile } from './paths.js'; import { getLedger,