@opensaas/stack-cli 0.4.0 → 0.6.0

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.
Files changed (90) hide show
  1. package/README.md +76 -0
  2. package/dist/commands/migrate.d.ts +9 -0
  3. package/dist/commands/migrate.d.ts.map +1 -0
  4. package/dist/commands/migrate.js +299 -0
  5. package/dist/commands/migrate.js.map +1 -0
  6. package/dist/index.js +3 -0
  7. package/dist/index.js.map +1 -1
  8. package/dist/mcp/lib/documentation-provider.d.ts +23 -0
  9. package/dist/mcp/lib/documentation-provider.d.ts.map +1 -1
  10. package/dist/mcp/lib/documentation-provider.js +471 -0
  11. package/dist/mcp/lib/documentation-provider.js.map +1 -1
  12. package/dist/mcp/lib/wizards/migration-wizard.d.ts +80 -0
  13. package/dist/mcp/lib/wizards/migration-wizard.d.ts.map +1 -0
  14. package/dist/mcp/lib/wizards/migration-wizard.js +499 -0
  15. package/dist/mcp/lib/wizards/migration-wizard.js.map +1 -0
  16. package/dist/mcp/server/index.d.ts.map +1 -1
  17. package/dist/mcp/server/index.js +103 -0
  18. package/dist/mcp/server/index.js.map +1 -1
  19. package/dist/mcp/server/stack-mcp-server.d.ts +85 -0
  20. package/dist/mcp/server/stack-mcp-server.d.ts.map +1 -1
  21. package/dist/mcp/server/stack-mcp-server.js +219 -0
  22. package/dist/mcp/server/stack-mcp-server.js.map +1 -1
  23. package/dist/migration/generators/migration-generator.d.ts +60 -0
  24. package/dist/migration/generators/migration-generator.d.ts.map +1 -0
  25. package/dist/migration/generators/migration-generator.js +510 -0
  26. package/dist/migration/generators/migration-generator.js.map +1 -0
  27. package/dist/migration/introspectors/index.d.ts +12 -0
  28. package/dist/migration/introspectors/index.d.ts.map +1 -0
  29. package/dist/migration/introspectors/index.js +10 -0
  30. package/dist/migration/introspectors/index.js.map +1 -0
  31. package/dist/migration/introspectors/keystone-introspector.d.ts +59 -0
  32. package/dist/migration/introspectors/keystone-introspector.d.ts.map +1 -0
  33. package/dist/migration/introspectors/keystone-introspector.js +229 -0
  34. package/dist/migration/introspectors/keystone-introspector.js.map +1 -0
  35. package/dist/migration/introspectors/nextjs-introspector.d.ts +59 -0
  36. package/dist/migration/introspectors/nextjs-introspector.d.ts.map +1 -0
  37. package/dist/migration/introspectors/nextjs-introspector.js +159 -0
  38. package/dist/migration/introspectors/nextjs-introspector.js.map +1 -0
  39. package/dist/migration/introspectors/prisma-introspector.d.ts +45 -0
  40. package/dist/migration/introspectors/prisma-introspector.d.ts.map +1 -0
  41. package/dist/migration/introspectors/prisma-introspector.js +190 -0
  42. package/dist/migration/introspectors/prisma-introspector.js.map +1 -0
  43. package/dist/migration/types.d.ts +86 -0
  44. package/dist/migration/types.d.ts.map +1 -0
  45. package/dist/migration/types.js +5 -0
  46. package/dist/migration/types.js.map +1 -0
  47. package/package.json +10 -2
  48. package/plugin/.claude-plugin/plugin.json +15 -0
  49. package/plugin/README.md +112 -0
  50. package/plugin/agents/migration-assistant.md +150 -0
  51. package/plugin/commands/analyze-schema.md +34 -0
  52. package/plugin/commands/generate-config.md +33 -0
  53. package/plugin/commands/validate-migration.md +34 -0
  54. package/plugin/skills/opensaas-migration/SKILL.md +192 -0
  55. package/.turbo/turbo-build.log +0 -4
  56. package/CHANGELOG.md +0 -410
  57. package/CLAUDE.md +0 -298
  58. package/src/commands/__snapshots__/generate.test.ts.snap +0 -413
  59. package/src/commands/dev.test.ts +0 -215
  60. package/src/commands/dev.ts +0 -48
  61. package/src/commands/generate.test.ts +0 -282
  62. package/src/commands/generate.ts +0 -182
  63. package/src/commands/init.ts +0 -34
  64. package/src/commands/mcp.ts +0 -135
  65. package/src/generator/__snapshots__/context.test.ts.snap +0 -361
  66. package/src/generator/__snapshots__/prisma.test.ts.snap +0 -174
  67. package/src/generator/__snapshots__/types.test.ts.snap +0 -1702
  68. package/src/generator/context.test.ts +0 -139
  69. package/src/generator/context.ts +0 -227
  70. package/src/generator/index.ts +0 -7
  71. package/src/generator/lists.test.ts +0 -335
  72. package/src/generator/lists.ts +0 -140
  73. package/src/generator/plugin-types.ts +0 -147
  74. package/src/generator/prisma-config.ts +0 -46
  75. package/src/generator/prisma-extensions.ts +0 -159
  76. package/src/generator/prisma.test.ts +0 -211
  77. package/src/generator/prisma.ts +0 -161
  78. package/src/generator/types.test.ts +0 -268
  79. package/src/generator/types.ts +0 -537
  80. package/src/index.ts +0 -42
  81. package/src/mcp/lib/documentation-provider.ts +0 -203
  82. package/src/mcp/lib/features/catalog.ts +0 -301
  83. package/src/mcp/lib/generators/feature-generator.ts +0 -598
  84. package/src/mcp/lib/types.ts +0 -89
  85. package/src/mcp/lib/wizards/wizard-engine.ts +0 -427
  86. package/src/mcp/server/index.ts +0 -240
  87. package/src/mcp/server/stack-mcp-server.ts +0 -301
  88. package/tsconfig.json +0 -13
  89. package/tsconfig.tsbuildinfo +0 -1
  90. package/vitest.config.ts +0 -26
