@mandujs/cli 0.15.1 → 0.15.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.
Files changed (90) hide show
  1. package/README.ko.md +33 -33
  2. package/README.md +354 -354
  3. package/package.json +2 -2
  4. package/src/commands/check.ts +71 -7
  5. package/src/commands/contract.ts +173 -173
  6. package/src/commands/dev.ts +9 -42
  7. package/src/commands/guard-arch.ts +303 -303
  8. package/src/commands/init.ts +50 -5
  9. package/src/commands/monitor.ts +300 -300
  10. package/src/commands/openapi.ts +107 -107
  11. package/src/commands/registry.ts +1 -0
  12. package/src/commands/start.ts +9 -42
  13. package/src/errors/codes.ts +35 -35
  14. package/src/errors/index.ts +2 -2
  15. package/src/errors/messages.ts +143 -143
  16. package/src/hooks/index.ts +17 -17
  17. package/src/hooks/preaction.ts +256 -256
  18. package/src/main.ts +9 -7
  19. package/src/terminal/banner.ts +166 -166
  20. package/src/terminal/help.ts +306 -306
  21. package/src/terminal/index.ts +71 -71
  22. package/src/terminal/output.ts +295 -295
  23. package/src/terminal/palette.ts +30 -30
  24. package/src/terminal/progress.ts +327 -327
  25. package/src/terminal/stream-writer.ts +214 -214
  26. package/src/terminal/table.ts +354 -354
  27. package/src/terminal/theme.ts +142 -142
  28. package/src/util/bun.ts +6 -6
  29. package/src/util/fs.ts +23 -23
  30. package/src/util/handlers.ts +49 -5
  31. package/src/util/lockfile.ts +66 -0
  32. package/src/util/output.ts +22 -22
  33. package/src/util/port.ts +71 -71
  34. package/templates/default/AGENTS.md +96 -96
  35. package/templates/default/app/api/health/route.ts +13 -13
  36. package/templates/default/app/globals.css +49 -49
  37. package/templates/default/app/layout.tsx +27 -27
  38. package/templates/default/app/page.tsx +38 -38
  39. package/templates/default/src/client/shared/lib/utils.ts +16 -16
  40. package/templates/default/src/client/shared/ui/button.tsx +57 -57
  41. package/templates/default/src/client/shared/ui/card.tsx +1 -1
  42. package/templates/default/src/client/shared/ui/index.ts +21 -21
  43. package/templates/default/src/client/shared/ui/input.tsx +5 -1
  44. package/templates/default/tests/example.test.ts +58 -58
  45. package/templates/default/tests/helpers.ts +52 -52
  46. package/templates/default/tests/setup.ts +9 -9
  47. package/templates/default/tsconfig.json +23 -23
  48. package/templates/realtime-chat/AGENTS.md +96 -0
  49. package/templates/realtime-chat/app/api/chat/messages/route.ts +63 -0
  50. package/templates/realtime-chat/app/api/chat/stream/route.ts +85 -0
  51. package/templates/realtime-chat/app/api/health/route.ts +13 -0
  52. package/templates/realtime-chat/app/globals.css +49 -0
  53. package/templates/realtime-chat/app/layout.tsx +27 -0
  54. package/templates/realtime-chat/app/page.tsx +16 -0
  55. package/templates/realtime-chat/package.json +34 -0
  56. package/templates/realtime-chat/src/client/app/index.ts +1 -0
  57. package/templates/realtime-chat/src/client/entities/index.ts +1 -0
  58. package/templates/realtime-chat/src/client/features/chat/chat-api.ts +209 -0
  59. package/templates/realtime-chat/src/client/features/chat/realtime-chat-starter.client.tsx +89 -0
  60. package/templates/realtime-chat/src/client/features/chat/use-realtime-chat.ts +65 -0
  61. package/templates/realtime-chat/src/client/features/index.ts +1 -0
  62. package/templates/realtime-chat/src/client/pages/index.ts +1 -0
  63. package/templates/realtime-chat/src/client/shared/index.ts +1 -0
  64. package/templates/realtime-chat/src/client/shared/lib/utils.ts +16 -0
  65. package/templates/realtime-chat/src/client/shared/ui/button.tsx +57 -0
  66. package/templates/realtime-chat/src/client/shared/ui/card.tsx +78 -0
  67. package/templates/realtime-chat/src/client/shared/ui/index.ts +21 -0
  68. package/templates/realtime-chat/src/client/shared/ui/input.tsx +28 -0
  69. package/templates/realtime-chat/src/client/widgets/index.ts +1 -0
  70. package/templates/realtime-chat/src/server/api/index.ts +1 -0
  71. package/templates/realtime-chat/src/server/application/ai-adapter.ts +24 -0
  72. package/templates/realtime-chat/src/server/application/chat-store.ts +158 -0
  73. package/templates/realtime-chat/src/server/application/index.ts +1 -0
  74. package/templates/realtime-chat/src/server/core/index.ts +1 -0
  75. package/templates/realtime-chat/src/server/domain/index.ts +1 -0
  76. package/templates/realtime-chat/src/server/infra/index.ts +1 -0
  77. package/templates/realtime-chat/src/shared/contracts/chat.ts +29 -0
  78. package/templates/realtime-chat/src/shared/contracts/index.ts +1 -0
  79. package/templates/realtime-chat/src/shared/env/index.ts +1 -0
  80. package/templates/realtime-chat/src/shared/schema/index.ts +1 -0
  81. package/templates/realtime-chat/src/shared/types/index.ts +1 -0
  82. package/templates/realtime-chat/src/shared/utils/client/index.ts +1 -0
  83. package/templates/realtime-chat/src/shared/utils/server/index.ts +1 -0
  84. package/templates/realtime-chat/tests/chat-api.sse.test.ts +188 -0
  85. package/templates/realtime-chat/tests/chat-starter.test.ts +200 -0
  86. package/templates/realtime-chat/tests/chat-store.concurrency.test.ts +39 -0
  87. package/templates/realtime-chat/tests/example.test.ts +58 -0
  88. package/templates/realtime-chat/tests/helpers.ts +52 -0
  89. package/templates/realtime-chat/tests/setup.ts +9 -0
  90. package/templates/realtime-chat/tsconfig.json +23 -0
