Compare commits

...

2 Commits

Author SHA1 Message Date
github-actions[bot]
c27a61361a docs: auto-update documentation based on changes in next branch
This PR was automatically generated to update documentation based on recent changes.

  Original commit: feat: add tm show (#1199)\n\n\n

  Co-authored-by: Claude <claude-assistant@anthropic.com>
2025-09-12 01:41:32 +00:00
Ralph Khreish
77e1ddc237 feat: add tm show (#1199) 2025-09-12 03:34:32 +02:00
29 changed files with 2325 additions and 1740 deletions

View File

@@ -6,7 +6,7 @@
"repo": "eyaltoledano/claude-task-master" "repo": "eyaltoledano/claude-task-master"
} }
], ],
"commit": false, "commit": true,
"fixed": [], "fixed": [],
"linked": [], "linked": [],
"access": "public", "access": "public",

3
.gitignore vendored
View File

@@ -94,3 +94,6 @@ apps/extension/.vscode-test/
# apps/extension # apps/extension
apps/extension/vsix-build/ apps/extension/vsix-build/
# turbo
.turbo

View File

@@ -13,8 +13,8 @@
}, },
"files": ["dist", "README.md"], "files": ["dist", "README.md"],
"scripts": { "scripts": {
"build": "tsup", "build": "tsc",
"dev": "tsup --watch", "dev": "tsc --watch",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"lint": "biome check src", "lint": "biome check src",
"format": "biome format --write src", "format": "biome format --write src",
@@ -37,10 +37,8 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@biomejs/biome": "^1.9.4",
"@tm/build-config": "*",
"@types/inquirer": "^9.0.3", "@types/inquirer": "^9.0.3",
"@types/node": "^22.10.5", "@types/node": "^22.10.5",
"tsup": "^8.3.0",
"tsx": "^4.20.4", "tsx": "^4.20.4",
"typescript": "^5.7.3", "typescript": "^5.7.3",
"vitest": "^2.1.8" "vitest": "^2.1.8"

View File

@@ -173,13 +173,6 @@ export class ListTasksCommand extends Command {
includeSubtasks: options.withSubtasks includeSubtasks: options.withSubtasks
}); });
// Runtime guard to prevent 'auto' from reaching CLI consumers
if (result.storageType === 'auto') {
throw new Error(
'Internal error: unresolved storage type reached CLI. Please check TaskService.getStorageType() implementation.'
);
}
return result as ListTasksResult; return result as ListTasksResult;
} }

View File

