UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

96 lines • 4.49 kB
// ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // ========= Copyright 2026 @ Strukto.AI All Rights Reserved. ========= import { PathSpec } from "../../../types.js"; import { extglobActive } from "../../../context/session_context.js"; import { patternShape } from "../../../utils/fnmatch.js"; import { dottedSpelling, posixNormpath } from "../../../utils/path.js"; import { stripSlash } from "../../../utils/slash.js"; import { hasGlob, unmarkGlobs } from "../../../utils/glob_walk.js"; import { relativeSpec } from "./relative.js"; const NON_PATH_CHAR = /[(){}=;|&<> ]/; const RELATIVE_PATH = /^(?:\.?[a-zA-Z0-9_-]*\/)*[a-zA-Z0-9_-]+\.[a-zA-Z0-9]+$/; // Every caller hands this an already-expanded word, so quote removal has // happened and a surviving backslash is a literal character of the name // (GNU reads a file named `a\b` as `cat '/data/a\b'`). Unescaping again // here corrupted both that name and any control character an escape had // produced. export function classifyWord(word, registry, cwd, access) { // Whether the word globs is read off the marks, but whether it looks // like a path at all is a question about the name itself, so the shape // tests below read the literal spelling. const wordHasGlob = hasGlob(word, access); const shape = wordHasGlob && extglobActive(access) ? patternShape(word) : unmarkGlobs(word); if (word.startsWith('/')) { // A quoted character names the mount literally (`'/team+'/*`). const mount = registry.tryMountFor(unmarkGlobs(word)); if (mount === null) return word; let isDir = word.endsWith('/'); const path = posixNormpath(word); if (!isDir && `${unmarkGlobs(path)}/` === mount.prefix) { isDir = true; } // `rawPath` keeps the spelling as typed, the way relativeSpec does: // `virtual` has already lost any `..`, and `cd -P` has to resolve the // link a `..` follows before applying it. `dotted` keeps it for the // walk that proves each `..` a directory, and a pattern's for the head // its listing walks. if (wordHasGlob) { const lastSlash = path.lastIndexOf('/'); return new PathSpec({ vfsPath: stripSlash(path), virtual: path, directory: path.slice(0, lastSlash + 1), pattern: path.slice(lastSlash + 1), rawPath: word, resolved: false, dotted: dottedSpelling(word), }); } if (isDir) { return new PathSpec({ vfsPath: stripSlash(path), virtual: path, directory: `${path}/`, rawPath: word, resolved: false, dotted: dottedSpelling(word), }); } const lastSlash = path.lastIndexOf('/'); return new PathSpec({ vfsPath: stripSlash(path), virtual: path, directory: path.slice(0, lastSlash + 1), rawPath: word, resolved: true, dotted: dottedSpelling(word), }); } // Relative glob: a pattern under cwd, a bare `*`, `?` or `[a-z]` // included, because bash expands every unquoted glob word (`echo *` // lists the directory, and `expr 4 * 3` is the classic mistake). A // quoted glob arrives with no marks and stays text. A word carrying // shell syntax beside the glob (`x=*`) is an argument, not a path. if (wordHasGlob && (word.includes('/') || !shape.startsWith('.'))) { if (NON_PATH_CHAR.test(shape)) return word; return relativeSpec(word, registry, cwd, access); } if (!wordHasGlob && word.includes('/') && RELATIVE_PATH.test(shape)) { return relativeSpec(word, registry, cwd, access); } return word; } //# sourceMappingURL=heuristic.js.map