@famgia/omnify 1.0.44 → 1.0.45
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ai-guides/config-guide.md +122 -0
- package/ai-guides/schema-guide.md +144 -0
- package/package.json +10 -9
- package/scripts/postinstall.js +36 -342
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
# Omnify Configuration Guide
|
|
2
|
+
|
|
3
|
+
## Configuration File
|
|
4
|
+
|
|
5
|
+
Create `omnify.config.ts` in project root:
|
|
6
|
+
|
|
7
|
+
```typescript
|
|
8
|
+
import { defineConfig } from '@famgia/omnify';
|
|
9
|
+
|
|
10
|
+
export default defineConfig({
|
|
11
|
+
schemasDir: './schemas',
|
|
12
|
+
|
|
13
|
+
database: {
|
|
14
|
+
driver: 'mysql', // 'mysql' | 'pgsql' | 'sqlite' | 'sqlsrv' | 'mariadb'
|
|
15
|
+
},
|
|
16
|
+
|
|
17
|
+
output: {
|
|
18
|
+
laravel: {
|
|
19
|
+
migrationsPath: './database/migrations/omnify',
|
|
20
|
+
modelsPath: './app/Models',
|
|
21
|
+
enumsPath: './app/Enums',
|
|
22
|
+
},
|
|
23
|
+
typescript: {
|
|
24
|
+
path: './src/types/model',
|
|
25
|
+
singleFile: false,
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
// Multi-language support (optional)
|
|
30
|
+
locale: {
|
|
31
|
+
locales: ['en', 'ja', 'vi'],
|
|
32
|
+
defaultLocale: 'en',
|
|
33
|
+
fallbackLocale: 'en',
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Configuration Options
|
|
39
|
+
|
|
40
|
+
### database (required)
|
|
41
|
+
| Option | Type | Description |
|
|
42
|
+
|--------|------|-------------|
|
|
43
|
+
| `driver` | string | Database driver: mysql, pgsql, sqlite, sqlsrv, mariadb |
|
|
44
|
+
| `devUrl` | string | Development database URL for Atlas diff |
|
|
45
|
+
| `enableFieldComments` | boolean | Enable field comments in migrations (MySQL) |
|
|
46
|
+
|
|
47
|
+
### output.laravel
|
|
48
|
+
| Option | Type | Description |
|
|
49
|
+
|--------|------|-------------|
|
|
50
|
+
| `migrationsPath` | string | Directory for generated migrations |
|
|
51
|
+
| `modelsPath` | string | Directory for generated models |
|
|
52
|
+
| `modelsNamespace` | string | PHP namespace for models |
|
|
53
|
+
| `factoriesPath` | string | Directory for generated factories |
|
|
54
|
+
| `enumsPath` | string | Directory for generated enums |
|
|
55
|
+
| `enumsNamespace` | string | PHP namespace for enums |
|
|
56
|
+
|
|
57
|
+
### output.typescript
|
|
58
|
+
| Option | Type | Description |
|
|
59
|
+
|--------|------|-------------|
|
|
60
|
+
| `path` | string | Output directory for TypeScript types |
|
|
61
|
+
| `singleFile` | boolean | Generate single file vs multiple files |
|
|
62
|
+
| `generateEnums` | boolean | Generate enum types |
|
|
63
|
+
| `generateRelationships` | boolean | Generate relationship types |
|
|
64
|
+
| `generateRules` | boolean | Generate Ant Design validation rules (default: true) |
|
|
65
|
+
| `validationTemplates` | object | Custom validation message templates |
|
|
66
|
+
|
|
67
|
+
#### Validation Templates
|
|
68
|
+
|
|
69
|
+
Customize validation messages for your locales:
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
{
|
|
73
|
+
output: {
|
|
74
|
+
typescript: {
|
|
75
|
+
validationTemplates: {
|
|
76
|
+
required: {
|
|
77
|
+
ja: '${displayName}を入力してください',
|
|
78
|
+
en: '${displayName} is required',
|
|
79
|
+
},
|
|
80
|
+
maxLength: {
|
|
81
|
+
ja: '${displayName}は${max}文字以内です',
|
|
82
|
+
en: '${displayName} must be at most ${max} characters',
|
|
83
|
+
},
|
|
84
|
+
minLength: { /* ... */ },
|
|
85
|
+
min: { /* ... */ },
|
|
86
|
+
max: { /* ... */ },
|
|
87
|
+
email: { /* ... */ },
|
|
88
|
+
url: { /* ... */ },
|
|
89
|
+
pattern: { /* ... */ },
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Built-in templates are available for: ja, en, vi, ko, zh
|
|
97
|
+
|
|
98
|
+
### locale (optional)
|
|
99
|
+
| Option | Type | Description |
|
|
100
|
+
|--------|------|-------------|
|
|
101
|
+
| `locales` | string[] | Supported locale codes: ['en', 'ja', 'vi'] |
|
|
102
|
+
| `defaultLocale` | string | Default locale for simple strings |
|
|
103
|
+
| `fallbackLocale` | string | Fallback when requested locale not found |
|
|
104
|
+
|
|
105
|
+
## Common Mistakes
|
|
106
|
+
|
|
107
|
+
**Wrong** - `locales` at root level:
|
|
108
|
+
```typescript
|
|
109
|
+
{
|
|
110
|
+
locales: ['en', 'ja'], // ERROR: locales not in OmnifyConfig
|
|
111
|
+
}
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Correct** - `locales` inside `locale` object:
|
|
115
|
+
```typescript
|
|
116
|
+
{
|
|
117
|
+
locale: {
|
|
118
|
+
locales: ['en', 'ja'],
|
|
119
|
+
defaultLocale: 'en',
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
```
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# Omnify Schema Format Guide
|
|
2
|
+
|
|
3
|
+
## Schema Location
|
|
4
|
+
|
|
5
|
+
All schemas are stored in `schemas/` directory with `.yaml` extension.
|
|
6
|
+
|
|
7
|
+
## Object Schema Structure
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
# yaml-language-server: $schema=./node_modules/.omnify/combined-schema.json
|
|
11
|
+
name: ModelName # Required: PascalCase
|
|
12
|
+
kind: object # Optional: 'object' (default) or 'enum'
|
|
13
|
+
displayName: # Optional: i18n display name
|
|
14
|
+
ja: 日本語名
|
|
15
|
+
en: English Name
|
|
16
|
+
description: # Optional: i18n description
|
|
17
|
+
ja: 説明文
|
|
18
|
+
en: Description
|
|
19
|
+
group: group-name # Optional: for organizing schemas
|
|
20
|
+
options:
|
|
21
|
+
softDelete: true # Enable soft delete (deleted_at column)
|
|
22
|
+
timestamps: true # Enable created_at, updated_at
|
|
23
|
+
table: custom_table # Custom table name
|
|
24
|
+
properties:
|
|
25
|
+
# Property definitions here
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Property Types
|
|
29
|
+
|
|
30
|
+
### String Types
|
|
31
|
+
| Type | Description | Options |
|
|
32
|
+
|------|-------------|---------|
|
|
33
|
+
| `String` | Short text (varchar) | `maxLength`, `minLength`, `default` |
|
|
34
|
+
| `LongText` | Long text (text) | `default` |
|
|
35
|
+
|
|
36
|
+
### Numeric Types
|
|
37
|
+
| Type | Description | Options |
|
|
38
|
+
|------|-------------|---------|
|
|
39
|
+
| `Int` | Integer | `min`, `max`, `default`, `unsigned` |
|
|
40
|
+
| `BigInt` | Big integer | `min`, `max`, `default`, `unsigned` |
|
|
41
|
+
| `Float` | Decimal | `precision`, `scale`, `default` |
|
|
42
|
+
|
|
43
|
+
### Other Types
|
|
44
|
+
| Type | Description | Options |
|
|
45
|
+
|------|-------------|---------|
|
|
46
|
+
| `Boolean` | True/false | `default` |
|
|
47
|
+
| `Date` | Date only | `default` |
|
|
48
|
+
| `DateTime` | Date and time | `default` |
|
|
49
|
+
| `Json` | JSON object | `default` |
|
|
50
|
+
| `EnumRef` | Reference to enum | `enum` (required), `default` |
|
|
51
|
+
|
|
52
|
+
### Association Type
|
|
53
|
+
| Type | Description | Options |
|
|
54
|
+
|------|-------------|---------|
|
|
55
|
+
| `Association` | Relation | `relation`, `target`, `onDelete`, `mappedBy` |
|
|
56
|
+
|
|
57
|
+
## Property Options
|
|
58
|
+
|
|
59
|
+
```yaml
|
|
60
|
+
properties:
|
|
61
|
+
name:
|
|
62
|
+
type: String
|
|
63
|
+
displayName:
|
|
64
|
+
ja: 名前
|
|
65
|
+
en: Name
|
|
66
|
+
required: true # Not nullable
|
|
67
|
+
unique: true # Unique constraint
|
|
68
|
+
index: true # Create index
|
|
69
|
+
maxLength: 255 # For String
|
|
70
|
+
default: 'default' # Default value
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Association Relations
|
|
74
|
+
|
|
75
|
+
### ManyToOne (N:1)
|
|
76
|
+
```yaml
|
|
77
|
+
author:
|
|
78
|
+
type: Association
|
|
79
|
+
relation: ManyToOne
|
|
80
|
+
target: User
|
|
81
|
+
onDelete: CASCADE # CASCADE, SET_NULL, RESTRICT
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### OneToMany (1:N)
|
|
85
|
+
```yaml
|
|
86
|
+
posts:
|
|
87
|
+
type: Association
|
|
88
|
+
relation: OneToMany
|
|
89
|
+
target: Post
|
|
90
|
+
mappedBy: author # Property name in Post that references this
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### ManyToMany (N:M)
|
|
94
|
+
```yaml
|
|
95
|
+
tags:
|
|
96
|
+
type: Association
|
|
97
|
+
relation: ManyToMany
|
|
98
|
+
target: Tag
|
|
99
|
+
pivotTable: post_tags # Optional: custom pivot table name
|
|
100
|
+
pivotFields: # Optional: extra pivot fields
|
|
101
|
+
- name: order
|
|
102
|
+
type: Int
|
|
103
|
+
default: 0
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### OneToOne (1:1)
|
|
107
|
+
```yaml
|
|
108
|
+
profile:
|
|
109
|
+
type: Association
|
|
110
|
+
relation: OneToOne
|
|
111
|
+
target: Profile
|
|
112
|
+
onDelete: CASCADE
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Enum Schema
|
|
116
|
+
|
|
117
|
+
```yaml
|
|
118
|
+
name: PostStatus
|
|
119
|
+
kind: enum
|
|
120
|
+
displayName:
|
|
121
|
+
ja: 投稿ステータス
|
|
122
|
+
en: Post Status
|
|
123
|
+
values:
|
|
124
|
+
draft: ドラフト # value: displayName format
|
|
125
|
+
published: 公開済み
|
|
126
|
+
archived: アーカイブ
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Use enum in object schema:
|
|
130
|
+
```yaml
|
|
131
|
+
status:
|
|
132
|
+
type: EnumRef
|
|
133
|
+
enum: PostStatus # Reference enum name
|
|
134
|
+
default: draft # Default value from enum
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## MCP Tools
|
|
138
|
+
|
|
139
|
+
If Omnify MCP is configured, these tools are available:
|
|
140
|
+
- `omnify_create_schema` - Generate schema YAML
|
|
141
|
+
- `omnify_validate_schema` - Validate YAML content
|
|
142
|
+
- `omnify_get_types` - Property types documentation
|
|
143
|
+
- `omnify_get_relationships` - Relationship guide
|
|
144
|
+
- `omnify_get_examples` - Example schemas
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@famgia/omnify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.45",
|
|
4
4
|
"description": "Schema-driven database migration system with TypeScript types and Laravel migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,17 +21,18 @@
|
|
|
21
21
|
"bin",
|
|
22
22
|
"docs",
|
|
23
23
|
"scripts",
|
|
24
|
+
"ai-guides",
|
|
24
25
|
"README.md"
|
|
25
26
|
],
|
|
26
27
|
"dependencies": {
|
|
27
|
-
"@famgia/omnify-cli": "0.0.
|
|
28
|
-
"@famgia/omnify-core": "0.0.
|
|
29
|
-
"@famgia/omnify-
|
|
30
|
-
"@famgia/omnify-
|
|
31
|
-
"@famgia/omnify-
|
|
32
|
-
"@famgia/omnify-
|
|
33
|
-
"@famgia/omnify-mcp": "0.0.
|
|
34
|
-
"@famgia/omnify-japan": "0.0.
|
|
28
|
+
"@famgia/omnify-cli": "0.0.44",
|
|
29
|
+
"@famgia/omnify-core": "0.0.40",
|
|
30
|
+
"@famgia/omnify-types": "0.0.37",
|
|
31
|
+
"@famgia/omnify-laravel": "0.0.46",
|
|
32
|
+
"@famgia/omnify-typescript": "0.0.29",
|
|
33
|
+
"@famgia/omnify-atlas": "0.0.36",
|
|
34
|
+
"@famgia/omnify-mcp": "0.0.28",
|
|
35
|
+
"@famgia/omnify-japan": "0.0.35"
|
|
35
36
|
},
|
|
36
37
|
"keywords": [
|
|
37
38
|
"omnify",
|
package/scripts/postinstall.js
CHANGED
|
@@ -3,6 +3,10 @@
|
|
|
3
3
|
import fs from 'fs';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import os from 'os';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
6
10
|
|
|
7
11
|
// Content for CLAUDE.md
|
|
8
12
|
const CLAUDE_MD_SECTION = `## Omnify
|
|
@@ -14,6 +18,7 @@ This project uses Omnify for schema-driven code generation.
|
|
|
14
18
|
- \`config-guide.md\` - Configuration (omnify.config.ts)
|
|
15
19
|
- \`laravel-guide.md\` - Laravel generator (if installed)
|
|
16
20
|
- \`typescript-guide.md\` - TypeScript generator (if installed)
|
|
21
|
+
- \`antdesign-guide.md\` - Ant Design Form integration (if installed)
|
|
17
22
|
|
|
18
23
|
**Commands**:
|
|
19
24
|
- \`npx omnify generate\` - Generate code from schemas
|
|
@@ -31,6 +36,7 @@ For detailed documentation, read these files:
|
|
|
31
36
|
- .claude/omnify/config-guide.md - Configuration (omnify.config.ts)
|
|
32
37
|
- .claude/omnify/laravel-guide.md - Laravel generator (if exists)
|
|
33
38
|
- .claude/omnify/typescript-guide.md - TypeScript generator (if exists)
|
|
39
|
+
- .claude/omnify/antdesign-guide.md - Ant Design Form integration (if exists)
|
|
34
40
|
|
|
35
41
|
Commands:
|
|
36
42
|
- npx omnify generate - Generate code from schemas
|
|
@@ -40,278 +46,6 @@ Commands:
|
|
|
40
46
|
// Marker to check if Omnify section exists
|
|
41
47
|
const OMNIFY_MARKER = '.claude/omnify/';
|
|
42
48
|
|
|
43
|
-
// Schema guide skill file
|
|
44
|
-
const SCHEMA_GUIDE_CONTENT = `# Omnify Schema Format Guide
|
|
45
|
-
|
|
46
|
-
## Schema Location
|
|
47
|
-
|
|
48
|
-
All schemas are stored in \`schemas/\` directory with \`.yaml\` extension.
|
|
49
|
-
|
|
50
|
-
## Object Schema Structure
|
|
51
|
-
|
|
52
|
-
\`\`\`yaml
|
|
53
|
-
# yaml-language-server: $schema=./node_modules/.omnify/combined-schema.json
|
|
54
|
-
name: ModelName # Required: PascalCase
|
|
55
|
-
kind: object # Optional: 'object' (default) or 'enum'
|
|
56
|
-
displayName: # Optional: i18n display name
|
|
57
|
-
ja: 日本語名
|
|
58
|
-
en: English Name
|
|
59
|
-
description: # Optional: i18n description
|
|
60
|
-
ja: 説明文
|
|
61
|
-
en: Description
|
|
62
|
-
group: group-name # Optional: for organizing schemas
|
|
63
|
-
options:
|
|
64
|
-
softDelete: true # Enable soft delete (deleted_at column)
|
|
65
|
-
timestamps: true # Enable created_at, updated_at
|
|
66
|
-
table: custom_table # Custom table name
|
|
67
|
-
properties:
|
|
68
|
-
# Property definitions here
|
|
69
|
-
\`\`\`
|
|
70
|
-
|
|
71
|
-
## Property Types
|
|
72
|
-
|
|
73
|
-
### String Types
|
|
74
|
-
| Type | Description | Options |
|
|
75
|
-
|------|-------------|---------|
|
|
76
|
-
| \`String\` | Short text (varchar) | \`maxLength\`, \`minLength\`, \`default\` |
|
|
77
|
-
| \`LongText\` | Long text (text) | \`default\` |
|
|
78
|
-
|
|
79
|
-
### Numeric Types
|
|
80
|
-
| Type | Description | Options |
|
|
81
|
-
|------|-------------|---------|
|
|
82
|
-
| \`Int\` | Integer | \`min\`, \`max\`, \`default\`, \`unsigned\` |
|
|
83
|
-
| \`BigInt\` | Big integer | \`min\`, \`max\`, \`default\`, \`unsigned\` |
|
|
84
|
-
| \`Float\` | Decimal | \`precision\`, \`scale\`, \`default\` |
|
|
85
|
-
|
|
86
|
-
### Other Types
|
|
87
|
-
| Type | Description | Options |
|
|
88
|
-
|------|-------------|---------|
|
|
89
|
-
| \`Boolean\` | True/false | \`default\` |
|
|
90
|
-
| \`Date\` | Date only | \`default\` |
|
|
91
|
-
| \`DateTime\` | Date and time | \`default\` |
|
|
92
|
-
| \`Json\` | JSON object | \`default\` |
|
|
93
|
-
| \`EnumRef\` | Reference to enum | \`enum\` (required), \`default\` |
|
|
94
|
-
|
|
95
|
-
### Association Type
|
|
96
|
-
| Type | Description | Options |
|
|
97
|
-
|------|-------------|---------|
|
|
98
|
-
| \`Association\` | Relation | \`relation\`, \`target\`, \`onDelete\`, \`mappedBy\` |
|
|
99
|
-
|
|
100
|
-
## Property Options
|
|
101
|
-
|
|
102
|
-
\`\`\`yaml
|
|
103
|
-
properties:
|
|
104
|
-
name:
|
|
105
|
-
type: String
|
|
106
|
-
displayName:
|
|
107
|
-
ja: 名前
|
|
108
|
-
en: Name
|
|
109
|
-
required: true # Not nullable
|
|
110
|
-
unique: true # Unique constraint
|
|
111
|
-
index: true # Create index
|
|
112
|
-
maxLength: 255 # For String
|
|
113
|
-
default: 'default' # Default value
|
|
114
|
-
\`\`\`
|
|
115
|
-
|
|
116
|
-
## Association Relations
|
|
117
|
-
|
|
118
|
-
### ManyToOne (N:1)
|
|
119
|
-
\`\`\`yaml
|
|
120
|
-
author:
|
|
121
|
-
type: Association
|
|
122
|
-
relation: ManyToOne
|
|
123
|
-
target: User
|
|
124
|
-
onDelete: CASCADE # CASCADE, SET_NULL, RESTRICT
|
|
125
|
-
\`\`\`
|
|
126
|
-
|
|
127
|
-
### OneToMany (1:N)
|
|
128
|
-
\`\`\`yaml
|
|
129
|
-
posts:
|
|
130
|
-
type: Association
|
|
131
|
-
relation: OneToMany
|
|
132
|
-
target: Post
|
|
133
|
-
mappedBy: author # Property name in Post that references this
|
|
134
|
-
\`\`\`
|
|
135
|
-
|
|
136
|
-
### ManyToMany (N:M)
|
|
137
|
-
\`\`\`yaml
|
|
138
|
-
tags:
|
|
139
|
-
type: Association
|
|
140
|
-
relation: ManyToMany
|
|
141
|
-
target: Tag
|
|
142
|
-
pivotTable: post_tags # Optional: custom pivot table name
|
|
143
|
-
pivotFields: # Optional: extra pivot fields
|
|
144
|
-
- name: order
|
|
145
|
-
type: Int
|
|
146
|
-
default: 0
|
|
147
|
-
\`\`\`
|
|
148
|
-
|
|
149
|
-
### OneToOne (1:1)
|
|
150
|
-
\`\`\`yaml
|
|
151
|
-
profile:
|
|
152
|
-
type: Association
|
|
153
|
-
relation: OneToOne
|
|
154
|
-
target: Profile
|
|
155
|
-
onDelete: CASCADE
|
|
156
|
-
\`\`\`
|
|
157
|
-
|
|
158
|
-
## Enum Schema
|
|
159
|
-
|
|
160
|
-
\`\`\`yaml
|
|
161
|
-
name: PostStatus
|
|
162
|
-
kind: enum
|
|
163
|
-
displayName:
|
|
164
|
-
ja: 投稿ステータス
|
|
165
|
-
en: Post Status
|
|
166
|
-
values:
|
|
167
|
-
draft: ドラフト # value: displayName format
|
|
168
|
-
published: 公開済み
|
|
169
|
-
archived: アーカイブ
|
|
170
|
-
\`\`\`
|
|
171
|
-
|
|
172
|
-
Use enum in object schema:
|
|
173
|
-
\`\`\`yaml
|
|
174
|
-
status:
|
|
175
|
-
type: EnumRef
|
|
176
|
-
enum: PostStatus # Reference enum name
|
|
177
|
-
default: draft # Default value from enum
|
|
178
|
-
\`\`\`
|
|
179
|
-
|
|
180
|
-
## MCP Tools
|
|
181
|
-
|
|
182
|
-
If Omnify MCP is configured, these tools are available:
|
|
183
|
-
- \`omnify_create_schema\` - Generate schema YAML
|
|
184
|
-
- \`omnify_validate_schema\` - Validate YAML content
|
|
185
|
-
- \`omnify_get_types\` - Property types documentation
|
|
186
|
-
- \`omnify_get_relationships\` - Relationship guide
|
|
187
|
-
- \`omnify_get_examples\` - Example schemas
|
|
188
|
-
`;
|
|
189
|
-
|
|
190
|
-
// Config guide skill file
|
|
191
|
-
const CONFIG_GUIDE_CONTENT = `# Omnify Configuration Guide
|
|
192
|
-
|
|
193
|
-
## Configuration File
|
|
194
|
-
|
|
195
|
-
Create \`omnify.config.ts\` in project root:
|
|
196
|
-
|
|
197
|
-
\`\`\`typescript
|
|
198
|
-
import { defineConfig } from '@famgia/omnify';
|
|
199
|
-
|
|
200
|
-
export default defineConfig({
|
|
201
|
-
schemasDir: './schemas',
|
|
202
|
-
|
|
203
|
-
database: {
|
|
204
|
-
driver: 'mysql', // 'mysql' | 'pgsql' | 'sqlite' | 'sqlsrv' | 'mariadb'
|
|
205
|
-
},
|
|
206
|
-
|
|
207
|
-
output: {
|
|
208
|
-
laravel: {
|
|
209
|
-
migrationsPath: './database/migrations/omnify',
|
|
210
|
-
modelsPath: './app/Models',
|
|
211
|
-
enumsPath: './app/Enums',
|
|
212
|
-
},
|
|
213
|
-
typescript: {
|
|
214
|
-
path: './src/types/model',
|
|
215
|
-
singleFile: false,
|
|
216
|
-
},
|
|
217
|
-
},
|
|
218
|
-
|
|
219
|
-
// Multi-language support (optional)
|
|
220
|
-
locale: {
|
|
221
|
-
locales: ['en', 'ja', 'vi'],
|
|
222
|
-
defaultLocale: 'en',
|
|
223
|
-
fallbackLocale: 'en',
|
|
224
|
-
},
|
|
225
|
-
});
|
|
226
|
-
\`\`\`
|
|
227
|
-
|
|
228
|
-
## Configuration Options
|
|
229
|
-
|
|
230
|
-
### database (required)
|
|
231
|
-
| Option | Type | Description |
|
|
232
|
-
|--------|------|-------------|
|
|
233
|
-
| \`driver\` | string | Database driver: mysql, pgsql, sqlite, sqlsrv, mariadb |
|
|
234
|
-
| \`devUrl\` | string | Development database URL for Atlas diff |
|
|
235
|
-
| \`enableFieldComments\` | boolean | Enable field comments in migrations (MySQL) |
|
|
236
|
-
|
|
237
|
-
### output.laravel
|
|
238
|
-
| Option | Type | Description |
|
|
239
|
-
|--------|------|-------------|
|
|
240
|
-
| \`migrationsPath\` | string | Directory for generated migrations |
|
|
241
|
-
| \`modelsPath\` | string | Directory for generated models |
|
|
242
|
-
| \`modelsNamespace\` | string | PHP namespace for models |
|
|
243
|
-
| \`factoriesPath\` | string | Directory for generated factories |
|
|
244
|
-
| \`enumsPath\` | string | Directory for generated enums |
|
|
245
|
-
| \`enumsNamespace\` | string | PHP namespace for enums |
|
|
246
|
-
|
|
247
|
-
### output.typescript
|
|
248
|
-
| Option | Type | Description |
|
|
249
|
-
|--------|------|-------------|
|
|
250
|
-
| \`path\` | string | Output directory for TypeScript types |
|
|
251
|
-
| \`singleFile\` | boolean | Generate single file vs multiple files |
|
|
252
|
-
| \`generateEnums\` | boolean | Generate enum types |
|
|
253
|
-
| \`generateRelationships\` | boolean | Generate relationship types |
|
|
254
|
-
| \`generateRules\` | boolean | Generate Ant Design validation rules (default: true) |
|
|
255
|
-
| \`validationTemplates\` | object | Custom validation message templates |
|
|
256
|
-
|
|
257
|
-
#### Validation Templates
|
|
258
|
-
|
|
259
|
-
Customize validation messages for your locales:
|
|
260
|
-
|
|
261
|
-
\`\`\`typescript
|
|
262
|
-
{
|
|
263
|
-
output: {
|
|
264
|
-
typescript: {
|
|
265
|
-
validationTemplates: {
|
|
266
|
-
required: {
|
|
267
|
-
ja: '\${displayName}を入力してください',
|
|
268
|
-
en: '\${displayName} is required',
|
|
269
|
-
},
|
|
270
|
-
maxLength: {
|
|
271
|
-
ja: '\${displayName}は\${max}文字以内です',
|
|
272
|
-
en: '\${displayName} must be at most \${max} characters',
|
|
273
|
-
},
|
|
274
|
-
minLength: { /* ... */ },
|
|
275
|
-
min: { /* ... */ },
|
|
276
|
-
max: { /* ... */ },
|
|
277
|
-
email: { /* ... */ },
|
|
278
|
-
url: { /* ... */ },
|
|
279
|
-
pattern: { /* ... */ },
|
|
280
|
-
},
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
}
|
|
284
|
-
\`\`\`
|
|
285
|
-
|
|
286
|
-
Built-in templates are available for: ja, en, vi, ko, zh
|
|
287
|
-
|
|
288
|
-
### locale (optional)
|
|
289
|
-
| Option | Type | Description |
|
|
290
|
-
|--------|------|-------------|
|
|
291
|
-
| \`locales\` | string[] | Supported locale codes: ['en', 'ja', 'vi'] |
|
|
292
|
-
| \`defaultLocale\` | string | Default locale for simple strings |
|
|
293
|
-
| \`fallbackLocale\` | string | Fallback when requested locale not found |
|
|
294
|
-
|
|
295
|
-
## Common Mistakes
|
|
296
|
-
|
|
297
|
-
❌ **Wrong** - \`locales\` at root level:
|
|
298
|
-
\`\`\`typescript
|
|
299
|
-
{
|
|
300
|
-
locales: ['en', 'ja'], // ERROR: locales not in OmnifyConfig
|
|
301
|
-
}
|
|
302
|
-
\`\`\`
|
|
303
|
-
|
|
304
|
-
✅ **Correct** - \`locales\` inside \`locale\` object:
|
|
305
|
-
\`\`\`typescript
|
|
306
|
-
{
|
|
307
|
-
locale: {
|
|
308
|
-
locales: ['en', 'ja'],
|
|
309
|
-
defaultLocale: 'en',
|
|
310
|
-
},
|
|
311
|
-
}
|
|
312
|
-
\`\`\`
|
|
313
|
-
`;
|
|
314
|
-
|
|
315
49
|
const MCP_CONFIG = {
|
|
316
50
|
omnify: {
|
|
317
51
|
command: 'npx',
|
|
@@ -319,9 +53,6 @@ const MCP_CONFIG = {
|
|
|
319
53
|
},
|
|
320
54
|
};
|
|
321
55
|
|
|
322
|
-
// Combined schema path (relative from project root)
|
|
323
|
-
const COMBINED_SCHEMA_PATH = 'node_modules/.omnify/combined-schema.json';
|
|
324
|
-
|
|
325
56
|
/**
|
|
326
57
|
* Generate combined JSON Schema from base schema + all plugin contributions
|
|
327
58
|
*/
|
|
@@ -332,34 +63,33 @@ function generateCombinedSchema(projectRoot) {
|
|
|
332
63
|
|
|
333
64
|
try {
|
|
334
65
|
// Read base schema from @famgia/omnify-types
|
|
335
|
-
// Check multiple possible locations (direct, nested via omnify, pnpm hoisted)
|
|
336
66
|
const possiblePaths = [
|
|
337
67
|
path.join(nodeModulesDir, '@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
338
68
|
path.join(nodeModulesDir, '@famgia/omnify/node_modules/@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
339
|
-
path.join(nodeModulesDir, '.pnpm/@famgia+omnify-types@*/node_modules/@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
340
69
|
];
|
|
341
70
|
|
|
342
71
|
let baseSchemaPath = null;
|
|
343
72
|
for (const p of possiblePaths) {
|
|
344
|
-
|
|
345
|
-
if (p.includes('*')) {
|
|
346
|
-
const dir = path.dirname(path.dirname(p));
|
|
347
|
-
if (fs.existsSync(path.dirname(dir))) {
|
|
348
|
-
const matches = fs.readdirSync(path.dirname(dir)).filter(f => f.startsWith('@famgia+omnify-types@'));
|
|
349
|
-
if (matches.length > 0) {
|
|
350
|
-
const matchPath = path.join(path.dirname(dir), matches[0], 'node_modules/@famgia/omnify-types/schemas/omnify-schema.json');
|
|
351
|
-
if (fs.existsSync(matchPath)) {
|
|
352
|
-
baseSchemaPath = matchPath;
|
|
353
|
-
break;
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
} else if (fs.existsSync(p)) {
|
|
73
|
+
if (fs.existsSync(p)) {
|
|
358
74
|
baseSchemaPath = p;
|
|
359
75
|
break;
|
|
360
76
|
}
|
|
361
77
|
}
|
|
362
78
|
|
|
79
|
+
// Try pnpm hoisted location
|
|
80
|
+
if (!baseSchemaPath) {
|
|
81
|
+
const pnpmDir = path.join(nodeModulesDir, '.pnpm');
|
|
82
|
+
if (fs.existsSync(pnpmDir)) {
|
|
83
|
+
const matches = fs.readdirSync(pnpmDir).filter(f => f.startsWith('@famgia+omnify-types@'));
|
|
84
|
+
if (matches.length > 0) {
|
|
85
|
+
const matchPath = path.join(pnpmDir, matches[0], 'node_modules/@famgia/omnify-types/schemas/omnify-schema.json');
|
|
86
|
+
if (fs.existsSync(matchPath)) {
|
|
87
|
+
baseSchemaPath = matchPath;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
363
93
|
if (!baseSchemaPath) {
|
|
364
94
|
console.log(' Note: Base schema not found, skipping combined schema generation');
|
|
365
95
|
return false;
|
|
@@ -368,7 +98,6 @@ function generateCombinedSchema(projectRoot) {
|
|
|
368
98
|
const baseSchema = JSON.parse(fs.readFileSync(baseSchemaPath, 'utf-8'));
|
|
369
99
|
|
|
370
100
|
// Find all @famgia/omnify-* plugins with schema contributions
|
|
371
|
-
// Check multiple possible locations (direct, nested via omnify)
|
|
372
101
|
const famgiaDirs = [
|
|
373
102
|
path.join(nodeModulesDir, '@famgia'),
|
|
374
103
|
path.join(nodeModulesDir, '@famgia/omnify/node_modules/@famgia'),
|
|
@@ -381,7 +110,6 @@ function generateCombinedSchema(projectRoot) {
|
|
|
381
110
|
|
|
382
111
|
const packages = fs.readdirSync(famgiaDir);
|
|
383
112
|
for (const pkg of packages) {
|
|
384
|
-
// Skip non-plugin packages and already processed
|
|
385
113
|
if (!pkg.startsWith('omnify-') || pkg === 'omnify-types' || pkg === 'omnify-mcp') {
|
|
386
114
|
continue;
|
|
387
115
|
}
|
|
@@ -402,14 +130,12 @@ function generateCombinedSchema(projectRoot) {
|
|
|
402
130
|
|
|
403
131
|
// Merge contributions into base schema
|
|
404
132
|
for (const { name, contribution } of pluginContributions) {
|
|
405
|
-
// Add definitions
|
|
406
133
|
if (contribution.definitions) {
|
|
407
134
|
for (const [defName, defValue] of Object.entries(contribution.definitions)) {
|
|
408
135
|
baseSchema.definitions[defName] = defValue;
|
|
409
136
|
}
|
|
410
137
|
}
|
|
411
138
|
|
|
412
|
-
// Add to PropertyDefinition oneOf
|
|
413
139
|
if (contribution.propertyTypes && baseSchema.definitions.PropertyDefinition) {
|
|
414
140
|
for (const typeName of contribution.propertyTypes) {
|
|
415
141
|
baseSchema.definitions.PropertyDefinition.oneOf.push({
|
|
@@ -421,16 +147,13 @@ function generateCombinedSchema(projectRoot) {
|
|
|
421
147
|
console.log(` Added schema contributions from @famgia/${name}`);
|
|
422
148
|
}
|
|
423
149
|
|
|
424
|
-
// Update schema $id to indicate it's combined
|
|
425
150
|
baseSchema.$id = 'omnify://combined-schema.json';
|
|
426
151
|
baseSchema.description = baseSchema.description + ' (Combined with plugin contributions)';
|
|
427
152
|
|
|
428
|
-
// Create output directory
|
|
429
153
|
if (!fs.existsSync(outputDir)) {
|
|
430
154
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
431
155
|
}
|
|
432
156
|
|
|
433
|
-
// Write combined schema
|
|
434
157
|
fs.writeFileSync(outputPath, JSON.stringify(baseSchema, null, 2), 'utf-8');
|
|
435
158
|
console.log(' Generated combined JSON schema at node_modules/.omnify/combined-schema.json');
|
|
436
159
|
|
|
@@ -442,47 +165,42 @@ function generateCombinedSchema(projectRoot) {
|
|
|
442
165
|
}
|
|
443
166
|
|
|
444
167
|
function findProjectRoot() {
|
|
445
|
-
// npm/pnpm set INIT_CWD to the directory where the install was run
|
|
446
|
-
// This is more reliable than process.cwd() during postinstall
|
|
447
168
|
let dir = process.env.INIT_CWD || process.cwd();
|
|
448
|
-
|
|
449
|
-
// If we're in node_modules, go up to find the actual project
|
|
450
169
|
const nodeModulesIndex = dir.indexOf('node_modules');
|
|
451
170
|
if (nodeModulesIndex !== -1) {
|
|
452
171
|
dir = dir.substring(0, nodeModulesIndex - 1);
|
|
453
172
|
}
|
|
454
|
-
|
|
455
|
-
// Verify it's a project root by checking for package.json
|
|
456
173
|
const packageJsonPath = path.join(dir, 'package.json');
|
|
457
174
|
if (fs.existsSync(packageJsonPath)) {
|
|
458
175
|
return dir;
|
|
459
176
|
}
|
|
460
|
-
|
|
461
177
|
return null;
|
|
462
178
|
}
|
|
463
179
|
|
|
464
|
-
function
|
|
180
|
+
function copyAiGuidesToProject(projectRoot) {
|
|
465
181
|
const omnifyDir = path.join(projectRoot, '.claude', 'omnify');
|
|
182
|
+
const aiGuidesDir = path.join(__dirname, '..', 'ai-guides');
|
|
466
183
|
|
|
467
184
|
try {
|
|
468
|
-
// Create .claude/omnify directory
|
|
469
185
|
if (!fs.existsSync(omnifyDir)) {
|
|
470
186
|
fs.mkdirSync(omnifyDir, { recursive: true });
|
|
471
187
|
}
|
|
472
188
|
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
189
|
+
if (fs.existsSync(aiGuidesDir)) {
|
|
190
|
+
const files = fs.readdirSync(aiGuidesDir);
|
|
191
|
+
for (const file of files) {
|
|
192
|
+
const srcPath = path.join(aiGuidesDir, file);
|
|
193
|
+
const destPath = path.join(omnifyDir, file);
|
|
476
194
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
195
|
+
if (fs.statSync(srcPath).isFile()) {
|
|
196
|
+
fs.copyFileSync(srcPath, destPath);
|
|
197
|
+
console.log(` Created .claude/omnify/${file}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
480
201
|
|
|
481
|
-
console.log(' Created .claude/omnify/schema-guide.md');
|
|
482
|
-
console.log(' Created .claude/omnify/config-guide.md');
|
|
483
202
|
return true;
|
|
484
|
-
} catch
|
|
485
|
-
console.log(' Note: Could not create .claude/omnify/ skill files');
|
|
203
|
+
} catch {
|
|
486
204
|
return false;
|
|
487
205
|
}
|
|
488
206
|
}
|
|
@@ -490,24 +208,20 @@ function createOmnifySkillFiles(projectRoot) {
|
|
|
490
208
|
function createClaudeMd(projectRoot) {
|
|
491
209
|
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
492
210
|
|
|
493
|
-
// Check if CLAUDE.md exists
|
|
494
211
|
if (fs.existsSync(claudeMdPath)) {
|
|
495
212
|
const existingContent = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
496
213
|
|
|
497
|
-
// Check if Omnify section already exists
|
|
498
214
|
if (existingContent.includes(OMNIFY_MARKER)) {
|
|
499
215
|
console.log(' CLAUDE.md already has Omnify section, skipping...');
|
|
500
216
|
return false;
|
|
501
217
|
}
|
|
502
218
|
|
|
503
|
-
// Append Omnify section to existing CLAUDE.md
|
|
504
219
|
const newContent = existingContent.trimEnd() + '\n\n' + CLAUDE_MD_SECTION;
|
|
505
220
|
fs.writeFileSync(claudeMdPath, newContent, 'utf-8');
|
|
506
221
|
console.log(' Appended Omnify section to CLAUDE.md');
|
|
507
222
|
return true;
|
|
508
223
|
}
|
|
509
224
|
|
|
510
|
-
// Create new CLAUDE.md with just the Omnify section
|
|
511
225
|
fs.writeFileSync(claudeMdPath, CLAUDE_MD_SECTION, 'utf-8');
|
|
512
226
|
console.log(' Created CLAUDE.md');
|
|
513
227
|
return true;
|
|
@@ -518,12 +232,10 @@ function createCursorRules(projectRoot) {
|
|
|
518
232
|
const cursorRulesPath = path.join(cursorDir, 'omnify.md');
|
|
519
233
|
|
|
520
234
|
try {
|
|
521
|
-
// Create .cursor/rules directory
|
|
522
235
|
if (!fs.existsSync(cursorDir)) {
|
|
523
236
|
fs.mkdirSync(cursorDir, { recursive: true });
|
|
524
237
|
}
|
|
525
238
|
|
|
526
|
-
// Always overwrite - this file is fully managed by Omnify
|
|
527
239
|
fs.writeFileSync(cursorRulesPath, CURSORRULES_CONTENT, 'utf-8');
|
|
528
240
|
console.log(' Updated .cursor/rules/omnify.md');
|
|
529
241
|
return true;
|
|
@@ -538,14 +250,12 @@ function configureClaudeMcp() {
|
|
|
538
250
|
const configPath = path.join(claudeDir, 'claude_desktop_config.json');
|
|
539
251
|
|
|
540
252
|
try {
|
|
541
|
-
// Create .claude directory if not exists
|
|
542
253
|
if (!fs.existsSync(claudeDir)) {
|
|
543
254
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
544
255
|
}
|
|
545
256
|
|
|
546
257
|
let config = { mcpServers: {} };
|
|
547
258
|
|
|
548
|
-
// Read existing config if exists
|
|
549
259
|
if (fs.existsSync(configPath)) {
|
|
550
260
|
try {
|
|
551
261
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
@@ -554,38 +264,30 @@ function configureClaudeMcp() {
|
|
|
554
264
|
config.mcpServers = {};
|
|
555
265
|
}
|
|
556
266
|
} catch {
|
|
557
|
-
// Invalid JSON, start fresh
|
|
558
267
|
config = { mcpServers: {} };
|
|
559
268
|
}
|
|
560
269
|
}
|
|
561
270
|
|
|
562
|
-
// Check if omnify is already configured
|
|
563
271
|
if (config.mcpServers.omnify) {
|
|
564
272
|
console.log(' Omnify MCP already configured');
|
|
565
273
|
return false;
|
|
566
274
|
}
|
|
567
275
|
|
|
568
|
-
// Add omnify MCP server
|
|
569
276
|
config.mcpServers.omnify = MCP_CONFIG.omnify;
|
|
570
|
-
|
|
571
|
-
// Write config
|
|
572
277
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
573
278
|
console.log(' Configured Omnify MCP server in ~/.claude/claude_desktop_config.json');
|
|
574
279
|
return true;
|
|
575
280
|
} catch (error) {
|
|
576
|
-
// Silently fail - MCP config is optional
|
|
577
281
|
console.log(' Note: Could not auto-configure MCP (optional)');
|
|
578
282
|
return false;
|
|
579
283
|
}
|
|
580
284
|
}
|
|
581
285
|
|
|
582
286
|
function main() {
|
|
583
|
-
// Skip in CI environments
|
|
584
287
|
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {
|
|
585
288
|
return;
|
|
586
289
|
}
|
|
587
290
|
|
|
588
|
-
// Skip if in omnify-ts monorepo (source code), but allow examples/
|
|
589
291
|
const projectDir = process.env.INIT_CWD || process.cwd();
|
|
590
292
|
if (projectDir.includes('omnify-ts') && !projectDir.includes('omnify-ts/examples')) {
|
|
591
293
|
return;
|
|
@@ -596,20 +298,12 @@ function main() {
|
|
|
596
298
|
const projectRoot = findProjectRoot();
|
|
597
299
|
|
|
598
300
|
if (projectRoot) {
|
|
599
|
-
// Generate combined JSON schema with plugin contributions
|
|
600
301
|
generateCombinedSchema(projectRoot);
|
|
601
|
-
|
|
602
|
-
// Create .claude/omnify/ skill files
|
|
603
|
-
createOmnifySkillFiles(projectRoot);
|
|
604
|
-
|
|
605
|
-
// Create or update CLAUDE.md in project root
|
|
302
|
+
copyAiGuidesToProject(projectRoot);
|
|
606
303
|
createClaudeMd(projectRoot);
|
|
607
|
-
|
|
608
|
-
// Create Cursor rules
|
|
609
304
|
createCursorRules(projectRoot);
|
|
610
305
|
}
|
|
611
306
|
|
|
612
|
-
// Configure MCP server in user's home directory
|
|
613
307
|
configureClaudeMcp();
|
|
614
308
|
|
|
615
309
|
console.log('\n✅ Omnify setup complete!\n');
|