@aaronshaf/ger 0.1.6 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aaronshaf/ger",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "bin": {
@@ -443,9 +443,9 @@ const formatHumanOutput = (
443
443
  console.log(`File: ${options.file}, Line: ${options.line}`)
444
444
  console.log(`Message: ${options.message}`)
445
445
  if (options.unresolved) console.log(`Status: Unresolved`)
446
- } else {
447
- console.log(`Message: ${review.message}`)
448
446
  }
447
+ // Note: For overall review messages, we don't display the content here
448
+ // since it was already shown in the "OVERALL REVIEW TO POST" section
449
449
  })
450
450
 
451
451
  // Main output formatter
@@ -66,14 +66,54 @@ const verifyCredentials = (credentials: GerritCredentials) =>
66
66
  },
67
67
  catch: (error) => {
68
68
  if (error instanceof Error) {
69
+ // Authentication/permission errors
69
70
  if (error.message.includes('401')) {
70
71
  return new ConfigError({
71
72
  message: 'Invalid credentials. Please check your username and password.',
72
73
  })
73
74
  }
75
+ if (error.message.includes('403')) {
76
+ return new ConfigError({
77
+ message: 'Access denied. Please verify your credentials and server permissions.',
78
+ })
79
+ }
80
+
81
+ // Network/hostname errors
74
82
  if (error.message.includes('ENOTFOUND')) {
75
- return new ConfigError({ message: 'Could not connect to Gerrit. Please check the URL.' })
83
+ return new ConfigError({
84
+ message: `Hostname not found. Please check that the Gerrit URL is correct.\nExample: https://gerrit.example.com (without /a/ or paths)`,
85
+ })
86
+ }
87
+ if (error.message.includes('ECONNREFUSED')) {
88
+ return new ConfigError({
89
+ message: `Connection refused. The server may be down or the port may be incorrect.\nPlease verify the URL and try again.`,
90
+ })
76
91
  }
92
+ if (error.message.includes('ETIMEDOUT')) {
93
+ return new ConfigError({
94
+ message: `Connection timed out. Please check:\n• Your internet connection\n• The Gerrit server URL\n• Any firewall or VPN settings`,
95
+ })
96
+ }
97
+ if (error.message.includes('certificate') || error.message.includes('SSL')) {
98
+ return new ConfigError({
99
+ message: `SSL/Certificate error. Please ensure the URL uses HTTPS and the certificate is valid.`,
100
+ })
101
+ }
102
+
103
+ // URL format errors
104
+ if (error.message.includes('Invalid URL') || error.message.includes('fetch failed')) {
105
+ return new ConfigError({
106
+ message: `Invalid URL format. Please use the full URL including https://\nExample: https://gerrit.example.com`,
107
+ })
108
+ }
109
+
110
+ // Generic network errors
111
+ if (error.message.includes('network') || error.message.includes('fetch')) {
112
+ return new ConfigError({
113
+ message: `Network error: ${error.message}\nPlease check your connection and the Gerrit server URL.`,
114
+ })
115
+ }
116
+
77
117
  return new ConfigError({ message: error.message })
78
118
  }
79
119
  return new ConfigError({ message: 'Unknown error occurred' })
@@ -112,64 +152,82 @@ const setupEffect = (configService: ConfigServiceImpl) =>
112
152
  try: async () => {
113
153
  console.log('')
114
154
 
115
- // Gerrit Host URL
116
- const host = await input({
117
- message: 'Gerrit Host URL (e.g., https://gerrit.example.com)',
118
- default: existingConfig?.host,
119
- })
155
+ // Enable raw mode for proper password masking
156
+ const wasRawMode = process.stdin.isRaw
157
+ if (process.stdin.isTTY && !wasRawMode) {
158
+ process.stdin.setRawMode(true)
159
+ }
120
160
 
121
- // Username
122
- const username = await input({
123
- message: 'Username (your Gerrit username)',
124
- default: existingConfig?.username,
125
- })
161
+ try {
162
+ // Gerrit Host URL
163
+ const host = await input({
164
+ message: 'Gerrit Host URL (e.g., https://gerrit.example.com)',
165
+ default: existingConfig?.host,
166
+ })
126
167
 
127
- // Password - similar to ji's pattern
128
- const passwordValue =
129
- (await password({
130
- message: 'HTTP Password (generated from Gerrit settings)',
131
- })) ||
132
- existingConfig?.password ||
133
- ''
168
+ // Username
169
+ const username = await input({
170
+ message: 'Username (your Gerrit username)',
171
+ default: existingConfig?.username,
172
+ })
134
173
 
135
- console.log('')
136
- console.log(chalk.yellow('Optional: AI Configuration'))
174
+ // Password - with proper masking and visual feedback
175
+ const passwordMessage = existingConfig?.password
176
+ ? `HTTP Password (generated from Gerrit settings) ${chalk.dim('(press Enter to keep existing)')}`
177
+ : 'HTTP Password (generated from Gerrit settings)'
137
178
 
138
- // Show detected AI tools
139
- if (availableTools.length > 0) {
140
- console.log(chalk.dim(`Detected AI tools: ${availableTools.join(', ')}`))
141
- }
179
+ const passwordValue =
180
+ (await password({
181
+ message: passwordMessage,
182
+ mask: true, // Show * characters as user types
183
+ })) ||
184
+ existingConfig?.password ||
185
+ ''
142
186
 
143
- // Get default suggestion
144
- const defaultCommand =
145
- existingConfig?.aiTool ||
146
- (availableTools.includes('claude') ? 'claude' : availableTools[0]) ||
147
- ''
148
-
149
- // AI tool command with smart default
150
- const aiToolCommand = await input({
151
- message:
152
- availableTools.length > 0
153
- ? 'AI tool command (detected from system)'
154
- : 'AI tool command (e.g., claude, llm, opencode, gemini)',
155
- default: defaultCommand || 'claude',
156
- })
187
+ console.log('')
188
+ console.log(chalk.yellow('Optional: AI Configuration'))
157
189
 
158
- // Build flat config
159
- const configData = {
160
- host: host.trim().replace(/\/$/, ''), // Remove trailing slash
161
- username: username.trim(),
162
- password: passwordValue,
163
- ...(aiToolCommand && {
164
- aiTool: aiToolCommand,
165
- }),
166
- aiAutoDetect: !aiToolCommand,
167
- }
190
+ // Show detected AI tools
191
+ if (availableTools.length > 0) {
192
+ console.log(chalk.dim(`Detected AI tools: ${availableTools.join(', ')}`))
193
+ }
168
194
 
169
- // Validate config using Schema instead of type assertion
170
- const fullConfig = Schema.decodeUnknownSync(AppConfig)(configData)
195
+ // Get default suggestion
196
+ const defaultCommand =
197
+ existingConfig?.aiTool ||
198
+ (availableTools.includes('claude') ? 'claude' : availableTools[0]) ||
199
+ ''
171
200
 
172
- return fullConfig
201
+ // AI tool command with smart default
202
+ const aiToolCommand = await input({
203
+ message:
204
+ availableTools.length > 0
205
+ ? 'AI tool command (detected from system)'
206
+ : 'AI tool command (e.g., claude, llm, opencode, gemini)',
207
+ default: defaultCommand || 'claude',
208
+ })
209
+
210
+ // Build flat config
211
+ const configData = {
212
+ host: host.trim().replace(/\/$/, ''), // Remove trailing slash
213
+ username: username.trim(),
214
+ password: passwordValue,
215
+ ...(aiToolCommand && {
216
+ aiTool: aiToolCommand,
217
+ }),
218
+ aiAutoDetect: !aiToolCommand,
219
+ }
220
+
221
+ // Validate config using Schema instead of type assertion
222
+ const fullConfig = Schema.decodeUnknownSync(AppConfig)(configData)
223
+
224
+ return fullConfig
225
+ } finally {
226
+ // Restore raw mode state
227
+ if (process.stdin.isTTY && !wasRawMode) {
228
+ process.stdin.setRawMode(false)
229
+ }
230
+ }
173
231
  },
174
232
  catch: (error) => {
175
233
  if (error instanceof Error && error.message.includes('User force closed')) {
@@ -205,11 +263,8 @@ const setupEffect = (configService: ConfigServiceImpl) =>
205
263
  Effect.catchAll((error) =>
206
264
  pipe(
207
265
  Console.error(
208
- chalk.red(
209
- `\nAuthentication failed: ${error instanceof ConfigError ? error.message : 'Unknown error'}`,
210
- ),
266
+ chalk.red(`\n${error instanceof ConfigError ? error.message : `Setup failed: ${error}`}`),
211
267
  ),
212
- Effect.flatMap(() => Console.error('Please check your credentials and try again.')),
213
268
  Effect.flatMap(() => Effect.fail(error)),
214
269
  ),
215
270
  ),
@@ -556,7 +556,8 @@ describe('comment command', () => {
556
556
 
557
557
  const output = mockConsoleLog.mock.calls.map((call) => call[0]).join('\n')
558
558
  expect(output).toContain('✓ Comment posted successfully!')
559
- expect(output).toContain('Message: Piped comment message')
559
+ // Note: The message content is no longer displayed after successful posting
560
+ // to avoid duplication with the review preview section
560
561
 
561
562
  // Restore process.stdin
562
563
  Object.defineProperty(process, 'stdin', {