Skip to content

v0.7.3

Choose a tag to compare

@github-actions github-actions released this 26 Aug 06:41
· 234 commits to main since this release

Before you double-click

This build is not signed or notarized, so macOS blocks the first launch. Allow it once:

  • macOS 15 Sequoia and later — double-click, dismiss the warning, then open
    System Settings ▸ Privacy & Security and click Open Anyway. Apple removed the
    Control-click shortcut for unsigned apps in macOS 15, so right-clicking does not help.
  • macOS 13–14 — right-click the app ▸ Open, then confirm.

Only the first launch asks.

Markdown and HTML leave the core: both formats are now drawn by a plugin that renders
diagrams and mathematics on your Mac, on every surface that shows a file — the viewer, the preview
panel, Quick View and the gallery. Searching inside archives reaches every archive the app can open,
says what it could not read, and no longer leaves extracted copies behind. Amazon S3 joins the drives,
the assistant summarises whole files and looks through your disk for one, and the documentation
website stops reporting its readers to anyone.

Added

  • Searching inside archives now means every archive the app can open. Turning on Search inside
    archives
    used to descend into the zip family only, so a .tar.gz holding a config file with the
    search term reported nothing found — even though pressing Enter opens that same file. Search, the
    panel's Enter, unpack, Test Archive and archive reload now share one authority for "is this an
    archive and who opens it", so tar, tar.gz/tgz, 7z, rar, xz, zst, iso, cpio, squashfs, single-file
    .gz streams, split zips (name.zip.001) and anything a packer plugin or the Extra archive
    extensions
    setting adds are all searched wherever they are browsable. Two long-standing gaps
    closed with it: unpacking a plugin-only format (Alt+F9 on a 7z) used to fail with "select an
    archive first" on a file the panel opens happily, and reloading an archive could silently swap a
    plugin's mount for the built-in zip reader.

  • A search that could not look somewhere now says so. Archives that were unreadable, encrypted,
    over the size ceiling or nested deeper than four levels used to be skipped in silence, which reads
    exactly like "the term is not in there". The status line now ends with how many were not searched.

  • Results found inside archives are usable. F3 opened them with a beep and Feed to Listbox
    dropped them without a word; both now work, as does copying one out with F5, because a hit carries
    the archive chain it came from instead of a path string nothing could resolve.

  • Amazon S3 and S3-compatible storage as a drive. Net ▸ Amazon S3 Connect… connects to Amazon
    S3, MinIO, Ceph, Cloudflare R2, Wasabi, Backblaze B2 or DigitalOcean Spaces, and the bucket list
    becomes the top level of a panel with each bucket a folder below it. Reading, writing, new folders
    and buckets, deleting, renaming and moving all work, and copies happen on the server rather than
    through your Mac. Profiles from the AWS command line are offered if you have them; secret keys go
    into the Keychain. A remembered connection becomes a chip in the drive bar that connects when you
    click it. Storage Class and ETag are available as panel columns.

    It is a plugin, so you can turn it off or remove it in Configuration ▸ Plugins…. The help topic
    Amazon S3 and S3-compatible storage describes what to expect of it — including that a bucket
    cannot be renamed, that an archived object must be restored before it can be read, and that unlike a
    disk, every request to a paid service costs money.

  • You can agree to part of the assistant's plan. When a plan covers several files — renaming a
    folder full of them, clearing out your Downloads — each one is a ticked line above the buttons. Untick
    what you want left alone and press Confirm & run: the rest goes ahead and the unticked files are
    not touched. Until now the only answers were all and nothing, so wanting all-but-three meant rejecting
    the plan and describing the exception in words for the assistant to get right on a second try.
    Unticking everything is the same as cancelling, and it says so rather than reporting that it did
    nothing.

  • Ask the assistant to find a file and it looks through your whole disk. "Find the PDF invoice from
    last month"
    , "where are all my node_modules folders?", "which file mentions the Aachen contract?"
    — including words inside files, which the ordinary search can only do once you point it at a
    folder. It uses the index macOS already keeps, so there is nothing to build and no waiting for it to
    catch up, and it tells you where it looked: your home folder, the whole computer, or just the folder a
    panel is showing. Two honest limits: macOS keeps some places out of its index, so "nothing found" is
    not proof a file is absent, and a file created moments ago may not be indexed yet — Find Files
    walks the folders itself and will still see it.

  • One click puts one panel's folder in the other, and says which side. Go ▸ Left = Right shows
    the right panel's folder on the left, Go ▸ Right = Left does the reverse, and both are on the
    button bar by default. Target = source (Ctrl+=) has always done this relative to whichever panel is
    active — which is the wrong shape for a button, because the same click then means two different
    things depending on where the focus happens to be. These name the side outright. An existing button
    bar gains the two buttons once; remove them and they stay removed.

  • The whole empty space at the right of a path bar opens the path for typing. Not just the pencil,
    which is eighteen points wide. Clicking a folder in the breadcrumb still goes there, and the narrow
    gaps between folder names still do nothing, so a click that just misses a name is a miss rather than
    a mode change. A click on a path bar now also makes that panel the active one.

  • The assistant summarises a whole file, however long. Its on-device model takes in a few
    thousand words at a time, so "summarise this report" failed outright on anything past about six
    kilobytes — measured on this machine: a four-kilobyte slice is answered, an eight-kilobyte one is
    refused, and the assistant was asking for sixty-four. It now reads a long file in slices and folds
    the slice summaries into one, so the length of a file costs time instead of failing. A 38 KB report
    comes back summarised, including what its last page says.

  • What the assistant did, and taking it back. Actions ▾ in the chat shows every change it
    made — what was asked of it, how it turned out, and the attempts the autonomy setting refused —
    and takes back the last change that has an inverse: a rename is renamed back, a move is moved back.
    Where nothing can be taken back the list says why, rather than offering a button that would lie.
    An external agent connected over MCP writes to the same log. You can also just ask the assistant
    to undo it.

  • An "AI ▸" action over a whole selection. Mark forty files and the action runs over all of
    them, one after another, with progress in the status line; Stop ends the run between files. This
    is the part a two-panel file manager was missing: the assistant could only ever act on the file
    under the cursor.

  • Answers you can act on. The chat renders the model's Markdown: a table is a table, a fenced
    block is a code block, a list is a list, and a path is clickable. (Make a table produces a
    well-formed Markdown table by construction, and the chat used to show it as rows of pipe
    characters.) Suggest a name now ends in a Rename button carrying the proposed name —
    pressing it is the approval, so nothing is asked twice.

  • Your own "AI ▸" actions. What each action asks the model is a file you can edit
    (aichat/skills.json, aichat/folder-skills.json), written out with the built-in wording on
    first run — and an action you invent is a real command: name plugin.ai.skill.<your-id> in the
    user menu, on the button bar or on a keyboard shortcut and it runs. A plugin can now declare that
    a command family is open to ids it does not itself list, which is what makes this possible without
    the host having to load a plugin to find out what it offers. Name an id that does not exist and
    the assistant says so rather than doing nothing.

  • An AI Summary panel column. It shows the first line of the summary for each file the assistant
    has already summarised, and stays empty for the rest — the column shows work already done and
    never starts a model itself. The plugin's other column, which detects a text file's language
    without any model, is now called Language rather than "AI Language".

  • The Git panel and its windows follow the colour scheme. In every dark palette the Git panel in the
    side panel was a white rectangle with white labels on it; the history, blame, branches, conflict and rebase
    windows ignored the scheme entirely. They now take their colours from the app — and follow a change while
    they are open.

  • PDFs and Word documents render in the preview, with zoom. The side panel's preview, Quick View and
    the info page now draw PDFs themselves — page by page, with the same zoom buttons a picture has (zoom in,
    zoom out, actual size, fit) — and show Word, OpenDocument and RTF documents as formatted, selectable text.
    Everything else is still previewed by macOS Quick Look. Reported as "PDF and DOCX are no longer rendered":
    what those formats went through before renders outside the application, where nothing inside it can tell a
    rendered page from a blank one — so this also makes the preview something that can be checked. If you
    prefer the system's preview for everything, switch Render PDFs and documents in the preview off in
    Configuration ▸ Edit/View.

  • Plugin column headers are translated, and can carry an icon. Git Status and the other plugin columns
    showed English headers in every language; they now use the plugin's own translations — while your saved
    column sets keep working, because only the header changed and not the column's identity. Git's status
    column also shows a real icon next to the word instead of a text glyph.

  • Blame in the editor's gutter. Git ▸ Blame in the Editor opens the file and writes who last touched
    each line next to the line numbers, with the commit, author, date and subject on hover; clicking a line
    opens that commit against its parent in the compare window. The mechanism is a new host service, so any
    plugin can annotate lines this way — coverage, a linter, anything per line.

  • The bundled plugins are the pitch now, not a footnote. Seventeen plugins ship inside the
    app and the landing page said so in one run-on sentence, two thirds of the way down, under a
    heading about the SDK — while the feature card above it advertised the ability to write
    plugins rather than the fourteen that are switched on the moment you launch. There is now a
    showcase near the top: what each one does in a line, the three that are off by default marked
    as such, the plugins window as proof that every one of them is there and individually
    switchable, and Disk Map, Git and the Uninstaller shown rather than described. The SDK keeps
    its own section further down, where it belongs.

  • Card grids were choosing their column count by font size. Every grid on the landing page
    used a rem track minimum, and Material sets html { font-size: 125% } — so 1rem is 20px on
    a default browser and was measured at 24px on another. A minmax(14rem, …) meant for three
    columns silently rendered two in an 826px content column. The minimums are in px now; the
    gaps stay in rem, because spacing should scale with type.

  • The terminal and the log viewer are shown, not just listed. Two of the most visual bundled
    plugins had no screenshot anywhere. Both are captured now — the terminal with the shell sitting
    in the folder the panel above it shows, the log viewer with a service log colour-coded by level
    — and the showcase strip on the landing page carries five pictures instead of three. Both help
    pages embed theirs too, in all nineteen languages, so they are no longer the only plugin pages
    without a picture.

