Metadata-Version: 2.4
Name: lucid-framework
Version: 0.1.0
Summary: A Python framework that tells you what to do next.
Project-URL: Homepage, https://github.com/sharik709/lucid-framework
Project-URL: Documentation, https://github.com/sharik709/lucid-framework#readme
Project-URL: Repository, https://github.com/sharik709/lucid-framework
Project-URL: Issues, https://github.com/sharik709/lucid-framework/issues
Author-email: Sharik Shaikh <shaikhsharik709@gmail.com>
License-Expression: MIT
Keywords: config,convention,dependency-injection,events,framework,ioc,pipeline,service-provider
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: lucid-config>=0.1.0
Requires-Dist: lucid-container>=0.1.0
Requires-Dist: lucid-events>=0.1.0
Requires-Dist: lucid-pipeline>=0.1.0
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1; extra == 'dev'
Description-Content-Type: text/markdown

# Lucid

**A Python framework that tells you what to do next.**

Django gives you an ORM and leaves you to figure out the rest. Flask gives you a route decorator and wishes you luck. FastAPI gives you type hints and a prayer. Every Python project ends up as a custom framework anyway — you just spend the first two weeks building it instead of building your product.

Lucid is the missing opinion layer. One install, one boot sequence, and you get dependency injection, configuration, event-driven architecture, and clean pipelines — all wired together and ready to go. You always know the next step because there's a preferred way to do everything.

```bash
pip install lucid-framework
```

