@mandujs/core 0.9.37 → 0.9.39

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.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  <p align="center">
8
8
  <strong>Mandu Framework Core</strong><br/>
9
- Spec, Generator, Guard, Runtime, Filling
9
+ Runtime, FS Routes, Guard, Bundler, Contract, Filling
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -21,170 +21,456 @@ bun add @mandujs/core
21
21
 
22
22
  > Typically used through `@mandujs/cli`. Direct usage is for advanced use cases.
23
23
 
24
- ## Module Structure
24
+ ## Quick Start
25
+
26
+ ### Basic API Handler (Filling)
27
+
28
+ ```typescript
29
+ import { Mandu } from "@mandujs/core";
30
+
31
+ export default Mandu.filling()
32
+ .guard((ctx) => {
33
+ if (!ctx.get("user")) return ctx.unauthorized("Login required");
34
+ })
35
+ .get(async (ctx) => {
36
+ const users = await db.users.findMany();
37
+ return ctx.ok({ data: users });
38
+ })
39
+ .post(async (ctx) => {
40
+ const body = await ctx.body<{ name: string }>();
41
+ const user = await db.users.create({ data: body });
42
+ return ctx.created({ data: user });
43
+ });
44
+ ```
45
+
46
+ ### Type-Safe Contract
47
+
48
+ ```typescript
49
+ import { Mandu } from "@mandujs/core";
50
+ import { z } from "zod";
51
+
52
+ const userContract = Mandu.contract({
53
+ request: {
54
+ GET: { query: z.object({ id: z.string() }) },
55
+ POST: { body: z.object({ name: z.string() }) }
56
+ },
57
+ response: {
58
+ 200: z.object({ data: z.any() }),
59
+ 400: z.object({ error: z.string() })
60
+ }
61
+ });
62
+
63
+ const handlers = Mandu.handler(userContract, {
64
+ GET: (ctx) => ({ data: fetchUser(ctx.query.id) }),
65
+ POST: (ctx) => ({ data: createUser(ctx.body) })
66
+ });
67
+ ```
68
+
69
+ ---
70
+
71
+ ## Module Overview
25
72
 
26
73
  ```
27
74
  @mandujs/core
28
- ├── spec/ # Spec schema and loading
29
- ├── generator/ # Code generation
30
- ├── guard/ # Architecture checking and auto-correction
31
- ├── runtime/ # Server and router
32
- └── report/ # Guard report generation
75
+ ├── router/ # FS Routes - file-system based routing
76
+ ├── guard/ # Mandu Guard - architecture enforcement
77
+ ├── runtime/ # Server, SSR, streaming
78
+ ├── filling/ # Handler chain API (Mandu.filling())
79
+ ├── contract/ # Type-safe API contracts
80
+ ├── bundler/ # Client bundling, HMR
81
+ ├── client/ # Island hydration, client router
82
+ ├── brain/ # Doctor, Watcher, Architecture analyzer
83
+ ├── change/ # Transaction & history
84
+ └── spec/ # Manifest schema & validation
33
85
  ```
34
86
 
35
- ## Spec Module
87
+ ---
88
+
89
+ ## FS Routes
36
90
 
37
- Route manifest schema definition and loading.
91
+ File-system based routing system.
38
92
 
39
93
  ```typescript
40
- import { loadManifest, RoutesManifest, RouteSpec } from "@mandujs/core";
94
+ import { scanRoutes, generateManifest, watchFSRoutes } from "@mandujs/core/router";
41
95
 
42
- // Load and validate manifest
43
- const result = await loadManifest("spec/routes.manifest.json");
96
+ // Scan routes from app/ directory
97
+ const result = await scanRoutes("/path/to/project");
98
+ console.log(result.routes);
44
99
 
45
- if (result.success && result.data) {
46
- const manifest: RoutesManifest = result.data;
47
- manifest.routes.forEach((route: RouteSpec) => {
48
- console.log(route.id, route.pattern, route.kind);
49
- });
50
- }
100
+ // Generate manifest
101
+ const { manifest } = await generateManifest("/path/to/project", {
102
+ outputPath: ".mandu/manifest.json"
103
+ });
104
+
105
+ // Watch for changes
106
+ const watcher = await watchFSRoutes("/path/to/project", {
107
+ onChange: (result) => console.log("Routes updated!", result.routes.length)
108
+ });
51
109
  ```
52
110
 
53
- ### Lock File
111
+ ### Route Patterns
54
112
 
