create-einja-app 0.1.2 → 0.2.2

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 (47) hide show
  1. package/README.md +80 -1
  2. package/dist/cli.js +875 -23
  3. package/dist/cli.js.map +1 -1
  4. package/package.json +1 -1
  5. package/templates/default/.env.development +25 -0
  6. package/templates/default/.env.local +24 -0
  7. package/templates/default/.env.production +25 -0
  8. package/templates/default/.env.staging +25 -0
  9. package/templates/default/.github/dependabot.yml +63 -0
  10. package/templates/default/.github/workflows/ci.yml +52 -0
  11. package/templates/default/.github/workflows/claude.yml +38 -0
  12. package/templates/default/.husky/_/applypatch-msg +2 -0
  13. package/templates/default/.husky/_/commit-msg +2 -0
  14. package/templates/default/.husky/_/gitignore +1 -0
  15. package/templates/default/.husky/_/h +22 -0
  16. package/templates/default/.husky/_/husky.sh +9 -0
  17. package/templates/default/.husky/_/post-applypatch +2 -0
  18. package/templates/default/.husky/_/post-checkout +2 -0
  19. package/templates/default/.husky/_/post-commit +2 -0
  20. package/templates/default/.husky/_/post-merge +2 -0
  21. package/templates/default/.husky/_/post-rewrite +2 -0
  22. package/templates/default/.husky/_/pre-applypatch +2 -0
  23. package/templates/default/.husky/_/pre-auto-gc +2 -0
  24. package/templates/default/.husky/_/pre-commit +2 -0
  25. package/templates/default/.husky/_/pre-merge-commit +2 -0
  26. package/templates/default/.husky/_/pre-push +2 -0
  27. package/templates/default/.husky/_/pre-rebase +2 -0
  28. package/templates/default/.husky/_/prepare-commit-msg +2 -0
  29. package/templates/default/.serena/gitignore +1 -0
  30. package/templates/default/.serena/project.yml +84 -0
  31. package/templates/default/CLAUDE.md +27 -0
  32. package/templates/default/README.md +150 -45
  33. package/templates/default/apps/web/server/presentation/routes/userRoutes.ts +2 -5
  34. package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTable.tsx +110 -113
  35. package/templates/default/apps/web/src/app/(authenticated)/data/_components/UserTableContainer.tsx +5 -17
  36. package/templates/default/apps/web/src/app/(authenticated)/data/page.tsx +9 -7
  37. package/templates/default/apps/web/src/app/api/rpc/[[...route]]/route.ts +1 -1
  38. package/templates/default/apps/web/src/hooks/api/prefetch-users.ts +63 -0
  39. package/templates/default/apps/web/src/hooks/{use-users.ts → api/use-users.ts} +11 -46
  40. package/templates/default/apps/web/src/lib/api/parse-response.ts +114 -0
  41. package/templates/default/apps/web/src/shared/schemas/user.ts +36 -0
  42. package/templates/default/gitignore +86 -0
  43. package/templates/default/package.json +1 -1
  44. package/templates/default/worktree.config.json +14 -0
  45. package/templates/default/.templateignore +0 -60
  46. package/templates/default/middleware.ts +0 -32
  47. package/templates/default/apps/web/src/lib/{api-client.ts → api/client.ts} +1 -1
package/dist/cli.js CHANGED
@@ -2,12 +2,12 @@
2
2
 
3
3
  // src/cli.ts
4
4
  import { Command } from "commander";
5
- import { readFileSync as readFileSync4 } from "fs";
6
- import { fileURLToPath as fileURLToPath2 } from "url";
7
- import { dirname as dirname3, join as join10 } from "path";
5
+ import { readFileSync as readFileSync5 } from "fs";
6
+ import { fileURLToPath as fileURLToPath3 } from "url";
7
+ import { dirname as dirname5, join as join14 } from "path";
8
8
 
9
9
  // src/commands/create.ts
10
- import { existsSync as existsSync3 } from "fs";
10
+ import { existsSync as existsSync3, readdirSync } from "fs";
11
11
  import { resolve } from "path";
12
12
  import ora2 from "ora";
13
13
 
@@ -28,6 +28,12 @@ async function promptProjectConfig(defaultProjectName) {
28
28
  return true;
29
29
  }
30
30
  },
