New Research: Supply Chain Attack on Axios Pulls Malicious Dependency from npm.Details →
Socket
Book a DemoSign in
Socket

llmail-core

Package Overview
Dependencies
Maintainers
0
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

llmail-core

Basic Inbox for LLMs

latest
npmnpm
Version
1.0.0
Version published
Maintainers
0
Created
Source

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:

  • Custom States & Types

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

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

    • Define custom file organization patterns
    • Control file naming and locations
    • Add metadata processing hooks
  • 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

  • Register the Plugin

    const llmail = new LLMail({
      plugins: [new TestTriagePlugin()]
    });
    await llmail.init();
    
  • 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

  • Initialization

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

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

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

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

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

Plugin Best Practices

  • Metadata

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

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

    • Use descriptive names
    • Provide helpful descriptions
    • Include usage examples
  • 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

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

License

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

FAQs

Package last updated on 04 Feb 2025

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts