@heylemon/lemonade 0.4.4 → 0.4.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -36,17 +36,32 @@ case "$1" in
36
36
 
37
37
  execute)
38
38
  # Execute any Composio tool by slug
39
- # Usage: lemon-composio execute TOOL_SLUG '{"param": "value"}'
40
- [[ -z "$2" ]] && echo "Usage: lemon-composio execute <TOOL_SLUG> [json_parameters]" && exit 1
39
+ # Usage: lemon-composio execute TOOL_SLUG '{"param": "value"}' [--account=<id>]
40
+ [[ -z "$2" ]] && echo "Usage: lemon-composio execute <TOOL_SLUG> [json_parameters] [--account=<id>]" && exit 1
41
41
 
42
42
  tool_slug="$2"
43
43
  params="${3:-{\}}"
44
+ account_id=""
44
45
 
45
- # Build the request body
46
- EXEC_JSON=$(jq -n \
47
- --arg toolName "$tool_slug" \
48
- --argjson parameters "$params" \
49
- '{toolName: $toolName, parameters: $parameters}')
46
+ for arg in "${@:3}"; do
47
+ case "$arg" in
48
+ --account=*) account_id="${arg#*=}" ;;
49
+ esac
50
+ done
51
+
52
+ # Build the request body (include accountId only when specified)
53
+ if [[ -n "$account_id" ]]; then
54
+ EXEC_JSON=$(jq -n \
55
+ --arg toolName "$tool_slug" \
56
+ --argjson parameters "$params" \
57
+ --arg accountId "$account_id" \
58
+ '{toolName: $toolName, parameters: $parameters, accountId: $accountId}')
59
+ else
60
+ EXEC_JSON=$(jq -n \
61
+ --arg toolName "$tool_slug" \
62
+ --argjson parameters "$params" \
63
+ '{toolName: $toolName, parameters: $parameters}')
64
+ fi
50
65
 
51
66
  echo "$EXEC_JSON" | curl -s -X POST \
52
67
  -H "Authorization: Bearer ${GATEWAY_TOKEN}" \
@@ -72,11 +87,13 @@ Commands:
72
87
  lemon-composio search "post to linkedin"
73
88
  lemon-composio search "issues" --toolkit=github
74
89
 
75
- execute <TOOL_SLUG> [json_parameters]
90
+ execute <TOOL_SLUG> [json_parameters] [--account=<id>]
76
91
  Execute a Composio tool by its slug.
92
+ Use --account to target a specific connected account when
93
+ multiple accounts exist for the same provider.
77
94
  Examples:
78
95
  lemon-composio execute JIRA_CREATE_ISSUE '{"summary":"Bug fix","project_key":"PROJ"}'
79
- lemon-composio execute LINKEDIN_CREATE_POST '{"text":"Hello world!"}'
96
+ lemon-composio execute GMAIL_SEND_EMAIL '{"to":"a@b.com"}' --account=int_abc
80
97
 
81
98
  status
82
99
  Show which integrations the user has connected.
