{"_id":"@armynante/fieldwork-db","name":"@armynante/fieldwork-db","dist-tags":{"latest":"1.8.3"},"versions":{"1.8.3":{"name":"@armynante/fieldwork-db","version":"1.8.3","description":"Rails-style database CLI for PostgreSQL with Bun","type":"module","main":"dist/index.js","bin":{"fw-db":"dist/cli/index.js"},"scripts":{"build":"bun build ./src/index.ts --outdir ./dist --target bun && bun build ./src/cli/index.ts --outdir ./dist/cli --target bun && cp -r ./src/templates ./dist/templates","dev":"bun run ./src/cli/index.ts","test":"bun run --bun vitest","test:watch":"bun run --bun vitest --watch","typecheck":"tsc --noEmit","prepublishOnly":"bun run build"},"dependencies":{"@armynante/fieldwork-core":"workspace:*","ejs":"^3.1.10"},"devDependencies":{"@armynante/fieldwork-test-utils":"workspace:*","@types/bun":"latest","@types/ejs":"^3.1.5","typescript":"^5.3.3","vitest":"^3.0.0"},"peerDependencies":{"hono":"^4.0.0"},"keywords":["database","cli","postgresql","bun","migrations","orm"],"author":{"name":"armynante"},"license":"MIT","gitHead":"b35b3ab33fa2b18877a3cc583e2b94f5ae811a8e","types":"./dist/index.d.ts","_id":"@armynante/fieldwork-db@1.8.3","_nodeVersion":"25.2.1","_npmVersion":"11.6.2","dist":{"integrity":"sha512-LMZl4X2CVKUO328wDPFUDOVOH4g4FumrXY5DjvMpNI35j46iKvfZTqRoZZB6sYkF5qc0IKaJgYq/qAcMVsQFkA==","shasum":"ad579a10742b7fb589cc437a36250d504ee05230","tarball":"https://registry.npmjs.org/@armynante/fieldwork-db/-/fieldwork-db-1.8.3.tgz","fileCount":60,"unpackedSize":594142,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIQCQy0WW5XQTfLrwsA8Vn/zIvT16UdNwC1uzujwXrDkiDwIgMsDEjhrvFvwqEjkYQybX1gbdmhkVJPVtPz25H3FqOB4="}]},"_npmUser":{"name":"armynante","email":"andrew.armenante@gmail.com"},"directories":{},"maintainers":[{"name":"armynante","email":"andrew.armenante@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/fieldwork-db_1.8.3_1767651540370_0.6862057082955817"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-05T22:19:00.255Z","1.8.3":"2026-01-05T22:19:00.553Z","modified":"2026-01-05T22:19:00.802Z"},"maintainers":[{"name":"armynante","email":"andrew.armenante@gmail.com"}],"description":"Rails-style database CLI for PostgreSQL with Bun","keywords":["database","cli","postgresql","bun","migrations","orm"],"author":{"name":"armynante"},"license":"MIT","readme":"# database-cli\n\nA Rails-inspired database management CLI for PostgreSQL with Bun. Designed for **AI agent workflows** with full interactive support for human developers.\n\n## Design Philosophy\n\n### Rails-Inspired Convention Over Configuration\n\nThis CLI draws heavy inspiration from Ruby on Rails' approach to database management:\n\n- **Generators** - `rails generate model` becomes `database-cli generate model`\n- **Migrations** - Simple SQL files with UP/DOWN sections, tracked in a schema_migrations table\n- **Associations** - Rails-style relationships: `belongs_to`, `has_many`, `has_many_through`, etc.\n- **Naming Conventions** - Automatic pluralization, snake_case tables, PascalCase models\n- **Environment Separation** - dev, test, and staging with isolated databases and ports\n\n### AI-First Developer Experience\n\nThis CLI is optimized for AI agent workflows while remaining fully usable by humans:\n\n- **Non-interactive mode** (`--non-interactive`) - Skip all prompts, use sensible defaults\n- **Explicit flags** - Every prompt can be bypassed with a corresponding flag\n- **Predictable output** - Consistent success/error messages for parsing\n- **Exit codes** - `0` for success, `1` for errors\n- **Composable commands** - Chain operations without manual intervention\n\n### Agent Guidance: Fix Issues at the Source\n\n**Important for AI agents:** When you encounter bugs or limitations in this CLI, **fix them in the database-cli package itself** rather than working around them in user code.\n\n- Do NOT monkey-patch generated code\n- Do NOT create wrapper scripts to handle CLI quirks\n- Do NOT add workarounds in the user's project\n- DO fix the issue in `src/` and add tests in `tests/`\n- DO run `bun test` to verify your fix doesn't break existing functionality\n\nThe CLI has 291 tests covering all commands, edge cases, and error handling. Add tests for any new functionality or bug fixes.\n\n## Features\n\n- **Code Generation**: Generate models, schemas, migrations, services, and API routes\n- **Migration Management**: Run, rollback, and track database migrations\n- **Docker Integration**: Built-in Docker Compose support for local PostgreSQL\n- **Multiple Environments**: Support for dev, test, and staging environments\n- **Custom Connections**: Connect to any PostgreSQL instance with custom host/port/database\n- **Type-Safe**: Full TypeScript support with generated type definitions\n- **Agent-Friendly**: Full non-interactive mode for CI/CD and AI agent workflows\n\n## Installation\n\n```bash\nbun add database-cli\n```\n\nOr install globally:\n\n```bash\nbun add -g database-cli\n```\n\n## Quick Start\n\n### 1. Initialize a new project\n\n```bash\ndatabase-cli init\n```\n\nThis creates:\n- `database.config.ts` - Database configuration\n- `.env.database` - Environment variables\n- `docker-compose.yml` - PostgreSQL containers (optional)\n- `db/migrations/` - Migration files directory\n- `db/seeds/` - Seed files directory\n- `src/models/` - Generated models\n- `src/services/` - Generated services\n- `src/api/routes/` - Generated API routes\n\n### 2. Start the database\n\n```bash\ndatabase-cli docker:up\n```\n\n### 3. Create the database\n\n```bash\ndatabase-cli db:create\n```\n\n### 4. Generate a model\n\n```bash\ndatabase-cli generate model User name:string email:string:unique age:integer:nullable\n```\n\nThis creates:\n- `src/models/schema/UserSchema.ts` - Type definitions\n- `src/models/User.ts` - Model class\n- `db/migrations/{timestamp}_create_users.sql` - Migration file\n\n### 5. Run migrations\n\n```bash\ndatabase-cli migrate\n```\n\n## Commands\n\n### Initialization\n\n```bash\ndatabase-cli init [options]\n```\n\nOptions:\n- `--project-name <name>` - Project name (default: directory name)\n- `--environments <envs>` - Comma-separated environments (default: dev,test)\n- `--skip-docker` - Don't generate docker-compose.yml\n- `--skip-core` - Don't generate BaseModel and BaseService\n\n### Code Generation\n\n#### Generate Model\n\n```bash\ndatabase-cli generate model <name> [columns...]\n```\n\nColumn format: `name:type[:modifier]`\n\n**Types:**\n- `string` - TEXT\n- `text` - TEXT\n- `integer` - INTEGER\n- `decimal` - DECIMAL\n- `boolean` - BOOLEAN\n- `timestamp` - TIMESTAMP\n- `references` - INTEGER with foreign key\n- `json` - JSONB\n\n**Modifiers:**\n- `nullable` - Allow NULL values\n- `unique` - Add UNIQUE constraint\n- `default:<value>` - Set default value\n  - For timestamps: `default:now` generates `DEFAULT NOW()`\n  - For UUIDs: `default:uuid` generates `DEFAULT gen_random_uuid()`\n\n**Examples:**\n```bash\n# Basic model\ndatabase-cli generate model User name:string email:string:unique\n\n# With nullable and default\ndatabase-cli generate model Product name:string price:decimal:default:0 active:boolean:default:true\n\n# With timestamp DEFAULT NOW()\ndatabase-cli generate model Article title:string published_at:timestamp:default:now\n\n# With relationships\ndatabase-cli generate model Post title:string body:text --belongs-to User\n\n# Skip migration\ndatabase-cli generate model Comment body:text --skip-migration\n```\n\n**Options:**\n- `--skip-migration` - Don't generate migration file\n- `--skip-timestamps` - Don't add created_at/updated_at columns\n- `--belongs-to <Model>` - Add belongsTo relationship (can be repeated)\n- `--has-many <Model>` - Add hasMany relationship (can be repeated)\n- `--has-many-polymorphic <Model:name>` - Add polymorphic hasMany (e.g., `Vote:voteable`)\n- `--belongs-to-polymorphic <name>` - Add polymorphic belongsTo (creates `{name}_id` and `{name}_type` columns)\n\n**Polymorphic Conflict Detection:**\n\nWhen generating models with `--has-many` or `--has-one`, the CLI automatically checks the database schema for potential conflicts:\n\n- If the child table has polymorphic columns (`{name}_id` + `{name}_type`), suggests using `--has-many-polymorphic`\n- If the child table already has FKs to other parents, warns about potential need for polymorphic association\n\n```bash\n$ database-cli generate model Post --has-many Vote\n\n! Table 'votes' has polymorphic columns: voteable_id + voteable_type\n  Suggestion: Use --has-many-polymorphic \"Vote:voteable\" instead of --has-many Vote\n\n? Continue with generation anyway? [y/N]:\n```\n\n#### Generate Migration\n\n```bash\ndatabase-cli generate migration <name>\n```\n\nCreates an empty migration file for custom SQL:\n```bash\ndatabase-cli generate migration add_role_to_users\n```\n\n#### Generate Service\n\n```bash\ndatabase-cli generate service <ModelName>\n```\n\nCreates a service class with CRUD operations.\n\n#### Generate Route\n\n```bash\ndatabase-cli generate route <ModelName>\n```\n\nCreates Hono API routes for the model.\n\n#### Generate All\n\n```bash\ndatabase-cli generate all <name> [columns...]\n```\n\nGenerates model, schema, migration, service, and route in one command:\n```bash\ndatabase-cli generate all Product name:string price:decimal --belongs-to Category\n```\n\n#### Generate API Client\n\n```bash\ndatabase-cli generate client\n```\n\nCreates a typed API client for frontend use based on existing models.\n\n### Migrations\n\n#### Run Migrations\n\n```bash\ndatabase-cli migrate [options]\n```\n\nOptions:\n- `-e, --environment <env>` - Target environment (default: dev)\n- `--steps <n>` - Only run n migrations\n- `--dry-run` - Preview SQL without executing (shows syntax-highlighted SQL)\n- `-p, --port <port>` - Custom database port\n- `-H, --host <host>` - Custom database host\n- `-d, --database <name>` - Custom database name\n\n#### Rollback Migrations\n\n```bash\ndatabase-cli rollback [options]\n```\n\nOptions:\n- `-e, --environment <env>` - Target environment (default: dev)\n- `--steps <n>` - Rollback n migrations (default: 1)\n- `--all` - Rollback all migrations\n- `--dry-run` - Preview SQL without executing (shows syntax-highlighted SQL)\n- `-f, --force` - Skip confirmation prompt\n\n#### Sync Migration State\n\n```bash\ndatabase-cli migrate:sync [options]\n```\n\nSyncs migration tracking with existing database tables. Use this when tables exist in the database but migrations aren't tracked (e.g., tables created manually or by another process).\n\nOptions:\n- `-e, --environment <env>` - Target environment (default: dev)\n- `--dry-run` - Preview what would happen without making changes\n- `--non-interactive` - Skip confirmation prompt (for CI/automation)\n- `-p, --port <port>` - Custom database port\n- `-d, --database <name>` - Custom database name\n\n**Example scenario:**\n```bash\n# Tables exist but migrate fails with \"relation already exists\"\ndatabase-cli migrate\n# PostgresError: relation \"users\" already exists\n\n# Use migrate:sync to fix the state\ndatabase-cli migrate:sync --dry-run  # Preview first\ndatabase-cli migrate:sync            # Sync and run remaining migrations\n```\n\nThe command will:\n1. Mark migrations as \"applied\" if their target tables already exist\n2. Run migrations for tables that don't exist\n3. Warn about ambiguous cases (some tables exist, some don't)\n\n#### View Migration Status\n\n```bash\ndatabase-cli migrate:status [options]\n```\n\nShows which migrations have been applied and which are pending.\n\n### Database Management\n\n#### Create Database\n\n```bash\ndatabase-cli db:create [-e environment]\n```\n\n#### Drop Database\n\n```bash\ndatabase-cli db:drop [-e environment] [-f]\n```\n\n#### Reset Database\n\n```bash\ndatabase-cli db:reset [-e environment] [-f]\n```\n\nDrops the database, recreates it, and runs all migrations.\n\n#### Open Console\n\n```bash\ndatabase-cli console [-e environment]\n```\n\nOpens an interactive psql shell connected to the database.\n\n### Schema Introspection\n\nView database schema like Rails' `db/schema.rb`:\n\n```bash\n# View all tables\ndatabase-cli schema\n\n# View specific table\ndatabase-cli schema --table users\n\n# Show with indexes\ndatabase-cli schema --show-indexes\n\n# Show only polymorphic relationships\ndatabase-cli schema --polymorphic\n\n# Export as JSON (useful for AI agents)\ndatabase-cli schema --json\n```\n\nOptions:\n- `--table <name>` - View single table schema\n- `--show-fks` - Show foreign key references (default: true)\n- `--show-indexes` - Show table indexes\n- `--polymorphic` - Show only polymorphic relationships\n- `--json` - Output schema as JSON\n\n**Example Output:**\n```\nDatabase Schema (dev)\n────────────────────────────────────────────────────────────────\n\nTable: users\n  id            SERIAL       PRIMARY KEY\n  email         TEXT         NOT NULL, UNIQUE\n  name          TEXT\n  created_at    TIMESTAMP    DEFAULT NOW()\n\nTable: posts\n  id            SERIAL       PRIMARY KEY\n  title         TEXT         NOT NULL\n  user_id       INTEGER      -> users(id)\n\nTable: votes\n  id            SERIAL       PRIMARY KEY\n  voteable_id   INTEGER\n  voteable_type TEXT         <- polymorphic\n  user_id       INTEGER      -> users(id)\n\nPolymorphic Relationships:\n  * voteable (voteable_id + voteable_type) on votes\n```\n\n### Docker Management\n\n#### Start Containers\n\n```bash\ndatabase-cli docker:up [-e environment]\n```\n\nStarts PostgreSQL containers and waits for them to be ready.\n\n#### Stop Containers\n\n```bash\ndatabase-cli docker:down [-e environment]\n```\n\n#### Container Status\n\n```bash\ndatabase-cli docker:status\n```\n\n### Seeding\n\n```bash\ndatabase-cli seed [options]\n```\n\nOptions:\n- `-e, --environment <env>` - Target environment\n- `--file <name>` - Run specific seed file\n\nSeed files should be placed in `db/seeds/` and export a default function or `seed` function.\n\n## Custom Connection Options\n\nAll database commands support custom connection options:\n\n```bash\n# Use custom port\ndatabase-cli migrate -p 15432\n\n# Use custom host\ndatabase-cli migrate -H db.example.com\n\n# Use custom database name\ndatabase-cli migrate -d my_custom_db\n\n# Combine options\ndatabase-cli db:create -p 5555 -H localhost -d myapp_production\n```\n\n## Environment Configuration\n\n### Environment Variables\n\nThe CLI respects these environment variables:\n\n```bash\n# Default connection\nDATABASE_URL=postgres://user:pass@host:port/database\n\n# Environment-specific (override defaults)\nDATABASE_URL_DEV=postgres://...\nDATABASE_URL_TEST=postgres://...\nDATABASE_URL_STAGING=postgres://...\n\n# Individual components\nDB_HOST=localhost\nDB_HOST_DEV=localhost\nDB_PORT=5432\nDB_PORT_TEST=5433\nDB_USER=postgres\nDB_PASSWORD=postgres\nDB_NAME=myapp_dev\n```\n\n### Default Ports by Environment\n\n- **dev**: 5432\n- **test**: 5433\n- **staging**: 5434\n\n## Migration File Format\n\nMigrations use a simple SQL format with UP and DOWN sections:\n\n```sql\n-- UP\nCREATE TABLE users (\n  id SERIAL PRIMARY KEY,\n  name TEXT,\n  email TEXT UNIQUE,\n  created_at TIMESTAMP,\n  updated_at TIMESTAMP\n);\n\n-- DOWN\nDROP TABLE users;\n```\n\n## Generated Code Examples\n\n### Schema (UserSchema.ts)\n\n```typescript\nimport type { ColumnDefinition, SchemaDefinition } from \"database-cli\";\n\nexport interface User {\n  id: number;\n  name: string;\n  email: string;\n  created_at: string;\n  updated_at: string;\n}\n\nexport type UserCreate = Omit<User, \"id\" | \"created_at\" | \"updated_at\">;\nexport type UserUpdate = Partial<UserCreate>;\n\nexport const UserSchema: SchemaDefinition = {\n  tableName: \"users\",\n  columns: {\n    id: { type: \"SERIAL\", primaryKey: true },\n    name: { type: \"TEXT\" },\n    email: { type: \"TEXT\", unique: true },\n    created_at: { type: \"TIMESTAMP\" },\n    updated_at: { type: \"TIMESTAMP\" },\n  },\n};\n```\n\n### Service (UserService.ts)\n\n```typescript\nimport { BaseService } from \"./BaseService\";\nimport type { User, UserCreate, UserUpdate } from \"../models/schema/UserSchema\";\n\nexport class UserService extends BaseService<User, UserCreate, UserUpdate> {\n  constructor(sql: any) {\n    super(sql, \"users\");\n  }\n}\n```\n\n### Route (users.ts)\n\n```typescript\nimport { Hono } from \"hono\";\nimport { UserService } from \"../../services/UserService\";\n\nconst app = new Hono();\n\napp.get(\"/\", async (c) => {\n  const service = new UserService(c.get(\"sql\"));\n  const users = await service.findAll();\n  return c.json(users);\n});\n\napp.get(\"/:id\", async (c) => {\n  const service = new UserService(c.get(\"sql\"));\n  const user = await service.findById(parseInt(c.req.param(\"id\")));\n  if (!user) return c.json({ error: \"Not found\" }, 404);\n  return c.json(user);\n});\n\napp.post(\"/\", async (c) => {\n  const service = new UserService(c.get(\"sql\"));\n  const data = await c.req.json();\n  const user = await service.create(data);\n  return c.json(user, 201);\n});\n\napp.put(\"/:id\", async (c) => {\n  const service = new UserService(c.get(\"sql\"));\n  const data = await c.req.json();\n  const user = await service.update(parseInt(c.req.param(\"id\")), data);\n  return c.json(user);\n});\n\napp.delete(\"/:id\", async (c) => {\n  const service = new UserService(c.get(\"sql\"));\n  await service.delete(parseInt(c.req.param(\"id\")));\n  return c.json({ success: true });\n});\n\nexport default app;\n```\n\n## Programmatic Usage\n\nThe CLI can also be used as a library:\n\n```typescript\nimport {\n  createDatabaseService,\n  createMigrationService,\n  createSchemaService,\n  GeneratorService,\n  DockerService,\n} from \"database-cli\";\n\n// Create services\nconst dbService = createDatabaseService(process.cwd(), \"dev\");\nconst migrationService = createMigrationService(process.cwd(), \"dev\");\nconst schemaService = createSchemaService(dbService);\nconst generatorService = new GeneratorService(process.cwd());\nconst dockerService = new DockerService(process.cwd());\n\n// Use custom connection\nconst customDbService = createDatabaseService(process.cwd(), \"dev\", {\n  port: 15432,\n  host: \"db.example.com\",\n  database: \"custom_db\",\n});\n\n// Database operations\nawait dbService.create();\nawait dbService.testConnection();\nconst tables = await dbService.listTables();\n\n// Schema introspection\nconst schema = await schemaService.getFullSchema();\nconst tableDetails = await schemaService.getTableSchema(\"users\");\nconst polymorphic = await schemaService.getAllPolymorphicPatterns();\nconst conflict = await schemaService.analyzeRelationConflict(\"Post\", \"Vote\", \"hasMany\");\n\n// Migration operations\nawait migrationService.up();\nawait migrationService.down(1);\nconst status = await migrationService.status();\n\n// Code generation\nconst definition = generatorService.parseModelDefinition({\n  name: \"Product\",\n  columns: [\"name:string\", \"price:decimal\"],\n});\nawait generatorService.generateAll({ name: \"Product\", columns: [\"name:string\"] });\n\n// Docker operations\nawait dockerService.up(\"dev\");\nawait dockerService.waitForReady(\"dev\");\nawait dockerService.status();\nawait dockerService.cleanup();\n```\n\n## Type Handling\n\n### Decimal/Numeric Columns Return Strings\n\nDECIMAL and NUMERIC columns are returned as strings in JSON responses to preserve precision:\n\n```json\n{\n  \"amount\": \"120000.50\",\n  \"rate\": \"0.05\"\n}\n```\n\nThis is intentional behavior to prevent floating-point precision loss. To convert in application code:\n\n```typescript\nconst amount = parseFloat(record.amount);\n// or for precise calculations:\nconst precise = new Decimal(record.amount);\n```\n\n### Timestamp Format\n\nAll timestamps are returned as ISO 8601 strings:\n\n```json\n{\n  \"created_at\": \"2024-12-15T00:51:21.914Z\"\n}\n```\n\n### Column Type Reference\n\n| CLI Type | PostgreSQL Type | TypeScript Type | Notes |\n|----------|-----------------|-----------------|-------|\n| `string` | TEXT | `string` | |\n| `text` | TEXT | `string` | |\n| `integer` | INTEGER | `number` | |\n| `bigint` | BIGINT | `number` | Large integers (v1.4.0) |\n| `smallint` | SMALLINT | `number` | Small integers (v1.4.0) |\n| `decimal` | DECIMAL | `string` | Precision preserved |\n| `boolean` | BOOLEAN | `boolean` | |\n| `timestamp` | TIMESTAMP | `string` | ISO 8601 format. Use `:default:now` for NOW() |\n| `date` | DATE | `string` | Date only (v1.4.0) |\n| `time` | TIME | `string` | Time only (v1.4.0) |\n| `uuid` | UUID | `string` | Use `:default:uuid` for gen_random_uuid() (v1.4.0) |\n| `money` | NUMERIC(10,2) | `number` | Fixed precision currency (v1.4.0) |\n| `text[]` | TEXT[] | `string[]` | Text array (v1.4.0) |\n| `json` | JSONB | `Record<string, unknown>` | |\n| `references` | INTEGER | `number` | With foreign key |\n| `enum[val1,val2,...]` | PostgreSQL ENUM | Union type | Creates named type |\n\n## Naming Conventions\n\nThe CLI follows Rails-style naming conventions:\n\n| Context | Convention | Example |\n|---------|------------|---------|\n| Model names | PascalCase | `JobTitle`, `TimeOffRequest` |\n| Table names | snake_case plural | `job_titles`, `time_off_requests` |\n| Route files | snake_case plural | `job_titles.ts`, `time_off_requests.ts` |\n| URL paths | kebab-case plural | `/job-titles`, `/time-off-requests` |\n| Foreign keys | snake_case_id | `job_title_id`, `user_id` |\n| Relation names | camelCase | `jobTitle`, `timeOffRequests` |\n\n## Advanced Relation Options\n\n### Inverse Relations (`--inverse`)\n\nWhen using `--belongs-to`, add `--inverse` to automatically add the inverse `hasMany` relation on the parent model:\n\n```bash\ndatabase-cli generate model Post title:string --belongs-to User --inverse\n```\n\nThis creates:\n- `PostSchema.ts` with `user: belongsTo(User)`\n- Updates `UserSchema.ts` with `posts: hasMany(Post)`\n\n### Self-Referential Relations (`--belongs-to-self`)\n\nFor hierarchical data (like categories with subcategories or employees with managers):\n\n```bash\ndatabase-cli generate model Department name:string --belongs-to-self parent\n```\n\nThis creates:\n- `parent_id` nullable foreign key referencing `departments(id)`\n- `parent: belongsTo(Department)` relation\n\n### Scoped/Nested Routes (`--scoped-to`)\n\nGenerate routes nested under a parent resource:\n\n```bash\ndatabase-cli generate route Employment --scoped-to Employee\n```\n\nGenerates routes like:\n- `GET /employees/:employeeId/employments`\n- `POST /employees/:employeeId/employments`\n- `PATCH /employees/:employeeId/employments/:id`\n\n### Workflow Action Routes (`--workflow`)\n\nGenerate action routes for workflow state changes:\n\n```bash\ndatabase-cli generate route TimeOffRequest --workflow approve,deny,cancel\n```\n\nGenerates:\n- Standard CRUD routes plus:\n- `POST /time-off-requests/:id/approve`\n- `POST /time-off-requests/:id/deny`\n- `POST /time-off-requests/:id/cancel`\n\n## Advanced Features (v1.3.0)\n\n### ENUM Type Support\n\nCreate PostgreSQL ENUM columns with automatic type generation:\n\n```bash\n# Basic enum\ndatabase-cli generate model Task status:enum[pending,active,completed]\n\n# Enum with default value\ndatabase-cli generate model Task status:enum[pending,active,completed]:default:pending\n\n# Multiple enums in one model\ndatabase-cli generate model Document status:enum[draft,published,archived] visibility:enum[public,private]\n```\n\nThis generates:\n- `CREATE TYPE tasks_status_enum AS ENUM ('pending', 'active', 'completed')` in migration\n- TypeScript union type: `status: 'pending' | 'active' | 'completed'`\n- Proper `DROP TYPE IF EXISTS` in down migration\n\n### Custom Foreign Key Names (`--belongs-to Model:alias`)\n\nUse aliases when you need multiple foreign keys to the same table:\n\n```bash\n# Multiple FKs to same table\ndatabase-cli generate model Post --belongs-to User:author --belongs-to User:editor\n\n# Creates: author_id and editor_id columns both referencing users(id)\n```\n\nThe alias becomes the FK column name (`author` → `author_id`) while still referencing the correct table.\n\n### Nullable Foreign Keys (`--belongs-to Model:nullable`)\n\nCreate optional relationships with `ON DELETE SET NULL`:\n\n```bash\n# Optional relationship\ndatabase-cli generate model Post --belongs-to User:removed_by:nullable\n\n# Creates: removed_by_id INTEGER (nullable) with ON DELETE SET NULL\n```\n\nNullable FKs:\n- Allow NULL values in the FK column\n- Use `ON DELETE SET NULL` instead of `ON DELETE CASCADE`\n- Generate optional TypeScript types (`removed_by_id?: number`)\n\n### Composite Primary Keys (`--composite-pk`, `--skip-id`)\n\nCreate join tables or tables with composite primary keys:\n\n```bash\n# Join table with composite PK\ndatabase-cli generate model CommunityMembership role:string \\\n  --belongs-to Community --belongs-to User \\\n  --composite-pk community_id,user_id --skip-id\n```\n\nOptions:\n- `--composite-pk col1,col2,...` - Specify columns for composite primary key\n- `--skip-id` - Skip auto-generated `id SERIAL PRIMARY KEY` column\n\nThis generates:\n```sql\nCREATE TABLE community_memberships (\n  community_id INTEGER NOT NULL REFERENCES communities(id),\n  user_id INTEGER NOT NULL REFERENCES users(id),\n  role TEXT,\n  PRIMARY KEY (community_id, user_id)\n);\n```\n\n## Testing\n\n### Test Philosophy\n\nTests verify that the CLI works exactly as users (both human and AI) would invoke it:\n\n1. **Real subprocess execution** - Commands run via `Bun.spawn()`, not direct function calls\n2. **Isolated environments** - Each test gets its own temp directory and database\n3. **Real database operations** - Docker containers with actual PostgreSQL\n4. **Dynamic code verification** - Generated models are imported and executed to verify they work\n5. **Comprehensive coverage** - 291 tests covering all commands, edge cases, and error handling\n\n### Running Tests\n\nTests are orchestrated via root vitest.config.ts.\n\n```bash\n# Run all fw-db tests\nbun run test\n\n# Run from root with project filter\nbun run --bun vitest run --project fw-db\n\n# Run specific test file\nbun run --bun vitest run tests/cli/init.cli.test.ts\n\n# Run with pattern match\nbun run --bun vitest run -t \"workflow\"\n```\n\n### Test Coverage\n\n| Category | Tests | Description |\n|----------|-------|-------------|\n| CLI Integration | 175 | All commands via subprocess |\n| Service Unit | 116 | DatabaseService, MigrationService, GeneratorService, etc. |\n| **Total** | **291** | |\n\nSee `tests/cli/README.md` for detailed test documentation and patterns.\n\n## Project Structure\n\n```\ndatabase-cli/\n├── src/\n│   ├── index.ts              # Public API exports\n│   ├── types.ts              # TypeScript definitions\n│   ├── cli/\n│   │   ├── index.ts          # CLI entry point\n│   │   ├── output.ts         # Console output utilities\n│   │   ├── prompt.ts         # Interactive prompts\n│   │   └── commands/         # Command implementations\n│   ├── services/\n│   │   ├── DatabaseService.ts    # Database lifecycle & introspection\n│   │   ├── DockerService.ts      # Docker Compose management\n│   │   ├── FileService.ts        # File operations\n│   │   ├── GeneratorService.ts   # Code generation\n│   │   ├── MigrationService.ts   # Migration management\n│   │   ├── SchemaService.ts      # Schema introspection & conflict detection\n│   │   └── TemplateService.ts    # EJS template processing\n│   ├── utils/\n│   │   ├── naming.ts         # String transformations\n│   │   ├── sql-types.ts      # Type mapping\n│   │   └── timestamp.ts      # Migration timestamps\n│   └── templates/            # EJS templates for code generation\n├── tests/\n│   ├── naming.test.ts\n│   ├── sql-types.test.ts\n│   ├── timestamp.test.ts\n│   └── integration.test.ts\n├── package.json\n└── tsconfig.json\n```\n\n## Requirements\n\n- Bun >= 1.0.0\n- PostgreSQL >= 14 (for local development)\n- Docker (optional, for docker:* commands)\n- psql (optional, for console command)\n\n## Publishing\n\nFrom the repository root:\n\n```bash\nbun run publish:database\n```\n\nOr directly:\n\n```bash\n./scripts/codeartifact-publish.sh Modules/database-cli\n```\n\n## License\n\nMIT\n","readmeFilename":"README.md","_rev":"1-e438024b5321fba6ece8296ae38554c4"}