vibe-commander 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/cli/commands/init-helpers.d.ts +1 -1
- package/dist/adapters/cli/commands/init-helpers.js +1 -1
- package/dist/adapters/cli/commands/init-interactive.js +3 -3
- package/dist/core/parsers/dep-line-parser.js +82 -25
- package/dist/core/parsers/dependency-extractor.js +48 -5
- package/dist/core/parsers/md-utils.d.ts +1 -1
- package/dist/core/parsers/md-utils.js +2 -2
- package/dist/core/renderers/section-renderer.js +3 -3
- package/dist/core/resolvers/config-generator.d.ts +1 -0
- package/dist/core/resolvers/config-generator.js +2 -1
- package/dist/core/resolvers/config-merger.js +0 -1
- package/package.json +1 -1
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* @module
|
|
11
11
|
*/
|
|
12
12
|
import type { ToolResult, ScanResult, InitAnswers, InitResult } from '../../../types/index.js';
|
|
13
|
-
export { DEFAULT_COMMANDS_PATH, DEFAULT_ROADMAP_PATH, DEFAULT_PLAN_DIR, DEFAULT_RESULT_DIR, DEFAULT_ID_PATTERN, DEFAULT_COMMAND_SECTION, DEFAULT_HEADER_TEMPLATE, extractStdinAnswers, buildAnswersFromScan, } from '../../../core/resolvers/config-generator.js';
|
|
13
|
+
export { DEFAULT_COMMANDS_PATH, DEFAULT_ROADMAP_PATH, DEFAULT_PLAN_DIR, DEFAULT_RESULT_DIR, DEFAULT_RUNBOOK_DIR, DEFAULT_ID_PATTERN, DEFAULT_COMMAND_SECTION, DEFAULT_HEADER_TEMPLATE, extractStdinAnswers, buildAnswersFromScan, } from '../../../core/resolvers/config-generator.js';
|
|
14
14
|
/**
|
|
15
15
|
* 수집된 정보로 설정 객체를 구성하고, 검증 후 파일에 쓴다
|
|
16
16
|
*
|
|
@@ -15,7 +15,7 @@ import { projectConfigSchema } from '../../../config/schema.js';
|
|
|
15
15
|
import { CYAN, YELLOW, GREEN, DIM, RESET, BOLD } from '../output.js';
|
|
16
16
|
import { mergeConfigs } from '../../../core/resolvers/config-merger.js';
|
|
17
17
|
import { buildConfigObject } from '../../../core/resolvers/config-generator.js';
|
|
18
|
-
export { DEFAULT_COMMANDS_PATH, DEFAULT_ROADMAP_PATH, DEFAULT_PLAN_DIR, DEFAULT_RESULT_DIR, DEFAULT_ID_PATTERN, DEFAULT_COMMAND_SECTION, DEFAULT_HEADER_TEMPLATE, extractStdinAnswers, buildAnswersFromScan, } from '../../../core/resolvers/config-generator.js';
|
|
18
|
+
export { DEFAULT_COMMANDS_PATH, DEFAULT_ROADMAP_PATH, DEFAULT_PLAN_DIR, DEFAULT_RESULT_DIR, DEFAULT_RUNBOOK_DIR, DEFAULT_ID_PATTERN, DEFAULT_COMMAND_SECTION, DEFAULT_HEADER_TEMPLATE, extractStdinAnswers, buildAnswersFromScan, } from '../../../core/resolvers/config-generator.js';
|
|
19
19
|
/**
|
|
20
20
|
* 수집된 정보로 설정 객체를 구성하고, 검증 후 파일에 쓴다
|
|
21
21
|
*
|
|
@@ -17,7 +17,7 @@ import { fail } from '../../../types/index.js';
|
|
|
17
17
|
import { CONFIG_FILENAME } from '../../../config/schema.js';
|
|
18
18
|
import { isValidRegex } from '../../../core/resolvers/config-validator.js';
|
|
19
19
|
import { CYAN, YELLOW, DIM, RESET, BOLD } from '../output.js';
|
|
20
|
-
import { DEFAULT_COMMANDS_PATH, DEFAULT_ROADMAP_PATH, DEFAULT_PLAN_DIR, DEFAULT_RESULT_DIR, DEFAULT_ID_PATTERN, DEFAULT_COMMAND_SECTION, buildAnswersFromScan, buildAndWriteConfig, printScanResults, } from './init-helpers.js';
|
|
20
|
+
import { DEFAULT_COMMANDS_PATH, DEFAULT_ROADMAP_PATH, DEFAULT_PLAN_DIR, DEFAULT_RESULT_DIR, DEFAULT_RUNBOOK_DIR, DEFAULT_ID_PATTERN, DEFAULT_COMMAND_SECTION, buildAnswersFromScan, buildAndWriteConfig, printScanResults, } from './init-helpers.js';
|
|
21
21
|
/**
|
|
22
22
|
* 대화형 모드 — readline으로 질문을 수집하고 설정 파일을 생성한다
|
|
23
23
|
*/
|
|
@@ -105,7 +105,7 @@ async function collectAnswers(rl) {
|
|
|
105
105
|
const roadmapPath = await askNullable(rl, '로드맵 파일 경로 (없으면 Enter)', DEFAULT_ROADMAP_PATH);
|
|
106
106
|
const planDir = await askWithDefault(rl, '계획서 디렉토리', DEFAULT_PLAN_DIR);
|
|
107
107
|
const resultDir = await askWithDefault(rl, '결과 보고서 디렉토리', DEFAULT_RESULT_DIR);
|
|
108
|
-
const runbookDir = await askNullable(rl, '런북 디렉토리 (없으면 Enter)',
|
|
108
|
+
const runbookDir = await askNullable(rl, '런북 디렉토리 (없으면 Enter)', DEFAULT_RUNBOOK_DIR);
|
|
109
109
|
const idPattern = await askWithDefault(rl, '유닛 ID 패턴 (정규식)', DEFAULT_ID_PATTERN);
|
|
110
110
|
const validPattern = isValidRegex(idPattern);
|
|
111
111
|
if (!validPattern) {
|
|
@@ -132,7 +132,7 @@ async function askWithDefault(rl, prompt, defaultValue) {
|
|
|
132
132
|
/**
|
|
133
133
|
* null 가능한 질문 프롬프트 (빈 입력 → null)
|
|
134
134
|
*/
|
|
135
|
-
async function askNullable(rl, prompt, defaultValue) {
|
|
135
|
+
async function askNullable(rl, prompt, defaultValue = '') {
|
|
136
136
|
const display = defaultValue ? ` ${DIM}[${defaultValue}]${RESET}` : '';
|
|
137
137
|
const answer = await rl.question(`${CYAN}? ${prompt}${RESET}${display}: `);
|
|
138
138
|
const trimmed = answer.trim();
|
|
@@ -43,12 +43,19 @@ export function parseDependencyLines(lines, idPattern) {
|
|
|
43
43
|
artifacts: currentArtifacts,
|
|
44
44
|
});
|
|
45
45
|
}
|
|
46
|
-
// 새 dep 파싱
|
|
47
|
-
const
|
|
48
|
-
if (
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
46
|
+
// 새 dep 파싱 (멀티 반환 지원)
|
|
47
|
+
const parsedList = extractDepFromLine(trimmed, idPattern);
|
|
48
|
+
if (parsedList.length > 0) {
|
|
49
|
+
// 마지막 이외 항목은 즉시 deps에 push (하위 항목은 마지막 유닛에만 연결)
|
|
50
|
+
for (let i = 0; i < parsedList.length - 1; i++) {
|
|
51
|
+
const dep = parsedList[i];
|
|
52
|
+
if (dep)
|
|
53
|
+
deps.push(dep);
|
|
54
|
+
}
|
|
55
|
+
const last = parsedList[parsedList.length - 1];
|
|
56
|
+
currentUnitId = last?.unitId ?? '';
|
|
57
|
+
currentDescription = last?.description ?? '';
|
|
58
|
+
currentArtifacts = last?.artifacts;
|
|
52
59
|
hasCurrent = true;
|
|
53
60
|
}
|
|
54
61
|
else {
|
|
@@ -83,49 +90,86 @@ export function parseDependencyLines(lines, idPattern) {
|
|
|
83
90
|
return { deps, contextNotes };
|
|
84
91
|
}
|
|
85
92
|
/**
|
|
86
|
-
* 한 라인에서 의존 유닛 정보를 추출한다
|
|
93
|
+
* 한 라인에서 의존 유닛 정보를 추출한다 (멀티 반환 지원)
|
|
87
94
|
*
|
|
88
95
|
* 추출 우선순위:
|
|
89
96
|
* 1. 링크 텍스트 `[ID](url)` — 더 구체적 (문서 링크 포함)
|
|
90
97
|
* 2. 볼드 텍스트 `**ID**` — 일반적인 의존성 표기
|
|
91
98
|
*
|
|
92
99
|
* idPattern이 제공되면 추출된 후보를 필터링하여 실제 유닛 ID만 선별.
|
|
100
|
+
* 한 줄에 콤마로 구분된 복수 유닛이 있을 경우 모두 추출.
|
|
93
101
|
*
|
|
94
102
|
* @param line - 리스트 항목 라인 (리스트 마커 제거 전)
|
|
95
103
|
* @param idPattern - 유닛 ID 필터링 패턴 (선택)
|
|
96
|
-
* @returns 추출된 DepInfo
|
|
104
|
+
* @returns 추출된 DepInfo 배열 (없으면 빈 배열)
|
|
97
105
|
*/
|
|
98
106
|
function extractDepFromLine(line, idPattern) {
|
|
99
|
-
// 리스트 마커 제거
|
|
100
107
|
const content = line.replace(/^[-*]\s+/, '');
|
|
101
|
-
// 후보 ID 수집 (링크 우선, 볼드 후순위)
|
|
102
108
|
const candidates = [];
|
|
103
|
-
// 링크 텍스트: [ID](url) — 중첩 괄호 1레벨 지원 (예: [U-002[Mvp]])
|
|
104
109
|
const linkRegex = /\[((?:[^[\]]*(?:\[[^\]]*\])?[^[\]]*)*)\]\([^)]*\)/g;
|
|
105
110
|
let match;
|
|
106
111
|
while ((match = linkRegex.exec(content)) !== null) {
|
|
107
112
|
if (match[1])
|
|
108
|
-
candidates.push({
|
|
113
|
+
candidates.push({
|
|
114
|
+
id: match[1].trim(),
|
|
115
|
+
source: 'link',
|
|
116
|
+
matchStart: match.index,
|
|
117
|
+
matchEnd: match.index + match[0].length,
|
|
118
|
+
});
|
|
109
119
|
}
|
|
110
|
-
// 볼드 텍스트: **ID**
|
|
111
120
|
const boldRegex = /\*\*([^*]+)\*\*/g;
|
|
112
121
|
while ((match = boldRegex.exec(content)) !== null) {
|
|
113
122
|
if (match[1])
|
|
114
|
-
candidates.push({
|
|
123
|
+
candidates.push({
|
|
124
|
+
id: match[1].trim(),
|
|
125
|
+
source: 'bold',
|
|
126
|
+
matchStart: match.index,
|
|
127
|
+
matchEnd: match.index + match[0].length,
|
|
128
|
+
});
|
|
115
129
|
}
|
|
116
130
|
if (candidates.length === 0)
|
|
117
|
-
return
|
|
118
|
-
// idPattern으로 필터링: 볼드 헤더(`**헤더**: [ID](url)`)에서 비유닛 볼드를 건너뜀
|
|
131
|
+
return [];
|
|
119
132
|
const idRegex = idPattern ? safeRegex(idPattern) : null;
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
133
|
+
const validCandidates = idRegex
|
|
134
|
+
? candidates
|
|
135
|
+
.filter((c) => idRegex.test(c.id))
|
|
136
|
+
.map((c) => {
|
|
137
|
+
// 볼드가 링크를 감싸는 경우(`**[ID](url)**`) ID를 실제 유닛 ID로 정규화
|
|
138
|
+
const actualMatch = c.id.match(idRegex);
|
|
139
|
+
return actualMatch ? { ...c, id: actualMatch[0] } : c;
|
|
140
|
+
})
|
|
141
|
+
: candidates[0]
|
|
142
|
+
? [candidates[0]]
|
|
143
|
+
: [];
|
|
144
|
+
// None 의존성 필터링 + 동일 ID 중복 제거 (첫 등장 유지)
|
|
145
|
+
const seen = new Set();
|
|
146
|
+
const filtered = validCandidates.filter((c) => {
|
|
147
|
+
if (isNoneDependency(c.id))
|
|
148
|
+
return false;
|
|
149
|
+
if (seen.has(c.id))
|
|
150
|
+
return false;
|
|
151
|
+
seen.add(c.id);
|
|
152
|
+
return true;
|
|
153
|
+
});
|
|
154
|
+
if (filtered.length === 0)
|
|
155
|
+
return [];
|
|
156
|
+
// 단일 후보: 기존 extractDescription 로직 사용 (성능/호환성 보장)
|
|
157
|
+
if (filtered.length === 1) {
|
|
158
|
+
const c = filtered[0];
|
|
159
|
+
if (!c)
|
|
160
|
+
return [];
|
|
161
|
+
const description = extractDescription(content, c.id, c.source);
|
|
162
|
+
return [{ unitId: c.id, description }];
|
|
163
|
+
}
|
|
164
|
+
// 복수 후보: 등장 순서 정렬 후 위치 기반 description 범위 분할
|
|
165
|
+
const sorted = [...filtered].sort((a, b) => a.matchStart - b.matchStart);
|
|
166
|
+
return sorted.map((c, i) => {
|
|
167
|
+
const nextCandidate = sorted[i + 1];
|
|
168
|
+
const nextStart = nextCandidate ? nextCandidate.matchStart : content.length;
|
|
169
|
+
const descText = content.slice(c.matchEnd, nextStart);
|
|
170
|
+
const description = cleanupMultiDescription(descText);
|
|
171
|
+
return { unitId: c.id, description };
|
|
172
|
+
});
|
|
129
173
|
}
|
|
130
174
|
/**
|
|
131
175
|
* ID 참조 이후의 설명 텍스트를 추출한다
|
|
@@ -157,6 +201,19 @@ function extractDescription(content, unitId, source) {
|
|
|
157
201
|
rest = rest.replace(/^[—-]\s+/, '');
|
|
158
202
|
return rest.trim();
|
|
159
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* 멀티 의존성 description 텍스트를 정리한다
|
|
206
|
+
*
|
|
207
|
+
* 위치 기반 범위 분할로 추출된 raw description에서
|
|
208
|
+
* 선행 구분자(`:`, `—`, `-`)와 후행 콤마를 제거.
|
|
209
|
+
*/
|
|
210
|
+
function cleanupMultiDescription(text) {
|
|
211
|
+
let rest = text.trim();
|
|
212
|
+
rest = rest.replace(/^[:—]\s*/, '');
|
|
213
|
+
rest = rest.replace(/^[—-]\s+/, '');
|
|
214
|
+
rest = rest.replace(/,\s*$/, '');
|
|
215
|
+
return rest.trim();
|
|
216
|
+
}
|
|
160
217
|
/**
|
|
161
218
|
* 하위 항목에서 artifact 텍스트를 추출한다
|
|
162
219
|
*
|
|
@@ -120,9 +120,16 @@ function extractBySource(content, config, idPattern) {
|
|
|
120
120
|
/** 섹션 기반으로 의존성과 비유닛 컨텍스트 항목을 추출한다 */
|
|
121
121
|
function extractFromSection(content, sectionName, idPattern) {
|
|
122
122
|
const sectionLines = findSectionLines(content, sectionName, { allowBoldMarker: true });
|
|
123
|
-
if (sectionLines.length
|
|
124
|
-
return
|
|
125
|
-
|
|
123
|
+
if (sectionLines.length > 0) {
|
|
124
|
+
return parseDependencyLines(sectionLines, idPattern);
|
|
125
|
+
}
|
|
126
|
+
// Fallback: 볼드 마커의 인라인 콘텐츠 추출
|
|
127
|
+
// `**이전 작업에서 가져올 것**: [U-102[Mmp]](url) — 설명` 형태 처리
|
|
128
|
+
const inlineContent = findBoldMarkerInlineContent(content, sectionName);
|
|
129
|
+
if (inlineContent) {
|
|
130
|
+
return parseDependencyLines([`- ${inlineContent}`], idPattern);
|
|
131
|
+
}
|
|
132
|
+
return { deps: [], contextNotes: [] };
|
|
126
133
|
}
|
|
127
134
|
/** YAML frontmatter의 depends 필드에서 의존성을 추출한다 (배열/콤마/단일값) */
|
|
128
135
|
function extractFromFrontmatter(content) {
|
|
@@ -179,13 +186,27 @@ function extractFromMetadataTable(content, tableConfig) {
|
|
|
179
186
|
* 2. 하이픈(-)은 유닛 ID와 혼동될 수 있으므로 반드시 앞뒤 공백 필요
|
|
180
187
|
*/
|
|
181
188
|
const DESC_SEPARATOR_RE = /^(?:(.+?)\s*([—:])\s*(.+)|(.+?)\s+-\s+(.+))$/;
|
|
189
|
+
/**
|
|
190
|
+
* 마크다운 링크 구문을 제거하고 텍스트만 추출한다
|
|
191
|
+
*
|
|
192
|
+
* `[U-102[Mmp]](U-102[Mmp].md)` → `U-102[Mmp]`
|
|
193
|
+
*
|
|
194
|
+
* 중첩 대괄호 1레벨 지원. 링크가 아니면 원본 반환.
|
|
195
|
+
*/
|
|
196
|
+
function stripMarkdownLink(token) {
|
|
197
|
+
const linkMatch = token.match(/^\[((?:[^[\]]*(?:\[[^\]]*\])?[^[\]]*)*)\]\([^)]*\)$/);
|
|
198
|
+
return linkMatch?.[1]?.trim() ?? token;
|
|
199
|
+
}
|
|
182
200
|
/**
|
|
183
201
|
* `U-101 — Token 발급` 같은 토큰에서 unitId와 description을 분리한다
|
|
184
202
|
*
|
|
203
|
+
* 마크다운 링크 구문(`[ID](url)`)이 있으면 먼저 제거한 뒤 처리.
|
|
185
204
|
* 구분자가 없으면 전체가 unitId, description은 빈 문자열.
|
|
186
205
|
*/
|
|
187
206
|
function splitUnitIdAndDescription(rawToken) {
|
|
188
|
-
|
|
207
|
+
// 마크다운 링크 제거: [text](url) → text
|
|
208
|
+
const stripped = stripMarkdownLink(rawToken);
|
|
209
|
+
const match = stripped.match(DESC_SEPARATOR_RE);
|
|
189
210
|
if (match) {
|
|
190
211
|
// 1번 그룹(ID), 3번 그룹(Desc) 또는 4번 그룹(ID), 5번 그룹(Desc)
|
|
191
212
|
const unitId = (match[1] || match[4] || '').trim();
|
|
@@ -194,7 +215,29 @@ function splitUnitIdAndDescription(rawToken) {
|
|
|
194
215
|
return { unitId, description };
|
|
195
216
|
}
|
|
196
217
|
}
|
|
197
|
-
return { unitId:
|
|
218
|
+
return { unitId: stripped, description: '' };
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* 볼드 마커 라인에서 인라인 콘텐츠를 추출한다
|
|
222
|
+
*
|
|
223
|
+
* `**섹션명**: [U-102[Mmp]](url) — 설명` → `[U-102[Mmp]](url) — 설명`
|
|
224
|
+
*
|
|
225
|
+
* findSectionLines가 볼드 마커 라인의 인라인 콘텐츠를 건너뛰는 경우의 Fallback.
|
|
226
|
+
*/
|
|
227
|
+
function findBoldMarkerInlineContent(content, sectionName) {
|
|
228
|
+
const marker = `**${sectionName}**`;
|
|
229
|
+
for (const line of content.split('\n')) {
|
|
230
|
+
const trimmed = line.trim();
|
|
231
|
+
const idx = trimmed.indexOf(marker);
|
|
232
|
+
if (idx === -1)
|
|
233
|
+
continue;
|
|
234
|
+
const afterMarker = trimmed
|
|
235
|
+
.slice(idx + marker.length)
|
|
236
|
+
.replace(/^\s*[:\-—]\s*/, '')
|
|
237
|
+
.trim();
|
|
238
|
+
return afterMarker || null;
|
|
239
|
+
}
|
|
240
|
+
return null;
|
|
198
241
|
}
|
|
199
242
|
/**
|
|
200
243
|
* 본문 전체에서 ID 패턴으로 의존성을 스캔한다 (Fallback 최후 수단)
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
export declare const FRONTMATTER_RE: RegExp;
|
|
11
11
|
/** 마크다운 헤딩 패턴: # ~ ###### 레벨 */
|
|
12
12
|
export declare const HEADING_RE: RegExp;
|
|
13
|
-
/** 볼드 마커 패턴: **섹션명** (콜론/대시
|
|
13
|
+
/** 볼드 마커 패턴: **섹션명** (콜론/대시 허용, 인라인 콘텐츠 포함) */
|
|
14
14
|
export declare const BOLD_MARKER_RE: RegExp;
|
|
15
15
|
/**
|
|
16
16
|
* 섹션 탐색 옵션
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
export const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---/;
|
|
11
11
|
/** 마크다운 헤딩 패턴: # ~ ###### 레벨 */
|
|
12
12
|
export const HEADING_RE = /^(#{1,6})\s+(.+)$/;
|
|
13
|
-
/** 볼드 마커 패턴: **섹션명** (콜론/대시
|
|
14
|
-
export const BOLD_MARKER_RE = /^\*\*[^*]
|
|
13
|
+
/** 볼드 마커 패턴: **섹션명** (콜론/대시 허용, 인라인 콘텐츠 포함) */
|
|
14
|
+
export const BOLD_MARKER_RE = /^\*\*[^*]+\*\*(?:\s*[:\-—]|\s*$)/;
|
|
15
15
|
/**
|
|
16
16
|
* 섹션명에 해당하는 콘텐츠 라인들을 추출한다
|
|
17
17
|
*
|
|
@@ -58,8 +58,8 @@ export function renderSection(context, unitTypeConfig, docPrefix = DEFAULT_DOC_P
|
|
|
58
58
|
/**
|
|
59
59
|
* 의존 유닛 목록과 비유닛 컨텍스트 항목을 렌더링한다
|
|
60
60
|
*
|
|
61
|
-
* 유닛 항목: `-
|
|
62
|
-
* 비유닛 항목: `- {description}` (
|
|
61
|
+
* 유닛 항목: `- {unitId}: {description}`
|
|
62
|
+
* 비유닛 항목: `- {description}` (유닛 ID 없이)
|
|
63
63
|
*/
|
|
64
64
|
function renderDepUnits(lines, context) {
|
|
65
65
|
lines.push('');
|
|
@@ -68,7 +68,7 @@ function renderDepUnits(lines, context) {
|
|
|
68
68
|
const hasContent = context.unit.depends.length > 0 || contextNotes.length > 0;
|
|
69
69
|
if (hasContent) {
|
|
70
70
|
for (const dep of context.unit.depends) {
|
|
71
|
-
lines.push(`-
|
|
71
|
+
lines.push(`- ${dep.unitId}: ${dep.description}`);
|
|
72
72
|
}
|
|
73
73
|
for (const note of contextNotes) {
|
|
74
74
|
lines.push(`- ${note}`);
|
|
@@ -12,6 +12,7 @@ export declare const DEFAULT_ROADMAP_PATH = "vibe/roadmap.md";
|
|
|
12
12
|
export declare const DEFAULT_PLAN_DIR = "vibe/unit-plans";
|
|
13
13
|
export declare const DEFAULT_RESULT_DIR = "vibe/unit-results";
|
|
14
14
|
export declare const DEFAULT_ID_PATTERN = "^(U-\\d+|CP-).*";
|
|
15
|
+
export declare const DEFAULT_RUNBOOK_DIR = "vibe/unit-runbooks";
|
|
15
16
|
export declare const DEFAULT_COMMAND_SECTION = "# \uC720\uB2DB \uAD6C\uD604";
|
|
16
17
|
export declare const DEFAULT_HEADER_TEMPLATE = "### \uD604\uC7AC \uAD6C\uD604 \uC720\uB2DB: {{title}}\n- \uD604\uC7AC \uAD6C\uD604 \uC720\uB2DB \uAC1C\uBC1C \uACC4\uD68D\uC11C: @{{planPath}}\n- \uD604\uC7AC \uAD6C\uD604 Commit(\uBCC0\uACBD\uC810 \uD655\uC778\uD558\uC5EC \uB9E5\uB77D\uC73C\uB85C \uC0AC\uC6A9): -";
|
|
17
18
|
/**
|
|
@@ -12,6 +12,7 @@ export const DEFAULT_ROADMAP_PATH = 'vibe/roadmap.md';
|
|
|
12
12
|
export const DEFAULT_PLAN_DIR = 'vibe/unit-plans';
|
|
13
13
|
export const DEFAULT_RESULT_DIR = 'vibe/unit-results';
|
|
14
14
|
export const DEFAULT_ID_PATTERN = '^(U-\\d+|CP-).*';
|
|
15
|
+
export const DEFAULT_RUNBOOK_DIR = 'vibe/unit-runbooks';
|
|
15
16
|
export const DEFAULT_COMMAND_SECTION = '# 유닛 구현';
|
|
16
17
|
export const DEFAULT_HEADER_TEMPLATE = '### 현재 구현 유닛: {{title}}\n- 현재 구현 유닛 개발 계획서: @{{planPath}}\n- 현재 구현 Commit(변경점 확인하여 맥락으로 사용): -';
|
|
17
18
|
/**
|
|
@@ -45,7 +46,7 @@ export function extractStdinAnswers(data) {
|
|
|
45
46
|
roadmapPath: DEFAULT_ROADMAP_PATH,
|
|
46
47
|
planDir: DEFAULT_PLAN_DIR,
|
|
47
48
|
resultDir: DEFAULT_RESULT_DIR,
|
|
48
|
-
runbookDir:
|
|
49
|
+
runbookDir: DEFAULT_RUNBOOK_DIR,
|
|
49
50
|
idPattern: DEFAULT_ID_PATTERN,
|
|
50
51
|
commandSection: DEFAULT_COMMAND_SECTION,
|
|
51
52
|
};
|
|
@@ -53,7 +53,6 @@ function mergePaths(existing, scanned) {
|
|
|
53
53
|
const existingDocRoots = isPlainObject(existing['docRoots']) ? existing['docRoots'] : {};
|
|
54
54
|
const mergedDocRoots = { ...existingDocRoots };
|
|
55
55
|
for (const key of Object.keys(scanned['docRoots'])) {
|
|
56
|
-
// 스캔 결과에 있는 docRoots 키는 무조건 반영 (갱신 또는 추가)
|
|
57
56
|
mergedDocRoots[key] = scanned['docRoots'][key];
|
|
58
57
|
}
|
|
59
58
|
merged['docRoots'] = mergedDocRoots;
|
package/package.json
CHANGED