vibe-commander 0.1.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/LICENSE +21 -0
- package/dist/adapters/cli/commands/context-resolver.d.ts +29 -0
- package/dist/adapters/cli/commands/context-resolver.js +93 -0
- package/dist/adapters/cli/commands/context.d.ts +26 -0
- package/dist/adapters/cli/commands/context.js +39 -0
- package/dist/adapters/cli/commands/git-helpers.d.ts +43 -0
- package/dist/adapters/cli/commands/git-helpers.js +99 -0
- package/dist/adapters/cli/commands/init-helpers.d.ts +30 -0
- package/dist/adapters/cli/commands/init-helpers.js +112 -0
- package/dist/adapters/cli/commands/init-interactive.d.ts +25 -0
- package/dist/adapters/cli/commands/init-interactive.js +143 -0
- package/dist/adapters/cli/commands/init.d.ts +25 -0
- package/dist/adapters/cli/commands/init.js +73 -0
- package/dist/adapters/cli/commands/io-helpers.d.ts +45 -0
- package/dist/adapters/cli/commands/io-helpers.js +144 -0
- package/dist/adapters/cli/commands/list-units.d.ts +39 -0
- package/dist/adapters/cli/commands/list-units.js +92 -0
- package/dist/adapters/cli/commands/prompt-helpers.d.ts +36 -0
- package/dist/adapters/cli/commands/prompt-helpers.js +113 -0
- package/dist/adapters/cli/commands/set-unit.d.ts +44 -0
- package/dist/adapters/cli/commands/set-unit.js +90 -0
- package/dist/adapters/cli/commands/skill-install.d.ts +31 -0
- package/dist/adapters/cli/commands/skill-install.js +85 -0
- package/dist/adapters/cli/commands/state-helpers.d.ts +28 -0
- package/dist/adapters/cli/commands/state-helpers.js +55 -0
- package/dist/adapters/cli/commands/update-commit.d.ts +42 -0
- package/dist/adapters/cli/commands/update-commit.js +180 -0
- package/dist/adapters/cli/commands/validate.d.ts +40 -0
- package/dist/adapters/cli/commands/validate.js +219 -0
- package/dist/adapters/cli/formatters-list.d.ts +36 -0
- package/dist/adapters/cli/formatters-list.js +106 -0
- package/dist/adapters/cli/formatters-unit.d.ts +44 -0
- package/dist/adapters/cli/formatters-unit.js +287 -0
- package/dist/adapters/cli/formatters.d.ts +2 -0
- package/dist/adapters/cli/formatters.js +3 -0
- package/dist/adapters/cli/index.d.ts +17 -0
- package/dist/adapters/cli/index.js +60 -0
- package/dist/adapters/cli/output.d.ts +58 -0
- package/dist/adapters/cli/output.js +159 -0
- package/dist/adapters/cli/router.d.ts +99 -0
- package/dist/adapters/cli/router.js +419 -0
- package/dist/adapters/cli/scanner.d.ts +19 -0
- package/dist/adapters/cli/scanner.js +227 -0
- package/dist/adapters/pipe/stdin-reader.d.ts +23 -0
- package/dist/adapters/pipe/stdin-reader.js +56 -0
- package/dist/config/loader.d.ts +27 -0
- package/dist/config/loader.js +84 -0
- package/dist/config/resolver.d.ts +71 -0
- package/dist/config/resolver.js +124 -0
- package/dist/config/schema.d.ts +205 -0
- package/dist/config/schema.js +186 -0
- package/dist/core/parsers/backlog-parser.d.ts +25 -0
- package/dist/core/parsers/backlog-parser.js +255 -0
- package/dist/core/parsers/dep-line-parser.d.ts +36 -0
- package/dist/core/parsers/dep-line-parser.js +176 -0
- package/dist/core/parsers/dependency-extractor.d.ts +64 -0
- package/dist/core/parsers/dependency-extractor.js +193 -0
- package/dist/core/parsers/index.d.ts +20 -0
- package/dist/core/parsers/index.js +20 -0
- package/dist/core/parsers/md-utils.d.ts +81 -0
- package/dist/core/parsers/md-utils.js +144 -0
- package/dist/core/parsers/metadata-parser.d.ts +49 -0
- package/dist/core/parsers/metadata-parser.js +133 -0
- package/dist/core/parsers/pairing-question-extractor.d.ts +42 -0
- package/dist/core/parsers/pairing-question-extractor.js +146 -0
- package/dist/core/parsers/plan-parser-helpers.d.ts +61 -0
- package/dist/core/parsers/plan-parser-helpers.js +90 -0
- package/dist/core/parsers/plan-parser.d.ts +55 -0
- package/dist/core/parsers/plan-parser.js +69 -0
- package/dist/core/parsers/title-extractor.d.ts +36 -0
- package/dist/core/parsers/title-extractor.js +101 -0
- package/dist/core/renderers/index.d.ts +15 -0
- package/dist/core/renderers/index.js +18 -0
- package/dist/core/renderers/interpolate.d.ts +92 -0
- package/dist/core/renderers/interpolate.js +117 -0
- package/dist/core/renderers/marker-utils.d.ts +60 -0
- package/dist/core/renderers/marker-utils.js +85 -0
- package/dist/core/renderers/section-renderer.d.ts +31 -0
- package/dist/core/renderers/section-renderer.js +171 -0
- package/dist/core/renderers/section-updater.d.ts +72 -0
- package/dist/core/renderers/section-updater.js +175 -0
- package/dist/core/renderers/template-engine.d.ts +69 -0
- package/dist/core/renderers/template-engine.js +92 -0
- package/dist/core/renderers/updater-helpers.d.ts +45 -0
- package/dist/core/renderers/updater-helpers.js +83 -0
- package/dist/core/resolvers/commit-filter.d.ts +84 -0
- package/dist/core/resolvers/commit-filter.js +95 -0
- package/dist/core/resolvers/config-generator.d.ts +32 -0
- package/dist/core/resolvers/config-generator.js +112 -0
- package/dist/core/resolvers/config-merger.d.ts +26 -0
- package/dist/core/resolvers/config-merger.js +89 -0
- package/dist/core/resolvers/config-validator.d.ts +42 -0
- package/dist/core/resolvers/config-validator.js +61 -0
- package/dist/core/resolvers/dep-commit-resolver.d.ts +63 -0
- package/dist/core/resolvers/dep-commit-resolver.js +158 -0
- package/dist/core/resolvers/dep-doc-resolver.d.ts +70 -0
- package/dist/core/resolvers/dep-doc-resolver.js +84 -0
- package/dist/core/resolvers/index.d.ts +15 -0
- package/dist/core/resolvers/index.js +12 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +8 -0
- package/dist/types/config.d.ts +10 -0
- package/dist/types/config.js +10 -0
- package/dist/types/context.d.ts +55 -0
- package/dist/types/context.js +10 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.js +10 -0
- package/dist/types/init.d.ts +56 -0
- package/dist/types/init.js +10 -0
- package/dist/types/tool-result.d.ts +75 -0
- package/dist/types/tool-result.js +40 -0
- package/dist/types/unit.d.ts +118 -0
- package/dist/types/unit.js +10 -0
- package/package.json +71 -0
- package/skills/claude/SKILL.md +375 -0
- package/skills/common/cli-reference.md +251 -0
- package/skills/cursor/SKILL.md +353 -0
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 메타데이터 테이블 파서 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* 계획서 마크다운의 메타데이터 테이블(2-column key-value)에서
|
|
5
|
+
* 유닛 메타정보를 추출한다.
|
|
6
|
+
*
|
|
7
|
+
* 설정의 `metadataTable` 필드명으로 시맨틱 키(unitId, phase, depends)에 매핑.
|
|
8
|
+
* 테이블이 없으면 모든 필드가 null인 결과 반환 (Graceful Degradation).
|
|
9
|
+
*
|
|
10
|
+
* @module
|
|
11
|
+
*/
|
|
12
|
+
import { ok } from '../../types/index.js';
|
|
13
|
+
import { normalizeLineEndings, isNoneDependency } from './md-utils.js';
|
|
14
|
+
/**
|
|
15
|
+
* 마크다운 2-column 테이블에서 메타데이터를 파싱한다
|
|
16
|
+
*
|
|
17
|
+
* 첫 번째 2-column 테이블을 찾아 key-value 쌍으로 추출하고,
|
|
18
|
+
* 설정의 필드명으로 시맨틱 키(unitId, phase, depends)에 매핑한다.
|
|
19
|
+
*
|
|
20
|
+
* @param content - 마크다운 전체 내용
|
|
21
|
+
* @param tableConfig - 메타데이터 테이블 필드명 매핑 설정
|
|
22
|
+
* @returns 파싱된 메타데이터 결과를 포함한 ToolResult
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const content = '| 항목 | 내용 |\n|---|---|\n| Unit ID | U-005 |\n| Phase | MVP |';
|
|
27
|
+
* const config = { idField: 'Unit ID', phaseField: 'Phase', dependsField: '의존성' };
|
|
28
|
+
* const result = parseMetadataTable(content, config);
|
|
29
|
+
* if (result.success) {
|
|
30
|
+
* // result.data.unitId === 'U-005'
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export function parseMetadataTable(content, tableConfig) {
|
|
35
|
+
const normalized = normalizeLineEndings(content);
|
|
36
|
+
const raw = extractFirstTwoColumnTable(normalized);
|
|
37
|
+
const dependsRaw = raw[tableConfig.dependsField];
|
|
38
|
+
const depends = dependsRaw
|
|
39
|
+
? dependsRaw
|
|
40
|
+
.split(',')
|
|
41
|
+
.map((s) => s.trim())
|
|
42
|
+
.filter((s) => !isNoneDependency(s))
|
|
43
|
+
: [];
|
|
44
|
+
return ok({
|
|
45
|
+
unitId: raw[tableConfig.idField],
|
|
46
|
+
phase: raw[tableConfig.phaseField],
|
|
47
|
+
depends,
|
|
48
|
+
raw,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
// ── 내부 헬퍼 ──
|
|
52
|
+
/** separator 행 패턴 (|---|---| 형식, 정렬 콜론 허용) */
|
|
53
|
+
const SEPARATOR_RE = /^\|(\s*:?-+:?\s*\|)+$/;
|
|
54
|
+
/**
|
|
55
|
+
* 라인이 테이블 행인지 판별한다
|
|
56
|
+
*
|
|
57
|
+
* `|`로 시작하고 `|`로 끝나는 행을 테이블 행으로 인식.
|
|
58
|
+
*/
|
|
59
|
+
function isTableRow(line) {
|
|
60
|
+
return line.startsWith('|') && line.endsWith('|');
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 라인이 테이블 separator 행인지 판별한다
|
|
64
|
+
*
|
|
65
|
+
* `|---|---|` 패턴을 매칭. 정렬 콜론(`:---:`, `:---`, `---:`) 허용.
|
|
66
|
+
*/
|
|
67
|
+
function isSeparatorRow(line) {
|
|
68
|
+
return SEPARATOR_RE.test(line);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 테이블 행에서 셀 값을 추출한다
|
|
72
|
+
*
|
|
73
|
+
* 양쪽 `|`를 제거하고 `|`로 분리한 뒤 각 셀을 trim.
|
|
74
|
+
*/
|
|
75
|
+
function parseTableCells(line) {
|
|
76
|
+
return line
|
|
77
|
+
.slice(1, -1)
|
|
78
|
+
.split('|')
|
|
79
|
+
.map((c) => c.trim());
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* 문서에서 첫 번째 2-column 테이블의 key-value 맵을 추출한다
|
|
83
|
+
*
|
|
84
|
+
* 알고리즘:
|
|
85
|
+
* 1. separator 행은 헤더 후보에서 제외
|
|
86
|
+
* 2. 2개 컬럼의 헤더 행 탐색
|
|
87
|
+
* 3. 바로 다음 행이 separator인지 확인
|
|
88
|
+
* 4. separator 이후 데이터 행들을 key-value로 파싱
|
|
89
|
+
* 5. 비테이블 행 또는 새 separator를 만나면 종료
|
|
90
|
+
*/
|
|
91
|
+
function extractFirstTwoColumnTable(content) {
|
|
92
|
+
const lines = content.split('\n');
|
|
93
|
+
const raw = {};
|
|
94
|
+
for (let i = 0; i < lines.length; i++) {
|
|
95
|
+
const lineRaw = lines[i];
|
|
96
|
+
if (lineRaw === undefined)
|
|
97
|
+
continue;
|
|
98
|
+
const line = lineRaw.trim();
|
|
99
|
+
// 테이블 행이 아니거나 separator면 건너뜀
|
|
100
|
+
if (!isTableRow(line))
|
|
101
|
+
continue;
|
|
102
|
+
if (isSeparatorRow(line))
|
|
103
|
+
continue;
|
|
104
|
+
// 2-column 헤더 행 확인
|
|
105
|
+
const headerCells = parseTableCells(line);
|
|
106
|
+
if (headerCells.length !== 2)
|
|
107
|
+
continue;
|
|
108
|
+
// 다음 행: separator 확인
|
|
109
|
+
const nextLine = lines[i + 1]?.trim();
|
|
110
|
+
if (!nextLine || !isSeparatorRow(nextLine))
|
|
111
|
+
continue;
|
|
112
|
+
// separator 이후 데이터 행 파싱
|
|
113
|
+
for (let j = i + 2; j < lines.length; j++) {
|
|
114
|
+
const dataLineRaw = lines[j];
|
|
115
|
+
if (dataLineRaw === undefined)
|
|
116
|
+
break;
|
|
117
|
+
const dataLine = dataLineRaw.trim();
|
|
118
|
+
if (!isTableRow(dataLine))
|
|
119
|
+
break;
|
|
120
|
+
if (isSeparatorRow(dataLine))
|
|
121
|
+
break;
|
|
122
|
+
const cells = parseTableCells(dataLine);
|
|
123
|
+
const key = cells[0];
|
|
124
|
+
const value = cells[1];
|
|
125
|
+
if (key !== undefined && key !== '' && value !== undefined && value !== '') {
|
|
126
|
+
raw[key] = value;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
break; // 첫 번째 2-column 테이블만 파싱
|
|
130
|
+
}
|
|
131
|
+
return raw;
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=metadata-parser.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 페어링 질문 추출기 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* 계획서 마크다운에서 페어링 질문(개발자 결정 필요 항목)을 추출한다.
|
|
5
|
+
* 체크박스(`- [ ]`/`- [x]`) 형식으로 결정 여부를 파악하고,
|
|
6
|
+
* 선택된 옵션(✅ 마커)도 함께 추출한다.
|
|
7
|
+
*
|
|
8
|
+
* PRD §4.2: PairingQuestion 데이터 모델
|
|
9
|
+
* set-unit 시 미결정 질문 목록을 표시하고, 결정된 질문은 건너뛴다.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { PairingQuestion, PlanParsingConfig, ToolResult } from '../../types/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* 계획서에서 페어링 질문을 추출한다
|
|
16
|
+
*
|
|
17
|
+
* 지정된 섹션명(config.pairingQuestionSection)에 해당하는 섹션을 찾아
|
|
18
|
+
* 체크박스 기반 질문 목록을 파싱한다.
|
|
19
|
+
*
|
|
20
|
+
* 지원하는 질문 형식:
|
|
21
|
+
* - `- [ ] **Q1**: 질문 텍스트` — 미결정
|
|
22
|
+
* - `- [x] **Q2**: 결정된 질문` — 결정됨
|
|
23
|
+
* - ` - ✅Option A: 선택된 옵션` — 선택된 옵션 마커
|
|
24
|
+
*
|
|
25
|
+
* 섹션이 없거나 질문이 없으면 빈 배열 반환 (Graceful Degradation).
|
|
26
|
+
*
|
|
27
|
+
* @param content - 계획서 마크다운 전체 내용
|
|
28
|
+
* @param config - 계획서 파싱 설정 (pairingQuestionSection 사용)
|
|
29
|
+
* @returns 추출된 페어링 질문 배열을 포함한 ToolResult. 질문이 없으면 빈 배열(success: true)
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* const content = '## 페어링 질문 (결정 필요)\n\n- [x] **Q1**: 전략 선택\n - ✅Option A: 선택됨';
|
|
34
|
+
* const config = { pairingQuestionSection: '페어링 질문' };
|
|
35
|
+
* const result = extractPairingQuestions(content, config as any);
|
|
36
|
+
* if (result.success) {
|
|
37
|
+
* // result.data → [{ id: 'Q1', text: '전략 선택', resolved: true, selectedOption: 'Option A: 선택됨' }]
|
|
38
|
+
* }
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractPairingQuestions(content: string, config: PlanParsingConfig): ToolResult<PairingQuestion[]>;
|
|
42
|
+
//# sourceMappingURL=pairing-question-extractor.d.ts.map
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 페어링 질문 추출기 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* 계획서 마크다운에서 페어링 질문(개발자 결정 필요 항목)을 추출한다.
|
|
5
|
+
* 체크박스(`- [ ]`/`- [x]`) 형식으로 결정 여부를 파악하고,
|
|
6
|
+
* 선택된 옵션(✅ 마커)도 함께 추출한다.
|
|
7
|
+
*
|
|
8
|
+
* PRD §4.2: PairingQuestion 데이터 모델
|
|
9
|
+
* set-unit 시 미결정 질문 목록을 표시하고, 결정된 질문은 건너뛴다.
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import { ok } from '../../types/index.js';
|
|
14
|
+
import { findSectionLines, normalizeLineEndings } from './md-utils.js';
|
|
15
|
+
/**
|
|
16
|
+
* 질문 라인 정규식 패턴
|
|
17
|
+
*
|
|
18
|
+
* 캡처 그룹:
|
|
19
|
+
* - checked: 체크 상태 ('x', 'X', 'v', 'V', 공백 등)
|
|
20
|
+
* - id: 질문 ID (예: "Q1", "Q2", "Question 1")
|
|
21
|
+
* - text: 질문 본문
|
|
22
|
+
*
|
|
23
|
+
* 지원 형식:
|
|
24
|
+
* - `- [ ] **Q1**: 텍스트`
|
|
25
|
+
* - `- [x] **Q2**: 텍스트`
|
|
26
|
+
* - `* [v] **Q3**: 텍스트` (v 마커 지원)
|
|
27
|
+
* - `- [ ] **Q4** 텍스트` (여러 공백, 구분자 생략 지원)
|
|
28
|
+
*/
|
|
29
|
+
const QUESTION_LINE = /^[-*]\s+\[(?<checked>[xXvV ]*)\]\s+\*\*(?<id>[^*]+)\*\*\s*[:\-—]?\s*(?<text>.+)$/;
|
|
30
|
+
/**
|
|
31
|
+
* 선택된 옵션 라인 정규식 패턴 (✅ 마커가 시작 위치)
|
|
32
|
+
*
|
|
33
|
+
* - ` - ✅Option A: 설명`
|
|
34
|
+
* - ` * ✅ Option B: 설명`
|
|
35
|
+
*/
|
|
36
|
+
const SELECTED_OPTION_START = /^[-*]\s+✅\s*(?<option>.+)$/;
|
|
37
|
+
/**
|
|
38
|
+
* 하위 옵션 라인 정규식 패턴 (리스트 항목)
|
|
39
|
+
*
|
|
40
|
+
* 들여쓰인 모든 리스트 항목을 캡처하여 옵션 목록 구성에 사용.
|
|
41
|
+
* - ` - Option A: 설명`
|
|
42
|
+
* - ` * Option B: 설명 ✅`
|
|
43
|
+
*/
|
|
44
|
+
const SUB_OPTION_LINE = /^[-*]\s+(?<optionText>.+)$/;
|
|
45
|
+
/**
|
|
46
|
+
* 계획서에서 페어링 질문을 추출한다
|
|
47
|
+
*
|
|
48
|
+
* 지정된 섹션명(config.pairingQuestionSection)에 해당하는 섹션을 찾아
|
|
49
|
+
* 체크박스 기반 질문 목록을 파싱한다.
|
|
50
|
+
*
|
|
51
|
+
* 지원하는 질문 형식:
|
|
52
|
+
* - `- [ ] **Q1**: 질문 텍스트` — 미결정
|
|
53
|
+
* - `- [x] **Q2**: 결정된 질문` — 결정됨
|
|
54
|
+
* - ` - ✅Option A: 선택된 옵션` — 선택된 옵션 마커
|
|
55
|
+
*
|
|
56
|
+
* 섹션이 없거나 질문이 없으면 빈 배열 반환 (Graceful Degradation).
|
|
57
|
+
*
|
|
58
|
+
* @param content - 계획서 마크다운 전체 내용
|
|
59
|
+
* @param config - 계획서 파싱 설정 (pairingQuestionSection 사용)
|
|
60
|
+
* @returns 추출된 페어링 질문 배열을 포함한 ToolResult. 질문이 없으면 빈 배열(success: true)
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const content = '## 페어링 질문 (결정 필요)\n\n- [x] **Q1**: 전략 선택\n - ✅Option A: 선택됨';
|
|
65
|
+
* const config = { pairingQuestionSection: '페어링 질문' };
|
|
66
|
+
* const result = extractPairingQuestions(content, config as any);
|
|
67
|
+
* if (result.success) {
|
|
68
|
+
* // result.data → [{ id: 'Q1', text: '전략 선택', resolved: true, selectedOption: 'Option A: 선택됨' }]
|
|
69
|
+
* }
|
|
70
|
+
* ```
|
|
71
|
+
*/
|
|
72
|
+
export function extractPairingQuestions(content, config) {
|
|
73
|
+
const normalized = normalizeLineEndings(content);
|
|
74
|
+
const sectionLines = findSectionLines(normalized, config.pairingQuestionSection);
|
|
75
|
+
if (sectionLines.length === 0)
|
|
76
|
+
return ok([]);
|
|
77
|
+
return ok(parseQuestionLines(sectionLines));
|
|
78
|
+
}
|
|
79
|
+
// ── 내부 헬퍼 ──
|
|
80
|
+
/**
|
|
81
|
+
* 섹션 내 라인들에서 페어링 질문을 파싱한다
|
|
82
|
+
*
|
|
83
|
+
* 상태 머신 패턴으로 라인을 순회하며:
|
|
84
|
+
* 1. 최상위 체크박스 라인 → 새 질문 시작
|
|
85
|
+
* 2. 들여쓰인 ✅ 라인 → 선택된 옵션 기록
|
|
86
|
+
*
|
|
87
|
+
* 해결되지 않은(unresolved) 질문에 ✅ 옵션이 있어도 무시한다.
|
|
88
|
+
*
|
|
89
|
+
* @param lines - 섹션 내 라인 배열
|
|
90
|
+
* @returns 파싱된 PairingQuestion 배열
|
|
91
|
+
*/
|
|
92
|
+
function parseQuestionLines(lines) {
|
|
93
|
+
const questions = [];
|
|
94
|
+
let current = null;
|
|
95
|
+
for (const line of lines) {
|
|
96
|
+
const trimmed = line.trim();
|
|
97
|
+
if (trimmed === '')
|
|
98
|
+
continue;
|
|
99
|
+
// 최상위 리스트 항목 판별: 들여쓰기 없는 라인
|
|
100
|
+
const isTopLevel = !/^\s{2,}/.test(line);
|
|
101
|
+
if (isTopLevel) {
|
|
102
|
+
const qMatch = trimmed.match(QUESTION_LINE);
|
|
103
|
+
if (qMatch?.groups) {
|
|
104
|
+
if (current)
|
|
105
|
+
questions.push(current);
|
|
106
|
+
const { checked, id, text } = qMatch.groups;
|
|
107
|
+
const mark = (checked ?? '').toLowerCase();
|
|
108
|
+
current = {
|
|
109
|
+
id: (id ?? '').trim(),
|
|
110
|
+
text: (text ?? '').trim(),
|
|
111
|
+
resolved: mark.includes('x') || mark.includes('v'),
|
|
112
|
+
selectedOption: undefined,
|
|
113
|
+
options: [],
|
|
114
|
+
};
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// 들여쓰인 하위 항목 — 옵션 수집 및 선택 옵션 감지
|
|
119
|
+
if (current && /^\s{2,}/.test(line)) {
|
|
120
|
+
const optMatch = trimmed.match(SUB_OPTION_LINE);
|
|
121
|
+
if (optMatch?.groups?.optionText) {
|
|
122
|
+
const rawText = optMatch.groups.optionText.trim();
|
|
123
|
+
const cleanText = rawText
|
|
124
|
+
.replace(/^\s*✅\s*/, '')
|
|
125
|
+
.replace(/\s*✅\s*$/, '')
|
|
126
|
+
.trim();
|
|
127
|
+
if (!current.options)
|
|
128
|
+
current.options = [];
|
|
129
|
+
current.options.push(cleanText);
|
|
130
|
+
if (current.resolved) {
|
|
131
|
+
const startMatch = trimmed.match(SELECTED_OPTION_START);
|
|
132
|
+
if (startMatch?.groups?.option) {
|
|
133
|
+
current.selectedOption = startMatch.groups.option.replace(/\s*✅\s*$/, '').trim();
|
|
134
|
+
}
|
|
135
|
+
else if (rawText.endsWith('✅')) {
|
|
136
|
+
current.selectedOption = cleanText;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (current)
|
|
143
|
+
questions.push(current);
|
|
144
|
+
return questions;
|
|
145
|
+
}
|
|
146
|
+
//# sourceMappingURL=pairing-question-extractor.js.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 계획서 파서 내부 헬퍼 함수 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { DepInfo, PairingQuestion, PlanParsingConfig } from '../../types/index.js';
|
|
7
|
+
/**
|
|
8
|
+
* 제목을 추출하고, 실패 시 unitId로 대체한다
|
|
9
|
+
*
|
|
10
|
+
* extractTitle 결과가 undefined이거나 빈 문자열이면
|
|
11
|
+
* unitId를 제목으로 사용하고 경고를 기록한다.
|
|
12
|
+
*
|
|
13
|
+
* @param content - 마크다운 내용
|
|
14
|
+
* @param titleSource - 제목 추출 소스 설정
|
|
15
|
+
* @param unitId - 대체 제목으로 사용할 유닛 ID
|
|
16
|
+
* @param warnings - 경고 메시지를 추가할 배열 (mutated)
|
|
17
|
+
* @returns 추출된 제목 또는 unitId
|
|
18
|
+
*/
|
|
19
|
+
export declare function extractTitleWithFallback(content: string, titleSource: string, unitId: string, warnings: string[]): string;
|
|
20
|
+
/**
|
|
21
|
+
* 메타데이터 테이블에서 phase를 추출한다
|
|
22
|
+
*
|
|
23
|
+
* phase는 선택적 필드이므로 추출 실패 시 경고 없이 undefined 반환.
|
|
24
|
+
*
|
|
25
|
+
* @param content - 마크다운 내용
|
|
26
|
+
* @param config - 파싱 설정 (metadataTable 사용)
|
|
27
|
+
* @returns phase 문자열 또는 undefined
|
|
28
|
+
*/
|
|
29
|
+
export declare function extractPhase(content: string, config: PlanParsingConfig): string | undefined;
|
|
30
|
+
/** 의존성 추출 결과 (유닛 의존성 + 비유닛 컨텍스트 항목) */
|
|
31
|
+
export interface DependencyExtractionResult {
|
|
32
|
+
depends: DepInfo[];
|
|
33
|
+
contextNotes: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 의존성과 비유닛 컨텍스트 항목을 추출하고, 실패 시 경고를 기록한다
|
|
37
|
+
*
|
|
38
|
+
* extractDependenciesWithNotes는 내부 Fallback 체인으로 복수 전략을 시도하므로
|
|
39
|
+
* 여기서는 최종 결과만 확인한다.
|
|
40
|
+
* 결과가 빈 배열이면 경고 없음 (의존성 없는 유닛일 수 있음).
|
|
41
|
+
*
|
|
42
|
+
* @param content - 마크다운 내용
|
|
43
|
+
* @param config - 파싱 설정
|
|
44
|
+
* @param idPattern - ID 매칭 패턴 (선택)
|
|
45
|
+
* @param warnings - 경고 배열 (mutated)
|
|
46
|
+
* @returns 유닛 의존성과 비유닛 컨텍스트 항목
|
|
47
|
+
*/
|
|
48
|
+
export declare function extractDependsWithWarning(content: string, config: PlanParsingConfig, idPattern: string | undefined, warnings: string[]): DependencyExtractionResult;
|
|
49
|
+
/**
|
|
50
|
+
* 페어링 질문을 추출하고, 실패 시 경고를 기록한다
|
|
51
|
+
*
|
|
52
|
+
* extractPairingQuestions가 실패하면 빈 배열로 대체하고 경고를 기록한다.
|
|
53
|
+
* 섹션이 없어 빈 배열이 반환되는 것은 정상 동작 (경고 없음).
|
|
54
|
+
*
|
|
55
|
+
* @param content - 마크다운 내용
|
|
56
|
+
* @param config - 파싱 설정
|
|
57
|
+
* @param warnings - 경고 배열 (mutated)
|
|
58
|
+
* @returns 추출된 페어링 질문 배열
|
|
59
|
+
*/
|
|
60
|
+
export declare function extractQuestionsWithWarning(content: string, config: PlanParsingConfig, warnings: string[]): PairingQuestion[];
|
|
61
|
+
//# sourceMappingURL=plan-parser-helpers.d.ts.map
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 계획서 파서 내부 헬퍼 함수 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { extractTitle } from './title-extractor.js';
|
|
7
|
+
import { parseMetadataTable } from './metadata-parser.js';
|
|
8
|
+
import { extractDependenciesWithNotes } from './dependency-extractor.js';
|
|
9
|
+
import { extractPairingQuestions } from './pairing-question-extractor.js';
|
|
10
|
+
/**
|
|
11
|
+
* 제목을 추출하고, 실패 시 unitId로 대체한다
|
|
12
|
+
*
|
|
13
|
+
* extractTitle 결과가 undefined이거나 빈 문자열이면
|
|
14
|
+
* unitId를 제목으로 사용하고 경고를 기록한다.
|
|
15
|
+
*
|
|
16
|
+
* @param content - 마크다운 내용
|
|
17
|
+
* @param titleSource - 제목 추출 소스 설정
|
|
18
|
+
* @param unitId - 대체 제목으로 사용할 유닛 ID
|
|
19
|
+
* @param warnings - 경고 메시지를 추가할 배열 (mutated)
|
|
20
|
+
* @returns 추출된 제목 또는 unitId
|
|
21
|
+
*/
|
|
22
|
+
export function extractTitleWithFallback(content, titleSource, unitId, warnings) {
|
|
23
|
+
const result = extractTitle(content, titleSource);
|
|
24
|
+
if (result.success && result.data !== undefined && result.data !== '') {
|
|
25
|
+
return result.data;
|
|
26
|
+
}
|
|
27
|
+
// Fallback: unitId를 제목으로 사용
|
|
28
|
+
warnings.push(`제목 추출 실패: titleSource="${titleSource}" 설정 확인. unitId를 제목으로 대체합니다.`);
|
|
29
|
+
return unitId;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 메타데이터 테이블에서 phase를 추출한다
|
|
33
|
+
*
|
|
34
|
+
* phase는 선택적 필드이므로 추출 실패 시 경고 없이 undefined 반환.
|
|
35
|
+
*
|
|
36
|
+
* @param content - 마크다운 내용
|
|
37
|
+
* @param config - 파싱 설정 (metadataTable 사용)
|
|
38
|
+
* @returns phase 문자열 또는 undefined
|
|
39
|
+
*/
|
|
40
|
+
export function extractPhase(content, config) {
|
|
41
|
+
const tableResult = parseMetadataTable(content, config.metadataTable);
|
|
42
|
+
if (tableResult.success && tableResult.data.phase !== undefined) {
|
|
43
|
+
return tableResult.data.phase;
|
|
44
|
+
}
|
|
45
|
+
// phase는 선택적 필드 — 경고 없이 undefined 반환
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* 의존성과 비유닛 컨텍스트 항목을 추출하고, 실패 시 경고를 기록한다
|
|
50
|
+
*
|
|
51
|
+
* extractDependenciesWithNotes는 내부 Fallback 체인으로 복수 전략을 시도하므로
|
|
52
|
+
* 여기서는 최종 결과만 확인한다.
|
|
53
|
+
* 결과가 빈 배열이면 경고 없음 (의존성 없는 유닛일 수 있음).
|
|
54
|
+
*
|
|
55
|
+
* @param content - 마크다운 내용
|
|
56
|
+
* @param config - 파싱 설정
|
|
57
|
+
* @param idPattern - ID 매칭 패턴 (선택)
|
|
58
|
+
* @param warnings - 경고 배열 (mutated)
|
|
59
|
+
* @returns 유닛 의존성과 비유닛 컨텍스트 항목
|
|
60
|
+
*/
|
|
61
|
+
export function extractDependsWithWarning(content, config, idPattern, warnings) {
|
|
62
|
+
const result = extractDependenciesWithNotes(content, config, idPattern);
|
|
63
|
+
if (result.success) {
|
|
64
|
+
return { depends: result.data.deps, contextNotes: result.data.contextNotes };
|
|
65
|
+
}
|
|
66
|
+
// 의존성 추출 실패 — 빈 결과 + 경고
|
|
67
|
+
warnings.push(`의존성 추출 실패: ${result.error.message}`);
|
|
68
|
+
return { depends: [], contextNotes: [] };
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 페어링 질문을 추출하고, 실패 시 경고를 기록한다
|
|
72
|
+
*
|
|
73
|
+
* extractPairingQuestions가 실패하면 빈 배열로 대체하고 경고를 기록한다.
|
|
74
|
+
* 섹션이 없어 빈 배열이 반환되는 것은 정상 동작 (경고 없음).
|
|
75
|
+
*
|
|
76
|
+
* @param content - 마크다운 내용
|
|
77
|
+
* @param config - 파싱 설정
|
|
78
|
+
* @param warnings - 경고 배열 (mutated)
|
|
79
|
+
* @returns 추출된 페어링 질문 배열
|
|
80
|
+
*/
|
|
81
|
+
export function extractQuestionsWithWarning(content, config, warnings) {
|
|
82
|
+
const result = extractPairingQuestions(content, config);
|
|
83
|
+
if (result.success) {
|
|
84
|
+
return result.data;
|
|
85
|
+
}
|
|
86
|
+
// 페어링 질문 추출 실패 — 빈 배열 + 경고
|
|
87
|
+
warnings.push(`페어링 질문 추출 실패: ${result.error.message}`);
|
|
88
|
+
return [];
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=plan-parser-helpers.js.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 통합 계획서 파서 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* 개별 추출기(제목/메타데이터/의존성/페어링 질문)를 **조합**하여
|
|
5
|
+
* 계획서 마크다운을 통합 파싱한다.
|
|
6
|
+
*
|
|
7
|
+
* Core 파서의 메인 엔트리포인트로, `set-unit` 등 상위 레이어에서 호출.
|
|
8
|
+
*
|
|
9
|
+
* 부분 실패 시에도 찾은 결과를 반환하고 warnings 배열로 실패 내용을 명시한다
|
|
10
|
+
* (Graceful Degradation — PRD 원칙 3).
|
|
11
|
+
*
|
|
12
|
+
* 페어링 질문 Q1 결정: Option A (success: true + warnings 배열)
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import type { PairingQuestion, PlanParsingConfig, ToolResult, UnitMeta } from '../../types/index.js';
|
|
17
|
+
/**
|
|
18
|
+
* parseUnitPlan 반환 타입
|
|
19
|
+
*
|
|
20
|
+
* 계획서에서 추출한 메타데이터, 페어링 질문, 경고 목록을 포함.
|
|
21
|
+
* warnings가 비어 있으면 모든 추출이 성공한 것.
|
|
22
|
+
*/
|
|
23
|
+
export interface ParseUnitPlanResult {
|
|
24
|
+
/** 유닛 메타데이터 (제목, phase, 의존성 등) */
|
|
25
|
+
meta: UnitMeta;
|
|
26
|
+
/** 페어링 질문 목록 (결정/미결정 모두 포함) */
|
|
27
|
+
questions: PairingQuestion[];
|
|
28
|
+
/** 부분 실패 시 경고 메시지 목록 (Graceful Degradation) */
|
|
29
|
+
warnings: string[];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 계획서 마크다운을 통합 파싱한다
|
|
33
|
+
*
|
|
34
|
+
* 개별 추출기를 순차적으로 호출하여 결과를 조합한다:
|
|
35
|
+
* 1. `extractTitle` → `UnitMeta.title` (실패 시 unitId로 대체)
|
|
36
|
+
* 2. `parseMetadataTable` → `UnitMeta.phase` (보조 소스)
|
|
37
|
+
* 3. `extractDependencies` → `UnitMeta.depends`
|
|
38
|
+
* 4. `extractPairingQuestions` → `questions`
|
|
39
|
+
*
|
|
40
|
+
* **Graceful Degradation** (PRD 원칙 3):
|
|
41
|
+
* - 제목 없으면 → unitId를 제목으로 사용 + 경고
|
|
42
|
+
* - 의존성 추출 실패 → 빈 배열 + 경고
|
|
43
|
+
* - 페어링 질문 추출 실패 → 빈 배열 + 경고
|
|
44
|
+
* - 빈 콘텐츠도 최소 결과를 반환
|
|
45
|
+
*
|
|
46
|
+
* `unitType`과 `planPath`는 Layer 2(Resolver)에서 채워지므로 빈 문자열로 설정.
|
|
47
|
+
*
|
|
48
|
+
* @param content - 계획서 마크다운 전체 내용
|
|
49
|
+
* @param unitId - 유닛 ID (외부에서 제공, 제목 fallback에 사용)
|
|
50
|
+
* @param parsingConfig - 계획서 파싱 설정 (titleSource, dependsSource 등)
|
|
51
|
+
* @param idPattern - 유닛 ID 매칭 정규식 패턴 (선택, extractDependencies 본문 스캔에 사용)
|
|
52
|
+
* @returns 파싱 결과(meta + questions + warnings)를 포함한 ToolResult. 항상 success: true
|
|
53
|
+
*/
|
|
54
|
+
export declare function parseUnitPlan(content: string, unitId: string, parsingConfig: PlanParsingConfig, idPattern?: string): ToolResult<ParseUnitPlanResult>;
|
|
55
|
+
//# sourceMappingURL=plan-parser.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 통합 계획서 파서 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* 개별 추출기(제목/메타데이터/의존성/페어링 질문)를 **조합**하여
|
|
5
|
+
* 계획서 마크다운을 통합 파싱한다.
|
|
6
|
+
*
|
|
7
|
+
* Core 파서의 메인 엔트리포인트로, `set-unit` 등 상위 레이어에서 호출.
|
|
8
|
+
*
|
|
9
|
+
* 부분 실패 시에도 찾은 결과를 반환하고 warnings 배열로 실패 내용을 명시한다
|
|
10
|
+
* (Graceful Degradation — PRD 원칙 3).
|
|
11
|
+
*
|
|
12
|
+
* 페어링 질문 Q1 결정: Option A (success: true + warnings 배열)
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import { ok } from '../../types/index.js';
|
|
17
|
+
import { normalizeLineEndings } from './md-utils.js';
|
|
18
|
+
import { extractTitleWithFallback, extractPhase, extractDependsWithWarning, extractQuestionsWithWarning, } from './plan-parser-helpers.js';
|
|
19
|
+
/**
|
|
20
|
+
* 계획서 마크다운을 통합 파싱한다
|
|
21
|
+
*
|
|
22
|
+
* 개별 추출기를 순차적으로 호출하여 결과를 조합한다:
|
|
23
|
+
* 1. `extractTitle` → `UnitMeta.title` (실패 시 unitId로 대체)
|
|
24
|
+
* 2. `parseMetadataTable` → `UnitMeta.phase` (보조 소스)
|
|
25
|
+
* 3. `extractDependencies` → `UnitMeta.depends`
|
|
26
|
+
* 4. `extractPairingQuestions` → `questions`
|
|
27
|
+
*
|
|
28
|
+
* **Graceful Degradation** (PRD 원칙 3):
|
|
29
|
+
* - 제목 없으면 → unitId를 제목으로 사용 + 경고
|
|
30
|
+
* - 의존성 추출 실패 → 빈 배열 + 경고
|
|
31
|
+
* - 페어링 질문 추출 실패 → 빈 배열 + 경고
|
|
32
|
+
* - 빈 콘텐츠도 최소 결과를 반환
|
|
33
|
+
*
|
|
34
|
+
* `unitType`과 `planPath`는 Layer 2(Resolver)에서 채워지므로 빈 문자열로 설정.
|
|
35
|
+
*
|
|
36
|
+
* @param content - 계획서 마크다운 전체 내용
|
|
37
|
+
* @param unitId - 유닛 ID (외부에서 제공, 제목 fallback에 사용)
|
|
38
|
+
* @param parsingConfig - 계획서 파싱 설정 (titleSource, dependsSource 등)
|
|
39
|
+
* @param idPattern - 유닛 ID 매칭 정규식 패턴 (선택, extractDependencies 본문 스캔에 사용)
|
|
40
|
+
* @returns 파싱 결과(meta + questions + warnings)를 포함한 ToolResult. 항상 success: true
|
|
41
|
+
*/
|
|
42
|
+
export function parseUnitPlan(content, unitId, parsingConfig, idPattern) {
|
|
43
|
+
const normalized = normalizeLineEndings(content);
|
|
44
|
+
const warnings = [];
|
|
45
|
+
// 1. 제목 추출 (실패 시 unitId로 대체)
|
|
46
|
+
const title = extractTitleWithFallback(normalized, parsingConfig.titleSource, unitId, warnings);
|
|
47
|
+
// 2. 메타데이터 테이블에서 phase 추출 (선택적 — 실패해도 경고 없음)
|
|
48
|
+
const phase = extractPhase(normalized, parsingConfig);
|
|
49
|
+
// 3. 의존성 추출 (Fallback 체인 적용)
|
|
50
|
+
const { depends, contextNotes } = extractDependsWithWarning(normalized, parsingConfig, idPattern, warnings);
|
|
51
|
+
// 4. 페어링 질문 추출
|
|
52
|
+
const questions = extractQuestionsWithWarning(normalized, parsingConfig, warnings);
|
|
53
|
+
// 결과 조합: unitType과 planPath는 Layer 2에서 채움
|
|
54
|
+
const meta = {
|
|
55
|
+
unitId,
|
|
56
|
+
unitType: '',
|
|
57
|
+
phase,
|
|
58
|
+
title,
|
|
59
|
+
planPath: '',
|
|
60
|
+
depends,
|
|
61
|
+
contextNotes: contextNotes.length > 0 ? contextNotes : undefined,
|
|
62
|
+
};
|
|
63
|
+
return ok({
|
|
64
|
+
meta,
|
|
65
|
+
questions,
|
|
66
|
+
warnings,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=plan-parser.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 제목 추출기 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* 계획서 마크다운에서 제목을 추출한다.
|
|
5
|
+
* `titleSource` 설정에 따라 H1 헤더 또는 YAML frontmatter에서 가져온다.
|
|
6
|
+
*
|
|
7
|
+
* PRD §8.1 계획서 파싱:
|
|
8
|
+
* titleSource: "h1" → 첫 번째 H1 라인에서 추출
|
|
9
|
+
* titleSource: "frontmatter:title" → YAML frontmatter의 title 필드
|
|
10
|
+
*
|
|
11
|
+
* @module
|
|
12
|
+
*/
|
|
13
|
+
import type { ToolResult } from '../../types/index.js';
|
|
14
|
+
/**
|
|
15
|
+
* 계획서에서 제목을 추출한다
|
|
16
|
+
*
|
|
17
|
+
* `titleSource` 설정값에 따라 추출 전략을 선택:
|
|
18
|
+
* - `"h1"`: 첫 번째 H1 헤더에서 추출
|
|
19
|
+
* - `"frontmatter:필드명"`: YAML frontmatter의 해당 필드에서 추출
|
|
20
|
+
*
|
|
21
|
+
* 못 찾으면 `undefined`를 포함한 성공 결과 반환 (에러가 아닌 부분 실패 — Graceful Degradation).
|
|
22
|
+
*
|
|
23
|
+
* @param content - 계획서 마크다운 전체 내용
|
|
24
|
+
* @param titleSource - 제목 추출 소스 설정 ("h1" | "frontmatter:title" 등)
|
|
25
|
+
* @returns 제목 문자열(또는 undefined)을 포함한 ToolResult
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const result = extractTitle('# U-005: 제목 추출기\n\n본문', 'h1');
|
|
30
|
+
* if (result.success) {
|
|
31
|
+
* // result.data → 'U-005: 제목 추출기'
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare function extractTitle(content: string, titleSource: string): ToolResult<string | undefined>;
|
|
36
|
+
//# sourceMappingURL=title-extractor.d.ts.map
|