Portfolio project: three native apps, hand-tested, no automated tests

mac-ai-apps

Native macOS menu bar apps that use a local LLM

Three small native apps, each a SwiftUI MenuBarExtra that talks to Ollama on localhost:11434: Lint cleans clipboard text, Pulse summarizes a Jira sprint, and MicDrop turns a spoken phrase into cleaned-up dictation or a command. They are separate Xcode projects with no shared package, built to learn native macOS development and local-model integration end to end. There are no automated tests, no CI, and no measured results: everything was checked by hand.

See what is measuredView the repoWhat is not built

  • 3 native macOS apps: Lint, Pulse, MicDrop
  • No automated tests, no CI
  • Tried by hand only
  • MicDrop is a work in progress
  • Not packaged or notarized
  • macOS 15.5 target not built here

01

The problem

Everyday text cleanup, sprint status and voice input, without sending data to a cloud API.

These apps touch clipboard contents, ticket data and voice, and none of that needs to leave the machine, so they use local models on purpose. The recurring design theme is not trusting a small model.

  • Small models drift

    Small local models are unreliable for open-ended tasks, which is why the apps validate or constrain their output.

  • Compute what can be computed

    Pulse computes counts, overdue and stale flags, sorting and per-assignee breakdowns in Swift, and asks Ollama only for a short narrative.

  • Default to the safe action

    MicDrop falls back to plain dictation whenever intent classification is unsure, and Slack sends wait for an explicit Confirm click.

02

How it works

Each app follows the same shape, copied by hand between projects rather than shared as a package: a trigger, deterministic Swift for anything that can be computed, a local model call, a validation or fallback step, and an output.

How the apps work: a hotkey, click or timer triggers deterministic Swift, which calls a local Ollama model; the output is validated or falls back to a safe default, then lands in the clipboard, popover, notification, paste or speech
Diagram drawn for this page from the README's architecture chart.

Swipe the diagram sideways to see all of it.

Stages of the diagram, one at a time

Stage 1 Trigger

Lint: Cmd+Shift+V (Carbon RegisterEventHotKey) or the menu bar. Pulse: menu bar click, refresh, or a daily 9am notification digest. MicDrop: Cmd+Option+D toggles recording.

Stage 2 Deterministic Swift

Pulse computes counts, overdue and stale flags (in progress for 3+ days), sorting and per-assignee breakdowns in Swift. MicDrop routes each recognised intent to one handler per command.

Stage 3 Local model

Each app uses an Ollama /api/chat client with separate system and user messages. Lint and MicDrop use llama3.2:latest; Pulse uses qwen2.5-coder:7b. MicDrop transcribes on-device with WhisperKit first.

Stage 4 Validate or fall back

Pulse checks the model's brief against computed facts and falls back to a deterministic sentence. MicDrop defaults to plain dictation on any failure, malformed JSON or doubt.

Described in the READMEs; none of it is covered by an automated test.

Stage 5 Output

Lint writes the cleaned text back to the clipboard and plays a sound. Pulse shows a popover and can copy standup text. MicDrop pastes dictation with a simulated Cmd+V or runs a command; query results are spoken and shown as a notification.

03

What is measured

There are no measured results for these apps: no tests, no benchmarks, no latency or accuracy figures. What can be verified is what each app does and the behaviours the READMEs document, which were read from the source and tried by hand. The tables below are descriptions, not test results.

What each app does, as documented in its README

Lint, Pulse and MicDrop are separate Xcode projects with no shared package.

AppTriggerModelOutput
LintCmd+Shift+V or menu barllama3.2:latestCleaned text written back to the clipboard, plus a sound
PulseMenu bar, refresh, or daily 9am digestqwen2.5-coder:7bSprint brief, flags, and a Copy for Standup action
MicDropCmd+Option+D, recording auto-stops after about 1.5 seconds of silencellama3.2:latest (intent), WhisperKit base.en (speech)Pasted dictation, or an executed command

Caveat Taken from the READMEs and read from source; none of it is covered by an automated test, and no run outcome is recorded in the repository.

Source: README.md, Lint/README.md, Pulse/README.md, MicDrop/README.md

The eleven MicDrop commands the code implements

As listed in MicDrop/README.md ("Commands the code implements"). MicDrop is marked work in progress.

IntentWhat it doesNotes from the README
calendarReads today's eventsEventKit, read-only
reminderCreates a reminder titled with the spoken textNo due-date parsing
noteCreates a new noteAppleScript, Notes.app
batteryReports percentage and whether on AC powerIOKit power sources
volumeSet a level, mute, up/down by 15The unmute branch is unreachable
webSearchOpens a Google search in the default browserNSWorkspace
openAppOpens an app by nameA few web-only services open in the browser
musicPlay, pause, next, previous, what's playingMusic.app only
timerIn-process timer that fires a notificationLost if the app quits
slackReports channel count, or resolves a channel and waits for a Confirm click before postingUser token from a local config file; cannot see or send direct messages
jiraCounts your tickets in open sprints by status categoryReads Pulse's local credentials file

