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,95 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 커밋 교차 검증 필터 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* `git diff-tree`로 수집된 변경 파일 목록을 기반으로,
|
|
5
|
+
* excludePaths/requirePaths glob 패턴에 따라 커밋을 필터링한다.
|
|
6
|
+
*
|
|
7
|
+
* **순수 함수**: child_process, fs 등 I/O 모듈을 사용하지 않음.
|
|
8
|
+
* 변경 파일 수집은 Adapter(Layer 3)의 책임이며,
|
|
9
|
+
* 이 함수는 이미 수집된 목록을 받아 필터링만 담당한다.
|
|
10
|
+
*
|
|
11
|
+
* **Q2 결정 (Option C — 독립 적용)**:
|
|
12
|
+
* excludePaths와 requirePaths를 독립 필터로 순차 적용.
|
|
13
|
+
* - excludePaths 필터: 변경 파일이 모두 exclude 패턴에 매칭되면 제거 (문서 전용 커밋)
|
|
14
|
+
* - requirePaths 필터: 변경 파일 중 require 패턴에 하나도 매칭 안 되면 제거
|
|
15
|
+
* - 두 필터가 모두 빈 배열이면 기존 동작 유지
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* 변경 파일 목록을 기반으로 커밋을 필터링한다
|
|
21
|
+
*
|
|
22
|
+
* excludePaths와 requirePaths를 독립 필터로 순차 적용 (Option C).
|
|
23
|
+
* 두 필터가 모두 빈 배열이면 모든 커밋 통과 (Graceful Degradation).
|
|
24
|
+
*
|
|
25
|
+
* @param commits - 변경 파일 목록이 포함된 커밋 배열
|
|
26
|
+
* @param options - 필터 옵션 (excludePaths, requirePaths)
|
|
27
|
+
* @returns 필터를 통과한 커밋 배열
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```typescript
|
|
31
|
+
* const commits = [
|
|
32
|
+
* { sha: 'abc1234', changedFiles: ['src/core/parser.ts', 'vibe/unit-plans/U-001.md'] },
|
|
33
|
+
* { sha: 'def5678', changedFiles: ['vibe/unit-plans/U-001.md'] },
|
|
34
|
+
* ];
|
|
35
|
+
* const filtered = filterCommitsByChangedFiles(commits, {
|
|
36
|
+
* excludePaths: ['vibe/**'],
|
|
37
|
+
* });
|
|
38
|
+
* // filtered → [{ sha: 'abc1234', changedFiles: [...] }]
|
|
39
|
+
* // def5678 제거: 변경 파일이 모두 vibe/** 패턴에 매칭됨
|
|
40
|
+
* ```
|
|
41
|
+
*/
|
|
42
|
+
export function filterCommitsByChangedFiles(commits, options = {}) {
|
|
43
|
+
const { excludePaths = [], requirePaths = [] } = options;
|
|
44
|
+
if (excludePaths.length === 0 && requirePaths.length === 0) {
|
|
45
|
+
return commits;
|
|
46
|
+
}
|
|
47
|
+
let result = commits;
|
|
48
|
+
// excludePaths 필터: 변경 파일이 모두 exclude 패턴에 매칭되면 제거
|
|
49
|
+
if (excludePaths.length > 0) {
|
|
50
|
+
result = result.filter((commit) => {
|
|
51
|
+
if (commit.changedFiles.length === 0)
|
|
52
|
+
return true;
|
|
53
|
+
// 모든 파일이 exclude 패턴 중 하나에 매칭되면 필터링 (false = 제거)
|
|
54
|
+
return !commit.changedFiles.every((file) => excludePaths.some((pattern) => matchesGlobPattern(file, pattern)));
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
// requirePaths 필터: 변경 파일 중 하나도 require 패턴에 매칭 안 되면 제거
|
|
58
|
+
if (requirePaths.length > 0) {
|
|
59
|
+
result = result.filter((commit) => {
|
|
60
|
+
if (commit.changedFiles.length === 0)
|
|
61
|
+
return false;
|
|
62
|
+
return commit.changedFiles.some((file) => requirePaths.some((pattern) => matchesGlobPattern(file, pattern)));
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
// ── 내부 헬퍼 ──
|
|
68
|
+
/**
|
|
69
|
+
* 파일 경로가 glob 패턴에 매칭되는지 검사한다
|
|
70
|
+
*
|
|
71
|
+
* 지원하는 와일드카드:
|
|
72
|
+
* - `**` — 모든 경로 세그먼트 매칭 (슬래시 포함)
|
|
73
|
+
* - `*` — 단일 경로 세그먼트 매칭 (슬래시 미포함)
|
|
74
|
+
*
|
|
75
|
+
* 예시:
|
|
76
|
+
* - `src/**` → `src/core/foo.ts` ✅, `docs/foo.ts` ❌
|
|
77
|
+
* - `**\/*.md` → `vibe/unit-plans/U-001.md` ✅, `src/core/foo.ts` ❌
|
|
78
|
+
* - `src/*.ts` → `src/foo.ts` ✅, `src/core/foo.ts` ❌
|
|
79
|
+
*
|
|
80
|
+
* @param filePath - 검사할 파일 경로 (프로젝트 루트 기준 상대 경로)
|
|
81
|
+
* @param pattern - glob 패턴
|
|
82
|
+
* @returns 매칭 여부
|
|
83
|
+
*/
|
|
84
|
+
export function matchesGlobPattern(filePath, pattern) {
|
|
85
|
+
const DOUBLE_STAR = '__DOUBLE_STAR__';
|
|
86
|
+
// 정규식 특수문자 이스케이프 (*와 ** 는 이후 단계에서 처리)
|
|
87
|
+
const regexStr = pattern
|
|
88
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&') // regex 특수문자 이스케이프
|
|
89
|
+
.replace(/\*\*/g, DOUBLE_STAR) // ** 임시 치환
|
|
90
|
+
.replace(/\*/g, '[^/]*') // * → 슬래시 미포함 임의 문자
|
|
91
|
+
.replace(new RegExp(`${DOUBLE_STAR}/`, 'g'), '(.*\\/)?') // **/ → 선택적 경로 접두사
|
|
92
|
+
.replace(new RegExp(DOUBLE_STAR, 'g'), '.*'); // 남은 ** → 임의 문자 (슬래시 포함)
|
|
93
|
+
return new RegExp(`^${regexStr}$`).test(filePath);
|
|
94
|
+
}
|
|
95
|
+
//# sourceMappingURL=commit-filter.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프로젝트 설정 생성 로직 — Layer 1 (Core / Pure Functions)
|
|
3
|
+
*
|
|
4
|
+
* 수집된 답변이나 스캔 결과를 바탕으로 ProjectConfig 객체를 생성한다.
|
|
5
|
+
* 이 모듈은 I/O를 수행하지 않으며, 데이터 구조 변환 및 기본값 관리를 담당한다.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
import type { ScanResult, InitAnswers } from '../../types/index.js';
|
|
10
|
+
export declare const DEFAULT_COMMANDS_PATH = "vibe/commands.md";
|
|
11
|
+
export declare const DEFAULT_ROADMAP_PATH = "vibe/roadmap.md";
|
|
12
|
+
export declare const DEFAULT_PLAN_DIR = "vibe/unit-plans";
|
|
13
|
+
export declare const DEFAULT_RESULT_DIR = "vibe/unit-results";
|
|
14
|
+
export declare const DEFAULT_ID_PATTERN = "^(U-\\d+|CP-).*";
|
|
15
|
+
export declare const DEFAULT_COMMAND_SECTION = "# \uC720\uB2DB \uAD6C\uD604";
|
|
16
|
+
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
|
+
* 외부 주입 데이터(stdin 등)에서 InitAnswers를 추출한다
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractStdinAnswers(data: Record<string, unknown>): InitAnswers;
|
|
21
|
+
/**
|
|
22
|
+
* 스캔 결과로부터 InitAnswers를 생성한다 (오버라이드 적용 가능)
|
|
23
|
+
*/
|
|
24
|
+
export declare function buildAnswersFromScan(scanResult: ScanResult, overrides?: Record<string, unknown>): InitAnswers;
|
|
25
|
+
/**
|
|
26
|
+
* InitAnswers → 설정 JSON 객체 구성 (최소 필드)
|
|
27
|
+
*
|
|
28
|
+
* 이 함수는 Zod 스키마에 정의된 기본값들을 제외한 핵심 필드만으로 객체를 구성합니다.
|
|
29
|
+
* 최종 결과는 Zod로 검증하거나 기존 설정과 병합하는 용도로 사용됩니다.
|
|
30
|
+
*/
|
|
31
|
+
export declare function buildConfigObject(answers: InitAnswers): Record<string, unknown>;
|
|
32
|
+
//# sourceMappingURL=config-generator.d.ts.map
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 프로젝트 설정 생성 로직 — Layer 1 (Core / Pure Functions)
|
|
3
|
+
*
|
|
4
|
+
* 수집된 답변이나 스캔 결과를 바탕으로 ProjectConfig 객체를 생성한다.
|
|
5
|
+
* 이 모듈은 I/O를 수행하지 않으며, 데이터 구조 변환 및 기본값 관리를 담당한다.
|
|
6
|
+
*
|
|
7
|
+
* @module
|
|
8
|
+
*/
|
|
9
|
+
// ── 기본값 상수 ──
|
|
10
|
+
export const DEFAULT_COMMANDS_PATH = 'vibe/commands.md';
|
|
11
|
+
export const DEFAULT_ROADMAP_PATH = 'vibe/roadmap.md';
|
|
12
|
+
export const DEFAULT_PLAN_DIR = 'vibe/unit-plans';
|
|
13
|
+
export const DEFAULT_RESULT_DIR = 'vibe/unit-results';
|
|
14
|
+
export const DEFAULT_ID_PATTERN = '^(U-\\d+|CP-).*';
|
|
15
|
+
export const DEFAULT_COMMAND_SECTION = '# 유닛 구현';
|
|
16
|
+
export const DEFAULT_HEADER_TEMPLATE = '### 현재 구현 유닛: {{title}}\n- 현재 구현 유닛 개발 계획서: @{{planPath}}\n- 현재 구현 Commit(변경점 확인하여 맥락으로 사용): -';
|
|
17
|
+
/**
|
|
18
|
+
* Record<string, unknown> 오버라이드를 InitAnswers 기본값에 적용한다
|
|
19
|
+
*/
|
|
20
|
+
function applyOverrides(defaults, overrides) {
|
|
21
|
+
return {
|
|
22
|
+
commandsPath: typeof overrides['commandsPath'] === 'string'
|
|
23
|
+
? overrides['commandsPath']
|
|
24
|
+
: defaults.commandsPath,
|
|
25
|
+
roadmapPath: typeof overrides['roadmapPath'] === 'string'
|
|
26
|
+
? overrides['roadmapPath']
|
|
27
|
+
: overrides['roadmapPath'] === null
|
|
28
|
+
? null
|
|
29
|
+
: defaults.roadmapPath,
|
|
30
|
+
planDir: typeof overrides['planDir'] === 'string' ? overrides['planDir'] : defaults.planDir,
|
|
31
|
+
resultDir: typeof overrides['resultDir'] === 'string' ? overrides['resultDir'] : defaults.resultDir,
|
|
32
|
+
runbookDir: typeof overrides['runbookDir'] === 'string' ? overrides['runbookDir'] : defaults.runbookDir,
|
|
33
|
+
idPattern: typeof overrides['idPattern'] === 'string' ? overrides['idPattern'] : defaults.idPattern,
|
|
34
|
+
commandSection: typeof overrides['commandSection'] === 'string'
|
|
35
|
+
? overrides['commandSection']
|
|
36
|
+
: defaults.commandSection,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 외부 주입 데이터(stdin 등)에서 InitAnswers를 추출한다
|
|
41
|
+
*/
|
|
42
|
+
export function extractStdinAnswers(data) {
|
|
43
|
+
const defaults = {
|
|
44
|
+
commandsPath: DEFAULT_COMMANDS_PATH,
|
|
45
|
+
roadmapPath: DEFAULT_ROADMAP_PATH,
|
|
46
|
+
planDir: DEFAULT_PLAN_DIR,
|
|
47
|
+
resultDir: DEFAULT_RESULT_DIR,
|
|
48
|
+
runbookDir: null,
|
|
49
|
+
idPattern: DEFAULT_ID_PATTERN,
|
|
50
|
+
commandSection: DEFAULT_COMMAND_SECTION,
|
|
51
|
+
};
|
|
52
|
+
return applyOverrides(defaults, data);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* 스캔 결과로부터 InitAnswers를 생성한다 (오버라이드 적용 가능)
|
|
56
|
+
*/
|
|
57
|
+
export function buildAnswersFromScan(scanResult, overrides) {
|
|
58
|
+
const planRoot = scanResult.docRoots.find((d) => d.role === 'plan');
|
|
59
|
+
const resultRoot = scanResult.docRoots.find((d) => d.role === 'result');
|
|
60
|
+
const runbookRoot = scanResult.docRoots.find((d) => d.role === 'runbook');
|
|
61
|
+
const commandFile = scanResult.commandFiles[0];
|
|
62
|
+
const roadmap = scanResult.roadmapCandidates[0] ?? null;
|
|
63
|
+
const bestSection = commandFile?.sections.find((s) => s.startsWith('# ') || s.startsWith('## '));
|
|
64
|
+
const defaults = {
|
|
65
|
+
commandsPath: commandFile?.filePath ?? DEFAULT_COMMANDS_PATH,
|
|
66
|
+
roadmapPath: roadmap ?? DEFAULT_ROADMAP_PATH,
|
|
67
|
+
planDir: planRoot?.dirPath ?? DEFAULT_PLAN_DIR,
|
|
68
|
+
resultDir: resultRoot?.dirPath ?? DEFAULT_RESULT_DIR,
|
|
69
|
+
runbookDir: runbookRoot?.dirPath ?? null,
|
|
70
|
+
idPattern: scanResult.suggestedIdPattern ?? DEFAULT_ID_PATTERN,
|
|
71
|
+
commandSection: bestSection ?? DEFAULT_COMMAND_SECTION,
|
|
72
|
+
};
|
|
73
|
+
if (!overrides)
|
|
74
|
+
return defaults;
|
|
75
|
+
return applyOverrides(defaults, overrides);
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* InitAnswers → 설정 JSON 객체 구성 (최소 필드)
|
|
79
|
+
*
|
|
80
|
+
* 이 함수는 Zod 스키마에 정의된 기본값들을 제외한 핵심 필드만으로 객체를 구성합니다.
|
|
81
|
+
* 최종 결과는 Zod로 검증하거나 기존 설정과 병합하는 용도로 사용됩니다.
|
|
82
|
+
*/
|
|
83
|
+
export function buildConfigObject(answers) {
|
|
84
|
+
const docRoots = {
|
|
85
|
+
plan: answers.planDir,
|
|
86
|
+
result: answers.resultDir,
|
|
87
|
+
};
|
|
88
|
+
if (answers.runbookDir) {
|
|
89
|
+
docRoots['runbook'] = answers.runbookDir;
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
$schema: 'https://vibe-commander/config-schema.json',
|
|
93
|
+
version: 1,
|
|
94
|
+
paths: {
|
|
95
|
+
commands: answers.commandsPath,
|
|
96
|
+
roadmap: answers.roadmapPath,
|
|
97
|
+
docRoots,
|
|
98
|
+
},
|
|
99
|
+
unitTypes: [
|
|
100
|
+
{
|
|
101
|
+
key: 'implement',
|
|
102
|
+
displayName: '유닛 구현',
|
|
103
|
+
idPattern: answers.idPattern,
|
|
104
|
+
planDir: 'plan',
|
|
105
|
+
commandSection: answers.commandSection,
|
|
106
|
+
collectDeps: true,
|
|
107
|
+
headerTemplate: DEFAULT_HEADER_TEMPLATE,
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
//# sourceMappingURL=config-generator.js.map
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 설정 병합 유틸리티 — Layer 1 (Core)
|
|
3
|
+
*
|
|
4
|
+
* 기존 설정(existing)에 스캔 결과(scanned)를 병합하여
|
|
5
|
+
* 커스텀 필드를 보존하면서 감지된 경로/패턴을 반영한다.
|
|
6
|
+
*
|
|
7
|
+
* 순수 함수: JSON in → JSON out, I/O 없음.
|
|
8
|
+
*
|
|
9
|
+
* 병합 전략:
|
|
10
|
+
* - `$schema`, `version`: 스캔 결과 우선
|
|
11
|
+
* - `paths`: 스캔 결과 우선 반영 (갱신), 없는 키 추가
|
|
12
|
+
* - `unitTypes`: 기존 배열 유지 + key 또는 idPattern이 새로운 항목만 추가
|
|
13
|
+
* - 기타 필드: 기존 값 보존 (커스텀 설정 보호)
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* 기존 설정에 스캔 결과를 병합한다
|
|
19
|
+
*
|
|
20
|
+
* @param existing - 기존 설정 파일의 파싱된 JSON 객체
|
|
21
|
+
* @param scanned - 스캔 결과로 생성된 최소 설정 객체
|
|
22
|
+
* @returns 병합된 설정 객체
|
|
23
|
+
*/
|
|
24
|
+
export declare function mergeConfigs(existing: Record<string, unknown>, scanned: Record<string, unknown>): Record<string, unknown>;
|
|
25
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
26
|
+
//# sourceMappingURL=config-merger.d.ts.map
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 설정 병합 유틸리티 — Layer 1 (Core)
|
|
3
|
+
*
|
|
4
|
+
* 기존 설정(existing)에 스캔 결과(scanned)를 병합하여
|
|
5
|
+
* 커스텀 필드를 보존하면서 감지된 경로/패턴을 반영한다.
|
|
6
|
+
*
|
|
7
|
+
* 순수 함수: JSON in → JSON out, I/O 없음.
|
|
8
|
+
*
|
|
9
|
+
* 병합 전략:
|
|
10
|
+
* - `$schema`, `version`: 스캔 결과 우선
|
|
11
|
+
* - `paths`: 스캔 결과 우선 반영 (갱신), 없는 키 추가
|
|
12
|
+
* - `unitTypes`: 기존 배열 유지 + key 또는 idPattern이 새로운 항목만 추가
|
|
13
|
+
* - 기타 필드: 기존 값 보존 (커스텀 설정 보호)
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* 기존 설정에 스캔 결과를 병합한다
|
|
19
|
+
*
|
|
20
|
+
* @param existing - 기존 설정 파일의 파싱된 JSON 객체
|
|
21
|
+
* @param scanned - 스캔 결과로 생성된 최소 설정 객체
|
|
22
|
+
* @returns 병합된 설정 객체
|
|
23
|
+
*/
|
|
24
|
+
export function mergeConfigs(existing, scanned) {
|
|
25
|
+
const merged = { ...existing };
|
|
26
|
+
if (scanned['$schema'] !== undefined)
|
|
27
|
+
merged['$schema'] = scanned['$schema'];
|
|
28
|
+
if (scanned['version'] !== undefined)
|
|
29
|
+
merged['version'] = scanned['version'];
|
|
30
|
+
if (isPlainObject(scanned['paths'])) {
|
|
31
|
+
merged['paths'] = mergePaths(isPlainObject(existing['paths']) ? existing['paths'] : {}, scanned['paths']);
|
|
32
|
+
}
|
|
33
|
+
if (Array.isArray(scanned['unitTypes'])) {
|
|
34
|
+
merged['unitTypes'] = mergeUnitTypes(Array.isArray(existing['unitTypes'])
|
|
35
|
+
? existing['unitTypes']
|
|
36
|
+
: [], scanned['unitTypes']);
|
|
37
|
+
}
|
|
38
|
+
return merged;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* paths 병합: 스캔 결과 우선 반영 (갱신), 없는 키 추가.
|
|
42
|
+
* docRoots는 키별 병합 (기존 키 보존).
|
|
43
|
+
*/
|
|
44
|
+
function mergePaths(existing, scanned) {
|
|
45
|
+
const merged = { ...existing };
|
|
46
|
+
for (const key of Object.keys(scanned)) {
|
|
47
|
+
if (key === 'docRoots')
|
|
48
|
+
continue;
|
|
49
|
+
// 스캔 결과를 우선하여 갱신하거나 추가함
|
|
50
|
+
merged[key] = scanned[key];
|
|
51
|
+
}
|
|
52
|
+
if (isPlainObject(scanned['docRoots'])) {
|
|
53
|
+
const existingDocRoots = isPlainObject(existing['docRoots']) ? existing['docRoots'] : {};
|
|
54
|
+
const mergedDocRoots = { ...existingDocRoots };
|
|
55
|
+
for (const key of Object.keys(scanned['docRoots'])) {
|
|
56
|
+
// 스캔 결과에 있는 docRoots 키는 무조건 반영 (갱신 또는 추가)
|
|
57
|
+
mergedDocRoots[key] = scanned['docRoots'][key];
|
|
58
|
+
}
|
|
59
|
+
merged['docRoots'] = mergedDocRoots;
|
|
60
|
+
}
|
|
61
|
+
return merged;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* unitTypes 병합: 기존 배열 유지 + key 또는 idPattern 필드로 중복 판단.
|
|
65
|
+
* idPattern이 유효한(비어있지 않은 문자열) 항목만 추가함.
|
|
66
|
+
*/
|
|
67
|
+
function mergeUnitTypes(existing, scanned) {
|
|
68
|
+
const merged = [...existing];
|
|
69
|
+
const existingKeys = new Set(existing.map((t) => (typeof t['key'] === 'string' ? t['key'] : '')).filter(Boolean));
|
|
70
|
+
const existingPatterns = new Set(existing.map((t) => (typeof t['idPattern'] === 'string' ? t['idPattern'] : '')).filter(Boolean));
|
|
71
|
+
for (const scannedType of scanned) {
|
|
72
|
+
const scannedKey = typeof scannedType['key'] === 'string' ? scannedType['key'] : '';
|
|
73
|
+
const scannedPattern = typeof scannedType['idPattern'] === 'string' ? scannedType['idPattern'] : '';
|
|
74
|
+
// 유효성 검사: key와 idPattern이 필수
|
|
75
|
+
if (!scannedKey || !scannedPattern)
|
|
76
|
+
continue;
|
|
77
|
+
// 중복 검사: key 또는 idPattern이 이미 존재하면 건너뜀
|
|
78
|
+
if (existingKeys.has(scannedKey) || existingPatterns.has(scannedPattern))
|
|
79
|
+
continue;
|
|
80
|
+
merged.push(scannedType);
|
|
81
|
+
existingKeys.add(scannedKey);
|
|
82
|
+
existingPatterns.add(scannedPattern);
|
|
83
|
+
}
|
|
84
|
+
return merged;
|
|
85
|
+
}
|
|
86
|
+
export function isPlainObject(value) {
|
|
87
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=config-merger.js.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 설정 검증 Core 로직 — Layer 1 (Core)
|
|
3
|
+
*
|
|
4
|
+
* 정규식 유효성 검사 및 패턴 충돌 감지 등 I/O가 없는 순수 함수들로 구성된다.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import type { ToolResult } from '../../types/index.js';
|
|
9
|
+
export interface ValidPattern {
|
|
10
|
+
key: string;
|
|
11
|
+
regex: RegExp;
|
|
12
|
+
}
|
|
13
|
+
export interface PatternConflict {
|
|
14
|
+
id: string;
|
|
15
|
+
matchingKeys: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 단일 정규식 문자열의 유효성을 검증한다
|
|
19
|
+
*
|
|
20
|
+
* @param pattern - 정규식 문자열
|
|
21
|
+
* @returns 유효한 정규식이면 true, 그렇지 않으면 false
|
|
22
|
+
*/
|
|
23
|
+
export declare function isValidRegex(pattern: string): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* 설정된 ID 패턴들이 유효한 정규식인지 검사한다
|
|
26
|
+
*
|
|
27
|
+
* @param patterns - 유닛 유형별 패턴 정보
|
|
28
|
+
* @returns 유효한 정규식 객체 목록 또는 에러
|
|
29
|
+
*/
|
|
30
|
+
export declare function validateRegexes(patterns: {
|
|
31
|
+
key: string;
|
|
32
|
+
idPattern: string;
|
|
33
|
+
}[]): ToolResult<ValidPattern[]>;
|
|
34
|
+
/**
|
|
35
|
+
* 주어진 ID 목록에 대해 여러 패턴이 동시에 매칭되는 충돌 케이스를 찾는다
|
|
36
|
+
*
|
|
37
|
+
* @param ids - docRoots에서 수집된 실제 ID 목록
|
|
38
|
+
* @param validRegexes - 유효성이 검증된 정규식 목록
|
|
39
|
+
* @returns 충돌이 발생한 ID와 매칭된 유형 키 목록
|
|
40
|
+
*/
|
|
41
|
+
export declare function findPatternConflicts(ids: string[], validRegexes: ValidPattern[]): PatternConflict[];
|
|
42
|
+
//# sourceMappingURL=config-validator.d.ts.map
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 설정 검증 Core 로직 — Layer 1 (Core)
|
|
3
|
+
*
|
|
4
|
+
* 정규식 유효성 검사 및 패턴 충돌 감지 등 I/O가 없는 순수 함수들로 구성된다.
|
|
5
|
+
*
|
|
6
|
+
* @module
|
|
7
|
+
*/
|
|
8
|
+
import { ok, fail } from '../../types/index.js';
|
|
9
|
+
/**
|
|
10
|
+
* 단일 정규식 문자열의 유효성을 검증한다
|
|
11
|
+
*
|
|
12
|
+
* @param pattern - 정규식 문자열
|
|
13
|
+
* @returns 유효한 정규식이면 true, 그렇지 않으면 false
|
|
14
|
+
*/
|
|
15
|
+
export function isValidRegex(pattern) {
|
|
16
|
+
try {
|
|
17
|
+
new RegExp(pattern);
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 설정된 ID 패턴들이 유효한 정규식인지 검사한다
|
|
26
|
+
*
|
|
27
|
+
* @param patterns - 유닛 유형별 패턴 정보
|
|
28
|
+
* @returns 유효한 정규식 객체 목록 또는 에러
|
|
29
|
+
*/
|
|
30
|
+
export function validateRegexes(patterns) {
|
|
31
|
+
const validRegexes = [];
|
|
32
|
+
for (const { key, idPattern } of patterns) {
|
|
33
|
+
try {
|
|
34
|
+
const regex = new RegExp(idPattern);
|
|
35
|
+
validRegexes.push({ key, regex });
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
const detail = err instanceof Error ? err.message : String(err);
|
|
39
|
+
return fail('INVALID_REGEX', `유닛 유형 "${key}"의 정규식이 유효하지 않습니다: ${idPattern}`, detail);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return ok(validRegexes);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 주어진 ID 목록에 대해 여러 패턴이 동시에 매칭되는 충돌 케이스를 찾는다
|
|
46
|
+
*
|
|
47
|
+
* @param ids - docRoots에서 수집된 실제 ID 목록
|
|
48
|
+
* @param validRegexes - 유효성이 검증된 정규식 목록
|
|
49
|
+
* @returns 충돌이 발생한 ID와 매칭된 유형 키 목록
|
|
50
|
+
*/
|
|
51
|
+
export function findPatternConflicts(ids, validRegexes) {
|
|
52
|
+
const conflicts = [];
|
|
53
|
+
for (const id of ids) {
|
|
54
|
+
const matchingKeys = validRegexes.filter((r) => r.regex.test(id)).map((r) => r.key);
|
|
55
|
+
if (matchingKeys.length > 1) {
|
|
56
|
+
conflicts.push({ id, matchingKeys });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return conflicts;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=config-validator.js.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 의존성 커밋 해석기 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* `git log --oneline` 출력 텍스트에서
|
|
5
|
+
* 의존 유닛 관련 커밋 SHA를 추출한다.
|
|
6
|
+
*
|
|
7
|
+
* **순수 함수**: child_process, fs 등 I/O 모듈을 사용하지 않음.
|
|
8
|
+
* git 명령 실행은 Adapter(Layer 3)의 책임이며,
|
|
9
|
+
* 이 함수는 텍스트 파싱만 담당한다.
|
|
10
|
+
*
|
|
11
|
+
* PRD §5 commitSearch 설정:
|
|
12
|
+
* strategy: "git-log-grep" | "conventional-commit" | "disabled"
|
|
13
|
+
* extractId: "shortId" | "bareId" | "unitId" (검색어 변환 방식)
|
|
14
|
+
* maxResults: 최대 결과 수
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import type { CommitSearchConfig, ToolResult } from '../../types/index.js';
|
|
19
|
+
/**
|
|
20
|
+
* 개별 커밋 정보
|
|
21
|
+
*
|
|
22
|
+
* git log 한 줄에서 파싱된 SHA와 메시지.
|
|
23
|
+
*/
|
|
24
|
+
export interface CommitEntry {
|
|
25
|
+
/** 커밋 SHA (7자 이상의 축약 또는 전체 SHA) */
|
|
26
|
+
sha: string;
|
|
27
|
+
/** 커밋 메시지 (한 줄) */
|
|
28
|
+
message: string;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* git log 출력에서 의존 유닛 관련 커밋을 추출한다
|
|
32
|
+
*
|
|
33
|
+
* 각 depId에서 검색어를 추출(config.extractId 설정 기반)하고,
|
|
34
|
+
* git log 출력의 각 줄에서 검색어가 포함된 커밋의 SHA를 수집한다.
|
|
35
|
+
*
|
|
36
|
+
* **Graceful Degradation**:
|
|
37
|
+
* - config.strategy가 "disabled"이면 빈 배열 반환 (정상)
|
|
38
|
+
* - depIds가 빈 배열이면 빈 배열 반환 (정상)
|
|
39
|
+
* - 매칭되는 커밋이 없으면 빈 배열 반환 (정상)
|
|
40
|
+
* - gitLogOutput이 빈 문자열이면 빈 배열 반환 (정상)
|
|
41
|
+
*
|
|
42
|
+
* git log --oneline 형식 기대:
|
|
43
|
+
* `abc1234 feat(core): U-005[Mvp] 제목 추출기`
|
|
44
|
+
* `def5678 fix: U-006 의존성 추출 버그 수정`
|
|
45
|
+
*
|
|
46
|
+
* @param depIds - 의존 유닛 ID 목록 (검색 대상)
|
|
47
|
+
* @param gitLogOutput - `git log --oneline` 출력 텍스트
|
|
48
|
+
* @param config - 커밋 탐색 설정 (strategy, extractId, maxResults)
|
|
49
|
+
* @returns 추출된 커밋 SHA 목록을 포함한 ToolResult
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* const depIds = ['U-005[Mvp]', 'U-006[Mvp]'];
|
|
54
|
+
* const gitLog = 'abc1234 feat: U-005 제목 추출기\ndef5678 fix: U-006 의존성';
|
|
55
|
+
* const config = { strategy: 'git-log-grep', extractId: 'shortId', maxResults: 5 };
|
|
56
|
+
* const result = findDepCommits(depIds, gitLog, config);
|
|
57
|
+
* if (result.success) {
|
|
58
|
+
* // result.data → ['abc1234', 'def5678']
|
|
59
|
+
* }
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare function findDepCommits(depIds: string[], gitLogOutput: string, config: CommitSearchConfig): ToolResult<CommitEntry[]>;
|
|
63
|
+
//# sourceMappingURL=dep-commit-resolver.d.ts.map
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 의존성 커밋 해석기 — Layer 1 순수 함수
|
|
3
|
+
*
|
|
4
|
+
* `git log --oneline` 출력 텍스트에서
|
|
5
|
+
* 의존 유닛 관련 커밋 SHA를 추출한다.
|
|
6
|
+
*
|
|
7
|
+
* **순수 함수**: child_process, fs 등 I/O 모듈을 사용하지 않음.
|
|
8
|
+
* git 명령 실행은 Adapter(Layer 3)의 책임이며,
|
|
9
|
+
* 이 함수는 텍스트 파싱만 담당한다.
|
|
10
|
+
*
|
|
11
|
+
* PRD §5 commitSearch 설정:
|
|
12
|
+
* strategy: "git-log-grep" | "conventional-commit" | "disabled"
|
|
13
|
+
* extractId: "shortId" | "bareId" | "unitId" (검색어 변환 방식)
|
|
14
|
+
* maxResults: 최대 결과 수
|
|
15
|
+
*
|
|
16
|
+
* @module
|
|
17
|
+
*/
|
|
18
|
+
import { ok } from '../../types/index.js';
|
|
19
|
+
import { toShortId, toBareId } from '../renderers/interpolate.js';
|
|
20
|
+
import { deduplicateBy, normalizeLineEndings } from '../parsers/md-utils.js';
|
|
21
|
+
/**
|
|
22
|
+
* git log 출력에서 의존 유닛 관련 커밋을 추출한다
|
|
23
|
+
*
|
|
24
|
+
* 각 depId에서 검색어를 추출(config.extractId 설정 기반)하고,
|
|
25
|
+
* git log 출력의 각 줄에서 검색어가 포함된 커밋의 SHA를 수집한다.
|
|
26
|
+
*
|
|
27
|
+
* **Graceful Degradation**:
|
|
28
|
+
* - config.strategy가 "disabled"이면 빈 배열 반환 (정상)
|
|
29
|
+
* - depIds가 빈 배열이면 빈 배열 반환 (정상)
|
|
30
|
+
* - 매칭되는 커밋이 없으면 빈 배열 반환 (정상)
|
|
31
|
+
* - gitLogOutput이 빈 문자열이면 빈 배열 반환 (정상)
|
|
32
|
+
*
|
|
33
|
+
* git log --oneline 형식 기대:
|
|
34
|
+
* `abc1234 feat(core): U-005[Mvp] 제목 추출기`
|
|
35
|
+
* `def5678 fix: U-006 의존성 추출 버그 수정`
|
|
36
|
+
*
|
|
37
|
+
* @param depIds - 의존 유닛 ID 목록 (검색 대상)
|
|
38
|
+
* @param gitLogOutput - `git log --oneline` 출력 텍스트
|
|
39
|
+
* @param config - 커밋 탐색 설정 (strategy, extractId, maxResults)
|
|
40
|
+
* @returns 추출된 커밋 SHA 목록을 포함한 ToolResult
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* const depIds = ['U-005[Mvp]', 'U-006[Mvp]'];
|
|
45
|
+
* const gitLog = 'abc1234 feat: U-005 제목 추출기\ndef5678 fix: U-006 의존성';
|
|
46
|
+
* const config = { strategy: 'git-log-grep', extractId: 'shortId', maxResults: 5 };
|
|
47
|
+
* const result = findDepCommits(depIds, gitLog, config);
|
|
48
|
+
* if (result.success) {
|
|
49
|
+
* // result.data → ['abc1234', 'def5678']
|
|
50
|
+
* }
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
export function findDepCommits(depIds, gitLogOutput, config) {
|
|
54
|
+
// disabled 전략이면 빈 결과 반환
|
|
55
|
+
if (config.strategy === 'disabled')
|
|
56
|
+
return ok([]);
|
|
57
|
+
// 빈 입력 처리
|
|
58
|
+
if (depIds.length === 0 || gitLogOutput.trim() === '')
|
|
59
|
+
return ok([]);
|
|
60
|
+
const normalizedLog = normalizeLineEndings(gitLogOutput);
|
|
61
|
+
// 1. 각 depId에서 검색어 추출
|
|
62
|
+
const searchTerms = buildSearchTerms(depIds, config.extractId);
|
|
63
|
+
if (searchTerms.length === 0)
|
|
64
|
+
return ok([]);
|
|
65
|
+
// 2. git log 출력 파싱
|
|
66
|
+
const allCommits = parseGitLogOutput(normalizedLog);
|
|
67
|
+
if (allCommits.length === 0)
|
|
68
|
+
return ok([]);
|
|
69
|
+
// 3. 검색어로 커밋 필터링
|
|
70
|
+
const matched = filterCommitsByTerms(allCommits, searchTerms);
|
|
71
|
+
// 4. 중복 제거 + maxResults 제한
|
|
72
|
+
const deduplicated = deduplicateBy(matched, (c) => c.sha);
|
|
73
|
+
const limited = deduplicated.slice(0, config.maxResults);
|
|
74
|
+
return ok(limited);
|
|
75
|
+
}
|
|
76
|
+
// ── 내부 헬퍼 ──
|
|
77
|
+
/**
|
|
78
|
+
* depIds에서 검색어 목록을 생성한다
|
|
79
|
+
*
|
|
80
|
+
* config.extractId 설정에 따라 ID 변환:
|
|
81
|
+
* - "shortId": phase 제거 (U-005[Mvp] → U-005)
|
|
82
|
+
* - "bareId": 숫자만 (U-005[Mvp] → 005)
|
|
83
|
+
* - "unitId" 또는 기타: 원본 그대로
|
|
84
|
+
*
|
|
85
|
+
* @param depIds - 의존 유닛 ID 목록
|
|
86
|
+
* @param extractId - ID 변환 방식
|
|
87
|
+
* @returns 검색어 목록 (중복 제거)
|
|
88
|
+
*/
|
|
89
|
+
function buildSearchTerms(depIds, extractId) {
|
|
90
|
+
const terms = new Set();
|
|
91
|
+
for (const depId of depIds) {
|
|
92
|
+
let term;
|
|
93
|
+
switch (extractId) {
|
|
94
|
+
case 'shortId':
|
|
95
|
+
term = toShortId(depId);
|
|
96
|
+
break;
|
|
97
|
+
case 'bareId':
|
|
98
|
+
term = toBareId(depId);
|
|
99
|
+
break;
|
|
100
|
+
default:
|
|
101
|
+
// "unitId" 또는 알 수 없는 값 → 원본 사용
|
|
102
|
+
term = depId;
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
if (term !== '')
|
|
106
|
+
terms.add(term);
|
|
107
|
+
}
|
|
108
|
+
return [...terms];
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* git log --oneline 출력을 파싱한다
|
|
112
|
+
*
|
|
113
|
+
* 각 줄에서 SHA(7자 이상 hex)와 메시지를 분리.
|
|
114
|
+
* 빈 줄, 파싱 불가 줄은 건너뛴다.
|
|
115
|
+
*
|
|
116
|
+
* 느슨한 정규식으로 다양한 git log 형식에 대응:
|
|
117
|
+
* - `abc1234 feat: 메시지`
|
|
118
|
+
* - `abc1234567890 메시지`
|
|
119
|
+
* - `abc1234 (HEAD -> main) 메시지`
|
|
120
|
+
*
|
|
121
|
+
* @param output - git log 출력 텍스트
|
|
122
|
+
* @returns CommitEntry 배열
|
|
123
|
+
*/
|
|
124
|
+
function parseGitLogOutput(output) {
|
|
125
|
+
const lines = output.split('\n');
|
|
126
|
+
const commits = [];
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
if (trimmed === '')
|
|
130
|
+
continue;
|
|
131
|
+
// SHA: 7자 이상의 hex 문자열 (줄 시작)
|
|
132
|
+
const match = trimmed.match(/^([0-9a-f]{7,40})\s+(.+)$/i);
|
|
133
|
+
if (match?.[1] && match[2]) {
|
|
134
|
+
commits.push({
|
|
135
|
+
sha: match[1],
|
|
136
|
+
message: match[2],
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return commits;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* 검색어가 포함된 커밋을 필터링한다
|
|
144
|
+
*
|
|
145
|
+
* 대소문자 무시로 검색하여 유연하게 매칭.
|
|
146
|
+
*
|
|
147
|
+
* @param commits - 전체 커밋 목록
|
|
148
|
+
* @param searchTerms - 검색어 목록
|
|
149
|
+
* @returns 매칭된 커밋 목록
|
|
150
|
+
*/
|
|
151
|
+
function filterCommitsByTerms(commits, searchTerms) {
|
|
152
|
+
const lowerTerms = searchTerms.map((t) => t.toLowerCase());
|
|
153
|
+
return commits.filter((commit) => {
|
|
154
|
+
const lowerMessage = commit.message.toLowerCase();
|
|
155
|
+
return lowerTerms.some((term) => lowerMessage.includes(term));
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=dep-commit-resolver.js.map
|