[![PyPI version](https://badge.fury.io/py/lucid-framework.svg)](https://pypi.org/project/lucid-framework/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)

---

## 30 Seconds to Running

```python
from lucid import Application

app = Application()

# Load config from .env and defaults
app.configure({
    "app": {"name": "my-project", "debug": True},
    "cache": {"driver": "memory"},
})

# Register your services
app.container.singleton(UserRepository, PostgresUserRepository)
app.container.singleton(PaymentGateway, StripeGateway)

# Register event listeners
app.events.listen(OrderCompleted, SendConfirmationEmail)
app.events.listen(OrderCompleted, UpdateInventory)

# Boot — everything wires itself
app.boot()

# Build any service — the entire dependency tree resolves automatically
service = app.make(OrderService)
service.process(order)
```

No setup scripts. No configuration classes that inherit from other configuration classes. No `settings.py` with 200 lines of `os.environ.get()`. You describe what you want, Lucid builds it.

---

## What's Inside

One install gives you the full core:

```
lucid-framework
├── lucid-container    →  Dependency injection with autowiring
├── lucid-config       →  Cascading config with .env support and type casting
├── lucid-events       →  Event dispatcher with typed events and prioritized listeners
└── lucid-pipeline     →  Multi-step data processing chains
```

Each package works standalone. The framework ties them into a single coherent application lifecycle.

---

## The Application

The `Application` class is the entry point. It creates the container, loads config, registers the event dispatcher, and boots your service providers — in the right order, every time.

### Creating an application

```python
from lucid import Application

app = Application()
```

This sets up:

- A `Container` instance with the application itself bound as `"app"`
- A `Config` instance bound as `ConfigContract` and aliased to `"config"`
- A `Dispatcher` instance bound as `DispatcherContract` and aliased to `"events"`

### `app.configure(defaults, env_path=".env")`

Loads configuration from defaults, `.env` files, and environment variables — in the right priority order.

```python
app.configure(
    defaults={
        "app": {
            "name": "my-project",
            "debug": False,
            "env": "production",
            "secret_key": None,
        },
        "database": {
            "host": "localhost",
            "port": 5432,
            "name": "mydb",
        },
        "cache": {"driver": "memory"},
        "mail": {"driver": "log"},
    },
    env_path=".env",
)
```

This does, in order:

1. Loads `defaults` as the base config.
2. Loads `.env` if it exists.
3. Loads `.env.local` if it exists (personal overrides, gitignored).
4. Loads real environment variables (highest file-based priority).

After this, `app.config` is ready.

### `app.config`

Shorthand for the config instance. Dot-notation access, type casting, the works.

```python
app.config.get("app.name")             # "my-project"
app.config.boolean("app.debug")        # False
app.config.integer("database.port")    # 5432
app.config.get("app.env")              # "production" (or APP_ENV from environment)
```

### `app.container`

Direct access to the container for binding services.

```python
app.container.singleton(CacheContract, RedisCache)
app.container.bind(ReportGenerator, PDFReportGenerator)
app.container.instance("stripe_key", "sk_live_...")
```

### `app.events`

Direct access to the event dispatcher.

```python
app.events.listen(UserRegistered, SendWelcomeEmail)
app.events.listen(UserRegistered, CreateDefaultSettings, priority=10)

@app.events.listen(OrderCompleted)
def log_order(event):
    print(f"Order {event.order_id} completed")
```

### `app.make(abstract)`

Shorthand for `app.container.make()`. Resolves any class or binding from the container.

```python
service = app.make(OrderService)
config = app.make("config")
events = app.make("events")
```

### `app.boot()`

Finalizes the application. Calls `boot()` on all registered service providers, freezes config (optional), and dispatches `AppBooted` event.

```python
app.boot()
```

After boot:

- All service providers have registered and booted.
- The container is fully wired.
- `AppBooted` event has fired.
- The application is ready to handle requests/tasks.

### `app.is_booted`

Property that returns `True` after `boot()` has been called.

---

## Service Providers

Service providers are how you organize your application's bindings and boot logic. Each provider is responsible for one subsystem.

### Writing a provider

```python
from lucid import ServiceProvider

class DatabaseServiceProvider(ServiceProvider):

    def register(self):
        """Bind things into the container. No resolving here."""
        self.app.container.singleton(DatabaseContract, lambda c: PostgresDatabase(
            host=c.make("config").get("database.host"),
            port=c.make("config").integer("database.port"),
            name=c.make("config").get("database.name"),
        ))

    def boot(self):
        """All providers have registered. Safe to resolve and interact."""
        db = self.app.make(DatabaseContract)
        db.connect()
```

### Registering providers

```python
app = Application()
app.configure(defaults)

# Register providers — register() called immediately on each
app.register(DatabaseServiceProvider)
app.register(CacheServiceProvider)
app.register(MailServiceProvider)

# Boot — boot() called on all providers in order
app.boot()
```

### Provider lifecycle

```
app.register(P)  →  P(app) instantiated  →  P.register() called
app.register(Q)  →  Q(app) instantiated  →  Q.register() called
app.register(R)  →  R(app) instantiated  →  R.register() called
app.boot()        →  P.boot() → Q.boot() → R.boot() → AppBooted dispatched
```

`register()` runs immediately when you call `app.register()`. All bindings are available by the time `boot()` runs. This means providers can depend on each other's bindings during `boot()`.

---

## Application Events

The framework fires lifecycle events you can hook into.

| Event             | When                               | Payload          |
|-------------------|------------------------------------|------------------|
| `AppBooting`      | Just before `boot()` runs providers| `app`            |
| `AppBooted`       | After all providers have booted    | `app`            |

```python
from lucid.events import AppBooted

@app.events.listen(AppBooted)
def on_ready(event):
    print(f"{event.app.config.get('app.name')} is ready!")
```

---

## Using Pipelines

Pipelines are available standalone — no special integration needed. But they shine when combined with the container.

```python
from lucid import Pipeline, Pipe

class ValidateRequest(Pipe):
    def __init__(self, config: ConfigContract):
        self.config = config

    def handle(self, data, next_pipe):
        if not data.get("api_key"):
            return {"error": "Missing API key"}
        if data["api_key"] != self.config.get("app.api_key"):
            return {"error": "Invalid API key"}
        return next_pipe(data)

class NormalizeData(Pipe):
    def handle(self, data, next_pipe):
        data["email"] = data.get("email", "").lower().strip()
        return next_pipe(data)

# Resolve pipe instances through the container — dependencies autowired
result = (
    Pipeline(request_data)
    .through([
        app.make(ValidateRequest),
        NormalizeData(),
        lambda data: {**data, "processed": True},
    ])
    .then(save_to_database)
)
```

---

## Real-World Examples

### API service

```python
from lucid import Application, ServiceProvider

# ── Config ──
defaults = {
    "app": {"name": "order-api", "debug": False, "secret_key": None},
    "database": {"host": "localhost", "port": 5432, "name": "orders"},
    "cache": {"driver": "memory"},
    "mail": {"driver": "log"},
}

# ── Providers ──
class RepositoryProvider(ServiceProvider):
    def register(self):
        self.app.container.singleton(UserRepository, PostgresUserRepository)
        self.app.container.singleton(OrderRepository, PostgresOrderRepository)

class PaymentProvider(ServiceProvider):
    def register(self):
        self.app.container.singleton(PaymentGateway, lambda c: StripeGateway(
            api_key=c.make("config").get("stripe.secret_key"),
        ))

class EventListenerProvider(ServiceProvider):
    def boot(self):
        events = self.app.events
        events.listen(OrderCompleted, SendConfirmationEmail)
        events.listen(OrderCompleted, UpdateInventory, priority=20)
        events.listen(PaymentFailed, NotifySupport)
        events.listen(UserRegistered, SendWelcomeEmail)
        events.listen(UserRegistered, CreateDefaultSettings, priority=10)

# ── Bootstrap ──
app = Application()
app.configure(defaults)

app.register(RepositoryProvider)
app.register(PaymentProvider)
app.register(EventListenerProvider)

app.boot()

# ── Use ──
order_service = app.make(OrderService)
order_service.process(incoming_order)
```

### CLI tool

```python
from lucid import Application

app = Application()
app.configure({
    "app": {"name": "data-migrator"},
    "source_db": {"host": "old-db.internal", "port": 5432},
    "target_db": {"host": "new-db.internal", "port": 5432},
})
app.boot()

migrator = app.make(DataMigrator)
migrator.run()
```

### Testing

```python
def create_test_app(**config_overrides):
    """Create a fresh application with test doubles."""
    app = Application()

    defaults = {
        "app": {"name": "test", "debug": True, "secret_key": "test-secret"},
        "database": {"host": "localhost", "name": "test_db"},
        "mail": {"driver": "log"},
    }
    defaults.update(config_overrides)
    app.configure(defaults)

    # Swap real implementations for test doubles
    app.container.instance(MailerContract, FakeMailer())
    app.container.instance(PaymentGateway, FakePaymentGateway(always_succeeds=True))
    app.container.instance(CacheContract, InMemoryCache())

    app.boot()
    return app


def test_order_completion():
    app = create_test_app()
    service = app.make(OrderService)

    result = service.process(test_order)

    assert result.status == "completed"
    assert app.make(MailerContract).last_sent.subject == "Order Confirmed"


def test_order_with_failed_payment():
    app = create_test_app()
    app.container.instance(PaymentGateway, FakePaymentGateway(always_fails=True))

    service = app.make(OrderService)
    result = service.process(test_order)

    assert result.status == "payment_failed"
```

Every test gets a clean application with isolated state. No global singletons, no monkeypatching, no test ordering issues.

### Integration with web frameworks

Lucid doesn't replace your web framework — it runs alongside it.

**With FastAPI:**

```python
from fastapi import FastAPI, Depends
from lucid import Application

# Bootstrap Lucid
lucid = Application()
lucid.configure(defaults)
lucid.register(DatabaseProvider)
lucid.register(CacheProvider)
lucid.boot()

# FastAPI app
api = FastAPI()

def get_lucid():
    return lucid

@api.post("/orders")
def create_order(data: OrderRequest, app: Application = Depends(get_lucid)):
    service = app.make(OrderService)
    return service.process(data)
```

**With Flask:**

```python
from flask import Flask

flask_app = Flask(__name__)

lucid = Application()
lucid.configure(defaults)
lucid.boot()

@flask_app.route("/orders", methods=["POST"])
def create_order():
    service = lucid.make(OrderService)
    return service.process(request.json)
```

Lucid manages your services, config, and events. The web framework manages HTTP. They compose cleanly without either one taking over.

---

## Directory Conventions

Lucid recommends (but doesn't enforce) this project structure:

```
my-project/
├── app/
│   ├── __init__.py
│   ├── services/              # Business logic
│   │   ├── order_service.py
│   │   ├── user_service.py
│   │   └── payment_service.py
│   ├── repositories/          # Data access
│   │   ├── user_repository.py
│   │   └── order_repository.py
│   ├── events/                # Event classes
│   │   ├── order_events.py
│   │   └── user_events.py
│   ├── listeners/             # Event listeners
│   │   ├── send_welcome_email.py
│   │   └── update_inventory.py
│   ├── contracts/             # ABCs / interfaces
│   │   ├── cache_contract.py
│   │   ├── mailer_contract.py
│   │   └── payment_contract.py
│   └── providers/             # Service providers
│       ├── database_provider.py
│       ├── cache_provider.py
│       └── mail_provider.py
├── config/
│   └── defaults.py            # Default configuration dict
├── .env                       # Environment defaults
├── .env.local                 # Personal overrides (gitignored)
├── bootstrap.py               # Application setup
├── main.py                    # Entry point
└── tests/
    ├── conftest.py            # Test app factory
    └── ...
```

**bootstrap.py:**

```python
from lucid import Application
from config.defaults import defaults
from app.providers.database_provider import DatabaseProvider
from app.providers.cache_provider import CacheProvider
from app.providers.mail_provider import MailProvider

def create_app() -> Application:
    app = Application()
    app.configure(defaults)

    app.register(DatabaseProvider)
    app.register(CacheProvider)
    app.register(MailProvider)

    app.boot()
    return app
```

**main.py:**

```python
from bootstrap import create_app

app = create_app()

# Your application logic starts here
```

This is a convention, not a requirement. Lucid works with any project structure — it's your container, your config, your events. Organize them however you want.

---

## Architecture

### Project Structure

```
lucid-framework/
├── src/
│   └── lucid/
│       ├── __init__.py            # Public API — re-exports everything
│       ├── application.py         # Application class
│       ├── events/
│       │   ├── __init__.py
│       │   ├── app_booting.py     # AppBooting event
│       │   └── app_booted.py      # AppBooted event
│       └── service_provider.py    # ServiceProvider base (re-exported or extended)
├── tests/
│   ├── __init__.py
│   ├── test_application.py        # Application lifecycle
│   ├── test_configure.py          # Config loading through Application
│   ├── test_providers.py          # Provider registration and boot
│   ├── test_events.py             # Lifecycle events
│   ├── test_make.py               # Container resolution through app
│   └── test_integration.py        # Full stack integration tests
├── pyproject.toml
├── README.md
├── LICENSE
└── CHANGELOG.md
```

### Implementation Notes

**The Application class internals:**

```python
from lucid_container import Container
from lucid_config import Config, ConfigContract
from lucid_events import Dispatcher, DispatcherContract

class Application:
    def __init__(self):
        self._container = Container()
        self._config = Config()
        self._dispatcher = Dispatcher(container=self._container)
        self._providers: list[ServiceProvider] = []
        self._booted = False

        # Bind core services
        self._container.instance("app", self)
        self._container.instance(ConfigContract, self._config)
        self._container.alias("config", ConfigContract)
        self._container.instance(DispatcherContract, self._dispatcher)
        self._container.alias("events", DispatcherContract)

    @property
    def container(self) -> Container:
        return self._container

    @property
    def config(self) -> Config:
        return self._config

    @property
    def events(self) -> Dispatcher:
        return self._dispatcher

    @property
    def is_booted(self) -> bool:
        return self._booted

    def configure(self, defaults: dict, env_path: str = ".env"):
        self._config.load_dict(defaults)
        self._config.load_env(env_path)
        self._config.load_env(f"{env_path}.local")
        self._config.load_env_vars()

    def register(self, provider_class: type):
        provider = provider_class(self)
        self._providers.append(provider)
        provider.register()

    def make(self, abstract):
        return self._container.make(abstract)

    def boot(self):
        if self._booted:
            return

        self._dispatcher.dispatch(AppBooting(self))

        for provider in self._providers:
            provider.boot()

        self._booted = True
        self._dispatcher.dispatch(AppBooted(self))
```

**What `configure()` does with missing files:**

If `.env` or `.env.local` doesn't exist, `load_env()` silently skips it (no `FileNotFoundError`). This means `configure()` always works — in development with a `.env` file and in production with only real environment variables.

**Re-exports in `__init__.py`:**

The framework re-exports the most common classes so users only need one import:

```python
# lucid/__init__.py
from lucid.application import Application
from lucid.service_provider import ServiceProvider
from lucid.events.app_booting import AppBooting
from lucid.events.app_booted import AppBooted

# Re-export from sub-packages for convenience
from lucid_container import Container
from lucid_config import Config, ConfigContract
from lucid_events import Event, Dispatcher, DispatcherContract, Listener, AsyncListener, Subscriber
from lucid_pipeline import Pipeline, AsyncPipeline, Pipe, AsyncPipe

__all__ = [
    # Framework
    "Application",
    "ServiceProvider",
    "AppBooting",
    "AppBooted",
    # Container
    "Container",
    # Config
    "Config",
    "ConfigContract",
    # Events
    "Event",
    "Dispatcher",
    "DispatcherContract",
    "Listener",
    "AsyncListener",
    "Subscriber",
    # Pipeline
    "Pipeline",
    "AsyncPipeline",
    "Pipe",
    "AsyncPipe",
]
```

This means users can write `from lucid import Application, Event, Listener, Pipeline` — one import line for everything.

**ServiceProvider base class:**

The framework either re-exports `ServiceProvider` from `lucid_container` or provides its own thin wrapper that adds the `self.app` property:

```python
class ServiceProvider:
    def __init__(self, app: "Application"):
        self.app = app

    def register(self) -> None:
        """Bind things into the container."""
        pass

    def boot(self) -> None:
        """Called after all providers have registered."""
        pass
```

### Public API

```python
from lucid import Application           # The app
from lucid import ServiceProvider        # Base for providers
from lucid import AppBooting, AppBooted  # Lifecycle events

# Everything from sub-packages, re-exported:
from lucid import Container              # DI container
from lucid import Config, ConfigContract  # Configuration
from lucid import Event, Dispatcher, DispatcherContract, Listener, AsyncListener, Subscriber  # Events
from lucid import Pipeline, AsyncPipeline, Pipe, AsyncPipe  # Pipelines
```

---

## pyproject.toml Specification

```toml
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"

[project]
name = "lucid-framework"
version = "0.1.0"
description = "A Python framework that tells you what to do next."
readme = "README.md"
license = "MIT"
requires-python = ">=3.10"
authors = [
    { name = "Sharik Shaikh", email = "shaikhsharik709@gmail.com" },
]
keywords = [
    "framework", "dependency-injection", "config", "events",
    "pipeline", "service-provider", "ioc", "convention",
]
classifiers = [
    "Development Status :: 4 - Beta",
    "Intended Audience :: Developers",
    "License :: OSI Approved :: MIT License",
    "Programming Language :: Python :: 3",
    "Programming Language :: Python :: 3.10",
    "Programming Language :: Python :: 3.11",
    "Programming Language :: Python :: 3.12",
    "Programming Language :: Python :: 3.13",
    "Topic :: Software Development :: Libraries :: Application Frameworks",
    "Topic :: Software Development :: Libraries :: Python Modules",
    "Typing :: Typed",
]
dependencies = [
    "lucid-pipeline>=0.1.0",
    "lucid-container>=0.1.0",
    "lucid-config>=0.1.0",
    "lucid-events>=0.1.0",
]

[project.urls]
Homepage = "https://github.com/sharik709/lucid-framework"
Documentation = "https://github.com/sharik709/lucid-framework#readme"
Repository = "https://github.com/sharik709/lucid-framework"
Issues = "https://github.com/sharik709/lucid-framework/issues"

[tool.pytest.ini_options]
testpaths = ["tests"]
asyncio_mode = "auto"

[tool.mypy]
strict = true

[project.optional-dependencies]
dev = ["pytest>=7.0", "pytest-asyncio>=0.21", "mypy>=1.0", "ruff>=0.1"]
```

---

## Test Cases to Implement

### Application Lifecycle

- `Application()` creates container, config, and dispatcher
- `app.container` returns a Container instance
- `app.config` returns a Config instance
- `app.events` returns a Dispatcher instance
- `app.is_booted` is `False` before boot, `True` after
- `app.boot()` called twice does nothing the second time

### Configure

- `app.configure(defaults)` loads defaults into config
- Config values accessible via `app.config.get()`
- Environment variables override defaults
- Missing `.env` file doesn't raise
- Missing `.env.local` file doesn't raise
- Type casting works after configure (`boolean`, `integer`, etc.)

### Container Access

- `app.make(SomeClass)` resolves from container
- `app.make("config")` returns the config (alias works)
- `app.make("events")` returns the dispatcher (alias works)
- `app.make("app")` returns the application itself
- `app.container.singleton()` bindings work through `app.make()`
- `app.container.bind()` bindings work through `app.make()`

### Service Providers

- `app.register(P)` instantiates P with the app
- `app.register(P)` calls `P.register()` immediately
- Provider's `self.app` is the application instance
- Provider can bind into `self.app.container` during `register()`
- Provider can read from `self.app.config` during `register()`
- `app.boot()` calls `boot()` on all registered providers
- `boot()` is called in registration order
- Provider boot can resolve bindings from other providers
- Provider boot can access the event dispatcher
- Multiple providers register without conflicts

### Lifecycle Events

- `AppBooting` is dispatched at the start of `boot()`
- `AppBooted` is dispatched after all providers have booted
- `AppBooting` event carries the app reference
- `AppBooted` event carries the app reference
- Listeners registered before `boot()` receive the events
- `AppBooting` fires before any provider `boot()` is called
- `AppBooted` fires after all provider `boot()` calls complete

### Re-exports

- `from lucid import Application` works
- `from lucid import ServiceProvider` works
- `from lucid import Container` works
- `from lucid import Config, ConfigContract` works
- `from lucid import Event, Dispatcher, Listener, Subscriber` works
- `from lucid import Pipeline, Pipe` works
- `from lucid import AppBooting, AppBooted` works

### Integration Tests

- Full stack: configure → register providers → boot → make service → use service
- Provider registers binding, another provider resolves it during boot
- Event listener registered in a provider fires when event dispatched
- Config loaded in configure, read by provider during register
- Container autowires a service whose dependencies were bound by providers
- Pipeline used inside a service that was resolved from the container
- Test double swap: instance() overrides a singleton for testing

### Edge Cases

- Application with no providers — boot succeeds
- Application with no config — boot succeeds (empty config)
- Registering a provider after boot raises error (or is silently ignored)
- `make()` before `boot()` works for bindings registered during `register()`
- Provider `register()` raising an exception surfaces clearly
- Provider `boot()` raising an exception surfaces clearly
- Two providers binding the same abstract — last one wins

---

## The Lucid Ecosystem

```
┌──────────────────────────────────────────────────────────┐
│                    lucid-framework                        │
│                                                          │
│   Application  ·  ServiceProvider  ·  Lifecycle Events   │
│                                                          │
├──────────┬──────────┬──────────────┬─────────────────────┤
│ lucid-   │ lucid-   │ lucid-       │ lucid-              │
│ container│ config   │ events       │ pipeline            │
│          │          │              │                     │
│ DI +     │ .env +   │ Typed events │ Multi-step          │
│ autowire │ dot      │ + listeners  │ data chains         │
│          │ notation │ + subscribers│                     │
└──────────┴──────────┴──────────────┴─────────────────────┘
                         ▼
              Feature packages (coming soon)
         lucid-cache  ·  lucid-mail  ·  lucid-queue
```

Each box is an independent PyPI package. Install the framework to get everything, or install only what you need.

---

## Coming Soon

- `lucid-cache` — Multi-driver cache (memory, file, Redis) with a unified API.
- `lucid-mail` — Multi-driver mail (SMTP, Mailgun, SES) with templates and queued sending.
- `lucid-queue` — Background job processing with swappable backends.
- `lucid-cli` — Artisan-style CLI for code generation, migrations, and task scheduling.

---

## License

MIT License. See [LICENSE](LICENSE) for details.
