Contributing
Development Setup
Section titled “Development Setup”# Clone and installgit clone https://github.com/pcutter1/sigil.gitcd sigilbun install
# Link globally (makes `sigil` CLI and `import "sigil"` work)bun link
# Type checkbun run typecheck
# Run testsbun testProject Structure
Section titled “Project Structure”sigil/├── src/│ ├── cli/ # CLI layer (bunli)│ │ ├── index.ts # Entry point, command registration│ │ └── commands/│ │ ├── up.ts # sigil up [name]│ │ ├── down.ts # sigil down [name]│ │ ├── status.ts # sigil status [name]│ │ └── init.ts # sigil init (stub)│ ├── sdk/ # Public API surface│ │ ├── index.ts # Re-exports everything│ │ └── environment.ts # Environment class│ ├── core/ # Internal orchestration│ │ ├── index.ts│ │ ├── orchestrator.ts # Entity lifecycle│ │ ├── container.ts # Docker wrapper│ │ └── state.ts # Instance state tracking│ └── entities/ # Entity/Interface system│ ├── entity.ts # Base Entity class│ ├── interface.ts # Base EntityInterface class│ └── primitives/│ ├── index.ts│ ├── postgres.ts # Docker-based Postgres│ ├── service.ts # Process-based service│ ├── browser.ts # Browser entity (stub)│ └── api.ts # API service entity (stub)├── docs/ # This documentation (Starlight)├── examples/│ └── shopping_list/ # Full-stack example app├── package.json├── tsconfig.json└── bunli.config.tsAdding a New Entity
Section titled “Adding a New Entity”To add a new entity primitive (e.g., Redis, MySQL):
1. Create the entity file
Section titled “1. Create the entity file”import { Entity, type EntityConfig } from "../entity";import { EntityInterface, type InterfaceConfig } from "../interface";
export interface RedisConfig extends EntityConfig { port?: number; version?: string;}
export class RedisInterface extends EntityInterface { constructor(port: number) { super({ protocol: "tcp", port }); }
getConnectionString(): string { return `redis://${this.host}:${this.port}`; }
async isReady(): Promise<boolean> { // Implement readiness check return true; }}
export class Redis extends Entity<RedisConfig> { private _redisInterface: RedisInterface;
constructor(name: string, config?: Partial<Omit<RedisConfig, "name">>) { const port = config?.port ?? 6379; super({ name, port, ...config }); this._redisInterface = new RedisInterface(port); this.addInterface(this._redisInterface); }
get connectionString(): string { return this._redisInterface.getConnectionString(); }
async start(): Promise<void> { this._status = "starting"; // Use ContainerManager to create/start Docker container this._status = "running"; }
async stop(): Promise<void> { this._status = "stopping"; // Stop and remove Docker container this._status = "stopped"; }}2. Export from primitives index
Section titled “2. Export from primitives index”export { Redis, RedisInterface } from "./redis";3. Export from SDK
Section titled “3. Export from SDK”export { Redis, RedisInterface } from "../entities/primitives/redis";4. Test it
Section titled “4. Test it”Create a simple config that uses the new entity and run sigil up.
Conventions
Section titled “Conventions”- Entity names should be lowercase and descriptive
- Config interfaces extend
EntityConfigand use optional fields with sensible defaults - Each entity creates its own interfaces in the constructor
start()must set_statusto"starting"then"running"(or"error")stop()must set_statusto"stopping"then"stopped"- Docker containers should be prefixed with
sigil-(e.g.,sigil-redis)