Allow manual context management (#376)
This commit is contained in:
@@ -4,6 +4,9 @@ import path from "node:path";
|
||||
import { isIgnored } from "isomorphic-git";
|
||||
import log from "electron-log";
|
||||
import { IS_TEST_BUILD } from "../ipc/utils/test_utils";
|
||||
import { glob } from "glob";
|
||||
import { AppChatContext } from "../lib/schemas";
|
||||
import { readSettings } from "@/main/settings";
|
||||
|
||||
const logger = log.scope("utils/codebase");
|
||||
|
||||
@@ -315,15 +318,31 @@ ${content}
|
||||
}
|
||||
}
|
||||
|
||||
export type CodebaseFile = {
|
||||
path: string;
|
||||
content: string;
|
||||
force?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract and format codebase files as a string to be included in prompts
|
||||
* @param appPath - Path to the codebase to extract
|
||||
* @returns Object containing formatted output and individual files
|
||||
*/
|
||||
export async function extractCodebase(appPath: string): Promise<{
|
||||
export async function extractCodebase({
|
||||
appPath,
|
||||
chatContext,
|
||||
}: {
|
||||
appPath: string;
|
||||
chatContext: AppChatContext;
|
||||
}): Promise<{
|
||||
formattedOutput: string;
|
||||
files: { path: string; content: string }[];
|
||||
files: CodebaseFile[];
|
||||
}> {
|
||||
const settings = readSettings();
|
||||
const isSmartContextEnabled =
|
||||
settings?.enableDyadPro && settings?.enableProSmartFilesContextMode;
|
||||
|
||||
try {
|
||||
await fsAsync.access(appPath);
|
||||
} catch {
|
||||
@@ -335,14 +354,67 @@ export async function extractCodebase(appPath: string): Promise<{
|
||||
const startTime = Date.now();
|
||||
|
||||
// Collect all relevant files
|
||||
const files = await collectFiles(appPath, appPath);
|
||||
let files = await collectFiles(appPath, appPath);
|
||||
|
||||
// Collect files from contextPaths and smartContextAutoIncludes
|
||||
const { contextPaths, smartContextAutoIncludes } = chatContext;
|
||||
const includedFiles = new Set<string>();
|
||||
const autoIncludedFiles = new Set<string>();
|
||||
|
||||
// Add files from contextPaths
|
||||
if (contextPaths && contextPaths.length > 0) {
|
||||
for (const p of contextPaths) {
|
||||
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);
|
||||
includedFiles.add(normalizedFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add files from smartContextAutoIncludes
|
||||
if (
|
||||
isSmartContextEnabled &&
|
||||
smartContextAutoIncludes &&
|
||||
smartContextAutoIncludes.length > 0
|
||||
) {
|
||||
for (const p of smartContextAutoIncludes) {
|
||||
const pattern = createFullGlobPath({
|
||||
appPath,
|
||||
globPath: p.globPath,
|
||||
});
|
||||
const matches = await glob(pattern, {
|
||||
nodir: true,
|
||||
absolute: true,
|
||||
});
|
||||
matches.forEach((file) => {
|
||||
const normalizedFile = path.normalize(file);
|
||||
autoIncludedFiles.add(normalizedFile);
|
||||
includedFiles.add(normalizedFile); // Also add to included files
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 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)));
|
||||
}
|
||||
|
||||
// Sort files by modification time (oldest first)
|
||||
// This is important for cache-ability.
|
||||
const sortedFiles = await sortFilesByModificationTime(files);
|
||||
const sortedFiles = await sortFilesByModificationTime([...new Set(files)]);
|
||||
|
||||
// Format files and collect individual file contents
|
||||
const filesArray: { path: string; content: string }[] = [];
|
||||
const filesArray: CodebaseFile[] = [];
|
||||
const formatPromises = sortedFiles.map(async (file) => {
|
||||
const formattedContent = await formatFile(file, appPath);
|
||||
|
||||
@@ -352,6 +424,9 @@ export async function extractCodebase(appPath: string): Promise<{
|
||||
// Why? Normalize Windows-style paths which causes lots of weird issues (e.g. Git commit)
|
||||
.split(path.sep)
|
||||
.join("/");
|
||||
|
||||
const isForced = autoIncludedFiles.has(path.normalize(file));
|
||||
|
||||
const fileContent = isOmittedFile(relativePath)
|
||||
? OMITTED_FILE_CONTENT
|
||||
: await readFileWithCache(file);
|
||||
@@ -359,6 +434,7 @@ export async function extractCodebase(appPath: string): Promise<{
|
||||
filesArray.push({
|
||||
path: relativePath,
|
||||
content: fileContent,
|
||||
force: isForced,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -413,3 +489,15 @@ async function sortFilesByModificationTime(files: string[]): Promise<string[]> {
|
||||
// Sort by modification time (oldest first)
|
||||
return fileStats.sort((a, b) => a.mtime - b.mtime).map((item) => item.file);
|
||||
}
|
||||
|
||||
function createFullGlobPath({
|
||||
appPath,
|
||||
globPath,
|
||||
}: {
|
||||
appPath: string;
|
||||
globPath: string;
|
||||
}): string {
|
||||
// By default the glob package treats "\" as an escape character.
|
||||
// We want the path to use forward slash for all platforms.
|
||||
return `${appPath.replace(/\\/g, "/")}/${globPath}`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user