@aaronshaf/confluence-cli 0.1.15 → 1.0.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/README.md +25 -23
- package/package.json +1 -1
- package/src/cli/commands/create.ts +21 -2
- package/src/cli/commands/folder.ts +189 -0
- package/src/cli/commands/spaces.ts +10 -1
- package/src/cli/commands/update.ts +72 -0
- package/src/cli/help.ts +70 -40
- package/src/cli/index.ts +74 -17
- package/src/cli/utils/args.ts +15 -0
- package/src/cli/utils/stdin.ts +14 -0
- package/src/lib/confluence-client/client.ts +42 -55
- package/src/lib/confluence-client/folder-operations.ts +41 -0
- package/src/lib/confluence-client/index.ts +1 -0
- package/src/lib/confluence-client/search-operations.ts +2 -1
- package/src/lib/confluence-client/space-operations.ts +133 -0
- package/src/lib/confluence-client/types.ts +27 -3
- package/src/test/confluence-client.test.ts +9 -7
- package/src/test/folder-command.test.ts +182 -0
- package/src/test/mocks/handlers.ts +12 -0
- package/src/test/spaces.test.ts +1 -1
- package/src/test/update.test.ts +115 -0
- package/src/cli/commands/duplicate-check.ts +0 -89
- package/src/cli/commands/file-rename.ts +0 -113
- package/src/cli/commands/folder-hierarchy.ts +0 -241
- package/src/cli/commands/push-errors.ts +0 -40
- package/src/cli/commands/push.ts +0 -699
- package/src/lib/dependency-sorter.ts +0 -233
- package/src/test/dependency-sorter.test.ts +0 -384
- package/src/test/file-rename.test.ts +0 -305
- package/src/test/folder-hierarchy.test.ts +0 -337
- package/src/test/push.test.ts +0 -551
package/README.md
CHANGED
|
@@ -1,40 +1,39 @@
|
|
|
1
|
-
#
|
|
1
|
+
# confluence-cli
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
## Install
|
|
3
|
+
## Installation
|
|
6
4
|
|
|
7
5
|
```bash
|
|
6
|
+
# Install Bun runtime
|
|
7
|
+
curl -fsSL https://bun.sh/install | bash
|
|
8
|
+
|
|
9
|
+
# Install confluence-cli
|
|
8
10
|
bun install -g @aaronshaf/confluence-cli
|
|
9
11
|
```
|
|
10
12
|
|
|
11
13
|
## Getting Started
|
|
12
14
|
|
|
13
15
|
```bash
|
|
14
|
-
#
|
|
16
|
+
# Configure your Confluence credentials
|
|
15
17
|
cn setup
|
|
16
18
|
|
|
17
|
-
#
|
|
18
|
-
cn
|
|
19
|
+
# Search pages
|
|
20
|
+
cn search "authentication"
|
|
19
21
|
|
|
20
|
-
#
|
|
21
|
-
|
|
22
|
-
cn pull
|
|
23
|
-
```
|
|
22
|
+
# Open a page in the browser
|
|
23
|
+
cn open "Getting Started"
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
# Create a page
|
|
26
|
+
cn create "My Page" --space ENG
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
# List spaces
|
|
29
|
+
cn spaces
|
|
30
|
+
```
|
|
29
31
|
|
|
30
32
|
## Commands
|
|
31
33
|
|
|
32
34
|
| Command | Description |
|
|
33
35
|
|---------|-------------|
|
|
34
36
|
| `cn setup` | Configure Confluence credentials |
|
|
35
|
-
| `cn clone <SPACE_KEY>` | Clone a space to a new folder |
|
|
36
|
-
| `cn pull` | Pull changes from Confluence as markdown |
|
|
37
|
-
| `cn push [file]` | Push local markdown file(s) to Confluence |
|
|
38
37
|
| `cn status` | Check connection and sync status |
|
|
39
38
|
| `cn tree` | Display page hierarchy |
|
|
40
39
|
| `cn open [page]` | Open page in browser |
|
|
@@ -42,20 +41,19 @@ Credentials are stored in `~/.cn/config.json`. Space configuration is saved to `
|
|
|
42
41
|
| `cn search <query>` | Search pages using CQL |
|
|
43
42
|
| `cn spaces` | List available spaces |
|
|
44
43
|
| `cn info <id\|file>` | Show page info and labels |
|
|
45
|
-
| `cn create <title>` | Create a new page |
|
|
44
|
+
| `cn create <title>` | Create a new page (pipe content via stdin) |
|
|
45
|
+
| `cn update <id>` | Update an existing page (pipe content via stdin) |
|
|
46
46
|
| `cn delete <id>` | Delete a page |
|
|
47
47
|
| `cn comments <id\|file>` | Show page comments |
|
|
48
48
|
| `cn labels <id\|file>` | Manage page labels |
|
|
49
49
|
| `cn move <id\|file> <parentId>` | Move a page to a new parent |
|
|
50
50
|
| `cn attachments <id\|file>` | Manage page attachments |
|
|
51
|
+
| `cn folder <subcommand>` | Manage folders (create, list, delete, move) |
|
|
52
|
+
| `cn clone <SPACE_KEY>` | Clone a space to a new folder |
|
|
53
|
+
| `cn pull` | Pull changes from Confluence as markdown |
|
|
51
54
|
|
|
52
55
|
Run `cn <command> --help` for details on each command.
|
|
53
56
|
|
|
54
|
-
## Requirements
|
|
55
|
-
|
|
56
|
-
- Bun 1.2.0+
|
|
57
|
-
- Confluence Cloud account
|
|
58
|
-
|
|
59
57
|
## Development
|
|
60
58
|
|
|
61
59
|
```bash
|
|
@@ -64,6 +62,10 @@ bun run cn --help
|
|
|
64
62
|
bun test
|
|
65
63
|
```
|
|
66
64
|
|
|
65
|
+
## See also
|
|
66
|
+
|
|
67
|
+
- [pchuri/confluence-cli](https://github.com/pchuri/confluence-cli)
|
|
68
|
+
|
|
67
69
|
## License
|
|
68
70
|
|
|
69
71
|
MIT
|
package/package.json
CHANGED
|
@@ -4,11 +4,13 @@ import { ConfigManager } from '../../lib/config.js';
|
|
|
4
4
|
import { EXIT_CODES } from '../../lib/errors.js';
|
|
5
5
|
import { readSpaceConfig } from '../../lib/space-config.js';
|
|
6
6
|
import { openUrl } from '../utils/browser.js';
|
|
7
|
+
import { VALID_FORMATS, isValidFormat, readStdin } from '../utils/stdin.js';
|
|
7
8
|
|
|
8
9
|
export interface CreateCommandOptions {
|
|
9
10
|
space?: string;
|
|
10
11
|
parent?: string;
|
|
11
12
|
open?: boolean;
|
|
13
|
+
format?: string;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
16
|
export async function createCommand(title: string, options: CreateCommandOptions = {}): Promise<void> {
|
|
@@ -20,6 +22,23 @@ export async function createCommand(title: string, options: CreateCommandOptions
|
|
|
20
22
|
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
21
23
|
}
|
|
22
24
|
|
|
25
|
+
const rawFormat = options.format ?? 'storage';
|
|
26
|
+
if (!isValidFormat(rawFormat)) {
|
|
27
|
+
console.error(chalk.red(`Invalid format: ${rawFormat}`));
|
|
28
|
+
console.log(chalk.gray(`Valid formats: ${VALID_FORMATS.join(', ')}`));
|
|
29
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
30
|
+
}
|
|
31
|
+
const representation = rawFormat;
|
|
32
|
+
|
|
33
|
+
let bodyValue = '';
|
|
34
|
+
if (!process.stdin.isTTY) {
|
|
35
|
+
bodyValue = await readStdin();
|
|
36
|
+
if (bodyValue.trim().length === 0) {
|
|
37
|
+
console.error(chalk.red('Stdin is empty. Provide content to create a page with body.'));
|
|
38
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
23
42
|
const client = new ConfluenceClient(config);
|
|
24
43
|
let spaceId: string | undefined;
|
|
25
44
|
|
|
@@ -41,8 +60,8 @@ export async function createCommand(title: string, options: CreateCommandOptions
|
|
|
41
60
|
title,
|
|
42
61
|
parentId: options.parent,
|
|
43
62
|
body: {
|
|
44
|
-
representation
|
|
45
|
-
value:
|
|
63
|
+
representation,
|
|
64
|
+
value: bodyValue,
|
|
46
65
|
},
|
|
47
66
|
});
|
|
48
67
|
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { confirm } from '@inquirer/prompts';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import { ConfluenceClient } from '../../lib/confluence-client/index.js';
|
|
4
|
+
import { ConfigManager } from '../../lib/config.js';
|
|
5
|
+
import { EXIT_CODES } from '../../lib/errors.js';
|
|
6
|
+
import { escapeXml } from '../../lib/formatters.js';
|
|
7
|
+
import { readSpaceConfig } from '../../lib/space-config.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Extract a flag value from an args array, e.g. --space DOCS -> "DOCS"
|
|
11
|
+
*/
|
|
12
|
+
function getFlagValue(args: string[], flag: string): string | undefined {
|
|
13
|
+
const idx = args.indexOf(flag);
|
|
14
|
+
if (idx !== -1 && idx + 1 < args.length && !args[idx + 1].startsWith('--')) {
|
|
15
|
+
return args[idx + 1];
|
|
16
|
+
}
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Get positional args by stripping flags and their values
|
|
22
|
+
*/
|
|
23
|
+
function getPositionals(args: string[], flagsWithValues: string[]): string[] {
|
|
24
|
+
const result: string[] = [];
|
|
25
|
+
for (let i = 0; i < args.length; i++) {
|
|
26
|
+
if (args[i].startsWith('--')) {
|
|
27
|
+
if (flagsWithValues.includes(args[i])) {
|
|
28
|
+
i++; // skip the value too
|
|
29
|
+
}
|
|
30
|
+
} else {
|
|
31
|
+
result.push(args[i]);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function folderCommand(subcommand: string, subArgs: string[], allArgs: string[]): Promise<void> {
|
|
38
|
+
const configManager = new ConfigManager();
|
|
39
|
+
const config = await configManager.getConfig();
|
|
40
|
+
|
|
41
|
+
if (!config) {
|
|
42
|
+
console.error(chalk.red('Not configured. Run: cn setup'));
|
|
43
|
+
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const client = new ConfluenceClient(config);
|
|
47
|
+
|
|
48
|
+
switch (subcommand) {
|
|
49
|
+
case 'create': {
|
|
50
|
+
const positionals = getPositionals(subArgs, ['--space', '--parent']);
|
|
51
|
+
const title = positionals[0];
|
|
52
|
+
if (!title) {
|
|
53
|
+
console.error(chalk.red('Folder title is required.'));
|
|
54
|
+
console.log(chalk.gray('Usage: cn folder create <title> --space <key>'));
|
|
55
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const spaceKeyArg = getFlagValue(allArgs, '--space');
|
|
59
|
+
const parentId = getFlagValue(allArgs, '--parent');
|
|
60
|
+
|
|
61
|
+
let spaceId: string;
|
|
62
|
+
|
|
63
|
+
if (spaceKeyArg) {
|
|
64
|
+
const space = await client.getSpaceByKey(spaceKeyArg);
|
|
65
|
+
spaceId = space.id;
|
|
66
|
+
} else {
|
|
67
|
+
const spaceConfig = readSpaceConfig(process.cwd());
|
|
68
|
+
if (!spaceConfig) {
|
|
69
|
+
console.error(chalk.red('Not in a cloned space directory. Use --space to specify a space key.'));
|
|
70
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
71
|
+
}
|
|
72
|
+
spaceId = spaceConfig.spaceId;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const folder = await client.createFolder({ spaceId, title, parentId });
|
|
76
|
+
console.log(`${chalk.green('Created:')} "${folder.title}" (${folder.id})`);
|
|
77
|
+
break;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
case 'list': {
|
|
81
|
+
const spaceKeyArg = getFlagValue(allArgs, '--space');
|
|
82
|
+
|
|
83
|
+
let spaceKey: string;
|
|
84
|
+
|
|
85
|
+
if (spaceKeyArg) {
|
|
86
|
+
spaceKey = spaceKeyArg;
|
|
87
|
+
} else {
|
|
88
|
+
const spaceConfig = readSpaceConfig(process.cwd());
|
|
89
|
+
if (!spaceConfig) {
|
|
90
|
+
console.error(chalk.red('Not in a cloned space directory. Use --space to specify a space key.'));
|
|
91
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
92
|
+
}
|
|
93
|
+
spaceKey = spaceConfig.spaceKey;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const cql = `type=folder AND space="${spaceKey.replace(/"/g, '\\"')}"`;
|
|
97
|
+
const PAGE_SIZE = 100;
|
|
98
|
+
const allResults: (typeof firstPage.results)[number][] = [];
|
|
99
|
+
let start = 0;
|
|
100
|
+
|
|
101
|
+
const firstPage = await client.search(cql, PAGE_SIZE, start);
|
|
102
|
+
allResults.push(...firstPage.results);
|
|
103
|
+
const total = firstPage.totalSize ?? firstPage.results.length;
|
|
104
|
+
|
|
105
|
+
while (allResults.length < total) {
|
|
106
|
+
start += PAGE_SIZE;
|
|
107
|
+
const page = await client.search(cql, PAGE_SIZE, start);
|
|
108
|
+
if (page.results.length === 0) break;
|
|
109
|
+
allResults.push(...page.results);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const xml = allArgs.includes('--xml');
|
|
113
|
+
|
|
114
|
+
if (xml) {
|
|
115
|
+
console.log('<folders>');
|
|
116
|
+
for (const result of allResults) {
|
|
117
|
+
const c = result.content;
|
|
118
|
+
if (c) {
|
|
119
|
+
console.log(` <folder>`);
|
|
120
|
+
console.log(` <id>${escapeXml(c.id ?? '')}</id>`);
|
|
121
|
+
console.log(` <title>${escapeXml(c.title ?? '')}</title>`);
|
|
122
|
+
console.log(` </folder>`);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
console.log('</folders>');
|
|
126
|
+
} else {
|
|
127
|
+
if (allResults.length === 0) {
|
|
128
|
+
console.log(chalk.gray('No folders found.'));
|
|
129
|
+
} else {
|
|
130
|
+
for (const result of allResults) {
|
|
131
|
+
const c = result.content;
|
|
132
|
+
if (c) {
|
|
133
|
+
console.log(`${chalk.cyan(c.id)} ${c.title}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
break;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
case 'delete': {
|
|
142
|
+
const positionals = getPositionals(subArgs, []);
|
|
143
|
+
const folderId = positionals[0];
|
|
144
|
+
if (!folderId) {
|
|
145
|
+
console.error(chalk.red('Folder ID is required.'));
|
|
146
|
+
console.log(chalk.gray('Usage: cn folder delete <id>'));
|
|
147
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!allArgs.includes('--force')) {
|
|
151
|
+
const folder = await client.getFolder(folderId);
|
|
152
|
+
const confirmed = await confirm({
|
|
153
|
+
message: `Delete folder "${folder.title}" (${folderId})?`,
|
|
154
|
+
default: false,
|
|
155
|
+
});
|
|
156
|
+
if (!confirmed) {
|
|
157
|
+
console.log('Cancelled.');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await client.deleteFolder(folderId);
|
|
163
|
+
console.log(`${chalk.green('Deleted:')} ${folderId}`);
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
case 'move': {
|
|
168
|
+
const positionals = getPositionals(subArgs, []);
|
|
169
|
+
if (positionals.length < 2) {
|
|
170
|
+
console.error(chalk.red('Folder ID and parent ID are required.'));
|
|
171
|
+
console.log(chalk.gray('Usage: cn folder move <id> <parentId>'));
|
|
172
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const folderId = positionals[0] as string;
|
|
176
|
+
const parentId = positionals[1] as string;
|
|
177
|
+
const [folder, parent] = await Promise.all([client.getFolder(folderId), client.getFolder(parentId)]);
|
|
178
|
+
|
|
179
|
+
await client.movePage(folderId, parentId);
|
|
180
|
+
console.log(`${chalk.green('Moved:')} "${folder.title}" under "${parent.title}"`);
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
default:
|
|
185
|
+
console.error(chalk.red(`Unknown folder subcommand: ${subcommand}`));
|
|
186
|
+
console.log(chalk.gray('Run "cn folder --help" for usage information.'));
|
|
187
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
@@ -6,6 +6,8 @@ import { escapeXml } from '../../lib/formatters.js';
|
|
|
6
6
|
|
|
7
7
|
export interface SpacesCommandOptions {
|
|
8
8
|
xml?: boolean;
|
|
9
|
+
limit?: number;
|
|
10
|
+
page?: number;
|
|
9
11
|
}
|
|
10
12
|
|
|
11
13
|
export async function spacesCommand(options: SpacesCommandOptions = {}): Promise<void> {
|
|
@@ -17,8 +19,11 @@ export async function spacesCommand(options: SpacesCommandOptions = {}): Promise
|
|
|
17
19
|
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
18
20
|
}
|
|
19
21
|
|
|
22
|
+
const limit = options.limit ?? 25;
|
|
23
|
+
const page = options.page ?? 1;
|
|
20
24
|
const client = new ConfluenceClient(config);
|
|
21
|
-
const
|
|
25
|
+
const response = await client.getSpaces(limit, page);
|
|
26
|
+
const spaces = response.results;
|
|
22
27
|
|
|
23
28
|
if (options.xml) {
|
|
24
29
|
console.log('<spaces>');
|
|
@@ -39,4 +44,8 @@ export async function spacesCommand(options: SpacesCommandOptions = {}): Promise
|
|
|
39
44
|
for (const space of spaces) {
|
|
40
45
|
console.log(`${chalk.bold(space.key)} ${space.name} ${chalk.gray(space.id)}`);
|
|
41
46
|
}
|
|
47
|
+
|
|
48
|
+
if (response.size === limit) {
|
|
49
|
+
console.log(chalk.gray(`\nPage ${page}. Use --page ${page + 1} for next page, --limit to change page size.`));
|
|
50
|
+
}
|
|
42
51
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { ConfluenceClient } from '../../lib/confluence-client/index.js';
|
|
3
|
+
import { ConfigManager } from '../../lib/config.js';
|
|
4
|
+
import { EXIT_CODES } from '../../lib/errors.js';
|
|
5
|
+
import { VALID_FORMATS, isValidFormat, readStdin } from '../utils/stdin.js';
|
|
6
|
+
|
|
7
|
+
export interface UpdateCommandOptions {
|
|
8
|
+
format?: string;
|
|
9
|
+
title?: string;
|
|
10
|
+
message?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export async function updateCommand(pageId: string, options: UpdateCommandOptions = {}): Promise<void> {
|
|
14
|
+
const configManager = new ConfigManager();
|
|
15
|
+
const config = await configManager.getConfig();
|
|
16
|
+
|
|
17
|
+
if (!config) {
|
|
18
|
+
console.error(chalk.red('Not configured. Run: cn setup'));
|
|
19
|
+
process.exit(EXIT_CODES.CONFIG_ERROR);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (process.stdin.isTTY) {
|
|
23
|
+
console.error(chalk.red('No content provided. Pipe content via stdin.'));
|
|
24
|
+
console.log(chalk.gray('Usage: echo "<p>Content</p>" | cn update <id>'));
|
|
25
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const rawFormat = options.format ?? 'storage';
|
|
29
|
+
if (!isValidFormat(rawFormat)) {
|
|
30
|
+
console.error(chalk.red(`Invalid format: ${rawFormat}`));
|
|
31
|
+
console.log(chalk.gray(`Valid formats: ${VALID_FORMATS.join(', ')}`));
|
|
32
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
33
|
+
}
|
|
34
|
+
const representation = rawFormat;
|
|
35
|
+
|
|
36
|
+
const bodyValue = await readStdin();
|
|
37
|
+
if (bodyValue.trim().length === 0) {
|
|
38
|
+
console.error(chalk.red('Stdin is empty. Provide content to update the page.'));
|
|
39
|
+
process.exit(EXIT_CODES.INVALID_ARGUMENTS);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const client = new ConfluenceClient(config);
|
|
43
|
+
const current = await client.getPage(pageId, false);
|
|
44
|
+
|
|
45
|
+
if (!current) {
|
|
46
|
+
console.error(chalk.red(`Page not found: ${pageId}`));
|
|
47
|
+
process.exit(EXIT_CODES.GENERAL_ERROR);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const currentVersion = current.version?.number ?? 1;
|
|
51
|
+
const title = options.title ?? current.title;
|
|
52
|
+
|
|
53
|
+
const updated = await client.updatePage({
|
|
54
|
+
id: pageId,
|
|
55
|
+
status: 'current',
|
|
56
|
+
title,
|
|
57
|
+
body: {
|
|
58
|
+
representation,
|
|
59
|
+
value: bodyValue,
|
|
60
|
+
},
|
|
61
|
+
version: {
|
|
62
|
+
number: currentVersion + 1,
|
|
63
|
+
message: options.message,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
console.log(`${chalk.green('Updated:')} ${chalk.bold(updated.title)} ${chalk.gray(updated.id)}`);
|
|
68
|
+
if (updated._links?.webui) {
|
|
69
|
+
const url = `${config.confluenceUrl}/wiki${updated._links.webui}`;
|
|
70
|
+
console.log(`URL: ${chalk.blue(url)}`);
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/cli/help.ts
CHANGED
|
@@ -89,41 +89,6 @@ ${chalk.yellow('Examples:')}
|
|
|
89
89
|
`);
|
|
90
90
|
}
|
|
91
91
|
|
|
92
|
-
export function showPushHelp(): void {
|
|
93
|
-
console.log(`
|
|
94
|
-
${chalk.bold('cn push - Push local markdown files to Confluence')}
|
|
95
|
-
|
|
96
|
-
${chalk.yellow('Usage:')}
|
|
97
|
-
cn push [file] [options]
|
|
98
|
-
|
|
99
|
-
${chalk.yellow('Description:')}
|
|
100
|
-
Push local markdown files to Confluence.
|
|
101
|
-
|
|
102
|
-
With a file argument: pushes that single file.
|
|
103
|
-
Without arguments: scans for changed files and prompts y/n for each.
|
|
104
|
-
|
|
105
|
-
${chalk.yellow('Arguments:')}
|
|
106
|
-
file Path to markdown file (optional)
|
|
107
|
-
|
|
108
|
-
${chalk.yellow('Options:')}
|
|
109
|
-
--force Ignore version conflicts and overwrite
|
|
110
|
-
--dry-run Preview changes without pushing
|
|
111
|
-
--help Show this help message
|
|
112
|
-
|
|
113
|
-
${chalk.yellow('Examples:')}
|
|
114
|
-
cn push Scan and prompt for all changed files
|
|
115
|
-
cn push --dry-run Preview what would be pushed
|
|
116
|
-
cn push ./docs/page.md Push single page
|
|
117
|
-
cn push ./docs/page.md --force Force push (ignore version conflict)
|
|
118
|
-
|
|
119
|
-
${chalk.yellow('Notes:')}
|
|
120
|
-
- New files (no page_id) will be created on Confluence
|
|
121
|
-
- Modified files are detected by comparing file mtime vs synced_at
|
|
122
|
-
- Only basic markdown elements are fully supported
|
|
123
|
-
- Files are automatically renamed to match page titles (except index.md/README.md)
|
|
124
|
-
`);
|
|
125
|
-
}
|
|
126
|
-
|
|
127
92
|
export function showStatusHelp(): void {
|
|
128
93
|
console.log(`
|
|
129
94
|
${chalk.bold('cn status - Check connection and sync status')}
|
|
@@ -179,7 +144,8 @@ ${chalk.yellow('Usage:')}
|
|
|
179
144
|
cn open [options]
|
|
180
145
|
|
|
181
146
|
${chalk.yellow('Description:')}
|
|
182
|
-
Opens a Confluence page in your default browser.
|
|
147
|
+
Opens a Confluence page in your default web browser (launches a browser window).
|
|
148
|
+
Not suitable for non-interactive environments (CI, bots, scripts).
|
|
183
149
|
Without arguments, opens the space home page.
|
|
184
150
|
|
|
185
151
|
${chalk.yellow('Arguments:')}
|
|
@@ -254,6 +220,7 @@ ${chalk.bold('cn create - Create a new Confluence page')}
|
|
|
254
220
|
|
|
255
221
|
${chalk.yellow('Usage:')}
|
|
256
222
|
cn create <title> [options]
|
|
223
|
+
echo "<p>Content</p>" | cn create <title> [options]
|
|
257
224
|
|
|
258
225
|
${chalk.yellow('Arguments:')}
|
|
259
226
|
title Page title (required)
|
|
@@ -261,8 +228,37 @@ ${chalk.yellow('Arguments:')}
|
|
|
261
228
|
${chalk.yellow('Options:')}
|
|
262
229
|
--space <key> Space key (required if not in cloned dir)
|
|
263
230
|
--parent <id> Parent page ID
|
|
231
|
+
--format <format> Body format: storage (default), wiki, atlas_doc_format
|
|
264
232
|
--open Open page in browser after creation
|
|
265
233
|
--help Show this help message
|
|
234
|
+
|
|
235
|
+
${chalk.yellow('Examples:')}
|
|
236
|
+
cn create "My Page" --space ENG
|
|
237
|
+
echo "<p>Hello</p>" | cn create "My Page" --space ENG
|
|
238
|
+
echo "h1. Hello" | cn create "Wiki Page" --space ENG --format wiki
|
|
239
|
+
`);
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function showUpdateHelp(): void {
|
|
243
|
+
console.log(`
|
|
244
|
+
${chalk.bold('cn update - Update an existing Confluence page')}
|
|
245
|
+
|
|
246
|
+
${chalk.yellow('Usage:')}
|
|
247
|
+
echo "<p>Content</p>" | cn update <id> [options]
|
|
248
|
+
|
|
249
|
+
${chalk.yellow('Arguments:')}
|
|
250
|
+
id Page ID (required)
|
|
251
|
+
|
|
252
|
+
${chalk.yellow('Options:')}
|
|
253
|
+
--format <format> Body format: storage (default), wiki, atlas_doc_format
|
|
254
|
+
--title <title> New page title (default: keep existing title)
|
|
255
|
+
--message <msg> Version message
|
|
256
|
+
--help Show this help message
|
|
257
|
+
|
|
258
|
+
${chalk.yellow('Examples:')}
|
|
259
|
+
echo "<p>Updated content</p>" | cn update 123456
|
|
260
|
+
echo "<p>New content</p>" | cn update 123456 --title "New Title"
|
|
261
|
+
echo "h1. Hello" | cn update 123456 --format wiki --message "Updated via automation"
|
|
266
262
|
`);
|
|
267
263
|
}
|
|
268
264
|
|
|
@@ -316,6 +312,35 @@ ${chalk.yellow('Options:')}
|
|
|
316
312
|
`);
|
|
317
313
|
}
|
|
318
314
|
|
|
315
|
+
export function showFolderHelp(): void {
|
|
316
|
+
console.log(`
|
|
317
|
+
${chalk.bold('cn folder - Manage Confluence folders')}
|
|
318
|
+
|
|
319
|
+
${chalk.yellow('Usage:')}
|
|
320
|
+
cn folder <subcommand> [options]
|
|
321
|
+
|
|
322
|
+
${chalk.yellow('Subcommands:')}
|
|
323
|
+
create <title> Create a new folder
|
|
324
|
+
list List folders in a space
|
|
325
|
+
delete <id> Delete a folder
|
|
326
|
+
move <id> <parentId> Move a folder to a new parent
|
|
327
|
+
|
|
328
|
+
${chalk.yellow('Options:')}
|
|
329
|
+
--space <key> Space key (required for create/list if not in cloned dir)
|
|
330
|
+
--parent <id> Parent folder ID (for create)
|
|
331
|
+
--force Skip confirmation prompt (for delete)
|
|
332
|
+
--xml Output in XML format (for list)
|
|
333
|
+
--help Show this help message
|
|
334
|
+
|
|
335
|
+
${chalk.yellow('Examples:')}
|
|
336
|
+
cn folder create "My Folder" --space DOCS
|
|
337
|
+
cn folder create "Nested" --space DOCS --parent 123456
|
|
338
|
+
cn folder list --space DOCS
|
|
339
|
+
cn folder delete 123456
|
|
340
|
+
cn folder move 123456 789012
|
|
341
|
+
`);
|
|
342
|
+
}
|
|
343
|
+
|
|
319
344
|
export function showMoveHelp(): void {
|
|
320
345
|
console.log(`
|
|
321
346
|
${chalk.bold('cn move - Move a page to a new parent')}
|
|
@@ -386,7 +411,7 @@ ${chalk.yellow('Commands:')}
|
|
|
386
411
|
cn setup Configure Confluence credentials
|
|
387
412
|
cn clone Clone a space to a new folder
|
|
388
413
|
cn pull Pull space to local folder
|
|
389
|
-
cn
|
|
414
|
+
cn folder Manage Confluence folders
|
|
390
415
|
cn status Check connection and sync status
|
|
391
416
|
cn tree Display page hierarchy
|
|
392
417
|
cn open Open page in browser
|
|
@@ -395,6 +420,7 @@ ${chalk.yellow('Commands:')}
|
|
|
395
420
|
cn spaces List available spaces
|
|
396
421
|
cn info Show page info and labels
|
|
397
422
|
cn create Create a new page
|
|
423
|
+
cn update Update an existing page
|
|
398
424
|
cn delete Delete a page
|
|
399
425
|
cn comments Show page comments
|
|
400
426
|
cn labels Manage page labels
|
|
@@ -414,10 +440,14 @@ ${chalk.yellow('Environment Variables:')}
|
|
|
414
440
|
|
|
415
441
|
${chalk.yellow('Examples:')}
|
|
416
442
|
cn setup Configure credentials
|
|
417
|
-
cn
|
|
418
|
-
cn
|
|
419
|
-
cn
|
|
443
|
+
cn spaces List available spaces
|
|
444
|
+
cn search "my topic" Search across all spaces
|
|
445
|
+
cn search "api" --space ENG Search within a space
|
|
446
|
+
cn info 123456 Show page info
|
|
420
447
|
cn open "My Page" Open page in browser
|
|
448
|
+
cn create "New Page" --space ENG Create a page
|
|
449
|
+
cn clone DOCS Clone DOCS space locally
|
|
450
|
+
cn pull Pull changes
|
|
421
451
|
|
|
422
452
|
${chalk.gray('For more information on a command, run: cn <command> --help')}
|
|
423
453
|
${chalk.gray('Confluence REST API reference: https://docs.atlassian.com/atlassian-confluence/REST/6.6.0/')}
|