vibe-commander 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +18 -0
- package/README.ko.md +643 -0
- package/README.md +643 -0
- package/dist/adapters/cli/commands/context-resolver.js +30 -0
- package/dist/adapters/cli/commands/git-helpers.js +5 -3
- package/dist/adapters/cli/commands/init-helpers.js +1 -1
- package/dist/adapters/cli/commands/io-helpers.js +6 -2
- package/dist/adapters/cli/commands/list-units.d.ts +2 -3
- package/dist/adapters/cli/commands/list-units.js +8 -34
- package/dist/adapters/cli/commands/schema.js +7 -3
- package/dist/adapters/cli/commands/set-unit.d.ts +5 -0
- package/dist/adapters/cli/commands/set-unit.js +149 -10
- package/dist/adapters/cli/commands/skill-install.js +10 -4
- package/dist/adapters/cli/formatters-schema.d.ts +17 -0
- package/dist/adapters/cli/formatters-schema.js +155 -0
- package/dist/adapters/cli/formatters-unit.d.ts +0 -8
- package/dist/adapters/cli/formatters-unit.js +4 -146
- package/dist/adapters/cli/index.js +2 -1
- package/dist/adapters/cli/output.js +3 -0
- package/dist/adapters/cli/router.d.ts +1 -12
- package/dist/adapters/cli/router.js +12 -107
- package/dist/adapters/cli/stdin-parser.d.ts +23 -0
- package/dist/adapters/cli/stdin-parser.js +121 -0
- package/dist/config/schema.d.ts +2 -9
- package/dist/config/schema.js +143 -56
- package/dist/core/parsers/dep-line-parser.js +7 -3
- package/dist/core/parsers/dependency-extractor.js +80 -14
- package/dist/core/parsers/index.d.ts +2 -1
- package/dist/core/parsers/index.js +3 -1
- package/dist/core/parsers/plan-parser-helpers.js +5 -0
- package/dist/core/parsers/subsection-extractor.d.ts +20 -0
- package/dist/core/parsers/subsection-extractor.js +56 -0
- package/dist/core/parsers/title-extractor.d.ts +16 -0
- package/dist/core/parsers/title-extractor.js +26 -0
- package/dist/core/renderers/index.d.ts +2 -1
- package/dist/core/renderers/index.js +1 -1
- package/dist/core/renderers/marker-utils.js +5 -3
- package/dist/core/renderers/schema-renderer.d.ts +16 -0
- package/dist/core/renderers/schema-renderer.js +21 -0
- package/dist/core/renderers/section-renderer.d.ts +9 -1
- package/dist/core/renderers/section-renderer.js +14 -5
- package/dist/core/resolvers/backlog-resolver.d.ts +37 -0
- package/dist/core/resolvers/backlog-resolver.js +50 -0
- package/dist/core/resolvers/index.d.ts +0 -2
- package/examples/nextjs-web.config.json +73 -0
- package/examples/python-backend.config.json +91 -0
- package/examples/tauri-app.config.json +95 -0
- package/examples/vscode-tasks.json +101 -0
- package/package.json +14 -2
- package/schemas/config-schema.json +409 -0
- package/skills/claude/SKILL.md +22 -0
- package/skills/common/cli-reference.md +22 -0
- package/skills/cursor/SKILL.md +22 -0
- package/dist/adapters/cli/formatters.d.ts +0 -2
- package/dist/adapters/cli/formatters.js +0 -3
|
@@ -19,9 +19,13 @@ export function formatSetUnit(result, json) {
|
|
|
19
19
|
const data = result.data;
|
|
20
20
|
if (!isSetUnitResult(data))
|
|
21
21
|
return;
|
|
22
|
+
const autoData = data;
|
|
22
23
|
console.log(`
|
|
23
24
|
${GREEN}✅ 작업 유닛 설정 완료: ${data.unitId}${RESET}
|
|
24
25
|
`);
|
|
26
|
+
if (autoData.autoSelected && autoData.selectedFrom) {
|
|
27
|
+
console.log(`${CYAN}🎯 자동 선택됨${RESET} ${DIM}(ready ${String(autoData.selectedFrom.ready)}개 중 첫 번째)${RESET}`);
|
|
28
|
+
}
|
|
25
29
|
console.log(`${BOLD}📝 제목${RESET}: ${data.title}`);
|
|
26
30
|
console.log(`${BOLD}📂 유형${RESET}: ${data.unitType}`);
|
|
27
31
|
console.log(`${BOLD}📄 계획서${RESET}: ${data.planPath}`);
|
|
@@ -231,150 +235,4 @@ function isValidateResult(data) {
|
|
|
231
235
|
'warnings' in data &&
|
|
232
236
|
'items' in data);
|
|
233
237
|
}
|
|
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
238
|
//# 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
|
|
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,
|
|
@@ -100,6 +100,7 @@ ${YELLOW}글로벌 옵션:${RESET}
|
|
|
100
100
|
${CYAN}--stdin${RESET} stdin에서 JSON 입력 읽기
|
|
101
101
|
|
|
102
102
|
${YELLOW}set-unit 옵션:${RESET}
|
|
103
|
+
${CYAN}--next${RESET} 백로그에서 ready 유닛을 자동 선택
|
|
103
104
|
${CYAN}--request${RESET} <key1,key2> 커스텀 특별 요청사항 키 선택 (콤마 구분)
|
|
104
105
|
${CYAN}--no-prompt${RESET} 페어링 질문 인터랙티브 프롬프트 비활성화
|
|
105
106
|
${CYAN}--dry-run${RESET} 커맨드 파일 수정 없이 미리보기만 출력
|
|
@@ -126,6 +127,8 @@ ${YELLOW}예시:${RESET}
|
|
|
126
127
|
${DIM}$ vc init${RESET}
|
|
127
128
|
${DIM}$ vc init --from-existing${RESET} ${DIM}# 기존 파일 스캔으로 자동 설정${RESET}
|
|
128
129
|
${DIM}$ vc set-unit U-013[Mvp]${RESET}
|
|
130
|
+
${DIM}$ vc set-unit --next${RESET} ${DIM}# 다음 ready 유닛 자동 선택${RESET}
|
|
131
|
+
${DIM}$ vc set-unit --next --dry-run${RESET} ${DIM}# 자동 선택 미리보기만${RESET}
|
|
129
132
|
${DIM}$ vc set-unit U-013[Mvp] --dry-run${RESET} ${DIM}# 커맨드 파일 미수정, 미리보기만${RESET}
|
|
130
133
|
${DIM}$ vc set-unit U-013[Mvp] --dry-run --json${RESET} ${DIM}# 미리보기를 JSON으로 출력${RESET}
|
|
131
134
|
${DIM}$ vc set-unit U-013[Mvp] --request mcp-nanobanana,mcp-codrag${RESET}
|
|
@@ -12,6 +12,7 @@ import type { ToolResult } from '../../types/index.js';
|
|
|
12
12
|
export interface SetUnitArgs {
|
|
13
13
|
command: 'set-unit';
|
|
14
14
|
unitId: string;
|
|
15
|
+
next: boolean;
|
|
15
16
|
requestKeys: string[];
|
|
16
17
|
noPrompt: boolean;
|
|
17
18
|
dryRun: boolean;
|
|
@@ -79,18 +80,6 @@ export type CommandFormatter = (result: ToolResult<unknown>, json: boolean) => v
|
|
|
79
80
|
* @returns 파싱된 커맨드 인자 또는 에러
|
|
80
81
|
*/
|
|
81
82
|
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
83
|
/**
|
|
95
84
|
* CLI 라우터 메인 실행 함수
|
|
96
85
|
*
|
|
@@ -7,9 +7,10 @@
|
|
|
7
7
|
*
|
|
8
8
|
* @module
|
|
9
9
|
*/
|
|
10
|
-
import {
|
|
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로부터 서브커맨드와 인자/옵션을 파싱한다
|
|
@@ -67,12 +68,16 @@ export function parseArgs(argv) {
|
|
|
67
68
|
/**
|
|
68
69
|
* set-unit 서브커맨드의 인자를 파싱한다
|
|
69
70
|
*
|
|
70
|
-
* 필수: <unitId>
|
|
71
|
+
* 필수: <unitId> 또는 --next (상호 배타)
|
|
71
72
|
*/
|
|
72
73
|
function parseSetUnit(args, json) {
|
|
74
|
+
const next = hasFlag(args, '--next');
|
|
73
75
|
const unitId = getPositionalArg(args);
|
|
74
|
-
if (
|
|
75
|
-
return fail('
|
|
76
|
+
if (next && unitId) {
|
|
77
|
+
return fail('CONFLICTING_OPTIONS', '--next와 <unitId>는 동시에 사용할 수 없습니다', '사용법: vc set-unit --next 또는 vc set-unit <unitId>');
|
|
78
|
+
}
|
|
79
|
+
if (!next && !unitId) {
|
|
80
|
+
return fail('MISSING_ARGUMENT', "'set-unit' 명령어에 <unitId> 인자 또는 --next 옵션이 필요합니다", '사용법: vc set-unit <unitId> 또는 vc set-unit --next');
|
|
76
81
|
}
|
|
77
82
|
const requestRaw = getOptionValue(args, '--request');
|
|
78
83
|
const requestKeys = requestRaw
|
|
@@ -85,7 +90,7 @@ function parseSetUnit(args, json) {
|
|
|
85
90
|
const dryRun = hasFlag(args, '--dry-run');
|
|
86
91
|
return {
|
|
87
92
|
success: true,
|
|
88
|
-
data: { command: 'set-unit', unitId, requestKeys, noPrompt, dryRun, json },
|
|
93
|
+
data: { command: 'set-unit', unitId: unitId ?? '', next, requestKeys, noPrompt, dryRun, json },
|
|
89
94
|
};
|
|
90
95
|
}
|
|
91
96
|
/**
|
|
@@ -245,106 +250,6 @@ function getOptionValue(args, option) {
|
|
|
245
250
|
function isValueOption(arg) {
|
|
246
251
|
return ['--mode', '--section', '--phase', '--request', '--output'].includes(arg);
|
|
247
252
|
}
|
|
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
253
|
// ── 메인 라우터 ──
|
|
349
254
|
/**
|
|
350
255
|
* CLI 라우터 메인 실행 함수
|
|
@@ -445,12 +350,12 @@ function emitError(result, json) {
|
|
|
445
350
|
process.exitCode = 1;
|
|
446
351
|
}
|
|
447
352
|
/**
|
|
448
|
-
* 스텁 핸들러 —
|
|
353
|
+
* 스텁 핸들러 — 미구현 커맨드용 임시 핸들러
|
|
449
354
|
*
|
|
450
355
|
* 해당 커맨드가 아직 구현되지 않았음을 알린다.
|
|
451
356
|
*/
|
|
452
357
|
function stubHandler(args) {
|
|
453
|
-
return fail('NOT_IMPLEMENTED', `'${args.command}' 명령어는 아직 구현되지 않았습니다`, '
|
|
358
|
+
return fail('NOT_IMPLEMENTED', `'${args.command}' 명령어는 아직 구현되지 않았습니다`, '이 명령어는 아직 구현되지 않았습니다');
|
|
454
359
|
}
|
|
455
360
|
/**
|
|
456
361
|
* 에러 상세 메시지에서 인자 이름을 추출
|
|
@@ -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
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* stdin JSON → ParsedArgs 변환 — Layer 3 (Adapter)
|
|
3
|
+
*
|
|
4
|
+
* stdin을 통해 전달된 JSON 데이터를 ParsedArgs로 변환한다.
|
|
5
|
+
* 서브커맨드는 argv에서 결정되고, 인자/옵션은 stdin JSON에서 추출.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import { ok, fail } from '../../types/index.js';
|
|
10
|
+
/**
|
|
11
|
+
* stdin JSON 데이터로부터 ParsedArgs를 생성한다
|
|
12
|
+
*
|
|
13
|
+
* 서브커맨드는 argv에서 결정되고, 인자/옵션은 stdin JSON에서 추출.
|
|
14
|
+
* --stdin은 --no-prompt를 암시한다 (stdin이 JSON 데이터에 사용되므로 인터랙티브 불가).
|
|
15
|
+
*
|
|
16
|
+
* @param subcommand - argv에서 추출한 서브커맨드 이름
|
|
17
|
+
* @param data - stdin에서 파싱한 JSON 객체
|
|
18
|
+
* @param json - --json 플래그 여부
|
|
19
|
+
* @returns ParsedArgs 또는 에러
|
|
20
|
+
*/
|
|
21
|
+
export function parseStdinArgs(subcommand, data, json, rawArgs) {
|
|
22
|
+
switch (subcommand) {
|
|
23
|
+
case 'set-unit': {
|
|
24
|
+
const next = data.next === true;
|
|
25
|
+
const unitId = typeof data.unitId === 'string' ? data.unitId : undefined;
|
|
26
|
+
if (next && unitId) {
|
|
27
|
+
return fail('CONFLICTING_OPTIONS', '"next"와 "unitId"는 동시에 사용할 수 없습니다', '예시: {"next": true} 또는 {"unitId": "U-001[Mvp]"}');
|
|
28
|
+
}
|
|
29
|
+
if (!next && !unitId) {
|
|
30
|
+
return fail('STDIN_MISSING_FIELD', 'stdin JSON에 "unitId" (string) 또는 "next": true 필드가 필요합니다', '예시: {"unitId": "U-001[Mvp]"} 또는 {"next": true}');
|
|
31
|
+
}
|
|
32
|
+
const requestKeys = Array.isArray(data.requestKeys)
|
|
33
|
+
? data.requestKeys.filter((k) => typeof k === 'string')
|
|
34
|
+
: [];
|
|
35
|
+
const dryRun = rawArgs?.includes('--dry-run') ?? false;
|
|
36
|
+
return ok({
|
|
37
|
+
command: 'set-unit',
|
|
38
|
+
unitId: unitId ?? '',
|
|
39
|
+
next,
|
|
40
|
+
requestKeys,
|
|
41
|
+
noPrompt: true,
|
|
42
|
+
dryRun,
|
|
43
|
+
json,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
case 'update-commit': {
|
|
47
|
+
const modeValue = typeof data.mode === 'string' ? data.mode : undefined;
|
|
48
|
+
const mode = modeValue === 'append' ? 'append' : 'replace';
|
|
49
|
+
const section = typeof data.section === 'string' ? data.section : undefined;
|
|
50
|
+
const base = {
|
|
51
|
+
command: 'update-commit',
|
|
52
|
+
mode,
|
|
53
|
+
...(section !== undefined && { section }),
|
|
54
|
+
json,
|
|
55
|
+
};
|
|
56
|
+
if (typeof data.sha === 'string') {
|
|
57
|
+
return ok({ ...base, commitMode: 'manual', sha: data.sha });
|
|
58
|
+
}
|
|
59
|
+
if (typeof data.count === 'number' && Number.isInteger(data.count) && data.count > 0) {
|
|
60
|
+
return ok({ ...base, commitMode: 'auto-recent', count: data.count });
|
|
61
|
+
}
|
|
62
|
+
return ok({ ...base, commitMode: 'auto-head' });
|
|
63
|
+
}
|
|
64
|
+
case 'list-units': {
|
|
65
|
+
const phase = typeof data.phase === 'string' ? data.phase : undefined;
|
|
66
|
+
return ok({
|
|
67
|
+
command: 'list-units',
|
|
68
|
+
...(phase !== undefined && { phase }),
|
|
69
|
+
json,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
case 'context': {
|
|
73
|
+
const unitId = typeof data.unitId === 'string' ? data.unitId : undefined;
|
|
74
|
+
if (!unitId) {
|
|
75
|
+
return fail('STDIN_MISSING_FIELD', 'stdin JSON에 "unitId" (string) 필드가 필요합니다', '예시: {"unitId": "U-001[Mvp]"}');
|
|
76
|
+
}
|
|
77
|
+
return ok({ command: 'context', unitId, json });
|
|
78
|
+
}
|
|
79
|
+
case 'validate':
|
|
80
|
+
return ok({ command: 'validate', json });
|
|
81
|
+
case 'init':
|
|
82
|
+
return ok({
|
|
83
|
+
command: 'init',
|
|
84
|
+
json,
|
|
85
|
+
stdinData: data,
|
|
86
|
+
fromExisting: rawArgs?.includes('--from-existing') ?? false,
|
|
87
|
+
force: rawArgs?.includes('--force') ?? false,
|
|
88
|
+
});
|
|
89
|
+
case 'skill': {
|
|
90
|
+
if (!rawArgs?.includes('install')) {
|
|
91
|
+
return fail('UNKNOWN_COMMAND', 'skill 서브커맨드가 필요합니다', '사용법: vc skill install --stdin --json');
|
|
92
|
+
}
|
|
93
|
+
const stdinTargets = Array.isArray(data.targets)
|
|
94
|
+
? data.targets.filter((t) => t === 'cursor' || t === 'claude' || t === 'gemini')
|
|
95
|
+
: [];
|
|
96
|
+
const targets = stdinTargets.length > 0 ? stdinTargets : ['cursor', 'claude', 'gemini'];
|
|
97
|
+
return ok({
|
|
98
|
+
command: 'skill-install',
|
|
99
|
+
targets,
|
|
100
|
+
force: rawArgs.includes('--force'),
|
|
101
|
+
json,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
case 'schema': {
|
|
105
|
+
const sub = typeof data.subcommand === 'string' ? data.subcommand : undefined;
|
|
106
|
+
if (sub && sub !== 'config' && sub !== 'commands' && sub !== 'types') {
|
|
107
|
+
return fail('UNKNOWN_SUBCOMMAND', `알 수 없는 schema 서브커맨드: ${sub}`, '사용 가능한 서브커맨드: config, commands, types');
|
|
108
|
+
}
|
|
109
|
+
const output = typeof data.output === 'string' ? data.output : undefined;
|
|
110
|
+
return ok({
|
|
111
|
+
command: 'schema',
|
|
112
|
+
...(sub !== undefined && { subcommand: sub }),
|
|
113
|
+
json,
|
|
114
|
+
...(output !== undefined && { output }),
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
default:
|
|
118
|
+
return fail('UNKNOWN_COMMAND', `알 수 없는 명령어: ${subcommand}`, '사용 가능한 명령어: init, set-unit, update-commit, list-units, context, validate, skill, schema');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
//# sourceMappingURL=stdin-parser.js.map
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -93,6 +93,7 @@ export declare const commandSectionsSchema: z.ZodObject<{
|
|
|
93
93
|
preserveOtherSections: z.ZodDefault<z.ZodBoolean>;
|
|
94
94
|
commitFieldName: z.ZodDefault<z.ZodString>;
|
|
95
95
|
useMarkers: z.ZodDefault<z.ZodBoolean>;
|
|
96
|
+
specialRequestsHeading: z.ZodDefault<z.ZodString>;
|
|
96
97
|
}, z.core.$strip>;
|
|
97
98
|
/** 기본 문서 탐색 설정 */
|
|
98
99
|
export declare const DEFAULT_DOC_DISCOVERY: {
|
|
@@ -173,6 +174,7 @@ export declare const projectConfigSchema: z.ZodObject<{
|
|
|
173
174
|
preserveOtherSections: z.ZodDefault<z.ZodBoolean>;
|
|
174
175
|
commitFieldName: z.ZodDefault<z.ZodString>;
|
|
175
176
|
useMarkers: z.ZodDefault<z.ZodBoolean>;
|
|
177
|
+
specialRequestsHeading: z.ZodDefault<z.ZodString>;
|
|
176
178
|
}, z.core.$strip>>;
|
|
177
179
|
defaultSpecialRequests: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
178
180
|
specialRequestsByType: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
@@ -202,13 +204,4 @@ export type InferredCommandSectionsConfig = z.infer<typeof commandSectionsSchema
|
|
|
202
204
|
export type InferredProjectConfig = z.infer<typeof projectConfigSchema>;
|
|
203
205
|
/** 설정 파일명 */
|
|
204
206
|
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>;
|
|
214
207
|
//# sourceMappingURL=schema.d.ts.map
|