@mandujs/cli 0.18.10 → 0.19.1

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.ko.md CHANGED
@@ -23,22 +23,30 @@ bun add -D @mandujs/cli
23
23
  ## 빠른 시작
24
24
 
25
25
  ```bash
26
- # 프로젝트 생성
27
- bunx @mandujs/cli init my-app
28
- cd my-app
26
+ # 대화형 모드 (권장)
27
+ bunx @mandujs/cli init
28
+
29
+ # 비대화형 모드
30
+ bunx @mandujs/cli init my-app --yes
29
31
 
30
32
  # 개발 서버 시작
33
+ cd my-app
31
34
  bun run dev
32
35
  ```
33
36
 
37
+ 대화형 모드에서는 프로젝트 이름, 템플릿, 의존성 설치 여부를 묻습니다.
38
+ `--yes` / `-y` 플래그로 프롬프트를 건너뛸 수 있고, `--no-install`로 자동 설치를 비활성화합니다.
39
+
34
40
  ## 명령어
35
41
 
36
- ### `mandu init <project-name>`
42
+ ### `mandu init [project-name]`
37
43
 
38
- 새 Mandu 프로젝트를 생성합니다.
44
+ 새 Mandu 프로젝트를 생성합니다. 이름 생략 시 대화형 모드로 진입합니다.
39
45
 
40
46
  ```bash
41
- bunx @mandujs/cli init my-app
47
+ bunx @mandujs/cli init # 대화형
48
+ bunx @mandujs/cli init my-app # 비대화형 (기본 옵션)
49
+ bunx @mandujs/cli init my-app --yes --no-install # 설치 건너뛰기
42
50
  ```
43
51
 
44
52
  생성되는 구조:
@@ -220,6 +228,18 @@ bun test # 테스트 실행
220
228
  bun test --watch # 감시 모드
221
229
  ```
222
230
 
231
+ ## AI 에이전트 통합
232
+
233
+ `mandu init` 시 MCP(Model Context Protocol) 설정이 자동 생성됩니다:
234
+
235
+ | 에이전트 | 설정 파일 | 자동 설정 |
236
+ |----------|-----------|-----------|
237
+ | Claude Code | `.mcp.json` | Yes |
238
+ | Claude Desktop | `.claude.json` | Yes |
239
+ | Gemini CLI | `.gemini/settings.json` | Yes |
240
+
241
+ `@mandujs/mcp` 서버가 `mandu_negotiate`, `mandu_generate_scaffold`, `mandu_guard` 등의 도구를 제공하여 AI 에이전트가 아키텍처를 자동으로 스캐폴딩, 검증, 유지합니다.
242
+
223
243
  ## 요구 사항
224
244
 
225
245
  - Bun >= 1.0.0
@@ -228,6 +248,7 @@ bun test --watch # 감시 모드
228
248
  ## 관련 패키지
229
249
 
230
250
  - [@mandujs/core](https://www.npmjs.com/package/@mandujs/core) - 핵심 런타임
251
+ - [@mandujs/mcp](https://www.npmjs.com/package/@mandujs/mcp) - MCP 서버
231
252
 
232
253
  ## 라이선스
233
254
 
package/README.md CHANGED
@@ -30,11 +30,15 @@ bunx @mandujs/cli init my-app
30
30
  ### 1. Create a New Project
31
31
 
32
32
  ```bash
33
- bunx @mandujs/cli init my-app
34
- cd my-app
35
- bun install
33
+ # Interactive mode (recommended)
34
+ bunx @mandujs/cli init
35
+
36
+ # Non-interactive mode
37
+ bunx @mandujs/cli init my-app --yes
36
38
  ```
37
39
 
40
+ The interactive mode asks for project name, template selection, and whether to install dependencies. Use `--yes` / `-y` to skip prompts. Use `--no-install` to skip automatic `bun install`.
41
+
38
42
  ### 2. Start Development Server
39
43
 
40
44
  ```bash
@@ -101,7 +105,7 @@ That's it!
101
105
 
102
106
  | Command | Description |
103
107
  |---------|-------------|
104
- | `mandu init [name]` | Create new project |
108
+ | `mandu init [name]` | Create new project (interactive / `--yes` for non-interactive) |
105
109
  | `mandu dev` | Start dev server (FS Routes + HMR) |
