feat(marketplace): command consolidation + 8 new plugins (v8.1.0 → v9.0.0) [BREAKING]
Phase 1b: Rename all ~94 commands across 12 plugins to /<noun> <action> sub-command pattern. Git-flow consolidated from 8→5 commands (commit variants absorbed into --push/--merge/--sync flags). Dispatch files, name: frontmatter, and cross-reference updates for all plugins. Phase 2: Design documents for 8 new plugins in docs/designs/. Phase 3: Scaffold 8 new plugins — saas-api-platform, saas-db-migrate, saas-react-platform, saas-test-pilot, data-seed, ops-release-manager, ops-deploy-pipeline, debug-mcp. Each with plugin.json, commands, agents, skills, README, and claude-md-integration. Marketplace grows from 12→20. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
90
plugins/data-seed/skills/faker-patterns.md
Normal file
90
plugins/data-seed/skills/faker-patterns.md
Normal file
@@ -0,0 +1,90 @@
|
||||
---
|
||||
name: faker-patterns
|
||||
description: Realistic data generation patterns using faker providers with locale awareness
|
||||
---
|
||||
|
||||
# Faker Patterns
|
||||
|
||||
## Purpose
|
||||
|
||||
Map schema column types and naming conventions to appropriate faker data generators. This skill ensures generated test data is realistic, locale-aware, and respects type constraints.
|
||||
|
||||
---
|
||||
|
||||
## Column Name to Provider Mapping
|
||||
|
||||
Use column name heuristics to select the most realistic faker provider:
|
||||
|
||||
| Column Name Pattern | Faker Provider | Example Output |
|
||||
|---------------------|---------------|----------------|
|
||||
| `*name`, `first_name` | `faker.name()` / `faker.first_name()` | "Alice Johnson" |
|
||||
| `*last_name`, `surname` | `faker.last_name()` | "Rodriguez" |
|
||||
| `*email` | `faker.email()` | "alice@example.com" |
|
||||
| `*phone*`, `*tel*` | `faker.phone_number()` | "+1-555-0123" |
|
||||
| `*address*`, `*street*` | `faker.street_address()` | "742 Evergreen Terrace" |
|
||||
| `*city` | `faker.city()` | "Toronto" |
|
||||
| `*state*`, `*province*` | `faker.state()` | "Ontario" |
|
||||
| `*country*` | `faker.country()` | "Canada" |
|
||||
| `*zip*`, `*postal*` | `faker.postcode()` | "M5V 2H1" |
|
||||
| `*url*`, `*website*` | `faker.url()` | "https://example.com" |
|
||||
| `*company*`, `*org*` | `faker.company()` | "Acme Corp" |
|
||||
| `*title*`, `*subject*` | `faker.sentence(nb_words=5)` | "Updated quarterly report summary" |
|
||||
| `*description*`, `*bio*`, `*body*` | `faker.paragraph()` | Multi-sentence text |
|
||||
| `*created*`, `*updated*`, `*_at` | `faker.date_time_between(start_date='-2y')` | "2024-06-15T10:30:00" |
|
||||
| `*date*`, `*dob*`, `*birth*` | `faker.date_of_birth(minimum_age=18)` | "1990-03-22" |
|
||||
| `*price*`, `*amount*`, `*cost*` | `faker.pydecimal(min_value=0.01, max_value=9999.99)` | 49.99 |
|
||||
| `*quantity*`, `*count*` | `faker.random_int(min=1, max=100)` | 7 |
|
||||
| `*status*` | Random from enum or `["active", "inactive", "pending"]` | "active" |
|
||||
| `*uuid*`, `*guid*` | `faker.uuid4()` | "550e8400-e29b-41d4-a716-446655440000" |
|
||||
| `*ip*`, `*ip_address*` | `faker.ipv4()` | "192.168.1.42" |
|
||||
| `*color*`, `*colour*` | `faker.hex_color()` | "#3498db" |
|
||||
| `*password*`, `*hash*` | `faker.sha256()` | Hash string (never plaintext) |
|
||||
| `*image*`, `*avatar*`, `*photo*` | `faker.image_url()` | "https://picsum.photos/200" |
|
||||
| `*slug*` | `faker.slug()` | "updated-quarterly-report" |
|
||||
| `*username*`, `*login*` | `faker.user_name()` | "alice_johnson42" |
|
||||
|
||||
## Type Fallback Mapping
|
||||
|
||||
When column name does not match any pattern, fall back to type-based generation:
|
||||
|
||||
| Canonical Type | Generator |
|
||||
|----------------|-----------|
|
||||
| `string` | `faker.pystr(max_chars=max_length)` |
|
||||
| `integer` | `faker.random_int(min=0, max=2147483647)` |
|
||||
| `float` | `faker.pyfloat(min_value=0, max_value=10000)` |
|
||||
| `decimal` | `faker.pydecimal(left_digits=precision-scale, right_digits=scale)` |
|
||||
| `boolean` | `faker.pybool()` |
|
||||
| `datetime` | `faker.date_time_between(start_date='-2y', end_date='now')` |
|
||||
| `date` | `faker.date_between(start_date='-2y', end_date='today')` |
|
||||
| `uuid` | `faker.uuid4()` |
|
||||
| `json` | `{"key": faker.word(), "value": faker.sentence()}` |
|
||||
|
||||
## Locale Support
|
||||
|
||||
Supported locales affect names, addresses, phone formats, and postal codes:
|
||||
|
||||
| Locale | Names | Addresses | Phone | Currency |
|
||||
|--------|-------|-----------|-------|----------|
|
||||
| `en_US` | English names | US addresses | US format | USD |
|
||||
| `en_CA` | English names | Canadian addresses | CA format | CAD |
|
||||
| `en_GB` | English names | UK addresses | UK format | GBP |
|
||||
| `pt_BR` | Portuguese names | Brazilian addresses | BR format | BRL |
|
||||
| `fr_FR` | French names | French addresses | FR format | EUR |
|
||||
| `de_DE` | German names | German addresses | DE format | EUR |
|
||||
| `ja_JP` | Japanese names | Japanese addresses | JP format | JPY |
|
||||
| `es_ES` | Spanish names | Spanish addresses | ES format | EUR |
|
||||
|
||||
Default locale: `en_US`. Override per-profile or per-command with `--locale`.
|
||||
|
||||
## Edge Case Values
|
||||
|
||||
Include at configurable ratio (default 10%):
|
||||
|
||||
| Type | Edge Cases |
|
||||
|------|------------|
|
||||
| `string` | Empty string `""`, max-length string, unicode characters, emoji, SQL special chars `'; DROP TABLE --` |
|
||||
| `integer` | 0, -1, MAX_INT, MIN_INT |
|
||||
| `float` | 0.0, -0.0, very small (0.0001), very large (999999.99) |
|
||||
| `date` | Today, yesterday, epoch (1970-01-01), leap day (2024-02-29) |
|
||||
| `boolean` | null (if nullable) |
|
||||
| `email` | Plus-addressed `user+tag@example.com`, long domain, subdomain email |
|
||||
116
plugins/data-seed/skills/profile-management.md
Normal file
116
plugins/data-seed/skills/profile-management.md
Normal file
@@ -0,0 +1,116 @@
|
||||
---
|
||||
name: profile-management
|
||||
description: Seed profile definitions with row counts, edge case ratios, and custom value overrides
|
||||
---
|
||||
|
||||
# Profile Management
|
||||
|
||||
## Purpose
|
||||
|
||||
Define and manage reusable seed data profiles that control how much data is generated, what edge cases are included, and what custom overrides apply. Profiles enable reproducible, consistent test data across environments.
|
||||
|
||||
---
|
||||
|
||||
## Profile Storage
|
||||
|
||||
Profiles are stored in `seed-profiles.json` in the configured output directory (default: `seeds/` or `fixtures/`).
|
||||
|
||||
## Profile Schema
|
||||
|
||||
```json
|
||||
{
|
||||
"profiles": [
|
||||
{
|
||||
"name": "profile-name",
|
||||
"description": "Human-readable description",
|
||||
"default_rows": 100,
|
||||
"table_overrides": {
|
||||
"table_name": 200
|
||||
},
|
||||
"edge_case_ratio": 0.1,
|
||||
"null_ratio": 0.05,
|
||||
"locale": "en_US",
|
||||
"seed_value": 42,
|
||||
"custom_values": {
|
||||
"table.column": ["value1", "value2", "value3"]
|
||||
},
|
||||
"relationship_density": {
|
||||
"many_to_many": 0.3,
|
||||
"self_ref_max_depth": 3
|
||||
}
|
||||
}
|
||||
],
|
||||
"default_profile": "medium"
|
||||
}
|
||||
```
|
||||
|
||||
## Field Definitions
|
||||
|
||||
| Field | Type | Required | Description |
|
||||
|-------|------|----------|-------------|
|
||||
| `name` | string | Yes | Unique profile identifier (lowercase, hyphens allowed) |
|
||||
| `description` | string | No | What this profile is for |
|
||||
| `default_rows` | integer | Yes | Row count for tables without explicit override |
|
||||
| `table_overrides` | object | No | Per-table row count overrides |
|
||||
| `edge_case_ratio` | float | No | Fraction of rows with edge case values (0.0 to 1.0, default 0.1) |
|
||||
| `null_ratio` | float | No | Fraction of nullable columns set to null (0.0 to 1.0, default 0.05) |
|
||||
| `locale` | string | No | Faker locale for name/address generation (default "en_US") |
|
||||
| `seed_value` | integer | No | Random seed for reproducibility (default: hash of profile name) |
|
||||
| `custom_values` | object | No | Column-specific value pools (table.column -> array of values) |
|
||||
| `relationship_density` | object | No | Controls many-to-many fill ratio and self-referential depth |
|
||||
|
||||
## Built-in Profiles
|
||||
|
||||
### small
|
||||
- `default_rows`: 10
|
||||
- `edge_case_ratio`: 0.0
|
||||
- `null_ratio`: 0.0
|
||||
- Use case: unit tests, schema validation, quick smoke tests
|
||||
- Characteristics: minimal data, no edge cases, all required fields populated
|
||||
|
||||
### medium
|
||||
- `default_rows`: 100
|
||||
- `edge_case_ratio`: 0.1
|
||||
- `null_ratio`: 0.05
|
||||
- Use case: development, manual testing, demo environments
|
||||
- Characteristics: realistic volume, occasional edge cases, some nulls
|
||||
|
||||
### large
|
||||
- `default_rows`: 1000
|
||||
- `edge_case_ratio`: 0.05
|
||||
- `null_ratio`: 0.03
|
||||
- Use case: performance testing, pagination testing, stress testing
|
||||
- Characteristics: high volume, lower edge case ratio to avoid noise
|
||||
|
||||
## Custom Value Overrides
|
||||
|
||||
Override the faker generator for specific columns with a weighted value pool:
|
||||
|
||||
```json
|
||||
{
|
||||
"custom_values": {
|
||||
"users.role": ["user", "user", "user", "admin"],
|
||||
"orders.status": ["completed", "completed", "pending", "cancelled", "refunded"],
|
||||
"products.currency": ["USD"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Values are selected randomly with replacement. Duplicate entries in the array increase that value's probability (e.g., "user" appears 3x = 75% probability).
|
||||
|
||||
## Profile Operations
|
||||
|
||||
### Resolution Order
|
||||
When determining row count for a table:
|
||||
1. Command-line `--rows` flag (highest priority)
|
||||
2. Profile `table_overrides` for that specific table
|
||||
3. Profile `default_rows`
|
||||
4. Built-in default: 100
|
||||
|
||||
### Validation Rules
|
||||
- Profile name must be unique within `seed-profiles.json`
|
||||
- `default_rows` must be >= 1
|
||||
- `edge_case_ratio` must be between 0.0 and 1.0
|
||||
- `null_ratio` must be between 0.0 and 1.0
|
||||
- Custom value arrays must not be empty
|
||||
- Cannot delete the last remaining profile
|
||||
118
plugins/data-seed/skills/relationship-resolution.md
Normal file
118
plugins/data-seed/skills/relationship-resolution.md
Normal file
@@ -0,0 +1,118 @@
|
||||
---
|
||||
name: relationship-resolution
|
||||
description: Foreign key resolution, dependency ordering, and circular dependency handling for seed data
|
||||
---
|
||||
|
||||
# Relationship Resolution
|
||||
|
||||
## Purpose
|
||||
|
||||
Determine the correct order for generating and inserting seed data across tables with foreign key dependencies. Handle edge cases including circular dependencies, self-referential relationships, and many-to-many through tables.
|
||||
|
||||
---
|
||||
|
||||
## Dependency Graph Construction
|
||||
|
||||
### Step 1: Extract Foreign Keys
|
||||
For each table, identify all columns with foreign key constraints:
|
||||
- Direct FK references to other tables
|
||||
- Self-referential FKs (same table)
|
||||
- Composite FKs spanning multiple columns
|
||||
|
||||
### Step 2: Build Directed Graph
|
||||
- Each table is a node
|
||||
- Each FK creates a directed edge: child -> parent (child depends on parent)
|
||||
- Self-referential edges are noted but excluded from ordering (handled separately)
|
||||
|
||||
### Step 3: Topological Sort
|
||||
- Apply topological sort to determine insertion order
|
||||
- Tables with no dependencies come first
|
||||
- Tables depending on others come after their dependencies
|
||||
- Result: ordered list where every table's dependencies appear before it
|
||||
|
||||
## Insertion Order Example
|
||||
|
||||
Given schema:
|
||||
```
|
||||
users (no FK)
|
||||
categories (no FK)
|
||||
products (FK -> categories)
|
||||
orders (FK -> users)
|
||||
order_items (FK -> orders, FK -> products)
|
||||
reviews (FK -> users, FK -> products)
|
||||
```
|
||||
|
||||
Insertion order: `users, categories, products, orders, order_items, reviews`
|
||||
|
||||
Deletion order (reverse): `reviews, order_items, orders, products, categories, users`
|
||||
|
||||
## Circular Dependency Handling
|
||||
|
||||
When topological sort detects a cycle:
|
||||
|
||||
### Strategy 1: Nullable FK Deferral
|
||||
If one FK in the cycle is nullable:
|
||||
1. Insert rows with nullable FK set to NULL
|
||||
2. Complete the cycle for the other table
|
||||
3. UPDATE the nullable FK to point to the now-existing rows
|
||||
|
||||
Example: `departments.manager_id -> employees`, `employees.department_id -> departments`
|
||||
1. Insert departments with `manager_id = NULL`
|
||||
2. Insert employees referencing departments
|
||||
3. UPDATE departments to set `manager_id` to an employee
|
||||
|
||||
### Strategy 2: Deferred Constraints
|
||||
If database supports deferred constraints (PostgreSQL):
|
||||
1. Set FK constraints to DEFERRED within transaction
|
||||
2. Insert all rows in any order
|
||||
3. Constraints checked at COMMIT time
|
||||
|
||||
### Strategy 3: Two-Pass Generation
|
||||
If neither strategy works:
|
||||
1. First pass: generate all rows without cross-cycle FK values
|
||||
2. Second pass: update FK values to reference generated rows from the other table
|
||||
|
||||
## Self-Referential Relationships
|
||||
|
||||
Common pattern: `employees.manager_id -> employees.id`
|
||||
|
||||
### Generation Strategy
|
||||
1. Generate root rows first (manager_id = NULL) — these are top-level managers
|
||||
2. Generate second tier referencing root rows
|
||||
3. Generate remaining rows referencing any previously generated row
|
||||
4. Depth distribution controlled by profile (default: max depth 3, pyramid shape)
|
||||
|
||||
### Configuration
|
||||
```json
|
||||
{
|
||||
"self_ref_null_ratio": 0.1,
|
||||
"self_ref_max_depth": 3,
|
||||
"self_ref_distribution": "pyramid"
|
||||
}
|
||||
```
|
||||
|
||||
## Many-to-Many Through Tables
|
||||
|
||||
Detection: a table with exactly two FK columns and no non-FK data columns (excluding PK and timestamps).
|
||||
|
||||
### Generation Strategy
|
||||
1. Generate both parent tables first
|
||||
2. Generate through table rows pairing random parents
|
||||
3. Respect uniqueness on the (FK1, FK2) composite — no duplicate pairings
|
||||
4. Density controlled by profile: sparse (10% of possible pairs), medium (30%), dense (60%)
|
||||
|
||||
## Deletion Order
|
||||
|
||||
When `--clean` is specified for `/seed apply`:
|
||||
1. Reverse the insertion order
|
||||
2. TRUNCATE or DELETE in this order to avoid FK violations
|
||||
3. For circular dependencies: disable FK checks, truncate, re-enable (with user confirmation)
|
||||
|
||||
## Error Handling
|
||||
|
||||
| Scenario | Response |
|
||||
|----------|----------|
|
||||
| Unresolvable cycle (no nullable FKs, no deferred constraints) | FAIL: report cycle, suggest schema modification |
|
||||
| Missing parent table in schema | FAIL: report orphaned FK reference |
|
||||
| FK references non-existent column | FAIL: report schema inconsistency |
|
||||
| Through table detection false positive | WARN: ask user to confirm junction table identification |
|
||||
81
plugins/data-seed/skills/schema-inference.md
Normal file
81
plugins/data-seed/skills/schema-inference.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
name: schema-inference
|
||||
description: Infer data types, constraints, and relationships from ORM models or raw SQL DDL
|
||||
---
|
||||
|
||||
# Schema Inference
|
||||
|
||||
## Purpose
|
||||
|
||||
Parse and normalize schema definitions from multiple ORM dialects into a unified internal representation. This skill enables data generation and validation commands to work across SQLAlchemy, Prisma, Django ORM, and raw SQL DDL without dialect-specific logic in every command.
|
||||
|
||||
---
|
||||
|
||||
## Supported Schema Sources
|
||||
|
||||
| Source | Detection | File Patterns |
|
||||
|--------|-----------|---------------|
|
||||
| SQLAlchemy | `from sqlalchemy import`, `Column(`, `mapped_column(` | `models.py`, `models/*.py` |
|
||||
| Prisma | `model` blocks with `@id`, `@relation` | `prisma/schema.prisma` |
|
||||
| Django ORM | `from django.db import models`, `models.CharField` | `models.py` with Django imports |
|
||||
| Raw SQL DDL | `CREATE TABLE` statements | `*.sql`, `schema.sql`, `migrations/*.sql` |
|
||||
| JSON Schema | `"type": "object"`, `"properties":` | `*.schema.json` |
|
||||
|
||||
## Type Normalization
|
||||
|
||||
Map dialect-specific types to a canonical set:
|
||||
|
||||
| Canonical Type | SQLAlchemy | Prisma | Django | SQL |
|
||||
|----------------|------------|--------|--------|-----|
|
||||
| `string` | `String(N)`, `Text` | `String` | `CharField`, `TextField` | `VARCHAR(N)`, `TEXT` |
|
||||
| `integer` | `Integer`, `BigInteger`, `SmallInteger` | `Int`, `BigInt` | `IntegerField`, `BigIntegerField` | `INT`, `BIGINT`, `SMALLINT` |
|
||||
| `float` | `Float`, `Numeric` | `Float` | `FloatField` | `FLOAT`, `REAL`, `DOUBLE` |
|
||||
| `decimal` | `Numeric(P,S)` | `Decimal` | `DecimalField` | `DECIMAL(P,S)`, `NUMERIC(P,S)` |
|
||||
| `boolean` | `Boolean` | `Boolean` | `BooleanField` | `BOOLEAN`, `BIT` |
|
||||
| `datetime` | `DateTime` | `DateTime` | `DateTimeField` | `TIMESTAMP`, `DATETIME` |
|
||||
| `date` | `Date` | `DateTime` | `DateField` | `DATE` |
|
||||
| `uuid` | `UUID` | `String @default(uuid())` | `UUIDField` | `UUID` |
|
||||
| `json` | `JSON` | `Json` | `JSONField` | `JSON`, `JSONB` |
|
||||
| `enum` | `Enum(...)` | `enum` block | `choices=` | `ENUM(...)`, `CHECK IN (...)` |
|
||||
|
||||
## Constraint Extraction
|
||||
|
||||
For each column, extract:
|
||||
- **nullable**: Whether NULL values are allowed (default: true unless PK or explicit NOT NULL)
|
||||
- **unique**: Whether values must be unique
|
||||
- **max_length**: For string types, the maximum character length
|
||||
- **precision/scale**: For decimal types
|
||||
- **default**: Default value expression
|
||||
- **check**: CHECK constraint expressions (e.g., `age >= 0`)
|
||||
- **primary_key**: Whether this column is part of the primary key
|
||||
|
||||
## Relationship Extraction
|
||||
|
||||
Identify foreign key relationships:
|
||||
- **parent_table**: The referenced table
|
||||
- **parent_column**: The referenced column (usually PK)
|
||||
- **on_delete**: CASCADE, SET NULL, RESTRICT, NO ACTION
|
||||
- **self_referential**: True if FK references same table
|
||||
- **many_to_many**: Detected from junction/through tables with two FKs and no additional non-FK columns
|
||||
|
||||
## Output Format
|
||||
|
||||
Internal representation used by other skills:
|
||||
|
||||
```json
|
||||
{
|
||||
"tables": {
|
||||
"users": {
|
||||
"columns": {
|
||||
"id": {"type": "integer", "primary_key": true, "nullable": false},
|
||||
"email": {"type": "string", "max_length": 255, "unique": true, "nullable": false},
|
||||
"name": {"type": "string", "max_length": 100, "nullable": false},
|
||||
"manager_id": {"type": "integer", "nullable": true, "foreign_key": {"table": "users", "column": "id"}}
|
||||
},
|
||||
"relationships": [
|
||||
{"type": "self_referential", "column": "manager_id", "references": "users.id"}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
27
plugins/data-seed/skills/visual-header.md
Normal file
27
plugins/data-seed/skills/visual-header.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Visual Header Skill
|
||||
|
||||
Standard visual header for data-seed commands.
|
||||
|
||||
## Header Template
|
||||
|
||||
```
|
||||
+----------------------------------------------------------------------+
|
||||
| DATA-SEED - [Context] |
|
||||
+----------------------------------------------------------------------+
|
||||
```
|
||||
|
||||
## Context Values by Command
|
||||
|
||||
| Command | Context |
|
||||
|---------|---------|
|
||||
| `/seed setup` | Setup Wizard |
|
||||
| `/seed generate` | Generate |
|
||||
| `/seed apply` | Apply |
|
||||
| `/seed profile` | Profile Management |
|
||||
| `/seed validate` | Validate |
|
||||
| Agent mode (seed-generator) | Data Generation |
|
||||
| Agent mode (seed-validator) | Validation |
|
||||
|
||||
## Usage
|
||||
|
||||
Display header at the start of every command response before proceeding with the operation.
|
||||
Reference in New Issue
Block a user