diff --git a/src/lib/source-lifecycle.test.ts b/src/lib/source-lifecycle.test.ts index 1450179f1..2211f511f 100644 --- a/src/lib/source-lifecycle.test.ts +++ b/src/lib/source-lifecycle.test.ts @@ -60,6 +60,21 @@ describe("source-lifecycle path helpers", () => { expect(isIngestableSourcePath("C:\\project\\raw\\sources\\book.MOBI")).toBe(true) }) + it("accepts code file extensions and handles case insensitivity", () => { + // lower-case code extensions + expect(isIngestableSourcePath("raw/sources/app.ts")).toBe(true) + expect(isIngestableSourcePath("raw/sources/app.tsx")).toBe(true) + expect(isIngestableSourcePath("raw/sources/lib.py")).toBe(true) + // mixed-case code extensions + expect(isIngestableSourcePath("raw/sources/MyClass.JAVA")).toBe(true) + expect(isIngestableSourcePath("C:\\project\\raw\\sources\\utils.CS")).toBe(true) + expect(isIngestableSourcePath("raw/sources/Component.Tsx")).toBe(true) + // still rejects unknown extensions + expect(isIngestableSourcePath("raw/sources/notes.xyz")).toBe(false) + // still rejects dotfiles even with valid code extensions + expect(isIngestableSourcePath("raw/sources/.eslintrc.json")).toBe(false) + }) + it("derives folder context from absolute raw/sources paths without leaking the project prefix", () => { expect( folderContextForSourcePath("/tmp/project/raw/sources/reports/2026/report.pdf"), diff --git a/src/lib/source-lifecycle.ts b/src/lib/source-lifecycle.ts index 946e1b683..901ccd4f9 100644 --- a/src/lib/source-lifecycle.ts +++ b/src/lib/source-lifecycle.ts @@ -64,6 +64,20 @@ export const INGESTABLE_SOURCE_EXTENSIONS = new Set([ "epub", "mobi", "org", + // Code file extensions + "js", + "ts", + "jsx", + "tsx", + "py", + "java", + "c", + "cpp", + "cs", + "rb", + "go", + "rs", + "php", ]) function flattenFiles(nodes: FileNode[]): FileNode[] { diff --git a/src/lib/source-watch-config.test.ts b/src/lib/source-watch-config.test.ts index 8ad2f95ce..6d4c4178d 100644 --- a/src/lib/source-watch-config.test.ts +++ b/src/lib/source-watch-config.test.ts @@ -36,6 +36,27 @@ describe("source watch config", () => { expect(isPathAllowedBySourceWatch("raw/sources/~$Document.docx", config)).toBe(false) }) + it("rejects code extensions under default config", () => { + expect(isPathAllowedBySourceWatch("raw/sources/app.ts", DEFAULT_SOURCE_WATCH_CONFIG)).toBe(false) + expect(isPathAllowedBySourceWatch("raw/sources/lib.py", DEFAULT_SOURCE_WATCH_CONFIG)).toBe(false) + expect(isPathAllowedBySourceWatch("raw/sources/main.go", DEFAULT_SOURCE_WATCH_CONFIG)).toBe(false) + }) + + it("allows code extensions when explicitly included", () => { + const config = normalizeSourceWatchConfig({ + includeExtensions: ["js", "ts", "tsx", "py", "go", "rs", "php"], + }) + + expect(isPathAllowedBySourceWatch("raw/sources/app.ts", config)).toBe(true) + expect(isPathAllowedBySourceWatch("raw/sources/lib.py", config)).toBe(true) + expect(isPathAllowedBySourceWatch("raw/sources/main.go", config)).toBe(true) + expect(isPathAllowedBySourceWatch("raw/sources/lib.rs", config)).toBe(true) + // still rejects unknown extensions + expect(isPathAllowedBySourceWatch("raw/sources/notes.xyz", config)).toBe(false) + // still rejects dotfiles + expect(isPathAllowedBySourceWatch("raw/sources/.hidden.ts", config)).toBe(false) + }) + it("normalizes comma-separated exclusion values from text fields", () => { const config = normalizeSourceWatchConfig({ includeExtensions: ["md, pdf", "docx"], diff --git a/src/lib/source-watch-config.ts b/src/lib/source-watch-config.ts index 2ac9abece..9d67b4153 100644 --- a/src/lib/source-watch-config.ts +++ b/src/lib/source-watch-config.ts @@ -25,6 +25,11 @@ export const SOURCE_WATCH_FILE_TYPE_GROUPS = [ id: "data", extensions: ["json", "yaml", "yml", "xml"], }, + // Code files + { + id: "code", + extensions: ["js", "ts", "jsx", "tsx", "py", "java", "c", "cpp", "cs", "rb", "go", "rs", "php"], + }, ] function normalizeExtensions(values: readonly string[] | undefined): string[] {