llmail-core
TypeScript icon, indicating that this package has built-in type declarations

1.0.0 • Public • Published

LLMail Core

An intent-based inbox and issue management system for LLM collaboration.

Core Concepts

  • Intent-Based: Metadata is the source of truth. The system helps align file locations with metadata instead of enforcing rigid rules.
  • Flexible Organization: Works with any combination of states and types through simple configuration.
  • Simple Directory Structure:
    • Active items live in state directories (issues/_state/)
    • Inactive items live in type directories (issues/type/)
    • New items start in the inbox (inbox/)
  • Extensibility: llmail-core provides a simple base you can use to build much more powerful systems.

Directory Structure

/
├── inbox/                    # New items needing triage
└── issues/                  # Root for all organized items
    ├── _working/           # Active items in 'working' state
    │   └── working-bug-a41d.md
    ├── _blocked/           # Active items in 'blocked' state
    │   └── blocked-feat-789.md
    ├── bug/                # Inactive bugs
    │   └── fixed-bug-456.md
    └── feat/               # Inactive features
        └── wontfix-feat-123.md

Configuration (llmail.yaml)

# Define available types and states
types_list:
  - bug
  - feat
  - spec

active_states:
  - working  # Creates _working folder
  - blocked  # Creates _blocked folder

inactive_reasons:
  - fixed
  - wontfix
  - duplicate

Quick Start

npm install llmail-core
import { LLMail } from 'llmail-core';

const llmail = new LLMail();

// Initialize with default configuration
await llmail.init();

// Create a new issue with metadata
const id = await llmail.new('bug', {
  content: 'This bug sucks.\n\nWe should fix it.'
});

// Create a new issue with metadata
const id = await llmail.new('bug', {
  frontmatter: {
    title: 'Critical Login Bug',
    severity: 'high',
    assignee: 'alice'
  }
  content: 'This bug sucks.\n\nWe should fix it.'
});

// Create a new inactive issue with metadata
const id = await llmail.new('bug', {
  active: false,
  content: 'We fixed this but it could come back if we do the dumb thing again, so recording this for future ref.'
});

// Mark it as done
await llmail.done(id);

// Mark it as done and add a state with more detail
await llmail.done(id, {
  state: 'fixed'
});

// Mark it as done with additional custom metadata
await llmail.done(id, {
  state: 'fixed',
  frontmatter: {
    resolution: 'Fixed in PR #123'
  }
});

// Move it to a different type and state
await llmail.mv(id, { type: 'test', state: 'working' });

// Move it to being active and change the state
await llmail.mv(id, { active: true, state: 'failing' });

// Get frontmatter data as an object
await llmail.getFrontmatter(id);

// Get active status of a given ID
await llmail.getFrontmatter(id).active;

// Get the state of a given ID
await llmail.getFrontmatter(id).state;

// Get any frontmatter field of a given ID
await llmail.getFrontmatter(id).myfrontmatterfield;

// Get the (non-frontmatter) contents of a given file
await llmail.getContent(id);

// Set the contents of a file -- overrides current content
await llmail.setContent(id, content);

// Structured Updates
// Add timestamped pseudo-xml-wrapped content
// metadata will be added as xml attributes
await llmail.appendUpdate({
    id, 
    content: "This content is wrapped in <Update> XML tags}, 
    metadata: {
        author: "Hank",
        saying: "Say thanks"
    }
})
// Allow skipping adding timestamp 
await llmail.prependUpdate(id, content, metadata, time=false)

CLI Usage

Initialize System

# Initialize with default configuration
llmail init

# Use specific config file
llmail init --config path/to/config.yaml

Create New Items

# Create in inbox (default for 'issue' type)
llmail new issue --title "New task"

# Create in specific state
llmail new bug --state active --title "Critical bug" --content "Some stuff about this bug"

# Create with frontmatter
llmail new feat --state active --frontmatter '{"priority": 1, "assignee": "alice"} --content "Some info about this feature\n\nIt will be a great thing."'

# Create with content
llmail new doc --content "Initial documentation draft"

Move Items

# Change state
llmail mv abc1 --state blocked

# Change type
llmail mv abc1 --type feat

# Update metadata during move
llmail mv abc1 --state active --frontmatter '{"assignee": "bob"}'

