create-nyoworks 2.7.2 → 3.0.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.
@@ -0,0 +1,45 @@
1
+ export interface AppDefinition {
2
+ id: string;
3
+ name: string;
4
+ name_tr: string;
5
+ description: string;
6
+ description_tr: string;
7
+ platforms: ("web" | "mobile" | "desktop")[];
8
+ features: string[];
9
+ integrations: string[];
10
+ }
11
+ export interface IntegrationDefinition {
12
+ name: string;
13
+ providers?: string[];
14
+ default?: string;
15
+ services?: string[];
16
+ free_tier?: Record<string, string>;
17
+ note?: string;
18
+ }
19
+ export interface PlatformDefinition {
20
+ name: string;
21
+ framework: string;
22
+ version: string;
23
+ }
24
+ export interface AppsConfig {
25
+ apps: AppDefinition[];
26
+ integrations: Record<string, IntegrationDefinition>;
27
+ platforms: Record<string, PlatformDefinition>;
28
+ }
29
+ export declare function loadAppsConfig(repoDir: string): Promise<AppsConfig>;
30
+ export declare function appToPromptChoice(app: AppDefinition, lang?: string): {
31
+ title: string;
32
+ value: string;
33
+ description: string;
34
+ };
35
+ export declare function integrationToPromptChoices(integration: IntegrationDefinition, integrationId: string): {
36
+ choices: {
37
+ title: string;
38
+ value: string;
39
+ }[];
40
+ initial: number;
41
+ message: string;
42
+ } | null;
43
+ export declare function getAppFeatures(app: AppDefinition): string[];
44
+ export declare function getAppIntegrations(app: AppDefinition): string[];
45
+ export declare function getAppPlatforms(app: AppDefinition): string[];
@@ -0,0 +1,68 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // Config Loader - Load app definitions from YAML
3
+ // ═══════════════════════════════════════════════════════════════════════════════
4
+ import fs from "fs-extra";
5
+ import path from "path";
6
+ import { parse } from "yaml";
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ // Loader Functions
9
+ // ─────────────────────────────────────────────────────────────────────────────
10
+ export async function loadAppsConfig(repoDir) {
11
+ const configPath = path.join(repoDir, "config", "apps.yaml");
12
+ if (!await fs.pathExists(configPath)) {
13
+ throw new Error("config/apps.yaml not found in repository");
14
+ }
15
+ const content = await fs.readFile(configPath, "utf8");
16
+ return parse(content);
17
+ }
18
+ export function appToPromptChoice(app, lang = "en") {
19
+ const name = lang === "tr" ? app.name_tr : app.name;
20
+ const desc = lang === "tr" ? app.description_tr : app.description;
21
+ return {
22
+ title: name,
23
+ value: app.id,
24
+ description: desc,
25
+ };
26
+ }
27
+ export function integrationToPromptChoices(integration, integrationId) {
28
+ if (!integration.providers || integration.providers.length <= 1) {
29
+ return null;
30
+ }
31
+ const defaultIndex = integration.providers.indexOf(integration.default || integration.providers[0]);
32
+ return {
33
+ choices: integration.providers.map((provider) => ({
34
+ title: formatProviderName(provider),
35
+ value: provider,
36
+ })),
37
+ initial: defaultIndex >= 0 ? defaultIndex : 0,
38
+ message: `${integration.name} provider:`,
39
+ };
40
+ }
41
+ function formatProviderName(provider) {
42
+ const names = {
43
+ mollie: "Mollie (iDEAL, Bancontact)",
44
+ adyen: "Adyen",
45
+ stripe: "Stripe",
46
+ resend: "Resend (3000/ay free)",
47
+ brevo: "Brevo (300/gün free)",
48
+ twilio: "Twilio",
49
+ plivo: "Plivo",
50
+ postnl: "PostNL",
51
+ sendcloud: "Sendcloud",
52
+ dhl: "DHL",
53
+ storecove: "Storecove",
54
+ };
55
+ return names[provider] || provider.charAt(0).toUpperCase() + provider.slice(1);
56
+ }
57
+ // ─────────────────────────────────────────────────────────────────────────────
58
+ // Feature Helpers
59
+ // ─────────────────────────────────────────────────────────────────────────────
60
+ export function getAppFeatures(app) {
61
+ return app.features || [];
62
+ }
63
+ export function getAppIntegrations(app) {
64
+ return app.integrations || [];
65
+ }
66
+ export function getAppPlatforms(app) {
67
+ return app.platforms || ["web"];
68
+ }
package/dist/init.js CHANGED
@@ -1,47 +1,23 @@
1
+ // ═══════════════════════════════════════════════════════════════════════════════
2
+ // Create NYOWORKS - Project Initialization
3
+ // ═══════════════════════════════════════════════════════════════════════════════
1
4
  import prompts from "prompts";