@@ -0,0 +1,406 @@
/**
* @fileoverview ShowCommand using Commander's native class pattern
* Extends Commander.Command for better integration with the framework
*/
import { Command } from 'commander';
import chalk from 'chalk';
import boxen from 'boxen';
import { createTaskMasterCore, type Task, type TaskMasterCore } from '@tm/core';
import type { StorageType } from '@tm/core/types';
import * as ui from '../utils/ui.js';
/**
* Options interface for the show command
*/
export interface ShowCommandOptions {
id?: string;
status?: string;
format?: 'text' | 'json';
silent?: boolean;
project?: string;
}
/**
* Result type from show command
*/
export interface ShowTaskResult {
task: Task | null;
found: boolean;
storageType: Exclude<StorageType, 'auto'>;
}
/**
* Result type for multiple tasks
*/
export interface ShowMultipleTasksResult {
tasks: Task[];
notFound: string[];
storageType: Exclude<StorageType, 'auto'>;
}
/**
* ShowCommand extending Commander's Command class
* This is a thin presentation layer over @tm/core
*/
export class ShowCommand extends Command {
private tmCore?: TaskMasterCore;
private lastResult?: ShowTaskResult | ShowMultipleTasksResult;
constructor(name?: string) {
super(name || 'show');
// Configure the command
this.description('Display detailed information about one or more tasks')
.argument('[id]', 'Task ID(s) to show (comma-separated for multiple)')
.option(
'-i, --id <id>',
'Task ID(s) to show (comma-separated for multiple)'
)
.option('-s, --status <status>', 'Filter subtasks by status')
.option('-f, --format <format>', 'Output format (text, json)', 'text')
.option('--silent', 'Suppress output (useful for programmatic usage)')
.option('-p, --project <path>', 'Project root directory', process.cwd())
.action(
async (taskId: string | undefined, options: ShowCommandOptions) => {
await this.executeCommand(taskId, options);
}
);
}
/**
* Execute the show command
*/
private async executeCommand(
taskId: string | undefined,
options: ShowCommandOptions
): Promise<void> {
try {
// Validate options
if (!this.validateOptions(options)) {
process.exit(1);
}
// Initialize tm-core
await this.initializeCore(options.project || process.cwd());
// Get the task ID from argument or option
const idArg = taskId || options.id;
if (!idArg) {
console.error(chalk.red('Error: Please provide a task ID'));
process.exit(1);
}
// Check if multiple IDs are provided (comma-separated)
const taskIds = idArg
.split(',')
.map((id) => id.trim())
.filter((id) => id.length > 0);
// Get tasks from core
const result =
taskIds.length > 1
? await this.getMultipleTasks(taskIds, options)
: await this.getSingleTask(taskIds[0], options);
// Store result for programmatic access
this.setLastResult(result);
// Display results
if (!options.silent) {
this.displayResults(result, options);
}
} catch (error: any) {
const msg = error?.getSanitizedDetails?.() ?? {
message: error?.message ?? String(error)
};
console.error(chalk.red(`Error: ${msg.message || 'Unexpected error'}`));
if (error.stack && process.env.DEBUG) {
console.error(chalk.gray(error.stack));
}
process.exit(1);
}
}
/**
* Validate command options
*/
private validateOptions(options: ShowCommandOptions): boolean {
// Validate format
if (options.format && !['text', 'json'].includes(options.format)) {
console.error(chalk.red(`Invalid format: ${options.format}`));
console.error(chalk.gray(`Valid formats: text, json`));
return false;
}
return true;
}
/**
* Initialize TaskMasterCore
*/
private async initializeCore(projectRoot: string): Promise<void> {
if (!this.tmCore) {
this.tmCore = await createTaskMasterCore({ projectPath: projectRoot });
}
}
/**
* Get a single task from tm-core
*/
private async getSingleTask(
taskId: string,
_options: ShowCommandOptions
): Promise<ShowTaskResult> {
if (!this.tmCore) {
throw new Error('TaskMasterCore not initialized');
}
// Get the task
const task = await this.tmCore.getTask(taskId);
// Get storage type
const storageType = this.tmCore.getStorageType();
return {
task,
found: task !== null,
storageType: storageType as Exclude<StorageType, 'auto'>
};
}
/**
* Get multiple tasks from tm-core
*/
private async getMultipleTasks(
taskIds: string[],
_options: ShowCommandOptions
): Promise<ShowMultipleTasksResult> {
if (!this.tmCore) {
throw new Error('TaskMasterCore not initialized');
}
const tasks: Task[] = [];
const notFound: string[] = [];
// Get each task individually
for (const taskId of taskIds) {
const task = await this.tmCore.getTask(taskId);
if (task) {
tasks.push(task);
} else {
notFound.push(taskId);
}
}
// Get storage type
const storageType = this.tmCore.getStorageType();
return {
tasks,
notFound,
storageType: storageType as Exclude<StorageType, 'auto'>
};
}
/**
* Display results based on format
*/
private displayResults(
result: ShowTaskResult | ShowMultipleTasksResult,
options: ShowCommandOptions
): void {
const format = options.format || 'text';
switch (format) {
case 'json':
this.displayJson(result);
break;
case 'text':
default:
if ('task' in result) {
// Single task result
this.displaySingleTask(result, options);
} else {
// Multiple tasks result
this.displayMultipleTasks(result, options);
}
break;
}
}
/**
* Display in JSON format
*/
private displayJson(result: ShowTaskResult | ShowMultipleTasksResult): void {
console.log(JSON.stringify(result, null, 2));
}
/**
* Display a single task in text format
*/
private displaySingleTask(
result: ShowTaskResult,
options: ShowCommandOptions
): void {
if (!result.found || !result.task) {
console.log(
boxen(chalk.yellow(`Task not found!`), {
padding: { top: 0, bottom: 0, left: 1, right: 1 },
borderColor: 'yellow',
borderStyle: 'round',
margin: { top: 1 }
})
);
return;
}
const task = result.task;
// Header
console.log(
boxen(chalk.white.bold(`Task #${task.id} - ${task.title}`), {
padding: { top: 0, bottom: 0, left: 1, right: 1 },
borderColor: 'blue',
borderStyle: 'round',
margin: { top: 1 }
})
);
// Task details
console.log(
`\n${chalk.blue.bold('Status:')} ${ui.getStatusWithColor(task.status)}`
);
console.log(
`${chalk.blue.bold('Priority:')} ${ui.getPriorityWithColor(task.priority)}`
);
if (task.description) {
console.log(`\n${chalk.blue.bold('Description:')}`);
console.log(task.description);
}
if (task.details) {
console.log(`\n${chalk.blue.bold('Details:')}`);
console.log(task.details);
}
// Dependencies
if (task.dependencies && task.dependencies.length > 0) {
console.log(`\n${chalk.blue.bold('Dependencies:')}`);
task.dependencies.forEach((dep) => {
console.log(` - ${chalk.cyan(dep)}`);
});
}
// Subtasks
if (task.subtasks && task.subtasks.length > 0) {
console.log(`\n${chalk.blue.bold('Subtasks:')}`);
// Filter subtasks by status if provided
const filteredSubtasks = options.status
? task.subtasks.filter((sub) => sub.status === options.status)
: task.subtasks;
if (filteredSubtasks.length === 0 && options.status) {
console.log(
chalk.gray(` No subtasks with status '${options.status}'`)
);
} else {
filteredSubtasks.forEach((subtask) => {
console.log(
` ${chalk.cyan(`${task.id}.${subtask.id}`)} ${ui.getStatusWithColor(subtask.status)} ${subtask.title}`
);
if (subtask.description) {
console.log(` ${chalk.gray(subtask.description)}`);
}
});
}
}
if (task.testStrategy) {
console.log(`\n${chalk.blue.bold('Test Strategy:')}`);
console.log(task.testStrategy);
}
console.log(`\n${chalk.gray('Storage: ' + result.storageType)}`);
}
/**
* Display multiple tasks in text format
*/
private displayMultipleTasks(
result: ShowMultipleTasksResult,
_options: ShowCommandOptions
): void {
// Header
ui.displayBanner(`Tasks (${result.tasks.length} found)`);
if (result.notFound.length > 0) {
console.log(chalk.yellow(`\n⚠ Not found: ${result.notFound.join(', ')}`));
}
if (result.tasks.length === 0) {
ui.displayWarning('No tasks found matching the criteria.');
return;
}
// Task table
console.log(chalk.blue.bold(`\n📋 Tasks:\n`));
console.log(
ui.createTaskTable(result.tasks, {
showSubtasks: true,
showDependencies: true
})
);
console.log(`\n${chalk.gray('Storage: ' + result.storageType)}`);
}
/**
* Set the last result for programmatic access
*/
private setLastResult(
result: ShowTaskResult | ShowMultipleTasksResult
): void {
this.lastResult = result;
}
/**
* Get the last result (for programmatic usage)
*/
getLastResult(): ShowTaskResult | ShowMultipleTasksResult | undefined {
return this.lastResult;
}
/**
* Clean up resources
*/
async cleanup(): Promise<void> {
if (this.tmCore) {
await this.tmCore.close();
this.tmCore = undefined;
}
}
/**
* Static method to register this command on an existing program
* This is for gradual migration - allows commands.js to use this
*/
static registerOn(program: Command): Command {
const showCommand = new ShowCommand();
program.addCommand(showCommand);
return showCommand;
}
/**
* Alternative registration that returns the command for chaining
* Can also configure the command name if needed
*/
static register(program: Command, name?: string): ShowCommand {
const showCommand = new ShowCommand(name);
program.addCommand(showCommand);
return showCommand;
}
}