Add a Timestamped XML-wrapped Update to an Item

Prepended to top of content by default

llmail update abc1 --update "Everything broke"

# Change type
llmail update abc1 -u "Everything's fixed"

# Update metadata during update
llmail update abc1 -u "Everything's fixed" --state fixed --frontmatter '{"fixed_reason": "nancy fixed it with science"}'

# Update metadata during update
llmail update abc1 -u "Everything's fixed" --state fixed --frontmatter '{"fixed_reason": "nancy fixed it with science"}'

# Append it to the bottom of the doc instead of the top
llmail update abc1 -u "Everything's fixed" --state fixed --frontmatter '{"fixed_reason": "nancy fixed it with science"}' --append

# Prepend the update to the bottom of the doc
# This is the default from cli but this is a valid option
llmail update abc1 -u "Everything's fixed" --state fixed --frontmatter '{"fixed_reason": "nancy fixed it with science"}' --prepend

Mark Done

# Mark as done with reason
llmail done abc1 --state fixed

# Include resolution details
llmail done abc1 --state fixed --frontmatter '{"resolution": "Fixed in PR #123"}'

Sync Files

# Preview changes
llmail sync --dry-run

# Apply changes
llmail sync

# Force sync without confirmation
llmail sync --force

Read files

Output contents of individual files to stdout

# output the full contents of an individual item
llmail info abc1 

# output just the frontmatter
llmail info abc1 --frontmatter

# output just updates that have been made
llmail info abc1 --updates

# output the last 3 updates that have been made
llmail info abc1 --updates 3

# output just the content
llmail info abc1 --content

# output the first 10 lines of content
llmail info abc1 --content 10

# output the updates then the frontmatter
llmail info abc1 --updates --frontmatter

# output a specific field
llmail info abc1.frontmatterfield

API Reference

Core Actions

Initialize System

await llmail.init(options?: {
  config?: string,  // Path to config file
}): Promise<void>

Create New Item

await llmail.new(type: string, options?: {
  state?: string,      // Initial state (defaults to 'inbox' for issue type)
  frontmatter?: {      // Additional frontmatter fields
    title?: string,
    [key: string]: any
  },
  content?: string,    // Initial content
}): Promise<string>    // Returns file ID

Move Item

await llmail.mv(id: string, options: {
  state?: string,
  type?: string,
  frontmatter?: Record<string, any>  // Additional frontmatter updates
}): Promise<void>

Mark Done

await llmail.done(id: string, options?: {
  state: string,     // Optional inactive state (ie 'fixed' or 'wontfix')
  frontmatter?: Record<string, any>  // Additional frontmatter updates  
}): Promise<void>

Sync Files

await llmail.sync(options?: {
  dryRun?: boolean,    // Just show what would change
  force?: boolean      // Skip confirmations
}): Promise<SyncResult>

Types

interface SyncResult {
  changes: Array<{
    id: string,
    from: string,
    to: string,
    state: string
  }>,
  errors?: Array<{
    id: string,
    error: string
  }>
}

interface FileInfo {
  id: string,
  path: string,
  type?: string,
  state?: string,
  metadata: Record<string, any>
}

Plugin System

LLMail provides a powerful plugin system that allows extending core functionality in several ways:

  1. Custom States & Types

    • Register new active states
    • Add inactive reasons
    • Define custom types
  2. Metadata Validation

    • Add custom validation rules
    • Enforce metadata requirements
    • Validate state transitions
  3. Intent Processing

    • Define custom file organization patterns
    • Control file naming and locations
    • Add metadata processing hooks
  4. Command Line Extensions

    • Add new CLI commands
    • Create command hierarchies
    • Integrate with external tools

Plugin Interface

Plugins implement the Plugin interface:

interface Plugin {
  /** Unique identifier for the plugin */
  name: string;

  /** Plugin priority - higher numbers run first (default: 0) */
  priority?: number;

  /** 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;

  /** Register additional CLI commands */
  registerCommands?: () => PluginCommand[];
}

Example Plugin: Test Triage

Here's a real-world example of a plugin that adds test state tracking:

export class TestTriagePlugin implements Plugin {
  name = 'test-triage';
  priority = 10; // High priority to ensure test patterns run first

  // Add test-specific states
  registerActiveStates() {
    return ['pass'];
  }