Caveat This lists what the source implements, not what was verified to work. The README states there is no automated test suite and that behaviour is read from the source.

Source: MicDrop/README.md

Documented entitlements

App Sandbox setting per app, from the committed .entitlements files and the READMEs.

AppApp SandboxNotes
LintOnNetwork client entitlement only, per the README
PulseOffThe README says Pulse is not sandboxed and has a debug log
MicDropOffNeeded for AppleScript, simulated keystrokes and shelling out to open, per the README

Caveat These are configuration values in committed files, not a security review. Sandboxing and entitlements were not audited.

Source: Lint/Lint/Lint.entitlements, Pulse/Pulse/Pulse.entitlements, MicDrop/MicDrop/MicDrop.entitlements

04

Architecture pattern

Pattern Deterministic-first with a local model behind a guard: compute what can be computed in Swift, ask a small local model only for what it must do, and validate or fall back.

There is no shared package; each app is small enough that copying a pattern was simpler than extracting a library. Reused by hand: the MenuBarExtra scaffold, the Ollama HTTP client shape, the UNUserNotificationCenterDelegate used so foreground notifications show with sound, and the Carbon global hotkey. The only third-party package is WhisperKit, used by MicDrop.

  1. Hotkey, menu bar click, or timerper-app trigger
  2. Per-app controllerorchestrates the steps
  3. Deterministic Swiftcounts, parsing, routing
  4. OllamaClientPOST /api/chat on localhost
  5. Local modelOllama on the same machine
  6. Validate or fall backPulse and MicDrop
  7. Outputclipboard, popover, notification, paste, or speech

Mermaid diagram in the repo README

05

Competencies demonstrated

  • Native macOS development

    MenuBarExtra, AppKit interop, Carbon RegisterEventHotKey global hotkeys, EventKit, IOKit, AppleScript, simulated paste via CGEvent.

  • Local LLM integration

    Ollama /api/chat clients using separate system and user messages; JSON-mode intent classification in MicDrop.

  • Defensive use of small models

    Pulse checks the LLM brief against ground truth and falls back to a deterministic sentence; MicDrop defaults to dictation when unsure.

  • Safe actions

    MicDrop Slack sends wait for an explicit Confirm click.

  • Debugging

    Per-app write-ups of root-caused bugs in the Lint and Pulse READMEs (three for Lint, five for Pulse).

  • Secrets hygiene

    No credentials in the repo; local 0600 files outside it, plus Keychain storage code in Pulse.

06

What is not built or not verified yet

Taken from the repository's own Known limitations and the MicDrop README. This is the prominent part of this page on purpose: the apps are unfinished and unverified in ways that matter.

  • not verified

    No automated tests

    There are no automated tests. Everything was checked by hand.

  • not verified

    No CI

    There is no CI, so no build or test is run on any change.

  • not verified

    macOS 15.5 build claim

    The "macOS 15.5+" platform claim comes from the Xcode projects' deployment target. These apps were not built or run to produce this page.

  • limit

    MicDrop is not sandboxed and logs transcripts

    MicDrop is not sandboxed and DebugLog.swift appends debug lines, including transcripts, to /tmp/micdrop-debug.log. The README says to delete or disable it before sharing a build. Pulse also has a debug log and is not sandboxed.

  • limit

    Credentials in local files

    No code-signing identity is configured; builds use the local ad-hoc setup, which is why Pulse stores credentials in a local file instead of the Keychain.

  • limit

    MicDrop: unmute can never run

    The "unmute" branch cannot be reached because it matches the mute check first.

  • not built

    MicDrop: Gmail

    MicDropCredentials.swift has fields for a Gmail OAuth client, but no Gmail command exists.

  • not built

    MicDrop: other missing pieces

    Reminder due dates, Spotify support, Focus / Do Not Disturb control, streaming transcription (the whole recording is transcribed after it stops), and cleanup of leftover "Miko" strings in notification titles from an earlier working name.

  • not built

    Packaging

    Apps are not packaged or notarized; run them from Xcode or a local Release build.

  • limit

    Small local models

    Small local models are unreliable for open-ended tasks, which is why the apps validate or constrain their output.

07

Roadmap

  • DoneLint: global hotkey clipboard cleaner on a local model
  • DonePulse: Jira sprint brief with a validated model narrative, Copy for Standup, and a daily 9am digest
  • DoneMicDrop: dictation plus eleven commands, with Slack sends behind a Confirm click (work in progress)
  • Not doneAutomated tests (none exist; not scheduled)
  • Not doneCI (none exists; not scheduled)
  • Not doneMicDrop gaps listed in its README: Gmail command, reminder due dates, the unreachable unmute branch (not scheduled)
  • Not donePackaged and notarized builds (not done; not scheduled)