@j0hanz/filesystem-mcp 1.8.0 → 1.9.1
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 +635 -377
- package/dist/cli.js +2 -2
- package/dist/completions.js +2 -3
- package/dist/index.js +2 -2
- package/dist/lib/file-operations/{common.d.ts → core.d.ts} +6 -0
- package/dist/lib/file-operations/{common.js → core.js} +45 -0
- package/dist/lib/file-operations/metadata.d.ts +73 -0
- package/dist/lib/file-operations/metadata.js +889 -0
- package/dist/lib/file-operations/{search-content.d.ts → search.d.ts} +40 -7
- package/dist/lib/file-operations/{search-content.js → search.js} +418 -20
- package/dist/lib/file-operations/{glob-engine.d.ts → traversal.d.ts} +18 -1
- package/dist/lib/file-operations/{glob-engine.js → traversal.js} +25 -2
- package/dist/lib/fs-helpers.js +1 -2
- package/dist/lib/observability.js +1 -1
- package/dist/lib/{path-validation.d.ts → paths.d.ts} +11 -0
- package/dist/lib/{path-validation.js → paths.js} +145 -12
- package/dist/lib/utils.d.ts +15 -0
- package/dist/lib/utils.js +33 -0
- package/dist/resources/generated-instructions.js +6 -6
- package/dist/resources/tool-catalog.js +9 -9
- package/dist/resources/tool-info.js +3 -3
- package/dist/resources/workflows.js +10 -23
- package/dist/schemas.d.ts +11 -11
- package/dist/schemas.js +18 -31
- package/dist/server/bootstrap.d.ts +19 -1
- package/dist/server/bootstrap.js +130 -44
- package/dist/server/roots-manager.d.ts +5 -2
- package/dist/server/roots-manager.js +17 -5
- package/dist/tools/apply-patch.js +4 -3
- package/dist/tools/calculate-hash.js +4 -4
- package/dist/tools/create-directory.js +1 -1
- package/dist/tools/delete-file.js +3 -3
- package/dist/tools/diff-files.js +2 -2
- package/dist/tools/edit-file.js +14 -13
- package/dist/tools/list-directory.js +5 -7
- package/dist/tools/move-file.js +2 -1
- package/dist/tools/read-multiple.js +3 -4
- package/dist/tools/read.js +3 -3
- package/dist/tools/replace-in-files.js +8 -10
- package/dist/tools/roots.js +4 -6
- package/dist/tools/search-content.js +7 -9
- package/dist/tools/search-files.js +4 -6
- package/dist/tools/shared.d.ts +2 -1
- package/dist/tools/shared.js +2 -1
- package/dist/tools/stat-many.js +4 -3
- package/dist/tools/stat.js +4 -3
- package/dist/tools/task-support.js +1 -1
- package/dist/tools/tree.js +3 -4
- package/dist/tools/write-file.js +1 -1
- package/package.json +4 -4
- package/dist/lib/file-operations/file-info.d.ts +0 -10
- package/dist/lib/file-operations/file-info.js +0 -143
- package/dist/lib/file-operations/gitignore.d.ts +0 -6
- package/dist/lib/file-operations/gitignore.js +0 -45
- package/dist/lib/file-operations/glob-helpers.d.ts +0 -18
- package/dist/lib/file-operations/glob-helpers.js +0 -23
- package/dist/lib/file-operations/list-directory.d.ts +0 -14
- package/dist/lib/file-operations/list-directory.js +0 -252
- package/dist/lib/file-operations/read-multiple-files.d.ts +0 -25
- package/dist/lib/file-operations/read-multiple-files.js +0 -252
- package/dist/lib/file-operations/search-files.d.ts +0 -27
- package/dist/lib/file-operations/search-files.js +0 -216
- package/dist/lib/file-operations/search-matcher.d.ts +0 -10
- package/dist/lib/file-operations/search-matcher.js +0 -72
- package/dist/lib/file-operations/search-worker.d.ts +0 -2
- package/dist/lib/file-operations/search-worker.js +0 -131
- package/dist/lib/file-operations/tree.d.ts +0 -28
- package/dist/lib/file-operations/tree.js +0 -265
- package/dist/lib/option-utils.d.ts +0 -3
- package/dist/lib/option-utils.js +0 -15
- package/dist/lib/path-format.d.ts +0 -1
- package/dist/lib/path-format.js +0 -7
- package/dist/lib/path-policy.d.ts +0 -2
- package/dist/lib/path-policy.js +0 -100
- package/dist/lib/progress-reporting.d.ts +0 -11
- package/dist/lib/progress-reporting.js +0 -13
- package/dist/lib/type-guards.d.ts +0 -1
- package/dist/lib/type-guards.js +0 -3
- package/dist/server/capabilities.d.ts +0 -10
- package/dist/server/capabilities.js +0 -48
- package/dist/server/logging.d.ts +0 -7
- package/dist/server/logging.js +0 -41
- package/dist/server/types.d.ts +0 -4
- package/dist/server/types.js +0 -1
package/dist/tools/roots.js
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import { joinLines } from '../config.js';
|
|
2
1
|
import { ErrorCode } from '../lib/errors.js';
|
|
3
|
-
import { getAllowedDirectories } from '../lib/
|
|
2
|
+
import { getAllowedDirectories } from '../lib/paths.js';
|
|
3
|
+
import { joinLines } from '../config.js';
|
|
4
4
|
import { ListAllowedDirectoriesInputSchema, ListAllowedDirectoriesOutputSchema, } from '../schemas.js';
|
|
5
5
|
import { buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
6
6
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
7
7
|
export const LIST_ALLOWED_DIRECTORIES_TOOL = {
|
|
8
8
|
name: 'roots',
|
|
9
9
|
title: 'Workspace Roots',
|
|
10
|
-
description: 'List
|
|
11
|
-
'Call this first to see available directories. ' +
|
|
12
|
-
'All other tools only work within these directories.',
|
|
10
|
+
description: 'List allowed workspace roots. Call first \u2014 all other tools are scoped to these directories.',
|
|
13
11
|
inputSchema: ListAllowedDirectoriesInputSchema,
|
|
14
12
|
outputSchema: ListAllowedDirectoriesOutputSchema,
|
|
15
13
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
16
|
-
nuances: ['
|
|
14
|
+
nuances: ['Returns absolute paths of all allowed directories.'],
|
|
17
15
|
};
|
|
18
16
|
function buildTextRoots(dirs) {
|
|
19
17
|
if (dirs.length === 0) {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import RE2 from 're2';
|
|
3
|
-
import { formatOperationSummary, joinLines } from '../config.js';
|
|
4
3
|
import { DEFAULT_EXCLUDE_PATTERNS } from '../lib/constants.js';
|
|
5
4
|
import { ErrorCode, formatUnknownErrorMessage, McpError, } from '../lib/errors.js';
|
|
6
|
-
import { searchContent } from '../lib/file-operations/search
|
|
5
|
+
import { searchContent } from '../lib/file-operations/search.js';
|
|
6
|
+
import { formatOperationSummary, joinLines } from '../config.js';
|
|
7
7
|
import { SearchContentInputSchema, SearchContentOutputSchema, } from '../schemas.js';
|
|
8
8
|
import { buildResourceLink, buildToolErrorResponse, buildToolResponse, createToolProgressSession, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, resolveFinalProgressCurrent, resolvePathOrRoot, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
9
9
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
@@ -11,20 +11,18 @@ const MAX_INLINE_MATCHES = parseInt(process.env['FS_CONTEXT_MAX_INLINE_MATCHES']
|
|
|
11
11
|
export const SEARCH_CONTENT_TOOL = {
|
|
12
12
|
name: 'grep',
|
|
13
13
|
title: 'Search Content',
|
|
14
|
-
description: 'Search for text
|
|
15
|
-
'
|
|
16
|
-
'
|
|
17
|
-
'Use `filePattern` to scope by file type (e.g. `**/*.ts`) and avoid noisy results. ' +
|
|
18
|
-
'Use includeHidden=true to include hidden files and directories.',
|
|
14
|
+
description: 'Search file contents for text (grep-like). Returns matching lines. ' +
|
|
15
|
+
'Scope with `filePattern` (e.g. `**/*.ts`) to reduce noise. ' +
|
|
16
|
+
'`includeHidden=true` for dotfiles.',
|
|
19
17
|
inputSchema: SearchContentInputSchema,
|
|
20
18
|
outputSchema: SearchContentOutputSchema,
|
|
21
19
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
22
20
|
nuances: [
|
|
23
|
-
'Inline
|
|
21
|
+
'Inline results capped at 50 matches; full results via `resourceUri`.',
|
|
24
22
|
'Skips binary and oversized files.',
|
|
25
23
|
],
|
|
26
24
|
gotchas: [
|
|
27
|
-
'Skips binary
|
|
25
|
+
'Skips binary/oversized files silently — verify with `stat` if no matches.',
|
|
28
26
|
],
|
|
29
27
|
taskSupport: 'optional',
|
|
30
28
|
};
|
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
|
-
import { formatOperationSummary, joinLines } from '../config.js';
|
|
3
2
|
import { DEFAULT_EXCLUDE_PATTERNS, DEFAULT_SEARCH_TIMEOUT_MS, } from '../lib/constants.js';
|
|
4
3
|
import { ErrorCode } from '../lib/errors.js';
|
|
5
|
-
import { searchFiles } from '../lib/file-operations/search
|
|
4
|
+
import { searchFiles } from '../lib/file-operations/search.js';
|
|
5
|
+
import { formatOperationSummary, joinLines } from '../config.js';
|
|
6
6
|
import { SearchFilesInputSchema, SearchFilesOutputSchema } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, createProgressReporter, decodeOffsetCursor, encodeOffsetCursor, executeToolWithDiagnostics, notifyProgress, READ_ONLY_TOOL_ANNOTATIONS, resolvePathOrRoot, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
8
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
9
9
|
export const SEARCH_FILES_TOOL = {
|
|
10
10
|
name: 'find',
|
|
11
11
|
title: 'Find Files',
|
|
12
|
-
description: 'Find files by glob pattern (e.g
|
|
13
|
-
'
|
|
14
|
-
'For text search inside files, use grep. ' +
|
|
15
|
-
'To bulk-edit the matched files, pass the same glob pattern to search_and_replace.',
|
|
12
|
+
description: 'Find files by glob pattern (e.g. `**/*.ts`). Returns matching files with metadata. ' +
|
|
13
|
+
'For content search, use `grep`. For bulk edits, pass the same glob to `search_and_replace`.',
|
|
16
14
|
inputSchema: SearchFilesInputSchema,
|
|
17
15
|
outputSchema: SearchFilesOutputSchema,
|
|
18
16
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ContentBlock, Icon, ProgressNotificationParams } from '@modelcontextprotocol/sdk/types.js';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import type { FileInfo } from '../config.js';
|
|
4
3
|
import { ErrorCode } from '../lib/errors.js';
|
|
5
4
|
import type { ResourceStore } from '../lib/resource-store.js';
|
|
5
|
+
import type { FileInfo } from '../config.js';
|
|
6
6
|
export { type ToolContract } from './contract.js';
|
|
7
7
|
export declare const READ_ONLY_TOOL_ANNOTATIONS: {
|
|
8
8
|
readonly readOnlyHint: true;
|
|
@@ -12,6 +12,7 @@ export declare const READ_ONLY_TOOL_ANNOTATIONS: {
|
|
|
12
12
|
};
|
|
13
13
|
export declare const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS: {
|
|
14
14
|
readonly readOnlyHint: false;
|
|
15
|
+
readonly idempotentHint: false;
|
|
15
16
|
readonly destructiveHint: true;
|
|
16
17
|
readonly openWorldHint: false;
|
|
17
18
|
};
|
package/dist/tools/shared.js
CHANGED
|
@@ -5,7 +5,7 @@ import { parseTrueEnvFlag } from '../lib/constants.js';
|
|
|
5
5
|
import { createDetailedError, ErrorCode, formatDetailedError, getSuggestion, McpError, } from '../lib/errors.js';
|
|
6
6
|
import { createTimedAbortSignal } from '../lib/fs-helpers.js';
|
|
7
7
|
import { withToolDiagnostics } from '../lib/observability.js';
|
|
8
|
-
import { getAllowedDirectories } from '../lib/
|
|
8
|
+
import { getAllowedDirectories } from '../lib/paths.js';
|
|
9
9
|
export {} from './contract.js';
|
|
10
10
|
const MAX_INLINE_CONTENT_CHARS = parseInt(process.env['FS_CONTEXT_MAX_INLINE_CHARS'] ?? '', 10) || 20_000;
|
|
11
11
|
const MAX_INLINE_PREVIEW_CHARS = 4_000;
|
|
@@ -24,6 +24,7 @@ export const READ_ONLY_TOOL_ANNOTATIONS = {
|
|
|
24
24
|
};
|
|
25
25
|
export const DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS = {
|
|
26
26
|
readOnlyHint: false,
|
|
27
|
+
idempotentHint: false,
|
|
27
28
|
destructiveHint: true,
|
|
28
29
|
openWorldHint: false,
|
|
29
30
|
};
|
package/dist/tools/stat-many.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
import { formatBytes, joinLines } from '../config.js';
|
|
2
1
|
import { DEFAULT_SEARCH_TIMEOUT_MS } from '../lib/constants.js';
|
|
3
2
|
import { ErrorCode } from '../lib/errors.js';
|
|
4
|
-
import { getMultipleFileInfo } from '../lib/file-operations/
|
|
3
|
+
import { getMultipleFileInfo } from '../lib/file-operations/metadata.js';
|
|
4
|
+
import { formatBytes, joinLines } from '../config.js';
|
|
5
5
|
import { GetMultipleFileInfoInputSchema, GetMultipleFileInfoOutputSchema, } from '../schemas.js';
|
|
6
6
|
import { buildBatchCompletionSuffix, buildBatchPathContext, buildFileInfoPayload, buildToolErrorResponse, buildToolResponse, createBatchProgressCallbacks, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, resolveFinalProgressCurrent, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
7
7
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
8
8
|
export const GET_MULTIPLE_FILE_INFO_TOOL = {
|
|
9
9
|
name: 'stat_many',
|
|
10
10
|
title: 'Get Multiple File Info',
|
|
11
|
-
description: 'Get metadata
|
|
11
|
+
description: 'Get metadata for multiple files/directories in one request. ' +
|
|
12
|
+
'Use `tokenEstimate` (size\u00f74) to pre-screen token cost before reading.',
|
|
12
13
|
inputSchema: GetMultipleFileInfoInputSchema,
|
|
13
14
|
outputSchema: GetMultipleFileInfoOutputSchema,
|
|
14
15
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
package/dist/tools/stat.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
|
-
import { formatBytes, joinLines } from '../config.js';
|
|
3
2
|
import { DEFAULT_SEARCH_TIMEOUT_MS } from '../lib/constants.js';
|
|
4
3
|
import { ErrorCode } from '../lib/errors.js';
|
|
5
|
-
import { getFileInfo } from '../lib/file-operations/
|
|
4
|
+
import { getFileInfo } from '../lib/file-operations/metadata.js';
|
|
5
|
+
import { formatBytes, joinLines } from '../config.js';
|
|
6
6
|
import { GetFileInfoInputSchema, GetFileInfoOutputSchema } from '../schemas.js';
|
|
7
7
|
import { buildFileInfoPayload, buildToolErrorResponse, buildToolResponse, executeToolWithDiagnostics, READ_ONLY_TOOL_ANNOTATIONS, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
8
|
export const GET_FILE_INFO_TOOL = {
|
|
9
9
|
name: 'stat',
|
|
10
10
|
title: 'Get File Info',
|
|
11
|
-
description: 'Get metadata
|
|
11
|
+
description: 'Get file/directory metadata: size, modified, permissions, mime, tokenEstimate. ' +
|
|
12
|
+
'Use `tokenEstimate` (size\u00f74) to pre-screen token cost before reading.',
|
|
12
13
|
inputSchema: GetFileInfoInputSchema,
|
|
13
14
|
outputSchema: GetFileInfoOutputSchema,
|
|
14
15
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { channel } from 'node:diagnostics_channel';
|
|
2
2
|
import { CallToolResultSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
3
3
|
import { ErrorCode, McpError } from '../lib/errors.js';
|
|
4
|
-
import { isRecord } from '../lib/
|
|
4
|
+
import { isRecord } from '../lib/utils.js';
|
|
5
5
|
import { buildToolErrorResponse, maybeStripStructuredContentFromResult, withDefaultIcons, } from './shared.js';
|
|
6
6
|
function isExperimentalTaskRegistration(value) {
|
|
7
7
|
if (!value || typeof value !== 'object')
|
package/dist/tools/tree.js
CHANGED
|
@@ -1,16 +1,15 @@
|
|
|
1
1
|
import * as path from 'node:path';
|
|
2
2
|
import { DEFAULT_SEARCH_TIMEOUT_MS } from '../lib/constants.js';
|
|
3
3
|
import { ErrorCode } from '../lib/errors.js';
|
|
4
|
-
import { formatTreeAscii, treeDirectory } from '../lib/file-operations/
|
|
4
|
+
import { formatTreeAscii, treeDirectory, } from '../lib/file-operations/metadata.js';
|
|
5
5
|
import { TreeInputSchema, TreeOutputSchema } from '../schemas.js';
|
|
6
6
|
import { buildToolErrorResponse, buildToolResponse, createProgressReporter, executeToolWithDiagnostics, notifyProgress, READ_ONLY_TOOL_ANNOTATIONS, resolvePathOrRoot, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
7
7
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
|
8
8
|
export const TREE_TOOL = {
|
|
9
9
|
name: 'tree',
|
|
10
10
|
title: 'Tree',
|
|
11
|
-
description: 'Render a directory tree (bounded recursion). ' +
|
|
12
|
-
'
|
|
13
|
-
'Note: maxDepth=0 returns only the root node with empty children array.',
|
|
11
|
+
description: 'Render a directory tree (bounded recursion). Returns ASCII tree + structured JSON. ' +
|
|
12
|
+
'`maxDepth=0` returns only the root node.',
|
|
14
13
|
inputSchema: TreeInputSchema,
|
|
15
14
|
outputSchema: TreeOutputSchema,
|
|
16
15
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
package/dist/tools/write-file.js
CHANGED
|
@@ -2,7 +2,7 @@ import * as fs from 'node:fs/promises';
|
|
|
2
2
|
import * as path from 'node:path';
|
|
3
3
|
import { ErrorCode } from '../lib/errors.js';
|
|
4
4
|
import { atomicWriteFile, withAbort } from '../lib/fs-helpers.js';
|
|
5
|
-
import { validatePathForWrite } from '../lib/
|
|
5
|
+
import { validatePathForWrite } from '../lib/paths.js';
|
|
6
6
|
import { WriteFileInputSchema, WriteFileOutputSchema } from '../schemas.js';
|
|
7
7
|
import { buildToolErrorResponse, buildToolResponse, DESTRUCTIVE_WRITE_TOOL_ANNOTATIONS, executeToolWithDiagnostics, withDefaultIcons, withValidatedArgs, wrapToolHandler, } from './shared.js';
|
|
8
8
|
import { registerToolTaskIfAvailable } from './task-support.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@j0hanz/filesystem-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"mcpName": "io.github.j0hanz/filesystem-mcp",
|
|
5
5
|
"description": "MCP Server that enables LLMs to interact with the local filesystem.",
|
|
6
6
|
"type": "module",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"commander": "^14.0.3",
|
|
69
69
|
"diff": "^8.0.3",
|
|
70
70
|
"ignore": "^7.0.5",
|
|
71
|
-
"re2": "^1.23.
|
|
71
|
+
"re2": "^1.23.3",
|
|
72
72
|
"safe-regex2": "^5.0.0",
|
|
73
73
|
"zod": "^4.3.6"
|
|
74
74
|
},
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
"@types/node": "^24",
|
|
79
79
|
"eslint": "^10.0.2",
|
|
80
80
|
"eslint-config-prettier": "^10.1.8",
|
|
81
|
-
"eslint-plugin-de-morgan": "^2.
|
|
82
|
-
"eslint-plugin-depend": "^1.
|
|
81
|
+
"eslint-plugin-de-morgan": "^2.1.1",
|
|
82
|
+
"eslint-plugin-depend": "^1.5.0",
|
|
83
83
|
"eslint-plugin-sonarjs": "^4.0.0",
|
|
84
84
|
"eslint-plugin-unused-imports": "^4.4.1",
|
|
85
85
|
"jscpd": "^4.0.8",
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { FileInfo, GetMultipleFileInfoResult } from '../../config.js';
|
|
2
|
-
interface FileInfoOptions {
|
|
3
|
-
includeMimeType?: boolean | undefined;
|
|
4
|
-
signal?: AbortSignal | undefined;
|
|
5
|
-
onProgress?: () => void;
|
|
6
|
-
}
|
|
7
|
-
export declare function getFileInfo(filePath: string, options?: FileInfoOptions): Promise<FileInfo>;
|
|
8
|
-
type GetMultipleFileInfoOptions = FileInfoOptions;
|
|
9
|
-
export declare function getMultipleFileInfo(paths: readonly string[], options?: GetMultipleFileInfoOptions): Promise<GetMultipleFileInfoResult>;
|
|
10
|
-
export {};
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
import * as fsp from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import { getMimeType, PARALLEL_CONCURRENCY } from '../constants.js';
|
|
4
|
-
import { isAbortError } from '../errors.js';
|
|
5
|
-
import { assertNotAborted, getFileType, isHidden, processInParallel, withAbort, } from '../fs-helpers.js';
|
|
6
|
-
import { assertAllowedFileAccess } from '../path-policy.js';
|
|
7
|
-
import { validateExistingPathDetailed } from '../path-validation.js';
|
|
8
|
-
import { applyIndexedErrors, applyIndexedValues } from './common.js';
|
|
9
|
-
const PERM_STRINGS = [
|
|
10
|
-
'---',
|
|
11
|
-
'--x',
|
|
12
|
-
'-w-',
|
|
13
|
-
'-wx',
|
|
14
|
-
'r--',
|
|
15
|
-
'r-x',
|
|
16
|
-
'rw-',
|
|
17
|
-
'rwx',
|
|
18
|
-
];
|
|
19
|
-
const UNKNOWN_PATH = '(unknown)';
|
|
20
|
-
function getPermissions(mode) {
|
|
21
|
-
const ownerIndex = (mode >> 6) & 0b111;
|
|
22
|
-
const groupIndex = (mode >> 3) & 0b111;
|
|
23
|
-
const otherIndex = mode & 0b111;
|
|
24
|
-
const owner = PERM_STRINGS[ownerIndex] ?? '---';
|
|
25
|
-
const group = PERM_STRINGS[groupIndex] ?? '---';
|
|
26
|
-
const other = PERM_STRINGS[otherIndex] ?? '---';
|
|
27
|
-
return `${owner}${group}${other}`;
|
|
28
|
-
}
|
|
29
|
-
function buildFileInfoResult(name, requestedPath, isSymlink, stats, mimeType, symlinkTarget) {
|
|
30
|
-
const tokenEstimate = stats.isFile() ? Math.ceil(stats.size / 4) : undefined;
|
|
31
|
-
return {
|
|
32
|
-
name,
|
|
33
|
-
path: requestedPath,
|
|
34
|
-
type: isSymlink ? 'symlink' : getFileType(stats),
|
|
35
|
-
size: stats.size,
|
|
36
|
-
...(tokenEstimate !== undefined ? { tokenEstimate } : {}),
|
|
37
|
-
created: stats.birthtime,
|
|
38
|
-
modified: stats.mtime,
|
|
39
|
-
accessed: stats.atime,
|
|
40
|
-
permissions: getPermissions(stats.mode),
|
|
41
|
-
isHidden: isHidden(name),
|
|
42
|
-
...(mimeType !== undefined ? { mimeType } : {}),
|
|
43
|
-
...(symlinkTarget !== undefined ? { symlinkTarget } : {}),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
async function getSymlinkTarget(pathToRead, signal) {
|
|
47
|
-
assertNotAborted(signal);
|
|
48
|
-
try {
|
|
49
|
-
return await withAbort(fsp.readlink(pathToRead), signal);
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
if (isAbortError(error))
|
|
53
|
-
throw error;
|
|
54
|
-
return undefined;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
export async function getFileInfo(filePath, options = {}) {
|
|
58
|
-
const { signal } = options;
|
|
59
|
-
assertNotAborted(signal);
|
|
60
|
-
const { requestedPath, resolvedPath, isSymlink } = await validateExistingPathDetailed(filePath, signal);
|
|
61
|
-
assertAllowedFileAccess(requestedPath, resolvedPath);
|
|
62
|
-
const name = path.basename(requestedPath);
|
|
63
|
-
const ext = path.extname(name).toLowerCase();
|
|
64
|
-
const includeMimeType = options.includeMimeType !== false;
|
|
65
|
-
const mimeType = includeMimeType && ext.length > 0 ? getMimeType(ext) : undefined;
|
|
66
|
-
const symlinkTarget = isSymlink
|
|
67
|
-
? await getSymlinkTarget(requestedPath, signal)
|
|
68
|
-
: undefined;
|
|
69
|
-
const stats = await withAbort(fsp.stat(resolvedPath), signal);
|
|
70
|
-
return buildFileInfoResult(name, requestedPath, isSymlink, stats, mimeType, symlinkTarget);
|
|
71
|
-
}
|
|
72
|
-
function buildEmptyResult() {
|
|
73
|
-
return {
|
|
74
|
-
results: [],
|
|
75
|
-
summary: { total: 0, succeeded: 0, failed: 0, totalSize: 0 },
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
async function processFileInfo(filePath, options) {
|
|
79
|
-
const info = await getFileInfo(filePath, options);
|
|
80
|
-
return { path: filePath, info };
|
|
81
|
-
}
|
|
82
|
-
function buildIndexedPathTasks(paths) {
|
|
83
|
-
const tasks = [];
|
|
84
|
-
for (let index = 0; index < paths.length; index += 1) {
|
|
85
|
-
const filePath = paths[index];
|
|
86
|
-
if (filePath === undefined)
|
|
87
|
-
continue;
|
|
88
|
-
tasks.push({ filePath, index });
|
|
89
|
-
}
|
|
90
|
-
return tasks;
|
|
91
|
-
}
|
|
92
|
-
async function readFileInfoInParallel(paths, options) {
|
|
93
|
-
return processInParallel(buildIndexedPathTasks(paths), async ({ filePath, index }) => {
|
|
94
|
-
const value = await processFileInfo(filePath, options);
|
|
95
|
-
options.onProgress?.();
|
|
96
|
-
return { index, value };
|
|
97
|
-
}, PARALLEL_CONCURRENCY, options.signal);
|
|
98
|
-
}
|
|
99
|
-
function calculateSummary(results) {
|
|
100
|
-
let succeeded = 0;
|
|
101
|
-
let failed = 0;
|
|
102
|
-
let totalSize = 0;
|
|
103
|
-
for (const result of results) {
|
|
104
|
-
if (result.info !== undefined) {
|
|
105
|
-
succeeded++;
|
|
106
|
-
totalSize += result.info.size;
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
failed++;
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
total: results.length,
|
|
114
|
-
succeeded,
|
|
115
|
-
failed,
|
|
116
|
-
totalSize,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
export async function getMultipleFileInfo(paths, options = {}) {
|
|
120
|
-
if (paths.length === 0)
|
|
121
|
-
return buildEmptyResult();
|
|
122
|
-
const output = new Array(paths.length);
|
|
123
|
-
for (let index = 0; index < paths.length; index += 1) {
|
|
124
|
-
output[index] = { path: paths[index] ?? UNKNOWN_PATH };
|
|
125
|
-
}
|
|
126
|
-
const { results, errors } = await readFileInfoInParallel(paths, options);
|
|
127
|
-
applyIndexedValues(output, results);
|
|
128
|
-
applyIndexedErrors({
|
|
129
|
-
output,
|
|
130
|
-
errors,
|
|
131
|
-
resolveIndex: (failureIndex) => failureIndex >= 0 && failureIndex < output.length
|
|
132
|
-
? failureIndex
|
|
133
|
-
: undefined,
|
|
134
|
-
buildValue: (resolvedIndex, error) => ({
|
|
135
|
-
path: paths[resolvedIndex] ?? UNKNOWN_PATH,
|
|
136
|
-
error: error.message,
|
|
137
|
-
}),
|
|
138
|
-
});
|
|
139
|
-
return {
|
|
140
|
-
results: output,
|
|
141
|
-
summary: calculateSummary(output),
|
|
142
|
-
};
|
|
143
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { type Ignore } from 'ignore';
|
|
2
|
-
export declare function loadRootGitignore(root: string, signal?: AbortSignal): Promise<Ignore | null>;
|
|
3
|
-
export declare function isIgnoredByGitignore(matcher: Ignore, root: string, absolutePath: string, options?: {
|
|
4
|
-
isDirectory?: boolean;
|
|
5
|
-
relativePath?: string;
|
|
6
|
-
}): boolean;
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as fs from 'node:fs/promises';
|
|
2
|
-
import * as path from 'node:path';
|
|
3
|
-
import ignore, {} from 'ignore';
|
|
4
|
-
import { isNodeError } from '../errors.js';
|
|
5
|
-
import { toPosixPath } from '../path-format.js';
|
|
6
|
-
function parseGitignoreLines(contents) {
|
|
7
|
-
const lines = [];
|
|
8
|
-
for (const line of contents.split(/\r?\n/u)) {
|
|
9
|
-
const trimmed = line.trim();
|
|
10
|
-
if (trimmed.length > 0) {
|
|
11
|
-
lines.push(trimmed);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
return lines;
|
|
15
|
-
}
|
|
16
|
-
export async function loadRootGitignore(root, signal) {
|
|
17
|
-
const gitignorePath = path.join(root, '.gitignore');
|
|
18
|
-
let contents;
|
|
19
|
-
try {
|
|
20
|
-
contents = await fs.readFile(gitignorePath, {
|
|
21
|
-
encoding: 'utf-8',
|
|
22
|
-
signal,
|
|
23
|
-
});
|
|
24
|
-
}
|
|
25
|
-
catch (error) {
|
|
26
|
-
if (isNodeError(error) && error.code === 'ENOENT') {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
throw error;
|
|
30
|
-
}
|
|
31
|
-
const matcher = ignore();
|
|
32
|
-
matcher.add(parseGitignoreLines(contents));
|
|
33
|
-
return matcher;
|
|
34
|
-
}
|
|
35
|
-
export function isIgnoredByGitignore(matcher, root, absolutePath, options = {}) {
|
|
36
|
-
let relative = options.relativePath;
|
|
37
|
-
relative ??= path.relative(root, absolutePath);
|
|
38
|
-
if (relative.length === 0)
|
|
39
|
-
return false;
|
|
40
|
-
const normalized = toPosixPath(relative);
|
|
41
|
-
if (options.isDirectory) {
|
|
42
|
-
return matcher.ignores(normalized.endsWith('/') ? normalized : `${normalized}/`);
|
|
43
|
-
}
|
|
44
|
-
return matcher.ignores(normalized);
|
|
45
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { globEntries } from './glob-engine.js';
|
|
2
|
-
export interface GlobConfig {
|
|
3
|
-
cwd: string;
|
|
4
|
-
pattern: string;
|
|
5
|
-
excludePatterns?: readonly string[];
|
|
6
|
-
includeHidden?: boolean;
|
|
7
|
-
baseNameMatch?: boolean;
|
|
8
|
-
caseSensitiveMatch?: boolean;
|
|
9
|
-
followSymbolicLinks?: boolean;
|
|
10
|
-
onlyFiles?: boolean;
|
|
11
|
-
stats?: boolean;
|
|
12
|
-
maxDepth?: number;
|
|
13
|
-
suppressErrors?: boolean;
|
|
14
|
-
}
|
|
15
|
-
/**
|
|
16
|
-
* Builds standard options for globEntries to ensure consistency across search tools.
|
|
17
|
-
*/
|
|
18
|
-
export declare function buildGlobOptions(config: GlobConfig): Parameters<typeof globEntries>[0];
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Builds standard options for globEntries to ensure consistency across search tools.
|
|
3
|
-
*/
|
|
4
|
-
export function buildGlobOptions(config) {
|
|
5
|
-
const options = {
|
|
6
|
-
cwd: config.cwd,
|
|
7
|
-
pattern: config.pattern,
|
|
8
|
-
excludePatterns: config.excludePatterns ?? [],
|
|
9
|
-
includeHidden: config.includeHidden ?? false,
|
|
10
|
-
baseNameMatch: config.baseNameMatch ?? false,
|
|
11
|
-
caseSensitiveMatch: config.caseSensitiveMatch ?? true,
|
|
12
|
-
followSymbolicLinks: config.followSymbolicLinks ?? false,
|
|
13
|
-
onlyFiles: config.onlyFiles ?? true,
|
|
14
|
-
stats: config.stats ?? false,
|
|
15
|
-
};
|
|
16
|
-
if (config.suppressErrors) {
|
|
17
|
-
options.suppressErrors = config.suppressErrors;
|
|
18
|
-
}
|
|
19
|
-
if (config.maxDepth !== undefined) {
|
|
20
|
-
options.maxDepth = config.maxDepth;
|
|
21
|
-
}
|
|
22
|
-
return options;
|
|
23
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { ListDirectoryResult } from '../../config.js';
|
|
2
|
-
interface ListDirectoryOptions {
|
|
3
|
-
includeHidden?: boolean;
|
|
4
|
-
excludePatterns?: readonly string[];
|
|
5
|
-
maxDepth?: number;
|
|
6
|
-
maxEntries?: number;
|
|
7
|
-
sortBy?: 'name' | 'size' | 'modified' | 'type';
|
|
8
|
-
includeSymlinkTargets?: boolean;
|
|
9
|
-
pattern?: string;
|
|
10
|
-
timeoutMs?: number;
|
|
11
|
-
signal?: AbortSignal;
|
|
12
|
-
}
|
|
13
|
-
export declare function listDirectory(dirPath: string, options?: ListDirectoryOptions): Promise<ListDirectoryResult>;
|
|
14
|
-
export {};
|