Skip to content


(Long-term) More powerful custom-action API; non-AppleScript custom actions #947

daniels220 opened this Issue · 4 comments

3 participants


More v.2 ideas (they seem to be getting longer but this is the last one, I promise :/):

I've wanted for a while to be able to write QS actions in Ruby or straight shell scripting. I understand it's a little tricky to handle files/other types correctly, but it'd be okay with me if QS made you jump through hoops. Here's a skeleton API idea:

  • Everything gets a prefix indicating its type, like QSFile or QSEmailAddress. Maybe even just straight text gets a QSText prefix, so that no escaping needs to be done later (i.e. if the user actually types "QSText:abc", the script would get "QSText:QSText:abc" and know that the second one is just literal text).
  • Input goes in the args. As per above a file argument looks like QSFile:(UNIX path). Direct and indirect objects are separated by --, so if there's more than one they're just in separate args, i.e. ARGV could look like ["QSFile:...","QSFile:...","--","QSText:..."], meaning two files comma-tricked and some text in pane 3.
  • Calling the script with "--specs"/"--args" or something as the sole argument should cause it to return (output to stdout) a string like TYPE[:INDIRECT_TYPE][->RETURN_TYPE], indicating the types of arguments it expects and may return. Perhaps an action that expects to be long-running could append "--long" to the end of that, indicating it should get an entry in the task viewer, the way "Run Applescript" has.
  • Either:
    • Output to stdout is interpreted as the returned object(s), ignored if there are none allowed, and if the action wants informational output it uses a CLI Growl interface or something, OR
    • Output to stdout is sent to the default notifier (i.e. almost always Growl), and the action is passed the path to the qs command line tool (probably in ARGV[1]/$1) and invokes that to return arguments. (QS could keep the PID of action scripts it spawns and have qs check the caller PID to match with an action, in case it wants to know whether this is user CLI use or an action returning.)
  • Output to stderr is displayed, one line at a time, in error dialogs (modal or not depending on #946). Maybe ending a line with a literal backslash can get you a two-line dialog. Alternatively use qs --err or something to send the user messages, leaving stderr to be sent to the debugging log if QS was built in debug mode (otherwise ignored entirely).
  • Complicated objects (entire contacts, for instance) are represented by (XML/JSON/YAML). This is a mess for shell scripts to parse, but Ruby/Python/etc. can handle multiline strings in ARGV just fine.
    • Alternatively, place complex objects on stdin (still in some data format), and put in the arguments something like QSContact->q34890fjlsaf (i.e. a multipart-MIME style boundary for where on stdin that object lives—also note the different delimiter, in this case -> instead of :, to indicate "look on stdin for the actual object").

So that's for shell. Totally just a skeleton, feel free to change. For AppleScript it's a lot easier—I'd like to see two things:

  • An overhaul of on get argument count to support fully specifying argument types and number, so I can have an action that is forced to take, e.g. a file in the first pane and text in the third. (Right now 3-pane actions with a file in the first pane are impossible to have at all, and there's some other buginess with 3-pane actions.) Maybe better to call it "on get argument types", to leave backwards compatibility.
    • Similarly the function that actually runs might want to be called "on process objects", leaving "on open" and "on process text" free for legacy actions.
    • Comma-tricked objects can thankfully be passed directly as native AppleScript arrays, so you can spend a lot less time parsing input.
  • Some extra commands inside tell application "Quicksilver" blocks for error and informational messages, and preferably the ability to silently run other QS actions (with something like tell application "Quicksilver" to run action "Name" with object <obj> [indirect object <obj>]). It'd be neat to be able to do this via qs, as well, with something like qs (filename) --action (action name) [--indirect (text-or-file)].
  • Complicated objects might have to be represented as AppleScript-native arrays-of-hashes-of-arrays with a "QSType" key at the top level or something. If it's possible to create native AppleScript "classes" on the fly, that'd be even better, although harder for plugin developers, since they'd have to do it themselves as well. Some objects probably already have an AppleScript class for them, i.e. you could pass contacts as the original AddressBook object that you can get at via AS.

For either shell or AS actions, it's up to the developer to know what the type prefixes are and what kind of hierarchy they're in (i.e. if I ask for QSText, I may get QSEmailAddress or QSURL, named as such). This would need to be documented somewhere but I don't think there's all that much. I could probably write a Ruby gem to handle the parsing automatically, that'd be a neat project.

Again, sorry for the feature-request spam—I totally understand if none of this gets implemented anytime soon. One of these days I may get around to learning Objective-C and helping out myself, too...

Quicksilver OS X member

What's the issue with running these scripts from an AppleScript?
If you can get all the arguments you need into the AppleScript, (and define the types the action runs on, which will be added soon) is that not enough?

See #1048

I'm going to close this for now as a "won't fix", since with #1048, AppleScript actions are now much more mature, and it'd be hard/unrealistic for us to implement another method.

For the 2nd half of your issue (relating to AS), most of those things have now been added.

The limitations still present are:

  • Currently only string or file representations of objects are passed to the AS. This is generally good enough though
  • I like your idea of being able to tell application Quicksilver to do action "action". But I'm not sure how this would work in reality - the "action" would have to be the identifier and not the name (because of localisation reasons) so then we'd have to expose the user to all the keys in this list which is pretty ugly. :(

Feel free to comment/reopen. I'm just stating that the AS API is much improved (please test out #1048 if know how to build QS) and it's unlikely any of the current developers will implement this.
That's the beauty of Open Source... ;-)

@pjrobertson pjrobertson closed this

Is what you can "tell" QuickSIlver documented somewhere? I'm assuming there is at least a way to tell it to open a file/string/URL (and then prompt the user for the action). That would be useful in general, but also for cascading AppleScripts (like the Move action opens the moved object for further action).

Quicksilver OS X member

You can see the what 'handlers' exist in the wiki.
I'll add a section on 'returning' items to Quicksilver. At the moment you can only return strings (which Quicksilver will then inspect and potentially turn into a file/URL/string/email address etc).


The only issue with running shell scripts through AppleScript is speed—when run from the command line, at least, AppleScripts take far longer to start up than a shell/Ruby/Python interpreter. This may not be an issue if they're run through Scripting Bridge in an already-running app. In any case, since I wrote the above I have a shiny new computer and haven't noticed any issues with AS actions, so yes, it's fine to close the first half.

As for the AS improvements: THANK YOU THANK YOU THANK YOU, they look amazing.

Regarding "do action", I'm okay with having the action IDs exposed to the "user"—the users in this case are basically developers, and IMO it's better to have the feature even if it's a little ugly. Just link to that file from a page on the wiki so people can find the appropriate keys and I'm happy :). But if it's a lot of trouble to implement, it can certainly wait for a later release.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.