@j0hanz/filesystem-mcp 2.1.4 → 2.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/dist/cli.d.ts +2 -14
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +14 -22
  4. package/dist/cli.js.map +1 -1
  5. package/dist/core/cursor.d.ts +2 -14
  6. package/dist/core/cursor.d.ts.map +1 -1
  7. package/dist/core/cursor.js +25 -39
  8. package/dist/core/cursor.js.map +1 -1
  9. package/dist/core/diff.d.ts +6 -0
  10. package/dist/core/diff.d.ts.map +1 -0
  11. package/dist/core/diff.js +21 -0
  12. package/dist/core/diff.js.map +1 -0
  13. package/dist/core/errors.d.ts +2 -31
  14. package/dist/core/errors.d.ts.map +1 -1
  15. package/dist/core/errors.js +15 -86
  16. package/dist/core/errors.js.map +1 -1
  17. package/dist/core/file-uri.d.ts +18 -2
  18. package/dist/core/file-uri.d.ts.map +1 -1
  19. package/dist/core/file-uri.js +34 -6
  20. package/dist/core/file-uri.js.map +1 -1
  21. package/dist/core/fmt.js +5 -5
  22. package/dist/core/fmt.js.map +1 -1
  23. package/dist/core/fs.d.ts +4 -13
  24. package/dist/core/fs.d.ts.map +1 -1
  25. package/dist/core/fs.js +4 -41
  26. package/dist/core/fs.js.map +1 -1
  27. package/dist/core/glob.d.ts +1 -7
  28. package/dist/core/glob.d.ts.map +1 -1
  29. package/dist/core/glob.js +1 -11
  30. package/dist/core/glob.js.map +1 -1
  31. package/dist/core/input-required.d.ts +2 -10
  32. package/dist/core/input-required.d.ts.map +1 -1
  33. package/dist/core/input-required.js +3 -12
  34. package/dist/core/input-required.js.map +1 -1
  35. package/dist/core/path-completer.d.ts.map +1 -1
  36. package/dist/core/path-completer.js +3 -7
  37. package/dist/core/path-completer.js.map +1 -1
  38. package/dist/core/path-discovery.js +2 -2
  39. package/dist/core/path-discovery.js.map +1 -1
  40. package/dist/core/path.d.ts +10 -25
  41. package/dist/core/path.d.ts.map +1 -1
  42. package/dist/core/path.js +28 -66
  43. package/dist/core/path.js.map +1 -1
  44. package/dist/core/primitives.d.ts +8 -3
  45. package/dist/core/primitives.d.ts.map +1 -1
  46. package/dist/core/primitives.js +10 -5
  47. package/dist/core/primitives.js.map +1 -1
  48. package/dist/core/read.d.ts +0 -16
  49. package/dist/core/read.d.ts.map +1 -1
  50. package/dist/core/read.js +19 -56
  51. package/dist/core/read.js.map +1 -1
  52. package/dist/core/schema.d.ts +1 -32
  53. package/dist/core/schema.d.ts.map +1 -1
  54. package/dist/core/schema.js +7 -66
  55. package/dist/core/schema.js.map +1 -1
  56. package/dist/core/search.d.ts +16 -5
  57. package/dist/core/search.d.ts.map +1 -1
  58. package/dist/core/search.js +118 -108
  59. package/dist/core/search.js.map +1 -1
  60. package/dist/core/sensitive.d.ts.map +1 -1
  61. package/dist/core/sensitive.js +7 -15
  62. package/dist/core/sensitive.js.map +1 -1
  63. package/dist/core/store.d.ts +1 -3
  64. package/dist/core/store.d.ts.map +1 -1
  65. package/dist/core/store.js +1 -5
  66. package/dist/core/store.js.map +1 -1
  67. package/dist/core/watcher-registry.d.ts +1 -8
  68. package/dist/core/watcher-registry.d.ts.map +1 -1
  69. package/dist/core/watcher-registry.js +3 -12
  70. package/dist/core/watcher-registry.js.map +1 -1
  71. package/dist/index.js +1 -8
  72. package/dist/index.js.map +1 -1
  73. package/dist/prompts.d.ts.map +1 -1
  74. package/dist/prompts.js +7 -33
  75. package/dist/prompts.js.map +1 -1
  76. package/dist/resources.d.ts +1 -1
  77. package/dist/resources.d.ts.map +1 -1
  78. package/dist/resources.js +3 -9
  79. package/dist/resources.js.map +1 -1
  80. package/dist/server.d.ts +0 -3
  81. package/dist/server.d.ts.map +1 -1
  82. package/dist/server.js +1 -5
  83. package/dist/server.js.map +1 -1
  84. package/dist/tools/batch.d.ts +5 -69
  85. package/dist/tools/batch.d.ts.map +1 -1
  86. package/dist/tools/batch.js +7 -117
  87. package/dist/tools/batch.js.map +1 -1
  88. package/dist/tools/create.d.ts.map +1 -1
  89. package/dist/tools/create.js +8 -18
  90. package/dist/tools/create.js.map +1 -1
  91. package/dist/tools/define.d.ts +1 -4
  92. package/dist/tools/define.d.ts.map +1 -1
  93. package/dist/tools/define.js +6 -24
  94. package/dist/tools/define.js.map +1 -1
  95. package/dist/tools/delete-file.d.ts.map +1 -1
  96. package/dist/tools/delete-file.js +6 -6
  97. package/dist/tools/delete-file.js.map +1 -1
  98. package/dist/tools/diff.d.ts.map +1 -1
  99. package/dist/tools/diff.js +3 -10
  100. package/dist/tools/diff.js.map +1 -1
  101. package/dist/tools/edit.d.ts.map +1 -1
  102. package/dist/tools/edit.js +43 -87
  103. package/dist/tools/edit.js.map +1 -1
  104. package/dist/tools/index.d.ts +0 -1
  105. package/dist/tools/index.d.ts.map +1 -1
  106. package/dist/tools/index.js +0 -1
  107. package/dist/tools/index.js.map +1 -1
  108. package/dist/tools/list.js +3 -2
  109. package/dist/tools/list.js.map +1 -1
  110. package/dist/tools/move.d.ts.map +1 -1
  111. package/dist/tools/move.js +169 -119
  112. package/dist/tools/move.js.map +1 -1
  113. package/dist/tools/patch.d.ts.map +1 -1
  114. package/dist/tools/patch.js +5 -23
  115. package/dist/tools/patch.js.map +1 -1
  116. package/dist/tools/progress.d.ts +0 -1
  117. package/dist/tools/progress.d.ts.map +1 -1
  118. package/dist/tools/progress.js +2 -8
  119. package/dist/tools/progress.js.map +1 -1
  120. package/dist/tools/read.d.ts.map +1 -1
  121. package/dist/tools/read.js +21 -25
  122. package/dist/tools/read.js.map +1 -1
  123. package/dist/tools/replace-in-files.d.ts.map +1 -1
  124. package/dist/tools/replace-in-files.js +25 -44
  125. package/dist/tools/replace-in-files.js.map +1 -1
  126. package/dist/tools/search-content.d.ts.map +1 -1
  127. package/dist/tools/search-content.js +17 -49
  128. package/dist/tools/search-content.js.map +1 -1
  129. package/dist/tools/search-files.d.ts.map +1 -1
  130. package/dist/tools/search-files.js +1 -2
  131. package/dist/tools/search-files.js.map +1 -1
  132. package/dist/tools/stat.d.ts.map +1 -1
  133. package/dist/tools/stat.js +5 -6
  134. package/dist/tools/stat.js.map +1 -1
  135. package/dist/transport/http-policy.d.ts +6 -0
  136. package/dist/transport/http-policy.d.ts.map +1 -1
  137. package/dist/transport/http-policy.js +2 -2
  138. package/dist/transport/http-policy.js.map +1 -1
  139. package/dist/transport/http.d.ts.map +1 -1
  140. package/dist/transport/http.js +4 -11
  141. package/dist/transport/http.js.map +1 -1
  142. package/dist/transport/stdio.js +1 -1
  143. package/dist/transport/stdio.js.map +1 -1
  144. package/dist/transport.d.ts +1 -3
  145. package/dist/transport.d.ts.map +1 -1
  146. package/dist/transport.js +5 -2
  147. package/dist/transport.js.map +1 -1
  148. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { isSafeGlobSyntax } from './glob.js';\nimport { MIME_KINDS } from './mime.js';\nimport { ENTRY_TYPES } from './primitives.js';\nimport { MAX_SEARCH_DEPTH } from './util.js';\n\n// Runtime: full ISO-8601 UTC; emits the standard `date-time` format on the wire\n// (no AJV warning — it is a known format, unlike sha256_hex / base64url).\n// No `id` on this or any other shared schema below: an `id` hoists the schema\n// into `$defs` and leaves a `$ref` at every use site, which is exactly the wire\n// weight `toDraft202012` (tools/define.ts) publishes without.\nexport const IsoDateTime = z.iso.datetime().meta({\n description: 'ISO 8601 UTC date-time string (e.g. 2024-01-15T12:00:00.000Z)',\n // `format: \"date-time\"` already pins the value; zod's ~330-char calendar\n // regex is pure wire weight. Runtime validation still runs it — this\n // suppresses the emitted keyword only.\n pattern: undefined,\n});\n\n// pattern (not z.hash('sha256')) so no `format: \"sha256_hex\"` keyword is emitted;\n// the SDK's AJV validator warns on that unknown format at every server start.\nexport const Sha256Hex = z\n .string()\n .regex(/^[0-9a-f]{64}$/)\n .meta({ description: 'SHA-256 digest as a 64-character lowercase hex string' });\n\nexport const NonNegInt = z\n .int({ message: 'Must be integer' })\n .nonnegative({ message: 'Must be ≥ 0' });\n\nexport const PositiveInt = z\n .int({ message: 'Must be integer' })\n .positive({ message: 'Must be > 0' });\n\nconst FILE_TYPES = ENTRY_TYPES;\nexport type FileType = (typeof FILE_TYPES)[number];\nexport const FileType = z.enum(FILE_TYPES);\n\nconst FILE_KINDS = MIME_KINDS;\nexport type FileKind = (typeof FILE_KINDS)[number];\nexport const FileKind = z.enum(FILE_KINDS);\n\nconst MAX_PATH_LENGTH = 4096;\n\nconst SHELL_METACHAR_RE = /[\\n\\r;|`]/;\n\nexport const isBlank = (val: string): boolean => val.trim().length === 0;\n\nconst PathBase = z\n .string()\n .min(1, { message: 'Path required' })\n .max(MAX_PATH_LENGTH, { message: `Path too long (max ${MAX_PATH_LENGTH} chars)` })\n .superRefine((val, ctx) => {\n if (val.length === 0) {\n return;\n }\n if (val.trim().length === 0) {\n ctx.addIssue({\n code: 'custom',\n message: 'Path cannot be empty or whitespace-only',\n fatal: true,\n });\n return z.NEVER;\n }\n if (val.includes('\\0')) {\n ctx.addIssue({\n code: 'custom',\n message: 'Path cannot contain null bytes',\n fatal: true,\n });\n return z.NEVER;\n }\n if (val.includes('..')) {\n ctx.addIssue({\n code: 'custom',\n message: 'Directory traversal sequences (\"..\") are forbidden',\n fatal: true,\n });\n return z.NEVER;\n }\n if (SHELL_METACHAR_RE.test(val)) {\n ctx.addIssue({\n code: 'custom',\n message: 'Path contains prohibited characters (newlines or shell metacharacters)',\n fatal: true,\n });\n return z.NEVER;\n }\n })\n .describe('File or directory path inside an allowed workspace root.');\n\nexport const OptionalPath = PathBase.optional();\nexport const RequiredPath = PathBase;\n\nexport const SafeGlobPattern = z\n .string()\n .min(1, { message: 'Pattern required' })\n .max(1000, { message: 'Max 1000 chars' })\n .superRefine((val, ctx) => {\n if (val.length === 0) {\n return;\n }\n if (val.trim().length === 0) {\n ctx.addIssue({\n code: 'custom',\n message: 'Pattern cannot be empty or whitespace-only',\n fatal: true,\n });\n return z.NEVER;\n }\n if (val.includes('\\0')) {\n ctx.addIssue({\n code: 'custom',\n message: 'Pattern cannot contain null bytes',\n fatal: true,\n });\n return z.NEVER;\n }\n if (!isSafeGlobSyntax(val)) {\n ctx.addIssue({\n code: 'custom',\n message: 'Invalid glob or unsafe path (absolute/.. forbidden)',\n fatal: true,\n });\n return z.NEVER;\n }\n if (SHELL_METACHAR_RE.test(val)) {\n ctx.addIssue({\n code: 'custom',\n message: 'Pattern contains prohibited characters (newlines or shell metacharacters)',\n fatal: true,\n });\n return z.NEVER;\n }\n })\n .describe('Relative glob pattern under the search root (e.g. \"**/*.ts\", \"src/**/*.js\").')\n .meta({ examples: ['**/*.ts', 'src/**/*.js', '*.{ts,tsx}'] });\n\n// Only the fields whose key does not already say what they hold carry a\n// description. A `.describe('Name')` on `name` costs every client tokens to\n// learn nothing.\nexport const FileInfoSchema = z.strictObject({\n name: z.string(),\n path: z.string(),\n type: FileType,\n // A directory's own inode size says nothing about what is inside it — 0 on\n // Windows, 4096 on ext4 — so say so rather than letting a caller read it as\n // \"empty\". This caveat used to live in stat's text summary, which is gone.\n size: NonNegInt.describe('Bytes; meaningless for a directory — use list for its contents'),\n tokenEstimate: NonNegInt.optional().describe('Rough token estimate; use to pre-screen read cost'),\n created: IsoDateTime,\n modified: IsoDateTime,\n accessed: IsoDateTime,\n permissions: z.string(),\n isHidden: z.boolean(),\n mimeType: z.string().optional(),\n symlinkTarget: z.string().optional().describe('Target (symlink)'),\n});\n\nexport const OperationSummarySchema = z.strictObject({\n total: NonNegInt,\n succeeded: NonNegInt,\n failed: NonNegInt,\n});\n\nexport const PerFileErrorSchema = z.strictObject({\n code: z.string(),\n message: z.string(),\n path: z.string().optional(),\n suggestion: z.string().optional(),\n});\n\n/** One entry of a tool's `failures[]`: the path that failed and why. */\nexport const PathFailureSchema = z.strictObject({\n path: z.string(),\n error: PerFileErrorSchema,\n});\n\n/**\n * One entry of a source→destination tool's `failures[]`. The verb and noun stay\n * per-op so the `.describe()` text reads correctly for each caller.\n */\nexport function pairFailureSchema(verb: 'copied' | 'moved', noun: 'copy' | 'move') {\n return z.strictObject({\n source: z.string().describe(`Source path that could not be ${verb}`),\n destination: z.string().describe(`Intended destination path for the failed ${noun}`),\n error: PerFileErrorSchema,\n });\n}\n\n/** Wire shape of a `pairFailureSchema` entry — the runtime twin of the schema. */\nexport type PairFailureItem = z.infer<ReturnType<typeof pairFailureSchema>>;\n\n// TODO(future): Wrap createReadRangeFields output in a typed Zod object with built-in\n// cross-validation. Safe for now — the only caller (read.ts) calls validateReadRange in its own superRefine.\ninterface ReadRangeDescriptions {\n head: string;\n tail: string;\n startLine: string;\n endLine: string;\n}\n\nexport function createReadRangeFields(descs: ReadRangeDescriptions) {\n return {\n head: z\n .int32()\n .min(1, { message: 'Min: 1' })\n .max(100000, { message: 'Max: 100,000' })\n .optional()\n .describe(descs.head),\n tail: z\n .int32()\n .min(1, { message: 'Min: 1' })\n .max(100000, { message: 'Max: 100,000' })\n .optional()\n .describe(descs.tail),\n startLine: z\n .int32()\n .min(1, { message: 'Min: 1' })\n .max(100000, { message: 'Max: 100,000' })\n .optional()\n .describe(descs.startLine),\n endLine: z\n .int32()\n .min(1, { message: 'Min: 1' })\n .max(100000, { message: 'Max: 100,000' })\n .optional()\n .describe(descs.endLine),\n };\n}\n\nexport function validateReadRange(\n value: {\n head?: number | undefined;\n tail?: number | undefined;\n startLine?: number | undefined;\n endLine?: number | undefined;\n },\n ctx: z.RefinementCtx,\n): void {\n const hasHead = value.head !== undefined;\n const hasTail = value.tail !== undefined;\n const hasStart = value.startLine !== undefined;\n const hasEnd = value.endLine !== undefined;\n\n if (hasHead && (hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['head'],\n message: \"Cannot use 'head' with 'startLine'/'endLine'\",\n params: {\n rule: 'head_no_line_range',\n conflictsWith: ['startLine', 'endLine'],\n suggestion: \"Use either 'head' alone or 'startLine'/'endLine' together.\",\n },\n input: value,\n });\n }\n if (hasTail && (hasHead || hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['tail'],\n message: \"Cannot use 'tail' with 'head'/'startLine'/'endLine'\",\n params: {\n rule: 'tail_exclusive',\n conflictsWith: ['head', 'startLine', 'endLine'],\n suggestion: \"Use 'tail' alone or use 'startLine'/'endLine' without 'tail'.\",\n },\n input: value,\n });\n }\n if (hasEnd && !hasStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' requires 'startLine' to be set\",\n params: {\n rule: 'endLine_requires_startLine',\n suggestion: \"Provide both 'startLine' and 'endLine' together.\",\n },\n input: value,\n });\n }\n const effectiveStart = value.startLine ?? 1;\n if (value.endLine !== undefined && value.endLine < effectiveStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' must be >= 'startLine'\",\n params: {\n rule: 'endLine_gte_startLine',\n conflictsWith: ['startLine'],\n suggestion: 'Set endLine to a value greater than or equal to startLine.',\n },\n input: value,\n });\n }\n}\n\nexport function defaultFalseBoolean(description: string): z.ZodDefault<z.ZodBoolean> {\n return z.boolean().default(false).describe(description);\n}\n\nexport const includeHiddenField = (): z.ZodDefault<z.ZodBoolean> =>\n defaultFalseBoolean('Include hidden items (starting with .)');\nexport const includeIgnoredField = (): z.ZodDefault<z.ZodBoolean> =>\n defaultFalseBoolean('Include ignored items (node_modules, .git, etc).');\nexport const maxDepthField = (): z.ZodOptional<z.ZodNumber> =>\n z\n .uint32()\n .min(0)\n .max(MAX_SEARCH_DEPTH)\n .optional()\n .describe('Max directory depth to scan; 0 = base directory only, omit for unlimited');\n\nconst DEFAULT_MAX_BATCH = 1000;\n\nexport type SingleOrBatchShape<TExtra extends z.ZodRawShape> = TExtra & {\n path: z.ZodOptional<typeof RequiredPath>;\n paths: z.ZodOptional<z.ZodArray<typeof RequiredPath>>;\n};\n\nexport function singleOrBatchPathsInput<TExtra extends z.ZodRawShape>(opts: {\n extra: TExtra;\n maxBatch?: number;\n}): z.ZodObject<SingleOrBatchShape<TExtra>> {\n const maxBatch = opts.maxBatch ?? DEFAULT_MAX_BATCH;\n\n const shape: z.ZodRawShape = {\n ...opts.extra,\n path: RequiredPath.optional().describe('Single file path; mutually exclusive with paths'),\n paths: z\n .array(RequiredPath)\n .min(1)\n .max(maxBatch)\n .optional()\n .describe(\n `Array of file paths for batch mode (max ${String(maxBatch)}); mutually exclusive with path`,\n ),\n };\n\n const base = z.strictObject(shape).superRefine((value, ctx) => {\n const hasPath = value['path'] !== undefined;\n const hasPaths = value['paths'] !== undefined;\n\n if (!hasPath && !hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['path'],\n message: \"Either 'path' or 'paths' must be provided\",\n input: value,\n });\n return;\n }\n if (hasPath && hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['paths'],\n message: \"Cannot use both 'path' and 'paths'\",\n input: value,\n });\n }\n });\n\n // Mirror the superRefine above on the wire: exactly one input mode. `{}` and\n // `path`+`paths` both fail this oneOf, matching the runtime rule.\n return base.meta({\n oneOf: [{ required: ['path'] }, { required: ['paths'] }],\n }) as z.ZodObject<SingleOrBatchShape<TExtra>>;\n}\n\n/**\n * Extract the filesystem paths from a {@link singleOrBatchPathsInput} shape\n * (`{ path?, paths?, files?[{path}] }`) for the executor's access-grant\n * pre-check. Exactly one of the three is set (enforced by the schema's\n * superRefine), so they are checked in priority order.\n */\nexport function singleOrBatchAccessPaths(args: {\n path?: string | undefined;\n paths?: string[] | undefined;\n files?: readonly { path: string }[] | undefined;\n}): string[] {\n if (args.path) return [args.path];\n if (args.paths) return [...args.paths];\n if (args.files) return args.files.map((f) => f.path);\n return [];\n}\n\n// `args` is the exact argument object the one producer (read's\n// buildReadContinuation) emits, not a free-form record: a bare\n// `z.record(z.string(), z.unknown())` renders as `additionalProperties: {}`,\n// which carries no validation keyword and constrains nothing — the Inspector's\n// portability check flags it, and a client cannot tell what to pass. Widen this\n// (to a union) only when a second tool starts emitting continuations.\nexport const ContinuationSchema = z.strictObject({\n tool: z.string().describe('Tool name to call for the next chunk'),\n args: z\n .strictObject({ path: z.string(), startLine: PositiveInt, endLine: PositiveInt })\n .describe('Ready-to-use arguments for the next call; pass verbatim'),\n hint: z.string().describe('One-sentence description of the data still remaining to be read'),\n});\n\n// ponytail: charset regex, not z.base64url() — the SDK's AJV warns on the\n// unknown `base64url` format; the server's own decode (cursor.ts) is the real\n// validation, so loosening the client-facing schema to the alphabet is safe.\nconst base64urlCursor = z.string().regex(/^[A-Za-z0-9_-]+$/);\n\n// Ships once per paginated tool — wording is budgeted (TOOL-SURFACE-002).\nexport const CursorSchema = base64urlCursor\n .optional()\n .describe(\n 'Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot ' +\n 'taken on the first call; it expires after ~60s — re-request without a cursor if rejected.',\n );\n\nexport const NextCursorSchema = base64urlCursor\n .optional()\n .describe('Cursor for the next page; omitted when this is the final page.');\n"]}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../src/core/schema.ts"],"names":[],"mappings":"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","sourcesContent":["import * as z from 'zod/v4';\n\nimport { isSafeGlobSyntax } from './glob.js';\nimport { MIME_KINDS } from './mime.js';\nimport { ENTRY_TYPES } from './primitives.js';\nimport { MAX_SEARCH_DEPTH } from './util.js';\n\n// Runtime: full ISO-8601 UTC; emits the standard `date-time` format on the wire\n// (no AJV warning — it is a known format, unlike sha256_hex / base64url).\n// No `id` on this or any other shared schema below: an `id` hoists the schema\n// into `$defs` and leaves a `$ref` at every use site, which is exactly the wire\n// weight `toDraft202012` (tools/define.ts) publishes without.\nexport const IsoDateTime = z.iso.datetime().meta({\n description: 'ISO 8601 UTC date-time string (e.g. 2024-01-15T12:00:00.000Z)',\n // `format: \"date-time\"` already pins the value; zod's ~330-char calendar\n // regex is pure wire weight. Runtime validation still runs it — this\n // suppresses the emitted keyword only.\n pattern: undefined,\n});\n\n// pattern (not z.hash('sha256')) so no `format: \"sha256_hex\"` keyword is emitted;\n// the SDK's AJV validator warns on that unknown format at every server start.\nexport const Sha256Hex = z\n .string()\n .regex(/^[0-9a-f]{64}$/)\n .meta({ description: 'SHA-256 digest as a 64-character lowercase hex string' });\n\nexport const NonNegInt = z\n .int({ message: 'Must be integer' })\n .nonnegative({ message: 'Must be ≥ 0' });\n\nexport const PositiveInt = z\n .int({ message: 'Must be integer' })\n .positive({ message: 'Must be > 0' });\n\nconst FILE_TYPES = ENTRY_TYPES;\nexport type FileType = (typeof FILE_TYPES)[number];\nexport const FileType = z.enum(FILE_TYPES);\n\nconst FILE_KINDS = MIME_KINDS;\nexport type FileKind = (typeof FILE_KINDS)[number];\nexport const FileKind = z.enum(FILE_KINDS);\n\nconst MAX_PATH_LENGTH = 4096;\n\nconst SHELL_METACHAR_RE = /[\\n\\r;|`]/;\n\nexport const isBlank = (val: string): boolean => val.trim().length === 0;\n\nconst PathBase = z\n .string()\n .min(1, { message: 'Path required' })\n .max(MAX_PATH_LENGTH, { message: `Path too long (max ${MAX_PATH_LENGTH} chars)` })\n .superRefine((val, ctx) => {\n if (val.length === 0) {\n return;\n }\n if (val.trim().length === 0) {\n ctx.addIssue({\n code: 'custom',\n message: 'Path cannot be empty or whitespace-only',\n fatal: true,\n });\n return z.NEVER;\n }\n if (val.includes('\\0')) {\n ctx.addIssue({\n code: 'custom',\n message: 'Path cannot contain null bytes',\n fatal: true,\n });\n return z.NEVER;\n }\n if (val.includes('..')) {\n ctx.addIssue({\n code: 'custom',\n message: 'Directory traversal sequences (\"..\") are forbidden',\n fatal: true,\n });\n return z.NEVER;\n }\n if (SHELL_METACHAR_RE.test(val)) {\n ctx.addIssue({\n code: 'custom',\n message: 'Path contains prohibited characters (newlines or shell metacharacters)',\n fatal: true,\n });\n return z.NEVER;\n }\n })\n .describe('File or directory path inside an allowed workspace root.');\n\nexport const OptionalPath = PathBase.optional();\nexport const RequiredPath = PathBase;\n\nexport const SafeGlobPattern = z\n .string()\n .min(1, { message: 'Pattern required' })\n .max(1000, { message: 'Max 1000 chars' })\n .superRefine((val, ctx) => {\n if (val.length === 0) {\n return;\n }\n if (val.trim().length === 0) {\n ctx.addIssue({\n code: 'custom',\n message: 'Pattern cannot be empty or whitespace-only',\n fatal: true,\n });\n return z.NEVER;\n }\n if (val.includes('\\0')) {\n ctx.addIssue({\n code: 'custom',\n message: 'Pattern cannot contain null bytes',\n fatal: true,\n });\n return z.NEVER;\n }\n if (!isSafeGlobSyntax(val)) {\n ctx.addIssue({\n code: 'custom',\n message: 'Invalid glob or unsafe path (absolute/.. forbidden)',\n fatal: true,\n });\n return z.NEVER;\n }\n if (SHELL_METACHAR_RE.test(val)) {\n ctx.addIssue({\n code: 'custom',\n message: 'Pattern contains prohibited characters (newlines or shell metacharacters)',\n fatal: true,\n });\n return z.NEVER;\n }\n })\n .describe('Relative glob pattern under the search root (e.g. \"**/*.ts\", \"src/**/*.js\").')\n .meta({ examples: ['**/*.ts', 'src/**/*.js', '*.{ts,tsx}'] });\n\n// Only the fields whose key does not already say what they hold carry a\n// description. A `.describe('Name')` on `name` costs every client tokens to\n// learn nothing.\nexport const FileInfoSchema = z.strictObject({\n name: z.string(),\n path: z.string(),\n type: FileType,\n // A directory's own inode size says nothing about what is inside it — 0 on\n // Windows, 4096 on ext4 — so say so rather than letting a caller read it as\n // \"empty\". This caveat used to live in stat's text summary, which is gone.\n size: NonNegInt.describe('Bytes; meaningless for a directory — use list for its contents'),\n tokenEstimate: NonNegInt.optional().describe('Rough token estimate; use to pre-screen read cost'),\n created: IsoDateTime,\n modified: IsoDateTime,\n accessed: IsoDateTime,\n permissions: z.string(),\n isHidden: z.boolean(),\n mimeType: z.string().optional(),\n symlinkTarget: z.string().optional().describe('Target (symlink)'),\n});\n\nexport const OperationSummarySchema = z.strictObject({\n total: NonNegInt,\n succeeded: NonNegInt,\n failed: NonNegInt,\n});\n\nexport const PerFileErrorSchema = z.strictObject({\n code: z.string(),\n message: z.string(),\n path: z.string().optional(),\n suggestion: z.string().optional(),\n});\n\n/** One entry of a tool's `failures[]`: the path that failed and why. */\nexport const PathFailureSchema = z.strictObject({\n path: z.string(),\n error: PerFileErrorSchema,\n});\n\nexport function validateReadRange(\n value: {\n head?: number | undefined;\n tail?: number | undefined;\n startLine?: number | undefined;\n endLine?: number | undefined;\n },\n ctx: z.RefinementCtx,\n): void {\n const hasHead = value.head !== undefined;\n const hasTail = value.tail !== undefined;\n const hasStart = value.startLine !== undefined;\n const hasEnd = value.endLine !== undefined;\n\n if (hasHead && (hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['head'],\n message:\n \"Cannot use 'head' with 'startLine'/'endLine'. Use either 'head' alone or 'startLine'/'endLine' together.\",\n input: value,\n });\n }\n if (hasTail && (hasHead || hasStart || hasEnd)) {\n ctx.addIssue({\n code: 'custom',\n path: ['tail'],\n message:\n \"Cannot use 'tail' with 'head'/'startLine'/'endLine'. Use 'tail' alone, or 'startLine'/'endLine' without 'tail'.\",\n input: value,\n });\n }\n if (hasEnd && !hasStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' requires 'startLine' to be set. Provide both together.\",\n input: value,\n });\n }\n const effectiveStart = value.startLine ?? 1;\n if (value.endLine !== undefined && value.endLine < effectiveStart) {\n ctx.addIssue({\n code: 'custom',\n path: ['endLine'],\n message: \"'endLine' must be >= 'startLine'\",\n input: value,\n });\n }\n}\n\nexport function defaultFalseBoolean(description: string): z.ZodDefault<z.ZodBoolean> {\n return z.boolean().default(false).describe(description);\n}\n\nexport const includeHiddenField = (): z.ZodDefault<z.ZodBoolean> =>\n defaultFalseBoolean('Include hidden items (starting with .)');\nexport const includeIgnoredField = (): z.ZodDefault<z.ZodBoolean> =>\n defaultFalseBoolean('Include ignored items (node_modules, .git, etc).');\nexport const maxDepthField = (): z.ZodOptional<z.ZodNumber> =>\n z\n .uint32()\n .min(0)\n .max(MAX_SEARCH_DEPTH)\n .optional()\n .describe('Max directory depth to scan; 0 = base directory only, omit for unlimited');\n\nconst DEFAULT_MAX_BATCH = 1000;\n\nexport type SingleOrBatchShape<TExtra extends z.ZodRawShape> = TExtra & {\n path: z.ZodOptional<typeof RequiredPath>;\n paths: z.ZodOptional<z.ZodArray<typeof RequiredPath>>;\n};\n\nexport function singleOrBatchPathsInput<TExtra extends z.ZodRawShape>(\n extra: TExtra,\n): z.ZodObject<SingleOrBatchShape<TExtra>> {\n const shape: z.ZodRawShape = {\n ...extra,\n path: RequiredPath.optional().describe('Single file path; mutually exclusive with paths'),\n paths: z\n .array(RequiredPath)\n .min(1)\n .max(DEFAULT_MAX_BATCH)\n .optional()\n .describe(\n `Array of file paths for batch mode (max ${String(DEFAULT_MAX_BATCH)}); mutually exclusive with path`,\n ),\n };\n\n const base = z.strictObject(shape).superRefine((value, ctx) => {\n const hasPath = value['path'] !== undefined;\n const hasPaths = value['paths'] !== undefined;\n\n if (!hasPath && !hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['path'],\n message: \"Either 'path' or 'paths' must be provided\",\n input: value,\n });\n return;\n }\n if (hasPath && hasPaths) {\n ctx.addIssue({\n code: 'custom',\n path: ['paths'],\n message: \"Cannot use both 'path' and 'paths'\",\n input: value,\n });\n }\n });\n\n // Mirror the superRefine above on the wire: exactly one input mode. `{}` and\n // `path`+`paths` both fail this oneOf, matching the runtime rule.\n return base.meta({\n oneOf: [{ required: ['path'] }, { required: ['paths'] }],\n }) as z.ZodObject<SingleOrBatchShape<TExtra>>;\n}\n\n/**\n * Extract the filesystem paths from a {@link singleOrBatchPathsInput} shape\n * (`{ path?, paths?, files?[{path}] }`) for the executor's access-grant\n * pre-check. Exactly one of the three is set (enforced by the schema's\n * superRefine), so they are checked in priority order.\n */\nexport function singleOrBatchAccessPaths(args: {\n path?: string | undefined;\n paths?: string[] | undefined;\n files?: readonly { path: string }[] | undefined;\n}): string[] {\n if (args.path) return [args.path];\n if (args.paths) return [...args.paths];\n if (args.files) return args.files.map((f) => f.path);\n return [];\n}\n\n// `args` is the exact argument object the one producer (read's\n// buildReadContinuation) emits, not a free-form record: a bare\n// `z.record(z.string(), z.unknown())` renders as `additionalProperties: {}`,\n// which carries no validation keyword and constrains nothing — the Inspector's\n// portability check flags it, and a client cannot tell what to pass. Widen this\n// (to a union) only when a second tool starts emitting continuations.\nexport const ContinuationSchema = z.strictObject({\n tool: z.string().describe('Tool name to call for the next chunk'),\n args: z\n .strictObject({ path: z.string(), startLine: PositiveInt, endLine: PositiveInt })\n .describe('Ready-to-use arguments for the next call; pass verbatim'),\n hint: z.string().describe('One-sentence description of the data still remaining to be read'),\n});\n\n// ponytail: charset regex, not z.base64url() — the SDK's AJV warns on the\n// unknown `base64url` format; the server's own decode (cursor.ts) is the real\n// validation, so loosening the client-facing schema to the alphabet is safe.\nconst base64urlCursor = z.string().regex(/^[A-Za-z0-9_-]+$/);\n\n// Ships once per paginated tool — wording is budgeted (TOOL-SURFACE-002).\nexport const CursorSchema = base64urlCursor\n .optional()\n .describe(\n 'Opaque pagination cursor; pass unchanged for the next page. Pages slice one snapshot ' +\n 'taken on the first call; it expires after ~60s — re-request without a cursor if rejected.',\n );\n\nexport const NextCursorSchema = base64urlCursor\n .optional()\n .describe('Cursor for the next page; omitted when this is the final page.');\n"]}
