mirror of
https://github.com/mergisi/awesome-openclaw-agents.git
synced 2026-08-14 08:51:56 +00:00
Add 5 new Gemma skills: regex-builder, meeting-notes, cover-letter, standup-summary, pricing-advisor
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
---
|
||||
name: cover-letter
|
||||
description: Describe the role and your background, get a concise, compelling cover letter paragraph.
|
||||
---
|
||||
|
||||
# Cover Letter
|
||||
|
||||
Give Gemma the job title, company, and a few words about yourself. Get a sharp cover letter opening paragraph — ready to send.
|
||||
|
||||
## Examples
|
||||
|
||||
* "Software engineer at Stripe, 4 years React experience, previously at a fintech startup"
|
||||
* "Product manager at a healthcare startup, background in data analytics and user research"
|
||||
* "Marketing manager at Nike, 3 years in growth marketing, strong in paid social"
|
||||
* "Data scientist at Google, PhD in statistics, built ML models for e-commerce"
|
||||
|
||||
## Instructions
|
||||
|
||||
You MUST use the `run_js` tool with the following exact parameters:
|
||||
|
||||
- data: A JSON string with the following fields:
|
||||
- role: String - the job title they are applying for
|
||||
- company: String - the company name
|
||||
- hook: String - one compelling opening sentence that grabs attention
|
||||
- value: String - one sentence about their most relevant experience or skill
|
||||
- fit: String - one sentence about why this role or company specifically
|
||||
- close: String - one sentence closing with a call to action
|
||||
@@ -0,0 +1,74 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Cover Letter</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #f8f6f1;
|
||||
font-family: Georgia, 'Times New Roman', serif;
|
||||
color: #2c2416;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 6%;
|
||||
}
|
||||
.card {
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 24px 20px;
|
||||
box-shadow: 0 2px 12px rgba(0,0,0,0.08);
|
||||
}
|
||||
.to-line {
|
||||
font-size: clamp(11px, 3vw, 13px);
|
||||
color: #999;
|
||||
font-family: -apple-system, sans-serif;
|
||||
margin-bottom: 18px;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
.to-line strong { color: #444; }
|
||||
.rule { height: 1px; background: #e5e0d5; margin-bottom: 18px; }
|
||||
.letter-body {
|
||||
font-size: clamp(14px, 4vw, 17px);
|
||||
line-height: 1.9;
|
||||
color: #2c2416;
|
||||
}
|
||||
.sentence { display: block; margin-bottom: 14px; }
|
||||
.sentence:last-child { margin-bottom: 0; }
|
||||
.footer {
|
||||
margin-top: 20px;
|
||||
font-size: 11px;
|
||||
color: #c5bba8;
|
||||
font-family: -apple-system, sans-serif;
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="to-line">Applying for: <strong id="role-company"></strong></div>
|
||||
<div class="rule"></div>
|
||||
<div class="letter-body">
|
||||
<span class="sentence" id="hook"></span>
|
||||
<span class="sentence" id="value"></span>
|
||||
<span class="sentence" id="fit"></span>
|
||||
<span class="sentence" id="close"></span>
|
||||
</div>
|
||||
<div class="footer">made on device with gemma</div>
|
||||
</div>
|
||||
<script>
|
||||
const p = new URLSearchParams(window.location.search);
|
||||
const role = p.get('role') || '';
|
||||
const company = p.get('company') || '';
|
||||
document.getElementById('role-company').textContent = role + (company ? ' at ' + company : '');
|
||||
document.getElementById('hook').textContent = p.get('hook') || '';
|
||||
document.getElementById('value').textContent = p.get('value') || '';
|
||||
document.getElementById('fit').textContent = p.get('fit') || '';
|
||||
document.getElementById('close').textContent = p.get('close') || '';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"><title>Cover Letter</title></head>
|
||||
<body>
|
||||
<script>
|
||||
window['ai_edge_gallery_get_result'] = async (data) => {
|
||||
const d = JSON.parse(data);
|
||||
return JSON.stringify({
|
||||
webview: {
|
||||
url: `../assets/webview.html?role=${encodeURIComponent(d.role)}&company=${encodeURIComponent(d.company)}&hook=${encodeURIComponent(d.hook)}&value=${encodeURIComponent(d.value)}&fit=${encodeURIComponent(d.fit)}&close=${encodeURIComponent(d.close)}`
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
name: meeting-notes
|
||||
description: Paste raw meeting notes and get a clean summary with key decisions and action items.
|
||||
---
|
||||
|
||||
# Meeting Notes
|
||||
|
||||
Turn messy meeting notes into a structured summary. Decisions, action items, and next steps — all extracted automatically.
|
||||
|
||||
## Examples
|
||||
|
||||
* "john said we should push the launch to next friday. sarah will handle the email campaign. budget approved at 5k. need to check with legal about the contract."
|
||||
* "discussed q2 roadmap. feature x gets priority. mike owns backend. lisa does design by end of month. still unclear on pricing."
|
||||
* "standup: blocked on api keys. will unblock tomorrow. demo moved to thursday."
|
||||
|
||||
## Instructions
|
||||
|
||||
You MUST use the `run_js` tool with the following exact parameters:
|
||||
|
||||
- data: A JSON string with the following fields:
|
||||
- title: String - a short title for this meeting (3-5 words)
|
||||
- summary: String - one sentence describing what the meeting was about
|
||||
- decision1: String - first key decision made, or empty string
|
||||
- decision2: String - second key decision made, or empty string
|
||||
- action1_who: String - name or role of person responsible for first action item
|
||||
- action1_what: String - what they need to do
|
||||
- action2_who: String - name or role for second action item, or empty string
|
||||
- action2_what: String - what they need to do, or empty string
|
||||
- action3_who: String - name or role for third action item, or empty string
|
||||
- action3_what: String - what they need to do, or empty string
|
||||
- next_step: String - the single most important next step overall
|
||||
@@ -0,0 +1,135 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Meeting Notes</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #fafafa;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
color: #1a1a1a;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 6%;
|
||||
}
|
||||
.card { width: 100%; }
|
||||
.meeting-title {
|
||||
font-size: clamp(18px, 5vw, 26px);
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.summary {
|
||||
font-size: clamp(12px, 3.5vw, 15px);
|
||||
color: #666;
|
||||
margin-bottom: 18px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.section { margin-bottom: 16px; }
|
||||
.section-label {
|
||||
font-size: 10px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 1.5px;
|
||||
text-transform: uppercase;
|
||||
color: #999;
|
||||
margin-bottom: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.section-label::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
.decision {
|
||||
background: #fff8e6;
|
||||
border-left: 3px solid #f59e0b;
|
||||
border-radius: 0 6px 6px 0;
|
||||
padding: 8px 12px;
|
||||
font-size: clamp(12px, 3.5vw, 14px);
|
||||
color: #1a1a1a;
|
||||
margin-bottom: 6px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.action {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.who {
|
||||
flex-shrink: 0;
|
||||
background: #e8f4fd;
|
||||
color: #1a7fc1;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
padding: 2px 8px;
|
||||
border-radius: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.what {
|
||||
font-size: clamp(12px, 3.5vw, 14px);
|
||||
color: #333;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.next {
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #86efac;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
font-size: clamp(13px, 3.5vw, 15px);
|
||||
font-weight: 600;
|
||||
color: #166534;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.footer { font-size: 11px; color: #ccc; text-align: right; margin-top: 14px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="meeting-title" id="title"></div>
|
||||
<div class="summary" id="summary"></div>
|
||||
|
||||
<div class="section" id="decisions-section">
|
||||
<div class="section-label">Decisions</div>
|
||||
<div id="decisions"></div>
|
||||
</div>
|
||||
|
||||
<div class="section" id="actions-section">
|
||||
<div class="section-label">Action Items</div>
|
||||
<div id="actions"></div>
|
||||
</div>
|
||||
|
||||
<div class="section">
|
||||
<div class="section-label">Next Step</div>
|
||||
<div class="next" id="next"></div>
|
||||
</div>
|
||||
|
||||
<div class="footer">made on device with gemma</div>
|
||||
</div>
|
||||
<script>
|
||||
const p = new URLSearchParams(window.location.search);
|
||||
document.getElementById('title').textContent = p.get('title') || 'Meeting Summary';
|
||||
document.getElementById('summary').textContent = p.get('summary') || '';
|
||||
document.getElementById('next').textContent = p.get('next') || '';
|
||||
|
||||
const decisionsEl = document.getElementById('decisions');
|
||||
[p.get('d1'), p.get('d2')].filter(Boolean).forEach(d => {
|
||||
decisionsEl.innerHTML += `<div class="decision">${d}</div>`;
|
||||
});
|
||||
if (!decisionsEl.innerHTML) document.getElementById('decisions-section').style.display = 'none';
|
||||
|
||||
const actionsEl = document.getElementById('actions');
|
||||
[[p.get('a1w'), p.get('a1t')], [p.get('a2w'), p.get('a2t')], [p.get('a3w'), p.get('a3t')]]
|
||||
.filter(([w, t]) => w && t)
|
||||
.forEach(([who, what]) => {
|
||||
actionsEl.innerHTML += `<div class="action"><span class="who">${who}</span><span class="what">${what}</span></div>`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"><title>Meeting Notes</title></head>
|
||||
<body>
|
||||
<script>
|
||||
window['ai_edge_gallery_get_result'] = async (data) => {
|
||||
const d = JSON.parse(data);
|
||||
return JSON.stringify({
|
||||
webview: {
|
||||
url: `../assets/webview.html?title=${encodeURIComponent(d.title)}&summary=${encodeURIComponent(d.summary)}&d1=${encodeURIComponent(d.decision1||'')}&d2=${encodeURIComponent(d.decision2||'')}&a1w=${encodeURIComponent(d.action1_who)}&a1t=${encodeURIComponent(d.action1_what)}&a2w=${encodeURIComponent(d.action2_who||'')}&a2t=${encodeURIComponent(d.action2_what||'')}&a3w=${encodeURIComponent(d.action3_who||'')}&a3t=${encodeURIComponent(d.action3_what||'')}&next=${encodeURIComponent(d.next_step)}`
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,34 @@
|
||||
---
|
||||
name: pricing-advisor
|
||||
description: Describe your product and target customer, get a pricing strategy with three tiers and rationale.
|
||||
---
|
||||
|
||||
# Pricing Advisor
|
||||
|
||||
Stop guessing your price. Describe what you built and who it's for — get a concrete pricing structure with the reasoning behind it.
|
||||
|
||||
## Examples
|
||||
|
||||
* "SaaS tool for freelancers that automates invoicing, early stage"
|
||||
* "Mobile app for fitness tracking, subscription model, targeting gym-goers"
|
||||
* "API service for developers that does image compression, usage-based"
|
||||
* "Online course about marketing for small business owners"
|
||||
* "B2B software for restaurant inventory management"
|
||||
|
||||
## Instructions
|
||||
|
||||
You MUST use the `run_js` tool with the following exact parameters:
|
||||
|
||||
- data: A JSON string with the following fields:
|
||||
- product: String - short description of the product
|
||||
- model: String - recommended pricing model (e.g. subscription, one-time, usage-based, freemium)
|
||||
- tier1_name: String - name for the entry tier (e.g. Starter, Free, Basic)
|
||||
- tier1_price: String - price for tier 1 with period (e.g. $9/mo, Free, $49 one-time)
|
||||
- tier1_for: String - who this tier is for in 5 words or less
|
||||
- tier2_name: String - name for the mid tier (e.g. Pro, Growth, Standard)
|
||||
- tier2_price: String - price for tier 2
|
||||
- tier2_for: String - who this tier is for in 5 words or less
|
||||
- tier3_name: String - name for the top tier (e.g. Team, Business, Enterprise)
|
||||
- tier3_price: String - price for tier 3
|
||||
- tier3_for: String - who this tier is for in 5 words or less
|
||||
- rationale: String - one sentence explaining the key pricing logic or anchor strategy
|
||||
@@ -0,0 +1,126 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Pricing Advisor</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #09090b;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
color: #fafafa;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 6%;
|
||||
}
|
||||
.card { width: 100%; }
|
||||
.product {
|
||||
font-size: clamp(16px, 4.5vw, 22px);
|
||||
font-weight: 700;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.model-badge {
|
||||
display: inline-block;
|
||||
padding: 3px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
background: #18181b;
|
||||
color: #a1a1aa;
|
||||
border: 1px solid #27272a;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
.tiers { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
|
||||
.tier {
|
||||
background: #18181b;
|
||||
border: 1px solid #27272a;
|
||||
border-radius: 12px;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
.tier.highlight {
|
||||
border-color: #3f3f46;
|
||||
background: #1c1c1f;
|
||||
}
|
||||
.tier-left { flex: 1; min-width: 0; }
|
||||
.tier-name {
|
||||
font-size: clamp(14px, 4vw, 17px);
|
||||
font-weight: 700;
|
||||
color: #fafafa;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
.tier-for {
|
||||
font-size: clamp(11px, 3vw, 13px);
|
||||
color: #71717a;
|
||||
}
|
||||
.tier-price {
|
||||
font-size: clamp(16px, 4.5vw, 22px);
|
||||
font-weight: 800;
|
||||
color: #fafafa;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
.rationale {
|
||||
background: #18181b;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
font-size: clamp(12px, 3vw, 14px);
|
||||
color: #71717a;
|
||||
line-height: 1.6;
|
||||
font-style: italic;
|
||||
}
|
||||
.rationale-label {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
color: #52525b;
|
||||
margin-bottom: 4px;
|
||||
font-style: normal;
|
||||
}
|
||||
.footer { font-size: 11px; color: #27272a; text-align: right; margin-top: 12px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="product" id="product"></div>
|
||||
<div class="model-badge" id="model"></div>
|
||||
<div class="tiers" id="tiers"></div>
|
||||
<div class="rationale">
|
||||
<div class="rationale-label">Strategy</div>
|
||||
<span id="rationale"></span>
|
||||
</div>
|
||||
<div class="footer">made on device with gemma</div>
|
||||
</div>
|
||||
<script>
|
||||
const p = new URLSearchParams(window.location.search);
|
||||
document.getElementById('product').textContent = p.get('product') || '';
|
||||
document.getElementById('model').textContent = p.get('model') || '';
|
||||
document.getElementById('rationale').textContent = p.get('rationale') || '';
|
||||
|
||||
const tiers = [
|
||||
{ name: p.get('t1n'), price: p.get('t1p'), for: p.get('t1f') },
|
||||
{ name: p.get('t2n'), price: p.get('t2p'), for: p.get('t2f'), highlight: true },
|
||||
{ name: p.get('t3n'), price: p.get('t3p'), for: p.get('t3f') },
|
||||
];
|
||||
const tiersEl = document.getElementById('tiers');
|
||||
tiers.forEach(t => {
|
||||
if (!t.name) return;
|
||||
tiersEl.innerHTML += `
|
||||
<div class="tier${t.highlight ? ' highlight' : ''}">
|
||||
<div class="tier-left">
|
||||
<div class="tier-name">${t.name}</div>
|
||||
<div class="tier-for">${t.for}</div>
|
||||
</div>
|
||||
<div class="tier-price">${t.price}</div>
|
||||
</div>`;
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"><title>Pricing Advisor</title></head>
|
||||
<body>
|
||||
<script>
|
||||
window['ai_edge_gallery_get_result'] = async (data) => {
|
||||
const d = JSON.parse(data);
|
||||
return JSON.stringify({
|
||||
webview: {
|
||||
url: `../assets/webview.html?product=${encodeURIComponent(d.product)}&model=${encodeURIComponent(d.model)}&t1n=${encodeURIComponent(d.tier1_name)}&t1p=${encodeURIComponent(d.tier1_price)}&t1f=${encodeURIComponent(d.tier1_for)}&t2n=${encodeURIComponent(d.tier2_name)}&t2p=${encodeURIComponent(d.tier2_price)}&t2f=${encodeURIComponent(d.tier2_for)}&t3n=${encodeURIComponent(d.tier3_name)}&t3p=${encodeURIComponent(d.tier3_price)}&t3f=${encodeURIComponent(d.tier3_for)}&rationale=${encodeURIComponent(d.rationale)}`
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,28 @@
|
||||
---
|
||||
name: regex-builder
|
||||
description: Describe a text pattern in plain English and get a working regular expression with an explanation.
|
||||
---
|
||||
|
||||
# Regex Builder
|
||||
|
||||
Turn plain English into a working regex. No regex knowledge required.
|
||||
|
||||
## Examples
|
||||
|
||||
* "Match email addresses"
|
||||
* "Find all phone numbers in format 555-123-4567"
|
||||
* "Match URLs that start with https"
|
||||
* "Find words that start with capital letters"
|
||||
* "Match dates in MM/DD/YYYY format"
|
||||
|
||||
## Instructions
|
||||
|
||||
You MUST use the `run_js` tool with the following exact parameters:
|
||||
|
||||
- data: A JSON string with the following fields:
|
||||
- description: String - the plain English description of the pattern
|
||||
- regex: String - the regular expression pattern (without surrounding slashes)
|
||||
- flags: String - regex flags like g, i, m (or empty string)
|
||||
- explanation: String - plain English explanation of how the regex works
|
||||
- example_match: String - one example string that this regex would match
|
||||
- example_no_match: String - one example string that this regex would NOT match
|
||||
@@ -0,0 +1,116 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Regex Builder</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #0d1117;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
color: #e6edf3;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 6%;
|
||||
}
|
||||
.card { width: 100%; }
|
||||
.label {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
color: #484f58;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
.description {
|
||||
font-size: clamp(15px, 4vw, 18px);
|
||||
font-weight: 600;
|
||||
color: #e6edf3;
|
||||
margin-bottom: 18px;
|
||||
line-height: 1.4;
|
||||
}
|
||||
.regex-box {
|
||||
background: #161b22;
|
||||
border: 1px solid #f78166;
|
||||
border-radius: 10px;
|
||||
padding: 16px;
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.slash { color: #f78166; font-size: 22px; font-family: monospace; }
|
||||
.regex-text {
|
||||
font-family: 'Courier New', monospace;
|
||||
font-size: clamp(14px, 4vw, 20px);
|
||||
color: #7ee787;
|
||||
word-break: break-all;
|
||||
flex: 1;
|
||||
}
|
||||
.flags { color: #f78166; font-family: monospace; font-size: 18px; }
|
||||
.explanation {
|
||||
background: #161b22;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
font-size: clamp(13px, 3.5vw, 15px);
|
||||
color: #8b949e;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.examples { display: flex; gap: 10px; margin-bottom: 14px; }
|
||||
.ex-card {
|
||||
flex: 1;
|
||||
background: #161b22;
|
||||
border-radius: 8px;
|
||||
padding: 10px;
|
||||
}
|
||||
.ex-card .label { margin-bottom: 4px; }
|
||||
.ex-match { border-top: 2px solid #3fb950; }
|
||||
.ex-nomatch { border-top: 2px solid #f85149; }
|
||||
.ex-text {
|
||||
font-family: monospace;
|
||||
font-size: clamp(11px, 3vw, 13px);
|
||||
word-break: break-all;
|
||||
}
|
||||
.ex-match .ex-text { color: #3fb950; }
|
||||
.ex-nomatch .ex-text { color: #f85149; }
|
||||
.footer { font-size: 11px; color: #484f58; text-align: right; margin-top: 4px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="label">Pattern for</div>
|
||||
<div class="description" id="description"></div>
|
||||
<div class="regex-box">
|
||||
<span class="slash">/</span>
|
||||
<span class="regex-text" id="regex"></span>
|
||||
<span class="slash">/</span>
|
||||
<span class="flags" id="flags"></span>
|
||||
</div>
|
||||
<div class="explanation" id="explanation"></div>
|
||||
<div class="examples">
|
||||
<div class="ex-card ex-match">
|
||||
<div class="label">Matches</div>
|
||||
<div class="ex-text" id="match"></div>
|
||||
</div>
|
||||
<div class="ex-card ex-nomatch">
|
||||
<div class="label">No match</div>
|
||||
<div class="ex-text" id="nomatch"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer">made on device with gemma</div>
|
||||
</div>
|
||||
<script>
|
||||
const p = new URLSearchParams(window.location.search);
|
||||
document.getElementById('description').textContent = p.get('description') || '';
|
||||
document.getElementById('regex').textContent = p.get('regex') || '';
|
||||
document.getElementById('flags').textContent = p.get('flags') || '';
|
||||
document.getElementById('explanation').textContent = p.get('explanation') || '';
|
||||
document.getElementById('match').textContent = p.get('match') || '';
|
||||
document.getElementById('nomatch').textContent = p.get('nomatch') || '';
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"><title>Regex Builder</title></head>
|
||||
<body>
|
||||
<script>
|
||||
window['ai_edge_gallery_get_result'] = async (data) => {
|
||||
const d = JSON.parse(data);
|
||||
return JSON.stringify({
|
||||
webview: {
|
||||
url: `../assets/webview.html?description=${encodeURIComponent(d.description)}®ex=${encodeURIComponent(d.regex)}&flags=${encodeURIComponent(d.flags||'')}&explanation=${encodeURIComponent(d.explanation)}&match=${encodeURIComponent(d.example_match)}&nomatch=${encodeURIComponent(d.example_no_match)}`
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: standup-summary
|
||||
description: Tell Gemma what you did, what you're working on, and any blockers. Get a clean standup update.
|
||||
---
|
||||
|
||||
# Standup Summary
|
||||
|
||||
Format your daily standup in seconds. Works for async standups, Slack updates, and team check-ins.
|
||||
|
||||
## Examples
|
||||
|
||||
* "finished the auth flow, working on dashboard today, no blockers"
|
||||
* "reviewed 3 PRs yesterday, today finishing the API integration, blocked on credentials from devops"
|
||||
* "wrote blog post draft, need to edit and publish today, waiting on design assets"
|
||||
* "fixed 2 bugs, implementing search feature today, no blockers but might need help with indexing"
|
||||
|
||||
## Instructions
|
||||
|
||||
You MUST use the `run_js` tool with the following exact parameters:
|
||||
|
||||
- data: A JSON string with the following fields:
|
||||
- yesterday: String - what was completed yesterday (one clear sentence)
|
||||
- today: String - what will be worked on today (one clear sentence)
|
||||
- blocker: String - any blockers or dependencies, or "No blockers" if none
|
||||
- eta: String - estimated completion or milestone if relevant, or empty string
|
||||
- mood: String - one word energy level: focused, energized, tired, steady, or blocked
|
||||
@@ -0,0 +1,118 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Standup Summary</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
background: #111827;
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
||||
color: #f9fafb;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
padding: 6%;
|
||||
}
|
||||
.card { width: 100%; }
|
||||
.top-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.title { font-size: clamp(16px, 4.5vw, 22px); font-weight: 700; }
|
||||
.mood {
|
||||
padding: 4px 12px;
|
||||
border-radius: 20px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
.focused { background: #1e3a5f; color: #60a5fa; }
|
||||
.energized { background: #1a3326; color: #34d399; }
|
||||
.tired { background: #3b2a1a; color: #fb923c; }
|
||||
.steady { background: #1f2937; color: #9ca3af; }
|
||||
.blocked { background: #3b1a1a; color: #f87171; }
|
||||
.row {
|
||||
background: #1f2937;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
.row-label {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 1px;
|
||||
text-transform: uppercase;
|
||||
margin-bottom: 6px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
.row-label .icon { font-size: 14px; }
|
||||
.yesterday .row-label { color: #9ca3af; }
|
||||
.today .row-label { color: #60a5fa; }
|
||||
.blocker-row .row-label { color: #f87171; }
|
||||
.eta-row .row-label { color: #a78bfa; }
|
||||
.row-text {
|
||||
font-size: clamp(13px, 3.5vw, 16px);
|
||||
line-height: 1.6;
|
||||
color: #e5e7eb;
|
||||
}
|
||||
.footer { font-size: 11px; color: #374151; text-align: right; margin-top: 10px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="top-row">
|
||||
<div class="title">Daily Standup</div>
|
||||
<div class="mood" id="mood"></div>
|
||||
</div>
|
||||
<div class="row yesterday">
|
||||
<div class="row-label"><span class="icon">✓</span> Yesterday</div>
|
||||
<div class="row-text" id="yesterday"></div>
|
||||
</div>
|
||||
<div class="row today">
|
||||
<div class="row-label"><span class="icon">→</span> Today</div>
|
||||
<div class="row-text" id="today"></div>
|
||||
</div>
|
||||
<div class="row blocker-row" id="blocker-row">
|
||||
<div class="row-label"><span class="icon">⚠</span> Blocker</div>
|
||||
<div class="row-text" id="blocker"></div>
|
||||
</div>
|
||||
<div class="row eta-row" id="eta-row">
|
||||
<div class="row-label"><span class="icon">◷</span> ETA</div>
|
||||
<div class="row-text" id="eta"></div>
|
||||
</div>
|
||||
<div class="footer">made on device with gemma</div>
|
||||
</div>
|
||||
<script>
|
||||
const p = new URLSearchParams(window.location.search);
|
||||
const mood = p.get('mood') || 'steady';
|
||||
const moodEl = document.getElementById('mood');
|
||||
moodEl.textContent = mood;
|
||||
moodEl.className = 'mood ' + mood.toLowerCase();
|
||||
|
||||
document.getElementById('yesterday').textContent = p.get('yesterday') || '';
|
||||
document.getElementById('today').textContent = p.get('today') || '';
|
||||
|
||||
const blocker = p.get('blocker') || '';
|
||||
if (blocker && blocker.toLowerCase() !== 'no blockers') {
|
||||
document.getElementById('blocker').textContent = blocker;
|
||||
} else {
|
||||
document.getElementById('blocker-row').style.display = 'none';
|
||||
}
|
||||
|
||||
const eta = p.get('eta') || '';
|
||||
if (eta) {
|
||||
document.getElementById('eta').textContent = eta;
|
||||
} else {
|
||||
document.getElementById('eta-row').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8"><title>Standup Summary</title></head>
|
||||
<body>
|
||||
<script>
|
||||
window['ai_edge_gallery_get_result'] = async (data) => {
|
||||
const d = JSON.parse(data);
|
||||
return JSON.stringify({
|
||||
webview: {
|
||||
url: `../assets/webview.html?yesterday=${encodeURIComponent(d.yesterday)}&today=${encodeURIComponent(d.today)}&blocker=${encodeURIComponent(d.blocker)}&eta=${encodeURIComponent(d.eta||'')}&mood=${encodeURIComponent(d.mood)}`
|
||||
}
|
||||
});
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user