@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 +27 -6
- package/README.md +23 -8
- package/package.json +1 -1
- package/src/commands/init.ts +197 -64
- package/src/commands/registry.ts +2 -0
- package/src/main.ts +4 -1
- package/templates/default/AGENTS.md +13 -0
- package/templates/realtime-chat/AGENTS.md +14 -1
- package/templates/realtime-chat/app/layout.tsx +6 -13
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
|
|
28
|
-
|
|
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
|
|
42
|
+
### `mandu init [project-name]`
|
|
37
43
|
|
|
38
|
-
새 Mandu 프로젝트를 생성합니다.
|
|
44
|
+
새 Mandu 프로젝트를 생성합니다. 이름 생략 시 대화형 모드로 진입합니다.
|
|
39
45
|
|
|
40
46
|
```bash
|
|
41
|
-
bunx @mandujs/cli init
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
|
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
|
-
|
|
369
|
+
MPL-2.0
|
package/package.json
CHANGED
package/src/commands/init.ts
CHANGED
|
@@ -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
|
|
204
|
-
|
|
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
|
|
291
|
+
const themeEnabled = options.theme || false;
|
|
218
292
|
const withCi = options.withCi || false;
|
|
219
293
|
|
|
220
|
-
console.log(
|
|
221
|
-
console.log(
|
|
222
|
-
console.log(
|
|
223
|
-
console.log(
|
|
224
|
-
console.log(
|
|
225
|
-
if (
|
|
226
|
-
console.log(
|
|
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(
|
|
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
|
|
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(
|
|
387
|
+
console.error(`\n${theme.error("❌")} 프로젝트 생성 실패:`, error);
|
|
271
388
|
return false;
|
|
272
389
|
}
|
|
273
390
|
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
288
|
-
if (
|
|
289
|
-
|
|
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
|
-
//
|
|
293
|
-
|
|
294
|
-
|
|
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
|
|
335
|
-
logMcpConfigStatus(".claude.json", mcpResult
|
|
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
|
|
470
|
+
if (lockfileResult!.success) {
|
|
341
471
|
console.log(` ${LOCKFILE_PATH} 생성됨`);
|
|
342
|
-
console.log(` 해시: ${lockfileResult
|
|
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,
|
|
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
|
-
<
|
|
362
|
-
|
|
363
|
-
|
|
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
|
-
|
|
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 {
|
package/src/commands/registry.ts
CHANGED
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 새 프로젝트 생성 (
|
|
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:
|
|
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
|
-
*
|
|
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
|
-
<
|
|
17
|
-
|
|
18
|
-
|
|
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
|
}
|