- {smartContextAutoIncludes.map((p) => (
+ {smartContextAutoIncludes.map((p: ContextPathResult) => (
({
queryKey: ["context-paths", appId],
queryFn: async () => {
- if (!appId) return { contextPaths: [], smartContextAutoIncludes: [] };
+ if (!appId)
+ return {
+ contextPaths: [],
+ smartContextAutoIncludes: [],
+ excludePaths: [],
+ };
const ipcClient = IpcClient.getInstance();
return ipcClient.getChatContextResults({ appId });
},
@@ -25,9 +30,17 @@ export function useContextPaths() {
const updateContextPathsMutation = useMutation<
unknown,
Error,
- { contextPaths: GlobPath[]; smartContextAutoIncludes?: GlobPath[] }
+ {
+ contextPaths: GlobPath[];
+ smartContextAutoIncludes?: GlobPath[];
+ excludePaths?: GlobPath[];
+ }
>({
- mutationFn: async ({ contextPaths, smartContextAutoIncludes }) => {
+ mutationFn: async ({
+ contextPaths,
+ smartContextAutoIncludes,
+ excludePaths,
+ }) => {
if (!appId) throw new Error("No app selected");
const ipcClient = IpcClient.getInstance();
return ipcClient.setChatContext({
@@ -35,6 +48,7 @@ export function useContextPaths() {
chatContext: {
contextPaths,
smartContextAutoIncludes: smartContextAutoIncludes || [],
+ excludePaths: excludePaths || [],
},
});
},
@@ -46,28 +60,63 @@ export function useContextPaths() {
const updateContextPaths = async (paths: GlobPath[]) => {
const currentAutoIncludes =
contextPathsData?.smartContextAutoIncludes || [];
+ const currentExcludePaths = contextPathsData?.excludePaths || [];
return updateContextPathsMutation.mutateAsync({
contextPaths: paths,
- smartContextAutoIncludes: currentAutoIncludes.map(({ globPath }) => ({
- globPath,
- })),
+ smartContextAutoIncludes: currentAutoIncludes.map(
+ ({ globPath }: { globPath: string }) => ({
+ globPath,
+ }),
+ ),
+ excludePaths: currentExcludePaths.map(
+ ({ globPath }: { globPath: string }) => ({
+ globPath,
+ }),
+ ),
});
};
const updateSmartContextAutoIncludes = async (paths: GlobPath[]) => {
const currentContextPaths = contextPathsData?.contextPaths || [];
+ const currentExcludePaths = contextPathsData?.excludePaths || [];
return updateContextPathsMutation.mutateAsync({
- contextPaths: currentContextPaths.map(({ globPath }) => ({ globPath })),
+ contextPaths: currentContextPaths.map(
+ ({ globPath }: { globPath: string }) => ({ globPath }),
+ ),
smartContextAutoIncludes: paths,
+ excludePaths: currentExcludePaths.map(
+ ({ globPath }: { globPath: string }) => ({
+ globPath,
+ }),
+ ),
+ });
+ };
+
+ const updateExcludePaths = async (paths: GlobPath[]) => {
+ const currentContextPaths = contextPathsData?.contextPaths || [];
+ const currentAutoIncludes =
+ contextPathsData?.smartContextAutoIncludes || [];
+ return updateContextPathsMutation.mutateAsync({
+ contextPaths: currentContextPaths.map(
+ ({ globPath }: { globPath: string }) => ({ globPath }),
+ ),
+ smartContextAutoIncludes: currentAutoIncludes.map(
+ ({ globPath }: { globPath: string }) => ({
+ globPath,
+ }),
+ ),
+ excludePaths: paths,
});
};
return {
contextPaths: contextPathsData?.contextPaths || [],
smartContextAutoIncludes: contextPathsData?.smartContextAutoIncludes || [],
+ excludePaths: contextPathsData?.excludePaths || [],
isLoading,
error,
updateContextPaths,
updateSmartContextAutoIncludes,
+ updateExcludePaths,
};
}
diff --git a/src/ipc/handlers/context_paths_handlers.ts b/src/ipc/handlers/context_paths_handlers.ts
index 415048c..ec2c1c6 100644
--- a/src/ipc/handlers/context_paths_handlers.ts
+++ b/src/ipc/handlers/context_paths_handlers.ts
@@ -39,10 +39,10 @@ export function registerContextPathsHandlers() {
const results: ContextPathResults = {
contextPaths: [],
smartContextAutoIncludes: [],
+ excludePaths: [],
};
- const { contextPaths, smartContextAutoIncludes } = validateChatContext(
- app.chatContext,
- );
+ const { contextPaths, smartContextAutoIncludes, excludePaths } =
+ validateChatContext(app.chatContext);
for (const contextPath of contextPaths) {
const { formattedOutput, files } = await extractCodebase({
appPath,
@@ -76,6 +76,23 @@ export function registerContextPathsHandlers() {
tokens: totalTokens,
});
}
+
+ for (const excludePath of excludePaths || []) {
+ const { formattedOutput, files } = await extractCodebase({
+ appPath,
+ chatContext: {
+ contextPaths: [excludePath],
+ smartContextAutoIncludes: [],
+ },
+ });
+ const totalTokens = estimateTokens(formattedOutput);
+
+ results.excludePaths.push({
+ ...excludePath,
+ files: files.length,
+ tokens: totalTokens,
+ });
+ }
return results;
},
);
diff --git a/src/ipc/utils/context_paths_utils.ts b/src/ipc/utils/context_paths_utils.ts
index fde60cb..664b389 100644
--- a/src/ipc/utils/context_paths_utils.ts
+++ b/src/ipc/utils/context_paths_utils.ts
@@ -8,6 +8,7 @@ export function validateChatContext(chatContext: unknown): AppChatContext {
return {
contextPaths: [],
smartContextAutoIncludes: [],
+ excludePaths: [],
};
}
@@ -20,6 +21,7 @@ export function validateChatContext(chatContext: unknown): AppChatContext {
return {
contextPaths: [],
smartContextAutoIncludes: [],
+ excludePaths: [],
};
}
}
diff --git a/src/lib/schemas.ts b/src/lib/schemas.ts
index 9f909db..47d5bc6 100644
--- a/src/lib/schemas.ts
+++ b/src/lib/schemas.ts
@@ -120,6 +120,7 @@ export type GlobPath = z.infer;
export const AppChatContextSchema = z.object({
contextPaths: z.array(GlobPathSchema),
smartContextAutoIncludes: z.array(GlobPathSchema),
+ excludePaths: z.array(GlobPathSchema).optional(),
});
export type AppChatContext = z.infer;
@@ -131,6 +132,7 @@ export type ContextPathResult = GlobPath & {
export type ContextPathResults = {
contextPaths: ContextPathResult[];
smartContextAutoIncludes: ContextPathResult[];
+ excludePaths: ContextPathResult[];
};
export const ReleaseChannelSchema = z.enum(["stable", "beta"]);
diff --git a/src/utils/codebase.ts b/src/utils/codebase.ts
index 1f885b5..7c7f7de 100644
--- a/src/utils/codebase.ts
+++ b/src/utils/codebase.ts
@@ -472,9 +472,10 @@ export async function extractCodebase({
}
// Collect files from contextPaths and smartContextAutoIncludes
- const { contextPaths, smartContextAutoIncludes } = chatContext;
+ const { contextPaths, smartContextAutoIncludes, excludePaths } = chatContext;
const includedFiles = new Set();
const autoIncludedFiles = new Set();
+ const excludedFiles = new Set();
// Add files from contextPaths
if (contextPaths && contextPaths.length > 0) {
@@ -509,6 +510,7 @@ export async function extractCodebase({
const matches = await glob(pattern, {
nodir: true,
absolute: true,
+ ignore: "**/node_modules/**",
});
matches.forEach((file) => {
const normalizedFile = path.normalize(file);
@@ -518,12 +520,36 @@ export async function extractCodebase({
}
}
+ // Add files from excludePaths
+ if (excludePaths && excludePaths.length > 0) {
+ for (const p of excludePaths) {
+ const pattern = createFullGlobPath({
+ appPath,
+ globPath: p.globPath,
+ });
+ const matches = await glob(pattern, {
+ nodir: true,
+ absolute: true,
+ ignore: "**/node_modules/**",
+ });
+ matches.forEach((file) => {
+ const normalizedFile = path.normalize(file);
+ excludedFiles.add(normalizedFile);
+ });
+ }
+ }
+
// Only filter files if contextPaths are provided
// If only smartContextAutoIncludes are provided, keep all files and just mark auto-includes as forced
if (contextPaths && contextPaths.length > 0) {
files = files.filter((file) => includedFiles.has(path.normalize(file)));
}
+ // Filter out excluded files (this takes precedence over include paths)
+ if (excludedFiles.size > 0) {
+ files = files.filter((file) => !excludedFiles.has(path.normalize(file)));
+ }
+
// Sort files by modification time (oldest first)
// This is important for cache-ability.
const sortedFiles = await sortFilesByModificationTime([...new Set(files)]);
@@ -543,7 +569,9 @@ export async function extractCodebase({
virtualFileSystem,
});
- const isForced = autoIncludedFiles.has(path.normalize(file));
+ const isForced =
+ autoIncludedFiles.has(path.normalize(file)) &&
+ !excludedFiles.has(path.normalize(file));
// Determine file content based on whether we should read it
let fileContent: string;