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.
Files changed (86) hide show
  1. package/.opencode/skills/architecture/SKILL.md +45 -0
  2. package/.opencode/skills/code-style/SKILL.md +76 -0
  3. package/.opencode/skills/git-confluence/SKILL.md +82 -0
  4. package/.opencode/skills/git-jira/SKILL.md +63 -0
  5. package/.opencode/skills/git-lab/SKILL.md +102 -0
  6. package/AGENTS.md +50 -0
  7. package/README.md +106 -106
  8. package/bin/git-api.ts +70 -0
  9. package/bin/git-confluence-page-search.ts +58 -0
  10. package/bin/git-confluence-page-show.ts +61 -0
  11. package/bin/git-confluence-page-update.ts +77 -0
  12. package/bin/git-confluence-page.ts +28 -0
  13. package/bin/git-confluence-space-list.ts +34 -0
  14. package/bin/git-confluence-space.ts +24 -0
  15. package/bin/git-confluence-whoami.ts +33 -0
  16. package/bin/git-confluence.ts +27 -0
  17. package/bin/git-jira-start.ts +1 -1
  18. package/bin/git-jira-whoami.ts +32 -0
  19. package/bin/git-jira.ts +2 -0
  20. package/bin/git-lab-project-mr-list.ts +57 -0
  21. package/bin/git-lab-project-mr.ts +24 -0
  22. package/bin/git-lab-project-pipeline-jobs.ts +46 -0
  23. package/bin/git-lab-project-pipeline-latest.ts +47 -0
  24. package/bin/git-lab-project-pipeline-log.ts +49 -0
  25. package/bin/git-lab-project-pipeline.ts +6 -0
  26. package/bin/git-lab-project.ts +5 -1
  27. package/bin/gitj-install-skills.ts +126 -0
  28. package/bin/gitj.ts +12 -0
  29. package/dist/bin/git-api.js +2156 -0
  30. package/dist/bin/git-bump.js +132 -121
  31. package/dist/bin/git-confluence-page-search.js +2079 -0
  32. package/dist/bin/git-confluence-page-show.js +2082 -0
  33. package/dist/bin/git-confluence-page-update.js +2093 -0
  34. package/dist/bin/git-confluence-page.js +2186 -0
  35. package/dist/bin/git-confluence-space-list.js +2061 -0
  36. package/dist/bin/git-confluence-space.js +2073 -0
  37. package/dist/bin/git-confluence-whoami.js +2060 -0
  38. package/dist/bin/git-confluence.js +2251 -0
  39. package/dist/bin/git-jira-issue-list.js +136 -125
  40. package/dist/bin/git-jira-issue-show.js +136 -125
  41. package/dist/bin/git-jira-issue.js +140 -129
  42. package/dist/bin/git-jira-start.js +138 -127
  43. package/dist/bin/git-jira-whoami.js +1972 -0
  44. package/dist/bin/git-jira.js +170 -139
  45. package/dist/bin/git-lab-group-list.js +321 -279
  46. package/dist/bin/git-lab-group.js +323 -281
  47. package/dist/bin/git-lab-merge-active.js +321 -279
  48. package/dist/bin/git-lab-merge-todo.js +321 -279
  49. package/dist/bin/git-lab-merge-train-list.js +289 -273
  50. package/dist/bin/git-lab-merge-train.js +291 -275
  51. package/dist/bin/git-lab-merge.js +330 -288
  52. package/dist/bin/git-lab-namespace-list.js +138 -127
  53. package/dist/bin/git-lab-namespace.js +140 -129
  54. package/dist/bin/git-lab-project-list.js +288 -272
  55. package/dist/bin/git-lab-project-mr-list.js +2740 -0
  56. package/dist/bin/git-lab-project-mr.js +2752 -0
  57. package/dist/bin/git-lab-project-pipeline-jobs.js +2734 -0
  58. package/dist/bin/git-lab-project-pipeline-latest.js +2736 -0
  59. package/dist/bin/git-lab-project-pipeline-list.js +323 -281
  60. package/dist/bin/git-lab-project-pipeline-log.js +2739 -0
  61. package/dist/bin/git-lab-project-pipeline.js +437 -292
  62. package/dist/bin/git-lab-project-whereami.js +292 -276
  63. package/dist/bin/git-lab-project.js +563 -288
  64. package/dist/bin/git-lab-whoami.js +142 -131
  65. package/dist/bin/git-lab.js +575 -338
  66. package/dist/bin/gitj-install-skills.js +1954 -0
  67. package/dist/bin/gitj.js +1385 -473
  68. package/dist/index.js +371 -187
  69. package/index.ts +1 -0
  70. package/lib/api.ts +177 -0
  71. package/lib/confluence/api.ts +132 -0
  72. package/lib/confluence/config.ts +25 -0
  73. package/lib/confluence/index.ts +3 -0
  74. package/lib/confluence/types.ts +59 -0
  75. package/lib/gitlab/index.ts +1 -0
  76. package/lib/gitlab/job.ts +31 -0
  77. package/lib/gitlab/merge-request.ts +20 -0
  78. package/lib/gitlab/pipeline.ts +28 -1
  79. package/lib/gitlab/project.ts +14 -5
  80. package/lib/help.ts +40 -0
  81. package/lib/jira.ts +2 -2
  82. package/package.json +18 -2
  83. package/tests/all-help.test.ts +6 -1
  84. package/tests/gitj.test.ts +1 -1
  85. package/tests/help-all.test.ts +29 -0
  86. package/bun.lockb +0 -0
