feat: add headless/non-interactive mode for baz review#105
feat: add headless/non-interactive mode for baz review#105achuth10 wants to merge 1 commit intobaz-scm:mainfrom
Conversation
Adds support for scripting and automation via positional URL argument and action flags, enabling use in CI pipelines and editor integrations without launching the interactive Ink UI. baz review <url> --chat "message" baz review <url> --walkthrough baz review <url> --spec-review Output streams as plain text to stdout. Interactive flow is unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
| const WALKTHROUGH_INITIAL_PROMPT = | ||
| "Please walk me through this pull request. Start by showing me a very short description on what the pull request do, followed by a brief summary of the sections. Do not include any section yet in your answer"; |
There was a problem hiding this comment.
WALKTHROUGH_INITIAL_PROMPT says "what the pull request do", so the instruction reads ungrammatical and could confuse readers—can we change it to "does" so the prompt stays clear and professional?
Finding type: Naming and Typos
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
In src/commands/headless.ts around lines 15 to 16, the constant
WALKTHROUGH_INITIAL_PROMPT contains the ungrammatical phrase 'what the pull request do'.
Update the string so it reads 'what the pull request does' (e.g., change 'what the pull
request do' to 'what the pull request does') to ensure the prompt is grammatically
correct and professional.
| let request: CheckoutChatRequest; | ||
| if (modeName === "baz" && ctx.bazRepoId) { | ||
| request = { | ||
| mode: "baz", | ||
| repoId: ctx.bazRepoId, | ||
| prId: ctx.prId, | ||
| issue, | ||
| freeText: message, | ||
| }; | ||
| } else { | ||
| request = { | ||
| mode: "tokens", |
There was a problem hiding this comment.
runHeadlessChat and runHeadlessWalkthrough duplicate the baz/tokens request builder (only freeText differs), so every change to CheckoutChatRequest mode/context must be mirrored twice and risks drift; can we extract a helper that takes IssueType + prompt text and returns the correct request?
Finding types: Code Dedup and Conventions Conciseness
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
In src/commands/headless.ts around lines 121 to 174, the runHeadlessChat and
runHeadlessWalkthrough functions duplicate the conditional construction of the
CheckoutChatRequest (the only difference is freeText). Refactor by adding a new helper
function (e.g. buildCheckoutRequest(ctx, modeName, issue, freeText):
CheckoutChatRequest) that encapsulates the if (modeName === "baz" && ctx.bazRepoId)
branch and returns the correct request shape for both baz and tokens modes. Update
runHeadlessChat and runHeadlessWalkthrough to call this helper with their issue and
freeText (WALKTHROUGH_INITIAL_PROMPT for walkthrough) and remove the duplicated
request-building code.
| .action(async (url, options) => { | ||
| if (url) { | ||
| const { runHeadless } = await import("./commands/headless.js"); | ||
| await runHeadless(url, options); | ||
| return; | ||
| } | ||
|
|
||
| try { |
There was a problem hiding this comment.
Headless flags now route to headless mode only when url is provided, so baz review --chat "message" (no URL) falls into the interactive flow and silently ignores the headless options, meaning the headless operation never runs; can we error/require a PR URL when headless flags are supplied so the CLI doesn't pretend to run headless?
Finding type: Logical Bugs
Want Baz to fix this for you? Activate Fixer
Other fix methods
Prompt for AI Agents:
In src/index.ts around lines 28 to 35, the action handler for the review command (the
async (url, options) => { ... } block) currently only runs headless when url is
provided, so passing headless flags without a URL silently falls through to interactive
mode. Update this handler to validate inputs: if any headless option (options.chat,
options.walkthrough, options.specReview) is truthy and url is not provided, call
program.error (or throw an error) with a clear message like "A PR URL is required when
using headless flags (e.g. --chat, --walkthrough, --spec-review)" and exit; otherwise
proceed as before. This ensures the CLI fails fast and doesn't ignore headless flags.
| throw new Error( | ||
| `Invalid GitHub PR URL: "${url}"\nExpected: https://github.com/owner/repo/pull/123`, | ||
| ); |
There was a problem hiding this comment.
parsePRUrl throws with raw url interpolated, so malformed URLs containing credentials/tokens get echoed in stderr logs via runHeadless; can we redact userinfo/query or avoid printing the full URL in this error?
Finding type: Basic Security Patterns
- Apply fix with Baz
Prompt for AI Agents:
In src/commands/headless.ts around lines 28 to 30, the parsePRUrl function currently
throws an Error that interpolates the raw url (which may contain credentials or
sensitive query params). Change this to either omit the URL from the message or sanitize
it first: implement a small sanitizeUrl helper that strips userinfo (/*user:pass@*/),
and removes or replaces the query string with ?<REDACTED>, then use the sanitized value
in the thrown Error (e.g. `Invalid GitHub PR URL: "<sanitized>"`) so sensitive data is
not leaked to stderr. Ensure the helper is small and local to this file and update the
thrown message accordingly.
| const delta = content.slice(lastLength); | ||
| if (delta) { | ||
| process.stdout.write(delta); | ||
| lastLength = content.length; |
There was a problem hiding this comment.
streamToStdout writes remote content directly to terminal, so ANSI/OSC escape sequences from PR/model data can execute terminal control actions in headless runs; can we sanitize control characters before process.stdout.write?
Finding type: Basic Security Patterns
- Apply fix with Baz
Prompt for AI Agents:
In src/commands/headless.ts around lines 96 to 99, the onUpdate handler in
streamToStdout writes remote content directly to process.stdout, which allows ANSI/OSC
escape sequences and control characters from PR/model data to execute terminal control
actions. Modify the onUpdate logic in streamToStdout to sanitize the delta before
writing: strip ANSI CSI escape sequences (e.g. \x1b\[[0-9;]*[A-Za-z]), OSC sequences
(e.g. \x1b\]...\x07 or terminated by ESC \\), and other non-printable C0 control
characters except for newline, carriage return, and tab; you may implement a small
helper function stripAnsiAndControlChars(text: string): string or use a vetted library,
then call that on the delta and write the sanitized text. Ensure lastLength bookkeeping
still works (either track original content length as before or compute it consistently
from the unsanitized content) and add a small unit-test or example input to show escape
sequences are removed.
| console.log(`${label} ${req.title}`); | ||
| if (req.description) { | ||
| console.log(` Description: ${req.description}`); | ||
| } | ||
| if (req.verdict_explanation) { | ||
| console.log(` Verdict: ${req.verdict_explanation}`); |
There was a problem hiding this comment.
Spec-review fields (req.title, req.description, req.verdict_explanation) are printed raw with console.log, so backend-controlled escape sequences can manipulate terminal output; can we sanitize these strings before printing?
Finding type: Basic Security Patterns
- Apply fix with Baz
Prompt for AI Agents:
In src/commands/headless.ts around lines 205 to 210, the runHeadlessSpecReview loop
prints req.title, req.description, and req.verdict_explanation directly to the terminal,
which allows backend-controlled ANSI escape sequences and control characters to
manipulate terminal output. Add a small sanitization helper (e.g., remove ANSI escape
sequences and non-printable control characters, and optionally truncate long strings)
and apply it to req.title, req.description, and req.verdict_explanation before any
console.log. Ensure the helper is used consistently in that loop so all printed fields
are sanitized.
| if (options.chat) { | ||
| await runHeadlessChat(ctx, options.chat, appConfig.mode.name); | ||
| } else if (options.walkthrough) { | ||
| await runHeadlessWalkthrough(ctx, appConfig.mode.name); | ||
| } else if (options.specReview) { | ||
| await runHeadlessSpecReview(ctx, appConfig.mode.name); |
There was a problem hiding this comment.
--chat, --walkthrough, and --spec-review can be passed together but this if/else if runs only the first match, so user intent is silently ignored; can we reject multiple action flags and require exactly one action?
Finding type: Logical Bugs
- Apply fix with Baz
Prompt for AI Agents:
In src/commands/headless.ts around lines 257-262, the runHeadless function currently
uses an if/else if chain that executes only the first matching action when multiple
flags (--chat, --walkthrough, --spec-review) are passed, silently ignoring the others.
Modify runHeadless to validate the action flags up-front: count how many of
options.chat, options.walkthrough, and options.specReview are truthy, and if the count
is not exactly 1, print a clear error instructing the user to specify exactly one action
and call process.exit(1). Place this validation before the try block that dispatches to
runHeadlessChat/runHeadlessWalkthrough/runHeadlessSpecReview and remove reliance on the
silent-first-match behavior.
User description
Adds support for scripting and automation via positional URL argument and action flags, enabling use in CI pipelines and editor integrations without launching the interactive Ink UI.
baz review --chat "message"
baz review --walkthrough
baz review --spec-review
Output streams as plain text to stdout. Interactive flow is unchanged.
Generated description
Below is a concise technical summary of the changes proposed in this PR:
Enable headless review automation by adding a
runHeadlessflow that parses PR URLs, resolves Baz/GitHub context, streams chat/walkthrough responses, and surfaces spec-review outputs throughstreamToStdout. Connect the CLIreviewcommand to callrunHeadlesswhen a URL is given so automation scripts can invoke the new modes without launching the Ink UI.reviewinvocations with a URL and action flags torunHeadless, exposing--chat,--walkthrough, and--spec-reviewoptions on the CLI.Modified files (1)
Latest Contributors(2)
runHeadless,streamToStdout, and Baz/GitHub clients so scripted invocations can operate without the interactive UI.Modified files (1)
Latest Contributors(0)