ya-git-jira 1.5.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 (89) 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 +121 -71
  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 +136 -125
  31. package/dist/bin/git-confluence-page-search.js +2079 -0
  32. package/dist/bin/{git-lab-projects.js → git-confluence-page-show.js} +294 -250
  33. package/dist/bin/{git-lab-projects-whereami.js → git-confluence-page-update.js} +300 -206
  34. package/dist/bin/git-confluence-page.js +2186 -0
  35. package/dist/bin/{git-lab-groups.js → git-confluence-space-list.js} +279 -210
  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 +144 -129
  40. package/dist/bin/git-jira-issue-show.js +144 -129
  41. package/dist/bin/git-jira-issue.js +148 -133
  42. package/dist/bin/git-jira-start.js +146 -131
  43. package/dist/bin/{git-lab-namespaces.js → git-jira-whoami.js} +214 -226
  44. package/dist/bin/git-jira.js +178 -143
  45. package/dist/bin/git-lab-group-list.js +326 -394
  46. package/dist/bin/git-lab-group.js +328 -396
  47. package/dist/bin/git-lab-merge-active.js +326 -394
  48. package/dist/bin/git-lab-merge-todo.js +326 -394
  49. package/dist/bin/git-lab-merge-train-list.js +294 -388
  50. package/dist/bin/git-lab-merge-train.js +296 -390
  51. package/dist/bin/git-lab-merge.js +335 -403
  52. package/dist/bin/git-lab-namespace-list.js +145 -135
  53. package/dist/bin/git-lab-namespace.js +147 -137
  54. package/dist/bin/git-lab-project-list.js +293 -387
  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 +328 -396
  60. package/dist/bin/git-lab-project-pipeline-log.js +2739 -0
  61. package/dist/bin/git-lab-project-pipeline.js +442 -407
  62. package/dist/bin/git-lab-project-whereami.js +297 -391
  63. package/dist/bin/git-lab-project.js +568 -403
  64. package/dist/bin/git-lab-whoami.js +149 -139
  65. package/dist/bin/git-lab.js +581 -454
  66. package/dist/bin/{git-lab-projects-list.js → gitj-install-skills.js} +226 -268
  67. package/dist/bin/gitj.js +1384 -578
  68. package/dist/index.js +379 -300
  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/git.ts +3 -3
  76. package/lib/gitlab/config.ts +5 -5
  77. package/lib/gitlab/index.ts +1 -0
  78. package/lib/gitlab/job.ts +31 -0
  79. package/lib/gitlab/merge-request.ts +20 -0
  80. package/lib/gitlab/pipeline.ts +28 -1
  81. package/lib/gitlab/project.ts +14 -5
  82. package/lib/help.ts +40 -0
  83. package/lib/jira.ts +11 -6
  84. package/lib/spawn.ts +3 -3
  85. package/package.json +18 -2
  86. package/tests/all-help.test.ts +6 -1
  87. package/tests/gitj.test.ts +1 -1
  88. package/tests/help-all.test.ts +29 -0
  89. package/bun.lockb +0 -0
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
+ }
@@ -0,0 +1,27 @@
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 whoami from './git-confluence-whoami'
7
+ import space from './git-confluence-space'
8
+ import page from './git-confluence-page'
9
+ const version = await getPackageVersion()
10
+
11
+ export function create(): Command {
12
+ const program: Command = new Command()
13
+ program
14
+ .version(version)
15
+ .name('confluence')
16
+ .description('Commands for working with Confluence')
17
+ .addCommand(whoami())
18
+ .addCommand(space())
19
+ .addCommand(page())
20
+ return program
21
+ }
22
+
23
+ export default create
24
+
25
+ if (isMain('git-confluence')) {
26
+ await create().parseAsync(Bun.argv)
27
+ }
@@ -8,7 +8,7 @@ import { isMain } from '../lib/is_main'
8
8
  const version = await getPackageVersion()
9
9
 