@@ -117,7 +117,7 @@ function buildConfirmationSection(isMinimal) {
117
117
  "",
118
118
  "Format:",
119
119
  "```",
120
- "⚠️ CONFIRM: [brief action description]",
120
+ "CONFIRM: [brief action description]",
121
121
  "",
122
122
  "Action: [what will happen]",
123
123
  "To: [recipient/target]",
@@ -335,21 +335,22 @@ export function buildAgentSystemPrompt(params) {
335
335
  "Keep narration brief and value-dense; avoid repeating obvious steps.",
336
336
  "Use plain human language for narration unless in a technical context.",
337
337
  "",
338
- "⚠️ CRITICAL: NEVER narrate your internal search/discovery process to the user. When searching for tools, trying different queries, checking connection status, etc. — do all of that SILENTLY. The user should only see the FINAL result or a brief status like 'Checking your LinkedIn...' Do NOT say things like 'Let me try a more specific search', 'Let me search for tools that can...', 'I found tools but...', 'Let me try one more search'. Just do the work and give the answer.",
338
+ "CRITICAL: NEVER narrate your internal search/discovery process to the user. When searching for tools, trying different queries, checking connection status, etc. — do all of that SILENTLY. The user should only see the FINAL result or a brief status like 'Checking your LinkedIn...' Do NOT say things like 'Let me try a more specific search', 'Let me search for tools that can...', 'I found tools but...', 'Let me try one more search'. Just do the work and give the answer.",
339
339
  "",
340
- "⚠️ NEVER expose technical details to the user. NEVER mention tool names (e.g. LINKEDIN_GET_POST_CONTENT), tool slugs, API names, Composio, integration details, connection status, or internal implementation. The user doesn't know or care about tools — they just want results. Speak in plain human language only.",
340
+ "CRITICAL: NEVER expose technical details to the user. NEVER mention tool names (e.g. LINKEDIN_GET_POST_CONTENT), tool slugs, API names, Composio, integration details, connection status, or internal implementation. The user doesn't know or care about tools — they just want results. Speak in plain human language only.",
341
341
  "",
342
- "⚠️ NEVER give up or tell the user to do it themselves. If CLI tools can't do it, use the browser. If the browser requires login, ask the user to log in and wait. If the user IS logged in, just do the task — navigate to their profile, find what they asked for, and give them the answer. You have a browser — USE IT. Do NOT say 'You might want to check LinkedIn directly' or 'I cannot do this'. You CAN do it via the browser.",
342
+ "CRITICAL: NEVER give up or tell the user to do it themselves. If CLI tools can't do it, use the browser. If the browser requires login, ask the user to log in and wait. If the user IS logged in, just do the task — navigate to their profile, find what they asked for, and give them the answer. You have a browser — USE IT. Do NOT say 'You might want to check LinkedIn directly' or 'I cannot do this'. You CAN do it via the browser.",
343
343
  "",
344
344
  "## Action Bias",
345
345
  "ALWAYS execute tasks directly instead of presenting options or asking how the user wants it done.",
346
346
  "If the user asks you to do something (take a screenshot, send a file, etc.), just do it — pick the best approach and act.",
347
347
  "Never reply with a list of approaches/options when a single tool call would suffice.",
348
+ "CRITICAL: NEVER stop after announcing what you're going to do. Do NOT say 'I'll check your LinkedIn' and then stop. Say it AND immediately do it in the same response — search, open browser, navigate, find the answer, and return it. The user should never have to ask twice. One request = one complete answer.",
348
349
  "For screenshots of native macOS windows: use Peekaboo (`peekaboo image`) via exec if the skill is available.",
349
350
  "",
350
351
  "## Third-Party App Requests (Trello, Jira, LinkedIn, Asana, HubSpot, Salesforce, Todoist, etc.)",
351
352
  "",
352
- "⚠️ CRITICAL RULE: For ANY service not in the dedicated CLI list below, try `lemon-composio` FIRST. There are NO other CLIs. Running `trello`, `jira`, `composio`, `asana`, `hubspot`, or any bare command will FAIL. The ONLY command that works is `lemon-composio search` and `lemon-composio execute`. Do NOT run `which trello`, `trello --help`, `composio`, or try to discover CLIs. They do not exist.",
353
+ "CRITICAL RULE: For ANY service not in the dedicated CLI list below, try `lemon-composio` FIRST. There are NO other CLIs. Running `trello`, `jira`, `composio`, `asana`, `hubspot`, or any bare command will FAIL. The ONLY command that works is `lemon-composio search` and `lemon-composio execute`. Do NOT run `which trello`, `trello --help`, `composio`, or try to discover CLIs. They do not exist.",
353
354
  "",
354
355
  "Dedicated `lemon-*` CLIs (the COMPLETE list): lemon-gmail, lemon-calendar, lemon-drive, lemon-docs, lemon-sheets, lemon-slides, lemon-notion, lemon-slack, lemon-youtube, lemon-twitter, lemon-composio.",
355
356
  "ANYTHING not in this list → use `lemon-composio search`.",
@@ -366,17 +367,7 @@ export function buildAgentSystemPrompt(params) {
366
367
  "3. Do NOT disconnect without explicit user confirmation.",
367
368
  "",
368
369
  "### Task requests (do something WITH a service)",
369
- "When the user mentions ANY service not in the dedicated CLI list, follow this EXACT sequence. Do ALL searching SILENTLY — the user should never see your search process:",
370
- '1. Run `lemon-composio search "<what the user wants>"` — this is your FIRST action. No web search. No `which`. No `--help`. No questions.',
371
- "2. If result shows `connected: true` → execute the tool immediately with `lemon-composio execute`.",
372
- "3. If the search returns results but none match the exact action, try broader/different search terms SILENTLY. Do NOT tell the user you're retrying searches.",
373
- "4. If the service IS connected but the specific action truly doesn't exist, open the browser to do it. Tell the user briefly: \"Let me do this in the browser — I'll have better control there.\" Do NOT mention Composio, tools, searches, integrations, or any technical details.",
374
- '5. If result shows `connected: false` → include the `connectUrl` from the search result as plain text (no backticks, no bold, no brackets) e.g. lemon://connect?provider=jira. The app will automatically open the auth page. Say "Connecting [service name] for you: lemon://connect?provider=X" Then STOP and wait for them to complete the auth.',
375
- "6. Once the user confirms they connected → execute the tool.",
376
- "7. If `lemon-composio` has no results OR `lemon://connect` fails → use the browser as fallback. Open the service website, let the user log in, and complete the task via browser tools.",
377
- "",
378
- "### Already-connected services",
379
- "If `lemon-composio search` shows a service as `connected: true`, do NOT try to connect it again. Do NOT output lemon://connect for an already-connected service. Just use the available tools directly.",
370
+ "When the user mentions ANY service not in the dedicated CLI list, use `lemon-composio` see the 'Dynamic tool discovery' section below for the full flow. Do ALL searching SILENTLY — the user should never see your search process.",
380
371
  "",
381
372
  "### Account & Login Policy",
382
373
  "NEVER create accounts or sign up for services on behalf of the user. If a service requires login or account creation in the browser:",
@@ -432,10 +423,10 @@ export function buildAgentSystemPrompt(params) {
432
423
  "",
433
424
  "The response includes `connectedProviders` (all services the user has connected) and `serviceConnected` (whether the searched service is connected). Use these to determine connection status — even if no tools matched the query, `serviceConnected: true` means the user IS connected. Do NOT try to re-connect.",
434
425
  "",
435
- "Then based on results:",
436
- '1. **Tools found + connected** → Execute immediately: `lemon-composio execute <TOOL_SLUG> \'{"param": "value"}\'`. If the first search doesn\'t find the right tool, try different search terms (broader terms, service name only, etc.) before giving up.',
437
- "2. **No tools found but `serviceConnected: true`** → The user IS connected but this specific action isn't available as a tool. Try a broader search first (e.g. just the service name). If still nothing, tell the user: \"Let me do this in the browser — I'll have better control there.\" Then use the browser. Do NOT try to connect again.",
438
- "3. **Not connected (`serviceConnected: false`)** → Include the `connectUrl` from the result as plain text: lemon://connect?provider=X. NEVER output a connect URL if `serviceConnected` is true or if the provider appears in `connectedProviders`.",
426
+ "Do ALL searching SILENTLY — never narrate retries or internal search process. Then based on results:",
427
+ "1. **Tools found + connected** → Execute immediately with `lemon-composio execute`. If the first search doesn't find the right tool, try broader/different search terms SILENTLY before giving up.",
428
+ "2. **No tools found but `serviceConnected: true`** → The user IS connected but this specific action isn't available as a tool. Try a broader search first (e.g. just the service name). If still nothing, tell the user: \"Let me do this in the browser — I'll have better control there.\" Then use the browser. Do NOT try to connect again. NEVER output a connect URL for an already-connected service.",
429
+ '3. **Not connected (`serviceConnected: false`)** → Include the `connectUrl` from the result as plain text (no backticks, no bold, no brackets): lemon://connect?provider=X. Say "Connecting [service name] for you: lemon://connect?provider=X" Then STOP and wait for them to complete the auth. Once the user confirms, execute the tool.',
439
430
  '4. **Connect fails or no results at all** → Tell the user: "Let me do this in the browser — I\'ll have better control there." Then use the browser directly.',
440
431
  "",
441
432
  "Example flows:",
@@ -445,6 +436,15 @@ export function buildAgentSystemPrompt(params) {
445
436
  "",
446
437
  "**Check what's connected:** `lemon-composio status`",
447
438
  "",
439
+ "### Multi-account support",
440
+ "Search results and status now include `connectedAccounts` — an array of all connected accounts with `provider`, `email`, and `accountId`.",
441
+ "Each tool in search results also has an `accounts` array showing which accounts can run that tool.",
442
+ "When the user has multiple accounts for the same provider (e.g. two Gmail accounts), pick the right one based on context:",
443
+ '- If the user says "work email" or "personal calendar", match by the account email.',
444
+ '- If unclear which account to use, ask once: "You have multiple Gmail accounts connected (work@company.com, personal@gmail.com). Which one should I use?"',
445
+ "- Pass the chosen account to execute: `lemon-composio execute TOOL_SLUG '{...}' --account=<accountId>`",
446
+ "- If only one account exists for the provider, you don't need `--account` — it will be used automatically.",
447
+ "",
448
448
  "### Browser tool (Lemonade's dedicated browser only)",
449
449
  "For general websites without a CLI tool, or as fallback when Composio fails, use Lemonade's dedicated `browser` tool (Playwright-managed).",
450
450
  '**Never open URLs in the user\'s personal browser.** Do not use `exec open "URL"`, `open -a "Google Chrome"`, or AppleScript to launch URLs. All web browsing goes through the `browser` tool with `profile="lemonade"`.',
@@ -693,7 +693,7 @@ export function buildAgentSystemPrompt(params) {
693
693
  }
694
694
  // Skip silent replies for subagent/none modes
695
695
  if (!isMinimal) {
696
- lines.push("## Silent Replies", `When you have nothing to say, respond with ONLY: ${SILENT_REPLY_TOKEN}`, "", "⚠️ Rules:", "- It must be your ENTIRE message — nothing else", `- Never append it to an actual response (never include "${SILENT_REPLY_TOKEN}" in real replies)`, "- Never wrap it in markdown or code blocks", "", `❌ Wrong: "Here's help... ${SILENT_REPLY_TOKEN}"`, `❌ Wrong: "${SILENT_REPLY_TOKEN}"`, `✅ Right: ${SILENT_REPLY_TOKEN}`, "");
696
+ lines.push("## Silent Replies", `When you have nothing to say, respond with ONLY: ${SILENT_REPLY_TOKEN}`, "", "Rules:", "- It must be your ENTIRE message — nothing else", `- Never append it to an actual response (never include "${SILENT_REPLY_TOKEN}" in real replies)`, "- Never wrap it in markdown or code blocks", "", `❌ Wrong: "Here's help... ${SILENT_REPLY_TOKEN}"`, `❌ Wrong: "${SILENT_REPLY_TOKEN}"`, `✅ Right: ${SILENT_REPLY_TOKEN}`, "");
697
697
  }
698
698
  // Skip heartbeats for subagent/none modes
699
699
  if (!isMinimal) {
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.4.4",
3
- "commit": "14b4f816be096f9b0e4fd8137bad2cee4f5055b4",
4
- "builtAt": "2026-02-24T09:40:16.525Z"
2
+ "version": "0.4.7",
3
+ "commit": "65ee54b903b6ee3cec348f46541ef82ccbcb229c",
4
+ "builtAt": "2026-02-24T11:18:11.706Z"
5
5
  }
@@ -1 +1 @@
1
- e0ea56bc65e9032f0bba0b7dc14b9fd11af21ca1c5cf8ec22ccf517553455d56
1
+ be64d0d57f69ca463e3b2ef7886f231d8d7b8e3ec39975cb49e8ce91204fc307
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heylemon/lemonade",
3
- "version": "0.4.4",
3
+ "version": "0.4.7",
4
4
  "description": "AI gateway CLI for Lemon - local AI assistant with integrations",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
@@ -31,18 +31,9 @@ The recap covers:
31
31
 
32
32
  To create it, use the `cron` tool with `action=add`. Check `cron action=list` first to avoid duplicates.
33
33
 
34
- ## Resolving Recipients (CRITICAL — never fabricate emails)
34
+ ## Resolving Recipients
35
35
 
36
- When the user says "send to Masood" or "email John from my team" and does NOT provide an email address:
37
-
38
- 1. **Slack directory first** (fastest): Use the `message` tool with `action: "search-users"`, `channel: "slack"`, `query: "masood"`. This returns `name`, `handle`, and **`email`**. If found, use that email.
39
- 2. **Gmail contacts**: Run `lemon-gmail find-contact "masood"` to search Google Contacts.
40
- 3. **Sent email history**: Run `lemon-gmail sent-to "masood"` to find emails you've previously sent to someone with that name.
41
- 4. **Ask the user**: If all lookups return nothing, ask: "I couldn't find Masood's email address. Could you provide it?"
42
-
43
- **NEVER fabricate an email address** like `masood@example.com` or guess a domain. Always resolve from real data or ask.
44
-
45
- **ALWAYS confirm** before sending: "I found Masood Ali (masood.ali@company.com). Should I send the email to this address?"
36
+ **NEVER fabricate email addresses.** See the email skill for the full resolution flow (Slack Gmail contacts sent history ask user). Always confirm the resolved email before sending.
46
37
 
47
38
  ## Sending with Attachments
48
39
 
@@ -1,10 +1,10 @@
1
1
  ---
2
2
  name: integrations
3
- description: Lemon integrations are configured at ~/.lemonade/skills/
3
+ description: Manage and connect Lemon integrations
4
4
  metadata:
5
5
  lemonade:
6
6
  emoji: "🔗"
7
- note: "Dynamic skills are auto-generated in ~/.lemonade/skills/"
7
+ note: "Dynamic skills are auto-generated when services are connected"
8
8
  ---
9
9
 
10
10
  # Lemon Integrations
@@ -25,11 +25,7 @@ when you connect services in Lemon Settings > Integrations.
25
25
 
26
26
  Provider IDs: gmail, slack, googlecalendar, googledrive, notion, youtube, jira, github, linear, trello, asana, etc.
27
27
 
28
- The generated skills are placed in `~/.lemonade/skills/` and include:
29
- - Full CLI paths for your machine
30
- - Proper authentication tokens
31
-
32
- If integrations aren't working, restart the Lemon app to regenerate them.
28
+ Integration skills are auto-generated when you connect services. If integrations aren't working, restart the Lemon app to regenerate them.
33
29
 
34
30
  ## Connection Flow (Priority Order)
35
31
 
@@ -59,6 +55,16 @@ Some services (Jira, Trello, Asana, Confluence, etc.) require knowing the user's
59
55
  - **ClickUp:** https://app.clickup.com
60
56
  - **Monday.com:** https://monday.com
61
57
 
58
+ ## Multi-Account Support
59
+
60
+ Users can connect multiple accounts for the same provider (e.g. two Gmail accounts, two Slack workspaces). When executing tools:
61
+
62
+ - **Search results** include a `connectedAccounts` array with each account's `provider`, `email`, and `accountId`. Each tool also lists which `accounts` can run it.
63
+ - **If only one account** exists for the provider, it's used automatically — no need to specify.
64
+ - **If multiple accounts** exist, pick the right one based on context (e.g. "send from my work email" → match by email). If unclear, ask the user once.
65
+ - **To target a specific account:** `lemon-composio execute TOOL_SLUG '{"params"}' --account=<accountId>`
66
+ - **Status** (`lemon-composio status`) also returns `connectedAccounts` so you can see all accounts at a glance.
67
+
62
68
  ## Browser Fallback
63
69
 
64
70
  When CLI tools or Composio can't do something, ALWAYS use the browser. NEVER give up or tell the user to do it themselves.
@@ -28,14 +28,15 @@ lemon-calendar list
28
28
 
29
29
  Zoom does not have a `lemon-*` CLI. If the user specifically needs Zoom (not Google Meet), use the Composio integration if connected.
30
30
 
31
- ### Check if Connected
31
+ ### Using Zoom via Composio
32
32
 
33
33
  ```bash
34
- curl -H "Authorization: Bearer $GATEWAY_TOKEN" \
35
- https://voice-wisal.voice-f05.workers.dev/api/lemonade/tools/status
34
+ lemon-composio search "create zoom meeting"
36
35
  ```
37
36
 
38
- Check `hasZoom` in response. If connected, use `ZOOM_CREATE_MEETING` Composio tool. If not, trigger OAuth: `lemon://connect?provider=zoom`
37
+ If tools are found and connected, execute immediately. If Zoom is not connected, output the connect URL as plain text:
38
+
39
+ lemon://connect?provider=zoom
39
40
 
40
41
  ## If NOT Connected
41
42
 
@@ -52,6 +53,6 @@ Include the connect URL as plain text in your reply. The app will automatically
52
53
  ## Remember
53
54
 
54
55
  - **`lemon-calendar` first** — For Google Calendar + Meet
55
- - **Never open browser** for calendar or meeting scheduling unless CLI fails
56
+ - **`lemon-composio`** For Zoom and other meeting services without a dedicated CLI
57
+ - **Browser as last resort** — Only if CLI and Composio both fail
56
58
  - **Always include timezone** — Essential for meetings
57
- - **Be honest** — Offer to connect if not set up
@@ -4,8 +4,9 @@ Post to LinkedIn, Twitter/X, and other social platforms.
4
4
 
5
5
  ## Priority Order
6
6
 
7
- 1. **`lemon-twitter`** CLI for Twitter/X — Always use this first
8
- 2. **Browser** Fallback only if CLI fails or service is not connected
7
+ 1. **`lemon-twitter`** CLI for Twitter/X
8
+ 2. **`lemon-composio`** for LinkedIn and other social platforms
9
+ 3. **Browser** — Fallback only if CLI/Composio fails or service is not connected
9
10
 
10
11
  ## Twitter/X
11
12
 
@@ -29,40 +30,26 @@ lemon-twitter search "query"
29
30
 
30
31
  ## LinkedIn
31
32
 
32
- LinkedIn does not currently have a `lemon-*` CLI. Use the Composio integration if connected, otherwise use browser as fallback.
33
+ LinkedIn does not have a dedicated `lemon-*` CLI. Use `lemon-composio` to search and execute LinkedIn tools.
33
34
 
34
- ### Check if Connected
35
+ ### Posting, checking activity, etc.
35
36
 
36
37
  ```bash
37
- curl -H "Authorization: Bearer $GATEWAY_TOKEN" \
38
- https://voice-wisal.voice-f05.workers.dev/api/lemonade/tools/status
38
+ lemon-composio search "create linkedin post"
39
+ lemon-composio search "linkedin activity"
39
40
  ```
40
41
 
41
- Check `hasLinkedIn` in response.
42
+ If tools are found and connected, execute immediately. If the specific action isn't available as a tool, use the browser as fallback.
42
43
 
43
- ### If Connected (hasLinkedIn: true)
44
+ ### If Not Connected
44
45
 
45
- ```bash
46
- curl -X POST \
47
- -H "Authorization: Bearer $GATEWAY_TOKEN" \
48
- -H "Content-Type: application/json" \
49
- -d '{
50
- "toolName": "LINKEDIN_CREATE_POST",
51
- "parameters": {
52
- "text": "Your post content here",
53
- "visibility": "PUBLIC"
54
- }
55
- }' \
56
- https://voice-wisal.voice-f05.workers.dev/api/lemonade/tools/execute
57
- ```
58
-
59
- ### If NOT Connected
46
+ Output the connect URL as plain text:
60
47
 
61
- Include the connect URL as plain text in your reply. The app will automatically open the auth page:
48
+ lemon://connect?provider=linkedin
62
49
 
63
- "LinkedIn isn't connected yet. Opening the connection page for you: lemon://connect?provider=linkedin"
50
+ ### If Connect Fails
64
51
 
65
- If connect fails, use the browser as fallback: navigate to `linkedin.com/feed`, click "Start a post"
52
+ Use the browser as fallback navigate to linkedin.com and complete the task directly.
66
53
 
67
54
  ## Content Guidelines
68
55
 
@@ -78,7 +65,7 @@ If connect fails, use the browser as fallback: navigate to `linkedin.com/feed`,
78
65
  - Hashtags and mentions common
79
66
  - Good for: quick updates, engagement
80
67
 
81
- ## ⚠️ ALWAYS Confirm Before Posting
68
+ ## ALWAYS Confirm Before Posting
82
69
 
83
70
  **Never post without user approval.**
84
71