@@ -1,9 +1,10 @@
1
1
  import { RE2 } from '@adguard/re2-wasm';
2
- import type { StoppedReason } from './concurrency.js';
3
2
  import type { PathGuard } from './path.js';
4
3
  interface SearchResult {
5
4
  file: string;
6
5
  line: number;
6
+ /** 0-indexed column of the first occurrence on the line. */
7
+ column: number;
7
8
  content: string;
8
9
  matchCount?: number;
9
10
  }
@@ -71,11 +72,20 @@ export interface SearchContentOutcome {
71
72
  skippedInaccessible: number;
72
73
  /** Files skipped unread because they exceed maxFileSize. */
73
74
  skippedTooLarge: number;
74
- stoppedReason?: StoppedReason;
75
+ /**
76
+ * `StoppedReason` narrowed to the stops these scans can produce: both call
77
+ * only `hitMaxResults`/`hitAbort` (see concurrency.ts) — never
78
+ * `hitMaxFiles`, which belongs to `replace_text`'s per-file cap.
79
+ */
80
+ stoppedReason?: 'maxResults' | 'timeout';
75
81
  };
76
82
  }
77
- /** Owns the compiled pattern's lifetime; the scan itself is `scanContent`. */
78
- export declare function searchContent(directory: string, pattern: string, options: SearchContentOptions, pathGuard: PathGuard): Promise<SearchContentOutcome>;
83
+ /**
84
+ * Owns the compiled pattern's lifetime. Pass `precompiled` to skip compiling
85
+ * here — the caller compiled the same pattern with the same flags and keeps
86
+ * owning the {@link freeRegex} call in that case.
87
+ */
88
+ export declare function searchContent(directory: string, pattern: string, options: SearchContentOptions, pathGuard: PathGuard, precompiled?: Regex): Promise<SearchContentOutcome>;
79
89
  export declare function searchFiles(directory: string, pattern: string, excludePatterns: string[], options: {
80
90
  maxResults?: number;
81
91
  includeHidden?: boolean;
@@ -93,7 +103,8 @@ export declare function searchFiles(directory: string, pattern: string, excludeP
93
103
  filesScanned: number;
94
104
  truncated: boolean;
95
105
  skippedInaccessible: number;
96
- stoppedReason?: StoppedReason;
106
+ /** Narrowed like SearchContentOutcome's — scans never hit `hitMaxFiles`. */
107
+ stoppedReason?: 'maxResults' | 'timeout';
97
108
  };
98
109
  }>;
99
110
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,GAAG,EAAE,MAAM,mBAAmB,CAAC;AAGxC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAI3C,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,KAAK,GAAG,GAAG,CAAC;AACxB,MAAM,WAAW,mBAAmB;IAClC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAKD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,mBAAwB,GAAG,KAAK,CAWtF;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,GAAG,SAAS,GAAG,IAAI,CAOxD;AAkBD,MAAM,WAAW,oBAAoB;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;IAC3B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,OAAO,EAAE;QACP;;;;WAIG;QACH,aAAa,EAAE,MAAM,CAAC;QACtB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,OAAO,CAAC;QACnB,4DAA4D;QAC5D,mBAAmB,EAAE,MAAM,CAAC;QAC5B,4DAA4D;QAC5D,eAAe,EAAE,MAAM,CAAC;QACxB,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B,CAAC;CACH;AAED,8EAA8E;AAC9E,wBAAsB,aAAa,CACjC,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,oBAAoB,EAC7B,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC,oBAAoB,CAAC,CAQ/B;AA6HD,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EACf,eAAe,EAAE,MAAM,EAAE,EACzB,OAAO,EAAE;IACP,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACzB,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,EACD,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC;IACT,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5B,OAAO,EAAE;QACP,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,SAAS,EAAE,OAAO,CAAC;QACnB,mBAAmB,EAAE,MAAM,CAAC;QAC5B,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B,CAAC;CACH,CAAC,CAiDD"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,5 @@
1
1
  import { stat as fsStat, readFile } from 'node:fs/promises';
2
+ import { basename } from 'node:path';
2
3
  import { RE2 } from '@adguard/re2-wasm';
3
4
  import { StopReasonTracker } from './concurrency.js';
4
5
  import { globEntries } from './glob.js';
@@ -56,31 +57,135 @@ export function freeRegex(regex) {
56
57
  }
57
58
  }