@@ -0,0 +1,190 @@
1
+ /**
2
+ * Prisma Schema Introspector
3
+ *
4
+ * Parses prisma/schema.prisma and extracts structured information
5
+ * about models, fields, relationships, and enums.
6
+ */
7
+ import fs from 'fs-extra';
8
+ import path from 'path';
9
+ export class PrismaIntrospector {
10
+ /**
11
+ * Introspect a Prisma schema file
12
+ */
13
+ async introspect(cwd, schemaPath = 'prisma/schema.prisma') {
14
+ const fullPath = path.isAbsolute(schemaPath) ? schemaPath : path.join(cwd, schemaPath);
15
+ if (!(await fs.pathExists(fullPath))) {
16
+ throw new Error(`Schema file not found: ${fullPath}`);
17
+ }
18
+ const schema = await fs.readFile(fullPath, 'utf-8');
19
+ return {
20
+ provider: this.extractProvider(schema),
21
+ models: this.extractModels(schema),
22
+ enums: this.extractEnums(schema),
23
+ };
24
+ }
25
+ /**
26
+ * Extract database provider from datasource block
27
+ */
28
+ extractProvider(schema) {
29
+ const match = schema.match(/datasource\s+\w+\s*\{[^}]*provider\s*=\s*"(\w+)"/);
30
+ return match ? match[1] : 'unknown';
31
+ }
32
+ /**
33
+ * Extract all model definitions
34
+ */
35
+ extractModels(schema) {
36
+ const models = [];
37
+ // Match model blocks
38
+ const modelRegex = /model\s+(\w+)\s*\{([^}]+)\}/g;
39
+ let match;
40
+ while ((match = modelRegex.exec(schema)) !== null) {
41
+ const name = match[1];
42
+ const body = match[2];
43
+ const fields = this.extractFields(body);
44
+ const primaryKey = fields.find((f) => f.isId)?.name || 'id';
45
+ models.push({
46
+ name,
47
+ fields,
48
+ hasRelations: fields.some((f) => f.relation !== undefined),
49
+ primaryKey,
50
+ });
51
+ }
52
+ return models;
53
+ }
54
+ /**
55
+ * Extract fields from a model body
56
+ */
57
+ extractFields(body) {
58
+ const fields = [];
59
+ const lines = body.split('\n');
60
+ for (const line of lines) {
61
+ const trimmed = line.trim();
62
+ // Skip empty lines, comments, and model-level attributes
63
+ if (!trimmed || trimmed.startsWith('//') || trimmed.startsWith('@@')) {
64
+ continue;
65
+ }
66
+ const field = this.parseFieldLine(trimmed);
67
+ if (field) {
68
+ fields.push(field);
69
+ }
70
+ }
71
+ return fields;
72
+ }
73
+ /**
74
+ * Parse a single field line
75
+ */
76
+ parseFieldLine(line) {
77
+ // Basic field pattern: name Type modifiers attributes
78
+ // Examples:
79
+ // id String @id @default(cuid())
80
+ // title String
81
+ // isActive Boolean? @default(true)
82
+ // posts Post[]
83
+ // author User @relation(fields: [authorId], references: [id])
84
+ // Remove comments
85
+ const withoutComment = line.split('//')[0].trim();
86
+ // Match field name and type
87
+ const fieldMatch = withoutComment.match(/^(\w+)\s+(\w+)(\?)?(\[\])?(.*)$/);
88
+ if (!fieldMatch)
89
+ return null;
90
+ const [, name, rawType, optional, isList, rest] = fieldMatch;
91
+ // Skip if this looks like an index or other non-field line
92
+ if (['@@', 'index', 'unique'].some((kw) => name.startsWith(kw))) {
93
+ return null;
94
+ }
95
+ const field = {
96
+ name,
97
+ type: rawType,
98
+ isRequired: !optional,
99
+ isUnique: rest.includes('@unique'),
100
+ isId: rest.includes('@id'),
101
+ isList: !!isList,
102
+ };
103
+ // Extract default value (handle nested parentheses)
104
+ const defaultMatch = rest.match(/@default\(/);
105
+ if (defaultMatch) {
106
+ const startIdx = rest.indexOf('@default(') + '@default('.length;
107
+ let depth = 1;
108
+ let endIdx = startIdx;
109
+ while (depth > 0 && endIdx < rest.length) {
110
+ if (rest[endIdx] === '(')
111
+ depth++;
112
+ else if (rest[endIdx] === ')')
113
+ depth--;
114
+ if (depth > 0)
115
+ endIdx++;
116
+ }
117
+ field.defaultValue = rest.substring(startIdx, endIdx);
118
+ }
119
+ // Extract relation
120
+ const relationMatch = rest.match(/@relation\(([^)]+)\)/);
121
+ if (relationMatch) {
122
+ const relationBody = relationMatch[1];
123
+ // Parse relation parts
124
+ const fieldsMatch = relationBody.match(/fields:\s*\[([^\]]+)\]/);
125
+ const referencesMatch = relationBody.match(/references:\s*\[([^\]]+)\]/);
126
+ const nameMatch = relationBody.match(/name:\s*"([^"]+)"/) || relationBody.match(/"([^"]+)"/);
127
+ field.relation = {
128
+ name: nameMatch ? nameMatch[1] : '',
129
+ model: rawType,
130
+ fields: fieldsMatch ? fieldsMatch[1].split(',').map((f) => f.trim()) : [],
131
+ references: referencesMatch ? referencesMatch[1].split(',').map((r) => r.trim()) : [],
132
+ };
133
+ }
134
+ return field;
135
+ }
136
+ /**
137
+ * Extract enum definitions
138
+ */
139
+ extractEnums(schema) {
140
+ const enums = [];
141
+ // Match enum blocks
142
+ const enumRegex = /enum\s+(\w+)\s*\{([^}]+)\}/g;
143
+ let match;
144
+ while ((match = enumRegex.exec(schema)) !== null) {
145
+ const name = match[1];
146
+ const body = match[2];
147
+ const values = body
148
+ .split('\n')
149
+ .map((line) => line.trim())
150
+ .filter((line) => line && !line.startsWith('//'));
151
+ enums.push({ name, values });
152
+ }
153
+ return enums;
154
+ }
155
+ /**
156
+ * Map Prisma type to OpenSaaS field type
157
+ */
158
+ mapPrismaTypeToOpenSaas(prismaType) {
159
+ const mappings = {
160
+ String: { type: 'text', import: 'text' },
161
+ Int: { type: 'integer', import: 'integer' },
162
+ Float: { type: 'float', import: 'float' },
163
+ Boolean: { type: 'checkbox', import: 'checkbox' },
164
+ DateTime: { type: 'timestamp', import: 'timestamp' },
165
+ Json: { type: 'json', import: 'json' },
166
+ BigInt: { type: 'text', import: 'text' }, // No native support
167
+ Decimal: { type: 'text', import: 'text' }, // No native support
168
+ Bytes: { type: 'text', import: 'text' }, // No native support
169
+ };
170
+ return mappings[prismaType] || { type: 'text', import: 'text' };
171
+ }
172
+ /**
173
+ * Get warnings for unsupported features
174
+ */
175
+ getWarnings(schema) {
176
+ const warnings = [];
177
+ // Check for unsupported types
178
+ for (const model of schema.models) {
179
+ for (const field of model.fields) {
180
+ if (['BigInt', 'Decimal', 'Bytes'].includes(field.type)) {
181
+ warnings.push(`Field "${model.name}.${field.name}" uses unsupported type "${field.type}" - will be mapped to text()`);
182
+ }
183
+ }
184
+ }
185
+ // Check for composite IDs
186
+ // This would require checking for @@id in the original schema
187
+ return warnings;
188
+ }
189
+ }
190
+ //# sourceMappingURL=prisma-introspector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prisma-introspector.js","sourceRoot":"","sources":["../../../src/migration/introspectors/prisma-introspector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Migration types - Shared types for the migration system
3
+ */
4
+ export type ProjectType = 'prisma' | 'nextjs' | 'keystone';
5
+ export interface ModelInfo {
6
+ name: string;
7
+ fieldCount: number;
8
+ }
9
+ export interface ProjectAnalysis {
10
+ projectTypes: ProjectType[];
11
+ cwd: string;
12
+ models?: ModelInfo[];
13
+ provider?: string;
14
+ hasAuth?: boolean;
15
+ authLibrary?: string;
16
+ }
17
+ export interface FieldMapping {
18
+ prismaType: string;
19
+ opensaasType: string;
20
+ opensaasImport: string;
21
+ }
22
+ export interface MigrationQuestion {
23
+ id: string;
24
+ text: string;
25
+ type: 'text' | 'select' | 'boolean' | 'multiselect';
26
+ options?: string[];
27
+ defaultValue?: string | boolean | string[];
28
+ required?: boolean;
29
+ dependsOn?: {
30
+ questionId: string;
31
+ value: string | boolean;
32
+ };
33
+ }
34
+ export interface MigrationSession {
35
+ id: string;
36
+ projectType: ProjectType;
37
+ analysis: ProjectAnalysis;
38
+ currentQuestionIndex: number;
39
+ answers: Record<string, string | boolean | string[]>;
40
+ generatedConfig?: string;
41
+ isComplete: boolean;
42
+ createdAt: Date;
43
+ updatedAt: Date;
44
+ }
45
+ export interface MigrationOutput {
46
+ configContent: string;
47
+ dependencies: string[];
48
+ files: Array<{
49
+ path: string;
50
+ content: string;
51
+ language: string;
52
+ description: string;
53
+ }>;
54
+ steps: string[];
55
+ warnings: string[];
56
+ }
57
+ export interface IntrospectedModel {
58
+ name: string;
59
+ fields: IntrospectedField[];
60
+ hasRelations: boolean;
61
+ primaryKey: string;
62
+ }
63
+ export interface IntrospectedField {
64
+ name: string;
65
+ type: string;
66
+ isRequired: boolean;
67
+ isUnique: boolean;
68
+ isId: boolean;
69
+ isList: boolean;
70
+ defaultValue?: string;
71
+ relation?: {
72
+ name: string;
73
+ model: string;
74
+ fields: string[];
75
+ references: string[];
76
+ };
77
+ }
78
+ export interface IntrospectedSchema {
79
+ provider: string;
80
+ models: IntrospectedModel[];
81
+ enums: Array<{
82
+ name: string;
83
+ values: string[];
84
+ }>;
85
+ }
86
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/migration/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Migration types - Shared types for the migration system
3
+ */
4
+ export {};
5
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/migration/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opensaas/stack-cli",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "CLI tools for OpenSaas Stack",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -18,6 +18,11 @@
18
18
  "bin": {
19
19
  "opensaas": "./bin/opensaas.js"
20
20
  },
