@heylemon/lemonade 0.2.3 → 0.2.5

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.
Files changed (35) hide show
  1. package/dist/build-info.json +3 -3
  2. package/dist/canvas-host/a2ui/.bundle.hash +1 -1
  3. package/package.json +1 -1
  4. package/skills/apple-notes/SKILL.md +0 -50
  5. package/skills/apple-reminders/SKILL.md +0 -67
  6. package/skills/brave-search/SKILL.md +0 -57
  7. package/skills/brave-search/content.js +0 -86
  8. package/skills/brave-search/package.json +0 -14
  9. package/skills/brave-search/search.js +0 -179
  10. package/skills/caldav-calendar/SKILL.md +0 -104
  11. package/skills/goplaces/SKILL.md +0 -30
  12. package/skills/local-places/SERVER_README.md +0 -101
  13. package/skills/local-places/SKILL.md +0 -91
  14. package/skills/local-places/pyproject.toml +0 -27
  15. package/skills/local-places/src/local_places/__init__.py +0 -2
  16. package/skills/local-places/src/local_places/google_places.py +0 -314
  17. package/skills/local-places/src/local_places/main.py +0 -65
  18. package/skills/local-places/src/local_places/schemas.py +0 -107
  19. package/skills/messages/SKILL.md +0 -125
  20. package/skills/openai-image-gen/SKILL.md +0 -71
  21. package/skills/openai-image-gen/scripts/gen.py +0 -255
  22. package/skills/ordercli/SKILL.md +0 -47
  23. package/skills/spotify-player/SKILL.md +0 -38
  24. package/skills/tavily-search/SKILL.md +0 -38
  25. package/skills/tavily-search/scripts/extract.mjs +0 -59
  26. package/skills/tavily-search/scripts/search.mjs +0 -101
  27. package/skills/youtube-watcher/SKILL.md +0 -46
  28. package/skills/youtube-watcher/scripts/get_transcript.py +0 -81
  29. /package/skills/eightctl/{SKILL.md → SKILL.md.disabled} +0 -0
  30. /package/skills/nano-banana-pro/{SKILL.md → SKILL.md.disabled} +0 -0
  31. /package/skills/openai-whisper-api/{SKILL.md → SKILL.md.disabled} +0 -0
  32. /package/skills/openhue/{SKILL.md → SKILL.md.disabled} +0 -0
  33. /package/skills/sag/{SKILL.md → SKILL.md.disabled} +0 -0
  34. /package/skills/sherpa-onnx-tts/{SKILL.md → SKILL.md.disabled} +0 -0
  35. /package/skills/sonoscli/{SKILL.md → SKILL.md.disabled} +0 -0
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.3",
3
- "commit": "a0535878314984aba3836015e906997cadb9bb7f",
4
- "builtAt": "2026-02-22T15:12:12.410Z"
2
+ "version": "0.2.5",
3
+ "commit": "80887609891b389ba300be9cfe0a533a54f2d4a9",
4
+ "builtAt": "2026-02-22T15:54:27.492Z"
5
5
  }
