@famgia/omnify-typescript 0.0.27 → 0.0.29

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.
@@ -0,0 +1,401 @@
1
+ # Omnify + Ant Design Integration Guide
2
+
3
+ This guide shows how to use Omnify-generated validation rules with Ant Design Forms.
4
+
5
+ ## Generated Files
6
+
7
+ Omnify generates validation rules in `rules/` directory:
8
+ - `rules/{Model}.rules.ts` - Validation rules for each model
9
+
10
+ ## File Structure
11
+
12
+ ```typescript
13
+ // rules/User.rules.ts
14
+
15
+ // Display name for model (multi-locale)
16
+ export const UserDisplayName: LocaleMap = {
17
+ ja: 'ユーザー',
18
+ en: 'User',
19
+ };
20
+
21
+ // Display names for properties (multi-locale)
22
+ export const UserPropertyDisplayNames: Record<string, LocaleMap> = {
23
+ name: { ja: '名前', en: 'Name' },
24
+ email: { ja: 'メールアドレス', en: 'Email' },
25
+ };
26
+
27
+ // Validation rules with multi-locale messages
28
+ export const UserRules: Record<string, ValidationRule[]> = {
29
+ name: [
30
+ { required: true, message: { ja: '名前は必須です', en: 'Name is required' } },
31
+ { max: 100, message: { ja: '名前は100文字以内で入力してください', en: 'Name must be at most 100 characters' } },
32
+ ],
33
+ email: [
34
+ { required: true, message: { ja: 'メールアドレスは必須です', en: 'Email is required' } },
35
+ { type: 'email', message: { ja: 'メールアドレスの形式が正しくありません', en: 'Email is not a valid email address' } },
36
+ ],
37
+ };
38
+
39
+ // Helper functions
40
+ export function getUserRules(locale: string): Record<string, Rule[]>;
41
+ export function getUserDisplayName(locale: string): string;
42
+ export function getUserPropertyDisplayName(property: string, locale: string): string;
43
+ ```
44
+
45
+ ## Basic Usage
46
+
47
+ ```tsx
48
+ import { Form, Input, Button } from 'antd';
49
+ import {
50
+ getUserRules,
51
+ getUserDisplayName,
52
+ getUserPropertyDisplayName
53
+ } from '@/types/model/rules/User.rules';
54
+
55
+ interface UserFormProps {
56
+ locale?: string;
57
+ onSubmit: (values: User) => void;
58
+ }
59
+
60
+ export function UserForm({ locale = 'ja', onSubmit }: UserFormProps) {
61
+ const [form] = Form.useForm();
62
+ const rules = getUserRules(locale);
63
+
64
+ return (
65
+ <Form
66
+ form={form}
67
+ layout="vertical"
68
+ onFinish={onSubmit}
69
+ >
70
+ <Form.Item
71
+ name="name"
72
+ label={getUserPropertyDisplayName('name', locale)}
73
+ rules={rules.name}
74
+ >
75
+ <Input placeholder={getUserPropertyDisplayName('name', locale)} />
76
+ </Form.Item>
77
+
78
+ <Form.Item
79
+ name="email"
80
+ label={getUserPropertyDisplayName('email', locale)}
81
+ rules={rules.email}
82
+ >
83
+ <Input type="email" />
84
+ </Form.Item>
85
+
86
+ <Form.Item>
87
+ <Button type="primary" htmlType="submit">
88
+ {locale === 'ja' ? '送信' : 'Submit'}
89
+ </Button>
90
+ </Form.Item>
91
+ </Form>
92
+ );
93
+ }
94
+ ```
95
+
96
+ ## With Edit Mode (Initial Values)
97
+
98
+ ```tsx
99
+ import { Form, Input, Button, Spin } from 'antd';
100
+ import { getUserRules, getUserPropertyDisplayName } from '@/types/model/rules/User.rules';
101
+ import { User } from '@/types/model';
102
+
103
+ interface UserEditFormProps {
104
+ user: User;
105
+ locale?: string;
106
+ onSubmit: (values: Partial<User>) => void;
107
+ loading?: boolean;
108
+ }
109
+
110
+ export function UserEditForm({ user, locale = 'ja', onSubmit, loading }: UserEditFormProps) {
111
+ const [form] = Form.useForm();
112
+ const rules = getUserRules(locale);
113
+
114
+ // Set initial values when user data changes
115
+ React.useEffect(() => {
116
+ form.setFieldsValue(user);
117
+ }, [user, form]);
118
+
119
+ return (
120
+ <Spin spinning={loading}>
121
+ <Form
122
+ form={form}
123
+ layout="vertical"
124
+ initialValues={user}
125
+ onFinish={onSubmit}
126
+ >
127
+ <Form.Item
128
+ name="name"
129
+ label={getUserPropertyDisplayName('name', locale)}
130
+ rules={rules.name}
131
+ >
132
+ <Input />
133
+ </Form.Item>
134
+
135
+ <Form.Item
136
+ name="email"
137
+ label={getUserPropertyDisplayName('email', locale)}
138
+ rules={rules.email}
139
+ >
140
+ <Input type="email" />
141
+ </Form.Item>
142
+
143
+ <Form.Item>
144
+ <Button type="primary" htmlType="submit" loading={loading}>
145
+ {locale === 'ja' ? '更新' : 'Update'}
146
+ </Button>
147
+ </Form.Item>
148
+ </Form>
149
+ </Spin>
150
+ );
151
+ }
152
+ ```
153
+
154
+ ## Dynamic Locale Switching
155
+
156
+ ```tsx
157
+ import { Form, Input, Select } from 'antd';
158
+ import { useState, useMemo } from 'react';
159
+ import { getUserRules, getUserPropertyDisplayName, getUserDisplayName } from '@/types/model/rules/User.rules';
160
+
161
+ export function UserFormWithLocale() {
162
+ const [locale, setLocale] = useState('ja');
163
+ const [form] = Form.useForm();
164
+
165
+ // Memoize rules to avoid recalculation
166
+ const rules = useMemo(() => getUserRules(locale), [locale]);
167
+
168
+ return (
169
+ <>
170
+ <Select value={locale} onChange={setLocale} style={{ marginBottom: 16 }}>
171
+ <Select.Option value="ja">日本語</Select.Option>
172
+ <Select.Option value="en">English</Select.Option>
173
+ <Select.Option value="vi">Tiếng Việt</Select.Option>
174
+ </Select>
175
+
176
+ <h2>{getUserDisplayName(locale)}</h2>
177
+
178
+ <Form form={form} layout="vertical">
179
+ <Form.Item
180
+ name="name"
181
+ label={getUserPropertyDisplayName('name', locale)}
182
+ rules={rules.name}
183
+ >
184
+ <Input />
185
+ </Form.Item>
186
+
187
+ <Form.Item
188
+ name="email"
189
+ label={getUserPropertyDisplayName('email', locale)}
190
+ rules={rules.email}
191
+ >
192
+ <Input type="email" />
193
+ </Form.Item>
194
+ </Form>
195
+ </>
196
+ );
197
+ }
198
+ ```
199
+
200
+ ## With Table Columns
201
+
202
+ ```tsx
203
+ import { Table, TableColumnsType } from 'antd';
204
+ import { getUserPropertyDisplayName } from '@/types/model/rules/User.rules';
205
+ import { User } from '@/types/model';
206
+
207
+ interface UserTableProps {
208
+ users: User[];
209
+ locale?: string;
210
+ loading?: boolean;
211
+ }
212
+
213
+ export function UserTable({ users, locale = 'ja', loading }: UserTableProps) {
214
+ const columns: TableColumnsType<User> = [
215
+ {
216
+ title: getUserPropertyDisplayName('name', locale),
217
+ dataIndex: 'name',
218
+ key: 'name',
219
+ },
220
+ {
221
+ title: getUserPropertyDisplayName('email', locale),
222
+ dataIndex: 'email',
223
+ key: 'email',
224
+ },
225
+ ];
226
+
227
+ return (
228
+ <Table
229
+ columns={columns}
230
+ dataSource={users}
231
+ rowKey="id"
232
+ loading={loading}
233
+ />
234
+ );
235
+ }
236
+ ```
237
+
238
+ ## Custom Form Hook
239
+
240
+ ```tsx
241
+ import { Form, FormInstance } from 'antd';
242
+ import { useMemo } from 'react';
243
+
244
+ // Generic hook for any model's rules
245
+ export function useModelForm<T>(
246
+ getRules: (locale: string) => Record<string, any[]>,
247
+ locale: string = 'ja'
248
+ ): {
249
+ form: FormInstance<T>;
250
+ rules: Record<string, any[]>;
251
+ } {
252
+ const [form] = Form.useForm<T>();
253
+ const rules = useMemo(() => getRules(locale), [getRules, locale]);
254
+
255
+ return { form, rules };
256
+ }
257
+
258
+ // Usage
259
+ import { getUserRules } from '@/types/model/rules/User.rules';
260
+
261
+ function MyComponent() {
262
+ const { form, rules } = useModelForm(getUserRules, 'ja');
263
+
264
+ return (
265
+ <Form form={form}>
266
+ <Form.Item name="name" rules={rules.name}>
267
+ <Input />
268
+ </Form.Item>
269
+ </Form>
270
+ );
271
+ }
272
+ ```
273
+
274
+ ## Modal Form
275
+
276
+ ```tsx
277
+ import { Modal, Form, Input, message } from 'antd';
278
+ import { getUserRules, getUserDisplayName, getUserPropertyDisplayName } from '@/types/model/rules/User.rules';
279
+ import { User } from '@/types/model';
280
+
281
+ interface UserModalFormProps {
282
+ open: boolean;
283
+ onClose: () => void;
284
+ onSubmit: (values: Partial<User>) => Promise<void>;
285
+ initialValues?: Partial<User>;
286
+ locale?: string;
287
+ }
288
+
289
+ export function UserModalForm({
290
+ open,
291
+ onClose,
292
+ onSubmit,
293
+ initialValues,
294
+ locale = 'ja'
295
+ }: UserModalFormProps) {
296
+ const [form] = Form.useForm();
297
+ const [loading, setLoading] = useState(false);
298
+ const rules = getUserRules(locale);
299
+
300
+ const handleSubmit = async () => {
301
+ try {
302
+ const values = await form.validateFields();
303
+ setLoading(true);
304
+ await onSubmit(values);
305
+ message.success(locale === 'ja' ? '保存しました' : 'Saved successfully');
306
+ form.resetFields();
307
+ onClose();
308
+ } catch (error) {
309
+ if (error instanceof Error) {
310
+ message.error(error.message);
311
+ }
312
+ } finally {
313
+ setLoading(false);
314
+ }
315
+ };
316
+
317
+ return (
318
+ <Modal
319
+ title={getUserDisplayName(locale)}
320
+ open={open}
321
+ onCancel={onClose}
322
+ onOk={handleSubmit}
323
+ confirmLoading={loading}
324
+ okText={locale === 'ja' ? '保存' : 'Save'}
325
+ cancelText={locale === 'ja' ? 'キャンセル' : 'Cancel'}
326
+ >
327
+ <Form
328
+ form={form}
329
+ layout="vertical"
330
+ initialValues={initialValues}
331
+ >
332
+ <Form.Item
333
+ name="name"
334
+ label={getUserPropertyDisplayName('name', locale)}
335
+ rules={rules.name}
336
+ >
337
+ <Input />
338
+ </Form.Item>
339
+
340
+ <Form.Item
341
+ name="email"
342
+ label={getUserPropertyDisplayName('email', locale)}
343
+ rules={rules.email}
344
+ >
345
+ <Input type="email" />
346
+ </Form.Item>
347
+ </Form>
348
+ </Modal>
349
+ );
350
+ }
351
+ ```
352
+
353
+ ## Validation Rule Types
354
+
355
+ Omnify generates these rule types based on schema:
356
+
357
+ | Schema | Generated Rule |
358
+ |--------|---------------|
359
+ | `required: true` | `{ required: true, message: {...} }` |
360
+ | `type: Email` | `{ type: 'email', message: {...} }` |
361
+ | `maxLength: N` | `{ max: N, message: {...} }` |
362
+ | `minLength: N` | `{ min: N, message: {...} }` |
363
+ | `max: N` (numeric) | `{ max: N, type: 'number', message: {...} }` |
364
+ | `min: N` (numeric) | `{ min: N, type: 'number', message: {...} }` |
365
+ | `pattern: regex` | `{ pattern: /regex/, message: {...} }` |
366
+
367
+ ## Built-in Validation Messages
368
+
369
+ Omnify includes templates for 5 languages:
370
+ - Japanese (ja)
371
+ - English (en)
372
+ - Vietnamese (vi)
373
+ - Korean (ko)
374
+ - Chinese (zh)
375
+
376
+ Custom templates can be configured in `omnify.config.ts`:
377
+
378
+ ```typescript
379
+ export default defineConfig({
380
+ output: {
381
+ typescript: {
382
+ validationTemplates: {
383
+ required: {
384
+ ja: '${displayName}を入力してください',
385
+ en: '${displayName} is required',
386
+ vi: '${displayName} là bắt buộc',
387
+ },
388
+ maxLength: {
389
+ ja: '${displayName}は${max}文字以内です',
390
+ en: '${displayName} must be at most ${max} characters',
391
+ },
392
+ },
393
+ },
394
+ },
395
+ });
396
+ ```
397
+
398
+ Available placeholders:
399
+ - `${displayName}` - Property display name
400
+ - `${min}` - Minimum value/length
401
+ - `${max}` - Maximum value/length
@@ -0,0 +1,254 @@
1
+ # Omnify TypeScript Generator Guide
2
+
3
+ This guide covers TypeScript-specific features and generated code patterns for Omnify.
4
+
5
+ ## Generated Files
6
+
7
+ When you run `npx omnify generate`, the following TypeScript files are generated:
8
+
9
+ - `base/*.ts` - Base model interfaces
10
+ - `enum/*.ts` - Enum types with multi-locale labels
11
+ - `rules/*.ts` - Ant Design compatible validation rules
12
+
13
+ ## Type Generation
14
+
15
+ ### Object Schema → Interface
16
+
17
+ ```yaml
18
+ # yaml-language-server: $schema=./node_modules/.omnify/combined-schema.json
19
+ name: User
20
+ properties:
21
+ id:
22
+ type: BigInt
23
+ required: true
24
+ name:
25
+ type: String
26
+ required: true
27
+ maxLength: 255
28
+ email:
29
+ type: String
30
+ required: true
31
+ unique: true
32
+ profile:
33
+ type: Json
34
+ createdAt:
35
+ type: DateTime
36
+ ```
37
+
38
+ Generated:
39
+ ```typescript
40
+ export interface User {
41
+ id: number;
42
+ name: string;
43
+ email: string;
44
+ profile: Record<string, unknown> | null;
45
+ createdAt: Date | null;
46
+ }
47
+ ```
48
+
49
+ ## Type Mapping
50
+
51
+ | Schema Type | TypeScript Type |
52
+ |-------------|-----------------|
53
+ | `String` | `string` |
54
+ | `LongText` | `string` |
55
+ | `Int` | `number` |
56
+ | `BigInt` | `number` |
57
+ | `Float` | `number` |
58
+ | `Boolean` | `boolean` |
59
+ | `Date` | `Date` |
60
+ | `DateTime` | `Date` |
61
+ | `Json` | `Record<string, unknown>` |
62
+ | `EnumRef` | Generated enum type |
63
+ | `Association` | Related model type / array |
64
+
65
+ ## Enum Generation (Multi-locale)
66
+
67
+ ```yaml
68
+ # schemas/PostStatus.yaml
69
+ name: PostStatus
70
+ kind: enum
71
+ displayName:
72
+ ja: 投稿ステータス
73
+ en: Post Status
74
+ values:
75
+ draft:
76
+ ja: 下書き
77
+ en: Draft
78
+ published:
79
+ ja: 公開済み
80
+ en: Published
81
+ archived:
82
+ ja: アーカイブ
83
+ en: Archived
84
+ ```
85
+
86
+ Generated:
87
+ ```typescript
88
+ export const PostStatus = {
89
+ draft: 'draft',
90
+ published: 'published',
91
+ archived: 'archived',
92
+ } as const;
93
+
94
+ export type PostStatus = typeof PostStatus[keyof typeof PostStatus];
95
+
96
+ // Multi-locale labels
97
+ export const PostStatusLabels: Record<PostStatus, Record<string, string>> = {
98
+ draft: { ja: '下書き', en: 'Draft' },
99
+ published: { ja: '公開済み', en: 'Published' },
100
+ archived: { ja: 'アーカイブ', en: 'Archived' },
101
+ };
102
+
103
+ // Get label for specific locale
104
+ export function getPostStatusLabel(value: PostStatus, locale: string = 'en'): string {
105
+ return PostStatusLabels[value]?.[locale] ?? PostStatusLabels[value]?.['en'] ?? value;
106
+ }
107
+
108
+ // Helper functions
109
+ export const PostStatusValues = Object.values(PostStatus);
110
+ export function isPostStatus(value: unknown): value is PostStatus {
111
+ return PostStatusValues.includes(value as PostStatus);
112
+ }
113
+ ```
114
+
115
+ ## Validation Rules (Ant Design)
116
+
117
+ Omnify generates Ant Design compatible validation rules in `rules/` directory.
118
+
119
+ **See detailed guide:** `.claude/omnify/antdesign-guide.md`
120
+
121
+ Quick example:
122
+ ```tsx
123
+ import { Form, Input } from 'antd';
124
+ import { getUserRules, getUserPropertyDisplayName } from './types/model/rules/User.rules';
125
+
126
+ function UserForm({ locale = 'ja' }) {
127
+ const rules = getUserRules(locale);
128
+ return (
129
+ <Form>
130
+ <Form.Item name="name" label={getUserPropertyDisplayName('name', locale)} rules={rules.name}>
131
+ <Input />
132
+ </Form.Item>
133
+ </Form>
134
+ );
135
+ }
136
+ ```
137
+
138
+ ## Association Types
139
+
140
+ ### ManyToOne
141
+ ```yaml
142
+ author:
143
+ type: Association
144
+ relation: ManyToOne
145
+ target: User
146
+ ```
147
+
148
+ Generated:
149
+ ```typescript
150
+ export interface Post {
151
+ authorId: number;
152
+ author?: User; // Optional: loaded relation
153
+ }
154
+ ```
155
+
156
+ ### OneToMany
157
+ ```yaml
158
+ posts:
159
+ type: Association
160
+ relation: OneToMany
161
+ target: Post
162
+ ```
163
+
164
+ Generated:
165
+ ```typescript
166
+ export interface User {
167
+ posts?: Post[]; // Optional: loaded relation array
168
+ }
169
+ ```
170
+
171
+ ### ManyToMany
172
+ ```yaml
173
+ tags:
174
+ type: Association
175
+ relation: ManyToMany
176
+ target: Tag
177
+ ```
178
+
179
+ Generated:
180
+ ```typescript
181
+ export interface Post {
182
+ tags?: Tag[]; // Optional: loaded relation array
183
+ }
184
+ ```
185
+
186
+ ## Nullable Fields
187
+
188
+ Fields without `required: true` are nullable:
189
+
190
+ ```yaml
191
+ description:
192
+ type: LongText # No required: true
193
+ ```
194
+
195
+ Generated:
196
+ ```typescript
197
+ description: string | null;
198
+ ```
199
+
200
+ ## Using Generated Types
201
+
202
+ ```typescript
203
+ import { User, Post, PostStatus, isPostStatus } from './types/omnify-types';
204
+
205
+ // Type-safe object creation
206
+ const user: User = {
207
+ id: 1,
208
+ name: 'John',
209
+ email: 'john@example.com',
210
+ profile: null,
211
+ createdAt: new Date(),
212
+ };
213
+
214
+ // Enum usage
215
+ const status: PostStatus = PostStatus.draft;
216
+
217
+ // Type guard
218
+ function handleStatus(value: unknown) {
219
+ if (isPostStatus(value)) {
220
+ console.log('Valid status:', value);
221
+ }
222
+ }
223
+ ```
224
+
225
+ ## Commands
226
+
227
+ ```bash
228
+ # Generate TypeScript types
229
+ npx omnify generate
230
+
231
+ # Force regenerate all files
232
+ npx omnify generate --force
233
+ ```
234
+
235
+ ## Configuration
236
+
237
+ ```typescript
238
+ // omnify.config.ts
239
+ import { defineConfig } from '@famgia/omnify';
240
+
241
+ export default defineConfig({
242
+ schemasDir: './schemas',
243
+ output: {
244
+ typescript: {
245
+ path: './src/types/model',
246
+ generateRules: true, // Generate Ant Design validation rules
247
+ },
248
+ },
249
+ locale: {
250
+ locales: ['ja', 'en'],
251
+ defaultLocale: 'ja',
252
+ },
253
+ });
254
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@famgia/omnify-typescript",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "description": "TypeScript type definitions generator for Omnify schemas",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -30,7 +30,8 @@
30
30
  },
31
31
  "files": [
32
32
  "dist",
33
- "scripts"
33
+ "scripts",
34
+ "ai-guides"
34
35
  ],
35
36
  "keywords": [
36
37
  "omnify",
@@ -47,7 +48,7 @@
47
48
  "directory": "packages/typescript-generator"
48
49
  },
49
50
  "dependencies": {
50
- "@famgia/omnify-types": "0.0.35"
51
+ "@famgia/omnify-types": "0.0.37"
51
52
  },
52
53
  "devDependencies": {
53
54
  "tsup": "^8.5.1",
@@ -2,228 +2,10 @@
2
2
 
3
3
  import fs from 'fs';
4
4
  import path from 'path';
5
+ import { fileURLToPath } from 'url';
5
6
 
6
- const TYPESCRIPT_GUIDE_CONTENT = `# Omnify TypeScript Generator Guide
7
-
8
- This guide covers TypeScript-specific features and generated code patterns for Omnify.
9
-
10
- ## Generated Files
11
-
12
- When you run \`npx omnify generate\`, the following TypeScript files are generated:
13
-
14
- - \`types/omnify-types.ts\` - All type definitions
15
- - \`types/enums.ts\` - Enum types and helpers
16
-
17
- ## Type Generation
18
-
19
- ### Object Schema → Interface
20
-
21
- \`\`\`yaml
22
- # yaml-language-server: $schema=./node_modules/.omnify/combined-schema.json
23
- name: User
24
- properties:
25
- id:
26
- type: BigInt
27
- required: true
28
- name:
29
- type: String
30
- required: true
31
- maxLength: 255
32
- email:
33
- type: String
34
- required: true
35
- unique: true
36
- profile:
37
- type: Json
38
- createdAt:
39
- type: DateTime
40
- \`\`\`
41
-
42
- Generated:
43
- \`\`\`typescript
44
- export interface User {
45
- id: number;
46
- name: string;
47
- email: string;
48
- profile: Record<string, unknown> | null;
49
- createdAt: Date | null;
50
- }
51
- \`\`\`
52
-
53
- ## Type Mapping
54
-
55
- | Schema Type | TypeScript Type |
56
- |-------------|-----------------|
57
- | \`String\` | \`string\` |
58
- | \`LongText\` | \`string\` |
59
- | \`Int\` | \`number\` |
60
- | \`BigInt\` | \`number\` |
61
- | \`Float\` | \`number\` |
62
- | \`Boolean\` | \`boolean\` |
63
- | \`Date\` | \`Date\` |
64
- | \`DateTime\` | \`Date\` |
65
- | \`Json\` | \`Record<string, unknown>\` |
66
- | \`EnumRef\` | Generated enum type |
67
- | \`Association\` | Related model type / array |
68
-
69
- ## Enum Generation
70
-
71
- \`\`\`yaml
72
- # schemas/PostStatus.yaml
73
- name: PostStatus
74
- kind: enum
75
- values:
76
- draft: 下書き
77
- published: 公開済み
78
- archived: アーカイブ
79
- \`\`\`
80
-
81
- Generated:
82
- \`\`\`typescript
83
- export const PostStatus = {
84
- draft: 'draft',
85
- published: 'published',
86
- archived: 'archived',
87
- } as const;
88
-
89
- export type PostStatus = typeof PostStatus[keyof typeof PostStatus];
90
-
91
- // Helper functions
92
- export const PostStatusValues = Object.values(PostStatus);
93
- export const PostStatusKeys = Object.keys(PostStatus) as (keyof typeof PostStatus)[];
94
-
95
- export function isPostStatus(value: unknown): value is PostStatus {
96
- return PostStatusValues.includes(value as PostStatus);
97
- }
98
-
99
- // Display names for UI
100
- export const PostStatusDisplayNames: Record<PostStatus, string> = {
101
- draft: '下書き',
102
- published: '公開済み',
103
- archived: 'アーカイブ',
104
- };
105
-
106
- export function getPostStatusDisplayName(value: PostStatus): string {
107
- return PostStatusDisplayNames[value];
108
- }
109
- \`\`\`
110
-
111
- ## Association Types
112
-
113
- ### ManyToOne
114
- \`\`\`yaml
115
- author:
116
- type: Association
117
- relation: ManyToOne
118
- target: User
119
- \`\`\`
120
-
121
- Generated:
122
- \`\`\`typescript
123
- export interface Post {
124
- authorId: number;
125
- author?: User; // Optional: loaded relation
126
- }
127
- \`\`\`
128
-
129
- ### OneToMany
130
- \`\`\`yaml
131
- posts:
132
- type: Association
133
- relation: OneToMany
134
- target: Post
135
- \`\`\`
136
-
137
- Generated:
138
- \`\`\`typescript
139
- export interface User {
140
- posts?: Post[]; // Optional: loaded relation array
141
- }
142
- \`\`\`
143
-
144
- ### ManyToMany
145
- \`\`\`yaml
146
- tags:
147
- type: Association
148
- relation: ManyToMany
149
- target: Tag
150
- \`\`\`
151
-
152
- Generated:
153
- \`\`\`typescript
154
- export interface Post {
155
- tags?: Tag[]; // Optional: loaded relation array
156
- }
157
- \`\`\`
158
-
159
- ## Nullable Fields
160
-
161
- Fields without \`required: true\` are nullable:
162
-
163
- \`\`\`yaml
164
- description:
165
- type: LongText # No required: true
166
- \`\`\`
167
-
168
- Generated:
169
- \`\`\`typescript
170
- description: string | null;
171
- \`\`\`
172
-
173
- ## Using Generated Types
174
-
175
- \`\`\`typescript
176
- import { User, Post, PostStatus, isPostStatus } from './types/omnify-types';
177
-
178
- // Type-safe object creation
179
- const user: User = {
180
- id: 1,
181
- name: 'John',
182
- email: 'john@example.com',
183
- profile: null,
184
- createdAt: new Date(),
185
- };
186
-
187
- // Enum usage
188
- const status: PostStatus = PostStatus.draft;
189
-
190
- // Type guard
191
- function handleStatus(value: unknown) {
192
- if (isPostStatus(value)) {
193
- console.log('Valid status:', value);
194
- }
195
- }
196
- \`\`\`
197
-
198
- ## Commands
199
-
200
- \`\`\`bash
201
- # Generate TypeScript types
202
- npx omnify generate --typescript
203
-
204
- # Generate to specific output
205
- npx omnify generate --typescript --output ./src/types
206
-
207
- # Watch for changes
208
- npx omnify watch --typescript
209
- \`\`\`
210
-
211
- ## Configuration
212
-
213
- \`\`\`javascript
214
- // omnify.config.js
215
- export default {
216
- schemasDir: './schemas',
217
- typescript: {
218
- outputDir: './types',
219
- outputFile: 'omnify-types.ts',
220
- enumsFile: 'enums.ts',
221
- generateHelpers: true, // Generate enum helpers
222
- strictNullChecks: true // Use | null for optional
223
- }
224
- };
225
- \`\`\`
226
- `;
7
+ const __filename = fileURLToPath(import.meta.url);
8
+ const __dirname = path.dirname(__filename);
227
9
 