Changed

  • The AI assistant now arrives switched off. Turn it on in Configuration ▸ Plugins…; leave it
    off and nothing about it appears — no AI ▸ menu, no chat, no column. It is in beta, and it can rename,
    move and delete files and run shell commands for you, each behind a plan you approve. That is a lot of
    reach to hand a new feature by default, and it is the same standard the filesystem-image and
    decompiler plugins already ship under. Without an API key it works entirely on your Mac, so this is
    about the reach and not about anything leaving the machine. The AI Column plugin, which fills a
    panel column from the same model, arrives switched off with it.

  • Markdown and HTML are now drawn by a plugin, and can do much more. Press F3 on a .md or
    .html file and you get nested lists, task lists with their boxes, tables with alignment,
    reference links and strikethrough — a real Markdown parser rather than an approximation. Diagrams
    written as ```mermaid blocks are drawn, and mathematics written between dollar signs is
    typeset. Both happen on your Mac: the engines ship inside the plugin, nothing is downloaded,
    and no part of your document is sent anywhere. A document with neither loads neither.

    The same rendering now appears in the preview panel and Quick View, so a preview and a full
    view of one file no longer disagree, and the gallery shows a small picture of a Markdown
    file's beginning instead of a generic icon. Apple's own Quick Look (Cmd+Y) is unchanged — that
    panel belongs to macOS.

    It is a plugin called Markdown and HTML, so you can switch it off in
    Configuration ▸ Plugins…; both formats then open as text, with the outline and syntax colouring
    intact. Its own settings page turns diagrams and mathematics on or off separately, sets the size
    above which a file opens as text, and says which engine version is in use and where it came from.
    If you need a different build of Mermaid or KaTeX, you can drop it in a folder and it is used
    instead.

    Two things it will not do, on purpose. A rendered page loads nothing over the network: an image
    whose address begins with http stays blank, because fetching it would tell that server when you
    opened the file. And a document's own scripts never run — HTML inside a Markdown file is shown as
    text, and an .html file is displayed with scripting switched off.

  • Code blocks in a rendered Markdown file are now coloured. Press F3 on a .md file and the
    Rendered view keeps the language written on each fence — ```swift, ```python —
    and colours comments, strings, numbers and keywords the way the editor does. A fence naming
    something the app has no lexer for, such as mermaid, stays a plain block rather than failing. The
    rendered page still loads nothing from the network.

  • The assistant is offered only the tools it is allowed to use. Under "read-only" the write and
    delete tools are no longer offered and then refused: for a model with a few thousand tokens of
    context, a round of attempts that can only fail is the budget for the real answer. Memory
    (remember/recall) and the semantic search reached only the cloud path before — the on-device
    default, which is what most people run, had no memory at all.

  • "Which file is about X" finds it. The semantic search ranked file names only, with an
    English-only embedding, so a German query fell back to counting shared words and the "semantic"
    part quietly did nothing. It now reads the beginning of each file too, follows the language of the
    query, and returns what is close to the best match instead of the whole folder.

  • A copy or a move is reported as done when it is done. Both tools queued the transfer and
    returned immediately, so the assistant announced a copy before a byte had moved — and a plan of
    several steps ran against a queue that had not started. They now wait for the transfer, which is
    still an ordinary background job in the Transfer Manager.

  • Reading, hashing and searching no longer block the window. The automation tools ran on the
    main thread, and "find duplicates" mapped every file into memory there. Hashing is streamed now,
    and the file-system tools run off the main actor.

  • A model change in Settings takes effect at once. The chat kept the provider and the system
    prompt it was built with, so switching to a cloud model looked like it did nothing until the panel
    happened to be rebuilt.

  • The MCP server follows the autonomy setting. It was fixed at "confirm changes", so "read-only"
    on the AI page held for the assistant in the window and not for an external agent on the socket —
    both of which are configured on that same page.

  • The Git menu reads like a menu. Inside a submenu already called Git, every entry said "Git" again:
    Git ▸ Git Status…. The titles are now Status…, Stage, Commit…, Push, Pull, Panel, Diff…,
    History… next to File History…, Branches, Stashes & Tags… and Blame (list)… beside the new
    Blame in the Editor — and the entries are in a sensible order instead of the order they were added in.

Fixed

  • Two libraries the app had started using were missing from its attributions. The Markdown
    plugin's parser — Swift Markdown, and the cmark-gfm it is built on — were pinned as dependencies
    but named nowhere, so Help ▸ Open Source & Third-Party Software… did not list them and their
    licences were not shipped. Both are there now, with their full texts. The generator had been
    looking in one place for the licence files while these two are resolved into another, and it warned
    about exactly that on every build without anyone acting on the warning.

  • A large folder now fills in as it loads. A directory with thousands of entries — a big folder on
    disk, an FTP or SFTP listing, an S3 bucket — showed an empty panel until the very last entry had
    arrived. The rows now appear as they come in. Navigating away while a folder is still loading no
    longer lets the slower listing win, and if a listing fails partway through, the panel goes back to
    showing the folder it was actually in rather than half of the one it could not open.

  • The same bytes now give the same answer inside an archive as on disk. Content search stopped
    after 16 MB of an archive member while a loose file had no such limit, so a match further into an
    archived log was reported as no match at all. Members past that point are extracted and searched
    exactly as local files.

  • Searching no longer leaves extracted archives behind. Descending into a nested archive wrote a
    temp directory that nothing ever removed; the extraction now belongs to the descent that made it,
    and an archive mount — built-in or plugin-backed — cleans up everything it extracted when it goes
    away. Anything earlier builds already left behind is cleared out at the next launch.

  • Opening an archive with many files in one folder is no longer quadratic. A tar holding 20,000
    files in a single directory took 30 seconds to open — an unpacked source tree or a node_modules
    tarball is exactly that shape. Under three seconds now. This one is older than the archive-search
    work and surfaced only once its performance budgets were written.

  • A search says which archives it could not read, not just how many. The new Details… button
    in Find Files lists each one with the reason: it could not be opened, it is password protected
    (the names inside were searched, the contents were not), it is larger than a search opens, or it
    is nested deeper than a search descends. The button stays hidden when a run had nothing to report.

  • A password-protected archive is reported instead of quietly passed over. Its member names were
    always searched and its contents never were, and nothing said so.

  • A condition on the Plugins tab no longer discards every result found inside an archive. The
    condition was checked against the result's displayed path, which for an archive hit is not a file,
    so those rows were dropped without a word as soon as any condition was set.

  • Editing a file inside an archive says so instead of losing the change. F4 opened a copy that
    Save wrote into while the archive stayed as it was, and nothing said so. Copy the file out with F5
    and edit the copy. Editing over SFTP, FTP and WebDAV still writes back as before, and a branch view
    still edits the real file.

  • A long search can be stopped while it is opening an archive. Cancelling checked only between
    archives, so a search that had just started on a large one had to see it through first.

  • Entering an archive twice no longer reads it twice. An open archive is remembered — up to 32 of
    them, and only while they stay small enough to be worth remembering — so leaving one and going back
    in, or unpacking it afterwards, no longer re-reads the whole directory.

  • Reading a tar is no longer paid for in full. The reader read whole files into memory before it
    could tell whether they were tars at all — so a large .xz, .zst or .7z was read cover to
    cover only to be handed on, and a dump.sql.gz was decompressed entirely before being rejected.

  • Images in an assistant answer are shown as images. An answer pointing at a picture on your Mac
    used to print the Markdown for it, brackets and all. It now appears, scaled to the width of the chat.
    Only files on this Mac are shown: an image address on the internet is left as visible text and is
    never fetched, because loading one would tell that server when you read the answer.

  • The documentation website no longer tells anyone that you are reading it. Every page used to
    fetch a font from Google and the repository's star count from GitHub, so opening a page reported
    it to two third parties — and none of the typography worked without a network. Both are gone; the
    site now uses the fonts your Mac already has. The only remaining request is the download button on
    the front page asking GitHub for the newest release, which cannot be known offline and still works
    without JavaScript.

  • .mdx, .mkdn and .mdwn files are now treated as Markdown everywhere. Three places in the
    app disagreed about which extensions count: one of them gave .mdx an outline but would not render
    it, another rendered .mkdn but would not reformat it. All three read one list now.

  • Diagrams on the documentation website now render without an internet connection. The
    architecture pages draw 34 diagrams, and the engine that draws them was fetched from a third-party
    CDN the moment a page was opened — so the diagrams were missing offline, and reading one told that
    CDN which page you were on. The engine now ships with the site.

  • Uploading to a server now shows progress and can be cancelled. Copying files into an FTP,
    SFTP, WebDAV or plugin panel used to run with no progress window and nothing to press — for one
    large file, an application that looked hung — and reported only a count at the end. It now uses the
    same transfer window as a local copy, with Cancel, pause and the speed limit. On a plugin drive the
    bar moves within a single file; on FTP and SFTP it advances a file at a time.

  • A cancelled download no longer reports success. Stopping a transfer on a plugin drive could
    report it as complete for a file that was never written.

  • Columns a plugin adds are no longer blank. A plugin can contribute extra columns for its own
    entries — the Task Manager's CPU and PID, an S3 drive's storage class. For a drive whose name
    contains a dot, which is every server address, the column stayed empty however it was configured.

  • A plugin drive in the drive bar now connects when you click it. A plugin that offers several
    saved connections showed one chip each, and clicking one opened the connection dialog instead of
    connecting — the chip looked like a shortcut and was not. It connects that saved connection
    directly now. If its password is no longer in the Keychain it says so, rather than quietly
    connecting without one.

  • Copying, creating a folder and renaming now work inside a mounted plugin drive. On a WebDAV
    server — or any drive a plugin provides — three keys did the wrong thing quietly. F5 into the drive
    handed the remote path to the local copy engine, so the file was written to a same-named folder on
    this Mac and reported as copied. F7 created a local folder named after the remote path. F6 renamed
    nothing and blamed the files. All three now go to the server, and a failure says which item it
    happened to. Cancelling a transfer also reaches the plugin now, instead of being noticed after the
    last byte had already arrived.

  • Summarising a very long file no longer fails at the last step. The assistant reads a long file in
    slices and combines the results; combining them was itself one request, and for a long enough file —
    or a talkative enough model — that request was too big for the on-device window. It failed only
    sometimes, and when it did the assistant reported it as though the file were the problem. The
    combining now happens in rounds, so it stays within the window however long the file is.

  • A folder that cannot be opened no longer moves the panel there. Opening one that macOS keeps
    private, or that permissions refuse, used to leave the panel claiming to be in it: the tab and the
    path said the new folder, the path bar said the old one, and the file list belonged to neither. The
    folder was even written to the session, so the next launch started somewhere it could not read. The
    panel now stays where it was, and everything on screen agrees about where that is.

  • And it says why, when macOS is the reason. A location such as your iOS device backups is visible,
    belongs to you, and its permissions say you may read it — and opening it is still refused, because
    macOS gates it on the app rather than on you. No amount of administrator rights helps, so "could
    not open" sent you looking for a permission that was never the problem. The panel now names it:
    macOS keeps private — see Commands ▸ Full Disk Access….

  • FEATURES.md listed "Archives" twice. One feature record of eighty-eight said
    category: archives where the rest say archive; the label table has no plural, so the
    generator fell back to capitalising the raw value — which produces the same heading the
    singular already produces. The record is fixed, and the generator now refuses a category that
    is not in the registry's own list instead of inventing a heading for it.

  • The other eighteen languages got the same site, and a front page. The translated help
    was published as a flat list of twelve sections with a three-line stub for a landing page —
    seventeen of the eighteen languages had a heading and the name of their language, nothing
    else. They now have the same five tabs as the English site, named in their own language, and
    a real front page: the opening paragraph of that language's own introduction topic followed
    by every topic grouped the way the tabs group them. No prose was invented for it — all of it
    is text a translator already wrote.

  • Sixteen languages showed a stray English "Plugins" section in the in-app help. Seven
    plugin topics sat under the translated word and six under the English one, because whoever
    added the later plugin pages copied the English section: along with them. Every reader of
    those sixteen languages saw two plugin sections in the Help Book, one of them untranslated.

  • The documentation site opened with the API reference. The first two navigation entries
    were API reference and Developer guide; Getting started came third, and the user
    guide, the plugins and the tutorials were far below. Nobody had decided that: the site's
    navigation is derived from each page's order:, a section ranks by the lowest order: of
    its pages, four sections tied at 10 — and the tie fell to alphabetical directory order.
    The API reference won outright because its generator wrote order: 5, the smallest number
    in the corpus. The site now has seven tabs in reading order — Get started, Using Peach
    Commander, Customise, Plugins, Tutorials, Reference & help, Develop — with the twenty-one
    former sections as collapsible groups beneath them, so no more than a handful of entries
    is ever on screen at once. The API reference is in the last tab. The landing page opens
    with three doors instead of a wall of prose: coming from Total Commander, new to two-panel
    managers, or here to write a plugin.

    This is a website-only key, so the in-app Help Book keeps its own structure and is
    byte-for-byte unchanged, and the eighteen translated Help subsites are untouched.

  • A user help page was invisible on the website. help/filesystem-images.md and
    plugins/filesystem-images.md declared the same slug. Pages are staged flat, so the
    developer page overwrote the user page while the navigation kept an entry for both — two
    titles, one file, and the user-facing Filesystem Images help was not published at all. The
    developer page is renamed, and a duplicate slug now stops the build instead of quietly
    winning. Also: the site said "MacOS & privacy" because section labels were auto-capitalised.

  • 266 dead links in the shipped in-app help. Seven topics link to each other as
    ](slug.md), which resolves on the website; an Apple Help Book bundle contains no Markdown
    at all, so every one of them was a dead href — fourteen links in each of the nineteen
    languages. The table of contents was always correct, which is why nothing looked wrong from
    the outside.

  • Toggling hidden files could kill the app, and switching panels showed the wrong thing first.
    Both commands ran their handler on a background thread and reached straight into AppKit from there:
    cm_SwitchHidSys called NSTableView.reloadData while the main thread was drawing, and AppKit's
    layout engine raised an exception nobody catches — an abort, mid-session, with the panel already
    showing the wrong directory for a while beforehand through cm_SwitchPanel. The cause was one
    annotation that does less than it reads like: CommandHandler is a @MainActor function type, but
    that only isolates the closure literals written at a handler: argument. A reference to a
    separately declared func keeps its own isolation, so all 37 named cm_*_handler functions were
    running wherever the command registry's continuation happened to be. They are main-actor-isolated
    in their own right now, and WindowControllerProtocol says so too, so the compiler will hold the
    rule instead of a comment claiming it. Tools/check-command-handler-isolation.py is the gate: the
    build could not catch this one, because the project compiles in the Swift 5 language mode and the
    conformance that crashed produced no warning at all.

  • Two panel properties and two macOS callbacks crossed the same boundary.
    PanelControllerProtocol looked safe because its methods are async — an async witness hops — but
    it also had three synchronous var requirements that did not. NSServicesMenuRequestor and
    QLPreviewPanelDataSource are ObjC protocols with no isolation of their own, so their witnesses
    now assert the main thread they are actually called on rather than assuming it quietly.

  • A crash used to make the next automated run hang instead of report. The launch-time crash-report
    prompt and the Full Disk Access prompt are both modal, and a modal owns the main queue an automation
    script is driven from: the script ran on inside the nested runloop, wrote its files, and then never
    quit. Both are skipped under -AutomationScript. The crash watermark is deliberately left alone, so
    the report still greets the user on their next ordinary launch.