feat: add live brew capture console#5
Conversation
📝 WalkthroughWalkthroughAdds a yargs-based CLI ( ChangesSocket capture CLI and typed events
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant runCli
participant runRecordCommand
participant Socket
participant writeRecordingArtifacts
participant FileSystem
User->>runCli: node socket-monitor record baseUrl
runCli->>runRecordCommand: dispatch(args)
runRecordCommand->>Socket: connect(baseUrl)
Socket-->>runRecordCommand: state/event entries
User->>runRecordCommand: quit key
runRecordCommand->>writeRecordingArtifacts: write(entries, sessionDir)
writeRecordingArtifacts->>FileSystem: raw-events.jsonl, normalized-timeline.json, chart-series.json
runRecordCommand-->>User: recording saved
sequenceDiagram
participant User
participant runConsoleCommand
participant SocketConsoleState
participant Socket
participant Terminal
User->>runConsoleCommand: console baseUrl
runConsoleCommand->>SocketConsoleState: createSocketConsoleState(baseUrl)
runConsoleCommand->>Socket: connect(baseUrl)
Socket-->>runConsoleCommand: events/state
runConsoleCommand->>SocketConsoleState: appendStreamEvent/appendStreamState
runConsoleCommand->>Terminal: buildConsolePanels + render
User->>runConsoleCommand: typed command / hotkey
runConsoleCommand->>SocketConsoleState: parseConsoleCommand -> apply action
runConsoleCommand->>Terminal: re-render panels
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (5)
packages/meticulous-client/utils/socket-payload.ts (1)
9-29: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting the shared
p/t/wfield schema.
sensorsSchemaandpayloadObjectSchemaboth independently declarep,t,wasfiniteNumberSchema. Extracting a shared fragment avoids the two definitions drifting apart if one is updated later.♻️ Proposed refactor to share the common metric fields
+const coreMetricFields = { + p: finiteNumberSchema, + t: finiteNumberSchema, + w: finiteNumberSchema, +}; + const sensorsSchema = z - .object({ - p: finiteNumberSchema, - t: finiteNumberSchema, - w: finiteNumberSchema, - }) + .object(coreMetricFields) .passthrough(); const payloadObjectSchema = z .object({ - p: finiteNumberSchema, + ...coreMetricFields, profile_time: finiteNumberSchema, sensors: sensorsSchema.optional(), - t: finiteNumberSchema, t_ext_1: finiteNumberSchema, t_tube: finiteNumberSchema, time: finiteNumberSchema, - w: finiteNumberSchema, weight_pred: finiteNumberSchema, }) .passthrough();🤖 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/meticulous-client/utils/socket-payload.ts` around lines 9 - 29, The `sensorsSchema` and `payloadObjectSchema` definitions both repeat the same `p`/`t`/`w` finite number fields, so extract that shared fragment into a reusable schema or object and compose it into both schemas. Update the `sensorsSchema` and `payloadObjectSchema` declarations in `socket-payload.ts` to use the shared metric field definition so the two shapes cannot drift apart.packages/meticulous-client/utils/socket-monitor.ts (2)
119-240: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the dead
.command()handler callbacks.Each
.command(...)call's fourth argument builds a return object (e.g. lines 133-138, 151-156, 179-186, 197-200), but.parseSync()discards handler return values — the actualSocketCliArgsis reconstructed manually afterward fromresult._[0]/result.*(lines 206-240). The handlers are therefore dead computations that duplicate the branching logic below and can silently drift out of sync with it.♻️ Proposed simplification
.command( 'console <baseUrl>', 'Run the interactive terminal console', (command) => withSharedEventFlags( command.positional('baseUrl', { demandOption: true, describe: 'Machine base URL, for example http://<machine-ip>:8080', type: 'string', }), ), - (args) => ({ - baseUrl: args.baseUrl, - command: 'console' as const, - depthLimit: args.depth, - sampleLimit: args.samples, - }), )(repeat for the other three
.command()calls), keeping only the manualresult-based branching at the end as the single source of truth.🤖 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/meticulous-client/utils/socket-monitor.ts` around lines 119 - 240, The .command() handler callbacks in parseCliArgs are dead work because parseSync() ignores their returned objects and the real SocketCliArgs is built later from result._[0] and result.*. Remove the fourth-argument handler functions from the console, monitor, record, and summarize command registrations, and keep the manual branching at the end of parseCliArgs as the only source of truth to avoid duplicated logic drifting out of sync.
906-908: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winUse
import.meta.mainfor the entry-point guard. Node 24 already supports it, so this can replace theprocess.argv[1]URL comparison entirely. If a fallback is needed,pathToFileURL(process.argv[1])is the safer legacy pattern.🤖 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/meticulous-client/utils/socket-monitor.ts` around lines 906 - 908, The entry-point check in socket-monitor.ts is using a manual process.argv[1] URL comparison instead of the newer standard guard. Update the top-level CLI bootstrap around runCli(hideBin(process.argv)) to use import.meta.main directly, and only keep a legacy fallback if absolutely needed by switching to pathToFileURL(process.argv[1]) rather than constructing a new URL from the argv string.packages/meticulous-client/utils/socket-monitor.test.ts (1)
105-120: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winBroaden
resolveRecordingPathtest coverage.Only the repo-root-relative fallback is tested. Given the PR explicitly hardens this path resolution to support running
summarizefrom different working directories, consider adding cases for: cwd-relative match,recordingsRoot-basename fallback, absolute-path passthrough, and the "no candidate exists" default-return branch.🤖 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/meticulous-client/utils/socket-monitor.test.ts` around lines 105 - 120, Broaden the resolveRecordingPath coverage in socket-monitor.test.ts by adding tests for the other resolution branches besides the repo-root-relative fallback. Use resolveRecordingPath with cwd-relative input to verify it resolves against the current working directory, add a recordingsRoot-basename fallback case, confirm absolute paths are returned unchanged, and cover the default-return path when exists() matches no candidates. Keep the new cases alongside the existing resolveRecordingPath describe block and reuse the existing exists mock pattern.packages/meticulous-client/utils/socket-console.ts (1)
282-321: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsolidate duplicated panel-layout math shared with
renderConsole.The
leftWidth/rightWidth/topHeightcomputation here is re-implemented inrenderConsole(packages/meticulous-client/utils/socket-monitor.ts, lines ~346-348) for actual terminal drawing. The two have already drifted: this file computesleftWidth = Math.max(24, Math.floor(size.width * 0.33)), whilerenderConsoleusesMath.floor((width - 1) * 0.33). SincedrawPanelre-truncates each line to its own (differently computed) width, this doesn't cause real overflow, but it can cause unnecessary/premature...truncation or wasted space by a character, and the duplication is a latent source of future drift bugs.Extract a single shared
computeConsoleLayout(size)helper (in this file, exported) and haverenderConsoleconsume it instead of recomputing independently.♻️ Proposed consolidation
+export type ConsoleLayout = { + bottomHeight: number; + bottomY: number; + leftWidth: number; + rightWidth: number; + topHeight: number; +}; + +export function computeConsoleLayout( + state: SocketConsoleState, + size: ConsoleSize, +): ConsoleLayout { + const bottomHeight = getConsoleBottomHeight(state); + const leftWidth = Math.max(24, Math.floor(size.width * 0.33)); + const rightWidth = Math.max(24, size.width - leftWidth - 1); + const topHeight = Math.max(8, size.height - bottomHeight - 1); + return { bottomHeight, bottomY: topHeight + 1, leftWidth, rightWidth, topHeight }; +}Then have both
buildConsolePanelsandrenderConsolecallcomputeConsoleLayoutinstead of recomputing the formula independently.🤖 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/meticulous-client/utils/socket-console.ts` around lines 282 - 321, The panel layout math is duplicated between buildConsolePanels and renderConsole and has already drifted, so extract a shared exported computeConsoleLayout(size) helper from socket-console.ts that returns leftWidth, rightWidth, and topHeight, then update buildConsolePanels and renderConsole to consume that helper instead of recomputing their own width/height formulas.
🤖 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/meticulous-client/utils/socket-monitor.ts`:
- Around line 462-508: Wrap the `dependencies.connectSocketIo(...)` call in
`runMonitorCommand` and `runRecordCommand` with the same try/catch style used by
`runConsoleCommand.connect`, so unreachable machines produce a clean CLI error
instead of an unhandled rejection. On failure, log/report the error through the
existing `logger`/terminal output path and stop the command gracefully; use the
`runMonitorCommand`, `runRecordCommand`, and `connectSocketIo` symbols to locate
the connection setup.
- Around line 40-42: The root path constants in socket-monitor.ts are using
URL.pathname, which can preserve encoded or platform-specific fragments and
break filesystem access. Update the DEFAULT_RECORDINGS_ROOT and
DEFAULT_REPO_ROOT definitions to convert the URL with fileURLToPath(...)
instead, and make sure the fileURLToPath helper is available in this module. Use
the existing constants as the place to apply the fix so all downstream
filesystem operations get proper native paths.
In `@packages/meticulous-client/utils/socket-recording.ts`:
- Around line 112-144: summarizeRecordingDirectory currently assumes
raw-events.jsonl always exists and contains valid JSON, so readFile and
JSON.parse can throw an opaque stack trace. Add error handling around the file
read and line parsing in summarizeRecordingDirectory to catch missing, empty, or
malformed recordings and rethrow a clear summarize-specific error. Keep the
existing RecordingSummary shape and use the summarizeRecordingDirectory and
RecordedSocketEntry flow to locate the affected path.
---
Nitpick comments:
In `@packages/meticulous-client/utils/socket-console.ts`:
- Around line 282-321: The panel layout math is duplicated between
buildConsolePanels and renderConsole and has already drifted, so extract a
shared exported computeConsoleLayout(size) helper from socket-console.ts that
returns leftWidth, rightWidth, and topHeight, then update buildConsolePanels and
renderConsole to consume that helper instead of recomputing their own
width/height formulas.
In `@packages/meticulous-client/utils/socket-monitor.test.ts`:
- Around line 105-120: Broaden the resolveRecordingPath coverage in
socket-monitor.test.ts by adding tests for the other resolution branches besides
the repo-root-relative fallback. Use resolveRecordingPath with cwd-relative
input to verify it resolves against the current working directory, add a
recordingsRoot-basename fallback case, confirm absolute paths are returned
unchanged, and cover the default-return path when exists() matches no
candidates. Keep the new cases alongside the existing resolveRecordingPath
describe block and reuse the existing exists mock pattern.
In `@packages/meticulous-client/utils/socket-monitor.ts`:
- Around line 119-240: The .command() handler callbacks in parseCliArgs are dead
work because parseSync() ignores their returned objects and the real
SocketCliArgs is built later from result._[0] and result.*. Remove the
fourth-argument handler functions from the console, monitor, record, and
summarize command registrations, and keep the manual branching at the end of
parseCliArgs as the only source of truth to avoid duplicated logic drifting out
of sync.
- Around line 906-908: The entry-point check in socket-monitor.ts is using a
manual process.argv[1] URL comparison instead of the newer standard guard.
Update the top-level CLI bootstrap around runCli(hideBin(process.argv)) to use
import.meta.main directly, and only keep a legacy fallback if absolutely needed
by switching to pathToFileURL(process.argv[1]) rather than constructing a new
URL from the argv string.
In `@packages/meticulous-client/utils/socket-payload.ts`:
- Around line 9-29: The `sensorsSchema` and `payloadObjectSchema` definitions
both repeat the same `p`/`t`/`w` finite number fields, so extract that shared
fragment into a reusable schema or object and compose it into both schemas.
Update the `sensorsSchema` and `payloadObjectSchema` declarations in
`socket-payload.ts` to use the shared metric field definition so the two shapes
cannot drift apart.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 4d2c7167-5171-4bf0-9f15-1a443abee6d4
⛔ Files ignored due to path filters (1)
yarn.lockis excluded by!**/yarn.lock,!**/*.lock
📒 Files selected for processing (16)
.gitignoredocs/plans/2026-07-01-live-brew-capture-cli.mdpackages/meticulous-client/README.mdpackages/meticulous-client/package.jsonpackages/meticulous-client/src/index.tspackages/meticulous-client/src/socket-types.test.tspackages/meticulous-client/src/socket-types.tspackages/meticulous-client/utils/socket-console.test.tspackages/meticulous-client/utils/socket-console.tspackages/meticulous-client/utils/socket-monitor.test.tspackages/meticulous-client/utils/socket-monitor.tspackages/meticulous-client/utils/socket-payload.test.tspackages/meticulous-client/utils/socket-payload.tspackages/meticulous-client/utils/socket-recording.test.tspackages/meticulous-client/utils/socket-recording.tspackages/meticulous-client/vitest.config.ts
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/meticulous-client/utils/socket-recording.test.ts (1)
240-316: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueCorrect error-path coverage for missing/malformed recordings.
Both new tests correctly exercise the
try/catchwrapping insummarizeRecordingDirectory— missingraw-events.jsonltriggers an ENOENT that's caught and wrapped, and the malformed-JSON line triggers aJSON.parsefailure that's likewise wrapped with the same error message format. Assertions match the implementation.Minor nit: the
mkdtemp/createdPaths.pushboilerplate is now repeated three times in this describe block (Lines 241-243, 289-291, 298-300). Consider extracting a smallcreateTempSessionDir()helper shared across the block to reduce repetition, though this isn't blocking.🤖 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/meticulous-client/utils/socket-recording.test.ts` around lines 240 - 316, The new tests for summarizeRecordingDirectory already cover the wrapped error paths correctly, so no logic change is needed; just reduce the repeated mkdtemp and createdPaths.push setup in this describe block by extracting a small shared helper such as createTempSessionDir and reusing it in the existing test cases.
🤖 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.
Nitpick comments:
In `@packages/meticulous-client/utils/socket-recording.test.ts`:
- Around line 240-316: The new tests for summarizeRecordingDirectory already
cover the wrapped error paths correctly, so no logic change is needed; just
reduce the repeated mkdtemp and createdPaths.push setup in this describe block
by extracting a small shared helper such as createTempSessionDir and reusing it
in the existing test cases.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 95079463-bb58-4157-a03b-69bfea9b4268
📒 Files selected for processing (7)
packages/meticulous-client/utils/socket-console.test.tspackages/meticulous-client/utils/socket-console.tspackages/meticulous-client/utils/socket-monitor.test.tspackages/meticulous-client/utils/socket-monitor.tspackages/meticulous-client/utils/socket-payload.tspackages/meticulous-client/utils/socket-recording.test.tspackages/meticulous-client/utils/socket-recording.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- packages/meticulous-client/utils/socket-monitor.test.ts
- packages/meticulous-client/utils/socket-console.test.ts
- packages/meticulous-client/utils/socket-payload.ts
- packages/meticulous-client/utils/socket-console.ts
- packages/meticulous-client/utils/socket-monitor.ts
- packages/meticulous-client/utils/socket-recording.ts
Overview
Adds the live brew capture CLI/TUI slice in
@shotlab/meticulous-clientso brew telemetry can be recorded locally, summarized offline, and inspected in a terminal console without involving the agent during the live run.Details
yargsCLI entrypoint exposingmonitor,record,summarize, andconsolepackages/meticulous-client/recordings/docs/plans/Related Tickets and/or Pull Requests
None.
Checklist
Summary by CodeRabbit
monitor,record,summarize, and an interactiveconsolemode for live terminal viewing.