confluence-cli 1.27.7 → 1.28.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.
@@ -1,11 +1,11 @@
1
1
  ---
2
2
  name: confluence
3
- description: Use confluence-cli to read, search, create, update, move, and delete Confluence pages and attachments from the terminal.
3
+ description: Use confluence-cli to read, search, create, update, move, delete, and convert Confluence pages and attachments from the terminal.
4
4
  ---
5
5
 
6
6
  # confluence-cli Skill
7
7
 
8
- A CLI tool for Atlassian Confluence. Lets you read, search, create, update, move, and delete pages and attachments from the terminal or from an agent.
8
+ A CLI tool for Atlassian Confluence. Lets you read, search, create, update, move, delete, and convert pages and attachments from the terminal or from an agent.
9
9
 
10
10
  ## Installation
11
11
 
@@ -627,6 +627,36 @@ confluence stats
627
627
 
628
628
  ---
629
629
 
630
+ ### `convert`
631
+
632
+ Convert between content formats locally without a Confluence server connection.
633
+
634
+ ```sh
635
+ confluence convert [--input-file <path>] [--output-file <path>] --input-format <format> --output-format <format>
636
+ ```
637
+
638
+ | Option | Default | Description |
639
+ |---|---|---|
640
+ | `--input-file`, `-i` | stdin | Input file path |
641
+ | `--output-file`, `-o` | stdout | Output file path |
642
+ | `--input-format` | — | Input format: `markdown`, `storage`, `html` (required) |
643
+ | `--output-format` | — | Output format: `markdown`, `storage`, `html`, `text` (required) |
644
+
645
+ Supported conversions: markdown→storage, markdown→html, markdown→text, html→storage, html→text, html→markdown, storage→markdown, storage→html, storage→text.
646
+
647
+ ```sh
648
+ # Markdown to Confluence storage format
649
+ confluence convert -i doc.md -o doc.xml --input-format markdown --output-format storage
650
+
651
+ # Pipe via stdin/stdout
652
+ echo "# Hello" | confluence convert --input-format markdown --output-format storage
653
+
654
+ # Storage format back to markdown
655
+ confluence convert -i page.xml --input-format storage --output-format markdown
656
+ ```
657
+
658
+ ---
659
+
630
660
  ### `install-skill`
631
661
 
632
662
  Copy the Claude Code skill documentation into your project's `.claude/skills/` directory so Claude Code can learn confluence-cli automatically.
@@ -682,6 +712,16 @@ confluence copy-tree 123456789 987654321 --dry-run
682
712
  confluence copy-tree 123456789 987654321 "Backup Copy"
683
713
  ```
684
714
 
715
+ ### Offline format conversion
716
+
717
+ ```sh
718
+ # Convert markdown to Confluence storage format (no server needed)
719
+ confluence convert -i doc.md -o doc.xml --input-format markdown --output-format storage
720
+
721
+ # Convert storage format to markdown for editing
722
+ confluence convert -i page.xml -o page.md --input-format storage --output-format markdown
723
+ ```
724
+
685
725
  ### Export a page for local editing
686
726
 
687
727
  ```sh
package/README.md CHANGED
@@ -18,6 +18,7 @@ A powerful command-line interface for Atlassian Confluence that allows you to re
18
18
  - 🛠️ **Edit workflow** - Export page content for editing and re-import
19
19
  - 🔀 **Profiles** - Manage multiple Confluence instances with named configuration profiles
20
20
  - 🔒 **Read-only mode** - Profile-level write protection for safe AI agent usage
21
+ - 🔄 **Format conversion** - Convert between Markdown, HTML, Storage, and text formats locally (no server required)
21
22
  - 🔧 **Easy setup** - Simple configuration with environment variables or interactive setup
22
23
 
23
24
  ## Installation
@@ -555,6 +556,7 @@ confluence stats
555
556
  | `profile use <name>` | Set the active configuration profile | |
556
557
  | `profile add <name>` | Add a new configuration profile | `-d, --domain`, `-p, --api-path`, `-a, --auth-type`, `-e, --email`, `-t, --token`, `--protocol`, `--read-only` |
557
558
  | `profile remove <name>` | Remove a configuration profile | |
559
+ | `convert` | Convert between content formats locally (no server required) | `--input-file <path>`, `--output-file <path>`, `--input-format <markdown\|storage\|html>`, `--output-format <markdown\|storage\|html\|text>` |
558
560
  | `stats` | View your usage statistics | |
559
561
 
560
562
  **Global option:** `--profile <name>` — Use a specific profile for any command (overrides `CONFLUENCE_PROFILE` env var and active profile).
@@ -597,6 +599,15 @@ confluence stats
597
599
  confluence profile list
598
600
  confluence profile use staging
599
601
  confluence --profile staging spaces
