Skip to content

feat: add headless/non-interactive mode for baz review#105

Closed
achuth10 wants to merge 1 commit intobaz-scm:mainfrom
achuth10:feat/headless-mode
Closed

feat: add headless/non-interactive mode for baz review#105
achuth10 wants to merge 1 commit intobaz-scm:mainfrom
achuth10:feat/headless-mode

Conversation

@achuth10
Copy link
Copy Markdown

@achuth10 achuth10 commented Mar 5, 2026

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 runHeadless flow that parses PR URLs, resolves Baz/GitHub context, streams chat/walkthrough responses, and surfaces spec-review outputs through streamToStdout. Connect the CLI review command to call runHeadless when a URL is given so automation scripts can invoke the new modes without launching the Ink UI.

  • Fix all Baz comments in this PR
TopicDetails
CLI review entry Route review invocations with a URL and action flags to runHeadless, exposing --chat, --walkthrough, and --spec-review options on the CLI.
Modified files (1)
  • src/index.ts
Latest Contributors(2)
UserCommitDate
internal-baz-ci-app[bot]chore-main-release-0.3...March 04, 2026
yuvalyacobyfeat-require-authentic...December 10, 2025
Headless review Enable headless chat, walkthrough, and spec-review flows via runHeadless, streamToStdout, and Baz/GitHub clients so scripted invocations can operate without the interactive UI.
Modified files (1)
  • src/commands/headless.ts
Latest Contributors(0)
UserCommitDate
This pull request is reviewed by Baz. Review like a pro on (Baz).

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>
@achuth10 achuth10 marked this pull request as draft March 5, 2026 06:00
@achuth10 achuth10 closed this Mar 5, 2026
Comment on lines +15 to +16
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";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Fix in Cursor

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.

Comment on lines +121 to +132
let request: CheckoutChatRequest;
if (modeName === "baz" && ctx.bazRepoId) {
request = {
mode: "baz",
repoId: ctx.bazRepoId,
prId: ctx.prId,
issue,
freeText: message,
};
} else {
request = {
mode: "tokens",
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Fix in Cursor

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.

Comment on lines +28 to 35
.action(async (url, options) => {
if (url) {
const { runHeadless } = await import("./commands/headless.js");
await runHeadless(url, options);
return;
}

try {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Fix in Cursor

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.

Comment on lines +28 to +30
throw new Error(
`Invalid GitHub PR URL: "${url}"\nExpected: https://github.com/owner/repo/pull/123`,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +96 to +99
const delta = content.slice(lastLength);
if (delta) {
process.stdout.write(delta);
lastLength = content.length;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +205 to +210
console.log(`${label} ${req.title}`);
if (req.description) {
console.log(` Description: ${req.description}`);
}
if (req.verdict_explanation) {
console.log(` Verdict: ${req.verdict_explanation}`);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment on lines +257 to +262
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);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

--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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant