Repository navigation
Releases
OMC 5.5
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.5 is built against ActionUI 0.9.10. It adds integration tests that drive a real, running applet, fixes two long-standing engine bugs (a hang on large standard input and a stale dialog value in the next command), stops closed ActionUI windows from staying in memory, and fixes a set of leaks. The embedded Python stays at 3.15.2.
Rebuild your applets with AppletBuilder 5.5. The engine fixes, the window memory fix and the ActionUI changes arrive only when an applet is rebuilt. Integration tests need an applet built with the 5.5 engine.
Integration tests against the real applet
omctest tests a handler script on its own, with a fake engine around it. 5.5 adds integration tests, which launch the applet itself and drive it the way a person would: run a command, click a control, wait until the applet is idle, then read a window's values.
- Put
*.itest.shfiles beside the applet and runappletbuilder itest MyApp.app, or press the new Integration Test button in the Build & Run pane (the buttons are now Build | Run | Test | Integration Test, then Show). - A test file uses omctest's
sectionandcheckwords plusitest_run,itest_wait_idle,itest_click,itest_value,itest_closeand friends.itest_run --windowruns a command as a given window's subcommand, so menu bar commands and unnamed menu items can be tested too. - Each test file gets a fresh launch with its own
$HOME, its own temporary folder and its own pasteboards, so a test does not touch the user's preferences, clipboard or Open Recent list.PASTE_AFTER_EXECUTIONsends no keystrokes in a test. - Save and open panels, alerts (the
WARNINGconfirmation, version warnings, error alerts) and thealertandnotifytools do not wait for a person: the test queues the answers (itest_answer_save,itest_answer_open,itest_answer_cancel,itest_answer_alert), and every panel, alert and notice is recorded for the test to check. - A failing test file keeps its folder and prints its path;
applet.logthere holds the applet's own output. - If a copy of the applet is already running, the Integration Test button asks to quit it first, because the copy would share its preferences and files with the test.
Only a build signed for local use can be driven this way: ad hoc, self-signed, or an Apple Development certificate. Developer ID, Mac App Store, TestFlight and other distribution signatures never serve the test interface, so a shipped applet cannot be driven by a test launch. An applet that refuses a test launch exits with status 78 and says why.
The full guide is omcitest_guide.md in AppletBuilder's documentation, and the OMC agent skill has a short integration-test section.
Engine fixes
- A large
STANDARD_INPUT_PIPEno longer hangs the applet. Input larger than the pipe buffer, fed to a script that writes output while it reads (cat,sed, a Python filter), deadlocked the applet with no error. This was in every release since 4.0. - The next command sees the dialog values the previous script set. When a script set a dialog value with
omc_dialog_controland exited, theNEXT_COMMAND_IDcommand could start before the change was applied and read the old value. This was in every release since 4.0.1. WAIT_FOR_TASK_COMPLETIONno longer keeps a CPU core busy while it waits. Python bridge calls,PROGRESSwindows withDELAYand other commands' output keep working during the wait.- Closed ActionUI windows are released. Every ActionUI window an applet showed stayed in memory until the applet quit. A window's model now goes once the window has closed and the last command started for it has ended, so an end command can still read the closed window through the ActionUI remote bridge.
INVOKEaccepts point, size and rect arguments. Messages such assetFrame:display:,setFrameOrigin:andsetContentSize:were refused as an unsupported structure on 64-bit macOS.omc_dialog_control --helpnow describes the structure argument format.PROGRESSoutput made of nothing but line breaks no longer drops the partial line that came before it.- An alert the system could not show no longer counts as a click on its default button.
- Leaks: a subcommand run with its parent's context leaked its whole runtime data, and a few smaller objects leaked in INVOKE structure arguments,
plisterand file checks. A leaks run over the whole test suite now finds none.
ActionUI 0.9.10
- HostSurface is registered in OMC dialogs, so a document that uses it loads. A HostSurface is drawn by code in the app's own process, and OMC handlers are separate processes, so in an OMC applet the element is an empty rectangle.
- Image: the deprecated
scaleModealias is removed. UsecontentMode; an Image with onlyscaleModenow shows at its own size. - Map: annotations are red pins with the title and subtitle under them. The annotation's content is no longer used as its title, and an annotation with no text still shows a pin.
- Chart: the
AxisMarksstride unitsquarterandweekOfMonthused to stop the applet; they are now refused with a warning and the automatic marks are kept. - Diff: the 4 MB limit now also holds for a file reached through a symbolic link, and a negative
maxRenderedLinesno longer crashes (DiffView 0.2.0). - Images: an image that declares too large a size is refused instead of crashing the applet (AsyncImageCache 0.2.3, RichText 0.4.0, ChatView 0.6.5).
- A document that does not parse again shows a readable error instead of "ActionUI.JSONParseError error 1".
- The Python and shell remote clients bundled in AppletBuilder gain
perform_actionandactionui_perform_action, which the integration tests use.
Documentation
$OMC_OBJ_TEXTcarries text only when the run brought some (a URL's text, a service's selection, a command run with text context) or the command declares it (__OBJ_TEXT__inCOMMANDorINPUT_PIPE, orOMC_OBJ_TEXTinENVIRONMENT_VARIABLES). Only then does the engine read the selection or the clipboard. The behavior is unchanged; the runtime context reference now says so.- The output window shows standard output only, not standard error. To see errors there, use
exec 2>&1in a shell handler,2>&1on one command, orsys.stderr = sys.stdoutin Python. - Do not use the Python bridge in a
WAIT_FOR_TASK_COMPLETIONhandler started by a value change in an ActionUI window (a picker, toggle, slider, field edit or row selection). Those actions arrive on the main queue, so each bridge call stalls the applet for 10 seconds and then fails with error 1005.
Compatibility notes
plisterwrites JSON with sorted keys. JSON now goes through OMC's own writer, which puts keys in a fixed order, so the first timeplistersaves an existing JSON file it may reorder its keys once. The content is unchanged. Property list formats are not affected.- Image
scaleModeis gone; see the ActionUI section. OMCTEST_API_VERSIONis unchanged at 7. Suites written for 5.4 run as they are.
In this distribution
- AppletBuilder.app - OMC applet development studio.
- OnMyCommandCM.plugin - contextual menu plugin for use with Shortcuts.app; commands load from
~/Library/Preferences/com.abracode.OnMyCommandCMPrefs.plist. - OMCService.service - macOS service template for standalone OMC-based system services.
- Skill/ - the OMC AI agent skill (three flavors) plus its installer.
- Scripts/ -
codesign_applet.sh,install_contextual_menu_plugin.sh,thin_distribution.sh,OMCApplet.entitlements, and the examplecom.abracode.OnMyCommandCMPrefs.plist.
See the main OMC README at https://github.com/abra-code/OMC/ for full documentation on commands, runtime context, dialogs, and services.
OMC 5.4.1
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.4.1 is a point release on top of 5.4, built against ActionUI 0.9.0. It moves the embedded Python to 3.15.2, adds a Chart element and checkbox lists, and fixes a clipboard bug that could keep a command from running, along with a set of ActionUI layout and input fixes.
Rebuild your applets with AppletBuilder 5.4.1. The ActionUI and engine fixes arrive only when an applet is rebuilt. The new Python does not: it has to be requested, and moving an applet from Python 3.14 to 3.15 is a decision of its own (see the next section).
Embedded Python 3.15.2
The embedded Python shipped in AppletBuilder, and copied into every new Python applet, moves from 3.14.8 to 3.15.2, with OpenSSL 4.1.0 (from 3.6.5), expat 2.9.0 and SQLite 3.54.0. lzma is included, as in 5.4.
Existing applets keep their Python until you ask. Turn on "Update Embedded Python" in Build & Run, or pass --update-python to appletbuilder build. Because 3.14 to 3.15 is a minor-version upgrade, AppletBuilder asks before replacing the runtime. The Build & Run pane now shows the applet's embedded Python version, so you can see which applets are still on 3.14.
Before you upgrade an applet:
- Compiled modules must be reinstalled. A package in
Contents/Library/Packagesthat contains a version-specific extension (a file ending in.cpython-314-darwin.so) does not load in 3.15. Install it again with the 3.15 interpreter. Pure-Python packages, and extensions built for the stable ABI (.abi3.so), keep working. - Anything installed into the runtime's own
site-packagesis replaced, as with every Python update.Contents/Library/Packagessurvives. - Compute a new thinning plan. A plan made for 3.14 does not describe 3.15's standard library. Thinning now follows 3.15's lazy imports; without that, a thinned 3.15 runtime lost modules such as
multiprocessingthat the standard library loads on first use, andconcurrent.futures.ProcessPoolExecutorfailed.
What 3.15 changes for handler scripts:
- UTF-8 is the default encoding.
open()and other I/O without anencodingargument read and write UTF-8, whatever the locale the applet was started with. SetPYTHONUTF8=0to get the old behavior, or passencodingexplicitly. - Lazy imports (PEP 810):
lazy import xandlazy from m import nat module level defer the import until the name is first used, which can shorten a handler's start-up. - New built-ins
frozendictandsentinel, unpacking in comprehensions, and a sampling profiler inprofiling.sampling. - Removed: the
sre_compile,sre_constantsandsre_parsemodules;glob.glob0()andglob.glob1();pathlib.PurePath.is_reserved()(useos.path.isreserved());platform.java_ver();http.server.CGIHTTPRequestHandler;types.CodeType.co_lnotab;ctypes.SetPointerType(); thepackageparameter ofimportlib.resources.files(); and keyword-argumentNamedTupleand zero-fieldTypedDictforms intyping. - Changed:
sqlite3.connect()takes every argument exceptdatabaseby keyword only;argparse.add_argument('-f', '-foo')now names the destinationfoo;base64.urlsafe_b64decode()no longer requires padding;datetime.strptime()raisesValueErrorfor a format with%dand no year;importlib.metadata.metadata()raisesMetadataNotFoundfor a missing package instead of returning an empty result;__cached__is no longer set on modules. - Tracebacks and several standard library command-line tools print in color by default. A handler that parses their output should set
NO_COLOR=1orPYTHON_COLORS=0.
Security fixes in 3.15.2 that matter to applets:
tarfile: thetarextraction filter accepted a link member that resolves to the destination folder itself (CVE-2026-114583).ssl: certificate chains that use any of a set of compromised eMudhra intermediate CA keys are now refused (CVE-2026-115557). A connection to a server that relies on one of them fails.http.cookiejar: a cookie with a negativeVersionwas accepted without checking its domain against the request host.
Charts
The new Chart element draws Swift Charts from JSON: LineMark, PointMark, BarMark, AreaMark, RuleMark, RectangleMark and SectorMark, grouped with Plot and repeated with ForEach. A chart can have axis marks and label formats, axis domains, color, symbol, line-style and symbol-size scales, annotations drawn next to marks, scrolling with a visible range, and a user selection that becomes the element's value and runs valueChangeActionID.
The data comes either inline in the chart's description or as rows, which a handler fills with the same omc_dialog_control commands as a Table (omc_table_set_rows, omc_table_set_rows_from_stdin and the others). Replace the whole description at runtime with omc_set_property chart '<json>'. The schema, with examples, is in the AppletBuilder documentation under Chart, and the UI editor has a template for it.
Checkbox lists and checkbox columns
- A
List(or any data-driven container) whose template is aTogglebecomes a checkbox list. Each row holds its own state in the column thatisOnnames ("isOn": "$1"). Clicking a checkbox writes"true"or"false"into that row and runs the Toggle'sactionIDwith the row index; it does not select the row. The row's other columns can supplytitleanddisabled. - A
Tablecolumn with"viewType": "Toggle"shows a checkbox in every row. The cell text is the state,disabledColumnnames a column that disables the checkbox per row, and the column'sactionIDruns with the row index as its context. Toggling does not change the table's selection.
Rows written by a handler change what the checkboxes show and run no action. The full rules are in the Toggle and Table documentation.
Clipboard text reaches commands again
A command with no text of its own gets $OMC_OBJ_TEXT from the clipboard. Two bugs could make such a command fail without running:
- When no clipboard item offered text, the lookup read past the end of the item's list of types.
- Plain UTF-8 text (
public.utf8-plain-text) was never read, so an item whose other text types had been promised by an application that has since quit gave no text at all.
Both are fixed, and UTF-16 text is read correctly when it is empty.
ActionUI fixes
hiddenhides the whole element. A hidden element drew its background, border, overlay and shadow while its content was hidden. Its space is still reserved, as documented.ScrollViewReaderscrolls. It never moved before. It now scrolls to its target when it appears, and again, animated, whenever a handler writes a target, including the same one again.SpacerminLengthfollows its stack. It is a minimum height in aVStackand a minimum width in anHStack. It used to be applied as a minimum width everywhere.DatePickerno longer emits tomorrow's date in the evening. A date-only picker wrote the UTC day, so in time zones west of UTC an evening selection came out as the next day. It now writes the local day. A date string with a zone designator (Z,+02:00) is now read as that instant and shown in the Mac's time zone; before, the designator was dropped.StepperlabelFormat:%dand%ishow the value instead of0, a format such as%@or%sshows the plain value instead of crashing the applet, and the number uses the user's decimal separator (2,5in a German locale).- A
Sliderwith a very fine step no longer hangs the applet. SwiftUI keeps every step in memory, so a large range with a small step could use gigabytes. A step that makes more than a million steps is ignored and the slider slides freely. - A reversed range no longer crashes. A
TextFieldlineLimitor number-format fraction length with its maximum below its minimum stopped the applet; the maximum is now raised to the minimum. - A read-only
TextEditorkeeps its text. Typing into it changed the screen but not the value, and undoing that typing could crash. Its text stays selectable. keyboardShortcutwith"modifiers": []binds the plain key, so a default button can answer Return and a cancel button Escape. An omittedmodifiersstill means Command.
AppletBuilder
- Build & Run shows what the applet runs on: its OMC engine version, with the version a build would update it to, and its embedded Python version. Both are read from the applet's files; opening a project never runs its interpreter.
- The Test button runs suites the way a terminal does. A suite started from AppletBuilder inherited AppletBuilder's own environment, so every
omc.window()call in a tested applet failed ("nothing could be read from descriptor 3") and AppletBuilder's control values reached handlers where a test set none. Each test file now starts clean.OMCTEST_API_VERSIONis unchanged at 7. - Thinning needs a matching Python. When an applet's runtime is already thinned, the analyzer used to fall back to
/usr/bin/python3, which is Python 3.9 and cannot read the 3.14 and 3.15 standard libraries, and the resulting plan quietly missed imports. Thinning now uses only an interpreter at least as new as the applet's, and otherwise stops with a message naming the version it needs. codesign_applet.shno longer gets an applet refused by notarization. A helper built with a plain Xcode build carries the debugger entitlementcom.apple.security.get-task-allow, and the script carried it over when re-signing, so the notary service refused the app. Signing with a certificate now drops it and says so; an ad hoc signing keeps it. If you keep your own copy of the script beside an applet, replace it with the one inScripts/.
Compatibility notes
- **
PhaseAnimatorand `Key...
OMC 5.4.0
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.4 is built against ActionUI 0.8.4. It closes a hole in applet URL schemes, gives every applet an identity of its own for macOS privacy prompts, fixes several List and Table selection bugs, and moves the embedded Python to 3.14.8, a security release.
If your applet is opened by links, read the first section before you rebuild. A command now has to opt in before a URL can run it, so an applet that relies on <scheme>://exe?commandID=... links stops answering them until you add one key.
Rebuild your applets with AppletBuilder 5.4. The URL rule, the window and selection fixes and the per-applet identity all arrive only when an applet is rebuilt. An applet with an embedded Python should be rebuilt with "Update Embedded Python" on (appletbuilder build --update-python).
A URL can run a command only when the command allows it
An applet that declares a URL scheme used to run any command named in a link of the form <scheme>://exe?commandID=<id>, with the text or file context chosen by whoever wrote the link. The applet's author could not turn this off. A link on a web page or in an email could run app.will.terminate, or a dialog handler with no window to act on. AppletBuilder adds a URL scheme to every new applet, so this applied to most applets, including ones that never meant to use links.
In 5.4 the exe URL runs a command only when the command sets URL_INVOCABLE to true:
{
"NAME": "MyApp",
"COMMAND_ID": "myapp.open.item",
"URL_INVOCABLE": true
}- Application lifecycle commands, dialog event handlers and the predefined
omc.dialog.*ids are never run by a URL, whatever the key says. - Each
file=value must be the absolute path of an existing file or folder. One bad value refuses the whole URL. - A refused URL runs nothing and writes one line to the system log, starting with
OMC: URL refused, with the command id and the reason. omc.app.handle-urlneeds no key and works as before: receiving URLs is its only purpose.- Every run started by a URL exports the URL as
$OMC_TRIGGER_URL. The variable is absent in every other run, so a command that is also reachable from a menu or a button can tell the two apart and be stricter with a link, for example by asking first.
Set the key only on commands meant to be entry points, and treat $OMC_OBJ_TEXT and $OMC_OBJ_PATH as untrusted in them: the sender of the link chose both. The full rules are in the command reference under "Running a Command from a URL".
The command verifier checks the key. It reports an error when URL_INVOCABLE is set on a command a URL can never run, and warns when a command sets it but Info.plist declares no URL scheme.
Each applet is its own program to macOS
Every applet's executable is a copy of AppletBuilder's, and until now all of them carried the same Mach-O UUID. macOS identifies a program by that UUID for Local Network privacy, so the "find devices on local networks" prompt, and the row in System Settings > Privacy & Security > Local Network, named whichever applet the Mac had seen first, and one answer covered all of them.
AppletBuilder now writes a UUID derived from the bundle identifier into the executable every time it signs an applet. It is the same after every build and every engine update, so the user is not asked again. Changing the bundle identifier makes the applet a new program to macOS.
- An applet built with an earlier AppletBuilder keeps the shared UUID until it is built again.
- An applet that reaches the local network, or starts a tool that does, should also carry
NSLocalNetworkUsageDescriptionin itsInfo.plist: it is the sentence macOS shows in the prompt. create --no-codesignis now ignored. An unsigned applet does not launch on Apple silicon.
New applets no longer keep the template's URL scheme
New Applet, and a rename on the General tab, added the applet's own URL scheme next to the template's instead of replacing it, so every new applet answered two schemes, one of them shared with every other applet made from the same template. The scheme list is now replaced as a whole.
The scheme is also derived more carefully from the name: it must start with a letter, so a name starting with a digit gets an applet- prefix. A failed write to Info.plist is now reported instead of passing silently, and New Applet removes its half-made copy.
Applets created with an earlier version still list the template's scheme. Check CFBundleURLSchemes in their Info.plist and remove the extra entry.
Lists and tables keep the right row selected
- Deselecting a row in a List runs its action. A List with rows cleared its value when the user deselected the selected row but did not run its
actionID, unlike a Table. A handler that tracks the selection never heard about it, and a detail pane went on showing the deselected row. - A click selects the row it lands on. In a List with hidden rows (rows with an empty first column), a click below a hidden row selected a different row.
- Refreshing the rows keeps the selection. When a handler rewrote the rows and another column of the selected row had changed, the highlight vanished with no event. The selection now stays on its row, matched by first column, and among rows sharing a first column it stays at the same place. No action runs for this.
- A click racing a refresh no longer brings back old data. A rows update arriving between a click and its effect could write the row's previous columns back as the value. This applies to List and Table.
ActionUI windows can be minimized
Regular ActionUI windows were created without the minimize ability, so the yellow button, Window > Minimize and Cmd-M did nothing in every ActionUI applet. They work now. Blocking dialogs and floating panels still do not minimize: a hidden modal window would leave the applet stuck.
Remote images in Chat and RichText
Chat and RichText have a new "remoteImages" property that decides when http and https images in their Markdown are fetched: "automatic" (the default, as before), "on-click" (a placeholder naming the image's host until the reader clicks it) or "never". Set one of the last two for Markdown that a model or an agent wrote, because fetching an image delivers whatever its URL carries to the image's host. A misspelled value is treated as "on-click", so a typo cannot turn the protection off.
Chat also gains sessionConfig for the acp transport (options set right after the session starts), and RichText no longer leaves a placeholder when several documents show the same image at once.
Embedded Python 3.14.8
The embedded Python shipped in AppletBuilder, and copied into every new Python applet, moves from 3.14.7 to 3.14.8, with OpenSSL 3.6.5 and expat 2.8.5. Python 3.14.8 is an expedited security release. The fixes that matter to applets:
tarfile: thetaranddataextraction filters could create directories outside the destination.zipfile: a small bzip2, LZMA or Zstandard member could expand into an unbounded allocation even when read in small pieces (CVE-2026-15310).urllib: a password stored for an https URL was offered to the matching http URL (CVE-2026-15806).ssl:SSLContext.wrap_bio()completed a handshake without checking the peer's host name when none was given, and a server's SNI callback could crash the process (CVE-2026-19445).- Formatting a float with a very large precision could crash.
A few behavior changes a handler could notice:
SSLContext.wrap_bio()on a context that checks host names now raisesValueErrorwhen noserver_hostnameis passed.plistlib.dumps()withskipkeys=Trueandsort_keys=Truedrops non-string keys instead of raisingTypeError.xml.etree.ElementTreeno longer treats characters such as U+00A0 as white space.tokenizeno longer produces<>as a single token.
The lzma module is included. import lzma works, .xz data can be read and written, and zipfile and tarfile handle LZMA-compressed members. liblzma is linked statically into the module, so nothing has to be installed on the user's Mac. Earlier releases were meant to include it but did not.
The distribution no longer names the machine it was built on. Python's generated configuration files and pip's package records carried the absolute path of the build directory. They are rewritten at build time; install paths are unaffected, because Python derives them from where the interpreter is running.
An applet does not pick up a new embedded Python on an ordinary build. Turn on "Update Embedded Python" in Build & Run, or pass --update-python to appletbuilder build. Modules installed into Contents/Library/Packages survive the update; anything installed into the runtime's own site-packages does not.
AppletBuilder
- ActionUI validation no longer needs Python. AppletBuilder ships
actionui-verify, a native verifier, and uses it for the build, the UI Files Validate button andappletbuilder validate. It prints the same lines with the same exit codes as the Python verifier, which stays as the fallback. Checking a document for one platform now follows the same rules as the runtime, and an element id repeated across platform variants of the same children is no longer reported as a duplicate. - Icon-only buttons have help strings. Hover over one to see what it does.
codesign_applet.shkeeps what a nested helper needs. Signing ad hoc used to re-sign every nested binary without its entitlements, so a helper that needs one launched but failed later. Entitlements of an ad hoc signed helper are now carried over; those from a real certificate are still dropped in an ad hoc run. A loose executable also keeps the identifier it was signed with instead of...
OMC version 5.3.1
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.3.1 is a point release on top of 5.3, built against ActionUI 0.8.3. It is a macOS 27 compatibility release.
If you ship OMC applets, rebuild them with AppletBuilder 5.3.1. Applets built with any earlier version have a window bug on macOS 27, and the fix lives in Abracode.framework, so an applet only gets it when it is rebuilt.
Rebuild your applets for macOS 27
On macOS 27, an ActionUI applet whose root window view is a NavigationSplitView - the usual two-pane, list-plus-detail layout - draws a strip of glass across the top of its detail pane, hiding roughly the first 52 points of content. The first line of the detail column may be partly or entirely behind it.
The same applet looks correct on macOS 26 and earlier, and nothing in the applet or its JSON has to change. Rebuilding it with AppletBuilder 5.3.1 is the whole fix.
Other applets are affected less visibly but should still be rebuilt: the window layout on macOS 27 is corrected across the board in this release.
Two things to know after rebuilding:
- Window sizes are unchanged. A dialog gets the same frame and the same layout area it had before, and
omc_window omc_resizestill means what it always meant. - A root background now shows through the titlebar, the way it does in a native SwiftUI window. If your applet paints a background on its root element and you want the titlebar to keep the system material, give the background a top inset.
A split view nested inside other content is still affected on macOS 27; only a split view used as the window's root view is fixed.
AppletBuilder previews match the real app again
appletbuilder preview and the Preview window were drawing in the old, pre-Liquid Glass style on macOS 26 and 27 - controls, materials and spacing all came out looking like an older system. Nothing reported an error, so the screenshots looked plausible; they just did not match what the applet looks like when you run it.
Previews are now rendered in the current system style. If you judged a layout from a preview taken with AppletBuilder 5.3, it is worth a second look.
Other fixes
- VideoPlayer - writing a new URL to a
VideoPlayernow changes what plays. Previously the first file kept playing, although the schema says writing a URL swaps the source. Volume, playback rate and the AirPlay route are preserved across the swap. - Shadow - a shadow with no color specified was drawing in solid black, about three times darker than a SwiftUI shadow. It now matches SwiftUI's own default, so shadows look right without having to set a color.
- Embedded Python thinning - when a package had been upgraded in place, an applet could report the version it used to have rather than the one it is running. Applying a thinning plan now clears out the leftover metadata that caused it.
- The framework's built-in minimum macOS version was stale and now reads 14.6, matching what OMC 5 requires.
In this distribution
- AppletBuilder.app - OMC applet development studio.
- OnMyCommandCM.plugin - contextual menu plugin for use with Shortcuts.app; commands load from
~/Library/Preferences/com.abracode.OnMyCommandCMPrefs.plist. - OMCService.service - macOS service template for standalone OMC-based system services.
- Skill/ - the OMC AI agent skill (three flavors) plus its installer.
- Scripts/ -
codesign_applet.sh,install_contextual_menu_plugin.sh,thin_distribution.sh,OMCApplet.entitlements, and the examplecom.abracode.OnMyCommandCMPrefs.plist.
See the main OMC README at https://github.com/abra-code/OMC/ for full documentation on commands, runtime context, dialogs, and services.
OMC version 5.3
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.3 is built against ActionUI 0.8.1. It adds a two-way bridge between a command handler and the ActionUI window it was dispatched from: for the first time a handler can read what is on screen right now, not only write to it. Python handlers get this through a new omc module that AppletBuilder installs for them. Also in this release: embedded-Python thinning built into AppletBuilder's Build & Run pane and the appletbuilder command-line tool, off-screen ActionUI JSON preview rendering that works on a locked screen, and an omctest harness that can test bridge handlers against a real host.
Highlights
Command handlers can read their ActionUI window
omc_dialog_control has always been able to set a value, fill a table or enable a control, but it has never been able to ask what a control currently holds. A handler's only view of its window was the snapshot the engine took at dispatch time - $OMC_ACTIONUI_VIEW_101_VALUE and its siblings - so anything the user changed afterward was invisible.
An applet with an ActionUI window now also serves the ActionUI remote protocol (JSON-RPC 2.0 over a Unix domain socket, from ActionUI 0.8). The engine starts the server with the first ActionUI window and stops it when the applet terminates. Closing the last window deliberately does not stop it, because a script the applet spawned may still be holding the endpoint.
Handlers find the bridge through the environment, under both OMC-prefixed and plain names, so a client written against the ActionUI protocol does not need to know its host is OMC:
OMC_ACTIONUI_REMOTE_ENDPOINT- a new always-exported special word - andACTIONUI_REMOTE_ENDPOINT, merged into every child's environment in every execution mode.ACTIONUI_WINDOW_UUID, the plain-name alias ofOMC_ACTIONUI_WINDOW_UUID.ACTIONUI_REMOTE_TOKEN_FD, the descriptor the handler's access token arrives on (see Security below).
The full reference is the new omc_python_bridge_guide.md; omc_runtime_context_reference.md lists the new variables.
The omc Python module
A Python handler can now simply import omc:
import omc
win = omc.window()
name = win.get_string(101) # what is in the field right now
rows = win.get_rows(5) # every row of the table, right nowomc.window()returns the dispatching window as anomc.OMCWindow, a subclass of ActionUI'sactionui_remote.Window, so every ActionUI verb - reading, writing, tables, adding and removing elements, modals and toasts, batching - travels over the bridge. It raisesomc.OMCErrorwith a specific message when the applet serves no bridge (a command file with noACTIONUI_WINDOW) or the command was not dispatched from a window.- OMC's own window verbs - terminate, select, set command id, title, resize, move and next command - call the existing
omc_dialog_controlandomc_next_commandtools with exactly the arguments a shell handler would use, so each verb still has a single implementation. omc.context()parses the runtime environment the engine exports, including the dispatch-time value snapshots, into named fields, so a handler no longer opens with a block ofos.environ.getcalls.
Nothing to install. AppletBuilder copies omc.py and actionui_remote.py into Contents/Library/Packages/ of every applet that has Python handlers and an embedded Python runtime, both when building and when creating an applet from a template. That directory is already on PYTHONPATH and is never touched by a Python runtime update, so the modules survive one. An applet that uses the system Python gets nothing, because the engine does not put that directory on PYTHONPATH for it.
AppletBuilder also vendors ActionUI's shell clients - actionui_remote.sh and actionui_remote.zsh, together with the two awk programs they load, actionui_remote_escape.awk and actionui_remote_walk.awk - and copies them into built applets. The install is all-or-nothing: a partial copy is rolled back, since three of the four files make a client that refuses to load.
Embedded Python thinning from the Build & Run pane
The Build & Run pane gains an Embedded Python Thinning group, and the appletbuilder agent command-line tool gains a matching appletbuilder thin-python plan|apply|plan-apply subcommand. Both drive one shared phase in lib.build.sh, the way Build and Test already do, and AppletBuilder now bundles the Python-Embedding thinning toolkit under Contents/Library/python_thinning, so neither needs a checkout of that repository.
The workflow is unchanged: write a reviewable JSON plan from a clone of the applet, then apply it, which removes what the plan names, verifies the result, and restores the interpreter if anything needed went with it. On a fresh Python applet that takes the interpreter from 61.3 MB to 43.1 MB.
Thinning deliberately stays out of Build, and every path that removes modules asks for confirmation first, naming the bundle. When no plan sits beside the bundle, apply falls back to the last plan written for that applet, matched on its bundle identifier, and says so in the log. That supports the intended release flow: plan against the development copy, apply to a distribution copy. An optional per-applet <App>.thinning-keep.txt lists modules to keep regardless of the analysis.
Off-screen preview that works on a locked screen
appletbuilder preview now runs the bundled ActionUIViewer with --hide-window, which draws the window in-process at an off-screen position. No window appears over the desktop, no Screen Recording permission is involved, and the capture works while the screen is locked - the usual state in an agent session, where the old window-server capture returned an empty image. Pass --show-window for the previous behavior.
Known gaps of the off-screen capture, from ActionUI 0.8.1: no window shadow, TabView tab segments draw as a solid block, and a locked screen gives controls the inactive gray tint.
The skill gains a hard rule telling an agent to screenshot an ActionUI change and look at the image, and the nib migration guide drops its "transparent or black on a locked screen" trap.
Security
The bridge token is handed to each handler on a descriptor, never in the environment
The ActionUI bridge refuses clients that do not present a token. A handler's environment is copied from the applet's, and a process's exec-time environment is visible to any process of the same user through ps -E - python3 and node cannot be made to hide it. A token passed in the environment would therefore have been published machine-wide for as long as the handler ran, and clearing the variable inside the handler does not help, because ps reads a snapshot taken at exec.
So the engine mints one token per spawned handler and delivers it on an inherited pipe: the token is written before the spawn, the read end becomes descriptor 3 in the child, and the parent closes both ends immediately. The child is told the number through ACTIONUI_REMOTE_TOKEN_FD=3 and never sees ACTIONUI_REMOTE_TOKEN - the host removes that from its own environment as soon as the bridge starts, and the spawn strips it from the child's environment as a second layer. The shipped Python and shell clients read the token from the descriptor.
Known limits, documented and pinned by tests:
- Grants live until the applet terminates. A handler may background work that needs the bridge later, and nothing reliably knows when everything a command spawned has finished.
exe_terminalandexe_itermget no token: Terminal is not the applet's child and inherits no descriptor, and writing the token into the export script would put it in a file in the clear.exe_silent_systemand the AppleScript execution modes get no token either, since they take neither an environment nor a descriptor from the engine. These modes can still change the window throughomc_dialog_control, which needs no token.
Nothing regresses: the bridge is new in this release.
Testing
omctest stands up a real bridge host
A Python handler that reads its window needs a host on the other end of the socket, and under test there is no engine. For an applet that ships the client, the harness now runs ActionUI's own fake host for the length of a test file, seeded with the window UUID the harness already exports and with the applet's elements. Without it, such a handler did not fail an assertion - it raised, and the whole file read as a crash.
It is a real host rather than a recording stub: the client and protocol are the shipped ones, so an applet is tested against a genuine implementation.
bridge_calledandbridge_valueare new assertions that read what the applet did over the bridge. They sit besideui_valueon purpose: ActionUI verbs travel over the bridge, while OMC's own window verbs still go throughomc_dialog_control- sobridge_valuereads back aset_valueandui_valuereads back aset_title.omc_window_switchnow carries the plain-name window UUID as well as the prefixed one. The client prefers the plain name, so leaving it behind pointed a handler at the window the test had just left.
New and wider test coverage
- An engine test drives the whole stack at once: the engine dispatches a Python handler, which imports the modules from where AppletBuilder installs them and drives a real ActionUI window over the real bridge, and the test then asks the engine independently whether it holds what the module wrote.
- New engine tests for the bridge host (
OMCActionUIRemoteTests) and the token-carrying spawn (OMCPopenTests). This also fixes five engine tests that had been failing since ActionUI turned on the token requirement. - A unit suite for the
omcmodule (`Tools/omc_py...
OMC version 5.2.1
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.2.1 is a point release on top of 5.2. It carries one security fix: the embedded Python bytecode cache no longer lives in world-writable /tmp - along with a find bar for RichText and Chat documents, a richer Chat element, an indeterminate linear progress bar, a test suite for AppletBuilder itself, two new documents written for AI agents, and an embedded Python refreshed to 3.14.7.
Security
The Python bytecode cache moved off world-writable /tmp
Every OMC applet with an embedded Python interpreter used to run with PYTHONPYCACHEPREFIX set to the compile-time literal /tmp/Pyc. On macOS /tmp is world-writable: its sticky bit stops one user from deleting or renaming another user's entries, but not from creating new ones. CPython validates a cached .pyc only against the mtime and size of its .py source, and an applet bundle is world-readable, so both of those are trivially reproducible by anyone with a local account.
This fix lives in Abracode.framework, so an applet picks it up when it is rebuilt against 5.2.1. If you ship applets with embedded Python, rebuild them with AppletBuilder 5.2.1. You may also want to remove any stale /tmp/Pyc left behind on machines you control.
A second, smaller instance of the same class is fixed in AppletBuilder: five hardcoded /tmp paths in its own scripts moved to ${TMPDIR:-/tmp}, including a world-writable path used for the converted help cache.
Highlights
Find in ActionUI RichText and Chat documents
Both the RichText and Chat elements gained a find bar and a programmatic search channel.
"showFindBar"(Boolean) puts a Cmd-F find bar over the element. Cmd-G and Shift-Cmd-G step forward and back, Escape closes, and the bar's menu toggles case-sensitive, whole-word, diacritic-insensitive, and regular-expression matching. It defaults tofalseonRichTextandtrueonChat, so every existing Chat document gains it.states["search"]lets the host drive the search instead. A non-empty string highlights that term - and, when the find bar is enabled, shows it in the bar without stealing focus. An empty string clears. The value is re-delivered on every states change, so a repeated identical value is ignored, which lets a reader dismiss the bar without it reopening.
In Chat the search spans the whole conversation: Cmd-G walks hits across messages, opening folded thought and tool cards that contain one and scrolling the hit into view, and the menu can widen the scope to thoughts and tool calls. Markdown bodies are searched as rendered text, so a styled **fox** is found by typing "fox". Deleted, streaming and still-running items are skipped until they settle.
ActionUI ProgressView: an indeterminate linear bar
ProgressView used to infer its shape purely from data - a value present meant a bar, absent meant a spinner - which left an indeterminate linear bar unreachable. A new "progressViewStyle" property takes "automatic" (the default, preserving the old behavior), "linear" (always a bar, determinate or not) or "circular" (a spinner or ring gauge).
AppletBuilder tests itself
AppletBuilder now ships its own omctest suite under Distribution/Tests/, 299 checks across four files, with a reference in Distribution/Tests/README.md:
Two new documents written for AI agents
-
omc_applet_catalog.mdis a classified inventory of the public first-party applets - 18 repositories undergithub.com/abra-code- written for an agent that is about to build a new applet. The skill teaches the format; the catalog tells you which existing app to copy from. It covers where the applets live and what is tracked versus added at build time, which applets are legacy Interface Builder nibs and therefore not agent-editable, a "I am building X, clone Y" quick-pick table, applet families, a technique cookbook mapping each UI and scripting pattern to the applet that demonstrates it, a full matrix, known gaps, and a suggested reading order. -
nib_to_actionui_migration.mdis an 11-step guide to porting a nib-based applet to ActionUI, each step with a verification gate before moving on: assessing the source, reusing the nib'stag=values as ActionUI ids, value-representation traps, layout verified withappletbuilder preview --screenshot, replacing editable combo boxes (ActionUI has no native equivalent - the guide pairs aTextFieldwith a companion dropdown at1000 + the field's id, and weighs a menu-stylePickeragainst aMenufor the dropdown itself), drag and drop onto a text field, manifest and command wiring verified withappletbuilder validate, shell-quoting the assembled command, building JSON from shell, tests, and the final hand-off to a human. It ends in a symptom index.The skill previously told agents that nibs "cannot be worked on," with no path forward. It now points at this guide: if the ask is to replace the nib rather than maintain it, that port is an agent-doable job.
Both documents ship inside AppletBuilder.app/Contents/Resources/Documentation/ as well as in the repo.
New in the ActionUI Chat element
The bundled Chat component moved from ChatView 0.5.6 to 0.6.2, which brings more than the find feature:
ChatImageItemnow carries sender, time, delivery state and reactions the way a file item does, groups into sender runs, shows the sender name, and has animageAddedevent that upserts by id.- Captions on photos and files. A new
captionfield onChatImageItemandChatFiletakes Markdown and is searchable as a body. A captioned photo becomes a full bubble, and photo and file bubbles gain "Reply" alongside "React". - Reactions on file and voice items, previously message-only, over a shared badge and menu implementation.
- A reaction badge now reserves space above a reacted bubble instead of stealing it from the gap above, the pill styling is flattened, and the macOS context menu's reaction entry is a labeled "React" menu rather than an unlabeled control group.
The underlying ActionUI RichText component moved from 0.1.1 to 0.2.3. Besides the search engine behind the find bars, that range adds regular-expression matching and fixes a TextKit 2 bug where highlight changes did not repaint until the next scroll.
Build hygiene
Development junk is swept before codesigning
Junk present at codesigning time is sealed into CodeResources and cannot be removed afterward without invalidating the signature. Both bundles are now swept immediately before their own signing step:
update_appletbuilder.shsweepsAppletBuilder.app, a fully regenerated build product, so its list also includes.gitand.svn.clean_build_junk()inlib.build.shsweeps a user's applet duringapplet_build(), since an OMC applet's.appbundle is the project. There.gitis deliberately preserved and only reported, and reported quietly, so a versioned applet does not pop an error window on every build.
The sweep covers Finder droppings, __pycache__ and .pyc, .pytest_cache, editor and agent scratch directories, and thin_distribution.sh leftovers. It matches symlinks as well as real files, uses case-insensitive matching so thumbs.db is caught on a case-insensitive volume, and resolves a symlinked bundle path to its physical path first, because find will not descend into a symlinked start path. Counts are reported as a before/after delta rather than a pre-scan, because rm and find -delete swallow errors - a failed sweep used to print green. A surviving item now halts the script.
For scale: there were 26 .claude directories inside .app bundles under the development tree when this was written.
AppletBuilder keeps Python's bytecode cache out of its own bundle
AppletBuilder imports three in-bundle Python packages plus its shipped standard library, and nothing redirected the cache, so Python wrote __pycache__ directories next to each of them. A single appletbuilder validate wrote 14 of them into AppletBuilder.app, and opening Help added four more; each one breaks the signed bundle's seal. Most were invisible, because Contents/Library/Python and Contents/Library/mistune are gitignored wholesale - only 2 of the 14 ever showed up in git status.
lib.common.sh now exports a PYTHONPYCACHEPREFIX default pointing outside the bundle, sourced by every AppletBuilder process including the appletbuilder agent CLI. An inherited value is never overwritten, so the engine's per-uid prefix and omctest's per-run scratch both still win.
Embedded Python 3.14.7
The embedded Python distribution shipped in AppletBuilder - and copied into every new Python applet - moves from 3.14.6 to 3.14.7.
Compatibility notes
- The bytecode-cache fix changes where an applet's
.pycfiles are written, from a single shared/tmp/Pycto a per-user directory under/var/folders/. Anything that read or cleaned/tmp/Pycneeds updating. Documentation and the skill now useexport PYTHONPYCACHEPREFIX="$TMPDIR/Pyc"in their pip-install snippets instead of the old/tmp/Pyc. Chatgains the find bar by default ("showFindBar"istrue). Set it tofalseif you do not want Cmd-F bound inside a chat view.ProgressViewdocuments that omit"progressViewStyle"are unaffected -"automatic"is exactly the previous behavior.OMCTEST_API_VERSIONis unchanged at 6. Suites written for 5.2 run as-is.
Documentation and skill
- New
Documentation/omc_applet_catalog.mdandDocumentation/nib_to_actionui_migration.md, both also bundled in AppletBuilder. - The skill's reference table points at both, and its NIB paragraph now offers the migration path instead of a dead end.
omc_python_scripting_guide.md,omctest_guide.md,SKILL.mdand the skill's co...
OMC version 5.2
OMC 5.2
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.2 is a testing and tooling release. Its centerpiece is omctest, a headless test harness that runs an applet's handlers against a mock OMC environment, wired into AppletBuilder as a Test button and an appletbuilder test subcommand. Around it, the command-line plister tool got three rounds of crash and data-loss fixes plus content-based format sniffing, codesign_applet.sh learned to preserve entitlements on nested code, applets launch faster and ship smaller, and includes latest ActionUI with add-ons.
Highlights
omctest: a headless test harness for applet handlers
Applet handlers can now be tested without a GUI and without a human clicking through dialogs. omctest runs a suite of shell or Python tests against a mock OMC environment: recording stubs for omc_dialog_control, plister and pasteboard, simulated alerts and command chains, input simulation, a check vocabulary, and virtual-window readback.
- The harness ships as
Agents/omctest.shinside AppletBuilder.app, with a full test-author reference atDocumentation/omctest_guide.md(API tables, verb replay table, seam contract, fixtures, troubleshooting). omc_control_defaults <document>resets a window to the values its ActionUI document actually declares, rather than to a blank state. This matters because many applets ship non-default toggle states, and a blank reset silently tests the wrong thing.chains_resetresets chain state between tests,ui_declare_idsdeclares runtime-minted view ids, andomctest_isolation_lintwarns about$HOMEanddefaultspaths the harness cannot intercept.$HOMEis isolated per test file, so a test that saves settings no longer writes into your real~/Library/Application Support.- Named pasteboards are emulated as files in the per-run scratch directory instead of being proxied to the per-login pasteboard server. That removes the last Mach-service dependency and makes suites runnable under the App Sandbox.
- A test file that runs zero assertions is now a failure, and a suite reports
CRASHandINCOMPLETEdistinctly instead of collapsing both into a generic failure.
OMCTEST_API_VERSION is currently 6. Assert a minimum in your suite if you rely on something recent: 2 added omc_control_defaults, 3 isolates $HOME, 4 isolates named pasteboards, 5 exports OMC_APP_PROCESS_ID, 6 makes named pasteboards files and drops OMCTEST_PB_PREFIX.
AppletBuilder runs the suite for you
- A Test button joins Build and Run in the Build & Run pane, streaming the suite's transcript into the same log.
- The pane gained a status row: an indeterminate spinner while a Build or Test runs, then a one-line colored verdict with the tally, for example "Tests failed - 649 passed, 1 failed, 8 files". Orange calls out the case where nothing ran at all, which otherwise reads as success.
appletbuilder build --testruns the suite after the runtime refresh and before signing, and halts the build if it fails. This is deliberately opt-in rather than folded into a plainbuild.appletbuilder validatenow emits an informational note when an applet has noTests/directory.
validate cross-checks ActionUI action ids against the command list
command_verifier now resolves every ActionUI actionID and *ActionID value against the applet's command list. It warns when a control's action id resolves to no command, and separately flags a case-only mismatch, which the engine treats as a dead control because dispatch is case-sensitive. This turns a manual by-eye step into an automated one. It was validated across 25 applets plus AppletBuilder itself, roughly 460 action ids, with no false positives.
plister: crash and data-loss fixes, and format sniffing
plister is the plist and JSON manipulation tool handlers lean on, and it went through three review rounds:
- Format sniffing. When a file's extension does not identify the format,
plisternow determines it from the content, trying JSON first and falling back to the plist parser. Custom project extensions work without a format flag. append dictandappend arraynow work; previously only scalars could be appended.- The on-disk format is preserved on write. A binary plist stays binary instead of being normalized to XML on every modification.
- Six crashes on non-UTF-8 bytes and missing arguments are fixed, across integer, real, boolean and date argument parsing, file paths, property paths, and insert keys.
- A failed save no longer reports success.
plisterreturned exit 0 after a write that did not happen; so diditerateafter a failed modifying subcommand. - A file that does not parse is no longer destroyed.
plister set dict <file> /previously overwrote an unparsable file with an empty container. - A
.or..component in a property path no longer hangs; it was allocating about 2 GB in 4 seconds.
New features
OMC_APP_PROCESS_ID
A new always-exported variable, $OMC_APP_PROCESS_ID (template form __APP_PROCESS_ID__), resolves to the process id of the OMC host process itself: the applet, OMCService, the Shortcuts observer, OMCEdit, or AppletBuilder. No ENVIRONMENT_VARIABLES declaration is needed.
This is distinct from the existing $OMC_FRONT_PROCESS_ID, which is the frontmost application and may be unrelated to the applet. Use $OMC_APP_PROCESS_ID when a handler needs to know whether the instance that owns some state is still alive, for example to avoid orphaning a spawned server process.
New ActionUI navigation elements
NavigationSplitView and NavigationStack are now available and documented for OMC dialogs, giving multi-pane sidebar and detail layouts and push-style navigation stacks. Each has two forms: static panes, or selection-driven destination switching via destinationViewId.
codesign_applet.sh: entitlements carried forward, plus two new flags
- Existing entitlements are preserved on re-signed nested code. Frameworks, helpers and XPC services previously had their entitlements silently stripped when re-signed, so a helper that needed something like
allow-jitwould crash the first time it ran after signing. --list-codeprints every path the script would sign, one per line, and exits without touching anything, so a caller can check the same set.--no-entitlements-searchuses only the entitlements file passed explicitly, never auto-discovering a stray neighboring.entitlementsfile.- Nested-bundle detection now works by structure as well as by extension, so a structurally-bundled but oddly-named item is signed correctly, versioned frameworks are signed per-version, and resource-only bundles such as SwiftPM privacy manifests are correctly left unsigned.
- The entitlements cache moved from a hardcoded
/tmpto$TMPDIR.
thin_applet_python.py
A new plan and apply tool shrinks an applet's embedded Python by tracing the bundle's own scripts directly, with no hand-written per-applet workload to maintain. Measured on the current applets: Zip 69.5 MB to 42.6 MB, ICEdit 56.5 MB to 38.0 MB, Watchdog 61.0 MB to 38.0 MB, Cadabra 108.2 MB to 89.9 MB, AIChat 47.5 MB to 29.1 MB.
Faster applet launch
The launch-time compileall of an applet's Resources/Scripts is disabled. It could never help, because CPython does not consult a bytecode cache for __main__, and it cost every launch between 24 and 123 ms to save at most 3 ms once. Handlers behave identically; cold launch is measurably faster.
Security
Bundled Chat component: redirects refused on the ACP handshake
The bundled Chat component moved from ChatView 0.2.1 to 0.5.6. As of 0.4.5 its remote ACP WebSocket client refuses redirects on the connection-upgrade handshake. Previously a followed redirect could hand the code-execution bridge credential to whatever host the redirect named, in cleartext over ws://.
The same version arc also fixed a recurring AppKit layout-loop crash that could kill a chat mid-answer, and added composer and conversation-summarizer support.
Compatibility notes
The plister fixes above correct real bugs, but a handler that worked around one of them may need a look:
- A plist that
plisterused to rewrite as XML on every modification now keeps its original format. Tooling that assumed "plister always normalizes to XML" no longer holds. - A bare
true,42, ornullat the root of a file with an unrecognized extension is read as a plist string, not sniffed as JSON. plister set dict <file> /, the documented create-if-missing idiom, now refuses to run against an existing file that fails to parse instead of overwriting it. A handler that relied on the old overwrite behavior will now see a failure.
omctest suites written against API version 2 or lower still run, but tests that read or write real user preferences may behave differently now that $HOME is isolated per test file. That is the intended fix, and it is worth re-reading such tests rather than assuming they still assert what they used to.
Documentation and skill
- New
Documentation/omctest_guide.md, the test-author reference for omctest. - New Skill section, "Testing an Applet (omctest)", in the claude and capable flavors.
OMC_APP_PROCESS_IDdocumented in the runtime-context reference, inbuilding_omc_applet.md, and in the Skill.- The action id cross-check is documented in
appletbuilder_user_guide.mdand in the agent troubleshooting checklist. - ActionUI schema and documentation refresh for
Chat,NavigationSplitView,NavigationStack,CommandMenu, andView, whosehiddensemantics are now stated explicitly: a hidden view is laid out but invisible, not collapsed.
Also in this release
- AppletBuilder's own application icon and several bundled template icons...
OMC version 5.1.0
OMC 5.1
OMC 5 requires macOS 14.6 or later. For older macOS versions, use OMC 4.x.
OMC 5.1 is a feature release focused on making applets easier for both people and AI agents to author: command manifests and menu bars move to JSON, the applet builder gains a headless command-line front end, and a family of new ActionUI dialog elements (Quick Look, Chat, rich text, diff, cached image, toast) becomes available to dialogs. It also lands several runtime correctness fixes and a security hardening of the engine's inter-process files.
Highlights
Command.json replaces Command.plist
Applet command manifests are now authored as Command.json. JSON is 1:1 equivalent to the old plist, is far friendlier to AI agents and to hand editing, and diffs cleanly in source control.
- The engine (Abracode.framework) resolves
Command.jsonbeforeCommand.plistwhen loading an applet, preferring JSON when both are present. ExistingCommand.plistapplets keep working unchanged. - AppletBuilder now creates new applets with
Command.json, ships JSON templates, and branches command editing and validation on the file format. - Validation gained a heuristic to tell an ActionUI JSON file apart from
Command.jsonand other non-ActionUI JSON, so command manifests are no longer mistaken for UI files.
MainMenu.json replaces MainMenu.nib
Applet menu bars can now be described in MainMenu.json, powered by ActionUI, instead of a compiled MainMenu.nib. The JSON describes modifications to the default app menu bar that ActionUI constructs: menus and items can be added, modified, or removed by targeting a standard item with replacing.
- AppletBuilder itself now ships nibless: its menu bar is built programmatically plus
MainMenu.json, and new applets are created withMainMenu.json. - New documentation covers the
MainMenu.jsonformat and a step-by-step guide for converting an applet fromMainMenu.nibtoMainMenu.json. - OMC 5.1 is required to run applets that use the new menu-bar format.
Help viewer rebuilt on ActionUI
The applet Help viewer was switched from a nib to an ActionUI window using a new WebView implementation that is stable on macOS 14.6 and no longer crashes.
AppletBuilder agent CLI
AppletBuilder now bundles a command-line front end, Contents/Resources/Agents/appletbuilder, that drives the same create / validate / build / prettify / preview / list operations as the GUI. It shares the GUI's build, create, and validation logic through a single source of truth (a reporter indirection over the shared shell libraries), so headless builds and interactive builds behave identically. This makes AppletBuilder scriptable and usable by AI agents.
New dialog elements and controls
New ActionUI elements
Abracode.framework now links and registers additional ActionUI add-on elements, so OMC dialogs can use:
- QuickLook - a file-preview element wrapping the system Quick Look view.
- Chat - a conversational message-list element.
- RichText - formatted text rendering.
- Diff - side-by-side / inline text diff.
- CachedImage - an image element with asynchronous loading and caching.
The bundled ActionUIViewer preview tool and the JSON verifier schemas were updated so dialogs using the new elements preview and validate out of the box.
Toasts: omc_present_toast / omc_dismiss_toast
omc_dialog_control gained two verbs for transient, non-modal feedback:
omc_present_toast <message> [duration_seconds] [action_title] [action_id]- shows a top-pinned snackbar that fades in, stays a few seconds (4 by default), then auto-dismisses. Supplying bothaction_titleandaction_idadds one inline button that dispatches the given subcommand when tapped.omc_dismiss_toast- dismisses it early.
Use a toast for unobtrusive "done" feedback that does not steal focus the way an alert does.
Programmatic Table/List row selection
omc_dialog_control can now select a Table or List row from a script without overwriting the rows (previously the only way to "select" was to set the control value, which replaced all rows with a single string):
omc_select_row <0-based index>- select by index (out of range clears the selection).omc_select_row_with_content <text> [1-based column]- select the first row whose column equals the text (exact, case-sensitive; omit the column or pass 0 to match any column).omc_deselect- clear the selection.
The verbs work on both Table and List, on both the ActionUI and legacy Nib window paths, and fire no selection actionID.
Windows respect SwiftUI content size limits
ActionUI dialog windows now respect the content's SwiftUI minimum and maximum size limits, so dialogs no longer resize past what their content supports.
Runtime fixes
- Applet command execution no longer blocked by contextual-menu activation gating. An applet whose main command used a file activation mode (
act_file/act_folder/act_file_or_folder) withUSE_NAV_DIALOG_FOR_MISSING_FILE_CONTEXT=falsewould silently fail to run when launched with no file context. Explicit command execution now bypasses the activation match that is only meant for contextual-menu / service population; contextual menus, OMCService, and the test app keep the previous gating. - Main command resolvable by
<NAME>.mainandmain. An explicitCOMMAND_ID=<NAME>.mainnow behaves exactly like a missing command id, andNEXT_COMMAND_ID/ subcommand references can address the main command by<NAME>.main,main, or the legacytop!. This fixes applets that set an explicit main-command id and then failed to launch or chain to themselves. The command verifier recognizes the main command declared either way. - INPUT_DIALOG text (e.g. passwords) no longer lost in chains. Text entered in an
INPUT_DIALOG(includinginput_password_text) is now carried into the per-object runtime data copy, so a subcommand in a chain receives it viaOMC_DLG_INPUT_TEXTinstead of an empty value. - DEFAULT_LOCATION tilde paths are expanded. A
DEFAULT_LOCATIONof~(or any path with a tilde) in the choose-file, choose-folder, and choose-object dialogs now opens at the intended directory instead of the disk root. - Fixed a crash in the input dialog.
- Removed a stale fixed icon from the progress dialog.
Security and robustness
- Engine IPC files moved off world-writable /tmp. The small files the engine passes between Abracode.framework and its spawned tools (
omc_dialog_controldialog values,omc_next_commandchaining) were written to a world-writable/tmp/OMCdirectory with no sticky bit, which allowed a local user to delete, replace, or redirect them, and the create sequence was TOCTOU-exploitable via a pre-planted symlink. All four touch points now resolve one path helper that builds<per-user temp dir>/OMC/<leaf>and creates theOMCsubdirectory mode 0700, private to the user. This works correctly across processes, including under the App Sandbox, because the tools inherit the app's$TMPDIR. omc_popennow marks a pipe file descriptor close-on-exec.- Fixed a leak in
OmcHostTaskManageron the throw path, plus additional low-severity robustness hardening and correctness fixes surfaced in review. - Fixed a batch of build warnings: deprecated pasteboard, activation, and document APIs.
Tooling and packaging
- codesign_applet.sh reworked for layout-independent deep signing: it discovers and signs nested code and loose non-code files regardless of bundle layout, adds a
--briefmode, and supports an entitlements override. This is the recommended way to deep-sign a built applet (replacing the deprecatedcodesign --deep). - build.sh reimagined to build the single "OMC Distribution" scheme (Abracode.framework, OMCApplet, OMCService, OnMyCommandCM together) instead of looping over every sub-project, and to propagate xcodebuild's exit code.
update_appletbuilder.shfixes and rework to build the add-on-aware ActionUIViewer and bundle the new ActionUI add-on documentation and verifier schemas; Python is no longer updated by default.- SPM package dependencies are now pinned to versioned releases (ActionUI core, Chat, RichText, Diff, CachedImage, Quick Look) rather than moving checkpoints.
- The AppletBuilder build warns when
.profrawcoverage files are left in the applet bundle. - Updated README and expanded documentation, including main-command id aliases and corrected file-panel documentation (
DIALOG_IDENTIFIER,DEFAULT_LOCATION). - The bundled AI agent skill was rebuilt and its activation criteria broadened.
In this distribution
- AppletBuilder.app - OMC applet development studio.
- OnMyCommandCM.plugin - contextual menu plugin for use with Shortcuts.app; commands load from
~/Library/Preferences/com.abracode.OnMyCommandCMPrefs.plist. - OMCService.service - macOS service template for standalone OMC-based system services.
- Skill/ - the OMC AI agent skill (three flavors) plus its installer.
- Scripts/ -
codesign_applet.sh,install_contextual_menu_plugin.sh,thin_distribution.sh,OMCApplet.entitlements, and the examplecom.abracode.OnMyCommandCMPrefs.plist.
See the main OMC README at https://github.com/abra-code/OMC/ for full documentation on commands, runtime context, dialogs, and services.
OMC version 5.0.2
OMC 5.0.2
The minimum macOS requirement for OMC 5.0+ is macOS 14.6
Update ActionUI library to 0.6
Create Command.plist verifier and add it to AppletBuilder and OMC skill
Added script validation for .sh, .zsh, .py, .applescript and allow manual validation in "Scripts" pane
Added Info.plist validator
Enabled all validations for all elements when building the applet: ActionUI json files, scripts, Command.plist, Info.plist
Added binaries thinning when building
Added a couple of icons for new applets
OMC version 5.0.1
OMC 5.0.1
The minimum macOS requirement for OMC 5.0+ is macOS 14.6
- Update ActionUI library, which includes new verifier in AppletBuilder
- Better support for embedded Python with PYTHONPYCACHEPREFIX, PYTHONPATH & PATH exported also for .sh scripts
- Updated embedded Python to 3.14.5
- Create OMC "skill" for AI agents with flavors for claude, capable (other frontier models), and lite (for local models)
- Documentation update