@aaronshaf/ger 2.0.2 → 2.0.4

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.
@@ -13,6 +13,7 @@ const execAsync = promisify(exec)
13
13
 
14
14
  interface IncomingOptions {
15
15
  xml?: boolean
16
+ json?: boolean
16
17
  interactive?: boolean
17
18
  }
18
19
 
@@ -161,7 +162,26 @@ export const incomingCommand = (
161
162
  return
162
163
  }
163
164
 
164
- if (options.xml) {
165
+ if (options.json) {
166
+ // JSON output
167
+ const groupedChanges = groupChangesByProject(changes)
168
+ const jsonOutput = {
169
+ status: 'success',
170
+ count: changes.length,
171
+ changes: groupedChanges.flatMap(({ project, changes: projectChanges }) =>
172
+ projectChanges.map((change) => ({
173
+ number: change._number,
174
+ subject: change.subject,
175
+ status: change.status,
176
+ project,
177
+ owner: change.owner?.name ?? 'Unknown',
178
+ ...(change.owner?.email ? { owner_email: change.owner.email } : {}),
179
+ ...(change.updated ? { updated: change.updated } : {}),
180
+ })),
181
+ ),
182
+ }
183
+ console.log(JSON.stringify(jsonOutput, null, 2))
184
+ } else if (options.xml) {
165
185
  // XML output
166
186
  const xmlOutput = [
167
187
  '<?xml version="1.0" encoding="UTF-8"?>',
@@ -11,6 +11,7 @@ import { type ConfigError, type ConfigServiceImpl } from '@/services/config'
11
11
  export interface InstallHookOptions {
12
12
  force?: boolean
13
13
  xml?: boolean
14
+ json?: boolean
14
15
  }
15
16
 
16
17
  export type InstallHookErrors = ConfigError | HookInstallError | NotGitRepoError
@@ -25,7 +26,19 @@ export const installHookCommand = (
25
26
  const hookExists = yield* commitHookService.hasHook()
26
27
 
27
28
  if (hookExists && !options.force) {
28
- if (options.xml) {
29
+ if (options.json) {
30
+ yield* Console.log(
31
+ JSON.stringify(
32
+ {
33
+ status: 'skipped',
34
+ message: 'commit-msg hook already installed',
35
+ hint: 'Use --force to overwrite',
36
+ },
37
+ null,
38
+ 2,
39
+ ),
40
+ )
41
+ } else if (options.xml) {
29
42
  yield* Console.log('<?xml version="1.0" encoding="UTF-8"?>')
30
43
  yield* Console.log('<install_hook_result>')
31
44
  yield* Console.log(' <status>skipped</status>')
@@ -40,16 +53,24 @@ export const installHookCommand = (
40
53
  }
41
54
 
42
55
  if (hookExists && options.force) {
43
- if (!options.xml) {
56
+ if (!options.xml && !options.json) {
44
57
  yield* Console.log(chalk.yellow('Overwriting existing commit-msg hook...'))
45
58
  }
46
59
  }
47
60
 
48
- // Install the hook (service logs progress messages in non-XML mode)
49
- yield* commitHookService.installHook()
61
+ const quiet = options.xml === true || options.json === true
62
+ yield* commitHookService.installHook(quiet)
50
63
 
51
- // Only output XML here - service already logs success message for non-XML mode
52
- if (options.xml) {
64
+ // Only output JSON/XML here - service already logs success message for plain mode
65
+ if (options.json) {
66
+ yield* Console.log(
67
+ JSON.stringify(
68
+ { status: 'success', message: 'commit-msg hook installed successfully' },
69
+ null,
70
+ 2,
71
+ ),
72
+ )
73
+ } else if (options.xml) {
53
74
  yield* Console.log('<?xml version="1.0" encoding="UTF-8"?>')
54
75
  yield* Console.log('<install_hook_result>')
55
76
  yield* Console.log(' <status>success</status>')
@@ -5,6 +5,7 @@ import { colors } from '@/utils/formatters'
5
5
 
6
6
  interface MineOptions {
7
7
  xml?: boolean
8
+ json?: boolean
8
9
  }
9
10
 
10
11
  // ANSI color codes
@@ -17,7 +18,23 @@ export const mineCommand = (
17
18
 
18
19
  const changes = yield* gerritApi.listChanges('owner:self status:open')
19
20
 
20
- if (options.xml) {
21
+ if (options.json) {
22
+ const jsonOutput = {
23
+ status: 'success',
24
+ count: changes.length,
25
+ changes: changes.map((change) => ({
26
+ number: change._number,
27
+ subject: change.subject,
28
+ project: change.project,
29
+ branch: change.branch,
30
+ status: change.status,
31
+ change_id: change.change_id,
32
+ ...(change.updated ? { updated: change.updated } : {}),
33
+ ...(change.owner?.name ? { owner: change.owner.name } : {}),
34
+ })),
35
+ }
36
+ console.log(JSON.stringify(jsonOutput, null, 2))
37
+ } else if (options.xml) {
21
38
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
22
39
  console.log(`<changes count="${changes.length}">`)
23
40
 
@@ -4,6 +4,7 @@ import { type ApiError, GerritApiService } from '@/api/gerrit'
4
4
  interface ProjectsOptions {
5
5
  pattern?: string
6
6
  xml?: boolean
7
+ json?: boolean
7
8
  }
8
9
 
9
10
  /**
@@ -27,7 +28,9 @@ export const projectsCommand = (
27
28
 
28
29
  // Handle empty results
29
30
  if (projects.length === 0) {
30
- if (options.xml) {
31
+ if (options.json) {
32
+ console.log(JSON.stringify({ status: 'success', projects: [] }, null, 2))
33
+ } else if (options.xml) {
31
34
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
32
35
  console.log(`<projects_result>`)
33
36
  console.log(` <status>success</status>`)
@@ -40,7 +43,24 @@ export const projectsCommand = (
40
43
  }
41
44
 
42
45
  // Output results
43
- if (options.xml) {
46
+ if (options.json) {
47
+ console.log(
48
+ JSON.stringify(
49
+ {
50
+ status: 'success',
51
+ count: projects.length,
52
+ projects: projects.map((project) => ({
53
+ id: project.id,
54
+ name: project.name,
55
+ ...(project.parent ? { parent: project.parent } : {}),
56
+ ...(project.state ? { state: project.state } : {}),
57
+ })),
58
+ },
59
+ null,
60
+ 2,
61
+ ),
62
+ )
63
+ } else if (options.xml) {
44
64
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
45
65
  console.log(`<projects_result>`)
46
66
  console.log(` <status>success</status>`)
@@ -6,6 +6,7 @@ import { escapeXML, sanitizeCDATA } from '@/utils/shell-safety'
6
6
  interface RebaseOptions {
7
7
  base?: string
8
8
  xml?: boolean
9
+ json?: boolean
9
10
  }
10
11
 
11
12
  /**
@@ -30,7 +31,21 @@ export const rebaseCommand = (
30
31
  // Perform the rebase - this returns the rebased change info
31
32
  const change = yield* gerritApi.rebaseChange(resolvedChangeId, { base: options.base })
32
33
 
33
- if (options.xml) {
34
+ if (options.json) {
35
+ console.log(
36
+ JSON.stringify(
37
+ {
38
+ status: 'success',
39
+ change_number: change._number,
40
+ subject: change.subject,
41
+ branch: change.branch,
42
+ ...(options.base ? { base: options.base } : {}),
43
+ },
44
+ null,
45
+ 2,
46
+ ),
47
+ )
48
+ } else if (options.xml) {
34
49
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
35
50
  console.log(`<rebase_result>`)
36
51
  console.log(` <status>success</status>`)
@@ -57,7 +72,9 @@ export const rebaseCommand = (
57
72
  ? error.message
58
73
  : String(error)
59
74
 
60
- if (options.xml) {
75
+ if (options.json) {
76
+ console.log(JSON.stringify({ status: 'error', error: errorMessage }, null, 2))
77
+ } else if (options.xml) {
61
78
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
62
79
  console.log(`<rebase_result>`)
63
80
  console.log(` <status>error</status>`)
@@ -6,6 +6,7 @@ interface RemoveReviewerOptions {
6
6
  change?: string
7
7
  notify?: string
8
8
  xml?: boolean
9
+ json?: boolean
9
10
  }
10
11
 
11
12
  type NotifyLevel = 'NONE' | 'OWNER' | 'OWNER_REVIEWERS' | 'ALL'
@@ -23,6 +24,10 @@ const outputXmlError = (message: string): void => {
23
24
  console.log(`</remove_reviewer_result>`)
24
25
  }
25
26
 
27
+ const outputJsonError = (message: string): void => {
28
+ console.log(JSON.stringify({ status: 'error', error: message }, null, 2))
29
+ }
30
+
26
31
  class ValidationError extends Error {
27
32
  readonly _tag = 'ValidationError'
28
33
  }
@@ -39,7 +44,9 @@ export const removeReviewerCommand = (
39
44
  if (!changeId) {
40
45
  const message =
41
46
  'Change ID is required. Use -c <change-id> or run from a branch with an active change.'
42
- if (options.xml) {
47
+ if (options.json) {
48
+ outputJsonError(message)
49
+ } else if (options.xml) {
43
50
  outputXmlError(message)
44
51
  } else {
45
52
  console.error(`✗ ${message}`)
@@ -49,7 +56,9 @@ export const removeReviewerCommand = (
49
56
 
50
57
  if (reviewers.length === 0) {
51
58
  const message = 'At least one reviewer is required.'
52
- if (options.xml) {
59
+ if (options.json) {
60
+ outputJsonError(message)
61
+ } else if (options.xml) {
53
62
  outputXmlError(message)
54
63
  } else {
55
64
  console.error(`✗ ${message}`)
@@ -62,7 +71,9 @@ export const removeReviewerCommand = (
62
71
  const upperNotify = options.notify.toUpperCase()
63
72
  if (!isValidNotifyLevel(upperNotify)) {
64
73
  const message = `Invalid notify level: ${options.notify}. Valid values: none, owner, owner_reviewers, all`
65
- if (options.xml) {
74
+ if (options.json) {
75
+ outputJsonError(message)
76
+ } else if (options.xml) {
66
77
  outputXmlError(message)
67
78
  } else {
68
79
  console.error(`✗ ${message}`)
@@ -90,7 +101,24 @@ export const removeReviewerCommand = (
90
101
  results.push({ reviewer, success: true })
91
102
  }
92
103
 
93
- if (options.xml) {
104
+ if (options.json) {
105
+ const allSuccess = results.every((r) => r.success)
106
+ console.log(
107
+ JSON.stringify(
108
+ {
109
+ status: allSuccess ? 'success' : 'partial_failure',
110
+ change_id: changeId,
111
+ reviewers: results.map((r) =>
112
+ r.success
113
+ ? { input: r.reviewer, status: 'removed' }
114
+ : { input: r.reviewer, error: r.error, status: 'failed' },
115
+ ),
116
+ },
117
+ null,
118
+ 2,
119
+ ),
120
+ )
121
+ } else if (options.xml) {
94
122
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
95
123
  console.log(`<remove_reviewer_result>`)
96
124
  console.log(` <change_id>${escapeXML(changeId)}</change_id>`)
@@ -4,6 +4,7 @@ import { type ApiError, GerritApiService } from '@/api/gerrit'
4
4
  interface RestoreOptions {
5
5
  message?: string
6
6
  xml?: boolean
7
+ json?: boolean
7
8
  }
8
9
 
9
10
  /**
@@ -31,7 +32,20 @@ export const restoreCommand = (
31
32
  // Perform the restore - this returns the restored change info
32
33
  const change = yield* gerritApi.restoreChange(changeId, options.message)
33
34
 
34
- if (options.xml) {
35
+ if (options.json) {
36
+ console.log(
37
+ JSON.stringify(
38
+ {
39
+ status: 'success',
40
+ change_number: change._number,
41
+ subject: change.subject,
42
+ ...(options.message ? { message: options.message } : {}),
43
+ },
44
+ null,
45
+ 2,
46
+ ),
47
+ )
48
+ } else if (options.xml) {
35
49
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
36
50
  console.log(`<restore_result>`)
37
51
  console.log(` <status>success</status>`)
@@ -46,6 +46,7 @@ Full query syntax: https://gerrit-review.googlesource.com/Documentation/user-sea
46
46
 
47
47
  interface SearchOptions {
48
48
  xml?: boolean
49
+ json?: boolean
49
50
  limit?: string
50
51
  }
51
52
 
@@ -93,7 +94,27 @@ export const searchCommand = (
93
94
  // Group changes by project (used by both output formats)
94
95
  const groupedChanges = changes.length > 0 ? groupChangesByProject(changes) : []
95
96
 
96
- if (options.xml) {
97
+ if (options.json) {
98
+ // JSON output
99
+ const jsonOutput = {
100
+ status: 'success',
101
+ query: finalQuery,
102
+ count: changes.length,
103
+ changes: groupedChanges.flatMap(({ project, changes: projectChanges }) =>
104
+ projectChanges.map((change) => ({
105
+ number: change._number,
106
+ subject: change.subject,
107
+ status: change.status,
108
+ project,
109
+ branch: change.branch,
110
+ owner: change.owner?.name ?? 'Unknown',
111
+ ...(change.owner?.email ? { owner_email: change.owner.email } : {}),
112
+ ...(change.updated ? { updated: change.updated } : {}),
113
+ })),
114
+ ),
115
+ }
116
+ console.log(JSON.stringify(jsonOutput, null, 2))
117
+ } else if (options.xml) {
97
118
  // XML output
98
119
  const xmlOutput = [
99
120
  '<?xml version="1.0" encoding="UTF-8"?>',
@@ -7,7 +7,6 @@ import { formatDiffPretty } from '@/utils/diff-formatters'
7
7
  import { sanitizeCDATA, escapeXML } from '@/utils/shell-safety'
8
8
  import { formatDate } from '@/utils/formatters'
9
9
  import { getChangeIdFromHead, GitError, NoChangeIdError } from '@/utils/git-commit'
10
- import { writeFileSync } from 'node:fs'
11
10
 
12
11
  export const SHOW_HELP_TEXT = `
13
12
  Examples:
@@ -34,6 +33,13 @@ interface ShowOptions {
34
33
  json?: boolean
35
34
  }
36
35
 
36
+ interface ReviewerIdentity {
37
+ accountId?: number
38
+ name?: string
39
+ email?: string
40
+ username?: string
41
+ }
42
+
37
43
  interface ChangeDetails {
38
44
  id: string
39
45
  number: number
@@ -49,6 +55,24 @@ interface ChangeDetails {
49
55
  updated?: string
50
56
  commitMessage: string
51
57
  topic?: string
58
+ reviewers: ReviewerIdentity[]
59
+ ccs: ReviewerIdentity[]
60
+ }
61
+
62
+ const formatReviewerLabel = (reviewer: ReviewerIdentity): string => {
63
+ const preferredIdentity = reviewer.name || reviewer.email || reviewer.username
64
+ if (!preferredIdentity) {
65
+ if (reviewer.accountId !== undefined) {
66
+ return `Account ${reviewer.accountId}`
67
+ }
68
+ return 'Unknown Reviewer'
69
+ }
70
+
71
+ if (reviewer.email && reviewer.name && reviewer.name !== reviewer.email) {
72
+ return `${reviewer.name} <${reviewer.email}>`
73
+ }
74
+
75
+ return preferredIdentity
52
76
  }
53
77
 
54
78
  const getChangeDetails = (
@@ -58,6 +82,21 @@ const getChangeDetails = (
58
82
  const gerritApi = yield* GerritApiService
59
83
  const change = yield* gerritApi.getChange(changeId)
60
84
 
85
+ let reviewerMap = change.reviewers
86
+ const shouldFetchReviewerFallback =
87
+ reviewerMap === undefined ||
88
+ (reviewerMap.REVIEWER === undefined && reviewerMap.CC === undefined)
89
+
90
+ if (shouldFetchReviewerFallback) {
91
+ const detailedChanges = yield* gerritApi
92
+ .listChanges(`change:${change._number}`)
93
+ .pipe(Effect.catchAll(() => Effect.succeed([])))
94
+ const detailedChange =
95
+ detailedChanges.find((candidate) => candidate._number === change._number) ||
96
+ detailedChanges[0]
97
+ reviewerMap = detailedChange?.reviewers
98
+ }
99
+
61
100
  return {
62
101
  id: change.change_id,
63
102
  number: change._number,
@@ -73,6 +112,18 @@ const getChangeDetails = (
73
112
  updated: change.updated,
74
113
  commitMessage: change.subject, // For now, using subject as commit message
75
114
  topic: change.topic,
115
+ reviewers: (reviewerMap?.REVIEWER ?? []).map((reviewer) => ({
116
+ accountId: reviewer._account_id,
117
+ name: reviewer.name,
118
+ email: reviewer.email,
119
+ username: reviewer.username,
120
+ })),
121
+ ccs: (reviewerMap?.CC ?? []).map((cc) => ({
122
+ accountId: cc._account_id,
123
+ name: cc.name,
124
+ email: cc.email,
125
+ username: cc.username,
126
+ })),
76
127
  }
77
128
  })
78
129
 
@@ -154,6 +205,14 @@ const formatShowPretty = (
154
205
  console.log(
155
206
  ` Updated: ${changeDetails.updated ? formatDate(changeDetails.updated) : 'Unknown'}`,
156
207
  )
208
+ if (changeDetails.reviewers.length > 0) {
209
+ console.log(
210
+ ` Reviewers: ${changeDetails.reviewers.map((reviewer) => formatReviewerLabel(reviewer)).join(', ')}`,
211
+ )
212
+ }
213
+ if (changeDetails.ccs.length > 0) {
214
+ console.log(` CCs: ${changeDetails.ccs.map((cc) => formatReviewerLabel(cc)).join(', ')}`)
215
+ }
157
216
  console.log(` Change-Id: ${changeDetails.id}`)
158
217
  console.log()
159
218
 
@@ -227,6 +286,22 @@ const formatShowJson = async (
227
286
  branch: changeDetails.branch,
228
287
  topic: changeDetails.topic,
229
288
  owner: removeUndefined(changeDetails.owner),
289
+ reviewers: changeDetails.reviewers.map((reviewer) =>
290
+ removeUndefined({
291
+ account_id: reviewer.accountId,
292
+ name: reviewer.name,
293
+ email: reviewer.email,
294
+ username: reviewer.username,
295
+ }),
296
+ ),
297
+ ccs: changeDetails.ccs.map((cc) =>
298
+ removeUndefined({
299
+ account_id: cc.accountId,
300
+ name: cc.name,
301
+ email: cc.email,
302
+ username: cc.username,
303
+ }),
304
+ ),
230
305
  created: changeDetails.created,
231
306
  updated: changeDetails.updated,
232
307
  }),
@@ -318,6 +393,44 @@ const formatShowXml = async (
318
393
  xmlParts.push(` <email>${escapeXML(changeDetails.owner.email)}</email>`)
319
394
  }
320
395
  xmlParts.push(` </owner>`)
396
+ xmlParts.push(` <reviewers>`)
397
+ xmlParts.push(` <count>${changeDetails.reviewers.length}</count>`)
398
+ for (const reviewer of changeDetails.reviewers) {
399
+ xmlParts.push(` <reviewer>`)
400
+ if (reviewer.accountId !== undefined) {
401
+ xmlParts.push(` <account_id>${reviewer.accountId}</account_id>`)
402
+ }
403
+ if (reviewer.name) {
404
+ xmlParts.push(` <name><![CDATA[${sanitizeCDATA(reviewer.name)}]]></name>`)
405
+ }
406
+ if (reviewer.email) {
407
+ xmlParts.push(` <email>${escapeXML(reviewer.email)}</email>`)
408
+ }
409
+ if (reviewer.username) {
410
+ xmlParts.push(` <username>${escapeXML(reviewer.username)}</username>`)
411
+ }
412
+ xmlParts.push(` </reviewer>`)
413
+ }
414
+ xmlParts.push(` </reviewers>`)
415
+ xmlParts.push(` <ccs>`)
416
+ xmlParts.push(` <count>${changeDetails.ccs.length}</count>`)
417
+ for (const cc of changeDetails.ccs) {
418
+ xmlParts.push(` <cc>`)
419
+ if (cc.accountId !== undefined) {
420
+ xmlParts.push(` <account_id>${cc.accountId}</account_id>`)
421
+ }
422
+ if (cc.name) {
423
+ xmlParts.push(` <name><![CDATA[${sanitizeCDATA(cc.name)}]]></name>`)
424
+ }
425
+ if (cc.email) {
426
+ xmlParts.push(` <email>${escapeXML(cc.email)}</email>`)
427
+ }
428
+ if (cc.username) {
429
+ xmlParts.push(` <username>${escapeXML(cc.username)}</username>`)
430
+ }
431
+ xmlParts.push(` </cc>`)
432
+ }
433
+ xmlParts.push(` </ccs>`)
321
434
  xmlParts.push(` <created>${escapeXML(changeDetails.created || '')}</created>`)
322
435
  xmlParts.push(` <updated>${escapeXML(changeDetails.updated || '')}</updated>`)
323
436
  xmlParts.push(` </change>`)
@@ -3,6 +3,7 @@ import { GerritApiService } from '@/api/gerrit'
3
3
 
4
4
  interface StatusOptions {
5
5
  xml?: boolean
6
+ json?: boolean
6
7
  }
7
8
 
8
9
  export const statusCommand = (
@@ -13,7 +14,16 @@ export const statusCommand = (
13
14
 
14
15
  const isConnected = yield* apiService.testConnection
15
16
 
16
- if (options.xml) {
17
+ if (options.json) {
18
+ // JSON output
19
+ console.log(
20
+ JSON.stringify(
21
+ { status: isConnected ? 'success' : 'error', connected: isConnected },
22
+ null,
23
+ 2,
24
+ ),
25
+ )
26
+ } else if (options.xml) {
17
27
  // XML output for LLM consumption
18
28
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
19
29
  console.log(`<status_result>`)
@@ -3,6 +3,7 @@ import { type ApiError, GerritApiService } from '@/api/gerrit'
3
3
 
4
4
  interface SubmitOptions {
5
5
  xml?: boolean
6
+ json?: boolean
6
7
  }
7
8
 
8
9
  /**
@@ -65,7 +66,21 @@ export const submitCommand = (
65
66
  reasons.push('Change does not meet submit requirements')
66
67
  }
67
68
 
68
- if (options.xml) {
69
+ if (options.json) {
70
+ console.log(
71
+ JSON.stringify(
72
+ {
73
+ status: 'error',
74
+ change_number: change._number,
75
+ subject: change.subject,
76
+ submittable: false,
77
+ reasons,
78
+ },
79
+ null,
80
+ 2,
81
+ ),
82
+ )
83
+ } else if (options.xml) {
69
84
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
70
85
  console.log(`<submit_result>`)
71
86
  console.log(` <status>error</status>`)
@@ -93,7 +108,20 @@ export const submitCommand = (
93
108
  // Change is submittable, proceed with submission
94
109
  const result = yield* gerritApi.submitChange(changeId)
95
110
 
96
- if (options.xml) {
111
+ if (options.json) {
112
+ console.log(
113
+ JSON.stringify(
114
+ {
115
+ status: 'success',
116
+ change_number: change._number,
117
+ subject: change.subject,
118
+ submit_status: result.status,
119
+ },
120
+ null,
121
+ 2,
122
+ ),
123
+ )
124
+ } else if (options.xml) {
97
125
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
98
126
  console.log(`<submit_result>`)
99
127
  console.log(` <status>success</status>`)
@@ -22,6 +22,7 @@ Note: When no change-id is provided, it will be auto-detected from the HEAD comm
22
22
 
23
23
  interface TopicOptions {
24
24
  xml?: boolean
25
+ json?: boolean
25
26
  delete?: boolean
26
27
  }
27
28
 
@@ -52,7 +53,15 @@ export const topicCommand = (
52
53
  if (options.delete) {
53
54
  yield* gerritApi.deleteTopic(resolvedChangeId)
54
55
 
55
- if (options.xml) {
56
+ if (options.json) {
57
+ console.log(
58
+ JSON.stringify(
59
+ { status: 'success', action: 'deleted', change_id: resolvedChangeId },
60
+ null,
61
+ 2,
62
+ ),
63
+ )
64
+ } else if (options.xml) {
56
65
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
57
66
  console.log(`<topic_result>`)
58
67
  console.log(` <status>success</status>`)
@@ -69,7 +78,15 @@ export const topicCommand = (
69
78
  if (topic !== undefined && topic.trim() !== '') {
70
79
  const newTopic = yield* gerritApi.setTopic(resolvedChangeId, topic)
71
80
 
72
- if (options.xml) {
81
+ if (options.json) {
82
+ console.log(
83
+ JSON.stringify(
84
+ { status: 'success', action: 'set', change_id: resolvedChangeId, topic: newTopic },
85
+ null,
86
+ 2,
87
+ ),
88
+ )
89
+ } else if (options.xml) {
73
90
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
74
91
  console.log(`<topic_result>`)
75
92
  console.log(` <status>success</status>`)
@@ -86,7 +103,20 @@ export const topicCommand = (
86
103
  // Handle get operation (default)
87
104
  const currentTopic = yield* gerritApi.getTopic(resolvedChangeId)
88
105
 
89
- if (options.xml) {
106
+ if (options.json) {
107
+ console.log(
108
+ JSON.stringify(
109
+ {
110
+ status: 'success',
111
+ action: 'get',
112
+ change_id: resolvedChangeId,
113
+ topic: currentTopic || null,
114
+ },
115
+ null,
116
+ 2,
117
+ ),
118
+ )
119
+ } else if (options.xml) {
90
120
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
91
121
  console.log(`<topic_result>`)
92
122
  console.log(` <status>success</status>`)