Skip to content

Contributing

Terminal window
# Clone and install
git clone https://github.com/pcutter1/sigil.git
cd sigil
bun install
# Link globally (makes `sigil` CLI and `import "sigil"` work)
bun link
# Type check
bun run typecheck
# Run tests
bun test
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.ts

To add a new entity primitive (e.g., Redis, MySQL):

src/entities/primitives/redis.ts
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";
}
}
src/entities/primitives/index.ts
export { Redis, RedisInterface } from "./redis";
src/sdk/index.ts
export { Redis, RedisInterface } from "../entities/primitives/redis";

Create a simple config that uses the new entity and run sigil up.

  • Entity names should be lowercase and descriptive
  • Config interfaces extend EntityConfig and use optional fields with sensible defaults
  • Each entity creates its own interfaces in the constructor
  • start() must set _status to "starting" then "running" (or "error")
  • stop() must set _status to "stopping" then "stopped"
  • Docker containers should be prefixed with sigil- (e.g., sigil-redis)