Skip to content

Add previewsmcp logs subcommand and promote Debugging section#133

Merged
obj-p merged 2 commits intomainfrom
docs/readme-logs-debugging
Apr 22, 2026
Merged

Add previewsmcp logs subcommand and promote Debugging section#133
obj-p merged 2 commits intomainfrom
docs/readme-logs-debugging

Conversation

@obj-p
Copy link
Copy Markdown
Owner

@obj-p obj-p commented Apr 21, 2026

Summary

  • New previewsmcp logs subcommand — prints the last N lines of ~/.previewsmcp/serve.log (default 100), or streams new lines with -f/--follow. Creates the log file if absent so it works before the daemon has ever started.
  • Promoted ## Debugging to a top-level README section (previously a sub-subsection inside ### Daemon model), leading with the new subcommand and keeping tail -F as the raw-file fallback.
  • Added previewsmcp logs -f to the "Enumeration and diagnostics" command list under ## Usage for discoverability.

Motivation

Users have no documented diagnostic path when run hangs during an iOS host build. The daemon stderr has always been redirected to ~/.previewsmcp/serve.log, and build-phase progress streams to the CLI's stderr via MCP log notifications, but none of it was surfaced in the README and there was no one-command shortcut.

Implementation notes

LogsCommand shells out to /usr/bin/tail rather than reimplementing log-follow in Swift. Process is used directly (not AsyncProcess, which buffers) with stdio wired to the CLI's own TTY handles so tail's output streams live.

Signal handling is the only non-trivial piece: after Process.run(), the parent installs DispatchSource.makeSignalSource for SIGINT/SIGTERM on a background queue (because waitUntilExit blocks main) and forwards them to the tail child. Two scenarios covered:

  • Ctrl-C in a terminal — the tty delivers SIGINT to the foreground process group, so tail receives it on its own; the forward is a redundant, idempotent poke.
  • kill -INT <parent-pid> from a supervisor/script — the signal arrives only at the parent; without forwarding, tail would be orphaned to launchd.

Flag defaults follow tail/kubectl logs/docker logs/journalctl convention: snapshot by default, explicit -f to stream.

Test plan

  • swift build — clean build
  • .build/debug/previewsmcp logs --help — expected flags present, discussion mentions the log path and $PREVIEWSMCP_SOCKET_DIR
  • logs with seeded log + -n 5 — returns the last 5 lines, exit 0
  • logs when log file is missing — creates the file, exits 0, empty stdout (no banner)
  • logs --follow — streams an appended sentinel line within 10s and exits within 5s of SIGINT to the parent PID (covered by LogsCommandTests.followStreamsAndExitsOnSIGINT)
  • PREVIEWSMCP_SOCKET_DIR redirect honored — verified implicitly via DaemonTestLock-scoped tests targeting an isolated dir
  • swift test --filter CLIIntegrationTests — all 66 tests across 12 suites pass

Out of scope (follow-ups worth filing)

  • --verbose / --debug flag threading through existing commands
  • Live subprocess stdout/stderr streaming during build phases (the "hang with no output" gap — when xcodebuild or swiftc is the one stuck, nothing new lands in serve.log until the child exits)

🤖 Generated with Claude Code

obj-p added a commit that referenced this pull request Apr 21, 2026
…rate signal comments

Review findings from PR #133 that are worth landing rather than
deferring:

- Extract `PipeBuffer` (née StderrBuffer) to its own file so both
  RunCommandTests and the new LogsCommandTests share one
  thread-safe pipe accumulator.
- Replace the 300ms magic sleep in the follow-mode test with a
  poke-on-every-poll pattern: rewrite the sentinel on each iteration
  until the stream observes it. Removes the dependency on
  subprocess-startup timing.
- Expand the LogsCommand signal-handling comments to document (a)
  the narrow benign race between process.run() and signal(SIG_IGN)
  and (b) why restoring SIG_DFL rather than the prior disposition
  is safe for this leaf command.
- Call out in the README that `previewsmcp logs` honors
  `PREVIEWSMCP_SOCKET_DIR` only when the variable is actually set
  for the `logs` invocation — an easy footgun when switching
  terminals during a debug session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@obj-p obj-p enabled auto-merge (squash) April 21, 2026 13:57
obj-p and others added 2 commits April 21, 2026 21:25
…ADME

When `run` appears stuck during an iOS host build, consumers had no
documented path to diagnose where the failure lives. The daemon's stderr
has always been redirected to `~/.previewsmcp/serve.log`, and progress
messages stream to the CLI's stderr via MCP log notifications, but none
of this was surfaced in the README and there was no one-command shortcut.

- `previewsmcp logs` prints the last N lines of the daemon log; `-f`
  follows. Creates the log file if absent so it works before the daemon
  has ever started. Forwards SIGINT/SIGTERM to the `tail` child so
  direct signals to the parent don't orphan it to launchd.
- `## Debugging` is now a top-level README section (previously buried as
  a sub-subsection inside `### Daemon model`), leading with the new
  subcommand and keeping `tail -F` as the raw-file fallback.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rate signal comments

Review findings from PR #133 that are worth landing rather than
deferring:

- Extract `PipeBuffer` (née StderrBuffer) to its own file so both
  RunCommandTests and the new LogsCommandTests share one
  thread-safe pipe accumulator.
- Replace the 300ms magic sleep in the follow-mode test with a
  poke-on-every-poll pattern: rewrite the sentinel on each iteration
  until the stream observes it. Removes the dependency on
  subprocess-startup timing.
- Expand the LogsCommand signal-handling comments to document (a)
  the narrow benign race between process.run() and signal(SIG_IGN)
  and (b) why restoring SIG_DFL rather than the prior disposition
  is safe for this leaf command.
- Call out in the README that `previewsmcp logs` honors
  `PREVIEWSMCP_SOCKET_DIR` only when the variable is actually set
  for the `logs` invocation — an easy footgun when switching
  terminals during a debug session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@obj-p obj-p force-pushed the docs/readme-logs-debugging branch from 1c0d09a to 030fa72 Compare April 22, 2026 01:26
@obj-p obj-p merged commit fbeea66 into main Apr 22, 2026
4 checks passed
@obj-p obj-p deleted the docs/readme-logs-debugging branch April 22, 2026 01:58
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