21
+ "files": [
22
+ "dist",
23
+ "bin",
24
+ "plugin"
25
+ ],
21
26
  "keywords": [
22
27
  "opensaas",
23
28
  "cli",
@@ -40,13 +45,16 @@
40
45
  "chalk": "^5.6.2",
41
46
  "chokidar": "^5.0.0",
42
47
  "commander": "^14.0.2",
48
+ "fs-extra": "^11.3.2",
49
+ "glob": "^13.0.0",
43
50
  "jiti": "^2.6.1",
44
51
  "ora": "^9.0.0",
45
52
  "prompts": "^2.4.2",
46
53
  "zod": "^4.1.13",
47
- "@opensaas/stack-core": "0.4.0"
54
+ "@opensaas/stack-core": "0.6.0"
48
55
  },
49
56
  "devDependencies": {
57
+ "@types/fs-extra": "^11.0.4",
50
58
  "@types/node": "^24.10.1",
51
59
  "@types/prompts": "^2.4.9",
52
60
  "@vitest/coverage-v8": "^4.0.15",
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "opensaas-migration",
3
+ "description": "OpenSaaS Stack migration assistant with AI-guided configuration, schema analysis, and code generation support",
4
+ "version": "1.0.0",
5
+ "author": {
6
+ "name": "OpenSaaS Team",
7
+ "url": "https://github.com/OpenSaasAU/stack"
8
+ },
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "https://github.com/OpenSaasAU/stack",
12
+ "directory": "packages/cli/plugin"
13
+ },
14
+ "keywords": ["migration", "opensaas", "prisma", "keystone", "nextjs", "code-generation"]
15
+ }
@@ -0,0 +1,112 @@
1
+ # OpenSaaS Migration Assistant Plugin
2
+
3
+ A Claude Code plugin that provides AI-guided migration assistance for converting existing Prisma, KeystoneJS, or Next.js projects to OpenSaaS Stack.
4
+
5
+ ## Features
6
+
7
+ - **Migration Assistant Agent**: Contextual agent that guides you through the migration process
8
+ - **Interactive Commands**: Slash commands for schema analysis, config generation, and validation
9
+ - **Migration Skill**: Expert knowledge about migration patterns and best practices
10
+ - **MCP Integration**: Works with the OpenSaaS MCP server for advanced tooling
11
+
12
+ ## Installation
13
+
14
+ This plugin is automatically set up when you run:
15
+
16
+ ```bash
17
+ npx @opensaas/stack-cli migrate --with-ai
18
+ ```
19
+
20
+ The CLI will:
21
+
22
+ 1. Install this plugin to your project
23
+ 2. Create `.claude/opensaas-project.json` with your project metadata
24
+ 3. Configure `.claude/settings.json` to enable the plugin
25
+ 4. Set up `.mcp.json` for MCP server integration
26
+
27
+ ## What's Included
28
+
29
+ ### Migration Assistant Agent
30
+
31
+ A specialized agent that:
32
+
33
+ - Reads your project metadata from `.claude/opensaas-project.json`
34
+ - Guides you through the migration wizard
35
+ - Explains access control patterns
36
+ - Generates `opensaas.config.ts`
37
+
38
+ ### Slash Commands
39
+
40
+ - `/analyze-schema` - Detailed schema analysis with recommendations
41
+ - `/generate-config` - Generate opensaas.config.ts
42
+ - `/validate-migration` - Validate generated configuration
43
+
44
+ ### Migration Skill
45
+
46
+ Expert knowledge including:
47
+
48
+ - Access control patterns
49
+ - Field type mappings
50
+ - Database configuration examples
51
+ - Common challenges and solutions
52
+
53
+ ## Usage
54
+
55
+ Once installed, simply ask Claude:
56
+
57
+ ```
58
+ Help me migrate to OpenSaaS Stack
59
+ ```
60
+
61
+ The migration assistant will:
62
+
63
+ 1. Read your project details
64
+ 2. Start the interactive wizard
65
+ 3. Guide you through configuration
66
+ 4. Generate your opensaas.config.ts
67
+
68
+ ## Project Metadata
69
+
70
+ The CLI creates `.claude/opensaas-project.json` with information about your project:
71
+
72
+ ```json
73
+ {
74
+ "projectTypes": ["prisma"],
75
+ "provider": "sqlite",
76
+ "models": [
77
+ { "name": "User", "fieldCount": 5 },
78
+ { "name": "Post", "fieldCount": 7 }
79
+ ],
80
+ "hasAuth": true
81
+ }
82
+ ```
83
+
84
+ The plugin reads this file to provide contextual assistance.
85
+
86
+ ## Development
87
+
88
+ This plugin is part of the `@opensaas/stack-cli` package and is distributed with it.
89
+
90
+ **Directory structure:**
91
+
92
+ ```
93
+ plugin/
94
+ ├── .claude-plugin/
95
+ │ └── plugin.json # Plugin manifest
96
+ ├── agents/
97
+ │ └── migration-assistant.md # Migration agent
98
+ ├── commands/
99
+ │ ├── analyze-schema.md
100
+ │ ├── generate-config.md
101
+ │ └── validate-migration.md
102
+ ├── skills/
103
+ │ └── opensaas-migration/
104
+ │ └── SKILL.md
105
+ └── README.md
106
+ ```
107
+
108
+ ## Links
109
+
110
+ - [OpenSaaS Stack Documentation](https://stack.opensaas.au/)
111
+ - [Migration Guide](https://stack.opensaas.au/guides/migration)
112
+ - [GitHub Repository](https://github.com/OpenSaasAU/stack)
@@ -0,0 +1,150 @@
1
+ ---
2
+ name: migration-assistant
3
+ description: OpenSaaS Stack migration expert. Use when helping users migrate from Prisma, KeystoneJS, or Next.js projects to OpenSaaS Stack. Proactively helps with schema analysis, access control configuration, and opensaas.config.ts generation.
4
+ model: sonnet
5
+ skills: opensaas-migration
6
+ ---
7
+
8
+ You are the OpenSaaS Stack Migration Assistant, helping users migrate their existing projects to OpenSaaS Stack.
9
+
10
+ ## Getting Project Context
11
+
12
+ **IMPORTANT**: Before starting, read the project metadata from `.claude/opensaas-project.json` to understand:
13
+
14
+ - Project type (Prisma, KeystoneJS, Next.js)
15
+ - Database provider
16
+ - Detected models and their structure
17
+ - Whether authentication is already present
18
+
19
+ This file is created by `npx @opensaas/stack-cli migrate --with-ai` and contains essential project information.
20
+
21
+ ## Your Role
22
+
23
+ Guide the user through a complete migration to OpenSaaS Stack:
24
+
25
+ 1. **Analyze** their current project structure
26
+ 2. **Explain** what OpenSaaS Stack offers (access control, admin UI, type safety)
27
+ 3. **Guide** them through the migration wizard
28
+ 4. **Generate** a working `opensaas.config.ts`
29
+ 5. **Validate** the generated configuration
30
+ 6. **Provide** clear next steps
31
+
32
+ ## Available MCP Tools
33
+
34
+ ### Schema Analysis
35
+
36
+ - `opensaas_introspect_prisma` - Analyze Prisma schema in detail
37
+ - `opensaas_introspect_keystone` - Analyze KeystoneJS config
38
+
39
+ ### Migration Wizard
40
+
41
+ - `opensaas_start_migration` - Start the interactive wizard
42
+ - `opensaas_answer_migration` - Answer wizard questions
43
+
44
+ ### Documentation
45
+
46
+ - `opensaas_search_migration_docs` - Search migration documentation
47
+ - `opensaas_get_example` - Get example code patterns
48
+
49
+ ### Validation
50
+
51
+ - `opensaas_validate_feature` - Validate implementation
52
+
53
+ ## Conversation Guidelines
54
+
55
+ ### When the user says "help me migrate" or similar:
56
+
57
+ 1. **Read project metadata** from `.claude/opensaas-project.json`:
58
+
59
+ ```
60
+ Use the Read tool to read .claude/opensaas-project.json
61
+ ```
62
+
63
+ 2. **Acknowledge** their project based on the metadata:
64
+
65
+ > "I can see you have a [PROJECT_TYPE] project with [MODEL_COUNT] models. Let me help you migrate to OpenSaaS Stack!"
66
+
67
+ 3. **Start the wizard** by calling:
68
+
69
+ ```
70
+ opensaas_start_migration({ projectType: "[project_type]" })
71
+ ```
72
+
73
+ 4. **Present questions naturally** - don't mention session IDs or technical details to the user
74
+
75
+ 5. **Explain choices** - help them understand what each option means:
76
+ - Access control patterns
77
+ - Authentication options
78
+ - Database configuration
79
+
80
+ 6. **Show progress** - let them know how far along they are
81
+
82
+ 7. **Generate the config** when complete and explain what was created
83
+
84
+ ### When explaining OpenSaaS Stack:
85
+
86
+ Highlight these benefits:
87
+
88
+ - **Built-in access control** - Secure by default
89
+ - **Admin UI** - Auto-generated from your schema
90
+ - **Type safety** - Full TypeScript support
91
+ - **Prisma integration** - Uses familiar ORM
92
+ - **Plugin system** - Easy to extend
93
+
94
+ ### When answering questions:
95
+
96
+ - Use `opensaas_search_migration_docs` to find accurate information
97
+ - Use `opensaas_get_example` to show code patterns
98
+ - Be honest if something isn't supported
99
+
100
+ ### Tone
101
+
102
+ - Be encouraging and helpful
103
+ - Explain technical concepts simply
104
+ - Celebrate progress ("Great choice!", "Almost there!")
105
+ - Don't overwhelm with information
106
+
107
+ ## Example Conversation
108
+
109
+ **User:** Help me migrate to OpenSaaS Stack
110
+
111
+ **You:** Let me check your project details...
112
+
113
+ [Read .claude/opensaas-project.json]
114
+
115
+ I can see you have a Prisma project with 5 models. OpenSaaS Stack will give you:
116
+
117
+ - Automatic admin UI for managing your data
118
+ - Built-in access control to secure your API
119
+ - Type-safe database operations
120
+
121
+ Let me start the migration wizard to configure your project...
122
+
123
+ [Call opensaas_start_migration]
124
+
125
+ **User:** [answers questions]
126
+
127
+ **You:** [Continue through wizard, explain each choice, generate final config]
128
+
129
+ ## Error Handling
130
+
131
+ If something goes wrong:
132
+
133
+ 1. Explain what happened in simple terms
134
+ 2. Suggest alternatives or manual steps
135
+ 3. Link to documentation for more help
136
+
137
+ If `.claude/opensaas-project.json` doesn't exist:
138
+
139
+ - Explain that `npx @opensaas/stack-cli migrate --with-ai` should be run first
140
+ - Offer to help them run it
141
+
142
+ ## After Migration
143
+
144
+ Once the config is generated, guide them through:
145
+
146
+ 1. Installing dependencies
147
+ 2. Running `opensaas generate`
148
+ 3. Running `prisma db push`
149
+ 4. Starting their dev server
150
+ 5. Visiting the admin UI
@@ -0,0 +1,34 @@
1
+ ---
2
+ description: Analyze the current project schema and provide a detailed breakdown
3
+ ---
4
+
5
+ Analyze the current project schema and provide a detailed breakdown.
6
+
7
+ ## Instructions
8
+
9
+ 1. Read `.claude/opensaas-project.json` to determine the project type
10
+ 2. Use `opensaas_introspect_prisma` or `opensaas_introspect_keystone` based on project type
11
+ 3. Present the results in a clear, organized format
12
+ 4. Highlight:
13
+ - All models and their fields
14
+ - Relationships between models
15
+ - Potential access control patterns
16
+ - Any issues or warnings
17
+
18
+ ## Output Format
19
+
20
+ Present like this:
21
+
22
+ ### Models Summary
23
+
24
+ | Model | Fields | Has Relations | Suggested Access |
25
+ | ----- | ------ | ------------- | ---------------- |
26
+ | ... | ... | ... | ... |
27
+
28
+ ### Detailed Analysis
29
+
30
+ [For each model, show fields and relationships]
31
+
32
+ ### Recommendations
33
+
34
+ [Based on the schema, suggest access control patterns]
@@ -0,0 +1,33 @@
1
+ ---
2
+ description: Generate the opensaas.config.ts file for this project
3
+ ---
4
+
5
+ Generate the opensaas.config.ts file for this project.
6
+
7
+ ## Instructions
8
+
9
+ 1. Read `.claude/opensaas-project.json` to get project details
10
+ 2. If migration wizard hasn't been started, start it:
11
+
12
+ ```
13
+ opensaas_start_migration({ projectType: "<project_type>" })
14
+ ```
15
+
16
+ 3. Guide the user through any remaining questions
17
+
18
+ 4. When complete, display:
19
+ - The generated config file
20
+ - Dependencies to install
21
+ - Next steps to run
22
+
23
+ 5. Offer to explain any part of the generated config
24
+
25
+ ## Quick Mode
26
+
27
+ If the user wants defaults, use these answers based on the project metadata:
28
+
29
+ - preserve_database: true
30
+ - db_provider: [from project metadata]
31
+ - enable_auth: [from project metadata]
32
+ - default_access: "public-read-auth-write"
33
+ - admin_base_path: "/admin"