glob extensions
This commit is contained in:
parent
ccad5c5407
commit
d5ad21d4fc
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -377,22 +377,12 @@ export const run = async (opts: IKBotTask): Promise<ProcessRunResult[]> => {
|
||||
ITEM: item,
|
||||
variables: { ITEM: item }
|
||||
}
|
||||
//override model if item is a model id
|
||||
const model = _models.find(m => m.id === item)
|
||||
if (model) {
|
||||
itemOpts.model = item
|
||||
}
|
||||
|
||||
let currentItemSpecificIncludes = [forward_slash(item)];
|
||||
const itemPathInfo = path.parse(item);
|
||||
|
||||
// Only add corresponding .cpp if --glob-extension=match-cpp is set
|
||||
if (opts.globExtension === 'match-cpp' && itemPathInfo.ext === '.h') {
|
||||
const cppFilePath = path.join(itemPathInfo.dir, `${itemPathInfo.name}.cpp`);
|
||||
if (exists(cppFilePath) && isFile(cppFilePath)) {
|
||||
currentItemSpecificIncludes.push(forward_slash(cppFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
itemOpts.include = [...opts.include, ...currentItemSpecificIncludes];
|
||||
|
||||
|
||||
@ -6,18 +6,11 @@ import { sync as dir } from '@polymech/fs/dir'
|
||||
import { createItem as toNode } from '@polymech/fs/inspect'
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { isFile, forward_slash } from '@polymech/commons'
|
||||
import { logger } from './index.js'
|
||||
import { lookup } from 'mime-types'
|
||||
import { globSync } from 'glob'
|
||||
import { globSync, hasMagic } from 'glob'
|
||||
import { EXCLUDE_GLOB, MAX_FILE_SIZE } from './constants.js'
|
||||
import { defaultMimeRegistry, IHandlerResult } from './mime-handlers.js'
|
||||
import { ChatCompletionContentPartImage } from 'openai/resources/index.mjs'
|
||||
import { IKBotTask, ICollector } from '@polymech/ai-tools'
|
||||
import { supported } from './commands/run-assistant.js'
|
||||
import { handleWebUrl } from './http.js'
|
||||
import { pathInfoEx } from '@polymech/commons'
|
||||
import { DEFAULT_ROOTS, DEFAULT_VARS } from '@polymech/commons'
|
||||
import { variables, generateSingleFileVariables } from './variables.js'
|
||||
import { IKBotTask} from '@polymech/ai-tools'
|
||||
import { resolveVariables } from '@polymech/commons'
|
||||
import { generateSingleFileVariables } from './variables.js'
|
||||
import { E_GlobExtensionType } from './zod_schema.js'
|
||||
|
||||
export const default_filters = {
|
||||
@ -44,26 +37,25 @@ const globExtensionPresets: Map<E_GlobExtensionType, string> = new Map([
|
||||
]);
|
||||
|
||||
const resolveAndGlobExtensionPattern = (
|
||||
initialFilePath: string,
|
||||
patternString: string,
|
||||
projectPath: string
|
||||
resolvedPatternString: string,
|
||||
): string[] => {
|
||||
const fileVars = generateSingleFileVariables(initialFilePath, projectPath);
|
||||
|
||||
let substitutedPattern = patternString;
|
||||
for (const key in fileVars) {
|
||||
const placeholder = new RegExp(`\\\${\\s*${key}\\s*}`, 'g');
|
||||
substitutedPattern = substitutedPattern.replace(placeholder, fileVars[key]);
|
||||
}
|
||||
|
||||
try {
|
||||
const foundFiles = globSync(substitutedPattern, {
|
||||
cwd: projectPath,
|
||||
absolute: false,
|
||||
nodir: true
|
||||
});
|
||||
return foundFiles.map(f => path.resolve(projectPath, f));
|
||||
if (!hasMagic(resolvedPatternString)) {
|
||||
// No magic characters, treat as a literal path
|
||||
if (default_filters.exists(resolvedPatternString) && default_filters.isFile(resolvedPatternString)) {
|
||||
return [forward_slash(resolvedPatternString)];
|
||||
}
|
||||
return []; // Literal path does not exist or is not a file
|
||||
} else {
|
||||
// Has magic characters, use globSync to expand
|
||||
const foundFiles = globSync(resolvedPatternString, {
|
||||
absolute: true, // Expecting resolvedPatternString to be absolute or glob to handle it
|
||||
nodir: true
|
||||
});
|
||||
return foundFiles.map(f => forward_slash(f));
|
||||
}
|
||||
} catch (e) {
|
||||
// console.warn(`Error processing globExtension pattern "${resolvedPatternString}": ${e.message}`);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
@ -118,13 +110,16 @@ export const glob = (
|
||||
const allFilesToConsider = new Set<string>(initialAbsoluteFiles);
|
||||
|
||||
if (options && typeof options.globExtension === 'string' && options.globExtension.trim() !== '') {
|
||||
let actualPatternToUse = options.globExtension;
|
||||
let rawPatternString = options.globExtension;
|
||||
if (globExtensionPresets.has(options.globExtension as E_GlobExtensionType)) {
|
||||
actualPatternToUse = globExtensionPresets.get(options.globExtension as E_GlobExtensionType)!;
|
||||
rawPatternString = globExtensionPresets.get(options.globExtension as E_GlobExtensionType)!;
|
||||
}
|
||||
|
||||
for (const initialFile of [...initialAbsoluteFiles]) {
|
||||
const additionalFiles = resolveAndGlobExtensionPattern(initialFile, actualPatternToUse, projectPath);
|
||||
const fileVars = generateSingleFileVariables(initialFile, projectPath);
|
||||
const fullyResolvedPattern = resolveVariables(rawPatternString, false, fileVars, false);
|
||||
|
||||
const additionalFiles = resolveAndGlobExtensionPattern(fullyResolvedPattern);
|
||||
additionalFiles.forEach(f => allFilesToConsider.add(f));
|
||||
}
|
||||
}
|
||||
@ -133,20 +128,16 @@ export const glob = (
|
||||
return false;
|
||||
}
|
||||
const relativeFilePath = path.relative(projectPath, absoluteFilePath);
|
||||
|
||||
const checkResult = globSync([forward_slash(relativeFilePath)], {
|
||||
cwd: projectPath,
|
||||
ignore: [...ignorePatterns],
|
||||
nodir: true,
|
||||
absolute: false
|
||||
});
|
||||
|
||||
if (checkResult.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
return { files: finalFiles.map(f => forward_slash(f)), webUrls }
|
||||
}
|
||||
|
||||
@ -20,7 +20,8 @@ describe('globExtension with complete_params output', () => {
|
||||
'PHApp-Profiles.cpp',
|
||||
'PHAppNetwork.cpp',
|
||||
'PHAppSettings.cpp',
|
||||
'PHAppWeb.cpp'
|
||||
'PHAppWeb.cpp',
|
||||
'PHApp.md'
|
||||
];
|
||||
const expectedAbsoluteFilePaths = expectedFileNames.map(f => path.normalize(path.resolve(testDataRoot, f)));
|
||||
|
||||
@ -28,27 +29,25 @@ describe('globExtension with complete_params output', () => {
|
||||
debug: () => {}, info: () => {}, warn: () => {}, error: () => {}, fatal: () => {},
|
||||
} as any;
|
||||
|
||||
it('should correctly include .h and related .cpp files using the "match-cpp" preset', async () => {
|
||||
it('should include .h, related .cpp files, and .md file using brace expansion in globExtension', async () => {
|
||||
const initialOpts: IKBotTask = {
|
||||
path: testDataRoot,
|
||||
include: ['*.h'],
|
||||
globExtension: 'match-cpp',
|
||||
globExtension: '${SRC_DIR}/${SRC_NAME}*.{cpp,md}',
|
||||
mode: E_Mode.COMPLETION,
|
||||
prompt: 'test-prompt-preset-match-cpp',
|
||||
prompt: 'test-prompt-brace-expansion',
|
||||
logger: mockLogger,
|
||||
};
|
||||
|
||||
// 1. Complete Options
|
||||
const completedOptions = await complete_options(initialOpts);
|
||||
delete initialOpts.client;
|
||||
expect(completedOptions).not.toBeNull();
|
||||
if (!completedOptions) return; // Guard for type safety
|
||||
// 2. Complete Messages (this is where glob and globExtension are applied)
|
||||
const { messages: gatheredMessages, files: gatheredFiles } = await complete_messages(initialOpts, completedOptions);
|
||||
|
||||
expect(gatheredMessages).toBeInstanceOf(Array);
|
||||
expect(gatheredMessages.length).toBeGreaterThan(0); // Prefs, Prompt + Files
|
||||
if (!completedOptions) return;
|
||||
|
||||
// 2. Complete Messages
|
||||
const { messages: gatheredMessages } = await complete_messages(initialOpts, completedOptions);
|
||||
expect(gatheredMessages).toBeInstanceOf(Array);
|
||||
|
||||
// 3. Complete Params
|
||||
const finalParams = await complete_params(completedOptions, gatheredMessages);
|
||||
expect(finalParams.messages).toBeInstanceOf(Array);
|
||||
@ -64,7 +63,6 @@ describe('globExtension with complete_params output', () => {
|
||||
return path.normalize(path.resolve(testDataRoot, metaRelPath));
|
||||
}
|
||||
if (msg.path && typeof msg.path === 'string') {
|
||||
// Assuming msg.path from complete_messages is relative to initialOpts.path (testDataRoot)
|
||||
return path.normalize(path.resolve(testDataRoot, msg.path));
|
||||
}
|
||||
return null;
|
||||
@ -73,14 +71,11 @@ describe('globExtension with complete_params output', () => {
|
||||
|
||||
const collectedPathsSet = new Set(collectedPathsFromFinalParams);
|
||||
|
||||
// console.log("Expected paths:", expectedAbsoluteFilePaths);
|
||||
// console.log("Collected paths from finalParams.messages:", collectedPathsFromFinalParams);
|
||||
|
||||
expectedAbsoluteFilePaths.forEach(expectedFile => {
|
||||
expect(collectedPathsSet.has(expectedFile), `Expected file ${path.basename(expectedFile)} (${expectedFile}) to be in finalParams.messages.`).toBe(true);
|
||||
});
|
||||
|
||||
expect(collectedPathsSet.size, "Number of unique collected files in finalParams.messages should match expected").toBe(expectedAbsoluteFilePaths.length);
|
||||
expect(collectedPathsSet.size).toBe(expectedAbsoluteFilePaths.length);
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user