106
110
  | `mandu build` | Build for production |
107
111
 
@@ -162,9 +166,8 @@ That's it!
162
166
  ### Modern Workflow (Recommended)
163
167
 
164
168
  ```bash
165
- # 1. Create project
166
- bunx @mandujs/cli init my-app
167
- cd my-app && bun install
169
+ # 1. Create project (interactive — auto installs dependencies)
170
+ bunx @mandujs/cli init
168
171
 
169
172
  # 2. Create pages
170
173
  # app/page.tsx → /
@@ -340,6 +343,18 @@ bunx mandu build --minify --sourcemap
340
343
 
341
344
  ---
342
345
 
346
+ ## AI Agent Integration
347
+
348
+ Mandu automatically configures MCP (Model Context Protocol) for AI coding agents during `init`:
349
+
350
+ | Agent | Config File | Auto-configured |
351
+ |-------|-------------|-----------------|
352
+ | Claude Code | `.mcp.json` | Yes |
353
+ | Claude Desktop | `.claude.json` | Yes |
354
+ | Gemini CLI | `.gemini/settings.json` | Yes |
355
+
356
+ The `@mandujs/mcp` server provides tools like `mandu_negotiate`, `mandu_generate_scaffold`, `mandu_guard`, and more — enabling AI agents to scaffold, validate, and maintain architecture automatically.
357
+
343
358
  ## Requirements
344
359
 
345
360
  - Bun >= 1.0.0
@@ -351,4 +366,4 @@ bunx mandu build --minify --sourcemap
351
366
 
352
367
  ## License
353
368
 
354
- MIT
369
+ MPL-2.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/cli",
3
- "version": "0.18.10",
3
+ "version": "0.19.1",
4
4
  "description": "Agent-Native Fullstack Framework - 에이전트가 코딩해도 아키텍처가 무너지지 않는 개발 OS",
5
5
  "type": "module",
6
6
  "main": "./src/main.ts",
@@ -1,6 +1,9 @@
1
1
  import path from "path";
2
2
  import fs from "fs/promises";
3
+ import { createInterface } from "readline/promises";
3
4
  import { CLI_ERROR_CODES, printCLIError } from "../errors";
5
+ import { startSpinner, runSteps } from "../terminal/progress";
6
+ import { theme } from "../terminal/theme";
4
7
  import {
5
8
  generateLockfile,
6
9
  writeLockfile,
@@ -18,6 +21,8 @@ export interface InitOptions {
18
21
  theme?: boolean;
19
22
  minimal?: boolean;
20
23
  withCi?: boolean;
24
+ yes?: boolean;
25
+ noInstall?: boolean;
21
26
  }
22
27
 
23
28
  const ALLOWED_TEMPLATES = ["default", "realtime-chat"] as const;
@@ -199,9 +204,78 @@ async function resolvePackageVersions(): Promise<{ coreVersion: string; cliVersi
199
204
  };
200
205
  }
201
206
 
207
+ interface InteractiveAnswers {
208
+ name: string;
209
+ template: AllowedTemplate;
210
+ install: boolean;
211
+ }
212
+
213
+ async function runInteractivePrompts(defaults: {
214
+ name: string;
215
+ template: string;
216
+ }): Promise<InteractiveAnswers> {
217
+ const rl = createInterface({
218
+ input: process.stdin,
219
+ output: process.stdout,
220
+ });
221
+
222
+ console.log(`\n${theme.heading("🥟 Mandu Init")}\n`);
223
+
224
+ // 1. Project name
225
+ const nameInput = await rl.question(
226
+ ` 프로젝트 이름 ${theme.muted(`(${defaults.name})`)} : `
227
+ );
228
+ const name = nameInput.trim() || defaults.name;
229
+
230
+ // 2. Template selection
231
+ console.log(`\n 템플릿 선택:`);
232
+ for (let i = 0; i < ALLOWED_TEMPLATES.length; i++) {
233
+ const t = ALLOWED_TEMPLATES[i];
234
+ const label = t === "default" ? "default (권장)" : t;
235
+ console.log(` ${theme.accent(`${i + 1})`)} ${label}`);
236
+ }
237
+ const templateInput = await rl.question(
238
+ `\n 번호 입력 ${theme.muted("(1)")} : `
239
+ );
240
+ const templateIndex = parseInt(templateInput.trim(), 10) - 1;
241
+ const template: AllowedTemplate =
242
+ templateIndex >= 0 && templateIndex < ALLOWED_TEMPLATES.length
243
+ ? ALLOWED_TEMPLATES[templateIndex]
244
+ : (resolveTemplateName(defaults.template) as AllowedTemplate) ?? "default";
245
+
246
+ // 3. Install dependencies?
247
+ const installInput = await rl.question(
248
+ `\n 의존성 설치 (bun install)? ${theme.muted("(Y/n)")} : `
249
+ );
250
+ const install = installInput.trim().toLowerCase() !== "n";
251
+
252
+ rl.close();
253
+ console.log();
254
+
255
+ return { name, template, install };
256
+ }
257
+
202
258
  export async function init(options: InitOptions = {}): Promise<boolean> {
203
- const projectName = options.name || "my-mandu-app";
204
- const requestedTemplate = options.template || "default";
259
+ const isInteractive = process.stdin.isTTY && !options.yes;
260
+
261
+ let projectName: string;
262
+ let requestedTemplate: string;
263
+ let shouldInstall: boolean;
264
+
265
+ if (isInteractive) {
266
+ const answers = await runInteractivePrompts({
267
+ name: options.name || "my-mandu-app",
268
+ template: options.template || "default",
269
+ });
270
+ projectName = answers.name;
271
+ requestedTemplate = answers.template;
272
+ shouldInstall = options.noInstall ? false : answers.install;
273
+ } else {
274
+ projectName = options.name || "my-mandu-app";
275
+ requestedTemplate = options.template || "default";
276
+ shouldInstall = !options.noInstall;
277
+ }
278
+
205
279
  const template = resolveTemplateName(requestedTemplate);
206
280
  const targetDir = path.resolve(process.cwd(), projectName);
207
281
 
@@ -214,19 +288,19 @@ export async function init(options: InitOptions = {}): Promise<boolean> {
214
288
  // Handle minimal flag (shortcut for --css none --ui none)
215
289
  const css: CSSFramework = options.minimal ? "none" : (options.css || "tailwind");
216
290
  const ui: UILibrary = options.minimal ? "none" : (options.ui || "shadcn");
217
- const theme = options.theme || false;
291
+ const themeEnabled = options.theme || false;
218
292
  const withCi = options.withCi || false;
219
293
 
220
- console.log(`🥟 Mandu Init`);
221
- console.log(`📁 프로젝트: ${projectName}`);
222
- console.log(`📦 템플릿: ${template}`);
223
- console.log(`🎨 CSS: ${css}${css !== "none" ? " (Tailwind CSS)" : ""}`);
224
- console.log(`🧩 UI: ${ui}${ui !== "none" ? " (shadcn/ui)" : ""}`);
225
- if (theme) {
226
- console.log(`🌙 테마: Dark mode 지원`);
294
+ console.log(`${theme.heading("🥟 Mandu Init")}`);
295
+ console.log(`${theme.info("📁")} 프로젝트: ${theme.accent(projectName)}`);
296
+ console.log(`${theme.info("📦")} 템플릿: ${theme.accent(template)}`);
297
+ console.log(`${theme.info("🎨")} CSS: ${css}${css !== "none" ? " (Tailwind CSS)" : ""}`);
298
+ console.log(`${theme.info("🧩")} UI: ${ui}${ui !== "none" ? " (shadcn/ui)" : ""}`);
299
+ if (themeEnabled) {
300
+ console.log(`${theme.info("🌙")} 테마: Dark mode 지원`);
227
301
  }
228
302
  if (withCi) {
229
- console.log(`🔄 CI/CD: GitHub Actions 워크플로우 포함`);
303
+ console.log(`${theme.info("🔄")} CI/CD: GitHub Actions 워크플로우 포함`);
230
304
  }
231
305
  console.log();
232
306
 
@@ -251,64 +325,119 @@ export async function init(options: InitOptions = {}): Promise<boolean> {
251
325
  return false;
252
326
  }
253
327
 
254
- console.log(`📋 템플릿 복사 중...`);
255
-
256
328
  const { coreVersion, cliVersion } = await resolvePackageVersions();
257
329
 
258
330
  const copyOptions: CopyOptions = {
259
331
  projectName,
260
332
  css,
261
333
  ui,
262
- theme,
334
+ theme: themeEnabled,
263
335
  coreVersion,
264
336
  cliVersion,
265
337
  };
266
338
 
339
+ // Run structured steps with progress
340
+ let mcpResult: McpConfigResult;
341
+ let lockfileResult: LockfileResult;
342
+
267
343
  try {
268
- await copyDir(templateDir, targetDir, copyOptions);
344
+ await runSteps([
345
+ {
346
+ label: "디렉토리 생성",
347
+ fn: async () => {
348
+ await fs.mkdir(targetDir, { recursive: true });
349
+ await fs.mkdir(path.join(targetDir, ".mandu/client"), { recursive: true });
350
+ },
351
+ },
352
+ {
353
+ label: "템플릿 복사",
354
+ fn: () => copyDir(templateDir, targetDir, copyOptions),
355
+ },
356
+ {
357
+ label: "설정 파일 생성",
358
+ fn: async () => {
359
+ if (withCi) {
360
+ await setupCiWorkflows(targetDir);
361
+ }
362
+ if (css === "none") {
363
+ await createMinimalLayout(targetDir, projectName);
364
+ }
365
+ if (ui === "none") {
366
+ await createMinimalPage(targetDir);
367
+ }
368
+ if (css === "none" || ui === "none") {
369
+ await updatePackageJson(targetDir, css, ui);
370
+ }
371
+ },
372
+ },
373
+ {
374
+ label: "MCP 설정",
375
+ fn: async () => {
376
+ mcpResult = await setupMcpConfig(targetDir);
377
+ },
378
+ },
379
+ {
380
+ label: "Lockfile 생성",
381
+ fn: async () => {
382
+ lockfileResult = await setupLockfile(targetDir);
383
+ },
384
+ },
385
+ ]);
269
386
  } catch (error) {
270
- console.error(`❌ 템플릿 복사 실패:`, error);
387
+ console.error(`\n${theme.error("❌")} 프로젝트 생성 실패:`, error);
271
388
  return false;
272
389
  }
273
390
 
274
- // Create .mandu directory for build output
275
- await fs.mkdir(path.join(targetDir, ".mandu/client"), { recursive: true });
276
-
277
- // Setup CI/CD workflows if requested
278
- if (withCi) {
279
- await setupCiWorkflows(targetDir);
391
+ // Validate project files
392
+ const requiredFiles = ["app/page.tsx", "package.json", "tsconfig.json"];
393
+ const missingFiles: string[] = [];
394
+ for (const file of requiredFiles) {
395
+ try {
396
+ await fs.access(path.join(targetDir, file));
397
+ } catch {
398
+ missingFiles.push(file);
399
+ }
280
400
  }
281
-
282
- // Create minimal layout.tsx if css=none (without globals.css import)
283
- if (css === "none") {
284
- await createMinimalLayout(targetDir, projectName);
401
+ if (missingFiles.length > 0) {
402
+ console.log(`\n${theme.warn("⚠")} 누락된 파일: ${missingFiles.join(", ")}`);
285
403
  }
286
404
 
287
- // Create minimal page.tsx if ui=none (without UI components)
288
- if (ui === "none") {
289
- await createMinimalPage(targetDir);
405
+ // Auto install dependencies
406
+ if (shouldInstall) {
407
+ const stopSpinner = startSpinner("패키지 설치 중 (bun install)...");
408
+ try {
409
+ const proc = Bun.spawn(["bun", "install"], {
410
+ cwd: targetDir,
411
+ stdout: "inherit",
412
+ stderr: "inherit",
413
+ });
414
+ const exitCode = await proc.exited;
415
+ if (exitCode === 0) {
416
+ stopSpinner("패키지 설치 완료");
417
+ } else {
418
+ stopSpinner();
419
+ console.log(`${theme.warn("⚠")} 패키지 설치 실패 (exit code: ${exitCode})`);
420
+ console.log(` ${theme.muted("프로젝트 디렉토리에서 직접 'bun install'을 실행해주세요.")}`);
421
+ }
422
+ } catch {
423
+ stopSpinner();
424
+ console.log(`${theme.warn("⚠")} 패키지 설치를 건너뛰었습니다.`);
425
+ console.log(` ${theme.muted("프로젝트 디렉토리에서 직접 'bun install'을 실행해주세요.")}`);
426
+ }
290
427
  }
291
428
 
292
- // Update package.json to remove unused dependencies
293
- if (css === "none" || ui === "none") {
294
- await updatePackageJson(targetDir, css, ui);
429
+ // Success message
430
+ console.log(`\n${theme.success("")} ${theme.heading("프로젝트 생성 완료!")}\n`);
431
+ console.log(`📍 위치: ${theme.path(targetDir)}`);
432
+ console.log(`\n${theme.heading("🚀 시작하기:")}`);
433
+ console.log(` ${theme.command(`cd ${projectName}`)}`);
434
+ if (!shouldInstall) {
435
+ console.log(` ${theme.command("bun install")}`);
295
436
  }
296
-
297
- // Setup .mcp.json for AI agent integration
298
- const mcpResult = await setupMcpConfig(targetDir);
299
-
300
- // Generate initial lockfile for config integrity
301
- const lockfileResult = await setupLockfile(targetDir);
302
-
303
- console.log(`\n✅ 프로젝트 생성 완료!\n`);
304
- console.log(`📍 위치: ${targetDir}`);
305
- console.log(`\n🚀 시작하기:`);
306
- console.log(` cd ${projectName}`);
307
- console.log(` bun install`);
308
- console.log(` bun run dev`);
437
+ console.log(` ${theme.command("bun run dev")}`);
309
438
  console.log(`\n💡 CLI 실행 참고 (환경별):`);
310
- console.log(` bun run dev # 권장 (로컬 스크립트)`);
311
- console.log(` bunx mandu dev # PATH에 mandu가 없을 때 대안`);
439
+ console.log(` ${theme.command("bun run dev")} ${theme.muted("# 권장 (로컬 스크립트)")}`);
440
+ console.log(` ${theme.command("bunx mandu dev")} ${theme.muted("# PATH에 mandu가 없을 때 대안")}`);
312
441
  console.log(`\n📂 파일 구조:`);
