Skip to content

Commit 985a1f8

Browse files
committed
feat: add --quiet flag to install scripts for CI environments
Signed-off-by: leocavalcante <leo@cavalcante.dev>
1 parent 20b4b82 commit 985a1f8

File tree

5 files changed

+239
-55
lines changed

5 files changed

+239
-55
lines changed

postinstall.mjs

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const AGENTS_SOURCE_DIR = getAgentsSourceDir(packageRoot)
2828
const flags = parseCliFlags(process.argv)
2929
const DRY_RUN = flags.dryRun
3030
const VERBOSE = flags.verbose
31+
const QUIET = flags.quiet
3132

3233
/** Print usage information and exit */
3334
if (flags.help) {
@@ -38,18 +39,22 @@ Install OpenCoder agents to ~/.config/opencode/agents/
3839
Options:
3940
--dry-run Simulate installation without copying files
4041
--verbose Enable verbose output for debugging
42+
--quiet Suppress non-error output (for CI environments)
4143
--help Show this help message and exit
4244
4345
Examples:
4446
node postinstall.mjs # Install agents
4547
node postinstall.mjs --dry-run # Preview what would be installed
46-
node postinstall.mjs --verbose # Install with detailed logging`)
48+
node postinstall.mjs --verbose # Install with detailed logging
49+
node postinstall.mjs --quiet # Install silently (errors only)`)
4750
process.exit(0)
4851
}
4952

50-
/** Create logger with verbose flag */
51-
const logger = createLogger(VERBOSE)
53+
/** Create logger with verbose and quiet flags */
54+
const logger = createLogger(VERBOSE, QUIET)
5255
const verbose = logger.verbose
56+
const log = logger.log
57+
const logError = logger.error
5358

