Merge branch 'next' of https://github.com/eyaltoledano/claude-task-master into joedanz/flexible-brand-rules
# Conflicts: # scripts/init.js # scripts/modules/commands.js # tests/integration/roo-files-inclusion.test.js # tests/integration/roo-init-functionality.test.js
This commit is contained in:
54
src/constants/paths.js
Normal file
54
src/constants/paths.js
Normal file
@@ -0,0 +1,54 @@
|
||||
/**
|
||||
* Path constants for Task Master application
|
||||
*/
|
||||
|
||||
// .taskmaster directory structure paths
|
||||
export const TASKMASTER_DIR = '.taskmaster';
|
||||
export const TASKMASTER_TASKS_DIR = '.taskmaster/tasks';
|
||||
export const TASKMASTER_DOCS_DIR = '.taskmaster/docs';
|
||||
export const TASKMASTER_REPORTS_DIR = '.taskmaster/reports';
|
||||
export const TASKMASTER_TEMPLATES_DIR = '.taskmaster/templates';
|
||||
|
||||
// Task Master configuration files
|
||||
export const TASKMASTER_CONFIG_FILE = '.taskmaster/config.json';
|
||||
export const LEGACY_CONFIG_FILE = '.taskmasterconfig';
|
||||
|
||||
// Task Master report files
|
||||
export const COMPLEXITY_REPORT_FILE =
|
||||
'.taskmaster/reports/task-complexity-report.json';
|
||||
export const LEGACY_COMPLEXITY_REPORT_FILE =
|
||||
'scripts/task-complexity-report.json';
|
||||
|
||||
// Task Master PRD file paths
|
||||
export const PRD_FILE = '.taskmaster/docs/prd.txt';
|
||||
export const LEGACY_PRD_FILE = 'scripts/prd.txt';
|
||||
|
||||
// Task Master template files
|
||||
export const EXAMPLE_PRD_FILE = '.taskmaster/templates/example_prd.txt';
|
||||
export const LEGACY_EXAMPLE_PRD_FILE = 'scripts/example_prd.txt';
|
||||
|
||||
// Task Master task file paths
|
||||
export const TASKMASTER_TASKS_FILE = '.taskmaster/tasks/tasks.json';
|
||||
export const LEGACY_TASKS_FILE = 'tasks/tasks.json';
|
||||
|
||||
// General project files (not Task Master specific but commonly used)
|
||||
export const ENV_EXAMPLE_FILE = '.env.example';
|
||||
export const GITIGNORE_FILE = '.gitignore';
|
||||
|
||||
// Task file naming pattern
|
||||
export const TASK_FILE_PREFIX = 'task_';
|
||||
export const TASK_FILE_EXTENSION = '.txt';
|
||||
|
||||
/**
|
||||
* Project markers used to identify a task-master project root
|
||||
* These files/directories indicate that a directory is a Task Master project
|
||||
*/
|
||||
export const PROJECT_MARKERS = [
|
||||
'.taskmaster', // New taskmaster directory
|
||||
LEGACY_CONFIG_FILE, // .taskmasterconfig
|
||||
'tasks.json', // Generic tasks file
|
||||
LEGACY_TASKS_FILE, // tasks/tasks.json (legacy location)
|
||||
TASKMASTER_TASKS_FILE, // .taskmaster/tasks/tasks.json (new location)
|
||||
'.git', // Git repository
|
||||
'.svn' // SVN repository
|
||||
];
|
||||
@@ -33,6 +33,20 @@ export const RULE_PROFILES = [
|
||||
'windsurf'
|
||||
];
|
||||
|
||||
/**
|
||||
* Centralized enum for all supported Roo agent modes
|
||||
* @type {string[]}
|
||||
* @description Available Roo Code IDE modes for rule generation
|
||||
*/
|
||||
export const ROO_MODES = [
|
||||
'architect',
|
||||
'ask',
|
||||
'boomerang',
|
||||
'code',
|
||||
'debug',
|
||||
'test'
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if a given rule profile is valid
|
||||
* @param {string} rulesProfile - The rule profile to check
|
||||
|
||||
31
src/utils/logger-utils.js
Normal file
31
src/utils/logger-utils.js
Normal file
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Logger utility functions for Task Master
|
||||
* Provides standardized logging patterns for both CLI and utility contexts
|
||||
*/
|
||||
|
||||
import { log as utilLog } from '../../scripts/modules/utils.js';
|
||||
|
||||
/**
|
||||
* Creates a standard logger object that wraps the utility log function
|
||||
* This provides a consistent logger interface across different parts of the application
|
||||
* @returns {Object} A logger object with standard logging methods (info, warn, error, debug, success)
|
||||
*/
|
||||
export function createStandardLogger() {
|
||||
return {
|
||||
info: (msg, ...args) => utilLog('info', msg, ...args),
|
||||
warn: (msg, ...args) => utilLog('warn', msg, ...args),
|
||||
error: (msg, ...args) => utilLog('error', msg, ...args),
|
||||
debug: (msg, ...args) => utilLog('debug', msg, ...args),
|
||||
success: (msg, ...args) => utilLog('success', msg, ...args)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a logger using either the provided logger or a default standard logger
|
||||
* This is the recommended pattern for functions that accept an optional logger parameter
|
||||
* @param {Object|null} providedLogger - Optional logger object passed from caller
|
||||
* @returns {Object} A logger object with standard logging methods
|
||||
*/
|
||||
export function getLoggerOrDefault(providedLogger = null) {
|
||||
return providedLogger || createStandardLogger();
|
||||
}
|
||||
446
src/utils/path-utils.js
Normal file
446
src/utils/path-utils.js
Normal file
@@ -0,0 +1,446 @@
|
||||
/**
|
||||
* Path utility functions for Task Master
|
||||
* Provides centralized path resolution logic for both CLI and MCP use cases
|
||||
*/
|
||||
|
||||
import path from 'path';
|
||||
import fs from 'fs';
|
||||
import {
|
||||
TASKMASTER_TASKS_FILE,
|
||||
LEGACY_TASKS_FILE,
|
||||
TASKMASTER_DOCS_DIR,
|
||||
TASKMASTER_REPORTS_DIR,
|
||||
COMPLEXITY_REPORT_FILE,
|
||||
TASKMASTER_CONFIG_FILE,
|
||||
LEGACY_CONFIG_FILE
|
||||
} from '../constants/paths.js';
|
||||
import { getLoggerOrDefault } from './logger-utils.js';
|
||||
|
||||
/**
|
||||
* Normalize project root to ensure it doesn't end with .taskmaster
|
||||
* This prevents double .taskmaster paths when using constants that include .taskmaster
|
||||
* @param {string} projectRoot - The project root path to normalize
|
||||
* @returns {string} - Normalized project root path
|
||||
*/
|
||||
export function normalizeProjectRoot(projectRoot) {
|
||||
if (!projectRoot) return projectRoot;
|
||||
|
||||
// Split the path into segments
|
||||
const segments = projectRoot.split(path.sep);
|
||||
|
||||
// Find the index of .taskmaster segment
|
||||
const taskmasterIndex = segments.findIndex(
|
||||
(segment) => segment === '.taskmaster'
|
||||
);
|
||||
|
||||
if (taskmasterIndex !== -1) {
|
||||
// If .taskmaster is found, return everything up to but not including .taskmaster
|
||||
const normalizedSegments = segments.slice(0, taskmasterIndex);
|
||||
return normalizedSegments.join(path.sep) || path.sep;
|
||||
}
|
||||
|
||||
return projectRoot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the project root directory by looking for project markers
|
||||
* @param {string} startDir - Directory to start searching from
|
||||
* @returns {string|null} - Project root path or null if not found
|
||||
*/
|
||||
export function findProjectRoot(startDir = process.cwd()) {
|
||||
const projectMarkers = [
|
||||
'.taskmaster',
|
||||
TASKMASTER_TASKS_FILE,
|
||||
'tasks.json',
|
||||
LEGACY_TASKS_FILE,
|
||||
'.git',
|
||||
'.svn',
|
||||
'package.json',
|
||||
'yarn.lock',
|
||||
'package-lock.json',
|
||||
'pnpm-lock.yaml'
|
||||
];
|
||||
|
||||
let currentDir = path.resolve(startDir);
|
||||
const rootDir = path.parse(currentDir).root;
|
||||
|
||||
while (currentDir !== rootDir) {
|
||||
// Check if current directory contains any project markers
|
||||
for (const marker of projectMarkers) {
|
||||
const markerPath = path.join(currentDir, marker);
|
||||
if (fs.existsSync(markerPath)) {
|
||||
return currentDir;
|
||||
}
|
||||
}
|
||||
currentDir = path.dirname(currentDir);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the tasks.json file path with fallback logic
|
||||
* @param {string|null} explicitPath - Explicit path provided by user (highest priority)
|
||||
* @param {Object|null} args - Args object from MCP args (optional)
|
||||
* @param {Object|null} log - Logger object (optional)
|
||||
* @returns {string|null} - Resolved tasks.json path or null if not found
|
||||
*/
|
||||
export function findTasksPath(explicitPath = null, args = null, log = null) {
|
||||
// Use the passed logger if available, otherwise use the default logger
|
||||
const logger = getLoggerOrDefault(log);
|
||||
|
||||
// 1. If explicit path is provided, use it (highest priority)
|
||||
if (explicitPath) {
|
||||
const resolvedPath = path.isAbsolute(explicitPath)
|
||||
? explicitPath
|
||||
: path.resolve(process.cwd(), explicitPath);
|
||||
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
logger.info?.(`Using explicit tasks path: ${resolvedPath}`);
|
||||
return resolvedPath;
|
||||
} else {
|
||||
logger.warn?.(
|
||||
`Explicit tasks path not found: ${resolvedPath}, trying fallbacks`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const possiblePaths = [
|
||||
path.join(projectRoot, TASKMASTER_TASKS_FILE), // .taskmaster/tasks/tasks.json (NEW)
|
||||
path.join(projectRoot, 'tasks.json'), // tasks.json in root (LEGACY)
|
||||
path.join(projectRoot, LEGACY_TASKS_FILE) // tasks/tasks.json (LEGACY)
|
||||
];
|
||||
|
||||
for (const tasksPath of possiblePaths) {
|
||||
if (fs.existsSync(tasksPath)) {
|
||||
logger.info?.(`Found tasks file at: ${tasksPath}`);
|
||||
|
||||
// Issue deprecation warning for legacy paths
|
||||
if (
|
||||
tasksPath.includes('tasks/tasks.json') &&
|
||||
!tasksPath.includes('.taskmaster')
|
||||
) {
|
||||
logger.warn?.(
|
||||
`⚠️ DEPRECATION WARNING: Found tasks.json in legacy location '${tasksPath}'. Please migrate to the new .taskmaster directory structure. Run 'task-master migrate' to automatically migrate your project.`
|
||||
);
|
||||
} else if (
|
||||
tasksPath.endsWith('tasks.json') &&
|
||||
!tasksPath.includes('.taskmaster') &&
|
||||
!tasksPath.includes('tasks/')
|
||||
) {
|
||||
logger.warn?.(
|
||||
`⚠️ DEPRECATION WARNING: Found tasks.json in legacy root location '${tasksPath}'. Please migrate to the new .taskmaster directory structure. Run 'task-master migrate' to automatically migrate your project.`
|
||||
);
|
||||
}
|
||||
|
||||
return tasksPath;
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn?.(`No tasks.json found in project: ${projectRoot}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the PRD document file path with fallback logic
|
||||
* @param {string|null} explicitPath - Explicit path provided by user (highest priority)
|
||||
* @param {Object|null} args - Args object for MCP context (optional)
|
||||
* @param {Object|null} log - Logger object (optional)
|
||||
* @returns {string|null} - Resolved PRD document path or null if not found
|
||||
*/
|
||||
export function findPRDPath(explicitPath = null, args = null, log = null) {
|
||||
const logger = getLoggerOrDefault(log);
|
||||
|
||||
// 1. If explicit path is provided, use it (highest priority)
|
||||
if (explicitPath) {
|
||||
const resolvedPath = path.isAbsolute(explicitPath)
|
||||
? explicitPath
|
||||
: path.resolve(process.cwd(), explicitPath);
|
||||
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
logger.info?.(`Using explicit PRD path: ${resolvedPath}`);
|
||||
return resolvedPath;
|
||||
} else {
|
||||
logger.warn?.(
|
||||
`Explicit PRD path not found: ${resolvedPath}, trying fallbacks`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const locations = [
|
||||
TASKMASTER_DOCS_DIR, // .taskmaster/docs/ (NEW)
|
||||
'scripts/', // Legacy location
|
||||
'' // Project root
|
||||
];
|
||||
|
||||
const fileNames = ['PRD.md', 'prd.md', 'PRD.txt', 'prd.txt'];
|
||||
|
||||
for (const location of locations) {
|
||||
for (const fileName of fileNames) {
|
||||
const prdPath = path.join(projectRoot, location, fileName);
|
||||
if (fs.existsSync(prdPath)) {
|
||||
logger.info?.(`Found PRD document at: ${prdPath}`);
|
||||
|
||||
// Issue deprecation warning for legacy paths
|
||||
if (location === 'scripts/' || location === '') {
|
||||
logger.warn?.(
|
||||
`⚠️ DEPRECATION WARNING: Found PRD file in legacy location '${prdPath}'. Please migrate to .taskmaster/docs/ directory. Run 'task-master migrate' to automatically migrate your project.`
|
||||
);
|
||||
}
|
||||
|
||||
return prdPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn?.(`No PRD document found in project: ${projectRoot}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the complexity report file path with fallback logic
|
||||
* @param {string|null} explicitPath - Explicit path provided by user (highest priority)
|
||||
* @param {Object|null} args - Args object for MCP context (optional)
|
||||
* @param {Object|null} log - Logger object (optional)
|
||||
* @returns {string|null} - Resolved complexity report path or null if not found
|
||||
*/
|
||||
export function findComplexityReportPath(
|
||||
explicitPath = null,
|
||||
args = null,
|
||||
log = null
|
||||
) {
|
||||
const logger = getLoggerOrDefault(log);
|
||||
|
||||
// 1. If explicit path is provided, use it (highest priority)
|
||||
if (explicitPath) {
|
||||
const resolvedPath = path.isAbsolute(explicitPath)
|
||||
? explicitPath
|
||||
: path.resolve(process.cwd(), explicitPath);
|
||||
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
logger.info?.(`Using explicit complexity report path: ${resolvedPath}`);
|
||||
return resolvedPath;
|
||||
} else {
|
||||
logger.warn?.(
|
||||
`Explicit complexity report path not found: ${resolvedPath}, trying fallbacks`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const locations = [
|
||||
TASKMASTER_REPORTS_DIR, // .taskmaster/reports/ (NEW)
|
||||
'scripts/', // Legacy location
|
||||
'' // Project root
|
||||
];
|
||||
|
||||
const fileNames = ['task-complexity-report.json', 'complexity-report.json'];
|
||||
|
||||
for (const location of locations) {
|
||||
for (const fileName of fileNames) {
|
||||
const reportPath = path.join(projectRoot, location, fileName);
|
||||
if (fs.existsSync(reportPath)) {
|
||||
logger.info?.(`Found complexity report at: ${reportPath}`);
|
||||
|
||||
// Issue deprecation warning for legacy paths
|
||||
if (location === 'scripts/' || location === '') {
|
||||
logger.warn?.(
|
||||
`⚠️ DEPRECATION WARNING: Found complexity report in legacy location '${reportPath}'. Please migrate to .taskmaster/reports/ directory. Run 'task-master migrate' to automatically migrate your project.`
|
||||
);
|
||||
}
|
||||
|
||||
return reportPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn?.(`No complexity report found in project: ${projectRoot}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve output path for tasks.json (create if needed)
|
||||
* @param {string|null} explicitPath - Explicit output path provided by user
|
||||
* @param {Object|null} args - Args object for MCP context (optional)
|
||||
* @param {Object|null} log - Logger object (optional)
|
||||
* @returns {string} - Resolved output path for tasks.json
|
||||
*/
|
||||
export function resolveTasksOutputPath(
|
||||
explicitPath = null,
|
||||
args = null,
|
||||
log = null
|
||||
) {
|
||||
const logger = getLoggerOrDefault(log);
|
||||
|
||||
// 1. If explicit path is provided, use it
|
||||
if (explicitPath) {
|
||||
const resolvedPath = path.isAbsolute(explicitPath)
|
||||
? explicitPath
|
||||
: path.resolve(process.cwd(), explicitPath);
|
||||
|
||||
logger.info?.(`Using explicit output path: ${resolvedPath}`);
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const rawProjectRoot =
|
||||
args?.projectRoot || findProjectRoot() || process.cwd();
|
||||
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Use new .taskmaster structure by default
|
||||
const defaultPath = path.join(projectRoot, TASKMASTER_TASKS_FILE);
|
||||
logger.info?.(`Using default output path: ${defaultPath}`);
|
||||
|
||||
// Ensure the directory exists
|
||||
const outputDir = path.dirname(defaultPath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
logger.info?.(`Creating tasks directory: ${outputDir}`);
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve output path for complexity report (create if needed)
|
||||
* @param {string|null} explicitPath - Explicit output path provided by user
|
||||
* @param {Object|null} args - Args object for MCP context (optional)
|
||||
* @param {Object|null} log - Logger object (optional)
|
||||
* @returns {string} - Resolved output path for complexity report
|
||||
*/
|
||||
export function resolveComplexityReportOutputPath(
|
||||
explicitPath = null,
|
||||
args = null,
|
||||
log = null
|
||||
) {
|
||||
const logger = getLoggerOrDefault(log);
|
||||
|
||||
// 1. If explicit path is provided, use it
|
||||
if (explicitPath) {
|
||||
const resolvedPath = path.isAbsolute(explicitPath)
|
||||
? explicitPath
|
||||
: path.resolve(process.cwd(), explicitPath);
|
||||
|
||||
logger.info?.(
|
||||
`Using explicit complexity report output path: ${resolvedPath}`
|
||||
);
|
||||
return resolvedPath;
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const rawProjectRoot =
|
||||
args?.projectRoot || findProjectRoot() || process.cwd();
|
||||
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Use new .taskmaster structure by default
|
||||
const defaultPath = path.join(projectRoot, COMPLEXITY_REPORT_FILE);
|
||||
logger.info?.(`Using default complexity report output path: ${defaultPath}`);
|
||||
|
||||
// Ensure the directory exists
|
||||
const outputDir = path.dirname(defaultPath);
|
||||
if (!fs.existsSync(outputDir)) {
|
||||
logger.info?.(`Creating reports directory: ${outputDir}`);
|
||||
fs.mkdirSync(outputDir, { recursive: true });
|
||||
}
|
||||
|
||||
return defaultPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the configuration file path with fallback logic
|
||||
* @param {string|null} explicitPath - Explicit path provided by user (highest priority)
|
||||
* @param {Object|null} args - Args object for MCP context (optional)
|
||||
* @param {Object|null} log - Logger object (optional)
|
||||
* @returns {string|null} - Resolved config file path or null if not found
|
||||
*/
|
||||
export function findConfigPath(explicitPath = null, args = null, log = null) {
|
||||
const logger = getLoggerOrDefault(log);
|
||||
|
||||
// 1. If explicit path is provided, use it (highest priority)
|
||||
if (explicitPath) {
|
||||
const resolvedPath = path.isAbsolute(explicitPath)
|
||||
? explicitPath
|
||||
: path.resolve(process.cwd(), explicitPath);
|
||||
|
||||
if (fs.existsSync(resolvedPath)) {
|
||||
logger.info?.(`Using explicit config path: ${resolvedPath}`);
|
||||
return resolvedPath;
|
||||
} else {
|
||||
logger.warn?.(
|
||||
`Explicit config path not found: ${resolvedPath}, trying fallbacks`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Try to get project root from args (MCP) or find it
|
||||
const rawProjectRoot = args?.projectRoot || findProjectRoot();
|
||||
|
||||
if (!rawProjectRoot) {
|
||||
logger.warn?.('Could not determine project root directory');
|
||||
return null;
|
||||
}
|
||||
|
||||
// 3. Normalize project root to prevent double .taskmaster paths
|
||||
const projectRoot = normalizeProjectRoot(rawProjectRoot);
|
||||
|
||||
// 4. Check possible locations in order of preference
|
||||
const possiblePaths = [
|
||||
path.join(projectRoot, TASKMASTER_CONFIG_FILE), // NEW location
|
||||
path.join(projectRoot, LEGACY_CONFIG_FILE) // LEGACY location
|
||||
];
|
||||
|
||||
for (const configPath of possiblePaths) {
|
||||
if (fs.existsSync(configPath)) {
|
||||
// Issue deprecation warning for legacy paths
|
||||
if (configPath?.endsWith(LEGACY_CONFIG_FILE)) {
|
||||
logger.warn?.(
|
||||
`⚠️ DEPRECATION WARNING: Found configuration in legacy location '${configPath}'. Please migrate to .taskmaster/config.json. Run 'task-master migrate' to automatically migrate your project.`
|
||||
);
|
||||
}
|
||||
|
||||
return configPath;
|
||||
}
|
||||
}
|
||||
|
||||
logger.warn?.(`No configuration file found in project: ${projectRoot}`);
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user