View File

@@ -5,6 +5,7 @@
// Commands // Commands
export { ListTasksCommand } from './commands/list.command.js'; export { ListTasksCommand } from './commands/list.command.js';
export { ShowCommand } from './commands/show.command.js';
export { AuthCommand } from './commands/auth.command.js'; export { AuthCommand } from './commands/auth.command.js';
export { ContextCommand } from './commands/context.command.js'; export { ContextCommand } from './commands/context.command.js';

View File

@@ -1,8 +0,0 @@
import { defineConfig } from 'tsup';
import { cliConfig, mergeConfig } from '@tm/build-config';
export default defineConfig(
mergeConfig(cliConfig, {
entry: ['src/index.ts']
})
);

View File

@@ -47,6 +47,20 @@ sidebarTitle: "CLI Commands"
# View a specific subtask (e.g., subtask 2 of task 1) # View a specific subtask (e.g., subtask 2 of task 1)
task-master show 1.2 task-master show 1.2
# Show multiple tasks at once (comma-separated)
task-master show 1,2,3
task-master show --id=1,2,3
# Filter subtasks by status
task-master show <id> --status=pending
task-master show <id> --status=done
# Output in JSON format (useful for scripts)
task-master show <id> --format=json
# Silent mode (suppress output, useful for programmatic usage)
task-master show <id> --silent
``` ```
</Accordion> </Accordion>

View File

@@ -9,6 +9,6 @@
"preview": "mintlify preview" "preview": "mintlify preview"
}, },
"devDependencies": { "devDependencies": {
"mintlify": "^4.0.0" "mintlify": "^4.2.111"
} }
} }

View File

