Compare commits
4 Commits
ralph/feat
...
fix/more.n
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee2d9921c2 | ||
|
|
fff2172a30 | ||
|
|
5fa7d2ecaa | ||
|
|
8e9d00e03d |
@@ -1500,10 +1500,16 @@ function registerCommands(programInstance) {
|
|||||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||||
.action(async (options) => {
|
.action(async (options) => {
|
||||||
// Initialize TaskMaster
|
// Initialize TaskMaster
|
||||||
const taskMaster = initTaskMaster({
|
const initOptions = {
|
||||||
tasksPath: options.file || true,
|
tasksPath: options.file || true
|
||||||
complexityReportPath: options.report || false
|
};
|
||||||
});
|
|
||||||
|
// Only pass complexityReportPath if user provided a custom path
|
||||||
|
if (options.report && options.report !== COMPLEXITY_REPORT_FILE) {
|
||||||
|
initOptions.complexityReportPath = options.report;
|
||||||
|
}
|
||||||
|
|
||||||
|
const taskMaster = initTaskMaster(initOptions);
|
||||||
|
|
||||||
const statusFilter = options.status;
|
const statusFilter = options.status;
|
||||||
const withSubtasks = options.withSubtasks || false;
|
const withSubtasks = options.withSubtasks || false;
|
||||||
@@ -1690,7 +1696,7 @@ function registerCommands(programInstance) {
|
|||||||
const outputPath =
|
const outputPath =
|
||||||
options.output === COMPLEXITY_REPORT_FILE && targetTag !== 'master'
|
options.output === COMPLEXITY_REPORT_FILE && targetTag !== 'master'
|
||||||
? baseOutputPath.replace('.json', `_${targetTag}.json`)
|
? baseOutputPath.replace('.json', `_${targetTag}.json`)
|
||||||
: baseOutputPath;
|
: options.output || baseOutputPath;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
chalk.blue(
|
chalk.blue(
|
||||||
@@ -1770,6 +1776,11 @@ function registerCommands(programInstance) {
|
|||||||
)
|
)
|
||||||
.option('--tag <tag>', 'Specify tag context for task operations')
|
.option('--tag <tag>', 'Specify tag context for task operations')
|
||||||
.action(async (prompt, options) => {
|
.action(async (prompt, options) => {
|
||||||
|
// Initialize TaskMaster
|
||||||
|
const taskMaster = initTaskMaster({
|
||||||
|
tasksPath: options.file || true
|
||||||
|
});
|
||||||
|
|
||||||
// Parameter validation
|
// Parameter validation
|
||||||
if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) {
|
if (!prompt || typeof prompt !== 'string' || prompt.trim().length === 0) {
|
||||||
console.error(
|
console.error(
|
||||||
@@ -2211,6 +2222,8 @@ ${result.result}
|
|||||||
tasksPath: options.file || true
|
tasksPath: options.file || true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const projectRoot = taskMaster.getProjectRoot();
|
||||||
|
|
||||||
// Show current tag context
|
// Show current tag context
|
||||||
displayCurrentTagIndicator(
|
displayCurrentTagIndicator(
|
||||||
options.tag || getCurrentTag(taskMaster.getProjectRoot()) || 'master'
|
options.tag || getCurrentTag(taskMaster.getProjectRoot()) || 'master'
|
||||||
@@ -3462,6 +3475,9 @@ Examples:
|
|||||||
const taskMaster = initTaskMaster({
|
const taskMaster = initTaskMaster({
|
||||||
tasksPath: options.file || false
|
tasksPath: options.file || false
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const projectRoot = taskMaster.getProjectRoot();
|
||||||
|
|
||||||
// Validate flags: cannot use multiple provider flags simultaneously
|
// Validate flags: cannot use multiple provider flags simultaneously
|
||||||
const providerFlags = [
|
const providerFlags = [
|
||||||
options.openrouter,
|
options.openrouter,
|
||||||
|
|||||||
@@ -4,7 +4,10 @@ import chalk from 'chalk';
|
|||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { fileURLToPath } from 'url';
|
import { fileURLToPath } from 'url';
|
||||||
import { log, findProjectRoot, resolveEnvVariable, isEmpty } from './utils.js';
|
import { log, findProjectRoot, resolveEnvVariable, isEmpty } from './utils.js';
|
||||||
import { LEGACY_CONFIG_FILE } from '../../src/constants/paths.js';
|
import {
|
||||||
|
LEGACY_CONFIG_FILE,
|
||||||
|
TASKMASTER_DIR
|
||||||
|
} from '../../src/constants/paths.js';
|
||||||
import { findConfigPath } from '../../src/utils/path-utils.js';
|
import { findConfigPath } from '../../src/utils/path-utils.js';
|
||||||
import {
|
import {
|
||||||
VALIDATED_PROVIDERS,
|
VALIDATED_PROVIDERS,
|
||||||
@@ -99,17 +102,30 @@ function _loadAndValidateConfig(explicitRoot = null) {
|
|||||||
if (rootToUse) {
|
if (rootToUse) {
|
||||||
configSource = `found root (${rootToUse})`;
|
configSource = `found root (${rootToUse})`;
|
||||||
} else {
|
} else {
|
||||||
// No root found, return defaults immediately
|
// No root found, use current working directory as fallback
|
||||||
return defaults;
|
// This prevents infinite loops during initialization
|
||||||
|
rootToUse = process.cwd();
|
||||||
|
configSource = `current directory (${rootToUse}) - no project markers found`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ---> End find project root logic <---
|
// ---> End find project root logic <---
|
||||||
|
|
||||||
// --- Find configuration file using centralized path utility ---
|
// --- Find configuration file ---
|
||||||
const configPath = findConfigPath(null, { projectRoot: rootToUse });
|
let configPath = null;
|
||||||
let config = { ...defaults }; // Start with a deep copy of defaults
|
let config = { ...defaults }; // Start with a deep copy of defaults
|
||||||
let configExists = false;
|
let configExists = false;
|
||||||
|
|
||||||
|
// During initialization (no project markers), skip config file search entirely
|
||||||
|
const hasProjectMarkers =
|
||||||
|
fs.existsSync(path.join(rootToUse, TASKMASTER_DIR)) ||
|
||||||
|
fs.existsSync(path.join(rootToUse, LEGACY_CONFIG_FILE));
|
||||||
|
|
||||||
|
if (hasProjectMarkers) {
|
||||||
|
// Only try to find config if we have project markers
|
||||||
|
// This prevents the repeated warnings during init
|
||||||
|
configPath = findConfigPath(null, { projectRoot: rootToUse });
|
||||||
|
}
|
||||||
|
|
||||||
if (configPath) {
|
if (configPath) {
|
||||||
configExists = true;
|
configExists = true;
|
||||||
const isLegacy = configPath.endsWith(LEGACY_CONFIG_FILE);
|
const isLegacy = configPath.endsWith(LEGACY_CONFIG_FILE);
|
||||||
@@ -199,11 +215,22 @@ function _loadAndValidateConfig(explicitRoot = null) {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
console.warn(
|
// Don't warn about missing config during initialization
|
||||||
chalk.yellow(
|
// Only warn if this looks like an existing project (has .taskmaster dir or legacy config marker)
|
||||||
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
|
const hasTaskmasterDir = fs.existsSync(
|
||||||
)
|
path.join(rootToUse, TASKMASTER_DIR)
|
||||||
);
|
);
|
||||||
|
const hasLegacyMarker = fs.existsSync(
|
||||||
|
path.join(rootToUse, LEGACY_CONFIG_FILE)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (hasTaskmasterDir || hasLegacyMarker) {
|
||||||
|
console.warn(
|
||||||
|
chalk.yellow(
|
||||||
|
`Warning: Configuration file not found at derived root (${rootToUse}). Using defaults.`
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Keep config as defaults
|
// Keep config as defaults
|
||||||
config = { ...defaults };
|
config = { ...defaults };
|
||||||
|
|||||||
@@ -469,7 +469,7 @@ async function expandTask(
|
|||||||
complexityReasoningContext: complexityReasoningContext,
|
complexityReasoningContext: complexityReasoningContext,
|
||||||
gatheredContext: gatheredContext,
|
gatheredContext: gatheredContext,
|
||||||
useResearch: useResearch,
|
useResearch: useResearch,
|
||||||
expansionPrompt: taskAnalysis?.expansionPrompt || null
|
expansionPrompt: taskAnalysis?.expansionPrompt || undefined
|
||||||
};
|
};
|
||||||
|
|
||||||
let variantKey = 'default';
|
let variantKey = 'default';
|
||||||
|
|||||||
@@ -205,12 +205,10 @@ async function performResearch(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Select variant based on detail level
|
// Load prompts - the research template handles detail level internally
|
||||||
const variantKey = detailLevel; // 'low', 'medium', or 'high'
|
|
||||||
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
const { systemPrompt, userPrompt } = await promptManager.loadPrompt(
|
||||||
'research',
|
'research',
|
||||||
promptParams,
|
promptParams
|
||||||
variantKey
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// Count tokens for system and user prompts
|
// Count tokens for system and user prompts
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ async function updateSubtaskById(
|
|||||||
title: parentTask.subtasks[subtaskIndex - 1].title,
|
title: parentTask.subtasks[subtaskIndex - 1].title,
|
||||||
status: parentTask.subtasks[subtaskIndex - 1].status
|
status: parentTask.subtasks[subtaskIndex - 1].status
|
||||||
}
|
}
|
||||||
: null;
|
: undefined;
|
||||||
const nextSubtask =
|
const nextSubtask =
|
||||||
subtaskIndex < parentTask.subtasks.length - 1
|
subtaskIndex < parentTask.subtasks.length - 1
|
||||||
? {
|
? {
|
||||||
@@ -222,7 +222,7 @@ async function updateSubtaskById(
|
|||||||
title: parentTask.subtasks[subtaskIndex + 1].title,
|
title: parentTask.subtasks[subtaskIndex + 1].title,
|
||||||
status: parentTask.subtasks[subtaskIndex + 1].status
|
status: parentTask.subtasks[subtaskIndex + 1].status
|
||||||
}
|
}
|
||||||
: null;
|
: undefined;
|
||||||
|
|
||||||
// Build prompts using PromptManager
|
// Build prompts using PromptManager
|
||||||
const promptManager = getPromptManager();
|
const promptManager = getPromptManager();
|
||||||
|
|||||||
@@ -218,7 +218,16 @@ export function initTaskMaster(overrides = {}) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remaining paths - only resolve if key exists in overrides
|
// Always set default paths first
|
||||||
|
// These can be overridden below if needed
|
||||||
|
paths.configPath = path.join(paths.projectRoot, TASKMASTER_CONFIG_FILE);
|
||||||
|
paths.statePath = path.join(
|
||||||
|
paths.taskMasterDir || path.join(paths.projectRoot, TASKMASTER_DIR),
|
||||||
|
'state.json'
|
||||||
|
);
|
||||||
|
paths.tasksPath = path.join(paths.projectRoot, TASKMASTER_TASKS_FILE);
|
||||||
|
|
||||||
|
// Handle overrides - only validate/resolve if explicitly provided
|
||||||
if ('configPath' in overrides) {
|
if ('configPath' in overrides) {
|
||||||
paths.configPath = resolvePath(
|
paths.configPath = resolvePath(
|
||||||
'config file',
|
'config file',
|
||||||
|
|||||||
@@ -557,7 +557,10 @@ describe('getConfig Tests', () => {
|
|||||||
// Assert
|
// Assert
|
||||||
expect(config).toEqual(DEFAULT_CONFIG);
|
expect(config).toEqual(DEFAULT_CONFIG);
|
||||||
expect(mockFindProjectRoot).not.toHaveBeenCalled(); // Explicit root provided
|
expect(mockFindProjectRoot).not.toHaveBeenCalled(); // Explicit root provided
|
||||||
expect(fsExistsSyncSpy).toHaveBeenCalledWith(MOCK_CONFIG_PATH);
|
// The implementation checks for .taskmaster directory first
|
||||||
|
expect(fsExistsSyncSpy).toHaveBeenCalledWith(
|
||||||
|
path.join(MOCK_PROJECT_ROOT, '.taskmaster')
|
||||||
|
);
|
||||||
expect(fsReadFileSyncSpy).not.toHaveBeenCalled(); // No read if file doesn't exist
|
expect(fsReadFileSyncSpy).not.toHaveBeenCalled(); // No read if file doesn't exist
|
||||||
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
expect(consoleWarnSpy).toHaveBeenCalledWith(
|
||||||
expect.stringContaining('not found at provided project root')
|
expect.stringContaining('not found at provided project root')
|
||||||
|
|||||||
@@ -184,7 +184,7 @@ jest.unstable_mockModule(
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Import the mocked modules
|
// Import the mocked modules
|
||||||
const { readJSON, writeJSON, log, CONFIG } = await import(
|
const { readJSON, writeJSON, log, CONFIG, findTaskById } = await import(
|
||||||
'../../../../../scripts/modules/utils.js'
|
'../../../../../scripts/modules/utils.js'
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -265,6 +265,13 @@ describe('analyzeTaskComplexity', () => {
|
|||||||
_rawTaggedData: sampleTasks
|
_rawTaggedData: sampleTasks
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Mock findTaskById to return the expected structure
|
||||||
|
findTaskById.mockImplementation((tasks, taskId) => {
|
||||||
|
const task = tasks?.find((t) => t.id === parseInt(taskId));
|
||||||
|
return { task: task || null, originalSubtaskCount: null };
|
||||||
|
});
|
||||||
|
|
||||||
generateTextService.mockResolvedValue(sampleApiResponse);
|
generateTextService.mockResolvedValue(sampleApiResponse);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ describe('initTaskMaster', () => {
|
|||||||
expect(taskMaster.getTasksPath()).toBeNull();
|
expect(taskMaster.getTasksPath()).toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('should return null when optional files not specified in overrides', () => {
|
test('should return default paths when optional files not specified in overrides', () => {
|
||||||
// Arrange - Remove all optional files
|
// Arrange - Remove all optional files
|
||||||
fs.unlinkSync(tasksPath);
|
fs.unlinkSync(tasksPath);
|
||||||
fs.unlinkSync(configPath);
|
fs.unlinkSync(configPath);
|
||||||
@@ -257,10 +257,16 @@ describe('initTaskMaster', () => {
|
|||||||
// Act - Don't specify any optional paths
|
// Act - Don't specify any optional paths
|
||||||
const taskMaster = initTaskMaster({});
|
const taskMaster = initTaskMaster({});
|
||||||
|
|
||||||
// Assert
|
// Assert - Should return absolute paths with default locations
|
||||||
expect(taskMaster.getTasksPath()).toBeUndefined();
|
expect(taskMaster.getTasksPath()).toBe(
|
||||||
expect(taskMaster.getConfigPath()).toBeUndefined();
|
path.join(tempDir, TASKMASTER_TASKS_FILE)
|
||||||
expect(taskMaster.getStatePath()).toBeUndefined();
|
);
|
||||||
|
expect(taskMaster.getConfigPath()).toBe(
|
||||||
|
path.join(tempDir, TASKMASTER_CONFIG_FILE)
|
||||||
|
);
|
||||||
|
expect(taskMaster.getStatePath()).toBe(
|
||||||
|
path.join(tempDir, TASKMASTER_DIR, 'state.json')
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -415,11 +421,19 @@ describe('initTaskMaster', () => {
|
|||||||
// Assert
|
// Assert
|
||||||
expect(taskMaster.getProjectRoot()).toBe(tempDir);
|
expect(taskMaster.getProjectRoot()).toBe(tempDir);
|
||||||
expect(taskMaster.getTaskMasterDir()).toBe(taskMasterDir);
|
expect(taskMaster.getTaskMasterDir()).toBe(taskMasterDir);
|
||||||
expect(taskMaster.getTasksPath()).toBeUndefined();
|
// Default paths are always set for tasks, config, and state
|
||||||
|
expect(taskMaster.getTasksPath()).toBe(
|
||||||
|
path.join(tempDir, TASKMASTER_TASKS_FILE)
|
||||||
|
);
|
||||||
|
expect(taskMaster.getConfigPath()).toBe(
|
||||||
|
path.join(tempDir, TASKMASTER_CONFIG_FILE)
|
||||||
|
);
|
||||||
|
expect(taskMaster.getStatePath()).toBe(
|
||||||
|
path.join(taskMasterDir, 'state.json')
|
||||||
|
);
|
||||||
|
// PRD and complexity report paths are undefined when not provided
|
||||||
expect(taskMaster.getPrdPath()).toBeUndefined();
|
expect(taskMaster.getPrdPath()).toBeUndefined();
|
||||||
expect(taskMaster.getComplexityReportPath()).toBeUndefined();
|
expect(taskMaster.getComplexityReportPath()).toBeUndefined();
|
||||||
expect(taskMaster.getConfigPath()).toBeUndefined();
|
|
||||||
expect(taskMaster.getStatePath()).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user