chore: fix CI with new typescript setup (#1194)

Co-authored-by: Ralph Khreish <Crunchyman-ralph@users.noreply.github.com>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
This commit is contained in:
Ralph Khreish
2025-09-09 23:35:47 +02:00
committed by GitHub
parent 587745046f
commit 3eeb19590a
20 changed files with 10514 additions and 4927 deletions

View File

@@ -9,70 +9,109 @@ on:
branches:
- main
- next
workflow_dispatch:
permissions:
contents: read
env:
DO_NOT_TRACK: 1
NODE_ENV: development
jobs:
setup:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- name: Install Dependencies
id: install
run: npm ci
timeout-minutes: 2
- name: Cache node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }}
# Fast checks that can run in parallel
format-check:
needs: setup
name: Format Check
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Restore node_modules
uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }}
- name: Install dependencies
run: npm install --frozen-lockfile --prefer-offline
timeout-minutes: 5
- name: Format Check
run: npm run format-check
env:
FORCE_COLOR: 1
test:
needs: setup
typecheck:
name: Typecheck
timeout-minutes: 10
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Restore node_modules
uses: actions/cache@v4
- name: Install dependencies
run: npm install --frozen-lockfile --prefer-offline
timeout-minutes: 5
- name: Typecheck
run: npm run typecheck
env:
FORCE_COLOR: 1
# Build job to ensure everything compiles
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
path: node_modules
key: ${{ runner.os }}-node-modules-${{ hashFiles('**/package-lock.json') }}
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm install --frozen-lockfile --prefer-offline
timeout-minutes: 5
- name: Build
run: npm run build
env:
NODE_ENV: production
FORCE_COLOR: 1
test:
name: Test
timeout-minutes: 15
runs-on: ubuntu-latest
needs: [format-check, typecheck, build]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-node@v4
with:
node-version: 20
cache: "npm"
- name: Install dependencies
run: npm install --frozen-lockfile --prefer-offline
timeout-minutes: 5
- name: Build packages (required for tests)
run: npm run build:packages
env:
NODE_ENV: production
- name: Run Tests
run: |
@@ -81,7 +120,6 @@ jobs:
NODE_ENV: test
CI: true
FORCE_COLOR: 1
timeout-minutes: 10
- name: Upload Test Results
if: always()

View File

