vibe-commander 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/AGENTS.md +1 -0
  2. package/README.ko.md +643 -0
  3. package/README.md +643 -0
  4. package/dist/adapters/cli/commands/git-helpers.js +5 -3
  5. package/dist/adapters/cli/commands/init-helpers.js +1 -1
  6. package/dist/adapters/cli/commands/io-helpers.js +6 -2
  7. package/dist/adapters/cli/commands/schema.js +7 -3
  8. package/dist/adapters/cli/commands/set-unit.js +8 -2
  9. package/dist/adapters/cli/commands/skill-install.js +10 -4
  10. package/dist/adapters/cli/formatters-schema.d.ts +17 -0
  11. package/dist/adapters/cli/formatters-schema.js +155 -0
  12. package/dist/adapters/cli/formatters-unit.d.ts +0 -8
  13. package/dist/adapters/cli/formatters-unit.js +0 -146
  14. package/dist/adapters/cli/index.js +2 -1
  15. package/dist/adapters/cli/router.d.ts +0 -12
  16. package/dist/adapters/cli/router.js +4 -103
  17. package/dist/adapters/cli/stdin-parser.d.ts +23 -0
  18. package/dist/adapters/cli/stdin-parser.js +109 -0
  19. package/dist/config/schema.d.ts +0 -9
  20. package/dist/config/schema.js +138 -56
  21. package/dist/core/renderers/index.d.ts +1 -1
  22. package/dist/core/renderers/index.js +1 -1
  23. package/dist/core/renderers/marker-utils.js +5 -3
  24. package/dist/core/renderers/schema-renderer.d.ts +16 -0
  25. package/dist/core/renderers/schema-renderer.js +21 -0
  26. package/dist/core/resolvers/index.d.ts +0 -2
  27. package/examples/nextjs-web.config.json +73 -0
  28. package/examples/python-backend.config.json +91 -0
  29. package/examples/tauri-app.config.json +95 -0
  30. package/examples/vscode-tasks.json +101 -0
  31. package/package.json +14 -2
  32. package/schemas/config-schema.json +409 -0
  33. package/skills/claude/SKILL.md +22 -0
  34. package/skills/common/cli-reference.md +22 -0
  35. package/skills/cursor/SKILL.md +22 -0
  36. package/dist/adapters/cli/formatters.d.ts +0 -2
  37. package/dist/adapters/cli/formatters.js +0 -3
@@ -15,8 +15,8 @@ import { writeFileSync, mkdirSync } from 'node:fs';
15
15
  import { resolve, dirname } from 'node:path';
16
16
  import { ok, fail } from '../../../types/index.js';
17
17
  import { loadConfig } from '../../../config/loader.js';
18
- import { getConfigJsonSchema } from '../../../config/schema.js';
19
- import { renderCommandsSchema, renderTypesSchema, SCHEMA_SUBCOMMANDS, } from '../../../core/renderers/schema-renderer.js';
18
+ import { projectConfigSchema } from '../../../config/schema.js';
19
+ import { renderConfigSchema, renderCommandsSchema, renderTypesSchema, SCHEMA_SUBCOMMANDS, } from '../../../core/renderers/schema-renderer.js';
20
20
  import { validatePath } from '../../../core/validators/input-validator.js';