@@ -103,8 +103,8 @@ async function main() {
// This prevents the multiple React instances issue // This prevents the multiple React instances issue
// Ensure React is resolved from the workspace root to avoid duplicates // Ensure React is resolved from the workspace root to avoid duplicates
alias: { alias: {
react: path.resolve(__dirname, 'node_modules/react'), react: path.resolve(__dirname, '../../node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom') 'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
}, },
define: { define: {
'process.env.NODE_ENV': production ? '"production"' : '"development"', 'process.env.NODE_ENV': production ? '"production"' : '"development"',
@@ -135,8 +135,8 @@ async function main() {
jsxImportSource: 'react', jsxImportSource: 'react',
external: ['*.css'], external: ['*.css'],
alias: { alias: {
react: path.resolve(__dirname, 'node_modules/react'), react: path.resolve(__dirname, '../../node_modules/react'),
'react-dom': path.resolve(__dirname, 'node_modules/react-dom') 'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
}, },
define: { define: {
'process.env.NODE_ENV': production ? '"production"' : '"development"', 'process.env.NODE_ENV': production ? '"production"' : '"development"',

View File

@@ -229,6 +229,7 @@
"build": "npm run build:js && npm run build:css", "build": "npm run build:js && npm run build:css",
"build:js": "node ./esbuild.js --production", "build:js": "node ./esbuild.js --production",
"build:css": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --minify", "build:css": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --minify",
"dev": "npm run watch",
"package": "npm exec node ./package.mjs", "package": "npm exec node ./package.mjs",
"package:direct": "node ./package.mjs", "package:direct": "node ./package.mjs",
"debug:env": "node ./debug-env.mjs", "debug:env": "node ./debug-env.mjs",

37
output.txt Normal file

File diff suppressed because one or more lines are too long

3120
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,23 +11,30 @@
}, },
"workspaces": ["apps/*", "packages/*", "."], "workspaces": ["apps/*", "packages/*", "."],
"scripts": { "scripts": {
"build": "npm run build:packages && tsup", "build": "npm run build:build-config && tsup",
"dev": "npm run build:packages && npm link && (npm run dev:packages & tsup --watch --onSuccess 'echo Build complete && npm link')", "dev": "tsup --watch",
"dev:packages": "(cd packages/tm-core && npm run dev) & (cd apps/cli && npm run dev) & wait", "turbo:dev": "turbo dev",
"dev:core": "cd packages/tm-core && npm run dev", "turbo:build": "turbo build",
"dev:cli": "cd apps/cli && npm run dev", "dev:main": "tsup --watch --onSuccess 'echo \"📦 Main package built\" && npm link'",
"build:packages": "npm run build:build-config && npm run build:core && npm run build:cli", "dev:legacy": "npm run build:build-config && concurrently -n \"core,cli,main\" -c \"blue,green,yellow\" \"npm run dev:core\" \"npm run dev:cli\" \"npm run dev:main\"",
"build:build-config": "cd packages/build-config && npm run build", "dev:core": "npm run dev -w @tm/core",
"build:core": "cd packages/tm-core && npm run build", "dev:cli": "npm run dev -w @tm/cli",
"build:cli": "cd apps/cli && npm run build", "build:packages": "turbo build --filter='./packages/*' --filter='./apps/*'",
"typecheck": "npm run typecheck:core && npm run typecheck:cli", "build:packages:parallel": "turbo build --filter='./packages/*' --filter='./apps/*'",
"typecheck:core": "cd packages/tm-core && npm run typecheck", "build:build-config": "npm run build -w @tm/build-config",
"typecheck:cli": "cd apps/cli && npm run typecheck", "build:core": "npm run build -w @tm/core",
"test": "node --experimental-vm-modules node_modules/.bin/jest", "build:cli": "npm run build -w @tm/cli",
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit", "typecheck": "turbo typecheck",
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration", "typecheck:all": "turbo typecheck",
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures", "typecheck:core": "npm run typecheck -w @tm/core",
"test:watch": "node --experimental-vm-modules node_modules/.bin/jest --watch", "typecheck:cli": "npm run typecheck -w @tm/cli",
"test": "turbo test",
"test:watch": "turbo test:watch",
"test:legacy": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest",
"test:debug": "NODE_ENV=development node --inspect --experimental-vm-modules node_modules/.bin/jest --no-cache --verbose",
"test:unit": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
"test:integration": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
"test:fails": "NODE_ENV=development node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage", "test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
"test:ci": "node --experimental-vm-modules node_modules/.bin/jest --coverage --ci", "test:ci": "node --experimental-vm-modules node_modules/.bin/jest --coverage --ci",
"test:e2e": "./tests/e2e/run_e2e.sh", "test:e2e": "./tests/e2e/run_e2e.sh",
@@ -35,10 +42,15 @@
"postpack": "chmod +x dist/task-master.js dist/mcp-server.js", "postpack": "chmod +x dist/task-master.js dist/mcp-server.js",
"changeset": "changeset", "changeset": "changeset",
"release": "changeset publish", "release": "changeset publish",
"publish-packages": "turbo run build lint test && changeset version && changeset publish",
"inspector": "npx @modelcontextprotocol/inspector node dist/mcp-server.js", "inspector": "npx @modelcontextprotocol/inspector node dist/mcp-server.js",
"mcp-server": "node dist/mcp-server.js", "mcp-server": "node dist/mcp-server.js",
"format-check": "biome format .", "format-check": "biome format .",
"format": "biome format . --write" "format": "biome format . --write",
"lint": "turbo lint",
"lint:all": "turbo lint",
"lint:legacy": "npm run lint --workspaces --if-present",
"test:all": "turbo test"
}, },
"keywords": [ "keywords": [
"claude", "claude",
@@ -108,6 +120,7 @@
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
"packageManager": "npm@10.9.2",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "git+https://github.com/eyaltoledano/claude-task-master.git" "url": "git+https://github.com/eyaltoledano/claude-task-master.git"
@@ -123,14 +136,13 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@biomejs/biome": "^1.9.4",
"@changesets/changelog-github": "^0.5.1", "@changesets/changelog-github": "^0.5.1",
"@changesets/cli": "^2.28.1", "@changesets/cli": "^2.28.1",
"dotenv-mono": "^1.5.1",
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
"concurrently": "^9.2.1",
"cross-env": "^10.0.0",
"dotenv-mono": "^1.5.1",
"execa": "^8.0.1", "execa": "^8.0.1",
"ink": "^5.0.1",
"jest": "^29.7.0", "jest": "^29.7.0",
"jest-environment-node": "^29.7.0", "jest-environment-node": "^29.7.0",
"mock-fs": "^5.5.0", "mock-fs": "^5.5.0",
@@ -138,6 +150,7 @@
"supertest": "^7.1.0", "supertest": "^7.1.0",
"tsup": "^8.5.0", "tsup": "^8.5.0",
"tsx": "^4.16.2", "tsx": "^4.16.2",
"turbo": "^2.5.6",
"typescript": "^5.9.2" "typescript": "^5.9.2"
} }
} }

View File

@@ -7,9 +7,8 @@
"types": "./dist/tsup.base.d.ts", "types": "./dist/tsup.base.d.ts",
"exports": { "exports": {
".": { ".": {
"types": "./src/tsup.base.ts", "types": "./dist/tsup.base.d.ts",
"import": "./dist/tsup.base.js", "import": "./dist/tsup.base.js"
"require": "./dist/tsup.base.cjs"
} }
}, },
"files": ["dist", "src"], "files": ["dist", "src"],
@@ -17,15 +16,14 @@
"author": "", "author": "",
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"build": "tsup", "build": "tsc",
"dev": "tsup --watch",
"typecheck": "tsc --noEmit" "typecheck": "tsc --noEmit"
}, },
"devDependencies": { "devDependencies": {
"tsup": "^8.5.0", "dotenv-mono": "^1.5.1",
"typescript": "^5.7.3" "typescript": "^5.7.3"
}, },
"peerDependencies": { "dependencies": {
"tsup": "^8.0.0" "tsup": "^8.5.0"
} }
} }

