Skip to content

feat(cli): add bolt logs command with tail and follow - #90

Merged
DevFlex-AI merged 1 commit into
devfrom
logs-command
Jul 31, 2026
Merged

feat(cli): add bolt logs command with tail and follow#90
DevFlex-AI merged 1 commit into
devfrom
logs-command

Conversation

@DevFlex-AI

@DevFlex-AI DevFlex-AI commented Jul 31, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #88

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Ports Crush's crush logs to bolt. The agent appends everything to one file (~/.local/share/opencode/log/opencode.log via Global.Path.log), but until now there was no CLI to read it. bolt logs prints the last 1000 lines (--tail <n> to change), and --follow/-f streams new lines. It's an instance: false command so it skips project bootstrap entirely.

Follow mode leans on the log being append-only:

// Follow by re-reading appended bytes whenever the file changes; the log
// is append-only so the previous size is always a valid resume offset.
let offset = Buffer.byteLength(text)
yield* Effect.callback<void>(() => {
  const watcher = fs.watch(path.dirname(FILE), (_, name) => {
    if (name !== path.basename(FILE)) return
    const size = fs.statSync(FILE, { throwIfNoEntry: false })?.size ?? 0
    // ... stream bytes from offset to size
  })
  return Effect.sync(() => watcher.close())
})

Watching the directory rather than the file handles the file being recreated, and a shrinking size resets the offset instead of streaming garbage. Missing log file fails with a clear message via fail().

How did you verify your code works?

  • bun run typecheck in packages/opencode
  • Ran bun src/index.ts logs --tail 2 against a seeded log file and got exactly the last two lines; missing-file path prints the error and exits non-zero

Screenshots / recordings

CLI output only.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

View with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is enabled.

Summary by CodeRabbit

  • New Features
    • Added a logs command to view recent application log entries.
    • Supports limiting output to a specified number of lines.
    • Added follow mode to display new log entries as they are written.
    • Provides a clear error when the configured log file is unavailable.

@vercel

vercel Bot commented Jul 31, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Actions Updated (UTC)
bolt-cli-app Skipped Skipped Jul 31, 2026 3:52pm

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The pull request adds a logs CLI command. The command prints trailing lines from the configured log file and can follow appended data. It handles missing files, truncation, and watcher cleanup.

Changes

Logs command

Layer / File(s) Summary
Log reading and follow behavior
packages/opencode/src/cli/cmd/logs.ts
Adds LogsCommand with configurable tail output, optional --follow streaming, missing-file errors, truncation handling, and watcher cleanup.
CLI command registration
packages/opencode/src/index.ts
Imports LogsCommand and registers it with yargs.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: thdxr

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant LogsCommand
  participant LogFile
  participant DirectoryWatcher
  CLI->>LogsCommand: Execute logs with tail and follow options
  LogsCommand->>LogFile: Validate and read trailing lines
  LogFile-->>LogsCommand: Return log content
  LogsCommand-->>CLI: Print trailing lines
  LogsCommand->>DirectoryWatcher: Watch the log directory
  DirectoryWatcher-->>LogsCommand: Report appended or truncated data
  LogsCommand->>LogFile: Read new bytes
  LogsCommand-->>CLI: Print appended log data
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the new CLI logs command and its tail and follow options.
Description check ✅ Passed The description follows the template, explains the implementation, documents verification, and marks the applicable checklist items.
Linked Issues check ✅ Passed The changes implement issue #88 by adding bolt logs with default and configurable tailing, follow mode, and log-file error handling.
Out of Scope Changes check ✅ Passed The changes are limited to the logs command and its CLI registration, which directly support issue #88.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch logs-command

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed. For unrecoverable errors, disable the tool in CodeRabbit configuration.


Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown

The following comment was made by an LLM, it may be inaccurate:

@DevFlex-AI
DevFlex-AI merged commit ede90d5 into dev Jul 31, 2026
19 of 20 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
packages/opencode/src/cli/cmd/logs.ts (2)

9-52: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

No automated tests for the new command.

The PR description mentions manual CLI verification with seeded and missing log files, but no test file is included for LogsCommand's tail-count, missing-file, and truncation-reset behavior. Consider adding a small test to lock in this behavior for future changes.