21
21
  /**
22
22
  * schema 커맨드를 실행한다
@@ -43,7 +43,11 @@ export function handleSchema(args, projectRoot) {
43
43
  let result;
44
44
  switch (subcommand) {
45
45
  case 'config': {
46
- const schema = getConfigJsonSchema();
46
+ const schema = renderConfigSchema(projectConfigSchema, {
47
+ id: 'https://raw.githubusercontent.com/viilab/vibe-commander/main/schemas/config-schema.json',
48
+ title: 'vibe-commander Configuration',
49
+ description: 'Unit-based vibe coding workflow automation CLI configuration. Defines project structure, unit types, parsing rules, and command sections.',
50
+ });
47
51
  result = { subcommand: 'config', schema };
48
52
  break;
49
53
  }
@@ -68,8 +68,14 @@ export async function handleSetUnit(args, projectRoot) {
68
68
  const markerOptions = useMarkers ? { sectionKey: unitTypeConfig.key } : undefined;
69
69
  const updateResult = updateSection(cmdFile.data.content, unitTypeConfig.commandSection, sectionBody, config.commandSections.separator, markerOptions);
70
70
  if (updateResult.success && updateResult.data.updated) {
71
- writeFileSync(cmdFile.data.absPath, updateResult.data.content, 'utf-8');
72
- commandsUpdated = true;
71
+ try {
72
+ writeFileSync(cmdFile.data.absPath, updateResult.data.content, 'utf-8');
73
+ commandsUpdated = true;
74
+ }
75
+ catch (err) {
76
+ const msg = err instanceof Error ? err.message : String(err);
77
+ warnings.push(`커맨드 파일 쓰기 실패: ${msg}`);
78
+ }
73
79
  }
74
80
  else if (updateResult.success && !updateResult.data.updated) {
75
81
  warnings.push(`커맨드 파일에서 '${unitTypeConfig.commandSection}' 섹션을 찾을 수 없습니다`);
@@ -77,9 +77,15 @@ function installForPlatform(platform, skillsRoot, projectRoot, force, result) {
77
77
  }
78
78
  result.overwritten.push(displayPath);
79
79
  }
80
- mkdirSync(dirname(targetPath), { recursive: true });
81
- const content = readFileSync(sourcePath, 'utf-8');
82
- writeFileSync(targetPath, content, 'utf-8');
83
- result.installed.push(displayPath);
80
+ try {
81
+ mkdirSync(dirname(targetPath), { recursive: true });
82
+ const content = readFileSync(sourcePath, 'utf-8');
83
+ writeFileSync(targetPath, content, 'utf-8');
84
+ result.installed.push(displayPath);
85
+ }
86
+ catch (err) {
87
+ const msg = err instanceof Error ? err.message : String(err);
88
+ result.skipped.push(`${displayPath} (I/O 오류: ${msg})`);
89
+ }
84
90
  }
85
91
  //# sourceMappingURL=skill-install.js.map
@@ -0,0 +1,17 @@
1
+ /**
2
+ * 스키마 및 SKILL 설치 포맷터 — Layer 3 (Adapter)
3
+ *
4
+ * schema, skill install 커맨드의 실행 결과를 포맷팅한다.
5
+ *
6
+ * @module
7
+ */
8
+ import type { ToolResult } from '../../types/index.js';
9
+ /**
10
+ * skill install 결과를 콘솔에 포맷팅하여 출력한다
11
+ */
12
+ export declare function formatSkillInstall(result: ToolResult<unknown>, json: boolean): void;
13
+ /**
14
+ * schema 결과를 콘솔에 포맷팅하여 출력한다
15
+ */
16
+ export declare function formatSchema(result: ToolResult<unknown>, json: boolean): void;
17
+ //# sourceMappingURL=formatters-schema.d.ts.map
@@ -0,0 +1,155 @@
1
+ /**
2
+ * 스키마 및 SKILL 설치 포맷터 — Layer 3 (Adapter)
3
+ *
4
+ * schema, skill install 커맨드의 실행 결과를 포맷팅한다.
5
+ *
6
+ * @module
7
+ */
8
+ import { RESET, GREEN, YELLOW, CYAN, DIM, BOLD, RED } from './output.js';
9
+ // ── skill install 포맷터 ──
10
+ /**
11
+ * skill install 결과를 콘솔에 포맷팅하여 출력한다
12
+ */
13
+ export function formatSkillInstall(result, json) {
14
+ if (!result.success)
15
+ return;
16
+ if (json) {
17
+ console.log(JSON.stringify(result, null, 2));
18
+ return;
19
+ }
20
+ const data = result.data;
21
+ if (!isSkillInstallResult(data))
22
+ return;
23
+ const totalDone = data.installed.length + data.overwritten.length;
24
+ console.log('');
25
+ if (totalDone > 0) {
26
+ console.log(`${GREEN}✅ SKILL 파일 설치 완료${RESET}`);
27
+ }
28
+ else {
29
+ console.log(`${YELLOW}⚠️ 설치된 파일 없음${RESET}`);
30
+ }
31
+ if (data.installed.length > 0) {
32
+ console.log('');
33
+ console.log(`${BOLD}📦 설치됨 (${String(data.installed.length)}개)${RESET}`);
34
+ for (const p of data.installed) {
35
+ console.log(` ${GREEN}+${RESET} ${CYAN}${p}${RESET}`);
36
+ }
37
+ }
38
+ if (data.overwritten.length > 0) {
39
+ console.log('');
40
+ console.log(`${BOLD}🔄 덮어씀 (${String(data.overwritten.length)}개)${RESET}`);
41
+ for (const p of data.overwritten) {
42
+ console.log(` ${YELLOW}~${RESET} ${CYAN}${p}${RESET}`);
43
+ }
44
+ }
45
+ if (data.skipped.length > 0) {
46
+ console.log('');
47
+ console.log(`${BOLD}⏭️ 건너뜀 (${String(data.skipped.length)}개)${RESET}`);
48
+ for (const s of data.skipped) {
49
+ console.log(` ${DIM}-${RESET} ${s}`);
50
+ }
51
+ }
52
+ console.log('');
53
+ }
54
+ /** SkillInstallResult 런타임 타입 가드 */
55
+ function isSkillInstallResult(data) {
56
+ return (data !== null &&
57
+ typeof data === 'object' &&
58
+ 'installed' in data &&
59
+ 'skipped' in data &&
60
+ 'overwritten' in data);
61
+ }
62
+ // ── schema 포맷터 ──
63
+ /**
64
+ * schema 결과를 콘솔에 포맷팅하여 출력한다
65
+ */
66
+ export function formatSchema(result, json) {
67
+ if (!result.success)
68
+ return;
69
+ if (json) {
70
+ console.log(JSON.stringify(result, null, 2));
71
+ return;
72
+ }
73
+ const data = result.data;
74
+ if (!isSchemaResult(data))
75
+ return;
76
+ if ('outputPath' in data && data.outputPath) {
77
+ console.log(`\n${GREEN}✅ 스키마가 파일에 저장되었습니다: ${CYAN}${data.outputPath}${RESET}\n`);
78
+ return;
79
+ }
80
+ switch (data.subcommand) {
81
+ case 'help':
82
+ formatSchemaHelp(data);
83
+ break;
84
+ case 'config':
85
+ console.log(JSON.stringify(data.schema, null, 2));
86
+ break;
87
+ case 'commands':
88
+ formatSchemaCommands(data);
89
+ break;
90
+ case 'types':
91
+ formatSchemaTypes(data);
92
+ break;
93
+ }
94
+ }
95
+ function formatSchemaHelp(data) {
96
+ console.log('');
97
+ console.log(`${BOLD}vc schema${RESET} — 런타임 스키마 조회`);
98
+ console.log('');
99
+ console.log(`${YELLOW}사용 가능한 서브커맨드:${RESET}`);
100
+ for (const sub of data.availableSubcommands) {
101
+ console.log(` ${CYAN}${sub.name}${RESET} ${sub.description}`);
102
+ }
103
+ console.log('');
104
+ console.log(`${YELLOW}사용법:${RESET} ${data.usage}`);
105
+ console.log('');
106
+ }
107
+ function formatSchemaCommands(data) {
108
+ console.log('');
109
+ console.log(`${BOLD}📋 vibe-commander CLI 스키마${RESET}`);
110
+ console.log('');
111
+ console.log(`${YELLOW}글로벌 옵션:${RESET}`);
112
+ for (const flag of data.globalFlags) {
113
+ const alias = flag.alias ? `, ${CYAN}${flag.alias}${RESET}` : '';
114
+ console.log(` ${CYAN}${flag.name}${RESET}${alias} ${DIM}${flag.description}${RESET}`);
115
+ }
116
+ console.log('');
117
+ console.log(`${YELLOW}명령어 (${String(data.commands.length)}개):${RESET}`);
118
+ for (const cmd of data.commands) {
119
+ const argStr = cmd.args.map((a) => (a.required ? `<${a.name}>` : `[${a.name}]`)).join(' ');
120
+ const nameWithArgs = argStr ? `${cmd.name} ${argStr}` : cmd.name;
121
+ console.log(` ${CYAN}${nameWithArgs}${RESET}`);
122
+ console.log(` ${cmd.description}`);
123
+ if (cmd.args.length > 0) {
124
+ for (const arg of cmd.args) {
125
+ const req = arg.required ? `${RED}필수${RESET}` : `${DIM}선택${RESET}`;
126
+ console.log(` ${DIM}인자:${RESET} ${arg.name} (${req}) — ${arg.description}`);
127
+ }
128
+ }
129
+ if (cmd.flags.length > 0) {
130
+ const flagNames = cmd.flags.map((f) => f.name).join(', ');
131
+ console.log(` ${DIM}플래그:${RESET} ${flagNames}`);
132
+ }
133
+ console.log('');
134
+ }
135
+ }
136
+ function formatSchemaTypes(data) {
137
+ console.log('');
138
+ console.log(`${BOLD}📋 프로젝트 유닛 타입 (${String(data.types.length)}개)${RESET}`);
139
+ console.log('');
140
+ for (const t of data.types) {
141
+ const displayName = t.displayName ? ` (${t.displayName})` : '';
142
+ console.log(` ${CYAN}${BOLD}${t.key}${RESET}${displayName}`);
143
+ console.log(` ${DIM}ID 패턴:${RESET} ${t.idPattern}`);
144
+ console.log(` ${DIM}계획서:${RESET} ${t.planDir}`);
145
+ console.log(` ${DIM}커맨드 섹션:${RESET} ${t.commandSection}`);
146
+ console.log(` ${DIM}의존성 수집:${RESET} ${t.collectDeps ? GREEN + '✅' : RED + '❌'}${RESET}`);
147
+ console.log(` ${DIM}헤더 템플릿:${RESET} ${t.headerTemplate}`);
148
+ console.log('');
149
+ }
150
+ }
151
+ /** SchemaResult 런타임 타입 가드 */
152
+ function isSchemaResult(data) {
153
+ return data !== null && typeof data === 'object' && 'subcommand' in data;
154
+ }
155
+ //# sourceMappingURL=formatters-schema.js.map
@@ -37,12 +37,4 @@ export declare function formatInit(result: ToolResult<unknown>, json: boolean):
37
37
  * exit code 규칙: 에러 있으면 1, 경고만 있으면 0
