Documentation Index Fetch the complete documentation index at: https://mintlify.com/yocxy2/RCLI/llms.txt
Use this file to discover all available pages before exploring further.
Actions are the core of RCLI’s macOS integration. Each action is a C++ function that executes via AppleScript, shell commands, or native APIs, and is automatically exposed to the LLM for tool calling.
Action System Overview
RCLI’s action system consists of:
ActionRegistry — Registers actions and dispatches execution
ActionDef — Defines action metadata (name, description, JSON schema)
ActionFunc — Function that executes the action and returns a result
Tool Engine — Parses LLM tool calls and routes to the registry
Actions live in src/actions/ and are registered in src/actions/action_registry.cpp.
Action Anatomy
Every action consists of three parts:
Implementation function — executes the action
Registration call — registers with the registry
JSON schema — defines parameters for the LLM
Example: Create Note Action
Here’s the create_note action from src/actions/notes_actions.cpp:
src/actions/notes_actions.cpp
inline ActionResult action_create_note ( const std :: string & args_json ) {
// Parse JSON parameters
std ::string title = json_get_string (args_json, "title" );
std ::string body = json_get_string (args_json, "body" );
std ::string folder = json_get_string (args_json, "folder" );
if ( title . empty () && body . empty ())
return { false , "" , "Title or body required" , "{ \" error \" : \" missing content \" }" };
// Try memo CLI if available
auto check = run_shell ( "which memo 2>/dev/null" );
if ( check . success ) {
std ::string cmd = "printf ' %s ' '" + escape_shell ( body . empty () ? title : body)
+ "' | memo notes -a '" + escape_shell (title) + "'" ;
auto r = run_shell (cmd);
if ( r . success )
return { true , "Created note: " + title, "" ,
"{ \" action \" : \" create_note \" , \" title \" : \" " + title + " \" , \" status \" : \" created \" }" };
}
// Fallback to AppleScript
std ::string script =
"tell application \" Notes \"\n "
" tell folder \" " + escape_applescript ( folder . empty () ? "Notes" : folder) + " \"\n "
" make new note with properties {name: \" " + escape_applescript (title) +
" \" , body: \" " + escape_applescript ( body . empty () ? title : body) + " \" } \n "
" end tell \n "
"end tell" ;
auto r = run_applescript (script);
if ( r . success )
return { true , "Created note: " + title, "" ,
"{ \" action \" : \" create_note \" , \" title \" : \" " + title + " \" , \" status \" : \" created \" }" };
return { false , "" , "Failed to create note: " + r . error , "{ \" error \" : \" " + r . error + " \" }" };
}
void register_notes_actions ( ActionRegistry & registry ) {
registry . register_action (
{ "create_note" , "Create a new note in Apple Notes" ,
"{ \" title \" : \" note title \" }" ,
true ,
"productivity" ,
"Create a note called Meeting Notes" ,
"rcli action create_note '{ \" title \" : \" Meeting Notes \" }'" },
action_create_note);
}
Step-by-Step: Adding a New Action
Choose a category
Actions are organized by category in src/actions/:
notes_actions.cpp — Apple Notes integration
reminders_actions.cpp — Reminders integration
messages_actions.cpp — Messages/iMessage
app_control_actions.cpp — Open/quit apps
window_actions.cpp — Window management
system_actions.cpp — System settings (volume, dark mode, lock screen)
media_actions.cpp — Spotify/Apple Music
web_actions.cpp — Web search
browser_actions.cpp — Safari/Chrome control
clipboard_actions.cpp — Clipboard read/write
files_actions.cpp — File search
navigation_actions.cpp — Maps integration
communication_actions.cpp — FaceTime
Pick an existing file or create a new one (e.g., calendar_actions.cpp).
Write the action function
Create a function that matches the ActionFunc signature: src/actions/my_category_actions.cpp
#include "actions/action_helpers.h"
#include "actions/applescript_executor.h"
namespace rcli {
inline ActionResult action_my_feature ( const std :: string & args_json ) {
// 1. Parse JSON parameters
std ::string param1 = json_get_string (args_json, "param1" );
std ::string param2 = json_get_string (args_json, "param2" );
// 2. Validate inputs
if ( param1 . empty ())
return { false , "" , "param1 is required" , "{ \" error \" : \" missing param1 \" }" };
// 3. Execute via AppleScript, shell, or native API
std ::string script = R"(
tell application "MyApp"
-- do something
end tell
)" ;
auto r = run_applescript (script);
// 4. Return success or failure
if ( r . success )
return { true , "Action completed" , "" , "{ \" status \" : \" ok \" }" };
return { false , "" , "Action failed: " + r . error , "{ \" error \" : \" " + r . error + " \" }" };
}
} // namespace rcli
Register the action
Add a registration function in the same file: void register_my_category_actions ( ActionRegistry & registry ) {
registry . register_action (
ActionDef{
"my_feature" , // Action name (used by LLM)
"Description of what it does" , // Human-readable description
"category" , // Category (productivity, media, system, etc.)
R"({"type":"object","properties":{"param1":{"type":"string"},"param2":{"type":"string"}},"required":["param1"]})" , // JSON schema
true , // Enabled by default?
"Example voice command" , // Example for users
"rcli action my_feature '{...}'" // CLI example
},
action_my_feature
);
}
Call registration in action_registry.cpp
Edit src/actions/action_registry.cpp and add your registration call: src/actions/action_registry.cpp
#include "actions/my_category_actions.h"
void ActionRegistry :: register_defaults () {
// Existing registrations...
register_notes_actions ( * this );
register_reminders_actions ( * this );
// ...
// Add your category
register_my_category_actions ( * this );
}
Update CMakeLists.txt
If you created a new .cpp file, add it to CMakeLists.txt: add_library (rcli STATIC
# ...
src/actions/my_category_actions.cpp
)
Rebuild and test
cd build
cmake --build . -j$( sysctl -n hw.ncpu )
./rcli actions # Should list your new action
./rcli action my_feature '{"param1": "test"}'
Action Helpers
src/actions/action_helpers.h provides utilities for parsing JSON and escaping strings:
JSON Parsing
// Extract string from JSON
std ::string value = json_get_string (args_json, "key" );
// Extract boolean
bool flag = json_get_bool (args_json, "enabled" );
// Extract integer
int count = json_get_int (args_json, "count" );
String Escaping
// Escape for AppleScript
std ::string safe = escape_applescript (user_input);
// Escape for shell commands
std ::string safe = escape_shell (user_input);
Execution
// Run AppleScript
auto result = run_applescript ( R"(
tell application "Finder"
activate
end tell
)" );
if ( result . success ) {
// result.output contains stdout
} else {
// result.error contains stderr
}
// Run shell command
auto result = run_shell ( "open -a Safari" );
ActionResult Structure
struct ActionResult {
bool success; // Did the action succeed?
std ::string output; // Human-readable success message
std ::string error; // Human-readable error message (if !success)
std ::string raw_json; // Machine-parseable JSON result
};
Success Example
return {
true , // success
"Created reminder: Buy milk" , // output
"" , // error (empty)
"{ \" action \" : \" create_reminder \" , \" title \" : \" Buy milk \" , \" status \" : \" created \" }"
};
Failure Example
return {
false , // success
"" , // output (empty)
"Safari is not running" , // error
"{ \" error \" : \" Safari is not running \" }" // raw_json
};
The LLM uses JSON schemas to understand action parameters. Use this format:
{
"type" : "object" ,
"properties" : {
"param1" : { "type" : "string" , "description" : "First parameter" },
"param2" : { "type" : "number" , "description" : "Second parameter" },
"param3" : { "type" : "boolean" , "description" : "Optional flag" }
},
"required" : [ "param1" ]
}
Escape quotes when embedding in C++ strings:
R"({"type":"object","properties":{"param1":{"type":"string"}},"required":["param1"]})"
User speaks or types
User: "Create a note called Project Ideas"
LLM receives tool definitions
The LLM sees all enabled actions as tool definitions: {
"name" : "create_note" ,
"description" : "Create a new note in Apple Notes" ,
"parameters" : { "type" : "object" , "properties" :{ "title" :{ "type" : "string" }}, ... }
}
LLM generates tool call
The LLM responds with a tool call in its native format: < tool_call >
{"name": "create_note", "arguments": {"title": "Project Ideas"}}
</ tool_call >
ToolEngine parses and executes
The ToolEngine extracts the call and dispatches to ActionRegistry: auto result = registry . execute ( "create_note" , '{"title": "Project Ideas"}' );
Action executes
The action function runs: run_applescript ( "tell application \" Notes \" to make new note..." );
Result returned to LLM
The LLM sees: { "action" : "create_note" , "title" : "Project Ideas" , "status" : "created" }
And responds to the user: RCLI: I've created a note called Project Ideas.
Testing Your Action
CLI Test
TUI Test
Voice Test
# Direct execution
rcli action my_feature '{"param1": "test"}'
# Via LLM
rcli ask "use my feature with test parameter"
Action Categories
Use these categories for consistency:
productivity — Notes, reminders, shortcuts
communication — Messages, FaceTime
media — Spotify, Apple Music, volume
system — Dark mode, volume, lock screen, battery, Wi-Fi
window — Close, minimize, fullscreen
web — Search, YouTube, Maps
clipboard — Read/write clipboard
files — Search files
browser — Get URL, list tabs
Advanced: Native macOS APIs
Some actions use Objective-C APIs instead of AppleScript for better performance. See src/audio/mic_permission.mm for examples.
src/audio/mic_permission.mm
#import <AVFoundation/AVFoundation.h>
bool request_mic_permission () {
__block bool granted = false ;
dispatch_semaphore_t sem = dispatch_semaphore_create ( 0 );
[AVCaptureDevice requestAccessForMediaType: AVMediaTypeAudio
completionHandler: ^ ( BOOL granted_) {
granted = granted_;
dispatch_semaphore_signal (sem);
}];
dispatch_semaphore_wait (sem, DISPATCH_TIME_FOREVER);
return granted;
}
Enabling/Disabling Actions
Users can enable/disable actions via:
rcli actions # List all actions
# Actions are persisted in ~/Library/RCLI/config/actions.json
Default state is set in ActionDef:
ActionDef{
"my_feature" ,
"Description" ,
"category" ,
R"({...})" ,
true , // <-- default_enabled = true
"Example" ,
"CLI example"
}
Next Steps
Project Structure Understand the codebase organization
Contributing Submit your new action as a PR
Building from Source Build and test your changes