@bestend/confluence-cli 1.15.8 → 1.16.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/AGENTS.md +105 -0
- package/bin/confluence.js +26 -9
- package/lib/confluence-client.js +9 -28
- package/package.json +1 -1
package/AGENTS.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Confluence CLI — 프로젝트 가이드
|
|
2
|
+
|
|
3
|
+
## 아키텍처
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
bin/
|
|
7
|
+
index.js # 엔트리포인트 (Node 버전 체크 → confluence.js 로딩)
|
|
8
|
+
confluence.js # Commander CLI 정의 (모든 커맨드 등록)
|
|
9
|
+
|
|
10
|
+
lib/
|
|
11
|
+
confluence-client.js # 핵심 클라이언트 (API 호출, 포맷 변환, 페이지 CRUD)
|
|
12
|
+
config.js # 설정 로드 (~/.confluence-cli/config.json)
|
|
13
|
+
analytics.js # 익명 사용 통계
|
|
14
|
+
|
|
15
|
+
tests/
|
|
16
|
+
confluence-client.test.js # Jest 단위 테스트
|
|
17
|
+
|
|
18
|
+
.github/workflows/
|
|
19
|
+
ci.yml # push/PR → lint + test (Node 18.x, 20.x)
|
|
20
|
+
publish.yml # tag push (v*) → npm publish with OIDC provenance
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## 코드 스타일
|
|
24
|
+
|
|
25
|
+
- ESLint flat config (`eslint.config.js`)
|
|
26
|
+
- 들여쓰기: 2칸 스페이스
|
|
27
|
+
- 따옴표: 작은따옴표
|
|
28
|
+
- 세미콜론: 필수
|
|
29
|
+
- `no-unused-vars`: `_` 접두사 무시
|
|
30
|
+
|
|
31
|
+
## 핵심 변환 파이프라인
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
Markdown → MarkdownIt.render() → HTML → htmlToConfluenceStorage() → Storage XML
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`htmlToConfluenceStorage()`는 MarkdownIt이 생성한 HTML 중 Confluence 전용 처리가 필요한 것만 변환:
|
|
38
|
+
- `<li>` → `<li><p>...</p></li>` (Confluence 필수)
|
|
39
|
+
- `<pre><code>` → `ac:structured-macro` code block with CDATA
|
|
40
|
+
- `<blockquote>` → info/warning/note 매크로
|
|
41
|
+
- `<th>`, `<td>` → `<p>` 래핑
|
|
42
|
+
- `<hr>` → `<hr />` (self-closing)
|
|
43
|
+
|
|
44
|
+
나머지 HTML 태그(h1-h6, p, strong, em, ul, ol, table 등)는 이미 Confluence storage와 호환되므로 변환하지 않음.
|
|
45
|
+
|
|
46
|
+
## 버전 관리
|
|
47
|
+
|
|
48
|
+
- `package.json`의 version은 직접 올리지 않아도 됨
|
|
49
|
+
- publish.yml이 git tag에서 버전을 추출해서 package.json을 자동 맞춤
|
|
50
|
+
- 단, 로컬 개발 시 혼동 방지를 위해 릴리스 전 맞춰두는 것을 권장
|
|
51
|
+
|
|
52
|
+
## 릴리스 절차
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# 1. 변경 커밋 (Conventional Commits)
|
|
56
|
+
git add -A
|
|
57
|
+
git commit -m "feat: add --parent option to create command"
|
|
58
|
+
|
|
59
|
+
# 2. 태그 생성 (SemVer)
|
|
60
|
+
git tag v1.16.0
|
|
61
|
+
|
|
62
|
+
# 3. 푸시 (커밋 + 태그)
|
|
63
|
+
git push origin main --follow-tags
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
태그 push 시 자동으로:
|
|
67
|
+
1. `ci.yml` → lint + test (push to main 트리거)
|
|
68
|
+
2. `publish.yml` → npm publish with OIDC provenance (tag 트리거)
|
|
69
|
+
|
|
70
|
+
### 버전 결정 기준
|
|
71
|
+
|
|
72
|
+
| 변경 유형 | 예시 | 버전 |
|
|
73
|
+
|-----------|------|------|
|
|
74
|
+
| 새 명령어/옵션 추가 | `--parent` 옵션 | minor (x.Y.0) |
|
|
75
|
+
| 버그 수정, 내부 정리 | no-op regex 제거 | patch (x.y.Z) |
|
|
76
|
+
| 하위 호환 깨짐 | 명령어 인터페이스 변경 | major (X.0.0) |
|
|
77
|
+
|
|
78
|
+
### npm OIDC Trusted Publishing
|
|
79
|
+
|
|
80
|
+
publish.yml은 `id-token: write` 권한으로 npm OIDC provenance를 사용.
|
|
81
|
+
npm 토큰이 아닌 GitHub Actions OIDC로 인증하므로 시크릿 관리 불필요.
|
|
82
|
+
Node.js 24.x (npm 11.5.1+) 필요.
|
|
83
|
+
|
|
84
|
+
## 테스트
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm test # Jest 단위 테스트 (40개)
|
|
88
|
+
npm run lint # ESLint
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
테스트는 `axios-mock-adapter`로 HTTP 모킹. 실제 Confluence 인스턴스 불필요.
|
|
92
|
+
|
|
93
|
+
## agent-rules 연동
|
|
94
|
+
|
|
95
|
+
이 CLI는 [mona/agent-rules](https://oss.navercorp.com/mona/agent-rules)의 `confluence-cli` 스킬로 사용됨:
|
|
96
|
+
- 스킬 문서: `sources/skills/confluence-cli/SKILL.md`
|
|
97
|
+
- 설치: `npm install -g @bestend/confluence-cli@latest`
|
|
98
|
+
|
|
99
|
+
CLI 버전 릴리스 후 agent-rules의 SKILL.md `metadata.version`도 맞춰 업데이트할 것.
|
|
100
|
+
|
|
101
|
+
## 금지 사항
|
|
102
|
+
|
|
103
|
+
- `htmlToConfluenceStorage()`에 no-op regex 추가 금지 (X→X 변환은 의미 없음)
|
|
104
|
+
- 전역 entity unescape (`<`→`<` 등) 금지 — code block 내부는 이미 별도 디코딩됨
|
|
105
|
+
- OIDC publish 설정 변경 시 Node.js 24.x 이상 유지 필수
|
package/bin/confluence.js
CHANGED
|
@@ -141,11 +141,12 @@ program
|
|
|
141
141
|
|
|
142
142
|
// Create command
|
|
143
143
|
program
|
|
144
|
-
.command('create <title>
|
|
144
|
+
.command('create <title> [spaceKey]')
|
|
145
145
|
.description('Create a new Confluence page')
|
|
146
146
|
.option('-f, --file <file>', 'Read content from file')
|
|
147
147
|
.option('-c, --content <content>', 'Page content as string')
|
|
148
148
|
.option('--format <format>', 'Content format (storage, html, markdown)', 'storage')
|
|
149
|
+
.option('--parent <parentId>', 'Parent page ID (creates page as child of this page)')
|
|
149
150
|
.option('--validate-storage', 'Validate storage content (XML well-formed) before sending')
|
|
150
151
|
.option('--no-sanitize-storage', 'Disable auto-escaping raw ampersands in storage format')
|
|
151
152
|
.action(async (title, spaceKey, options) => {
|
|
@@ -168,14 +169,30 @@ program
|
|
|
168
169
|
throw new Error('Either --file or --content option is required');
|
|
169
170
|
}
|
|
170
171
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
172
|
+
let result;
|
|
173
|
+
if (options.parent) {
|
|
174
|
+
const parentInfo = await client.getPageInfo(options.parent);
|
|
175
|
+
const derivedSpaceKey = parentInfo.space.key;
|
|
176
|
+
result = await client.createChildPage(title, derivedSpaceKey, options.parent, content, options.format, {
|
|
177
|
+
validateStorage: options.validateStorage,
|
|
178
|
+
sanitizeStorage: options.sanitizeStorage
|
|
179
|
+
});
|
|
180
|
+
console.log(chalk.green('✅ Page created successfully!'));
|
|
181
|
+
console.log(`Title: ${chalk.blue(result.title)}`);
|
|
182
|
+
console.log(`ID: ${chalk.blue(result.id)}`);
|
|
183
|
+
console.log(`Parent: ${chalk.blue(parentInfo.title)} (${options.parent})`);
|
|
184
|
+
} else {
|
|
185
|
+
if (!spaceKey) {
|
|
186
|
+
throw new Error('Space key is required when --parent is not specified');
|
|
187
|
+
}
|
|
188
|
+
result = await client.createPage(title, spaceKey, content, options.format, {
|
|
189
|
+
validateStorage: options.validateStorage,
|
|
190
|
+
sanitizeStorage: options.sanitizeStorage
|
|
191
|
+
});
|
|
192
|
+
console.log(chalk.green('✅ Page created successfully!'));
|
|
193
|
+
console.log(`Title: ${chalk.blue(result.title)}`);
|
|
194
|
+
console.log(`ID: ${chalk.blue(result.id)}`);
|
|
195
|
+
}
|
|
179
196
|
console.log(`Space: ${chalk.blue(result.space.name)} (${result.space.key})`);
|
|
180
197
|
console.log(`URL: ${chalk.gray(`https://${config.domain}/wiki${result._links.webui}`)}`);
|
|
181
198
|
|
package/lib/confluence-client.js
CHANGED
|
@@ -853,27 +853,18 @@ class ConfluenceClient {
|
|
|
853
853
|
* Convert HTML to native Confluence storage format
|
|
854
854
|
*/
|
|
855
855
|
htmlToConfluenceStorage(html) {
|
|
856
|
+
// MarkdownIt already produces valid HTML tags (h1-h6, p, strong, em, ul, ol, table, etc.)
|
|
857
|
+
// This function only transforms elements that need Confluence-specific handling:
|
|
858
|
+
// - li content wrapped in p (Confluence requirement)
|
|
859
|
+
// - pre/code → ac:structured-macro code blocks with CDATA
|
|
860
|
+
// - blockquote → info/warning/note macros
|
|
861
|
+
// - th/td content wrapped in p
|
|
862
|
+
// - hr → self-closing hr /
|
|
856
863
|
let storage = html;
|
|
857
864
|
|
|
858
|
-
//
|
|
859
|
-
storage = storage.replace(/<h([1-6])>(.*?)<\/h[1-6]>/g, '<h$1>$2</h$1>');
|
|
860
|
-
|
|
861
|
-
// Convert paragraphs
|
|
862
|
-
storage = storage.replace(/<p>(.*?)<\/p>/g, '<p>$1</p>');
|
|
863
|
-
|
|
864
|
-
// Convert strong/bold text
|
|
865
|
-
storage = storage.replace(/<strong>(.*?)<\/strong>/g, '<strong>$1</strong>');
|
|
866
|
-
|
|
867
|
-
// Convert emphasis/italic text
|
|
868
|
-
storage = storage.replace(/<em>(.*?)<\/em>/g, '<em>$1</em>');
|
|
869
|
-
|
|
870
|
-
// Convert unordered lists
|
|
871
|
-
storage = storage.replace(/<ul>(.*?)<\/ul>/gs, '<ul>$1</ul>');
|
|
865
|
+
// Wrap li content in p tags (Confluence requirement)
|
|
872
866
|
storage = storage.replace(/<li>(.*?)<\/li>/g, '<li><p>$1</p></li>');
|
|
873
867
|
|
|
874
|
-
// Convert ordered lists
|
|
875
|
-
storage = storage.replace(/<ol>(.*?)<\/ol>/gs, '<ol>$1</ol>');
|
|
876
|
-
|
|
877
868
|
// Convert code blocks to Confluence code macro
|
|
878
869
|
storage = storage.replace(/<pre><code(?:\s+class="language-(\w+)")?>(.*?)<\/code><\/pre>/gs, (_, lang, code) => {
|
|
879
870
|
const language = lang || 'text';
|
|
@@ -892,9 +883,6 @@ class ConfluenceClient {
|
|
|
892
883
|
</ac:structured-macro>`;
|
|
893
884
|
});
|
|
894
885
|
|
|
895
|
-
// Convert inline code
|
|
896
|
-
storage = storage.replace(/<code>(.*?)<\/code>/g, '<code>$1</code>');
|
|
897
|
-
|
|
898
886
|
// Convert blockquotes to appropriate macros based on content
|
|
899
887
|
storage = storage.replace(/<blockquote>(.*?)<\/blockquote>/gs, (_, content) => {
|
|
900
888
|
// Check for admonition patterns
|
|
@@ -921,11 +909,7 @@ class ConfluenceClient {
|
|
|
921
909
|
}
|
|
922
910
|
});
|
|
923
911
|
|
|
924
|
-
//
|
|
925
|
-
storage = storage.replace(/<table>(.*?)<\/table>/gs, '<table>$1</table>');
|
|
926
|
-
storage = storage.replace(/<thead>(.*?)<\/thead>/gs, '<thead>$1</thead>');
|
|
927
|
-
storage = storage.replace(/<tbody>(.*?)<\/tbody>/gs, '<tbody>$1</tbody>');
|
|
928
|
-
storage = storage.replace(/<tr>(.*?)<\/tr>/gs, '<tr>$1</tr>');
|
|
912
|
+
// Wrap th/td content in p tags (Confluence requirement)
|
|
929
913
|
storage = storage.replace(/<th>(.*?)<\/th>/g, '<th><p>$1</p></th>');
|
|
930
914
|
storage = storage.replace(/<td>(.*?)<\/td>/g, '<td><p>$1</p></td>');
|
|
931
915
|
|
|
@@ -934,9 +918,6 @@ class ConfluenceClient {
|
|
|
934
918
|
// Convert horizontal rules
|
|
935
919
|
storage = storage.replace(/<hr\s*\/?>/g, '<hr />');
|
|
936
920
|
|
|
937
|
-
// Clean up any remaining HTML entities and normalize whitespace
|
|
938
|
-
storage = storage.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&');
|
|
939
|
-
|
|
940
921
|
return storage;
|
|
941
922
|
}
|
|
942
923
|
|