55
113
  ```typescript
56
- import { writeLock, readLock } from "@mandujs/core";
114
+ import { pathToPattern, parseSegments } from "@mandujs/core/router";
115
+
116
+ // Convert path to URL pattern
117
+ pathToPattern("users/[id]/posts"); // → "/users/:id/posts"
118
+ pathToPattern("docs/[...slug]"); // → "/docs/:slug*"
119
+ pathToPattern("(auth)/login"); // → "/login" (group ignored)
120
+
121
+ // Parse segments
122
+ parseSegments("[id]"); // → [{ type: "dynamic", name: "id" }]
123
+ parseSegments("[...slug]"); // → [{ type: "catch-all", name: "slug" }]
124
+ ```
125
+
126
+ ---
57
127
 
58
- // Write lock file
59
- const lock = await writeLock("spec/spec.lock.json", manifest);
60
- console.log(lock.routesHash);
128
+ ## Mandu Guard
61
129
 
62
- // Read lock file
63
- const existing = await readLock("spec/spec.lock.json");
130
+ Real-time architecture enforcement with preset support.
131
+
132
+ ```typescript
133
+ import {
134
+ createGuardWatcher,
135
+ checkDirectory,
136
+ getPreset,
137
+ listPresets
138
+ } from "@mandujs/core/guard";
139
+
140
+ // One-time check
141
+ const report = await checkDirectory(
142
+ { preset: "mandu" },
143
+ process.cwd()
144
+ );
145
+ console.log(`Violations: ${report.totalViolations}`);
146
+
147
+ // Real-time watching
148
+ const watcher = createGuardWatcher({
149
+ config: { preset: "mandu", srcDir: "src" },
150
+ rootDir: process.cwd(),
151
+ onViolation: (v) => console.log(`${v.filePath}: ${v.ruleDescription}`),
152
+ });
153
+ watcher.start();
154
+
155
+ // List available presets
156
+ listPresets().forEach(p => console.log(p.name, p.description));
64
157
  ```
65
158
 
66
- ## Generator Module
159
+ ### Presets
160
+
161
+ | Preset | Layers | Use Case |
162
+ |--------|--------|----------|
163
+ | `mandu` | app, pages, widgets, features, entities, api, application, domain, infra, core, shared | Fullstack (default) |
164
+ | `fsd` | app, pages, widgets, features, entities, shared | Frontend |
165
+ | `clean` | api, application, domain, infra, shared | Backend |
166
+ | `hexagonal` | adapters, ports, application, domain | DDD |
167
+ | `atomic` | pages, templates, organisms, molecules, atoms | UI |
67
168
 
68
- Spec-based code generation.
169
+ ### AST-based Analysis
69
170
 
70
171
  ```typescript
71
- import { generateRoutes, GenerateResult } from "@mandujs/core";
172
+ import { extractImportsAST, analyzeModuleAST } from "@mandujs/core/guard";
72
173
 
73
- const result: GenerateResult = await generateRoutes(manifest, "./");
174
+ // Extract imports with AST (more accurate than regex)
175
+ const imports = extractImportsAST(code);
176
+ // → [{ path: "./utils", type: "static", line: 1, namedImports: ["foo"] }]
74
177
 
75
- console.log("Created:", result.created);
76
- console.log("Skipped:", result.skipped); // Existing slot files
178
+ // Full module analysis
179
+ const analysis = analyzeModuleAST(code, "src/features/user/api.ts");
77
180
  ```
78
181
 
79
- ### Template Functions
182
+ ### Statistics & Trends
80
183
 
81
184
  ```typescript
82
185
  import {
83
- generateApiHandler,
84
- generateApiHandlerWithSlot,
85
- generateSlotLogic,
86
- generatePageComponent
87
- } from "@mandujs/core";
186
+ createScanRecord,
187
+ addScanRecord,
188
+ analyzeTrend,
189
+ generateGuardMarkdownReport
190
+ } from "@mandujs/core/guard";
191
+
192
+ // Save scan for trend analysis
193
+ const record = createScanRecord(report, "mandu");
194
+ await addScanRecord(rootDir, record);
195
+
196
+ // Analyze improvement trend
197
+ const trend = analyzeTrend(records, 7); // 7 days
198
+ console.log(trend.trend); // "improving" | "stable" | "degrading"
199
+
200
+ // Generate reports
201
+ const markdown = generateGuardMarkdownReport(report, trend);
202
+ ```
203
+
204
+ ---
88
205
 
