confluence-cli 1.0.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/.eslintrc.js +23 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +34 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +26 -0
- package/.github/pull_request_template.md +31 -0
- package/.github/workflows/ci.yml +58 -0
- package/.releaserc +17 -0
- package/CHANGELOG.md +35 -0
- package/CONTRIBUTING.md +220 -0
- package/LICENSE +21 -0
- package/README.md +178 -0
- package/bin/confluence.js +110 -0
- package/bin/index.js +24 -0
- package/examples/demo.sh +43 -0
- package/jest.config.js +13 -0
- package/lib/config.js +96 -0
- package/lib/confluence-client.js +137 -0
- package/package.json +48 -0
- package/tests/confluence-client.test.js +29 -0
package/.eslintrc.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
env: {
|
|
3
|
+
es2021: true,
|
|
4
|
+
node: true,
|
|
5
|
+
jest: true
|
|
6
|
+
},
|
|
7
|
+
extends: [
|
|
8
|
+
'eslint:recommended'
|
|
9
|
+
],
|
|
10
|
+
parserOptions: {
|
|
11
|
+
ecmaVersion: 12,
|
|
12
|
+
sourceType: 'module'
|
|
13
|
+
},
|
|
14
|
+
rules: {
|
|
15
|
+
'indent': ['error', 2],
|
|
16
|
+
'linebreak-style': ['error', 'unix'],
|
|
17
|
+
'quotes': ['error', 'single'],
|
|
18
|
+
'semi': ['error', 'always'],
|
|
19
|
+
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
20
|
+
'no-console': 'off',
|
|
21
|
+
'no-process-exit': 'off'
|
|
22
|
+
}
|
|
23
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Bug report
|
|
3
|
+
about: Create a report to help us improve
|
|
4
|
+
title: '[BUG] '
|
|
5
|
+
labels: bug
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Describe the bug**
|
|
11
|
+
A clear and concise description of what the bug is.
|
|
12
|
+
|
|
13
|
+
**To Reproduce**
|
|
14
|
+
Steps to reproduce the behavior:
|
|
15
|
+
1. Run command '...'
|
|
16
|
+
2. See error
|
|
17
|
+
|
|
18
|
+
**Expected behavior**
|
|
19
|
+
A clear and concise description of what you expected to happen.
|
|
20
|
+
|
|
21
|
+
**Screenshots**
|
|
22
|
+
If applicable, add screenshots to help explain your problem.
|
|
23
|
+
|
|
24
|
+
**Environment (please complete the following information):**
|
|
25
|
+
- OS: [e.g. macOS, Windows, Linux]
|
|
26
|
+
- Node.js version: [e.g. 18.17.0]
|
|
27
|
+
- confluence-cli version: [e.g. 1.0.0]
|
|
28
|
+
- Confluence version: [e.g. Cloud, Server 8.5]
|
|
29
|
+
|
|
30
|
+
**Additional context**
|
|
31
|
+
Add any other context about the problem here.
|
|
32
|
+
|
|
33
|
+
**Error logs**
|
|
34
|
+
If applicable, add error logs or stack traces.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: Feature request
|
|
3
|
+
about: Suggest an idea for this project
|
|
4
|
+
title: '[FEATURE] '
|
|
5
|
+
labels: enhancement
|
|
6
|
+
assignees: ''
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
|
12
|
+
|
|
13
|
+
**Describe the solution you'd like**
|
|
14
|
+
A clear and concise description of what you want to happen.
|
|
15
|
+
|
|
16
|
+
**Describe alternatives you've considered**
|
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
|
18
|
+
|
|
19
|
+
**Use case**
|
|
20
|
+
Describe how this feature would be used and who would benefit from it.
|
|
21
|
+
|
|
22
|
+
**Additional context**
|
|
23
|
+
Add any other context or screenshots about the feature request here.
|
|
24
|
+
|
|
25
|
+
**Implementation suggestions**
|
|
26
|
+
If you have ideas about how this could be implemented, please share them here.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
## Pull Request Template
|
|
2
|
+
|
|
3
|
+
### Description
|
|
4
|
+
Brief description of what this PR does.
|
|
5
|
+
|
|
6
|
+
### Type of Change
|
|
7
|
+
- [ ] Bug fix (non-breaking change which fixes an issue)
|
|
8
|
+
- [ ] New feature (non-breaking change which adds functionality)
|
|
9
|
+
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
|
|
10
|
+
- [ ] Documentation update
|
|
11
|
+
- [ ] Performance improvement
|
|
12
|
+
- [ ] Code refactoring
|
|
13
|
+
|
|
14
|
+
### Testing
|
|
15
|
+
- [ ] Tests pass locally with my changes
|
|
16
|
+
- [ ] I have added tests that prove my fix is effective or that my feature works
|
|
17
|
+
- [ ] New and existing unit tests pass locally with my changes
|
|
18
|
+
|
|
19
|
+
### Checklist
|
|
20
|
+
- [ ] My code follows the style guidelines of this project
|
|
21
|
+
- [ ] I have performed a self-review of my own code
|
|
22
|
+
- [ ] I have commented my code, particularly in hard-to-understand areas
|
|
23
|
+
- [ ] I have made corresponding changes to the documentation
|
|
24
|
+
- [ ] My changes generate no new warnings
|
|
25
|
+
- [ ] Any dependent changes have been merged and published in downstream modules
|
|
26
|
+
|
|
27
|
+
### Screenshots (if applicable)
|
|
28
|
+
Add screenshots to help explain your changes.
|
|
29
|
+
|
|
30
|
+
### Additional Context
|
|
31
|
+
Add any other context about the pull request here.
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: CI/CD
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [ main, develop ]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [ main ]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [16.x, 18.x, 20.x]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v3
|
|
19
|
+
|
|
20
|
+
- name: Use Node.js ${{ matrix.node-version }}
|
|
21
|
+
uses: actions/setup-node@v3
|
|
22
|
+
with:
|
|
23
|
+
node-version: ${{ matrix.node-version }}
|
|
24
|
+
cache: 'npm'
|
|
25
|
+
|
|
26
|
+
- run: npm ci
|
|
27
|
+
- run: npm run lint
|
|
28
|
+
- run: npm test
|
|
29
|
+
|
|
30
|
+
security:
|
|
31
|
+
runs-on: ubuntu-latest
|
|
32
|
+
steps:
|
|
33
|
+
- uses: actions/checkout@v3
|
|
34
|
+
- name: Run npm audit
|
|
35
|
+
run: npm audit --audit-level moderate
|
|
36
|
+
|
|
37
|
+
publish:
|
|
38
|
+
needs: [test, security]
|
|
39
|
+
runs-on: ubuntu-latest
|
|
40
|
+
if: github.ref == 'refs/heads/main'
|
|
41
|
+
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v3
|
|
44
|
+
|
|
45
|
+
- name: Setup Node.js
|
|
46
|
+
uses: actions/setup-node@v3
|
|
47
|
+
with:
|
|
48
|
+
node-version: '18'
|
|
49
|
+
registry-url: 'https://registry.npmjs.org'
|
|
50
|
+
|
|
51
|
+
- run: npm ci
|
|
52
|
+
- run: npm test
|
|
53
|
+
|
|
54
|
+
- name: Semantic Release
|
|
55
|
+
uses: cycjimmy/semantic-release-action@v3
|
|
56
|
+
env:
|
|
57
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
58
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/.releaserc
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
"branches": ["main"],
|
|
3
|
+
"plugins": [
|
|
4
|
+
"@semantic-release/commit-analyzer",
|
|
5
|
+
"@semantic-release/release-notes-generator",
|
|
6
|
+
"@semantic-release/changelog",
|
|
7
|
+
"@semantic-release/npm",
|
|
8
|
+
"@semantic-release/github",
|
|
9
|
+
[
|
|
10
|
+
"@semantic-release/git",
|
|
11
|
+
{
|
|
12
|
+
"assets": ["package.json", "CHANGELOG.md"],
|
|
13
|
+
"message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
]
|
|
17
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# Confluence CLI Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [1.0.0] - 2025-06-26
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release of Confluence CLI
|
|
12
|
+
- Read Confluence pages by ID or URL
|
|
13
|
+
- Search functionality with customizable limits
|
|
14
|
+
- Page information display
|
|
15
|
+
- List all Confluence spaces
|
|
16
|
+
- Interactive configuration setup
|
|
17
|
+
- Environment variable support
|
|
18
|
+
- HTML and text output formats
|
|
19
|
+
- Comprehensive README with examples
|
|
20
|
+
- MIT License
|
|
21
|
+
|
|
22
|
+
### Features
|
|
23
|
+
- `confluence init` - Interactive configuration setup
|
|
24
|
+
- `confluence read <pageId>` - Read page content with format options
|
|
25
|
+
- `confluence info <pageId>` - Display page information
|
|
26
|
+
- `confluence search <query>` - Search pages with optional limit
|
|
27
|
+
- `confluence spaces` - List all available spaces
|
|
28
|
+
|
|
29
|
+
### Dependencies
|
|
30
|
+
- commander for CLI framework
|
|
31
|
+
- axios for HTTP requests
|
|
32
|
+
- chalk for colored output
|
|
33
|
+
- inquirer for interactive prompts
|
|
34
|
+
- html-to-text for content conversion
|
|
35
|
+
- ora for loading indicators
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Contributing to Confluence CLI
|
|
2
|
+
|
|
3
|
+
Thank you for your interest in contributing to Confluence CLI! This document provides guidelines and information about contributing to this project.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Code of Conduct](#code-of-conduct)
|
|
8
|
+
- [Getting Started](#getting-started)
|
|
9
|
+
- [Development Setup](#development-setup)
|
|
10
|
+
- [Making Changes](#making-changes)
|
|
11
|
+
- [Testing](#testing)
|
|
12
|
+
- [Submitting Changes](#submitting-changes)
|
|
13
|
+
- [Coding Standards](#coding-standards)
|
|
14
|
+
|
|
15
|
+
## Code of Conduct
|
|
16
|
+
|
|
17
|
+
This project adheres to a code of conduct. By participating, you are expected to uphold this code. Please be respectful and considerate in all interactions.
|
|
18
|
+
|
|
19
|
+
## Getting Started
|
|
20
|
+
|
|
21
|
+
1. Fork the repository on GitHub
|
|
22
|
+
2. Clone your fork locally
|
|
23
|
+
3. Create a branch for your changes
|
|
24
|
+
4. Make your changes
|
|
25
|
+
5. Test your changes
|
|
26
|
+
6. Submit a pull request
|
|
27
|
+
|
|
28
|
+
## Development Setup
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Clone your fork
|
|
32
|
+
git clone https://github.com/your-username/confluence-cli.git
|
|
33
|
+
cd confluence-cli
|
|
34
|
+
|
|
35
|
+
# Install dependencies
|
|
36
|
+
npm install
|
|
37
|
+
|
|
38
|
+
# Set up your test environment
|
|
39
|
+
export CONFLUENCE_DOMAIN="your-test-domain.atlassian.net"
|
|
40
|
+
export CONFLUENCE_API_TOKEN="your-test-token"
|
|
41
|
+
|
|
42
|
+
# Test the CLI locally
|
|
43
|
+
node bin/confluence.js --help
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
## Making Changes
|
|
47
|
+
|
|
48
|
+
### Branch Naming
|
|
49
|
+
|
|
50
|
+
Use descriptive branch names:
|
|
51
|
+
- `feature/add-page-creation` - for new features
|
|
52
|
+
- `fix/search-pagination` - for bug fixes
|
|
53
|
+
- `docs/update-readme` - for documentation updates
|
|
54
|
+
- `refactor/client-architecture` - for refactoring
|
|
55
|
+
|
|
56
|
+
### Commit Messages
|
|
57
|
+
|
|
58
|
+
Write clear, descriptive commit messages:
|
|
59
|
+
```
|
|
60
|
+
feat: add page creation functionality
|
|
61
|
+
|
|
62
|
+
- Add create command to CLI
|
|
63
|
+
- Implement createPage method in client
|
|
64
|
+
- Add tests for page creation
|
|
65
|
+
- Update README with new command
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Use conventional commit format:
|
|
69
|
+
- `feat:` - new features
|
|
70
|
+
- `fix:` - bug fixes
|
|
71
|
+
- `docs:` - documentation changes
|
|
72
|
+
- `style:` - formatting changes
|
|
73
|
+
- `refactor:` - code refactoring
|
|
74
|
+
- `test:` - adding tests
|
|
75
|
+
- `chore:` - maintenance tasks
|
|
76
|
+
|
|
77
|
+
## Testing
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
# Run all tests
|
|
81
|
+
npm test
|
|
82
|
+
|
|
83
|
+
# Run tests in watch mode
|
|
84
|
+
npm run test:watch
|
|
85
|
+
|
|
86
|
+
# Check test coverage
|
|
87
|
+
npm run test:coverage
|
|
88
|
+
|
|
89
|
+
# Manual testing
|
|
90
|
+
node bin/confluence.js read 123456789
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Test Guidelines
|
|
94
|
+
|
|
95
|
+
- Write tests for new functionality
|
|
96
|
+
- Ensure existing tests pass
|
|
97
|
+
- Aim for good test coverage
|
|
98
|
+
- Use descriptive test names
|
|
99
|
+
- Mock external API calls
|
|
100
|
+
|
|
101
|
+
## Submitting Changes
|
|
102
|
+
|
|
103
|
+
1. **Push your changes** to your fork
|
|
104
|
+
2. **Create a pull request** against the main branch
|
|
105
|
+
3. **Fill out the PR template** with:
|
|
106
|
+
- Description of changes
|
|
107
|
+
- Type of change (bug fix, feature, etc.)
|
|
108
|
+
- Testing performed
|
|
109
|
+
- Screenshots (if applicable)
|
|
110
|
+
|
|
111
|
+
### Pull Request Guidelines
|
|
112
|
+
|
|
113
|
+
- Keep PRs focused and atomic
|
|
114
|
+
- Include tests for new functionality
|
|
115
|
+
- Update documentation as needed
|
|
116
|
+
- Ensure CI passes
|
|
117
|
+
- Request review from maintainers
|
|
118
|
+
|
|
119
|
+
## Coding Standards
|
|
120
|
+
|
|
121
|
+
### JavaScript Style
|
|
122
|
+
|
|
123
|
+
- Use ES6+ features when appropriate
|
|
124
|
+
- Follow existing code style
|
|
125
|
+
- Use meaningful variable names
|
|
126
|
+
- Add comments for complex logic
|
|
127
|
+
- Keep functions small and focused
|
|
128
|
+
|
|
129
|
+
### File Structure
|
|
130
|
+
|
|
131
|
+
```
|
|
132
|
+
bin/ # CLI entry points
|
|
133
|
+
lib/ # Core library code
|
|
134
|
+
├── confluence-client.js
|
|
135
|
+
├── config.js
|
|
136
|
+
└── utils.js
|
|
137
|
+
tests/ # Test files
|
|
138
|
+
docs/ # Additional documentation
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Error Handling
|
|
142
|
+
|
|
143
|
+
- Always handle errors gracefully
|
|
144
|
+
- Provide helpful error messages
|
|
145
|
+
- Use appropriate exit codes
|
|
146
|
+
- Log errors appropriately
|
|
147
|
+
|
|
148
|
+
### Documentation
|
|
149
|
+
|
|
150
|
+
- Update README for new features
|
|
151
|
+
- Add JSDoc comments for functions
|
|
152
|
+
- Update CHANGELOG for releases
|
|
153
|
+
- Include usage examples
|
|
154
|
+
|
|
155
|
+
## Feature Requests
|
|
156
|
+
|
|
157
|
+
Before implementing major features:
|
|
158
|
+
|
|
159
|
+
1. **Check existing issues** to avoid duplication
|
|
160
|
+
2. **Create an issue** to discuss the feature
|
|
161
|
+
3. **Get maintainer feedback** before starting work
|
|
162
|
+
4. **Follow the agreed approach** in implementation
|
|
163
|
+
|
|
164
|
+
## Bug Reports
|
|
165
|
+
|
|
166
|
+
When reporting bugs:
|
|
167
|
+
|
|
168
|
+
1. **Check existing issues** first
|
|
169
|
+
2. **Provide reproduction steps**
|
|
170
|
+
3. **Include environment details**:
|
|
171
|
+
- Node.js version
|
|
172
|
+
- OS and version
|
|
173
|
+
- CLI version
|
|
174
|
+
4. **Share error messages** and logs
|
|
175
|
+
|
|
176
|
+
## Development Tips
|
|
177
|
+
|
|
178
|
+
### Local Testing
|
|
179
|
+
|
|
180
|
+
```bash
|
|
181
|
+
# Test against your Confluence instance
|
|
182
|
+
export CONFLUENCE_DOMAIN="your-domain.atlassian.net"
|
|
183
|
+
export CONFLUENCE_API_TOKEN="your-token"
|
|
184
|
+
|
|
185
|
+
# Test commands
|
|
186
|
+
node bin/confluence.js spaces
|
|
187
|
+
node bin/confluence.js search "test"
|
|
188
|
+
node bin/confluence.js read 123456789
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
### Debugging
|
|
192
|
+
|
|
193
|
+
```bash
|
|
194
|
+
# Enable debug mode
|
|
195
|
+
DEBUG=confluence-cli node bin/confluence.js read 123456789
|
|
196
|
+
|
|
197
|
+
# Use Node.js debugger
|
|
198
|
+
node --inspect-brk bin/confluence.js read 123456789
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
## Release Process
|
|
202
|
+
|
|
203
|
+
For maintainers:
|
|
204
|
+
|
|
205
|
+
1. Update version in `package.json`
|
|
206
|
+
2. Update `CHANGELOG.md`
|
|
207
|
+
3. Create git tag
|
|
208
|
+
4. Push to npm
|
|
209
|
+
5. Create GitHub release
|
|
210
|
+
|
|
211
|
+
## Questions?
|
|
212
|
+
|
|
213
|
+
If you have questions about contributing:
|
|
214
|
+
|
|
215
|
+
1. Check existing documentation
|
|
216
|
+
2. Search closed issues
|
|
217
|
+
3. Ask in a new issue
|
|
218
|
+
4. Contact maintainers
|
|
219
|
+
|
|
220
|
+
Thank you for contributing to Confluence CLI! 🚀
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Confluence CLI Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# Confluence CLI
|
|
2
|
+
|
|
3
|
+
A powerful command-line interface for Atlassian Confluence that allows you to read, search, and manage your Confluence content from the terminal.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 📖 **Read pages** - Get page content in text or HTML format
|
|
8
|
+
- 🔍 **Search** - Find pages using Confluence's powerful search
|
|
9
|
+
- ℹ️ **Page info** - Get detailed information about pages
|
|
10
|
+
- 🏠 **List spaces** - View all available Confluence spaces
|
|
11
|
+
- 🔧 **Easy setup** - Simple configuration with environment variables or interactive setup
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install -g confluence-cli
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or run directly with npx:
|
|
20
|
+
```bash
|
|
21
|
+
npx confluence-cli
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Quick Start
|
|
25
|
+
|
|
26
|
+
1. **Initialize configuration:**
|
|
27
|
+
```bash
|
|
28
|
+
confluence init
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
2. **Read a page:**
|
|
32
|
+
```bash
|
|
33
|
+
confluence read 123456789
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
3. **Search for pages:**
|
|
37
|
+
```bash
|
|
38
|
+
confluence search "my search term"
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Configuration
|
|
42
|
+
|
|
43
|
+
### Option 1: Interactive Setup
|
|
44
|
+
```bash
|
|
45
|
+
confluence init
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Option 2: Environment Variables
|
|
49
|
+
```bash
|
|
50
|
+
export CONFLUENCE_DOMAIN="your-domain.atlassian.net"
|
|
51
|
+
export CONFLUENCE_API_TOKEN="your-api-token"
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Getting Your API Token
|
|
55
|
+
|
|
56
|
+
1. Go to [Atlassian Account Settings](https://id.atlassian.com/manage-profile/security/api-tokens)
|
|
57
|
+
2. Click "Create API token"
|
|
58
|
+
3. Give it a label (e.g., "confluence-cli")
|
|
59
|
+
4. Copy the generated token
|
|
60
|
+
|
|
61
|
+
## Usage
|
|
62
|
+
|
|
63
|
+
### Read a Page
|
|
64
|
+
```bash
|
|
65
|
+
# Read by page ID
|
|
66
|
+
confluence read 123456789
|
|
67
|
+
|
|
68
|
+
# Read with HTML format
|
|
69
|
+
confluence read 123456789 --format html
|
|
70
|
+
|
|
71
|
+
# Read by URL (with pageId parameter)
|
|
72
|
+
confluence read "https://yourcompany.atlassian.net/wiki/spaces/SPACE/pages/123456789/Page+Title"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Get Page Information
|
|
76
|
+
```bash
|
|
77
|
+
confluence info 123456789
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Search Pages
|
|
81
|
+
```bash
|
|
82
|
+
# Basic search
|
|
83
|
+
confluence search "search term"
|
|
84
|
+
|
|
85
|
+
# Limit results
|
|
86
|
+
confluence search "search term" --limit 5
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### List Spaces
|
|
90
|
+
```bash
|
|
91
|
+
confluence spaces
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Commands
|
|
95
|
+
|
|
96
|
+
| Command | Description | Options |
|
|
97
|
+
|---------|-------------|---------|
|
|
98
|
+
| `init` | Initialize CLI configuration | - |
|
|
99
|
+
| `read <pageId>` | Read page content | `--format <html\|text>` |
|
|
100
|
+
| `info <pageId>` | Get page information | - |
|
|
101
|
+
| `search <query>` | Search for pages | `--limit <number>` |
|
|
102
|
+
| `spaces` | List all spaces | - |
|
|
103
|
+
|
|
104
|
+
## Examples
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Setup
|
|
108
|
+
confluence init
|
|
109
|
+
|
|
110
|
+
# Read a page as text
|
|
111
|
+
confluence read 123456789
|
|
112
|
+
|
|
113
|
+
# Read a page as HTML
|
|
114
|
+
confluence read 123456789 --format html
|
|
115
|
+
|
|
116
|
+
# Get page details
|
|
117
|
+
confluence info 123456789
|
|
118
|
+
|
|
119
|
+
# Search with limit
|
|
120
|
+
confluence search "API documentation" --limit 3
|
|
121
|
+
|
|
122
|
+
# List all spaces
|
|
123
|
+
confluence spaces
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
## Development
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
# Clone the repository
|
|
130
|
+
git clone https://github.com/pchuri/confluence-cli.git
|
|
131
|
+
cd confluence-cli
|
|
132
|
+
|
|
133
|
+
# Install dependencies
|
|
134
|
+
npm install
|
|
135
|
+
|
|
136
|
+
# Run locally
|
|
137
|
+
npm start -- --help
|
|
138
|
+
|
|
139
|
+
# Run tests
|
|
140
|
+
npm test
|
|
141
|
+
|
|
142
|
+
# Lint code
|
|
143
|
+
npm run lint
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Contributing
|
|
147
|
+
|
|
148
|
+
1. Fork the repository
|
|
149
|
+
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
|
150
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
151
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
152
|
+
5. Open a Pull Request
|
|
153
|
+
|
|
154
|
+
## License
|
|
155
|
+
|
|
156
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
157
|
+
|
|
158
|
+
## Roadmap
|
|
159
|
+
|
|
160
|
+
- [ ] Create and update pages
|
|
161
|
+
- [ ] Page templates
|
|
162
|
+
- [ ] Bulk operations
|
|
163
|
+
- [ ] Export pages to different formats
|
|
164
|
+
- [ ] Integration with other Atlassian tools (Jira)
|
|
165
|
+
- [ ] Page attachments management
|
|
166
|
+
- [ ] Comments and reviews
|
|
167
|
+
|
|
168
|
+
## Support
|
|
169
|
+
|
|
170
|
+
If you encounter any issues or have questions:
|
|
171
|
+
|
|
172
|
+
1. Check the [Issues](https://github.com/pchuri/confluence-cli/issues) page
|
|
173
|
+
2. Create a new issue if your problem isn't already reported
|
|
174
|
+
3. Provide detailed information about your environment and the issue
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
Made with ❤️ for the Confluence community
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { program } = require('commander');
|
|
4
|
+
const chalk = require('chalk');
|
|
5
|
+
const ConfluenceClient = require('../lib/confluence-client');
|
|
6
|
+
const { getConfig, initConfig } = require('../lib/config');
|
|
7
|
+
|
|
8
|
+
program
|
|
9
|
+
.name('confluence')
|
|
10
|
+
.description('CLI tool for Atlassian Confluence')
|
|
11
|
+
.version('1.0.0');
|
|
12
|
+
|
|
13
|
+
// Init command
|
|
14
|
+
program
|
|
15
|
+
.command('init')
|
|
16
|
+
.description('Initialize Confluence CLI configuration')
|
|
17
|
+
.action(async () => {
|
|
18
|
+
await initConfig();
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// Read command
|
|
22
|
+
program
|
|
23
|
+
.command('read <pageId>')
|
|
24
|
+
.description('Read a Confluence page by ID or URL')
|
|
25
|
+
.option('-f, --format <format>', 'Output format (html, text)', 'text')
|
|
26
|
+
.action(async (pageId, options) => {
|
|
27
|
+
try {
|
|
28
|
+
const config = getConfig();
|
|
29
|
+
const client = new ConfluenceClient(config);
|
|
30
|
+
const content = await client.readPage(pageId, options.format);
|
|
31
|
+
console.log(content);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
console.error(chalk.red('Error:'), error.message);
|
|
34
|
+
process.exit(1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// Info command
|
|
39
|
+
program
|
|
40
|
+
.command('info <pageId>')
|
|
41
|
+
.description('Get information about a Confluence page')
|
|
42
|
+
.action(async (pageId) => {
|
|
43
|
+
try {
|
|
44
|
+
const config = getConfig();
|
|
45
|
+
const client = new ConfluenceClient(config);
|
|
46
|
+
const info = await client.getPageInfo(pageId);
|
|
47
|
+
console.log(chalk.blue('Page Information:'));
|
|
48
|
+
console.log(`Title: ${chalk.green(info.title)}`);
|
|
49
|
+
console.log(`ID: ${chalk.green(info.id)}`);
|
|
50
|
+
console.log(`Type: ${chalk.green(info.type)}`);
|
|
51
|
+
console.log(`Status: ${chalk.green(info.status)}`);
|
|
52
|
+
if (info.space) {
|
|
53
|
+
console.log(`Space: ${chalk.green(info.space.name)} (${info.space.key})`);
|
|
54
|
+
}
|
|
55
|
+
} catch (error) {
|
|
56
|
+
console.error(chalk.red('Error:'), error.message);
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// Search command
|
|
62
|
+
program
|
|
63
|
+
.command('search <query>')
|
|
64
|
+
.description('Search for Confluence pages')
|
|
65
|
+
.option('-l, --limit <limit>', 'Limit number of results', '10')
|
|
66
|
+
.action(async (query, options) => {
|
|
67
|
+
try {
|
|
68
|
+
const config = getConfig();
|
|
69
|
+
const client = new ConfluenceClient(config);
|
|
70
|
+
const results = await client.search(query, parseInt(options.limit));
|
|
71
|
+
|
|
72
|
+
if (results.length === 0) {
|
|
73
|
+
console.log(chalk.yellow('No results found.'));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
console.log(chalk.blue(`Found ${results.length} results:`));
|
|
78
|
+
results.forEach((result, index) => {
|
|
79
|
+
console.log(`${index + 1}. ${chalk.green(result.title)} (ID: ${result.id})`);
|
|
80
|
+
if (result.excerpt) {
|
|
81
|
+
console.log(` ${chalk.gray(result.excerpt)}`);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} catch (error) {
|
|
85
|
+
console.error(chalk.red('Error:'), error.message);
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// List spaces command
|
|
91
|
+
program
|
|
92
|
+
.command('spaces')
|
|
93
|
+
.description('List all Confluence spaces')
|
|
94
|
+
.action(async () => {
|
|
95
|
+
try {
|
|
96
|
+
const config = getConfig();
|
|
97
|
+
const client = new ConfluenceClient(config);
|
|
98
|
+
const spaces = await client.getSpaces();
|
|
99
|
+
|
|
100
|
+
console.log(chalk.blue('Available spaces:'));
|
|
101
|
+
spaces.forEach(space => {
|
|
102
|
+
console.log(`${chalk.green(space.key)} - ${space.name}`);
|
|
103
|
+
});
|
|
104
|
+
} catch (error) {
|
|
105
|
+
console.error(chalk.red('Error:'), error.message);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
program.parse();
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* confluence-cli
|
|
5
|
+
* A powerful command-line interface for Atlassian Confluence
|
|
6
|
+
*
|
|
7
|
+
* @author Your Name
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
// Make sure we're using a supported Node.js version
|
|
14
|
+
const nodeVersion = process.version;
|
|
15
|
+
const requiredVersion = '14.0.0';
|
|
16
|
+
|
|
17
|
+
if (!nodeVersion.startsWith('v') ||
|
|
18
|
+
parseInt(nodeVersion.slice(1).split('.')[0]) < parseInt(requiredVersion.split('.')[0])) {
|
|
19
|
+
console.error(`Error: Node.js ${requiredVersion} or higher is required. You are using ${nodeVersion}.`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Load the main CLI application
|
|
24
|
+
require('./confluence.js');
|
package/examples/demo.sh
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# Example script showing how to use confluence-cli
|
|
4
|
+
|
|
5
|
+
echo "🚀 Confluence CLI Examples"
|
|
6
|
+
echo "========================="
|
|
7
|
+
|
|
8
|
+
# Check if confluence command is available
|
|
9
|
+
if ! command -v confluence &> /dev/null; then
|
|
10
|
+
echo "❌ confluence command not found. Please install confluence-cli first:"
|
|
11
|
+
echo " npm install -g confluence-cli"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
echo ""
|
|
16
|
+
echo "📋 Listing all spaces..."
|
|
17
|
+
confluence spaces
|
|
18
|
+
|
|
19
|
+
echo ""
|
|
20
|
+
echo "🔍 Searching for 'API' documentation..."
|
|
21
|
+
confluence search "API" --limit 5
|
|
22
|
+
|
|
23
|
+
echo ""
|
|
24
|
+
echo "ℹ️ Getting information about a specific page..."
|
|
25
|
+
# Replace this with an actual page ID from your Confluence
|
|
26
|
+
read -p "Enter a page ID to get info: " PAGE_ID
|
|
27
|
+
if [ ! -z "$PAGE_ID" ]; then
|
|
28
|
+
confluence info "$PAGE_ID"
|
|
29
|
+
|
|
30
|
+
echo ""
|
|
31
|
+
read -p "Do you want to read this page? (y/N): " READ_PAGE
|
|
32
|
+
if [ "$READ_PAGE" = "y" ] || [ "$READ_PAGE" = "Y" ]; then
|
|
33
|
+
echo ""
|
|
34
|
+
echo "📖 Reading page content..."
|
|
35
|
+
confluence read "$PAGE_ID" | head -20
|
|
36
|
+
echo ""
|
|
37
|
+
echo "(Showing first 20 lines only)"
|
|
38
|
+
fi
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
echo ""
|
|
42
|
+
echo "✅ Examples completed!"
|
|
43
|
+
echo "💡 Run 'confluence --help' for more commands"
|
package/jest.config.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module.exports = {
|
|
2
|
+
testEnvironment: 'node',
|
|
3
|
+
collectCoverageFrom: [
|
|
4
|
+
'lib/**/*.js',
|
|
5
|
+
'bin/**/*.js',
|
|
6
|
+
'!node_modules/**'
|
|
7
|
+
],
|
|
8
|
+
coverageDirectory: 'coverage',
|
|
9
|
+
coverageReporters: ['text', 'lcov', 'html'],
|
|
10
|
+
testMatch: [
|
|
11
|
+
'**/tests/**/*.test.js'
|
|
12
|
+
]
|
|
13
|
+
};
|
package/lib/config.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const inquirer = require('inquirer');
|
|
5
|
+
const chalk = require('chalk');
|
|
6
|
+
|
|
7
|
+
const CONFIG_DIR = path.join(os.homedir(), '.confluence-cli');
|
|
8
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Initialize configuration
|
|
12
|
+
*/
|
|
13
|
+
async function initConfig() {
|
|
14
|
+
console.log(chalk.blue('🚀 Confluence CLI Configuration'));
|
|
15
|
+
console.log('Please provide your Confluence connection details:\n');
|
|
16
|
+
|
|
17
|
+
const answers = await inquirer.prompt([
|
|
18
|
+
{
|
|
19
|
+
type: 'input',
|
|
20
|
+
name: 'domain',
|
|
21
|
+
message: 'Confluence domain (e.g., yourcompany.atlassian.net):',
|
|
22
|
+
validate: (input) => {
|
|
23
|
+
if (!input.trim()) {
|
|
24
|
+
return 'Domain is required';
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
type: 'password',
|
|
31
|
+
name: 'token',
|
|
32
|
+
message: 'API Token:',
|
|
33
|
+
validate: (input) => {
|
|
34
|
+
if (!input.trim()) {
|
|
35
|
+
return 'API Token is required';
|
|
36
|
+
}
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
]);
|
|
41
|
+
|
|
42
|
+
// Create config directory if it doesn't exist
|
|
43
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
44
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Save configuration
|
|
48
|
+
const config = {
|
|
49
|
+
domain: answers.domain.trim(),
|
|
50
|
+
token: answers.token.trim()
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
54
|
+
|
|
55
|
+
console.log(chalk.green('✅ Configuration saved successfully!'));
|
|
56
|
+
console.log(`Config file location: ${chalk.gray(CONFIG_FILE)}`);
|
|
57
|
+
console.log(chalk.yellow('\n💡 Tip: You can regenerate this config anytime by running "confluence init"'));
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Get configuration
|
|
62
|
+
*/
|
|
63
|
+
function getConfig() {
|
|
64
|
+
// First check for environment variables
|
|
65
|
+
const envDomain = process.env.CONFLUENCE_DOMAIN || process.env.CONFLUENCE_HOST;
|
|
66
|
+
const envToken = process.env.CONFLUENCE_API_TOKEN;
|
|
67
|
+
|
|
68
|
+
if (envDomain && envToken) {
|
|
69
|
+
return {
|
|
70
|
+
domain: envDomain,
|
|
71
|
+
token: envToken
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Check for config file
|
|
76
|
+
if (!fs.existsSync(CONFIG_FILE)) {
|
|
77
|
+
console.error(chalk.red('❌ No configuration found!'));
|
|
78
|
+
console.log(chalk.yellow('Please run "confluence init" to set up your configuration.'));
|
|
79
|
+
console.log(chalk.gray('Or set environment variables: CONFLUENCE_DOMAIN and CONFLUENCE_API_TOKEN'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const config = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
|
|
85
|
+
return config;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error(chalk.red('❌ Error reading configuration file:'), error.message);
|
|
88
|
+
console.log(chalk.yellow('Please run "confluence init" to recreate your configuration.'));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = {
|
|
94
|
+
initConfig,
|
|
95
|
+
getConfig
|
|
96
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
const axios = require('axios');
|
|
2
|
+
const { convert } = require('html-to-text');
|
|
3
|
+
|
|
4
|
+
class ConfluenceClient {
|
|
5
|
+
constructor(config) {
|
|
6
|
+
this.baseURL = `https://${config.domain}/rest/api`;
|
|
7
|
+
this.token = config.token;
|
|
8
|
+
this.domain = config.domain;
|
|
9
|
+
|
|
10
|
+
this.client = axios.create({
|
|
11
|
+
baseURL: this.baseURL,
|
|
12
|
+
headers: {
|
|
13
|
+
'Authorization': `Bearer ${this.token}`,
|
|
14
|
+
'Content-Type': 'application/json'
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Extract page ID from URL or return the ID if it's already a number
|
|
21
|
+
*/
|
|
22
|
+
extractPageId(pageIdOrUrl) {
|
|
23
|
+
if (typeof pageIdOrUrl === 'number' || /^\d+$/.test(pageIdOrUrl)) {
|
|
24
|
+
return pageIdOrUrl;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Check if it's a Confluence URL
|
|
28
|
+
if (pageIdOrUrl.includes(this.domain)) {
|
|
29
|
+
// Extract pageId from URL parameter
|
|
30
|
+
const pageIdMatch = pageIdOrUrl.match(/pageId=(\d+)/);
|
|
31
|
+
if (pageIdMatch) {
|
|
32
|
+
return pageIdMatch[1];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Handle display URLs - would need to search by space and title
|
|
36
|
+
const displayMatch = pageIdOrUrl.match(/\/display\/([^/]+)\/(.+)/);
|
|
37
|
+
if (displayMatch) {
|
|
38
|
+
throw new Error('Display URLs not yet supported. Please use page ID or viewpage URL with pageId parameter.');
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return pageIdOrUrl;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Read a Confluence page content
|
|
47
|
+
*/
|
|
48
|
+
async readPage(pageIdOrUrl, format = 'text') {
|
|
49
|
+
const pageId = this.extractPageId(pageIdOrUrl);
|
|
50
|
+
|
|
51
|
+
const response = await this.client.get(`/content/${pageId}`, {
|
|
52
|
+
params: {
|
|
53
|
+
expand: 'body.storage'
|
|
54
|
+
}
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const htmlContent = response.data.body.storage.value;
|
|
58
|
+
|
|
59
|
+
if (format === 'html') {
|
|
60
|
+
return htmlContent;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Convert HTML to text
|
|
64
|
+
return convert(htmlContent, {
|
|
65
|
+
wordwrap: 80,
|
|
66
|
+
selectors: [
|
|
67
|
+
{ selector: 'h1', options: { uppercase: false } },
|
|
68
|
+
{ selector: 'h2', options: { uppercase: false } },
|
|
69
|
+
{ selector: 'h3', options: { uppercase: false } },
|
|
70
|
+
{ selector: 'table', options: { uppercaseHeaderCells: false } }
|
|
71
|
+
]
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Get page information
|
|
77
|
+
*/
|
|
78
|
+
async getPageInfo(pageIdOrUrl) {
|
|
79
|
+
const pageId = this.extractPageId(pageIdOrUrl);
|
|
80
|
+
|
|
81
|
+
const response = await this.client.get(`/content/${pageId}`, {
|
|
82
|
+
params: {
|
|
83
|
+
expand: 'space'
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
title: response.data.title,
|
|
89
|
+
id: response.data.id,
|
|
90
|
+
type: response.data.type,
|
|
91
|
+
status: response.data.status,
|
|
92
|
+
space: response.data.space
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Search for pages
|
|
98
|
+
*/
|
|
99
|
+
async search(query, limit = 10) {
|
|
100
|
+
const response = await this.client.get('/search', {
|
|
101
|
+
params: {
|
|
102
|
+
cql: `text ~ "${query}"`,
|
|
103
|
+
limit: limit
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
return response.data.results.map(result => {
|
|
108
|
+
// Handle different result structures
|
|
109
|
+
const content = result.content || result;
|
|
110
|
+
return {
|
|
111
|
+
id: content.id || 'Unknown',
|
|
112
|
+
title: content.title || 'Untitled',
|
|
113
|
+
type: content.type || 'Unknown',
|
|
114
|
+
excerpt: result.excerpt || content.excerpt || ''
|
|
115
|
+
};
|
|
116
|
+
}).filter(item => item.id !== 'Unknown'); // Filter out items without valid IDs
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get all spaces
|
|
121
|
+
*/
|
|
122
|
+
async getSpaces() {
|
|
123
|
+
const response = await this.client.get('/space', {
|
|
124
|
+
params: {
|
|
125
|
+
limit: 500
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return response.data.results.map(space => ({
|
|
130
|
+
key: space.key,
|
|
131
|
+
name: space.name,
|
|
132
|
+
type: space.type
|
|
133
|
+
}));
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
module.exports = ConfluenceClient;
|
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "confluence-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A command-line interface for Atlassian Confluence",
|
|
5
|
+
"main": "index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"confluence": "./bin/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"start": "node bin/confluence.js",
|
|
11
|
+
"test": "jest",
|
|
12
|
+
"lint": "eslint .",
|
|
13
|
+
"lint:fix": "eslint . --fix"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"confluence",
|
|
17
|
+
"atlassian",
|
|
18
|
+
"cli",
|
|
19
|
+
"wiki",
|
|
20
|
+
"documentation"
|
|
21
|
+
],
|
|
22
|
+
"author": "Your Name",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"commander": "^11.1.0",
|
|
26
|
+
"axios": "^1.6.2",
|
|
27
|
+
"chalk": "^4.1.2",
|
|
28
|
+
"inquirer": "^8.2.6",
|
|
29
|
+
"ora": "^5.4.1",
|
|
30
|
+
"html-to-text": "^9.0.5"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"eslint": "^8.55.0",
|
|
34
|
+
"jest": "^29.7.0",
|
|
35
|
+
"@types/node": "^20.10.0"
|
|
36
|
+
},
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=14.0.0"
|
|
39
|
+
},
|
|
40
|
+
"repository": {
|
|
41
|
+
"type": "git",
|
|
42
|
+
"url": "https://github.com/pchuri/confluence-cli.git"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/pchuri/confluence-cli/issues"
|
|
46
|
+
},
|
|
47
|
+
"homepage": "https://github.com/pchuri/confluence-cli#readme"
|
|
48
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const ConfluenceClient = require('../lib/confluence-client');
|
|
2
|
+
|
|
3
|
+
describe('ConfluenceClient', () => {
|
|
4
|
+
let client;
|
|
5
|
+
|
|
6
|
+
beforeEach(() => {
|
|
7
|
+
client = new ConfluenceClient({
|
|
8
|
+
domain: 'test.atlassian.net',
|
|
9
|
+
token: 'test-token'
|
|
10
|
+
});
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe('extractPageId', () => {
|
|
14
|
+
test('should return numeric page ID as is', () => {
|
|
15
|
+
expect(client.extractPageId('123456789')).toBe('123456789');
|
|
16
|
+
expect(client.extractPageId(123456789)).toBe(123456789);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test('should extract page ID from URL with pageId parameter', () => {
|
|
20
|
+
const url = 'https://test.atlassian.net/wiki/spaces/TEST/pages/123456789/Page+Title';
|
|
21
|
+
expect(client.extractPageId(url + '?pageId=987654321')).toBe('987654321');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('should throw error for display URLs', () => {
|
|
25
|
+
const displayUrl = 'https://test.atlassian.net/display/TEST/Page+Title';
|
|
26
|
+
expect(() => client.extractPageId(displayUrl)).toThrow('Display URLs not yet supported');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|