10
10
  function toKebab(s: string): string {
11
- return s.replace(/([a-z]+)([A-Z]+)/g, "$1_2").toLowerCase()
11
+ return s.replace(/([a-z]+)([A-Z]+)/g, "$1_$2").toLowerCase()
12
12
  .replace(/(\W+)/g, "-")
13
13
  .replace(/-$/, "")
14
14
  }
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from 'commander'
4
+ import { getPackageVersion } from '../lib/package'
5
+ import { getMyself } from '../lib/jira'
6
+ import { isMain } from '../lib/is_main'
7
+ const version = await getPackageVersion()
8
+
9
+ export function create(): Command {
10
+ const program: Command = new Command()
11
+ program
12
+ .version(version)
13
+ .name('whoami')
14
+ .description('Show the current Jira user')
15
+ .option('-v, --verbose', 'Verbose output')
16
+ .action(async (options) => {
17
+ const myself = await getMyself()
18
+ if (options.verbose) {
19
+ console.log(myself)
20
+ } else {
21
+ const { displayName, emailAddress, accountId } = myself as any
22
+ console.log({ displayName, emailAddress, accountId })
23
+ }
24
+ })
25
+ return program
26
+ }
27
+
28
+ export default create
29
+
30
+ if (isMain('git-jira-whoami')) {
31
+ await create().parseAsync(Bun.argv)
32
+ }
package/bin/git-jira.ts CHANGED
@@ -6,6 +6,7 @@ import { isMain } from '../lib/is_main'
6
6
  import start from './git-jira-start'
7
7
  import issue from './git-jira-issue'
8
8
  import issues from './git-jira-issue-list'
9
+ import whoami from './git-jira-whoami'
9
10
  const version = await getPackageVersion()
10
11
 
