import { logger } from './utils/logger.cjs'; /** * Core LLMail configuration interface. * All paths are relative to the config file location. */ interface LlmailConfig { types_list: string[]; active_states: string[]; inactive_reasons: string[]; issues_dir: string; inbox_dir: string; type_dirs?: string[]; plugins?: string | string[] | Array<{ name: string; config?: Record; }>; } type ResolvedConfig = LlmailConfig; /** * Load and validate config from a file */ declare function loadConfigFromFile(fs: FileSystemAdapter, configPath: string, rootDir: string): Promise; /** * Load llmail config from the default location or specified path */ declare function loadLlmailConfig(fs: FileSystemAdapter, rootDir?: string, configPath?: string): Promise; /** * Save config to the default location */ declare function saveLlmailConfig(fs: FileSystemAdapter, config: LlmailConfig, configPath: string): Promise; /** * Core type definitions for the activity-based file management system. * These types represent the fundamental concepts and operations in the system. */ /** * Metadata for an activity file, following Postel's Law: * - Liberal in what we accept (additional fields allowed) * - Conservative in what we require (only core fields mandatory) */ interface FrontmatterMetadata { /** Unique identifier for the activity */ id: string; /** Type of activity (e.g., 'bug', 'feature', 'task') */ type: string; /** Current state of the activity (active state or inactive reason) */ state?: string; /** * Whether the item is active. Used to determine inbox vs type archive placement when no state is set. * - active: true + no state = inbox * - active: false/undefined + no state = type archive * - If state is set, active status is inferred from the state type */ active?: boolean; /** Title of the activity */ title?: string; /** Creation timestamp */ created?: string; /** Allow any additional fields (Postel's Law) */ [key: string]: any; } /** * Result of an operation that affects files. * Provides both success/failure status and the affected filepath. */ interface OperationResult { /** Whether the operation succeeded */ success: boolean; /** Path to the affected file */ filepath: string; /** Error message if operation failed */ error?: string; } /** * Configuration for activity management. * Defines valid types, states, and reasons. */ interface Config { /** List of valid activity types */ types_list: string[]; /** List of valid active states */ active_states: string[]; /** List of valid inactive reasons */ inactive_reasons: string[]; /** Plugin configurations */ plugins?: (string | PluginConfig)[]; } /** * Configuration for pathname handling. * Defines root paths for the system. */ interface PathConfig { /** Root directory for all activity files */ issues_dir: string; /** Directory for new/unprocessed activities */ inbox_dir: string; } /** * Validation result with detailed error information. * Used internally by services for rich error handling. */ interface ValidationResult { valid: boolean; errors: string[]; warnings: string[]; guidance?: string[]; } /** * File operation options for atomic operations. * Used internally by services for safe file operations. */ interface FileOperationOptions { /** Whether to create a backup before operation */ createBackup?: boolean; /** Whether to use atomic rename */ atomic?: boolean; /** Whether to preserve existing file if it exists */ preserve?: boolean; } /** * Plugin configuration as specified in llmail.yaml */ interface PluginConfig { /** Plugin name or configuration object */ name: string; /** Optional plugin-specific configuration */ config?: Record; } /** * Command context available to plugin commands */ interface CommandContext$1 { /** Core llmail instance */ llmail: LLMail; /** Logger instance */ logger: typeof logger; /** Current configuration */ config: ResolvedConfig; /** Current working directory */ cwd: string; /** Plugin service for accessing configurations */ pluginService: PluginService$1; } /** * Command option definition */ interface CommandOption { /** Option name */ name: string; /** Option description */ description?: string; /** Option type (boolean, string, number) */ type?: 'boolean' | 'string' | 'number'; /** Whether the option is required */ required?: boolean; /** Default value if not specified */ default?: any; } /** * Plugin command definition */ interface PluginCommand$1 { /** Command name */ name: string; /** Command description */ description: string; /** Command options */ options?: CommandOption[]; /** Subcommands */ subcommands?: PluginCommand$1[]; /** Hook called before command execution */ beforeExecute?: (args: Record) => Promise>; /** Main command execution */ execute: (args: Record, context: CommandContext$1) => Promise; /** Hook called after successful execution */ afterExecute?: (result: any) => Promise; /** Hook called on command error */ onError?: (error: Error, args: Record) => Promise; } /** * Plugin service interface */ interface PluginService$1 { /** Get configuration for a specific plugin */ getPluginConfig(pluginName: string): Record; /** Set configuration for a specific plugin */ setPluginConfig(pluginName: string, config: Record): void; /** Register a new plugin */ registerPlugin(plugin: Plugin$1): void; /** Get all registered plugins */ getPlugins(): Plugin$1[]; } /** * Core plugin interface that all plugins must implement */ interface Plugin$1 { /** Unique identifier for the plugin */ name: string; /** Plugin priority - higher numbers run first (default: 0) */ priority?: number; /** Default configuration for this plugin */ defaultConfig?: Record; /** Optional function to validate plugin configuration */ validateConfig?: (config: Record) => ValidationResult; /** Register new or override existing intent patterns */ registerIntents?: (existingPatterns: IntentPattern$1[]) => IntentPattern$1[]; /** Register additional valid types */ registerTypes?: () => string[]; /** Register additional valid active states */ registerActiveStates?: () => string[]; /** Register additional valid inactive reasons */ registerInactiveReasons?: () => string[]; /** Custom metadata validation hook */ validateMetadata?: (metadata: FrontmatterMetadata) => ValidationResult; /** Post-processing hook for interpreted intents */ postInterpretIntent?: (intent: InterpretedIntent$1) => InterpretedIntent$1; /** Register additional CLI commands */ registerCommands?: () => PluginCommand$1[]; } /** * Intent pattern for file organization */ interface IntentPattern$1 { /** Pattern to match against file state */ pattern: { metadata: Record; }; /** Function to interpret matched pattern */ interpretation: (state: { metadata: Record; }) => { targetLocation: { dirname: string; basename: string; }; targetMetadata: Record; }; } /** * Result of intent interpretation */ interface InterpretedIntent$1 { /** Target location for the file */ targetLocation: { dirname: string; basename: string; }; /** Updated metadata after interpretation */ targetMetadata: Record; } /** * Context object passed to plugin commands during execution */ interface CommandContext { llmail: LLMail; logger: typeof logger; config: ResolvedConfig; cwd: string; pluginService: PluginService; } /** * Describes a command that a plugin may register to extend the llmail CLI. */ interface PluginCommand { name: string; description: string; subcommands?: PluginCommand[]; options?: Array<{ name: string; description?: string; type?: 'boolean' | 'string' | 'number'; required?: boolean; default?: any; }>; /** Execute the command with arguments and context */ execute: (args: Record, context: CommandContext) => Promise; /** Optional hook that runs before command execution. Can modify args. */ beforeExecute?: (args: Record) => Promise>; /** Optional hook that runs after successful command execution */ afterExecute?: (result: any) => Promise; /** Optional hook that runs when an error occurs. Can transform the error. */ onError?: (error: Error, args: Record) => Promise; } /** * Interface that all plugins must implement */ interface Plugin { /** Unique identifier for the plugin */ name: string; /** Plugin priority - higher numbers run first (default: 0) */ priority?: number; /** Default configuration for this plugin */ defaultConfig?: Record; /** Optional function to validate plugin configuration */ validateConfig?: (config: Record) => ValidationResult; /** Register new or override existing intent patterns */ registerIntents?: (existingPatterns: IntentPattern[]) => IntentPattern[]; /** Register additional valid types */ registerTypes?: () => string[]; /** Register additional valid active states */ registerActiveStates?: () => string[]; /** Register additional valid inactive reasons */ registerInactiveReasons?: () => string[]; /** Custom metadata validation hook */ validateMetadata?: (metadata: FrontmatterMetadata) => ValidationResult; /** Post-processing hook for interpreted intents */ postInterpretIntent?: (intent: InterpretedIntent) => InterpretedIntent; /** Optional: Register additional CLI commands for this plugin */ registerCommands?: () => PluginCommand[]; } declare class PluginService { private plugins; private registeredTypes; private registeredActiveStates; private registeredInactiveReasons; private pluginConfigs; private commands; /** * Register a new plugin */ registerPlugin(plugin: Plugin): void; /** * Get configuration for a specific plugin */ getPluginConfig(pluginName: string): any; /** * Set configuration for a specific plugin */ setPluginConfig(pluginName: string, config: any): void; /** * Get all registered plugins */ getPlugins(): Plugin[]; /** * Apply all plugin intent patterns to the existing patterns */ applyPluginIntents(existingPatterns: IntentPattern[]): IntentPattern[]; /** * Get all valid types (including plugin-provided ones) */ getValidTypes(): string[]; /** * Get all valid active states (including plugin-provided ones) */ getValidActiveStates(): string[]; /** * Get all valid inactive reasons (including plugin-provided ones) */ getValidInactiveReasons(): string[]; /** * Get all valid states (both active states and inactive reasons) */ getValidStates(): string[]; /** * Run validation hooks from all plugins */ validateMetadata(metadata: FrontmatterMetadata): ValidationResult; /** * Run post-processing hooks from all plugins */ processIntent(intent: InterpretedIntent): InterpretedIntent; /** * Register core types that plugins can extend */ registerCoreTypes(types: string[]): void; /** * Register core active states that plugins can extend */ registerCoreActiveStates(states: string[]): void; /** * Register core inactive reasons that plugins can extend */ registerCoreInactiveReasons(reasons: string[]): void; /** * Collects all plugin commands from every plugin in the order of their priority. */ getAllCommands(): PluginCommand[]; } interface FileLocation$1 { dirname: string; basename: string; } interface FileState { pathname: string; metadata: FrontmatterMetadata; } interface FileIntent { targetDir: string; metadata: FrontmatterMetadata; } interface InterpretedIntent { targetLocation: FileLocation$1; targetMetadata: FrontmatterMetadata; reason: string; } interface LocationPattern { inDirectory?: string | ((dir: string) => boolean); hasTypeDir?: boolean; } interface MetadataPattern { hasState?: boolean; specificState?: string | ((state: string) => boolean); } interface IntentPattern { pattern: { location?: LocationPattern; metadata?: MetadataPattern; }; interpretation: (state: FileState) => InterpretedIntent; } declare class IntentService { private config; private pluginService?; private intentPatterns; private validTypes; constructor(config: ResolvedConfig, pluginService?: PluginService | undefined); /** * Get all registered intent patterns, including plugin-provided ones */ getIntentPatterns(): IntentPattern[]; isValidState(state: string): boolean; private isValidInactiveReason; private isValidType; isActiveState(state: string): boolean; isInactiveReason(state: string): boolean; getCanonicalState(state: string): string; getTargetDirForState(state: string, type: string): string; getTypeArchiveDir(type: string): string; matchState(state: FileState): { matches: boolean; state?: string; }; interpretState(state: string): { canonicalState?: string; isActive: boolean; }; matchesState(metadata: FrontmatterMetadata, stateInfo: { isActive: boolean; canonicalState?: string; }): boolean; interpretIntent(state: FileState): InterpretedIntent; validateFinalState(state: FileState): ValidationResult; validateStateTransition(initialState: FileState, targetState: FileState): ValidationResult; validateMetadataAgainstConfig(metadata: FrontmatterMetadata): ValidationResult; validateMove(sourcePath: string, targetPath: string, metadata: FrontmatterMetadata): ValidationResult; /** * Clean state transition by removing invalid fields and normalizing values */ cleanStateTransition(current: FrontmatterMetadata, next: Partial): FrontmatterMetadata; /** * Interprets metadata and infers active state */ interpretMetadata(metadata: Partial): FrontmatterMetadata; /** * Interprets a state transition and returns the necessary metadata updates */ interpretStateTransition(targetState: string, options?: { reason?: string; }): Partial; getValidTypes(): string[]; } interface ParsedPathname { type: string; id: string; extension?: string; active?: boolean; state?: string; } interface FileLocation { dirname: string; basename: string; } /** * Service for path manipulation and location determination. * Does NOT handle any filesystem operations. */ declare class PathnameService { private config; private intentService; constructor(config: ResolvedConfig, intentService: IntentService); getConfig(): ResolvedConfig; deriveFileLocation(metadata: FrontmatterMetadata): FileLocation; joinPath(...parts: string[]): string; resolvePath(pathname: string, from?: string): string; toCanonicalPath(pathname: string): string; getActivityDirectories(): string[]; getStateFullPath(state: string): string; getTypeDirs(): string[]; parsePathname(pathname: string): ParsedPathname | null; parseBasename(basename: string): ParsedPathname | null; getNewPath(pathname: string, metadata: FrontmatterMetadata): string; isInboxDirectory(dirname: string): boolean; isTypeDirectory(dirname: string): boolean; matchesId(filename: string, id: string): boolean; getPossiblePathsForId(id: string): string[]; getPossiblePathsForType(type: string): string[]; getPossiblePathsForState(state: string): string[]; /** * Get the canonical path where a NEW file with this ID should be located. * This returns the default location for new files (inbox). * * To find an existing file by ID, use IssueService.findFileById() instead, * as that method handles all the necessary validation and error checking. * * @param id The ID to get the path for * @returns The canonical path where a new file with this ID should be created */ getPathForId(id: string): string; } interface FileSystemAdapter { readFile(path: string): Promise; writeFile(path: string, data: string): Promise; exists(path: string): Promise; pathExists(path: string): Promise; ensureDir(path: string): Promise; readdir(path: string): Promise; move(src: string, dest: string): Promise; remove(path: string): Promise; createReadStream(path: string): NodeJS.ReadableStream; createWriteStream(path: string): NodeJS.WritableStream; stat(path: string): Promise<{ isDirectory(): boolean; }>; rename(oldPath: string, newPath: string): Promise; mkdtemp(prefix: string): Promise; rm(path: string, options: { recursive?: boolean; force?: boolean; }): Promise; } declare const realFileSystemAdapter: FileSystemAdapter; /** * Wraps a FileSystemAdapter to handle path resolution relative to a root directory. * This ensures all paths passed to the underlying adapter are properly prefixed with the root directory. */ declare class RootedFileSystemAdapter implements FileSystemAdapter { private fs; private rootDir; private pathnameService?; constructor(fs: FileSystemAdapter, rootDir: string, pathnameService?: PathnameService | undefined); private resolvePath; readFile(filePath: string): Promise; writeFile(filePath: string, content: string): Promise; exists(filePath: string): Promise; pathExists(filePath: string): Promise; ensureDir(dirPath: string): Promise; readdir(dirPath: string): Promise; move(src: string, dest: string): Promise; remove(filePath: string): Promise; createReadStream(filePath: string): NodeJS.ReadableStream; createWriteStream(filePath: string): NodeJS.WritableStream; stat(filePath: string): Promise<{ isDirectory(): boolean; }>; rename(oldPath: string, newPath: string): Promise; mkdtemp(prefix: string): Promise; rm(path: string, options: { recursive?: boolean; force?: boolean; }): Promise; } interface LLMailOptions { fs?: FileSystemAdapter; config?: ResolvedConfig; plugins?: Plugin[]; rootDir?: string; pluginService?: PluginService; } interface MoveOptions { state?: string; type?: string; active?: boolean; inactive?: boolean; } interface NewOptions { title?: string; content?: string; state?: string; frontmatter?: Record; } interface DoneOptions { state?: string; frontmatter?: Record; } interface SyncOptions { include?: string; exclude?: string; dryRun?: boolean; force?: boolean; } interface SyncResult { changes: Array<{ id: string; from: string; to: string; state: string; }>; errors?: Array<{ id: string; error: string; }>; } interface Issue { id: string; filePath: string; metadata: FrontmatterMetadata; content?: string; } /** * Main class for managing issues and their lifecycle. * Provides a high-level API that coordinates between various services. */ declare class LLMail { private fs; private config?; private issueService; private pathnameService; private intentService; private fileService; private pluginService; private plugins; private rootDir; constructor(options?: LLMailOptions); /** * Initialize the LLMail system and all its services */ init(): Promise; private ensureInitialized; /** * Create a new issue */ new(type: string | undefined, options?: NewOptions): Promise; /** * Move an issue to a new state or type */ mv(id: string, options?: MoveOptions): Promise; move(id: string, options: MoveOptions): Promise; /** * Get an issue by ID */ getIssueById(id: string): Promise; /** * Update issue frontmatter */ updateFrontmatter(id: string, updates: Partial): Promise; /** * Append an update to an issue */ appendUpdate(id: string, content: string, metadata?: Record, time?: boolean): Promise; /** * Prepend an update to an issue */ prependUpdate(id: string, content: string, metadata?: Record, time?: boolean): Promise; /** * Sync file locations with their metadata */ sync(options?: SyncOptions): Promise; /** * Get all valid issue types */ getIssueTypes(): Promise; /** * Get all valid issue states */ getIssueStates(): Promise; /** * Find issues by type */ findIssuesByType(type: string): Promise; /** * Find issues by state */ findIssuesByState(state: string): Promise; done(id: string, options?: DoneOptions): Promise; /** * Returns only the content section (excludes frontmatter and xml) */ getContent(id: string): Promise; /** * Overwrites the entire content region (between frontmatter and XML) */ setContent(id: string, newContent: string): Promise; /** * Appends text to the end of content region, above the xml region */ appendContent(id: string, content: string): Promise; /** * Prepends text to the start of content region, below the frontmatter */ prependContent(id: string, content: string): Promise; /** * Provides read-access to the plugin service instance, * so that consumers can fetch plugin commands, etc. */ getPluginService(): PluginService; /** * Get the current resolved config */ getConfig(): ResolvedConfig; /** * Get the intent service instance */ getIntentService(): IntentService; } export { type LocationPattern as A, type MetadataPattern as B, type Config as C, type IntentPattern as D, type ParsedPathname as E, type FileSystemAdapter as F, type FileLocation as G, type DoneOptions as H, IntentService as I, type SyncResult as J, type Issue as K, type LlmailConfig as L, type MoveOptions as M, type NewOptions as N, type OperationResult as O, PathnameService as P, RootedFileSystemAdapter as R, type SyncOptions as S, type ValidationResult as V, type ResolvedConfig as a, loadLlmailConfig as b, type Plugin as c, type FrontmatterMetadata as d, PluginService as e, LLMail as f, type LLMailOptions as g, type PathConfig as h, type FileOperationOptions as i, type PluginConfig as j, type CommandContext$1 as k, loadConfigFromFile as l, type CommandOption as m, type PluginCommand$1 as n, type PluginService$1 as o, type Plugin$1 as p, type IntentPattern$1 as q, realFileSystemAdapter as r, saveLlmailConfig as s, type InterpretedIntent$1 as t, type CommandContext as u, type PluginCommand as v, type FileLocation$1 as w, type FileState as x, type FileIntent as y, type InterpretedIntent as z };