vibe-commander 0.1.0 → 0.2.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.
@@ -64,6 +64,12 @@ export function printError(result, json) {
64
64
  if (result.error.details) {
65
65
  console.error(`${DIM} 상세: ${result.error.details}${RESET}`);
66
66
  }
67
+ if (result.error.suggestions && result.error.suggestions.length > 0) {
68
+ console.error(`${YELLOW} 혹시 이것을 찾으셨나요?${RESET}`);
69
+ for (const s of result.error.suggestions) {
70
+ console.error(`${CYAN} → ${s}${RESET}`);
71
+ }
72
+ }
67
73
  }
68
74
  }
69
75
  /**
@@ -85,6 +91,7 @@ ${YELLOW}명령어:${RESET}
85
91
  ${CYAN}context${RESET} <unitId> 유닛 컨텍스트 조회 (읽기 전용)
86
92
  ${CYAN}validate${RESET} 설정 파일 검증
87
93
  ${CYAN}skill install${RESET} SKILL 파일 설치 (Cursor/Claude/Gemini)
94
+ ${CYAN}schema${RESET} <config|commands|types> 런타임 스키마 조회
88
95
 
89
96
  ${YELLOW}글로벌 옵션:${RESET}
90
97
  ${CYAN}--help${RESET}, ${CYAN}-h${RESET} 도움말 출력
@@ -95,6 +102,7 @@ ${YELLOW}글로벌 옵션:${RESET}
95
102
  ${YELLOW}set-unit 옵션:${RESET}
96
103
  ${CYAN}--request${RESET} <key1,key2> 커스텀 특별 요청사항 키 선택 (콤마 구분)
97
104
  ${CYAN}--no-prompt${RESET} 페어링 질문 인터랙티브 프롬프트 비활성화
105
+ ${CYAN}--dry-run${RESET} 커맨드 파일 수정 없이 미리보기만 출력
98
106
 
99
107
  ${YELLOW}update-commit 옵션:${RESET}
100
108
  ${CYAN}--mode${RESET} replace|append 값 교체 또는 추가 (기본: replace)
@@ -109,11 +117,17 @@ ${YELLOW}skill install 옵션:${RESET}
109
117
  ${CYAN}--gemini${RESET} Gemini만 설치 (.gemini/skills/)
110
118
  ${CYAN}--force${RESET} 기존 파일 덮어쓰기
111
119
 
120
+ ${YELLOW}schema 옵션:${RESET}
121
+ ${CYAN}--output${RESET} <path> 결과를 파일로 저장
122
+ ${CYAN}--json${RESET} JSON 형식으로 출력
123
+
112
124
  ${YELLOW}예시:${RESET}
113
125
  ${DIM}$ vc validate${RESET} ${DIM}# 설정 파일 종합 검증${RESET}
114
126
  ${DIM}$ vc init${RESET}
115
127
  ${DIM}$ vc init --from-existing${RESET} ${DIM}# 기존 파일 스캔으로 자동 설정${RESET}
116
128
  ${DIM}$ vc set-unit U-013[Mvp]${RESET}
129
+ ${DIM}$ vc set-unit U-013[Mvp] --dry-run${RESET} ${DIM}# 커맨드 파일 미수정, 미리보기만${RESET}
130
+ ${DIM}$ vc set-unit U-013[Mvp] --dry-run --json${RESET} ${DIM}# 미리보기를 JSON으로 출력${RESET}
117
131
  ${DIM}$ vc set-unit U-013[Mvp] --request mcp-nanobanana,mcp-codrag${RESET}
118
132
  ${DIM}$ vc update-commit${RESET} ${DIM}# HEAD SHA 자동 감지${RESET}
119
133
  ${DIM}$ vc update-commit 3${RESET} ${DIM}# 최근 3개 커밋 SHA${RESET}
@@ -123,6 +137,10 @@ ${YELLOW}예시:${RESET}
123
137
  ${DIM}$ vc skill install${RESET} ${DIM}# Cursor + Claude + Gemini 모두 설치${RESET}
124
138
  ${DIM}$ vc skill install --cursor${RESET} ${DIM}# Cursor만 설치${RESET}
125
139
  ${DIM}$ vc skill install --force${RESET} ${DIM}# 기존 파일 덮어쓰기${RESET}
140
+ ${DIM}$ vc schema config${RESET} ${DIM}# 설정 파일 JSON Schema 출력${RESET}
141
+ ${DIM}$ vc schema commands --json${RESET} ${DIM}# 커맨드 스키마 JSON 출력${RESET}
142
+ ${DIM}$ vc schema types${RESET} ${DIM}# 프로젝트 유닛 타입 출력${RESET}
143
+ ${DIM}$ vc schema config --output schema.json${RESET} ${DIM}# JSON Schema를 파일로 저장${RESET}
126
144
 
127
145
  ${YELLOW}파이프 모드 (에이전트/오케스트레이터 연동):${RESET}
128
146
  ${DIM}$ echo '{"unitId":"U-013[Mvp]"}' | vc set-unit --stdin --json${RESET}
@@ -14,6 +14,7 @@ export interface SetUnitArgs {
14
14
  unitId: string;
15
15
  requestKeys: string[];
16
16
  noPrompt: boolean;
17
+ dryRun: boolean;
17
18
  json: boolean;
18
19
  }
19
20
  export interface UpdateCommitArgs {
@@ -52,13 +53,19 @@ export interface SkillInstallArgs {
52
53
  force: boolean;
53
54
  json: boolean;
54
55
  }
56
+ export interface SchemaArgs {
57
+ command: 'schema';
58
+ subcommand?: 'config' | 'commands' | 'types';
59
+ output?: string;
60
+ json: boolean;
61
+ }
55
62
  export interface HelpArgs {
56
63
  command: 'help';
57
64
  }
58
65
  export interface VersionArgs {
59
66
  command: 'version';
60
67
  }
61
- export type ParsedArgs = SetUnitArgs | UpdateCommitArgs | ListUnitsArgs | ContextArgs | ValidateArgs | InitArgs | SkillInstallArgs | HelpArgs | VersionArgs;
68
+ export type ParsedArgs = SetUnitArgs | UpdateCommitArgs | ListUnitsArgs | ContextArgs | ValidateArgs | InitArgs | SkillInstallArgs | SchemaArgs | HelpArgs | VersionArgs;
62
69
  export type CommandHandler = (args: ParsedArgs, projectRoot: string) => Promise<ToolResult<unknown>> | ToolResult<unknown>;
63
70
  /** 커맨드별 커스텀 출력 포맷터 (list-units, context 등에서 사용) */
