Merge pull request #2 from RazvanBugoi/codex/extend-resolvepath-to-substitute-tokens
Improve installer path resolution
This commit is contained in:
commit
e0bb3a2b5b
|
|
@ -15,6 +15,7 @@ const path = require('node:path');
|
||||||
const fs = require('fs-extra');
|
const fs = require('fs-extra');
|
||||||
const { YamlXmlBuilder } = require('../tools/cli/lib/yaml-xml-builder');
|
const { YamlXmlBuilder } = require('../tools/cli/lib/yaml-xml-builder');
|
||||||
const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator');
|
const { ManifestGenerator } = require('../tools/cli/installers/lib/core/manifest-generator');
|
||||||
|
const { resolvePath } = require('../tools/cli/lib/agent/installer');
|
||||||
|
|
||||||
// ANSI colors
|
// ANSI colors
|
||||||
const colors = {
|
const colors = {
|
||||||
|
|
@ -138,18 +139,20 @@ async function runTests() {
|
||||||
console.log(`${colors.yellow}Test Suite 3: Path Variable Resolution${colors.reset}\n`);
|
console.log(`${colors.yellow}Test Suite 3: Path Variable Resolution${colors.reset}\n`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const builder = new YamlXmlBuilder();
|
const context = {
|
||||||
|
projectRoot: '/home/project',
|
||||||
|
bmadFolder: 'bmad-custom',
|
||||||
|
installed_path: '/home/project/bmad-custom/workflows/demo',
|
||||||
|
config_source: '/home/project/bmad-custom/config.yaml',
|
||||||
|
};
|
||||||
|
|
||||||
// Test path resolution logic (if exposed)
|
const testPath = '{project-root}/{bmad_folder}/workflows/{installed_path}/config?{config_source}';
|
||||||
// This would test {project-root}, {installed_path}, {config_source} resolution
|
const resolved = resolvePath(testPath, context);
|
||||||
|
|
||||||
const testPath = '{project-root}/bmad/bmm/config.yaml';
|
|
||||||
const expectedPattern = /\/bmad\/bmm\/config\.yaml$/;
|
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
true, // Placeholder - would test actual resolution
|
resolved === '/home/project/bmad-custom/workflows//home/project/bmad-custom/workflows/demo/config?/home/project/bmad-custom/config.yaml',
|
||||||
'Path variable resolution pattern matches expected format',
|
'Path variable resolution replaces all documented tokens',
|
||||||
'Note: This test validates path resolution logic exists',
|
`Resolved: ${resolved}`,
|
||||||
);
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
assert(false, 'Path resolution works', error.message);
|
assert(false, 'Path resolution works', error.message);
|
||||||
|
|
|
||||||
|
|
@ -36,13 +36,46 @@ function findBmadConfig(startPath = process.cwd()) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve path variables like {project-root} and {bmad-folder}
|
* Resolve path variables like {project-root}, {bmad-folder}, {installed_path}
|
||||||
|
* and {config_source} using provided context data
|
||||||
* @param {string} pathStr - Path with variables
|
* @param {string} pathStr - Path with variables
|
||||||
* @param {Object} context - Contains projectRoot, bmadFolder
|
* @param {Object} context - Contains projectRoot, bmadFolder, installed_path, config_source
|
||||||
* @returns {string} Resolved path
|
* @returns {string} Resolved path
|
||||||
*/
|
*/
|
||||||
function resolvePath(pathStr, context) {
|
function resolvePath(pathStr, context = {}) {
|
||||||
return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}', context.bmadFolder);
|
if (!pathStr || typeof pathStr !== 'string') return pathStr;
|
||||||
|
|
||||||
|
const normalizedContext = {
|
||||||
|
projectRoot: context.projectRoot,
|
||||||
|
bmadFolder: context.bmadFolder,
|
||||||
|
installed_path: context.installed_path || context.installedPath,
|
||||||
|
config_source: context.config_source || context.configSource,
|
||||||
|
};
|
||||||
|
|
||||||
|
const replacements = {
|
||||||
|
'{project-root}': normalizedContext.projectRoot,
|
||||||
|
'{project_root}': normalizedContext.projectRoot,
|
||||||
|
'{bmad-folder}': normalizedContext.bmadFolder,
|
||||||
|
'{bmad_folder}': normalizedContext.bmadFolder,
|
||||||
|
'{installed_path}': normalizedContext.installed_path,
|
||||||
|
'{config_source}': normalizedContext.config_source,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Also map any additional context keys directly to {key} tokens
|
||||||
|
for (const [key, value] of Object.entries(context)) {
|
||||||
|
if (value === undefined || value === null) continue;
|
||||||
|
|
||||||
|
const snakeKey = key.replace(/[A-Z]/g, (m) => `_${m.toLowerCase()}`);
|
||||||
|
const dashKey = snakeKey.replaceAll('_', '-');
|
||||||
|
|
||||||
|
replacements[`{${snakeKey}}`] ??= value;
|
||||||
|
replacements[`{${dashKey}}`] ??= value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object.entries(replacements).reduce((result, [token, value]) => {
|
||||||
|
if (value === undefined || value === null) return result;
|
||||||
|
return result.replaceAll(token, value);
|
||||||
|
}, pathStr);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -281,7 +314,13 @@ function installAgent(agentInfo, answers, targetPath, options = {}) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Find and copy sidecar folder
|
// Find and copy sidecar folder
|
||||||
const sidecarFiles = copyAgentSidecarFiles(agentInfo.path, agentSidecarDir, agentInfo.yamlFile);
|
const installContext = {
|
||||||
|
projectRoot: options.projectRoot || process.cwd(),
|
||||||
|
bmadFolder: options.bmadFolder || '.bmad',
|
||||||
|
...(options.installContext || {}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const sidecarFiles = copyAgentSidecarFiles(agentInfo.path, agentSidecarDir, agentInfo.yamlFile, installContext);
|
||||||
result.sidecarCopied = true;
|
result.sidecarCopied = true;
|
||||||
result.sidecarFiles = sidecarFiles;
|
result.sidecarFiles = sidecarFiles;
|
||||||
result.sidecarDir = agentSidecarDir;
|
result.sidecarDir = agentSidecarDir;
|
||||||
|
|
@ -335,10 +374,12 @@ function copySidecarFiles(sourceDir, targetDir, excludeYaml) {
|
||||||
* @param {string} excludeYaml - The .agent.yaml file to exclude
|
* @param {string} excludeYaml - The .agent.yaml file to exclude
|
||||||
* @returns {Array} List of copied files
|
* @returns {Array} List of copied files
|
||||||
*/
|
*/
|
||||||
function copyAgentSidecarFiles(sourceDir, targetSidecarDir, excludeYaml) {
|
function copyAgentSidecarFiles(sourceDir, targetSidecarDir, excludeYaml, pathContext = {}) {
|
||||||
const copied = [];
|
const copied = [];
|
||||||
const preserved = [];
|
const preserved = [];
|
||||||
|
|
||||||
|
const textExtensions = ['.md', '.mdx', '.yaml', '.yml', '.json', '.txt', '.xml', '.csv'];
|
||||||
|
|
||||||
// Find folders with "sidecar" in the name
|
// Find folders with "sidecar" in the name
|
||||||
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
const entries = fs.readdirSync(sourceDir, { withFileTypes: true });
|
||||||
|
|
||||||
|
|
@ -368,7 +409,22 @@ function copyAgentSidecarFiles(sourceDir, targetSidecarDir, excludeYaml) {
|
||||||
// File exists - preserve it
|
// File exists - preserve it
|
||||||
preserved.push(destPath);
|
preserved.push(destPath);
|
||||||
} else {
|
} else {
|
||||||
// File doesn't exist - copy it
|
// File doesn't exist - copy it with placeholder resolution when applicable
|
||||||
|
const ext = path.extname(srcPath).toLowerCase();
|
||||||
|
const shouldResolve = Object.keys(pathContext).length > 0 && textExtensions.includes(ext);
|
||||||
|
|
||||||
|
if (shouldResolve) {
|
||||||
|
try {
|
||||||
|
const content = fs.readFileSync(srcPath, 'utf8');
|
||||||
|
const resolved = resolvePath(content, pathContext);
|
||||||
|
fs.writeFileSync(destPath, resolved, 'utf8');
|
||||||
|
copied.push(destPath);
|
||||||
|
continue;
|
||||||
|
} catch {
|
||||||
|
// Fall back to raw copy below if resolution fails
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fs.copyFileSync(srcPath, destPath);
|
fs.copyFileSync(srcPath, destPath);
|
||||||
copied.push(destPath);
|
copied.push(destPath);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue