Skip to content

Commit 262a2b3

Browse files
committed
accept fsSource input in sdk
1 parent 9f57029 commit 262a2b3

File tree

22 files changed

+328
-257
lines changed

22 files changed

+328
-257
lines changed

common/src/project-file-tree.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import fs from 'fs'
21
import path from 'path'
32

43
import * as ignore from 'ignore'
@@ -7,14 +6,20 @@ import { sortBy } from 'lodash'
76
import { DEFAULT_IGNORED_PATHS } from './old-constants'
87
import { isValidProjectRoot } from './util/file'
98

9+
import type { CodebuffFileSystem } from './types/filesystem'
1010
import type { DirectoryNode, FileTreeNode } from './util/file'
1111

1212
export const DEFAULT_MAX_FILES = 10_000
1313

14-
export function getProjectFileTree(
15-
projectRoot: string,
16-
{ maxFiles = DEFAULT_MAX_FILES }: { maxFiles?: number } = {},
17-
): FileTreeNode[] {
14+
export function getProjectFileTree(params: {
15+
projectRoot: string
16+
maxFiles?: number
17+
fs: CodebuffFileSystem
18+
}): FileTreeNode[] {
19+
const withDefaults = { maxFiles: DEFAULT_MAX_FILES, ...params }
20+
const { projectRoot, fs } = withDefaults
21+
let { maxFiles } = withDefaults
22+
1823
const start = Date.now()
1924
const defaultIgnore = ignore.default()
2025
for (const pattern of DEFAULT_IGNORED_PATHS) {
@@ -50,7 +55,7 @@ export function getProjectFileTree(
5055
const mergedIgnore = ignore
5156
.default()
5257
.add(currentIgnore)
53-
.add(parseGitignore(fullPath, projectRoot))
58+
.add(parseGitignore({ fullDirPath: fullPath, projectRoot, fs }))
5459

5560
try {
5661
const files = fs.readdirSync(fullPath)
@@ -146,10 +151,13 @@ function rebaseGitignorePattern(
146151
return isNegated ? `!${rebased}` : rebased
147152
}
148153

149-
export function parseGitignore(
150-
fullDirPath: string,
151-
projectRoot: string,
152-
): ignore.Ignore {
154+
export function parseGitignore(params: {
155+
fullDirPath: string
156+
projectRoot: string
157+
fs: CodebuffFileSystem
158+
}): ignore.Ignore {
159+
const { fullDirPath, projectRoot, fs } = params
160+
153161
const ig = ignore.default()
154162
const relativeDirPath = path.relative(projectRoot, fullDirPath)
155163
const ignoreFiles = [
@@ -210,7 +218,13 @@ export function getLastReadFilePaths(
210218
.map((node) => node.filePath)
211219
}
212220

213-
export function isFileIgnored(filePath: string, projectRoot: string): boolean {
221+
export function isFileIgnored(params: {
222+
filePath: string
223+
projectRoot: string
224+
fs: CodebuffFileSystem
225+
}): boolean {
226+
const { filePath, projectRoot, fs } = params
227+
214228
const defaultIgnore = ignore.default()
215229
for (const pattern of DEFAULT_IGNORED_PATHS) {
216230
defaultIgnore.add(pattern)
@@ -226,7 +240,9 @@ export function isFileIgnored(filePath: string, projectRoot: string): boolean {
226240
const mergedIgnore = ignore.default().add(defaultIgnore)
227241
let currentDir = dirPath
228242
while (currentDir.startsWith(projectRoot)) {
229-
mergedIgnore.add(parseGitignore(currentDir, projectRoot))
243+
mergedIgnore.add(
244+
parseGitignore({ fullDirPath: currentDir, projectRoot, fs }),
245+
)
230246
currentDir = path.dirname(currentDir)
231247
}
232248

common/src/types/filesystem.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import type fs from 'fs'
2+
3+
/** File system used for Codebuff SDK.
4+
*
5+
* Compatible with the `'fs'` module
6+
*/
7+
export type CodebuffFileSystem = Pick<
8+
typeof fs,
9+
| 'existsSync'
10+
| 'mkdirSync'
11+
| 'readdirSync'
12+
| 'readFileSync'
13+
| 'statSync'
14+
| 'writeFileSync'
15+
> & { promises: Pick<typeof fs.promises, 'readdir'> }

common/src/types/source.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export type Source<T> =
2+
| T
3+
| Promise<T>
4+
| (T extends (...args: unknown[]) => unknown ? never : () => T | Promise<T>)

common/src/util/file.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import * as fs from 'fs'
21
import * as os from 'os'
32
import * as path from 'path'
43

54
import { z } from 'zod/v4'
65

76
import { CodebuffConfigSchema } from '../json-config/constants'
87

8+
import type { CodebuffFileSystem } from '../types/filesystem'
9+
910
export const FileTreeNodeSchema: z.ZodType<FileTreeNode> = z.object({
1011
name: z.string(),
1112
type: z.enum(['file', 'directory']),
@@ -214,7 +215,12 @@ export const ensureEndsWithNewline = (
214215
return contents + '\n'
215216
}
216217

217-
export const ensureDirectoryExists = (baseDir: string) => {
218+
export const ensureDirectoryExists = (params: {
219+
baseDir: string
220+
fs: CodebuffFileSystem
221+
}) => {
222+
const { baseDir, fs } = params
223+
218224
if (!fs.existsSync(baseDir)) {
219225
fs.mkdirSync(baseDir, { recursive: true })
220226
}
@@ -243,9 +249,14 @@ export function isValidFilePath(path: string) {
243249
return true
244250
}
245251

246-
export function isDir(p: string): boolean {
252+
export function isDir(params: {
253+
path: string
254+
fs: CodebuffFileSystem
255+
}): boolean {
256+
const { path, fs } = params
257+
247258
try {
248-
return fs.statSync(p).isDirectory()
259+
return fs.statSync(path).isDirectory()
249260
} catch {
250261
return false
251262
}

common/src/util/git.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { execSync } from 'child_process'
2-
import * as fs from 'fs'
32
import * as path from 'path'
43

54
import type { FileChanges } from '../actions'
5+
import type { CodebuffFileSystem } from '../types/filesystem'
66

77
const maxBuffer = 50 * 1024 * 1024 // 50 MB
88

@@ -38,7 +38,13 @@ export function stageAllChanges(): boolean {
3838
}
3939
}
4040

41-
export function stagePatches(dir: string, changes: FileChanges): boolean {
41+
export function stagePatches(params: {
42+
dir: string
43+
changes: FileChanges
44+
fs: CodebuffFileSystem
45+
}): boolean {
46+
const { dir, changes, fs } = params
47+
4248
try {
4349
const fileNames = changes.map((change) => change.path)
4450
const existingFileNames = fileNames.filter((filePath) =>

evals/scaffolding.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ export function createFileReadingMock(projectRoot: string) {
137137
export async function getProjectFileContext(
138138
projectPath: string,
139139
): Promise<ProjectFileContext> {
140-
const fileTree = getProjectFileTree(projectPath)
140+
const fileTree = getProjectFileTree({ projectRoot: projectPath, fs })
141141
const allFilePaths = getAllFilePaths(fileTree)
142142
const knowledgeFilePaths = allFilePaths.filter((filePath) =>
143143
filePath.endsWith('knowledge.md'),

npm-app/src/browser-runner.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -504,7 +504,7 @@ export class BrowserRunner {
504504
try {
505505
const chatDir = getCurrentChatDir()
506506
const screenshotsDir = path.join(chatDir, 'screenshots')
507-
ensureDirectoryExists(screenshotsDir)
507+
ensureDirectoryExists({ baseDir: screenshotsDir, fs })
508508

509509
const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
510510
const filename = `screenshot-${timestamp}.jpg`

npm-app/src/cli.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -608,7 +608,10 @@ export class CLI {
608608
.filter((file) => file.startsWith(partial))
609609
.map(
610610
(file) =>
611-
file + (isDir(path.join(baseDir, file)) ? directorySuffix : ''),
611+
file +
612+
(isDir({ path: path.join(baseDir, file), fs })
613+
? directorySuffix
614+
: ''),
612615
)
613616
return [fsMatches, partial]
614617
} catch {

npm-app/src/credentials.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import type { User } from '@codebuff/common/util/credentials'
22

33
import { z } from 'zod/v4'
44

5-
import { logger } from './utils/logger'
6-
7-
import { existsSync, readFileSync } from 'fs'
5+
import fs from 'fs'
86
import path from 'node:path'
97
import os from 'os'
108

@@ -40,6 +38,8 @@ export const userFromJson = (
4038

4139
import { ensureDirectoryExists } from '@codebuff/common/util/file'
4240

41+
import { logger } from './utils/logger'
42+
4343
export const CONFIG_DIR = path.join(
4444
os.homedir(),
4545
'.config',
@@ -52,7 +52,7 @@ export const CONFIG_DIR = path.join(
5252
)
5353

5454
// Ensure config directory exists
55-
ensureDirectoryExists(CONFIG_DIR)
55+
ensureDirectoryExists({ baseDir: CONFIG_DIR, fs })
5656
export const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json')
5757

5858
/**
@@ -61,12 +61,12 @@ export const CREDENTIALS_PATH = path.join(CONFIG_DIR, 'credentials.json')
6161
*/
6262
export const getUserCredentials = (): User | null => {
6363
// Read user credentials directly from file
64-
if (!existsSync(CREDENTIALS_PATH)) {
64+
if (!fs.existsSync(CREDENTIALS_PATH)) {
6565
return null
6666
}
6767

6868
try {
69-
const credentialsFile = readFileSync(CREDENTIALS_PATH, 'utf8')
69+
const credentialsFile = fs.readFileSync(CREDENTIALS_PATH, 'utf8')
7070
const user = userFromJson(credentialsFile)
7171
return user || null
7272
} catch (error) {

npm-app/src/project-files.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export function getProjectDataDir(): string {
8686
export function getCurrentChatDir(): string {
8787
const chatId = getCurrentChatId()
8888
const dir = path.join(getProjectDataDir(), 'chats', chatId)
89-
ensureDirectoryExists(dir)
89+
ensureDirectoryExists({ baseDir: dir, fs })
9090
return dir
9191
}
9292

@@ -262,7 +262,7 @@ export const getProjectFileContext = async (
262262
!cachedProjectFileContext ||
263263
cachedProjectFileContext.projectRoot !== projectRoot
264264
) {
265-
const fileTree = getProjectFileTree(projectRoot)
265+
const fileTree = getProjectFileTree({ projectRoot, fs })
266266
const flattenedNodes = flattenTree(fileTree)
267267
const allFilePaths = flattenedNodes
268268
.filter((node) => node.type === 'file')
@@ -457,7 +457,7 @@ export function getChangesSinceLastFileVersion(
457457
export function getFiles(filePaths: string[]) {
458458
const result: Record<string, string | null> = {}
459459
const MAX_FILE_SIZE = 1024 * 1024 // 1MB in bytes
460-
const ig = parseGitignore(projectRoot, projectRoot)
460+
const ig = parseGitignore({ fullDirPath: projectRoot, projectRoot, fs })
461461

462462
for (const filePath of filePaths) {
463463
if (!filePath) {

0 commit comments

Comments
 (0)