89
- // Generate API handler
90
- const code = generateApiHandler(route);
206
+ ## Filling API
207
+
208
+ Handler chain for business logic.
209
+
210
+ ```typescript
211
+ import { Mandu } from "@mandujs/core";
212
+
213
+ export default Mandu.filling()
214
+ // Lifecycle hooks
215
+ .onRequest((ctx) => {
216
+ ctx.set("requestId", crypto.randomUUID());
217
+ })
218
+
219
+ // Guard (return Response to block)
220
+ .guard((ctx) => {
221
+ if (!ctx.get("user")) return ctx.unauthorized("Login required");
222
+ })
223
+
224
+ // Handlers
225
+ .get(async (ctx) => {
226
+ return ctx.ok({ users: await fetchUsers() });
227
+ })
228
+
229
+ .post(async (ctx) => {
230
+ const body = await ctx.body();
231
+ return ctx.created({ user: await createUser(body) });
232
+ })
233
+
234
+ // After response
235
+ .afterResponse((ctx) => {
236
+ console.log("Request completed:", ctx.get("requestId"));
237
+ });
238
+ ```
91
239
 
92
- // API handler with slot
93
- const codeWithSlot = generateApiHandlerWithSlot(route);
240
+ ### Middleware (Compose-style)
94
241
 
95
- // Slot logic file
96
- const slotCode = generateSlotLogic(route);
242
+ ```typescript
243
+ export default Mandu.filling()
244
+ .middleware(async (ctx, next) => {
245
+ console.log("before");
246
+ await next();
247
+ console.log("after");
248
+ })
249
+ .get((ctx) => ctx.ok({ ok: true }));
97
250
  ```
98
251
 
99
- ## Guard Module
252
+ ### Context API
253
+
254
+ | Method | Description |
255
+ |--------|-------------|
256
+ | `ctx.ok(data)` | 200 OK |
257
+ | `ctx.created(data)` | 201 Created |
258
+ | `ctx.noContent()` | 204 No Content |
259
+ | `ctx.error(message)` | 400 Bad Request |
260
+ | `ctx.unauthorized(message)` | 401 Unauthorized |
261
+ | `ctx.forbidden(message)` | 403 Forbidden |
262
+ | `ctx.notFound(message)` | 404 Not Found |
263
+ | `ctx.fail(message)` | 500 Internal Server Error |
264
+ | `ctx.body<T>()` | Parse request body |
265
+ | `ctx.params` | Route parameters |
266
+ | `ctx.query` | Query parameters |
267
+ | `ctx.set(key, value)` | Store in context |
268
+ | `ctx.get<T>(key)` | Retrieve from context |
269
+
270
+ ---
271
+
272
+ ## Contract API
100
273
 
101
- Architecture rule checking and auto-correction.
274
+ Type-safe API contracts with Zod.
102
275
 
103
276
  ```typescript
104
- import {
105
- runGuardCheck,
106
- runAutoCorrect,
107
- GuardResult,
108
- GuardViolation
109
- } from "@mandujs/core";
277
+ import { Mandu } from "@mandujs/core";
278
+ import { z } from "zod";
279
+
280
+ // Define contract
281
+ const userContract = Mandu.contract({
282
+ request: {
283
+ GET: { query: z.object({ id: z.string() }) },
284
+ POST: { body: z.object({ name: z.string() }) }
285
+ },
286
+ response: {
287
+ 200: z.object({ data: z.any() }),
288
+ 400: z.object({ error: z.string() })
289
+ }
290
+ });
110
291
 
111
- // Run check
112
- const result: GuardResult = await runGuardCheck(manifest, "./");
292
+ // Create typed handlers
293
+ const handlers = Mandu.handler(userContract, {
294
+ GET: (ctx) => ({ data: fetchUser(ctx.query.id) }),
295
+ POST: (ctx) => ({ data: createUser(ctx.body) })
296
+ });
113
297
 
114
- if (!result.passed) {
115
- result.violations.forEach((v: GuardViolation) => {
116
- console.log(`${v.rule}: ${v.message}`);
117
- });
298
+ // Type-safe client
299
+ const client = Mandu.client(userContract, { baseUrl: "/api/users" });
300
+ const result = await client.GET({ query: { id: "123" } });
301
+ ```
118
302
 
