diff --git a/src/claude/hooks.ts b/src/claude/hooks.ts index fcead00c..f59fd2ea 100644 --- a/src/claude/hooks.ts +++ b/src/claude/hooks.ts @@ -155,7 +155,7 @@ function graftJson(dir: string, args: string[], timeout: number = CHILD_TIMEOUT_ // out to the real CLI (which isn't built relative to the TS source under test). const cliPath = process.env.GRAFT_TEST_CLI ?? graftCliPath(); const out = execFileSync(process.execPath, [cliPath, ...args], - { cwd: dir, encoding: 'utf8', timeout, stdio: ['ignore', 'pipe', 'ignore'] }); + { cwd: dir, encoding: 'utf8', timeout, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true }); return JSON.parse(out); } catch (e: any) { // `graft check` exits non-zero when the graph is stale (by design) but still @@ -517,7 +517,7 @@ export function trackRepoSeen(dir: string, now = Date.now()): void { const mark = join(cacheDir(place.checkout), 'trail-repo-seen.json'); if (now - (readJson<{ at?: number }>(mark)?.at ?? 0) < DAY_MS) return; writeJsonAtomic(mark, { at: now }, true); - const tracked = execFileSync('git', ['ls-files', '--', '.trail'], { cwd: place.checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 }); + const tracked = execFileSync('git', ['ls-files', '--', '.trail'], { cwd: place.checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true }); track( 'repo_seen', { trail_committed: String(tracked.trim().length > 0), learnings_bucket: countBucket(learningFiles(place.checkout).length), workspace: String(inWorkspace(place.checkout)) }, @@ -540,7 +540,7 @@ export function trackLearningsCommitted(dir: string): void { const place = repoPlace(dir); if (!place.git) return; const listed = execFileSync('git', ['ls-tree', '-r', '--name-only', 'HEAD', '--', '.trail/learnings'], { - cwd: place.checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, + cwd: place.checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true, }); const count = listed.split('\n').filter((f) => f.endsWith('.md')).length; const mark = join(cacheDir(place.checkout), 'trail-learnings-committed.json'); @@ -569,7 +569,7 @@ export function uncommittedDirective(dir: string, now = Date.now()): string { const place = repoPlace(dir); if (!place.git) return ''; const status = execFileSync('git', ['status', '--porcelain', '--untracked-files=all', '--', '.trail/learnings'], { - cwd: place.checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, + cwd: place.checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true, }); const n = status.split('\n').filter((l) => l.trimEnd().endsWith('.md')).length; const setup = setupUncommitted(place.checkout); @@ -596,7 +596,7 @@ export function uncommittedDirective(dir: string, now = Date.now()): string { */ export function setupUncommitted(checkout: string): boolean { if (!existsSync(join(checkout, '.trail', 'hook.cjs'))) return false; - const git = (args: string[]) => execFileSync('git', args, { cwd: checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000 }); + const git = (args: string[]) => execFileSync('git', args, { cwd: checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], timeout: 3000, windowsHide: true }); try { git(['ls-files', '--error-unmatch', '--', '.trail/hook.cjs']); } catch { diff --git a/src/claude/init.ts b/src/claude/init.ts index e54df085..6cc836f6 100644 --- a/src/claude/init.ts +++ b/src/claude/init.ts @@ -97,7 +97,7 @@ export function buildGraphIfMissing(dir: string, opts: { build?: boolean; cliPat try { // execArgv carries the loader graft itself was started with (tsx, when run // from source), so the child can run a .ts entry point too. - execFileSync(process.execPath, [...process.execArgv, opts.cliPath, 'build', '.'], { cwd: dir, stdio: 'inherit', timeout: 300000 }); + execFileSync(process.execPath, [...process.execArgv, opts.cliPath, 'build', '.'], { cwd: dir, stdio: 'inherit', timeout: 300000, windowsHide: true }); return true; } catch { return false; @@ -255,13 +255,13 @@ function dropServer(path: string, names: string[]): void { */ export function keepOutOfGit(dir: string, rel: string): void { try { - execFileSync('git', ['check-ignore', '-q', '--', rel], { cwd: dir, stdio: 'ignore' }); + execFileSync('git', ['check-ignore', '-q', '--', rel], { cwd: dir, stdio: 'ignore', windowsHide: true }); return; // exit 0: already ignored } catch (e) { if ((e as { status?: number }).status !== 1) return; // 128: not a git checkout } try { - let exclude = execFileSync('git', ['rev-parse', '--git-path', 'info/exclude'], { cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] }).trim(); + let exclude = execFileSync('git', ['rev-parse', '--git-path', 'info/exclude'], { cwd: dir, encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true }).trim(); if (!isAbsolute(exclude)) exclude = join(dir, exclude); const line = `/${rel.split('\\').join('/')}`; const text = existsSync(exclude) ? readFileSync(exclude, 'utf8') : ''; diff --git a/src/claude/shim-template.ts b/src/claude/shim-template.ts index 3e9e2cb7..1807a7d9 100644 --- a/src/claude/shim-template.ts +++ b/src/claude/shim-template.ts @@ -110,7 +110,7 @@ function onPath() { // The global node_modules dir per npm (handles Homebrew/Windows/volta). Queried on demand. function globalRoot() { try { - const root = execFileSync('npm', ['root', '-g'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform === 'win32' }).trim(); + const root = execFileSync('npm', ['root', '-g'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'], shell: process.platform === 'win32', windowsHide: true }).trim(); return root || null; } catch { return null; /* npm unavailable */ } } diff --git a/src/claude/sync-run.ts b/src/claude/sync-run.ts index e07acb8e..8a68fbe3 100644 --- a/src/claude/sync-run.ts +++ b/src/claude/sync-run.ts @@ -13,7 +13,7 @@ function realBuild(dir: string): void { // Mirrors `withContextDirArg` in hooks.ts: a no-op unless GRAFT_DIR is set, so an // unconfigured repo's rebuild sees byte-identical argv to before this existed. if (process.env.GRAFT_DIR) args.push('--dir', resolveContextDir(dir)); - execFileSync(process.execPath, args, { cwd: dir, stdio: 'ignore', timeout: 120000 }); + execFileSync(process.execPath, args, { cwd: dir, stdio: 'ignore', timeout: 120000, windowsHide: true }); } export function runSync(dir: string, build: (d: string) => void = realBuild): void {