Files
brain/tools/glob-restricted-filter.mjs
T

41 lines
1.4 KiB
JavaScript

#!/usr/bin/env node
/**
* Glob F8 post-execution filter (router-gate v4 Stream C, spec §5.2 F8 closure).
*
* Pure: the consumer hook expands the glob (glob.sync) and passes the already-
* matched path array here. We strip paths that live under a /restricted/ segment
* (e.g. subagent-block files the controller must not read) or whose inode is in
* the protectedInodes Set (injected). No `glob` npm dependency in this module.
*/
const RUNTIME_RE = /[~/\\]\.claude[/\\]runtime[/\\]/;
const DOUBLE_STAR_RE = /\*\*/;
/**
* True when an incoming Glob pattern targets ~/.claude/runtime with a ** wildcard
* and therefore must be post-filtered.
* @param {string} pattern
*/
export function globNeedsFilter(pattern) {
if (typeof pattern !== 'string') return false;
return RUNTIME_RE.test(pattern) && DOUBLE_STAR_RE.test(pattern);
}
/**
* Filter an already-expanded glob match list.
* @param {string[]} matches
* @param {{isProtectedInode?: (path: string) => boolean}} [deps]
* @returns {string[]}
*/
export function filterRestrictedMatches(matches, deps = {}) {
if (!Array.isArray(matches)) return [];
const isProtectedInode = typeof deps.isProtectedInode === 'function' ? deps.isProtectedInode : () => false;
return matches.filter((m) => {
if (typeof m !== 'string') return false;
const norm = m.replace(/\\/g, '/');
if (norm.includes('/restricted/')) return false;
if (isProtectedInode(m)) return false;
return true;
});
}