228
10
  function findProjectRoot() {
229
11
  // npm/pnpm set INIT_CWD to the directory where the install was run
@@ -239,17 +21,30 @@ function findProjectRoot() {
239
21
  return null;
240
22
  }
241
23
 
242
- function createTypescriptSkillFile(projectRoot) {
24
+ function copyAiGuidesToProject(projectRoot) {
243
25
  const omnifyDir = path.join(projectRoot, '.claude', 'omnify');
26
+ const aiGuidesDir = path.join(__dirname, '..', 'ai-guides');
244
27
 
245
28
  try {
29
+ // Create target directory
246
30
  if (!fs.existsSync(omnifyDir)) {
247
31
  fs.mkdirSync(omnifyDir, { recursive: true });
248
32
  }
249
33
 
250
- const guidePath = path.join(omnifyDir, 'typescript-guide.md');
251
- fs.writeFileSync(guidePath, TYPESCRIPT_GUIDE_CONTENT, 'utf-8');
252
- console.log(' Created .claude/omnify/typescript-guide.md');
34
+ // Copy all files from ai-guides directory
35
+ if (fs.existsSync(aiGuidesDir)) {
36
+ const files = fs.readdirSync(aiGuidesDir);
37
+ for (const file of files) {
38
+ const srcPath = path.join(aiGuidesDir, file);
39
+ const destPath = path.join(omnifyDir, file);
40
+
41
+ if (fs.statSync(srcPath).isFile()) {
42
+ fs.copyFileSync(srcPath, destPath);
43
+ console.log(` Created .claude/omnify/${file}`);
44
+ }
45
+ }
46
+ }
47
+
253
48
  return true;
254
49
  } catch {
255
50
  return false;
@@ -269,7 +64,7 @@ function main() {
269
64
 
270
65
  const projectRoot = findProjectRoot();
271
66
  if (projectRoot) {
272
- createTypescriptSkillFile(projectRoot);
67
+ copyAiGuidesToProject(projectRoot);
273
68
  }
274
69
  }
275
70