Do you want me to draft a test for the tail and missing-file paths?

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/logs.ts` around lines 9 - 52, Add focused
automated coverage for LogsCommand, exercising tail-count output, the
missing-log-file failure path, and follow-mode offset reset when the log is
truncated. Reuse the command’s existing test conventions and mock
filesystem/process output as needed, without changing the command behavior.

1-2: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Use the provided filesystem service and extract log-following logic.

FSUtil.Service extends FileSystem.FileSystem and is available in AppRuntime. Use its watch and offset-aware stream methods instead of raw filesystem APIs. Move offset, truncation, and event handling into a helper below LogsCommand.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/logs.ts` around lines 1 - 2, Update LogsCommand
to use the AppRuntime-provided FSUtil.Service rather than node:fs or node:path,
and extract log-following into a helper below LogsCommand. Implement the helper
with the service’s offset-aware stream and watch methods, preserving offset
tracking, truncation handling, and filesystem event processing.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/opencode/src/cli/cmd/logs.ts`:
- Around line 27-32: Update the log-reading flow around FILE and Effect.promise
to use a fallible promise wrapper such as Effect.tryPromise, converting read
failures into the existing fail(...) CLI error path when the file is removed or
unreadable after the existence check. Also avoid buffering the entire file in
memory for --tail; use a streaming or seek-based approach that retrieves only
the requested final lines while preserving current empty-line and tail-count
behavior.

---

Nitpick comments:
In `@packages/opencode/src/cli/cmd/logs.ts`:
- Around line 9-52: Add focused automated coverage for LogsCommand, exercising
tail-count output, the missing-log-file failure path, and follow-mode offset
reset when the log is truncated. Reuse the command’s existing test conventions
and mock filesystem/process output as needed, without changing the command
behavior.
- Around line 1-2: Update LogsCommand to use the AppRuntime-provided
FSUtil.Service rather than node:fs or node:path, and extract log-following into
a helper below LogsCommand. Implement the helper with the service’s offset-aware
stream and watch methods, preserving offset tracking, truncation handling, and
filesystem event processing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6b302e9c-0f11-4019-a7e2-3e3343a97d27

📥 Commits

Reviewing files that changed from the base of the PR and between 04ef8bf and 72fd7cb.

📒 Files selected for processing (2)
  • packages/opencode/src/cli/cmd/logs.ts
  • packages/opencode/src/index.ts

Comment on lines +27 to +32
if (!fs.existsSync(FILE)) return yield* fail(`no log file at ${FILE}`)
const text = yield* Effect.promise(() => Bun.file(FILE).text())
const lines = text.split("\n")
if (lines.at(-1) === "") lines.pop()
const count = Math.max(0, Math.floor(args.tail))
for (const line of lines.slice(-count)) console.log(line)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard against a race between the existence check and the read, and note the full-file read cost.

fs.existsSync(FILE) and Bun.file(FILE).text() are two separate operations. If the file is removed between them, Bun.file(FILE).text() rejects, and since it is wrapped in Effect.promise (which assumes the promise never rejects), the rejection surfaces as an unhandled defect instead of the CLI's normal fail(...) error path. Use Effect.tryPromise (or a similar fallible wrapper) so a missing/unreadable file after the initial check still produces a clean CLI error.

Separately, the whole log file is read into memory to compute the last --tail lines. For a long-lived, unrotated log this reads and buffers the entire file just to print a small tail.

🛡️ Proposed fix for the unhandled rejection path
-    if (!fs.existsSync(FILE)) return yield* fail(`no log file at ${FILE}`)
-    const text = yield* Effect.promise(() => Bun.file(FILE).text())
+    if (!fs.existsSync(FILE)) return yield* fail(`no log file at ${FILE}`)
+    const text = yield* Effect.tryPromise({
+      try: () => Bun.file(FILE).text(),
+      catch: () => new CliError({ message: `unable to read log file at ${FILE}`, exitCode: 1 }),
+    })
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!fs.existsSync(FILE)) return yield* fail(`no log file at ${FILE}`)
const text = yield* Effect.promise(() => Bun.file(FILE).text())
const lines = text.split("\n")
if (lines.at(-1) === "") lines.pop()
const count = Math.max(0, Math.floor(args.tail))
for (const line of lines.slice(-count)) console.log(line)
if (!fs.existsSync(FILE)) return yield* fail(`no log file at ${FILE}`)
const text = yield* Effect.tryPromise({
try: () => Bun.file(FILE).text(),
catch: () => new CliError({ message: `unable to read log file at ${FILE}`, exitCode: 1 }),
})
const lines = text.split("\n")
if (lines.at(-1) === "") lines.pop()
const count = Math.max(0, Math.floor(args.tail))
for (const line of lines.slice(-count)) console.log(line)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/opencode/src/cli/cmd/logs.ts` around lines 27 - 32, Update the
log-reading flow around FILE and Effect.promise to use a fallible promise
wrapper such as Effect.tryPromise, converting read failures into the existing
fail(...) CLI error path when the file is removed or unreadable after the
existence check. Also avoid buffering the entire file in memory for --tail; use
a streaming or seek-based approach that retrieves only the requested final lines
while preserving current empty-line and tail-count 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.

Add a bolt logs command to read the agent log

1 participant