119
- // Run auto-correction
120
- const corrected = await runAutoCorrect(result.violations, manifest, "./");
121
- console.log("Fixed:", corrected.steps);
122
- console.log("Remaining violations:", corrected.remainingViolations);
123
- }
303
+ ---
304
+
305
+ ## Runtime
306
+
307
+ Server and SSR.
308
+
309
+ ```typescript
310
+ import { startServer, registerApiHandler, registerPageLoader } from "@mandujs/core";
311
+
312
+ // Register handlers
313
+ registerApiHandler("getUsers", async (req) => ({ users: [] }));
314
+ registerPageLoader("home", () => import("./pages/Home"));
315
+
316
+ // Start server
317
+ const server = startServer(manifest, { port: 3000 });
318
+ ```
319
+
320
+ ### Streaming SSR
321
+
322
+ ```typescript
323
+ import { renderToStream } from "@mandujs/core";
324
+
325
+ const stream = await renderToStream(<App />, {
326
+ bootstrapScripts: ["/client.js"],
327
+ onError: (err) => console.error(err)
328
+ });
329
+ ```
330
+
331
+ ---
332
+
333
+ ## Client (Islands & Router)
334
+
335
+ ### Island Hydration
336
+
337
+ ```typescript
338
+ import { createIsland, partial } from "@mandujs/core/client";
339
+
340
+ // Define island
341
+ const CounterIsland = createIsland({
342
+ name: "counter",
343
+ component: Counter,
344
+ priority: "visible"
345
+ });
346
+
347
+ // Partial (smaller than island)
348
+ const ButtonPartial = partial("submit-btn", SubmitButton);
124
349
  ```
125
350
 
126
- ### Guard Rules
351
+ ### Client Router
127
352
 
128
- | Rule ID | Description | Auto-correctable |
129
- |---------|-------------|------------------|
130
- | `SPEC_HASH_MISMATCH` | Spec and lock hash mismatch | ✅ |
131
- | `GENERATED_MANUAL_EDIT` | Manual edit to generated file | ✅ |
132
- | `HANDLER_NOT_FOUND` | Handler file not found | ❌ |
133
- | `COMPONENT_NOT_FOUND` | Component file not found | ❌ |
134
- | `SLOT_NOT_FOUND` | Slot file not found | ✅ |
353
+ ```typescript
354
+ import { useRouter, useParams, Link, NavLink } from "@mandujs/core/client";
355
+
356
+ function Navigation() {
357
+ const router = useRouter();
358
+ const params = useParams();
359
+
360
+ return (
361
+ <nav>
362
+ <NavLink href="/users" activeClass="active">Users</NavLink>
363
+ <button onClick={() => router.push("/settings")}>Settings</button>
364
+ </nav>
365
+ );
366
+ }
367
+ ```
135
368
 
136
- ## Runtime Module
369
+ ---
137
370
 
138
- Server startup and routing.
371
+ ## Brain (AI Assistant)
372
+
373
+ Doctor and architecture analyzer.
139
374
 
140
375
  ```typescript
141
376
  import {
142
- startServer,
143
- registerApiHandler,
144
- registerPageLoader
377
+ initializeBrain,
378
+ getBrain,
379
+ analyzeViolations,
380
+ initializeArchitectureAnalyzer
145
381
  } from "@mandujs/core";
146
382
 
147
- // Register API handler
148
- registerApiHandler("getUsers", async (req) => {
149
- return { users: [] };
383
+ // Initialize
384
+ await initializeBrain();
385
+ const brain = getBrain();
386
+
387
+ // Analyze violations with suggestions
388
+ const analysis = await analyzeViolations(violations, { useLLM: true });
389
+ console.log(analysis.patches); // Suggested fixes
390
+
391
+ // Architecture analyzer
392
+ const analyzer = initializeArchitectureAnalyzer(rootDir);
393
+ const locationResult = await analyzer.checkLocation({ path: "src/features/user.ts" });
394
+ const importResult = await analyzer.checkImports({
395
+ sourceFile: "src/features/user.ts",
396
+ imports: ["../entities/product"]
150
397
  });
398
+ ```
151
399
 
152
- // Register page loader
153
- registerPageLoader("homePage", () => import("./pages/Home"));
400
+ ---
154
401
 
