@j0hanz/filesystem-mcp 1.19.0 → 2.0.0
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.
- package/README.md +333 -838
- package/dist/cli-help.d.ts +3 -0
- package/dist/cli-help.d.ts.map +1 -0
- package/dist/cli-help.js +169 -0
- package/dist/cli-help.js.map +1 -0
- package/dist/cli.d.ts +28 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +168 -123
- package/dist/cli.js.map +1 -0
- package/dist/core/concurrency.d.ts +70 -0
- package/dist/core/concurrency.d.ts.map +1 -0
- package/dist/core/concurrency.js +173 -0
- package/dist/core/concurrency.js.map +1 -0
- package/dist/core/config.d.ts +19 -0
- package/dist/core/config.d.ts.map +1 -0
- package/dist/core/config.js +11 -0
- package/dist/core/config.js.map +1 -0
- package/dist/core/cursor.d.ts +23 -0
- package/dist/core/cursor.d.ts.map +1 -0
- package/dist/core/cursor.js +62 -0
- package/dist/core/cursor.js.map +1 -0
- package/dist/core/errors.d.ts +96 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +356 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/file-uri.d.ts +24 -0
- package/dist/core/file-uri.d.ts.map +1 -0
- package/dist/core/file-uri.js +52 -0
- package/dist/core/file-uri.js.map +1 -0
- package/dist/core/fmt.d.ts +38 -0
- package/dist/core/fmt.d.ts.map +1 -0
- package/dist/core/fmt.js +109 -0
- package/dist/core/fmt.js.map +1 -0
- package/dist/core/fs.d.ts +108 -0
- package/dist/core/fs.d.ts.map +1 -0
- package/dist/core/fs.js +243 -0
- package/dist/core/fs.js.map +1 -0
- package/dist/core/glob.d.ts +41 -0
- package/dist/core/glob.d.ts.map +1 -0
- package/dist/core/glob.js +399 -0
- package/dist/core/glob.js.map +1 -0
- package/dist/core/input-required.d.ts +147 -0
- package/dist/core/input-required.d.ts.map +1 -0
- package/dist/core/input-required.js +261 -0
- package/dist/core/input-required.js.map +1 -0
- package/dist/core/mime.d.ts +15 -0
- package/dist/core/mime.d.ts.map +1 -0
- package/dist/core/mime.js +188 -0
- package/dist/core/mime.js.map +1 -0
- package/dist/core/observability.d.ts +10 -0
- package/dist/core/observability.d.ts.map +1 -0
- package/dist/core/observability.js +76 -0
- package/dist/core/observability.js.map +1 -0
- package/dist/core/page-store.d.ts +41 -0
- package/dist/core/page-store.d.ts.map +1 -0
- package/dist/core/page-store.js +74 -0
- package/dist/core/page-store.js.map +1 -0
- package/dist/core/path-completer.d.ts +10 -0
- package/dist/core/path-completer.d.ts.map +1 -0
- package/dist/core/path-completer.js +227 -0
- package/dist/core/path-completer.js.map +1 -0
- package/dist/core/path-discovery.d.ts +8 -0
- package/dist/core/path-discovery.d.ts.map +1 -0
- package/dist/core/path-discovery.js +105 -0
- package/dist/core/path-discovery.js.map +1 -0
- package/dist/core/path-utils.d.ts +9 -0
- package/dist/core/path-utils.d.ts.map +1 -0
- package/dist/core/path-utils.js +121 -0
- package/dist/core/path-utils.js.map +1 -0
- package/dist/core/path.d.ts +148 -0
- package/dist/core/path.d.ts.map +1 -0
- package/dist/core/path.js +674 -0
- package/dist/core/path.js.map +1 -0
- package/dist/core/primitives.d.ts +23 -0
- package/dist/core/primitives.d.ts.map +1 -0
- package/dist/core/primitives.js +54 -0
- package/dist/core/primitives.js.map +1 -0
- package/dist/core/read.d.ts +74 -0
- package/dist/core/read.d.ts.map +1 -0
- package/dist/core/read.js +562 -0
- package/dist/core/read.js.map +1 -0
- package/dist/core/schema.d.ts +139 -0
- package/dist/core/schema.d.ts.map +1 -0
- package/dist/core/schema.js +345 -0
- package/dist/core/schema.js.map +1 -0
- package/dist/core/search.d.ts +100 -0
- package/dist/core/search.d.ts.map +1 -0
- package/dist/core/search.js +248 -0
- package/dist/core/search.js.map +1 -0
- package/dist/core/sensitive.d.ts +6 -0
- package/dist/core/sensitive.d.ts.map +1 -0
- package/dist/core/sensitive.js +142 -0
- package/dist/core/sensitive.js.map +1 -0
- package/dist/core/store.d.ts +57 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +162 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/util.d.ts +26 -0
- package/dist/core/util.d.ts.map +1 -0
- package/dist/core/util.js +66 -0
- package/dist/core/util.js.map +1 -0
- package/dist/core/watcher-registry.d.ts +68 -0
- package/dist/core/watcher-registry.d.ts.map +1 -0
- package/dist/core/watcher-registry.js +312 -0
- package/dist/core/watcher-registry.js.map +1 -0
- package/dist/http-policy.d.ts +121 -0
- package/dist/http-policy.d.ts.map +1 -0
- package/dist/http-policy.js +340 -0
- package/dist/http-policy.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -41
- package/dist/index.js.map +1 -0
- package/dist/instructions.d.ts +12 -0
- package/dist/instructions.d.ts.map +1 -0
- package/dist/instructions.js +71 -0
- package/dist/instructions.js.map +1 -0
- package/dist/prompts.d.ts +8 -6
- package/dist/prompts.d.ts.map +1 -0
- package/dist/prompts.js +74 -168
- package/dist/prompts.js.map +1 -0
- package/dist/resources.d.ts +73 -9
- package/dist/resources.d.ts.map +1 -0
- package/dist/resources.js +392 -163
- package/dist/resources.js.map +1 -0
- package/dist/server.d.ts +62 -1
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +139 -1
- package/dist/server.js.map +1 -0
- package/dist/tools/batch.d.ts +89 -0
- package/dist/tools/batch.d.ts.map +1 -0
- package/dist/tools/batch.js +157 -0
- package/dist/tools/batch.js.map +1 -0
- package/dist/tools/create.d.ts +2 -0
- package/dist/tools/create.d.ts.map +1 -0
- package/dist/tools/create.js +142 -0
- package/dist/tools/create.js.map +1 -0
- package/dist/tools/define.d.ts +110 -0
- package/dist/tools/define.d.ts.map +1 -0
- package/dist/tools/define.js +394 -0
- package/dist/tools/define.js.map +1 -0
- package/dist/tools/delete-file.d.ts +2 -4
- package/dist/tools/delete-file.d.ts.map +1 -0
- package/dist/tools/delete-file.js +330 -87
- package/dist/tools/delete-file.js.map +1 -0
- package/dist/tools/diff.d.ts +2 -0
- package/dist/tools/diff.d.ts.map +1 -0
- package/dist/tools/diff.js +94 -0
- package/dist/tools/diff.js.map +1 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.d.ts.map +1 -0
- package/dist/tools/edit.js +442 -0
- package/dist/tools/edit.js.map +1 -0
- package/dist/tools/index.d.ts +26 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +52 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/tools/list.d.ts +2 -0
- package/dist/tools/list.d.ts.map +1 -0
- package/dist/tools/list.js +291 -0
- package/dist/tools/list.js.map +1 -0
- package/dist/tools/move.d.ts +2 -0
- package/dist/tools/move.d.ts.map +1 -0
- package/dist/tools/move.js +321 -0
- package/dist/tools/move.js.map +1 -0
- package/dist/tools/patch.d.ts +2 -0
- package/dist/tools/patch.d.ts.map +1 -0
- package/dist/tools/patch.js +155 -0
- package/dist/tools/patch.js.map +1 -0
- package/dist/tools/progress.d.ts +52 -0
- package/dist/tools/progress.d.ts.map +1 -0
- package/dist/tools/progress.js +172 -0
- package/dist/tools/progress.js.map +1 -0
- package/dist/tools/read.d.ts +2 -4
- package/dist/tools/read.d.ts.map +1 -0
- package/dist/tools/read.js +398 -152
- package/dist/tools/read.js.map +1 -0
- package/dist/tools/replace-in-files.d.ts +2 -4
- package/dist/tools/replace-in-files.d.ts.map +1 -0
- package/dist/tools/replace-in-files.js +435 -264
- package/dist/tools/replace-in-files.js.map +1 -0
- package/dist/tools/roots.d.ts +2 -4
- package/dist/tools/roots.d.ts.map +1 -0
- package/dist/tools/roots.js +29 -52
- package/dist/tools/roots.js.map +1 -0
- package/dist/tools/search-content.d.ts +2 -4
- package/dist/tools/search-content.d.ts.map +1 -0
- package/dist/tools/search-content.js +262 -228
- package/dist/tools/search-content.js.map +1 -0
- package/dist/tools/search-files.d.ts +2 -4
- package/dist/tools/search-files.d.ts.map +1 -0
- package/dist/tools/search-files.js +162 -161
- package/dist/tools/search-files.js.map +1 -0
- package/dist/tools/stat.d.ts +2 -4
- package/dist/tools/stat.d.ts.map +1 -0
- package/dist/tools/stat.js +202 -62
- package/dist/tools/stat.js.map +1 -0
- package/dist/transport/http.d.ts +5 -0
- package/dist/transport/http.d.ts.map +1 -0
- package/dist/transport/http.js +276 -0
- package/dist/transport/http.js.map +1 -0
- package/dist/transport/shared.d.ts +45 -0
- package/dist/transport/shared.d.ts.map +1 -0
- package/dist/transport/shared.js +65 -0
- package/dist/transport/shared.js.map +1 -0
- package/dist/transport/stdio.d.ts +32 -0
- package/dist/transport/stdio.d.ts.map +1 -0
- package/dist/transport/stdio.js +279 -0
- package/dist/transport/stdio.js.map +1 -0
- package/dist/transport.d.ts +5 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +4 -0
- package/dist/transport.js.map +1 -0
- package/package.json +37 -46
- package/dist/assets/logo.svg +0 -35
- package/dist/completions.d.ts +0 -2
- package/dist/completions.js +0 -572
- package/dist/config.d.ts +0 -119
- package/dist/config.js +0 -31
- package/dist/lib/abort.d.ts +0 -7
- package/dist/lib/abort.js +0 -81
- package/dist/lib/constants.d.ts +0 -42
- package/dist/lib/constants.js +0 -313
- package/dist/lib/errors.d.ts +0 -28
- package/dist/lib/errors.js +0 -301
- package/dist/lib/file-operations/core.d.ts +0 -53
- package/dist/lib/file-operations/core.js +0 -144
- package/dist/lib/file-operations/metadata.d.ts +0 -77
- package/dist/lib/file-operations/metadata.js +0 -878
- package/dist/lib/file-operations/search.d.ts +0 -46
- package/dist/lib/file-operations/search.js +0 -1194
- package/dist/lib/file-operations/traversal.d.ts +0 -40
- package/dist/lib/file-operations/traversal.js +0 -310
- package/dist/lib/fs-helpers.d.ts +0 -46
- package/dist/lib/fs-helpers.js +0 -606
- package/dist/lib/globs.d.ts +0 -2
- package/dist/lib/globs.js +0 -19
- package/dist/lib/logger.d.ts +0 -27
- package/dist/lib/logger.js +0 -91
- package/dist/lib/observability.d.ts +0 -33
- package/dist/lib/observability.js +0 -373
- package/dist/lib/paths.d.ts +0 -35
- package/dist/lib/paths.js +0 -634
- package/dist/lib/resource-store.d.ts +0 -28
- package/dist/lib/resource-store.js +0 -195
- package/dist/lib/utils.d.ts +0 -7
- package/dist/lib/utils.js +0 -37
- package/dist/lib/zod-codecs.d.ts +0 -2
- package/dist/lib/zod-codecs.js +0 -18
- package/dist/pkg-info.d.ts +0 -7
- package/dist/pkg-info.js +0 -9
- package/dist/resources/generated-instructions.d.ts +0 -1
- package/dist/resources/generated-instructions.js +0 -105
- package/dist/resources/tool-catalog.d.ts +0 -2
- package/dist/resources/tool-catalog.js +0 -78
- package/dist/resources/tool-info.d.ts +0 -9
- package/dist/resources/tool-info.js +0 -206
- package/dist/resources/workflows.d.ts +0 -1
- package/dist/resources/workflows.js +0 -72
- package/dist/schemas.d.ts +0 -505
- package/dist/schemas.js +0 -676
- package/dist/server/bootstrap.d.ts +0 -6
- package/dist/server/bootstrap.js +0 -563
- package/dist/server/event-store.d.ts +0 -18
- package/dist/server/event-store.js +0 -71
- package/dist/server/roots-manager.d.ts +0 -28
- package/dist/server/roots-manager.js +0 -227
- package/dist/server/task-store.d.ts +0 -10
- package/dist/server/task-store.js +0 -91
- package/dist/tools/apply-patch.d.ts +0 -4
- package/dist/tools/apply-patch.js +0 -235
- package/dist/tools/calculate-hash.d.ts +0 -4
- package/dist/tools/calculate-hash.js +0 -187
- package/dist/tools/contract.d.ts +0 -50
- package/dist/tools/contract.js +0 -1
- package/dist/tools/create-directory.d.ts +0 -4
- package/dist/tools/create-directory.js +0 -68
- package/dist/tools/diff-files.d.ts +0 -4
- package/dist/tools/diff-files.js +0 -136
- package/dist/tools/edit-file.d.ts +0 -4
- package/dist/tools/edit-file.js +0 -256
- package/dist/tools/icons.d.ts +0 -15
- package/dist/tools/icons.js +0 -24
- package/dist/tools/list-directory.d.ts +0 -4
- package/dist/tools/list-directory.js +0 -218
- package/dist/tools/move-file.d.ts +0 -4
- package/dist/tools/move-file.js +0 -178
- package/dist/tools/read-multiple.d.ts +0 -4
- package/dist/tools/read-multiple.js +0 -207
- package/dist/tools/shared.d.ts +0 -172
- package/dist/tools/shared.js +0 -599
- package/dist/tools/stat-many.d.ts +0 -4
- package/dist/tools/stat-many.js +0 -104
- package/dist/tools/task-support.d.ts +0 -29
- package/dist/tools/task-support.js +0 -472
- package/dist/tools/tree.d.ts +0 -4
- package/dist/tools/tree.js +0 -102
- package/dist/tools/write-file.d.ts +0 -4
- package/dist/tools/write-file.js +0 -60
- package/dist/tools.d.ts +0 -5
- package/dist/tools.js +0 -54
|
@@ -1,121 +1,121 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
3
|
-
import
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import { basename, dirname } from 'node:path';
|
|
3
|
+
import * as z from 'zod/v4';
|
|
4
|
+
import { SearchStoppedReasonSchema } from '../core/concurrency.js';
|
|
5
|
+
import { createFirstPage, readNextPage } from '../core/cursor.js';
|
|
6
|
+
import { ErrorCode, FsError } from '../core/errors.js';
|
|
7
|
+
import { formatCount, truncateProgressPattern } from '../core/fmt.js';
|
|
8
|
+
import { DEFAULT_EXCLUDE_PATTERNS } from '../core/glob.js';
|
|
9
|
+
import { toPosixRelative } from '../core/path.js';
|
|
10
|
+
import { escapeRegexLiteral } from '../core/primitives.js';
|
|
11
|
+
import { CursorSchema, defaultFalseBoolean, includeHiddenField, includeIgnoredField, isBlank, maxDepthField, NextCursorSchema, NonNegInt, OptionalPath, PositiveInt, SafeGlobPattern, } from '../core/schema.js';
|
|
12
|
+
import { compileRegex, freeRegex, searchContent } from '../core/search.js';
|
|
13
|
+
import { putJsonResource } from '../core/store.js';
|
|
14
|
+
import { DEFAULT_SEARCH_CONTENT_RESULTS, DEFAULT_SEARCH_TIMEOUT_MS, MAX_SEARCH_RESULTS, parseEnvInt, } from '../core/util.js';
|
|
15
|
+
import { defineTool } from './define.js';
|
|
11
16
|
/**
|
|
12
17
|
* Configuration constants for the Search Content tool.
|
|
13
18
|
*/
|
|
14
19
|
const CONFIG = {
|
|
15
|
-
MAX_INLINE_MATCHES:
|
|
16
|
-
COMPLETION_LABELS: {
|
|
17
|
-
timeout: 'timeout',
|
|
18
|
-
maxResults: 'max results',
|
|
19
|
-
maxFiles: 'max files',
|
|
20
|
-
},
|
|
20
|
+
MAX_INLINE_MATCHES: parseEnvInt('FS_MAX_INLINE_MATCHES', 50, 1, 10_000),
|
|
21
21
|
};
|
|
22
|
-
const TRUTHY_SUMMARY_FIELDS = [
|
|
23
|
-
'filesMatched',
|
|
24
|
-
'skippedTooLarge',
|
|
25
|
-
'skippedBinary',
|
|
26
|
-
'skippedInaccessible',
|
|
27
|
-
];
|
|
28
|
-
function buildStructuredSummaryFields(summary) {
|
|
29
|
-
const result = {};
|
|
30
|
-
for (const key of TRUTHY_SUMMARY_FIELDS) {
|
|
31
|
-
const value = summary[key];
|
|
32
|
-
if (value) {
|
|
33
|
-
result[key] = value;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
if (summary.truncated) {
|
|
37
|
-
result.truncated = true;
|
|
38
|
-
}
|
|
39
|
-
if (summary.stoppedReason) {
|
|
40
|
-
result.stoppedReason = summary.stoppedReason;
|
|
41
|
-
}
|
|
42
|
-
return result;
|
|
43
|
-
}
|
|
44
|
-
function buildCompletionSuffix(count, filesMatched, scope, stoppedReason) {
|
|
45
|
-
if (count === 0)
|
|
46
|
-
return `No matches in ${scope}`;
|
|
47
|
-
const matchWord = count === 1 ? 'match' : 'matches';
|
|
48
|
-
const fileWord = filesMatched === 1 ? 'file' : 'files';
|
|
49
|
-
const reasonSuffix = stoppedReason !== undefined
|
|
50
|
-
? ` [${CONFIG.COMPLETION_LABELS[stoppedReason]}]`
|
|
51
|
-
: '';
|
|
52
|
-
return `${count} ${matchWord} in ${filesMatched} ${fileWord}${reasonSuffix}`;
|
|
53
|
-
}
|
|
54
22
|
function buildSearchPreviewState(payloads) {
|
|
55
23
|
const needsExternalize = payloads.length > CONFIG.MAX_INLINE_MATCHES;
|
|
56
|
-
const visibleCount = needsExternalize
|
|
57
|
-
? CONFIG.MAX_INLINE_MATCHES
|
|
58
|
-
: payloads.length;
|
|
24
|
+
const visibleCount = needsExternalize ? CONFIG.MAX_INLINE_MATCHES : payloads.length;
|
|
59
25
|
return {
|
|
60
26
|
needsExternalize,
|
|
61
27
|
visiblePayloads: payloads.slice(0, visibleCount),
|
|
62
|
-
heading: buildHeading(payloads.length, visibleCount),
|
|
63
28
|
};
|
|
64
29
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
30
|
+
const GrepInputSchema = z.strictObject({
|
|
31
|
+
path: OptionalPath.describe('File to search, or directory to search under (default: the whole first allowed root). Naming a file searches that file alone: pattern is ignored and hidden/ignored filtering does not apply.'),
|
|
32
|
+
pattern: SafeGlobPattern.optional().describe('Glob to restrict search to specific file types (e.g. **/*.ts); default: all text files'),
|
|
33
|
+
searchPattern: z
|
|
34
|
+
.string()
|
|
35
|
+
.min(1)
|
|
36
|
+
.max(10000)
|
|
37
|
+
.refine((val) => !isBlank(val), {
|
|
38
|
+
message: 'searchPattern cannot be empty or whitespace-only',
|
|
39
|
+
})
|
|
40
|
+
.describe('Exact literal text or RE2 regex pattern to search for in file contents. When isRegex=true, uses RE2 syntax (no lookahead, lookbehind, or backreferences). Cannot be empty or whitespace-only.')
|
|
41
|
+
.meta({ examples: ['TODO', 'function\\s+(\\w+)', 'import.*from'] }),
|
|
42
|
+
isRegex: defaultFalseBoolean('Treat searchPattern as a regex (default: literal text match)'),
|
|
43
|
+
includeHidden: includeHiddenField(),
|
|
44
|
+
includeIgnored: includeIgnoredField(),
|
|
45
|
+
caseSensitive: defaultFalseBoolean('Enable case-sensitive matching (default: case-insensitive)'),
|
|
46
|
+
maxResults: z
|
|
47
|
+
.uint32()
|
|
48
|
+
.min(1)
|
|
49
|
+
.max(MAX_SEARCH_RESULTS)
|
|
50
|
+
.optional()
|
|
51
|
+
.default(DEFAULT_SEARCH_CONTENT_RESULTS)
|
|
52
|
+
.describe('Maximum number of matching lines to return per page'),
|
|
53
|
+
maxDepth: maxDepthField(),
|
|
54
|
+
cursor: CursorSchema,
|
|
55
|
+
});
|
|
56
|
+
const GrepOutputSchema = z.strictObject({
|
|
57
|
+
matches: z
|
|
58
|
+
.array(z.strictObject({
|
|
59
|
+
file: z.string().describe('File path relative to the search root'),
|
|
60
|
+
line: PositiveInt.describe('1-indexed line number of the match'),
|
|
61
|
+
column: NonNegInt.optional().describe('0-indexed column offset of the match start'),
|
|
62
|
+
content: z.string().describe('Full text of the matching line'),
|
|
63
|
+
matchCount: NonNegInt.optional().describe('Number of pattern occurrences on this line'),
|
|
64
|
+
}))
|
|
65
|
+
.describe('Flat list of matches sorted by file path then line number'),
|
|
66
|
+
totalMatches: NonNegInt.optional().describe("Total matching lines, one per entry in matches (not per occurrence); see that entry's matchCount for occurrences on a line."),
|
|
67
|
+
filesMatched: NonNegInt.optional().describe('Number of files containing at least one match'),
|
|
68
|
+
filesScanned: NonNegInt.optional().describe('Total number of files examined'),
|
|
69
|
+
skippedInaccessible: NonNegInt.optional().describe('Files skipped unread due to permission or access errors'),
|
|
70
|
+
skippedTooLarge: NonNegInt.optional().describe('Files skipped unread because they exceed the text-file size limit; raise the limit or narrow pattern if a match was expected in one'),
|
|
71
|
+
truncated: z
|
|
72
|
+
.boolean()
|
|
73
|
+
.optional()
|
|
74
|
+
.describe('True when the match list was cut due to maxResults or timeout'),
|
|
75
|
+
stoppedReason: SearchStoppedReasonSchema.describe('Why the search ended early: maxResults = result cap reached, timeout = time limit hit or the request was cancelled. Absent when the scan ran to completion.'),
|
|
76
|
+
resourceUri: z
|
|
77
|
+
.string()
|
|
78
|
+
.optional()
|
|
79
|
+
.describe('URI to the full match list in the resource store (present when matches exceed the inline limit)'),
|
|
80
|
+
nextCursor: NextCursorSchema,
|
|
81
|
+
});
|
|
82
|
+
function buildSearchMatchDetail(totalMatches, filesMatched) {
|
|
83
|
+
const matchDetail = formatCount(totalMatches, 'match', 'matches');
|
|
84
|
+
if (filesMatched <= 0)
|
|
85
|
+
return matchDetail;
|
|
86
|
+
return `${matchDetail} · ${formatCount(filesMatched, 'file', 'files')}`;
|
|
102
87
|
}
|
|
103
|
-
function
|
|
88
|
+
function searchContentOutput(matches, metadata) {
|
|
104
89
|
return {
|
|
105
|
-
ok: true,
|
|
106
90
|
matches,
|
|
107
|
-
totalMatches:
|
|
108
|
-
filesScanned:
|
|
109
|
-
...
|
|
91
|
+
totalMatches: metadata.totalMatches,
|
|
92
|
+
filesScanned: metadata.filesScanned,
|
|
93
|
+
...(metadata.filesMatched ? { filesMatched: metadata.filesMatched } : {}),
|
|
94
|
+
...(metadata.truncated ? { truncated: true } : {}),
|
|
95
|
+
...(metadata.stoppedReason !== undefined ? { stoppedReason: metadata.stoppedReason } : {}),
|
|
96
|
+
...(metadata.skippedInaccessible ? { skippedInaccessible: metadata.skippedInaccessible } : {}),
|
|
97
|
+
...(metadata.skippedTooLarge ? { skippedTooLarge: metadata.skippedTooLarge } : {}),
|
|
110
98
|
};
|
|
111
99
|
}
|
|
100
|
+
function findColumnOffset(content, context) {
|
|
101
|
+
if (context.matcher) {
|
|
102
|
+
context.matcher.lastIndex = 0;
|
|
103
|
+
const match = context.matcher.exec(content);
|
|
104
|
+
return match ? match.index : undefined;
|
|
105
|
+
}
|
|
106
|
+
if (context.caseSensitive) {
|
|
107
|
+
const idx = content.indexOf(context.pattern);
|
|
108
|
+
return idx >= 0 ? idx : undefined;
|
|
109
|
+
}
|
|
110
|
+
return undefined;
|
|
111
|
+
}
|
|
112
112
|
function buildSortedPayloads(result, context) {
|
|
113
113
|
const relativeByFile = new Map();
|
|
114
114
|
const getRelativeFile = (file) => {
|
|
115
115
|
const cached = relativeByFile.get(file);
|
|
116
116
|
if (cached !== undefined)
|
|
117
117
|
return cached;
|
|
118
|
-
const rel =
|
|
118
|
+
const rel = toPosixRelative(result.basePath, file);
|
|
119
119
|
relativeByFile.set(file, rel);
|
|
120
120
|
return rel;
|
|
121
121
|
};
|
|
@@ -128,10 +128,6 @@ function buildSortedPayloads(result, context) {
|
|
|
128
128
|
...(column !== undefined ? { column } : {}),
|
|
129
129
|
content: match.content,
|
|
130
130
|
matchCount: match.matchCount,
|
|
131
|
-
...(match.contextBefore
|
|
132
|
-
? { contextBefore: [...match.contextBefore] }
|
|
133
|
-
: {}),
|
|
134
|
-
...(match.contextAfter ? { contextAfter: [...match.contextAfter] } : {}),
|
|
135
131
|
};
|
|
136
132
|
return { payload, originalIndex };
|
|
137
133
|
});
|
|
@@ -145,154 +141,192 @@ function buildSortedPayloads(result, context) {
|
|
|
145
141
|
});
|
|
146
142
|
return payloads.map((p) => p.payload);
|
|
147
143
|
}
|
|
148
|
-
function
|
|
149
|
-
|
|
150
|
-
return heading;
|
|
151
|
-
const parts = [heading];
|
|
152
|
-
for (const match of matches) {
|
|
153
|
-
parts.push(`\n ${match.file}:${String(match.line).padStart(4)}: ${match.content}`);
|
|
154
|
-
}
|
|
155
|
-
return parts.join('');
|
|
156
|
-
}
|
|
157
|
-
function resolveTruncatedReason(summary) {
|
|
158
|
-
if (summary.stoppedReason === 'timeout')
|
|
159
|
-
return 'timeout';
|
|
160
|
-
if (summary.stoppedReason === 'maxFiles') {
|
|
161
|
-
return `max files (${summary.filesScanned})`;
|
|
162
|
-
}
|
|
163
|
-
return `max results (${summary.matches})`;
|
|
164
|
-
}
|
|
165
|
-
function findColumnOffset(content, context) {
|
|
166
|
-
try {
|
|
167
|
-
if (context.matcher) {
|
|
168
|
-
context.matcher.lastIndex = 0;
|
|
169
|
-
const match = context.matcher.exec(content);
|
|
170
|
-
return match ? match.index : undefined;
|
|
171
|
-
}
|
|
172
|
-
if (context.caseSensitive) {
|
|
173
|
-
const idx = content.indexOf(context.pattern);
|
|
174
|
-
return idx >= 0 ? idx : undefined;
|
|
175
|
-
}
|
|
176
|
-
// Case-insensitive literal search
|
|
177
|
-
const idx = content.toLowerCase().indexOf(context.foldedPattern ?? '');
|
|
178
|
-
return idx >= 0 ? idx : undefined;
|
|
179
|
-
}
|
|
180
|
-
catch {
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
}
|
|
184
|
-
async function executeSearch(args, basePath, signal, onProgress) {
|
|
185
|
-
const excludePatterns = args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS;
|
|
186
|
-
const options = {
|
|
144
|
+
function buildSearchContentOptions(args, signal) {
|
|
145
|
+
return {
|
|
187
146
|
includeHidden: args.includeHidden,
|
|
188
|
-
excludePatterns,
|
|
189
|
-
filePattern: args.
|
|
147
|
+
excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,
|
|
148
|
+
filePattern: args.pattern ?? '**/*',
|
|
190
149
|
caseSensitive: args.caseSensitive,
|
|
191
|
-
|
|
192
|
-
contextLines: args.contextLines,
|
|
150
|
+
isRegex: args.isRegex,
|
|
193
151
|
maxResults: args.maxResults,
|
|
194
|
-
|
|
152
|
+
respectGitignore: !args.includeIgnored,
|
|
153
|
+
...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),
|
|
195
154
|
...(signal ? { signal } : {}),
|
|
196
|
-
...(onProgress ? { onProgress } : {}),
|
|
197
155
|
};
|
|
198
|
-
try {
|
|
199
|
-
return await searchContent(basePath, args.pattern, options);
|
|
200
|
-
}
|
|
201
|
-
catch (error) {
|
|
202
|
-
if (error instanceof Error && /regular expression/i.test(error.message)) {
|
|
203
|
-
throw new McpError(ErrorCode.INVALID_PATTERN, `Invalid regex pattern: ${formatUnknownErrorMessage(error)} (RE2: no lookahead/lookbehind/backrefs)`);
|
|
204
|
-
}
|
|
205
|
-
throw error;
|
|
206
|
-
}
|
|
207
156
|
}
|
|
208
157
|
function createSearchMatcher(args) {
|
|
209
|
-
if (
|
|
210
|
-
return
|
|
211
|
-
try {
|
|
212
|
-
const flags = args.caseSensitive ? '' : 'i';
|
|
213
|
-
return new RE2(args.pattern, flags);
|
|
158
|
+
if (args.isRegex) {
|
|
159
|
+
return compileRegex(args.searchPattern, { caseSensitive: args.caseSensitive });
|
|
214
160
|
}
|
|
215
|
-
|
|
216
|
-
|
|
161
|
+
if (!args.caseSensitive) {
|
|
162
|
+
return compileRegex(escapeRegexLiteral(args.searchPattern), { caseSensitive: false });
|
|
217
163
|
}
|
|
164
|
+
return undefined;
|
|
218
165
|
}
|
|
219
166
|
function createSearchContext(args, matcher) {
|
|
220
167
|
return {
|
|
221
|
-
pattern: args.
|
|
168
|
+
pattern: args.searchPattern,
|
|
222
169
|
caseSensitive: args.caseSensitive,
|
|
223
170
|
...(matcher ? { matcher } : {}),
|
|
224
|
-
...(!args.isRegex && !args.caseSensitive
|
|
225
|
-
? { foldedPattern: args.pattern.toLowerCase() }
|
|
226
|
-
: {}),
|
|
227
171
|
};
|
|
228
172
|
}
|
|
229
|
-
|
|
230
|
-
const
|
|
231
|
-
const
|
|
232
|
-
|
|
173
|
+
function buildExternalizedResponse(fullStructured, preview, resourceStore, searchPattern) {
|
|
174
|
+
const { entry, link } = putJsonResource(resourceStore, `'${searchPattern}' matches`, fullStructured);
|
|
175
|
+
const structuredForResponse = {
|
|
176
|
+
...fullStructured,
|
|
177
|
+
resourceUri: entry.uri,
|
|
178
|
+
};
|
|
179
|
+
if (preview.needsExternalize) {
|
|
180
|
+
structuredForResponse.matches = preview.visiblePayloads;
|
|
181
|
+
structuredForResponse.truncated = true;
|
|
182
|
+
}
|
|
183
|
+
return { structured: structuredForResponse, link };
|
|
184
|
+
}
|
|
185
|
+
function sortedPayloads(result, args, regexMatcher) {
|
|
233
186
|
const searchContext = createSearchContext(args, regexMatcher);
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
187
|
+
return buildSortedPayloads(result, searchContext);
|
|
188
|
+
}
|
|
189
|
+
function finalizeSearchOutput(fullStructured, preview, resourceStore, searchPattern) {
|
|
237
190
|
if (resourceStore && preview.needsExternalize) {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
const entry = resourceStore.putText({
|
|
244
|
-
name: 'grep:matches',
|
|
245
|
-
mimeType: 'application/json',
|
|
246
|
-
text: JSON.stringify(fullStructured),
|
|
247
|
-
});
|
|
248
|
-
previewStructured.resourceUri = entry.uri;
|
|
249
|
-
const text = buildSearchText(preview.heading, preview.visiblePayloads);
|
|
250
|
-
return buildToolResponse(text, previewStructured, [
|
|
251
|
-
buildResourceLink({
|
|
252
|
-
uri: entry.uri,
|
|
253
|
-
name: entry.name,
|
|
254
|
-
mimeType: entry.mimeType,
|
|
255
|
-
description: 'Full grep results as JSON (structuredContent)',
|
|
256
|
-
expiresAt: entry.expiresAt,
|
|
257
|
-
}),
|
|
258
|
-
]);
|
|
191
|
+
return buildExternalizedResponse(fullStructured, preview, resourceStore, searchPattern ?? '');
|
|
192
|
+
}
|
|
193
|
+
if (preview.needsExternalize) {
|
|
194
|
+
fullStructured.matches = preview.visiblePayloads;
|
|
195
|
+
fullStructured.truncated = true;
|
|
259
196
|
}
|
|
260
|
-
|
|
261
|
-
return buildToolResponse(text, fullStructured);
|
|
197
|
+
return { structured: fullStructured };
|
|
262
198
|
}
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
199
|
+
/**
|
|
200
|
+
* `path` is documented as "file to search, or directory to search under", but
|
|
201
|
+
* the scan itself only walks directories. A file path is therefore rewritten
|
|
202
|
+
* into the scan it means: its parent as the base, its own name as the glob.
|
|
203
|
+
* Naming a file is an explicit request for that file, so hidden/ignored
|
|
204
|
+
* filtering is lifted for it — otherwise `search_text` on a path the caller can
|
|
205
|
+
* see returned nothing.
|
|
206
|
+
*/
|
|
207
|
+
async function resolveSearchScope(args, ctx) {
|
|
208
|
+
const requested = ctx.fs.pathGuard.resolvePathOrRoot(args.path);
|
|
209
|
+
// One resolution, one stat: validateExistingDirectory would redo both.
|
|
210
|
+
const resolved = await ctx.fs.pathGuard.validateExistingPath(requested);
|
|
211
|
+
const stats = await stat(resolved);
|
|
212
|
+
if (!stats.isFile()) {
|
|
213
|
+
if (!stats.isDirectory()) {
|
|
214
|
+
throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);
|
|
215
|
+
}
|
|
216
|
+
return { basePath: resolved, args };
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
basePath: dirname(resolved),
|
|
220
|
+
// ponytail: the name goes through as a glob, so a file whose name contains
|
|
221
|
+
// glob metacharacters (`[id].ts`) matches as a pattern rather than
|
|
222
|
+
// literally. Escape it here if that ever bites.
|
|
223
|
+
args: { ...args, pattern: basename(resolved), includeHidden: true, includeIgnored: true },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
function searchContentQueryKey(args, requestedPath) {
|
|
227
|
+
return JSON.stringify({
|
|
228
|
+
method: 'search_text',
|
|
229
|
+
path: requestedPath,
|
|
230
|
+
pattern: args.pattern,
|
|
231
|
+
searchPattern: args.searchPattern,
|
|
232
|
+
isRegex: args.isRegex,
|
|
233
|
+
includeHidden: args.includeHidden,
|
|
234
|
+
includeIgnored: args.includeIgnored,
|
|
235
|
+
caseSensitive: args.caseSensitive,
|
|
236
|
+
maxDepth: args.maxDepth,
|
|
296
237
|
});
|
|
297
|
-
registerStandardTool(server, SEARCH_CONTENT_TOOL, handler, options);
|
|
298
238
|
}
|
|
239
|
+
async function handleSearchContent(args, ctx) {
|
|
240
|
+
const requestedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);
|
|
241
|
+
const queryKey = searchContentQueryKey(args, requestedPath);
|
|
242
|
+
if (args.cursor !== undefined) {
|
|
243
|
+
const paged = readNextPage({
|
|
244
|
+
store: ctx.pageStore,
|
|
245
|
+
queryKey,
|
|
246
|
+
cursor: args.cursor,
|
|
247
|
+
pageSize: args.maxResults,
|
|
248
|
+
});
|
|
249
|
+
const fullStructured = searchContentOutput([...paged.page], paged.metadata);
|
|
250
|
+
if (paged.nextCursor !== undefined)
|
|
251
|
+
fullStructured.nextCursor = paged.nextCursor;
|
|
252
|
+
const preview = buildSearchPreviewState([...paged.page]);
|
|
253
|
+
return finalizeSearchOutput(fullStructured, preview, ctx.resourceStore, args.searchPattern);
|
|
254
|
+
}
|
|
255
|
+
const { basePath, args: scoped } = await resolveSearchScope(args, ctx);
|
|
256
|
+
const regexMatcher = createSearchMatcher(scoped);
|
|
257
|
+
const result = await searchContent(basePath, scoped.searchPattern, buildSearchContentOptions({ ...scoped, maxResults: MAX_SEARCH_RESULTS }, ctx.signal), ctx.fs.pathGuard);
|
|
258
|
+
// regexMatcher holds wasm memory re2-wasm never reclaims on its own.
|
|
259
|
+
let allMatchPayloads;
|
|
260
|
+
try {
|
|
261
|
+
allMatchPayloads = sortedPayloads(result, scoped, regexMatcher);
|
|
262
|
+
}
|
|
263
|
+
finally {
|
|
264
|
+
freeRegex(regexMatcher);
|
|
265
|
+
}
|
|
266
|
+
const metadata = {
|
|
267
|
+
totalMatches: result.summary.matchingLines,
|
|
268
|
+
filesScanned: result.summary.filesScanned,
|
|
269
|
+
...(result.summary.filesMatched ? { filesMatched: result.summary.filesMatched } : {}),
|
|
270
|
+
truncated: result.summary.truncated,
|
|
271
|
+
...(result.summary.stoppedReason !== undefined && result.summary.stoppedReason !== 'maxFiles'
|
|
272
|
+
? { stoppedReason: result.summary.stoppedReason }
|
|
273
|
+
: {}),
|
|
274
|
+
...(result.summary.skippedInaccessible
|
|
275
|
+
? { skippedInaccessible: result.summary.skippedInaccessible }
|
|
276
|
+
: {}),
|
|
277
|
+
...(result.summary.skippedTooLarge ? { skippedTooLarge: result.summary.skippedTooLarge } : {}),
|
|
278
|
+
};
|
|
279
|
+
const paged = createFirstPage({
|
|
280
|
+
store: ctx.pageStore,
|
|
281
|
+
queryKey,
|
|
282
|
+
items: allMatchPayloads,
|
|
283
|
+
metadata,
|
|
284
|
+
pageSize: scoped.maxResults,
|
|
285
|
+
});
|
|
286
|
+
const fullStructured = searchContentOutput([...paged.page], metadata);
|
|
287
|
+
if (paged.nextCursor !== undefined)
|
|
288
|
+
fullStructured.nextCursor = paged.nextCursor;
|
|
289
|
+
const preview = buildSearchPreviewState([...paged.page]);
|
|
290
|
+
const { structured, link } = finalizeSearchOutput(fullStructured, preview, ctx.resourceStore, args.searchPattern);
|
|
291
|
+
return {
|
|
292
|
+
structured,
|
|
293
|
+
...(link !== undefined ? { link } : {}),
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
export const SEARCH_CONTENT = defineTool({
|
|
297
|
+
name: 'search_text',
|
|
298
|
+
title: 'Search Content',
|
|
299
|
+
description: 'Search file contents by text or regex (grep-style). Returns matching lines with file path, ' +
|
|
300
|
+
'1-indexed line number and 0-indexed column offset. ' +
|
|
301
|
+
'Scope to specific file types with pattern (e.g. **/*.ts). ' +
|
|
302
|
+
'Set includeHidden=true to include dotfiles. Use find_files to search by filename instead.',
|
|
303
|
+
input: GrepInputSchema,
|
|
304
|
+
output: GrepOutputSchema,
|
|
305
|
+
annotations: {
|
|
306
|
+
readOnlyHint: true,
|
|
307
|
+
idempotentHint: true,
|
|
308
|
+
destructiveHint: false,
|
|
309
|
+
openWorldHint: false,
|
|
310
|
+
},
|
|
311
|
+
timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,
|
|
312
|
+
defaultErrorCode: ErrorCode.UNKNOWN,
|
|
313
|
+
progress: (args) => ({
|
|
314
|
+
label: 'Search',
|
|
315
|
+
subject: truncateProgressPattern(args.searchPattern),
|
|
316
|
+
}),
|
|
317
|
+
progressDone: (_args, result) => ({
|
|
318
|
+
detail: buildSearchMatchDetail(result.totalMatches ?? 0, result.filesMatched ?? 0),
|
|
319
|
+
}),
|
|
320
|
+
accessPaths: (args) => (args.path ? [args.path] : []),
|
|
321
|
+
run: async (args, ctx) => {
|
|
322
|
+
const { structured, link } = await handleSearchContent(args, ctx);
|
|
323
|
+
const text = structured.matches.length > 0
|
|
324
|
+
? structured.matches.map((m) => `${m.file}:${String(m.line)}: ${m.content}`).join('\n')
|
|
325
|
+
: `No matches for '${args.searchPattern}'`;
|
|
326
|
+
if (link) {
|
|
327
|
+
return { structured, text, resources: [link] };
|
|
328
|
+
}
|
|
329
|
+
return { structured, text };
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
//# sourceMappingURL=search-content.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-content.js","sourceRoot":"","sources":["../../src/tools/search-content.ts"],"names":[],"mappings":"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","sourcesContent":["import { stat } from 'node:fs/promises';\nimport { basename, dirname } from 'node:path';\n\nimport * as z from 'zod/v4';\n\nimport { SearchStoppedReasonSchema } from '../core/concurrency.js';\nimport { createFirstPage, readNextPage } from '../core/cursor.js';\nimport { ErrorCode, FsError } from '../core/errors.js';\nimport { formatCount, truncateProgressPattern } from '../core/fmt.js';\nimport { DEFAULT_EXCLUDE_PATTERNS } from '../core/glob.js';\nimport { toPosixRelative } from '../core/path.js';\nimport { escapeRegexLiteral } from '../core/primitives.js';\nimport {\n CursorSchema,\n defaultFalseBoolean,\n includeHiddenField,\n includeIgnoredField,\n isBlank,\n maxDepthField,\n NextCursorSchema,\n NonNegInt,\n OptionalPath,\n PositiveInt,\n SafeGlobPattern,\n} from '../core/schema.js';\nimport type { Regex, SearchContentOptions } from '../core/search.js';\nimport { compileRegex, freeRegex, searchContent } from '../core/search.js';\nimport type { ResourceStore } from '../core/store.js';\nimport { putJsonResource } from '../core/store.js';\nimport {\n DEFAULT_SEARCH_CONTENT_RESULTS,\n DEFAULT_SEARCH_TIMEOUT_MS,\n MAX_SEARCH_RESULTS,\n parseEnvInt,\n} from '../core/util.js';\nimport { defineTool, type ToolCtx } from './define.js';\n\n/**\n * Configuration constants for the Search Content tool.\n */\nconst CONFIG = {\n MAX_INLINE_MATCHES: parseEnvInt('FS_MAX_INLINE_MATCHES', 50, 1, 10_000),\n} as const;\n\n// Type Definitions\ntype SearchInput = z.infer<typeof GrepInputSchema>;\ntype SearchOutput = z.infer<typeof GrepOutputSchema>;\ntype SearchMatchPayload = NonNullable<SearchOutput['matches']>[number];\ntype SearchResultValue = Awaited<ReturnType<typeof searchContent>>;\n\ninterface SearchPreviewState {\n needsExternalize: boolean;\n visiblePayloads: SearchMatchPayload[];\n}\n\ninterface SearchContext {\n pattern: string;\n matcher?: Regex;\n caseSensitive: boolean;\n}\n\ninterface SearchContentPageMetadata {\n readonly totalMatches: number;\n readonly filesScanned: number;\n readonly filesMatched?: number;\n readonly truncated: boolean;\n readonly stoppedReason?: SearchOutput['stoppedReason'];\n readonly skippedInaccessible?: number;\n readonly skippedTooLarge?: number;\n}\n\nfunction buildSearchPreviewState(payloads: SearchMatchPayload[]): SearchPreviewState {\n const needsExternalize = payloads.length > CONFIG.MAX_INLINE_MATCHES;\n const visibleCount = needsExternalize ? CONFIG.MAX_INLINE_MATCHES : payloads.length;\n\n return {\n needsExternalize,\n visiblePayloads: payloads.slice(0, visibleCount),\n };\n}\n\nconst GrepInputSchema = z.strictObject({\n path: OptionalPath.describe(\n 'File to search, or directory to search under (default: the whole first allowed root). Naming a file searches that file alone: pattern is ignored and hidden/ignored filtering does not apply.',\n ),\n pattern: SafeGlobPattern.optional().describe(\n 'Glob to restrict search to specific file types (e.g. **/*.ts); default: all text files',\n ),\n searchPattern: z\n .string()\n .min(1)\n .max(10000)\n .refine((val) => !isBlank(val), {\n message: 'searchPattern cannot be empty or whitespace-only',\n })\n .describe(\n 'Exact literal text or RE2 regex pattern to search for in file contents. When isRegex=true, uses RE2 syntax (no lookahead, lookbehind, or backreferences). Cannot be empty or whitespace-only.',\n )\n .meta({ examples: ['TODO', 'function\\\\s+(\\\\w+)', 'import.*from'] }),\n isRegex: defaultFalseBoolean('Treat searchPattern as a regex (default: literal text match)'),\n includeHidden: includeHiddenField(),\n includeIgnored: includeIgnoredField(),\n caseSensitive: defaultFalseBoolean('Enable case-sensitive matching (default: case-insensitive)'),\n maxResults: z\n .uint32()\n .min(1)\n .max(MAX_SEARCH_RESULTS)\n .optional()\n .default(DEFAULT_SEARCH_CONTENT_RESULTS)\n .describe('Maximum number of matching lines to return per page'),\n maxDepth: maxDepthField(),\n cursor: CursorSchema,\n});\n\nconst GrepOutputSchema = z.strictObject({\n matches: z\n .array(\n z.strictObject({\n file: z.string().describe('File path relative to the search root'),\n line: PositiveInt.describe('1-indexed line number of the match'),\n column: NonNegInt.optional().describe('0-indexed column offset of the match start'),\n content: z.string().describe('Full text of the matching line'),\n matchCount: NonNegInt.optional().describe('Number of pattern occurrences on this line'),\n }),\n )\n .describe('Flat list of matches sorted by file path then line number'),\n totalMatches: NonNegInt.optional().describe(\n \"Total matching lines, one per entry in matches (not per occurrence); see that entry's matchCount for occurrences on a line.\",\n ),\n filesMatched: NonNegInt.optional().describe('Number of files containing at least one match'),\n filesScanned: NonNegInt.optional().describe('Total number of files examined'),\n skippedInaccessible: NonNegInt.optional().describe(\n 'Files skipped unread due to permission or access errors',\n ),\n skippedTooLarge: NonNegInt.optional().describe(\n 'Files skipped unread because they exceed the text-file size limit; raise the limit or narrow pattern if a match was expected in one',\n ),\n truncated: z\n .boolean()\n .optional()\n .describe('True when the match list was cut due to maxResults or timeout'),\n stoppedReason: SearchStoppedReasonSchema.describe(\n 'Why the search ended early: maxResults = result cap reached, timeout = time limit hit or the request was cancelled. Absent when the scan ran to completion.',\n ),\n resourceUri: z\n .string()\n .optional()\n .describe(\n 'URI to the full match list in the resource store (present when matches exceed the inline limit)',\n ),\n nextCursor: NextCursorSchema,\n});\n\nfunction buildSearchMatchDetail(totalMatches: number, filesMatched: number): string {\n const matchDetail = formatCount(totalMatches, 'match', 'matches');\n if (filesMatched <= 0) return matchDetail;\n return `${matchDetail} · ${formatCount(filesMatched, 'file', 'files')}`;\n}\n\nfunction searchContentOutput(\n matches: SearchMatchPayload[],\n metadata: SearchContentPageMetadata,\n): SearchOutput {\n return {\n matches,\n totalMatches: metadata.totalMatches,\n filesScanned: metadata.filesScanned,\n ...(metadata.filesMatched ? { filesMatched: metadata.filesMatched } : {}),\n ...(metadata.truncated ? { truncated: true } : {}),\n ...(metadata.stoppedReason !== undefined ? { stoppedReason: metadata.stoppedReason } : {}),\n ...(metadata.skippedInaccessible ? { skippedInaccessible: metadata.skippedInaccessible } : {}),\n ...(metadata.skippedTooLarge ? { skippedTooLarge: metadata.skippedTooLarge } : {}),\n };\n}\n\nfunction findColumnOffset(content: string, context: SearchContext): number | undefined {\n if (context.matcher) {\n context.matcher.lastIndex = 0;\n const match = context.matcher.exec(content);\n return match ? match.index : undefined;\n }\n if (context.caseSensitive) {\n const idx = content.indexOf(context.pattern);\n return idx >= 0 ? idx : undefined;\n }\n return undefined;\n}\n\nfunction buildSortedPayloads(\n result: SearchResultValue,\n context: SearchContext,\n): SearchMatchPayload[] {\n const relativeByFile = new Map<string, string>();\n\n const getRelativeFile = (file: string): string => {\n const cached = relativeByFile.get(file);\n if (cached !== undefined) return cached;\n\n const rel = toPosixRelative(result.basePath, file);\n relativeByFile.set(file, rel);\n return rel;\n };\n\n const payloads = result.matches.map((match, originalIndex) => {\n const relFile = getRelativeFile(match.file);\n const column = findColumnOffset(match.content, context);\n\n const payload: SearchMatchPayload = {\n file: relFile,\n line: match.line,\n ...(column !== undefined ? { column } : {}),\n content: match.content,\n matchCount: match.matchCount,\n };\n\n return { payload, originalIndex };\n });\n\n payloads.sort((left, right) => {\n const fileCompare = left.payload.file.localeCompare(right.payload.file);\n if (fileCompare !== 0) return fileCompare;\n if (left.payload.line !== right.payload.line) return left.payload.line - right.payload.line;\n return left.originalIndex - right.originalIndex;\n });\n\n return payloads.map((p) => p.payload);\n}\n\nfunction buildSearchContentOptions(args: SearchInput, signal?: AbortSignal): SearchContentOptions {\n return {\n includeHidden: args.includeHidden,\n excludePatterns: args.includeIgnored ? [] : DEFAULT_EXCLUDE_PATTERNS,\n filePattern: args.pattern ?? '**/*',\n caseSensitive: args.caseSensitive,\n isRegex: args.isRegex,\n maxResults: args.maxResults,\n respectGitignore: !args.includeIgnored,\n ...(args.maxDepth !== undefined ? { maxDepth: args.maxDepth } : {}),\n ...(signal ? { signal } : {}),\n };\n}\n\nfunction createSearchMatcher(args: SearchInput): Regex | undefined {\n if (args.isRegex) {\n return compileRegex(args.searchPattern, { caseSensitive: args.caseSensitive });\n }\n if (!args.caseSensitive) {\n return compileRegex(escapeRegexLiteral(args.searchPattern), { caseSensitive: false });\n }\n return undefined;\n}\n\nfunction createSearchContext(args: SearchInput, matcher: Regex | undefined): SearchContext {\n return {\n pattern: args.searchPattern,\n caseSensitive: args.caseSensitive,\n ...(matcher ? { matcher } : {}),\n };\n}\n\nfunction buildExternalizedResponse(\n fullStructured: SearchOutput,\n preview: SearchPreviewState,\n resourceStore: ResourceStore,\n searchPattern: string,\n): { structured: SearchOutput; link: ReturnType<typeof putJsonResource>['link'] } {\n const { entry, link } = putJsonResource(\n resourceStore,\n `'${searchPattern}' matches`,\n fullStructured,\n );\n\n const structuredForResponse: SearchOutput = {\n ...fullStructured,\n resourceUri: entry.uri,\n };\n\n if (preview.needsExternalize) {\n structuredForResponse.matches = preview.visiblePayloads;\n structuredForResponse.truncated = true;\n }\n\n return { structured: structuredForResponse, link };\n}\n\nfunction sortedPayloads(\n result: SearchResultValue,\n args: SearchInput,\n regexMatcher: Regex | undefined,\n): SearchMatchPayload[] {\n const searchContext = createSearchContext(args, regexMatcher);\n return buildSortedPayloads(result, searchContext);\n}\n\nfunction finalizeSearchOutput(\n fullStructured: SearchOutput,\n preview: SearchPreviewState,\n resourceStore?: ResourceStore,\n searchPattern?: string,\n): { structured: SearchOutput; link?: ReturnType<typeof putJsonResource>['link'] } {\n if (resourceStore && preview.needsExternalize) {\n return buildExternalizedResponse(fullStructured, preview, resourceStore, searchPattern ?? '');\n }\n\n if (preview.needsExternalize) {\n fullStructured.matches = preview.visiblePayloads;\n fullStructured.truncated = true;\n }\n\n return { structured: fullStructured };\n}\n\n/**\n * `path` is documented as \"file to search, or directory to search under\", but\n * the scan itself only walks directories. A file path is therefore rewritten\n * into the scan it means: its parent as the base, its own name as the glob.\n * Naming a file is an explicit request for that file, so hidden/ignored\n * filtering is lifted for it — otherwise `search_text` on a path the caller can\n * see returned nothing.\n */\nasync function resolveSearchScope(\n args: SearchInput,\n ctx: ToolCtx,\n): Promise<{ basePath: string; args: SearchInput }> {\n const requested = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n // One resolution, one stat: validateExistingDirectory would redo both.\n const resolved = await ctx.fs.pathGuard.validateExistingPath(requested);\n const stats = await stat(resolved);\n if (!stats.isFile()) {\n if (!stats.isDirectory()) {\n throw new FsError(ErrorCode.NOT_DIRECTORY, 'Not a directory', requested);\n }\n return { basePath: resolved, args };\n }\n return {\n basePath: dirname(resolved),\n // ponytail: the name goes through as a glob, so a file whose name contains\n // glob metacharacters (`[id].ts`) matches as a pattern rather than\n // literally. Escape it here if that ever bites.\n args: { ...args, pattern: basename(resolved), includeHidden: true, includeIgnored: true },\n };\n}\n\nfunction searchContentQueryKey(args: SearchInput, requestedPath: string): string {\n return JSON.stringify({\n method: 'search_text',\n path: requestedPath,\n pattern: args.pattern,\n searchPattern: args.searchPattern,\n isRegex: args.isRegex,\n includeHidden: args.includeHidden,\n includeIgnored: args.includeIgnored,\n caseSensitive: args.caseSensitive,\n maxDepth: args.maxDepth,\n });\n}\n\nasync function handleSearchContent(\n args: SearchInput,\n ctx: ToolCtx,\n): Promise<{\n structured: SearchOutput;\n link?: ReturnType<typeof putJsonResource>['link'];\n}> {\n const requestedPath = ctx.fs.pathGuard.resolvePathOrRoot(args.path);\n const queryKey = searchContentQueryKey(args, requestedPath);\n\n if (args.cursor !== undefined) {\n const paged = readNextPage<SearchMatchPayload, SearchContentPageMetadata>({\n store: ctx.pageStore,\n queryKey,\n cursor: args.cursor,\n pageSize: args.maxResults,\n });\n const fullStructured = searchContentOutput([...paged.page], paged.metadata);\n if (paged.nextCursor !== undefined) fullStructured.nextCursor = paged.nextCursor;\n const preview = buildSearchPreviewState([...paged.page]);\n return finalizeSearchOutput(fullStructured, preview, ctx.resourceStore, args.searchPattern);\n }\n\n const { basePath, args: scoped } = await resolveSearchScope(args, ctx);\n const regexMatcher = createSearchMatcher(scoped);\n\n const result = await searchContent(\n basePath,\n scoped.searchPattern,\n buildSearchContentOptions({ ...scoped, maxResults: MAX_SEARCH_RESULTS }, ctx.signal),\n ctx.fs.pathGuard,\n );\n\n // regexMatcher holds wasm memory re2-wasm never reclaims on its own.\n let allMatchPayloads: SearchMatchPayload[];\n try {\n allMatchPayloads = sortedPayloads(result, scoped, regexMatcher);\n } finally {\n freeRegex(regexMatcher);\n }\n const metadata: SearchContentPageMetadata = {\n totalMatches: result.summary.matchingLines,\n filesScanned: result.summary.filesScanned,\n ...(result.summary.filesMatched ? { filesMatched: result.summary.filesMatched } : {}),\n truncated: result.summary.truncated,\n ...(result.summary.stoppedReason !== undefined && result.summary.stoppedReason !== 'maxFiles'\n ? { stoppedReason: result.summary.stoppedReason }\n : {}),\n ...(result.summary.skippedInaccessible\n ? { skippedInaccessible: result.summary.skippedInaccessible }\n : {}),\n ...(result.summary.skippedTooLarge ? { skippedTooLarge: result.summary.skippedTooLarge } : {}),\n };\n const paged = createFirstPage<SearchMatchPayload, SearchContentPageMetadata>({\n store: ctx.pageStore,\n queryKey,\n items: allMatchPayloads,\n metadata,\n pageSize: scoped.maxResults,\n });\n const fullStructured = searchContentOutput([...paged.page], metadata);\n if (paged.nextCursor !== undefined) fullStructured.nextCursor = paged.nextCursor;\n const preview = buildSearchPreviewState([...paged.page]);\n const { structured, link } = finalizeSearchOutput(\n fullStructured,\n preview,\n ctx.resourceStore,\n args.searchPattern,\n );\n\n return {\n structured,\n ...(link !== undefined ? { link } : {}),\n };\n}\n\nexport const SEARCH_CONTENT = defineTool({\n name: 'search_text',\n title: 'Search Content',\n description:\n 'Search file contents by text or regex (grep-style). Returns matching lines with file path, ' +\n '1-indexed line number and 0-indexed column offset. ' +\n 'Scope to specific file types with pattern (e.g. **/*.ts). ' +\n 'Set includeHidden=true to include dotfiles. Use find_files to search by filename instead.',\n input: GrepInputSchema,\n output: GrepOutputSchema,\n annotations: {\n readOnlyHint: true,\n idempotentHint: true,\n destructiveHint: false,\n openWorldHint: false,\n },\n timeoutMs: DEFAULT_SEARCH_TIMEOUT_MS,\n defaultErrorCode: ErrorCode.UNKNOWN,\n progress: (args) => ({\n label: 'Search',\n subject: truncateProgressPattern(args.searchPattern),\n }),\n progressDone: (_args, result) => ({\n detail: buildSearchMatchDetail(result.totalMatches ?? 0, result.filesMatched ?? 0),\n }),\n accessPaths: (args) => (args.path ? [args.path] : []),\n run: async (args, ctx) => {\n const { structured, link } = await handleSearchContent(args, ctx);\n const text =\n structured.matches.length > 0\n ? structured.matches.map((m) => `${m.file}:${String(m.line)}: ${m.content}`).join('\\n')\n : `No matches for '${args.searchPattern}'`;\n if (link) {\n return { structured, text, resources: [link] };\n }\n return { structured, text };\n },\n});\n"]}
|
|
@@ -1,4 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
export declare const SEARCH_FILES_TOOL: ToolContract;
|
|
4
|
-
export declare function registerSearchFilesTool(server: McpServer, options?: ToolRegistrationOptions): void;
|
|
1
|
+
export declare const SEARCH_FILES: import("./define.js").DefinedTool;
|
|
2
|
+
//# sourceMappingURL=search-files.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"search-files.d.ts","sourceRoot":"","sources":["../../src/tools/search-files.ts"],"names":[],"mappings":"AAuNA,eAAO,MAAM,YAAY,mCAoCvB,CAAC"}
|