@famgia/omnify 1.0.43 → 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 -309
|
@@ -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-
|
|
29
|
-
"@famgia/omnify-
|
|
30
|
-
"@famgia/omnify-laravel": "0.0.
|
|
31
|
-
"@famgia/omnify-typescript": "0.0.
|
|
32
|
-
"@famgia/omnify-
|
|
33
|
-
"@famgia/omnify-
|
|
34
|
-
"@famgia/omnify-
|
|
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,245 +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
|
-
|
|
255
|
-
### locale (optional)
|
|
256
|
-
| Option | Type | Description |
|
|
257
|
-
|--------|------|-------------|
|
|
258
|
-
| \`locales\` | string[] | Supported locale codes: ['en', 'ja', 'vi'] |
|
|
259
|
-
| \`defaultLocale\` | string | Default locale for simple strings |
|
|
260
|
-
| \`fallbackLocale\` | string | Fallback when requested locale not found |
|
|
261
|
-
|
|
262
|
-
## Common Mistakes
|
|
263
|
-
|
|
264
|
-
❌ **Wrong** - \`locales\` at root level:
|
|
265
|
-
\`\`\`typescript
|
|
266
|
-
{
|
|
267
|
-
locales: ['en', 'ja'], // ERROR: locales not in OmnifyConfig
|
|
268
|
-
}
|
|
269
|
-
\`\`\`
|
|
270
|
-
|
|
271
|
-
✅ **Correct** - \`locales\` inside \`locale\` object:
|
|
272
|
-
\`\`\`typescript
|
|
273
|
-
{
|
|
274
|
-
locale: {
|
|
275
|
-
locales: ['en', 'ja'],
|
|
276
|
-
defaultLocale: 'en',
|
|
277
|
-
},
|
|
278
|
-
}
|
|
279
|
-
\`\`\`
|
|
280
|
-
`;
|
|
281
|
-
|
|
282
49
|
const MCP_CONFIG = {
|
|
283
50
|
omnify: {
|
|
284
51
|
command: 'npx',
|
|
@@ -286,9 +53,6 @@ const MCP_CONFIG = {
|
|
|
286
53
|
},
|
|
287
54
|
};
|
|
288
55
|
|
|
289
|
-
// Combined schema path (relative from project root)
|
|
290
|
-
const COMBINED_SCHEMA_PATH = 'node_modules/.omnify/combined-schema.json';
|
|
291
|
-
|
|
292
56
|
/**
|
|
293
57
|
* Generate combined JSON Schema from base schema + all plugin contributions
|
|
294
58
|
*/
|
|
@@ -299,34 +63,33 @@ function generateCombinedSchema(projectRoot) {
|
|
|
299
63
|
|
|
300
64
|
try {
|
|
301
65
|
// Read base schema from @famgia/omnify-types
|
|
302
|
-
// Check multiple possible locations (direct, nested via omnify, pnpm hoisted)
|
|
303
66
|
const possiblePaths = [
|
|
304
67
|
path.join(nodeModulesDir, '@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
305
68
|
path.join(nodeModulesDir, '@famgia/omnify/node_modules/@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
306
|
-
path.join(nodeModulesDir, '.pnpm/@famgia+omnify-types@*/node_modules/@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
307
69
|
];
|
|
308
70
|
|
|
309
71
|
let baseSchemaPath = null;
|
|
310
72
|
for (const p of possiblePaths) {
|
|
311
|
-
|
|
312
|
-
if (p.includes('*')) {
|
|
313
|
-
const dir = path.dirname(path.dirname(p));
|
|
314
|
-
if (fs.existsSync(path.dirname(dir))) {
|
|
315
|
-
const matches = fs.readdirSync(path.dirname(dir)).filter(f => f.startsWith('@famgia+omnify-types@'));
|
|
316
|
-
if (matches.length > 0) {
|
|
317
|
-
const matchPath = path.join(path.dirname(dir), matches[0], 'node_modules/@famgia/omnify-types/schemas/omnify-schema.json');
|
|
318
|
-
if (fs.existsSync(matchPath)) {
|
|
319
|
-
baseSchemaPath = matchPath;
|
|
320
|
-
break;
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
} else if (fs.existsSync(p)) {
|
|
73
|
+
if (fs.existsSync(p)) {
|
|
325
74
|
baseSchemaPath = p;
|
|
326
75
|
break;
|
|
327
76
|
}
|
|
328
77
|
}
|
|
329
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
|
+
|
|
330
93
|
if (!baseSchemaPath) {
|
|
331
94
|
console.log(' Note: Base schema not found, skipping combined schema generation');
|
|
332
95
|
return false;
|
|
@@ -335,7 +98,6 @@ function generateCombinedSchema(projectRoot) {
|
|
|
335
98
|
const baseSchema = JSON.parse(fs.readFileSync(baseSchemaPath, 'utf-8'));
|
|
336
99
|
|
|
337
100
|
// Find all @famgia/omnify-* plugins with schema contributions
|
|
338
|
-
// Check multiple possible locations (direct, nested via omnify)
|
|
339
101
|
const famgiaDirs = [
|
|
340
102
|
path.join(nodeModulesDir, '@famgia'),
|
|
341
103
|
path.join(nodeModulesDir, '@famgia/omnify/node_modules/@famgia'),
|
|
@@ -348,7 +110,6 @@ function generateCombinedSchema(projectRoot) {
|
|
|
348
110
|
|
|
349
111
|
const packages = fs.readdirSync(famgiaDir);
|
|
350
112
|
for (const pkg of packages) {
|
|
351
|
-
// Skip non-plugin packages and already processed
|
|
352
113
|
if (!pkg.startsWith('omnify-') || pkg === 'omnify-types' || pkg === 'omnify-mcp') {
|
|
353
114
|
continue;
|
|
354
115
|
}
|
|
@@ -369,14 +130,12 @@ function generateCombinedSchema(projectRoot) {
|
|
|
369
130
|
|
|
370
131
|
// Merge contributions into base schema
|
|
371
132
|
for (const { name, contribution } of pluginContributions) {
|
|
372
|
-
// Add definitions
|
|
373
133
|
if (contribution.definitions) {
|
|
374
134
|
for (const [defName, defValue] of Object.entries(contribution.definitions)) {
|
|
375
135
|
baseSchema.definitions[defName] = defValue;
|
|
376
136
|
}
|
|
377
137
|
}
|
|
378
138
|
|
|
379
|
-
// Add to PropertyDefinition oneOf
|
|
380
139
|
if (contribution.propertyTypes && baseSchema.definitions.PropertyDefinition) {
|
|
381
140
|
for (const typeName of contribution.propertyTypes) {
|
|
382
141
|
baseSchema.definitions.PropertyDefinition.oneOf.push({
|
|
@@ -388,16 +147,13 @@ function generateCombinedSchema(projectRoot) {
|
|
|
388
147
|
console.log(` Added schema contributions from @famgia/${name}`);
|
|
389
148
|
}
|
|
390
149
|
|
|
391
|
-
// Update schema $id to indicate it's combined
|
|
392
150
|
baseSchema.$id = 'omnify://combined-schema.json';
|
|
393
151
|
baseSchema.description = baseSchema.description + ' (Combined with plugin contributions)';
|
|
394
152
|
|
|
395
|
-
// Create output directory
|
|
396
153
|
if (!fs.existsSync(outputDir)) {
|
|
397
154
|
fs.mkdirSync(outputDir, { recursive: true });
|
|
398
155
|
}
|
|
399
156
|
|
|
400
|
-
// Write combined schema
|
|
401
157
|
fs.writeFileSync(outputPath, JSON.stringify(baseSchema, null, 2), 'utf-8');
|
|
402
158
|
console.log(' Generated combined JSON schema at node_modules/.omnify/combined-schema.json');
|
|
403
159
|
|
|
@@ -409,47 +165,42 @@ function generateCombinedSchema(projectRoot) {
|
|
|
409
165
|
}
|
|
410
166
|
|
|
411
167
|
function findProjectRoot() {
|
|
412
|
-
// npm/pnpm set INIT_CWD to the directory where the install was run
|
|
413
|
-
// This is more reliable than process.cwd() during postinstall
|
|
414
168
|
let dir = process.env.INIT_CWD || process.cwd();
|
|
415
|
-
|
|
416
|
-
// If we're in node_modules, go up to find the actual project
|
|
417
169
|
const nodeModulesIndex = dir.indexOf('node_modules');
|
|
418
170
|
if (nodeModulesIndex !== -1) {
|
|
419
171
|
dir = dir.substring(0, nodeModulesIndex - 1);
|
|
420
172
|
}
|
|
421
|
-
|
|
422
|
-
// Verify it's a project root by checking for package.json
|
|
423
173
|
const packageJsonPath = path.join(dir, 'package.json');
|
|
424
174
|
if (fs.existsSync(packageJsonPath)) {
|
|
425
175
|
return dir;
|
|
426
176
|
}
|
|
427
|
-
|
|
428
177
|
return null;
|
|
429
178
|
}
|
|
430
179
|
|
|
431
|
-
function
|
|
180
|
+
function copyAiGuidesToProject(projectRoot) {
|
|
432
181
|
const omnifyDir = path.join(projectRoot, '.claude', 'omnify');
|
|
182
|
+
const aiGuidesDir = path.join(__dirname, '..', 'ai-guides');
|
|
433
183
|
|
|
434
184
|
try {
|
|
435
|
-
// Create .claude/omnify directory
|
|
436
185
|
if (!fs.existsSync(omnifyDir)) {
|
|
437
186
|
fs.mkdirSync(omnifyDir, { recursive: true });
|
|
438
187
|
}
|
|
439
188
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
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);
|
|
443
194
|
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
195
|
+
if (fs.statSync(srcPath).isFile()) {
|
|
196
|
+
fs.copyFileSync(srcPath, destPath);
|
|
197
|
+
console.log(` Created .claude/omnify/${file}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
447
201
|
|
|
448
|
-
console.log(' Created .claude/omnify/schema-guide.md');
|
|
449
|
-
console.log(' Created .claude/omnify/config-guide.md');
|
|
450
202
|
return true;
|
|
451
|
-
} catch
|
|
452
|
-
console.log(' Note: Could not create .claude/omnify/ skill files');
|
|
203
|
+
} catch {
|
|
453
204
|
return false;
|
|
454
205
|
}
|
|
455
206
|
}
|
|
@@ -457,24 +208,20 @@ function createOmnifySkillFiles(projectRoot) {
|
|
|
457
208
|
function createClaudeMd(projectRoot) {
|
|
458
209
|
const claudeMdPath = path.join(projectRoot, 'CLAUDE.md');
|
|
459
210
|
|
|
460
|
-
// Check if CLAUDE.md exists
|
|
461
211
|
if (fs.existsSync(claudeMdPath)) {
|
|
462
212
|
const existingContent = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
463
213
|
|
|
464
|
-
// Check if Omnify section already exists
|
|
465
214
|
if (existingContent.includes(OMNIFY_MARKER)) {
|
|
466
215
|
console.log(' CLAUDE.md already has Omnify section, skipping...');
|
|
467
216
|
return false;
|
|
468
217
|
}
|
|
469
218
|
|
|
470
|
-
// Append Omnify section to existing CLAUDE.md
|
|
471
219
|
const newContent = existingContent.trimEnd() + '\n\n' + CLAUDE_MD_SECTION;
|
|
472
220
|
fs.writeFileSync(claudeMdPath, newContent, 'utf-8');
|
|
473
221
|
console.log(' Appended Omnify section to CLAUDE.md');
|
|
474
222
|
return true;
|
|
475
223
|
}
|
|
476
224
|
|
|
477
|
-
// Create new CLAUDE.md with just the Omnify section
|
|
478
225
|
fs.writeFileSync(claudeMdPath, CLAUDE_MD_SECTION, 'utf-8');
|
|
479
226
|
console.log(' Created CLAUDE.md');
|
|
480
227
|
return true;
|
|
@@ -485,12 +232,10 @@ function createCursorRules(projectRoot) {
|
|
|
485
232
|
const cursorRulesPath = path.join(cursorDir, 'omnify.md');
|
|
486
233
|
|
|
487
234
|
try {
|
|
488
|
-
// Create .cursor/rules directory
|
|
489
235
|
if (!fs.existsSync(cursorDir)) {
|
|
490
236
|
fs.mkdirSync(cursorDir, { recursive: true });
|
|
491
237
|
}
|
|
492
238
|
|
|
493
|
-
// Always overwrite - this file is fully managed by Omnify
|
|
494
239
|
fs.writeFileSync(cursorRulesPath, CURSORRULES_CONTENT, 'utf-8');
|
|
495
240
|
console.log(' Updated .cursor/rules/omnify.md');
|
|
496
241
|
return true;
|
|
@@ -505,14 +250,12 @@ function configureClaudeMcp() {
|
|
|
505
250
|
const configPath = path.join(claudeDir, 'claude_desktop_config.json');
|
|
506
251
|
|
|
507
252
|
try {
|
|
508
|
-
// Create .claude directory if not exists
|
|
509
253
|
if (!fs.existsSync(claudeDir)) {
|
|
510
254
|
fs.mkdirSync(claudeDir, { recursive: true });
|
|
511
255
|
}
|
|
512
256
|
|
|
513
257
|
let config = { mcpServers: {} };
|
|
514
258
|
|
|
515
|
-
// Read existing config if exists
|
|
516
259
|
if (fs.existsSync(configPath)) {
|
|
517
260
|
try {
|
|
518
261
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
@@ -521,38 +264,30 @@ function configureClaudeMcp() {
|
|
|
521
264
|
config.mcpServers = {};
|
|
522
265
|
}
|
|
523
266
|
} catch {
|
|
524
|
-
// Invalid JSON, start fresh
|
|
525
267
|
config = { mcpServers: {} };
|
|
526
268
|
}
|
|
527
269
|
}
|
|
528
270
|
|
|
529
|
-
// Check if omnify is already configured
|
|
530
271
|
if (config.mcpServers.omnify) {
|
|
531
272
|
console.log(' Omnify MCP already configured');
|
|
532
273
|
return false;
|
|
533
274
|
}
|
|
534
275
|
|
|
535
|
-
// Add omnify MCP server
|
|
536
276
|
config.mcpServers.omnify = MCP_CONFIG.omnify;
|
|
537
|
-
|
|
538
|
-
// Write config
|
|
539
277
|
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
540
278
|
console.log(' Configured Omnify MCP server in ~/.claude/claude_desktop_config.json');
|
|
541
279
|
return true;
|
|
542
280
|
} catch (error) {
|
|
543
|
-
// Silently fail - MCP config is optional
|
|
544
281
|
console.log(' Note: Could not auto-configure MCP (optional)');
|
|
545
282
|
return false;
|
|
546
283
|
}
|
|
547
284
|
}
|
|
548
285
|
|
|
549
286
|
function main() {
|
|
550
|
-
// Skip in CI environments
|
|
551
287
|
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) {
|
|
552
288
|
return;
|
|
553
289
|
}
|
|
554
290
|
|
|
555
|
-
// Skip if in omnify-ts monorepo (source code), but allow examples/
|
|
556
291
|
const projectDir = process.env.INIT_CWD || process.cwd();
|
|
557
292
|
if (projectDir.includes('omnify-ts') && !projectDir.includes('omnify-ts/examples')) {
|
|
558
293
|
return;
|
|
@@ -563,20 +298,12 @@ function main() {
|
|
|
563
298
|
const projectRoot = findProjectRoot();
|
|
564
299
|
|
|
565
300
|
if (projectRoot) {
|
|
566
|
-
// Generate combined JSON schema with plugin contributions
|
|
567
301
|
generateCombinedSchema(projectRoot);
|
|
568
|
-
|
|
569
|
-
// Create .claude/omnify/ skill files
|
|
570
|
-
createOmnifySkillFiles(projectRoot);
|
|
571
|
-
|
|
572
|
-
// Create or update CLAUDE.md in project root
|
|
302
|
+
copyAiGuidesToProject(projectRoot);
|
|
573
303
|
createClaudeMd(projectRoot);
|
|
574
|
-
|
|
575
|
-
// Create Cursor rules
|
|
576
304
|
createCursorRules(projectRoot);
|
|
577
305
|
}
|
|
578
306
|
|
|
579
|
-
// Configure MCP server in user's home directory
|
|
580
307
|
configureClaudeMcp();
|
|
581
308
|
|
|
582
309
|
console.log('\n✅ Omnify setup complete!\n');
|