mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Update contextFileName to support an optional list of strings (#1001)
This commit is contained in:
@@ -74,7 +74,7 @@ export interface ConfigParameters {
|
||||
geminiMdFileCount?: number;
|
||||
approvalMode?: ApprovalMode;
|
||||
showMemoryUsage?: boolean;
|
||||
contextFileName?: string;
|
||||
contextFileName?: string | string[];
|
||||
geminiIgnorePatterns?: string[];
|
||||
accessibility?: AccessibilitySettings;
|
||||
telemetry?: boolean;
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
MemoryTool,
|
||||
setGeminiMdFilename,
|
||||
getCurrentGeminiMdFilename,
|
||||
getAllGeminiMdFilenames,
|
||||
DEFAULT_CONTEXT_FILENAME,
|
||||
} from './memoryTool.js';
|
||||
import * as fs from 'fs/promises';
|
||||
@@ -74,6 +75,13 @@ describe('MemoryTool', () => {
|
||||
setGeminiMdFilename('');
|
||||
expect(getCurrentGeminiMdFilename()).toBe(initialName);
|
||||
});
|
||||
|
||||
it('should handle an array of filenames', () => {
|
||||
const newNames = ['CUSTOM_CONTEXT.md', 'ANOTHER_CONTEXT.md'];
|
||||
setGeminiMdFilename(newNames);
|
||||
expect(getCurrentGeminiMdFilename()).toBe('CUSTOM_CONTEXT.md');
|
||||
expect(getAllGeminiMdFilenames()).toEqual(newNames);
|
||||
});
|
||||
});
|
||||
|
||||
describe('performAddMemoryEntry (static method)', () => {
|
||||
|
||||
@@ -51,18 +51,32 @@ export const MEMORY_SECTION_HEADER = '## Gemini Added Memories';
|
||||
|
||||
// This variable will hold the currently configured filename for GEMINI.md context files.
|
||||
// It defaults to DEFAULT_CONTEXT_FILENAME but can be overridden by setGeminiMdFilename.
|
||||
let currentGeminiMdFilename = DEFAULT_CONTEXT_FILENAME;
|
||||
let currentGeminiMdFilename: string | string[] = DEFAULT_CONTEXT_FILENAME;
|
||||
|
||||
export function setGeminiMdFilename(newFilename: string): void {
|
||||
if (newFilename && newFilename.trim() !== '') {
|
||||
export function setGeminiMdFilename(newFilename: string | string[]): void {
|
||||
if (Array.isArray(newFilename)) {
|
||||
if (newFilename.length > 0) {
|
||||
currentGeminiMdFilename = newFilename.map((name) => name.trim());
|
||||
}
|
||||
} else if (newFilename && newFilename.trim() !== '') {
|
||||
currentGeminiMdFilename = newFilename.trim();
|
||||
}
|
||||
}
|
||||
|
||||
export function getCurrentGeminiMdFilename(): string {
|
||||
if (Array.isArray(currentGeminiMdFilename)) {
|
||||
return currentGeminiMdFilename[0];
|
||||
}
|
||||
return currentGeminiMdFilename;
|
||||
}
|
||||
|
||||
export function getAllGeminiMdFilenames(): string[] {
|
||||
if (Array.isArray(currentGeminiMdFilename)) {
|
||||
return currentGeminiMdFilename;
|
||||
}
|
||||
return [currentGeminiMdFilename];
|
||||
}
|
||||
|
||||
interface SaveMemoryParams {
|
||||
fact: string;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { homedir } from 'os';
|
||||
import { bfsFileSearch } from './bfsFileSearch.js';
|
||||
import {
|
||||
GEMINI_CONFIG_DIR,
|
||||
getCurrentGeminiMdFilename,
|
||||
getAllGeminiMdFilenames,
|
||||
} from '../tools/memoryTool.js';
|
||||
|
||||
// Simple console logger, similar to the one previously in CLI's config.ts
|
||||
@@ -83,131 +83,135 @@ async function getGeminiMdFilePathsInternal(
|
||||
debugMode: boolean,
|
||||
extensionContextFilePaths: string[] = [],
|
||||
): Promise<string[]> {
|
||||
const resolvedCwd = path.resolve(currentWorkingDirectory);
|
||||
const resolvedHome = path.resolve(userHomePath);
|
||||
const globalMemoryPath = path.join(
|
||||
resolvedHome,
|
||||
GEMINI_CONFIG_DIR,
|
||||
getCurrentGeminiMdFilename(),
|
||||
);
|
||||
const paths: string[] = [];
|
||||
const allPaths = new Set<string>();
|
||||
const geminiMdFilenames = getAllGeminiMdFilenames();
|
||||
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Searching for ${getCurrentGeminiMdFilename()} starting from CWD: ${resolvedCwd}`,
|
||||
for (const geminiMdFilename of geminiMdFilenames) {
|
||||
const resolvedCwd = path.resolve(currentWorkingDirectory);
|
||||
const resolvedHome = path.resolve(userHomePath);
|
||||
const globalMemoryPath = path.join(
|
||||
resolvedHome,
|
||||
GEMINI_CONFIG_DIR,
|
||||
geminiMdFilename,
|
||||
);
|
||||
if (debugMode) logger.debug(`User home directory: ${resolvedHome}`);
|
||||
|
||||
try {
|
||||
await fs.access(globalMemoryPath, fsSync.constants.R_OK);
|
||||
paths.push(globalMemoryPath);
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Found readable global ${getCurrentGeminiMdFilename()}: ${globalMemoryPath}`,
|
||||
`Searching for ${geminiMdFilename} starting from CWD: ${resolvedCwd}`,
|
||||
);
|
||||
} catch {
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Global ${getCurrentGeminiMdFilename()} not found or not readable: ${globalMemoryPath}`,
|
||||
);
|
||||
}
|
||||
if (debugMode) logger.debug(`User home directory: ${resolvedHome}`);
|
||||
|
||||
const projectRoot = await findProjectRoot(resolvedCwd);
|
||||
if (debugMode)
|
||||
logger.debug(`Determined project root: ${projectRoot ?? 'None'}`);
|
||||
|
||||
const upwardPaths: string[] = [];
|
||||
let currentDir = resolvedCwd;
|
||||
// Determine the directory that signifies the top of the project or user-specific space.
|
||||
const ultimateStopDir = projectRoot
|
||||
? path.dirname(projectRoot)
|
||||
: path.dirname(resolvedHome);
|
||||
|
||||
while (currentDir && currentDir !== path.dirname(currentDir)) {
|
||||
// Loop until filesystem root or currentDir is empty
|
||||
if (debugMode) {
|
||||
logger.debug(
|
||||
`Checking for ${getCurrentGeminiMdFilename()} in (upward scan): ${currentDir}`,
|
||||
);
|
||||
try {
|
||||
await fs.access(globalMemoryPath, fsSync.constants.R_OK);
|
||||
allPaths.add(globalMemoryPath);
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Found readable global ${geminiMdFilename}: ${globalMemoryPath}`,
|
||||
);
|
||||
} catch {
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Global ${geminiMdFilename} not found or not readable: ${globalMemoryPath}`,
|
||||
);
|
||||
}
|
||||
|
||||
// Skip the global .gemini directory itself during upward scan from CWD,
|
||||
// as global is handled separately and explicitly first.
|
||||
if (currentDir === path.join(resolvedHome, GEMINI_CONFIG_DIR)) {
|
||||
const projectRoot = await findProjectRoot(resolvedCwd);
|
||||
if (debugMode)
|
||||
logger.debug(`Determined project root: ${projectRoot ?? 'None'}`);
|
||||
|
||||
const upwardPaths: string[] = [];
|
||||
let currentDir = resolvedCwd;
|
||||
// Determine the directory that signifies the top of the project or user-specific space.
|
||||
const ultimateStopDir = projectRoot
|
||||
? path.dirname(projectRoot)
|
||||
: path.dirname(resolvedHome);
|
||||
|
||||
while (currentDir && currentDir !== path.dirname(currentDir)) {
|
||||
// Loop until filesystem root or currentDir is empty
|
||||
if (debugMode) {
|
||||
logger.debug(
|
||||
`Upward scan reached global config dir path, stopping upward search here: ${currentDir}`,
|
||||
`Checking for ${geminiMdFilename} in (upward scan): ${currentDir}`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const potentialPath = path.join(currentDir, getCurrentGeminiMdFilename());
|
||||
try {
|
||||
await fs.access(potentialPath, fsSync.constants.R_OK);
|
||||
// Add to upwardPaths only if it's not the already added globalMemoryPath
|
||||
if (potentialPath !== globalMemoryPath) {
|
||||
upwardPaths.unshift(potentialPath);
|
||||
// Skip the global .gemini directory itself during upward scan from CWD,
|
||||
// as global is handled separately and explicitly first.
|
||||
if (currentDir === path.join(resolvedHome, GEMINI_CONFIG_DIR)) {
|
||||
if (debugMode) {
|
||||
logger.debug(
|
||||
`Found readable upward ${getCurrentGeminiMdFilename()}: ${potentialPath}`,
|
||||
`Upward scan reached global config dir path, stopping upward search here: ${currentDir}`,
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
const potentialPath = path.join(currentDir, geminiMdFilename);
|
||||
try {
|
||||
await fs.access(potentialPath, fsSync.constants.R_OK);
|
||||
// Add to upwardPaths only if it's not the already added globalMemoryPath
|
||||
if (potentialPath !== globalMemoryPath) {
|
||||
upwardPaths.unshift(potentialPath);
|
||||
if (debugMode) {
|
||||
logger.debug(
|
||||
`Found readable upward ${geminiMdFilename}: ${potentialPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if (debugMode) {
|
||||
logger.debug(
|
||||
`Upward ${geminiMdFilename} not found or not readable in: ${currentDir}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
if (debugMode) {
|
||||
logger.debug(
|
||||
`Upward ${getCurrentGeminiMdFilename()} not found or not readable in: ${currentDir}`,
|
||||
);
|
||||
|
||||
// Stop condition: if currentDir is the ultimateStopDir, break after this iteration.
|
||||
if (currentDir === ultimateStopDir) {
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Reached ultimate stop directory for upward scan: ${currentDir}`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
currentDir = path.dirname(currentDir);
|
||||
}
|
||||
upwardPaths.forEach((p) => allPaths.add(p));
|
||||
|
||||
// Stop condition: if currentDir is the ultimateStopDir, break after this iteration.
|
||||
if (currentDir === ultimateStopDir) {
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Reached ultimate stop directory for upward scan: ${currentDir}`,
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
currentDir = path.dirname(currentDir);
|
||||
}
|
||||
paths.push(...upwardPaths);
|
||||
|
||||
const downwardPaths = await bfsFileSearch(resolvedCwd, {
|
||||
fileName: getCurrentGeminiMdFilename(),
|
||||
maxDirs: MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY,
|
||||
debug: debugMode,
|
||||
respectGitIgnore: true,
|
||||
projectRoot: projectRoot || resolvedCwd,
|
||||
});
|
||||
downwardPaths.sort(); // Sort for consistent ordering, though hierarchy might be more complex
|
||||
if (debugMode && downwardPaths.length > 0)
|
||||
logger.debug(
|
||||
`Found downward ${getCurrentGeminiMdFilename()} files (sorted): ${JSON.stringify(
|
||||
downwardPaths,
|
||||
)}`,
|
||||
);
|
||||
// Add downward paths only if they haven't been included already (e.g. from upward scan)
|
||||
for (const dPath of downwardPaths) {
|
||||
if (!paths.includes(dPath)) {
|
||||
paths.push(dPath);
|
||||
const downwardPaths = await bfsFileSearch(resolvedCwd, {
|
||||
fileName: geminiMdFilename,
|
||||
maxDirs: MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY,
|
||||
debug: debugMode,
|
||||
respectGitIgnore: true,
|
||||
projectRoot: projectRoot || resolvedCwd,
|
||||
});
|
||||
downwardPaths.sort(); // Sort for consistent ordering, though hierarchy might be more complex
|
||||
if (debugMode && downwardPaths.length > 0)
|
||||
logger.debug(
|
||||
`Found downward ${geminiMdFilename} files (sorted): ${JSON.stringify(
|
||||
downwardPaths,
|
||||
)}`,
|
||||
);
|
||||
// Add downward paths only if they haven't been included already (e.g. from upward scan)
|
||||
for (const dPath of downwardPaths) {
|
||||
allPaths.add(dPath);
|
||||
}
|
||||
}
|
||||
|
||||
// Add extension context file paths
|
||||
for (const extensionPath of extensionContextFilePaths) {
|
||||
if (!paths.includes(extensionPath)) {
|
||||
paths.push(extensionPath);
|
||||
}
|
||||
allPaths.add(extensionPath);
|
||||
}
|
||||
|
||||
const finalPaths = Array.from(allPaths);
|
||||
|
||||
if (debugMode)
|
||||
logger.debug(
|
||||
`Final ordered ${getCurrentGeminiMdFilename()} paths to read: ${JSON.stringify(paths)}`,
|
||||
`Final ordered ${getAllGeminiMdFilenames()} paths to read: ${JSON.stringify(
|
||||
finalPaths,
|
||||
)}`,
|
||||
);
|
||||
return paths;
|
||||
return finalPaths;
|
||||
}
|
||||
|
||||
async function readGeminiMdFiles(
|
||||
@@ -228,7 +232,7 @@ async function readGeminiMdFiles(
|
||||
if (!isTestEnv) {
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
logger.warn(
|
||||
`Warning: Could not read ${getCurrentGeminiMdFilename()} file at ${filePath}. Error: ${message}`,
|
||||
`Warning: Could not read ${getAllGeminiMdFilenames()} file at ${filePath}. Error: ${message}`,
|
||||
);
|
||||
}
|
||||
results.push({ filePath, content: null }); // Still include it with null content
|
||||
|
||||
Reference in New Issue
Block a user