Build Keyboard Maestro

YESback to the verdict

0%0 of 20 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A background agent where a trigger runs a sequence of deterministic actions with conditions and loops, every run is logged step by step, a macro can be stopped mid-run, and nothing executes without you having seen what it will do.

estimated effort weekendthe files for this build are in the project pack

LanguageSwift 6 and SwiftUI in XcodeApp typeA background agent with LSUIElement trueExecutionAn interpreter over an action tree, not compiled codeStorageMacros as JSON in Application Support, versionedLoggingEvery action's inputs, outputs and duration

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree if you own one

    Why Every action surface here is a platform API.

    Get it Apple menu, then About This Mac.

    Verify About This Mac reports macOS 14 or higher

  2. installfree

    Why Builds and signs the agent.

    Get it Mac App Store or developer.apple.com/xcode. open ↗

    Verify xcodebuild -version prints 16 or higher

  3. decidefree

    Why Keystrokes, menu clicks and window control all require it, and it lets the engine do anything you could do.

    Get it System Settings, then Privacy and Security, then Accessibility. Re-granted during development whenever the binary changes.

    Verify A keystroke action reaches another application

  4. have readyfree

    Why Phase 3 needs variety: one that responds well to Accessibility, one that is slow to activate, and one that ignores window commands.

    Get it Anything already installed. Include at least one slow starter, since activation timing is the common bug.

    Verify Each is exercised by a macro and each case is handled

  5. 99 USD per year

    Why Product phase only. Without it, no notarization and a warning on other machines.

    Get it Enrol at developer.apple.com/programs. open ↗

    Verify A Developer ID Application certificate appears in Xcode settings

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- macros(id, name, group_id, enabled, notes)
- triggers(id, macro_id, kind, spec_json) · kind is hotkey, app_launch, app_quit, folder_change, time or manual
- actions(id, macro_id, parent_id, ordinal, kind, params_json) · a tree, because conditions and loops contain other actions
- variables(macro_id, name, value, scope) · scope is run or persistent
- runs(id, macro_id, started_at, finished_at, state, trigger_kind), run_steps(run_id, action_id, at, duration_ms, result, error)
- The run_steps table is the debugger. A macro engine without one is a black box that occasionally does something surprising.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
MACROS_PATHrequired~/Library/Application Support/Macros/macros.jsonMacro definitions. Readable and version controllable.
RUN_LOG_DBrequired~/Library/Application Support/Macros/runs.dbStep-by-step run history. This is the debugger.
MAX_LOOP_ITERATIONSrequired1000Hard cap on any loop. Exceeding it stops the macro and logs why.
MAX_RUN_SECONDSrequired120Hard cap on a single run. A macro that exceeds it is halted.
SHELL_TIMEOUT_MSrequired10000Per shell action, killing the process group on expiry.
RUN_LOG_RETENTION_DAYSrequired30How long run history is kept.

The build, in order

  1. An interpreter you can watch

    A macro of three actions runs, and you can see each step complete.

    1. Parent and ordinal, because conditions and loops contain children. Flat lists cannot express control flow.

      Files Action.swift

    2. Walk the tree, log every step's start, duration and result. The log is written as it goes, not at the end.

      Files Interpreter.swift

    3. Check for cancellation between actions and record the run as cancelled. A macro you cannot stop is a macro you will not run.

      Files Interpreter.swift

    done when · tick each as it passes
    watch out
    • Building the action library before the execution model means every action is written against an interpreter that later changes shape. Get run logging, stepping and cancellation right with three trivial actions, then add the useful ones against a stable contract.
  2. Control flow

    Macros can branch and repeat without becoming unstoppable.

    1. Variables, frontmost application, file existence and window state. Each is a small evaluator returning a boolean.

      Files Conditions.swift

    2. With children, evaluated per iteration. The interpreter already walks trees, so this is a node kind.

      Files Loops.swift

    3. Both, with the reason recorded in the run log. Test by writing a deliberately infinite loop.

      Files Interpreter.swift

    done when · tick each as it passes
    watch out
    • A while loop whose condition never becomes false will run until the machine is restarted, and it will do so while holding whatever the loop was doing. A hard iteration cap and a run timeout are not defensive extras; they are what makes loops safe to offer at all.
  3. Actions that do something

    Macros can act on applications, text, files and the clipboard.

    1. Built into the activation action rather than left to the user. This is the most common macro bug and it is fixable once.

      Files Actions/Activate.swift

    2. Keystrokes, menu clicks, shell, clipboard, window and open. Each independent behind one interface.

      Files Actions/

    3. A shell action's output becomes a variable a later action can use. This is what makes sequences useful rather than merely sequential.

      Files Actions/Shell.swift

    done when · tick each as it passes
    watch out
    • Sending keystrokes to an application that has not finished activating delivers them to whatever was frontmost a moment ago, which at best does nothing and at worst types into the wrong document. Wait for activation to complete before sending, and make that wait part of the action rather than a pause the user has to remember to add.
  4. Triggers

    Macros fire on their own, and two macros never fight over one trigger.

    1. Hotkeys through the Carbon registration, application events through workspace notifications, folder changes through FSEvents.

      Files Triggers/

    2. Size stable across two checks. The same rule any folder watcher needs.

      Files Triggers/Folder.swift

    3. Compare against enabled macros and refuse rather than resolving by order.

      Files Conflicts.swift

    done when · tick each as it passes
    watch out
    • A folder change trigger firing on an incomplete file copy runs the macro against a partial file, which for anything that moves or processes files is worse than not running at all. Reuse a settle check before firing, exactly as a folder watcher would.
  5. Sharing macros safely

    A macro can be exported and imported, and importing one is an informed decision.

    1. A schema version, refused if newer than this build understands.

      Files Export.swift

    2. Every action listed, shell and keystroke actions individually approved, applied atomically.

      Files ImportPreview.swift

    3. The interpreter logs without executing side effects. Verify a file action changed nothing.

      Files Interpreter.swift

    done when · tick each as it passes
    watch out
    • An imported macro containing a shell action is arbitrary code that will run on a trigger you may not have read carefully. List every shell command in the import preview and require individual approval, because macros are exactly the kind of file people download and run.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more productivity utilities.