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 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
- // Read page with attachment extraction enabled
449
- const content = await client.readPage(pageId, format, { extractReferencedAttachments: true });
450
- const referencedAttachments = client._referencedAttachments || new Set();
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
- // Only download attachments that are referenced in the page content
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
- program.parse();
709
+ if (process.argv.length <= 2) {
710
+ program.help({ error: false });
711
+ }
712
+
713
+ program.parse(process.argv);
@@ -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.11.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