5459
/**
5560
* Main entry point for the postinstall script.
@@ -79,7 +84,7 @@ const verbose = logger.verbose
7984
*/
8085
async function main() {
8186
const prefix = DRY_RUN ? "[DRY-RUN] " : ""
82-
console.log(`${prefix}opencode-plugin-opencoder: Installing agents...`)
87+
log(`${prefix}opencode-plugin-opencoder: Installing agents...`)
8388

8489
verbose(`Package root: ${packageRoot}`)
8590
verbose(`Source directory: ${AGENTS_SOURCE_DIR}`)
@@ -90,10 +95,10 @@ async function main() {
9095
if (!existsSync(AGENTS_TARGET_DIR)) {
9196
verbose(`Target directory does not exist, creating...`)
9297
if (DRY_RUN) {
93-
console.log(`${prefix}Would create ${AGENTS_TARGET_DIR}`)
98+
log(`${prefix}Would create ${AGENTS_TARGET_DIR}`)
9499
} else {
95100
mkdirSync(AGENTS_TARGET_DIR, { recursive: true })
96-
console.log(` Created ${AGENTS_TARGET_DIR}`)
101+
log(` Created ${AGENTS_TARGET_DIR}`)
97102
}
98103
} else {
99104
verbose(`Target directory already exists`)
@@ -102,7 +107,7 @@ async function main() {
102107
// Check if source directory exists
103108
verbose(`Checking source directory exists...`)
104109
if (!existsSync(AGENTS_SOURCE_DIR)) {
105-
console.error(`${prefix} Error: Source agents directory not found at ${AGENTS_SOURCE_DIR}`)
110+
logError(`${prefix} Error: Source agents directory not found at ${AGENTS_SOURCE_DIR}`)
106111
process.exit(1)
107112
}
108113
verbose(`Source directory found`)
@@ -114,7 +119,7 @@ async function main() {
114119
verbose(`Markdown files found: ${files.length}`)
115120

116121
if (files.length === 0) {
117-
console.error(`${prefix} Error: No agent files found in agents/ directory`)
122+
logError(`${prefix} Error: No agent files found in agents/ directory`)
118123
process.exit(1)
119124
}
120125

@@ -149,7 +154,7 @@ async function main() {
149154
}
150155
verbose(` Validation passed`)
151156
successes.push(file)
152-
console.log(`${prefix}Would install: ${file} -> ${targetPath}`)
157+
log(`${prefix}Would install: ${file} -> ${targetPath}`)
153158
} else {
154159
verbose(` Copying file...`)
155160
await retryOnTransientError(() => copyFileSync(sourcePath, targetPath))
@@ -176,41 +181,44 @@ async function main() {
176181
verbose(` Validation passed`)
177182

178183
successes.push(file)
179-
console.log(` Installed: ${file}`)
184+
log(` Installed: ${file}`)
180185
}
181186
} catch (err) {
182187
const error = err instanceof Error ? err : new Error(String(err))
183188
const message = getErrorMessage(error, file, targetPath)
184189
failures.push({ file, message })
185-
console.error(`${prefix} Failed: ${file} - ${message}`)
190+
logError(`${prefix} Failed: ${file} - ${message}`)
186191
}
187192
}
188193

189194
// Print summary
190195
verbose(`Installation summary: ${successes.length} succeeded, ${failures.length} failed`)
191-
console.log("")
196+
log("")
192197
if (successes.length > 0 && failures.length === 0) {
198+
// Final success message - always show even in quiet mode
193199
console.log(
194200
`${prefix}opencode-plugin-opencoder: Successfully installed ${successes.length} agent(s)`,
195201
)
196-
console.log(`${prefix} Location: ${AGENTS_TARGET_DIR}`)
202+
log(`${prefix} Location: ${AGENTS_TARGET_DIR}`)
197203
if (!DRY_RUN) {
198-
console.log("\nTo use the autonomous development loop, run:")
199-
console.log(" opencode @opencoder")
204+
log("\nTo use the autonomous development loop, run:")
205+
log(" opencode @opencoder")
200206
}
201207
} else if (successes.length > 0 && failures.length > 0) {
208+
// Final partial success message - always show even in quiet mode
202209
console.log(
203210
`${prefix}opencode-plugin-opencoder: Installed ${successes.length} of ${files.length} agent(s)`,
204211
)
205-
console.log(`${prefix} Location: ${AGENTS_TARGET_DIR}`)
206-
console.error(`\n${prefix} ${failures.length} file(s) failed to install:`)
212+
log(`${prefix} Location: ${AGENTS_TARGET_DIR}`)
213+
logError(`\n${prefix} ${failures.length} file(s) failed to install:`)
207214
for (const { file, message } of failures) {
208-
console.error(`${prefix} - ${file}: ${message}`)
215+
logError(`${prefix} - ${file}: ${message}`)
209216
}
210217
} else {
218+
// Final failure message - always show
211219
console.error(`${prefix}opencode-plugin-opencoder: Failed to install any agents`)
212220
for (const { file, message } of failures) {
213-
console.error(`${prefix} - ${file}: ${message}`)
221+
logError(`${prefix} - ${file}: ${message}`)
214222
}
215223
process.exit(1)
216224
}

preuninstall.mjs

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ const AGENTS_SOURCE_DIR = getAgentsSourceDir(packageRoot)
2727
const flags = parseCliFlags(process.argv)
2828
const DRY_RUN = flags.dryRun
2929
const VERBOSE = flags.verbose
30+
const QUIET = flags.quiet
3031

3132
/** Print usage information and exit */
3233
if (flags.help) {
@@ -37,18 +38,22 @@ Remove OpenCoder agents from ~/.config/opencode/agents/
3738
Options:
3839
--dry-run Simulate removal without deleting files
3940
--verbose Enable verbose output for debugging
41+
--quiet Suppress non-error output (for CI environments)
4042
--help Show this help message and exit
4143
4244
Examples:
4345
node preuninstall.mjs # Remove agents
4446
node preuninstall.mjs --dry-run # Preview what would be removed
45-
node preuninstall.mjs --verbose # Remove with detailed logging`)
47+
node preuninstall.mjs --verbose # Remove with detailed logging
48+
node preuninstall.mjs --quiet # Remove silently (errors only)`)
4649
process.exit(0)
4750
}
4851

49-
/** Create logger with verbose flag */
50-
const logger = createLogger(VERBOSE)
52+
/** Create logger with verbose and quiet flags */
53+
const logger = createLogger(VERBOSE, QUIET)
5154
const verbose = logger.verbose
55+
const log = logger.log
56+
const logError = logger.error
5257

5358
/**
5459
* Main entry point for the preuninstall script.
@@ -77,7 +82,7 @@ const verbose = logger.verbose
7782
*/
7883
async function main() {
7984
const prefix = DRY_RUN ? "[DRY-RUN] " : ""
80-
console.log(`${prefix}opencode-plugin-opencoder: Removing agents...`)
85+
log(`${prefix}opencode-plugin-opencoder: Removing agents...`)
8186

8287
verbose(`Package root: ${packageRoot}`)
8388
verbose(`Source directory: ${AGENTS_SOURCE_DIR}`)
@@ -88,7 +93,7 @@ async function main() {
8893
verbose(`Checking if target directory exists...`)
8994
if (!existsSync(AGENTS_TARGET_DIR)) {
9095
verbose(`Target directory does not exist`)
91-
console.log(`${prefix} No agents directory found, nothing to remove`)
96+
log(`${prefix} No agents directory found, nothing to remove`)
9297
return
9398
}
9499
verbose(`Target directory exists`)
@@ -97,7 +102,7 @@ async function main() {
97102
verbose(`Checking if source directory exists...`)
98103
if (!existsSync(AGENTS_SOURCE_DIR)) {
99104
verbose(`Source directory does not exist`)
100-
console.log(`${prefix} Source agents directory not found, skipping cleanup`)
105+
log(`${prefix} Source agents directory not found, skipping cleanup`)
101106
return
102107
}
103108
verbose(`Source directory exists`)
@@ -108,7 +113,7 @@ async function main() {
108113
verbose(`Markdown files to remove: ${agentFiles.length}`)
109114

110115
if (agentFiles.length === 0) {
111-
console.log(`${prefix} No agent files to remove`)
116+
log(`${prefix} No agent files to remove`)
112117
return
113118
}
114119

@@ -123,18 +128,18 @@ async function main() {
123128
verbose(` File exists, removing...`)
124129
try {
125130
if (DRY_RUN) {
126-
console.log(`${prefix}Would remove: ${targetPath}`)
131+
log(`${prefix}Would remove: ${targetPath}`)
127132
removedCount++
128133
} else {
129134
await retryOnTransientError(() => unlinkSync(targetPath))
130-
console.log(` Removed: ${file}`)
135+
log(` Removed: ${file}`)
131136
removedCount++
132137
}
133138
verbose(` Successfully removed`)
134139
} catch (err) {
135140
const error = err instanceof Error ? err : new Error(String(err))
136141
const message = getErrorMessage(error, file, targetPath)
137-
console.error(`${prefix} Warning: Could not remove ${file}: ${message}`)
142+
logError(`${prefix} Warning: Could not remove ${file}: ${message}`)
138143
verbose(` Error details: ${error.message}`)
139144
}
140145
} else {
@@ -144,8 +149,10 @@ async function main() {
144149

145150
verbose(`Removal summary: ${removedCount} files removed`)
146151
if (removedCount > 0) {
152+
// Final success message - always show even in quiet mode
147153
console.log(`\n${prefix}opencode-plugin-opencoder: Removed ${removedCount} agent(s)`)
148154
} else {
155+
// Final status message - always show even in quiet mode
149156
console.log(`\n${prefix}opencode-plugin-opencoder: No agents were installed, nothing removed`)
150157
}
151158
}

src/paths.d.mts

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,8 @@ export interface CliFlags {
304304
dryRun: boolean
305305
/** Enable verbose logging output */
306306
verbose: boolean
307+
/** Suppress non-error output (for CI environments) */
308+
quiet: boolean
307309
/** Display help information */
308310
help: boolean
309311
}
@@ -314,6 +316,7 @@ export interface CliFlags {
314316
* Recognizes the following flags:
315317
* - `--dry-run`: Simulate the operation without making changes
316318
* - `--verbose`: Enable verbose logging output
319+
* - `--quiet`: Suppress non-error output (for CI environments)
317320
* - `--help`: Display help information
318321
*
319322
* @param argv - The command line arguments array (typically process.argv)
@@ -330,41 +333,51 @@ export interface CliFlags {
330333
* @example
331334
* // Parse custom arguments
332335
* const flags = parseCliFlags(["node", "script.js", "--verbose", "--dry-run"])
333-
* // flags = { dryRun: true, verbose: true, help: false }
336+
* // flags = { dryRun: true, verbose: true, quiet: false, help: false }
334337
*/
335338
export function parseCliFlags(argv: string[]): CliFlags
336339

337340
/**
338-
* Logger object with standard and verbose logging methods.
341+
* Logger object with standard, verbose, and error logging methods.
339342
*/
340343
export interface Logger {
341-
/** Log a message to console */
344+
/** Log a message to console (suppressed in quiet mode) */
342345
log: (message: string) => void
343-
/** Log a verbose message (only when verbose mode is enabled) */
346+
/** Log a verbose message (only when verbose mode is enabled, suppressed in quiet mode) */
344347
verbose: (message: string) => void
348+
/** Log an error message to stderr (never suppressed) */
349+
error: (message: string) => void
345350
}
346351

347352
/**
348-
* Creates a logger object with standard and verbose logging methods.
353+
* Creates a logger object with standard, verbose, and error logging methods.
349354
*
350-
* The logger provides two methods:
351-
* - `log(message)`: Always logs to console.log
355+
* The logger provides three methods:
356+
* - `log(message)`: Logs to console.log unless quiet mode is enabled
352357
* - `verbose(message)`: Only logs when verbose mode is enabled, prefixed with [VERBOSE]
358+
* - `error(message)`: Always logs to console.error (never suppressed)
353359
*
354360
* @param verbose - Whether verbose logging is enabled
355-
* @returns Logger object with log and verbose methods
361+
* @param quiet - Whether quiet mode is enabled (suppresses non-error output)
362+
* @returns Logger object with log, verbose, and error methods
356363
*
357364
* @example
358365
* const logger = createLogger(true)
359366
* logger.log("Installing agents...") // Always prints
360367
* logger.verbose("Source: /path/to/src") // Prints: [VERBOSE] Source: /path/to/src
368+
* logger.error("Failed to install") // Always prints to stderr
361369
*
362370
* @example
363371
* const logger = createLogger(false)
364372
* logger.log("Installing agents...") // Prints
365373
* logger.verbose("Source: /path/to/src") // Does nothing (verbose disabled)
374+
*
375+
* @example
376+
* const logger = createLogger(false, true) // quiet mode
377+
* logger.log("Installing agents...") // Suppressed (quiet mode)
378+
* logger.error("Failed to install") // Still prints (errors always shown)
366379
*/
367-
export function createLogger(verbose: boolean): Logger
380+
export function createLogger(verbose: boolean, quiet?: boolean): Logger
368381

369382
/**
370383
* Result of validating an agent file.

0 commit comments

Comments
 (0)