mycontext-cli 3.0.1 ā 3.0.3
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/README.md +67 -5
- package/dist/cli.js +29 -15
- package/dist/cli.js.map +1 -1
- package/dist/commands/help.d.ts.map +1 -1
- package/dist/commands/help.js +94 -25
- package/dist/commands/help.js.map +1 -1
- package/dist/commands/init.d.ts +14 -17
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +148 -485
- package/dist/commands/init.js.map +1 -1
- package/dist/package.json +1 -1
- package/dist/templates/instantdb/auth.ts +89 -0
- package/dist/templates/instantdb/instant-admin.ts +9 -0
- package/dist/templates/instantdb/instant-client.ts +36 -0
- package/dist/templates/instantdb/instantdb-storage.ts +236 -0
- package/dist/utils/fileSystem.d.ts +9 -0
- package/dist/utils/fileSystem.d.ts.map +1 -1
- package/dist/utils/fileSystem.js +37 -1
- package/dist/utils/fileSystem.js.map +1 -1
- package/dist/utils/geminiClient.d.ts.map +1 -1
- package/dist/utils/geminiClient.js +16 -10
- package/dist/utils/geminiClient.js.map +1 -1
- package/dist/utils/hybridAIClient.d.ts.map +1 -1
- package/dist/utils/hybridAIClient.js +24 -6
- package/dist/utils/hybridAIClient.js.map +1 -1
- package/package.json +1 -1
- package/dist/templates/instantdb/db.template.ts +0 -14
- package/dist/templates/instantdb/home-client.template.tsx +0 -127
- package/dist/templates/instantdb/page.template.tsx +0 -5
- package/dist/templates/instantdb/perms.template.ts +0 -9
- package/dist/templates/instantdb/schema.template.ts +0 -28
package/dist/commands/init.js
CHANGED
|
@@ -44,9 +44,7 @@ const gradient_string_1 = __importDefault(require("gradient-string"));
|
|
|
44
44
|
const spinner_1 = require("../utils/spinner");
|
|
45
45
|
const fileSystem_1 = require("../utils/fileSystem");
|
|
46
46
|
const child_process_1 = require("child_process");
|
|
47
|
-
const fs = __importStar(require("fs-extra"));
|
|
48
47
|
const path = __importStar(require("path"));
|
|
49
|
-
const envExampleGenerator_1 = require("../utils/envExampleGenerator");
|
|
50
48
|
class InitCommand {
|
|
51
49
|
constructor() {
|
|
52
50
|
this.fs = new fileSystem_1.FileSystemManager();
|
|
@@ -56,51 +54,10 @@ class InitCommand {
|
|
|
56
54
|
try {
|
|
57
55
|
// Display ASCII art branding
|
|
58
56
|
this.displayBranding();
|
|
59
|
-
// Handle
|
|
57
|
+
// Handle project name
|
|
60
58
|
let finalProjectName = projectName;
|
|
61
|
-
let useCurrentDir =
|
|
62
|
-
if (
|
|
63
|
-
const currentDir = path.basename(process.cwd());
|
|
64
|
-
finalProjectName = currentDir;
|
|
65
|
-
useCurrentDir = true;
|
|
66
|
-
console.log(chalk_1.default.cyan(`š Initializing in current directory: ${currentDir}`));
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
// Validate project name for new directories
|
|
70
|
-
if (!this.isValidProjectName(projectName)) {
|
|
71
|
-
throw new Error("Project name must be alphanumeric with hyphens or underscores only");
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Check if this is an existing project and analyze it
|
|
75
|
-
if (useCurrentDir || options.existing) {
|
|
76
|
-
const isExistingProject = await this.isExistingProject(process.cwd());
|
|
77
|
-
if (isExistingProject) {
|
|
78
|
-
console.log(chalk_1.default.yellow("š Existing project detected!"));
|
|
79
|
-
if (options.analyze) {
|
|
80
|
-
// Run analysis on existing project
|
|
81
|
-
await this.analyzeExistingProject(process.cwd(), options);
|
|
82
|
-
return;
|
|
83
|
-
}
|
|
84
|
-
else {
|
|
85
|
-
// Ask user if they want to analyze
|
|
86
|
-
const shouldAnalyze = await this.askToAnalyze();
|
|
87
|
-
if (shouldAnalyze) {
|
|
88
|
-
await this.analyzeExistingProject(process.cwd(), options);
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
const workingDir = process.cwd();
|
|
95
|
-
// Interactive prompts if not using --yes flag
|
|
96
|
-
let finalDescription = options.description;
|
|
97
|
-
let finalFramework = options.framework;
|
|
98
|
-
// Set framework based on explicit flags
|
|
99
|
-
if (options.next || options.scaffoldNext) {
|
|
100
|
-
finalFramework = "nextjs";
|
|
101
|
-
}
|
|
102
|
-
// Minimal prompts - only ask for project name if not provided
|
|
103
|
-
if (!options.yes && !finalProjectName) {
|
|
59
|
+
let useCurrentDir = projectName === ".";
|
|
60
|
+
if (!finalProjectName && !options.yes) {
|
|
104
61
|
const responses = await (0, prompts_1.default)([
|
|
105
62
|
{
|
|
106
63
|
type: "text",
|
|
@@ -111,129 +68,175 @@ class InitCommand {
|
|
|
111
68
|
},
|
|
112
69
|
]);
|
|
113
70
|
finalProjectName = responses.name || "my-app";
|
|
71
|
+
useCurrentDir = finalProjectName === ".";
|
|
114
72
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
finalFramework = finalFramework || "instantdb"; // Auto-select InstantDB
|
|
118
|
-
// Validate required fields
|
|
119
|
-
if (!finalDescription) {
|
|
120
|
-
throw new Error("Project description is required");
|
|
73
|
+
if (!finalProjectName) {
|
|
74
|
+
finalProjectName = "my-app";
|
|
121
75
|
}
|
|
76
|
+
const workingDir = process.cwd();
|
|
77
|
+
const projectPath = useCurrentDir
|
|
78
|
+
? workingDir
|
|
79
|
+
: path.resolve(workingDir, finalProjectName);
|
|
122
80
|
spinner.start();
|
|
123
|
-
//
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
: path.join(workingDir, finalProjectName);
|
|
129
|
-
const packageJsonPath = path.join(projectPath, "package.json");
|
|
130
|
-
if (!(await fs.pathExists(packageJsonPath))) {
|
|
131
|
-
spinner.updateText("Setting up Next.js project...");
|
|
132
|
-
await this.setupNextJSProject(finalProjectName, workingDir, useCurrentDir);
|
|
133
|
-
}
|
|
134
|
-
// 2. Setup shadcn/ui FIRST (before InstantDB)
|
|
135
|
-
const shouldInitShadcn = options.skipShadcn !== true;
|
|
136
|
-
if (shouldInitShadcn) {
|
|
137
|
-
spinner.updateText("Initializing shadcn/ui...");
|
|
138
|
-
await this.setupShadcn(finalProjectName, workingDir, useCurrentDir);
|
|
139
|
-
}
|
|
140
|
-
// 3. Setup InstantDB (MyContext branded flow)
|
|
141
|
-
spinner.stop(); // Stop spinner for InstantDB setup output
|
|
142
|
-
await this.setupInstantDBProject(finalProjectName, workingDir, useCurrentDir);
|
|
143
|
-
spinner.start(); // Restart spinner for remaining setup
|
|
81
|
+
// Determine framework (default to instantdb for backward compatibility)
|
|
82
|
+
const framework = options.framework || "instantdb";
|
|
83
|
+
if (framework === "instantdb") {
|
|
84
|
+
// InstantDB workflow
|
|
85
|
+
await this.initInstantDBProject(spinner, workingDir, projectPath, finalProjectName, options, useCurrentDir);
|
|
144
86
|
}
|
|
145
|
-
else if (
|
|
146
|
-
// Next.js only
|
|
147
|
-
|
|
148
|
-
await this.setupNextJSProject(finalProjectName, workingDir, useCurrentDir);
|
|
149
|
-
// Setup shadcn/ui for Next.js projects
|
|
150
|
-
const shouldInitShadcn = options.skipShadcn !== true;
|
|
151
|
-
if (shouldInitShadcn) {
|
|
152
|
-
const projectPath = useCurrentDir
|
|
153
|
-
? workingDir
|
|
154
|
-
: path.join(workingDir, finalProjectName);
|
|
155
|
-
const packageJsonPath = path.join(projectPath, "package.json");
|
|
156
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
157
|
-
spinner.updateText("Initializing shadcn/ui...");
|
|
158
|
-
await this.setupShadcn(finalProjectName, workingDir, useCurrentDir);
|
|
159
|
-
}
|
|
160
|
-
else {
|
|
161
|
-
console.log(chalk_1.default.yellow(" ā ļø shadcn/ui init skipped (no package.json found). Run it inside an existing Next.js project."));
|
|
162
|
-
console.log(chalk_1.default.gray(" pnpm dlx shadcn@latest init -y"));
|
|
163
|
-
}
|
|
164
|
-
}
|
|
87
|
+
else if (framework === "nextjs" || framework === "next") {
|
|
88
|
+
// Next.js workflow (shadcn + MyContext only)
|
|
89
|
+
await this.initNextJSProject(spinner, workingDir, projectPath, finalProjectName, options, useCurrentDir);
|
|
165
90
|
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Write .mycontext/.env.example with guidance
|
|
170
|
-
try {
|
|
171
|
-
const projectPath = useCurrentDir
|
|
172
|
-
? workingDir
|
|
173
|
-
: path.join(workingDir, finalProjectName);
|
|
174
|
-
const envDir = path.join(projectPath, ".mycontext");
|
|
175
|
-
await fs.ensureDir(envDir);
|
|
176
|
-
const envExamplePath = path.join(envDir, ".env.example");
|
|
177
|
-
const envExample = await envExampleGenerator_1.EnvExampleGenerator.generateForProject(projectPath);
|
|
178
|
-
await fs.writeFile(envExamplePath, envExample);
|
|
91
|
+
else {
|
|
92
|
+
// Default: MyContext only
|
|
93
|
+
await this.initBasicProject(spinner, projectPath, finalProjectName, options, useCurrentDir);
|
|
179
94
|
}
|
|
180
|
-
catch { }
|
|
181
|
-
// Setup Studio (if bundled)
|
|
182
|
-
const projectPath = useCurrentDir
|
|
183
|
-
? workingDir
|
|
184
|
-
: path.join(workingDir, finalProjectName);
|
|
185
|
-
await this.setupStudio(projectPath);
|
|
186
|
-
spinner.success({
|
|
187
|
-
text: `Project "${finalProjectName}" initialized successfully!`,
|
|
188
|
-
});
|
|
189
|
-
// PRD Review Workflow - Encourage users to review and update PRD
|
|
190
|
-
await this.handlePRDReviewWorkflow(finalProjectName, workingDir, useCurrentDir);
|
|
191
|
-
// Show next steps with framework-specific guidance
|
|
192
|
-
this.showNextSteps(config, finalFramework, useCurrentDir);
|
|
193
95
|
}
|
|
194
96
|
catch (error) {
|
|
195
97
|
spinner.error({ text: "Failed to initialize project" });
|
|
196
|
-
|
|
98
|
+
console.error(chalk_1.default.red(`Error: ${error instanceof Error ? error.message : String(error)}`));
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Initialize an InstantDB project with full setup
|
|
104
|
+
*/
|
|
105
|
+
async initInstantDBProject(spinner, workingDir, projectPath, projectName, options, useCurrentDir) {
|
|
106
|
+
// 1. Run shadcn init
|
|
107
|
+
spinner.updateText("Running shadcn init...");
|
|
108
|
+
(0, child_process_1.execSync)("pnpm dlx shadcn@latest init", {
|
|
109
|
+
cwd: workingDir,
|
|
110
|
+
stdio: "inherit",
|
|
111
|
+
});
|
|
112
|
+
// 2. Prompt user for instant-cli init
|
|
113
|
+
spinner.stop();
|
|
114
|
+
const { runInstantInit } = await (0, prompts_1.default)({
|
|
115
|
+
type: "confirm",
|
|
116
|
+
name: "runInstantInit",
|
|
117
|
+
message: "Run 'npx instant-cli init' to initialize InstantDB?",
|
|
118
|
+
initial: true,
|
|
119
|
+
});
|
|
120
|
+
if (runInstantInit) {
|
|
121
|
+
spinner.start();
|
|
122
|
+
spinner.updateText("Running instant-cli init...");
|
|
123
|
+
(0, child_process_1.execSync)("npx instant-cli init", {
|
|
124
|
+
cwd: projectPath,
|
|
125
|
+
stdio: "inherit",
|
|
126
|
+
});
|
|
127
|
+
// Prompt user to push schemas
|
|
128
|
+
spinner.stop();
|
|
129
|
+
const { pushSchemas } = await (0, prompts_1.default)({
|
|
130
|
+
type: "confirm",
|
|
131
|
+
name: "pushSchemas",
|
|
132
|
+
message: "Push schemas to InstantDB dashboard? (Make sure you've configured your app)",
|
|
133
|
+
initial: false,
|
|
134
|
+
});
|
|
135
|
+
if (pushSchemas) {
|
|
136
|
+
spinner.start();
|
|
137
|
+
spinner.updateText("Pushing schemas to InstantDB...");
|
|
138
|
+
(0, child_process_1.execSync)("npx instant-cli push", {
|
|
139
|
+
cwd: projectPath,
|
|
140
|
+
stdio: "inherit",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
197
143
|
}
|
|
144
|
+
// 3. Install @instantdb/react and @instantdb/admin
|
|
145
|
+
spinner.start();
|
|
146
|
+
spinner.updateText("Installing InstantDB packages...");
|
|
147
|
+
(0, child_process_1.execSync)("pnpm add @instantdb/react @instantdb/admin", {
|
|
148
|
+
cwd: projectPath,
|
|
149
|
+
stdio: "inherit",
|
|
150
|
+
});
|
|
151
|
+
// Install bcrypt for auth utilities
|
|
152
|
+
spinner.updateText("Installing auth dependencies...");
|
|
153
|
+
(0, child_process_1.execSync)("pnpm add bcryptjs nanoid && pnpm add -D @types/bcryptjs", {
|
|
154
|
+
cwd: projectPath,
|
|
155
|
+
stdio: "inherit",
|
|
156
|
+
});
|
|
157
|
+
// 4. Copy InstantDB templates to lib folder
|
|
158
|
+
spinner.updateText("Copying InstantDB template files...");
|
|
159
|
+
await this.fs.copyInstantDBTemplates(projectPath);
|
|
160
|
+
// 5. Initialize MyContext directory structure and context
|
|
161
|
+
spinner.updateText("Initializing MyContext project files...");
|
|
162
|
+
const config = await this.fs.initializeProject(projectName, options.description || `${projectName} - AI-powered app`, workingDir, useCurrentDir);
|
|
163
|
+
spinner.success({
|
|
164
|
+
text: `Project "${projectName}" initialized successfully with InstantDB!`,
|
|
165
|
+
});
|
|
166
|
+
// Show next steps
|
|
167
|
+
this.showNextSteps(config, "instantdb", useCurrentDir);
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Initialize a Next.js project (shadcn + MyContext only)
|
|
171
|
+
*/
|
|
172
|
+
async initNextJSProject(spinner, workingDir, projectPath, projectName, options, useCurrentDir) {
|
|
173
|
+
// 1. Run shadcn init
|
|
174
|
+
spinner.updateText("Running shadcn init...");
|
|
175
|
+
(0, child_process_1.execSync)("pnpm dlx shadcn@latest init", {
|
|
176
|
+
cwd: workingDir,
|
|
177
|
+
stdio: "inherit",
|
|
178
|
+
});
|
|
179
|
+
// 2. Initialize MyContext directory structure and context
|
|
180
|
+
spinner.updateText("Initializing MyContext project files...");
|
|
181
|
+
const config = await this.fs.initializeProject(projectName, options.description || `${projectName} - Next.js app`, workingDir, useCurrentDir);
|
|
182
|
+
spinner.success({
|
|
183
|
+
text: `Project "${projectName}" initialized successfully with Next.js!`,
|
|
184
|
+
});
|
|
185
|
+
// Show next steps
|
|
186
|
+
this.showNextSteps(config, "nextjs", useCurrentDir);
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Initialize a basic MyContext project (no framework)
|
|
190
|
+
*/
|
|
191
|
+
async initBasicProject(spinner, projectPath, projectName, options, useCurrentDir) {
|
|
192
|
+
spinner.updateText("Initializing MyContext project files...");
|
|
193
|
+
const config = await this.fs.initializeProject(projectName, options.description || `${projectName} - AI-powered app`, process.cwd(), useCurrentDir);
|
|
194
|
+
spinner.success({
|
|
195
|
+
text: `Project "${projectName}" initialized successfully!`,
|
|
196
|
+
});
|
|
197
|
+
// Show next steps
|
|
198
|
+
this.showNextSteps(config, undefined, useCurrentDir);
|
|
198
199
|
}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
console.log(chalk_1.default.gray(" The PRD is the foundation of your project - make sure it accurately describes your requirements."));
|
|
203
|
-
const projectPath = useCurrentDir
|
|
204
|
-
? workingDir
|
|
205
|
-
: path.join(workingDir, projectName);
|
|
206
|
-
const prdPath = path.join(projectPath, ".mycontext", "01-prd.md");
|
|
207
|
-
console.log(chalk_1.default.cyan("\nš Your PRD has been initialized at:"));
|
|
208
|
-
console.log(chalk_1.default.gray(` ${prdPath}`));
|
|
209
|
-
console.log(chalk_1.default.cyan("\nš Next steps:"));
|
|
210
|
-
console.log(chalk_1.default.gray(" 1. Open and review the PRD file"));
|
|
211
|
-
console.log(chalk_1.default.gray(" 2. Update it with your specific requirements"));
|
|
212
|
-
console.log(chalk_1.default.gray(" 3. Add user stories, acceptance criteria, and technical details"));
|
|
213
|
-
console.log(chalk_1.default.gray(" 4. Run 'mycontext generate context' when ready"));
|
|
214
|
-
console.log(chalk_1.default.yellow("\nā ļø Reminder: Context generation requires a reviewed PRD!"));
|
|
215
|
-
console.log(chalk_1.default.gray(" The AI will use your PRD to generate accurate types and brand guidelines."));
|
|
200
|
+
isValidProjectName(name) {
|
|
201
|
+
// Allow alphanumeric, hyphens, and underscores
|
|
202
|
+
return /^[a-zA-Z0-9._-]+$/.test(name);
|
|
216
203
|
}
|
|
217
204
|
showNextSteps(config, framework, useCurrentDir) {
|
|
218
|
-
const projectPath = useCurrentDir ?
|
|
205
|
+
const projectPath = useCurrentDir ? "." : config.name;
|
|
219
206
|
console.log(chalk_1.default.blue("\nšÆ Quick Start:\n"));
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
207
|
+
if (!useCurrentDir) {
|
|
208
|
+
console.log(chalk_1.default.yellow("1. Navigate to your project:"));
|
|
209
|
+
console.log(chalk_1.default.gray(` cd ${projectPath}\n`));
|
|
210
|
+
}
|
|
211
|
+
if (framework === "instantdb") {
|
|
212
|
+
console.log(chalk_1.default.yellow("2. Configure InstantDB:"));
|
|
213
|
+
console.log(chalk_1.default.gray(" ⢠Add your InstantDB App ID to .env.local:"));
|
|
214
|
+
console.log(chalk_1.default.cyan(" NEXT_PUBLIC_INSTANT_APP_ID=your-app-id"));
|
|
215
|
+
console.log(chalk_1.default.gray(" ⢠Template files copied to lib/ (or src/lib/):"));
|
|
216
|
+
console.log(chalk_1.default.gray(" - instant-client.ts (Client SDK)"));
|
|
217
|
+
console.log(chalk_1.default.gray(" - instant-admin.ts (Admin SDK)"));
|
|
218
|
+
console.log(chalk_1.default.gray(" - auth.ts (Auth helpers)"));
|
|
219
|
+
console.log(chalk_1.default.gray(" - instantdb-storage.ts (File storage)\n"));
|
|
220
|
+
}
|
|
221
|
+
console.log(chalk_1.default.yellow("3. ļø Analyze a screenshot (Gemini 2.0 Flash):"));
|
|
223
222
|
console.log(chalk_1.default.cyan(" mycontext analyze /path/to/screenshot.png"));
|
|
224
223
|
console.log(chalk_1.default.gray(" # Reverse-engineer any UI into a comprehensive spec!\n"));
|
|
225
|
-
console.log(chalk_1.default.yellow("
|
|
226
|
-
console.log(chalk_1.default.gray("
|
|
224
|
+
console.log(chalk_1.default.yellow("4. Configure AI provider:"));
|
|
225
|
+
console.log(chalk_1.default.gray(" š„ Gemini (Free - Recommended for screenshots):"));
|
|
227
226
|
console.log(chalk_1.default.gray(" Get API key: https://aistudio.google.com/apikey"));
|
|
228
227
|
console.log(chalk_1.default.cyan(" echo 'GEMINI_API_KEY=your-key' >> .mycontext/.env\n"));
|
|
229
228
|
console.log(chalk_1.default.gray(" š Claude (Best for text generation):"));
|
|
230
229
|
console.log(chalk_1.default.gray(" https://console.anthropic.com/\n"));
|
|
231
|
-
console.log(chalk_1.default.yellow("
|
|
230
|
+
console.log(chalk_1.default.yellow("5. Generate full context:"));
|
|
232
231
|
console.log(chalk_1.default.gray(" mycontext generate context --full\n"));
|
|
233
|
-
console.log(chalk_1.default.yellow("
|
|
232
|
+
console.log(chalk_1.default.yellow("6. Start development:"));
|
|
234
233
|
console.log(chalk_1.default.gray(" pnpm dev\n"));
|
|
235
234
|
console.log(chalk_1.default.green("⨠Tips:"));
|
|
236
235
|
console.log(chalk_1.default.gray("⢠Check .mycontext/ for all generated files"));
|
|
236
|
+
if (framework === "instantdb") {
|
|
237
|
+
console.log(chalk_1.default.gray("⢠InstantDB templates are ready to use in your lib/ folder"));
|
|
238
|
+
console.log(chalk_1.default.gray("⢠Update instant.schema.ts with your data model"));
|
|
239
|
+
}
|
|
237
240
|
console.log(chalk_1.default.gray("⢠Use --yes flag to skip prompts"));
|
|
238
241
|
console.log(chalk_1.default.gray("⢠Run 'mycontext status' to check project progress\n"));
|
|
239
242
|
// Exit the process gracefully after displaying all information
|
|
@@ -255,346 +258,6 @@ class InitCommand {
|
|
|
255
258
|
console.log(chalk_1.default.blue.bold("\nš MyContext - Screenshot to Spec\n"));
|
|
256
259
|
}
|
|
257
260
|
}
|
|
258
|
-
async detectPackageManager() {
|
|
259
|
-
const pnpmLock = path.join(process.cwd(), "pnpm-lock.yaml");
|
|
260
|
-
const yarnLock = path.join(process.cwd(), "yarn.lock");
|
|
261
|
-
const packageLock = path.join(process.cwd(), "package-lock.json");
|
|
262
|
-
if (await fs.pathExists(pnpmLock))
|
|
263
|
-
return "pnpm";
|
|
264
|
-
if (await fs.pathExists(yarnLock))
|
|
265
|
-
return "yarn";
|
|
266
|
-
if (await fs.pathExists(packageLock))
|
|
267
|
-
return "npm";
|
|
268
|
-
return "pnpm"; // Default to pnpm
|
|
269
|
-
}
|
|
270
|
-
async installInstantDBDeps(projectPath) {
|
|
271
|
-
try {
|
|
272
|
-
const packageManager = await this.detectPackageManager();
|
|
273
|
-
const installCmd = packageManager === "pnpm"
|
|
274
|
-
? "pnpm add"
|
|
275
|
-
: packageManager === "yarn"
|
|
276
|
-
? "yarn add"
|
|
277
|
-
: "npm install";
|
|
278
|
-
console.log(chalk_1.default.gray(" Installing InstantDB dependencies..."));
|
|
279
|
-
(0, child_process_1.execSync)(`${installCmd} @instantdb/react @instantdb/admin @tanstack/react-query`, {
|
|
280
|
-
cwd: projectPath,
|
|
281
|
-
stdio: "inherit",
|
|
282
|
-
timeout: 180000,
|
|
283
|
-
});
|
|
284
|
-
console.log(chalk_1.default.green(" ā
Dependencies installed"));
|
|
285
|
-
}
|
|
286
|
-
catch (error) {
|
|
287
|
-
console.log(chalk_1.default.yellow(" ā ļø Failed to install dependencies automatically"));
|
|
288
|
-
console.log(chalk_1.default.gray(" Run manually:"));
|
|
289
|
-
console.log(chalk_1.default.gray(" pnpm add @instantdb/react @instantdb/admin @tanstack/react-query"));
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
async generateInstantDBSchema(projectPath) {
|
|
293
|
-
try {
|
|
294
|
-
const schemaTemplatePath = path.join(__dirname, "../templates/instantdb/schema.template.ts");
|
|
295
|
-
const schemaContent = await fs.readFile(schemaTemplatePath, "utf-8");
|
|
296
|
-
const schemaPath = path.join(projectPath, "instant.schema.ts");
|
|
297
|
-
await fs.writeFile(schemaPath, schemaContent);
|
|
298
|
-
console.log(chalk_1.default.green(" ā
instant.schema.ts created"));
|
|
299
|
-
}
|
|
300
|
-
catch (error) {
|
|
301
|
-
console.log(chalk_1.default.yellow(" ā ļø Failed to create schema file"));
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
async generateInstantDBPerms(projectPath) {
|
|
305
|
-
try {
|
|
306
|
-
const permsTemplatePath = path.join(__dirname, "../templates/instantdb/perms.template.ts");
|
|
307
|
-
const permsContent = await fs.readFile(permsTemplatePath, "utf-8");
|
|
308
|
-
const permsPath = path.join(projectPath, "instant.perms.ts");
|
|
309
|
-
await fs.writeFile(permsPath, permsContent);
|
|
310
|
-
console.log(chalk_1.default.green(" ā
instant.perms.ts created"));
|
|
311
|
-
}
|
|
312
|
-
catch (error) {
|
|
313
|
-
console.log(chalk_1.default.yellow(" ā ļø Failed to create permissions file"));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
async generateDBClient(projectPath) {
|
|
317
|
-
try {
|
|
318
|
-
const dbTemplatePath = path.join(__dirname, "../templates/instantdb/db.template.ts");
|
|
319
|
-
const dbContent = await fs.readFile(dbTemplatePath, "utf-8");
|
|
320
|
-
const libDir = path.join(projectPath, "lib");
|
|
321
|
-
await fs.ensureDir(libDir);
|
|
322
|
-
const dbPath = path.join(libDir, "db.ts");
|
|
323
|
-
await fs.writeFile(dbPath, dbContent);
|
|
324
|
-
console.log(chalk_1.default.green(" ā
lib/db.ts created"));
|
|
325
|
-
}
|
|
326
|
-
catch (error) {
|
|
327
|
-
console.log(chalk_1.default.yellow(" ā ļø Failed to create database client"));
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
async createInstantDBEnv(projectPath, appId) {
|
|
331
|
-
try {
|
|
332
|
-
const envPath = path.join(projectPath, ".env");
|
|
333
|
-
const envContent = `# InstantDB Configuration
|
|
334
|
-
NEXT_PUBLIC_INSTANT_APP_ID=${appId || "__YOUR_APP_ID_HERE__"}
|
|
335
|
-
|
|
336
|
-
# Get your app ID from: https://instantdb.com/dash
|
|
337
|
-
# Create a new app or use an existing one
|
|
338
|
-
`;
|
|
339
|
-
// Check if .env exists
|
|
340
|
-
if (await fs.pathExists(envPath)) {
|
|
341
|
-
const existingEnv = await fs.readFile(envPath, "utf-8");
|
|
342
|
-
if (!existingEnv.includes("NEXT_PUBLIC_INSTANT_APP_ID")) {
|
|
343
|
-
await fs.appendFile(envPath, "\n" + envContent);
|
|
344
|
-
console.log(chalk_1.default.green(" ā
.env updated with InstantDB config"));
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
console.log(chalk_1.default.gray(" ā
.env already has InstantDB config"));
|
|
348
|
-
}
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
await fs.writeFile(envPath, envContent);
|
|
352
|
-
console.log(chalk_1.default.green(" ā
.env created"));
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
catch (error) {
|
|
356
|
-
console.log(chalk_1.default.yellow(" ā ļø Failed to create .env file"));
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
async generateSampleComponents(projectPath) {
|
|
360
|
-
try {
|
|
361
|
-
// Generate home-client.tsx
|
|
362
|
-
const homeClientTemplatePath = path.join(__dirname, "../templates/instantdb/home-client.template.tsx");
|
|
363
|
-
const homeClientContent = await fs.readFile(homeClientTemplatePath, "utf-8");
|
|
364
|
-
const appDir = path.join(projectPath, "app");
|
|
365
|
-
await fs.ensureDir(appDir);
|
|
366
|
-
const homeClientPath = path.join(appDir, "home-client.tsx");
|
|
367
|
-
await fs.writeFile(homeClientPath, homeClientContent);
|
|
368
|
-
console.log(chalk_1.default.green(" ā
app/home-client.tsx created"));
|
|
369
|
-
// Generate/update page.tsx
|
|
370
|
-
const pageTemplatePath = path.join(__dirname, "../templates/instantdb/page.template.tsx");
|
|
371
|
-
const pageContent = await fs.readFile(pageTemplatePath, "utf-8");
|
|
372
|
-
const pagePath = path.join(appDir, "page.tsx");
|
|
373
|
-
await fs.writeFile(pagePath, pageContent);
|
|
374
|
-
console.log(chalk_1.default.green(" ā
app/page.tsx updated"));
|
|
375
|
-
}
|
|
376
|
-
catch (error) {
|
|
377
|
-
console.log(chalk_1.default.yellow(" ā ļø Failed to create sample components"));
|
|
378
|
-
}
|
|
379
|
-
}
|
|
380
|
-
async pushInstantDBSchema(projectPath) {
|
|
381
|
-
try {
|
|
382
|
-
console.log(chalk_1.default.gray(" Pushing schema to InstantDB..."));
|
|
383
|
-
(0, child_process_1.execSync)("npx instant-cli@latest push -y", {
|
|
384
|
-
cwd: projectPath,
|
|
385
|
-
stdio: "inherit",
|
|
386
|
-
timeout: 60000,
|
|
387
|
-
});
|
|
388
|
-
console.log(chalk_1.default.green(" ā
Schema pushed to InstantDB"));
|
|
389
|
-
}
|
|
390
|
-
catch (error) {
|
|
391
|
-
console.log(chalk_1.default.yellow(" ā ļø Schema push failed (you can push it manually later)"));
|
|
392
|
-
console.log(chalk_1.default.gray(" Run: npx instant-cli@latest push"));
|
|
393
|
-
}
|
|
394
|
-
}
|
|
395
|
-
isValidProjectName(name) {
|
|
396
|
-
// Allow alphanumeric, hyphens, and underscores
|
|
397
|
-
return /^[a-zA-Z0-9_-]+$/.test(name);
|
|
398
|
-
}
|
|
399
|
-
async setupInstantDBProject(projectName, workingDir, useCurrentDir) {
|
|
400
|
-
try {
|
|
401
|
-
const projectPath = useCurrentDir
|
|
402
|
-
? workingDir
|
|
403
|
-
: path.join(workingDir, projectName);
|
|
404
|
-
// Check if InstantDB project already exists
|
|
405
|
-
const instantSchemaPath = path.join(projectPath, "instant.schema.ts");
|
|
406
|
-
const packageJsonPath = path.join(projectPath, "package.json");
|
|
407
|
-
if ((await fs.pathExists(instantSchemaPath)) &&
|
|
408
|
-
(await fs.pathExists(packageJsonPath))) {
|
|
409
|
-
console.log(chalk_1.default.gray(" ā
InstantDB project structure detected"));
|
|
410
|
-
return;
|
|
411
|
-
}
|
|
412
|
-
console.log(chalk_1.default.blue("\nšļø Setting up InstantDB...\n"));
|
|
413
|
-
// Step 1: Install dependencies
|
|
414
|
-
await this.installInstantDBDeps(projectPath);
|
|
415
|
-
// Step 2: Generate schema
|
|
416
|
-
await this.generateInstantDBSchema(projectPath);
|
|
417
|
-
// Step 3: Generate permissions
|
|
418
|
-
await this.generateInstantDBPerms(projectPath);
|
|
419
|
-
// Step 4: Create database client
|
|
420
|
-
await this.generateDBClient(projectPath);
|
|
421
|
-
// Step 5: Create environment file
|
|
422
|
-
await this.createInstantDBEnv(projectPath);
|
|
423
|
-
// Step 6: Generate sample components
|
|
424
|
-
await this.generateSampleComponents(projectPath);
|
|
425
|
-
// Step 7: Push schema to InstantDB
|
|
426
|
-
await this.pushInstantDBSchema(projectPath);
|
|
427
|
-
console.log(chalk_1.default.green("\nā
InstantDB setup complete!\n"));
|
|
428
|
-
console.log(chalk_1.default.yellow("š Next steps:"));
|
|
429
|
-
console.log(chalk_1.default.gray(" 1. Get your App ID from: https://instantdb.com/dash"));
|
|
430
|
-
console.log(chalk_1.default.gray(" 2. Update NEXT_PUBLIC_INSTANT_APP_ID in .env"));
|
|
431
|
-
console.log(chalk_1.default.gray(" 3. Run: pnpm dev"));
|
|
432
|
-
console.log(chalk_1.default.gray(" 4. Open http://localhost:3000 to see your todo app\n"));
|
|
433
|
-
}
|
|
434
|
-
catch (error) {
|
|
435
|
-
console.log(chalk_1.default.yellow(`\nā ļø InstantDB setup encountered an issue: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
436
|
-
console.log(chalk_1.default.gray(" You can complete the setup manually if needed"));
|
|
437
|
-
console.log(chalk_1.default.blue("š MyContext will continue with project setup...\n"));
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
async setupNextJSProject(projectName, workingDir, useCurrentDir) {
|
|
441
|
-
try {
|
|
442
|
-
const projectPath = path.join(workingDir, projectName);
|
|
443
|
-
// Check if Next.js project already exists
|
|
444
|
-
const packageJsonPath = path.join(projectPath, "package.json");
|
|
445
|
-
if (await fs.pathExists(packageJsonPath)) {
|
|
446
|
-
console.log(chalk_1.default.gray(" ā
Next.js project structure detected"));
|
|
447
|
-
return;
|
|
448
|
-
}
|
|
449
|
-
// Create Next.js project
|
|
450
|
-
console.log(chalk_1.default.gray(" Creating Next.js project..."));
|
|
451
|
-
try {
|
|
452
|
-
(0, child_process_1.execSync)(`npx create-next-app@latest ${projectName} --typescript --tailwind --eslint --app --import-alias "@/*" --yes`, {
|
|
453
|
-
cwd: workingDir,
|
|
454
|
-
stdio: "inherit",
|
|
455
|
-
timeout: 300000, // 5 minutes
|
|
456
|
-
});
|
|
457
|
-
console.log(chalk_1.default.green(" ā
Next.js project created"));
|
|
458
|
-
}
|
|
459
|
-
catch (error) {
|
|
460
|
-
console.log(chalk_1.default.yellow(` ā ļø Failed to create Next.js project automatically`));
|
|
461
|
-
console.log(chalk_1.default.gray(` Please create it manually:`));
|
|
462
|
-
console.log(chalk_1.default.gray(` npx create-next-app@latest ${projectName} --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"`));
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
catch (error) {
|
|
466
|
-
console.log(chalk_1.default.yellow(` ā ļø Next.js setup encountered an issue: ${error instanceof Error ? error.message : "Unknown error"}`));
|
|
467
|
-
console.log(chalk_1.default.gray(" You can create the Next.js project manually if needed"));
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
async setupShadcn(projectName, workingDir, useCurrentDir) {
|
|
471
|
-
const projectPath = path.join(workingDir, projectName);
|
|
472
|
-
try {
|
|
473
|
-
console.log(chalk_1.default.gray(" Running shadcn/ui init (pnpm first)..."));
|
|
474
|
-
try {
|
|
475
|
-
(0, child_process_1.execSync)(`pnpm dlx shadcn@latest init -y`, {
|
|
476
|
-
cwd: projectPath,
|
|
477
|
-
stdio: "inherit",
|
|
478
|
-
timeout: 180000,
|
|
479
|
-
});
|
|
480
|
-
console.log(chalk_1.default.green(" ā
shadcn/ui initialized (pnpm)"));
|
|
481
|
-
return;
|
|
482
|
-
}
|
|
483
|
-
catch (e) {
|
|
484
|
-
console.log(chalk_1.default.gray(" pnpm not available or failed, trying npx..."));
|
|
485
|
-
}
|
|
486
|
-
(0, child_process_1.execSync)(`npx shadcn@latest init -y`, {
|
|
487
|
-
cwd: projectPath,
|
|
488
|
-
stdio: "inherit",
|
|
489
|
-
timeout: 180000,
|
|
490
|
-
});
|
|
491
|
-
console.log(chalk_1.default.green(" ā
shadcn/ui initialized (npx)"));
|
|
492
|
-
}
|
|
493
|
-
catch (error) {
|
|
494
|
-
console.log(chalk_1.default.yellow(" ā ļø shadcn/ui init failed. You can run it manually inside the project:"));
|
|
495
|
-
console.log(chalk_1.default.gray(" pnpm dlx shadcn@latest init -y"));
|
|
496
|
-
console.log(chalk_1.default.gray(" # or"));
|
|
497
|
-
console.log(chalk_1.default.gray(" npx shadcn@latest init -y"));
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
async isExistingProject(projectPath) {
|
|
501
|
-
const packageJsonPath = path.join(projectPath, "package.json");
|
|
502
|
-
const instantSchemaPath = path.join(projectPath, "instant.schema.ts");
|
|
503
|
-
const nextConfigPath = path.join(projectPath, "next.config.js");
|
|
504
|
-
const nextConfigTsPath = path.join(projectPath, "next.config.ts");
|
|
505
|
-
const hasPackageJson = await fs.pathExists(packageJsonPath);
|
|
506
|
-
const hasInstantSchema = await fs.pathExists(instantSchemaPath);
|
|
507
|
-
const hasNextConfig = (await fs.pathExists(nextConfigPath)) ||
|
|
508
|
-
(await fs.pathExists(nextConfigTsPath));
|
|
509
|
-
if (!hasPackageJson)
|
|
510
|
-
return false;
|
|
511
|
-
try {
|
|
512
|
-
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf-8"));
|
|
513
|
-
// Check for InstantDB project
|
|
514
|
-
const hasInstantDependency = packageJson.dependencies?.["@instantdb/react"] ||
|
|
515
|
-
packageJson.dependencies?.["@instantdb/core"];
|
|
516
|
-
// Check for Next.js project
|
|
517
|
-
const hasNextDependency = packageJson.dependencies?.next || packageJson.devDependencies?.next;
|
|
518
|
-
return ((hasInstantDependency && hasInstantSchema) ||
|
|
519
|
-
(hasNextDependency && hasNextConfig));
|
|
520
|
-
}
|
|
521
|
-
catch {
|
|
522
|
-
return false;
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
async askToAnalyze() {
|
|
526
|
-
const response = await (0, prompts_1.default)({
|
|
527
|
-
type: "confirm",
|
|
528
|
-
name: "analyze",
|
|
529
|
-
message: "Would you like to analyze this existing project and generate context files?",
|
|
530
|
-
initial: true,
|
|
531
|
-
});
|
|
532
|
-
return response.analyze;
|
|
533
|
-
}
|
|
534
|
-
async analyzeExistingProject(projectPath, options) {
|
|
535
|
-
console.log(chalk_1.default.blue.bold("š Analyzing Existing Project\n"));
|
|
536
|
-
try {
|
|
537
|
-
// Import and use AnalyzeCommand
|
|
538
|
-
const { AnalyzeCommand } = await Promise.resolve().then(() => __importStar(require("./analyze")));
|
|
539
|
-
const analyzeCommand = new AnalyzeCommand();
|
|
540
|
-
await analyzeCommand.execute(projectPath, {
|
|
541
|
-
output: ".mycontext",
|
|
542
|
-
generateContext: true,
|
|
543
|
-
includeBrand: true,
|
|
544
|
-
includeTypes: true,
|
|
545
|
-
includeComponents: true,
|
|
546
|
-
verbose: options.verbose || false,
|
|
547
|
-
});
|
|
548
|
-
console.log(chalk_1.default.green.bold("\nā
Existing project analysis completed!"));
|
|
549
|
-
console.log(chalk_1.default.yellow("\nš Next Steps:"));
|
|
550
|
-
console.log(chalk_1.default.gray("1. Review the generated context files in .mycontext/"));
|
|
551
|
-
console.log(chalk_1.default.gray("2. Run 'mycontext generate context --full' to enhance context"));
|
|
552
|
-
console.log(chalk_1.default.gray("3. Run 'mycontext generate components-list' to plan new components"));
|
|
553
|
-
console.log(chalk_1.default.gray("4. Run 'mycontext generate-components' to create new components"));
|
|
554
|
-
}
|
|
555
|
-
catch (error) {
|
|
556
|
-
console.error(chalk_1.default.red("ā Analysis failed:"), error);
|
|
557
|
-
// Don't re-throw - let the CLI handle it
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
async setupStudio(projectPath) {
|
|
561
|
-
try {
|
|
562
|
-
const studioPath = path.join(projectPath, "studio");
|
|
563
|
-
// Check if Studio is bundled (exists in CLI directory)
|
|
564
|
-
const bundledStudioPath = path.join(__dirname, "../../studio");
|
|
565
|
-
if (!(await this.fs.exists(bundledStudioPath))) {
|
|
566
|
-
console.log(chalk_1.default.yellow("š± Studio not bundled - skipping setup"));
|
|
567
|
-
return;
|
|
568
|
-
}
|
|
569
|
-
console.log(chalk_1.default.blue("š± Setting up MyContext Studio preview..."));
|
|
570
|
-
// Copy Studio to project
|
|
571
|
-
await fs.copy(bundledStudioPath, studioPath);
|
|
572
|
-
// Install Studio dependencies
|
|
573
|
-
console.log(chalk_1.default.blue("š¦ Installing Studio dependencies..."));
|
|
574
|
-
try {
|
|
575
|
-
(0, child_process_1.execSync)("pnpm install", {
|
|
576
|
-
cwd: studioPath,
|
|
577
|
-
stdio: "inherit",
|
|
578
|
-
timeout: 120000, // 2 minutes timeout
|
|
579
|
-
});
|
|
580
|
-
}
|
|
581
|
-
catch (error) {
|
|
582
|
-
console.log(chalk_1.default.yellow("ā ļø Failed to install Studio dependencies. You can run 'cd studio && pnpm install' manually."));
|
|
583
|
-
}
|
|
584
|
-
// Create .env.local for Studio
|
|
585
|
-
const envLocalPath = path.join(studioPath, ".env.local");
|
|
586
|
-
const envContent = `# MyContext Studio Configuration
|
|
587
|
-
NEXT_PUBLIC_CLI_COMPONENTS_PATH=../components/generated
|
|
588
|
-
NEXT_PUBLIC_STUDIO_VERSION=0.1.0
|
|
589
|
-
`;
|
|
590
|
-
await fs.writeFile(envLocalPath, envContent);
|
|
591
|
-
console.log(chalk_1.default.green("ā
Studio setup complete!"));
|
|
592
|
-
console.log(chalk_1.default.gray(" Run 'pnpm studio:dev' to start the preview server"));
|
|
593
|
-
}
|
|
594
|
-
catch (error) {
|
|
595
|
-
console.log(chalk_1.default.yellow("ā ļø Studio setup failed - you can set it up manually later"));
|
|
596
|
-
}
|
|
597
|
-
}
|
|
598
261
|
}
|
|
599
262
|
exports.InitCommand = InitCommand;
|
|
600
263
|
//# sourceMappingURL=init.js.map
|