@famgia/omnify 1.0.89 → 1.0.91
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/schema-guide.md +38 -0
- package/package.json +9 -9
- package/scripts/postinstall.js +154 -287
|
@@ -75,6 +75,10 @@ properties:
|
|
|
75
75
|
displayName:
|
|
76
76
|
ja: 名前
|
|
77
77
|
en: Name
|
|
78
|
+
placeholder: # Placeholder for form inputs (multi-language)
|
|
79
|
+
ja: 名前を入力
|
|
80
|
+
en: Enter name
|
|
81
|
+
vi: Nhập tên
|
|
78
82
|
nullable: false # Nullable (default: false)
|
|
79
83
|
unique: true # Unique constraint
|
|
80
84
|
primary: true # Primary key (use with options.id: false)
|
|
@@ -84,6 +88,40 @@ properties:
|
|
|
84
88
|
maxLength: 255
|
|
85
89
|
```
|
|
86
90
|
|
|
91
|
+
## Placeholder for Compound Types
|
|
92
|
+
|
|
93
|
+
For compound types (like JapaneseName, JapaneseAddress), you can customize placeholders per field:
|
|
94
|
+
|
|
95
|
+
```yaml
|
|
96
|
+
properties:
|
|
97
|
+
name:
|
|
98
|
+
type: JapaneseName
|
|
99
|
+
displayName:
|
|
100
|
+
ja: 氏名
|
|
101
|
+
en: Full Name
|
|
102
|
+
fields: # Per-field overrides for compound types
|
|
103
|
+
Lastname:
|
|
104
|
+
placeholder: # Override default placeholder
|
|
105
|
+
ja: 姓を入力
|
|
106
|
+
en: Enter last name
|
|
107
|
+
Firstname:
|
|
108
|
+
placeholder:
|
|
109
|
+
ja: 名を入力
|
|
110
|
+
en: Enter first name
|
|
111
|
+
KanaLastname:
|
|
112
|
+
nullable: true
|
|
113
|
+
placeholder:
|
|
114
|
+
ja: セイ(カナ)
|
|
115
|
+
en: Last name (Kana)
|
|
116
|
+
KanaFirstname:
|
|
117
|
+
nullable: true
|
|
118
|
+
placeholder:
|
|
119
|
+
ja: メイ(カナ)
|
|
120
|
+
en: First name (Kana)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Note**: Compound types from plugins (like `@famgia/omnify-japan`) come with default placeholders for common locales (en, ja, vi). You can override them in your schema as shown above.
|
|
124
|
+
|
|
87
125
|
## Association Relations
|
|
88
126
|
|
|
89
127
|
### ManyToOne (N:1)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@famgia/omnify",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.91",
|
|
4
4
|
"description": "Schema-driven database migration system with TypeScript types and Laravel migrations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
"README.md"
|
|
26
26
|
],
|
|
27
27
|
"dependencies": {
|
|
28
|
-
"@famgia/omnify-
|
|
29
|
-
"@famgia/omnify-
|
|
30
|
-
"@famgia/omnify-
|
|
31
|
-
"@famgia/omnify-
|
|
32
|
-
"@famgia/omnify-
|
|
33
|
-
"@famgia/omnify-
|
|
34
|
-
"@famgia/omnify-
|
|
35
|
-
"@famgia/omnify-japan": "0.0.
|
|
28
|
+
"@famgia/omnify-cli": "0.0.87",
|
|
29
|
+
"@famgia/omnify-core": "0.0.81",
|
|
30
|
+
"@famgia/omnify-types": "0.0.79",
|
|
31
|
+
"@famgia/omnify-laravel": "0.0.90",
|
|
32
|
+
"@famgia/omnify-typescript": "0.0.69",
|
|
33
|
+
"@famgia/omnify-mcp": "0.0.67",
|
|
34
|
+
"@famgia/omnify-atlas": "0.0.75",
|
|
35
|
+
"@famgia/omnify-japan": "0.0.74"
|
|
36
36
|
},
|
|
37
37
|
"keywords": [
|
|
38
38
|
"omnify",
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* @famgia/omnify postinstall
|
|
5
|
+
*
|
|
6
|
+
* Sets up AI assistant integration:
|
|
7
|
+
* 1. Generate combined JSON Schema (for YAML validation in editors)
|
|
8
|
+
* 2. Copy AI guides to .claude/omnify/
|
|
9
|
+
* 3. Create/update CLAUDE.md
|
|
10
|
+
* 4. Create .cursor/rules/omnify.md
|
|
11
|
+
* 5. Configure Claude MCP server
|
|
12
|
+
*/
|
|
13
|
+
|
|
3
14
|
import fs from 'fs';
|
|
4
15
|
import path from 'path';
|
|
5
16
|
import os from 'os';
|
|
@@ -8,7 +19,10 @@ import { fileURLToPath } from 'url';
|
|
|
8
19
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
20
|
const __dirname = path.dirname(__filename);
|
|
10
21
|
|
|
11
|
-
//
|
|
22
|
+
// ============================================================================
|
|
23
|
+
// Content Templates
|
|
24
|
+
// ============================================================================
|
|
25
|
+
|
|
12
26
|
const CLAUDE_MD_SECTION = `## Omnify
|
|
13
27
|
|
|
14
28
|
This project uses Omnify for schema-driven code generation.
|
|
@@ -18,15 +32,13 @@ This project uses Omnify for schema-driven code generation.
|
|
|
18
32
|
- \`config-guide.md\` - Configuration (omnify.config.ts)
|
|
19
33
|
- \`laravel-guide.md\` - Laravel generator (if installed)
|
|
20
34
|
- \`typescript-guide.md\` - TypeScript generator (if installed)
|
|
21
|
-
- \`antdesign-guide.md\` - Ant Design Form integration (if installed)
|
|
22
35
|
|
|
23
36
|
**Commands**:
|
|
24
37
|
- \`npx omnify generate\` - Generate code from schemas
|
|
25
38
|
- \`npx omnify validate\` - Validate schemas
|
|
26
39
|
`;
|
|
27
40
|
|
|
28
|
-
|
|
29
|
-
const CURSORRULES_CONTENT = `# Omnify Schema Rules
|
|
41
|
+
const CURSOR_RULES = `# Omnify Schema Rules
|
|
30
42
|
|
|
31
43
|
This project uses Omnify for schema-driven code generation.
|
|
32
44
|
Schemas are in \`schemas/\` directory with \`.yaml\` extension.
|
|
@@ -36,358 +48,213 @@ For detailed documentation, read these files:
|
|
|
36
48
|
- .claude/omnify/config-guide.md - Configuration (omnify.config.ts)
|
|
37
49
|
- .claude/omnify/laravel-guide.md - Laravel generator (if exists)
|
|
38
50
|
- .claude/omnify/typescript-guide.md - TypeScript generator (if exists)
|
|
39
|
-
- .claude/omnify/antdesign-guide.md - Ant Design Form integration (if exists)
|
|
40
51
|
|
|
41
52
|
Commands:
|
|
42
53
|
- npx omnify generate - Generate code from schemas
|
|
43
54
|
- npx omnify validate - Validate all schemas
|
|
44
55
|
`;
|
|
45
56
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Helper Functions
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
function findProjectRoot() {
|
|
62
|
+
let dir = process.env.INIT_CWD || process.cwd();
|
|
63
|
+
const idx = dir.indexOf('node_modules');
|
|
64
|
+
if (idx !== -1) dir = dir.substring(0, idx - 1);
|
|
65
|
+
return fs.existsSync(path.join(dir, 'package.json')) ? dir : null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function copyFiles(srcDir, destDir) {
|
|
69
|
+
if (!fs.existsSync(srcDir)) return [];
|
|
70
|
+
if (!fs.existsSync(destDir)) fs.mkdirSync(destDir, { recursive: true });
|
|
71
|
+
|
|
72
|
+
const copied = [];
|
|
73
|
+
for (const file of fs.readdirSync(srcDir)) {
|
|
74
|
+
const src = path.join(srcDir, file);
|
|
75
|
+
if (fs.statSync(src).isFile()) {
|
|
76
|
+
fs.copyFileSync(src, path.join(destDir, file));
|
|
77
|
+
copied.push(file);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return copied;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// ============================================================================
|
|
84
|
+
// Setup Functions
|
|
85
|
+
// ============================================================================
|
|
52
86
|
|
|
53
|
-
/**
|
|
54
|
-
* Generate combined JSON Schema from base schema + all plugin contributions
|
|
55
|
-
*/
|
|
56
87
|
function generateCombinedSchema(projectRoot) {
|
|
57
|
-
const
|
|
58
|
-
const outputDir = path.join(
|
|
88
|
+
const nodeModules = path.join(projectRoot, 'node_modules');
|
|
89
|
+
const outputDir = path.join(nodeModules, '.omnify');
|
|
59
90
|
const outputPath = path.join(outputDir, 'combined-schema.json');
|
|
60
91
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
];
|
|
67
|
-
|
|
68
|
-
let baseSchemaPath = null;
|
|
69
|
-
for (const p of possiblePaths) {
|
|
70
|
-
if (fs.existsSync(p)) {
|
|
71
|
-
baseSchemaPath = p;
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
92
|
+
// Find base schema
|
|
93
|
+
const searchPaths = [
|
|
94
|
+
path.join(nodeModules, '@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
95
|
+
path.join(nodeModules, '@famgia/omnify/node_modules/@famgia/omnify-types/schemas/omnify-schema.json'),
|
|
96
|
+
];
|
|
75
97
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const matchPath = path.join(pnpmDir, matches[0], 'node_modules/@famgia/omnify-types/schemas/omnify-schema.json');
|
|
83
|
-
if (fs.existsSync(matchPath)) {
|
|
84
|
-
baseSchemaPath = matchPath;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
}
|
|
98
|
+
// Also check pnpm hoisted location
|
|
99
|
+
const pnpmDir = path.join(nodeModules, '.pnpm');
|
|
100
|
+
if (fs.existsSync(pnpmDir)) {
|
|
101
|
+
const match = fs.readdirSync(pnpmDir).find(f => f.startsWith('@famgia+omnify-types@'));
|
|
102
|
+
if (match) {
|
|
103
|
+
searchPaths.push(path.join(pnpmDir, match, 'node_modules/@famgia/omnify-types/schemas/omnify-schema.json'));
|
|
88
104
|
}
|
|
105
|
+
}
|
|
89
106
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
107
|
+
const baseSchemaPath = searchPaths.find(p => fs.existsSync(p));
|
|
108
|
+
if (!baseSchemaPath) return false;
|
|
94
109
|
|
|
110
|
+
try {
|
|
95
111
|
const baseSchema = JSON.parse(fs.readFileSync(baseSchemaPath, 'utf-8'));
|
|
96
112
|
|
|
97
|
-
// Find
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
const pluginContributions = [];
|
|
103
|
-
const processedPlugins = new Set();
|
|
104
|
-
|
|
105
|
-
for (const famgiaDir of famgiaDirs) {
|
|
106
|
-
if (!fs.existsSync(famgiaDir)) continue;
|
|
107
|
-
|
|
108
|
-
const packages = fs.readdirSync(famgiaDir);
|
|
109
|
-
for (const pkg of packages) {
|
|
110
|
-
if (!pkg.startsWith('omnify-') || pkg === 'omnify-types' || pkg === 'omnify-mcp') {
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
if (processedPlugins.has(pkg)) continue;
|
|
113
|
+
// Find plugin schema contributions
|
|
114
|
+
const famgiaDir = path.join(nodeModules, '@famgia');
|
|
115
|
+
if (fs.existsSync(famgiaDir)) {
|
|
116
|
+
for (const pkg of fs.readdirSync(famgiaDir)) {
|
|
117
|
+
if (!pkg.startsWith('omnify-') || pkg === 'omnify-types' || pkg === 'omnify-mcp') continue;
|
|
114
118
|
|
|
115
119
|
const contributionPath = path.join(famgiaDir, pkg, 'schemas/schema-contribution.json');
|
|
116
120
|
if (fs.existsSync(contributionPath)) {
|
|
117
121
|
try {
|
|
118
122
|
const contribution = JSON.parse(fs.readFileSync(contributionPath, 'utf-8'));
|
|
119
|
-
pluginContributions.push({ name: pkg, contribution });
|
|
120
|
-
processedPlugins.add(pkg);
|
|
121
|
-
} catch {
|
|
122
|
-
// Invalid JSON, skip
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
123
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
baseSchema.definitions[defName] = defValue;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
124
|
+
// Merge definitions
|
|
125
|
+
if (contribution.definitions) {
|
|
126
|
+
Object.assign(baseSchema.definitions, contribution.definitions);
|
|
127
|
+
}
|
|
135
128
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
129
|
+
// Add property types
|
|
130
|
+
if (contribution.propertyTypes && baseSchema.definitions.PropertyDefinition) {
|
|
131
|
+
for (const typeName of contribution.propertyTypes) {
|
|
132
|
+
baseSchema.definitions.PropertyDefinition.oneOf.push({
|
|
133
|
+
"$ref": `#/definitions/${typeName}`
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
} catch { /* skip invalid */ }
|
|
141
138
|
}
|
|
142
139
|
}
|
|
143
|
-
|
|
144
|
-
console.log(` Added schema contributions from @famgia/${name}`);
|
|
145
140
|
}
|
|
146
141
|
|
|
147
142
|
baseSchema.$id = 'omnify://combined-schema.json';
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (!fs.existsSync(outputDir)) {
|
|
151
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
fs.writeFileSync(outputPath, JSON.stringify(baseSchema, null, 2), 'utf-8');
|
|
155
|
-
console.log(' Generated combined JSON schema at node_modules/.omnify/combined-schema.json');
|
|
156
|
-
|
|
143
|
+
if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });
|
|
144
|
+
fs.writeFileSync(outputPath, JSON.stringify(baseSchema, null, 2));
|
|
157
145
|
return true;
|
|
158
|
-
} catch
|
|
159
|
-
console.log(' Note: Could not generate combined schema');
|
|
146
|
+
} catch {
|
|
160
147
|
return false;
|
|
161
148
|
}
|
|
162
149
|
}
|
|
163
150
|
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
const
|
|
167
|
-
|
|
168
|
-
dir = dir.substring(0, nodeModulesIndex - 1);
|
|
169
|
-
}
|
|
170
|
-
const packageJsonPath = path.join(dir, 'package.json');
|
|
171
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
172
|
-
return dir;
|
|
173
|
-
}
|
|
174
|
-
return null;
|
|
151
|
+
function copyAiGuides(projectRoot) {
|
|
152
|
+
const src = path.join(__dirname, '..', 'ai-guides');
|
|
153
|
+
const dest = path.join(projectRoot, '.claude', 'omnify');
|
|
154
|
+
return copyFiles(src, dest);
|
|
175
155
|
}
|
|
176
156
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
*/
|
|
180
|
-
function findPluginAiGuides(projectRoot) {
|
|
181
|
-
const nodeModulesDir = path.join(projectRoot, 'node_modules');
|
|
182
|
-
const aiGuidesDirs = [];
|
|
183
|
-
|
|
184
|
-
// Check @famgia directory
|
|
185
|
-
const famgiaDir = path.join(nodeModulesDir, '@famgia');
|
|
186
|
-
if (fs.existsSync(famgiaDir)) {
|
|
187
|
-
const packages = fs.readdirSync(famgiaDir);
|
|
188
|
-
for (const pkg of packages) {
|
|
189
|
-
if (pkg.startsWith('omnify')) {
|
|
190
|
-
const aiGuidesPath = path.join(famgiaDir, pkg, 'ai-guides');
|
|
191
|
-
if (fs.existsSync(aiGuidesPath)) {
|
|
192
|
-
aiGuidesDirs.push(aiGuidesPath);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
}
|
|
157
|
+
function setupClaudeMd(projectRoot) {
|
|
158
|
+
const filePath = path.join(projectRoot, 'CLAUDE.md');
|
|
197
159
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
try {
|
|
202
|
-
const pnpmPackages = fs.readdirSync(pnpmDir);
|
|
203
|
-
for (const pkg of pnpmPackages) {
|
|
204
|
-
if (pkg.startsWith('@famgia+omnify')) {
|
|
205
|
-
// Extract package name from pnpm folder name (e.g., @famgia+omnify-laravel@0.0.70 -> omnify-laravel)
|
|
206
|
-
const match = pkg.match(/@famgia\+([^@]+)@/);
|
|
207
|
-
if (match) {
|
|
208
|
-
const pkgName = match[1]; // e.g., omnify-laravel
|
|
209
|
-
const aiGuidesPath = path.join(pnpmDir, pkg, 'node_modules/@famgia', pkgName, 'ai-guides');
|
|
210
|
-
if (fs.existsSync(aiGuidesPath)) {
|
|
211
|
-
aiGuidesDirs.push(aiGuidesPath);
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
} catch {
|
|
217
|
-
// Ignore errors reading pnpm directory
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
return aiGuidesDirs;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
function copyAiGuidesToProject(projectRoot) {
|
|
225
|
-
const omnifyDir = path.join(projectRoot, '.claude', 'omnify');
|
|
226
|
-
|
|
227
|
-
try {
|
|
228
|
-
// Create .claude/omnify directory
|
|
229
|
-
if (!fs.existsSync(omnifyDir)) {
|
|
230
|
-
fs.mkdirSync(omnifyDir, { recursive: true });
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
// Track copied files to avoid duplicates
|
|
234
|
-
const copiedFiles = new Set();
|
|
235
|
-
|
|
236
|
-
// Helper to copy files without duplicates
|
|
237
|
-
const copyWithoutDuplicates = (srcDir) => {
|
|
238
|
-
if (!fs.existsSync(srcDir)) return;
|
|
239
|
-
const files = fs.readdirSync(srcDir);
|
|
240
|
-
for (const file of files) {
|
|
241
|
-
if (copiedFiles.has(file)) continue;
|
|
242
|
-
const srcPath = path.join(srcDir, file);
|
|
243
|
-
const destPath = path.join(omnifyDir, file);
|
|
244
|
-
if (fs.statSync(srcPath).isFile()) {
|
|
245
|
-
fs.copyFileSync(srcPath, destPath);
|
|
246
|
-
copiedFiles.add(file);
|
|
247
|
-
console.log(` Created .claude/omnify/${file}`);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
// Copy from main omnify package first
|
|
253
|
-
const mainAiGuidesDir = path.join(__dirname, '..', 'ai-guides');
|
|
254
|
-
copyWithoutDuplicates(mainAiGuidesDir);
|
|
255
|
-
|
|
256
|
-
// Copy from all @famgia/omnify-* plugin packages
|
|
257
|
-
const pluginDirs = findPluginAiGuides(projectRoot);
|
|
258
|
-
for (const pluginDir of pluginDirs) {
|
|
259
|
-
copyWithoutDuplicates(pluginDir);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
return true;
|
|
263
|
-
} catch {
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
}
|
|
160
|
+
if (fs.existsSync(filePath)) {
|
|
161
|
+
let content = fs.readFileSync(filePath, 'utf-8');
|
|
162
|
+
const match = content.match(/## Omnify[^\n]*/);
|
|
267
163
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
const SECTION_END_MARKERS = ['## ', '---', '# ']; // Next section indicators
|
|
272
|
-
|
|
273
|
-
if (fs.existsSync(claudeMdPath)) {
|
|
274
|
-
let content = fs.readFileSync(claudeMdPath, 'utf-8');
|
|
275
|
-
|
|
276
|
-
// Find existing Omnify section (## Omnify or ## Omnify Schema Integration, etc.)
|
|
277
|
-
const omnifyMatch = content.match(/## Omnify[^\n]*/);
|
|
278
|
-
if (omnifyMatch) {
|
|
279
|
-
const startIdx = content.indexOf(omnifyMatch[0]);
|
|
280
|
-
// Find the end of the section (next ## heading or --- or end of file)
|
|
164
|
+
if (match) {
|
|
165
|
+
// Replace existing section
|
|
166
|
+
const startIdx = content.indexOf(match[0]);
|
|
281
167
|
let endIdx = content.length;
|
|
282
|
-
const
|
|
168
|
+
const after = content.substring(startIdx + match[0].length);
|
|
283
169
|
|
|
284
|
-
for (const marker of
|
|
285
|
-
const
|
|
286
|
-
if (
|
|
287
|
-
endIdx = startIdx +
|
|
170
|
+
for (const marker of ['## ', '---', '# ']) {
|
|
171
|
+
const idx = after.indexOf('\n' + marker);
|
|
172
|
+
if (idx !== -1 && idx < endIdx - startIdx - match[0].length) {
|
|
173
|
+
endIdx = startIdx + match[0].length + idx + 1;
|
|
288
174
|
}
|
|
289
175
|
}
|
|
290
176
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
content =
|
|
295
|
-
fs.writeFileSync(claudeMdPath, content, 'utf-8');
|
|
296
|
-
console.log(' Updated Omnify section in CLAUDE.md');
|
|
297
|
-
return true;
|
|
177
|
+
content = content.substring(0, startIdx) + CLAUDE_MD_SECTION + content.substring(endIdx);
|
|
178
|
+
} else {
|
|
179
|
+
// Append section
|
|
180
|
+
content = content.trimEnd() + '\n\n' + CLAUDE_MD_SECTION;
|
|
298
181
|
}
|
|
299
182
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
fs.writeFileSync(
|
|
303
|
-
console.log(' Appended Omnify section to CLAUDE.md');
|
|
304
|
-
return true;
|
|
183
|
+
fs.writeFileSync(filePath, content);
|
|
184
|
+
} else {
|
|
185
|
+
fs.writeFileSync(filePath, CLAUDE_MD_SECTION);
|
|
305
186
|
}
|
|
306
|
-
|
|
307
|
-
fs.writeFileSync(claudeMdPath, CLAUDE_MD_SECTION, 'utf-8');
|
|
308
|
-
console.log(' Created CLAUDE.md');
|
|
309
|
-
return true;
|
|
310
187
|
}
|
|
311
188
|
|
|
312
|
-
function
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
try {
|
|
317
|
-
if (!fs.existsSync(cursorDir)) {
|
|
318
|
-
fs.mkdirSync(cursorDir, { recursive: true });
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
fs.writeFileSync(cursorRulesPath, CURSORRULES_CONTENT, 'utf-8');
|
|
322
|
-
console.log(' Updated .cursor/rules/omnify.md');
|
|
323
|
-
return true;
|
|
324
|
-
} catch {
|
|
325
|
-
return false;
|
|
326
|
-
}
|
|
189
|
+
function setupCursorRules(projectRoot) {
|
|
190
|
+
const dir = path.join(projectRoot, '.cursor', 'rules');
|
|
191
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
192
|
+
fs.writeFileSync(path.join(dir, 'omnify.md'), CURSOR_RULES);
|
|
327
193
|
}
|
|
328
194
|
|
|
329
|
-
function
|
|
330
|
-
const
|
|
331
|
-
const claudeDir = path.join(homeDir, '.claude');
|
|
332
|
-
const configPath = path.join(claudeDir, 'claude_desktop_config.json');
|
|
195
|
+
function setupClaudeMcp() {
|
|
196
|
+
const configPath = path.join(os.homedir(), '.claude', 'claude_desktop_config.json');
|
|
333
197
|
|
|
334
198
|
try {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
}
|
|
199
|
+
const dir = path.dirname(configPath);
|
|
200
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
338
201
|
|
|
339
202
|
let config = { mcpServers: {} };
|
|
340
|
-
|
|
341
203
|
if (fs.existsSync(configPath)) {
|
|
342
204
|
try {
|
|
343
|
-
|
|
344
|
-
config =
|
|
345
|
-
|
|
346
|
-
config.mcpServers = {};
|
|
347
|
-
}
|
|
348
|
-
} catch {
|
|
349
|
-
config = { mcpServers: {} };
|
|
350
|
-
}
|
|
205
|
+
config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
206
|
+
config.mcpServers = config.mcpServers || {};
|
|
207
|
+
} catch { /* use default */ }
|
|
351
208
|
}
|
|
352
209
|
|
|
353
|
-
if (config.mcpServers.omnify) {
|
|
354
|
-
|
|
355
|
-
|
|
210
|
+
if (!config.mcpServers.omnify) {
|
|
211
|
+
config.mcpServers.omnify = { command: 'npx', args: ['@famgia/omnify-mcp'] };
|
|
212
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
356
213
|
}
|
|
357
|
-
|
|
358
|
-
config.mcpServers.omnify = MCP_CONFIG.omnify;
|
|
359
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), 'utf-8');
|
|
360
|
-
console.log(' Configured Omnify MCP server in ~/.claude/claude_desktop_config.json');
|
|
361
|
-
return true;
|
|
362
|
-
} catch (error) {
|
|
363
|
-
console.log(' Note: Could not auto-configure MCP (optional)');
|
|
364
|
-
return false;
|
|
365
|
-
}
|
|
214
|
+
} catch { /* optional, ignore errors */ }
|
|
366
215
|
}
|
|
367
216
|
|
|
217
|
+
// ============================================================================
|
|
218
|
+
// Main
|
|
219
|
+
// ============================================================================
|
|
220
|
+
|
|
368
221
|
function main() {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
}
|
|
222
|
+
// Skip in CI
|
|
223
|
+
if (process.env.CI || process.env.CONTINUOUS_INTEGRATION) return;
|
|
372
224
|
|
|
225
|
+
// Skip in monorepo source (but allow examples/)
|
|
373
226
|
const projectDir = process.env.INIT_CWD || process.cwd();
|
|
374
|
-
if (projectDir.includes('omnify-ts') && !projectDir.includes('omnify-ts/examples'))
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
console.log('\n🔧 Omnify: Setting up Claude Code integration...\n');
|
|
227
|
+
if (projectDir.includes('omnify-ts') && !projectDir.includes('omnify-ts/examples')) return;
|
|
379
228
|
|
|
380
229
|
const projectRoot = findProjectRoot();
|
|
230
|
+
if (!projectRoot) return;
|
|
231
|
+
|
|
232
|
+
console.log('\n🔧 Omnify: Setting up AI integration...\n');
|
|
233
|
+
|
|
234
|
+
const results = [];
|
|
381
235
|
|
|
382
|
-
if (projectRoot) {
|
|
383
|
-
|
|
384
|
-
copyAiGuidesToProject(projectRoot);
|
|
385
|
-
createClaudeMd(projectRoot);
|
|
386
|
-
createCursorRules(projectRoot);
|
|
236
|
+
if (generateCombinedSchema(projectRoot)) {
|
|
237
|
+
results.push('✓ Generated combined JSON schema');
|
|
387
238
|
}
|
|
388
239
|
|
|
389
|
-
|
|
240
|
+
const guides = copyAiGuides(projectRoot);
|
|
241
|
+
if (guides.length > 0) {
|
|
242
|
+
results.push(`✓ Copied ${guides.length} AI guides to .claude/omnify/`);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
setupClaudeMd(projectRoot);
|
|
247
|
+
results.push('✓ Updated CLAUDE.md');
|
|
248
|
+
} catch { /* ignore */ }
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
setupCursorRules(projectRoot);
|
|
252
|
+
results.push('✓ Updated .cursor/rules/omnify.md');
|
|
253
|
+
} catch { /* ignore */ }
|
|
254
|
+
|
|
255
|
+
setupClaudeMcp();
|
|
390
256
|
|
|
257
|
+
for (const r of results) console.log(` ${r}`);
|
|
391
258
|
console.log('\n✅ Omnify setup complete!\n');
|
|
392
259
|
}
|
|
393
260
|
|