602
+
603
+ # Convert markdown to Confluence storage format (no server required)
604
+ confluence convert --input-file doc.md --input-format markdown --output-format storage
605
+
606
+ # Pipe conversion via stdin/stdout
607
+ echo "# Hello" | confluence convert --input-format markdown --output-format storage
608
+
609
+ # Convert storage format back to markdown
610
+ confluence convert -i page.xml -o page.md --input-format storage --output-format markdown
600
611
  ```
601
612
 
602
613
  ## Development
package/bin/confluence.js CHANGED
@@ -1952,6 +1952,93 @@ profileCmd
1952
1952
  }
1953
1953
  });
1954
1954
 
1955
+ // Convert command (local format conversion, no server connection required)
1956
+ const VALID_INPUT_FORMATS = ['markdown', 'storage', 'html'];
1957
+ const VALID_OUTPUT_FORMATS = ['markdown', 'storage', 'html', 'text'];
1958
+
1959
+ program
1960
+ .command('convert')
1961
+ .description('Convert between content formats locally (no server connection required)')
1962
+ .option('-i, --input-file <file>', 'Input file path (reads from stdin if omitted)')
1963
+ .option('-o, --output-file <file>', 'Output file path (writes to stdout if omitted)')
1964
+ .option('--input-format <format>', `Input format (${VALID_INPUT_FORMATS.join(', ')})`)
1965
+ .option('--output-format <format>', `Output format (${VALID_OUTPUT_FORMATS.join(', ')})`)
1966
+ .action(async (options) => {
1967
+ const analytics = new Analytics();
1968
+ try {
1969
+ if (!options.inputFormat) {
1970
+ console.error(chalk.red('Error: --input-format is required.'));
1971
+ process.exit(1);
1972
+ }
1973
+ if (!options.outputFormat) {
1974
+ console.error(chalk.red('Error: --output-format is required.'));
1975
+ process.exit(1);
1976
+ }
1977
+ if (!VALID_INPUT_FORMATS.includes(options.inputFormat)) {
1978
+ console.error(chalk.red(`Error: Invalid input format "${options.inputFormat}". Valid: ${VALID_INPUT_FORMATS.join(', ')}`));
1979
+ process.exit(1);
1980
+ }
1981
+ if (!VALID_OUTPUT_FORMATS.includes(options.outputFormat)) {
1982
+ console.error(chalk.red(`Error: Invalid output format "${options.outputFormat}". Valid: ${VALID_OUTPUT_FORMATS.join(', ')}`));
1983
+ process.exit(1);
1984
+ }
1985
+ if (options.inputFormat === options.outputFormat) {
1986
+ console.error(chalk.red('Error: Input and output formats must be different.'));
1987
+ process.exit(1);
1988
+ }
1989
+
1990
+ const fs = require('fs');
1991
+ let input;
1992
+ if (options.inputFile) {
1993
+ input = fs.readFileSync(options.inputFile, 'utf-8');
1994
+ } else {
1995
+ input = fs.readFileSync(process.stdin.fd, 'utf-8');
1996
+ }
1997
+
1998
+ const converter = ConfluenceClient.createLocalConverter();
1999
+ let output;
2000
+
2001
+ if (options.inputFormat === 'markdown' && options.outputFormat === 'storage') {
2002
+ output = converter.markdownToStorage(input);
2003
+ } else if (options.inputFormat === 'markdown' && options.outputFormat === 'html') {
2004
+ output = converter.markdown.render(input);
2005
+ } else if (options.inputFormat === 'html' && options.outputFormat === 'storage') {
2006
+ output = converter.htmlToConfluenceStorage(input);
2007
+ } else if (options.inputFormat === 'storage' && options.outputFormat === 'markdown') {
2008
+ output = converter.storageToMarkdown(input);
2009
+ } else if (options.inputFormat === 'storage' && options.outputFormat === 'text') {
2010
+ const { convert: htmlToText } = require('html-to-text');
2011
+ output = htmlToText(input, { wordwrap: 130 });
2012
+ } else if (options.inputFormat === 'storage' && options.outputFormat === 'html') {
2013
+ output = input; // storage format is already HTML-based
2014
+ } else if (options.inputFormat === 'html' && options.outputFormat === 'text') {
2015
+ const { convert: htmlToText } = require('html-to-text');
2016
+ output = htmlToText(input, { wordwrap: 130 });
2017
+ } else if (options.inputFormat === 'html' && options.outputFormat === 'markdown') {
2018
+ output = converter.storageToMarkdown(input);
2019
+ } else if (options.inputFormat === 'markdown' && options.outputFormat === 'text') {
2020
+ const html = converter.markdown.render(input);
2021
+ const { convert: htmlToText } = require('html-to-text');
2022
+ output = htmlToText(html, { wordwrap: 130 });
2023
+ } else {
2024
+ console.error(chalk.red(`Error: Conversion from "${options.inputFormat}" to "${options.outputFormat}" is not supported.`));
2025
+ process.exit(1);
2026
+ }
2027
+
2028
+ if (options.outputFile) {
2029
+ fs.writeFileSync(options.outputFile, output, 'utf-8');
2030
+ console.error(chalk.green(`Converted ${options.inputFormat} → ${options.outputFormat}: ${options.outputFile}`));
2031
+ } else {
2032
+ process.stdout.write(output);
2033
+ }
2034
+ analytics.track('convert', true);
2035
+ } catch (error) {
2036
+ analytics.track('convert', false);
2037
+ console.error(chalk.red('Error:'), error.message);
2038
+ process.exit(1);
2039
+ }
2040
+ });
2041
+
1955
2042
  // Exported for testing
1956
2043
  module.exports = {
1957
2044
  program,
@@ -2104,5 +2104,12 @@ class ConfluenceClient {
2104
2104
  }
2105
2105
  }
2106
2106
 
2107
+ ConfluenceClient.createLocalConverter = function () {
2108
+ const instance = Object.create(ConfluenceClient.prototype);
2109
+ instance.markdown = new MarkdownIt();
2110
+ instance.setupConfluenceMarkdownExtensions();
2111
+ return instance;
2112
+ };
2113
+
2107
2114
  module.exports = ConfluenceClient;
2108
2115
  module.exports.NAMED_ENTITIES = NAMED_ENTITIES;