58
59
  /**
59
- * Count non-overlapping occurrences of a global regex in a single line. Guards
60
- * zero-length matches (e.g. `a*`) so they cannot loop forever.
60
+ * Find non-overlapping occurrences of a global regex in a single line, reporting
61
+ * the first match's column alongside the count. Guards zero-length matches
62
+ * (e.g. `a*`) so they cannot loop forever.
61
63
  */
62
- function countLineMatches(regex, line) {
64
+ function findLineMatches(regex, line) {
63
65
  regex.lastIndex = 0;
64
66
  let count = 0;
67
+ let column = -1;
65
68
  let match;
66
69
  while ((match = regex.exec(line)) !== null) {
70
+ if (column === -1)
71
+ column = match.index;
67
72
  count++;
68
73
  if (match.index === regex.lastIndex)
69
74
  regex.lastIndex++; // advance past zero-length match
70
75
  if (count >= MAX_MATCHES_PER_LINE)
71
76
  break;
72
77
  }
73
- return count;
78
+ return count > 0 ? { count, column } : undefined;
74
79
  }
75
- /** Owns the compiled pattern's lifetime; the scan itself is `scanContent`. */
76
- export async function searchContent(directory, pattern, options, pathGuard) {
77
- const regexPattern = options.isRegex ? pattern || '' : escapeRegexLiteral(pattern || '');
78
- const regex = compileRegex(regexPattern, { caseSensitive: Boolean(options.caseSensitive) });
80
+ /**
81
+ * Owns the compiled pattern's lifetime. Pass `precompiled` to skip compiling
82
+ * here — the caller compiled the same pattern with the same flags and keeps
83
+ * owning the {@link freeRegex} call in that case.
84
+ */
85
+ export async function searchContent(directory, pattern, options, pathGuard, precompiled) {
86
+ const regex = precompiled ??
87
+ compileRegex(options.isRegex ? pattern || '' : escapeRegexLiteral(pattern || ''), {
88
+ caseSensitive: Boolean(options.caseSensitive),
89
+ });
79
90
  try {
80
- return await scanContent(directory, options, pathGuard, regex);
91
+ const matches = [];
92
+ const maxResults = options.maxResults ?? 100;
93
+ const maxFileSize = options.maxFileSize ?? getMaxTextFileSize();
94
+ const entries = globEntries({
95
+ cwd: directory,
96
+ pattern: options.filePattern ?? '**/*',
97
+ excludePatterns: options.excludePatterns ?? [],
98
+ includeHidden: Boolean(options.includeHidden),
99
+ respectGitignore: Boolean(options.respectGitignore),
100
+ maxDepth: options.maxDepth ?? 100,
101
+ suppressErrors: true,
102
+ });
103
+ let filesScanned = 0;
104
+ let filesMatched = 0;
105
+ let matchingLines = 0;
106
+ let skippedTooLarge = 0;
107
+ const counters = { skippedInaccessible: 0, stoppedByAbort: false };
108
+ for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {
109
+ if (matches.length >= maxResults)
110
+ break;
111
+ // Skip oversized files before reading to avoid unbounded memory use. Count
112
+ // them: "no matches" for a reason other than the pattern must be visible.
113
+ try {
114
+ const stats = await fsStat(entry.path);
115
+ if (stats.size > maxFileSize) {
116
+ skippedTooLarge++;
117
+ continue;
118
+ }
119
+ }
120
+ catch {
121
+ counters.skippedInaccessible++;
122
+ continue;
123
+ }
124
+ filesScanned++;
125
+ try {
126
+ const content = await readFile(entry.path, { encoding: 'utf-8', signal: options.signal });
127
+ const lines = content.split('\n');
128
+ let matchedFile = false;
129
+ for (let i = 0; i < lines.length; i++) {
130
+ const line = lines[i];
131
+ if (line === undefined)
132
+ continue;
133
+ // One scan per line: findLineMatches resets lastIndex itself, so it
134
+ // doubles as the "does this line match" test.
135
+ const found = findLineMatches(regex, line);
136
+ if (found) {
137
+ matchedFile = true;
138
+ matchingLines++;
139
+ matches.push({
140
+ file: entry.path,
141
+ line: i + 1,
142
+ column: found.column,
143
+ content: line,
144
+ matchCount: found.count,
145
+ });
146
+ if (matches.length >= maxResults)
147
+ break;
148
+ }
149
+ }
150
+ if (matchedFile)
151
+ filesMatched++;
152
+ }
153
+ catch {
154
+ // A read failure while the signal is aborted IS the abort, not an
155
+ // unreadable file — stop rather than spend another iteration and then
156
+ // report a cut-short scan as complete.
157
+ if (options.signal?.aborted) {
158
+ counters.stoppedByAbort = true;
159
+ break;
160
+ }
161
+ // ignore read errors (e.g. binary files)
162
+ }
163
+ }
164
+ const tracker = new StopReasonTracker();
165
+ if (matches.length >= maxResults)
166
+ tracker.hitMaxResults();
167
+ if (counters.stoppedByAbort)
168
+ tracker.hitAbort();
169
+ // resolve() is StoppedReason | undefined, but this scan only records
170
+ // maxResults/timeout stops (see the summary type's narrowing note).
171
+ const stoppedReason = tracker.resolve();
172
+ return {
173
+ basePath: directory,
174
+ matches,
175
+ summary: {
176
+ matchingLines,
177
+ filesScanned,
178
+ filesMatched,
179
+ truncated: tracker.truncated,
180
+ skippedInaccessible: counters.skippedInaccessible,
181
+ skippedTooLarge,
182
+ ...(stoppedReason ? { stoppedReason } : {}),
183
+ },
184
+ };
81
185
  }
82
186
  finally {
83
- freeRegex(regex);
187
+ if (precompiled === undefined)
188
+ freeRegex(regex);
84
189
  }
85
190
  }
86
191
  async function* guardedEntries(entries, pathGuard, signal, counters) {
@@ -102,99 +207,6 @@ async function* guardedEntries(entries, pathGuard, signal, counters) {
102
207
  yield entry;
103
208
  }
104
209
  }
105
- async function scanContent(directory, options, pathGuard, regex) {
106
- const matches = [];
107
- const maxResults = options.maxResults ?? 100;
108
- const maxFileSize = options.maxFileSize ?? getMaxTextFileSize();
109
- const entries = globEntries({
110
- cwd: directory,
111
- pattern: options.filePattern ?? '**/*',
112
- excludePatterns: options.excludePatterns ?? [],
113
- includeHidden: Boolean(options.includeHidden),
114
- respectGitignore: Boolean(options.respectGitignore),
115
- maxDepth: options.maxDepth ?? 100,
116
- suppressErrors: true,
117
- });
118
- let filesScanned = 0;
119
- let filesMatched = 0;
120
- let matchingLines = 0;
121
- let skippedTooLarge = 0;
122
- const counters = { skippedInaccessible: 0, stoppedByAbort: false };
123
- for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {
124
- if (matches.length >= maxResults)
125
- break;
126
- // Skip oversized files before reading to avoid unbounded memory use. Count
127
- // them: "no matches" for a reason other than the pattern must be visible.
128
- try {
129
- const stats = await fsStat(entry.path);
130
- if (stats.size > maxFileSize) {
131
- skippedTooLarge++;
132
- continue;
133
- }
134
- }
135
- catch {
136
- counters.skippedInaccessible++;
137
- continue;
138
- }
139
- filesScanned++;
140
- try {
141
- const content = await readFile(entry.path, { encoding: 'utf-8', signal: options.signal });
142
- const lines = content.split('\n');
143
- let matchedFile = false;
144
- for (let i = 0; i < lines.length; i++) {
145
- const line = lines[i];
146
- if (line === undefined)
147
- continue;
148
- // One scan per line: countLineMatches resets lastIndex itself, so it
149
- // doubles as the "does this line match" test.
150
- const occurrences = countLineMatches(regex, line);
151
- if (occurrences > 0) {
152
- matchedFile = true;
153
- matchingLines++;
154
- matches.push({
155
- file: entry.path,
156
- line: i + 1,
157
- content: line,
158
- matchCount: occurrences,
159
- });
160
- if (matches.length >= maxResults)
161
- break;
162
- }
163
- }
164
- if (matchedFile)
165
- filesMatched++;
166
- }
167
- catch {
168
- // A read failure while the signal is aborted IS the abort, not an
169
- // unreadable file — stop rather than spend another iteration and then
170
- // report a cut-short scan as complete.
171
- if (options.signal?.aborted) {
172
- counters.stoppedByAbort = true;
173
- break;
174
- }
175
- // ignore read errors (e.g. binary files)
176
- }
177
- }
178
- const tracker = new StopReasonTracker();
179
- if (matches.length >= maxResults)
180
- tracker.hitMaxResults();
181
- if (counters.stoppedByAbort)
182
- tracker.hitAbort();
183
- const stoppedReason = tracker.resolve();
184
- return {
185
- basePath: directory,
186
- matches,
187
- summary: {
188
- matchingLines,
189
- filesScanned,
190
- filesMatched,
191
- truncated: tracker.truncated,
192
- skippedInaccessible: counters.skippedInaccessible,
193
- skippedTooLarge,
194
- ...(stoppedReason ? { stoppedReason } : {}),
195
- },
196
- };
197
- }
198
210
  export async function searchFiles(directory, pattern, excludePatterns, options, pathGuard) {
199
211
  const maxResults = options.maxResults ?? 100;
200
212
  const entries = globEntries({
@@ -218,11 +230,7 @@ export async function searchFiles(directory, pattern, excludePatterns, options,
218
230
  // Sorting — only name / path are supported; size / modified were removed
219
231
  // (the glob never collected stats, so they were always undefined).
220
232
  if (options.sortBy === 'name') {
221
- results.sort((a, b) => {
222
- const aName = a.path.split(/[/\\]/).pop() ?? '';
223
- const bName = b.path.split(/[/\\]/).pop() ?? '';
224
- return aName.localeCompare(bName);
225
- });
233
+ results.sort((a, b) => basename(a.path).localeCompare(basename(b.path)));
226
234
  }
227
235
  else {
228
236
  results.sort((a, b) => a.path.localeCompare(b.path));
@@ -232,6 +240,8 @@ export async function searchFiles(directory, pattern, excludePatterns, options,
232
240
  tracker.hitMaxResults();
233
241
  if (counters.stoppedByAbort)
234
242
  tracker.hitAbort();
243
+ // resolve() is StoppedReason | undefined, but this scan only records
244
+ // maxResults/timeout stops (see the summary type's narrowing note).
235
245
  const stoppedReason = tracker.resolve();
236
246
  return {
237
247
  basePath: directory,
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat as fsStat, readFile } from 'node:fs/promises';\n\nimport type { RE2ExecArray } from '@adguard/re2-wasm';\nimport { RE2 } from '@adguard/re2-wasm';\n\nimport { StopReasonTracker } from './concurrency.js';\nimport type { StoppedReason } from './concurrency.js';\nimport { globEntries, type GlobEntry } from './glob.js';\nimport type { PathGuard } from './path.js';\nimport { escapeRegexLiteral } from './primitives.js';\nimport { getMaxTextFileSize } from './util.js';\n\ninterface SearchResult {\n file: string;\n line: number;\n content: string;\n matchCount?: number;\n}\n\nexport type Regex = RE2;\nexport interface RegexCompileOptions {\n caseSensitive?: boolean;\n}\n\n/** Occurrence-counting bound, so one pathological line cannot spin forever. */\nconst MAX_MATCHES_PER_LINE = 100_000;\n\n/**\n * Compile a pattern on RE2 rather than on V8's irregexp.\n *\n * Patterns arrive from the MCP client, so a backtracking engine would let one\n * request pin the event loop with no way out: an abort signal cannot preempt a\n * synchronous `exec`, and on stdio that wedges the whole server. RE2 matches in\n * time linear in the input and cannot backtrack at all, so the hazard is gone\n * rather than bounded.\n *\n * RE2 rejects the constructs the tool schema documents as unsupported —\n * lookahead, lookbehind, backreferences — with its own {@link SyntaxError},\n * which the tool layer turns into a normal tool error. It always matches in\n * Unicode mode and requires the `u` flag to say so.\n *\n * Every compiled pattern owns memory in re2-wasm's fixed 16 MB heap, which\n * `ALLOW_MEMORY_GROWTH` is off for. re2-wasm never frees it and a\n * FinalizationRegistry does not keep up (V8 sees no pressure from the wasm\n * heap), so exhaustion is an emscripten `abort()` that kills regex search for\n * the rest of the process. Every caller MUST pass the result to\n * {@link freeRegex} when it is done with it.\n */\nexport function compileRegex(pattern: string, options: RegexCompileOptions = {}): Regex {\n const flags = options.caseSensitive ? 'gu' : 'giu';\n try {\n return new RE2(pattern, flags);\n } catch (error) {\n if (!(error instanceof SyntaxError)) throw error;\n throw new SyntaxError(\n `${error.message} — lookahead, lookbehind, and backreferences are not supported.`,\n { cause: error },\n );\n }\n}\n\n/**\n * Release a compiled pattern's wasm memory. re2-wasm exposes no disposal of its\n * own, so this reaches the embind handle it holds privately; a version that\n * renames the field degrades to the pre-existing leak rather than throwing.\n * Idempotent — a second call on an already-freed handle is swallowed. Using a\n * {@link Regex} after freeing it is undefined behaviour in the wasm heap, so\n * free only in a `finally` that owns the compile.\n */\nexport function freeRegex(regex: Regex | undefined): void {\n const handle = (regex as unknown as { wrapper?: { delete?: () => void } } | undefined)?.wrapper;\n try {\n handle?.delete?.();\n } catch {\n // already deleted, or a re2-wasm build without embind disposal\n }\n}\n\n/**\n * Count non-overlapping occurrences of a global regex in a single line. Guards\n * zero-length matches (e.g. `a*`) so they cannot loop forever.\n */\nfunction countLineMatches(regex: Regex, line: string): number {\n regex.lastIndex = 0;\n let count = 0;\n let match: RE2ExecArray | null;\n while ((match = regex.exec(line)) !== null) {\n count++;\n if (match.index === regex.lastIndex) regex.lastIndex++; // advance past zero-length match\n if (count >= MAX_MATCHES_PER_LINE) break;\n }\n return count;\n}\n\nexport interface SearchContentOptions {\n caseSensitive?: boolean;\n isRegex?: boolean;\n maxResults?: number;\n filePattern?: string;\n excludePatterns?: string[];\n respectGitignore?: boolean;\n includeHidden?: boolean;\n maxDepth?: number;\n maxFileSize?: number;\n signal?: AbortSignal;\n}\n\nexport interface SearchContentOutcome {\n basePath: string;\n matches: SearchResult[];\n summary: {\n /**\n * Matching *lines*, one per entry in `matches` — not pattern occurrences.\n * A line with three occurrences counts once here and reports 3 in its own\n * `SearchResult.matchCount`.\n */\n matchingLines: number;\n filesScanned: number;\n filesMatched: number;\n truncated: boolean;\n /** Files the guard rejected or that could not be stat'd. */\n skippedInaccessible: number;\n /** Files skipped unread because they exceed maxFileSize. */\n skippedTooLarge: number;\n stoppedReason?: StoppedReason;\n };\n}\n\n/** Owns the compiled pattern's lifetime; the scan itself is `scanContent`. */\nexport async function searchContent(\n directory: string,\n pattern: string,\n options: SearchContentOptions,\n pathGuard: PathGuard,\n): Promise<SearchContentOutcome> {\n const regexPattern = options.isRegex ? pattern || '' : escapeRegexLiteral(pattern || '');\n const regex = compileRegex(regexPattern, { caseSensitive: Boolean(options.caseSensitive) });\n try {\n return await scanContent(directory, options, pathGuard, regex);\n } finally {\n freeRegex(regex);\n }\n}\n\nasync function* guardedEntries(\n entries: AsyncIterable<GlobEntry>,\n pathGuard: PathGuard,\n signal: AbortSignal | undefined,\n counters: { skippedInaccessible: number; stoppedByAbort: boolean },\n): AsyncGenerator<GlobEntry> {\n // The signal carries both client cancellation and the tool's search timeout,\n // so an abort means \"return what we have, marked incomplete\" rather than\n // throw — but it must never be reported as a finished scan.\n for await (const entry of entries) {\n if (signal?.aborted) {\n counters.stoppedByAbort = true;\n return;\n }\n try {\n await pathGuard.validateExistingPath(entry.path);\n } catch {\n counters.skippedInaccessible++;\n continue;\n }\n yield entry;\n }\n}\n\nasync function scanContent(\n directory: string,\n options: SearchContentOptions,\n pathGuard: PathGuard,\n regex: Regex,\n): Promise<SearchContentOutcome> {\n const matches: SearchResult[] = [];\n const maxResults = options.maxResults ?? 100;\n const maxFileSize = options.maxFileSize ?? getMaxTextFileSize();\n\n const entries = globEntries({\n cwd: directory,\n pattern: options.filePattern ?? '**/*',\n excludePatterns: options.excludePatterns ?? [],\n includeHidden: Boolean(options.includeHidden),\n respectGitignore: Boolean(options.respectGitignore),\n maxDepth: options.maxDepth ?? 100,\n suppressErrors: true,\n });\n\n let filesScanned = 0;\n let filesMatched = 0;\n let matchingLines = 0;\n let skippedTooLarge = 0;\n const counters = { skippedInaccessible: 0, stoppedByAbort: false };\n\n for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {\n if (matches.length >= maxResults) break;\n\n // Skip oversized files before reading to avoid unbounded memory use. Count\n // them: \"no matches\" for a reason other than the pattern must be visible.\n try {\n const stats = await fsStat(entry.path);\n if (stats.size > maxFileSize) {\n skippedTooLarge++;\n continue;\n }\n } catch {\n counters.skippedInaccessible++;\n continue;\n }\n\n filesScanned++;\n\n try {\n const content = await readFile(entry.path, { encoding: 'utf-8', signal: options.signal });\n const lines = content.split('\\n');\n let matchedFile = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n // One scan per line: countLineMatches resets lastIndex itself, so it\n // doubles as the \"does this line match\" test.\n const occurrences = countLineMatches(regex, line);\n if (occurrences > 0) {\n matchedFile = true;\n matchingLines++;\n matches.push({\n file: entry.path,\n line: i + 1,\n content: line,\n matchCount: occurrences,\n });\n if (matches.length >= maxResults) break;\n }\n }\n if (matchedFile) filesMatched++;\n } catch {\n // A read failure while the signal is aborted IS the abort, not an\n // unreadable file — stop rather than spend another iteration and then\n // report a cut-short scan as complete.\n if (options.signal?.aborted) {\n counters.stoppedByAbort = true;\n break;\n }\n // ignore read errors (e.g. binary files)\n }\n }\n\n const tracker = new StopReasonTracker();\n if (matches.length >= maxResults) tracker.hitMaxResults();\n if (counters.stoppedByAbort) tracker.hitAbort();\n const stoppedReason = tracker.resolve();\n\n return {\n basePath: directory,\n matches,\n summary: {\n matchingLines,\n filesScanned,\n filesMatched,\n truncated: tracker.truncated,\n skippedInaccessible: counters.skippedInaccessible,\n skippedTooLarge,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n\nexport async function searchFiles(\n directory: string,\n pattern: string,\n excludePatterns: string[],\n options: {\n maxResults?: number;\n includeHidden?: boolean;\n sortBy?: 'name' | 'path';\n respectGitignore?: boolean;\n maxDepth?: number;\n signal?: AbortSignal;\n },\n pathGuard: PathGuard,\n): Promise<{\n basePath: string;\n results: { path: string }[];\n summary: {\n matched: number;\n filesScanned: number;\n truncated: boolean;\n skippedInaccessible: number;\n stoppedReason?: StoppedReason;\n };\n}> {\n const maxResults = options.maxResults ?? 100;\n const entries = globEntries({\n cwd: directory,\n pattern,\n excludePatterns,\n includeHidden: Boolean(options.includeHidden),\n respectGitignore: Boolean(options.respectGitignore),\n maxDepth: options.maxDepth ?? 100,\n suppressErrors: true,\n });\n const results: { path: string }[] = [];\n let filesScanned = 0;\n const counters = { skippedInaccessible: 0, stoppedByAbort: false };\n\n for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {\n if (results.length >= maxResults) break;\n filesScanned++;\n results.push({ path: entry.path });\n }\n\n // Sorting — only name / path are supported; size / modified were removed\n // (the glob never collected stats, so they were always undefined).\n if (options.sortBy === 'name') {\n results.sort((a, b) => {\n const aName = a.path.split(/[/\\\\]/).pop() ?? '';\n const bName = b.path.split(/[/\\\\]/).pop() ?? '';\n return aName.localeCompare(bName);\n });\n } else {\n results.sort((a, b) => a.path.localeCompare(b.path));\n }\n\n const tracker = new StopReasonTracker();\n if (results.length >= maxResults) tracker.hitMaxResults();\n if (counters.stoppedByAbort) tracker.hitAbort();\n const stoppedReason = tracker.resolve();\n\n return {\n basePath: directory,\n results,\n summary: {\n matched: results.length,\n filesScanned,\n truncated: tracker.truncated,\n skippedInaccessible: counters.skippedInaccessible,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n"]}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/core/search.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat as fsStat, readFile } from 'node:fs/promises';\nimport { basename } from 'node:path';\n\nimport type { RE2ExecArray } from '@adguard/re2-wasm';\nimport { RE2 } from '@adguard/re2-wasm';\n\nimport { StopReasonTracker } from './concurrency.js';\nimport { globEntries, type GlobEntry } from './glob.js';\nimport type { PathGuard } from './path.js';\nimport { escapeRegexLiteral } from './primitives.js';\nimport { getMaxTextFileSize } from './util.js';\n\ninterface SearchResult {\n file: string;\n line: number;\n /** 0-indexed column of the first occurrence on the line. */\n column: number;\n content: string;\n matchCount?: number;\n}\n\nexport type Regex = RE2;\nexport interface RegexCompileOptions {\n caseSensitive?: boolean;\n}\n\n/** Occurrence-counting bound, so one pathological line cannot spin forever. */\nconst MAX_MATCHES_PER_LINE = 100_000;\n\n/**\n * Compile a pattern on RE2 rather than on V8's irregexp.\n *\n * Patterns arrive from the MCP client, so a backtracking engine would let one\n * request pin the event loop with no way out: an abort signal cannot preempt a\n * synchronous `exec`, and on stdio that wedges the whole server. RE2 matches in\n * time linear in the input and cannot backtrack at all, so the hazard is gone\n * rather than bounded.\n *\n * RE2 rejects the constructs the tool schema documents as unsupported —\n * lookahead, lookbehind, backreferences — with its own {@link SyntaxError},\n * which the tool layer turns into a normal tool error. It always matches in\n * Unicode mode and requires the `u` flag to say so.\n *\n * Every compiled pattern owns memory in re2-wasm's fixed 16 MB heap, which\n * `ALLOW_MEMORY_GROWTH` is off for. re2-wasm never frees it and a\n * FinalizationRegistry does not keep up (V8 sees no pressure from the wasm\n * heap), so exhaustion is an emscripten `abort()` that kills regex search for\n * the rest of the process. Every caller MUST pass the result to\n * {@link freeRegex} when it is done with it.\n */\nexport function compileRegex(pattern: string, options: RegexCompileOptions = {}): Regex {\n const flags = options.caseSensitive ? 'gu' : 'giu';\n try {\n return new RE2(pattern, flags);\n } catch (error) {\n if (!(error instanceof SyntaxError)) throw error;\n throw new SyntaxError(\n `${error.message} — lookahead, lookbehind, and backreferences are not supported.`,\n { cause: error },\n );\n }\n}\n\n/**\n * Release a compiled pattern's wasm memory. re2-wasm exposes no disposal of its\n * own, so this reaches the embind handle it holds privately; a version that\n * renames the field degrades to the pre-existing leak rather than throwing.\n * Idempotent — a second call on an already-freed handle is swallowed. Using a\n * {@link Regex} after freeing it is undefined behaviour in the wasm heap, so\n * free only in a `finally` that owns the compile.\n */\nexport function freeRegex(regex: Regex | undefined): void {\n const handle = (regex as unknown as { wrapper?: { delete?: () => void } } | undefined)?.wrapper;\n try {\n handle?.delete?.();\n } catch {\n // already deleted, or a re2-wasm build without embind disposal\n }\n}\n\n/**\n * Find non-overlapping occurrences of a global regex in a single line, reporting\n * the first match's column alongside the count. Guards zero-length matches\n * (e.g. `a*`) so they cannot loop forever.\n */\nfunction findLineMatches(\n regex: Regex,\n line: string,\n): { count: number; column: number } | undefined {\n regex.lastIndex = 0;\n let count = 0;\n let column = -1;\n let match: RE2ExecArray | null;\n while ((match = regex.exec(line)) !== null) {\n if (column === -1) column = match.index;\n count++;\n if (match.index === regex.lastIndex) regex.lastIndex++; // advance past zero-length match\n if (count >= MAX_MATCHES_PER_LINE) break;\n }\n return count > 0 ? { count, column } : undefined;\n}\n\nexport interface SearchContentOptions {\n caseSensitive?: boolean;\n isRegex?: boolean;\n maxResults?: number;\n filePattern?: string;\n excludePatterns?: string[];\n respectGitignore?: boolean;\n includeHidden?: boolean;\n maxDepth?: number;\n maxFileSize?: number;\n signal?: AbortSignal;\n}\n\nexport interface SearchContentOutcome {\n basePath: string;\n matches: SearchResult[];\n summary: {\n /**\n * Matching *lines*, one per entry in `matches` — not pattern occurrences.\n * A line with three occurrences counts once here and reports 3 in its own\n * `SearchResult.matchCount`.\n */\n matchingLines: number;\n filesScanned: number;\n filesMatched: number;\n truncated: boolean;\n /** Files the guard rejected or that could not be stat'd. */\n skippedInaccessible: number;\n /** Files skipped unread because they exceed maxFileSize. */\n skippedTooLarge: number;\n /**\n * `StoppedReason` narrowed to the stops these scans can produce: both call\n * only `hitMaxResults`/`hitAbort` (see concurrency.ts) — never\n * `hitMaxFiles`, which belongs to `replace_text`'s per-file cap.\n */\n stoppedReason?: 'maxResults' | 'timeout';\n };\n}\n\n/**\n * Owns the compiled pattern's lifetime. Pass `precompiled` to skip compiling\n * here — the caller compiled the same pattern with the same flags and keeps\n * owning the {@link freeRegex} call in that case.\n */\nexport async function searchContent(\n directory: string,\n pattern: string,\n options: SearchContentOptions,\n pathGuard: PathGuard,\n precompiled?: Regex,\n): Promise<SearchContentOutcome> {\n const regex =\n precompiled ??\n compileRegex(options.isRegex ? pattern || '' : escapeRegexLiteral(pattern || ''), {\n caseSensitive: Boolean(options.caseSensitive),\n });\n try {\n const matches: SearchResult[] = [];\n const maxResults = options.maxResults ?? 100;\n const maxFileSize = options.maxFileSize ?? getMaxTextFileSize();\n\n const entries = globEntries({\n cwd: directory,\n pattern: options.filePattern ?? '**/*',\n excludePatterns: options.excludePatterns ?? [],\n includeHidden: Boolean(options.includeHidden),\n respectGitignore: Boolean(options.respectGitignore),\n maxDepth: options.maxDepth ?? 100,\n suppressErrors: true,\n });\n\n let filesScanned = 0;\n let filesMatched = 0;\n let matchingLines = 0;\n let skippedTooLarge = 0;\n const counters = { skippedInaccessible: 0, stoppedByAbort: false };\n\n for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {\n if (matches.length >= maxResults) break;\n\n // Skip oversized files before reading to avoid unbounded memory use. Count\n // them: \"no matches\" for a reason other than the pattern must be visible.\n try {\n const stats = await fsStat(entry.path);\n if (stats.size > maxFileSize) {\n skippedTooLarge++;\n continue;\n }\n } catch {\n counters.skippedInaccessible++;\n continue;\n }\n\n filesScanned++;\n\n try {\n const content = await readFile(entry.path, { encoding: 'utf-8', signal: options.signal });\n const lines = content.split('\\n');\n let matchedFile = false;\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line === undefined) continue;\n // One scan per line: findLineMatches resets lastIndex itself, so it\n // doubles as the \"does this line match\" test.\n const found = findLineMatches(regex, line);\n if (found) {\n matchedFile = true;\n matchingLines++;\n matches.push({\n file: entry.path,\n line: i + 1,\n column: found.column,\n content: line,\n matchCount: found.count,\n });\n if (matches.length >= maxResults) break;\n }\n }\n if (matchedFile) filesMatched++;\n } catch {\n // A read failure while the signal is aborted IS the abort, not an\n // unreadable file — stop rather than spend another iteration and then\n // report a cut-short scan as complete.\n if (options.signal?.aborted) {\n counters.stoppedByAbort = true;\n break;\n }\n // ignore read errors (e.g. binary files)\n }\n }\n\n const tracker = new StopReasonTracker();\n if (matches.length >= maxResults) tracker.hitMaxResults();\n if (counters.stoppedByAbort) tracker.hitAbort();\n // resolve() is StoppedReason | undefined, but this scan only records\n // maxResults/timeout stops (see the summary type's narrowing note).\n const stoppedReason = tracker.resolve() as 'maxResults' | 'timeout' | undefined;\n\n return {\n basePath: directory,\n matches,\n summary: {\n matchingLines,\n filesScanned,\n filesMatched,\n truncated: tracker.truncated,\n skippedInaccessible: counters.skippedInaccessible,\n skippedTooLarge,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n } finally {\n if (precompiled === undefined) freeRegex(regex);\n }\n}\n\nasync function* guardedEntries(\n entries: AsyncIterable<GlobEntry>,\n pathGuard: PathGuard,\n signal: AbortSignal | undefined,\n counters: { skippedInaccessible: number; stoppedByAbort: boolean },\n): AsyncGenerator<GlobEntry> {\n // The signal carries both client cancellation and the tool's search timeout,\n // so an abort means \"return what we have, marked incomplete\" rather than\n // throw — but it must never be reported as a finished scan.\n for await (const entry of entries) {\n if (signal?.aborted) {\n counters.stoppedByAbort = true;\n return;\n }\n try {\n await pathGuard.validateExistingPath(entry.path);\n } catch {\n counters.skippedInaccessible++;\n continue;\n }\n yield entry;\n }\n}\n\nexport async function searchFiles(\n directory: string,\n pattern: string,\n excludePatterns: string[],\n options: {\n maxResults?: number;\n includeHidden?: boolean;\n sortBy?: 'name' | 'path';\n respectGitignore?: boolean;\n maxDepth?: number;\n signal?: AbortSignal;\n },\n pathGuard: PathGuard,\n): Promise<{\n basePath: string;\n results: { path: string }[];\n summary: {\n matched: number;\n filesScanned: number;\n truncated: boolean;\n skippedInaccessible: number;\n /** Narrowed like SearchContentOutcome's — scans never hit `hitMaxFiles`. */\n stoppedReason?: 'maxResults' | 'timeout';\n };\n}> {\n const maxResults = options.maxResults ?? 100;\n const entries = globEntries({\n cwd: directory,\n pattern,\n excludePatterns,\n includeHidden: Boolean(options.includeHidden),\n respectGitignore: Boolean(options.respectGitignore),\n maxDepth: options.maxDepth ?? 100,\n suppressErrors: true,\n });\n const results: { path: string }[] = [];\n let filesScanned = 0;\n const counters = { skippedInaccessible: 0, stoppedByAbort: false };\n\n for await (const entry of guardedEntries(entries, pathGuard, options.signal, counters)) {\n if (results.length >= maxResults) break;\n filesScanned++;\n results.push({ path: entry.path });\n }\n\n // Sorting — only name / path are supported; size / modified were removed\n // (the glob never collected stats, so they were always undefined).\n if (options.sortBy === 'name') {\n results.sort((a, b) => basename(a.path).localeCompare(basename(b.path)));\n } else {\n results.sort((a, b) => a.path.localeCompare(b.path));\n }\n\n const tracker = new StopReasonTracker();\n if (results.length >= maxResults) tracker.hitMaxResults();\n if (counters.stoppedByAbort) tracker.hitAbort();\n // resolve() is StoppedReason | undefined, but this scan only records\n // maxResults/timeout stops (see the summary type's narrowing note).\n const stoppedReason = tracker.resolve() as 'maxResults' | 'timeout' | undefined;\n\n return {\n basePath: directory,\n results,\n summary: {\n matched: results.length,\n filesScanned,\n truncated: tracker.truncated,\n skippedInaccessible: counters.skippedInaccessible,\n ...(stoppedReason ? { stoppedReason } : {}),\n },\n };\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"sensitive.d.ts","sourceRoot":"","sources":["../../src/core/sensitive.ts"],"names":[],"mappings":"AA+JA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAElC,QAAQ,GAAE,SAAS,MAAM,EAA6B;IAIlE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAWvC"}
1
+ {"version":3,"file":"sensitive.d.ts","sourceRoot":"","sources":["../../src/core/sensitive.ts"],"names":[],"mappings":"AAiJA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAqB;gBAElC,QAAQ,GAAE,SAAS,MAAM,EAA6B;IAIlE,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAWvC"}
@@ -30,23 +30,15 @@ function compilePatternGlobs(normalizedPattern) {
30
30
  }
31
31
  return Array.from(globs);
32
32
  }
33
- function compilePatterns(patterns) {
34
- const deduped = Array.from(new Set(patterns.map((p) => p.trim()).filter((p) => p.length > 0)));
35
- return deduped.map((pattern) => {
36
- const normalized = normalizeForMatch(pattern);
37
- const matchesPath = normalized.includes('/');
38
- return {
39
- globs: matchesPath ? compilePatternGlobs(normalized) : [normalized],
40
- matchesPath,
41
- };
42
- });
43
- }
44
33
  function toPatternSet(patterns) {
45
34
  const pathGlobs = new Set();
46
35
  const nameGlobs = new Set();
47
- for (const pattern of patterns) {
48
- const target = pattern.matchesPath ? pathGlobs : nameGlobs;
49
- for (const glob of pattern.globs) {
36
+ const deduped = Array.from(new Set(patterns.map((p) => p.trim()).filter((p) => p.length > 0)));
37
+ for (const pattern of deduped) {
38
+ const normalized = normalizeForMatch(pattern);
39
+ const matchesPath = normalized.includes('/');
40
+ const target = matchesPath ? pathGlobs : nameGlobs;
41
+ for (const glob of matchesPath ? compilePatternGlobs(normalized) : [normalized]) {
50
42
  target.add(glob);
51
43
  }
52
44
  }
@@ -127,7 +119,7 @@ function buildSensitivePatterns() {
127
119
  export class SensitiveMatcher {
128
120
  patterns;
129
121
  constructor(patterns = buildSensitivePatterns()) {
130
- this.patterns = toPatternSet(compilePatterns(patterns));
122
+ this.patterns = toPatternSet(patterns);
131
123
  }
132
124
  isSensitive(filePath) {
133
125
  if (this.patterns.pathGlobs.length === 0 && this.patterns.nameGlobs.length === 0) {