vibe-commander 0.2.2 → 0.2.4

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.
@@ -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 === 0)
124
- return { deps: [], contextNotes: [] };
125
- return parseDependencyLines(sectionLines, idPattern);
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
- const match = rawToken.match(DESC_SEPARATOR_RE);
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: rawToken, description: '' };
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 = /^\*\*[^*]+\*\*\s*[:\-—]?\s*$/;
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
- * 유닛 항목: `- **{unitId}**: {description}`
62
- * 비유닛 항목: `- {description}` (볼드 유닛 ID 없이)
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(`- **${dep.unitId}**: ${dep.description}`);
71
+ lines.push(`- ${dep.unitId}: ${dep.description}`);
72
72
  }
73
73
  for (const note of contextNotes) {
74
74
  lines.push(`- ${note}`);
@@ -79,17 +79,24 @@ function renderDepUnits(lines, context) {
79
79
  }
80
80
  }
81
81
  /**
82
- * 의존성 문서 경로를 렌더링한다
82
+ * 의존성 문서 경로를 유닛별로 그룹핑하여 렌더링한다
83
83
  *
84
- * 형식: `- @{path}` (docPrefix 제어 가능)
84
+ * 의존 유닛의 계획서, 결과서, 런북 3종을 `- @{path}` 형식으로 한줄씩 렌더링.
85
+ * 유닛이 여러 개일 때 유닛 ID 주석으로 그룹 구분.
85
86
  */
86
87
  function renderDepDocs(lines, context, docPrefix) {
87
88
  lines.push('');
88
89
  lines.push('### 의존성 문서:');
89
- const allDocs = context.depDocs.flatMap((d) => d.found);
90
- if (allDocs.length > 0) {
91
- for (const doc of allDocs) {
92
- lines.push(`- ${docPrefix}${doc.path}`);
90
+ const nonEmptyGroups = context.depDocs.filter((d) => d.found.length > 0);
91
+ if (nonEmptyGroups.length > 0) {
92
+ const useGroupHeaders = nonEmptyGroups.length > 1;
93
+ for (const group of nonEmptyGroups) {
94
+ if (useGroupHeaders) {
95
+ lines.push(`<!-- ${group.unitId} -->`);
96
+ }
97
+ for (const doc of group.found) {
98
+ lines.push(`- ${docPrefix}${doc.path}`);
99
+ }
93
100
  }
94
101
  }
95
102
  else {
@@ -100,10 +107,11 @@ function renderDepDocs(lines, context, docPrefix) {
100
107
  * 의존성 커밋 SHA를 렌더링한다
101
108
  *
102
109
  * 형식: `- {sha}`
110
+ * 헤딩에 "(변경점 확인하여 맥락으로 사용)" 주석 포함
103
111
  */
104
112
  function renderDepCommits(lines, context) {
105
113
  lines.push('');
106
- lines.push('### 의존성 Commits:');
114
+ lines.push('### 의존성 Commits (변경점 확인하여 맥락으로 사용):');
107
115
  if (context.depCommits.length > 0) {
108
116
  for (const sha of context.depCommits) {
109
117
  lines.push(`- ${sha}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vibe-commander",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "type": "module",
5
5
  "description": "Unit-based vibe coding workflow automation CLI — automate context collection from unit ID to command file in 10 seconds",
6
6
  "license": "MIT",