@@ -19,6 +19,23 @@ export interface InitOptions {
19
19
  minimal?: boolean;
20
20
  }
21
21
 
22
+ const ALLOWED_TEMPLATES = ["default", "realtime-chat"] as const;
23
+ const DEFAULT_MAX_BACKUP_SUFFIX_ATTEMPTS = 50;
24
+ const BACKUP_SUFFIX_START_INDEX = 1;
25
+ type AllowedTemplate = (typeof ALLOWED_TEMPLATES)[number];
26
+
27
+ export function isAllowedTemplate(template: string): template is AllowedTemplate {
28
+ return (ALLOWED_TEMPLATES as readonly string[]).includes(template);
29
+ }
30
+
31
+ function resolveTemplateName(template: string): AllowedTemplate | null {
32
+ const normalized = path.posix.normalize(template.replace(/\\/g, "/")).trim();
33
+ if (!normalized || normalized.includes("..") || normalized.startsWith("/")) {
34
+ return null;
35
+ }
36
+ return isAllowedTemplate(normalized) ? normalized : null;
37
+ }
38
+
22
39
  // Files to skip based on CSS/UI options
23
40
  const CSS_FILES = [
24
41
  "tailwind.config.ts",
@@ -183,9 +200,16 @@ async function resolvePackageVersions(): Promise<{ coreVersion: string; cliVersi
183
200
 
184
201
  export async function init(options: InitOptions = {}): Promise<boolean> {
185
202
  const projectName = options.name || "my-mandu-app";
186
- const template = options.template || "default";
203
+ const requestedTemplate = options.template || "default";
204
+ const template = resolveTemplateName(requestedTemplate);
187
205
  const targetDir = path.resolve(process.cwd(), projectName);
188
206
 
207
+ if (!template) {
208
+ printCLIError(CLI_ERROR_CODES.INIT_TEMPLATE_NOT_FOUND, { template: requestedTemplate });
209
+ console.error(` 사용 가능한 템플릿: ${ALLOWED_TEMPLATES.join(", ")}`);
210
+ return false;
211
+ }
212
+
189
213
  // Handle minimal flag (shortcut for --css none --ui none)
190
214
  const css: CSSFramework = options.minimal ? "none" : (options.css || "tailwind");
191
215
  const ui: UILibrary = options.minimal ? "none" : (options.ui || "shadcn");
@@ -218,7 +242,7 @@ export async function init(options: InitOptions = {}): Promise<boolean> {
218
242
  await fs.access(templateDir);
219
243
  } catch {
220
244
  printCLIError(CLI_ERROR_CODES.INIT_TEMPLATE_NOT_FOUND, { template });
221
- console.error(` 사용 가능한 템플릿: default`);
245
+ console.error(` 사용 가능한 템플릿: ${ALLOWED_TEMPLATES.join(", ")}`);
222
246
  return false;
223
247
  }
224
248
 
@@ -272,6 +296,9 @@ export async function init(options: InitOptions = {}): Promise<boolean> {
272
296
  console.log(` cd ${projectName}`);
273
297
  console.log(` bun install`);
274
298
  console.log(` bun run dev`);
299
+ console.log(`\n💡 CLI 실행 참고 (환경별):`);
300
+ console.log(` bun run dev # 권장 (로컬 스크립트)`);
301
+ console.log(` bunx mandu dev # PATH에 mandu가 없을 때 대안`);
275
302
  console.log(`\n📂 파일 구조:`);
276
303
  console.log(` app/layout.tsx → 루트 레이아웃`);
277
304
  console.log(` app/page.tsx → http://localhost:3000/`);
@@ -461,7 +488,18 @@ function logMcpConfigStatus(
461
488
  * - 파일 없으면 새로 생성
462
489
  * - 파일 있으면 mandu 서버만 추가/업데이트 (다른 설정 유지)
463
490
  */
464
- async function setupMcpConfig(targetDir: string): Promise<McpConfigResult> {
491
+ interface SetupMcpConfigOptions {
492
+ maxBackupSuffixAttempts?: number;
493
+ }
494
+
495
+ export const __test__ = {
496
+ setupMcpConfig,
497
+ };
498
+
499
+ async function setupMcpConfig(
500
+ targetDir: string,
501
+ options: SetupMcpConfigOptions = {}
502
+ ): Promise<McpConfigResult> {
465
503
  const mcpPath = path.join(targetDir, ".mcp.json");
466
504
  const claudePath = path.join(targetDir, ".claude.json");
467
505
 
@@ -490,8 +528,15 @@ async function setupMcpConfig(targetDir: string): Promise<McpConfigResult> {
490
528
  if (!(await fileExists(base))) {
491
529
  return base;
492
530
  }
493
- for (let i = 1; i <= 50; i++) {
494
- const candidate = `${basePath}.bak.${i}`;
531
+ const maxBackupSuffixAttempts =
532
+ options.maxBackupSuffixAttempts ?? DEFAULT_MAX_BACKUP_SUFFIX_ATTEMPTS;
533
+
534
+ for (
535
+ let suffixIndex = BACKUP_SUFFIX_START_INDEX;
536
+ suffixIndex <= maxBackupSuffixAttempts;
537
+ suffixIndex++
538
+ ) {
539
+ const candidate = `${basePath}.bak.${suffixIndex}`;
495
540
  if (!(await fileExists(candidate))) {
496
541
  return candidate;
497
542
  }