38
38
  */
39
39
  export declare function formatValidate(result: ToolResult<unknown>, json: boolean): void;
40
- /**
41
- * skill install 결과를 콘솔에 포맷팅하여 출력한다
42
- */
43
- export declare function formatSkillInstall(result: ToolResult<unknown>, json: boolean): void;
44
- /**
45
- * schema 결과를 콘솔에 포맷팅하여 출력한다
46
- */
47
- export declare function formatSchema(result: ToolResult<unknown>, json: boolean): void;
48
40
  //# sourceMappingURL=formatters-unit.d.ts.map
@@ -231,150 +231,4 @@ function isValidateResult(data) {
231
231
  'warnings' in data &&
232
232
  'items' in data);
233
233
  }
234
- // ── skill install 포맷터 ──
235
- /**
236
- * skill install 결과를 콘솔에 포맷팅하여 출력한다
237
- */
238
- export function formatSkillInstall(result, json) {
239
- if (!result.success)
240
- return;
241
- if (json) {
242
- console.log(JSON.stringify(result, null, 2));
243
- return;
244
- }
245
- const data = result.data;
246
- if (!isSkillInstallResult(data))
247
- return;
248
- const totalDone = data.installed.length + data.overwritten.length;
249
- console.log('');
250
- if (totalDone > 0) {
251
- console.log(`${GREEN}✅ SKILL 파일 설치 완료${RESET}`);
252
- }
253
- else {
254
- console.log(`${YELLOW}⚠️ 설치된 파일 없음${RESET}`);
255
- }
256
- if (data.installed.length > 0) {
257
- console.log('');
258
- console.log(`${BOLD}📦 설치됨 (${String(data.installed.length)}개)${RESET}`);
259
- for (const p of data.installed) {
260
- console.log(` ${GREEN}+${RESET} ${CYAN}${p}${RESET}`);
261
- }
262
- }
263
- if (data.overwritten.length > 0) {
264
- console.log('');
265
- console.log(`${BOLD}🔄 덮어씀 (${String(data.overwritten.length)}개)${RESET}`);
266
- for (const p of data.overwritten) {
267
- console.log(` ${YELLOW}~${RESET} ${CYAN}${p}${RESET}`);
268
- }
269
- }
270
- if (data.skipped.length > 0) {
271
- console.log('');
272
- console.log(`${BOLD}⏭️ 건너뜀 (${String(data.skipped.length)}개)${RESET}`);
273
- for (const s of data.skipped) {
274
- console.log(` ${DIM}-${RESET} ${s}`);
275
- }
276
- }
277
- console.log('');
278
- }
279
- /** SkillInstallResult 런타임 타입 가드 */
280
- function isSkillInstallResult(data) {
281
- return (data !== null &&
282
- typeof data === 'object' &&
283
- 'installed' in data &&
284
- 'skipped' in data &&
285
- 'overwritten' in data);
286
- }
287
- // ── schema 포맷터 ──
288
- /**
289
- * schema 결과를 콘솔에 포맷팅하여 출력한다
290
- */
291
- export function formatSchema(result, json) {
292
- if (!result.success)
293
- return;
294
- if (json) {
295
- console.log(JSON.stringify(result, null, 2));
296
- return;
297
- }
298
- const data = result.data;
299
- if (!isSchemaResult(data))
300
- return;
301
- if ('outputPath' in data && data.outputPath) {
302
- console.log(`\n${GREEN}✅ 스키마가 파일에 저장되었습니다: ${CYAN}${data.outputPath}${RESET}\n`);
303
- return;
304
- }
305
- switch (data.subcommand) {
306
- case 'help':
307
- formatSchemaHelp(data);
308
- break;
309
- case 'config':
310
- console.log(JSON.stringify(data.schema, null, 2));
311
- break;
312
- case 'commands':
313
- formatSchemaCommands(data);
314
- break;
315
- case 'types':
316
- formatSchemaTypes(data);
317
- break;
318
- }
319
- }
320
- function formatSchemaHelp(data) {
321
- console.log('');
322
- console.log(`${BOLD}vc schema${RESET} — 런타임 스키마 조회`);
323
- console.log('');
324
- console.log(`${YELLOW}사용 가능한 서브커맨드:${RESET}`);
325
- for (const sub of data.availableSubcommands) {
326
- console.log(` ${CYAN}${sub.name}${RESET} ${sub.description}`);
327
- }
328
- console.log('');
329
- console.log(`${YELLOW}사용법:${RESET} ${data.usage}`);
330
- console.log('');
331
- }
332
- function formatSchemaCommands(data) {
333
- console.log('');
334
- console.log(`${BOLD}📋 vibe-commander CLI 스키마${RESET}`);
335
- console.log('');
336
- console.log(`${YELLOW}글로벌 옵션:${RESET}`);
337
- for (const flag of data.globalFlags) {
338
- const alias = flag.alias ? `, ${CYAN}${flag.alias}${RESET}` : '';
339
- console.log(` ${CYAN}${flag.name}${RESET}${alias} ${DIM}${flag.description}${RESET}`);
340
- }
341
- console.log('');
342
- console.log(`${YELLOW}명령어 (${String(data.commands.length)}개):${RESET}`);
343
- for (const cmd of data.commands) {
344
- const argStr = cmd.args.map((a) => (a.required ? `<${a.name}>` : `[${a.name}]`)).join(' ');
345
- const nameWithArgs = argStr ? `${cmd.name} ${argStr}` : cmd.name;
346
- console.log(` ${CYAN}${nameWithArgs}${RESET}`);
347
- console.log(` ${cmd.description}`);
348
- if (cmd.args.length > 0) {
349
- for (const arg of cmd.args) {
350
- const req = arg.required ? `${RED}필수${RESET}` : `${DIM}선택${RESET}`;
351
- console.log(` ${DIM}인자:${RESET} ${arg.name} (${req}) — ${arg.description}`);
352
- }
353
- }
354
- if (cmd.flags.length > 0) {
355
- const flagNames = cmd.flags.map((f) => f.name).join(', ');
356
- console.log(` ${DIM}플래그:${RESET} ${flagNames}`);
357
- }
358
- console.log('');
359
- }
360
- }
361
- function formatSchemaTypes(data) {
362
- console.log('');
363
- console.log(`${BOLD}📋 프로젝트 유닛 타입 (${String(data.types.length)}개)${RESET}`);
364
- console.log('');
365
- for (const t of data.types) {
366
- const displayName = t.displayName ? ` (${t.displayName})` : '';
367
- console.log(` ${CYAN}${BOLD}${t.key}${RESET}${displayName}`);
368
- console.log(` ${DIM}ID 패턴:${RESET} ${t.idPattern}`);
369
- console.log(` ${DIM}계획서:${RESET} ${t.planDir}`);
370
- console.log(` ${DIM}커맨드 섹션:${RESET} ${t.commandSection}`);
371
- console.log(` ${DIM}의존성 수집:${RESET} ${t.collectDeps ? GREEN + '✅' : RED + '❌'}${RESET}`);
372
- console.log(` ${DIM}헤더 템플릿:${RESET} ${t.headerTemplate}`);
373
- console.log('');
374
- }
375
- }
376
- /** SchemaResult 런타임 타입 가드 */
377
- function isSchemaResult(data) {
378
- return data !== null && typeof data === 'object' && 'subcommand' in data;
379
- }
380
234
  //# sourceMappingURL=formatters-unit.js.map
