@aaronshaf/ger 2.0.3 → 2.0.5

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.
@@ -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,24 @@ 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
+ ...(change.labels ? { labels: change.labels } : {}),
35
+ })),
36
+ }
37
+ console.log(JSON.stringify(jsonOutput, null, 2))
38
+ } else if (options.xml) {
21
39
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
22
40
  console.log(`<changes count="${changes.length}">`)
23
41
 
@@ -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"?>',
@@ -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>`)
@@ -8,6 +8,7 @@ interface VoteOptions {
8
8
  label?: string[]
9
9
  message?: string
10
10
  xml?: boolean
11
+ json?: boolean
11
12
  }
12
13
 
13
14
  /**
@@ -92,7 +93,20 @@ export const voteCommand = (
92
93
  yield* gerritApi.postReview(changeId, reviewInput)
93
94
 
94
95
  // Output success
95
- if (options.xml) {
96
+ if (options.json) {
97
+ console.log(
98
+ JSON.stringify(
99
+ {
100
+ status: 'success',
101
+ change_id: changeId,
102
+ labels,
103
+ ...(options.message ? { message: options.message } : {}),
104
+ },
105
+ null,
106
+ 2,
107
+ ),
108
+ )
109
+ } else if (options.xml) {
96
110
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
97
111
  console.log(`<vote_result>`)
98
112
  console.log(` <status>success</status>`)
@@ -7,6 +7,7 @@ import { type ConfigError, ConfigService } from '@/services/config'
7
7
 
8
8
  interface WorkspaceOptions {
9
9
  xml?: boolean
10
+ json?: boolean
10
11
  }
11
12
 
12
13
  const parseChangeSpec = (changeSpec: string): { changeId: string; patchset?: string } => {
@@ -129,7 +130,11 @@ export const workspaceCommand = (
129
130
 
130
131
  // Check if worktree already exists
131
132
  if (fs.existsSync(workspaceDir)) {
132
- if (options.xml) {
133
+ if (options.json) {
134
+ console.log(
135
+ JSON.stringify({ status: 'success', path: workspaceDir, exists: true }, null, 2),
136
+ )
137
+ } else if (options.xml) {
133
138
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
134
139
  console.log(`<workspace>`)
135
140
  console.log(` <path>${workspaceDir}</path>`)
@@ -150,7 +155,7 @@ export const workspaceCommand = (
150
155
 
151
156
  // Fetch the change ref
152
157
  const changeRef = revision.ref
153
- if (!options.xml) {
158
+ if (!options.xml && !options.json) {
154
159
  console.log(`Fetching change ${change._number}: ${change.subject}`)
155
160
  }
156
161
 
@@ -168,7 +173,7 @@ export const workspaceCommand = (
168
173
  }
169
174
 
170
175
  // Create worktree
171
- if (!options.xml) {
176
+ if (!options.xml && !options.json) {
172
177
  console.log(`Creating worktree at: ${workspaceDir}`)
173
178
  }
174
179
 
@@ -185,7 +190,21 @@ export const workspaceCommand = (
185
190
  throw new Error(`Failed to create worktree: ${error}`)
186
191
  }
187
192
 
188
- if (options.xml) {
193
+ if (options.json) {
194
+ console.log(
195
+ JSON.stringify(
196
+ {
197
+ status: 'success',
198
+ path: workspaceDir,
199
+ change_number: change._number,
200
+ subject: change.subject,
201
+ created: true,
202
+ },
203
+ null,
204
+ 2,
205
+ ),
206
+ )
207
+ } else if (options.xml) {
189
208
  console.log(`<?xml version="1.0" encoding="UTF-8"?>`)
190
209
  console.log(`<workspace>`)
191
210
  console.log(` <path>${workspaceDir}</path>`)