chore: apply nitpicks of 0.36.0 post-merge

This commit is contained in:
Ralph Khreish
2025-11-27 20:48:18 +01:00
parent 9e934650b7
commit a852cd028e
9 changed files with 83 additions and 15 deletions

View File

@@ -70,6 +70,7 @@ export function parseChangelogHighlights(
}
// Find the version section
// Version validated above (semver format), safe to construct regex
const versionRegex = new RegExp(
`## ${version.replace(/\./g, '\\.')}\\s*\\n`,
'i'

View File

@@ -101,7 +101,7 @@ export async function downloadTarballWithProgress(
const options = {
hostname: url.hostname,
path: url.pathname,
path: url.pathname + url.search,
method: 'GET',
headers: {
'User-Agent': `task-master-ai/${version}`

View File

@@ -26,6 +26,9 @@ const INSTALL_PHASES = [
/** Total weight for percentage calculation */
const TOTAL_WEIGHT = INSTALL_PHASES.reduce((sum, p) => sum + p.weight, 0);
/** Timeout for npm install operations (5 minutes) */
const INSTALL_TIMEOUT_MS = 5 * 60 * 1000;
/**
* Parse npm output to determine current installation phase index
*/
@@ -152,6 +155,21 @@ async function installFromTarball(tarballPath: string): Promise<boolean> {
);
let errorOutput = '';
let completed = false;
// Set timeout for npm install
const installTimeout = setTimeout(() => {
if (completed) return;
completed = true;
installProcess.kill('SIGTERM');
clearInterval(progressInterval);
progressBar.stop();
console.log(chalk.red('✖') + chalk.red(' Installation timed out'));
if (fs.existsSync(tarballPath)) {
fs.unlink(tarballPath, () => {});
}
resolve(false);
}, INSTALL_TIMEOUT_MS);
// Parse stdout for progress hints
installProcess.stdout.on('data', (data) => {
@@ -188,6 +206,9 @@ async function installFromTarball(tarballPath: string): Promise<boolean> {
});
installProcess.on('close', (code) => {
if (completed) return;
completed = true;
clearTimeout(installTimeout);
clearInterval(progressInterval);
// Complete the progress bar
@@ -227,6 +248,9 @@ async function installFromTarball(tarballPath: string): Promise<boolean> {
});
installProcess.on('error', (error) => {
if (completed) return;
completed = true;
clearTimeout(installTimeout);
clearInterval(progressInterval);
progressBar.stop();
@@ -270,6 +294,21 @@ async function performDirectNpmInstall(
);
let errorOutput = '';
let completed = false;
// Set timeout for npm install
const installTimeout = setTimeout(() => {
if (completed) return;
completed = true;
updateProcess.kill('SIGTERM');
spinner.fail(chalk.red('Auto-update timed out'));
console.log(
chalk.cyan(
`Please run manually: npm install -g task-master-ai@${latestVersion}`
)
);
resolve(false);
}, INSTALL_TIMEOUT_MS);
updateProcess.stdout.on('data', () => {
spinner.text = chalk.blue(
@@ -282,6 +321,10 @@ async function performDirectNpmInstall(
});
updateProcess.on('close', (code) => {
if (completed) return;
completed = true;
clearTimeout(installTimeout);
if (code === 0) {
spinner.succeed(
chalk.green(
@@ -304,6 +347,10 @@ async function performDirectNpmInstall(
});
updateProcess.on('error', (error) => {
if (completed) return;
completed = true;
clearTimeout(installTimeout);
spinner.fail(chalk.red('Auto-update failed'));
console.log(chalk.red('Error:'), error.message);
console.log(

View File

@@ -77,6 +77,11 @@ describe('generate MCP tool', () => {
// Parse the MCP protocol response: { content: [{ type: "text", text: "<json>" }] }
const mcpResponse = JSON.parse(output);
if (!mcpResponse.content?.[0]?.text) {
throw new Error(
`Unexpected MCP response format: ${JSON.stringify(mcpResponse)}`
);
}
const resultText = mcpResponse.content[0].text;
return JSON.parse(resultText);
};

6
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "task-master-ai",
"version": "0.35.0",
"version": "0.36.0-rc.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "task-master-ai",
"version": "0.35.0",
"version": "0.36.0-rc.0",
"license": "MIT WITH Commons-Clause",
"workspaces": [
"apps/*",
@@ -194,7 +194,7 @@
"react-dom": "^19.0.0",
"tailwind-merge": "^3.3.1",
"tailwindcss": "4.1.11",
"task-master-ai": "*",
"task-master-ai": "0.36.0-rc.0",
"typescript": "^5.9.2"
},
"engines": {

View File

@@ -49,6 +49,15 @@ describe('createAPICallError', () => {
expect(error.message).toBe('Simple error');
expect(error.isRetryable).toBe(false);
});
it('should set requestBodyValues when promptExcerpt is provided', () => {
const error = createAPICallError({
message: 'Test error',
promptExcerpt: 'Sample prompt'
});
expect(error.requestBodyValues).toEqual({ prompt: 'Sample prompt' });
});
});
describe('createAuthenticationError', () => {

View File

@@ -334,6 +334,8 @@ export class TaskFileGeneratorService {
return ' ✗';
case 'deferred':
return ' ⏸';
case 'review':
return ' 👁';
default:
return '';
}

View File

@@ -809,9 +809,12 @@ export class TaskService {
* Releases file locks and other storage resources
*/
async close(): Promise<void> {
if (this.storage) {
if (!this.storage) return;
try {
await this.storage.close();
} finally {
this.initialized = false;
}
this.initialized = false;
}
}

View File

@@ -70,9 +70,9 @@ describe('Hamster Rules Distribution', () => {
});
describe('Rules add command distributes hamster file', () => {
// Test each profile that should receive hamster rules
PROFILES_WITH_DEFAULT_RULES.forEach((profile) => {
test(`${profile} profile receives hamster rules via 'rules add'`, () => {
test.each(PROFILES_WITH_DEFAULT_RULES)(
'%s profile receives hamster rules via "rules add"',
(profile) => {
// Create a unique temp directory for this test
const tempDir = fs.mkdtempSync(
path.join(os.tmpdir(), `tm-hamster-test-${profile}-`)
@@ -104,15 +104,16 @@ describe('Hamster Rules Distribution', () => {
// Cleanup temp directory
fs.rmSync(tempDir, { recursive: true, force: true });
}
});
});
}
);
});
describe('Profiles without default rules', () => {
// These profiles use different mechanisms (CLAUDE.md, AGENTS.md, etc.)
// and don't include the defaultFileMap rules
PROFILES_WITHOUT_DEFAULT_RULES.forEach((profile) => {
test(`${profile} profile does not use defaultFileMap (has custom mechanism)`, async () => {
test.each(PROFILES_WITHOUT_DEFAULT_RULES)(
'%s profile does not use defaultFileMap (has custom mechanism)',
async (profile) => {
// Dynamically import the profile to check its configuration
const profileModule = await import(
`../../../src/profiles/${profile}.js`
@@ -122,7 +123,7 @@ describe('Hamster Rules Distribution', () => {
// These profiles should have includeDefaultRules: false
// which means they won't automatically get hamster files via defaultFileMap
expect(profileExport.includeDefaultRules).toBe(false);
});
});
}
);
});
});