31
+ {
32
+ type: "confirm",
33
+ name: "useCurrentDir",
34
+ message: "\u4ECA\u3044\u308B\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u76F4\u63A5\u4F5C\u6210\u3057\u307E\u3059\u304B\uFF1F\uFF08No\u306A\u3089\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4F5C\u6210\uFF09",
35
+ default: false
36
+ },
31
37
  {
32
38
  type: "input",
33
39
  name: "packageScope",
@@ -54,7 +60,7 @@ async function promptProjectConfig(defaultProjectName) {
54
60
  {
55
61
  type: "confirm",
56
62
  name: "setupEinjaCli",
57
- message: "@einja/cli \u3092\u81EA\u52D5\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u307E\u3059\u304B\uFF1F",
63
+ message: "@einja/dev-cli \u3092\u81EA\u52D5\u30BB\u30C3\u30C8\u30A2\u30C3\u30D7\u3057\u307E\u3059\u304B\uFF1F",
58
64
  default: true
59
65
  },
60
66
  {
@@ -147,7 +153,8 @@ async function promptProjectConfig(defaultProjectName) {
147
153
  authMethod: answers.authMethod,
148
154
  tools,
149
155
  setupEinjaCli: answers.setupEinjaCli,
150
- worktreeConfig
156
+ worktreeConfig,
157
+ useCurrentDir: answers.useCurrentDir
151
158
  };
152
159
  }
153
160
 
@@ -295,6 +302,21 @@ function renameTemplateFiles(targetPath) {
295
302
  removeSync(file);
296
303
  }
297
304
  }
305
+ function renameSpecialFiles(targetPath) {
306
+ const gitignoreFiles = glob.sync("**/gitignore", {
307
+ cwd: targetPath,
308
+ absolute: true,
309
+ dot: true
310
+ });
311
+ for (const file of gitignoreFiles) {
312
+ const dir = dirname2(file);
313
+ const newPath = join2(dir, ".gitignore");
314
+ if (existsSync2(file)) {
315
+ copySync(file, newPath);
316
+ removeSync(file);
317
+ }
318
+ }
319
+ }
298
320
  function excludeAuthFiles(targetPath, authMethod) {
299
321
  const excludePatterns = getAuthExcludePatterns(authMethod);
300
322
  if (excludePatterns.length === 0) {
@@ -331,7 +353,7 @@ async function generateTemplate(config, targetPath) {
331
353
  "dist",
332
354
  "logs",
333
355
  ".env",
334
- ".env.local",
356
+ // フェイルセーフ(暗号化キーファイル)
335
357
  ".DS_Store",
336
358
  "Thumbs.db",
337
359
  "coverage"
@@ -349,6 +371,7 @@ async function generateTemplate(config, targetPath) {
349
371
  });
350
372
  excludeAuthFiles(targetPath, config.authMethod);
351
373
  renameTemplateFiles(targetPath);
374
+ renameSpecialFiles(targetPath);
352
375
  info("\u30D7\u30EC\u30FC\u30B9\u30DB\u30EB\u30C0\u30FC\u5909\u6570\u3092\u7F6E\u63DB\u4E2D...");
353
376
  const variables = {
354
377
  projectName: config.projectName,
@@ -413,10 +436,15 @@ function printCompletionMessage(config) {
413
436
  console.log(chalk2.bold("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:"));
414
437
  console.log();
415
438
  console.log(chalk2.cyan(` cd ${config.projectName}`));
439
+ console.log(chalk2.cyan(" pnpm env:update # \u74B0\u5883\u5909\u6570\u3092\u8A2D\u5B9A"));
416
440
  console.log(chalk2.cyan(" docker-compose up -d postgres"));
417
441
  console.log(chalk2.cyan(" pnpm dev"));
418
442
  console.log();
419
- console.log(chalk2.gray("\u958B\u767A\u30B5\u30FC\u30D0\u30FC: http://localhost:3000"));
443
+ console.log(chalk2.gray("\u958B\u767A\u30B5\u30FC\u30D0\u30FC: \u30BF\u30FC\u30DF\u30CA\u30EB\u306B\u8868\u793A\u3055\u308C\u308BURL\u3092\u78BA\u8A8D"));
444
+ console.log();
445
+ console.log(
446
+ chalk2.yellow("\u26A0 \u91CD\u8981: ") + chalk2.gray("pnpm env:update \u3067\u74B0\u5883\u5909\u6570\u3092\u81EA\u5206\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u7528\u306B\u518D\u8A2D\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044")
447
+ );
420
448
  console.log();
421
449
  console.log(chalk2.gray("\u8A73\u7D30\u306F README.md \u3092\u3054\u78BA\u8A8D\u304F\u3060\u3055\u3044\u3002"));
422
450
  console.log();
@@ -457,19 +485,29 @@ async function execPostSetup(config, targetPath, options) {
457
485
  await promptAndExecuteDirenvAllow(targetPath);
458
486
  }
459
487
  if (config.setupEinjaCli) {
460
- const einjaSpinner = ora("@einja/cli \u3092\u521D\u671F\u5316\u4E2D...").start();
488
+ const einjaSpinner = ora("@einja/dev-cli \u3092\u521D\u671F\u5316\u4E2D...").start();
461
489
  try {
462
- await execa("npx", ["@einja/cli", "init"], { cwd: targetPath });
463
- einjaSpinner.succeed("@einja/cli \u3092\u521D\u671F\u5316\u3057\u307E\u3057\u305F");
490
+ await execa("npx", ["@einja/dev-cli", "init", "--force"], { cwd: targetPath });
491
+ einjaSpinner.succeed("@einja/dev-cli \u3092\u521D\u671F\u5316\u3057\u307E\u3057\u305F");
464
492
  } catch (error2) {
465
- einjaSpinner.fail("@einja/cli \u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
466
- warn("\u5F8C\u3067\u624B\u52D5\u3067 'npx @einja/cli init' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
493
+ einjaSpinner.fail("@einja/dev-cli \u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
494
+ warn("\u5F8C\u3067\u624B\u52D5\u3067 'npx @einja/dev-cli init' \u3092\u5B9F\u884C\u3057\u3066\u304F\u3060\u3055\u3044");
467
495
  }
468
496
  }
469
497
  printCompletionMessage(config);
470
498
  }
471
499
 
472
500
  // src/commands/create.ts
501
+ function isDirectoryEmpty(dirPath) {
502
+ if (!existsSync3(dirPath)) {
503
+ return true;
504
+ }
505
+ const files = readdirSync(dirPath);
506
+ const significantFiles = files.filter(
507
+ (f) => !f.startsWith(".")
508
+ );
509
+ return significantFiles.length === 0;
510
+ }
473
511
  function validateProjectName(projectName) {
474
512
  const regex = /^[a-zA-Z][a-zA-Z0-9_-]{0,49}$/;
475
513
  if (!regex.test(projectName)) {
@@ -502,7 +540,8 @@ async function createCommand(projectName, options) {
502
540
  husky: true
503
541
  },
504
542
  setupEinjaCli: true,
505
- worktreeConfig: void 0
543
+ worktreeConfig: void 0,
544
+ useCurrentDir: false
506
545
  };
507
546
  info(`\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u540D: ${config.projectName}`);
508
547
  info(`\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8: ${config.template}`);
@@ -510,11 +549,19 @@ async function createCommand(projectName, options) {
510
549
  } else {
511
550
  config = await promptProjectConfig(projectName);
512
551
  }
513
- const targetPath = resolve(process.cwd(), config.projectName);
514
- if (checkProjectExists(targetPath)) {
515
- error(`\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA '${config.projectName}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`);
516
- info("\u5225\u306E\u540D\u524D\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001\u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u3066\u304F\u3060\u3055\u3044");
517
- process.exit(1);
552
+ const targetPath = config.useCurrentDir ? process.cwd() : resolve(process.cwd(), config.projectName);
553
+ if (config.useCurrentDir) {
554
+ if (!isDirectoryEmpty(targetPath)) {
555
+ error("\u73FE\u5728\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u306B\u30D5\u30A1\u30A4\u30EB\u304C\u5B58\u5728\u3057\u307E\u3059");
556
+ info("\u7A7A\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3067\u5B9F\u884C\u3059\u308B\u304B\u3001\u30B5\u30D6\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u4F5C\u6210\u3057\u3066\u304F\u3060\u3055\u3044");
557
+ process.exit(1);
558
+ }
559
+ } else {
560
+ if (checkProjectExists(targetPath)) {
561
+ error(`\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA '${config.projectName}' \u306F\u65E2\u306B\u5B58\u5728\u3057\u307E\u3059`);
562
+ info("\u5225\u306E\u540D\u524D\u3092\u6307\u5B9A\u3059\u308B\u304B\u3001\u65E2\u5B58\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u3092\u524A\u9664\u3057\u3066\u304F\u3060\u3055\u3044");
563
+ process.exit(1);
564
+ }
518
565
  }
519
566
  const spinner = ora2("\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4F5C\u6210\u4E2D...").start();
520
567
  try {
@@ -984,11 +1031,808 @@ async function setupCommand() {
984
1031
  success("\u958B\u767A\u3092\u958B\u59CB\u3067\u304D\u307E\u3059\uFF01");
985
1032
  }
986
1033
 
1034
+ // src/commands/add.ts
1035
+ import { existsSync as existsSync6 } from "fs";
1036
+ import { readFile as readFile2 } from "fs/promises";
1037
+ import { dirname as dirname4, join as join13 } from "path";
1038
+ import { fileURLToPath as fileURLToPath2 } from "url";
1039
+ import ora4 from "ora";
1040
+
1041
+ // src/prompts/add.ts
1042
+ import inquirer5 from "inquirer";
1043
+ function getDefaultAddConfig() {
1044
+ return {
1045
+ components: {
1046
+ packages: true,
1047
+ apps: true,
1048
+ config: true
1049
+ },
1050
+ packageComponents: ["front-core", "server-core", "config", "ui"],
1051
+ appComponents: ["web"],
1052
+ dryRun: false
1053
+ };
1054
+ }
1055
+ async function promptAddConfig(dryRun) {
1056
+ const componentAnswers = await inquirer5.prompt([
1057
+ {
1058
+ type: "checkbox",
1059
+ name: "components",
1060
+ message: "\u8FFD\u52A0\u3059\u308B\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u3092\u9078\u629E\uFF08Space\u3067\u9078\u629E\u3001Enter\u3067\u78BA\u5B9A\uFF09:",
1061
+ choices: [
1062
+ {
1063
+ name: "packages/ - \u5171\u901A\u30D1\u30C3\u30B1\u30FC\u30B8\uFF08front-core, server-core, config, ui\uFF09",
1064
+ value: "packages",
1065
+ checked: true
1066
+ },
1067
+ {
1068
+ name: "apps/ - \u30A2\u30D7\u30EA\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8",
1069
+ value: "apps",
1070
+ checked: true
1071
+ },
1072
+ {
1073
+ name: "\u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB - turbo.json, pnpm-workspace.yaml \u7B49",
1074
+ value: "config",
1075
+ checked: true
1076
+ }
1077
+ ]
1078
+ }
1079
+ ]);
1080
+ const selectedComponents = componentAnswers.components;
1081
+ const hasPackages = selectedComponents.includes("packages");
1082
+ const hasApps = selectedComponents.includes("apps");
1083
+ const hasConfig = selectedComponents.includes("config");
1084
+ let packageComponents = [];
1085
+ if (hasPackages) {
1086
+ const packageAnswers = await inquirer5.prompt([
1087
+ {
1088
+ type: "checkbox",
1089
+ name: "packages",
1090
+ message: "\u8FFD\u52A0\u3059\u308B\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E:",
1091
+ choices: [
1092
+ {
1093
+ name: "front-core - \u30D5\u30ED\u30F3\u30C8\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08\u8A8D\u8A3C\u8A2D\u5B9A\u3001hooks\u3001utils\uFF09",
1094
+ value: "front-core",
1095
+ checked: true
1096
+ },
1097
+ {
1098
+ name: "server-core - \u30D0\u30C3\u30AF\u30A8\u30F3\u30C9\u5171\u901A\u5C64\uFF08Prisma\u3001\u30C9\u30E1\u30A4\u30F3\u30ED\u30B8\u30C3\u30AF\uFF09",
1099
+ value: "server-core",
1100
+ checked: true
1101
+ },
1102
+ {
1103
+ name: "config - \u5171\u901A\u8A2D\u5B9A\uFF08Biome, TypeScript, Panda CSS\uFF09",
1104
+ value: "config",
1105
+ checked: true
1106
+ },
1107
+ {
1108
+ name: "ui - \u5171\u901AUI\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\uFF08shadcn/ui\uFF09",
1109
+ value: "ui",
1110
+ checked: true
1111
+ }
1112
+ ],
1113
+ validate: (input) => {
1114
+ if (input.length === 0) {
1115
+ return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
1116
+ }
1117
+ return true;
1118
+ }
1119
+ }
1120
+ ]);
1121
+ packageComponents = packageAnswers.packages;
1122
+ }
1123
+ let appComponents = [];
1124
+ if (hasApps) {
1125
+ const appAnswers = await inquirer5.prompt([
1126
+ {
1127
+ type: "checkbox",
1128
+ name: "apps",
1129
+ message: "\u8FFD\u52A0\u3059\u308B\u30A2\u30D7\u30EA\u3092\u9078\u629E:",
1130
+ choices: [
1131
+ {
1132
+ name: "web - \u30E1\u30A4\u30F3\u7BA1\u7406\u753B\u9762\u30A2\u30D7\u30EA\uFF08Next.js + App Router\uFF09",
1133
+ value: "web",
1134
+ checked: true
1135
+ }
1136
+ ],
1137
+ validate: (input) => {
1138
+ if (input.length === 0) {
1139
+ return "\u5C11\u306A\u304F\u3068\u30821\u3064\u306E\u30A2\u30D7\u30EA\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044";
1140
+ }
1141
+ return true;
1142
+ }
1143
+ }
1144
+ ]);
1145
+ appComponents = appAnswers.apps;
1146
+ }
1147
+ return {
1148
+ components: {
1149
+ packages: hasPackages,
1150
+ apps: hasApps,
1151
+ config: hasConfig
1152
+ },
1153
+ packageComponents,
1154
+ appComponents,
1155
+ dryRun
1156
+ };
1157
+ }
1158
+
1159
+ // src/generators/partials/packages.ts
1160
+ import { readdir } from "fs/promises";
1161
+ import { join as join10 } from "path";
1162
+
1163
+ // src/utils/merger.ts
1164
+ import { readFileSync as readFileSync4, writeFileSync as writeFileSync5, existsSync as existsSync5 } from "fs";
1165
+ import { dirname as dirname3 } from "path";
1166
+ function mergeTextWithMarkers(templateContent, existingContent) {
1167
+ if (existingContent === null) {
1168
+ return templateContent;
1169
+ }
1170
+ const templateSections = parseMarkers(templateContent);
1171
+ const localSections = parseMarkers(existingContent);
1172
+ const hasMarkers = templateSections.some(
1173
+ (s) => s.type === "managed" || s.type === "seed"
1174
+ );
1175
+ if (!hasMarkers) {
1176
+ return existingContent;
1177
+ }
1178
+ const templateManagedById = /* @__PURE__ */ new Map();
1179
+ const templateSeedById = /* @__PURE__ */ new Map();
1180
+ const processedTemplateIds = /* @__PURE__ */ new Set();
1181
+ for (const section of templateSections) {
1182
+ if (section.type === "managed" && section.id) {
1183
+ templateManagedById.set(section.id, section);
1184
+ } else if (section.type === "seed" && section.id) {
1185
+ templateSeedById.set(section.id, section);
1186
+ }
1187
+ }
1188
+ const result = [];
1189
+ for (const localSection of localSections) {
1190
+ if (localSection.type === "managed") {
1191
+ const match = localSection.id ? templateManagedById.get(localSection.id) : void 0;
1192
+ if (localSection.id && match) {
1193
+ processedTemplateIds.add(localSection.id);
1194
+ result.push(match.content);
1195
+ } else if (!localSection.id) {
1196
+ result.push(localSection.content);
1197
+ }
1198
+ } else if (localSection.type === "seed") {
1199
+ if (localSection.id) {
1200
+ processedTemplateIds.add(localSection.id);
1201
+ }
1202
+ result.push(localSection.content);
1203
+ } else {
1204
+ result.push(localSection.content);
1205
+ }
1206
+ }
1207
+ for (const [id, section] of templateManagedById) {
1208
+ if (!processedTemplateIds.has(id)) {
1209
+ result.push(section.content);
1210
+ }
1211
+ }
1212
+ for (const [id, section] of templateSeedById) {
1213
+ if (!processedTemplateIds.has(id)) {
1214
+ result.push(section.content);
1215
+ }
1216
+ }
1217
+ const firstElement = result[0];
1218
+ if (result.length > 0 && firstElement !== void 0 && firstElement.length === 0) {
1219
+ result.shift();
1220
+ }
1221
+ return result.join("\n");
1222
+ }
1223
+ function mergeJson(templateJson, existingJson, jsonPaths, filePath = "package.json") {
1224
+ if (existingJson === null) {
1225
+ return JSON.parse(JSON.stringify(templateJson));
1226
+ }
1227
+ return deepMergeWithPaths(
1228
+ templateJson,
1229
+ existingJson,
1230
+ jsonPaths,
1231
+ filePath,
1232
+ ""
1233
+ );
1234
+ }
1235
+ function deepMergeWithPaths(template, existing, jsonPaths, filePath, currentPath) {
1236
+ const result = JSON.parse(JSON.stringify(existing));
1237
+ for (const [key, templateValue] of Object.entries(template)) {
1238
+ const keyPath = currentPath ? `${currentPath}.${key}` : key;
1239
+ const existingValue = existing[key];
1240
+ if (isPathManaged(filePath, keyPath, jsonPaths)) {
1241
+ result[key] = deepClone(templateValue);
1242
+ } else if (isPathSeed(filePath, keyPath, jsonPaths)) {
1243
+ if (typeof templateValue === "object" && templateValue !== null && !Array.isArray(templateValue) && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
1244
+ result[key] = deepMergeWithPaths(
1245
+ templateValue,
1246
+ existingValue,
1247
+ jsonPaths,
1248
+ filePath,
1249
+ keyPath
1250
+ );
1251
+ } else if (!(key in existing)) {
1252
+ result[key] = deepClone(templateValue);
1253
+ }
1254
+ } else if (typeof templateValue === "object" && templateValue !== null && !Array.isArray(templateValue) && typeof existingValue === "object" && existingValue !== null && !Array.isArray(existingValue)) {
1255
+ result[key] = deepMergeWithPaths(
1256
+ templateValue,
1257
+ existingValue,
1258
+ jsonPaths,
1259
+ filePath,
1260
+ keyPath
1261
+ );
1262
+ } else if (!(key in existing)) {
1263
+ result[key] = deepClone(templateValue);
1264
+ }
1265
+ }
1266
+ return result;
1267
+ }
1268
+ function deepClone(value) {
1269
+ if (value === void 0) {
1270
+ return void 0;
1271
+ }
1272
+ return JSON.parse(JSON.stringify(value));
1273
+ }
1274
+ async function loadSyncMetadata(targetDir) {
1275
+ const metadataPath = `${targetDir}/.einja-sync.json`;
1276
+ if (!existsSync5(metadataPath)) {
1277
+ return null;
1278
+ }
1279
+ try {
1280
+ const content = readFileSync4(metadataPath, "utf-8");
1281
+ return JSON.parse(content);
1282
+ } catch {
1283
+ return null;
1284
+ }
1285
+ }
1286
+ async function saveSyncMetadata(targetDir, metadata) {
1287
+ const metadataPath = `${targetDir}/.einja-sync.json`;
1288
+ ensureDir(dirname3(metadataPath));
1289
+ writeFileSync5(metadataPath, JSON.stringify(metadata, null, 2), "utf-8");
1290
+ }
1291
+ async function mergeAndWriteFile(templatePath, targetPath, syncMetadata) {
1292
+ const templateContent = readFileSync4(templatePath, "utf-8");
1293
+ const targetExists = existsSync5(targetPath);
1294
+ const existingContent = targetExists ? readFileSync4(targetPath, "utf-8") : null;
1295
+ const isJsonFile = targetPath.endsWith(".json");
1296
+ let mergedContent;
1297
+ let action;
1298
+ if (!targetExists) {
1299
+ mergedContent = templateContent;
1300
+ action = "created";
1301
+ } else if (isJsonFile) {
1302
+ try {
1303
+ const templateJson = JSON.parse(templateContent);
1304
+ const existingJson = existingContent ? JSON.parse(existingContent) : null;
1305
+ const jsonPaths = syncMetadata.jsonPaths || { managed: {}, seed: {} };
1306
+ const fileName = targetPath.split("/").pop() || "package.json";
1307
+ const mergedJson = mergeJson(templateJson, existingJson, jsonPaths, fileName);
1308
+ mergedContent = JSON.stringify(mergedJson, null, 2);
1309
+ action = "merged";
1310
+ } catch {
1311
+ mergedContent = templateContent;
1312
+ action = "overwritten";
1313
+ }
1314
+ } else {
1315
+ mergedContent = mergeTextWithMarkers(templateContent, existingContent);
1316
+ if (mergedContent === existingContent) {
1317
+ action = "skipped";
1318
+ } else {
1319
+ action = "merged";
1320
+ }
1321
+ }
1322
+ if (action !== "skipped") {
1323
+ ensureDir(dirname3(targetPath));
1324
+ writeFileSync5(targetPath, mergedContent, "utf-8");
1325
+ }
1326
+ return { action, path: targetPath };
1327
+ }
1328
+ function parseMarkers(content) {
1329
+ const lines = content.split("\n");
1330
+ const sections = [];
1331
+ let currentType = "unmanaged";
1332
+ let currentStartLine = 1;
1333
+ let currentContent = [];
1334
+ let currentId;
1335
+ for (let i = 0; i < lines.length; i++) {
1336
+ const line = lines[i];
1337
+ const lineNumber = i + 1;
1338
+ const startMarker = parseStartMarker(line);
1339
+ if (startMarker) {
1340
+ if (currentType !== "unmanaged") {
1341
+ currentContent.push(line);
1342
+ continue;
1343
+ }
1344
+ if (currentContent.length > 0 || sections.length === 0) {
1345
+ sections.push({
1346
+ type: "unmanaged",
1347
+ startLine: currentStartLine,
1348
+ endLine: lineNumber - 1,
1349
+ content: currentContent.join("\n")
1350
+ });
1351
+ }
1352
+ currentType = startMarker.type;
1353
+ currentId = startMarker.id;
1354
+ currentStartLine = lineNumber;
1355
+ currentContent = [line];
1356
+ } else if (parseEndMarker(line)) {
1357
+ if (currentType === "unmanaged") {
1358
+ currentContent.push(line);
1359
+ continue;
1360
+ }
1361
+ currentContent.push(line);
1362
+ sections.push({
1363
+ type: currentType,
1364
+ startLine: currentStartLine,
1365
+ endLine: lineNumber,
1366
+ content: currentContent.join("\n"),
1367
+ id: currentId
1368
+ });
1369
+ currentType = "unmanaged";
1370
+ currentId = void 0;
1371
+ currentStartLine = lineNumber + 1;
1372
+ currentContent = [];
1373
+ } else {
1374
+ currentContent.push(line);
1375
+ }
1376
+ }
1377
+ if (currentContent.length > 0 || sections.length === 0) {
1378
+ sections.push({
1379
+ type: currentType,
1380
+ startLine: currentStartLine,
1381
+ endLine: lines.length,
1382
+ content: currentContent.join("\n"),
1383
+ id: currentId
1384
+ });
1385
+ }
1386
+ return sections;
1387
+ }
1388
+ function parseStartMarker(line) {
1389
+ const markdownManagedPattern = /^<!--\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*-->$/;
1390
+ let match = line.match(markdownManagedPattern);
1391
+ if (match) {
1392
+ return { type: "managed", id: match[1] || void 0 };
1393
+ }
1394
+ const markdownSeedPattern = /^<!--\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*-->$/;
1395
+ match = line.match(markdownSeedPattern);
1396
+ if (match) {
1397
+ return { type: "seed", id: match[1] || void 0 };
1398
+ }
1399
+ const yamlManagedPattern = /^\s*#\s*@einja:managed:start(?:\s+id="([^"]+)")?\s*$/;
1400
+ match = line.match(yamlManagedPattern);
1401
+ if (match) {
1402
+ return { type: "managed", id: match[1] || void 0 };
1403
+ }
1404
+ const yamlSeedPattern = /^\s*#\s*@einja:seed:start(?:\s+id="([^"]+)")?\s*$/;
1405
+ match = line.match(yamlSeedPattern);
1406
+ if (match) {
1407
+ return { type: "seed", id: match[1] || void 0 };
1408
+ }
1409
+ return null;
1410
+ }
1411
+ function parseEndMarker(line) {
1412
+ if (/^<!--\s*@einja:managed:end\s*-->$/.test(line)) {
1413
+ return "managed";
1414
+ }
1415
+ if (/^<!--\s*@einja:seed:end\s*-->$/.test(line)) {
1416
+ return "seed";
1417
+ }
1418
+ if (/^\s*#\s*@einja:managed:end\s*$/.test(line)) {
1419
+ return "managed";
1420
+ }
1421
+ if (/^\s*#\s*@einja:seed:end\s*$/.test(line)) {
1422
+ return "seed";
1423
+ }
1424
+ return null;
1425
+ }
1426
+ function isPathManaged(filePath, keyPath, jsonPaths) {
1427
+ const managedPaths = jsonPaths.managed[filePath] || [];
1428
+ return managedPaths.some(
1429
+ (p) => keyPath === p || keyPath.startsWith(`${p}.`)
1430
+ );
1431
+ }
1432
+ function isPathSeed(filePath, keyPath, jsonPaths) {
1433
+ const seedPaths = jsonPaths.seed[filePath] || [];
1434
+ return seedPaths.some((p) => keyPath === p || keyPath.startsWith(`${p}.`));
1435
+ }
1436
+
1437
+ // src/generators/partials/packages.ts
1438
+ async function addPackages(options, components, syncMetadata) {
1439
+ const added = [];
1440
+ const skipped = [];
1441
+ const merged = [];
1442
+ const { targetDir, templateDir, config } = options;
1443
+ for (const component of components) {
1444
+ const componentName = component === "front-core" ? "front-core" : component === "server-core" ? "server-core" : component === "config" ? "config" : "ui";
1445
+ const srcDir = join10(templateDir, "packages", componentName);
1446
+ const destDir = join10(targetDir, "packages", componentName);
1447
+ info(`Adding package component: ${componentName}`);
1448
+ await copyDirectory(
1449
+ srcDir,
1450
+ destDir,
1451
+ { added, skipped, merged },
1452
+ config.dryRun,
1453
+ syncMetadata
1454
+ );
1455
+ }
1456
+ return { added, skipped, merged };
1457
+ }
1458
+ async function copyDirectory(srcDir, destDir, result, dryRun, syncMetadata) {
1459
+ const entries = await readdir(srcDir, { withFileTypes: true });
1460
+ for (const entry of entries) {
1461
+ const srcPath = join10(srcDir, entry.name);
1462
+ const destPath = join10(destDir, entry.name);
1463
+ if (entry.isDirectory()) {
1464
+ await copyDirectory(srcPath, destPath, result, dryRun, syncMetadata);
1465
+ } else {
1466
+ if (!dryRun) {
1467
+ const mergeResult = await mergeAndWriteFile(
1468
+ srcPath,
1469
+ destPath,
1470
+ syncMetadata
1471
+ );
1472
+ if (mergeResult.action === "created") {
1473
+ result.added.push(destPath);
1474
+ } else if (mergeResult.action === "skipped") {
1475
+ result.skipped.push(destPath);
1476
+ } else if (mergeResult.action === "merged") {
1477
+ result.merged.push(destPath);
1478
+ }
1479
+ } else {
1480
+ result.skipped.push(destPath);
1481
+ }
1482
+ }
1483
+ }
1484
+ }
1485
+
1486
+ // src/generators/partials/apps.ts
1487
+ import { readdir as readdir2 } from "fs/promises";
1488
+ import { join as join11 } from "path";
1489
+ async function addApps(options, components, syncMetadata) {
1490
+ const added = [];
1491
+ const skipped = [];
1492
+ const merged = [];
1493
+ const { targetDir, templateDir, config } = options;
1494
+ for (const component of components) {
1495
+ const componentName = component === "web" ? "web" : component;
1496
+ const srcDir = join11(templateDir, "apps", componentName);
1497
+ const destDir = join11(targetDir, "apps", componentName);
1498
+ info(`Adding app component: ${componentName}`);
1499
+ await copyDirectory2(
1500
+ srcDir,
1501
+ destDir,
1502
+ { added, skipped, merged },
1503
+ config.dryRun,
1504
+ syncMetadata
1505
+ );
1506
+ }
1507
+ return { added, skipped, merged };
1508
+ }
1509
+ async function copyDirectory2(srcDir, destDir, result, dryRun, syncMetadata) {
1510
+ const entries = await readdir2(srcDir, { withFileTypes: true });
1511
+ for (const entry of entries) {
1512
+ const srcPath = join11(srcDir, entry.name);
1513
+ const destPath = join11(destDir, entry.name);
1514
+ if (entry.isDirectory()) {
1515
+ await copyDirectory2(srcPath, destPath, result, dryRun, syncMetadata);
1516
+ } else {
1517
+ if (!dryRun) {
1518
+ const mergeResult = await mergeAndWriteFile(
1519
+ srcPath,
1520
+ destPath,
1521
+ syncMetadata
1522
+ );
1523
+ if (mergeResult.action === "created") {
1524
+ result.added.push(destPath);
1525
+ } else if (mergeResult.action === "skipped") {
1526
+ result.skipped.push(destPath);
1527
+ } else if (mergeResult.action === "merged") {
1528
+ result.merged.push(destPath);
1529
+ }
1530
+ } else {
1531
+ result.skipped.push(destPath);
1532
+ }
1533
+ }
1534
+ }
1535
+ }
1536
+
1537
+ // src/generators/partials/config.ts
1538
+ import { readdir as readdir3, readFile } from "fs/promises";
1539
+ import { join as join12, relative as relative2, sep } from "path";
1540
+ async function addConfigFiles(options, syncMetadata) {
1541
+ const added = [];
1542
+ const skipped = [];
1543
+ const merged = [];
1544
+ const { targetDir, templateDir, config } = options;
1545
+ info("Adding config files from template root");
1546
+ const excludedPaths = /* @__PURE__ */ new Set([
1547
+ ".claude",
1548
+ "docs/einja",
1549
+ "CLAUDE.md",
1550
+ ".mcp.json",
1551
+ "node_modules",
1552
+ ".turbo",
1553
+ "next-env.d.ts",
1554
+ "styled-system",
1555
+ "pnpm-lock.yaml",
1556
+ "package-lock.json",
1557
+ "packages",
1558
+ "apps"
1559
+ ]);
1560
+ const gitignorePatterns = await loadGitignorePatterns(templateDir);
1561
+ await copyConfigDirectory(
1562
+ templateDir,
1563
+ targetDir,
1564
+ templateDir,
1565
+ // rootDir として templateDir を渡す
1566
+ { added, skipped, merged },
1567
+ config.dryRun,
1568
+ excludedPaths,
1569
+ gitignorePatterns,
1570
+ syncMetadata
1571
+ );
1572
+ return { added, skipped, merged };
1573
+ }
1574
+ async function loadGitignorePatterns(templateDir) {
1575
+ const patterns = /* @__PURE__ */ new Set();
1576
+ const gitignorePath = join12(templateDir, ".gitignore");
1577
+ try {
1578
+ const content = await readFile(gitignorePath, "utf-8");
1579
+ const lines = content.split("\n");
1580
+ for (const line of lines) {
1581
+ const trimmed = line.trim();
1582
+ if (trimmed && !trimmed.startsWith("#")) {
1583
+ const pattern = trimmed.startsWith("/") ? trimmed.slice(1) : trimmed;
1584
+ patterns.add(pattern);
1585
+ }
1586
+ }
1587
+ } catch {
1588
+ }
1589
+ return patterns;
1590
+ }
1591
+ async function copyConfigDirectory(srcDir, destDir, rootDir, result, dryRun, excludedPaths, gitignorePatterns, syncMetadata) {
1592
+ const entries = await readdir3(srcDir, { withFileTypes: true });
1593
+ for (const entry of entries) {
1594
+ const srcPath = join12(srcDir, entry.name);
1595
+ const destPath = join12(destDir, entry.name);
1596
+ const rawRelativePath = relative2(rootDir, srcPath);
1597
+ const relativePath = rawRelativePath.split(sep).join("/");
1598
+ if (shouldExclude(relativePath, excludedPaths, gitignorePatterns)) {
1599
+ continue;
1600
+ }
1601
+ if (entry.isDirectory()) {
1602
+ await copyConfigDirectory(
1603
+ srcPath,
1604
+ destPath,
1605
+ rootDir,
1606
+ // rootDir を引き継ぐ
1607
+ result,
1608
+ dryRun,
1609
+ excludedPaths,
1610
+ gitignorePatterns,
1611
+ syncMetadata
1612
+ );
1613
+ } else {
1614
+ if (!dryRun) {
1615
+ const mergeResult = await mergeAndWriteFile(
1616
+ srcPath,
1617
+ destPath,
1618
+ syncMetadata
1619
+ );
1620
+ if (mergeResult.action === "created") {
1621
+ result.added.push(destPath);
1622
+ } else if (mergeResult.action === "skipped") {
1623
+ result.skipped.push(destPath);
1624
+ } else if (mergeResult.action === "merged") {
1625
+ result.merged.push(destPath);
1626
+ }
1627
+ } else {
1628
+ result.skipped.push(destPath);
1629
+ }
1630
+ }
1631
+ }
1632
+ }
1633
+ function shouldExclude(relativePath, excludedPaths, gitignorePatterns) {
1634
+ for (const excluded of excludedPaths) {
1635
+ if (relativePath === excluded || relativePath.startsWith(`${excluded}/`)) {
1636
+ return true;
1637
+ }
1638
+ }
1639
+ for (const pattern of gitignorePatterns) {
1640
+ if (matchPattern(relativePath, pattern)) {
1641
+ return true;
1642
+ }
1643
+ }
1644
+ return false;
1645
+ }
1646
+ function matchPattern(path, pattern) {
1647
+ if (pattern.endsWith("/")) {
1648
+ const dirPattern = pattern.slice(0, -1);
1649
+ return path === dirPattern || path.startsWith(`${dirPattern}/`);
1650
+ }
1651
+ if (pattern.includes("*")) {
1652
+ const regexPattern = pattern.replace(/\./g, "\\.").replace(/\*/g, ".*");
1653
+ return new RegExp(`^${regexPattern}$`).test(path);
1654
+ }
1655
+ return path === pattern || path.startsWith(`${pattern}/`);
1656
+ }
1657
+
1658
+ // src/commands/add.ts
1659
+ function getTemplatePath2(templateName) {
1660
+ const __filename3 = fileURLToPath2(import.meta.url);
1661
+ const __dirname3 = dirname4(__filename3);
1662
+ const distPath = join13(__dirname3, "../templates", templateName);
1663
+ const srcPath = join13(__dirname3, "../../templates", templateName);
1664
+ if (existsSync6(distPath)) {
1665
+ return distPath;
1666
+ }
1667
+ if (existsSync6(srcPath)) {
1668
+ return srcPath;
1669
+ }
1670
+ return distPath;
1671
+ }
1672
+ async function loadTemplateSyncMetadata(templateDir) {
1673
+ const syncFilePath = join13(templateDir, ".einja-sync.json");
1674
+ try {
1675
+ const content = await readFile2(syncFilePath, "utf-8");
1676
+ return JSON.parse(content);
1677
+ } catch {
1678
+ return null;
1679
+ }
1680
+ }
1681
+ function mergeSyncMetadata(template, existing) {
1682
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1683
+ const jsonPaths = template?.jsonPaths ?? existing?.jsonPaths ?? { managed: {}, seed: {} };
1684
+ return {
1685
+ version: template?.version ?? existing?.version ?? "1.0.0",
1686
+ lastSync: now,
1687
+ templateVersion: template?.templateVersion ?? "1.0.0",
1688
+ files: { ...existing?.files ?? {}, ...template?.files ?? {} },
1689
+ jsonPaths
1690
+ };
1691
+ }
1692
+ async function addCommand(options) {
1693
+ try {
1694
+ const targetDir = process.cwd();
1695
+ let config;
1696
+ if (options.skipPrompts) {
1697
+ config = getDefaultAddConfig();
1698
+ info("\u30C7\u30D5\u30A9\u30EB\u30C8\u8A2D\u5B9A\u3092\u4F7F\u7528\u3057\u307E\u3059\uFF08\u3059\u3079\u3066\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8\u3092\u9078\u629E\uFF09");
1699
+ } else {
1700
+ config = await promptAddConfig(options.dryRun);
1701
+ }
1702
+ config.dryRun = options.dryRun;
1703
+ if (config.dryRun) {
1704
+ warn("dry-run\u30E2\u30FC\u30C9: \u5B9F\u969B\u306E\u30D5\u30A1\u30A4\u30EB\u64CD\u4F5C\u306F\u884C\u3044\u307E\u305B\u3093");
1705
+ info("\n--- \u8FFD\u52A0\u4E88\u5B9A\u306E\u30B3\u30F3\u30DD\u30FC\u30CD\u30F3\u30C8 ---");
1706
+ if (config.components.packages) {
1707
+ info(
1708
+ `- packages/: ${config.packageComponents.join(", ")}`
1709
+ );
1710
+ }
1711
+ if (config.components.apps) {
1712
+ info(`- apps/: ${config.appComponents.join(", ")}`);
1713
+ }
1714
+ if (config.components.config) {
1715
+ info("- \u76F4\u4E0B\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB: turbo.json, pnpm-workspace.yaml \u7B49");
1716
+ }
1717
+ info("---\n");
1718
+ }
1719
+ const templateDir = getTemplatePath2("default");
1720
+ if (!existsSync6(templateDir)) {
1721
+ throw new Error("\u30C6\u30F3\u30D7\u30EC\u30FC\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093: default");
1722
+ }
1723
+ const templateMetadata = await loadTemplateSyncMetadata(templateDir);
1724
+ const existingMetadata = await loadSyncMetadata(targetDir);
1725
+ const syncMetadata = mergeSyncMetadata(templateMetadata, existingMetadata);
1726
+ const addOptions = {
1727
+ targetDir,
1728
+ templateDir,
1729
+ config
1730
+ };
1731
+ let totalAdded = 0;
1732
+ let totalMerged = 0;
1733
+ let totalSkipped = 0;
1734
+ if (config.components.packages && config.packageComponents.length > 0) {
1735
+ const spinner = ora4("\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u8FFD\u52A0\u4E2D...").start();
1736
+ try {
1737
+ const result = await addPackages(
1738
+ addOptions,
1739
+ config.packageComponents,
1740
+ syncMetadata
1741
+ );
1742
+ totalAdded += result.added.length;
1743
+ totalMerged += result.merged.length;
1744
+ totalSkipped += result.skipped.length;
1745
+ spinner.succeed(
1746
+ `\u30D1\u30C3\u30B1\u30FC\u30B8\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\uFF08\u8FFD\u52A0: ${result.added.length}, \u30DE\u30FC\u30B8: ${result.merged.length}, \u30B9\u30AD\u30C3\u30D7: ${result.skipped.length}\uFF09`
1747
+ );
1748
+ } catch (error2) {
1749
+ spinner.fail("\u30D1\u30C3\u30B1\u30FC\u30B8\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1750
+ throw error2;
1751
+ }
1752
+ }
1753
+ if (config.components.apps && config.appComponents.length > 0) {
1754
+ const spinner = ora4("\u30A2\u30D7\u30EA\u3092\u8FFD\u52A0\u4E2D...").start();
1755
+ try {
1756
+ const result = await addApps(
1757
+ addOptions,
1758
+ config.appComponents,
1759
+ syncMetadata
1760
+ );
1761
+ totalAdded += result.added.length;
1762
+ totalMerged += result.merged.length;
1763
+ totalSkipped += result.skipped.length;
1764
+ spinner.succeed(
1765
+ `\u30A2\u30D7\u30EA\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\uFF08\u8FFD\u52A0: ${result.added.length}, \u30DE\u30FC\u30B8: ${result.merged.length}, \u30B9\u30AD\u30C3\u30D7: ${result.skipped.length}\uFF09`
1766
+ );
1767
+ } catch (error2) {
1768
+ spinner.fail("\u30A2\u30D7\u30EA\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1769
+ throw error2;
1770
+ }
1771
+ }
1772
+ if (config.components.config) {
1773
+ const spinner = ora4("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0\u4E2D...").start();
1774
+ try {
1775
+ const result = await addConfigFiles(addOptions, syncMetadata);
1776
+ totalAdded += result.added.length;
1777
+ totalMerged += result.merged.length;
1778
+ totalSkipped += result.skipped.length;
1779
+ spinner.succeed(
1780
+ `\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u3092\u8FFD\u52A0\u3057\u307E\u3057\u305F\uFF08\u8FFD\u52A0: ${result.added.length}, \u30DE\u30FC\u30B8: ${result.merged.length}, \u30B9\u30AD\u30C3\u30D7: ${result.skipped.length}\uFF09`
1781
+ );
1782
+ } catch (error2) {
1783
+ spinner.fail("\u8A2D\u5B9A\u30D5\u30A1\u30A4\u30EB\u306E\u8FFD\u52A0\u306B\u5931\u6557\u3057\u307E\u3057\u305F");
1784
+ throw error2;
1785
+ }
1786
+ }
1787
+ if (!config.dryRun) {
1788
+ syncMetadata.lastSync = (/* @__PURE__ */ new Date()).toISOString();
1789
+ await saveSyncMetadata(targetDir, syncMetadata);
1790
+ }
1791
+ success("\n\u2713 \u8FFD\u52A0\u5B8C\u4E86\uFF01\n");
1792
+ if (config.dryRun) {
1793
+ info("\uFF08dry-run\u30E2\u30FC\u30C9\u306E\u305F\u3081\u3001\u5B9F\u969B\u306E\u5909\u66F4\u306F\u884C\u308F\u308C\u3066\u3044\u307E\u305B\u3093\uFF09\n");
1794
+ }
1795
+ info(`\u8FFD\u52A0\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalAdded}\u500B`);
1796
+ info(`\u30DE\u30FC\u30B8\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalMerged}\u500B`);
1797
+ info(`\u30B9\u30AD\u30C3\u30D7\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB: ${totalSkipped}\u500B
1798
+ `);
1799
+ const packageJsonPath2 = join13(targetDir, "package.json");
1800
+ if (existsSync6(packageJsonPath2)) {
1801
+ const packageJson2 = await import(packageJsonPath2);
1802
+ const hasEinjaCli = packageJson2.default?.devDependencies?.["@einja/dev-cli"] || packageJson2.default?.dependencies?.["@einja/dev-cli"];
1803
+ if (!hasEinjaCli) {
1804
+ info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
1805
+ info("1. pnpm install");
1806
+ info("2. pnpm dev:setup");
1807
+ info("\n\u63A8\u5968:");
1808
+ info(" einja\u958B\u767A\u652F\u63F4CLI (@einja/dev-cli) \u306E\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB:");
1809
+ info(" pnpm add -D @einja/dev-cli\n");
1810
+ } else {
1811
+ info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
1812
+ info("1. pnpm install");
1813
+ info("2. pnpm dev:setup\n");
1814
+ }
1815
+ } else {
1816
+ info("\u6B21\u306E\u30B9\u30C6\u30C3\u30D7:");
1817
+ info("1. pnpm install");
1818
+ info("2. pnpm dev:setup\n");
1819
+ }
1820
+ } catch (error2) {
1821
+ error("\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u307E\u3057\u305F:");
1822
+ if (error2 instanceof Error) {
1823
+ error(error2.message);
1824
+ } else {
1825
+ error(String(error2));
1826
+ }
1827
+ process.exit(1);
1828
+ }
1829
+ }
1830
+
987
1831
  // src/cli.ts
988
- var __filename2 = fileURLToPath2(import.meta.url);
989
- var __dirname2 = dirname3(__filename2);
990
- var packageJsonPath = join10(__dirname2, "../package.json");
991
- var packageJson = JSON.parse(readFileSync4(packageJsonPath, "utf-8"));
1832
+ var __filename2 = fileURLToPath3(import.meta.url);
1833
+ var __dirname2 = dirname5(__filename2);
1834
+ var packageJsonPath = join14(__dirname2, "../package.json");
1835
+ var packageJson = JSON.parse(readFileSync5(packageJsonPath, "utf-8"));
992
1836
  var program = new Command();
993
1837
  program.name("create-einja-app").description("CLI tool to create new projects with Einja Management Template").version(packageJson.version);
994
1838
  program.argument("[project-name]", "Project name").option("--skip-git", "Skip git initialization").option("--skip-install", "Skip package installation").option("-y, --yes", "Skip interactive prompts").action(
@@ -999,5 +1843,13 @@ program.argument("[project-name]", "Project name").option("--skip-git", "Skip gi
999
1843
  program.command("setup").description("Setup tools for existing project").action(async () => {
1000
1844
  await setupCommand();
1001
1845
  });
1846
+ program.command("add").description("Add einja components to existing monorepo").option("-y, --yes", "Skip prompts and use defaults (select all)").option("--all", "Select all components (same as -y)").option("--dry-run", "Preview changes without making them").action(
1847
+ async (options) => {
1848
+ await addCommand({
1849
+ skipPrompts: options.yes || options.all || false,
1850
+ dryRun: options.dryRun || false
1851
+ });
1852
+ }
1853
+ );
1002
1854
  program.parse();
1003
1855
  //# sourceMappingURL=cli.js.map