feat: add tm show (#1199)
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -93,4 +93,7 @@ dev-debug.log
|
|||||||
apps/extension/.vscode-test/
|
apps/extension/.vscode-test/
|
||||||
|
|
||||||
# apps/extension
|
# apps/extension
|
||||||
apps/extension/vsix-build/
|
apps/extension/vsix-build/
|
||||||
|
|
||||||
|
# turbo
|
||||||
|
.turbo
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
406
apps/cli/src/commands/show.command.ts
Normal file
406
apps/cli/src/commands/show.command.ts
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
import { defineConfig } from 'tsup';
|
|
||||||
import { cliConfig, mergeConfig } from '@tm/build-config';
|
|
||||||
|
|
||||||
export default defineConfig(
|
|
||||||
mergeConfig(cliConfig, {
|
|
||||||
entry: ['src/index.ts']
|
|
||||||
})
|
|
||||||
);
|
|
||||||
@@ -9,6 +9,6 @@
|
|||||||
"preview": "mintlify preview"
|
"preview": "mintlify preview"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"mintlify": "^4.0.0"
|
"mintlify": "^4.2.111"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"',
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
3120
package-lock.json
generated
3120
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
57
package.json
57
package.json
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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'
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -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"
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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';
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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"]
|
||||||
|
|||||||
@@ -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']
|
|
||||||
})
|
|
||||||
);
|
|
||||||
@@ -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')
|
||||||
|
|||||||
@@ -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
54
turbo.json
Normal 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*"]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user