confluence-cli 1.11.1 → 1.12.1
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 +16 -0
- package/bin/confluence.js +61 -7
- package/lib/confluence-client.js +10 -0
- package/package.json +3 -2
- package/tests/confluence-client.test.js +23 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
## [1.12.1](https://github.com/pchuri/confluence-cli/compare/v1.12.0...v1.12.1) (2025-12-31)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* align README with CLI behavior ([#26](https://github.com/pchuri/confluence-cli/issues/26)) ([b24c7cf](https://github.com/pchuri/confluence-cli/commit/b24c7cf4a645383812a1cb7239b1db41ded77f8d))
|
|
7
|
+
|
|
8
|
+
# [1.12.0](https://github.com/pchuri/confluence-cli/compare/v1.11.1...v1.12.0) (2025-12-31)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Features
|
|
12
|
+
|
|
13
|
+
* add page delete command ([#25](https://github.com/pchuri/confluence-cli/issues/25)) ([bc3e412](https://github.com/pchuri/confluence-cli/commit/bc3e412a6ccd0774d62ab0816a6c2735cbd470a4))
|
|
14
|
+
|
|
1
15
|
## [1.11.1](https://github.com/pchuri/confluence-cli/compare/v1.11.0...v1.11.1) (2025-12-17)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -10,6 +10,7 @@ A powerful command-line interface for Atlassian Confluence that allows you to re
|
|
|
10
10
|
- 🏠 **List spaces** - View all available Confluence spaces
|
|
11
11
|
- ✏️ **Create pages** - Create new pages with support for Markdown, HTML, or Storage format
|
|
12
12
|
- 📝 **Update pages** - Update existing page content and titles
|
|
13
|
+
- 🗑️ **Delete pages** - Delete (or move to trash) pages by ID or URL
|
|
13
14
|
- 📎 **Attachments** - List or download page attachments
|
|
14
15
|
- 📦 **Export** - Save a page and its attachments to a local folder
|
|
15
16
|
- 🛠️ **Edit workflow** - Export page content for editing and re-import
|
|
@@ -209,6 +210,18 @@ confluence update 123456789 --file ./updated-content.md --format markdown
|
|
|
209
210
|
confluence update 123456789 --title "New Title" --content "And new content"
|
|
210
211
|
```
|
|
211
212
|
|
|
213
|
+
### Delete a Page
|
|
214
|
+
```bash
|
|
215
|
+
# Delete by page ID (prompts for confirmation)
|
|
216
|
+
confluence delete 123456789
|
|
217
|
+
|
|
218
|
+
# Delete by URL
|
|
219
|
+
confluence delete "https://your-domain.atlassian.net/wiki/viewpage.action?pageId=123456789"
|
|
220
|
+
|
|
221
|
+
# Skip confirmation (useful for scripts)
|
|
222
|
+
confluence delete 123456789 --yes
|
|
223
|
+
```
|
|
224
|
+
|
|
212
225
|
### Edit Workflow
|
|
213
226
|
The `edit` and `update` commands work together to create a seamless editing workflow.
|
|
214
227
|
```bash
|
|
@@ -241,7 +254,10 @@ confluence stats
|
|
|
241
254
|
| `create-child <title> <parentId>` | Create a child page | `--content <string>`, `--file <path>`, `--format <storage\|html\|markdown>` |
|
|
242
255
|
| `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` |
|
|
243
256
|
| `update <pageId>` | Update a page's title or content | `--title <string>`, `--content <string>`, `--file <path>`, `--format <storage\|html\|markdown>` |
|
|
257
|
+
| `delete <pageId_or_url>` | Delete a page by ID or URL | `--yes` |
|
|
244
258
|
| `edit <pageId>` | Export page content for editing | `--output <file>` |
|
|
259
|
+
| `attachments <pageId_or_url>` | List or download attachments for a page | `--limit <number>`, `--pattern <glob>`, `--download`, `--dest <directory>` |
|
|
260
|
+
| `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` |
|
|
245
261
|
| `stats` | View your usage statistics | |
|
|
246
262
|
|
|
247
263
|
## Examples
|
package/bin/confluence.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
const { program } = require('commander');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
+
const inquirer = require('inquirer');
|
|
5
6
|
const ConfluenceClient = require('../lib/confluence-client');
|
|
6
7
|
const { getConfig, initConfig } = require('../lib/config');
|
|
7
8
|
const Analytics = require('../lib/analytics');
|
|
@@ -274,6 +275,49 @@ program
|
|
|
274
275
|
}
|
|
275
276
|
});
|
|
276
277
|
|
|
278
|
+
// Delete command
|
|
279
|
+
program
|
|
280
|
+
.command('delete <pageIdOrUrl>')
|
|
281
|
+
.description('Delete a Confluence page by ID or URL')
|
|
282
|
+
.option('-y, --yes', 'Skip confirmation prompt')
|
|
283
|
+
.action(async (pageIdOrUrl, options) => {
|
|
284
|
+
const analytics = new Analytics();
|
|
285
|
+
try {
|
|
286
|
+
const config = getConfig();
|
|
287
|
+
const client = new ConfluenceClient(config);
|
|
288
|
+
const pageInfo = await client.getPageInfo(pageIdOrUrl);
|
|
289
|
+
|
|
290
|
+
if (!options.yes) {
|
|
291
|
+
const spaceLabel = pageInfo.space?.key ? ` (${pageInfo.space.key})` : '';
|
|
292
|
+
const { confirmed } = await inquirer.prompt([
|
|
293
|
+
{
|
|
294
|
+
type: 'confirm',
|
|
295
|
+
name: 'confirmed',
|
|
296
|
+
default: false,
|
|
297
|
+
message: `Delete "${pageInfo.title}" (ID: ${pageInfo.id})${spaceLabel}?`
|
|
298
|
+
}
|
|
299
|
+
]);
|
|
300
|
+
|
|
301
|
+
if (!confirmed) {
|
|
302
|
+
console.log(chalk.yellow('Cancelled.'));
|
|
303
|
+
analytics.track('delete_cancel', true);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const result = await client.deletePage(pageInfo.id);
|
|
309
|
+
|
|
310
|
+
console.log(chalk.green('✅ Page deleted successfully!'));
|
|
311
|
+
console.log(`Title: ${chalk.blue(pageInfo.title)}`);
|
|
312
|
+
console.log(`ID: ${chalk.blue(result.id)}`);
|
|
313
|
+
analytics.track('delete', true);
|
|
314
|
+
} catch (error) {
|
|
315
|
+
analytics.track('delete', false);
|
|
316
|
+
console.error(chalk.red('Error:'), error.message);
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
|
|
277
321
|
// Edit command - opens page content for editing
|
|
278
322
|
program
|
|
279
323
|
.command('edit <pageId>')
|
|
@@ -431,6 +475,7 @@ program
|
|
|
431
475
|
.option('--file <filename>', 'Content filename (default: page.<ext>)')
|
|
432
476
|
.option('--attachments-dir <name>', 'Subdirectory for attachments', 'attachments')
|
|
433
477
|
.option('--pattern <glob>', 'Filter attachments by filename (e.g., "*.png")')
|
|
478
|
+
.option('--referenced-only', 'Download only attachments referenced in the page content')
|
|
434
479
|
.option('--skip-attachments', 'Do not download attachments')
|
|
435
480
|
.action(async (pageId, options) => {
|
|
436
481
|
const analytics = new Analytics();
|
|
@@ -445,9 +490,14 @@ program
|
|
|
445
490
|
const contentExt = formatExt[format] || 'txt';
|
|
446
491
|
|
|
447
492
|
const pageInfo = await client.getPageInfo(pageId);
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
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;
|
|
451
501
|
|
|
452
502
|
const baseDir = path.resolve(options.dest || '.');
|
|
453
503
|
const folderName = sanitizeTitle(pageInfo.title || 'page');
|
|
@@ -466,13 +516,13 @@ program
|
|
|
466
516
|
const pattern = options.pattern ? options.pattern.trim() : null;
|
|
467
517
|
const allAttachments = await client.getAllAttachments(pageId);
|
|
468
518
|
|
|
469
|
-
// Filter: only referenced attachments (unless pattern is specified, then use pattern)
|
|
470
519
|
let filtered;
|
|
471
520
|
if (pattern) {
|
|
472
521
|
filtered = allAttachments.filter(att => client.matchesPattern(att.title, pattern));
|
|
522
|
+
} else if (options.referencedOnly) {
|
|
523
|
+
filtered = allAttachments.filter(att => referencedAttachments?.has(att.title));
|
|
473
524
|
} else {
|
|
474
|
-
|
|
475
|
-
filtered = allAttachments.filter(att => referencedAttachments.has(att.title));
|
|
525
|
+
filtered = allAttachments;
|
|
476
526
|
}
|
|
477
527
|
|
|
478
528
|
if (filtered.length === 0) {
|
|
@@ -656,4 +706,8 @@ program
|
|
|
656
706
|
}
|
|
657
707
|
});
|
|
658
708
|
|
|
659
|
-
|
|
709
|
+
if (process.argv.length <= 2) {
|
|
710
|
+
program.help({ error: false });
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
program.parse(process.argv);
|
package/lib/confluence-client.js
CHANGED
|
@@ -1208,6 +1208,16 @@ class ConfluenceClient {
|
|
|
1208
1208
|
};
|
|
1209
1209
|
}
|
|
1210
1210
|
|
|
1211
|
+
/**
|
|
1212
|
+
* Delete a Confluence page
|
|
1213
|
+
* Note: Confluence may move the page to trash depending on instance settings.
|
|
1214
|
+
*/
|
|
1215
|
+
async deletePage(pageIdOrUrl) {
|
|
1216
|
+
const pageId = await this.extractPageId(pageIdOrUrl);
|
|
1217
|
+
await this.client.delete(`/content/${pageId}`);
|
|
1218
|
+
return { id: String(pageId) };
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1211
1221
|
/**
|
|
1212
1222
|
* Search for a page by title and space
|
|
1213
1223
|
*/
|
package/package.json
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "confluence-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.12.1",
|
|
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",
|
|
@@ -267,6 +267,29 @@ describe('ConfluenceClient', () => {
|
|
|
267
267
|
expect(typeof client.getPageForEdit).toBe('function');
|
|
268
268
|
expect(typeof client.createChildPage).toBe('function');
|
|
269
269
|
expect(typeof client.findPageByTitle).toBe('function');
|
|
270
|
+
expect(typeof client.deletePage).toBe('function');
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
describe('deletePage', () => {
|
|
275
|
+
test('should delete a page by ID', async () => {
|
|
276
|
+
const mock = new MockAdapter(client.client);
|
|
277
|
+
mock.onDelete('/content/123456789').reply(204);
|
|
278
|
+
|
|
279
|
+
await expect(client.deletePage('123456789')).resolves.toEqual({ id: '123456789' });
|
|
280
|
+
|
|
281
|
+
mock.restore();
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('should delete a page by URL', async () => {
|
|
285
|
+
const mock = new MockAdapter(client.client);
|
|
286
|
+
mock.onDelete('/content/987654321').reply(204);
|
|
287
|
+
|
|
288
|
+
await expect(
|
|
289
|
+
client.deletePage('https://test.atlassian.net/wiki/viewpage.action?pageId=987654321')
|
|
290
|
+
).resolves.toEqual({ id: '987654321' });
|
|
291
|
+
|
|
292
|
+
mock.restore();
|
|
270
293
|
});
|
|
271
294
|
});
|
|
272
295
|
|