@mandujs/cli 0.15.0 → 0.15.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.
- package/README.ko.md +33 -33
- package/README.md +354 -354
- package/package.json +2 -2
- package/src/commands/check.ts +71 -7
- package/src/commands/contract.ts +173 -173
- package/src/commands/dev.ts +9 -42
- package/src/commands/guard-arch.ts +303 -303
- package/src/commands/init.ts +50 -5
- package/src/commands/monitor.ts +300 -300
- package/src/commands/openapi.ts +107 -107
- package/src/commands/registry.ts +1 -0
- package/src/commands/start.ts +9 -42
- package/src/errors/codes.ts +35 -35
- package/src/errors/index.ts +2 -2
- package/src/errors/messages.ts +143 -143
- package/src/hooks/index.ts +17 -17
- package/src/hooks/preaction.ts +256 -256
- package/src/main.ts +9 -7
- package/src/terminal/banner.ts +166 -166
- package/src/terminal/help.ts +306 -306
- package/src/terminal/index.ts +71 -71
- package/src/terminal/output.ts +295 -295
- package/src/terminal/palette.ts +30 -30
- package/src/terminal/progress.ts +327 -327
- package/src/terminal/stream-writer.ts +214 -214
- package/src/terminal/table.ts +354 -354
- package/src/terminal/theme.ts +142 -142
- package/src/util/bun.ts +6 -6
- package/src/util/fs.ts +23 -23
- package/src/util/handlers.ts +49 -5
- package/src/util/lockfile.ts +66 -0
- package/src/util/output.ts +22 -22
- package/src/util/port.ts +71 -71
- package/templates/default/AGENTS.md +96 -96
- package/templates/default/app/api/health/route.ts +13 -13
- package/templates/default/app/globals.css +49 -49
- package/templates/default/app/layout.tsx +27 -27
- package/templates/default/app/page.tsx +38 -38
- package/templates/default/src/client/shared/lib/utils.ts +16 -16
- package/templates/default/src/client/shared/ui/button.tsx +57 -57
- package/templates/default/src/client/shared/ui/card.tsx +1 -1
- package/templates/default/src/client/shared/ui/index.ts +21 -21
- package/templates/default/src/client/shared/ui/input.tsx +5 -1
- package/templates/default/tests/example.test.ts +58 -58
- package/templates/default/tests/helpers.ts +52 -52
- package/templates/default/tests/setup.ts +9 -9
- package/templates/default/tsconfig.json +23 -23
- package/templates/realtime-chat/AGENTS.md +96 -0
- package/templates/realtime-chat/app/api/chat/messages/route.ts +63 -0
- package/templates/realtime-chat/app/api/chat/stream/route.ts +48 -0
- package/templates/realtime-chat/app/api/health/route.ts +13 -0
- package/templates/realtime-chat/app/globals.css +49 -0
- package/templates/realtime-chat/app/layout.tsx +27 -0
- package/templates/realtime-chat/app/page.tsx +16 -0
- package/templates/realtime-chat/package.json +34 -0
- package/templates/realtime-chat/src/client/app/index.ts +1 -0
- package/templates/realtime-chat/src/client/entities/index.ts +1 -0
- package/templates/realtime-chat/src/client/features/chat/chat-api.ts +177 -0
- package/templates/realtime-chat/src/client/features/chat/realtime-chat-starter.client.tsx +89 -0
- package/templates/realtime-chat/src/client/features/chat/use-realtime-chat.ts +73 -0
- package/templates/realtime-chat/src/client/features/index.ts +1 -0
- package/templates/realtime-chat/src/client/pages/index.ts +1 -0
- package/templates/realtime-chat/src/client/shared/index.ts +1 -0
- package/templates/realtime-chat/src/client/shared/lib/utils.ts +16 -0
- package/templates/realtime-chat/src/client/shared/ui/button.tsx +57 -0
- package/templates/realtime-chat/src/client/shared/ui/card.tsx +78 -0
- package/templates/realtime-chat/src/client/shared/ui/index.ts +21 -0
- package/templates/realtime-chat/src/client/shared/ui/input.tsx +28 -0
- package/templates/realtime-chat/src/client/widgets/index.ts +1 -0
- package/templates/realtime-chat/src/server/api/index.ts +1 -0
- package/templates/realtime-chat/src/server/application/ai-adapter.ts +24 -0
- package/templates/realtime-chat/src/server/application/chat-store.ts +88 -0
- package/templates/realtime-chat/src/server/application/index.ts +1 -0
- package/templates/realtime-chat/src/server/core/index.ts +1 -0
- package/templates/realtime-chat/src/server/domain/index.ts +1 -0
- package/templates/realtime-chat/src/server/infra/index.ts +1 -0
- package/templates/realtime-chat/src/shared/contracts/chat.ts +29 -0
- package/templates/realtime-chat/src/shared/contracts/index.ts +1 -0
- package/templates/realtime-chat/src/shared/env/index.ts +1 -0
- package/templates/realtime-chat/src/shared/schema/index.ts +1 -0
- package/templates/realtime-chat/src/shared/types/index.ts +1 -0
- package/templates/realtime-chat/src/shared/utils/client/index.ts +1 -0
- package/templates/realtime-chat/src/shared/utils/server/index.ts +1 -0
- package/templates/realtime-chat/tests/chat-api.sse.test.ts +151 -0
- package/templates/realtime-chat/tests/chat-starter.test.ts +149 -0
- package/templates/realtime-chat/tests/chat-store.concurrency.test.ts +39 -0
- package/templates/realtime-chat/tests/example.test.ts +58 -0
- package/templates/realtime-chat/tests/helpers.ts +52 -0
- package/templates/realtime-chat/tests/setup.ts +9 -0
- package/templates/realtime-chat/tsconfig.json +23 -0
package/src/hooks/preaction.ts
CHANGED
|
@@ -1,256 +1,256 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* DNA-016: Pre-Action Hooks
|
|
3
|
-
*
|
|
4
|
-
* 명령어 실행 전 공통 작업 수행
|
|
5
|
-
* - 프로세스 타이틀 설정
|
|
6
|
-
* - 조건부 배너 표시
|
|
7
|
-
* - Verbose 모드 설정
|
|
8
|
-
* - 설정 로드
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { shouldShowBanner, renderMiniBanner } from "../terminal/banner.js";
|
|
12
|
-
import { loadManduConfig, type ManduConfig } from "@mandujs/core";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Pre-Action 컨텍스트
|
|
16
|
-
*/
|
|
17
|
-
export interface PreActionContext {
|
|
18
|
-
/** 현재 명령어 */
|
|
19
|
-
command: string;
|
|
20
|
-
/** 서브커맨드 */
|
|
21
|
-
subcommand?: string;
|
|
22
|
-
/** 명령어 옵션 */
|
|
23
|
-
options: Record<string, string>;
|
|
24
|
-
/** 로드된 설정 */
|
|
25
|
-
config?: ManduConfig;
|
|
26
|
-
/** verbose 모드 여부 */
|
|
27
|
-
verbose: boolean;
|
|
28
|
-
/** 작업 디렉토리 */
|
|
29
|
-
cwd: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Pre-Action 훅 타입
|
|
34
|
-
*/
|
|
35
|
-
export type PreActionHook = (ctx: PreActionContext) => void | Promise<void>;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Pre-Action 훅 레지스트리
|
|
39
|
-
*/
|
|
40
|
-
class PreActionRegistry {
|
|
41
|
-
private hooks: PreActionHook[] = [];
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* 훅 등록
|
|
45
|
-
*/
|
|
46
|
-
register(hook: PreActionHook): void {
|
|
47
|
-
this.hooks.push(hook);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* 훅 제거
|
|
52
|
-
*/
|
|
53
|
-
unregister(hook: PreActionHook): boolean {
|
|
54
|
-
const index = this.hooks.indexOf(hook);
|
|
55
|
-
if (index >= 0) {
|
|
56
|
-
this.hooks.splice(index, 1);
|
|
57
|
-
return true;
|
|
58
|
-
}
|
|
59
|
-
return false;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* 모든 훅 실행
|
|
64
|
-
*/
|
|
65
|
-
async runAll(ctx: PreActionContext): Promise<void> {
|
|
66
|
-
for (const hook of this.hooks) {
|
|
67
|
-
await hook(ctx);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* 훅 초기화
|
|
73
|
-
*/
|
|
74
|
-
clear(): void {
|
|
75
|
-
this.hooks = [];
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* 등록된 훅 수
|
|
80
|
-
*/
|
|
81
|
-
get size(): number {
|
|
82
|
-
return this.hooks.length;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* 전역 Pre-Action 훅 레지스트리
|
|
88
|
-
*/
|
|
89
|
-
export const preActionRegistry = new PreActionRegistry();
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* 설정 로드가 필요없는 명령어
|
|
93
|
-
*/
|
|
94
|
-
const SKIP_CONFIG_COMMANDS = new Set([
|
|
95
|
-
"init",
|
|
96
|
-
"help",
|
|
97
|
-
"version",
|
|
98
|
-
"completion",
|
|
99
|
-
]);
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* 배너 표시가 필요없는 명령어
|
|
103
|
-
*/
|
|
104
|
-
const SKIP_BANNER_COMMANDS = new Set([
|
|
105
|
-
"completion",
|
|
106
|
-
"version",
|
|
107
|
-
]);
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* verbose 전역 상태
|
|
111
|
-
*/
|
|
112
|
-
let globalVerbose = false;
|
|
113
|
-
|
|
114
|
-
/**
|
|
115
|
-
* verbose 모드 설정
|
|
116
|
-
*/
|
|
117
|
-
export function setVerbose(value: boolean): void {
|
|
118
|
-
globalVerbose = value;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* verbose 모드 확인
|
|
123
|
-
*/
|
|
124
|
-
export function isVerbose(): boolean {
|
|
125
|
-
return globalVerbose;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* 프로세스 타이틀 설정
|
|
130
|
-
*/
|
|
131
|
-
export function setProcessTitle(command: string, subcommand?: string): void {
|
|
132
|
-
const title = subcommand
|
|
133
|
-
? `mandu ${command} ${subcommand}`
|
|
134
|
-
: `mandu ${command}`;
|
|
135
|
-
|
|
136
|
-
if (typeof process.title !== "undefined") {
|
|
137
|
-
process.title = title;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* 기본 Pre-Action 실행
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* ```ts
|
|
146
|
-
* const ctx = await runPreAction({
|
|
147
|
-
* command: "dev",
|
|
148
|
-
* options: { port: "3000" },
|
|
149
|
-
* });
|
|
150
|
-
*
|
|
151
|
-
* // ctx.config 에서 로드된 설정 사용
|
|
152
|
-
* // ctx.verbose 로 verbose 모드 확인
|
|
153
|
-
* ```
|
|
154
|
-
*/
|
|
155
|
-
export async function runPreAction(params: {
|
|
156
|
-
command: string;
|
|
157
|
-
subcommand?: string;
|
|
158
|
-
options: Record<string, string>;
|
|
159
|
-
cwd?: string;
|
|
160
|
-
version?: string;
|
|
161
|
-
}): Promise<PreActionContext> {
|
|
162
|
-
const {
|
|
163
|
-
command,
|
|
164
|
-
subcommand,
|
|
165
|
-
options,
|
|
166
|
-
cwd = process.cwd(),
|
|
167
|
-
version,
|
|
168
|
-
} = params;
|
|
169
|
-
|
|
170
|
-
// 1. verbose 모드 확인
|
|
171
|
-
const verbose = options.verbose === "true" || process.env.MANDU_VERBOSE === "true";
|
|
172
|
-
setVerbose(verbose);
|
|
173
|
-
|
|
174
|
-
// 2. 프로세스 타이틀 설정
|
|
175
|
-
setProcessTitle(command, subcommand);
|
|
176
|
-
|
|
177
|
-
// 3. 조건부 배너 표시
|
|
178
|
-
const showBanner =
|
|
179
|
-
!SKIP_BANNER_COMMANDS.has(command) &&
|
|
180
|
-
!isTruthyEnv("MANDU_HIDE_BANNER") &&
|
|
181
|
-
shouldShowBanner();
|
|
182
|
-
|
|
183
|
-
if (showBanner && version) {
|
|
184
|
-
console.log(renderMiniBanner(version));
|
|
185
|
-
console.log();
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// 4. 설정 로드 (필요한 명령어만)
|
|
189
|
-
let config: ManduConfig | undefined;
|
|
190
|
-
if (!SKIP_CONFIG_COMMANDS.has(command)) {
|
|
191
|
-
try {
|
|
192
|
-
config = await loadManduConfig(cwd);
|
|
193
|
-
} catch {
|
|
194
|
-
// 설정 로드 실패 시 무시 (옵션 설정만 사용)
|
|
195
|
-
if (verbose) {
|
|
196
|
-
console.warn("[mandu] Config load failed, using defaults");
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
// Pre-Action 컨텍스트 생성
|
|
202
|
-
const ctx: PreActionContext = {
|
|
203
|
-
command,
|
|
204
|
-
subcommand,
|
|
205
|
-
options,
|
|
206
|
-
config,
|
|
207
|
-
verbose,
|
|
208
|
-
cwd,
|
|
209
|
-
};
|
|
210
|
-
|
|
211
|
-
// 5. 등록된 훅 실행
|
|
212
|
-
await preActionRegistry.runAll(ctx);
|
|
213
|
-
|
|
214
|
-
return ctx;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* 환경변수가 truthy인지 확인
|
|
219
|
-
*/
|
|
220
|
-
function isTruthyEnv(key: string): boolean {
|
|
221
|
-
const value = process.env[key];
|
|
222
|
-
if (!value) return false;
|
|
223
|
-
return !["0", "false", "no", ""].includes(value.toLowerCase());
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Pre-Action 훅 등록 헬퍼
|
|
228
|
-
*
|
|
229
|
-
* @example
|
|
230
|
-
* ```ts
|
|
231
|
-
* registerPreActionHook(async (ctx) => {
|
|
232
|
-
* if (ctx.command === "dev") {
|
|
233
|
-
* console.log("Starting development mode...");
|
|
234
|
-
* }
|
|
235
|
-
* });
|
|
236
|
-
* ```
|
|
237
|
-
*/
|
|
238
|
-
export function registerPreActionHook(hook: PreActionHook): () => void {
|
|
239
|
-
preActionRegistry.register(hook);
|
|
240
|
-
return () => preActionRegistry.unregister(hook);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* 기본 훅들 등록
|
|
245
|
-
*/
|
|
246
|
-
export function registerDefaultHooks(): void {
|
|
247
|
-
// 예: 개발 모드에서 추가 정보 표시
|
|
248
|
-
registerPreActionHook((ctx) => {
|
|
249
|
-
if (ctx.verbose && ctx.config) {
|
|
250
|
-
console.log(`[mandu] Config loaded from ${ctx.cwd}`);
|
|
251
|
-
if (ctx.config.server?.port) {
|
|
252
|
-
console.log(`[mandu] Server port: ${ctx.config.server.port}`);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
});
|
|
256
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* DNA-016: Pre-Action Hooks
|
|
3
|
+
*
|
|
4
|
+
* 명령어 실행 전 공통 작업 수행
|
|
5
|
+
* - 프로세스 타이틀 설정
|
|
6
|
+
* - 조건부 배너 표시
|
|
7
|
+
* - Verbose 모드 설정
|
|
8
|
+
* - 설정 로드
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { shouldShowBanner, renderMiniBanner } from "../terminal/banner.js";
|
|
12
|
+
import { loadManduConfig, type ManduConfig } from "@mandujs/core";
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Pre-Action 컨텍스트
|
|
16
|
+
*/
|
|
17
|
+
export interface PreActionContext {
|
|
18
|
+
/** 현재 명령어 */
|
|
19
|
+
command: string;
|
|
20
|
+
/** 서브커맨드 */
|
|
21
|
+
subcommand?: string;
|
|
22
|
+
/** 명령어 옵션 */
|
|
23
|
+
options: Record<string, string>;
|
|
24
|
+
/** 로드된 설정 */
|
|
25
|
+
config?: ManduConfig;
|
|
26
|
+
/** verbose 모드 여부 */
|
|
27
|
+
verbose: boolean;
|
|
28
|
+
/** 작업 디렉토리 */
|
|
29
|
+
cwd: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Pre-Action 훅 타입
|
|
34
|
+
*/
|
|
35
|
+
export type PreActionHook = (ctx: PreActionContext) => void | Promise<void>;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Pre-Action 훅 레지스트리
|
|
39
|
+
*/
|
|
40
|
+
class PreActionRegistry {
|
|
41
|
+
private hooks: PreActionHook[] = [];
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 훅 등록
|
|
45
|
+
*/
|
|
46
|
+
register(hook: PreActionHook): void {
|
|
47
|
+
this.hooks.push(hook);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* 훅 제거
|
|
52
|
+
*/
|
|
53
|
+
unregister(hook: PreActionHook): boolean {
|
|
54
|
+
const index = this.hooks.indexOf(hook);
|
|
55
|
+
if (index >= 0) {
|
|
56
|
+
this.hooks.splice(index, 1);
|
|
57
|
+
return true;
|
|
58
|
+
}
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 모든 훅 실행
|
|
64
|
+
*/
|
|
65
|
+
async runAll(ctx: PreActionContext): Promise<void> {
|
|
66
|
+
for (const hook of this.hooks) {
|
|
67
|
+
await hook(ctx);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* 훅 초기화
|
|
73
|
+
*/
|
|
74
|
+
clear(): void {
|
|
75
|
+
this.hooks = [];
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* 등록된 훅 수
|
|
80
|
+
*/
|
|
81
|
+
get size(): number {
|
|
82
|
+
return this.hooks.length;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* 전역 Pre-Action 훅 레지스트리
|
|
88
|
+
*/
|
|
89
|
+
export const preActionRegistry = new PreActionRegistry();
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* 설정 로드가 필요없는 명령어
|
|
93
|
+
*/
|
|
94
|
+
const SKIP_CONFIG_COMMANDS = new Set([
|
|
95
|
+
"init",
|
|
96
|
+
"help",
|
|
97
|
+
"version",
|
|
98
|
+
"completion",
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* 배너 표시가 필요없는 명령어
|
|
103
|
+
*/
|
|
104
|
+
const SKIP_BANNER_COMMANDS = new Set([
|
|
105
|
+
"completion",
|
|
106
|
+
"version",
|
|
107
|
+
]);
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* verbose 전역 상태
|
|
111
|
+
*/
|
|
112
|
+
let globalVerbose = false;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* verbose 모드 설정
|
|
116
|
+
*/
|
|
117
|
+
export function setVerbose(value: boolean): void {
|
|
118
|
+
globalVerbose = value;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* verbose 모드 확인
|
|
123
|
+
*/
|
|
124
|
+
export function isVerbose(): boolean {
|
|
125
|
+
return globalVerbose;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 프로세스 타이틀 설정
|
|
130
|
+
*/
|
|
131
|
+
export function setProcessTitle(command: string, subcommand?: string): void {
|
|
132
|
+
const title = subcommand
|
|
133
|
+
? `mandu ${command} ${subcommand}`
|
|
134
|
+
: `mandu ${command}`;
|
|
135
|
+
|
|
136
|
+
if (typeof process.title !== "undefined") {
|
|
137
|
+
process.title = title;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* 기본 Pre-Action 실행
|
|
143
|
+
*
|
|
144
|
+
* @example
|
|
145
|
+
* ```ts
|
|
146
|
+
* const ctx = await runPreAction({
|
|
147
|
+
* command: "dev",
|
|
148
|
+
* options: { port: "3000" },
|
|
149
|
+
* });
|
|
150
|
+
*
|
|
151
|
+
* // ctx.config 에서 로드된 설정 사용
|
|
152
|
+
* // ctx.verbose 로 verbose 모드 확인
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
155
|
+
export async function runPreAction(params: {
|
|
156
|
+
command: string;
|
|
157
|
+
subcommand?: string;
|
|
158
|
+
options: Record<string, string>;
|
|
159
|
+
cwd?: string;
|
|
160
|
+
version?: string;
|
|
161
|
+
}): Promise<PreActionContext> {
|
|
162
|
+
const {
|
|
163
|
+
command,
|
|
164
|
+
subcommand,
|
|
165
|
+
options,
|
|
166
|
+
cwd = process.cwd(),
|
|
167
|
+
version,
|
|
168
|
+
} = params;
|
|
169
|
+
|
|
170
|
+
// 1. verbose 모드 확인
|
|
171
|
+
const verbose = options.verbose === "true" || process.env.MANDU_VERBOSE === "true";
|
|
172
|
+
setVerbose(verbose);
|
|
173
|
+
|
|
174
|
+
// 2. 프로세스 타이틀 설정
|
|
175
|
+
setProcessTitle(command, subcommand);
|
|
176
|
+
|
|
177
|
+
// 3. 조건부 배너 표시
|
|
178
|
+
const showBanner =
|
|
179
|
+
!SKIP_BANNER_COMMANDS.has(command) &&
|
|
180
|
+
!isTruthyEnv("MANDU_HIDE_BANNER") &&
|
|
181
|
+
shouldShowBanner();
|
|
182
|
+
|
|
183
|
+
if (showBanner && version) {
|
|
184
|
+
console.log(renderMiniBanner(version));
|
|
185
|
+
console.log();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// 4. 설정 로드 (필요한 명령어만)
|
|
189
|
+
let config: ManduConfig | undefined;
|
|
190
|
+
if (!SKIP_CONFIG_COMMANDS.has(command)) {
|
|
191
|
+
try {
|
|
192
|
+
config = await loadManduConfig(cwd);
|
|
193
|
+
} catch {
|
|
194
|
+
// 설정 로드 실패 시 무시 (옵션 설정만 사용)
|
|
195
|
+
if (verbose) {
|
|
196
|
+
console.warn("[mandu] Config load failed, using defaults");
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// Pre-Action 컨텍스트 생성
|
|
202
|
+
const ctx: PreActionContext = {
|
|
203
|
+
command,
|
|
204
|
+
subcommand,
|
|
205
|
+
options,
|
|
206
|
+
config,
|
|
207
|
+
verbose,
|
|
208
|
+
cwd,
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// 5. 등록된 훅 실행
|
|
212
|
+
await preActionRegistry.runAll(ctx);
|
|
213
|
+
|
|
214
|
+
return ctx;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* 환경변수가 truthy인지 확인
|
|
219
|
+
*/
|
|
220
|
+
function isTruthyEnv(key: string): boolean {
|
|
221
|
+
const value = process.env[key];
|
|
222
|
+
if (!value) return false;
|
|
223
|
+
return !["0", "false", "no", ""].includes(value.toLowerCase());
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Pre-Action 훅 등록 헬퍼
|
|
228
|
+
*
|
|
229
|
+
* @example
|
|
230
|
+
* ```ts
|
|
231
|
+
* registerPreActionHook(async (ctx) => {
|
|
232
|
+
* if (ctx.command === "dev") {
|
|
233
|
+
* console.log("Starting development mode...");
|
|
234
|
+
* }
|
|
235
|
+
* });
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
export function registerPreActionHook(hook: PreActionHook): () => void {
|
|
239
|
+
preActionRegistry.register(hook);
|
|
240
|
+
return () => preActionRegistry.unregister(hook);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* 기본 훅들 등록
|
|
245
|
+
*/
|
|
246
|
+
export function registerDefaultHooks(): void {
|
|
247
|
+
// 예: 개발 모드에서 추가 정보 표시
|
|
248
|
+
registerPreActionHook((ctx) => {
|
|
249
|
+
if (ctx.verbose && ctx.config) {
|
|
250
|
+
console.log(`[mandu] Config loaded from ${ctx.cwd}`);
|
|
251
|
+
if (ctx.config.server?.port) {
|
|
252
|
+
console.log(`[mandu] Server port: ${ctx.config.server.port}`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
package/src/main.ts
CHANGED
|
@@ -62,6 +62,7 @@ Commands:
|
|
|
62
62
|
|
|
63
63
|
Options:
|
|
64
64
|
--name <name> init 시 프로젝트 이름 (기본: my-mandu-app)
|
|
65
|
+
--template <name> init 템플릿: default, realtime-chat (기본: default)
|
|
65
66
|
--css <framework> init 시 CSS 프레임워크: tailwind, panda, none (기본: tailwind)
|
|
66
67
|
--ui <library> init 시 UI 라이브러리: shadcn, ark, none (기본: shadcn)
|
|
67
68
|
--theme init 시 다크모드 테마 시스템 추가
|
|
@@ -102,8 +103,9 @@ Notes:
|
|
|
102
103
|
- 포트 충돌 시 다음 사용 가능한 포트로 자동 변경됩니다.
|
|
103
104
|
|
|
104
105
|
Examples:
|
|
105
|
-
bunx mandu init --name my-app
|
|
106
|
-
bunx mandu init
|
|
106
|
+
bunx mandu init --name my-app # Tailwind + shadcn/ui 기본
|
|
107
|
+
bunx mandu init --name chat-app --template realtime-chat # 실시간 채팅 스타터 템플릿
|
|
108
|
+
bunx mandu init my-app --minimal # CSS/UI 없이 최소 템플릿
|
|
107
109
|
bunx mandu dev
|
|
108
110
|
bunx mandu build --watch
|
|
109
111
|
bunx mandu guard
|
|
@@ -141,7 +143,7 @@ Brain (sLLM) Workflow:
|
|
|
141
143
|
/**
|
|
142
144
|
* 인자 파싱
|
|
143
145
|
*/
|
|
144
|
-
function parseArgs(args: string[]): { command: string; options: Record<string, string> } {
|
|
146
|
+
export function parseArgs(args: string[]): { command: string; options: Record<string, string> } {
|
|
145
147
|
const options: Record<string, string> = {};
|
|
146
148
|
let command = "";
|
|
147
149
|
const shortFlags: Record<string, string> = {
|
|
@@ -184,8 +186,7 @@ function parseArgs(args: string[]): { command: string; options: Record<string, s
|
|
|
184
186
|
/**
|
|
185
187
|
* 메인 함수
|
|
186
188
|
*/
|
|
187
|
-
async function main(): Promise<void> {
|
|
188
|
-
const args = process.argv.slice(2);
|
|
189
|
+
export async function main(args = process.argv.slice(2)): Promise<void> {
|
|
189
190
|
const { command, options } = parseArgs(args);
|
|
190
191
|
|
|
191
192
|
// 도움말 처리
|
|
@@ -234,7 +235,8 @@ async function main(): Promise<void> {
|
|
|
234
235
|
process.exit(1);
|
|
235
236
|
}
|
|
236
237
|
|
|
237
|
-
process.exit(0);
|
|
238
238
|
}
|
|
239
239
|
|
|
240
|
-
|
|
240
|
+
if (import.meta.main) {
|
|
241
|
+
main().catch((error) => handleCLIError(error));
|
|
242
|
+
}
|