  registerInactiveReasons() {
    return ['fail'];
  }

  // Add test type
  registerTypes() {
    return ['test'];
  }

  // Validate test metadata
  validateMetadata(metadata: FrontmatterMetadata) {
    if (metadata.isTest) {
      if (!metadata.testId) {
        return {
          valid: false,
          errors: ['Test files must have a testId']
        };
      }
      if (metadata.state === 'fail' && !metadata.failureReason) {
        return {
          valid: false,
          errors: ['Failed tests must have a failure reason']
        };
      }
    }
    return { valid: true };
  }

  // Register test-specific commands
  registerCommands() {
    return [{
      name: 'test',
      description: 'Test management commands',
      subcommands: [
        {
          name: 'run',
          description: 'Run tests',
          options: [
            { name: 'suite', type: 'string', description: 'Test suite to run' }
          ],
          execute: async (args) => {
            // Run test implementation
          }
        },
        {
          name: 'report',
          description: 'Generate test report',
          execute: async () => {
            // Report generation implementation
          }
        }
      ]
    }];
  }

  // Custom file organization for tests
  registerIntents(existingPatterns: IntentPattern[]) {
    const testPatterns = [
      {
        pattern: {
          metadata: { specificState: 'pass' }
        },
        interpretation: (state) => ({
          targetLocation: {
            dirname: path.join('issues', '_pass'),
            basename: `${state.metadata.testId}-passed.md`
          },
          targetMetadata: {
            ...state.metadata,
            active: true,
            state: 'pass'
          }
        })
      },
      // ... more patterns
    ];
    return [...testPatterns, ...existingPatterns];
  }
}

Using Plugins

  1. Register the Plugin

    const llmail = new LLMail({
      plugins: [new TestTriagePlugin()]
    });
    await llmail.init();
  2. Use Plugin Features

    // Create a test with plugin-specific metadata
    const id = await llmail.new('test', {
      frontmatter: {
        isTest: true,
        testId: 'render-test',
        title: 'UI Rendering Test'
      }
    });
    
    // Use plugin commands
    llmail test run --suite unit
    llmail test report

Plugin Development Guidelines

  1. Initialization

    • Register early in the application lifecycle
    • Use priority to control execution order
    • Initialize any required resources
  2. State Management

    • Keep plugin state isolated
    • Use metadata for persistence
    • Clean up resources when done
  3. Error Handling

    • Validate inputs thoroughly
    • Provide clear error messages
    • Handle failures gracefully
  4. Integration

    • Work with existing llmail patterns
    • Follow file naming conventions
    • Respect metadata constraints
  5. Testing

    • Test all plugin functionality
    • Verify error conditions
    • Check state transitions

Plugin Best Practices

  1. Metadata

    • Use clear, namespaced keys
    • Document all custom fields
    • Validate required fields
  2. File Organization

    • Follow llmail directory patterns
    • Use clear file naming
    • Document structure changes
  3. Commands

    • Use descriptive names
    • Provide helpful descriptions
    • Include usage examples
  4. Performance

    • Minimize filesystem operations
    • Cache when appropriate
    • Handle large datasets

For more examples, see the examples/ directory in the repository.

File Organization Rules

Active Items

  • Live in state-specific directories prefixed with underscore (e.g., issues/_working/)
  • Follow naming pattern: {state}-{type}-{id}.md
  • Must have a state when outside inbox

Inactive Items

  • Live in type directories (e.g., issues/bug/)
  • Follow naming pattern: [{state}-]{type}-{id}.md
  • State is optional but preserved when present

Inbox Items

  • Live in inbox/ directory
  • Follow naming pattern: {type}-{id}.md
  • No state in filename or metadata

Intent System

LLMail uses an intent-based system where Location Follows Metadata: Running 'sync' will move files where their metadata says they should be.

Examples

// Sync aligns locations with metadata
await llmail.sync();  // Moves files to match their metadata state

// Everything else preserved
await llmail.done('abc1', {
  state: 'fixed'   // Only changes state-related fields
});                 // All other metadata preserved

Contributing

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Readme

Keywords

none

Package Sidebar

Install

npm i llmail-core

Weekly Downloads

4

Version

1.0.0

License

none

Unpacked Size

11 MB

Total Files

258

Last publish

Collaborators

  • adamavenir