@@ -4,12 +4,11 @@
"description": "Task Master CLI - Command line interface for task management",
"type": "module",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
"types": "./src/index.ts",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./dist/index.js",
"require": "./dist/index.js"
"import": "./dist/index.js"
}
},
"files": ["dist", "README.md"],
@@ -20,12 +19,17 @@
"lint": "biome check src",
"format": "biome format --write src",
"test": "vitest run",
"test:watch": "vitest"
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"test:unit": "vitest run -t unit",
"test:integration": "vitest run -t integration",
"test:e2e": "vitest run --dir tests/e2e",
"test:ci": "vitest run --coverage --reporter=dot"
},
"dependencies": {
"@tm/core": "*",
"boxen": "^7.1.1",
"chalk": "^5.3.0",
"chalk": "5.6.2",
"cli-table3": "^0.6.5",
"commander": "^12.1.0",
"inquirer": "^9.2.10",
@@ -33,6 +37,7 @@
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@tm/build-config": "*",
"@types/inquirer": "^9.0.3",
"@types/node": "^22.10.5",
"tsup": "^8.3.0",

View File

@@ -15,6 +15,7 @@ import {
STATUS_ICONS,
type OutputFormat
} from '@tm/core';
import type { StorageType } from '@tm/core/types';
import * as ui from '../utils/ui.js';
/**
@@ -37,7 +38,7 @@ export interface ListTasksResult {
total: number;
filtered: number;
tag?: string;
storageType: 'file' | 'api';
storageType: Exclude<StorageType, 'auto'>;
}
/**
@@ -172,6 +173,13 @@ export class ListTasksCommand extends Command {
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;
}

View File

@@ -6,7 +6,7 @@
import chalk from 'chalk';
import boxen from 'boxen';
import Table from 'cli-table3';
import type { Task, TaskStatus, TaskPriority } from '@tm/core';
import type { Task, TaskStatus, TaskPriority } from '@tm/core/types';
/**
* Get colored status display with ASCII icons (matches scripts/modules/ui.js style)

View File

@@ -1,15 +1,8 @@
import { defineConfig } from 'tsup';
import { cliConfig, mergeConfig } from '@tm/build-config';
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
target: 'node18',
splitting: false,
sourcemap: true,
clean: true,
dts: true,
shims: true,
esbuildOptions(options) {
options.platform = 'node';
}
});
export default defineConfig(
mergeConfig(cliConfig, {
entry: ['src/index.ts']
})
);

14811
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -16,13 +16,20 @@
"dev:packages": "(cd packages/tm-core && npm run dev) & (cd apps/cli && npm run dev) & wait",
"dev:core": "cd packages/tm-core && npm run dev",
"dev:cli": "cd apps/cli && npm run dev",
"build:packages": "npm run build:core && npm run build:cli",
"build:packages": "npm run build:build-config && npm run build:core && npm run build:cli",
"build:build-config": "cd packages/build-config && npm run build",
"build:core": "cd packages/tm-core && npm run build",
"build:cli": "cd apps/cli && npm run build",
"typecheck": "npm run typecheck:core && npm run typecheck:cli",
"typecheck:core": "cd packages/tm-core && npm run typecheck",
"typecheck:cli": "cd apps/cli && npm run typecheck",
"test": "node --experimental-vm-modules node_modules/.bin/jest",
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
"test:fails": "node --experimental-vm-modules node_modules/.bin/jest --onlyFailures",
"test:watch": "node --experimental-vm-modules node_modules/.bin/jest --watch",
"test:coverage": "node --experimental-vm-modules node_modules/.bin/jest --coverage",
"test:ci": "node --experimental-vm-modules node_modules/.bin/jest --coverage --ci",
"test:e2e": "./tests/e2e/run_e2e.sh",
"test:e2e-report": "./tests/e2e/run_e2e.sh --analyze-log",
"postpack": "chmod +x dist/task-master.js dist/mcp-server.js",
@@ -67,7 +74,7 @@
"ajv": "^8.17.1",
"ajv-formats": "^3.0.1",
"boxen": "^8.0.1",
"chalk": "^5.4.1",
"chalk": "5.6.2",
"cli-highlight": "^2.1.11",
"cli-progress": "^3.12.0",
"cli-table3": "^0.6.5",

View File

@@ -0,0 +1,31 @@
{
"name": "@tm/build-config",
"version": "1.0.0",
"description": "Shared build configuration for Task Master monorepo",
"type": "module",
"main": "./dist/tsup.base.js",
"types": "./dist/tsup.base.d.ts",
"exports": {
".": {
"types": "./src/tsup.base.ts",
"import": "./dist/tsup.base.js",
"require": "./dist/tsup.base.cjs"
}
},
"files": ["dist", "src"],
"keywords": ["build-config", "tsup", "monorepo"],
"author": "",
"license": "MIT",
"scripts": {
"build": "tsup",
"dev": "tsup --watch",
"typecheck": "tsc --noEmit"
},
"devDependencies": {
"tsup": "^8.5.0",
"typescript": "^5.7.3"
},
"peerDependencies": {
"tsup": "^8.0.0"
}
}

View File

@@ -0,0 +1,151 @@
/**
* Base tsup configuration for Task Master monorepo
* Provides shared configuration that can be extended by individual packages
*/
import type { Options } from 'tsup';
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = !isProduction;
/**
* Base configuration for library packages (tm-core, etc.)
*/
export const libraryConfig: Partial<Options> = {
format: ['cjs', 'esm'],
target: 'es2022',
// Sourcemaps only in development to reduce production bundle size
sourcemap: isDevelopment,
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
*/
export const cliConfig: Partial<Options> = {
format: ['esm'],
target: 'node18',
splitting: false,
// Sourcemaps only in development to reduce production bundle size
sourcemap: isDevelopment,
clean: true,
dts: true,
shims: true,
// Enable minification in production for smaller bundles
minify: 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,
// Sourcemaps only in development to reduce production bundle size
sourcemap: isDevelopment,
clean: true,
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) {
options.platform = 'node';
// Allow importing TypeScript from JavaScript
options.resolveExtensions = ['.ts', '.js', '.mjs', '.json'];
// Better source mapping in development only
options.sourcesContent = isDevelopment;
// Keep original names for better debugging in development
options.keepNames = isDevelopment;
}
};
/**
* Common external modules that should not be bundled
*/
export const commonExternals = [
// Native Node.js modules
'fs',
'path',
'child_process',
'crypto',
'os',
'url',
'util',
'stream',
'http',
'https',
'events',
'assert',
'buffer',
'querystring',
'readline',
'zlib',
'tty',
'net',
'dgram',
'dns',
'tls',
'cluster',
'process',
'module'
];
/**
* Utility function to merge configurations
*/
export function mergeConfig(
baseConfig: Partial<Options>,
overrides: Partial<Options>
): Options {
return {
...baseConfig,
...overrides,
// Merge arrays instead of overwriting
external: [...(baseConfig.external || []), ...(overrides.external || [])],
// Merge esbuildOptions
esbuildOptions(options, context) {
if (baseConfig.esbuildOptions) {
baseConfig.esbuildOptions(options, context);
}
if (overrides.esbuildOptions) {
overrides.esbuildOptions(options, context);
}
}
} as Options;
}
/**
* Environment helpers
*/
export const env = {
isProduction,
isDevelopment,
NODE_ENV: process.env.NODE_ENV || 'development'
};

View File

@@ -0,0 +1,20 @@
{
"compilerOptions": {
"target": "ES2022",
"lib": ["ES2022"],
"module": "ESNext",
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"allowJs": true,
"strict": true,
"noEmit": true,
"resolveJsonModule": true,
"isolatedModules": true,
"declaration": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

View File

@@ -0,0 +1,23 @@
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

@@ -1,15 +1,55 @@
{
"name": "@tm/core",
"version": "1.0.0",
"private": true,
"description": "Core library for Task Master - TypeScript task management system",
"type": "module",
"types": "./dist/index.d.ts",
"types": "./src/index.ts",
"main": "./dist/index.js",
"exports": {
".": {
"types": "./src/index.ts",
"import": "./dist/index.js",
"require": "./dist/index.js"
"import": "./dist/index.js"
},
"./auth": {
"types": "./src/auth/index.ts",
"import": "./dist/auth/index.js"
},
"./storage": {
"types": "./src/storage/index.ts",
"import": "./dist/storage/index.js"
},
"./config": {
"types": "./src/config/index.ts",
"import": "./dist/config/index.js"
},
"./providers": {
"types": "./src/providers/index.ts",
"import": "./dist/providers/index.js"
},
"./services": {
"types": "./src/services/index.ts",
"import": "./dist/services/index.js"
},
"./errors": {
"types": "./src/errors/index.ts",
"import": "./dist/errors/index.js"
},
"./logger": {
"types": "./src/logger/index.ts",
"import": "./dist/logger/index.js"
},
"./types": {
"types": "./src/types/index.ts",
"import": "./dist/types/index.js"
},
"./interfaces": {
"types": "./src/interfaces/index.ts",
"import": "./dist/interfaces/index.js"
},
"./utils": {
"types": "./src/utils/index.ts",
"import": "./dist/utils/index.js"
}
},
"scripts": {
@@ -26,10 +66,12 @@
"typecheck": "tsc --noEmit"
},
"dependencies": {
"@supabase/supabase-js": "^2.57.4",
"zod": "^3.22.4"
},
"devDependencies": {
"@biomejs/biome": "^1.9.4",
"@tm/build-config": "*",
"@types/node": "^20.11.30",
"@vitest/coverage-v8": "^2.0.5",
"ts-node": "^10.9.2",

View File

@@ -3,7 +3,11 @@
* This file defines the contract for configuration management
*/
import type { TaskComplexity, TaskPriority } from '../types/index.js';
import type {
TaskComplexity,
TaskPriority,
StorageType
} from '../types/index.js';
/**
* Model configuration for different AI roles
@@ -73,14 +77,6 @@ export interface TagSettings {
tagNamingConvention: 'kebab-case' | 'camelCase' | 'snake_case';
}
/**
* Storage type options
* - 'file': Local file system storage
* - 'api': Remote API storage (Hamster integration)
* - 'auto': Automatically detect based on auth status
*/
export type StorageType = 'file' | 'api' | 'auto';
/**
* Runtime storage configuration used for storage backend selection
* This is what getStorageConfig() returns and what StorageFactory expects

View File

@@ -3,7 +3,12 @@
* Core service for task operations - handles business logic between storage and API
*/
import type { Task, TaskFilter, TaskStatus } from '../types/index.js';
import type {
Task,
TaskFilter,
TaskStatus,
StorageType
} from '../types/index.js';
import type { IStorage } from '../interfaces/storage.interface.js';
import { ConfigManager } from '../config/config-manager.js';
import { StorageFactory } from '../storage/storage-factory.js';
@@ -23,7 +28,7 @@ export interface TaskListResult {
/** The tag these tasks belong to (only present if explicitly provided) */
tag?: string;
/** Storage type being used */
storageType: 'file' | 'api';
storageType: StorageType;
}
/**
@@ -113,7 +118,7 @@ export class TaskService {
total: rawTasks.length,
filtered: filteredEntities.length,
tag: options.tag, // Only include tag if explicitly provided
storageType: this.configManager.getStorageConfig().type
storageType: this.getStorageType()
};
} catch (error) {
throw new TaskMasterError(
@@ -166,7 +171,7 @@ export class TaskService {
byStatus: Record<TaskStatus, number>;
withSubtasks: number;
blocked: number;
storageType: 'file' | 'api';
storageType: StorageType;
}> {
const result = await this.getTaskList({
tag,
@@ -334,8 +339,12 @@ export class TaskService {
/**
* Get current storage type
*/
getStorageType(): 'file' | 'api' {
return this.configManager.getStorageConfig().type;
getStorageType(): StorageType {
// Prefer the runtime storage type if available to avoid exposing 'auto'
const s = this.storage as { getType?: () => 'file' | 'api' } | null;
const runtimeType = s?.getType?.();
return (runtimeType ??
this.configManager.getStorageConfig().type) as StorageType;
}
/**

View File

@@ -10,7 +10,12 @@ import {
} from './services/task-service.js';
import { ERROR_CODES, TaskMasterError } from './errors/task-master-error.js';
import type { IConfiguration } from './interfaces/configuration.interface.js';
import type { Task, TaskStatus, TaskFilter } from './types/index.js';
import type {
Task,
TaskStatus,
TaskFilter,
StorageType
} from './types/index.js';
/**
* Options for creating TaskMasterCore instance
@@ -152,7 +157,7 @@ export class TaskMasterCore {
/**
* Get current storage type
*/
getStorageType(): 'file' | 'api' {
getStorageType(): StorageType {
return this.taskService.getStorageType();
}

View File

@@ -2,6 +2,14 @@
* Core type definitions for Task Master
*/
/**
* Storage type options
* - 'file': Local file system storage
* - 'api': Remote API storage (Hamster integration)
* - 'auto': Automatically detect based on auth status
*/
export type StorageType = 'file' | 'api' | 'auto';
// ============================================================================
// Type Literals
// ============================================================================

View File

@@ -7,6 +7,7 @@
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"baseUrl": ".",
"rootDir": "./src",
"strict": true,
"noImplicitAny": true,
@@ -27,16 +28,7 @@
"moduleDetection": "force",
"types": ["node"],
"resolveJsonModule": true,
"isolatedModules": true,
"paths": {
"@/*": ["./src/*"],
"@/types": ["./src/types"],
"@/providers": ["./src/providers"],
"@/storage": ["./src/storage"],
"@/parser": ["./src/parser"],
"@/utils": ["./src/utils"],
"@/errors": ["./src/errors"]
}
"isolatedModules": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"]

View File

@@ -1,8 +1,15 @@
import { defineConfig } from 'tsup';
import { libraryConfig, mergeConfig } from '@tm/build-config';
export default defineConfig({
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',
@@ -10,18 +17,8 @@ export default defineConfig({
'utils/index': 'src/utils/index.ts',
'errors/index': 'src/errors/index.ts'
},
format: ['cjs', 'esm'],
dts: true,
sourcemap: true,
clean: true,
splitting: false,
treeshake: true,
minify: false,
target: 'es2022',
tsconfig: './tsconfig.json',
outDir: 'dist',
external: ['zod'],
esbuildOptions(options) {
options.conditions = ['module'];
}
});
external: ['zod', '@supabase/supabase-js']
})
);

View File

@@ -198,11 +198,13 @@ jest.unstable_mockModule('fs', () => ({
default: {
existsSync: jest.fn(() => false),
readFileSync: jest.fn(),
writeFileSync: mockWriteFileSync
writeFileSync: mockWriteFileSync,
unlinkSync: jest.fn()
},
existsSync: jest.fn(() => false),
readFileSync: jest.fn(),
writeFileSync: mockWriteFileSync
writeFileSync: mockWriteFileSync,
unlinkSync: jest.fn()
}));
jest.unstable_mockModule(

View File

@@ -1,55 +1,20 @@
import { defineConfig } from 'tsup';
import {
executableConfig,
mergeConfig,
commonExternals
} from '@tm/build-config';
export default defineConfig({
export default defineConfig(
mergeConfig(executableConfig, {
entry: {
'task-master': 'bin/task-master.js',
'mcp-server': 'mcp-server/server.js'
},
format: ['esm'],
target: 'node18',
splitting: false,
sourcemap: true,
clean: true,
bundle: true, // Bundle everything into one file
outDir: 'dist',
publicDir: 'public',
// Handle TypeScript imports transparently
loader: {
'.js': 'jsx',
'.ts': 'ts'
},
esbuildOptions(options) {
options.platform = 'node';
// Allow importing TypeScript from JavaScript
options.resolveExtensions = ['.ts', '.js', '.mjs', '.json'];
},
// Bundle our monorepo packages but keep node_modules external
noExternal: [/@tm\/.*/],
external: [
// Keep native node modules external
'fs',
'path',
'child_process',
'crypto',
'os',
'url',
'util',
'stream',
'http',
'https',
'events',
'assert',
'buffer',
'querystring',
'readline',
'zlib',
'tty',
'net',
'dgram',
'dns',
'tls',
'cluster',
'process',
'module'
]
});
external: commonExternals
})
);