chore: apply requested changes

This commit is contained in:
Ralph Khreish
2025-11-07 21:22:25 +01:00
parent 04783a7f1e
commit 4583ea2ed7
5 changed files with 45 additions and 97 deletions

View File

@@ -7,7 +7,8 @@ import {
OAuthFlowOptions,
AuthenticationError,
AuthConfig,
UserContext
UserContext,
UserContextWithBrief
} from '../types.js';
import { ContextStore } from '../services/context-store.js';
import { OAuthService } from '../services/oauth-service.js';
@@ -18,6 +19,10 @@ import {
type Brief,
type RemoteTask
} from '../services/organization.service.js';
import {
ERROR_CODES,
TaskMasterError
} from '../../../common/errors/task-master-error.js';
import { getLogger } from '../../../common/logger/index.js';
import fs from 'fs';
import path from 'path';
@@ -430,4 +435,28 @@ export class AuthManager {
const service = await this.getOrganizationService();
return service.getTasks(briefId);
}
/**
* Ensure a brief is selected in the current context
* Throws a TaskMasterError if no brief is selected
* @param operation - The operation name for error context
* @returns The current user context with a guaranteed briefId
*/
ensureBriefSelected(operation: string): UserContextWithBrief {
const context = this.getContext();
if (!context?.briefId) {
throw new TaskMasterError(
'No brief selected',
ERROR_CODES.NO_BRIEF_SELECTED,
{
operation,
userMessage:
'No brief selected. Please select a brief first using: tm context brief <brief-id> or tm context brief <brief-url>'
}
);
}
return context as UserContextWithBrief;
}
}

View File

@@ -22,6 +22,11 @@ export interface UserContext {
updatedAt: string;
}
/**
* User context with a guaranteed briefId
*/
export type UserContextWithBrief = UserContext & { briefId: string };
export interface OAuthFlowOptions {
/** Callback to open the browser with the auth URL. If not provided, browser won't be opened */
openBrowser?: (url: string) => Promise<void>;

View File

@@ -53,13 +53,6 @@ export interface ExpandTaskOptions {
force?: boolean;
}
/**
* Auth context with a guaranteed briefId
*/
type ContextWithBrief = NonNullable<
ReturnType<typeof AuthManager.prototype.getContext>
> & { briefId: string };
/**
* TaskExpansionService handles AI-powered task expansion
*/
@@ -93,7 +86,7 @@ export class TaskExpansionService {
): Promise<ExpandTaskResult> {
try {
// Get brief context from AuthManager
const context = this.ensureBriefSelected('expandTask');
const context = this.authManager.ensureBriefSelected('expandTask');
// Get the task being expanded to extract existing subtasks
const task = await this.repository.getTask(this.projectId, taskId);
@@ -101,7 +94,7 @@ export class TaskExpansionService {
if (!task) {
throw new TaskMasterError(
`Task ${taskId} not found`,
ERROR_CODES.VALIDATION_ERROR,
ERROR_CODES.TASK_NOT_FOUND,
{
operation: 'expandTask',
taskId,
@@ -230,26 +223,4 @@ export class TaskExpansionService {
);
}
}
/**
* Ensure a brief is selected in the current context
* @returns The current auth context with a valid briefId
*/
private ensureBriefSelected(operation: string): ContextWithBrief {
const context = this.authManager.getContext();
if (!context?.briefId) {
throw new TaskMasterError(
'No brief selected',
ERROR_CODES.NO_BRIEF_SELECTED,
{
operation,
userMessage:
'No brief selected. Please select a brief first using: tm context brief <brief-id> or tm context brief <brief-url>'
}
);
}
return context as ContextWithBrief;
}
}

View File

@@ -28,13 +28,6 @@ interface GetTaskResponse {
};
}
/**
* Auth context with a guaranteed briefId
*/
type ContextWithBrief = NonNullable<
ReturnType<typeof AuthManager.prototype.getContext>
> & { briefId: string };
/**
* TaskRetrievalService handles fetching tasks with enriched document content
* Uses repository for task structure and API endpoint for document content
@@ -65,14 +58,14 @@ export class TaskRetrievalService {
*/
async getTask(taskId: string): Promise<Task | null> {
try {
this.ensureBriefSelected('getTask');
this.authManager.ensureBriefSelected('getTask');
const task = await this.repository.getTask(this.projectId, taskId);
if (!task) {
throw new TaskMasterError(
`Task ${taskId} not found`,
ERROR_CODES.VALIDATION_ERROR,
ERROR_CODES.TASK_NOT_FOUND,
{
operation: 'getTask',
taskId,
@@ -127,26 +120,4 @@ export class TaskRetrievalService {
);
}
}
/**
* Ensure a brief is selected in the current context
* @returns The current auth context with a valid briefId
*/
private ensureBriefSelected(operation: string): ContextWithBrief {
const context = this.authManager.getContext();
if (!context?.briefId) {
throw new TaskMasterError(
'No brief selected',
ERROR_CODES.NO_BRIEF_SELECTED,
{
operation,
userMessage:
'No brief selected. Please select a brief first using: tm context brief <brief-id> or tm context brief <brief-url>'
}
);
}
return context as ContextWithBrief;
}
}

View File

@@ -47,13 +47,6 @@ export interface ApiStorageConfig {
maxRetries?: number;
}
/**
* Auth context with a guaranteed briefId
*/
type ContextWithBrief = NonNullable<
ReturnType<typeof AuthManager.prototype.getContext>
> & { briefId: string };
/**
* Response from the update task with prompt API endpoint
*/
@@ -205,7 +198,8 @@ export class ApiStorage implements IStorage {
await this.ensureInitialized();
try {
const context = this.ensureBriefSelected('loadTasks');
const context =
AuthManager.getInstance().ensureBriefSelected('loadTasks');
// Load tasks from the current brief context with filters pushed to repository
const tasks = await this.retryOperation(() =>
@@ -276,7 +270,7 @@ export class ApiStorage implements IStorage {
try {
const retrievalService = this.getRetrievalService();
return retrievalService.getTask(taskId);
return await this.retryOperation(() => retrievalService.getTask(taskId));
} catch (error) {
this.wrapError(error, 'Failed to load task from API', {
operation: 'loadTask',
@@ -636,7 +630,7 @@ export class ApiStorage implements IStorage {
await this.ensureInitialized();
try {
this.ensureBriefSelected('updateTaskStatus');
AuthManager.getInstance().ensureBriefSelected('updateTaskStatus');
const existingTask = await this.retryOperation(() =>
this.repository.getTask(this.projectId, taskId)
@@ -897,29 +891,6 @@ export class ApiStorage implements IStorage {
}
}
/**
* Ensure a brief is selected in the current context
* @returns The current auth context with a valid briefId
*/
private ensureBriefSelected(operation: string): ContextWithBrief {
const authManager = AuthManager.getInstance();
const context = authManager.getContext();
if (!context?.briefId) {
throw new TaskMasterError(
'No brief selected',
ERROR_CODES.NO_BRIEF_SELECTED,
{
operation,
userMessage:
'No brief selected. Please select a brief first using: tm context brief <brief-id> or tm context brief <brief-url>'
}
);
}
return context as ContextWithBrief;
}
/**
* Get or create API client instance with auth
*/
@@ -936,7 +907,8 @@ export class ApiStorage implements IStorage {
);
}
const context = this.ensureBriefSelected('getApiClient');
const context =
AuthManager.getInstance().ensureBriefSelected('getApiClient');
const authManager = AuthManager.getInstance();
this.apiClient = new ApiClient({