64
71
  export type CommandFormatter = (result: ToolResult<unknown>, json: boolean) => void;
@@ -50,6 +50,8 @@ export function parseArgs(argv) {
50
50
  }
51
51
  case 'skill':
52
52
  return parseSkillCommand(rest, json);
53
+ case 'schema':
54
+ return parseSchemaCommand(rest, json);
53
55
  case 'help':
54
56
  case '--help':
55
57
  case '-h':
@@ -59,7 +61,7 @@ export function parseArgs(argv) {
59
61
  case '-v':
60
62
  return { success: true, data: { command: 'version' } };
61
63
  default:
62
- return fail('UNKNOWN_COMMAND', `알 수 없는 명령어: ${subcommand}`, `사용 가능한 명령어: init, set-unit, update-commit, list-units, context, validate, skill`);
64
+ return fail('UNKNOWN_COMMAND', `알 수 없는 명령어: ${subcommand}`, `사용 가능한 명령어: init, set-unit, update-commit, list-units, context, validate, skill, schema`);
63
65
  }
64
66
  }
65
67
  /**
@@ -80,7 +82,11 @@ function parseSetUnit(args, json) {
80
82
  .filter(Boolean)
81
83
  : [];
82
84
  const noPrompt = hasFlag(args, '--no-prompt');
83
- return { success: true, data: { command: 'set-unit', unitId, requestKeys, noPrompt, json } };
85
+ const dryRun = hasFlag(args, '--dry-run');
86
+ return {
87
+ success: true,
88
+ data: { command: 'set-unit', unitId, requestKeys, noPrompt, dryRun, json },
89
+ };
84
90
  }
85
91
  /**
86
92
  * update-commit 서브커맨드의 인자를 파싱한다
@@ -170,6 +176,29 @@ function parseSkillCommand(args, json) {
170
176
  ];
171
177
  return { success: true, data: { command: 'skill-install', targets, force, json } };
172
178
  }
179
+ /**
180
+ * schema 그룹 커맨드의 서브커맨드를 파싱한다
181
+ *
182
+ * 지원: schema [config|commands|types] [--output <path>] [--json]
183
+ * 서브커맨드 미지정 시 도움말 표시를 위해 subcommand 없이 반환
184
+ */
185
+ function parseSchemaCommand(args, json) {
186
+ const sub = getPositionalArg(args);
187
+ const output = getOptionValue(args, '--output');
188
+ if (!sub) {
189
+ return {
190
+ success: true,
191
+ data: { command: 'schema', json, ...(output !== undefined && { output }) },
192
+ };
193
+ }
194
+ if (sub !== 'config' && sub !== 'commands' && sub !== 'types') {
195
+ return fail('UNKNOWN_SUBCOMMAND', `알 수 없는 schema 서브커맨드: ${sub}`, '사용 가능한 서브커맨드: config, commands, types');
196
+ }
197
+ return {
198
+ success: true,
199
+ data: { command: 'schema', subcommand: sub, json, ...(output !== undefined && { output }) },
200
+ };
201
+ }
173
202
  // ── 유틸리티 함수 ──
