ya-git-jira 1.6.0 → 2.0.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/.opencode/skills/architecture/SKILL.md +45 -0
- package/.opencode/skills/code-style/SKILL.md +76 -0
- package/.opencode/skills/git-confluence/SKILL.md +82 -0
- package/.opencode/skills/git-jira/SKILL.md +63 -0
- package/.opencode/skills/git-lab/SKILL.md +102 -0
- package/AGENTS.md +50 -0
- package/README.md +106 -106
- package/bin/git-api.ts +70 -0
- package/bin/git-confluence-page-search.ts +58 -0
- package/bin/git-confluence-page-show.ts +61 -0
- package/bin/git-confluence-page-update.ts +77 -0
- package/bin/git-confluence-page.ts +28 -0
- package/bin/git-confluence-space-list.ts +34 -0
- package/bin/git-confluence-space.ts +24 -0
- package/bin/git-confluence-whoami.ts +33 -0
- package/bin/git-confluence.ts +27 -0
- package/bin/git-jira-start.ts +1 -1
- package/bin/git-jira-whoami.ts +32 -0
- package/bin/git-jira.ts +2 -0
- package/bin/git-lab-project-mr-list.ts +57 -0
- package/bin/git-lab-project-mr.ts +24 -0
- package/bin/git-lab-project-pipeline-jobs.ts +46 -0
- package/bin/git-lab-project-pipeline-latest.ts +47 -0
- package/bin/git-lab-project-pipeline-log.ts +49 -0
- package/bin/git-lab-project-pipeline.ts +6 -0
- package/bin/git-lab-project.ts +5 -1
- package/bin/gitj-install-skills.ts +126 -0
- package/bin/gitj.ts +12 -0
- package/dist/bin/git-api.js +2156 -0
- package/dist/bin/git-bump.js +132 -121
- package/dist/bin/git-confluence-page-search.js +2079 -0
- package/dist/bin/git-confluence-page-show.js +2082 -0
- package/dist/bin/git-confluence-page-update.js +2093 -0
- package/dist/bin/git-confluence-page.js +2186 -0
- package/dist/bin/git-confluence-space-list.js +2061 -0
- package/dist/bin/git-confluence-space.js +2073 -0
- package/dist/bin/git-confluence-whoami.js +2060 -0
- package/dist/bin/git-confluence.js +2251 -0
- package/dist/bin/git-jira-issue-list.js +136 -125
- package/dist/bin/git-jira-issue-show.js +136 -125
- package/dist/bin/git-jira-issue.js +140 -129
- package/dist/bin/git-jira-start.js +138 -127
- package/dist/bin/git-jira-whoami.js +1972 -0
- package/dist/bin/git-jira.js +170 -139
- package/dist/bin/git-lab-group-list.js +321 -279
- package/dist/bin/git-lab-group.js +323 -281
- package/dist/bin/git-lab-merge-active.js +321 -279
- package/dist/bin/git-lab-merge-todo.js +321 -279
- package/dist/bin/git-lab-merge-train-list.js +289 -273
- package/dist/bin/git-lab-merge-train.js +291 -275
- package/dist/bin/git-lab-merge.js +330 -288
- package/dist/bin/git-lab-namespace-list.js +138 -127
- package/dist/bin/git-lab-namespace.js +140 -129
- package/dist/bin/git-lab-project-list.js +288 -272
- package/dist/bin/git-lab-project-mr-list.js +2740 -0
- package/dist/bin/git-lab-project-mr.js +2752 -0
- package/dist/bin/git-lab-project-pipeline-jobs.js +2734 -0
- package/dist/bin/git-lab-project-pipeline-latest.js +2736 -0
- package/dist/bin/git-lab-project-pipeline-list.js +323 -281
- package/dist/bin/git-lab-project-pipeline-log.js +2739 -0
- package/dist/bin/git-lab-project-pipeline.js +437 -292
- package/dist/bin/git-lab-project-whereami.js +292 -276
- package/dist/bin/git-lab-project.js +563 -288
- package/dist/bin/git-lab-whoami.js +142 -131
- package/dist/bin/git-lab.js +575 -338
- package/dist/bin/gitj-install-skills.js +1954 -0
- package/dist/bin/gitj.js +1385 -473
- package/dist/index.js +371 -187
- package/index.ts +1 -0
- package/lib/api.ts +177 -0
- package/lib/confluence/api.ts +132 -0
- package/lib/confluence/config.ts +25 -0
- package/lib/confluence/index.ts +3 -0
- package/lib/confluence/types.ts +59 -0
- package/lib/gitlab/index.ts +1 -0
- package/lib/gitlab/job.ts +31 -0
- package/lib/gitlab/merge-request.ts +20 -0
- package/lib/gitlab/pipeline.ts +28 -1
- package/lib/gitlab/project.ts +14 -5
- package/lib/help.ts +40 -0
- package/lib/jira.ts +2 -2
- package/package.json +18 -2
- package/tests/all-help.test.ts +6 -1
- package/tests/gitj.test.ts +1 -1
- package/tests/help-all.test.ts +29 -0
- package/bun.lockb +0 -0
package/README.md
CHANGED
|
@@ -1,51 +1,89 @@
|
|
|
1
|
-
# ya-git-jira
|
|
1
|
+
# ya-git-jira
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
in the name.
|
|
3
|
+
Git extensions for Jira, GitLab, and Confluence. Each command is a standalone
|
|
4
|
+
executable that `git` discovers automatically (e.g. `git jira start`, `git lab
|
|
5
|
+
merge active`, `git confluence page search`). A unified `gitj` wrapper is also
|
|
6
|
+
provided.
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
## Requirements
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
[Bun](https://bun.sh) (not Node.js):
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
```
|
|
13
|
+
curl -fsSL https://bun.sh/install | bash
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Install
|
|
17
|
+
|
|
18
|
+
```
|
|
19
|
+
npm install -g ya-git-jira # or bun / yarn / pnpm
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Configuration
|
|
23
|
+
|
|
24
|
+
All configuration is via `git config`. Use `--global` if the same settings apply
|
|
25
|
+
across repositories.
|
|
26
|
+
|
|
27
|
+
### Jira
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
git config jira.host yourcompany.atlassian.net
|
|
31
|
+
git config jira.token "<api-token>"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Create an API token: <https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/>
|
|
16
35
|
|
|
17
|
-
|
|
36
|
+
### GitLab
|
|
18
37
|
|
|
19
38
|
```
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
git config gitlab.host gitlab.com # default if omitted
|
|
40
|
+
git config gitlab.token "<api-token>"
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Create a personal access token: <https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token>
|
|
22
44
|
|
|
23
|
-
|
|
24
|
-
-V, --version output the version number
|
|
25
|
-
-h, --help display help for command
|
|
45
|
+
### Confluence
|
|
26
46
|
|
|
27
|
-
Commands:
|
|
28
|
-
bump [options] Bump the version number in the current branch
|
|
29
|
-
jira [options] Commands for working with Jira
|
|
30
|
-
lab [options] Commands for working with GitLab
|
|
31
47
|
```
|
|
48
|
+
git config confluence.host yourcompany.atlassian.net
|
|
49
|
+
git config confluence.token "<api-token>"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The token is an Atlassian API token (same kind as Jira).
|
|
53
|
+
|
|
54
|
+
### Email address
|
|
55
|
+
|
|
56
|
+
Commands fall back to `user.email` from git config. Override per-service if needed:
|
|
32
57
|
|
|
33
|
-
|
|
58
|
+
```
|
|
59
|
+
git config jira.user <email>
|
|
60
|
+
git config gitlab.user <email>
|
|
61
|
+
git config confluence.user <email>
|
|
62
|
+
```
|
|
34
63
|
|
|
35
|
-
|
|
36
|
-
and naming conventions that are indended to make it easy to navigate the existing
|
|
37
|
-
commands and to also make it relatively easy to decide where a new command should
|
|
38
|
-
go into the hierarchy. The current hierarchy is:
|
|
64
|
+
## Command hierarchy
|
|
39
65
|
|
|
40
66
|
```
|
|
41
67
|
gitj
|
|
42
|
-
|
|
68
|
+
api authenticated REST request to any service
|
|
69
|
+
bump bump the version suffix of the current branch
|
|
70
|
+
confluence
|
|
71
|
+
whoami
|
|
72
|
+
space
|
|
73
|
+
list
|
|
74
|
+
page
|
|
75
|
+
search
|
|
76
|
+
show
|
|
77
|
+
update
|
|
78
|
+
install-skills install AI agent skills for a coding framework
|
|
43
79
|
jira
|
|
80
|
+
whoami
|
|
81
|
+
start create a topic branch from a Jira issue
|
|
44
82
|
issue
|
|
45
83
|
list
|
|
46
84
|
show
|
|
47
|
-
start
|
|
48
85
|
lab
|
|
86
|
+
whoami
|
|
49
87
|
group
|
|
50
88
|
list
|
|
51
89
|
merge
|
|
@@ -57,113 +95,75 @@ gitj
|
|
|
57
95
|
list
|
|
58
96
|
project
|
|
59
97
|
list
|
|
98
|
+
mr
|
|
99
|
+
list
|
|
60
100
|
pipeline
|
|
101
|
+
jobs
|
|
102
|
+
latest
|
|
61
103
|
list
|
|
104
|
+
log
|
|
62
105
|
whereami
|
|
63
|
-
whoami
|
|
64
106
|
```
|
|
65
107
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
that multiple items are return, whereas `show` implies seeing the details for a single item.
|
|
69
|
-
|
|
70
|
-
The `merge` subcommands `active` vs `todo` are both commands that result in a list.
|
|
71
|
-
We might refactor them to instead be `list --active` and `list --todo` which would
|
|
72
|
-
be more consistent.
|
|
73
|
-
|
|
74
|
-
The `git jira start` command might more logically be `git jira issue start`
|
|
75
|
-
but `start` implies *issue* and it is expected to be one of the most commonly
|
|
76
|
-
executed commands so we elevate it the hierarchy.
|
|
108
|
+
Every leaf command supports `--help`. Run `gitj --help-all` to print the full
|
|
109
|
+
tree with descriptions.
|
|
77
110
|
|
|
78
|
-
##
|
|
111
|
+
## Noteworthy commands
|
|
79
112
|
|
|
80
|
-
|
|
81
|
-
```bash
|
|
82
|
-
$ git jira-start <issue>
|
|
83
|
-
```
|
|
113
|
+
### git jira start
|
|
84
114
|
|
|
85
|
-
#### Examples:
|
|
86
|
-
```bash
|
|
87
|
-
$ git jira-start BUG-0042
|
|
88
115
|
```
|
|
89
|
-
|
|
90
|
-
The command retrieves the summary line for the issue and converts it to
|
|
91
|
-
a suitable branch name using the kebab-case-convention. If BUG-0042 had
|
|
92
|
-
the summary "fix the thing" then the branch name will be `BUG-0042-fix-the-thing`.
|
|
93
|
-
|
|
94
|
-
The command does not (yet) change the status of the issue.
|
|
95
|
-
|
|
96
|
-
## git-bump -- Create a new branch based on the current branch
|
|
97
|
-
|
|
98
|
-
Usage:
|
|
99
|
-
```bash
|
|
100
|
-
$ git bump
|
|
116
|
+
git jira start BUG-42
|
|
101
117
|
```
|
|
102
118
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
Assume the current branch is `BUG-0042-fix-the-thing`.
|
|
106
|
-
Executing the bump command once will create a new branch named `BUG-0042-fix-the-thing.v1`. If you execute the bump command again it will
|
|
107
|
-
create a branch `BUG-0042-fix-the-thing.v2`.
|
|
119
|
+
Fetches the issue summary from Jira, converts it to kebab-case, and creates a
|
|
120
|
+
branch like `BUG-42-fix-the-thing`.
|
|
108
121
|
|
|
109
|
-
|
|
110
|
-
It just checks to see if the current branch already ends with `.v`<*num*>,
|
|
111
|
-
in which case it increments *num* but otherwise leaves the branch name as is.
|
|
112
|
-
If the current branch does not end with `.v`<*num*> then it simply appends the
|
|
113
|
-
suffix `.v1`.
|
|
114
|
-
|
|
115
|
-
## Bun required
|
|
116
|
-
|
|
117
|
-
This package uses [bun](https://bun.sh) instead of [node](https://nodejs.org/en).
|
|
118
|
-
You must install it before you install this package.
|
|
122
|
+
### git bump
|
|
119
123
|
|
|
120
124
|
```
|
|
121
|
-
|
|
125
|
+
git bump
|
|
122
126
|
```
|
|
123
127
|
|
|
124
|
-
|
|
128
|
+
Appends or increments a `.vN` suffix on the current branch name:
|
|
129
|
+
`BUG-42-fix-the-thing` -> `.v1` -> `.v2` -> ...
|
|
125
130
|
|
|
126
|
-
|
|
131
|
+
### git api
|
|
127
132
|
|
|
128
133
|
```
|
|
129
|
-
|
|
134
|
+
git api jira /rest/api/3/myself
|
|
135
|
+
git api gitlab /api/v4/user
|
|
136
|
+
git api confluence /wiki/api/v2/spaces
|
|
130
137
|
```
|
|
131
138
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
All configuration is via `git config` settings. If your company has multiple
|
|
135
|
-
repositories that all using Jira issue tracking then you probably want to use
|
|
136
|
-
the global config by adding the `--global` option to the commands below.
|
|
137
|
-
|
|
138
|
-
### Jira
|
|
139
|
+
Make arbitrary authenticated REST calls to any configured service. Useful for
|
|
140
|
+
one-off queries and scripting.
|
|
139
141
|
|
|
140
|
-
|
|
141
|
-
To create an API token follow the instructions [here](https://support.atlassian.com/atlassian-account/docs/manage-api-tokens-for-your-atlassian-account/).
|
|
142
|
+
### gitj install-skills
|
|
142
143
|
|
|
143
144
|
```
|
|
144
|
-
|
|
145
|
-
|
|
145
|
+
gitj install-skills opencode # symlinks to ~/.config/opencode/skills/
|
|
146
|
+
gitj install-skills copilot # symlinks to ~/.copilot/skills/
|
|
147
|
+
gitj install-skills claude # symlinks to .claude/skills/ (project-level)
|
|
148
|
+
gitj install-skills opencode --copy # copy instead of symlink
|
|
149
|
+
gitj install-skills opencode --force # overwrite existing directories
|
|
146
150
|
```
|
|
147
151
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
for many users. To create an API token follow the instructions [here](https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#create-a-personal-access-token)
|
|
152
|
-
|
|
153
|
-
```
|
|
154
|
-
$ git config gitlab.host gitlab.com
|
|
155
|
-
$ git config gitlab.token "<long token here>"
|
|
156
|
-
```
|
|
152
|
+
Installs AI agent skill files (`git-jira`, `git-lab`, `git-confluence`) so that
|
|
153
|
+
coding assistants (OpenCode, GitHub Copilot, Claude Code) know how to use these
|
|
154
|
+
tools.
|
|
157
155
|
|
|
158
|
-
|
|
156
|
+
## AI agent skills
|
|
159
157
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
using these two settings:
|
|
158
|
+
The `.opencode/skills/` directory contains concise skill files for AI coding
|
|
159
|
+
agents. These tell agents that the tools exist, how auth works, and key workflow
|
|
160
|
+
patterns. Install them with `gitj install-skills <framework>`.
|
|
164
161
|
|
|
162
|
+
## Development
|
|
165
163
|
|
|
166
164
|
```
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
bun install # install dependencies
|
|
166
|
+
bun run build # build to dist/
|
|
167
|
+
bun test # run all tests
|
|
168
|
+
bunx tsc --noEmit # type check
|
|
169
169
|
```
|
package/bin/git-api.ts
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { isMain } from '../lib/is_main'
|
|
6
|
+
import { apiRequest, apiPaginate, serviceNames } from '../lib/api.ts'
|
|
7
|
+
|
|
8
|
+
const version = await getPackageVersion()
|
|
9
|
+
|
|
10
|
+
const httpMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS']
|
|
11
|
+
|
|
12
|
+
export function create(): Command {
|
|
13
|
+
const program: Command = new Command()
|
|
14
|
+
program
|
|
15
|
+
.version(version)
|
|
16
|
+
.name('api')
|
|
17
|
+
.description('make an authenticated API request')
|
|
18
|
+
.argument('<service>', `service name (${serviceNames.join(', ')})`)
|
|
19
|
+
.argument('<endpoint>', 'API endpoint path (e.g. /user, /myself)')
|
|
20
|
+
.option('-X, --method <method>', 'HTTP method (default: GET, auto-promotes to POST with --data)')
|
|
21
|
+
.option('-d, --data <json>', 'request body as JSON string')
|
|
22
|
+
.option('--raw', 'don\'t prepend the default base path')
|
|
23
|
+
.option('--paginate', 'follow pagination links and return all results')
|
|
24
|
+
.option('-v, --verbose', 'show response status and headers on stderr')
|
|
25
|
+
.action(async (service: string, endpoint: string, options) => {
|
|
26
|
+
let method = options.method?.toUpperCase() ?? (options.data ? 'POST' : 'GET')
|
|
27
|
+
if (!httpMethods.includes(method)) {
|
|
28
|
+
console.error(`Unknown HTTP method: ${method}`)
|
|
29
|
+
process.exit(1)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (options.paginate && method !== 'GET') {
|
|
33
|
+
console.error('--paginate only works with GET requests')
|
|
34
|
+
process.exit(1)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const requestOptions = {
|
|
38
|
+
raw: options.raw,
|
|
39
|
+
data: options.data,
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
let result
|
|
43
|
+
if (options.paginate) {
|
|
44
|
+
result = await apiPaginate(service, endpoint, requestOptions)
|
|
45
|
+
} else {
|
|
46
|
+
result = await apiRequest(service, method, endpoint, requestOptions)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (options.verbose) {
|
|
50
|
+
console.error(`HTTP ${result.status}`)
|
|
51
|
+
result.headers.forEach((value, key) => {
|
|
52
|
+
console.error(`${key}: ${value}`)
|
|
53
|
+
})
|
|
54
|
+
console.error('')
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log(JSON.stringify(result.body, null, 2))
|
|
58
|
+
|
|
59
|
+
if (result.status >= 400) {
|
|
60
|
+
process.exit(1)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
return program
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export default create
|
|
67
|
+
|
|
68
|
+
if (isMain('git-api')) {
|
|
69
|
+
await create().parseAsync(Bun.argv)
|
|
70
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { confluenceApi, confluenceSearch } from '../lib/confluence'
|
|
6
|
+
import { getConfluenceConfig } from '../lib/confluence'
|
|
7
|
+
import type { Page, SearchResult } from '../lib/confluence'
|
|
8
|
+
import { isMain } from '../lib/is_main'
|
|
9
|
+
const version = await getPackageVersion()
|
|
10
|
+
|
|
11
|
+
export function create(): Command {
|
|
12
|
+
const program: Command = new Command()
|
|
13
|
+
program
|
|
14
|
+
.version(version)
|
|
15
|
+
.name('search')
|
|
16
|
+
.description('Search for Confluence pages by title (fuzzy by default)')
|
|
17
|
+
.argument('query', 'Search query')
|
|
18
|
+
.option('-v, --verbose', 'Verbose output')
|
|
19
|
+
.option('--exact', 'Exact title match (uses v2 API)')
|
|
20
|
+
.option('--full-text', 'Search page body content in addition to title')
|
|
21
|
+
.action(async (query: string, options) => {
|
|
22
|
+
const { host } = await getConfluenceConfig()
|
|
23
|
+
|
|
24
|
+
if (options.exact) {
|
|
25
|
+
const pages = await confluenceApi(`pages?title=${encodeURIComponent(query)}`) as Array<Page>
|
|
26
|
+
if (options.verbose) {
|
|
27
|
+
console.log(pages)
|
|
28
|
+
} else {
|
|
29
|
+
for (const page of pages) {
|
|
30
|
+
const url = `https://${host}/wiki/spaces/${page.spaceId}/pages/${page.id}`
|
|
31
|
+
console.log(`${page.id}\t${page.title}\t${url}`)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const field = options.fullText ? 'text' : 'title'
|
|
38
|
+
const cql = `type=page AND ${field} ~ "${query}"`
|
|
39
|
+
const results = await confluenceSearch(cql) as Array<SearchResult>
|
|
40
|
+
if (options.verbose) {
|
|
41
|
+
console.log(results)
|
|
42
|
+
} else {
|
|
43
|
+
for (const result of results) {
|
|
44
|
+
const id = result.content.id
|
|
45
|
+
const title = result.content.title
|
|
46
|
+
const url = `https://${host}${result.url}`
|
|
47
|
+
console.log(`${id}\t${title}\t${url}`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
return program
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export default create
|
|
55
|
+
|
|
56
|
+
if (isMain('git-confluence-page-search')) {
|
|
57
|
+
await create().parseAsync(Bun.argv)
|
|
58
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { confluenceApi } from '../lib/confluence'
|
|
6
|
+
import { getConfluenceConfig } from '../lib/confluence'
|
|
7
|
+
import type { Page } from '../lib/confluence'
|
|
8
|
+
import { isMain } from '../lib/is_main'
|
|
9
|
+
const version = await getPackageVersion()
|
|
10
|
+
|
|
11
|
+
export function create(): Command {
|
|
12
|
+
const program: Command = new Command()
|
|
13
|
+
program
|
|
14
|
+
.version(version)
|
|
15
|
+
.name('show')
|
|
16
|
+
.description('Show information about a Confluence page')
|
|
17
|
+
.argument('id', 'Page ID')
|
|
18
|
+
.option('-v, --verbose', 'Verbose output')
|
|
19
|
+
.option('-b, --body-format <format>', 'Include page body (storage or atlas_doc_format)')
|
|
20
|
+
.option('--body-only', 'Output only the body content value (requires --body-format)')
|
|
21
|
+
.action(async (id: string, options) => {
|
|
22
|
+
let endpoint = `pages/${id}`
|
|
23
|
+
if (options.bodyFormat) {
|
|
24
|
+
endpoint += `?body-format=${options.bodyFormat}`
|
|
25
|
+
}
|
|
26
|
+
const page = await confluenceApi(endpoint) as Page
|
|
27
|
+
if (options.bodyOnly) {
|
|
28
|
+
if (!options.bodyFormat) {
|
|
29
|
+
console.error('--body-only requires --body-format')
|
|
30
|
+
process.exit(1)
|
|
31
|
+
}
|
|
32
|
+
const body = page.body
|
|
33
|
+
const content = options.bodyFormat === 'storage'
|
|
34
|
+
? body?.storage?.value
|
|
35
|
+
: body?.atlas_doc_format?.value
|
|
36
|
+
if (content) {
|
|
37
|
+
console.log(content)
|
|
38
|
+
} else {
|
|
39
|
+
console.error('No body content returned')
|
|
40
|
+
process.exit(1)
|
|
41
|
+
}
|
|
42
|
+
} else if (options.verbose) {
|
|
43
|
+
console.log(page)
|
|
44
|
+
} else {
|
|
45
|
+
const { host } = await getConfluenceConfig()
|
|
46
|
+
const url = `https://${host}/wiki/spaces/${page.spaceId}/pages/${page.id}`
|
|
47
|
+
const result: Record<string, unknown> = { id: page.id, title: page.title, spaceId: page.spaceId, url }
|
|
48
|
+
if (page.body?.storage?.value) {
|
|
49
|
+
result.bodyLength = page.body.storage.value.length
|
|
50
|
+
}
|
|
51
|
+
console.log(result)
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
return program
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export default create
|
|
58
|
+
|
|
59
|
+
if (isMain('git-confluence-page-show')) {
|
|
60
|
+
await create().parseAsync(Bun.argv)
|
|
61
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { confluenceApi, confluenceApiWrite } from '../lib/confluence'
|
|
6
|
+
import type { Page } from '../lib/confluence'
|
|
7
|
+
import type { JSONValue } from '../lib/json'
|
|
8
|
+
import { isMain } from '../lib/is_main'
|
|
9
|
+
const version = await getPackageVersion()
|
|
10
|
+
|
|
11
|
+
async function readStdin(): Promise<string> {
|
|
12
|
+
const chunks: Buffer[] = []
|
|
13
|
+
for await (const chunk of Bun.stdin.stream()) {
|
|
14
|
+
chunks.push(Buffer.from(chunk))
|
|
15
|
+
}
|
|
16
|
+
return Buffer.concat(chunks).toString('utf-8')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function create(): Command {
|
|
20
|
+
const program: Command = new Command()
|
|
21
|
+
program
|
|
22
|
+
.version(version)
|
|
23
|
+
.name('update')
|
|
24
|
+
.description('Update a Confluence page')
|
|
25
|
+
.argument('id', 'Page ID')
|
|
26
|
+
.option('-t, --title <title>', 'New page title (defaults to current title)')
|
|
27
|
+
.option('-f, --file <path>', 'Read body content from file (default: stdin)')
|
|
28
|
+
.option('-m, --message <message>', 'Version message')
|
|
29
|
+
.option('-v, --verbose', 'Verbose output')
|
|
30
|
+
.action(async (id: string, options) => {
|
|
31
|
+
const current = await confluenceApi(`pages/${id}`) as Page
|
|
32
|
+
|
|
33
|
+
let content: string
|
|
34
|
+
if (options.file) {
|
|
35
|
+
const file = Bun.file(options.file)
|
|
36
|
+
content = await file.text()
|
|
37
|
+
} else {
|
|
38
|
+
content = await readStdin()
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!content.trim()) {
|
|
42
|
+
console.error('No content provided. Pipe content via stdin or use --file.')
|
|
43
|
+
process.exit(1)
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const title = options.title || current.title
|
|
47
|
+
const nextVersion = current.version.number + 1
|
|
48
|
+
|
|
49
|
+
const body: JSONValue = {
|
|
50
|
+
id,
|
|
51
|
+
status: 'current',
|
|
52
|
+
title,
|
|
53
|
+
body: {
|
|
54
|
+
representation: 'storage',
|
|
55
|
+
value: content,
|
|
56
|
+
},
|
|
57
|
+
version: {
|
|
58
|
+
number: nextVersion,
|
|
59
|
+
message: options.message || '',
|
|
60
|
+
},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const result = await confluenceApiWrite(`pages/${id}`, 'PUT', body) as Page
|
|
64
|
+
if (options.verbose) {
|
|
65
|
+
console.log(result)
|
|
66
|
+
} else {
|
|
67
|
+
console.log(`Updated page ${result.id} "${result.title}" to version ${result.version.number}`)
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
return program
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default create
|
|
74
|
+
|
|
75
|
+
if (isMain('git-confluence-page-update')) {
|
|
76
|
+
await create().parseAsync(Bun.argv)
|
|
77
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { isMain } from '../lib/is_main'
|
|
6
|
+
import search from './git-confluence-page-search'
|
|
7
|
+
import show from './git-confluence-page-show'
|
|
8
|
+
import update from './git-confluence-page-update'
|
|
9
|
+
const version = await getPackageVersion()
|
|
10
|
+
|
|
11
|
+
export function create(): Command {
|
|
12
|
+
const program: Command = new Command()
|
|
13
|
+
program
|
|
14
|
+
.version(version)
|
|
15
|
+
.name('page')
|
|
16
|
+
.description('Commands for working with Confluence pages')
|
|
17
|
+
.addCommand(search())
|
|
18
|
+
.addCommand(show())
|
|
19
|
+
.addCommand(update())
|
|
20
|
+
.action(() => program.help())
|
|
21
|
+
return program
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export default create
|
|
25
|
+
|
|
26
|
+
if (isMain('git-confluence-page')) {
|
|
27
|
+
await create().parseAsync(Bun.argv)
|
|
28
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { confluenceApi } from '../lib/confluence'
|
|
6
|
+
import type { Space } from '../lib/confluence'
|
|
7
|
+
import { isMain } from '../lib/is_main'
|
|
8
|
+
const version = await getPackageVersion()
|
|
9
|
+
|
|
10
|
+
export function create(): Command {
|
|
11
|
+
const program: Command = new Command()
|
|
12
|
+
program
|
|
13
|
+
.version(version)
|
|
14
|
+
.name('list')
|
|
15
|
+
.description('List Confluence spaces')
|
|
16
|
+
.option('-v, --verbose', 'Verbose output')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
const spaces = await confluenceApi('spaces') as Array<Space>
|
|
19
|
+
if (options.verbose) {
|
|
20
|
+
console.log(spaces)
|
|
21
|
+
} else {
|
|
22
|
+
for (const space of spaces) {
|
|
23
|
+
console.log(`${space.key}\t${space.name}`)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
return program
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export default create
|
|
31
|
+
|
|
32
|
+
if (isMain('git-confluence-space-list')) {
|
|
33
|
+
await create().parseAsync(Bun.argv)
|
|
34
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { isMain } from '../lib/is_main'
|
|
6
|
+
import list from './git-confluence-space-list'
|
|
7
|
+
const version = await getPackageVersion()
|
|
8
|
+
|
|
9
|
+
export function create(): Command {
|
|
10
|
+
const program: Command = new Command()
|
|
11
|
+
program
|
|
12
|
+
.version(version)
|
|
13
|
+
.name('space')
|
|
14
|
+
.description('Commands for working with Confluence spaces')
|
|
15
|
+
.addCommand(list())
|
|
16
|
+
.action(() => program.help())
|
|
17
|
+
return program
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default create
|
|
21
|
+
|
|
22
|
+
if (isMain('git-confluence-space')) {
|
|
23
|
+
await create().parseAsync(Bun.argv)
|
|
24
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
import { getPackageVersion } from '../lib/package'
|
|
5
|
+
import { confluenceApiV1 } from '../lib/confluence'
|
|
6
|
+
import type { ConfluenceUser } from '../lib/confluence'
|
|
7
|
+
import { isMain } from '../lib/is_main'
|
|
8
|
+
const version = await getPackageVersion()
|
|
9
|
+
|
|
10
|
+
export function create(): Command {
|
|
11
|
+
const program: Command = new Command()
|
|
12
|
+
program
|
|
13
|
+
.version(version)
|
|
14
|
+
.name('whoami')
|
|
15
|
+
.description('Show the current Confluence user')
|
|
16
|
+
.option('-v, --verbose', 'Verbose output')
|
|
17
|
+
.action(async (options) => {
|
|
18
|
+
const myself = await confluenceApiV1('user/current') as ConfluenceUser
|
|
19
|
+
if (options.verbose) {
|
|
20
|
+
console.log(myself)
|
|
21
|
+
} else {
|
|
22
|
+
const { displayName, email, accountId } = myself
|
|
23
|
+
console.log({ displayName, email, accountId })
|
|
24
|
+
}
|
|
25
|
+
})
|
|
26
|
+
return program
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export default create
|
|
30
|
+
|
|
31
|
+
if (isMain('git-confluence-whoami')) {
|
|
32
|
+
await create().parseAsync(Bun.argv)
|
|
33
|
+
}
|