confluence-cli 1.12.0 → 1.13.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/CHANGELOG.md +14 -0
- package/README.md +32 -2
- package/bin/confluence.js +170 -7
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.13.0](https://github.com/pchuri/confluence-cli/compare/v1.12.1...v1.13.0) (2026-01-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* add children command to list child pages ([#27](https://github.com/pchuri/confluence-cli/issues/27)) ([7e8b4ed](https://github.com/pchuri/confluence-cli/commit/7e8b4ed1b0ed69a7e1de52dfaf0c1ff36973f78b))
|
|
7
|
+
|
|
8
|
+
## [1.12.1](https://github.com/pchuri/confluence-cli/compare/v1.12.0...v1.12.1) (2025-12-31)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* align README with CLI behavior ([#26](https://github.com/pchuri/confluence-cli/issues/26)) ([b24c7cf](https://github.com/pchuri/confluence-cli/commit/b24c7cf4a645383812a1cb7239b1db41ded77f8d))
|
|
14
|
+
|
|
1
15
|
# [1.12.0](https://github.com/pchuri/confluence-cli/compare/v1.11.1...v1.12.0) (2025-12-31)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -44,12 +44,17 @@ npx confluence-cli
|
|
|
44
44
|
confluence search "my search term"
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
4. **
|
|
47
|
+
4. **List child pages:**
|
|
48
|
+
```bash
|
|
49
|
+
confluence children 123456789
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
5. **Create a new page:**
|
|
48
53
|
```bash
|
|
49
54
|
confluence create "My New Page" SPACEKEY --content "Hello World!"
|
|
50
55
|
```
|
|
51
56
|
|
|
52
|
-
|
|
57
|
+
6. **Update a page:**
|
|
53
58
|
```bash
|
|
54
59
|
confluence update 123456789 --content "Updated content"
|
|
55
60
|
```
|
|
@@ -139,6 +144,27 @@ confluence export 123456789 --skip-attachments
|
|
|
139
144
|
confluence spaces
|
|
140
145
|
```
|
|
141
146
|
|
|
147
|
+
### List Child Pages
|
|
148
|
+
```bash
|
|
149
|
+
# List direct child pages
|
|
150
|
+
confluence children 123456789
|
|
151
|
+
|
|
152
|
+
# List all descendants recursively
|
|
153
|
+
confluence children 123456789 --recursive
|
|
154
|
+
|
|
155
|
+
# Display as tree structure
|
|
156
|
+
confluence children 123456789 --recursive --format tree
|
|
157
|
+
|
|
158
|
+
# Show page IDs and URLs
|
|
159
|
+
confluence children 123456789 --show-id --show-url
|
|
160
|
+
|
|
161
|
+
# Limit recursion depth
|
|
162
|
+
confluence children 123456789 --recursive --max-depth 3
|
|
163
|
+
|
|
164
|
+
# Output as JSON for scripting
|
|
165
|
+
confluence children 123456789 --recursive --format json > children.json
|
|
166
|
+
```
|
|
167
|
+
|
|
142
168
|
### Find a Page by Title
|
|
143
169
|
```bash
|
|
144
170
|
# Find page by title
|
|
@@ -250,11 +276,15 @@ confluence stats
|
|
|
250
276
|
| `search <query>` | Search for pages | `--limit <number>` |
|
|
251
277
|
| `spaces` | List all available spaces | |
|
|
252
278
|
| `find <title>` | Find a page by its title | `--space <spaceKey>` |
|
|
279
|
+
| `children <pageId>` | List child pages of a page | `--recursive`, `--max-depth <number>`, `--format <list\|tree\|json>`, `--show-url`, `--show-id` |
|
|
253
280
|
| `create <title> <spaceKey>` | Create a new page | `--content <string>`, `--file <path>`, `--format <storage\|html\|markdown>`|
|
|
254
281
|
| `create-child <title> <parentId>` | Create a child page | `--content <string>`, `--file <path>`, `--format <storage\|html\|markdown>` |
|
|
255
282
|
| `copy-tree <sourcePageId> <targetParentId> [newTitle]` | Copy page tree with all children | `--max-depth <number>`, `--exclude <patterns>`, `--delay-ms <ms>`, `--copy-suffix <text>`, `--dry-run`, `--fail-on-error`, `--quiet` |
|
|
256
283
|
| `update <pageId>` | Update a page's title or content | `--title <string>`, `--content <string>`, `--file <path>`, `--format <storage\|html\|markdown>` |
|
|
284
|
+
| `delete <pageId_or_url>` | Delete a page by ID or URL | `--yes` |
|
|
257
285
|
| `edit <pageId>` | Export page content for editing | `--output <file>` |
|
|
286
|
+
| `attachments <pageId_or_url>` | List or download attachments for a page | `--limit <number>`, `--pattern <glob>`, `--download`, `--dest <directory>` |
|
|
287
|
+
| `export <pageId_or_url>` | Export a page to a directory with its attachments | `--format <html\|text\|markdown>`, `--dest <directory>`, `--file <filename>`, `--attachments-dir <name>`, `--pattern <glob>`, `--referenced-only`, `--skip-attachments` |
|
|
258
288
|
| `stats` | View your usage statistics | |
|
|
259
289
|
|
|
260
290
|
## Examples
|
package/bin/confluence.js
CHANGED
|
@@ -475,6 +475,7 @@ program
|
|
|
475
475
|
.option('--file <filename>', 'Content filename (default: page.<ext>)')
|
|
476
476
|
.option('--attachments-dir <name>', 'Subdirectory for attachments', 'attachments')
|
|
477
477
|
.option('--pattern <glob>', 'Filter attachments by filename (e.g., "*.png")')
|
|
478
|
+
.option('--referenced-only', 'Download only attachments referenced in the page content')
|
|
478
479
|
.option('--skip-attachments', 'Do not download attachments')
|
|
479
480
|
.action(async (pageId, options) => {
|
|
480
481
|
const analytics = new Analytics();
|
|
@@ -489,9 +490,14 @@ program
|
|
|
489
490
|
const contentExt = formatExt[format] || 'txt';
|
|
490
491
|
|
|
491
492
|
const pageInfo = await client.getPageInfo(pageId);
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
493
|
+
const content = await client.readPage(
|
|
494
|
+
pageId,
|
|
495
|
+
format,
|
|
496
|
+
options.referencedOnly ? { extractReferencedAttachments: true } : {}
|
|
497
|
+
);
|
|
498
|
+
const referencedAttachments = options.referencedOnly
|
|
499
|
+
? (client._referencedAttachments || new Set())
|
|
500
|
+
: null;
|
|
495
501
|
|
|
496
502
|
const baseDir = path.resolve(options.dest || '.');
|
|
497
503
|
const folderName = sanitizeTitle(pageInfo.title || 'page');
|
|
@@ -510,13 +516,13 @@ program
|
|
|
510
516
|
const pattern = options.pattern ? options.pattern.trim() : null;
|
|
511
517
|
const allAttachments = await client.getAllAttachments(pageId);
|
|
512
518
|
|
|
513
|
-
// Filter: only referenced attachments (unless pattern is specified, then use pattern)
|
|
514
519
|
let filtered;
|
|
515
520
|
if (pattern) {
|
|
516
521
|
filtered = allAttachments.filter(att => client.matchesPattern(att.title, pattern));
|
|
522
|
+
} else if (options.referencedOnly) {
|
|
523
|
+
filtered = allAttachments.filter(att => referencedAttachments?.has(att.title));
|
|
517
524
|
} else {
|
|
518
|
-
|
|
519
|
-
filtered = allAttachments.filter(att => referencedAttachments.has(att.title));
|
|
525
|
+
filtered = allAttachments;
|
|
520
526
|
}
|
|
521
527
|
|
|
522
528
|
if (filtered.length === 0) {
|
|
@@ -700,4 +706,161 @@ program
|
|
|
700
706
|
}
|
|
701
707
|
});
|
|
702
708
|
|
|
703
|
-
|
|
709
|
+
// List children command
|
|
710
|
+
program
|
|
711
|
+
.command('children <pageId>')
|
|
712
|
+
.description('List child pages of a Confluence page')
|
|
713
|
+
.option('-r, --recursive', 'List all descendants recursively', false)
|
|
714
|
+
.option('--max-depth <number>', 'Maximum depth for recursive listing', '10')
|
|
715
|
+
.option('--format <format>', 'Output format (list, tree, json)', 'list')
|
|
716
|
+
.option('--show-url', 'Show page URLs', false)
|
|
717
|
+
.option('--show-id', 'Show page IDs', false)
|
|
718
|
+
.action(async (pageId, options) => {
|
|
719
|
+
const analytics = new Analytics();
|
|
720
|
+
try {
|
|
721
|
+
const config = getConfig();
|
|
722
|
+
const client = new ConfluenceClient(config);
|
|
723
|
+
|
|
724
|
+
// Extract page ID from URL if needed
|
|
725
|
+
const resolvedPageId = await client.extractPageId(pageId);
|
|
726
|
+
|
|
727
|
+
// Get children
|
|
728
|
+
let children;
|
|
729
|
+
if (options.recursive) {
|
|
730
|
+
const maxDepth = parseInt(options.maxDepth) || 10;
|
|
731
|
+
children = await client.getAllDescendantPages(resolvedPageId, maxDepth);
|
|
732
|
+
} else {
|
|
733
|
+
children = await client.getChildPages(resolvedPageId);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
if (children.length === 0) {
|
|
737
|
+
console.log(chalk.yellow('No child pages found.'));
|
|
738
|
+
analytics.track('children', true);
|
|
739
|
+
return;
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
// Format output
|
|
743
|
+
const format = options.format.toLowerCase();
|
|
744
|
+
|
|
745
|
+
if (format === 'json') {
|
|
746
|
+
// JSON output
|
|
747
|
+
const output = {
|
|
748
|
+
pageId: resolvedPageId,
|
|
749
|
+
childCount: children.length,
|
|
750
|
+
children: children.map(page => ({
|
|
751
|
+
id: page.id,
|
|
752
|
+
title: page.title,
|
|
753
|
+
type: page.type,
|
|
754
|
+
status: page.status,
|
|
755
|
+
spaceKey: page.space?.key,
|
|
756
|
+
url: `https://${config.domain}/wiki/spaces/${page.space?.key}/pages/${page.id}`,
|
|
757
|
+
parentId: page.parentId || resolvedPageId
|
|
758
|
+
}))
|
|
759
|
+
};
|
|
760
|
+
console.log(JSON.stringify(output, null, 2));
|
|
761
|
+
} else if (format === 'tree' && options.recursive) {
|
|
762
|
+
// Tree format (only for recursive mode)
|
|
763
|
+
const pageInfo = await client.getPageInfo(resolvedPageId);
|
|
764
|
+
console.log(chalk.blue(`📁 ${pageInfo.title}`));
|
|
765
|
+
|
|
766
|
+
// Build tree structure
|
|
767
|
+
const tree = buildTree(children, resolvedPageId);
|
|
768
|
+
printTree(tree, config, options, 1);
|
|
769
|
+
|
|
770
|
+
console.log('');
|
|
771
|
+
console.log(chalk.gray(`Total: ${children.length} child page${children.length === 1 ? '' : 's'}`));
|
|
772
|
+
} else {
|
|
773
|
+
// List format (default)
|
|
774
|
+
console.log(chalk.blue('Child pages:'));
|
|
775
|
+
console.log('');
|
|
776
|
+
|
|
777
|
+
children.forEach((page, index) => {
|
|
778
|
+
let output = `${index + 1}. ${chalk.green(page.title)}`;
|
|
779
|
+
|
|
780
|
+
if (options.showId) {
|
|
781
|
+
output += ` ${chalk.gray(`(ID: ${page.id})`)}`;
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (options.showUrl) {
|
|
785
|
+
const url = `https://${config.domain}/wiki/spaces/${page.space?.key}/pages/${page.id}`;
|
|
786
|
+
output += `\n ${chalk.gray(url)}`;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
if (options.recursive && page.parentId && page.parentId !== resolvedPageId) {
|
|
790
|
+
output += ` ${chalk.dim('(nested)')}`;
|
|
791
|
+
}
|
|
792
|
+
|
|
793
|
+
console.log(output);
|
|
794
|
+
});
|
|
795
|
+
|
|
796
|
+
console.log('');
|
|
797
|
+
console.log(chalk.gray(`Total: ${children.length} child page${children.length === 1 ? '' : 's'}`));
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
analytics.track('children', true);
|
|
801
|
+
} catch (error) {
|
|
802
|
+
analytics.track('children', false);
|
|
803
|
+
console.error(chalk.red('Error:'), error.message);
|
|
804
|
+
process.exit(1);
|
|
805
|
+
}
|
|
806
|
+
});
|
|
807
|
+
|
|
808
|
+
// Helper function to build tree structure
|
|
809
|
+
function buildTree(pages, rootId) {
|
|
810
|
+
const tree = [];
|
|
811
|
+
const pageMap = new Map();
|
|
812
|
+
|
|
813
|
+
// Create a map of all pages
|
|
814
|
+
pages.forEach(page => {
|
|
815
|
+
pageMap.set(page.id, { ...page, children: [] });
|
|
816
|
+
});
|
|
817
|
+
|
|
818
|
+
// Build tree structure
|
|
819
|
+
pages.forEach(page => {
|
|
820
|
+
const node = pageMap.get(page.id);
|
|
821
|
+
const parentId = page.parentId || rootId;
|
|
822
|
+
|
|
823
|
+
if (parentId === rootId) {
|
|
824
|
+
tree.push(node);
|
|
825
|
+
} else {
|
|
826
|
+
const parent = pageMap.get(parentId);
|
|
827
|
+
if (parent) {
|
|
828
|
+
parent.children.push(node);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
return tree;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
// Helper function to print tree
|
|
837
|
+
function printTree(nodes, config, options, depth = 1) {
|
|
838
|
+
nodes.forEach((node, index) => {
|
|
839
|
+
const isLast = index === nodes.length - 1;
|
|
840
|
+
const indent = ' '.repeat(depth - 1);
|
|
841
|
+
const prefix = isLast ? '└── ' : '├── ';
|
|
842
|
+
|
|
843
|
+
let output = `${indent}${prefix}📄 ${chalk.green(node.title)}`;
|
|
844
|
+
|
|
845
|
+
if (options.showId) {
|
|
846
|
+
output += ` ${chalk.gray(`(ID: ${node.id})`)}`;
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
if (options.showUrl) {
|
|
850
|
+
const url = `https://${config.domain}/wiki/spaces/${node.space?.key}/pages/${node.id}`;
|
|
851
|
+
output += `\n${indent}${isLast ? ' ' : '│ '}${chalk.gray(url)}`;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
console.log(output);
|
|
855
|
+
|
|
856
|
+
if (node.children && node.children.length > 0) {
|
|
857
|
+
printTree(node.children, config, options, depth + 1);
|
|
858
|
+
}
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
if (process.argv.length <= 2) {
|
|
863
|
+
program.help({ error: false });
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "confluence-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"description": "A command-line interface for Atlassian Confluence with page creation and editing capabilities",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"confluence": "bin/index.js"
|
|
7
|
+
"confluence": "bin/index.js",
|
|
8
|
+
"confluence-cli": "bin/index.js"
|
|
8
9
|
},
|
|
9
10
|
"scripts": {
|
|
10
11
|
"start": "node bin/confluence.js",
|