174
203
  /**
175
204
  * args에 특정 플래그가 존재하는지 확인
@@ -214,7 +243,7 @@ function getOptionValue(args, option) {
214
243
  * 값을 갖는 옵션인지 판별
215
244
  */
216
245
  function isValueOption(arg) {
217
- return ['--mode', '--section', '--phase', '--request'].includes(arg);
246
+ return ['--mode', '--section', '--phase', '--request', '--output'].includes(arg);
218
247
  }
219
248
  // ── stdin → ParsedArgs 변환 ──
220
249
  /**
@@ -238,7 +267,8 @@ export function parseStdinArgs(subcommand, data, json, rawArgs) {
238
267
  const requestKeys = Array.isArray(data.requestKeys)
239
268
  ? data.requestKeys.filter((k) => typeof k === 'string')
240
269
  : [];
241
- return ok({ command: 'set-unit', unitId, requestKeys, noPrompt: true, json });
270
+ const dryRun = rawArgs?.includes('--dry-run') ?? false;
271
+ return ok({ command: 'set-unit', unitId, requestKeys, noPrompt: true, dryRun, json });
242
272
  }
243
273
  case 'update-commit': {
244
274
  const modeValue = typeof data.mode === 'string' ? data.mode : undefined;
@@ -298,8 +328,21 @@ export function parseStdinArgs(subcommand, data, json, rawArgs) {
298
328
  json,
299
329
  });
300
330
  }
331
+ case 'schema': {
332
+ const sub = typeof data.subcommand === 'string' ? data.subcommand : undefined;
333
+ if (sub && sub !== 'config' && sub !== 'commands' && sub !== 'types') {
334
+ return fail('UNKNOWN_SUBCOMMAND', `알 수 없는 schema 서브커맨드: ${sub}`, '사용 가능한 서브커맨드: config, commands, types');
335
+ }
336
+ const output = typeof data.output === 'string' ? data.output : undefined;
337
+ return ok({
338
+ command: 'schema',
339
+ ...(sub !== undefined && { subcommand: sub }),
340
+ json,
341
+ ...(output !== undefined && { output }),
342
+ });
343
+ }
301
344
  default:
302
- return fail('UNKNOWN_COMMAND', `알 수 없는 명령어: ${subcommand}`, '사용 가능한 명령어: init, set-unit, update-commit, list-units, context, validate, skill');
345
+ return fail('UNKNOWN_COMMAND', `알 수 없는 명령어: ${subcommand}`, '사용 가능한 명령어: init, set-unit, update-commit, list-units, context, validate, skill, schema');
303
346
  }
304
347
  }
305
348
  // ── 메인 라우터 ──
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * PRD §5 설정 파일 구조를 Zod 4 스키마로 모델링.
5
5
  * 각 서브 스키마는 독립적으로 정의하여 재사용 및 테스트 용이성 확보.
6
- * 페어링 질문 Q2(Option B) 결정에 따라 snapmit 스타일 기본값 제공.
6
+ * 범용 기본값을 제공하여 다양한 프로젝트에서 즉시 사용 가능.
7
7
  *
8
8
  * @module
9
9
  */