11
12
  export function create(): Command {
@@ -17,6 +18,7 @@ export function create(): Command {
17
18
  .addCommand(start())
18
19
  .addCommand(issue())
19
20
  .addCommand(issues())
21
+ .addCommand(whoami())
20
22
  return program
21
23
  }
22
24
 
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from 'commander'
4
+ import { getPackageVersion } from '../lib/package'
5
+ import { getMergeRequestsByBranch, type MergeRequest } from "../lib/gitlab"
6
+ import { findProject } from '../lib/gitlab/project'
7
+ import { getRemote, getCurrentBranch } from '../lib/git'
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('list')
16
+ .description('List open merge requests for a project and branch')
17
+ .option('-v, --verbose', 'Verbose output')
18
+ .option('-p, --project <path>', 'Project path (e.g. etagen-internal/eta-lib/base). Defaults to current directory remote.')
19
+ .option('-b, --branch <branch>', 'Source branch to filter on. Defaults to current branch.')
20
+ .action(async (options) => {
21
+ let projectPath = options.project
22
+ if (!projectPath) {
23
+ const remote = await getRemote()
24
+ const project = await findProject(remote)
25
+ if (!project) {
26
+ console.error(`Could not resolve project from remote: ${remote}`)
27
+ process.exit(1)
28
+ }
29
+ projectPath = project.path_with_namespace
30
+ }
31
+
32
+ const branch = options.branch || await getCurrentBranch()
33
+
34
+ const mrs: Array<MergeRequest> = await getMergeRequestsByBranch(projectPath, branch)
35
+ if (!mrs.length) {
36
+ console.error(`No open MRs for branch '${branch}' in ${projectPath}`)
37
+ process.exit(0)
38
+ }
39
+ if (options.verbose) {
40
+ console.log(mrs)
41
+ }
42
+ else {
43
+ const filtered = mrs.map(m => {
44
+ const { iid, title, web_url, source_branch, target_branch, state, draft } = m
45
+ return { iid, title, web_url, source_branch, target_branch, state, draft }
46
+ })
47
+ console.log(filtered)
48
+ }
49
+ })
50
+ return program
51
+ }
52
+
53
+ export default create
54
+
55
+ if (isMain('git-lab-project-mr-list')) {
56
+ await create().parseAsync(Bun.argv)
57
+ }
@@ -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-lab-project-mr-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('mr')
14
+ .description('Commands for working with merge requests')
15
+ .addCommand(list())
16
+ .action(() => program.help())
17
+ return program
18
+ }
19
+
20
+ export default create
21
+
22
+ if (isMain('git-lab-project-mr')) {
23
+ await create().parseAsync(Bun.argv)
24
+ }
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from 'commander'
4
+ import { getPackageVersion } from '../lib/package'
5
+ import { getPipelineJobs, type Job } from '../lib/gitlab'
6
+ import { isMain } from '../lib/is_main'
7
+
8
+ const version = await getPackageVersion()
9
+
10
+ export function create(): Command {
11
+ const program: Command = new Command()
12
+ program
13
+ .version(version)
14
+ .name('jobs')
15
+ .description('List jobs for a pipeline')
16
+ .requiredOption('-p, --pipeline <id>', 'Pipeline ID')
17
+ .option('-v, --verbose', 'Verbose output')
18
+ .action(async (options) => {
19
+ const pipelineId = parseInt(options.pipeline, 10)
20
+ if (isNaN(pipelineId)) {
21
+ console.error(`Invalid pipeline ID: ${options.pipeline}`)
22
+ process.exit(1)
23
+ }
24
+ const jobs: Array<Job> = await getPipelineJobs(pipelineId)
25
+ if (!jobs || jobs.length === 0) {
26
+ console.error(`No jobs found for pipeline ${pipelineId}`)
27
+ process.exit(1)
28
+ }
29
+ if (options.verbose) {
30
+ console.log(JSON.stringify(jobs, null, 2))
31
+ } else {
32
+ const filtered = jobs.map((j: Job) => {
33
+ const { id, name, stage, status, duration, failure_reason } = j
34
+ return { id, name, stage, status, duration, failure_reason }
35
+ })
36
+ console.log(JSON.stringify(filtered, null, 2))
37
+ }
38
+ })
39
+ return program
40
+ }
41
+
42
+ export default create
43
+
44
+ if (isMain('git-lab-project-pipeline-jobs')) {
45
+ await create().parseAsync(Bun.argv)
46
+ }
@@ -0,0 +1,47 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { Command } from 'commander'
4
+ import { getPackageVersion } from '../lib/package'
5
+ import { getLatestPipeline, type Pipeline, getPipelineJobs, type Job } from '../lib/gitlab'
6
+ import { isMain } from '../lib/is_main'
7
+
8
+ const version = await getPackageVersion()
9
+
10
+ export function create(): Command {
11
+ const program: Command = new Command()
12
+ program
13
+ .version(version)
14
+ .name('latest')
15
+ .description('Show jobs for the latest pipeline on the current branch')
16
+ .option('-v, --verbose', 'Verbose output')
17
+ .action(async (options) => {
18
+ const pipeline: Pipeline | undefined = await getLatestPipeline()
19
+ if (!pipeline) {
20
+ console.error('No pipeline found for the current branch')
21
+ process.exit(1)
22
+ }
23
+ const { id, status, ref, web_url } = pipeline
24
+ console.log(JSON.stringify({ id, status, ref, web_url }, null, 2))
25
+ const jobs: Array<Job> = await getPipelineJobs(pipeline.id)
26
+ if (!jobs || jobs.length === 0) {
27
+ console.error(`No jobs found for pipeline ${pipeline.id}`)
28
+ process.exit(1)
29
+ }
30
+ if (options.verbose) {
31
+ console.log(JSON.stringify(jobs, null, 2))
32
+ } else {
33
+ const filtered = jobs.map((j: Job) => {
34
+ const { id, name, stage, status, duration, failure_reason } = j
35
+ return { id, name, stage, status, duration, failure_reason }
36
+ })
37
+ console.log(JSON.stringify(filtered, null, 2))
38
+ }
39
+ })
40
+ return program
41
+ }
42
+
43
+ export default create
44
+
45
+ if (isMain('git-lab-project-pipeline-latest')) {
46
+ await create().parseAsync(Bun.argv)
47
+ }