@su-record/vibe 2.8.50 → 2.8.52
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/README.ko.md +190 -0
- package/README.md +190 -694
- package/agents/ui-previewer.md +1 -1
- package/commands/vibe.run.md +1 -1
- package/commands/vibe.spec.md +2 -2
- package/commands/vibe.utils.md +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +77 -10
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/postinstall/constants.d.ts.map +1 -1
- package/dist/cli/postinstall/constants.js +2 -0
- package/dist/cli/postinstall/constants.js.map +1 -1
- package/dist/cli/postinstall/inline-skills.js +2 -2
- package/dist/cli/postinstall/inline-skills.js.map +1 -1
- package/dist/cli/postinstall/main.js +1 -1
- package/dist/cli/postinstall/main.js.map +1 -1
- package/dist/cli/types.d.ts +12 -0
- package/dist/cli/types.d.ts.map +1 -1
- package/dist/infra/lib/SkillRepository.js +2 -2
- package/hooks/hooks.json +4 -0
- package/hooks/scripts/devlog-gen.js +230 -0
- package/package.json +1 -1
- package/skills/brand-assets/SKILL.md +1 -1
- package/skills/chub-usage/SKILL.md +33 -9
- package/skills/devlog/SKILL.md +143 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stop Hook - 개발일지 자동 생성
|
|
3
|
+
*
|
|
4
|
+
* 매 커밋 후 카운터를 확인하여 interval(기본 10)에 도달하면
|
|
5
|
+
* LLM을 통해 개발일지를 생성하고 설정된 블로그 레포에 저장합니다.
|
|
6
|
+
*
|
|
7
|
+
* Config: .claude/vibe/config.json → devlog 섹션
|
|
8
|
+
*/
|
|
9
|
+
import { execSync } from 'child_process';
|
|
10
|
+
import fs from 'fs';
|
|
11
|
+
import path from 'path';
|
|
12
|
+
import { PROJECT_DIR, readProjectConfig } from './utils.js';
|
|
13
|
+
|
|
14
|
+
const DEFAULTS = {
|
|
15
|
+
targetDir: 'posts',
|
|
16
|
+
prefix: 'devlog',
|
|
17
|
+
interval: 10,
|
|
18
|
+
autoPush: false,
|
|
19
|
+
lang: 'ko',
|
|
20
|
+
category: 'dev-log',
|
|
21
|
+
tags: [],
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
function getDevlogConfig() {
|
|
25
|
+
const config = readProjectConfig();
|
|
26
|
+
const devlog = config.devlog;
|
|
27
|
+
if (!devlog || !devlog.enabled) return null;
|
|
28
|
+
if (!devlog.targetRepo) return null;
|
|
29
|
+
return { ...DEFAULTS, ...devlog };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function getProjectName() {
|
|
33
|
+
try {
|
|
34
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(PROJECT_DIR, 'package.json'), 'utf-8'));
|
|
35
|
+
return pkg.name || path.basename(PROJECT_DIR);
|
|
36
|
+
} catch {
|
|
37
|
+
return path.basename(PROJECT_DIR);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getGitAuthor() {
|
|
42
|
+
try {
|
|
43
|
+
return execSync('git config user.name', { cwd: PROJECT_DIR, encoding: 'utf-8' }).trim();
|
|
44
|
+
} catch {
|
|
45
|
+
return 'Unknown';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function findLastDevlogNumber(config) {
|
|
50
|
+
const dir = path.join(config.targetRepo, config.targetDir);
|
|
51
|
+
if (!fs.existsSync(dir)) return 0;
|
|
52
|
+
|
|
53
|
+
const files = fs.readdirSync(dir)
|
|
54
|
+
.filter(f => f.startsWith(config.prefix + '-') && f.endsWith('.md'))
|
|
55
|
+
.sort();
|
|
56
|
+
|
|
57
|
+
if (files.length === 0) return 0;
|
|
58
|
+
|
|
59
|
+
const last = files[files.length - 1];
|
|
60
|
+
const match = last.match(new RegExp(`${config.prefix}-(\\d+)\\.md$`));
|
|
61
|
+
return match ? parseInt(match[1], 10) : 0;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function findLastDevlogDate(config) {
|
|
65
|
+
const num = findLastDevlogNumber(config);
|
|
66
|
+
if (num === 0) return null;
|
|
67
|
+
|
|
68
|
+
const padded = String(num).padStart(4, '0');
|
|
69
|
+
const filePath = path.join(config.targetRepo, config.targetDir, `${config.prefix}-${padded}.md`);
|
|
70
|
+
|
|
71
|
+
try {
|
|
72
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
73
|
+
const dateMatch = content.match(/^date:\s*"?(\d{4}-\d{2}-\d{2})"?/m);
|
|
74
|
+
return dateMatch ? dateMatch[1] : null;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function countCommitsSinceLastDevlog(config) {
|
|
81
|
+
const lastDate = findLastDevlogDate(config);
|
|
82
|
+
try {
|
|
83
|
+
const args = lastDate ? `--after="${lastDate}"` : '';
|
|
84
|
+
const log = execSync(`git log --oneline ${args}`, {
|
|
85
|
+
cwd: PROJECT_DIR,
|
|
86
|
+
encoding: 'utf-8',
|
|
87
|
+
}).trim();
|
|
88
|
+
return log ? log.split('\n').length : 0;
|
|
89
|
+
} catch {
|
|
90
|
+
return 0;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function getCommitsSinceLastDevlog(config) {
|
|
95
|
+
const lastDate = findLastDevlogDate(config);
|
|
96
|
+
const args = lastDate ? `--after="${lastDate}"` : '';
|
|
97
|
+
try {
|
|
98
|
+
return execSync(
|
|
99
|
+
`git log --oneline --reverse --format="%h %ad %s" --date=short ${args}`,
|
|
100
|
+
{ cwd: PROJECT_DIR, encoding: 'utf-8' }
|
|
101
|
+
).trim();
|
|
102
|
+
} catch {
|
|
103
|
+
return '';
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function generateDevlogContent(config, commits, nextNumber) {
|
|
108
|
+
const projectName = getProjectName();
|
|
109
|
+
const author = config.author || getGitAuthor();
|
|
110
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
111
|
+
const padded = String(nextNumber).padStart(4, '0');
|
|
112
|
+
|
|
113
|
+
const lines = commits.split('\n').filter(Boolean);
|
|
114
|
+
const interval = Math.min(lines.length, config.interval);
|
|
115
|
+
|
|
116
|
+
// 이번 배치의 커밋만 (최대 interval개)
|
|
117
|
+
const batch = lines.slice(0, interval);
|
|
118
|
+
const dates = batch.map(l => l.split(' ')[1]).filter(Boolean);
|
|
119
|
+
const startDate = dates[0] || today;
|
|
120
|
+
const endDate = dates[dates.length - 1] || today;
|
|
121
|
+
|
|
122
|
+
// 버전 범프 감지
|
|
123
|
+
const versions = batch
|
|
124
|
+
.map(l => l.split(' ').slice(2).join(' '))
|
|
125
|
+
.filter(msg => /^\d+\.\d+/.test(msg));
|
|
126
|
+
const startVersion = versions[0] || '';
|
|
127
|
+
const endVersion = versions[versions.length - 1] || '';
|
|
128
|
+
|
|
129
|
+
// 의미 있는 커밋 분류
|
|
130
|
+
const meaningful = batch
|
|
131
|
+
.map(l => {
|
|
132
|
+
const parts = l.split(' ');
|
|
133
|
+
return { hash: parts[0], date: parts[1], msg: parts.slice(2).join(' ') };
|
|
134
|
+
})
|
|
135
|
+
.filter(c => !/^\d+\.\d+/.test(c.msg)); // 버전 범프 제외
|
|
136
|
+
|
|
137
|
+
// 커밋 테이블 생성
|
|
138
|
+
const commitTable = meaningful.slice(0, 8).map(c => {
|
|
139
|
+
const shortMsg = c.msg.length > 60 ? c.msg.slice(0, 57) + '...' : c.msg;
|
|
140
|
+
return `| \`${shortMsg}\` | |`;
|
|
141
|
+
}).join('\n');
|
|
142
|
+
|
|
143
|
+
const versionInfo = startVersion && endVersion
|
|
144
|
+
? `- **버전**: ${startVersion} → ${endVersion}`
|
|
145
|
+
: '';
|
|
146
|
+
|
|
147
|
+
return `---
|
|
148
|
+
title: "${projectName} 개발일지 #${nextNumber} - (${interval}개 커밋)"
|
|
149
|
+
date: "${today}"
|
|
150
|
+
category: "${config.category}"
|
|
151
|
+
description: ""
|
|
152
|
+
tags: [${[projectName, ...config.tags].map(t => `"${t}"`).join(', ')}]
|
|
153
|
+
author: "${author}"
|
|
154
|
+
lang: "${config.lang}"
|
|
155
|
+
---
|
|
156
|
+
|
|
157
|
+
# ${projectName} 개발일지 #${nextNumber}
|
|
158
|
+
|
|
159
|
+
**작업 기간**: ${startDate}${startDate !== endDate ? ` ~ ${endDate}` : ''}
|
|
160
|
+
|
|
161
|
+
## 이번 기간 작업 내용
|
|
162
|
+
|
|
163
|
+
### ${interval}개 커밋
|
|
164
|
+
|
|
165
|
+
| 커밋 | 내용 |
|
|
166
|
+
|------|------|
|
|
167
|
+
${commitTable}
|
|
168
|
+
|
|
169
|
+
## 개발 현황
|
|
170
|
+
|
|
171
|
+
${versionInfo}
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
**다음 개발일지**: ${config.prefix}-${String(nextNumber + 1).padStart(4, '0')} (다음 ${config.interval}개 커밋 후)
|
|
176
|
+
`;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function writeDevlog(config, content, nextNumber) {
|
|
180
|
+
const padded = String(nextNumber).padStart(4, '0');
|
|
181
|
+
const targetDir = path.join(config.targetRepo, config.targetDir);
|
|
182
|
+
const filePath = path.join(targetDir, `${config.prefix}-${padded}.md`);
|
|
183
|
+
|
|
184
|
+
if (!fs.existsSync(targetDir)) {
|
|
185
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fs.writeFileSync(filePath, content, 'utf-8');
|
|
189
|
+
console.log(`[DEVLOG] Generated: ${filePath}`);
|
|
190
|
+
|
|
191
|
+
if (config.autoPush) {
|
|
192
|
+
try {
|
|
193
|
+
const relPath = path.join(config.targetDir, `${config.prefix}-${padded}.md`);
|
|
194
|
+
execSync(`git add "${relPath}"`, { cwd: config.targetRepo, stdio: 'ignore' });
|
|
195
|
+
execSync(`git commit -m "post: Add ${config.prefix} #${nextNumber}"`, {
|
|
196
|
+
cwd: config.targetRepo,
|
|
197
|
+
stdio: 'ignore',
|
|
198
|
+
});
|
|
199
|
+
execSync('git push', { cwd: config.targetRepo, stdio: 'ignore' });
|
|
200
|
+
console.log(`[DEVLOG] Pushed to ${config.targetRepo}`);
|
|
201
|
+
} catch (e) {
|
|
202
|
+
console.error(`[DEVLOG] Push failed: ${e.message}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
try {
|
|
208
|
+
const config = getDevlogConfig();
|
|
209
|
+
if (!config) process.exit(0);
|
|
210
|
+
|
|
211
|
+
if (!fs.existsSync(config.targetRepo)) {
|
|
212
|
+
console.error(`[DEVLOG] targetRepo not found: ${config.targetRepo}`);
|
|
213
|
+
process.exit(0);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
const commitCount = countCommitsSinceLastDevlog(config);
|
|
217
|
+
if (commitCount < config.interval) {
|
|
218
|
+
process.exit(0);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const nextNumber = findLastDevlogNumber(config) + 1;
|
|
222
|
+
const commits = getCommitsSinceLastDevlog(config);
|
|
223
|
+
|
|
224
|
+
if (!commits) process.exit(0);
|
|
225
|
+
|
|
226
|
+
const content = generateDevlogContent(config, commits, nextNumber);
|
|
227
|
+
writeDevlog(config, content, nextNumber);
|
|
228
|
+
} catch {
|
|
229
|
+
// devlog generation failure should never block
|
|
230
|
+
}
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@ Auto-generate app icons and favicons based on SPEC brand information.
|
|
|
17
17
|
|
|
18
18
|
## Prerequisites
|
|
19
19
|
|
|
20
|
-
- Gemini API key configured (`vibe gemini
|
|
20
|
+
- Gemini API key configured (`vibe gemini key <key>`)
|
|
21
21
|
- SPEC with brand context (app name, colors, style)
|
|
22
22
|
|
|
23
23
|
## Generated Assets
|
|
@@ -19,12 +19,6 @@ Training data의 지식 컷오프 문제를 해결합니다.
|
|
|
19
19
|
| 웹 검색 → 노이즈 섞인 결과 | chub search → 큐레이션된 문서만 |
|
|
20
20
|
| 세션마다 같은 실수 반복 | chub annotate → 학습 누적 |
|
|
21
21
|
|
|
22
|
-
## Prerequisites
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
npm install -g @aisuite/chub
|
|
26
|
-
```
|
|
27
|
-
|
|
28
22
|
## When to Use
|
|
29
23
|
|
|
30
24
|
| Situation | Example |
|
|
@@ -39,6 +33,8 @@ npm install -g @aisuite/chub
|
|
|
39
33
|
```
|
|
40
34
|
외부 API/SDK 코드 작성 요청
|
|
41
35
|
↓
|
|
36
|
+
Step 0: chub 설치 확인 (없으면 자동 설치)
|
|
37
|
+
↓
|
|
42
38
|
Step 1: chub search "<라이브러리명>"
|
|
43
39
|
↓
|
|
44
40
|
Step 2: chub get <id> --lang ts
|
|
@@ -48,6 +44,32 @@ Step 3: 문서 기반 코드 작성
|
|
|
48
44
|
Step 4: gotcha 발견 시 chub annotate
|
|
49
45
|
```
|
|
50
46
|
|
|
47
|
+
## Step 0 — 자동 설치 (Auto-install)
|
|
48
|
+
|
|
49
|
+
**스킬 실행 전 반드시 먼저 수행한다.**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# 1. chub 존재 여부 확인
|
|
53
|
+
which chub || command -v chub
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
chub이 없으면 자동 설치를 시도한다:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npm install -g @aisuite/chub
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
설치 실패 시 `npx @aisuite/chub`로 대체 실행한다:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# search 예시
|
|
66
|
+
npx @aisuite/chub search "stripe"
|
|
67
|
+
# get 예시
|
|
68
|
+
npx @aisuite/chub get stripe/api --lang ts
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
`npx`도 실패하면 context7 또는 Web Search로 폴백한다 (Fallback Chain 참조).
|
|
72
|
+
|
|
51
73
|
## Usage
|
|
52
74
|
|
|
53
75
|
### Step 1 — 문서 검색
|
|
@@ -107,9 +129,11 @@ chub search # 인자 없이 실행하면 전체 목록 확인 가능
|
|
|
107
129
|
## Fallback Chain
|
|
108
130
|
|
|
109
131
|
```
|
|
110
|
-
chub
|
|
132
|
+
which chub 실패
|
|
133
|
+
↓
|
|
134
|
+
npm install -g @aisuite/chub (자동 시도)
|
|
111
135
|
↓
|
|
112
|
-
|
|
136
|
+
실패 시 npx @aisuite/chub <command> (임시 실행)
|
|
113
137
|
↓
|
|
114
|
-
|
|
138
|
+
npx도 실패 시 context7 또는 Web Search fallback
|
|
115
139
|
```
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: devlog
|
|
3
|
+
tier: standard
|
|
4
|
+
description: "Auto-generate devlog posts from git commit history. Triggers every N commits, writes markdown to configured target repo."
|
|
5
|
+
triggers: [devlog, 개발일지, dev log, devlog 작성, 개발일지 작성]
|
|
6
|
+
priority: 60
|
|
7
|
+
---
|
|
8
|
+
# Devlog Auto-Generator
|
|
9
|
+
|
|
10
|
+
Git 커밋 히스토리를 분석하여 개발일지를 자동 생성하고, 설정된 블로그 레포에 포스트로 저장합니다.
|
|
11
|
+
|
|
12
|
+
## Config
|
|
13
|
+
|
|
14
|
+
`.claude/vibe/config.json`의 `devlog` 섹션:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"devlog": {
|
|
19
|
+
"enabled": true,
|
|
20
|
+
"targetRepo": "/absolute/path/to/blog-repo",
|
|
21
|
+
"targetDir": "posts",
|
|
22
|
+
"prefix": "devlog",
|
|
23
|
+
"interval": 10,
|
|
24
|
+
"autoPush": false,
|
|
25
|
+
"lang": "ko",
|
|
26
|
+
"author": "Su",
|
|
27
|
+
"category": "dev-log",
|
|
28
|
+
"tags": []
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
| Key | Required | Default | Description |
|
|
34
|
+
|-----|----------|---------|-------------|
|
|
35
|
+
| `enabled` | Y | `false` | 활성화 여부 |
|
|
36
|
+
| `targetRepo` | Y | — | 블로그 레포 절대 경로 |
|
|
37
|
+
| `targetDir` | N | `"posts"` | 포스트 저장 디렉토리 |
|
|
38
|
+
| `prefix` | N | `"devlog"` | 파일명 프리픽스 (`{prefix}-{NNNN}.md`) |
|
|
39
|
+
| `interval` | N | `10` | 몇 커밋마다 생성할지 |
|
|
40
|
+
| `autoPush` | N | `false` | targetRepo에 자동 commit+push |
|
|
41
|
+
| `lang` | N | `"ko"` | 작성 언어 |
|
|
42
|
+
| `author` | N | git user.name | 작성자 |
|
|
43
|
+
| `category` | N | `"dev-log"` | frontmatter category |
|
|
44
|
+
| `tags` | N | `[]` | 기본 태그 (프로젝트명 등 자동 추가) |
|
|
45
|
+
|
|
46
|
+
## Trigger Modes
|
|
47
|
+
|
|
48
|
+
### 1. Auto (post-commit hook)
|
|
49
|
+
|
|
50
|
+
`devlog-gen.js` hook이 매 커밋마다 카운터를 확인하고, `interval`에 도달하면 `llm-orchestrate.js`를 통해 개발일지를 생성합니다.
|
|
51
|
+
|
|
52
|
+
### 2. Manual
|
|
53
|
+
|
|
54
|
+
사용자가 직접 `/devlog` 또는 `개발일지 작성` 키워드로 트리거. 마지막 개발일지 이후의 커밋을 모아 즉시 생성합니다.
|
|
55
|
+
|
|
56
|
+
## Generation Process
|
|
57
|
+
|
|
58
|
+
### Step 1: Collect Commits
|
|
59
|
+
|
|
60
|
+
마지막 개발일지의 날짜 이후 커밋을 수집합니다:
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
# 마지막 devlog 파일에서 날짜 추출
|
|
64
|
+
# targetRepo/targetDir/{prefix}-NNNN.md 의 frontmatter date
|
|
65
|
+
git log --oneline --after="{last_date}" --reverse
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Step 2: Analyze & Group
|
|
69
|
+
|
|
70
|
+
- 버전 범프 커밋 식별 (X.Y.Z 패턴)
|
|
71
|
+
- feat/fix/refactor/docs 분류
|
|
72
|
+
- 주요 커밋 선별 (의미 있는 변경)
|
|
73
|
+
|
|
74
|
+
### Step 3: Generate Markdown
|
|
75
|
+
|
|
76
|
+
다음 frontmatter 포맷으로 생성:
|
|
77
|
+
|
|
78
|
+
```markdown
|
|
79
|
+
---
|
|
80
|
+
title: "{project_name} 개발일지 #{next_number} - {summary_title} ({interval}개 커밋)"
|
|
81
|
+
date: "{today}"
|
|
82
|
+
category: "{category}"
|
|
83
|
+
description: "{one_line_description}"
|
|
84
|
+
tags: [{project_tags}, {auto_detected_tags}]
|
|
85
|
+
author: "{author}"
|
|
86
|
+
lang: "{lang}"
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
# {title}
|
|
90
|
+
|
|
91
|
+
**작업 기간**: {start_date} ~ {end_date}
|
|
92
|
+
|
|
93
|
+
## 이번 기간 작업 내용
|
|
94
|
+
|
|
95
|
+
### {theme} ({interval}개 커밋)
|
|
96
|
+
|
|
97
|
+
{overview_paragraph}
|
|
98
|
+
|
|
99
|
+
| 커밋 | 내용 |
|
|
100
|
+
|------|------|
|
|
101
|
+
| `{meaningful_commit_message}` | **{highlight}** |
|
|
102
|
+
...
|
|
103
|
+
|
|
104
|
+
## 작업 하이라이트
|
|
105
|
+
|
|
106
|
+
{2-3 highlights with code blocks or diagrams}
|
|
107
|
+
|
|
108
|
+
## 개발 현황
|
|
109
|
+
|
|
110
|
+
- **버전**: {start_version} → {end_version}
|
|
111
|
+
- **핵심**: {key_changes}
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
**다음 개발일지**: {prefix}-{next+1} (다음 {interval}개 커밋 후)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Step 4: Write File
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
{targetRepo}/{targetDir}/{prefix}-{NNNN}.md
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
번호는 기존 파일에서 마지막 번호 + 1로 자동 결정.
|
|
125
|
+
|
|
126
|
+
### Step 5: (Optional) Auto Push
|
|
127
|
+
|
|
128
|
+
`autoPush: true`이면:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
cd {targetRepo}
|
|
132
|
+
git add {targetDir}/{prefix}-{NNNN}.md
|
|
133
|
+
git commit -m "post: Add {prefix} #{NNNN}"
|
|
134
|
+
git push
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
## Rules
|
|
138
|
+
|
|
139
|
+
- **커밋 메시지 원문 보존** — 커밋 메시지를 그대로 인용, 임의 변경 금지
|
|
140
|
+
- **버전 범프 커밋 포함** — 테이블에는 포함하되 하이라이트에서 제외
|
|
141
|
+
- **하이라이트는 최대 3개** — 가장 영향력 있는 변경만 선별
|
|
142
|
+
- **코드 블록 활용** — Before/After 비교, 아키텍처 다이어그램 권장
|
|
143
|
+
- **숫자 강조** — 줄 수 변화(-N줄), 버전 범위, 파일 수 등
|