@@ -1 +1 @@
1
- dda15b99462cc0b2ef3beeaf3b47c8ead319daf27fd6466820c14e02ea7b21a3
1
+ f7aff81ae06daba57524cebf957881ed76d554113073508696e255d618aa3b03
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heylemon/lemonade",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "AI gateway CLI for Lemon - local AI assistant with integrations",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
@@ -1,50 +0,0 @@
1
- ---
2
- name: apple-notes
3
- description: Manage Apple Notes via the `memo` CLI on macOS (create, view, edit, delete, search, move, and export notes). Use when a user asks Lemonade to add a note, list notes, search notes, or manage note folders.
4
- homepage: https://github.com/antoniorodr/memo
5
- metadata: {"lemonade":{"emoji":"📝","os":["darwin"],"requires":{"bins":["memo"]},"install":[{"id":"brew","kind":"brew","formula":"antoniorodr/memo/memo","bins":["memo"],"label":"Install memo via Homebrew"}]}}
6
- ---
7
-
8
- # Apple Notes CLI
9
-
10
- Use `memo notes` to manage Apple Notes directly from the terminal. Create, view, edit, delete, search, move notes between folders, and export to HTML/Markdown.
11
-
12
- Setup
13
- - Install (Homebrew): `brew tap antoniorodr/memo && brew install antoniorodr/memo/memo`
14
- - Manual (pip): `pip install .` (after cloning the repo)
15
- - macOS-only; if prompted, grant Automation access to Notes.app.
16
-
17
- View Notes
18
- - List all notes: `memo notes`
19
- - Filter by folder: `memo notes -f "Folder Name"`
20
- - Search notes (fuzzy): `memo notes -s "query"`
21
-
22
- Create Notes
23
- - Add a new note: `memo notes -a`
24
- - Opens an interactive editor to compose the note.
25
- - Quick add with title: `memo notes -a "Note Title"`
26
-
27
- Edit Notes
28
- - Edit existing note: `memo notes -e`
29
- - Interactive selection of note to edit.
30
-
31
- Delete Notes
32
- - Delete a note: `memo notes -d`
33
- - Interactive selection of note to delete.
34
-
35
- Move Notes
36
- - Move note to folder: `memo notes -m`
37
- - Interactive selection of note and destination folder.
38
-
39
- Export Notes
40
- - Export to HTML/Markdown: `memo notes -ex`
41
- - Exports selected note; uses Mistune for markdown processing.
42
-
43
- Limitations
44
- - Cannot edit notes containing images or attachments.
45
- - Interactive prompts may require terminal access.
46
-
47
- Notes
48
- - macOS-only.
49
- - Requires Apple Notes.app to be accessible.
50
- - For automation, grant permissions in System Settings > Privacy & Security > Automation.
@@ -1,67 +0,0 @@
1
- ---
2
- name: apple-reminders
3
- description: Manage Apple Reminders via the `remindctl` CLI on macOS (list, add, edit, complete, delete). Supports lists, date filters, and JSON/plain output.
4
- homepage: https://github.com/steipete/remindctl
5
- metadata: {"lemonade":{"emoji":"⏰","os":["darwin"],"requires":{"bins":["remindctl"]},"install":[{"id":"brew","kind":"brew","formula":"steipete/tap/remindctl","bins":["remindctl"],"label":"Install remindctl via Homebrew"}]}}
6
- ---
7
-
8
- # Apple Reminders CLI (remindctl)
9
-
10
- Use `remindctl` to manage Apple Reminders directly from the terminal. It supports list filtering, date-based views, and scripting output.
11
-
12
- Setup
13
- - Install (Homebrew): `brew install steipete/tap/remindctl`
14
- - From source: `pnpm install && pnpm build` (binary at `./bin/remindctl`)
15
- - macOS-only; grant Reminders permission when prompted.
16
-
17
- Permissions
18
- - Check status: `remindctl status`
19
- - Request access: `remindctl authorize`
20
-
21
- View Reminders
22
- - Default (today): `remindctl`
23
- - Today: `remindctl today`
24
- - Tomorrow: `remindctl tomorrow`
25
- - Week: `remindctl week`
26
- - Overdue: `remindctl overdue`
27
- - Upcoming: `remindctl upcoming`
28
- - Completed: `remindctl completed`
29
- - All: `remindctl all`
30
- - Specific date: `remindctl 2026-01-04`
31
-
32
- Manage Lists
33
- - List all lists: `remindctl list`
34
- - Show list: `remindctl list Work`
35
- - Create list: `remindctl list Projects --create`
36
- - Rename list: `remindctl list Work --rename Office`
37
- - Delete list: `remindctl list Work --delete`
38
-
39
- Create Reminders
40
- - Quick add: `remindctl add "Buy milk"`
41
- - With list + due: `remindctl add --title "Call mom" --list Personal --due tomorrow`
42
-
43
- Edit Reminders
44
- - Edit title/due: `remindctl edit 1 --title "New title" --due 2026-01-04`
45
-
46
- Complete Reminders
47
- - Complete by id: `remindctl complete 1 2 3`
48
-
49
- Delete Reminders
50
- - Delete by id: `remindctl delete 4A83 --force`
51
-
52
- Output Formats
53
- - JSON (scripting): `remindctl today --json`
54
- - Plain TSV: `remindctl today --plain`
55
- - Counts only: `remindctl today --quiet`
56
-
57
- Date Formats
58
- Accepted by `--due` and date filters:
59
- - `today`, `tomorrow`, `yesterday`
60
- - `YYYY-MM-DD`
61
- - `YYYY-MM-DD HH:mm`
62
- - ISO 8601 (`2026-01-04T12:34:56Z`)
63
-
64
- Notes
65
- - macOS-only.
66
- - If access is denied, enable Terminal/remindctl in System Settings → Privacy & Security → Reminders.
67
- - If running over SSH, grant access on the Mac that runs the command.
@@ -1,57 +0,0 @@
1
- ---
2
- name: brave-search
3
- description: Web search and content extraction via Brave Search API. Use for searching documentation, facts, or any web content. Lightweight, no browser required.
4
- metadata: {"lemonade":{"emoji":"🦁","requires":{"bins":["node"]}}}
5
- ---
6
-
7
- # Brave Search
8
-
9
- Headless web search and content extraction using Brave Search. No browser required.
10
-
11
- ## Setup
12
-
13
- Run once before first use:
14
-
15
- ```bash
16
- cd {baseDir}
17
- npm ci
18
- ```
19
-
20
- ## Search
21
-
22
- ```bash
23
- node {baseDir}/search.js "query" # Basic search (5 results)
24
- node {baseDir}/search.js "query" -n 10 # More results
25
- node {baseDir}/search.js "query" --content # Include page content as markdown
26
- node {baseDir}/search.js "query" -n 3 --content # Combined
27
- ```
28
-
29
- ## Extract Page Content
30
-
31
- ```bash
32
- node {baseDir}/content.js https://example.com/article
33
- ```
34
-
35
- Fetches a URL and extracts readable content as markdown.
36
-
37
- ## Output Format
38
-
39
- ```
40
- --- Result 1 ---
41
- Title: Page Title
42
- Link: https://example.com/page
43
- Snippet: Description from search results
44
- Content: (if --content flag used)
45
- Markdown content extracted from the page...
46
-
47
- --- Result 2 ---
48
- ...
49
- ```
50
-
51
- ## When to Use
52
-
53
- - Searching for documentation or API references
54
- - Looking up facts or current information
55
- - Fetching content from specific URLs
56
- - Any task requiring web search without interactive browsing
57
- ```
@@ -1,86 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Readability } from "@mozilla/readability";
4
- import { JSDOM } from "jsdom";
5
- import TurndownService from "turndown";
6
- import { gfm } from "turndown-plugin-gfm";
7
-
8
- const url = process.argv[2];
9
-
10
- if (!url) {
11
- console.log("Usage: content.js <url>");
12
- console.log("\nExtracts readable content from a webpage as markdown.");
13
- console.log("\nExamples:");
14
- console.log(" content.js https://example.com/article");
15
- console.log(" content.js https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html");
16
- process.exit(1);
17
- }
18
-
19
- function htmlToMarkdown(html) {
20
- const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
21
- turndown.use(gfm);
22
- turndown.addRule("removeEmptyLinks", {
23
- filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
24
- replacement: () => "",
25
- });
26
- return turndown
27
- .turndown(html)
28
- .replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
29
- .replace(/ +/g, " ")
30
- .replace(/\s+,/g, ",")
31
- .replace(/\s+\./g, ".")
32
- .replace(/\n{3,}/g, "\n\n")
33
- .trim();
34
- }
35
-
36
- try {
37
- const response = await fetch(url, {
38
- headers: {
39
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
40
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
41
- "Accept-Language": "en-US,en;q=0.9",
42
- },
43
- signal: AbortSignal.timeout(15000),
44
- });
45
-
46
- if (!response.ok) {
47
- console.error(`HTTP ${response.status}: ${response.statusText}`);
48
- process.exit(1);
49
- }
50
-
51
- const html = await response.text();
52
- const dom = new JSDOM(html, { url });
53
- const reader = new Readability(dom.window.document);
54
- const article = reader.parse();
55
-
56
- if (article && article.content) {
57
- if (article.title) {
58
- console.log(`# ${article.title}\n`);
59
- }
60
- console.log(htmlToMarkdown(article.content));
61
- process.exit(0);
62
- }
63
-
64
- // Fallback: try to extract main content
65
- const fallbackDoc = new JSDOM(html, { url });
66
- const body = fallbackDoc.window.document;
67
- body.querySelectorAll("script, style, noscript, nav, header, footer, aside").forEach(el => el.remove());
68
-
69
- const title = body.querySelector("title")?.textContent?.trim();
70
- const main = body.querySelector("main, article, [role='main'], .content, #content") || body.body;
71
-
72
- if (title) {
73
- console.log(`# ${title}\n`);
74
- }
75
-
76
- const text = main?.innerHTML || "";
77
- if (text.trim().length > 100) {
78
- console.log(htmlToMarkdown(text));
79
- } else {
80
- console.error("Could not extract readable content from this page.");
81
- process.exit(1);
82
- }
83
- } catch (e) {
84
- console.error(`Error: ${e.message}`);
85
- process.exit(1);
86
- }
@@ -1,14 +0,0 @@
1
- {
2
- "name": "brave-search",
3
- "version": "1.0.0",
4
- "type": "module",
5
- "description": "Headless web search via Brave Search - no browser required",
6
- "author": "Mario Zechner",
7
- "license": "MIT",
8
- "dependencies": {
9
- "@mozilla/readability": "^0.6.0",
10
- "jsdom": "^27.0.1",
11
- "turndown": "^7.2.2",
12
- "turndown-plugin-gfm": "^1.0.2"
13
- }
14
- }
@@ -1,179 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Readability } from "@mozilla/readability";
4
- import { JSDOM } from "jsdom";
5
- import TurndownService from "turndown";
6
- import { gfm } from "turndown-plugin-gfm";
7
-
8
- const args = process.argv.slice(2);
9
-
10
- const contentIndex = args.indexOf("--content");
11
- const fetchContent = contentIndex !== -1;
12
- if (fetchContent) args.splice(contentIndex, 1);
13
-
14
- let numResults = 5;
15
- const nIndex = args.indexOf("-n");
16
- if (nIndex !== -1 && args[nIndex + 1]) {
17
- numResults = parseInt(args[nIndex + 1], 10);
18
- args.splice(nIndex, 2);
19
- }
20
-
21
- const query = args.join(" ");
22
-
23
- if (!query) {
24
- console.log("Usage: search.js [-n] [--content]");
25
- console.log("\nOptions:");
26
- console.log(" -n Number of results (default: 5)");
27
- console.log(" --content Fetch readable content as markdown");
28
- console.log("\nExamples:");
29
- console.log(' search.js "javascript async await"');
30
- console.log(' search.js "rust programming" -n 10');
31
- console.log(' search.js "climate change" --content');
32
- process.exit(1);
33
- }
34
-
35
- async function fetchBraveResults(query, numResults) {
36
- const url = `https://search.brave.com/search?q=${encodeURIComponent(query)}`;
37
-
38
- const response = await fetch(url, {
39
- headers: {
40
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36",
41
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8",
42
- "Accept-Language": "en-US,en;q=0.9",
43
- "sec-ch-ua": '"Chromium";v="142", "Google Chrome";v="142", "Not_A Brand";v="99"',
44
- "sec-ch-ua-mobile": "?0",
45
- "sec-ch-ua-platform": '"macOS"',
46
- "sec-fetch-dest": "document",
47
- "sec-fetch-mode": "navigate",
48
- "sec-fetch-site": "none",
49
- "sec-fetch-user": "?1",
50
- }
51
- });
52
-
53
- if (!response.ok) {
54
- throw new Error(`HTTP ${response.status}: ${response.statusText}`);
55
- }
56
-
57
- const html = await response.text();
58
- const dom = new JSDOM(html);
59
- const doc = dom.window.document;
60
-
61
- const results = [];
62
-
63
- // Find all search result snippets with data-type="web"
64
- const snippets = doc.querySelectorAll('div.snippet[data-type="web"]');
65
-
66
- for (const snippet of snippets) {
67
- if (results.length >= numResults) break;
68
-
69
- // Get the main link and title
70
- const titleLink = snippet.querySelector('a.svelte-14r20fy');
71
- if (!titleLink) continue;
72
-
73
- const link = titleLink.getAttribute('href');
74
- if (!link || link.includes('brave.com')) continue;
75
-
76
- const titleEl = titleLink.querySelector('.title');
77
- const title = titleEl?.textContent?.trim() || titleLink.textContent?.trim() || '';
78
-
79
- // Get the snippet/description
80
- const descEl = snippet.querySelector('.generic-snippet .content');
81
- let snippetText = descEl?.textContent?.trim() || '';
82
- // Remove date prefix if present
83
- snippetText = snippetText.replace(/^[A-Z][a-z]+ \d+, \d{4} -\s*/, '');
84
-
85
- if (title && link) {
86
- results.push({ title, link, snippet: snippetText });
87
- }
88
- }
89
-
90
- return results;
91
- }
92
-
93
- function htmlToMarkdown(html) {
94
- const turndown = new TurndownService({ headingStyle: "atx", codeBlockStyle: "fenced" });
95
- turndown.use(gfm);
96
- turndown.addRule("removeEmptyLinks", {
97
- filter: (node) => node.nodeName === "A" && !node.textContent?.trim(),
98
- replacement: () => "",
99
- });
100
- return turndown
101
- .turndown(html)
102
- .replace(/\[\\?\[\s*\\?\]\]\([^)]*\)/g, "")
103
- .replace(/ +/g, " ")
104
- .replace(/\s+,/g, ",")
105
- .replace(/\s+\./g, ".")
106
- .replace(/\n{3,}/g, "\n\n")
107
- .trim();
108
- }
109
-
110
- async function fetchPageContent(url) {
111
- try {
112
- const response = await fetch(url, {
113
- headers: {
114
- "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
115
- "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
116
- },
117
- signal: AbortSignal.timeout(10000),
118
- });
119
-
120
- if (!response.ok) {
121
- return `(HTTP ${response.status})`;
122
- }
123
-
124
- const html = await response.text();
125
- const dom = new JSDOM(html, { url });
126
- const reader = new Readability(dom.window.document);
127
- const article = reader.parse();
128
-
129
- if (article && article.content) {
130
- return htmlToMarkdown(article.content).substring(0, 5000);
131
- }
132
-
133
- // Fallback: try to get main content
134
- const fallbackDoc = new JSDOM(html, { url });
135
- const body = fallbackDoc.window.document;
136
- body.querySelectorAll("script, style, noscript, nav, header, footer, aside").forEach(el => el.remove());
137
- const main = body.querySelector("main, article, [role='main'], .content, #content") || body.body;
138
- const text = main?.textContent || "";
139
-
140
- if (text.trim().length > 100) {
141
- return text.trim().substring(0, 5000);
142
- }
143
-
144
- return "(Could not extract content)";
145
- } catch (e) {
146
- return `(Error: ${e.message})`;
147
- }
148
- }
149
-
150
- // Main
151
- try {
152
- const results = await fetchBraveResults(query, numResults);
153
-
154
- if (results.length === 0) {
155
- console.error("No results found.");
156
- process.exit(0);
157
- }
158
-
159
- if (fetchContent) {
160
- for (const result of results) {
161
- result.content = await fetchPageContent(result.link);
162
- }
163
- }
164
-
165
- for (let i = 0; i < results.length; i++) {
166
- const r = results[i];
167
- console.log(`--- Result ${i + 1} ---`);
168
- console.log(`Title: ${r.title}`);
169
- console.log(`Link: ${r.link}`);
170
- console.log(`Snippet: ${r.snippet}`);
171
- if (r.content) {
172
- console.log(`Content:\n${r.content}`);
173
- }
174
- console.log("");
175
- }
176
- } catch (e) {
177
- console.error(`Error: ${e.message}`);
178
- process.exit(1);
179
- }
@@ -1,104 +0,0 @@
1
- ---
2
- name: caldav-calendar
3
- description: Sync and query CalDAV calendars (iCloud, Google, Fastmail, Nextcloud, etc.) using vdirsyncer + khal. Works on Linux.
4
- metadata: {"lemonade":{"emoji":"📅","os":["linux"],"requires":{"bins":["vdirsyncer","khal"]},"install":[{"id":"apt","kind":"apt","packages":["vdirsyncer","khal"],"bins":["vdirsyncer","khal"],"label":"Install vdirsyncer + khal via apt"}]}}
5
- ---
6
-
7
- # CalDAV Calendar (vdirsyncer + khal)
8
-
9
- **vdirsyncer** syncs CalDAV calendars to local `.ics` files. **khal** reads and writes them.
10
-
11
- ## Sync First
12
-
13
- Always sync before querying or after making changes:
14
- ```bash
15
- vdirsyncer sync
16
- ```
17
-
18
- ## View Events
19
-
20
- ```bash
21
- khal list # Today
22
- khal list today 7d # Next 7 days
23
- khal list tomorrow # Tomorrow
24
- khal list 2026-01-15 2026-01-20 # Date range
25
- khal list -a Work today # Specific calendar
26
- ```
27
-
28
- ## Search
29
-
30
- ```bash
31
- khal search "meeting"
32
- khal search "dentist" --format "{start-date} {title}"
33
- ```
34
-
35
- ## Create Events
36
-
37
- ```bash
38
- khal new 2026-01-15 10:00 11:00 "Meeting title"
39
- khal new 2026-01-15 "All day event"
40
- khal new tomorrow 14:00 15:30 "Call" -a Work
41
- khal new 2026-01-15 10:00 11:00 "With notes" :: Description goes here
42
- ```
43
-
44
- After creating, sync to push changes:
45
- ```bash
46
- vdirsyncer sync
47
- ```
48
-
49
- ## Initial Setup
50
-
51
- ### 1. Configure vdirsyncer (`~/.config/vdirsyncer/config`)
52
-
53
- Example for iCloud:
54
- ```ini
55
- [general]
56
- status_path = "~/.local/share/vdirsyncer/status/"
57
-
58
- [pair icloud_calendar]
59
- a = "icloud_remote"
60
- b = "icloud_local"
61
- collections = ["from a", "from b"]
62
- conflict_resolution = "a wins"
63
-
64
- [storage icloud_remote]
65
- type = "caldav"
66
- url = "https://caldav.icloud.com/"
67
- username = "you@icloud.com"
68
- password.fetch = ["command", "cat", "~/.config/vdirsyncer/icloud_password"]
69
-
70
- [storage icloud_local]
71
- type = "filesystem"
72
- path = "~/.local/share/vdirsyncer/calendars/"
73
- fileext = ".ics"
74
- ```
75
-
76
- Provider URLs:
77
- - iCloud: `https://caldav.icloud.com/`
78
- - Google: Use `google_calendar` storage type
79
- - Fastmail: `https://caldav.fastmail.com/dav/calendars/user/EMAIL/`
80
- - Nextcloud: `https://YOUR.CLOUD/remote.php/dav/calendars/USERNAME/`
81
-
82
- ### 2. Configure khal (`~/.config/khal/config`)
83
-
84
- ```ini
85
- [calendars]
86
- [[my_calendars]]
87
- path = ~/.local/share/vdirsyncer/calendars/*
88
- type = discover
89
-
90
- [default]
91
- default_calendar = Home
92
- highlight_event_days = True
93
-
94
- [locale]
95
- timeformat = %H:%M
96
- dateformat = %Y-%m-%d
97
- ```
98
-
99
- ### 3. Discover and sync
100
-
101
- ```bash
102
- vdirsyncer discover
103
- vdirsyncer sync
104
- ```
@@ -1,30 +0,0 @@
1
- ---
2
- name: goplaces
3
- description: Query Google Places API (New) via the goplaces CLI for text search, place details, resolve, and reviews. Use for human-friendly place lookup or JSON output for scripts.
4
- homepage: https://github.com/steipete/goplaces
5
- metadata: {"lemonade":{"emoji":"📍","requires":{"bins":["goplaces"],"env":["GOOGLE_PLACES_API_KEY"]},"primaryEnv":"GOOGLE_PLACES_API_KEY","install":[{"id":"brew","kind":"brew","formula":"steipete/tap/goplaces","bins":["goplaces"],"label":"Install goplaces (brew)"}]}}
6
- ---
7
-
8
- # goplaces
9
-
10
- Modern Google Places API (New) CLI. Human output by default, `--json` for scripts.
11
-
12
- Install
13
- - Homebrew: `brew install steipete/tap/goplaces`
14
-
15
- Config
16
- - `GOOGLE_PLACES_API_KEY` required.
17
- - Optional: `GOOGLE_PLACES_BASE_URL` for testing/proxying.
18
-
19
- Common commands
20
- - Search: `goplaces search "coffee" --open-now --min-rating 4 --limit 5`
21
- - Bias: `goplaces search "pizza" --lat 40.8 --lng -73.9 --radius-m 3000`
22
- - Pagination: `goplaces search "pizza" --page-token "NEXT_PAGE_TOKEN"`
23
- - Resolve: `goplaces resolve "Soho, London" --limit 5`
24
- - Details: `goplaces details <place_id> --reviews`
25
- - JSON: `goplaces search "sushi" --json`
26
-
27
- Notes
28
- - `--no-color` or `NO_COLOR` disables ANSI color.
29
- - Price levels: 0..4 (free → very expensive).
30
- - Type filter sends only the first `--type` value (API accepts one).