package/README.md CHANGED
@@ -1,51 +1,89 @@
1
- # ya-git-jira - Yet Another Git Jira
1
+ # ya-git-jira
2
2
 
3
- This package installs several scripts that are written to be
4
- usable as `git` extensions, i.e. sub-commands of the `git` command.
5
- The extensions faciliate workflow when using `git` for source control and `jira`
6
- for issue tracking. Other similar packages exist -- thus the "yet another"
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
- This package will likely evolve over time to include more workflow cases.
8
+ ## Requirements
10
9
 
11
- ## gitj -- A test driver to use instead of `git <command>`
10
+ [Bun](https://bun.sh) (not Node.js):
12
11
 
13
- It can be useful to run these commands as if they were being invoked through
14
- `git` but using a proxy for `git` than can only execute the commands in this
15
- package.
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
- For example, to see the available top level commands, run `gitj --help`:
36
+ ### GitLab
18
37
 
19
38
  ```
20
- $ gitj help
21
- Usage: gitj [options] [command]
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
- Options:
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
- ## Command hierarchy and naming conventions
58
+ ```
59
+ git config jira.user <email>
60
+ git config gitlab.user <email>
61
+ git config confluence.user <email>
62
+ ```
34
63
 
35
- The `git jira` and `git lab` commands are arranged in a hierarchy with a structure
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
- bump
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
- The pattern `<command> list | show` that is used for `issue` will probably become
67
- a common pattern everywhere that `list` appears above. The subcommand `list` implies
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
- ## git-jira-start -- Create a new topic branch for work on an issue
111
+ ## Noteworthy commands
79
112
 
80
- #### Usage:
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
- This command is not specific to Jira. It simply reads the current branch name and creates a new branch with the version bumped, i.e. incremented.
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
- The `git bump` command will work whatever the current branch name is.
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
- $ curl -fsSL https://bun.sh/install | bash
125
+ git bump
122
126
  ```
123
127
 
124
- ## Install with any npm-compatible package manager
128
+ Appends or increments a `.vN` suffix on the current branch name:
129
+ `BUG-42-fix-the-thing` -> `.v1` -> `.v2` -> ...
125
130
 
126
- You can install ya-git-jira via `npm`, or `yarn` or `pnpm` or `bun`.
131
+ ### git api
127
132
 
128
133
  ```
129
- $ npm install -g ya-git-jira
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
- ## Configuration
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
- The `git jira` comands require your Jira `host` and `token`. The `host` is your Jira cloud service (usually `yourcompany.atlassian.net`).
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
- $ git config jira.host yourcompany.atlassian.net
145
- $ git config jira.token "<long token here>"
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
- ### GitLab
149
-
150
- Likewise the `git lab` comands require your GitLab `host` and `token`, though the default `host` `gitlab.com` will be sufficient
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
- ### Email address
156
+ ## AI agent skills
159
157
 
160
- Both `git lab` and `git jira` also need the email address associated associated with those accounts.
161
- Since `git` itself requires an email addres via the setting `user.email`, it is a reasonable default setting
162
- that will work for many users. But if necessary, you can specify the different email addresses
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
- $ git config jira.user <email-address>
168
- $ git config gitlab.user <email-address>
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
+ }