@@ -202,4 +202,13 @@ export type InferredCommandSectionsConfig = z.infer<typeof commandSectionsSchema
202
202
  export type InferredProjectConfig = z.infer<typeof projectConfigSchema>;
203
203
  /** 설정 파일명 */
204
204
  export declare const CONFIG_FILENAME = "vibe-commander.config.json";
205
+ /**
206
+ * 프로젝트 설정 스키마를 JSON Schema (Draft 2020-12) 형식으로 반환한다
207
+ *
208
+ * Zod 4의 z.toJSONSchema() 내장 기능을 활용하여 변환.
209
+ * 에이전트가 설정 파일 구조를 런타임에 조회할 수 있도록 한다.
210
+ *
211
+ * @returns JSON Schema 호환 객체
212
+ */
213
+ export declare function getConfigJsonSchema(): Record<string, unknown>;
205
214
  //# sourceMappingURL=schema.d.ts.map
@@ -3,7 +3,7 @@
3
3
  *
4
4
  * PRD §5 설정 파일 구조를 Zod 4 스키마로 모델링.
5
5
  * 각 서브 스키마는 독립적으로 정의하여 재사용 및 테스트 용이성 확보.
6
- * 페어링 질문 Q2(Option B) 결정에 따라 snapmit 스타일 기본값 제공.
6
+ * 범용 기본값을 제공하여 다양한 프로젝트에서 즉시 사용 가능.
7
7
  *
8
8
  * @module
9
9
  */
@@ -12,14 +12,14 @@ import { z } from 'zod';
12
12
  /** 기본 경로 설정 스키마 */
