UNPKG

@stackmemoryai/stackmemory

Version:

Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.

522 lines (521 loc) 16.9 kB
import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __pathDirname } from 'path'; const __filename = __fileURLToPath(import.meta.url); const __dirname = __pathDirname(__filename); var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => { ErrorCode2["DB_CONNECTION_FAILED"] = "DB_001"; ErrorCode2["DB_QUERY_FAILED"] = "DB_002"; ErrorCode2["DB_TRANSACTION_FAILED"] = "DB_003"; ErrorCode2["DB_MIGRATION_FAILED"] = "DB_004"; ErrorCode2["DB_CONSTRAINT_VIOLATION"] = "DB_005"; ErrorCode2["DB_SCHEMA_ERROR"] = "DB_006"; ErrorCode2["DB_INSERT_FAILED"] = "DB_007"; ErrorCode2["DB_UPDATE_FAILED"] = "DB_008"; ErrorCode2["DB_DELETE_FAILED"] = "DB_009"; ErrorCode2["DB_CORRUPTION"] = "DB_010"; ErrorCode2["FRAME_NOT_FOUND"] = "FRAME_001"; ErrorCode2["FRAME_INVALID_STATE"] = "FRAME_002"; ErrorCode2["FRAME_PARENT_NOT_FOUND"] = "FRAME_003"; ErrorCode2["FRAME_CYCLE_DETECTED"] = "FRAME_004"; ErrorCode2["FRAME_ALREADY_CLOSED"] = "FRAME_005"; ErrorCode2["FRAME_INIT_FAILED"] = "FRAME_006"; ErrorCode2["FRAME_INVALID_INPUT"] = "FRAME_007"; ErrorCode2["FRAME_STACK_OVERFLOW"] = "FRAME_008"; ErrorCode2["TASK_NOT_FOUND"] = "TASK_001"; ErrorCode2["TASK_INVALID_STATE"] = "TASK_002"; ErrorCode2["TASK_DEPENDENCY_CONFLICT"] = "TASK_003"; ErrorCode2["TASK_CIRCULAR_DEPENDENCY"] = "TASK_004"; ErrorCode2["LINEAR_AUTH_FAILED"] = "LINEAR_001"; ErrorCode2["LINEAR_API_ERROR"] = "LINEAR_002"; ErrorCode2["LINEAR_SYNC_FAILED"] = "LINEAR_003"; ErrorCode2["LINEAR_WEBHOOK_FAILED"] = "LINEAR_004"; ErrorCode2["MCP_TOOL_NOT_FOUND"] = "MCP_001"; ErrorCode2["MCP_INVALID_PARAMS"] = "MCP_002"; ErrorCode2["MCP_EXECUTION_FAILED"] = "MCP_003"; ErrorCode2["MCP_RATE_LIMITED"] = "MCP_004"; ErrorCode2["PROJECT_NOT_FOUND"] = "PROJECT_001"; ErrorCode2["PROJECT_INVALID_PATH"] = "PROJECT_002"; ErrorCode2["PROJECT_GIT_ERROR"] = "PROJECT_003"; ErrorCode2["VALIDATION_FAILED"] = "VAL_001"; ErrorCode2["INVALID_INPUT"] = "VAL_002"; ErrorCode2["MISSING_REQUIRED_FIELD"] = "VAL_003"; ErrorCode2["TYPE_MISMATCH"] = "VAL_004"; ErrorCode2["INITIALIZATION_ERROR"] = "SYS_001"; ErrorCode2["NOT_FOUND"] = "SYS_002"; ErrorCode2["INTERNAL_ERROR"] = "SYS_003"; ErrorCode2["CONFIGURATION_ERROR"] = "SYS_004"; ErrorCode2["PERMISSION_DENIED"] = "SYS_005"; ErrorCode2["RESOURCE_EXHAUSTED"] = "SYS_006"; ErrorCode2["SERVICE_UNAVAILABLE"] = "SYS_007"; ErrorCode2["SYSTEM_INIT_FAILED"] = "SYS_008"; ErrorCode2["UNKNOWN_ERROR"] = "SYS_009"; ErrorCode2["OPERATION_TIMEOUT"] = "SYS_010"; ErrorCode2["AUTH_FAILED"] = "AUTH_001"; ErrorCode2["TOKEN_EXPIRED"] = "AUTH_002"; ErrorCode2["INVALID_CREDENTIALS"] = "AUTH_003"; ErrorCode2["FILE_NOT_FOUND"] = "FS_001"; ErrorCode2["DISK_FULL"] = "FS_002"; ErrorCode2["NOT_GIT_REPO"] = "GIT_001"; ErrorCode2["GIT_COMMAND_FAILED"] = "GIT_002"; ErrorCode2["INVALID_BRANCH"] = "GIT_003"; ErrorCode2["NETWORK_ERROR"] = "NET_001"; ErrorCode2["API_ERROR"] = "NET_002"; ErrorCode2["STACK_CONTEXT_NOT_FOUND"] = "COLLAB_001"; ErrorCode2["HANDOFF_REQUEST_EXPIRED"] = "COLLAB_002"; ErrorCode2["MERGE_CONFLICT_UNRESOLVABLE"] = "COLLAB_003"; ErrorCode2["PERMISSION_VIOLATION"] = "COLLAB_004"; ErrorCode2["OPERATION_FAILED"] = "COLLAB_005"; ErrorCode2["OPERATION_EXPIRED"] = "COLLAB_006"; ErrorCode2["INVALID_STATE"] = "COLLAB_007"; ErrorCode2["RESOURCE_NOT_FOUND"] = "COLLAB_008"; ErrorCode2["HANDOFF_ALREADY_EXISTS"] = "COLLAB_009"; ErrorCode2["MERGE_SESSION_INVALID"] = "COLLAB_010"; ErrorCode2["STACK_SWITCH_FAILED"] = "COLLAB_011"; ErrorCode2["APPROVAL_TIMEOUT"] = "COLLAB_012"; ErrorCode2["CONFLICT_RESOLUTION_FAILED"] = "COLLAB_013"; ErrorCode2["TEAM_ACCESS_DENIED"] = "COLLAB_014"; ErrorCode2["STACK_LIMIT_EXCEEDED"] = "COLLAB_015"; return ErrorCode2; })(ErrorCode || {}); class StackMemoryError extends Error { code; context; cause; isRetryable; httpStatus; timestamp; constructor(options) { super(options.message); this.name = this.constructor.name; this.code = options.code; this.context = options.context; this.cause = options.cause; this.isRetryable = options.isRetryable ?? false; this.httpStatus = options.httpStatus ?? 500; this.timestamp = /* @__PURE__ */ new Date(); if (Error.captureStackTrace) { Error.captureStackTrace(this, this.constructor); } } toJSON() { return { name: this.name, code: this.code, message: this.message, context: this.context, isRetryable: this.isRetryable, httpStatus: this.httpStatus, timestamp: this.timestamp.toISOString(), stack: this.stack, cause: this.cause?.message }; } } class DatabaseError extends StackMemoryError { constructor(message, code = "DB_002" /* DB_QUERY_FAILED */, context, cause) { super({ code, message, context, cause, isRetryable: code === "DB_001" /* DB_CONNECTION_FAILED */, httpStatus: 503 }); } } class FrameError extends StackMemoryError { constructor(message, code = "FRAME_002" /* FRAME_INVALID_STATE */, context) { super({ code, message, context, isRetryable: false, httpStatus: 400 }); } } class TaskError extends StackMemoryError { constructor(message, code = "TASK_002" /* TASK_INVALID_STATE */, context) { super({ code, message, context, isRetryable: false, httpStatus: 400 }); } } class IntegrationError extends StackMemoryError { constructor(message, code = "LINEAR_002" /* LINEAR_API_ERROR */, context, cause) { super({ code, message, context, cause, isRetryable: true, httpStatus: 502 }); } } class MCPError extends StackMemoryError { constructor(message, code = "MCP_003" /* MCP_EXECUTION_FAILED */, context) { super({ code, message, context, isRetryable: code === "MCP_004" /* MCP_RATE_LIMITED */, httpStatus: code === "MCP_004" /* MCP_RATE_LIMITED */ ? 429 : 400 }); } } class ValidationError extends StackMemoryError { constructor(message, code = "VAL_001" /* VALIDATION_FAILED */, context) { super({ code, message, context, isRetryable: false, httpStatus: 400 }); } } class ProjectError extends StackMemoryError { constructor(message, code = "PROJECT_001" /* PROJECT_NOT_FOUND */, context) { super({ code, message, context, isRetryable: false, httpStatus: 404 }); } } class SystemError extends StackMemoryError { constructor(message, code = "SYS_003" /* INTERNAL_ERROR */, context, cause) { super({ code, message, context, cause, isRetryable: code === "SYS_007" /* SERVICE_UNAVAILABLE */, httpStatus: 500 }); } } function isRetryableError(error) { if (error instanceof StackMemoryError) { return error.isRetryable; } if (error instanceof Error) { const message = error.message.toLowerCase(); return message.includes("econnrefused") || message.includes("timeout") || message.includes("enotfound") || message.includes("socket hang up"); } return false; } function getErrorMessage(error) { if (error instanceof Error) { return error.message; } if (typeof error === "string") { return error; } if (error && typeof error === "object" && "message" in error) { return String(error.message); } return "An unknown error occurred"; } function wrapError(error, defaultMessage, code = "SYS_003" /* INTERNAL_ERROR */, context) { if (error instanceof StackMemoryError) { return error; } const cause = error instanceof Error ? error : void 0; const message = error instanceof Error ? error.message : defaultMessage; return new SystemError(message, code, context, cause); } function isStackMemoryError(error) { return error instanceof StackMemoryError; } function createErrorHandler(defaultContext) { return (error, additionalContext) => { const context = { ...defaultContext, ...additionalContext }; if (error instanceof StackMemoryError) { return new StackMemoryError({ code: error.code, message: error.message, context: { ...error.context, ...context }, cause: error.cause, isRetryable: error.isRetryable, httpStatus: error.httpStatus }); } return wrapError( error, getErrorMessage(error), "SYS_003" /* INTERNAL_ERROR */, context ); }; } function getUserFriendlyMessage(code) { switch (code) { // Auth errors case "AUTH_001" /* AUTH_FAILED */: return "Authentication failed. Please check your credentials and try again."; case "AUTH_002" /* TOKEN_EXPIRED */: return "Your session has expired. Please log in again."; case "AUTH_003" /* INVALID_CREDENTIALS */: return "Invalid credentials provided. Please check and try again."; // File system errors case "FS_001" /* FILE_NOT_FOUND */: return "The requested file or directory was not found."; case "SYS_005" /* PERMISSION_DENIED */: return "Permission denied. Please check file permissions or run with appropriate privileges."; case "FS_002" /* DISK_FULL */: return "Insufficient disk space. Please free up space and try again."; // Git errors case "GIT_001" /* NOT_GIT_REPO */: return "This command requires a git repository. Please run it from within a git repository."; case "GIT_002" /* GIT_COMMAND_FAILED */: return "Git operation failed. Please ensure your repository is in a valid state."; case "GIT_003" /* INVALID_BRANCH */: return "Invalid branch specified. Please check the branch name and try again."; // Database errors case "DB_001" /* DB_CONNECTION_FAILED */: return "Database connection failed. Please try again or contact support if the issue persists."; case "DB_002" /* DB_QUERY_FAILED */: return "Database query failed. Please try again."; case "DB_010" /* DB_CORRUPTION */: return "Database appears to be corrupted. Please contact support."; // Network errors case "NET_001" /* NETWORK_ERROR */: return "Network error. Please check your internet connection and try again."; case "NET_002" /* API_ERROR */: return "API request failed. Please try again later."; case "SYS_010" /* OPERATION_TIMEOUT */: return "The operation timed out. Please try again."; // Validation errors case "VAL_002" /* INVALID_INPUT */: return "Invalid input provided. Please check your command and try again."; case "VAL_001" /* VALIDATION_FAILED */: return "Validation failed. Please check your input and try again."; case "VAL_003" /* MISSING_REQUIRED_FIELD */: return "A required field is missing. Please provide all required information."; // System errors case "SYS_004" /* CONFIGURATION_ERROR */: return "Configuration error. Please check your settings."; case "SYS_007" /* SERVICE_UNAVAILABLE */: return "Service is temporarily unavailable. Please try again later."; // Default default: return "An unexpected error occurred. Please try again or contact support."; } } class ErrorHandler { static retryMap = /* @__PURE__ */ new Map(); static MAX_RETRIES = 3; /** * Handle an error and exit the process */ static handle(error, operation) { if (error instanceof StackMemoryError) { const userMessage = getUserFriendlyMessage(error.code); console.error(`\u274C ${userMessage}`); if (error.isRetryable) { console.error("\u{1F4A1} This error may be recoverable. Please try again."); } process.exit(1); } if (error instanceof Error) { let stackMemoryError; if ("code" in error && typeof error.code === "string") { stackMemoryError = ErrorHandler.fromNodeError( error, { operation } ); } else { stackMemoryError = wrapError( error, error.message, "COLLAB_005" /* OPERATION_FAILED */, { operation } ); } const userMessage = getUserFriendlyMessage(stackMemoryError.code); console.error(`\u274C ${userMessage}`); if (stackMemoryError.isRetryable) { console.error("\u{1F4A1} This error may be recoverable. Please try again."); } process.exit(1); } console.error("\u274C An unexpected error occurred."); process.exit(1); } /** * Convert Node.js error to StackMemoryError */ static fromNodeError(nodeError, context = {}) { const code = nodeError.code; switch (code) { case "ENOENT": return new SystemError( `File or directory not found: ${nodeError.path}`, "FS_001" /* FILE_NOT_FOUND */, { ...context, path: nodeError.path }, nodeError ); case "EACCES": case "EPERM": return new SystemError( `Permission denied: ${nodeError.path}`, "SYS_005" /* PERMISSION_DENIED */, { ...context, path: nodeError.path }, nodeError ); case "ENOSPC": return new SystemError( "No space left on device", "FS_002" /* DISK_FULL */, context, nodeError ); case "ETIMEDOUT": return new SystemError( "Operation timed out", "SYS_010" /* OPERATION_TIMEOUT */, context, nodeError ); default: return new SystemError( nodeError.message, "SYS_009" /* UNKNOWN_ERROR */, { ...context, nodeErrorCode: code }, nodeError ); } } /** * Safely execute an operation with optional fallback */ static async safeExecute(operation, operationName, fallback) { try { return await operation(); } catch (error) { if (fallback !== void 0) { return fallback; } ErrorHandler.handle(error, operationName); } } /** * Execute with automatic retry and exponential backoff */ static async withRetry(operation, operationName, maxRetries = ErrorHandler.MAX_RETRIES) { let lastError; for (let attempt = 1; attempt <= maxRetries; attempt++) { try { const result = await operation(); ErrorHandler.retryMap.delete(operationName); return result; } catch (error) { lastError = error; if (error instanceof StackMemoryError && !error.isRetryable) { ErrorHandler.handle(error, operationName); } if (attempt === maxRetries) { break; } const delay = Math.min(1e3 * Math.pow(2, attempt - 1), 5e3); await new Promise((resolve) => setTimeout(resolve, delay)); } } ErrorHandler.handle( lastError, `${operationName} (after ${maxRetries} attempts)` ); } /** * Create a circuit breaker for an operation */ static createCircuitBreaker(operation, operationName, threshold = 5) { let failures = 0; let lastFailure = 0; const resetTimeout = 3e4; return async () => { const now = Date.now(); if (now - lastFailure > resetTimeout) { failures = 0; } if (failures >= threshold) { throw new SystemError( `Circuit breaker open for '${operationName}'`, "SYS_007" /* SERVICE_UNAVAILABLE */, { operationName, failures, threshold } ); } try { const result = await operation(); failures = 0; return result; } catch (error) { failures++; lastFailure = now; throw error; } }; } } const validateInput = (value, name, validator) => { if (!validator(value)) { throw new ValidationError( `Invalid ${name}: ${String(value)}`, "VAL_002" /* INVALID_INPUT */, { name, value } ); } }; const validateEmail = (email) => { const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; if (!emailRegex.test(email) || email.length > 254) { throw new ValidationError( `Invalid email format: ${email}`, "VAL_002" /* INVALID_INPUT */, { email } ); } }; const validatePath = (filePath) => { if (!filePath || filePath.includes("..") || filePath.includes("\0")) { throw new ValidationError( `Invalid path: ${filePath}`, "VAL_002" /* INVALID_INPUT */, { path: filePath } ); } }; export * from "./error-utils.js"; export { DatabaseError, ErrorCode, ErrorHandler, FrameError, IntegrationError, MCPError, ProjectError, StackMemoryError, SystemError, TaskError, ValidationError, createErrorHandler, getErrorMessage, getUserFriendlyMessage, isRetryableError, isStackMemoryError, validateEmail, validateInput, validatePath, wrapError }; //# sourceMappingURL=index.js.map