mirror of
https://github.com/eyaltoledano/claude-task-master.git
synced 2026-01-30 06:12:05 +00:00
chore: refactor tm-core to host more of our "core" commands (#1331)
This commit is contained in:
@@ -1,28 +0,0 @@
|
||||
/**
|
||||
* Authentication module exports
|
||||
*/
|
||||
|
||||
export { AuthManager } from './auth-manager.js';
|
||||
export { CredentialStore } from './credential-store.js';
|
||||
export { OAuthService } from './oauth-service.js';
|
||||
export { SupabaseSessionStorage } from './supabase-session-storage.js';
|
||||
export type {
|
||||
Organization,
|
||||
Brief,
|
||||
RemoteTask
|
||||
} from '../services/organization.service.js';
|
||||
|
||||
export type {
|
||||
AuthCredentials,
|
||||
OAuthFlowOptions,
|
||||
AuthConfig,
|
||||
CliData,
|
||||
UserContext
|
||||
} from './types.js';
|
||||
|
||||
export { AuthenticationError } from './types.js';
|
||||
|
||||
export {
|
||||
DEFAULT_AUTH_CONFIG,
|
||||
getAuthConfig
|
||||
} from './config.js';
|
||||
@@ -8,8 +8,8 @@ export type * from './storage.interface.js';
|
||||
export * from './storage.interface.js';
|
||||
|
||||
// AI Provider interfaces
|
||||
export type * from './ai-provider.interface.js';
|
||||
export * from './ai-provider.interface.js';
|
||||
export type * from '../../modules/ai/interfaces/ai-provider.interface.js';
|
||||
export * from '../../modules/ai/interfaces/ai-provider.interface.js';
|
||||
|
||||
// Configuration interfaces
|
||||
export type * from './configuration.interface.js';
|
||||
@@ -164,6 +164,12 @@ export interface IStorage {
|
||||
* @returns Promise that resolves to storage statistics
|
||||
*/
|
||||
getStats(): Promise<StorageStats>;
|
||||
|
||||
/**
|
||||
* Get the storage type identifier
|
||||
* @returns The type of storage implementation ('file' or 'api')
|
||||
*/
|
||||
getStorageType(): 'file' | 'api';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -241,6 +247,7 @@ export abstract class BaseStorage implements IStorage {
|
||||
abstract initialize(): Promise<void>;
|
||||
abstract close(): Promise<void>;
|
||||
abstract getStats(): Promise<StorageStats>;
|
||||
abstract getStorageType(): 'file' | 'api';
|
||||
|
||||
/**
|
||||
* Utility method to generate backup filename
|
||||
@@ -1,12 +0,0 @@
|
||||
/**
|
||||
* Public API for the executors module
|
||||
*/
|
||||
|
||||
export * from './types.js';
|
||||
export { BaseExecutor } from './base-executor.js';
|
||||
export { ClaudeExecutor } from './claude-executor.js';
|
||||
export { ExecutorFactory } from './executor-factory.js';
|
||||
export {
|
||||
ExecutorService,
|
||||
type ExecutorServiceOptions
|
||||
} from './executor-service.js';
|
||||
@@ -1,117 +1,127 @@
|
||||
/**
|
||||
* @fileoverview Main entry point for the tm-core package
|
||||
* This file exports all public APIs from the core Task Master library
|
||||
* @fileoverview Main entry point for @tm/core
|
||||
* Provides unified access to all Task Master functionality through TmCore
|
||||
*/
|
||||
|
||||
// Export main facade
|
||||
export {
|
||||
TaskMasterCore,
|
||||
createTaskMasterCore,
|
||||
type TaskMasterCoreOptions,
|
||||
type ListTasksResult,
|
||||
type StartTaskOptions,
|
||||
type StartTaskResult,
|
||||
type ConflictCheckResult,
|
||||
type ExportTasksOptions,
|
||||
type ExportResult
|
||||
} from './task-master-core.js';
|
||||
import type { TasksDomain } from './modules/tasks/tasks-domain.js';
|
||||
|
||||
// Re-export types
|
||||
export type * from './types/index.js';
|
||||
// ========== Primary API ==========
|
||||
|
||||
// Re-export interfaces (types only to avoid conflicts)
|
||||
export type * from './interfaces/index.js';
|
||||
/**
|
||||
* Create a new TmCore instance - The ONLY way to use tm-core
|
||||
*
|
||||
* @example
|
||||
* ```typescript
|
||||
* import { createTmCore } from '@tm/core';
|
||||
*
|
||||
* const tmcore = await createTmCore({
|
||||
* projectPath: process.cwd()
|
||||
* });
|
||||
*
|
||||
* // Access domains
|
||||
* await tmcore.auth.login({ ... });
|
||||
* const tasks = await tmcore.tasks.list();
|
||||
* await tmcore.workflow.start({ taskId: '1' });
|
||||
* await tmcore.git.commit('feat: add feature');
|
||||
* const config = tmcore.config.get('models.main');
|
||||
* ```
|
||||
*/
|
||||
export { createTmCore, TmCore, type TmCoreOptions } from './tm-core.js';
|
||||
|
||||
// Re-export constants
|
||||
export * from './constants/index.js';
|
||||
// ========== Type Exports ==========
|
||||
|
||||
// Re-export providers
|
||||
export * from './providers/index.js';
|
||||
// Common types that consumers need
|
||||
export type * from './common/types/index.js';
|
||||
|
||||
// Re-export storage (selectively to avoid conflicts)
|
||||
export {
|
||||
FileStorage,
|
||||
ApiStorage,
|
||||
StorageFactory,
|
||||
type ApiStorageConfig
|
||||
} from './storage/index.js';
|
||||
export { PlaceholderStorage, type StorageAdapter } from './storage/index.js';
|
||||
// Common interfaces
|
||||
export type * from './common/interfaces/index.js';
|
||||
|
||||
// Re-export parser
|
||||
export * from './parser/index.js';
|
||||
// Constants
|
||||
export * from './common/constants/index.js';
|
||||
|
||||
// Re-export utilities
|
||||
export * from './utils/index.js';
|
||||
// Errors
|
||||
export * from './common/errors/index.js';
|
||||
|
||||
// Re-export errors
|
||||
export * from './errors/index.js';
|
||||
// ========== Domain-Specific Type Exports ==========
|
||||
|
||||
// Re-export entities
|
||||
export { TaskEntity } from './entities/task.entity.js';
|
||||
// Task types
|
||||
export type {
|
||||
TaskListResult,
|
||||
GetTaskListOptions
|
||||
} from './modules/tasks/services/task-service.js';
|
||||
|
||||
// Re-export authentication
|
||||
export {
|
||||
AuthManager,
|
||||
AuthenticationError,
|
||||
type AuthCredentials,
|
||||
type OAuthFlowOptions,
|
||||
type AuthConfig
|
||||
} from './auth/index.js';
|
||||
export type {
|
||||
StartTaskOptions,
|
||||
StartTaskResult,
|
||||
ConflictCheckResult
|
||||
} from './modules/tasks/services/task-execution-service.js';
|
||||
|
||||
// Re-export logger
|
||||
export { getLogger, createLogger, setGlobalLogger } from './logger/index.js';
|
||||
export type {
|
||||
PreflightResult,
|
||||
CheckResult
|
||||
} from './modules/tasks/services/preflight-checker.service.js';
|
||||
|
||||
// Re-export executors
|
||||
export * from './executors/index.js';
|
||||
// Task domain result types
|
||||
export type TaskWithSubtaskResult = Awaited<ReturnType<TasksDomain['get']>>;
|
||||
|
||||
// Re-export reports
|
||||
export {
|
||||
ComplexityReportManager,
|
||||
type ComplexityReport,
|
||||
type ComplexityReportMetadata,
|
||||
type ComplexityAnalysis,
|
||||
type TaskComplexityData
|
||||
} from './reports/index.js';
|
||||
// Auth types
|
||||
export type {
|
||||
AuthCredentials,
|
||||
OAuthFlowOptions,
|
||||
UserContext
|
||||
} from './modules/auth/types.js';
|
||||
export { AuthenticationError } from './modules/auth/types.js';
|
||||
|
||||
// Re-export services
|
||||
export {
|
||||
PreflightChecker,
|
||||
TaskLoaderService,
|
||||
type CheckResult,
|
||||
type PreflightResult,
|
||||
type TaskValidationResult,
|
||||
type ValidationErrorType,
|
||||
type DependencyIssue
|
||||
} from './services/index.js';
|
||||
// Workflow types
|
||||
export type {
|
||||
StartWorkflowOptions,
|
||||
WorkflowStatus,
|
||||
NextAction
|
||||
} from './modules/workflow/services/workflow.service.js';
|
||||
|
||||
// Re-export Git adapter
|
||||
export { GitAdapter } from './git/git-adapter.js';
|
||||
export {
|
||||
CommitMessageGenerator,
|
||||
type CommitMessageOptions
|
||||
} from './git/commit-message-generator.js';
|
||||
|
||||
// Re-export workflow orchestrator, state manager, activity logger, and types
|
||||
export { WorkflowOrchestrator } from './workflow/workflow-orchestrator.js';
|
||||
export { WorkflowStateManager } from './workflow/workflow-state-manager.js';
|
||||
export { WorkflowActivityLogger } from './workflow/workflow-activity-logger.js';
|
||||
export type {
|
||||
WorkflowPhase,
|
||||
TDDPhase,
|
||||
WorkflowContext,
|
||||
WorkflowState,
|
||||
WorkflowEvent,
|
||||
WorkflowEventData,
|
||||
WorkflowEventListener,
|
||||
SubtaskInfo,
|
||||
TestResult,
|
||||
WorkflowError
|
||||
} from './workflow/types.js';
|
||||
TestResult
|
||||
} from './modules/workflow/types.js';
|
||||
|
||||
// Re-export workflow service
|
||||
export { WorkflowService } from './services/workflow.service.js';
|
||||
// Git types
|
||||
export type { CommitMessageOptions } from './modules/git/services/commit-message-generator.js';
|
||||
|
||||
// Integration types
|
||||
export type {
|
||||
StartWorkflowOptions,
|
||||
WorkflowStatus,
|
||||
NextAction
|
||||
} from './services/workflow.service.js';
|
||||
ExportTasksOptions,
|
||||
ExportResult
|
||||
} from './modules/integration/services/export.service.js';
|
||||
|
||||
// Reports types
|
||||
export type {
|
||||
ComplexityReport,
|
||||
ComplexityReportMetadata,
|
||||
ComplexityAnalysis,
|
||||
TaskComplexityData
|
||||
} from './modules/reports/types.js';
|
||||
|
||||
// ========== Advanced API (for CLI/Extension/MCP) ==========
|
||||
|
||||
// Auth - Advanced
|
||||
export { AuthManager } from './modules/auth/managers/auth-manager.js';
|
||||
|
||||
// Workflow - Advanced
|
||||
export { WorkflowOrchestrator } from './modules/workflow/orchestrators/workflow-orchestrator.js';
|
||||
export { WorkflowStateManager } from './modules/workflow/managers/workflow-state-manager.js';
|
||||
export { WorkflowService } from './modules/workflow/services/workflow.service.js';
|
||||
export type { SubtaskInfo } from './modules/workflow/types.js';
|
||||
|
||||
// Git - Advanced
|
||||
export { GitAdapter } from './modules/git/adapters/git-adapter.js';
|
||||
export { CommitMessageGenerator } from './modules/git/services/commit-message-generator.js';
|
||||
|
||||
// Tasks - Advanced
|
||||
export { PreflightChecker } from './modules/tasks/services/preflight-checker.service.js';
|
||||
export { TaskLoaderService } from './modules/tasks/services/task-loader.service.js';
|
||||
|
||||
// Integration - Advanced
|
||||
export { ExportService } from './modules/integration/services/export.service.js';
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
// Export all from AI module
|
||||
export * from './ai/index.js';
|
||||
export * from './providers/index.js';
|
||||
|
||||
// Storage providers will be exported here when implemented
|
||||
// export * from './storage/index.js';
|
||||
@@ -6,12 +6,15 @@
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../errors/task-master-error.js';
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import type {
|
||||
AIOptions,
|
||||
AIResponse,
|
||||
IAIProvider
|
||||
} from '../../interfaces/ai-provider.interface.js';
|
||||
IAIProvider,
|
||||
ProviderUsageStats,
|
||||
ProviderInfo,
|
||||
AIModel
|
||||
} from '../interfaces/ai-provider.interface.js';
|
||||
|
||||
// Constants for retry logic
|
||||
const DEFAULT_MAX_RETRIES = 3;
|
||||
@@ -428,17 +431,10 @@ export abstract class BaseProvider implements IAIProvider {
|
||||
options?: AIOptions
|
||||
): AsyncIterator<Partial<AIResponse>>;
|
||||
abstract isAvailable(): Promise<boolean>;
|
||||
abstract getProviderInfo(): import(
|
||||
'../../interfaces/ai-provider.interface.js'
|
||||
).ProviderInfo;
|
||||
abstract getAvailableModels(): import(
|
||||
'../../interfaces/ai-provider.interface.js'
|
||||
).AIModel[];
|
||||
abstract getProviderInfo(): ProviderInfo;
|
||||
abstract getAvailableModels(): AIModel[];
|
||||
abstract validateCredentials(): Promise<boolean>;
|
||||
abstract getUsageStats(): Promise<
|
||||
| import('../../interfaces/ai-provider.interface.js').ProviderUsageStats
|
||||
| null
|
||||
>;
|
||||
abstract getUsageStats(): Promise<ProviderUsageStats | null>;
|
||||
abstract initialize(): Promise<void>;
|
||||
abstract close(): Promise<void>;
|
||||
}
|
||||
208
packages/tm-core/src/modules/auth/auth-domain.ts
Normal file
208
packages/tm-core/src/modules/auth/auth-domain.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
/**
|
||||
* @fileoverview Auth Domain Facade
|
||||
* Public API for authentication and authorization
|
||||
*/
|
||||
|
||||
import path from 'node:path';
|
||||
import { AuthManager } from './managers/auth-manager.js';
|
||||
import type {
|
||||
AuthCredentials,
|
||||
OAuthFlowOptions,
|
||||
UserContext
|
||||
} from './types.js';
|
||||
import type {
|
||||
Organization,
|
||||
Brief,
|
||||
RemoteTask
|
||||
} from './services/organization.service.js';
|
||||
import type { StorageType } from '../../common/types/index.js';
|
||||
|
||||
/**
|
||||
* Display information for storage context
|
||||
*/
|
||||
export interface StorageDisplayInfo {
|
||||
storageType: Exclude<StorageType, 'auto'>;
|
||||
briefInfo?: {
|
||||
briefId: string;
|
||||
briefName: string;
|
||||
orgSlug?: string;
|
||||
webAppUrl?: string;
|
||||
};
|
||||
filePath?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Auth Domain - Unified API for authentication operations
|
||||
*/
|
||||
export class AuthDomain {
|
||||
private authManager: AuthManager;
|
||||
|
||||
constructor() {
|
||||
this.authManager = AuthManager.getInstance();
|
||||
}
|
||||
|
||||
// ========== Authentication ==========
|
||||
|
||||
/**
|
||||
* Check if user is authenticated
|
||||
*/
|
||||
isAuthenticated(): boolean {
|
||||
return this.authManager.isAuthenticated();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get stored credentials
|
||||
*/
|
||||
getCredentials(): AuthCredentials | null {
|
||||
return this.authManager.getCredentials();
|
||||
}
|
||||
|
||||
/**
|
||||
* Authenticate with OAuth flow
|
||||
*/
|
||||
async authenticateWithOAuth(
|
||||
options?: OAuthFlowOptions
|
||||
): Promise<AuthCredentials> {
|
||||
return this.authManager.authenticateWithOAuth(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get OAuth authorization URL
|
||||
*/
|
||||
getAuthorizationUrl(): string | null {
|
||||
return this.authManager.getAuthorizationUrl();
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh authentication token
|
||||
*/
|
||||
async refreshToken(): Promise<AuthCredentials> {
|
||||
return this.authManager.refreshToken();
|
||||
}
|
||||
|
||||
/**
|
||||
* Logout current user
|
||||
*/
|
||||
async logout(): Promise<void> {
|
||||
return this.authManager.logout();
|
||||
}
|
||||
|
||||
// ========== User Context Management ==========
|
||||
|
||||
/**
|
||||
* Get current user context (org/brief selection)
|
||||
*/
|
||||
getContext(): UserContext | null {
|
||||
return this.authManager.getContext();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update user context
|
||||
*/
|
||||
updateContext(context: Partial<UserContext>): void {
|
||||
return this.authManager.updateContext(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear user context
|
||||
*/
|
||||
clearContext(): void {
|
||||
return this.authManager.clearContext();
|
||||
}
|
||||
|
||||
// ========== Organization Management ==========
|
||||
|
||||
/**
|
||||
* Get all organizations for the authenticated user
|
||||
*/
|
||||
async getOrganizations(): Promise<Organization[]> {
|
||||
return this.authManager.getOrganizations();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific organization by ID
|
||||
*/
|
||||
async getOrganization(orgId: string): Promise<Organization | null> {
|
||||
return this.authManager.getOrganization(orgId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all briefs for a specific organization
|
||||
*/
|
||||
async getBriefs(orgId: string): Promise<Brief[]> {
|
||||
return this.authManager.getBriefs(orgId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a specific brief by ID
|
||||
*/
|
||||
async getBrief(briefId: string): Promise<Brief | null> {
|
||||
return this.authManager.getBrief(briefId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all tasks for a specific brief
|
||||
*/
|
||||
async getTasks(briefId: string): Promise<RemoteTask[]> {
|
||||
return this.authManager.getTasks(briefId);
|
||||
}
|
||||
|
||||
// ========== Display Information ==========
|
||||
|
||||
/**
|
||||
* Get storage display information for UI presentation
|
||||
* Includes brief info for API storage, file path for file storage
|
||||
*
|
||||
* @param resolvedStorageType - The actual storage type being used at runtime.
|
||||
* Get this from tmCore.tasks.getStorageType()
|
||||
*/
|
||||
getStorageDisplayInfo(
|
||||
resolvedStorageType: 'file' | 'api'
|
||||
): StorageDisplayInfo {
|
||||
if (resolvedStorageType === 'api') {
|
||||
const context = this.getContext();
|
||||
if (context?.briefId && context?.briefName) {
|
||||
return {
|
||||
storageType: 'api',
|
||||
briefInfo: {
|
||||
briefId: context.briefId,
|
||||
briefName: context.briefName,
|
||||
orgSlug: context.orgSlug,
|
||||
webAppUrl: this.getWebAppUrl()
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Default to file storage display
|
||||
return {
|
||||
storageType: 'file',
|
||||
filePath: path.join('.taskmaster', 'tasks', 'tasks.json')
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get web app base URL from environment configuration
|
||||
* @private
|
||||
*/
|
||||
private getWebAppUrl(): string | undefined {
|
||||
const baseDomain =
|
||||
process.env.TM_BASE_DOMAIN || process.env.TM_PUBLIC_BASE_DOMAIN;
|
||||
|
||||
if (!baseDomain) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// If it already includes protocol, use as-is
|
||||
if (baseDomain.startsWith('http://') || baseDomain.startsWith('https://')) {
|
||||
return baseDomain;
|
||||
}
|
||||
|
||||
// Otherwise, add protocol based on domain
|
||||
if (baseDomain.includes('localhost') || baseDomain.includes('127.0.0.1')) {
|
||||
return `http://${baseDomain}`;
|
||||
}
|
||||
|
||||
return `https://${baseDomain}`;
|
||||
}
|
||||
}
|
||||
29
packages/tm-core/src/modules/auth/index.ts
Normal file
29
packages/tm-core/src/modules/auth/index.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Authentication module exports
|
||||
*/
|
||||
|
||||
export { AuthDomain, type StorageDisplayInfo } from './auth-domain.js';
|
||||
export { AuthManager } from './managers/auth-manager.js';
|
||||
export { CredentialStore } from './services/credential-store.js';
|
||||
export { OAuthService } from './services/oauth-service.js';
|
||||
export { SupabaseSessionStorage } from './services/supabase-session-storage.js';
|
||||
export type {
|
||||
Organization,
|
||||
Brief,
|
||||
RemoteTask
|
||||
} from './services/organization.service.js';
|
||||
|
||||
export type {
|
||||
AuthCredentials,
|
||||
OAuthFlowOptions,
|
||||
AuthConfig,
|
||||
CliData,
|
||||
UserContext
|
||||
} from './types.js';
|
||||
|
||||
export { AuthenticationError } from './types.js';
|
||||
|
||||
export {
|
||||
DEFAULT_AUTH_CONFIG,
|
||||
getAuthConfig
|
||||
} from './config.js';
|
||||
@@ -8,17 +8,17 @@ import {
|
||||
AuthenticationError,
|
||||
AuthConfig,
|
||||
UserContext
|
||||
} from './types.js';
|
||||
import { CredentialStore } from './credential-store.js';
|
||||
import { OAuthService } from './oauth-service.js';
|
||||
import { SupabaseAuthClient } from '../clients/supabase-client.js';
|
||||
} from '../types.js';
|
||||
import { CredentialStore } from '../services/credential-store.js';
|
||||
import { OAuthService } from '../services/oauth-service.js';
|
||||
import { SupabaseAuthClient } from '../../integration/clients/supabase-client.js';
|
||||
import {
|
||||
OrganizationService,
|
||||
type Organization,
|
||||
type Brief,
|
||||
type RemoteTask
|
||||
} from '../services/organization.service.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
/**
|
||||
* Authentication manager class
|
||||
@@ -3,9 +3,9 @@
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import { CredentialStore } from './credential-store.js';
|
||||
import { AuthenticationError } from './types.js';
|
||||
import type { AuthCredentials } from './types.js';
|
||||
import { CredentialStore } from '../services/credential-store.js';
|
||||
import { AuthenticationError } from '../types.js';
|
||||
import type { AuthCredentials } from '../types.js';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { AuthCredentials, AuthenticationError, AuthConfig } from './types.js';
|
||||
import { getAuthConfig } from './config.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import { AuthCredentials, AuthenticationError, AuthConfig } from '../types.js';
|
||||
import { getAuthConfig } from '../config.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
/**
|
||||
* CredentialStore manages the persistence and retrieval of authentication credentials.
|
||||
@@ -12,12 +12,12 @@ import {
|
||||
OAuthFlowOptions,
|
||||
AuthConfig,
|
||||
CliData
|
||||
} from './types.js';
|
||||
import { CredentialStore } from './credential-store.js';
|
||||
import { SupabaseAuthClient } from '../clients/supabase-client.js';
|
||||
import { getAuthConfig } from './config.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import packageJson from '../../../../package.json' with { type: 'json' };
|
||||
} from '../types.js';
|
||||
import { CredentialStore } from '../services/credential-store.js';
|
||||
import { SupabaseAuthClient } from '../../integration/clients/supabase-client.js';
|
||||
import { getAuthConfig } from '../config.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
import packageJson from '../../../../../../package.json' with { type: 'json' };
|
||||
|
||||
export class OAuthService {
|
||||
private logger = getLogger('OAuthService');
|
||||
@@ -4,9 +4,12 @@
|
||||
*/
|
||||
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { Database } from '../types/database.types.js';
|
||||
import { TaskMasterError, ERROR_CODES } from '../errors/task-master-error.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import { Database } from '../../../common/types/database.types.js';
|
||||
import {
|
||||
TaskMasterError,
|
||||
ERROR_CODES
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
/**
|
||||
* Organization data structure
|
||||
@@ -6,10 +6,10 @@
|
||||
* auth.json credential storage, maintaining backward compatibility
|
||||
*/
|
||||
|
||||
import { SupportedStorage } from '@supabase/supabase-js';
|
||||
import type { SupportedStorage } from '@supabase/supabase-js';
|
||||
import { CredentialStore } from './credential-store.js';
|
||||
import { AuthCredentials } from './types.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import type { AuthCredentials } from '../types.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
const STORAGE_KEY = 'sb-taskmaster-auth-token';
|
||||
|
||||
@@ -29,20 +29,14 @@ export class SupabaseSessionStorage implements SupportedStorage {
|
||||
const session = {
|
||||
access_token: credentials.token,
|
||||
refresh_token: credentials.refreshToken || '',
|
||||
expires_at: credentials.expiresAt
|
||||
? Math.floor(new Date(credentials.expiresAt).getTime() / 1000)
|
||||
: Math.floor(Date.now() / 1000) + 3600, // Default to 1 hour
|
||||
// Don't default to arbitrary values - let Supabase handle refresh
|
||||
...(credentials.expiresAt && {
|
||||
expires_at: Math.floor(new Date(credentials.expiresAt).getTime() / 1000)
|
||||
}),
|
||||
token_type: 'bearer',
|
||||
user: {
|
||||
id: credentials.userId,
|
||||
email: credentials.email || '',
|
||||
aud: 'authenticated',
|
||||
role: 'authenticated',
|
||||
email_confirmed_at: new Date().toISOString(),
|
||||
app_metadata: {},
|
||||
user_metadata: {},
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
email: credentials.email || ''
|
||||
}
|
||||
};
|
||||
return session;
|
||||
@@ -55,11 +49,14 @@ export class SupabaseSessionStorage implements SupportedStorage {
|
||||
sessionData: any
|
||||
): Partial<AuthCredentials> {
|
||||
try {
|
||||
const session = JSON.parse(sessionData);
|
||||
// Handle both string and object formats (Supabase may pass either)
|
||||
const session =
|
||||
typeof sessionData === 'string' ? JSON.parse(sessionData) : sessionData;
|
||||
|
||||
return {
|
||||
token: session.access_token,
|
||||
refreshToken: session.refresh_token,
|
||||
userId: session.user?.id || 'unknown',
|
||||
userId: session.user?.id,
|
||||
email: session.user?.email,
|
||||
expiresAt: session.expires_at
|
||||
? new Date(session.expires_at * 1000).toISOString()
|
||||
@@ -78,21 +75,29 @@ export class SupabaseSessionStorage implements SupportedStorage {
|
||||
// Supabase uses a specific key pattern for sessions
|
||||
if (key === STORAGE_KEY || key.includes('auth-token')) {
|
||||
try {
|
||||
const credentials = this.store.getCredentials({ allowExpired: true });
|
||||
if (credentials && credentials.token) {
|
||||
// Build and return a session object from our stored credentials
|
||||
const session = this.buildSessionFromCredentials(credentials);
|
||||
return JSON.stringify(session);
|
||||
// Get credentials and let Supabase handle expiry/refresh internally
|
||||
const credentials = this.store.getCredentials();
|
||||
|
||||
// Only return a session if we have BOTH access token AND refresh token
|
||||
// Supabase will handle refresh if session is expired
|
||||
if (!credentials?.token || !credentials?.refreshToken) {
|
||||
this.logger.debug('No valid credentials found');
|
||||
return null;
|
||||
}
|
||||
|
||||
const session = this.buildSessionFromCredentials(credentials);
|
||||
return JSON.stringify(session);
|
||||
} catch (error) {
|
||||
this.logger.error('Error getting session:', error);
|
||||
}
|
||||
}
|
||||
// Return null if no valid session exists - Supabase expects this
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set item in storage - Supabase will store the session with a specific key
|
||||
* CRITICAL: This is called during refresh token rotation - must be atomic
|
||||
*/
|
||||
setItem(key: string, value: string): void {
|
||||
// Only handle Supabase session keys
|
||||
@@ -102,21 +107,64 @@ export class SupabaseSessionStorage implements SupportedStorage {
|
||||
|
||||
// Parse the session and update our credentials
|
||||
const sessionUpdates = this.parseSessionToCredentials(value);
|
||||
const existingCredentials = this.store.getCredentials();
|
||||
const existingCredentials = this.store.getCredentials({
|
||||
allowExpired: true
|
||||
});
|
||||
|
||||
if (sessionUpdates.token) {
|
||||
const updatedCredentials: AuthCredentials = {
|
||||
...existingCredentials,
|
||||
...sessionUpdates,
|
||||
savedAt: new Date().toISOString(),
|
||||
selectedContext: existingCredentials?.selectedContext
|
||||
} as AuthCredentials;
|
||||
// CRITICAL: Only save if we have both tokens - prevents partial session states
|
||||
// Refresh token rotation means we MUST persist the new refresh token immediately
|
||||
if (!sessionUpdates.token || !sessionUpdates.refreshToken) {
|
||||
this.logger.warn(
|
||||
'Received incomplete session update - skipping save to prevent token rotation issues',
|
||||
{
|
||||
hasToken: !!sessionUpdates.token,
|
||||
hasRefreshToken: !!sessionUpdates.refreshToken
|
||||
}
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.store.saveCredentials(updatedCredentials);
|
||||
this.logger.info(
|
||||
'Successfully saved refreshed credentials from Supabase'
|
||||
// Log the refresh token rotation for debugging
|
||||
const isRotation =
|
||||
existingCredentials?.refreshToken !== sessionUpdates.refreshToken;
|
||||
if (isRotation) {
|
||||
this.logger.debug(
|
||||
'Refresh token rotated - storing new refresh token atomically'
|
||||
);
|
||||
}
|
||||
|
||||
// Build updated credentials - ATOMIC update of both tokens
|
||||
const userId = sessionUpdates.userId ?? existingCredentials?.userId;
|
||||
|
||||
// Runtime assertion: userId is required for AuthCredentials
|
||||
if (!userId) {
|
||||
this.logger.error(
|
||||
'Cannot save credentials: userId is missing from both session update and existing credentials'
|
||||
);
|
||||
throw new Error('Invalid session state: userId is required');
|
||||
}
|
||||
|
||||
const updatedCredentials: AuthCredentials = {
|
||||
...(existingCredentials ?? {}),
|
||||
token: sessionUpdates.token,
|
||||
refreshToken: sessionUpdates.refreshToken,
|
||||
expiresAt: sessionUpdates.expiresAt,
|
||||
userId,
|
||||
email: sessionUpdates.email ?? existingCredentials?.email,
|
||||
savedAt: new Date().toISOString(),
|
||||
selectedContext: existingCredentials?.selectedContext
|
||||
} as AuthCredentials;
|
||||
|
||||
// Save synchronously to ensure atomicity during refresh
|
||||
this.store.saveCredentials(updatedCredentials);
|
||||
|
||||
this.logger.info(
|
||||
'Successfully saved refreshed credentials from Supabase',
|
||||
{
|
||||
tokenRotated: isRotation,
|
||||
expiresAt: updatedCredentials.expiresAt
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
this.logger.error('Error setting session:', error);
|
||||
}
|
||||
8
packages/tm-core/src/modules/commands/index.ts
Normal file
8
packages/tm-core/src/modules/commands/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Commands domain - Placeholder for future migration
|
||||
* This module will handle command execution and orchestration
|
||||
* when migrated from scripts/modules/commands.js
|
||||
*/
|
||||
|
||||
// TODO: Migrate commands.js from scripts/modules/
|
||||
// export * from './handlers/command-handler.js';
|
||||
116
packages/tm-core/src/modules/config/config-domain.ts
Normal file
116
packages/tm-core/src/modules/config/config-domain.ts
Normal file
@@ -0,0 +1,116 @@
|
||||
/**
|
||||
* @fileoverview Config Domain Facade
|
||||
* Public API for configuration management
|
||||
*/
|
||||
|
||||
import type { ConfigManager } from './managers/config-manager.js';
|
||||
import type {
|
||||
PartialConfiguration,
|
||||
RuntimeStorageConfig
|
||||
} from '../../common/interfaces/configuration.interface.js';
|
||||
|
||||
/**
|
||||
* Config Domain - Unified API for configuration operations
|
||||
*/
|
||||
export class ConfigDomain {
|
||||
constructor(private configManager: ConfigManager) {}
|
||||
|
||||
// ========== Configuration Access ==========
|
||||
|
||||
/**
|
||||
* Get the full configuration
|
||||
*/
|
||||
getConfig(): PartialConfiguration {
|
||||
return this.configManager.getConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get storage configuration
|
||||
*/
|
||||
getStorageConfig(): RuntimeStorageConfig {
|
||||
return this.configManager.getStorageConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get model configuration
|
||||
*/
|
||||
getModelConfig() {
|
||||
return this.configManager.getModelConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get response language
|
||||
*/
|
||||
getResponseLanguage(): string {
|
||||
return this.configManager.getResponseLanguage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get project root path
|
||||
*/
|
||||
getProjectRoot(): string {
|
||||
return this.configManager.getProjectRoot();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if API is explicitly configured
|
||||
*/
|
||||
isApiExplicitlyConfigured(): boolean {
|
||||
return this.configManager.isApiExplicitlyConfigured();
|
||||
}
|
||||
|
||||
// ========== Runtime State ==========
|
||||
|
||||
/**
|
||||
* Get the currently active tag
|
||||
*/
|
||||
getActiveTag(): string {
|
||||
return this.configManager.getActiveTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the active tag
|
||||
*/
|
||||
async setActiveTag(tag: string): Promise<void> {
|
||||
return this.configManager.setActiveTag(tag);
|
||||
}
|
||||
|
||||
// ========== Configuration Updates ==========
|
||||
|
||||
/**
|
||||
* Update configuration
|
||||
*/
|
||||
async updateConfig(updates: PartialConfiguration): Promise<void> {
|
||||
return this.configManager.updateConfig(updates);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set response language
|
||||
*/
|
||||
async setResponseLanguage(language: string): Promise<void> {
|
||||
return this.configManager.setResponseLanguage(language);
|
||||
}
|
||||
|
||||
/**
|
||||
* Save current configuration
|
||||
*/
|
||||
async saveConfig(): Promise<void> {
|
||||
return this.configManager.saveConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset configuration to defaults
|
||||
*/
|
||||
async reset(): Promise<void> {
|
||||
return this.configManager.reset();
|
||||
}
|
||||
|
||||
// ========== Utilities ==========
|
||||
|
||||
/**
|
||||
* Get configuration sources for debugging
|
||||
*/
|
||||
getConfigSources() {
|
||||
return this.configManager.getConfigSources();
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
*/
|
||||
|
||||
// Export the main ConfigManager
|
||||
export { ConfigManager } from './config-manager.js';
|
||||
export { ConfigManager } from './managers/config-manager.js';
|
||||
|
||||
// Export all configuration services for advanced usage
|
||||
export {
|
||||
@@ -38,7 +38,7 @@ export type {
|
||||
ConfigProperty,
|
||||
IConfigurationFactory,
|
||||
IConfigurationManager
|
||||
} from '../interfaces/configuration.interface.js';
|
||||
} from '../../common/interfaces/configuration.interface.js';
|
||||
|
||||
// Re-export default values
|
||||
export { DEFAULT_CONFIG_VALUES } from '../interfaces/configuration.interface.js';
|
||||
export { DEFAULT_CONFIG_VALUES } from '../../common/interfaces/configuration.interface.js';
|
||||
@@ -5,19 +5,19 @@
|
||||
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import { ConfigManager } from './config-manager.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../interfaces/configuration.interface.js';
|
||||
import { ConfigLoader } from './services/config-loader.service.js';
|
||||
import { ConfigMerger } from './services/config-merger.service.js';
|
||||
import { RuntimeStateManager } from './services/runtime-state-manager.service.js';
|
||||
import { ConfigPersistence } from './services/config-persistence.service.js';
|
||||
import { EnvironmentConfigProvider } from './services/environment-config-provider.service.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';
|
||||
import { ConfigLoader } from '../services/config-loader.service.js';
|
||||
import { ConfigMerger } from '../services/config-merger.service.js';
|
||||
import { RuntimeStateManager } from '../services/runtime-state-manager.service.js';
|
||||
import { ConfigPersistence } from '../services/config-persistence.service.js';
|
||||
import { EnvironmentConfigProvider } from '../services/environment-config-provider.service.js';
|
||||
|
||||
// Mock all services
|
||||
vi.mock('./services/config-loader.service.js');
|
||||
vi.mock('./services/config-merger.service.js');
|
||||
vi.mock('./services/runtime-state-manager.service.js');
|
||||
vi.mock('./services/config-persistence.service.js');
|
||||
vi.mock('./services/environment-config-provider.service.js');
|
||||
vi.mock('../services/config-loader.service.js');
|
||||
vi.mock('../services/config-merger.service.js');
|
||||
vi.mock('../services/runtime-state-manager.service.js');
|
||||
vi.mock('../services/config-persistence.service.js');
|
||||
vi.mock('../services/environment-config-provider.service.js');
|
||||
|
||||
describe('ConfigManager', () => {
|
||||
let manager: ConfigManager;
|
||||
@@ -361,23 +361,6 @@ describe('ConfigManager', () => {
|
||||
|
||||
expect(sources).toEqual(mockSources);
|
||||
});
|
||||
|
||||
it('should return no-op function for watch (not implemented)', () => {
|
||||
const warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
||||
const callback = vi.fn();
|
||||
|
||||
const unsubscribe = manager.watch(callback);
|
||||
|
||||
expect(warnSpy).toHaveBeenCalledWith(
|
||||
'Configuration watching not yet implemented'
|
||||
);
|
||||
expect(unsubscribe).toBeInstanceOf(Function);
|
||||
|
||||
// Calling unsubscribe should not throw
|
||||
expect(() => unsubscribe()).not.toThrow();
|
||||
|
||||
warnSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
@@ -9,16 +9,16 @@
|
||||
import type {
|
||||
PartialConfiguration,
|
||||
RuntimeStorageConfig
|
||||
} from '../interfaces/configuration.interface.js';
|
||||
import { DEFAULT_CONFIG_VALUES as DEFAULTS } from '../interfaces/configuration.interface.js';
|
||||
import { ConfigLoader } from './services/config-loader.service.js';
|
||||
} from '../../../common/interfaces/configuration.interface.js';
|
||||
import { DEFAULT_CONFIG_VALUES as DEFAULTS } from '../../../common/interfaces/configuration.interface.js';
|
||||
import { ConfigLoader } from '../services/config-loader.service.js';
|
||||
import {
|
||||
ConfigMerger,
|
||||
CONFIG_PRECEDENCE
|
||||
} from './services/config-merger.service.js';
|
||||
import { RuntimeStateManager } from './services/runtime-state-manager.service.js';
|
||||
import { ConfigPersistence } from './services/config-persistence.service.js';
|
||||
import { EnvironmentConfigProvider } from './services/environment-config-provider.service.js';
|
||||
} from '../services/config-merger.service.js';
|
||||
import { RuntimeStateManager } from '../services/runtime-state-manager.service.js';
|
||||
import { ConfigPersistence } from '../services/config-persistence.service.js';
|
||||
import { EnvironmentConfigProvider } from '../services/environment-config-provider.service.js';
|
||||
|
||||
/**
|
||||
* ConfigManager orchestrates all configuration services
|
||||
@@ -5,7 +5,7 @@
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import { ConfigLoader } from './config-loader.service.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';
|
||||
|
||||
vi.mock('node:fs', () => ({
|
||||
promises: {
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
import { promises as fs } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import type { PartialConfiguration } from '../../interfaces/configuration.interface.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.js';
|
||||
import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../errors/task-master-error.js';
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
|
||||
/**
|
||||
* ConfigLoader handles loading configuration from files
|
||||
@@ -3,7 +3,7 @@
|
||||
* Responsible for merging configurations from multiple sources with precedence
|
||||
*/
|
||||
|
||||
import type { PartialConfiguration } from '../../interfaces/configuration.interface.js';
|
||||
import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
|
||||
|
||||
/**
|
||||
* Configuration source with precedence
|
||||
@@ -5,12 +5,12 @@
|
||||
|
||||
import { promises as fs } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import type { PartialConfiguration } from '../../interfaces/configuration.interface.js';
|
||||
import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../errors/task-master-error.js';
|
||||
import { getLogger } from '../../logger/index.js';
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
/**
|
||||
* Persistence options
|
||||
@@ -3,8 +3,8 @@
|
||||
* Extracts configuration from environment variables
|
||||
*/
|
||||
|
||||
import type { PartialConfiguration } from '../../interfaces/configuration.interface.js';
|
||||
import { getLogger } from '../../logger/index.js';
|
||||
import type { PartialConfiguration } from '../../../common/interfaces/configuration.interface.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
/**
|
||||
* Environment variable mapping definition
|
||||
@@ -5,7 +5,7 @@
|
||||
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
||||
import { promises as fs } from 'node:fs';
|
||||
import { RuntimeStateManager } from './runtime-state-manager.service.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';
|
||||
|
||||
vi.mock('node:fs', () => ({
|
||||
promises: {
|
||||
@@ -8,9 +8,9 @@ import path from 'node:path';
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../errors/task-master-error.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../interfaces/configuration.interface.js';
|
||||
import { getLogger } from '../../logger/index.js';
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import { DEFAULT_CONFIG_VALUES } from '../../../common/interfaces/configuration.interface.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
/**
|
||||
* Runtime state data structure
|
||||
8
packages/tm-core/src/modules/dependencies/index.ts
Normal file
8
packages/tm-core/src/modules/dependencies/index.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* @fileoverview Dependencies domain - Placeholder for future migration
|
||||
* This module will handle dependency management, graphs, and validation
|
||||
* when migrated from scripts/modules/dependency-manager.js
|
||||
*/
|
||||
|
||||
// TODO: Migrate dependency-manager.js from scripts/modules/
|
||||
// export * from './services/dependency-manager.js';
|
||||
@@ -2,9 +2,9 @@
|
||||
* Base executor class providing common functionality for all executors
|
||||
*/
|
||||
|
||||
import type { Task } from '../types/index.js';
|
||||
import type { ITaskExecutor, ExecutorType, ExecutionResult } from './types.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import type { Task } from '../../../common/types/index.js';
|
||||
import type { ITaskExecutor, ExecutorType, ExecutionResult } from '../types.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
export abstract class BaseExecutor implements ITaskExecutor {
|
||||
protected readonly logger = getLogger('BaseExecutor');
|
||||
@@ -3,13 +3,13 @@
|
||||
*/
|
||||
|
||||
import { spawn } from 'child_process';
|
||||
import type { Task } from '../types/index.js';
|
||||
import type { Task } from '../../../common/types/index.js';
|
||||
import type {
|
||||
ExecutorType,
|
||||
ExecutionResult,
|
||||
ClaudeExecutorConfig
|
||||
} from './types.js';
|
||||
import { BaseExecutor } from './base-executor.js';
|
||||
} from '../types.js';
|
||||
import { BaseExecutor } from '../executors/base-executor.js';
|
||||
|
||||
export class ClaudeExecutor extends BaseExecutor {
|
||||
private claudeConfig: ClaudeExecutorConfig;
|
||||
@@ -2,9 +2,9 @@
|
||||
* Factory for creating task executors
|
||||
*/
|
||||
|
||||
import type { ITaskExecutor, ExecutorOptions, ExecutorType } from './types.js';
|
||||
import { ClaudeExecutor } from './claude-executor.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import type { ITaskExecutor, ExecutorOptions, ExecutorType } from '../types.js';
|
||||
import { ClaudeExecutor } from '../executors/claude-executor.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
export class ExecutorFactory {
|
||||
private static logger = getLogger('ExecutorFactory');
|
||||
12
packages/tm-core/src/modules/execution/index.ts
Normal file
12
packages/tm-core/src/modules/execution/index.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
/**
|
||||
* Public API for the executors module
|
||||
*/
|
||||
|
||||
export * from './types.js';
|
||||
export { BaseExecutor } from './executors/base-executor.js';
|
||||
export { ClaudeExecutor } from './executors/claude-executor.js';
|
||||
export { ExecutorFactory } from './executors/executor-factory.js';
|
||||
export {
|
||||
ExecutorService,
|
||||
type ExecutorServiceOptions
|
||||
} from './services/executor-service.js';
|
||||
@@ -2,15 +2,15 @@
|
||||
* Service for managing task execution
|
||||
*/
|
||||
|
||||
import type { Task } from '../types/index.js';
|
||||
import type { Task } from '../../../common/types/index.js';
|
||||
import type {
|
||||
ITaskExecutor,
|
||||
ExecutorOptions,
|
||||
ExecutionResult,
|
||||
ExecutorType
|
||||
} from './types.js';
|
||||
import { ExecutorFactory } from './executor-factory.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
} from '../types.js';
|
||||
import { ExecutorFactory } from '../executors/executor-factory.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
export interface ExecutorServiceOptions {
|
||||
projectRoot: string;
|
||||
@@ -2,7 +2,7 @@
|
||||
* Executor types and interfaces for Task Master
|
||||
*/
|
||||
|
||||
import type { Task } from '../types/index.js';
|
||||
import type { Task } from '../../common/types/index.js';
|
||||
|
||||
/**
|
||||
* Supported executor types
|
||||
@@ -5,7 +5,7 @@
|
||||
* @module git-adapter
|
||||
*/
|
||||
|
||||
import { simpleGit, type SimpleGit } from 'simple-git';
|
||||
import { simpleGit, type SimpleGit, type StatusResult } from 'simple-git';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'path';
|
||||
|
||||
@@ -216,14 +216,14 @@ export class GitAdapter {
|
||||
* Gets the detailed status of the working tree.
|
||||
* Returns raw status from simple-git with all file changes.
|
||||
*
|
||||
* @returns {Promise<import('simple-git').StatusResult>} Detailed status object
|
||||
* @returns {Promise<StatusResult>} Detailed status object
|
||||
*
|
||||
* @example
|
||||
* const status = await git.getStatus();
|
||||
* console.log('Modified files:', status.modified);
|
||||
* console.log('Staged files:', status.staged);
|
||||
*/
|
||||
async getStatus(): Promise<import('simple-git').StatusResult> {
|
||||
async getStatus(): Promise<StatusResult> {
|
||||
return await this.git.status();
|
||||
}
|
||||
|
||||
247
packages/tm-core/src/modules/git/git-domain.ts
Normal file
247
packages/tm-core/src/modules/git/git-domain.ts
Normal file
@@ -0,0 +1,247 @@
|
||||
/**
|
||||
* @fileoverview Git Domain Facade
|
||||
* Public API for Git operations
|
||||
*/
|
||||
|
||||
import { GitAdapter } from './adapters/git-adapter.js';
|
||||
import { CommitMessageGenerator } from './services/commit-message-generator.js';
|
||||
import type { CommitMessageOptions } from './services/commit-message-generator.js';
|
||||
import type { StatusResult } from 'simple-git';
|
||||
|
||||
/**
|
||||
* Git Domain - Unified API for Git operations
|
||||
*/
|
||||
export class GitDomain {
|
||||
private gitAdapter: GitAdapter;
|
||||
private commitGenerator: CommitMessageGenerator;
|
||||
|
||||
constructor(projectPath: string) {
|
||||
this.gitAdapter = new GitAdapter(projectPath);
|
||||
this.commitGenerator = new CommitMessageGenerator();
|
||||
}
|
||||
|
||||
// ========== Repository Validation ==========
|
||||
|
||||
/**
|
||||
* Check if directory is a git repository
|
||||
*/
|
||||
async isGitRepository(): Promise<boolean> {
|
||||
return this.gitAdapter.isGitRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure we're in a valid git repository
|
||||
*/
|
||||
async ensureGitRepository(): Promise<void> {
|
||||
return this.gitAdapter.ensureGitRepository();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get repository root path
|
||||
*/
|
||||
async getRepositoryRoot(): Promise<string> {
|
||||
return this.gitAdapter.getRepositoryRoot();
|
||||
}
|
||||
|
||||
// ========== Working Tree Status ==========
|
||||
|
||||
/**
|
||||
* Check if working tree is clean
|
||||
*/
|
||||
async isWorkingTreeClean(): Promise<boolean> {
|
||||
return this.gitAdapter.isWorkingTreeClean();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get git status
|
||||
*/
|
||||
async getStatus(): Promise<StatusResult> {
|
||||
return this.gitAdapter.getStatus();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get status summary
|
||||
*/
|
||||
async getStatusSummary(): Promise<{
|
||||
isClean: boolean;
|
||||
staged: number;
|
||||
modified: number;
|
||||
deleted: number;
|
||||
untracked: number;
|
||||
totalChanges: number;
|
||||
}> {
|
||||
return this.gitAdapter.getStatusSummary();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are uncommitted changes
|
||||
*/
|
||||
async hasUncommittedChanges(): Promise<boolean> {
|
||||
return this.gitAdapter.hasUncommittedChanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are staged changes
|
||||
*/
|
||||
async hasStagedChanges(): Promise<boolean> {
|
||||
return this.gitAdapter.hasStagedChanges();
|
||||
}
|
||||
|
||||
// ========== Branch Operations ==========
|
||||
|
||||
/**
|
||||
* Get current branch name
|
||||
*/
|
||||
async getCurrentBranch(): Promise<string> {
|
||||
return this.gitAdapter.getCurrentBranch();
|
||||
}
|
||||
|
||||
/**
|
||||
* List all local branches
|
||||
*/
|
||||
async listBranches(): Promise<string[]> {
|
||||
return this.gitAdapter.listBranches();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a branch exists
|
||||
*/
|
||||
async branchExists(branchName: string): Promise<boolean> {
|
||||
return this.gitAdapter.branchExists(branchName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new branch
|
||||
*/
|
||||
async createBranch(
|
||||
branchName: string,
|
||||
options?: { checkout?: boolean }
|
||||
): Promise<void> {
|
||||
return this.gitAdapter.createBranch(branchName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checkout an existing branch
|
||||
*/
|
||||
async checkoutBranch(
|
||||
branchName: string,
|
||||
options?: { force?: boolean }
|
||||
): Promise<void> {
|
||||
return this.gitAdapter.checkoutBranch(branchName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and checkout a new branch
|
||||
*/
|
||||
async createAndCheckoutBranch(branchName: string): Promise<void> {
|
||||
return this.gitAdapter.createAndCheckoutBranch(branchName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a branch
|
||||
*/
|
||||
async deleteBranch(
|
||||
branchName: string,
|
||||
options?: { force?: boolean }
|
||||
): Promise<void> {
|
||||
return this.gitAdapter.deleteBranch(branchName, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get default branch name
|
||||
*/
|
||||
async getDefaultBranch(): Promise<string> {
|
||||
return this.gitAdapter.getDefaultBranch();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if on default branch
|
||||
*/
|
||||
async isOnDefaultBranch(): Promise<boolean> {
|
||||
return this.gitAdapter.isOnDefaultBranch();
|
||||
}
|
||||
|
||||
// ========== Commit Operations ==========
|
||||
|
||||
/**
|
||||
* Stage files for commit
|
||||
*/
|
||||
async stageFiles(files: string[]): Promise<void> {
|
||||
return this.gitAdapter.stageFiles(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unstage files
|
||||
*/
|
||||
async unstageFiles(files: string[]): Promise<void> {
|
||||
return this.gitAdapter.unstageFiles(files);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a commit
|
||||
*/
|
||||
async createCommit(
|
||||
message: string,
|
||||
options?: {
|
||||
metadata?: Record<string, string>;
|
||||
allowEmpty?: boolean;
|
||||
enforceNonDefaultBranch?: boolean;
|
||||
force?: boolean;
|
||||
}
|
||||
): Promise<void> {
|
||||
return this.gitAdapter.createCommit(message, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get commit log
|
||||
*/
|
||||
async getCommitLog(options?: { maxCount?: number }): Promise<any[]> {
|
||||
return this.gitAdapter.getCommitLog(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get last commit
|
||||
*/
|
||||
async getLastCommit(): Promise<any> {
|
||||
return this.gitAdapter.getLastCommit();
|
||||
}
|
||||
|
||||
// ========== Remote Operations ==========
|
||||
|
||||
/**
|
||||
* Check if repository has remotes
|
||||
*/
|
||||
async hasRemote(): Promise<boolean> {
|
||||
return this.gitAdapter.hasRemote();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all configured remotes
|
||||
*/
|
||||
async getRemotes(): Promise<any[]> {
|
||||
return this.gitAdapter.getRemotes();
|
||||
}
|
||||
|
||||
// ========== Commit Message Generation ==========
|
||||
|
||||
/**
|
||||
* Generate a conventional commit message
|
||||
*/
|
||||
generateCommitMessage(options: CommitMessageOptions): string {
|
||||
return this.commitGenerator.generateMessage(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a conventional commit message
|
||||
*/
|
||||
validateCommitMessage(message: string) {
|
||||
return this.commitGenerator.validateConventionalCommit(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a commit message
|
||||
*/
|
||||
parseCommitMessage(message: string) {
|
||||
return this.commitGenerator.parseCommitMessage(message);
|
||||
}
|
||||
}
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
// Export GitAdapter
|
||||
export { GitAdapter } from './git-adapter.js';
|
||||
export { GitAdapter } from './adapters/git-adapter.js';
|
||||
|
||||
// Export branch name utilities
|
||||
export {
|
||||
generateBranchName,
|
||||
sanitizeBranchName
|
||||
} from './branch-name-generator.js';
|
||||
} from './services/branch-name-generator.js';
|
||||
@@ -8,10 +8,10 @@ import {
|
||||
User,
|
||||
Session
|
||||
} from '@supabase/supabase-js';
|
||||
import { AuthenticationError } from '../auth/types.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import { SupabaseSessionStorage } from '../auth/supabase-session-storage.js';
|
||||
import { CredentialStore } from '../auth/credential-store.js';
|
||||
import { AuthenticationError } from '../../auth/types.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
import { SupabaseSessionStorage } from '../../auth/services/supabase-session-storage.js';
|
||||
import { CredentialStore } from '../../auth/services/credential-store.js';
|
||||
|
||||
export class SupabaseAuthClient {
|
||||
private client: SupabaseJSClient | null = null;
|
||||
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* @fileoverview Integration Domain Facade
|
||||
* Public API for integration with external systems
|
||||
*/
|
||||
|
||||
import type { ConfigManager } from '../config/managers/config-manager.js';
|
||||
import { AuthManager } from '../auth/managers/auth-manager.js';
|
||||
import { ExportService } from './services/export.service.js';
|
||||
import type {
|
||||
ExportTasksOptions,
|
||||
ExportResult
|
||||
} from './services/export.service.js';
|
||||
|
||||
/**
|
||||
* Integration Domain - Unified API for external system integration
|
||||
*/
|
||||
export class IntegrationDomain {
|
||||
private exportService: ExportService;
|
||||
|
||||
constructor(configManager: ConfigManager) {
|
||||
// Get singleton AuthManager instance
|
||||
const authManager = AuthManager.getInstance();
|
||||
this.exportService = new ExportService(configManager, authManager);
|
||||
}
|
||||
|
||||
// ========== Export Operations ==========
|
||||
|
||||
/**
|
||||
* Export tasks to external systems (e.g., Hamster briefs)
|
||||
*/
|
||||
async exportTasks(options: ExportTasksOptions): Promise<ExportResult> {
|
||||
return this.exportService.exportTasks(options);
|
||||
}
|
||||
}
|
||||
@@ -3,12 +3,15 @@
|
||||
* Core service for exporting tasks to external systems (e.g., Hamster briefs)
|
||||
*/
|
||||
|
||||
import type { Task, TaskStatus } from '../types/index.js';
|
||||
import type { UserContext } from '../auth/types.js';
|
||||
import { ConfigManager } from '../config/config-manager.js';
|
||||
import { AuthManager } from '../auth/auth-manager.js';
|
||||
import { ERROR_CODES, TaskMasterError } from '../errors/task-master-error.js';
|
||||
import { FileStorage } from '../storage/file-storage/index.js';
|
||||
import type { Task, TaskStatus } from '../../../common/types/index.js';
|
||||
import type { UserContext } from '../../auth/types.js';
|
||||
import { ConfigManager } from '../../config/managers/config-manager.js';
|
||||
import { AuthManager } from '../../auth/managers/auth-manager.js';
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import { FileStorage } from '../../storage/adapters/file-storage/index.js';
|
||||
|
||||
// Type definitions for the bulk API response
|
||||
interface TaskImportResult {
|
||||
@@ -2,7 +2,7 @@
|
||||
* @fileoverview Reports module exports
|
||||
*/
|
||||
|
||||
export { ComplexityReportManager } from './complexity-report-manager.js';
|
||||
export { ComplexityReportManager } from './managers/complexity-report-manager.js';
|
||||
export type {
|
||||
ComplexityReport,
|
||||
ComplexityReportMetadata,
|
||||
@@ -9,8 +9,8 @@ import type {
|
||||
ComplexityReport,
|
||||
ComplexityAnalysis,
|
||||
TaskComplexityData
|
||||
} from './types.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
} from '../types.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
|
||||
const logger = getLogger('ComplexityReportManager');
|
||||
|
||||
@@ -8,18 +8,21 @@ import type {
|
||||
StorageStats,
|
||||
UpdateStatusResult,
|
||||
LoadTasksOptions
|
||||
} from '../interfaces/storage.interface.js';
|
||||
} from '../../../common/interfaces/storage.interface.js';
|
||||
import type {
|
||||
Task,
|
||||
TaskMetadata,
|
||||
TaskTag,
|
||||
TaskStatus
|
||||
} from '../types/index.js';
|
||||
import { ERROR_CODES, TaskMasterError } from '../errors/task-master-error.js';
|
||||
import { TaskRepository } from '../repositories/task-repository.interface.js';
|
||||
import { SupabaseTaskRepository } from '../repositories/supabase/index.js';
|
||||
} from '../../../common/types/index.js';
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import { TaskRepository } from '../../tasks/repositories/task-repository.interface.js';
|
||||
import { SupabaseTaskRepository } from '../../tasks/repositories/supabase/index.js';
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { AuthManager } from '../auth/auth-manager.js';
|
||||
import { AuthManager } from '../../auth/managers/auth-manager.js';
|
||||
|
||||
/**
|
||||
* API storage configuration
|
||||
@@ -122,7 +125,7 @@ export class ApiStorage implements IStorage {
|
||||
/**
|
||||
* Get the storage type
|
||||
*/
|
||||
getType(): 'api' {
|
||||
getStorageType(): 'api' {
|
||||
return 'api';
|
||||
}
|
||||
|
||||
@@ -2,17 +2,21 @@
|
||||
* @fileoverview Refactored file-based storage implementation for Task Master
|
||||
*/
|
||||
|
||||
import type { Task, TaskMetadata, TaskStatus } from '../../types/index.js';
|
||||
import type {
|
||||
Task,
|
||||
TaskMetadata,
|
||||
TaskStatus
|
||||
} from '../../../../common/types/index.js';
|
||||
import type {
|
||||
IStorage,
|
||||
StorageStats,
|
||||
UpdateStatusResult,
|
||||
LoadTasksOptions
|
||||
} from '../../interfaces/storage.interface.js';
|
||||
} from '../../../../common/interfaces/storage.interface.js';
|
||||
import { FormatHandler } from './format-handler.js';
|
||||
import { FileOperations } from './file-operations.js';
|
||||
import { PathResolver } from './path-resolver.js';
|
||||
import { ComplexityReportManager } from '../../reports/complexity-report-manager.js';
|
||||
import { ComplexityReportManager } from '../../../reports/managers/complexity-report-manager.js';
|
||||
|
||||
/**
|
||||
* File-based storage implementation using a single tasks.json file with separated concerns
|
||||
@@ -47,7 +51,7 @@ export class FileStorage implements IStorage {
|
||||
/**
|
||||
* Get the storage type
|
||||
*/
|
||||
getType(): 'file' {
|
||||
getStorageType(): 'file' {
|
||||
return 'file';
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* @fileoverview Format handler for task storage files
|
||||
*/
|
||||
|
||||
import type { Task, TaskMetadata } from '../../types/index.js';
|
||||
import type { Task, TaskMetadata } from '../../../../common/types/index.js';
|
||||
|
||||
export interface FileStorageData {
|
||||
tasks: Task[];
|
||||
@@ -4,9 +4,9 @@
|
||||
*/
|
||||
|
||||
// Export storage implementations
|
||||
export { FileStorage } from './file-storage/index.js';
|
||||
export { ApiStorage, type ApiStorageConfig } from './api-storage.js';
|
||||
export { StorageFactory } from './storage-factory.js';
|
||||
export { FileStorage } from './adapters/file-storage/index.js';
|
||||
export { ApiStorage, type ApiStorageConfig } from './adapters/api-storage.js';
|
||||
export { StorageFactory } from './services/storage-factory.js';
|
||||
|
||||
// Export activity logger
|
||||
export {
|
||||
@@ -15,13 +15,13 @@ export {
|
||||
filterActivityLog,
|
||||
type ActivityEvent,
|
||||
type ActivityFilter
|
||||
} from './activity-logger.js';
|
||||
} from './adapters/activity-logger.js';
|
||||
|
||||
// Export storage interface and types
|
||||
export type {
|
||||
IStorage,
|
||||
StorageStats
|
||||
} from '../interfaces/storage.interface.js';
|
||||
} from '../../common/interfaces/storage.interface.js';
|
||||
|
||||
// Placeholder exports - these will be implemented in later tasks
|
||||
export interface StorageAdapter {
|
||||
@@ -2,18 +2,21 @@
|
||||
* @fileoverview Storage factory for creating appropriate storage implementations
|
||||
*/
|
||||
|
||||
import type { IStorage } from '../interfaces/storage.interface.js';
|
||||
import type { IStorage } from '../../../common/interfaces/storage.interface.js';
|
||||
import type {
|
||||
IConfiguration,
|
||||
RuntimeStorageConfig,
|
||||
StorageSettings
|
||||
} from '../interfaces/configuration.interface.js';
|
||||
import { FileStorage } from './file-storage/index.js';
|
||||
import { ApiStorage } from './api-storage.js';
|
||||
import { ERROR_CODES, TaskMasterError } from '../errors/task-master-error.js';
|
||||
import { AuthManager } from '../auth/auth-manager.js';
|
||||
import { getLogger } from '../logger/index.js';
|
||||
import { SupabaseAuthClient } from '../clients/supabase-client.js';
|
||||
} from '../../../common/interfaces/configuration.interface.js';
|
||||
import { FileStorage } from '../adapters/file-storage/index.js';
|
||||
import { ApiStorage } from '../adapters/api-storage.js';
|
||||
import {
|
||||
ERROR_CODES,
|
||||
TaskMasterError
|
||||
} from '../../../common/errors/task-master-error.js';
|
||||
import { AuthManager } from '../../auth/managers/auth-manager.js';
|
||||
import { getLogger } from '../../../common/logger/index.js';
|
||||
import { SupabaseAuthClient } from '../../integration/clients/supabase-client.js';
|
||||
|
||||
/**
|
||||
* Factory for creating storage implementations based on configuration
|
||||
@@ -2,13 +2,13 @@
|
||||
* @fileoverview Task entity with business rules and domain logic
|
||||
*/
|
||||
|
||||
import { ERROR_CODES, TaskMasterError } from '../errors/task-master-error.js';
|
||||
import { ERROR_CODES, TaskMasterError } from '../../../common/errors/task-master-error.js';
|
||||
import type {
|
||||
Subtask,
|
||||
Task,
|
||||
TaskPriority,
|
||||
TaskStatus
|
||||
} from '../types/index.js';
|
||||
} from '../../../common/types/index.js';
|
||||
|
||||
/**
|
||||
* Task entity representing a task with business logic
|
||||
@@ -3,7 +3,7 @@
|
||||
* This file exports all parsing-related classes and functions
|
||||
*/
|
||||
|
||||
import type { PlaceholderTask } from '../types/index.js';
|
||||
import type { PlaceholderTask } from '../../../common/types/index.js';
|
||||
|
||||
// Parser implementations will be defined here
|
||||
// export * from './prd-parser.js';
|
||||
@@ -1,6 +1,6 @@
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { Database } from '../../types/database.types.js';
|
||||
import { DependencyWithDisplayId } from '../../types/repository-types.js';
|
||||
import { Database } from '../../../../common/types/database.types.js';
|
||||
import { DependencyWithDisplayId } from '../../../../common/types/repository-types.js';
|
||||
|
||||
/**
|
||||
* Handles fetching and processing of task dependencies with display_ids
|
||||
@@ -1,14 +1,14 @@
|
||||
import { SupabaseClient } from '@supabase/supabase-js';
|
||||
import { Task } from '../../types/index.js';
|
||||
import { Database, Json } from '../../types/database.types.js';
|
||||
import { TaskMapper } from '../../mappers/TaskMapper.js';
|
||||
import { AuthManager } from '../../auth/auth-manager.js';
|
||||
import { Task } from '../../../../common/types/index.js';
|
||||
import { Database, Json } from '../../../../common/types/database.types.js';
|
||||
import { TaskMapper } from '../../../../common/mappers/TaskMapper.js';
|
||||
import { AuthManager } from '../../../auth/managers/auth-manager.js';
|
||||
import { DependencyFetcher } from './dependency-fetcher.js';
|
||||
import {
|
||||
TaskWithRelations,
|
||||
TaskDatabaseUpdate
|
||||
} from '../../types/repository-types.js';
|
||||
import { LoadTasksOptions } from '../../interfaces/storage.interface.js';
|
||||
} from '../../../../common/types/repository-types.js';
|
||||
import { LoadTasksOptions } from '../../../../common/interfaces/storage.interface.js';
|
||||
import { z } from 'zod';
|
||||
|
||||
// Zod schema for task status validation
|
||||
@@ -47,8 +47,8 @@ export class SupabaseTaskRepository {
|
||||
* Gets the current brief ID from auth context
|
||||
* @throws {Error} If no brief is selected
|
||||
*/
|
||||
private async getBriefIdOrThrow(): Promise<string> {
|
||||
const context = await this.authManager.getContext();
|
||||
private getBriefIdOrThrow(): string {
|
||||
const context = this.authManager.getContext();
|
||||
if (!context?.briefId) {
|
||||
throw new Error(
|
||||
'No brief selected. Please select a brief first using: tm context brief'
|
||||
@@ -61,7 +61,7 @@ export class SupabaseTaskRepository {
|
||||
_projectId?: string,
|
||||
options?: LoadTasksOptions
|
||||
): Promise<Task[]> {
|
||||
const briefId = await this.getBriefIdOrThrow();
|
||||
const briefId = this.getBriefIdOrThrow();
|
||||
|
||||
// Build query with filters
|
||||
let query = this.supabase
|
||||
@@ -114,7 +114,7 @@ export class SupabaseTaskRepository {
|
||||
}
|
||||
|
||||
async getTask(_projectId: string, taskId: string): Promise<Task | null> {
|
||||
const briefId = await this.getBriefIdOrThrow();
|
||||
const briefId = this.getBriefIdOrThrow();
|
||||
|
||||
const { data, error } = await this.supabase
|
||||
.from('tasks')
|
||||
@@ -157,7 +157,7 @@ export class SupabaseTaskRepository {
|
||||
taskId: string,
|
||||
updates: Partial<Task>
|
||||
): Promise<Task> {
|
||||
const briefId = await this.getBriefIdOrThrow();
|
||||
const briefId = this.getBriefIdOrThrow();
|
||||
|
||||
// Validate updates using Zod schema
|
||||
try {
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Task, TaskTag } from '../types/index.js';
|
||||
import { LoadTasksOptions } from '../interfaces/storage.interface.js';
|
||||
import { Task, TaskTag } from '../../../common/types/index.js';
|
||||
import { LoadTasksOptions } from '../../../common/interfaces/storage.interface.js';
|
||||
|
||||
export interface TaskRepository {
|
||||
// Task operations
|
||||
@@ -6,12 +6,12 @@
|
||||
import { readFileSync, existsSync, readdirSync } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { execSync } from 'child_process';
|
||||
import { getLogger } from '../logger/factory.js';
|
||||
import { getLogger } from '../../../common/logger/factory.js';
|
||||
import {
|
||||
isGitRepository,
|
||||
isGhCliAvailable,
|
||||
getDefaultBranch
|
||||
} from '../utils/git-utils.js';
|
||||
} from '../../../common/utils/git-utils.js';
|
||||
|
||||
const logger = getLogger('PreflightChecker');
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Extracted from CLI start command to be reusable across CLI and extension
|
||||
*/
|
||||
|
||||
import type { Task } from '../types/index.js';
|
||||
import type { Task } from '../../../common/types/index.js';
|
||||
import type { TaskService } from './task-service.js';
|
||||
|
||||
export interface StartTaskOptions {
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user