@heylemon/lemonade 0.2.2 → 0.2.3

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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.2.2",
3
- "commit": "86be80740f2b0083dd4e3816f9c7d85e53780b2b",
4
- "builtAt": "2026-02-22T13:52:37.675Z"
2
+ "version": "0.2.3",
3
+ "commit": "a0535878314984aba3836015e906997cadb9bb7f",
4
+ "builtAt": "2026-02-22T15:12:12.410Z"
5
5
  }
@@ -1 +1 @@
1
- b91bfc9de067fe8db99816d14e4bf59e599bae482cd3f3511d73b463997f3112
1
+ dda15b99462cc0b2ef3beeaf3b47c8ead319daf27fd6466820c14e02ea7b21a3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heylemon/lemonade",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "AI gateway CLI for Lemon - local AI assistant with integrations",
5
5
  "publishConfig": {
6
6
  "access": "restricted"
@@ -0,0 +1,57 @@
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
+ ```
@@ -0,0 +1,86 @@
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
+ }
@@ -0,0 +1,14 @@
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
+ }
@@ -0,0 +1,179 @@
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
+ }
@@ -0,0 +1,104 @@
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
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ name: frontend-design
3
+ description: Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
4
+ ---
5
+
6
+ This skill guides creation of distinctive, production-grade frontend interfaces that avoid generic "AI slop" aesthetics. Implement real working code with exceptional attention to aesthetic details and creative choices.
7
+
8
+ The user provides frontend requirements: a component, page, application, or interface to build. They may include context about the purpose, audience, or technical constraints.
9
+
10
+ ## Design Thinking
11
+
12
+ Before coding, understand the context and commit to a BOLD aesthetic direction:
13
+ - **Purpose**: What problem does this interface solve? Who uses it?
14
+ - **Tone**: Pick an extreme: brutally minimal, maximalist chaos, retro-futuristic, organic/natural, luxury/refined, playful/toy-like, editorial/magazine, brutalist/raw, art deco/geometric, soft/pastel, industrial/utilitarian, etc.
15
+ - **Constraints**: Technical requirements (framework, performance, accessibility).
16
+ - **Differentiation**: What makes this UNFORGETTABLE? What's the one thing someone will remember?
17
+
18
+ **CRITICAL**: Choose a clear conceptual direction and execute it with precision. Bold maximalism and refined minimalism both work - the key is intentionality, not intensity.
19
+
20
+ Then implement working code (HTML/CSS/JS, React, Vue, etc.) that is:
21
+ - Production-grade and functional
22
+ - Visually striking and memorable
23
+ - Cohesive with a clear aesthetic point-of-view
24
+ - Meticulously refined in every detail
25
+
26
+ ## Frontend Aesthetics Guidelines
27
+
28
+ Focus on:
29
+ - **Typography**: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics; unexpected, characterful font choices. Pair a distinctive display font with a refined body font.
30
+ - **Color & Theme**: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes.
31
+ - **Motion**: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Focus on high-impact moments: one well-orchestrated page load with staggered reveals creates more delight than scattered micro-interactions.
32
+ - **Spatial Composition**: Unexpected layouts. Asymmetry. Overlap. Diagonal flow. Grid-breaking elements. Generous negative space OR controlled density.
33
+ - **Backgrounds & Visual Details**: Create atmosphere and depth rather than defaulting to solid colors. Add contextual effects and textures that match the overall aesthetic.
34
+
35
+ NEVER use generic AI-generated aesthetics like overused font families (Inter, Roboto, Arial, system fonts), cliched color schemes (particularly purple gradients on white backgrounds), predictable layouts, and cookie-cutter design that lacks context-specific character.
36
+
37
+ Interpret creatively and make unexpected choices that feel genuinely designed for the context. No design should be the same. Vary between light and dark themes, different fonts, different aesthetics.
38
+
39
+ **IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details.
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: self-improvement
3
+ description: "Captures learnings, errors, and corrections to enable continuous improvement. Use when: (1) A command or operation fails unexpectedly, (2) User corrects the agent, (3) User requests a capability that doesn't exist, (4) An external API or tool fails, (5) Agent realizes its knowledge is outdated or incorrect, (6) A better approach is discovered for a recurring task."
4
+ metadata: {"lemonade":{"emoji":"🧠"}}
5
+ ---
6
+
7
+ # Self-Improvement Skill
8
+
9
+ Log learnings and errors to markdown files for continuous improvement. Important learnings get promoted to project memory.
10
+
11
+ ## Quick Reference
12
+
13
+ | Situation | Action |
14
+ |-----------|--------|
15
+ | Command/operation fails | Log to `.learnings/ERRORS.md` |
16
+ | User corrects you | Log to `.learnings/LEARNINGS.md` with category `correction` |
17
+ | User wants missing feature | Log to `.learnings/FEATURE_REQUESTS.md` |
18
+ | API/external tool fails | Log to `.learnings/ERRORS.md` with integration details |
19
+ | Knowledge was outdated | Log to `.learnings/LEARNINGS.md` with category `knowledge_gap` |
20
+ | Found better approach | Log to `.learnings/LEARNINGS.md` with category `best_practice` |
21
+ | Broadly applicable learning | Promote to project memory files |
22
+
23
+ ## Setup
24
+
25
+ ```bash
26
+ mkdir -p ~/.lemonade/workspace/.learnings
27
+ ```
28
+
29
+ ## Logging Format
30
+
31
+ ### Learning Entry
32
+
33
+ Append to `.learnings/LEARNINGS.md`:
34
+
35
+ ```markdown
36
+ ## [LRN-YYYYMMDD-XXX] category
37
+
38
+ **Logged**: ISO-8601 timestamp
39
+ **Priority**: low | medium | high | critical
40
+ **Status**: pending
41
+ **Area**: frontend | backend | infra | tests | docs | config
42
+
43
+ ### Summary
44
+ One-line description of what was learned
45
+
46
+ ### Details
47
+ Full context: what happened, what was wrong, what's correct
48
+
49
+ ### Suggested Action
50
+ Specific fix or improvement to make
51
+
52
+ ### Metadata
53
+ - Source: conversation | error | user_feedback
54
+ - Related Files: path/to/file.ext
55
+ - Tags: tag1, tag2
56
+ ```
57
+
58
+ ### Error Entry
59
+
60
+ Append to `.learnings/ERRORS.md`:
61
+
62
+ ```markdown
63
+ ## [ERR-YYYYMMDD-XXX] skill_or_command_name
64
+
65
+ **Logged**: ISO-8601 timestamp
66
+ **Priority**: high
67
+ **Status**: pending
68
+
69
+ ### Summary
70
+ Brief description of what failed
71
+
72
+ ### Error
73
+ Actual error message or output
74
+
75
+ ### Context
76
+ - Command/operation attempted
77
+ - Input or parameters used
78
+
79
+ ### Suggested Fix
80
+ If identifiable, what might resolve this
81
+ ```
82
+
83
+ ### Feature Request Entry
84
+
85
+ Append to `.learnings/FEATURE_REQUESTS.md`:
86
+
87
+ ```markdown
88
+ ## [FEAT-YYYYMMDD-XXX] capability_name
89
+
90
+ **Logged**: ISO-8601 timestamp
91
+ **Priority**: medium
92
+ **Status**: pending
93
+
94
+ ### Requested Capability
95
+ What the user wanted to do
96
+
97
+ ### User Context
98
+ Why they needed it
99
+
100
+ ### Suggested Implementation
101
+ How this could be built
102
+ ```
103
+
104
+ ## Detection Triggers
105
+
106
+ Automatically log when you notice:
107
+
108
+ **Corrections**: "No, that's not right...", "Actually, it should be...", "You're wrong about..."
109
+
110
+ **Feature Requests**: "Can you also...", "I wish you could...", "Is there a way to..."
111
+
112
+ **Knowledge Gaps**: User provides information you didn't know, documentation is outdated
113
+
114
+ **Errors**: Command returns non-zero exit code, exception or stack trace
115
+
116
+ ## Resolving Entries
117
+
118
+ When an issue is fixed, update `**Status**: pending` to `**Status**: resolved` and add a resolution block.
119
+
120
+ ## Best Practices
121
+
122
+ 1. Log immediately - context is freshest right after the issue
123
+ 2. Be specific - future agents need to understand quickly
124
+ 3. Include reproduction steps - especially for errors
125
+ 4. Link related files - makes fixes easier
126
+ 5. Suggest concrete fixes - not just "investigate"
127
+ 6. Promote aggressively - if in doubt, add to project memory
128
+ ```