313
442
  console.log(` app/layout.tsx → 루트 레이아웃`);
314
443
  console.log(` app/page.tsx → http://localhost:3000/`);
@@ -331,15 +460,16 @@ export async function init(options: InitOptions = {}): Promise<boolean> {
331
460
 
332
461
  // MCP 설정 안내
333
462
  console.log(`\n🤖 AI 에이전트 통합:`);
334
- logMcpConfigStatus(".mcp.json", mcpResult.mcpJson, "Claude Code 자동 연결");
335
- logMcpConfigStatus(".claude.json", mcpResult.claudeJson, "Claude MCP 로컬 범위");
463
+ logMcpConfigStatus(".mcp.json", mcpResult!.mcpJson, "Claude Code 자동 연결");
464
+ logMcpConfigStatus(".claude.json", mcpResult!.claudeJson, "Claude MCP 로컬 범위");
465
+ logMcpConfigStatus(".gemini/settings.json", mcpResult!.geminiJson, "Gemini CLI 자동 연결");
336
466
  console.log(` AGENTS.md → 에이전트 가이드 (Bun 사용 명시)`);
337
467
 
338
468
  // Lockfile 안내
339
469
  console.log(`\n🔒 설정 무결성:`);
340
- if (lockfileResult.success) {
470
+ if (lockfileResult!.success) {
341
471
  console.log(` ${LOCKFILE_PATH} 생성됨`);
342
- console.log(` 해시: ${lockfileResult.hash}`);
472
+ console.log(` 해시: ${lockfileResult!.hash}`);
343
473
  } else {
344
474
  console.log(` Lockfile 생성 건너뜀 (설정 없음)`);
345
475
  }
@@ -347,9 +477,12 @@ export async function init(options: InitOptions = {}): Promise<boolean> {
347
477
  return true;
348
478
  }
349
479
 
350
- async function createMinimalLayout(targetDir: string, projectName: string): Promise<void> {
480
+ async function createMinimalLayout(targetDir: string, _projectName: string): Promise<void> {
351
481
  const layoutContent = `/**
352
482
  * Root Layout (Minimal)
483
+ *
484
+ * - html/head/body 태그는 Mandu SSR이 자동으로 생성합니다
485
+ * - 여기서는 body 내부의 공통 래퍼만 정의합니다
353
486
  */
354
487
 
355
488
  interface RootLayoutProps {
@@ -358,16 +491,9 @@ interface RootLayoutProps {
358
491
 
359
492
  export default function RootLayout({ children }: RootLayoutProps) {
360
493
  return (
361
- <html lang="ko">
362
- <head>
363
- <meta charSet="UTF-8" />
364
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
365
- <title>${projectName}</title>
366
- </head>
367
- <body>
368
- {children}
369
- </body>
370
- </html>
494
+ <div className="min-h-screen">
495
+ {children}
496
+ </div>
371
497
  );
372
498
  }
373
499
  `;
@@ -458,6 +584,7 @@ interface McpConfigFileResult {
458
584
  interface McpConfigResult {
459
585
  mcpJson: McpConfigFileResult;
460
586
  claudeJson: McpConfigFileResult;
587
+ geminiJson: McpConfigFileResult;
461
588
  }
462
589
 
463
590
  function logMcpConfigStatus(
@@ -494,7 +621,7 @@ function logMcpConfigStatus(
494
621
  }
495
622
 
496
623
  /**
497
- * .mcp.json / .claude.json 설정 (AI 에이전트 통합)
624
+ * .mcp.json / .claude.json / .gemini/settings.json 설정 (AI 에이전트 통합)
498
625
  * - 파일 없으면 새로 생성
499
626
  * - 파일 있으면 mandu 서버만 추가/업데이트 (다른 설정 유지)
500
627
  */
@@ -512,6 +639,8 @@ async function setupMcpConfig(
512
639
  ): Promise<McpConfigResult> {
513
640
  const mcpPath = path.join(targetDir, ".mcp.json");
514
641
  const claudePath = path.join(targetDir, ".claude.json");
642
+ const geminiDir = path.join(targetDir, ".gemini");
643
+ const geminiPath = path.join(geminiDir, "settings.json");
515
644
 
516
645
  const manduServer = {
517
646
  command: "bunx",
@@ -601,7 +730,11 @@ async function setupMcpConfig(
601
730
  const mcpJson = await updateMcpFile(mcpPath);
602
731
  const claudeJson = await updateMcpFile(claudePath);
603
732
 
604
- return { mcpJson, claudeJson };
733
+ // Gemini CLI: .gemini/settings.json (프로젝트 스코프)
734
+ await fs.mkdir(geminiDir, { recursive: true });
735
+ const geminiJson = await updateMcpFile(geminiPath);
736
+
737
+ return { mcpJson, claudeJson, geminiJson };
605
738
  }
606
739
 
607
740
  interface LockfileResult {
@@ -95,6 +95,8 @@ registerCommand({
95
95
  theme: ctx.options.theme === "true",
96
96
  minimal: ctx.options.minimal === "true",
97
97
  withCi: ctx.options["with-ci"] === "true",
98
+ yes: ctx.options.yes === "true",
99
+ noInstall: ctx.options["no-install"] === "true",
98
100
  });
99
101
  },
100
102
  });
package/src/main.ts CHANGED
@@ -20,7 +20,7 @@ ${theme.heading("🥟 Mandu CLI")} ${theme.muted(`v${VERSION}`)} - Agent-Native
20
20
  ${theme.heading("Usage:")} ${theme.command("bunx mandu")} ${theme.option("<command>")} [options]
21
21
 
22
22
  Commands:
23
- init 새 프로젝트 생성 (Tailwind + shadcn/ui 기본 포함)
23
+ init 새 프로젝트 생성 (대화형 / --yes로 비대화형)
24
24
  check FS Routes + Guard 통합 검사
25
25
  routes generate FS Routes 스캔 및 매니페스트 생성
26
26
  routes list 현재 라우트 목록 출력
@@ -76,6 +76,8 @@ Options:
76
76
  --theme init 시 다크모드 테마 시스템 추가
77
77
  --minimal init 시 CSS/UI 없이 최소 템플릿 생성 (--css none --ui none)
78
78
  --with-ci init 시 GitHub Actions CI/CD 워크플로우 포함 (ATE E2E 테스트)
79
+ --yes, -y init 시 대화형 프롬프트 건너뛰기 (기존 비대화형 동작)
80
+ --no-install init 시 패키지 설치 건너뛰기
79
81
  --file <path> spec-upsert spec 파일/monitor 로그 파일 경로
80
82
  --watch build/guard arch 파일 감시 모드
81
83
  --output <path> routes/openapi/doctor/contract/guard 출력 경로
@@ -173,6 +175,7 @@ export function parseArgs(args: string[]): { command: string; options: Record<st
173
175
  q: "quiet",
174
176
  v: "verify",
175
177
  d: "diff",
178
+ y: "yes",
176
179
  };
177
180
 
178
181
  for (let i = 0; i < args.length; i++) {
@@ -101,6 +101,19 @@ bun run build # 프로덕션 빌드
101
101
  bun run guard # 아키텍처 검증
102
102
  ```
103
103
 
104
+ ## AI 에이전트 MCP 설정
105
+
106
+ 이 프로젝트는 `@mandujs/mcp` MCP 서버를 통해 AI 에이전트와 통합됩니다.
107
+ `mandu init` 시 자동으로 설정 파일이 생성됩니다:
108
+
109
+ | 에이전트 | 설정 파일 | 비고 |
110
+ |----------|-----------|------|
111
+ | Claude Code | `.mcp.json` | 자동 연결 |
112
+ | Claude Desktop | `.claude.json` | 로컬 범위 |
113
+ | Gemini CLI | `.gemini/settings.json` | 자동 연결 |
114
+
115
+ MCP 서버가 제공하는 도구: `mandu_negotiate`, `mandu_generate_scaffold`, `mandu_guard` 등
116
+
104
117
  ## 기술 스택
105
118
 
106
119
  - **Runtime**: Bun 1.x
@@ -83,11 +83,24 @@ import { Button } from "@/client/shared/ui/button";
83
83
 
84
84
  ```bash
85
85
  bun install # 최초 설치
86
- bun run dev # 개발 서버 (http://localhost:4000)
86
+ bun run dev # 개발 서버 (http://localhost:3333)
87
87
  bun run build # 프로덕션 빌드
88
88
  bun run guard # 아키텍처 검증
89
89
  ```
90
90
 
91
+ ## AI 에이전트 MCP 설정
92
+
93
+ 이 프로젝트는 `@mandujs/mcp` MCP 서버를 통해 AI 에이전트와 통합됩니다.
94
+ `mandu init` 시 자동으로 설정 파일이 생성됩니다:
95
+
96
+ | 에이전트 | 설정 파일 | 비고 |
97
+ |----------|-----------|------|
98
+ | Claude Code | `.mcp.json` | 자동 연결 |
99
+ | Claude Desktop | `.claude.json` | 로컬 범위 |
100
+ | Gemini CLI | `.gemini/settings.json` | 자동 연결 |
101
+
102
+ MCP 서버가 제공하는 도구: `mandu_negotiate`, `mandu_generate_scaffold`, `mandu_guard` 등
103
+
91
104
  ## 기술 스택
92
105
 
93
106
  - **Runtime**: Bun 1.x
@@ -2,26 +2,19 @@
2
2
  * Root Layout
3
3
  *
4
4
  * 모든 페이지의 공통 레이아웃
5
- * globals.css를 여기서 임포트
5
+ * - html/head/body 태그는 Mandu SSR이 자동으로 생성합니다
6
+ * - 여기서는 body 내부의 공통 래퍼만 정의합니다
7
+ * - CSS는 Mandu가 자동으로 주입합니다: /.mandu/client/globals.css
6
8
  */
7
9
 
8
- import "./globals.css";
9
-
10
10
  interface RootLayoutProps {
11
11
  children: React.ReactNode;
12
12
  }
13
13
 
14
14
  export default function RootLayout({ children }: RootLayoutProps) {
15
15
  return (
16
- <html lang="ko">
17
- <head>
18
- <meta charSet="UTF-8" />
19
- <meta name="viewport" content="width=device-width, initial-scale=1.0" />
20
- <title>{{PROJECT_NAME}}</title>
21
- </head>
22
- <body className="min-h-screen bg-background font-sans antialiased">
23
- {children}
24
- </body>
25
- </html>
16
+ <div className="min-h-screen bg-background font-sans antialiased">
17
+ {children}
18
+ </div>
26
19
  );
27
20
  }