logs-start

SkillMonitoring & ops

Start a VivaDicta log capture. Defaults to the simulator; takes sim, device, or structured to pick a tier.

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the logs-start skill

What this skill tells your AI

The instructions your AI receives, as published by n0an/vivadicta in .agents/skills/logs-start/SKILL.md and read by ahel’s review.

Start a log capture. Stop it with logs-stop, which resolves the active tier on its own - you never have to name it again.

Argument

$ARGUMENTS selects the tier. Empty means sim.

ArgumentTierReaches extensions?Cost
(empty) or simSimulator, live unified logyes (all sim processes)none
deviceDevice, live stdoutno, main app onlynone
structuredDevice, unified log archiveyesinteractive sudo, large archive

Anything else: say what was passed, list the three, and stop. Do not guess.

When invoked with no argument, start the simulator capture as usual, then close your report with this reminder so the other tiers stay discoverable:

Started the simulator capture (default). Other tiers: /logs-start device for live device stdout (main app only), /logs-start structured for the device unified log (the only tier that sees the keyboard and other extensions).

Do not ask which tier to use instead of starting - the default is the default. Only mention the alternatives after the capture is already running.

Choosing a tier

  • sim is the default because it is free and the richest: real unified-log metadata, timestamps, levels, categories, threads.
  • device is for hardware-only bugs. Live and cheap, but see the limitation below - it cannot see the keyboard or any other extension.
  • structured is the only tier that sees extensions on hardware. Reach for it when the keyboard, share, action, or widget target is involved, or for a post-mortem .logarchive you want to reopen in Console.app.

sim

  1. mkdir -p logs
  2. Run ./scripts/launch_simulator.sh in a background/long-lived shell so log stream keeps running. It also writes logs/.sim-capture.pid naming the streaming process, which is what logs-stop reads to end this capture and nothing else.
  3. Report the logs/sim-YYYYMMDD-HHMMSS.log path the script prints.

Attaches to the already-booted Simulator; it does not relaunch the app, and it fails if none is booted. Captures Logger output filtered to subsystem == "com.antonnovoselov.VivaDicta"; raw print() is not included.

device

  1. ./scripts/launch_device.sh --check first - it resolves device and bundle id without launching. Fix whatever it reports before continuing.
  2. mkdir -p logs
  3. Run ./scripts/launch_device.sh in a background/long-lived shell. It writes logs/.device-capture.pid naming the devicectl process, which is what logs-stop reads.
  4. Report the logs/device-YYYYMMDD-HHMMSS.log path.

Uses xcrun devicectl device process launch --console with ENABLE_PRINT_LOGS=1. --terminate-existing restarts the app, so in-progress state is lost.

Limitation: main app only, no extensions

--console pipes the stdout of the single process devicectl launched. Every extension runs in its own system-spawned process, so devicectl never sees it. Their output is absent entirely and nothing says so - you get a complete-looking stream that is silently missing a target.

Measured: a capture during keyboard use carried 213 lines from VivaDicta/ and 0 from VivaDictaKeyboard/, though the keyboard logs from KeyboardViewController, KeyboardTextProcessor and VivaModeManager. Use structured for extensions.

Timestamps

Lines carry time, level and call site, because LoggerExtension's print mirror stamps them itself:

19:09:40.709 [INFO] VivaDicta/AIService.swift:666 Loaded 1 Viva Modes

That is app-side, so it only appears in builds you install. Bare, unstamped lines mean the installed binary predates the print mirror - rebuild to the device rather than changing the capture. Category and thread are still absent; stdout has no room for unified-log metadata.

structured

Starts nothing - it only records where to collect from. logs-stop does the work.

  1. Find the device:
    xcrun xctrace list devices | grep iPhone | grep -v Simulator | head -1
    
  2. mkdir -p llmtemp
  3. date '+%Y-%m-%d %H:%M:%S' > llmtemp/.device-log-start-time
    echo "<UDID>" > llmtemp/.device-log-udid
    
  4. Report the start time and UDID, and warn that logs-stop will need interactive sudo and will write a large archive.

Why this tier reaches extensions: sudo log collect --device-udid pulls the device's whole unified log, and extension lines survive the subsystem filter because LoggerExtension.swift hardcodes one subsystem across all targets:

private nonisolated let kLoggingSubsystem = "com.antonnovoselov.VivaDicta"

That constant is load-bearing. If a target is ever changed to log under its own bundle id, it disappears from these captures silently.

Concurrent captures

sim and device can run at once; logs-stop handles all active tiers, reading one marker file per tier. Do not start a second capture of the same tier - the marker is overwritten, so the first stream is orphaned and its file stops receiving the lines you expect.

The markers are why logs-stop never has to guess. It ends the tier that was actually started, instead of matching simctl spawn.*log stream against the machine and catching an unrelated log stream someone else is running.

Manual analysis

ls -lt logs/sim-*.log | head -5
grep -i "error\|fault" logs/sim-*.log
grep "\[AppState\]" logs/sim-*.log

log show logs/vivadicta_device_*.logarchive \
  --predicate 'subsystem == "com.antonnovoselov.VivaDicta"'

Related

Signals

GitHub stars
112
Forks
10
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
logs-start
Source
github.com/n0an/vivadicta
logs-start: Skill · ahel