@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.
- package/dist/build-info.json +3 -3
- package/dist/canvas-host/a2ui/.bundle.hash +1 -1
- package/package.json +1 -1
- package/skills/apple-notes/SKILL.md +0 -50
- package/skills/apple-reminders/SKILL.md +0 -67
- package/skills/brave-search/SKILL.md +0 -57
- package/skills/brave-search/content.js +0 -86
- package/skills/brave-search/package.json +0 -14
- package/skills/brave-search/search.js +0 -179
- package/skills/caldav-calendar/SKILL.md +0 -104
- package/skills/goplaces/SKILL.md +0 -30
- package/skills/local-places/SERVER_README.md +0 -101
- package/skills/local-places/SKILL.md +0 -91
- package/skills/local-places/pyproject.toml +0 -27
- package/skills/local-places/src/local_places/__init__.py +0 -2
- package/skills/local-places/src/local_places/google_places.py +0 -314
- package/skills/local-places/src/local_places/main.py +0 -65
- package/skills/local-places/src/local_places/schemas.py +0 -107
- package/skills/messages/SKILL.md +0 -125
- package/skills/openai-image-gen/SKILL.md +0 -71
- package/skills/openai-image-gen/scripts/gen.py +0 -255
- package/skills/ordercli/SKILL.md +0 -47
- package/skills/spotify-player/SKILL.md +0 -38
- package/skills/tavily-search/SKILL.md +0 -38
- package/skills/tavily-search/scripts/extract.mjs +0 -59
- package/skills/tavily-search/scripts/search.mjs +0 -101
- package/skills/youtube-watcher/SKILL.md +0 -46
- package/skills/youtube-watcher/scripts/get_transcript.py +0 -81
- /package/skills/eightctl/{SKILL.md → SKILL.md.disabled} +0 -0
- /package/skills/nano-banana-pro/{SKILL.md → SKILL.md.disabled} +0 -0
- /package/skills/openai-whisper-api/{SKILL.md → SKILL.md.disabled} +0 -0
- /package/skills/openhue/{SKILL.md → SKILL.md.disabled} +0 -0
- /package/skills/sag/{SKILL.md → SKILL.md.disabled} +0 -0
- /package/skills/sherpa-onnx-tts/{SKILL.md → SKILL.md.disabled} +0 -0
- /package/skills/sonoscli/{SKILL.md → SKILL.md.disabled} +0 -0
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
f7aff81ae06daba57524cebf957881ed76d554113073508696e255d618aa3b03
|
package/package.json
CHANGED
|
@@ -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
|
-
```
|
package/skills/goplaces/SKILL.md
DELETED
|
@@ -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).
|