fix: cross-platform observer-dir containment; clarify SDK stdin pipe

claude-review feedback on PR #2124.

- shouldTrackProject: literal `cwd.startsWith(OBSERVER_SESSIONS_DIR + '/')`
  hard-coded a POSIX separator and missed Windows backslash paths plus any
  trailing-slash variance. Switched to a path.relative-based isWithin()
  helper so Windows hook input under observer-sessions\\... is also excluded.
- spawnSdkProcess: added a comment explaining why stdin must be 'pipe' —
  SpawnedSdkProcess.stdin is typed NonNullable and the Claude Agent SDK
  consumes that pipe; 'ignore' would null it and the null-check below
  would tear the child down on every spawn.
This commit is contained in:
Alex Newman
2026-04-24 19:36:41 -07:00
parent 24dfa82194
commit 227fad102b
3 changed files with 140 additions and 126 deletions

File diff suppressed because one or more lines are too long

View File

@@ -11,10 +11,17 @@
* references `isProjectExcluded` directly — the import lives only here.
*/
import { relative, isAbsolute } from 'path';
import { isProjectExcluded } from '../utils/project-filter.js';
import { loadFromFileOnce } from './hook-settings.js';
import { OBSERVER_SESSIONS_DIR } from './paths.js';
function isWithin(child: string, parent: string): boolean {
if (child === parent) return true;
const rel = relative(parent, child);
return rel.length > 0 && !rel.startsWith('..') && !isAbsolute(rel);
}
/**
* @returns true when the project at `cwd` is NOT excluded from claude-mem
* tracking, i.e., the hook should proceed; false when the project
@@ -27,7 +34,9 @@ import { OBSERVER_SESSIONS_DIR } from './paths.js';
*/
export function shouldTrackProject(cwd: string): boolean {
if (!cwd) return true;
if (cwd === OBSERVER_SESSIONS_DIR || cwd.startsWith(OBSERVER_SESSIONS_DIR + '/')) {
// path.relative handles separator differences (Windows '\\' vs POSIX '/')
// and trailing-slash variance, which a literal startsWith would miss.
if (isWithin(cwd, OBSERVER_SESSIONS_DIR)) {
return false;
}
const settings = loadFromFileOnce();

View File

@@ -665,6 +665,11 @@ export function spawnSdkProcess(
// child becomes leader of a new process group whose pgid equals its pid.
// Windows: detached:true decouples the child from the parent console; there
// is no POSIX group, but the flag is still safe to pass.
//
// stdin must be 'pipe' (not 'ignore') because SpawnedSdkProcess.stdin is
// typed NonNullable<...> and the Claude Agent SDK consumes that pipe to
// stream prompts in. With 'ignore', child.stdin would be null and the
// null-check below (line ~737) would tear the child down immediately.
const child = useCmdWrapper
? spawn('cmd.exe', ['/d', '/c', options.command, ...filteredArgs], {
cwd: options.cwd,