* Grep the *content* of a single workspace file (under `files/`), as opposed to * grep which searches the in-memory VFS map (workflow JSON, metadata, * plans, memories — workspace files appear there only as metadata). * * Content search applies to workspace files only and must ta
(
path: string,
pattern: string,
options?: GrepOptions
)
| 672 | * memory. |
| 673 | */ |
| 674 | async grepFile( |
| 675 | path: string, |
| 676 | pattern: string, |
| 677 | options?: GrepOptions |
| 678 | ): Promise<GrepMatch[] | string[] | ops.GrepCountEntry[]> { |
| 679 | const normalized = path.replace(/^\/+/, '') |
| 680 | // Prefer the path verbatim when it is itself a file leaf (e.g. a file literally |
| 681 | // named "content"); otherwise drop a trailing "/content" read suffix. |
| 682 | const leaf = this.files.has(normalized) ? normalized : normalized.replace(/\/content$/, '') |
| 683 | |
| 684 | const isWorkspaceFilePath = /^(recently-deleted\/)?files(\/|$)/.test(leaf) |
| 685 | if (!isWorkspaceFilePath || !this.files.has(leaf)) { |
| 686 | const suggestions = this.suggestSimilar(leaf) |
| 687 | const hint = |
| 688 | suggestions.length > 0 |
| 689 | ? ` Did you mean: ${suggestions.join(', ')}?` |
| 690 | : ' Use glob to find the exact file path, then grep that single file.' |
| 691 | throw new ops.WorkspaceFileGrepError( |
| 692 | `Grep over workspace file content must target a single workspace file (e.g. path: "files/report.csv"). "${path}" is not a single workspace file.${hint}` |
| 693 | ) |
| 694 | } |
| 695 | |
| 696 | const contentPath = `${leaf}/content` |
| 697 | const result = await this.readFileContent(contentPath) |
| 698 | if (!result) { |
| 699 | throw new ops.WorkspaceFileGrepError(`Workspace file content not found for "${path}".`) |
| 700 | } |
| 701 | |
| 702 | return ops.grepReadResult(leaf, result, pattern, contentPath, options) |
| 703 | } |
| 704 | |
| 705 | glob(pattern: string): string[] { |
| 706 | // glob matches keys only, so it resolves no lazy content — it sees the full |
no test coverage detected