View File

@@ -3,78 +3,56 @@
* Provides shared configuration that can be extended by individual packages * Provides shared configuration that can be extended by individual packages
*/ */
import type { Options } from 'tsup'; import type { Options } from 'tsup';
import * as dotenv from 'dotenv-mono';
dotenv.load();
console.log(
'TM_PUBLIC_BASE_DOMAIN:',
process.env.TM_PUBLIC_BASE_DOMAIN,
'TM_PUBLIC_SUPABASE_URL:',
process.env.TM_PUBLIC_SUPABASE_URL,
'TM_PUBLIC_SUPABASE_ANON_KEY:',
process.env.TM_PUBLIC_SUPABASE_ANON_KEY
);
const isProduction = process.env.NODE_ENV === 'production'; const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = !isProduction; const isDevelopment = !isProduction;
const envVariables = {
TM_PUBLIC_BASE_DOMAIN: process.env.TM_PUBLIC_BASE_DOMAIN ?? '',
TM_PUBLIC_SUPABASE_URL: process.env.TM_PUBLIC_SUPABASE_URL ?? '',
TM_PUBLIC_SUPABASE_ANON_KEY: process.env.TM_PUBLIC_SUPABASE_ANON_KEY ?? ''
};
console.log('envVariables:', envVariables);
/** /**
* Base configuration for library packages (tm-core, etc.) * Environment helpers
*/ */
export const libraryConfig: Partial<Options> = { export const env = {
format: ['cjs', 'esm'], isProduction,
target: 'es2022', isDevelopment,
// Sourcemaps only in development to reduce production bundle size NODE_ENV: process.env.NODE_ENV || 'development',
sourcemap: isDevelopment, ...envVariables
clean: true,
dts: true,
// Enable optimizations in production
splitting: isProduction,
treeshake: isProduction,
minify: isProduction,
bundle: true,
esbuildOptions(options) {
options.conditions = ['module'];
// Better source mapping in development only
options.sourcesContent = isDevelopment;
// Keep original names for better debugging in development
options.keepNames = isDevelopment;
},
// Watch mode configuration for development
watch: isDevelopment ? ['src'] : false
}; };
/** /**
* Base configuration for CLI packages * Base tsup configuration for all packages
* Since everything gets bundled into root dist/ anyway, use consistent settings
*/ */
export const cliConfig: Partial<Options> = { export const baseConfig: Partial<Options> = {
format: ['esm'], format: ['esm'],
target: 'node18', target: 'node18',
splitting: false,
// Sourcemaps only in development to reduce production bundle size
sourcemap: isDevelopment, sourcemap: isDevelopment,
clean: true, clean: true,
dts: true, dts: false,
shims: true,
// Enable minification in production for smaller bundles
minify: isProduction, minify: isProduction,
treeshake: isProduction, treeshake: isProduction,
esbuildOptions(options) {
options.platform = 'node';
// Better source mapping in development only
options.sourcesContent = isDevelopment;
// Keep original names for better debugging in development
options.keepNames = isDevelopment;
}
};
/**
* Base configuration for executable bundles (root level)
*/
export const executableConfig: Partial<Options> = {
format: ['esm'],
target: 'node18',
splitting: false, splitting: false,
// Sourcemaps only in development to reduce production bundle size // Don't bundle any other dependencies (auto-external all node_modules)
sourcemap: isDevelopment, external: [/^[^./]/],
clean: true, env: envVariables,
bundle: true, // Bundle everything into one file
// Minify in production for smaller executables
minify: isProduction,
// Handle TypeScript imports transparently
loader: {
'.js': 'jsx',
'.ts': 'ts'
},
esbuildOptions(options) { esbuildOptions(options) {
options.platform = 'node'; options.platform = 'node';
// Allow importing TypeScript from JavaScript // Allow importing TypeScript from JavaScript
@@ -83,14 +61,18 @@ export const executableConfig: Partial<Options> = {
options.sourcesContent = isDevelopment; options.sourcesContent = isDevelopment;
// Keep original names for better debugging in development // Keep original names for better debugging in development
options.keepNames = isDevelopment; options.keepNames = isDevelopment;
} },
// Watch mode configuration for development
watch: false
}; };
/** /**
* Common external modules that should not be bundled * Legacy external modules list - kept for backwards compatibility
* Note: When using tsup-node, this is not needed as it automatically
* excludes dependencies and peerDependencies from package.json
*/ */
export const commonExternals = [ export const commonExternals = [
// Native Node.js modules // Native Node.js modules (for cases where tsup is used instead of tsup-node)
'fs', 'fs',
'path', 'path',
'child_process', 'child_process',
@@ -119,6 +101,7 @@ export const commonExternals = [
/** /**
* Utility function to merge configurations * Utility function to merge configurations
* Simplified for tsup-node usage
*/ */
export function mergeConfig( export function mergeConfig(
baseConfig: Partial<Options>, baseConfig: Partial<Options>,
@@ -127,8 +110,6 @@ export function mergeConfig(
return { return {
...baseConfig, ...baseConfig,
...overrides, ...overrides,
// Merge arrays instead of overwriting
external: [...(baseConfig.external || []), ...(overrides.external || [])],
// Merge esbuildOptions // Merge esbuildOptions
esbuildOptions(options, context) { esbuildOptions(options, context) {
if (baseConfig.esbuildOptions) { if (baseConfig.esbuildOptions) {
@@ -140,12 +121,3 @@ export function mergeConfig(
} }
} as Options; } as Options;
} }
/**
* Environment helpers
*/
export const env = {
isProduction,
isDevelopment,
NODE_ENV: process.env.NODE_ENV || 'development'
};

View File

@@ -6,9 +6,10 @@
"moduleResolution": "bundler", "moduleResolution": "bundler",
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"esModuleInterop": true, "esModuleInterop": true,
"baseUrl": ".",
"outDir": "dist",
"allowJs": true, "allowJs": true,
"strict": true, "strict": true,
"noEmit": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true, "isolatedModules": true,
"declaration": true, "declaration": true,

View File

@@ -1,23 +0,0 @@
import { defineConfig } from 'tsup';
const isProduction = process.env.NODE_ENV === 'production';
export default defineConfig({
entry: ['src/tsup.base.ts'],
format: ['esm', 'cjs'],
target: 'node18',
// Sourcemaps only in development
sourcemap: !isProduction,
clean: true,
dts: true,
// Enable minification in production
minify: isProduction,
treeshake: isProduction,
external: ['tsup'],
esbuildOptions(options) {
// Better source mapping in development only
options.sourcesContent = !isProduction;
// Keep original names for better debugging in development
options.keepNames = !isProduction;
}
});

View File

@@ -53,8 +53,8 @@
} }
}, },
"scripts": { "scripts": {
"build": "tsup", "build": "tsc",
"dev": "tsup --watch", "dev": "tsc --watch",
"test": "vitest run", "test": "vitest run",
"test:watch": "vitest", "test:watch": "vitest",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --coverage",
@@ -71,18 +71,16 @@
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "^1.9.4", "@biomejs/biome": "^1.9.4",
"@tm/build-config": "*",
"@types/node": "^20.11.30", "@types/node": "^20.11.30",
"@vitest/coverage-v8": "^2.0.5", "@vitest/coverage-v8": "^2.0.5",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"tsup": "^8.0.2",
"typescript": "^5.4.3", "typescript": "^5.4.3",
"vitest": "^2.0.5" "vitest": "^2.0.5"
}, },
"engines": { "engines": {
"node": ">=18.0.0" "node": ">=18.0.0"
}, },
"files": ["dist", "README.md", "CHANGELOG.md"], "files": ["src", "README.md", "CHANGELOG.md"],
"keywords": ["task-management", "typescript", "ai", "prd", "parser"], "keywords": ["task-management", "typescript", "ai", "prd", "parser"],
"author": "Task Master AI", "author": "Task Master AI",
"license": "MIT" "license": "MIT"

View File

@@ -31,7 +31,7 @@ export class AuthManager {
private organizationService?: OrganizationService; private organizationService?: OrganizationService;
private constructor(config?: Partial<AuthConfig>) { private constructor(config?: Partial<AuthConfig>) {
this.credentialStore = new CredentialStore(config); this.credentialStore = CredentialStore.getInstance(config);
this.supabaseClient = new SupabaseAuthClient(); this.supabaseClient = new SupabaseAuthClient();
this.oauthService = new OAuthService(this.credentialStore, config); this.oauthService = new OAuthService(this.credentialStore, config);
@@ -73,6 +73,7 @@ export class AuthManager {
*/ */
static resetInstance(): void { static resetInstance(): void {
AuthManager.instance = null; AuthManager.instance = null;
CredentialStore.resetInstance();
} }
/** /**

View File

@@ -19,15 +19,39 @@ import { getLogger } from '../logger/index.js';
* human-readable persisted format in the auth.json file. * human-readable persisted format in the auth.json file.
*/ */
export class CredentialStore { export class CredentialStore {
private static instance: CredentialStore | null = null;
private logger = getLogger('CredentialStore'); private logger = getLogger('CredentialStore');
private config: AuthConfig; private config: AuthConfig;
// Clock skew tolerance for expiry checks (30 seconds) // Clock skew tolerance for expiry checks (30 seconds)
private readonly CLOCK_SKEW_MS = 30_000; private readonly CLOCK_SKEW_MS = 30_000;
constructor(config?: Partial<AuthConfig>) { private constructor(config?: Partial<AuthConfig>) {
this.config = getAuthConfig(config); this.config = getAuthConfig(config);
} }
/**
* Get the singleton instance of CredentialStore
*/
static getInstance(config?: Partial<AuthConfig>): CredentialStore {
if (!CredentialStore.instance) {
CredentialStore.instance = new CredentialStore(config);
} else if (config) {
// Warn if config is provided after initialization
const logger = getLogger('CredentialStore');
logger.warn(
'getInstance called with config after initialization; config is ignored.'
);
}
return CredentialStore.instance;
}
/**
* Reset the singleton instance (useful for testing)
*/
static resetInstance(): void {
CredentialStore.instance = null;
}
/** /**
* Get stored authentication credentials * Get stored authentication credentials
* @returns AuthCredentials with expiresAt as number (milliseconds) for runtime use * @returns AuthCredentials with expiresAt as number (milliseconds) for runtime use

View File

@@ -5,7 +5,7 @@
export { AuthManager } from './auth-manager.js'; export { AuthManager } from './auth-manager.js';
export { CredentialStore } from './credential-store.js'; export { CredentialStore } from './credential-store.js';
export { OAuthService } from './oauth-service.js'; export { OAuthService } from './oauth-service.js';
export { SupabaseSessionStorage } from './supabase-session-storage'; export { SupabaseSessionStorage } from './supabase-session-storage.js';
export type { export type {
Organization, Organization,
Brief, Brief,

View File

@@ -7,9 +7,9 @@
*/ */
import { SupportedStorage } from '@supabase/supabase-js'; import { SupportedStorage } from '@supabase/supabase-js';
import { CredentialStore } from './credential-store'; import { CredentialStore } from './credential-store.js';
import { AuthCredentials } from './types'; import { AuthCredentials } from './types.js';
import { getLogger } from '../logger'; import { getLogger } from '../logger/index.js';
const STORAGE_KEY = 'sb-taskmaster-auth-token'; const STORAGE_KEY = 'sb-taskmaster-auth-token';

View File

@@ -10,8 +10,8 @@ import {
} from '@supabase/supabase-js'; } from '@supabase/supabase-js';
import { AuthenticationError } from '../auth/types.js'; import { AuthenticationError } from '../auth/types.js';
import { getLogger } from '../logger/index.js'; import { getLogger } from '../logger/index.js';
import { SupabaseSessionStorage } from '../auth/supabase-session-storage'; import { SupabaseSessionStorage } from '../auth/supabase-session-storage.js';
import { CredentialStore } from '../auth/credential-store'; import { CredentialStore } from '../auth/credential-store.js';
export class SupabaseAuthClient { export class SupabaseAuthClient {
private client: SupabaseJSClient | null = null; private client: SupabaseJSClient | null = null;
@@ -19,7 +19,7 @@ export class SupabaseAuthClient {
private logger = getLogger('SupabaseAuthClient'); private logger = getLogger('SupabaseAuthClient');
constructor() { constructor() {
const credentialStore = new CredentialStore(); const credentialStore = CredentialStore.getInstance();
this.sessionStorage = new SupabaseSessionStorage(credentialStore); this.sessionStorage = new SupabaseSessionStorage(credentialStore);
} }

View File

@@ -1,7 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "target": "ES2022",
"module": "ESNext", "module": "NodeNext",
"lib": ["ES2022"], "lib": ["ES2022"],
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
@@ -24,11 +24,12 @@
"esModuleInterop": true, "esModuleInterop": true,
"skipLibCheck": true, "skipLibCheck": true,
"forceConsistentCasingInFileNames": true, "forceConsistentCasingInFileNames": true,
"moduleResolution": "bundler", "moduleResolution": "NodeNext",
"moduleDetection": "force", "moduleDetection": "force",
"types": ["node"], "types": ["node"],
"resolveJsonModule": true, "resolveJsonModule": true,
"isolatedModules": true "isolatedModules": true,
"allowImportingTsExtensions": false
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"] "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"]

View File

@@ -1,24 +0,0 @@
import { defineConfig } from 'tsup';
import { libraryConfig, mergeConfig } from '@tm/build-config';
export default defineConfig(
mergeConfig(libraryConfig, {
entry: {
index: 'src/index.ts',
'auth/index': 'src/auth/index.ts',
'config/index': 'src/config/index.ts',
'services/index': 'src/services/index.ts',
'logger/index': 'src/logger/index.ts',
'interfaces/index': 'src/interfaces/index.ts',
'types/index': 'src/types/index.ts',
'providers/index': 'src/providers/index.ts',
'storage/index': 'src/storage/index.ts',
'parser/index': 'src/parser/index.ts',
'utils/index': 'src/utils/index.ts',
'errors/index': 'src/errors/index.ts'
},
tsconfig: './tsconfig.json',
outDir: 'dist',
external: ['zod', '@supabase/supabase-js']
})
);

View File

@@ -16,7 +16,12 @@ import ora from 'ora'; // Import ora
import { log, readJSON } from './utils.js'; import { log, readJSON } from './utils.js';
// Import new commands from @tm/cli // Import new commands from @tm/cli
import { ListTasksCommand, AuthCommand, ContextCommand } from '@tm/cli'; import {
ListTasksCommand,
ShowCommand,
AuthCommand,
ContextCommand
} from '@tm/cli';
import { import {
parsePRD, parsePRD,
@@ -1749,6 +1754,10 @@ function registerCommands(programInstance) {
// Manages workspace context (org/brief selection) // Manages workspace context (org/brief selection)
ContextCommand.registerOn(programInstance); ContextCommand.registerOn(programInstance);
// Register the show command from @tm/cli
// Displays detailed information about tasks
ShowCommand.registerOn(programInstance);
// expand command // expand command
programInstance programInstance
.command('expand') .command('expand')
@@ -2567,80 +2576,6 @@ ${result.result}
); );
}); });
// show command
programInstance
.command('show')
.description(
`Display detailed information about one or more tasks${chalk.reset('')}`
)
.argument('[id]', 'Task ID(s) to show (comma-separated for multiple)')
.option(
'-i, --id <id>',
'Task ID(s) to show (comma-separated for multiple)'
)
.option('-s, --status <status>', 'Filter subtasks by status')
.option(
'-f, --file <file>',
'Path to the tasks file',
TASKMASTER_TASKS_FILE
)
.option(
'-r, --report <report>',
'Path to the complexity report file',
COMPLEXITY_REPORT_FILE
)
.option('--tag <tag>', 'Specify tag context for task operations')
.action(async (taskId, options) => {
// Initialize TaskMaster
const initOptions = {
tasksPath: options.file || true,
tag: options.tag
};
// 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 idArg = taskId || options.id;
const statusFilter = options.status;
const tag = taskMaster.getCurrentTag();
// Show current tag context
displayCurrentTagIndicator(tag);
if (!idArg) {
console.error(chalk.red('Error: Please provide a task ID'));
process.exit(1);
}
// Check if multiple IDs are provided (comma-separated)
const taskIds = idArg
.split(',')
.map((id) => id.trim())
.filter((id) => id.length > 0);
if (taskIds.length > 1) {
// Multiple tasks - use compact summary view with interactive drill-down
await displayMultipleTasksSummary(
taskMaster.getTasksPath(),
taskIds,
taskMaster.getComplexityReportPath(),
statusFilter,
{ projectRoot: taskMaster.getProjectRoot(), tag }
);
} else {
// Single task - use detailed view
await displayTaskById(
taskMaster.getTasksPath(),
taskIds[0],
taskMaster.getComplexityReportPath(),
statusFilter,
{ projectRoot: taskMaster.getProjectRoot(), tag }
);
}
});
// add-dependency command // add-dependency command
programInstance programInstance
.command('add-dependency') .command('add-dependency')

View File

@@ -1,12 +1,8 @@
import { defineConfig } from 'tsup'; import { defineConfig } from 'tsup';
import { import { baseConfig, mergeConfig } from '@tm/build-config';
executableConfig,
mergeConfig,
commonExternals
} from '@tm/build-config';
export default defineConfig( export default defineConfig(
mergeConfig(executableConfig, { mergeConfig(baseConfig, {
entry: { entry: {
'task-master': 'bin/task-master.js', 'task-master': 'bin/task-master.js',
'mcp-server': 'mcp-server/server.js' 'mcp-server': 'mcp-server/server.js'
@@ -15,6 +11,16 @@ export default defineConfig(
publicDir: 'public', publicDir: 'public',
// Bundle our monorepo packages but keep node_modules external // Bundle our monorepo packages but keep node_modules external
noExternal: [/@tm\/.*/], noExternal: [/@tm\/.*/],
external: commonExternals // Ensure no code splitting
splitting: false,
// Better watch configuration
ignoreWatch: [
'dist',
'node_modules',
'.git',
'tests',
'*.test.*',
'*.spec.*'
]
}) })
); );

54
turbo.json Normal file
View File

@@ -0,0 +1,54 @@
{
"$schema": "https://turbo.build/schema.json",
"extends": ["//"],
"tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": ["dist/**"],
"outputLogs": "new-only"
},
"dev": {
"cache": false,
"persistent": true,
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
"!{packages,apps}/**/dist/**",
"!{packages,apps}/**/node_modules/**"
]
},
"test": {
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
"!{packages,apps}/**/dist/**",
"!{packages,apps}/**/node_modules/**"
],
"outputLogs": "new-only"
},
"test:watch": {
"cache": false,
"persistent": true,
"dependsOn": ["^build"]
},
"lint": {
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
"!{packages,apps}/**/dist/**",
"!{packages,apps}/**/node_modules/**"
],
"outputLogs": "new-only"
},
"typecheck": {
"dependsOn": ["^build"],
"inputs": [
"$TURBO_DEFAULT$",
"!{packages,apps}/**/dist/**",
"!{packages,apps}/**/node_modules/**"
],
"outputLogs": "new-only"
}
},
"globalDependencies": ["turbo.json", "tsconfig.json", ".env*"]
}