2
5
  import pc from "picocolors";
3
6
  import fs from "fs-extra";
4
7
  import path from "path";
5
8
  import os from "os";
9
+ import { fileURLToPath } from "url";
6
10
  import { execa } from "execa";
7
11
  import { replacePlaceholders } from "./replace.js";
8
12
  import { checkDependencies, showClaudeMaxWarning, getDockerComposeCommand } from "./checks.js";
13
+ import { loadAppsConfig, appToPromptChoice, integrationToPromptChoices, getAppFeatures, getAppPlatforms, } from "./config-loader.js";
14
+ const __filename = fileURLToPath(import.meta.url);
15
+ const __dirname = path.dirname(__filename);
16
+ // ─────────────────────────────────────────────────────────────────────────────
17
+ // Constants
18
+ // ─────────────────────────────────────────────────────────────────────────────
9
19
  const REPO = "naimozcan/nyoworks-framework";
10
20
  const BRANCH = "main";
11
- const PRODUCT_TYPES = [
12
- { title: "E-Commerce", value: "ecommerce", description: "Online store, product sales" },
13
- { title: "Booking", value: "booking", description: "Appointment/reservation system" },
14
- { title: "SaaS Platform", value: "saas", description: "Subscription-based software" },
15
- { title: "Marketplace", value: "marketplace", description: "Multi-vendor platform" },
16
- { title: "Content/Blog", value: "content", description: "Blog, news, CMS" },
17
- { title: "CRM", value: "crm", description: "Customer relationship management" },
18
- { title: "Custom", value: "custom", description: "Manual feature selection" },
19
- ];
20
- const REQUIRED_FEATURES = {
21
- ecommerce: ["payments", "crm", "notifications", "search", "storage"],
22
- booking: ["appointments", "payments", "crm", "notifications"],
23
- saas: ["payments", "subscriptions", "analytics", "notifications", "audit", "multitenant"],
24
- marketplace: ["payments", "crm", "analytics", "notifications", "audit", "search", "storage", "multitenant"],
25
- content: ["analytics", "search", "storage"],
26
- crm: ["crm", "analytics", "notifications", "audit", "export"],
27
- custom: [],
28
- };
29
- const AVAILABLE_FEATURES = [
30
- { title: "Analytics", value: "analytics", description: "User behavior tracking" },
31
- { title: "Appointments", value: "appointments", description: "Booking system" },
32
- { title: "Audit", value: "audit", description: "Activity logging" },
33
- { title: "Auth Social", value: "auth-social", description: "Google, Apple, GitHub OAuth" },
34
- { title: "CRM", value: "crm", description: "Customer relationship management" },
35
- { title: "Export", value: "export", description: "PDF/CSV export" },
36
- { title: "i18n", value: "i18n", description: "Multi-language support" },
37
- { title: "Multitenant", value: "multitenant", description: "Multi-organization support" },
38
- { title: "Notifications", value: "notifications", description: "Email, SMS, Push" },
39
- { title: "Payments", value: "payments", description: "Stripe integration" },
40
- { title: "Realtime", value: "realtime", description: "WebSocket support" },
41
- { title: "Search", value: "search", description: "Full-text search" },
42
- { title: "Storage", value: "storage", description: "File uploads (S3/R2)" },
43
- { title: "Subscriptions", value: "subscriptions", description: "Plans & usage limits" },
44
- ];
45
21
  const AVAILABLE_PLATFORMS = [
46
22
  { title: "Web", value: "web", description: "Next.js 16" },
47
23
  { title: "Mobile", value: "mobile", description: "Expo SDK 54" },
@@ -57,6 +33,16 @@ const LANGUAGE_RESPONSES = {
57
33
  en: "English",
58
34
  nl: "Dutch",
59
35
  };
36
+ const ADDITIONAL_FEATURES = [
37
+ { title: "Realtime", value: "realtime", description: "WebSocket support" },
38
+ { title: "i18n", value: "i18n", description: "Multi-language support" },
39
+ { title: "Auth Social", value: "auth-social", description: "Google, Apple, GitHub OAuth" },
40
+ { title: "Multitenant", value: "multitenant", description: "Multi-organization support" },
41
+ { title: "Subscriptions", value: "subscriptions", description: "Plans & usage limits" },
42
+ ];
43
+ // ─────────────────────────────────────────────────────────────────────────────
44
+ // Helper Functions
45
+ // ─────────────────────────────────────────────────────────────────────────────
60
46
  function generateCode(name) {
61
47
  return name
62
48
  .toUpperCase()
@@ -84,13 +70,44 @@ async function downloadRepo(repo, branch) {
84
70
  await execa("tar", ["-xzf", tarFile, "-C", tempDir]);
85
71
  return path.join(tempDir, `nyoworks-framework-${branch}`);
86
72
  }
73
+ function getLocalRepoPath() {
74
+ const localPath = path.resolve(__dirname, "..", "..", "..");
75
+ const configPath = path.join(localPath, "config", "apps.yaml");
76
+ if (fs.existsSync(configPath)) {
77
+ return localPath;
78
+ }
79
+ return null;
80
+ }
81
+ async function replacePackageNames(targetDir, slug) {
82
+ const pkgFiles = await findPackageJsonFiles(targetDir);
83
+ for (const pkgFile of pkgFiles) {
84
+ let content = await fs.readFile(pkgFile, "utf8");
85
+ content = content.replace(/@nyoworks\//g, `@${slug}/`);
86
+ await fs.writeFile(pkgFile, content);
87
+ }
88
+ }
89
+ async function findPackageJsonFiles(dir) {
90
+ const results = [];
91
+ const entries = await fs.readdir(dir, { withFileTypes: true });
92
+ for (const entry of entries) {
93
+ const fullPath = path.join(dir, entry.name);
94
+ if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== ".git") {
95
+ results.push(...await findPackageJsonFiles(fullPath));
96
+ }
97
+ else if (entry.isFile() && entry.name === "package.json") {
98
+ results.push(fullPath);
99
+ }
100
+ }
101
+ return results;
102
+ }
103
+ // ─────────────────────────────────────────────────────────────────────────────
104
+ // Main Function
105
+ // ─────────────────────────────────────────────────────────────────────────────
87
106
  export async function createProject(projectName) {
88
107
  console.log();
89
108
  console.log(pc.cyan(pc.bold(" NYOWORKS Framework")));
90
109
  console.log(pc.dim(" Create a new project"));
91
110
  console.log();
92
- let productType = "custom";
93
- let requiredFeatures = [];
94
111
  const nameResponse = await prompts({
95
112
  type: projectName ? null : "text",
96
113
  name: "name",
@@ -102,77 +119,119 @@ export async function createProject(projectName) {
102
119
  console.log(pc.red("Aborted."));
103
120
  process.exit(1);
104
121
  }
105
- const productResponse = await prompts({
106
- type: "select",
107
- name: "productType",
108
- message: "Product type:",
109
- choices: PRODUCT_TYPES,
110
- initial: 0,
111
- });
112
- if (productResponse.productType) {
113
- productType = productResponse.productType;
114
- requiredFeatures = REQUIRED_FEATURES[productType] || [];
115
- }
116
- const featureChoices = AVAILABLE_FEATURES.map((f) => {
117
- const isRequired = requiredFeatures.includes(f.value);
118
- return {
119
- title: isRequired ? `${f.title} [required]` : f.title,
120
- value: f.value,
121
- description: f.description,
122
- selected: isRequired,
123
- };
124
- });
125
- const response = await prompts([
126
- {
127
- type: "multiselect",
128
- name: "platforms",
129
- message: "Select platforms:",
130
- choices: AVAILABLE_PLATFORMS,
131
- min: 1,
132
- hint: "- Space to select. Return to submit",
133
- instructions: false,
134
- },
135
- {
136
- type: "multiselect",
137
- name: "features",
138
- message: "Select features:",
139
- choices: featureChoices,
140
- hint: "- Space to select. Return to submit",
141
- instructions: false,
142
- },
143
- {
144
- type: "select",
145
- name: "language",
146
- message: "Agent response language:",
147
- choices: AVAILABLE_LANGUAGES,
148
- initial: 0,
149
- },
150
- ]);
151
122
  const name = (nameResponse.name || projectName);
152
123
  const code = generateCode(name);
153
124
  const slug = generateSlug(name);
154
125
  const databaseName = generateDatabaseName(name);
155
- const platforms = response.platforms || ["web"];
156
- const selectedFeatures = response.features || [];
157
- const features = [...new Set([...requiredFeatures, ...selectedFeatures])];
158
- const language = response.language || "tr";
159
126
  const targetDir = path.resolve(process.cwd(), slug);
160
127
  if (fs.existsSync(targetDir)) {
161
128
  console.log(pc.red(`Directory ${slug} already exists.`));
162
129
  process.exit(1);
163
130
  }
164
131
  console.log();
165
- process.stdout.write(pc.cyan("Downloading from GitHub..."));
166
132
  let repoDir;
167
- try {
168
- repoDir = await downloadRepo(REPO, BRANCH);
169
- console.log(pc.green(" done"));
133
+ let config;
134
+ let isLocalMode = false;
135
+ const localRepo = getLocalRepoPath();
136
+ if (localRepo) {
137
+ process.stdout.write(pc.cyan("Using local framework..."));
138
+ repoDir = localRepo;
139
+ isLocalMode = true;
140
+ try {
141
+ config = await loadAppsConfig(repoDir);
142
+ console.log(pc.green(" done"));
143
+ }
144
+ catch (error) {
145
+ console.log(pc.red(" failed"));
146
+ console.error(pc.red("Failed to load local config."));
147
+ process.exit(1);
148
+ }
170
149
  }
171
- catch (error) {
172
- console.log(pc.red(" failed"));
173
- console.error(pc.red("Failed to download from GitHub. Check your internet connection."));
150
+ else {
151
+ process.stdout.write(pc.cyan("Downloading from GitHub..."));
152
+ try {
153
+ repoDir = await downloadRepo(REPO, BRANCH);
154
+ config = await loadAppsConfig(repoDir);
155
+ console.log(pc.green(" done"));
156
+ }
157
+ catch (error) {
158
+ console.log(pc.red(" failed"));
159
+ console.error(pc.red("Failed to download from GitHub. Check your internet connection."));
160
+ process.exit(1);
161
+ }
162
+ }
163
+ const appChoices = config.apps.map((app) => appToPromptChoice(app, "tr"));
164
+ const appResponse = await prompts({
165
+ type: "select",
166
+ name: "appId",
167
+ message: "App tipi:",
168
+ choices: appChoices,
169
+ initial: 0,
170
+ });
171
+ if (!appResponse.appId) {
172
+ console.log(pc.red("Aborted."));
174
173
  process.exit(1);
175
174
  }
175
+ const selectedApp = config.apps.find((a) => a.id === appResponse.appId);
176
+ const appPlatforms = getAppPlatforms(selectedApp);
177
+ const appFeatures = getAppFeatures(selectedApp);
178
+ const platformChoices = AVAILABLE_PLATFORMS
179
+ .filter((p) => appPlatforms.includes(p.value))
180
+ .map((p) => ({ ...p, selected: true }));
181
+ const platformResponse = await prompts({
182
+ type: "multiselect",
183
+ name: "platforms",
184
+ message: "Platforms:",
185
+ choices: platformChoices,
186
+ min: 1,
187
+ hint: "- Space to select. Return to submit",
188
+ instructions: false,
189
+ });
190
+ const platforms = platformResponse.platforms || ["web"];
191
+ const selectedProviders = {};
192
+ for (const integrationId of selectedApp.integrations || []) {
193
+ const integration = config.integrations?.[integrationId];
194
+ if (!integration)
195
+ continue;
196
+ const promptConfig = integrationToPromptChoices(integration, integrationId);
197
+ if (!promptConfig) {
198
+ selectedProviders[integrationId] = integration.default || integration.providers?.[0] || integrationId;
199
+ continue;
200
+ }
201
+ const providerResponse = await prompts({
202
+ type: "select",
203
+ name: "provider",
204
+ message: promptConfig.message,
205
+ choices: promptConfig.choices,
206
+ initial: promptConfig.initial,
207
+ });
208
+ if (providerResponse.provider) {
209
+ selectedProviders[integrationId] = providerResponse.provider;
210
+ }
211
+ }
212
+ const additionalFeaturesNotInApp = ADDITIONAL_FEATURES.filter((f) => !appFeatures.includes(f.value));
213
+ let additionalFeatures = [];
214
+ if (additionalFeaturesNotInApp.length > 0) {
215
+ const additionalResponse = await prompts({
216
+ type: "multiselect",
217
+ name: "features",
218
+ message: "Ek feature'lar (isteğe bağlı):",
219
+ choices: additionalFeaturesNotInApp,
220
+ hint: "- Space to select. Return to submit",
221
+ instructions: false,
222
+ });
223
+ additionalFeatures = additionalResponse.features || [];
224
+ }
225
+ const languageResponse = await prompts({
226
+ type: "select",
227
+ name: "language",
228
+ message: "Agent language:",
229
+ choices: AVAILABLE_LANGUAGES,
230
+ initial: 0,
231
+ });
232
+ const language = languageResponse.language || "tr";
233
+ const features = [...new Set([...appFeatures, ...additionalFeatures])];
234
+ const appId = selectedApp.id;
176
235
  process.stdout.write(pc.dim(" Copying files..."));
177
236
  await fs.ensureDir(targetDir);
178
237
  const corePaths = [
@@ -187,6 +246,7 @@ export async function createProject(projectName) {
187
246
  "docs/bible",
188
247
  "mcp-server",
189
248
  ".claude",
249
+ "config",
190
250
  ];
191
251
  for (const p of corePaths) {
192
252
  const src = path.join(repoDir, p);
@@ -196,10 +256,20 @@ export async function createProject(projectName) {
196
256
  }
197
257
  }
198
258
  for (const platform of platforms) {
199
- const src = path.join(repoDir, `apps/${platform}`);
200
- const dest = path.join(targetDir, `apps/${platform}`);
201
- if (await fs.pathExists(src)) {
202
- await fs.copy(src, dest);
259
+ const templateSrc = path.join(repoDir, `apps/_templates/${platform}`);
260
+ const legacySrc = path.join(repoDir, `apps/${platform}`);
261
+ const dest = path.join(targetDir, `apps/${appId}/${platform}`);
262
+ if (await fs.pathExists(templateSrc)) {
263
+ await fs.copy(templateSrc, dest);
264
+ }
265
+ else if (await fs.pathExists(legacySrc)) {
266
+ await fs.copy(legacySrc, dest);
267
+ }
268
+ const pkgPath = path.join(dest, "package.json");
269
+ if (await fs.pathExists(pkgPath)) {
270
+ const pkg = await fs.readJson(pkgPath);
271
+ pkg.name = `@${slug}/${appId}-${platform}`;
272
+ await fs.writeJson(pkgPath, pkg, { spaces: 2 });
203
273
  }
204
274
  }
205
275
  for (const feature of features) {
@@ -209,6 +279,26 @@ export async function createProject(projectName) {
209
279
  await fs.copy(src, dest);
210
280
  }
211
281
  }
282
+ const integrationFeatureMap = {
283
+ payments: ["payments"],
284
+ shipping: ["shipping"],
285
+ invoicing: ["invoicing"],
286
+ whatsapp: ["whatsapp"],
287
+ google: ["google"],
288
+ ai: ["ai"],
289
+ email: [],
290
+ sms: [],
291
+ };
292
+ for (const integrationId of selectedApp.integrations || []) {
293
+ const featuresToCopy = integrationFeatureMap[integrationId] || [];
294
+ for (const featureName of featuresToCopy) {
295
+ const src = path.join(repoDir, `packages/features/${featureName}`);
296
+ const dest = path.join(targetDir, `packages/features/${featureName}`);
297
+ if (await fs.pathExists(src) && !await fs.pathExists(dest)) {
298
+ await fs.copy(src, dest);
299
+ }
300
+ }
301
+ }
212
302
  const rootFiles = [
213
303
  "package.json",
214
304
  "pnpm-workspace.yaml",
@@ -227,14 +317,21 @@ export async function createProject(projectName) {
227
317
  await fs.copy(src, dest);
228
318
  }
229
319
  }
230
- await fs.remove(path.dirname(repoDir));
320
+ if (!isLocalMode) {
321
+ await fs.remove(path.dirname(repoDir));
322
+ }
323
+ console.log(pc.green(" done"));
324
+ process.stdout.write(pc.dim(" Updating package names..."));
325
+ await replacePackageNames(targetDir, slug);
231
326
  console.log(pc.green(" done"));
232
327
  const placeholders = {
233
328
  "${PROJECT_NAME}": name,
234
329
  "${PROJECT_CODE}": code,
235
330
  "${PROJECT_SLUG}": slug,
236
331
  "${DATABASE_NAME}": databaseName,
237
- "${PRODUCT_TYPE}": productType,
332
+ "${APP_ID}": appId,
333
+ "${APP_NAME}": selectedApp.name,
334
+ "${PRODUCT_TYPE}": appId,
238
335
  "${RESPONSE_LANGUAGE}": LANGUAGE_RESPONSES[language] || "Turkish",
239
336
  };
240
337
  process.stdout.write(pc.dim(" Replacing placeholders..."));
@@ -242,7 +339,8 @@ export async function createProject(projectName) {
242
339
  console.log(pc.green(" done"));
243
340
  for (const feature of features) {
244
341
  const featureDoc = path.join(targetDir, "docs", "bible", "features", `${feature}.md`);
245
- const content = `# Feature: ${feature.charAt(0).toUpperCase() + feature.slice(1)}
342
+ if (!await fs.pathExists(featureDoc)) {
343
+ const content = `# Feature: ${feature.charAt(0).toUpperCase() + feature.slice(1)}
246
344
 
247
345
  ## Overview
248
346
 
@@ -275,17 +373,30 @@ See \`docs/bible/data/schema.md\`
275
373
  |----|----------|-----------|
276
374
  | T-xxx | [Decision] | [Why] |
277
375
  `;
278
- await fs.outputFile(featureDoc, content);
279
- console.log(pc.dim(` Created docs/bible/features/${feature}.md`));
376
+ await fs.outputFile(featureDoc, content);
377
+ console.log(pc.dim(` Created docs/bible/features/${feature}.md`));
378
+ }
280
379
  }
281
380
  const configPath = path.join(targetDir, "nyoworks.config.yaml");
282
381
  if (await fs.pathExists(configPath)) {
283
- let config = await fs.readFile(configPath, "utf8");
382
+ let configContent = await fs.readFile(configPath, "utf8");
284
383
  if (features.length > 0) {
285
- config = config.replace(/enabled: \[\]/, `enabled:\n${features.map((f) => ` - ${f}`).join("\n")}`);
384
+ configContent = configContent.replace(/enabled: \[\]/, `enabled:\n${features.map((f) => ` - ${f}`).join("\n")}`);
286
385
  }
287
- config = config.replace(/targets:\n - web/, `targets:\n${platforms.map((p) => ` - ${p}`).join("\n")}`);
288
- await fs.writeFile(configPath, config);
386
+ configContent = configContent.replace(/targets:\n - web/, `targets:\n${platforms.map((p) => ` - ${p}`).join("\n")}`);
387
+ await fs.writeFile(configPath, configContent);
388
+ }
389
+ const workspacePath = path.join(targetDir, "pnpm-workspace.yaml");
390
+ if (await fs.pathExists(workspacePath)) {
391
+ const workspaceContent = `packages:
392
+ - "apps/server"
393
+ - "apps/*/*"
394
+ - "packages/*"
395
+ - "packages/features/*"
396
+ - "packages/platforms/*"
397
+ - "mcp-server"
398
+ `;
399
+ await fs.writeFile(workspacePath, workspaceContent);
289
400
  }
290
401
  console.log();
291
402
  console.log(pc.green(pc.bold("Project created successfully!")));
@@ -307,9 +418,12 @@ See \`docs/bible/data/schema.md\`
307
418
  console.log(pc.dim(" Configuration:"));
308
419
  console.log(pc.dim(` Name: ${name}`));
309
420
  console.log(pc.dim(` Code: ${code}`));
310
- console.log(pc.dim(` Product: ${productType}`));
421
+ console.log(pc.dim(` App: ${selectedApp.name} (${appId})`));
311
422
  console.log(pc.dim(` Platforms: ${platforms.join(", ")}`));
312
423
  console.log(pc.dim(` Features: ${features.join(", ") || "none"}`));
424
+ if (Object.keys(selectedProviders).length > 0) {
425
+ console.log(pc.dim(` Providers: ${Object.entries(selectedProviders).map(([k, v]) => `${k}:${v}`).join(", ")}`));
426
+ }
313
427
  console.log(pc.dim(` Language: ${LANGUAGE_RESPONSES[language] || "Turkish"}`));
314
428
  console.log();
315
429
  process.stdout.write(pc.dim(" Building MCP server..."));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-nyoworks",
3
- "version": "2.7.2",
3
+ "version": "3.0.0",
4
4
  "description": "Create a new NYOWORKS project",
5
5
  "type": "module",
6
6
  "bin": {
@@ -30,7 +30,8 @@
30
30
  "execa": "^9.6.1",
31
31
  "fs-extra": "^11.3.3",
32
32
  "picocolors": "^1.1.1",
33
- "prompts": "^2.4.2"
33
+ "prompts": "^2.4.2",
34
+ "yaml": "^2.8.2"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/fs-extra": "^11.0.4",