@@ -23,7 +23,8 @@ import { handleInit } from './commands/init.js';
23
23
  import { handleValidate } from './commands/validate.js';
24
24
  import { handleSkillInstall } from './commands/skill-install.js';
25
25
  import { handleSchema } from './commands/schema.js';
26
- import { formatContext, formatSetUnit, formatInit, formatValidate, formatSkillInstall, formatSchema, } from './formatters-unit.js';
26
+ import { formatContext, formatSetUnit, formatInit, formatValidate } from './formatters-unit.js';
27
+ import { formatSkillInstall, formatSchema } from './formatters-schema.js';
27
28
  import { formatListUnits, formatUpdateCommit } from './formatters-list.js';
28
29
  const handlers = {
29
30
  init: handleInit,
@@ -79,18 +79,6 @@ export type CommandFormatter = (result: ToolResult<unknown>, json: boolean) => v
79
79
  * @returns 파싱된 커맨드 인자 또는 에러
80
80
  */
81
81
  export declare function parseArgs(argv: readonly string[]): ToolResult<ParsedArgs>;
82
- /**
83
- * stdin JSON 데이터로부터 ParsedArgs를 생성한다
84
- *
85
- * 서브커맨드는 argv에서 결정되고, 인자/옵션은 stdin JSON에서 추출.
86
- * --stdin은 --no-prompt를 암시한다 (stdin이 JSON 데이터에 사용되므로 인터랙티브 불가).
87
- *
88
- * @param subcommand - argv에서 추출한 서브커맨드 이름
89
- * @param data - stdin에서 파싱한 JSON 객체
90
- * @param json - --json 플래그 여부
91
- * @returns ParsedArgs 또는 에러
92
- */
93
- export declare function parseStdinArgs(subcommand: string, data: Record<string, unknown>, json: boolean, rawArgs?: readonly string[]): ToolResult<ParsedArgs>;
94
82
  /**
95
83
  * CLI 라우터 메인 실행 함수
96
84
  *
@@ -7,9 +7,10 @@
7
7
  *
8
8
  * @module
9
9
  */
10
- import { ok, fail } from '../../types/index.js';
10
+ import { fail } from '../../types/index.js';
11
11
  import { printHelp, printVersion, printUnknownCommand, printMissingArg, printSuccess, printError, } from './output.js';
12
12
  import { readStdin, parseStdinJson } from '../pipe/stdin-reader.js';
13
+ import { parseStdinArgs } from './stdin-parser.js';
13
14
  // ── arg 파싱 ──
14
15
  /**
15
16
  * process.argv로부터 서브커맨드와 인자/옵션을 파싱한다
@@ -245,106 +246,6 @@ function getOptionValue(args, option) {
245
246
  function isValueOption(arg) {
246
247
  return ['--mode', '--section', '--phase', '--request', '--output'].includes(arg);
247
248
  }
248
- // ── stdin → ParsedArgs 변환 ──
249
- /**
250
- * stdin JSON 데이터로부터 ParsedArgs를 생성한다
251
- *
252
- * 서브커맨드는 argv에서 결정되고, 인자/옵션은 stdin JSON에서 추출.
253
- * --stdin은 --no-prompt를 암시한다 (stdin이 JSON 데이터에 사용되므로 인터랙티브 불가).
254
- *
255
- * @param subcommand - argv에서 추출한 서브커맨드 이름
256
- * @param data - stdin에서 파싱한 JSON 객체
257
- * @param json - --json 플래그 여부
258
- * @returns ParsedArgs 또는 에러
259
- */
260
- export function parseStdinArgs(subcommand, data, json, rawArgs) {
261
- switch (subcommand) {
262
- case 'set-unit': {
263
- const unitId = typeof data.unitId === 'string' ? data.unitId : undefined;
264
- if (!unitId) {
265
- return fail('STDIN_MISSING_FIELD', 'stdin JSON에 "unitId" (string) 필드가 필요합니다', '예시: {"unitId": "U-001[Mvp]"}');
266
- }
267
- const requestKeys = Array.isArray(data.requestKeys)
268
- ? data.requestKeys.filter((k) => typeof k === 'string')
269
- : [];
270
- const dryRun = rawArgs?.includes('--dry-run') ?? false;
271
- return ok({ command: 'set-unit', unitId, requestKeys, noPrompt: true, dryRun, json });
272
- }
273
- case 'update-commit': {
274
- const modeValue = typeof data.mode === 'string' ? data.mode : undefined;
275
- const mode = modeValue === 'append' ? 'append' : 'replace';
276
- const section = typeof data.section === 'string' ? data.section : undefined;
277
- const base = {
278
- command: 'update-commit',
279
- mode,
280
- ...(section !== undefined && { section }),
281
- json,
282
- };
283
- if (typeof data.sha === 'string') {
284
- return ok({ ...base, commitMode: 'manual', sha: data.sha });
285
- }
286
- if (typeof data.count === 'number' && Number.isInteger(data.count) && data.count > 0) {
287
- return ok({ ...base, commitMode: 'auto-recent', count: data.count });
288
- }
289
- return ok({ ...base, commitMode: 'auto-head' });
290
- }
291
- case 'list-units': {
292
- const phase = typeof data.phase === 'string' ? data.phase : undefined;
293
- return ok({
294
- command: 'list-units',
295
- ...(phase !== undefined && { phase }),
296
- json,
297
- });
298
- }
299
- case 'context': {
300
- const unitId = typeof data.unitId === 'string' ? data.unitId : undefined;
301
- if (!unitId) {
302
- return fail('STDIN_MISSING_FIELD', 'stdin JSON에 "unitId" (string) 필드가 필요합니다', '예시: {"unitId": "U-001[Mvp]"}');
303
- }
304
- return ok({ command: 'context', unitId, json });
305
- }
306
- case 'validate':
307
- return ok({ command: 'validate', json });
308
- case 'init':
309
- return ok({
310
- command: 'init',
311
- json,
312
- stdinData: data,
313
- fromExisting: rawArgs?.includes('--from-existing') ?? false,
314
- force: rawArgs?.includes('--force') ?? false,
315
- });
316
- case 'skill': {
317
- if (!rawArgs?.includes('install')) {
318
- return fail('UNKNOWN_COMMAND', 'skill 서브커맨드가 필요합니다', '사용법: vc skill install --stdin --json');
319
- }
320
- const stdinTargets = Array.isArray(data.targets)
321
- ? data.targets.filter((t) => t === 'cursor' || t === 'claude' || t === 'gemini')
322
- : [];
323
- const targets = stdinTargets.length > 0 ? stdinTargets : ['cursor', 'claude', 'gemini'];
324
- return ok({
325
- command: 'skill-install',
326
- targets,
327
- force: rawArgs.includes('--force'),
328
- json,
329
- });
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
- }
344
- default:
345
- return fail('UNKNOWN_COMMAND', `알 수 없는 명령어: ${subcommand}`, '사용 가능한 명령어: init, set-unit, update-commit, list-units, context, validate, skill, schema');
346
- }
347
- }
348
249
  // ── 메인 라우터 ──
349
250
  /**
350
251
  * CLI 라우터 메인 실행 함수
@@ -445,12 +346,12 @@ function emitError(result, json) {
445
346
  process.exitCode = 1;
446
347
  }
447
348
  /**
448
- * 스텁 핸들러 — 후속 유닛에서 교체될 임시 핸들러
349
+ * 스텁 핸들러 — 미구현 커맨드용 임시 핸들러
449
350
  *
450
351
  * 해당 커맨드가 아직 구현되지 않았음을 알린다.
451
352
  */
452
353
  function stubHandler(args) {
453
- return fail('NOT_IMPLEMENTED', `'${args.command}' 명령어는 아직 구현되지 않았습니다`, '후속 유닛(U-014 ~ U-016)에서 구현 예정');
354
+ return fail('NOT_IMPLEMENTED', `'${args.command}' 명령어는 아직 구현되지 않았습니다`, ' 명령어는 아직 구현되지 않았습니다');
454
355
  }
455
356
  /**
456
357
  * 에러 상세 메시지에서 인자 이름을 추출
@@ -0,0 +1,23 @@
1
+ /**
2
+ * stdin JSON → ParsedArgs 변환 — Layer 3 (Adapter)
3
+ *
4
+ * stdin을 통해 전달된 JSON 데이터를 ParsedArgs로 변환한다.
5
+ * 서브커맨드는 argv에서 결정되고, 인자/옵션은 stdin JSON에서 추출.
6
+ *
7
+ * @module
8
+ */
9
+ import type { ToolResult } from '../../types/index.js';
10
+ import type { ParsedArgs } from './router.js';
11
+ /**
12
+ * stdin JSON 데이터로부터 ParsedArgs를 생성한다
13
+ *
14
+ * 서브커맨드는 argv에서 결정되고, 인자/옵션은 stdin JSON에서 추출.
15
+ * --stdin은 --no-prompt를 암시한다 (stdin이 JSON 데이터에 사용되므로 인터랙티브 불가).
16
+ *
17
+ * @param subcommand - argv에서 추출한 서브커맨드 이름
18
+ * @param data - stdin에서 파싱한 JSON 객체
19
+ * @param json - --json 플래그 여부
20
+ * @returns ParsedArgs 또는 에러
21
+ */
22
+ export declare function parseStdinArgs(subcommand: string, data: Record<string, unknown>, json: boolean, rawArgs?: readonly string[]): ToolResult<ParsedArgs>;
23
+ //# sourceMappingURL=stdin-parser.d.ts.map