155
- // Start server
156
- const server = startServer(manifest, { port: 3000 });
402
+ ## Bundler
403
+
404
+ Client bundling with HMR.
405
+
406
+ ```typescript
407
+ import { buildClientBundle, createDevBundler } from "@mandujs/core/bundler";
157
408
 
158
- // Stop
159
- server.stop();
409
+ // Production build
410
+ const result = await buildClientBundle(manifest, {
411
+ outDir: ".mandu/client",
412
+ minify: true,
413
+ sourcemap: true
414
+ });
415
+
416
+ // Development with HMR
417
+ const devBundler = await createDevBundler(manifest, {
418
+ rootDir: process.cwd(),
419
+ isDev: true
420
+ });
160
421
  ```
161
422
 
162
- ## Report Module
423
+ ---
163
424
 
164
- Guard result report generation.
425
+ ## Transaction API
426
+
427
+ Atomic changes with rollback.
165
428
 
166
429
  ```typescript
167
- import { buildGuardReport } from "@mandujs/core";
430
+ import { beginChange, commitChange, rollbackChange } from "@mandujs/core";
431
+
432
+ // Start transaction
433
+ const { changeId, snapshotId } = await beginChange(rootDir, "Add user API");
434
+
435
+ // Make changes...
168
436
 
169
- const report = buildGuardReport(guardResult, lockPath);
170
- console.log(report); // Formatted text report
437
+ // Commit or rollback
438
+ await commitChange(rootDir);
439
+ // or
440
+ await rollbackChange(rootDir);
171
441
  ```
172
442
 
443
+ ---
444
+
173
445
  ## Types
174
446
 
175
447
  ```typescript
176
448
  import type {
449
+ // Spec
177
450
  RoutesManifest,
178
451
  RouteSpec,
179
- RouteKind,
180
- SpecLock,
181
- GuardResult,
182
- GuardViolation,
183
- GenerateResult,
184
- AutoCorrectResult,
452
+
453
+ // Guard
454
+ GuardPreset,
455
+ GuardConfig,
456
+ Violation,
457
+ ViolationReport,
458
+
459
+ // Router
460
+ ScanResult,
461
+ FSRouteConfig,
462
+
463
+ // Contract
464
+ ContractDefinition,
465
+ ContractHandlers,
466
+
467
+ // Filling
468
+ ManduContext,
185
469
  } from "@mandujs/core";
186
470
  ```
187
471
 
472
+ ---
473
+
188
474
  ## Requirements
189
475
 
190
476
  - Bun >= 1.0.0