13
13
  export const pathsSchema = z.object({
14
14
  /** 커맨드 파일 경로 (프로젝트 루트 기준) */
15
- commands: z.string().default('vibe/commands.md'),
15
+ commands: z.string().default('commands.md'),
16
16
  /** 로드맵 파일 경로. null이면 list-units 비활성화 */
17
- roadmap: z.string().nullable().default('vibe/roadmap.md'),
17
+ roadmap: z.string().nullable().default('roadmap.md'),
18
18
  /** 역할별 문서 루트 디렉토리 (키: 역할명, 값: 상대 경로) */
19
19
  docRoots: z.record(z.string(), z.string()).default({
20
- plan: 'vibe/unit-plans',
21
- result: 'vibe/unit-results',
22
- runbook: 'vibe/unit-runbooks',
20
+ plan: 'unit-plans',
21
+ result: 'unit-results',
22
+ runbook: 'unit-runbooks',
23
23
  }),
24
24
  /** 리팩토링 서브유닛 디렉토리. null이면 서브유닛 미사용 */
25
25
  refactorSubDir: z.string().nullable().default(null),
@@ -183,4 +183,15 @@ export const projectConfigSchema = z.object({
183
183
  });
184
184
  /** 설정 파일명 */
185
185
  export const CONFIG_FILENAME = 'vibe-commander.config.json';
186
+ /**
187
+ * 프로젝트 설정 스키마를 JSON Schema (Draft 2020-12) 형식으로 반환한다
188
+ *
189
+ * Zod 4의 z.toJSONSchema() 내장 기능을 활용하여 변환.
190
+ * 에이전트가 설정 파일 구조를 런타임에 조회할 수 있도록 한다.
191
+ *
192
+ * @returns JSON Schema 호환 객체
193
+ */
194
+ export function getConfigJsonSchema() {
195
+ return z.toJSONSchema(projectConfigSchema);
196
+ }
186
197
  //# sourceMappingURL=schema.js.map
@@ -12,4 +12,6 @@ export { updateSection, updateField } from './section-updater.js';
12
12
  export type { UpdateResult, MarkerOptions } from './section-updater.js';
13
13
  export { createBeginMarker, createEndMarker, findMarkerRange, isValidSectionKey, wrapWithMarkers, stripMarkers, } from './marker-utils.js';
14
14
  export type { MarkerRange } from './marker-utils.js';
15
+ export { COMMAND_REGISTRY, SCHEMA_SUBCOMMANDS, renderCommandsSchema, renderTypesSchema, } from './schema-renderer.js';
16
+ export type { ArgSchema, FlagSchema, CommandSchema, TypeSchemaEntry, SchemaSubcommandInfo, SchemaResult, } from './schema-renderer.js';
15
17
  //# sourceMappingURL=index.d.ts.map
@@ -15,4 +15,6 @@ export { renderSection } from './section-renderer.js';
15
15
  export { updateSection, updateField } from './section-updater.js';
16
16
  // 마커 유틸리티
17
17
  export { createBeginMarker, createEndMarker, findMarkerRange, isValidSectionKey, wrapWithMarkers, stripMarkers, } from './marker-utils.js';
18
+ // 스키마 렌더러
19
+ export { COMMAND_REGISTRY, SCHEMA_SUBCOMMANDS, renderCommandsSchema, renderTypesSchema, } from './schema-renderer.js';
18
20
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,92 @@
1
+ /**
2
+ * 스키마 렌더러 — Layer 1 (Core)
3
+ *
4
+ * CLI 커맨드 메타데이터와 유닛 타입 정보를 구조화된 형식으로 렌더링한다.
5
+ * 에이전트가 런타임에 CLI 스키마를 조회하여 문서 없이도 CLI를 활용할 수 있게 지원.
6
+ * 순수 함수만 포함 — I/O 없음.
7
+ *
8
+ * @module
9
+ */
10
+ import type { UnitTypeConfig } from '../../types/index.js';
11
+ /** CLI 커맨드 인자 스키마 */
12
+ export interface ArgSchema {
13
+ name: string;
14
+ required: boolean;
15
+ description: string;
16
+ }
17
+ /** CLI 커맨드 플래그 스키마 */
18
+ export interface FlagSchema {
19
+ name: string;
20
+ alias?: string;
21
+ type: 'boolean' | 'string';
22
+ description: string;
23
+ }
24
+ /** CLI 커맨드 스키마 */
25
+ export interface CommandSchema {
26
+ name: string;
27
+ description: string;
28
+ args: ArgSchema[];
29
+ flags: FlagSchema[];
30
+ }
31
+ /** 유닛 타입 스키마 출력 항목 */
32
+ export interface TypeSchemaEntry {
33
+ key: string;
34
+ displayName?: string;
35
+ idPattern: string;
36
+ planDir: string;
37
+ commandSection: string;
38
+ collectDeps: boolean;
39
+ headerTemplate: string;
40
+ }
41
+ /** schema 서브커맨드 정보 */
42
+ export interface SchemaSubcommandInfo {
43
+ name: string;
44
+ description: string;
45
+ }
46
+ /** schema 커맨드 결과 (discriminated union) */
47
+ export type SchemaResult = {
48
+ subcommand: 'help';
49
+ availableSubcommands: SchemaSubcommandInfo[];
50
+ usage: string;
51
+ } | {
52
+ subcommand: 'config';
53
+ schema: Record<string, unknown>;
54
+ outputPath?: string;
55
+ } | {
56
+ subcommand: 'commands';
57
+ globalFlags: FlagSchema[];
58
+ commands: CommandSchema[];
59
+ outputPath?: string;
60
+ } | {
61
+ subcommand: 'types';
62
+ types: TypeSchemaEntry[];
63
+ outputPath?: string;
64
+ };
65
+ /**
66
+ * 모든 CLI 커맨드의 메타데이터 레지스트리.
67
+ * 새 커맨드 추가 시 이 배열도 함께 업데이트해야 한다.
68
+ */
69
+ export declare const COMMAND_REGISTRY: CommandSchema[];
70
+ /** schema 서브커맨드 도움말 정보 */
71
+ export declare const SCHEMA_SUBCOMMANDS: SchemaSubcommandInfo[];
72
+ /**
73
+ * 커맨드 메타데이터를 구조화된 형태로 렌더링한다
74
+ *
75
+ * 글로벌 플래그와 커맨드별 메타데이터를 결합하여 반환.
76
+ * 에이전트가 사용 가능한 모든 커맨드와 옵션을 파악할 수 있도록 한다.
77
+ */
78
+ export declare function renderCommandsSchema(): {
79
+ globalFlags: FlagSchema[];
80
+ commands: CommandSchema[];
81
+ };
82
+ /**
83
+ * 유닛 타입 정보를 구조화된 형태로 렌더링한다
84
+ *
85
+ * ProjectConfig의 unitTypes 배열을 TypeSchemaEntry로 변환.
86
+ * 에이전트가 유효한 유닛 ID 패턴과 프로젝트 구조를 파악할 수 있도록 한다.
87
+ *
88
+ * @param unitTypes - 프로젝트 설정의 unitTypes 배열
89
+ * @returns 구조화된 타입 스키마 배열
90
+ */
91
+ export declare function renderTypesSchema(unitTypes: UnitTypeConfig[]): TypeSchemaEntry[];
92
+ //# sourceMappingURL=schema-renderer.d.ts.map
@@ -0,0 +1,162 @@
1
+ /**
2
+ * 스키마 렌더러 — Layer 1 (Core)
3
+ *
4
+ * CLI 커맨드 메타데이터와 유닛 타입 정보를 구조화된 형식으로 렌더링한다.
5
+ * 에이전트가 런타임에 CLI 스키마를 조회하여 문서 없이도 CLI를 활용할 수 있게 지원.
6
+ * 순수 함수만 포함 — I/O 없음.
7
+ *
8
+ * @module
9
+ */
10
+ // ── 커맨드 레지스트리 (정적 메타데이터) ──
11
+ const GLOBAL_FLAGS = [
12
+ { name: '--help', alias: '-h', type: 'boolean', description: '도움말 출력' },
13
+ { name: '--version', alias: '-v', type: 'boolean', description: '버전 출력' },
14
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력 (ToolResult 구조)' },
15
+ { name: '--stdin', type: 'boolean', description: 'stdin에서 JSON 입력 읽기' },
16
+ ];
17
+ /**
18
+ * 모든 CLI 커맨드의 메타데이터 레지스트리.
19
+ * 새 커맨드 추가 시 이 배열도 함께 업데이트해야 한다.
20
+ */
21
+ export const COMMAND_REGISTRY = [
22
+ {
23
+ name: 'init',
24
+ description: '프로젝트 초기화 (설정 파일 생성)',
25
+ args: [],
26
+ flags: [
27
+ {
28
+ name: '--from-existing',
29
+ type: 'boolean',
30
+ description: '기존 파일 스캔으로 ID 패턴/디렉토리 자동 감지',
31
+ },
32
+ { name: '--force', type: 'boolean', description: '기존 설정 파일 덮어쓰기' },
33
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
34
+ { name: '--stdin', type: 'boolean', description: 'stdin에서 JSON 입력 읽기' },
35
+ ],
36
+ },
37
+ {
38
+ name: 'set-unit',
39
+ description: '현재 작업 유닛 설정 (커맨드 파일 섹션 자동 구성)',
40
+ args: [{ name: 'unitId', required: true, description: '설정할 유닛 ID (예: U-001[Mvp])' }],
41
+ flags: [
42
+ {
43
+ name: '--request',
44
+ type: 'string',
45
+ description: '커스텀 특별 요청사항 키 선택 (콤마 구분)',
46
+ },
47
+ {
48
+ name: '--no-prompt',
49
+ type: 'boolean',
50
+ description: '페어링 질문 인터랙티브 프롬프트 비활성화',
51
+ },
52
+ { name: '--dry-run', type: 'boolean', description: '커맨드 파일 수정 없이 미리보기만 출력' },
53
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
54
+ { name: '--stdin', type: 'boolean', description: 'stdin에서 JSON 입력 읽기' },
55
+ ],
56
+ },
57
+ {
58
+ name: 'update-commit',
59
+ description: '커밋 SHA 업데이트 (인자 없음=HEAD, 숫자=최근 N개)',
60
+ args: [
61
+ { name: 'sha|N', required: false, description: 'SHA 해시 또는 최근 커밋 수 (생략 시 HEAD)' },
62
+ ],
63
+ flags: [
64
+ {
65
+ name: '--mode',
66
+ type: 'string',
67
+ description: '값 교체(replace) 또는 추가(append) (기본: replace)',
68
+ },
69
+ { name: '--section', type: 'string', description: '대상 섹션 지정 (미지정 시 자동 감지)' },
70
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
71
+ { name: '--stdin', type: 'boolean', description: 'stdin에서 JSON 입력 읽기' },
72
+ ],
73
+ },
74
+ {
75
+ name: 'list-units',
76
+ description: '미완료 유닛 목록 조회',
77
+ args: [],
78
+ flags: [
79
+ { name: '--phase', type: 'string', description: '특정 Phase로 필터링 (예: Mvp, Mmp)' },
80
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
81
+ { name: '--stdin', type: 'boolean', description: 'stdin에서 JSON 입력 읽기' },
82
+ ],
83
+ },
84
+ {
85
+ name: 'context',
86
+ description: '유닛 컨텍스트 조회 (읽기 전용)',
87
+ args: [{ name: 'unitId', required: true, description: '조회할 유닛 ID (예: U-001[Mvp])' }],
88
+ flags: [
89
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
90
+ { name: '--stdin', type: 'boolean', description: 'stdin에서 JSON 입력 읽기' },
91
+ ],
92
+ },
93
+ {
94
+ name: 'validate',
95
+ description: '설정 파일 종합 검증 (스키마/경로/정규식/패턴충돌)',
96
+ args: [],
97
+ flags: [{ name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' }],
98
+ },
99
+ {
100
+ name: 'skill install',
101
+ description: 'SKILL 파일 설치 (Cursor/Claude Code/Gemini)',
102
+ args: [],
103
+ flags: [
104
+ { name: '--cursor', type: 'boolean', description: 'Cursor만 설치 (.cursor/skills/)' },
105
+ { name: '--claude', type: 'boolean', description: 'Claude Code만 설치 (.claude/skills/)' },
106
+ { name: '--gemini', type: 'boolean', description: 'Gemini만 설치 (.gemini/skills/)' },
107
+ { name: '--force', type: 'boolean', description: '기존 파일 덮어쓰기' },
108
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
109
+ ],
110
+ },
111
+ {
112
+ name: 'schema',
113
+ description: '런타임 스키마 조회 (config/commands/types)',
114
+ args: [
115
+ { name: 'subcommand', required: false, description: '조회 대상 (config, commands, types)' },
116
+ ],
117
+ flags: [
118
+ { name: '--output', type: 'string', description: '결과를 파일로 저장할 경로' },
119
+ { name: '--json', type: 'boolean', description: 'JSON 형식으로 출력' },
120
+ ],
121
+ },
122
+ ];
123
+ /** schema 서브커맨드 도움말 정보 */
124
+ export const SCHEMA_SUBCOMMANDS = [
125
+ { name: 'config', description: '설정 파일(vibe-commander.config.json) JSON Schema 출력' },
126
+ { name: 'commands', description: '사용 가능한 커맨드 목록 + 파라미터 + 플래그 출력' },
127
+ { name: 'types', description: '현재 프로젝트의 유닛 유형 목록 + ID 패턴 출력' },
128
+ ];
129
+ // ── 공개 API ──
130
+ /**
131
+ * 커맨드 메타데이터를 구조화된 형태로 렌더링한다
132
+ *
133
+ * 글로벌 플래그와 커맨드별 메타데이터를 결합하여 반환.
134
+ * 에이전트가 사용 가능한 모든 커맨드와 옵션을 파악할 수 있도록 한다.
135
+ */
136
+ export function renderCommandsSchema() {
137
+ return {
138
+ globalFlags: GLOBAL_FLAGS,
139
+ commands: COMMAND_REGISTRY,
140
+ };
141
+ }
142
+ /**
143
+ * 유닛 타입 정보를 구조화된 형태로 렌더링한다
144
+ *
145
+ * ProjectConfig의 unitTypes 배열을 TypeSchemaEntry로 변환.
146
+ * 에이전트가 유효한 유닛 ID 패턴과 프로젝트 구조를 파악할 수 있도록 한다.
147
+ *
148
+ * @param unitTypes - 프로젝트 설정의 unitTypes 배열
149
+ * @returns 구조화된 타입 스키마 배열
150
+ */
151
+ export function renderTypesSchema(unitTypes) {
152
+ return unitTypes.map((ut) => ({
153
+ key: ut.key,
154
+ ...(ut.displayName !== undefined && { displayName: ut.displayName }),
155
+ idPattern: ut.idPattern,
156
+ planDir: ut.planDir,
157
+ commandSection: ut.commandSection,
158
+ collectDeps: ut.collectDeps,
159
+ headerTemplate: ut.headerTemplate,
160
+ }));
161
+ }
162
+ //# sourceMappingURL=schema-renderer.js.map
@@ -0,0 +1,50 @@
1
+ /**
2
+ * 입력 검증 순수 함수 — Layer 1 (Core)
3
+ *
4
+ * 에이전트 환각(hallucination) 방어를 위한 입력 검증 유틸리티.
5
+ * 제어 문자, URL 인코딩, 경로 순회, 쿼리 파라미터 등 위험한 입력을 거부하고,
6
+ * 유사 유닛 ID를 레벤슈타인 거리 기반으로 제안한다.
7
+ *
8
+ * I/O 없는 순수 함수만 포함 (Core Layer 1 규칙 준수).
9
+ *
10
+ * @module
11
+ */
12
+ /** 입력 검증 결과 */
13
+ export interface ValidationResult {
14
+ valid: boolean;
15
+ error?: string;
16
+ suggestions?: string[];
17
+ }
18
+ /**
19
+ * 유닛 ID의 안전성과 형식을 검증한다
20
+ *
21
+ * 검증 순서:
22
+ * 1. 빈 값 체크
23
+ * 2. 제어 문자 (ASCII < 0x20) 거부
24
+ * 3. URL 인코딩 (% 포함) 거부
25
+ * 4. 경로 순회 (../, ..\) 거부
26
+ * 5. 쿼리 파라미터 (?, #) 거부
27
+ * 6. knownIds 제공 시 매칭 실패하면 유사 ID 제안
28
+ *
29
+ * @param id - 검증할 유닛 ID
30
+ * @param knownIds - 알려진 유닛 ID 목록 (fuzzy match에 사용, 선택)
31
+ * @returns 검증 결과
32
+ */
33
+ export declare function validateUnitId(id: string, knownIds?: string[]): ValidationResult;
34
+ /**
35
+ * Git SHA의 형식을 검증한다
36
+ *
37
+ * 유효한 SHA 형식: 7~40자리 16진수 문자열
38
+ *
39
+ * @param sha - 검증할 SHA 문자열
40
+ * @returns 검증 결과
41
+ */
42
+ export declare function validateSha(sha: string): ValidationResult;
43
+ /**
44
+ * 경로 문자열의 안전성을 검증한다
45
+ *
46
+ * @param pathStr - 검증할 경로 문자열
47
+ * @returns 검증 결과
48
+ */
49
+ export declare function validatePath(pathStr: string): ValidationResult;
50
+ //# sourceMappingURL=input-validator.d.ts.map