os_intents_cli 0.1.1 copy "os_intents_cli: ^0.1.1" to clipboard
os_intents_cli: ^0.1.1 copied to clipboard

The os_intents command line tool: build, sync, install and doctor. A dev dependency of any app that uses os_intents.

os_intents_cli #

The only part of os_intents that touches a native project. build_runner derives its output paths from its input paths, so it cannot write into ios/ or android/ at all; it stops at a manifest next to the generated Dart and this CLI carries the manifest the rest of the way.

dev_dependencies:
  os_intents_cli: ^0.1.0
dart run os_intents_cli:os_intents build             # all three steps, in order
dart run os_intents_cli:os_intents sync              # manifest → ios/Runner/OsIntents/*.swift
dart run os_intents_cli:os_intents sync --android    # also → android/…/<applicationId>/*.kt
dart run os_intents_cli:os_intents sync --check      # drift guard for CI, writes nothing
dart run os_intents_cli:os_intents install           # register what sync wrote, per platform
dart run os_intents_cli:os_intents install --check   # …and the drift guard for that
dart run os_intents_cli:os_intents doctor            # what will the OS actually see?
dart run os_intents_cli:os_intents doctor --android  # …and what will an agent see?

build #

One command, because the order matters and stopping one step short produces a build that succeeds with no intents in it:

dart run os_intents_cli:os_intents build

It runs build_runner, then sync, then install. Every step is idempotent, which is what makes it safe as the habitual one — run it after any change to an annotation and the two native projects catch up.

--android is forwarded to sync and means exactly what it does there. --no-codegen skips build_runner for a project with a watcher already running. build_runner is started with the same Dart that is running the CLI, so an SDK pinned with fvm stays pinned.

install #

Two one-time edits, one per platform, skipped where the platform is absent:

ios/Runner.xcodeproj adds ios/Runner/OsIntents to the Runner target
AndroidManifest.xml points the launcher <activity> at @xml/os_intents_shortcuts

They are the same kind of thing. sync writes files that both native builds will happily ignore until the project is told they exist, and neither omission produces an error anywhere — just an app whose intents are not there.

Both edits are decided against the parsed project, applied as text so the diff stays small (five lines in the manifest, four per Swift file in the pbxproj), and parsed again before anything is written. On iOS every anchor is an object id looked up in the parsed project, not one of the ids Flutter's template happens to use: those are not a contract, and an anchor that fails to match would leave the sources referenced but in no build phase.

Both are idempotent, repair a project left half-edited, and refuse rather than guess when the project is not a shape they understand — a target under another name (--target), a file already referenced elsewhere, a project using synchronized folders, an app with two launcher activities, an activity already pointing android.app.shortcuts at a shortcuts file of its own. Every refusal names the manual step, which works just as well: the rest of the toolchain does not care how the registration happened, and doctor confirms the result either way.

--no-xcode and --no-manifest restrict it to one platform. Backups are left next to each edited file, as *.os_intents.bak.

install --check #

The companion to sync --check, and the half that catches a different failure. sync --check proves the generated files match the manifest; this proves the native projects reference them:

  missing: Runner does not compile these, so they reach the build as nothing:
    • OsIntentsEnums.swift

Writes nothing, exits non-zero, and reports both platforms rather than stopping at the first. The case it exists for is not hypothetical: this check was written after a new emitter output — OsIntentsEnums.swift — turned out to have been generated into the example and never registered, so it compiled into nothing while build_runner, sync and Xcode all reported success.

doctor #

Everything else in the toolchain reports on its own step. build_runner says it generated Dart, sync says it wrote Swift, Xcode says it compiled — and the intents can still be invisible, because the generated folder was never added to the Runner target, or because the app already had an AppShortcutsProvider and iOS silently kept that one instead. Neither failure produces an error anywhere.

doctor reads Metadata.appintents/extract.actionsdata out of a built bundle — the file the OS itself indexes — lists what is in it, and compares that against the manifests:

Intents the OS will see (3)
  AddTaskOsIntent  "Add task"
      Creates a new task in the Inbox
      runs without opening the app
      title         String          required
      dueDate       Date            optional
      project       ProjectEntity   optional

Entities (1)
  ProjectEntity  "Project"  resolved by Runner.ProjectQuery

Spoken phrases (3 via Runner.OsIntentsShortcuts)
  AddTaskOsIntent
      "Add a task to ${applicationName}"
      "New ${applicationName} task"
  root.ssu.yaml  present — Siri has the phrase model

It exits non-zero when something declared in Dart did not reach the bundle. Warnings — a stale build, an optionality mismatch — are reported but do not fail.

Build first; doctor reads a bundle, not source:

flutter build ios --simulator --debug
dart run os_intents_cli:os_intents doctor

--app points it at a specific bundle, -C at a project root other than the working directory.

The metadata format is Apple's and undocumented. Every field actions_data.dart reads was observed in a bundle iOS actually indexed, and the parser reports what it does not recognise instead of guessing — a type #12 in the output means the format moved and the reader has not caught up.

doctor --android #

The same question on the other platform: an agent either sees your functions or it does not, and nothing in the build says which.

flutter build apk --debug
dart run os_intents_cli:os_intents doctor --android
AppFunctions the OS will see (2)
  addTask
      Creates a new task in the Inbox
      title         String          required
      dueDate       Long            optional
  dueToday
      Tasks due today

App shortcuts XML: packaged

It reads the APK, not the sources — sync --check already proves the files on disk match the manifest, and only the artefact can say they were packaged. The AppFunction metadata is plain XML in assets/, so no binary XML decoding is involved and the reader stays pure.

What it catches: a headless intent that never reached the metadata, a description that stopped matching the one in Dart (they travel as KDoc, so a stale build shows up here), a function left over from an earlier build that an agent may still offer, and a missing shortcuts XML.

And one that is easy to get wrong by hand: optionality. The generated Kotlin lists every property in <required>, but androidx.appfunctions treats a nullable property as optional however it is listed — so doctor reports what the runtime will enforce, not what the file says.

An APK with no AppFunction metadata is reported as a note rather than an error. That is the default state: the layer is opt-in behind sync --android.

doctor --device #

The third question, and the only one that needs a running device:

dart run os_intents_cli:os_intents doctor --device
On the device, as com.example.tasks
  dueToday        Tasks due today
  openInbox       Open inbox

sync --check proves the generated XML matches the manifest. --android proves it was packaged. This proves the system accepted it — three different things that fail separately.

ShortcutManager drops a shortcut it does not like without reporting it, caps how many it will hold, and resolves every label through the resource table. So the check worth having is the label: if it comes back as os_intents_addTask_label_short instead of "Add task", the string resource never made it into the build, and nothing but the device can tell you.

It reads adb shell dumpsys shortcut and needs the app installed. adb is found on PATH, via ANDROID_HOME / ANDROID_SDK_ROOT, in the usual SDK location, or wherever ADB_BIN points. No device, no adb, or no applicationId are reported as warnings and not as errors — "could not ask" is not the same answer as "your shortcuts are broken".

0
likes
0
points
315
downloads

Publisher

unverified uploader

Weekly Downloads

The os_intents command line tool: build, sync, install and doctor. A dev dependency of any app that uses os_intents.

Repository (GitHub)
View/report issues

Topics

#app-intents #siri #shortcuts #codegen #cli

License

unknown (license)

Dependencies

archive, args, crypto, os_intents_gen, path, xml

More

Packages that depend on os_intents_cli