@@ -194,6 +480,7 @@ import type {
194
480
  ## Related Packages
195
481
 
196
482
  - [@mandujs/cli](https://www.npmjs.com/package/@mandujs/cli) - CLI tool
483
+ - [@mandujs/mcp](https://www.npmjs.com/package/@mandujs/mcp) - MCP server for AI agents
197
484
 
198
485
  ## License
199
486
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.9.37",
3
+ "version": "0.9.39",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -43,8 +43,8 @@
43
43
  "bun": ">=1.0.0"
44
44
  },
45
45
  "peerDependencies": {
46
- "react": ">=18.0.0",
47
- "react-dom": ">=18.0.0",
46
+ "react": "^19.0.0",
47
+ "react-dom": "^19.0.0",
48
48
  "zod": ">=3.0.0"
49
49
  },
50
50
  "dependencies": {
@@ -34,7 +34,7 @@ export {
34
34
  formatPatch,
35
35
  printDoctorReport,
36
36
  generateJsonReport,
37
- generateMarkdownReport,
37
+ generateDoctorMarkdownReport,
38
38
  formatDoctorReport,
39
39
  type ReportFormat,
40
40
  } from "./reporter";
@@ -222,9 +222,9 @@ export function generateJsonReport(analysis: DoctorAnalysis): string {
222
222
  }
223
223
 
224
224
  /**
225
- * Generate a Markdown report
225
+ * Generate a Markdown report (Doctor Analysis)
226
226
  */
227
- export function generateMarkdownReport(analysis: DoctorAnalysis): string {
227
+ export function generateDoctorMarkdownReport(analysis: DoctorAnalysis): string {
228
228
  const lines: string[] = [];
229
229
 
230
230
  lines.push("# 🩺 Mandu Doctor Report");
@@ -328,7 +328,7 @@ export function formatDoctorReport(
328
328
  return generateJsonReport(analysis);
329
329
 
330
330
  case "markdown":
331
- return generateMarkdownReport(analysis);
331
+ return generateDoctorMarkdownReport(analysis);
332
332
 
333
333
  default:
334
334
  printDoctorReport(analysis);
@@ -169,7 +169,7 @@ export {
169
169
  loadStatistics,
170
170
  saveStatistics,
171
171
  addScanRecord,
172
- generateMarkdownReport,
172
+ generateGuardMarkdownReport,
173
173
  generateHTMLReport,
174
174
  type ScanRecord,
175
175
  type StatisticsStore,
@@ -360,9 +360,9 @@ export async function addScanRecord(
360
360
  // ═══════════════════════════════════════════════════════════════════════════
361
361
 
362
362
  /**
363
- * 마크다운 리포트 생성
363
+ * 마크다운 리포트 생성 (Guard Report)
364
364
  */
365
- export function generateMarkdownReport(
365
+ export function generateGuardMarkdownReport(
366
366
  report: ViolationReport,
367
367
  trend?: TrendAnalysis | null,
368
368
  layerStats?: LayerStatistics[]
@@ -499,7 +499,7 @@ export function generateHTMLReport(
499
499
  trend?: TrendAnalysis | null,
500
500
  layerStats?: LayerStatistics[]
501
501
  ): string {
502
- const markdown = generateMarkdownReport(report, trend, layerStats);
502
+ const markdown = generateGuardMarkdownReport(report, trend, layerStats);
503
503
 
504
504
  // 간단한 마크다운 → HTML 변환
505
505
  let html = markdown
@@ -84,6 +84,9 @@ export interface GuardConfig {
84
84
  /** FS Routes 통합 */
85
85
  fsRoutes?: FSRoutesGuardConfig;
86
86
 
87
+ /** 실시간 출력 형식 */
88
+ realtimeOutput?: "console" | "agent" | "json";
89
+
87
90
  /** 캐시 사용 여부 (기본값: true) */
88
91
  cache?: boolean;
89
92
 
@@ -320,6 +323,7 @@ export const DEFAULT_GUARD_CONFIG: Required<Omit<GuardConfig, "preset" | "layers
320
323
  deepNesting: "info",
321
324
  crossSliceDependency: "warn",
322
325
  },
326
+ realtimeOutput: "console",
323
327
  cache: true,
324
328
  incremental: true,
325
329
  debounceMs: 100,
@@ -20,7 +20,11 @@ import type {
20
20
  import { WATCH_EXTENSIONS, DEFAULT_GUARD_CONFIG } from "./types";
21
21
  import { analyzeFile, shouldAnalyzeFile } from "./analyzer";
22
22
  import { validateFileAnalysis, detectCircularDependencies } from "./validator";
23
- import { printRealtimeViolation } from "./reporter";
23
+ import {
24
+ printRealtimeViolation,
25
+ formatViolationForAgent,
26
+ formatViolationAsAgentJSON,
27
+ } from "./reporter";
24
28
  import { getPreset } from "./presets";
25
29
 
26
30
  // ═══════════════════════════════════════════════════════════════════════════
@@ -119,12 +123,24 @@ export function createGuardWatcher(options: WatcherOptions): GuardWatcher {
119
123
  // 콜백 호출
120
124
  onFileAnalyzed?.(analysis, violations);
121
125
 
126
+ const realtimeOutput = config.realtimeOutput ?? DEFAULT_GUARD_CONFIG.realtimeOutput;
127
+
122
128
  // 위반 처리
123
129
  for (const violation of violations) {
124
130
  onViolation?.(violation);
125
131
 
126
132
  if (!silent) {
127
- printRealtimeViolation(violation);
133
+ switch (realtimeOutput) {
134
+ case "agent":
135
+ console.log(formatViolationForAgent(violation, config.preset));
136
+ break;
137
+ case "json":
138
+ console.log(formatViolationAsAgentJSON(violation, config.preset));
139
+ break;
140
+ case "console":
141
+ default:
142
+ printRealtimeViolation(violation);
143
+ }
128
144
  }
129
145
  }
130
146
  } catch (error) {
@@ -239,7 +255,8 @@ export function createGuardWatcher(options: WatcherOptions): GuardWatcher {
239
255
  watcher.on("change", (path) => handleFileChange("change", resolve(rootDir, path)));
240
256
  watcher.on("unlink", (path) => handleFileChange("unlink", resolve(rootDir, path)));
241
257
 
242
- if (!silent) {
258
+ const realtimeOutput = config.realtimeOutput ?? DEFAULT_GUARD_CONFIG.realtimeOutput;
259
+ if (!silent && realtimeOutput === "console") {
243
260
  console.log(`[Guard] 🛡️ Watching ${Array.from(scanRoots).join(", ")} for architecture violations...`);
244
261
  }
245
262
  },