@gaonjs/cli 0.4.0 → 0.10.0
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/dist/commands/check.d.ts +50 -0
- package/dist/commands/check.js +286 -0
- package/dist/commands/console.d.ts +46 -0
- package/dist/commands/console.js +129 -0
- package/dist/commands/db.d.ts +3 -1
- package/dist/commands/db.js +8 -2
- package/dist/commands/g.d.ts +1 -1
- package/dist/commands/g.js +27 -3
- package/dist/commands/mcp.d.ts +15 -0
- package/dist/commands/mcp.js +78 -0
- package/dist/commands/new.d.ts +45 -0
- package/dist/commands/new.js +274 -0
- package/dist/commands/test.d.ts +11 -0
- package/dist/commands/test.js +119 -0
- package/dist/db/diff.js +5 -0
- package/dist/db/journal.d.ts +34 -0
- package/dist/db/journal.js +71 -0
- package/dist/db/migrate.d.ts +6 -1
- package/dist/db/migrate.js +120 -102
- package/dist/db/replay.d.ts +49 -0
- package/dist/db/replay.js +148 -0
- package/dist/db/status.d.ts +12 -0
- package/dist/db/status.js +61 -0
- package/dist/dev/index.d.ts +2 -0
- package/dist/dev/index.js +2 -0
- package/dist/dev/vite.d.ts +67 -0
- package/dist/dev/vite.js +126 -0
- package/dist/dev.d.ts +18 -0
- package/dist/dev.js +15 -0
- package/dist/doctor/agents-doc-index.d.ts +4 -0
- package/dist/doctor/agents-doc-index.js +80 -0
- package/dist/doctor/fixers/dependency-direction.d.ts +9 -0
- package/dist/doctor/fixers/dependency-direction.js +98 -0
- package/dist/doctor/fixers/index.d.ts +15 -0
- package/dist/doctor/fixers/index.js +66 -0
- package/dist/doctor/fixers/schema-filename.d.ts +14 -0
- package/dist/doctor/fixers/schema-filename.js +104 -0
- package/dist/doctor/fixers/types.d.ts +59 -0
- package/dist/doctor/fixers/types.js +15 -0
- package/dist/doctor/no-auto-import.d.ts +10 -0
- package/dist/doctor/no-auto-import.js +158 -0
- package/dist/doctor/schema-filename.d.ts +6 -0
- package/dist/doctor/schema-filename.js +81 -0
- package/dist/doctor/shared-composable-purity.d.ts +8 -0
- package/dist/doctor/shared-composable-purity.js +164 -0
- package/dist/doctor/types.d.ts +1 -1
- package/dist/doctor/types.js +6 -5
- package/dist/doctor.d.ts +51 -0
- package/dist/doctor.js +191 -7
- package/dist/generate.js +2 -2
- package/dist/hub.d.ts +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.js +154 -16
- package/dist/mcp/index.d.ts +7 -0
- package/dist/mcp/index.js +7 -0
- package/dist/mcp/server.d.ts +50 -0
- package/dist/mcp/server.js +102 -0
- package/dist/mcp/tools.d.ts +109 -0
- package/dist/mcp/tools.js +485 -0
- package/dist/scaffold/app.d.ts +5 -0
- package/dist/scaffold/app.js +172 -0
- package/dist/scaffold/controller.js +2 -2
- package/dist/scaffold/index.d.ts +2 -1
- package/dist/scaffold/index.js +2 -1
- package/dist/scaffold/job.d.ts +5 -0
- package/dist/scaffold/job.js +35 -0
- package/dist/scaffold/model.js +8 -8
- package/dist/templates/auth/auth.wiring.ts.tpl +1 -1
- package/dist/templates/auth/registration.controller.ts.tpl +1 -1
- package/dist/templates/auth/session.controller.ts.tpl +1 -1
- package/dist/templates/auth/user.model.ts.tpl +1 -1
- package/dist/templates/index.d.ts +23 -0
- package/dist/templates/index.js +66 -0
- package/dist/templates/index.ts +85 -0
- package/dist/templates/project/.env.example.tpl +18 -0
- package/dist/templates/project/.gitignore.tpl +24 -0
- package/dist/templates/project/.npmrc.tpl +4 -0
- package/dist/templates/project/AGENTS.md.tpl +210 -0
- package/dist/templates/project/CLAUDE.md.tpl +119 -0
- package/dist/templates/project/agents/async.md.tpl +218 -0
- package/dist/templates/project/agents/data.md.tpl +532 -0
- package/dist/templates/project/agents/frontend.md.tpl +201 -0
- package/dist/templates/project/agents/realtime.md.tpl +157 -0
- package/dist/templates/project/agents/security.md.tpl +92 -0
- package/dist/templates/project/agents/testing.md.tpl +101 -0
- package/dist/templates/project/agents/web.md.tpl +177 -0
- package/dist/templates/project/apps/web/channels/.gitkeep.tpl +1 -0
- package/dist/templates/project/apps/web/components/.gitkeep.tpl +1 -0
- package/dist/templates/project/apps/web/composables/useApiPing.ts.tpl +25 -0
- package/dist/templates/project/apps/web/controllers/home.ts.tpl +19 -0
- package/dist/templates/project/apps/web/index.html.tpl +18 -0
- package/dist/templates/project/apps/web/layouts/Default.vue.tpl +43 -0
- package/dist/templates/project/apps/web/main.ts.tpl +24 -0
- package/dist/templates/project/apps/web/pages/Home/Index.vue.tpl +36 -0
- package/dist/templates/project/apps/web/routes.ts.tpl +8 -0
- package/dist/templates/project/docker-compose.yaml.tpl +73 -0
- package/dist/templates/project/domain/events/.gitkeep.tpl +1 -0
- package/dist/templates/project/domain/jobs/.gitkeep.tpl +1 -0
- package/dist/templates/project/domain/listeners/.gitkeep.tpl +1 -0
- package/dist/templates/project/domain/mails/.gitkeep.tpl +1 -0
- package/dist/templates/project/domain/models/.gitkeep.tpl +1 -0
- package/dist/templates/project/domain/schema/.gitkeep.tpl +1 -0
- package/dist/templates/project/domain/services/.gitkeep.tpl +1 -0
- package/dist/templates/project/gaon.config.ts.tpl +27 -0
- package/dist/templates/project/package.json.tpl +30 -0
- package/dist/templates/project/pnpm-workspace.yaml.tpl +11 -0
- package/dist/templates/project/shared/components/.gitkeep.tpl +1 -0
- package/dist/templates/project/shared/composables/useDebounce.ts.tpl +21 -0
- package/dist/templates/project/tsconfig.json.tpl +25 -0
- package/dist/templates/project/vite.config.ts.tpl +23 -0
- package/dist/tsResolve.js +1 -1
- package/dist/work.d.ts +2 -2
- package/dist/work.js +3 -1
- package/package.json +13 -11
- package/dist/__fixtures__/db-minimal/domain/schema/widgets.d.ts +0 -12
- package/dist/__fixtures__/db-minimal/domain/schema/widgets.js +0 -7
- package/dist/__fixtures__/db-minimal/gaon.config.d.ts +0 -2
- package/dist/__fixtures__/db-minimal/gaon.config.js +0 -11
- package/dist/check.d.ts +0 -29
- package/dist/check.js +0 -92
package/dist/doctor.js
CHANGED
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @gaonjs/cli · `gaon doctor` — 정적 검사 (M9-E · CLI DX 완성)
|
|
2
|
+
* @gaonjs/cli · `gaon doctor` — 정적 검사 (M9-E · CLI DX 완성 · E-5 확장)
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 8 검사를 조립한다:
|
|
5
5
|
* 1) response-mixing (errata E-3 §C · 라이브)
|
|
6
6
|
* 2) n-plus-one (errata E-4 (e))
|
|
7
7
|
* 3) dependency-direction (CLAUDE.md §5 · 4 규칙)
|
|
8
8
|
* 4) connections (v0.15 §4.5)
|
|
9
9
|
* 5) migration-diff (최소 감지 · 상세는 M9-D)
|
|
10
|
+
* 6) shared-composable-purity (errata E-5 §2.2 · 결정 25)
|
|
11
|
+
* 7) no-auto-import (errata E-5 §2.4 · v0.15 §1.2)
|
|
12
|
+
* 8) schema-filename (§1.1 · 결정 38 · 스키마 파일명 camelCase)
|
|
10
13
|
*
|
|
11
14
|
* 각 검사는 순수 함수(cwd → RuleReport). 상위 runDoctorCommand 가 조립해
|
|
12
15
|
* DoctorResult 로 낸다. --json 은 자동화(CI)를 위해 반드시 파싱 가능한
|
|
@@ -15,25 +18,36 @@
|
|
|
15
18
|
* 하위 호환: 기존 응답 혼용 API(`inspectControllerSource`·`runDoctor`)는
|
|
16
19
|
* 그대로 export — 기존 테스트가 계속 동작한다.
|
|
17
20
|
*/
|
|
18
|
-
import {
|
|
21
|
+
import { rename, writeFile } from 'node:fs/promises';
|
|
22
|
+
import { existsSync } from 'node:fs';
|
|
23
|
+
import { join, resolve } from 'node:path';
|
|
19
24
|
import ts from 'typescript';
|
|
20
25
|
import { checkResponseMixing } from './doctor/response-mixing.js';
|
|
21
26
|
import { checkNPlusOne } from './doctor/n-plus-one.js';
|
|
22
27
|
import { checkDependencyDirection } from './doctor/dependency-direction.js';
|
|
23
28
|
import { checkConnections } from './doctor/connections.js';
|
|
24
29
|
import { checkMigrationDiff } from './doctor/migration-diff.js';
|
|
30
|
+
import { checkSharedComposablePurity } from './doctor/shared-composable-purity.js';
|
|
31
|
+
import { checkNoAutoImport } from './doctor/no-auto-import.js';
|
|
32
|
+
import { checkSchemaFilename } from './doctor/schema-filename.js';
|
|
33
|
+
import { checkAgentsDocIndex } from './doctor/agents-doc-index.js';
|
|
25
34
|
import { renderHuman, renderJson } from './doctor/reporter.js';
|
|
26
35
|
import { checkTypeScriptApi, detectProject, fatalNoProject, fatalTsApiMissing, } from './doctor/setup.js';
|
|
27
36
|
import { makeResult, } from './doctor/types.js';
|
|
37
|
+
import { FIXERS, FIXER_CAPABILITIES } from './doctor/fixers/index.js';
|
|
28
38
|
export { inspectControllerSource, checkResponseMixing } from './doctor/response-mixing.js';
|
|
29
39
|
export { inspectControllerForNPlusOne, checkNPlusOne } from './doctor/n-plus-one.js';
|
|
30
40
|
export { extractRelativeImports, checkDependencyDirection } from './doctor/dependency-direction.js';
|
|
31
41
|
export { extractConfigDbKeys, extractKeyUses, checkConnections } from './doctor/connections.js';
|
|
32
42
|
export { scanSchema, checkMigrationDiff } from './doctor/migration-diff.js';
|
|
43
|
+
export { inspectSharedComposable, checkSharedComposablePurity, } from './doctor/shared-composable-purity.js';
|
|
44
|
+
export { inspectConfigForAutoImport, inspectPackageJson, checkNoAutoImport, } from './doctor/no-auto-import.js';
|
|
45
|
+
export { expectedSchemaStem, extractTableName, checkSchemaFilename, } from './doctor/schema-filename.js';
|
|
46
|
+
export { extractAgentDocRefs, checkAgentsDocIndex } from './doctor/agents-doc-index.js';
|
|
33
47
|
export { renderHuman, renderJson } from './doctor/reporter.js';
|
|
34
48
|
export { checkTypeScriptApi, detectProject, fatalNoProject, fatalTsApiMissing, } from './doctor/setup.js';
|
|
35
49
|
/**
|
|
36
|
-
* 실행할 검사 이름. 지정 없음(undefined) =
|
|
50
|
+
* 실행할 검사 이름. 지정 없음(undefined) = 7개 모두.
|
|
37
51
|
*/
|
|
38
52
|
const ALL_RULES = [
|
|
39
53
|
'response-mixing',
|
|
@@ -41,6 +55,10 @@ const ALL_RULES = [
|
|
|
41
55
|
'dependency-direction',
|
|
42
56
|
'connections',
|
|
43
57
|
'migration-diff',
|
|
58
|
+
'shared-composable-purity',
|
|
59
|
+
'no-auto-import',
|
|
60
|
+
'schema-filename',
|
|
61
|
+
'agents-doc-index',
|
|
44
62
|
];
|
|
45
63
|
const CHECKERS = {
|
|
46
64
|
'response-mixing': checkResponseMixing,
|
|
@@ -48,6 +66,10 @@ const CHECKERS = {
|
|
|
48
66
|
'dependency-direction': checkDependencyDirection,
|
|
49
67
|
connections: checkConnections,
|
|
50
68
|
'migration-diff': checkMigrationDiff,
|
|
69
|
+
'shared-composable-purity': checkSharedComposablePurity,
|
|
70
|
+
'no-auto-import': checkNoAutoImport,
|
|
71
|
+
'schema-filename': checkSchemaFilename,
|
|
72
|
+
'agents-doc-index': checkAgentsDocIndex,
|
|
51
73
|
};
|
|
52
74
|
/**
|
|
53
75
|
* `gaon doctor` 진입점 (M9-E · 확장 · M9-E-Fix 하드닝).
|
|
@@ -120,10 +142,172 @@ export async function runDoctorCommand(opts = {}) {
|
|
|
120
142
|
});
|
|
121
143
|
}
|
|
122
144
|
}
|
|
123
|
-
const
|
|
124
|
-
|
|
145
|
+
const baseResult = makeResult(reports);
|
|
146
|
+
// --fix 가 없으면 종전 동작 그대로.
|
|
147
|
+
if (!opts.fix) {
|
|
148
|
+
const out = json ? renderJson(baseResult) : renderHuman(baseResult);
|
|
149
|
+
process.stdout.write(out + '\n');
|
|
150
|
+
return baseResult;
|
|
151
|
+
}
|
|
152
|
+
// --fix: fixer 를 계획하고, --yes 이면 실제 편집한다. reports 를 그대로
|
|
153
|
+
// 넘겨 규칙별 위반을 fixer 가 판단한다(passed 는 건너뜀).
|
|
154
|
+
const fixReport = await runDoctorFix(reports, root, opts.yes === true);
|
|
155
|
+
const resultWithFix = { ...baseResult, fix: fixReport };
|
|
156
|
+
const out = json
|
|
157
|
+
? JSON.stringify(resultWithFix)
|
|
158
|
+
: renderHuman(baseResult) + '\n\n' + renderFixHuman(fixReport);
|
|
125
159
|
process.stdout.write(out + '\n');
|
|
126
|
-
return
|
|
160
|
+
return resultWithFix;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* fixer 를 계획·(승인 시)실행한다. 파일 편집은 여기서만 일어난다 —
|
|
164
|
+
* fixer 자체는 순수 계산 함수.
|
|
165
|
+
*
|
|
166
|
+
* 안전:
|
|
167
|
+
* 1) apply=false(=--yes 없음) 면 dry-run — 파일을 만지지 않는다.
|
|
168
|
+
* 2) apply=true 면 원본을 `<파일>.bak-<타임스탬프>` 로 백업 후 편집.
|
|
169
|
+
* 3) 편집 중 예외가 나면 그 파일만 실패로 리포트하고 다음 파일을 계속.
|
|
170
|
+
*/
|
|
171
|
+
export async function runDoctorFix(reports, cwd, apply) {
|
|
172
|
+
const outcomes = [];
|
|
173
|
+
const manualRequired = [];
|
|
174
|
+
const ts = timestampSuffix(new Date());
|
|
175
|
+
for (const report of reports) {
|
|
176
|
+
const issues = report.issues.filter((i) => i.level !== 'passed');
|
|
177
|
+
if (issues.length === 0)
|
|
178
|
+
continue;
|
|
179
|
+
const fixer = FIXERS[report.rule];
|
|
180
|
+
if (!fixer) {
|
|
181
|
+
const cap = FIXER_CAPABILITIES.find((c) => c.rule === report.rule);
|
|
182
|
+
manualRequired.push({
|
|
183
|
+
rule: report.rule,
|
|
184
|
+
issueCount: issues.length,
|
|
185
|
+
note: cap?.note ?? '수동 수정 필요',
|
|
186
|
+
});
|
|
187
|
+
continue;
|
|
188
|
+
}
|
|
189
|
+
let plans;
|
|
190
|
+
try {
|
|
191
|
+
plans = await fixer(issues, cwd);
|
|
192
|
+
}
|
|
193
|
+
catch (err) {
|
|
194
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
195
|
+
outcomes.push({
|
|
196
|
+
rule: report.rule,
|
|
197
|
+
file: '(fixer error)',
|
|
198
|
+
summary: `fixer 실행 실패: ${msg}`,
|
|
199
|
+
applied: false,
|
|
200
|
+
});
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
203
|
+
for (const plan of plans) {
|
|
204
|
+
const label = plan.kind === 'rename' ? `${plan.file} → ${plan.to}` : plan.file;
|
|
205
|
+
if (!apply) {
|
|
206
|
+
outcomes.push({
|
|
207
|
+
rule: report.rule,
|
|
208
|
+
file: label,
|
|
209
|
+
summary: plan.summary,
|
|
210
|
+
applied: false,
|
|
211
|
+
});
|
|
212
|
+
continue;
|
|
213
|
+
}
|
|
214
|
+
try {
|
|
215
|
+
if (plan.kind === 'rename') {
|
|
216
|
+
// 이동 대상이 이미 있으면 데이터를 덮지 않고 건너뛴다(수동 확인).
|
|
217
|
+
if (existsSync(join(cwd, plan.to))) {
|
|
218
|
+
outcomes.push({
|
|
219
|
+
rule: report.rule,
|
|
220
|
+
file: label,
|
|
221
|
+
summary: `이동 대상이 이미 존재해 건너뜀(수동 확인 필요): ${plan.to}`,
|
|
222
|
+
applied: false,
|
|
223
|
+
});
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
// importer 참조를 먼저 갱신(각각 백업), 그다음 파일 이동(내용 불변).
|
|
227
|
+
const backups = [];
|
|
228
|
+
for (const ref of plan.refEdits) {
|
|
229
|
+
const bak = `${ref.file}.bak-${ts}`;
|
|
230
|
+
await writeFile(join(cwd, bak), ref.before, 'utf8');
|
|
231
|
+
await writeFile(join(cwd, ref.file), ref.after, 'utf8');
|
|
232
|
+
backups.push(bak);
|
|
233
|
+
}
|
|
234
|
+
await rename(join(cwd, plan.file), join(cwd, plan.to));
|
|
235
|
+
outcomes.push({
|
|
236
|
+
rule: report.rule,
|
|
237
|
+
file: label,
|
|
238
|
+
summary: plan.summary + (backups.length ? `\n ↳ importer 백업: ${backups.join(', ')}` : ''),
|
|
239
|
+
applied: true,
|
|
240
|
+
backup: backups[0],
|
|
241
|
+
});
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
// 내용 재작성 — 원본 백업 먼저.
|
|
245
|
+
const abs = join(cwd, plan.file);
|
|
246
|
+
const backupRel = `${plan.file}.bak-${ts}`;
|
|
247
|
+
await writeFile(join(cwd, backupRel), plan.before, 'utf8');
|
|
248
|
+
await writeFile(abs, plan.after, 'utf8');
|
|
249
|
+
outcomes.push({
|
|
250
|
+
rule: report.rule,
|
|
251
|
+
file: plan.file,
|
|
252
|
+
summary: plan.summary,
|
|
253
|
+
applied: true,
|
|
254
|
+
backup: backupRel,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
259
|
+
outcomes.push({
|
|
260
|
+
rule: report.rule,
|
|
261
|
+
file: label,
|
|
262
|
+
summary: `편집 실패(원본 유지): ${msg}`,
|
|
263
|
+
applied: false,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return { outcomes, manualRequired, applied: apply };
|
|
269
|
+
}
|
|
270
|
+
/** 사람 친화 fix 리포트. json 은 상위 renderJson 이 그대로 직렬화. */
|
|
271
|
+
export function renderFixHuman(report) {
|
|
272
|
+
const lines = [];
|
|
273
|
+
const mode = report.applied ? '적용' : 'dry-run(--yes 없음 · 계획만)';
|
|
274
|
+
lines.push(` gaon doctor --fix · ${mode}`);
|
|
275
|
+
if (report.outcomes.length === 0 && report.manualRequired.length === 0) {
|
|
276
|
+
lines.push(' (자동 정정할 대상이 없습니다.)');
|
|
277
|
+
return lines.join('\n');
|
|
278
|
+
}
|
|
279
|
+
for (const o of report.outcomes) {
|
|
280
|
+
const mark = o.applied ? '✓' : '·';
|
|
281
|
+
lines.push('');
|
|
282
|
+
lines.push(` ${mark} [${o.rule}] ${o.file}`);
|
|
283
|
+
for (const ln of o.summary.split('\n'))
|
|
284
|
+
lines.push(` ${ln}`);
|
|
285
|
+
if (o.backup)
|
|
286
|
+
lines.push(` ↳ 원본 백업: ${o.backup}`);
|
|
287
|
+
}
|
|
288
|
+
if (report.manualRequired.length > 0) {
|
|
289
|
+
lines.push('');
|
|
290
|
+
lines.push(' 수동 수정 필요:');
|
|
291
|
+
for (const m of report.manualRequired) {
|
|
292
|
+
lines.push(` · [${m.rule}] 위반 ${m.issueCount}건 · ${m.note}`);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
if (!report.applied && report.outcomes.some((o) => !o.applied)) {
|
|
296
|
+
lines.push('');
|
|
297
|
+
lines.push(' → 실제 편집은 --fix --yes 로 진행하세요(원본은 자동 백업).');
|
|
298
|
+
}
|
|
299
|
+
return lines.join('\n');
|
|
300
|
+
}
|
|
301
|
+
/** 파일명 안전 문자만으로 만든 로컬 타임스탬프(YYYYMMDD-HHMMSS). */
|
|
302
|
+
function timestampSuffix(d) {
|
|
303
|
+
const pad = (n, w = 2) => String(n).padStart(w, '0');
|
|
304
|
+
const y = d.getFullYear();
|
|
305
|
+
const M = pad(d.getMonth() + 1);
|
|
306
|
+
const D = pad(d.getDate());
|
|
307
|
+
const h = pad(d.getHours());
|
|
308
|
+
const m = pad(d.getMinutes());
|
|
309
|
+
const s = pad(d.getSeconds());
|
|
310
|
+
return `${y}${M}${D}-${h}${m}${s}`;
|
|
127
311
|
}
|
|
128
312
|
/**
|
|
129
313
|
* 하위 호환: 응답 혼용만 검사해 legacy shape 을 낸다.
|
package/dist/generate.js
CHANGED
|
@@ -26,8 +26,8 @@ function renderTemplate(name, app) {
|
|
|
26
26
|
}
|
|
27
27
|
/** 템플릿 파일 → 생성 경로 매핑(라우트 제외 — 라우트는 패치로 처리). */
|
|
28
28
|
const TEMPLATES = [
|
|
29
|
-
{ tpl: 'user.schema.ts.tpl', out: () => 'domain/schema/
|
|
30
|
-
{ tpl: 'user.model.ts.tpl', out: () => 'domain/models/
|
|
29
|
+
{ tpl: 'user.schema.ts.tpl', out: () => 'domain/schema/users.ts' },
|
|
30
|
+
{ tpl: 'user.model.ts.tpl', out: () => 'domain/models/User.ts' },
|
|
31
31
|
{ tpl: 'auth.wiring.ts.tpl', out: (a) => `apps/${a}/auth.ts` },
|
|
32
32
|
{ tpl: 'session.controller.ts.tpl', out: (a) => `apps/${a}/controllers/session.ts` },
|
|
33
33
|
{ tpl: 'registration.controller.ts.tpl', out: (a) => `apps/${a}/controllers/registration.ts` },
|
package/dist/hub.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export interface HubCommandOptions {
|
|
2
2
|
readonly json?: boolean;
|
|
3
|
-
/** NATS 접속지. 생략 시 GAON_NATS_URL,
|
|
3
|
+
/** NATS 접속지. 생략 시 NATS_URL(그다음 하위호환 GAON_NATS_URL), 기본(4222). */
|
|
4
4
|
readonly natsUrl?: string;
|
|
5
5
|
/** 리스 TTL(ms). 생략 시 env GAON_HUB_TTL_MS, 그다음 허브 기본(5000). */
|
|
6
6
|
readonly ttlMs?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -2,7 +2,10 @@ import { type DoctorRule } from "./doctor.js";
|
|
|
2
2
|
export { startDev, resolveDevLayout, type DevDeps, type DevLayout, type DevApp, type DevEvent, type DevHandle, } from "./dev.js";
|
|
3
3
|
export { runDevCommand, type DevCommandOptions } from "./commands/dev.js";
|
|
4
4
|
export { createDevConsole, findComposeFile, isDockerAvailable, inspectCompose, composeUp, composeDown, ensureInfra, startTscWatchers, killChild, startRestartWatcher, isRestartChange, resolveWatchRoots, type DevConsole, type DevConsoleOptions, type DevSource, type DevLevel, type ComposeStatus, type ComposeUpOptions, type EnsureInfraResult, type DockerLocateOptions, type TscWatcherOptions, type TscWatcherHandle, type RestartWatcherOptions, type RestartWatcherHandle, } from "./dev/index.js";
|
|
5
|
-
export {
|
|
5
|
+
export { runCheckCommand, type CheckCommandOptions, type CheckStep, type CheckStepStatus, type CheckStepResult, } from "./commands/check.js";
|
|
6
|
+
export { runNewCommand, type NewCommandOptions, type NewCommandResult } from "./commands/new.js";
|
|
7
|
+
export { runConsoleCommand, type ConsoleCommandOptions } from "./commands/console.js";
|
|
8
|
+
export { runTestCommand, type TestCommandOptions, type TestScope } from "./commands/test.js";
|
|
6
9
|
export { writeAuthScaffold, authScaffoldFiles, patchRoutes, runGenerateAuthCommand, type AuthScaffoldOptions, type ScaffoldFile, type ScaffoldResult, type GenerateAuthOptions, } from "./generate.js";
|
|
7
10
|
export { runGenerateCommand, planScaffold, parseGenerateArgs, type GenerateType, type GenerateOptions, type GenerateResult, } from "./commands/g.js";
|
|
8
11
|
export { runHubCommand, type HubCommandOptions } from "./hub.js";
|
|
@@ -12,7 +15,8 @@ export { runJobsCommand, type JobsCommandOptions } from "./jobs.js";
|
|
|
12
15
|
export { runDbSeedCommand, loadSeed, type DbSeedOptions, type DbSeedResult } from "./db.js";
|
|
13
16
|
export { runDbCommand, type DbSubcommand, type DbCommandOptions, } from "./commands/db.js";
|
|
14
17
|
export { runDbDiff, runDbMigrate, runDbReset, resolveDbTarget, type DbDiffOptions, type DbDiffResult, type DbMigrateOptions, type DbMigrateResult, type DbResetOptions, type DbResetResult, type ResolveDbOptions, type ResolvedDbTarget, } from "./db/index.js";
|
|
15
|
-
export { runDoctorCommand, runDoctor, inspectControllerSource, checkResponseMixing, checkNPlusOne, checkDependencyDirection, checkConnections, checkMigrationDiff, renderHuman, renderJson, type DoctorResult, type DoctorCheck, type DoctorLevel, type DoctorRule, type RuleReport, type DoctorIssue, type LegacyDoctorResult, type ActionUsage, type ResponseKind, type DoctorCommandOptions, } from "./doctor.js";
|
|
18
|
+
export { runDoctorCommand, runDoctor, runDoctorFix, renderFixHuman, inspectControllerSource, checkResponseMixing, checkNPlusOne, checkDependencyDirection, checkConnections, checkMigrationDiff, renderHuman, renderJson, type DoctorResult, type DoctorResultWithFix, type DoctorCheck, type DoctorLevel, type DoctorRule, type RuleReport, type DoctorIssue, type LegacyDoctorResult, type ActionUsage, type ResponseKind, type DoctorCommandOptions, type FixOutcome, type DoctorFixReport, } from "./doctor.js";
|
|
19
|
+
export { FIXERS, FIXER_CAPABILITIES, fixDependencyDirection, fixDomainToSharedTypeOnly, type Fixer, type FixerCapability, type FixerPlan, } from "./doctor/fixers/index.js";
|
|
16
20
|
export { loadDomain, type LoadedDomain } from "./domain.js";
|
|
17
21
|
export interface RoadmapReport {
|
|
18
22
|
readonly name: "gaon";
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
*/
|
|
13
13
|
import { MILESTONES, VERSION, HOMEPAGE } from "@gaonjs/core";
|
|
14
14
|
import { runDevCommand } from "./commands/dev.js";
|
|
15
|
-
import { runCheckCommand } from "./check.js";
|
|
15
|
+
import { runCheckCommand } from "./commands/check.js";
|
|
16
|
+
import { runNewCommand } from "./commands/new.js";
|
|
17
|
+
import { runConsoleCommand } from "./commands/console.js";
|
|
18
|
+
import { runTestCommand } from "./commands/test.js";
|
|
16
19
|
import { runGenerateAuthCommand } from "./generate.js";
|
|
17
20
|
import { runGenerateCommand } from "./commands/g.js";
|
|
18
21
|
import { runHubCommand } from "./hub.js";
|
|
@@ -21,10 +24,14 @@ import { runWorkCommand } from "./work.js";
|
|
|
21
24
|
import { runJobsCommand } from "./jobs.js";
|
|
22
25
|
import { runDbCommand } from "./commands/db.js";
|
|
23
26
|
import { runDoctorCommand } from "./doctor.js";
|
|
27
|
+
import { runMcpCommand } from "./commands/mcp.js";
|
|
24
28
|
export { startDev, resolveDevLayout, } from "./dev.js";
|
|
25
29
|
export { runDevCommand } from "./commands/dev.js";
|
|
26
30
|
export { createDevConsole, findComposeFile, isDockerAvailable, inspectCompose, composeUp, composeDown, ensureInfra, startTscWatchers, killChild, startRestartWatcher, isRestartChange, resolveWatchRoots, } from "./dev/index.js";
|
|
27
|
-
export {
|
|
31
|
+
export { runCheckCommand, } from "./commands/check.js";
|
|
32
|
+
export { runNewCommand } from "./commands/new.js";
|
|
33
|
+
export { runConsoleCommand } from "./commands/console.js";
|
|
34
|
+
export { runTestCommand } from "./commands/test.js";
|
|
28
35
|
export { writeAuthScaffold, authScaffoldFiles, patchRoutes, runGenerateAuthCommand, } from "./generate.js";
|
|
29
36
|
export { runGenerateCommand, planScaffold, parseGenerateArgs, } from "./commands/g.js";
|
|
30
37
|
export { runHubCommand } from "./hub.js";
|
|
@@ -34,7 +41,8 @@ export { runJobsCommand } from "./jobs.js";
|
|
|
34
41
|
export { runDbSeedCommand, loadSeed } from "./db.js";
|
|
35
42
|
export { runDbCommand, } from "./commands/db.js";
|
|
36
43
|
export { runDbDiff, runDbMigrate, runDbReset, resolveDbTarget, } from "./db/index.js";
|
|
37
|
-
export { runDoctorCommand, runDoctor, inspectControllerSource, checkResponseMixing, checkNPlusOne, checkDependencyDirection, checkConnections, checkMigrationDiff, renderHuman, renderJson, } from "./doctor.js";
|
|
44
|
+
export { runDoctorCommand, runDoctor, runDoctorFix, renderFixHuman, inspectControllerSource, checkResponseMixing, checkNPlusOne, checkDependencyDirection, checkConnections, checkMigrationDiff, renderHuman, renderJson, } from "./doctor.js";
|
|
45
|
+
export { FIXERS, FIXER_CAPABILITIES, fixDependencyDirection, fixDomainToSharedTypeOnly, } from "./doctor/fixers/index.js";
|
|
38
46
|
export { loadDomain } from "./domain.js";
|
|
39
47
|
/** `--json` 출력용 구조화 리포트. */
|
|
40
48
|
export function roadmapReport(version = VERSION) {
|
|
@@ -79,6 +87,7 @@ function renderHelp(version = VERSION) {
|
|
|
79
87
|
"",
|
|
80
88
|
" 사용법:",
|
|
81
89
|
" gaon 로드맵과 개발 상태를 출력",
|
|
90
|
+
" gaon new <name> 새 프로젝트 스캐폴드 (파일 → 설치 → git · --skip-install · --skip-git · --pm <이름>)",
|
|
82
91
|
" gaon dev 개발 스택 통합 (Docker · .gaon · serve · tsc/vue-tsc · 재시작 워처)",
|
|
83
92
|
" gaon dev --stop-docker Ctrl+C 시 Docker Compose 도 down",
|
|
84
93
|
" gaon dev --no-watch|--no-tsc|--no-vue-tsc|--no-docker 개별 debug 옵션",
|
|
@@ -86,23 +95,31 @@ function renderHelp(version = VERSION) {
|
|
|
86
95
|
" gaon dev --json 통합 콘솔을 JSON 라인으로 출력(자동화)",
|
|
87
96
|
" gaon serve 웹 서버 부팅 (gaon.config.ts 자동 배선 · Fastify listen)",
|
|
88
97
|
" gaon serve --port <n> --host <h> 리슨 포트·호스트 (config 값을 덮음)",
|
|
89
|
-
" gaon check
|
|
90
|
-
" gaon
|
|
98
|
+
" gaon check typecheck · vue-tsc · build 통합 검사 (--only <step> · --include-doctor)",
|
|
99
|
+
" gaon console 프로젝트 컨텍스트 REPL (--no-config)",
|
|
100
|
+
" gaon test 테스트 러너 (--scope unit|integration|all · -- vitest 인자)",
|
|
101
|
+
" gaon doctor 정적 검사 (7 검사 · 응답 혼용·N+1·의존·커넥션·마이그·컴포저블 순수·자동 import)",
|
|
91
102
|
" gaon doctor --json 자동화용 JSON 출력",
|
|
92
103
|
" gaon doctor --check=n-plus-one,connections 선택 검사만 실행",
|
|
104
|
+
" gaon doctor --fix 기계 정정 가능한 위반 계획(dry-run · v0.16 §7.5.3)",
|
|
105
|
+
" gaon doctor --fix --yes 실제 편집 적용(원본은 .bak-<타임스탬프> 로 자동 백업)",
|
|
93
106
|
" gaon g auth 인증 스캐폴드 생성 (회원가입·로그인·세션·보호 라우트)",
|
|
94
107
|
" gaon g controller <name> 컨트롤러 스캐폴드 (Rails 관례 · 페이지+JSON 액션)",
|
|
95
108
|
" gaon g model <Name> 모델 스캐폴드 (스키마+모델 · E-4 컬럼 예시)",
|
|
96
109
|
" gaon g page <Path/Name> Vue 페이지 (Inertia SPA · pageProps 브리지)",
|
|
97
110
|
" gaon g job <Name> 비동기 잡 (domain/jobs · later/in/at)",
|
|
111
|
+
" gaon g app <name> 앱 스캐폴드 (apps/<name>/ · routes·controllers·pages·layouts)",
|
|
98
112
|
" gaon g <type> --overwrite 기존 파일 덮어쓰기 · --app <이름> · --json",
|
|
113
|
+
" gaon mcp 내장 MCP 서버 · AI 도구 4종 (list_routes·get_schema·run_migration·run_tests · --http)",
|
|
99
114
|
" gaon hub 실시간 허브 프로세스 (프레즌스 권위·중계 · 리더 선출 HA)",
|
|
100
115
|
" gaon work 워커 프로세스 (잡·리스너·스케줄·아웃박스 · graceful drain)",
|
|
101
116
|
" gaon jobs list --failed DLQ(실패 잡) 목록",
|
|
102
117
|
" gaon jobs retry <id> DLQ 잡 재적재",
|
|
103
|
-
" gaon db diff 스키마 ↔ DB 차이
|
|
104
|
-
" gaon db migrate 스키마
|
|
105
|
-
" gaon db migrate
|
|
118
|
+
" gaon db diff 스키마 ↔ DB 차이 미리보기 (적용 X · --json · --db <키>)",
|
|
119
|
+
" gaon db migrate db/migrations/*.ts replay + 스키마 diff 적용 + 이력",
|
|
120
|
+
" gaon db migrate down 가장 최근 이력 한 건 롤백",
|
|
121
|
+
" gaon db migrate --dry-run 적용 없이 실행 예정 파일 + up SQL 만 출력",
|
|
122
|
+
" gaon db status 마이그레이션 파일 적용/대기 + 스키마 drift",
|
|
106
123
|
" gaon db reset --yes DROP ALL → 재마이그 → seed (--yes 필수 · production 거부)",
|
|
107
124
|
" gaon db seed domain/seed.ts 실행 (M8)",
|
|
108
125
|
" gaon --json 같은 정보를 JSON 으로 출력",
|
|
@@ -124,6 +141,8 @@ export function parseDoctorChecks(argv) {
|
|
|
124
141
|
"dependency-direction",
|
|
125
142
|
"connections",
|
|
126
143
|
"migration-diff",
|
|
144
|
+
"shared-composable-purity",
|
|
145
|
+
"no-auto-import",
|
|
127
146
|
];
|
|
128
147
|
const isKnown = (s) => known.includes(s);
|
|
129
148
|
const out = [];
|
|
@@ -179,9 +198,21 @@ export function runCli(argv, opts = {}) {
|
|
|
179
198
|
});
|
|
180
199
|
return;
|
|
181
200
|
}
|
|
182
|
-
// `gaon check` —
|
|
201
|
+
// `gaon check` — typecheck · vue-tsc · build (·doctor) 통합 검사(M9-G).
|
|
202
|
+
// package.json 스크립트 관례 재사용. --only <step> 로 단일 단계, --include-doctor
|
|
203
|
+
// 로 doctor 포함. 종료 코드로 결과 전달.
|
|
183
204
|
if (argv[0] === "check") {
|
|
184
|
-
|
|
205
|
+
const knownSteps = ["typecheck", "vue-tsc", "build", "doctor"];
|
|
206
|
+
const onlyIdx = argv.indexOf("--only");
|
|
207
|
+
const onlyRaw = onlyIdx >= 0 ? argv[onlyIdx + 1] : undefined;
|
|
208
|
+
const only = onlyRaw && knownSteps.includes(onlyRaw)
|
|
209
|
+
? onlyRaw
|
|
210
|
+
: undefined;
|
|
211
|
+
void runCheckCommand({
|
|
212
|
+
json: argv.includes("--json"),
|
|
213
|
+
only,
|
|
214
|
+
includeDoctor: argv.includes("--include-doctor"),
|
|
215
|
+
})
|
|
185
216
|
.then((code) => {
|
|
186
217
|
process.exitCode = code;
|
|
187
218
|
})
|
|
@@ -197,12 +228,18 @@ export function runCli(argv, opts = {}) {
|
|
|
197
228
|
// exit code (M9-E-Fix): fatal → 2(사용자 오류) / errors > 0 → 1 / 그 외 → 0.
|
|
198
229
|
if (argv[0] === "doctor") {
|
|
199
230
|
const checks = parseDoctorChecks(argv);
|
|
200
|
-
|
|
231
|
+
const fix = argv.includes("--fix");
|
|
232
|
+
const yes = argv.includes("--yes");
|
|
233
|
+
void runDoctorCommand({ json: argv.includes("--json"), checks, fix, yes })
|
|
201
234
|
.then((result) => {
|
|
202
235
|
if (result.fatal) {
|
|
203
236
|
process.exitCode = 2;
|
|
204
237
|
return;
|
|
205
238
|
}
|
|
239
|
+
// --fix --yes: 편집이 실제로 일어났고 나머지 위반이 없으면 0,
|
|
240
|
+
// 아직 수동 수정이 남아 있으면 1(자동화 정합).
|
|
241
|
+
// --fix (dry-run): errors > 0 이면 1(안 고침) · 계획만 있어도 1.
|
|
242
|
+
// 기본: errors > 0 이면 1.
|
|
206
243
|
process.exitCode = result.errors.length > 0 ? 1 : 0;
|
|
207
244
|
})
|
|
208
245
|
.catch((err) => {
|
|
@@ -212,6 +249,25 @@ export function runCli(argv, opts = {}) {
|
|
|
212
249
|
});
|
|
213
250
|
return;
|
|
214
251
|
}
|
|
252
|
+
// `gaon mcp` — 내장 MCP 서버(M10-B · 정본 §12 질문 8). AI 에이전트에게 4종
|
|
253
|
+
// 도구(list_routes·get_schema·run_migration·run_tests)를 노출한다. stdio(기본)
|
|
254
|
+
// 또는 --http 전송. 표시 버전은 파사드 버전을 주입한다.
|
|
255
|
+
if (argv[0] === "mcp") {
|
|
256
|
+
void runMcpCommand({
|
|
257
|
+
json: argv.includes("--json"),
|
|
258
|
+
transport: argv.includes("--http") ? "http" : "stdio",
|
|
259
|
+
version,
|
|
260
|
+
})
|
|
261
|
+
.then((code) => {
|
|
262
|
+
process.exitCode = code;
|
|
263
|
+
})
|
|
264
|
+
.catch((err) => {
|
|
265
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
266
|
+
process.stderr.write(` ✗ gaon mcp 실패: ${msg}\n`);
|
|
267
|
+
process.exitCode = 1;
|
|
268
|
+
});
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
215
271
|
// `gaon hub` — 실시간 허브 프로세스(§7 M6). 운영 프로세스 3종 중 하나.
|
|
216
272
|
// NATS·리더 선출 대기로 프로세스를 살려 두고, SIGINT/SIGTERM 에 그레이스풀 종료.
|
|
217
273
|
if (argv[0] === "hub") {
|
|
@@ -248,10 +304,10 @@ export function runCli(argv, opts = {}) {
|
|
|
248
304
|
// `gaon db <sub>` — diff · migrate · reset · seed (§7 M8/M9-D).
|
|
249
305
|
if (argv[0] === "db") {
|
|
250
306
|
const sub = argv[1];
|
|
251
|
-
const known = ["diff", "migrate", "reset", "seed"];
|
|
307
|
+
const known = ["diff", "migrate", "reset", "seed", "status"];
|
|
252
308
|
if (!sub || !known.includes(sub)) {
|
|
253
309
|
process.stderr.write(` ✗ 알 수 없는 db 서브커맨드: ${sub ?? "(없음)"}\n` +
|
|
254
|
-
` → 지원: gaon db diff | migrate | reset | seed\n` +
|
|
310
|
+
` → 지원: gaon db diff | migrate | reset | seed | status\n` +
|
|
255
311
|
` → 옵션: --json · --db <키> · --config <path> · --yes · --dry-run\n`);
|
|
256
312
|
process.exitCode = 1;
|
|
257
313
|
return;
|
|
@@ -264,6 +320,8 @@ export function runCli(argv, opts = {}) {
|
|
|
264
320
|
config: cfgIdx >= 0 ? argv[cfgIdx + 1] : undefined,
|
|
265
321
|
yes: argv.includes("--yes"),
|
|
266
322
|
dryRun: argv.includes("--dry-run"),
|
|
323
|
+
// `gaon db migrate down` — 위치 인자로 롤백 지시.
|
|
324
|
+
down: sub === "migrate" && argv[2] === "down",
|
|
267
325
|
};
|
|
268
326
|
void runDbCommand(sub, dbOpts)
|
|
269
327
|
.then((code) => {
|
|
@@ -287,7 +345,7 @@ export function runCli(argv, opts = {}) {
|
|
|
287
345
|
process.exitCode = code;
|
|
288
346
|
return;
|
|
289
347
|
}
|
|
290
|
-
const known = ["controller", "model", "page", "job"];
|
|
348
|
+
const known = ["controller", "model", "page", "job", "app"];
|
|
291
349
|
const type = argv[1];
|
|
292
350
|
if (type && known.includes(type)) {
|
|
293
351
|
const rest = argv.slice(2);
|
|
@@ -316,8 +374,13 @@ export function runCli(argv, opts = {}) {
|
|
|
316
374
|
name = a;
|
|
317
375
|
}
|
|
318
376
|
if (!name) {
|
|
377
|
+
const example = type === "page"
|
|
378
|
+
? "Posts/Index"
|
|
379
|
+
: type === "app"
|
|
380
|
+
? "admin"
|
|
381
|
+
: "Post";
|
|
319
382
|
process.stderr.write(` ✗ gaon g ${type}: 이름이 없습니다.\n` +
|
|
320
|
-
` → 예: gaon g ${type} ${
|
|
383
|
+
` → 예: gaon g ${type} ${example}\n`);
|
|
321
384
|
process.exitCode = 1;
|
|
322
385
|
return;
|
|
323
386
|
}
|
|
@@ -326,11 +389,86 @@ export function runCli(argv, opts = {}) {
|
|
|
326
389
|
return;
|
|
327
390
|
}
|
|
328
391
|
process.stderr.write(` ✗ 알 수 없는 제너레이터: ${argv[1] ?? "(없음)"}\n` +
|
|
329
|
-
` → 현재 지원: gaon g auth | controller | model | page | job\n` +
|
|
392
|
+
` → 현재 지원: gaon g auth | controller | model | page | job | app\n` +
|
|
330
393
|
` → 옵션: --app <이름> · --overwrite · --json\n`);
|
|
331
394
|
process.exitCode = 1;
|
|
332
395
|
return;
|
|
333
396
|
}
|
|
397
|
+
// `gaon new <name>` — 프로젝트 스캐폴드(M9-F). 파일 생성 → 의존성 설치 → git init.
|
|
398
|
+
if (argv[0] === "new") {
|
|
399
|
+
const rest = argv.slice(1);
|
|
400
|
+
let name;
|
|
401
|
+
for (const a of rest) {
|
|
402
|
+
if (!a.startsWith("--") && name === undefined)
|
|
403
|
+
name = a;
|
|
404
|
+
}
|
|
405
|
+
if (!name) {
|
|
406
|
+
process.stderr.write(" ✗ gaon new: 프로젝트 이름이 없습니다.\n → 예: gaon new demo\n");
|
|
407
|
+
process.exitCode = 1;
|
|
408
|
+
return;
|
|
409
|
+
}
|
|
410
|
+
const pmIdx = rest.indexOf("--pm");
|
|
411
|
+
const pmRaw = pmIdx >= 0 ? rest[pmIdx + 1] : undefined;
|
|
412
|
+
const packageManager = pmRaw === "pnpm" || pmRaw === "npm" || pmRaw === "yarn" ? pmRaw : undefined;
|
|
413
|
+
void runNewCommand(name, {
|
|
414
|
+
json: argv.includes("--json"),
|
|
415
|
+
skipInstall: argv.includes("--skip-install"),
|
|
416
|
+
skipGit: argv.includes("--skip-git"),
|
|
417
|
+
packageManager,
|
|
418
|
+
})
|
|
419
|
+
.then((code) => {
|
|
420
|
+
process.exitCode = code;
|
|
421
|
+
})
|
|
422
|
+
.catch((err) => {
|
|
423
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
424
|
+
process.stderr.write(` ✗ gaon new 실패: ${msg}\n`);
|
|
425
|
+
process.exitCode = 1;
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
// `gaon console` — 프로젝트 컨텍스트 REPL(M9-G). --no-config 로 배선 없이 기동.
|
|
430
|
+
if (argv[0] === "console") {
|
|
431
|
+
void runConsoleCommand({
|
|
432
|
+
json: argv.includes("--json"),
|
|
433
|
+
noConfig: argv.includes("--no-config"),
|
|
434
|
+
}).catch((err) => {
|
|
435
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
436
|
+
process.stderr.write(` ✗ gaon console 실패: ${msg}\n`);
|
|
437
|
+
process.exitCode = 1;
|
|
438
|
+
});
|
|
439
|
+
return;
|
|
440
|
+
}
|
|
441
|
+
// `gaon test [--scope unit|integration|all] [-- vitest 인자]` — 테스트 러너(M9-G).
|
|
442
|
+
if (argv[0] === "test") {
|
|
443
|
+
const rest = argv.slice(1);
|
|
444
|
+
const scopeIdx = rest.indexOf("--scope");
|
|
445
|
+
const scopeRaw = scopeIdx >= 0 ? rest[scopeIdx + 1] : undefined;
|
|
446
|
+
const scope = scopeRaw === "unit" || scopeRaw === "integration" || scopeRaw === "all"
|
|
447
|
+
? scopeRaw
|
|
448
|
+
: undefined;
|
|
449
|
+
const passthrough = [];
|
|
450
|
+
for (let i = 0; i < rest.length; i++) {
|
|
451
|
+
const a = rest[i];
|
|
452
|
+
if (a === "--scope") {
|
|
453
|
+
i++;
|
|
454
|
+
continue;
|
|
455
|
+
}
|
|
456
|
+
if (a === "--json")
|
|
457
|
+
continue;
|
|
458
|
+
if (a !== undefined)
|
|
459
|
+
passthrough.push(a);
|
|
460
|
+
}
|
|
461
|
+
void runTestCommand(passthrough, { json: argv.includes("--json"), scope })
|
|
462
|
+
.then((code) => {
|
|
463
|
+
process.exitCode = code;
|
|
464
|
+
})
|
|
465
|
+
.catch((err) => {
|
|
466
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
467
|
+
process.stderr.write(` ✗ gaon test 실패: ${msg}\n`);
|
|
468
|
+
process.exitCode = 1;
|
|
469
|
+
});
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
334
472
|
if (argv.includes("--help") || argv.includes("-h")) {
|
|
335
473
|
process.stdout.write(renderHelp(version) + "\n");
|
|
336
474
|
return;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @gaonjs/cli · MCP 모듈 public exports — v0.15 §13.5 M10.
|
|
3
|
+
*
|
|
4
|
+
* `gaon mcp` 명령이 소비하는 조립 API. tools.ts 의 TOOLS 카탈로그는 SSOT.
|
|
5
|
+
*/
|
|
6
|
+
export { createMcpServer, startMcpServer, SERVER_NAME, type McpServerOptions, type McpServerHandle, type McpServerVersion, } from './server.js';
|
|
7
|
+
export { TOOLS, findTool, listRoutesTool, getSchemaTool, runMigrationTool, runTestsTool, projectSummary, type McpToolSpec, type ToolResult, type ToolArgs, } from './tools.js';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @gaonjs/cli · MCP 모듈 public exports — v0.15 §13.5 M10.
|
|
3
|
+
*
|
|
4
|
+
* `gaon mcp` 명령이 소비하는 조립 API. tools.ts 의 TOOLS 카탈로그는 SSOT.
|
|
5
|
+
*/
|
|
6
|
+
export { createMcpServer, startMcpServer, SERVER_NAME, } from './server.js';
|
|
7
|
+
export { TOOLS, findTool, listRoutesTool, getSchemaTool, runMigrationTool, runTestsTool, projectSummary, } from './tools.js';
|