migrate: consolidate all skills and agents from ~/clawd

- Moved 4 skills: browser-testing, claude-multimedia, exa-search, gravl-research
- Moved 14 agents: architect, backend-dev, browser-tester, coach, data, flight, frontend-dev, gravl-pm, gravl-researcher, nutritionist, research, reviewer, staging, update
- Created symlinks from ~/clawd/skills and ~/clawd/agents back to hub
- Single source of truth in claude-agents-skills repo
This commit is contained in:
2026-03-01 09:56:30 +01:00
parent 72d0676416
commit 8cc0dcb167
38 changed files with 5356 additions and 0 deletions
+78
View File
@@ -0,0 +1,78 @@
---
name: exa-search
description: Web search, code search, company research, and deep research via Exa AI. Use for real-time web queries, finding code examples, researching companies, finding people, or starting deep research tasks. Triggers on web search needs, code lookups, company/people research.
---
# Exa Search
Exa AI search via their hosted MCP server. Provides web search, code context, company research, people search, and deep research capabilities.
## Quick Start
```bash
# Web search
~/clawd/skills/exa-search/scripts/exa-cli.mjs search "latest AI agent frameworks 2026"
# Code search
~/clawd/skills/exa-search/scripts/exa-cli.mjs code "Python async retry with exponential backoff"
# Company research
~/clawd/skills/exa-search/scripts/exa-cli.mjs company "Anthropic AI"
# People search
~/clawd/skills/exa-search/scripts/exa-cli.mjs people "CTO machine learning startups Stockholm"
# Crawl specific URL
~/clawd/skills/exa-search/scripts/exa-cli.mjs crawl "https://example.com/article"
# Deep research (async)
~/clawd/skills/exa-search/scripts/exa-cli.mjs research "State of quantum computing 2026"
~/clawd/skills/exa-search/scripts/exa-cli.mjs research-check <task-id>
```
## Available Tools
| Tool | Use Case |
|------|----------|
| `search` | General web search, news, current events |
| `search-advanced` | Filtered search (domains, dates, content type) |
| `code` | Code examples, docs, GitHub, StackOverflow |
| `crawl` | Full content from specific URL |
| `company` | Company info, news, competitors |
| `people` | LinkedIn profiles, professional bios |
| `research` | Start deep AI research task |
| `research-check` | Check research task status |
## API Key (Optional)
Free tier has rate limits. Add your key for higher limits:
```bash
export EXA_API_KEY="your-key-here"
```
Get key: https://dashboard.exa.ai/api-keys
## Advanced Search Options
```bash
# Filter by domain
exa-cli.mjs search-advanced "AI news" --domains techcrunch.com,wired.com
# Date range
exa-cli.mjs search-advanced "OpenAI announcements" --after 2026-01-01
# More results
exa-cli.mjs search "topic" --num 20
```
## Tool Details
See [references/tools.md](references/tools.md) for full parameter documentation.
## Tips
- **Code search**: Include language name ("Python", "Go", "TypeScript") for better results
- **Company research**: Start with company name, then drill into specifics
- **Deep research**: Returns task ID; poll with `research-check` until complete
- **Rate limits**: Free tier ~1 req/sec; add API key for more
+211
View File
@@ -0,0 +1,211 @@
# Exa MCP Tools Reference
## web_search_exa
Basic web search with clean, ready-to-use content.
**Parameters:**
- `query` (string, required): Search query
- `numResults` (number, optional): Number of results (default: 10)
**Example:**
```bash
exa-cli.mjs search "AI agent frameworks 2026"
```
---
## web_search_advanced_exa
Advanced search with full control over filters.
**Parameters:**
- `query` (string, required): Search query
- `numResults` (number, optional): Number of results
- `type` (string, optional): "auto", "neural", "deep"
- `category` (string, optional): "company", "news", "tweet", "people", "personal site"
- `includeDomains` (array, optional): Only include these domains
- `excludeDomains` (array, optional): Exclude these domains
- `startPublishedDate` (string, optional): ISO date YYYY-MM-DD
- `endPublishedDate` (string, optional): ISO date YYYY-MM-DD
- `livecrawl` (string, optional): "never", "fallback", "always"
**Category Restrictions:**
- `category: "company"`: Cannot use domain or date filters
- `category: "people"`: Cannot use date filters, excludeDomains, or excludeText
- `includeText`/`excludeText`: Only single-item arrays
**Examples:**
```bash
# Domain filter
exa-cli.mjs search-advanced "AI news" --domains techcrunch.com,wired.com
# Date filter
exa-cli.mjs search-advanced "OpenAI" --after 2026-01-01 --before 2026-02-01
# More results
exa-cli.mjs search-advanced "machine learning" --num 30
```
---
## get_code_context_exa
Find code examples, documentation, and solutions from GitHub, StackOverflow, and technical docs.
**Parameters:**
- `query` (string, required): Code-related query
- `tokensNum` (number, optional): Token limit (default: 5000, range: 1000-50000)
**Best Practices:**
- Always include programming language in query
- Include framework + version when relevant
- Use exact identifiers (function names, error messages)
**Token Strategy:**
- Focused snippet: 1000-3000
- Most tasks: 5000
- Complex integration: 10000-20000
**Examples:**
```bash
# Python specific
exa-cli.mjs code "Python asyncio retry exponential backoff"
# Framework specific
exa-cli.mjs code "Next.js 14 server actions form handling"
# Error lookup
exa-cli.mjs code "ECONNREFUSED Node.js Docker"
```
---
## crawling_exa
Get full content from a specific URL.
**Parameters:**
- `url` (string, required): URL to crawl
**Example:**
```bash
exa-cli.mjs crawl "https://docs.anthropic.com/claude/docs"
```
---
## company_research_exa
Research any company for business information, news, and insights.
**Parameters:**
- `companyName` (string, required): Company name to research
**Returns:**
- Company overview
- Recent news
- Key metrics (when available)
- Competitors
- Funding info
**Example:**
```bash
exa-cli.mjs company "Stripe"
exa-cli.mjs company "Klarna fintech"
```
---
## people_search_exa
Find people and their professional profiles.
**Parameters:**
- `query` (string, required): Search query (role, name, company, etc.)
- `numResults` (number, optional): Number of results
**Notes:**
- Returns public LinkedIn profiles and professional bios
- Works best with role + location/company
**Examples:**
```bash
exa-cli.mjs people "VP Engineering AI startups San Francisco"
exa-cli.mjs people "CTO fintech Stockholm"
```
---
## deep_researcher_start
Start an AI research agent that searches, reads, and writes a detailed report.
**Parameters:**
- `query` (string, required): Research topic
**Returns:**
- `taskId`: Use with `research-check` to get results
**Note:** Deep research is async. Poll with `research-check` until status is "completed".
**Example:**
```bash
exa-cli.mjs research "Current state of nuclear fusion energy startups 2026"
# Returns: taskId: abc123...
exa-cli.mjs research-check abc123
# Poll until complete
```
---
## deep_researcher_check
Check status and get results from a deep research task.
**Parameters:**
- `taskId` (string, required): Task ID from `deep_researcher_start`
**Status Values:**
- `pending`: Still processing
- `completed`: Results ready
- `failed`: Error occurred
**Example:**
```bash
exa-cli.mjs research-check abc123-def456-...
```
---
## Rate Limits
**Free Plan:**
- ~1 request/second
- 2000 requests/month
**With API Key:**
- Higher limits based on plan
- Get key: https://dashboard.exa.ai/api-keys
Set key:
```bash
export EXA_API_KEY="your-key-here"
```
---
## MCP Server URL
Base: `https://mcp.exa.ai/mcp`
With all tools:
```
https://mcp.exa.ai/mcp?tools=web_search_exa,web_search_advanced_exa,get_code_context_exa,crawling_exa,company_research_exa,people_search_exa,deep_researcher_start,deep_researcher_check
```
With API key:
```
https://mcp.exa.ai/mcp?exaApiKey=YOUR_KEY&tools=...
```
+221
View File
@@ -0,0 +1,221 @@
#!/usr/bin/env node
/**
* Exa Search CLI - Wrapper for Exa MCP Server
*
* Usage:
* ./exa-cli.mjs search "query"
* ./exa-cli.mjs code "Python async patterns"
* ./exa-cli.mjs company "Anthropic"
* ./exa-cli.mjs people "CTO AI startups"
* ./exa-cli.mjs crawl "https://example.com"
* ./exa-cli.mjs research "topic for deep research"
* ./exa-cli.mjs research-check <taskId>
*/
const MCP_BASE = 'https://mcp.exa.ai/mcp';
const TOOLS_PARAM = 'tools=web_search_exa,web_search_advanced_exa,get_code_context_exa,crawling_exa,company_research_exa,people_search_exa,deep_researcher_start,deep_researcher_check';
function getMcpUrl() {
const apiKey = process.env.EXA_API_KEY;
let url = `${MCP_BASE}?${TOOLS_PARAM}`;
if (apiKey) {
url += `&exaApiKey=${apiKey}`;
}
return url;
}
async function callMcp(toolName, args) {
const url = getMcpUrl();
const request = {
jsonrpc: '2.0',
id: Date.now(),
method: 'tools/call',
params: {
name: toolName,
arguments: args
}
};
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream',
},
body: JSON.stringify(request)
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${await response.text()}`);
}
const contentType = response.headers.get('content-type') || '';
// Handle SSE streaming response
if (contentType.includes('text/event-stream')) {
const text = await response.text();
const lines = text.split('\n');
let result = null;
for (const line of lines) {
if (line.startsWith('data: ')) {
const data = line.slice(6);
if (data && data !== '[DONE]') {
try {
const parsed = JSON.parse(data);
if (parsed.result) {
result = parsed.result;
} else if (parsed.error) {
throw new Error(`MCP Error: ${JSON.stringify(parsed.error)}`);
}
} catch (e) {
// Skip non-JSON lines
}
}
}
}
return result;
}
// Handle regular JSON response
const result = await response.json();
if (result.error) {
throw new Error(`MCP Error: ${JSON.stringify(result.error)}`);
}
return result.result;
}
function parseArgs(args) {
const parsed = { _: [] };
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg.startsWith('--')) {
const key = arg.slice(2);
const next = args[i + 1];
if (next && !next.startsWith('--')) {
parsed[key] = next;
i++;
} else {
parsed[key] = true;
}
} else {
parsed._.push(arg);
}
}
return parsed;
}
function formatContent(content) {
if (!content) return '';
if (Array.isArray(content)) {
return content.map(c => c.text || JSON.stringify(c)).join('\n');
}
if (typeof content === 'string') return content;
return JSON.stringify(content, null, 2);
}
async function main() {
const args = parseArgs(process.argv.slice(2));
const command = args._[0];
const query = args._.slice(1).join(' ');
const numResults = parseInt(args.num) || 10;
if (!command) {
console.log(`Exa Search CLI
Commands:
search <query> Web search
search-advanced <query> Advanced search with filters
code <query> Code/documentation search
crawl <url> Get full page content
company <name> Company research
people <query> People/profile search
research <topic> Start deep research
research-check <taskId> Check research status
Options:
--num <n> Number of results (default: 10)
--domains <list> Comma-separated domains (advanced search)
--after <date> Results after date YYYY-MM-DD (advanced search)
--before <date> Results before date YYYY-MM-DD (advanced search)
--tokens <n> Token limit for code search (default: 5000)
Environment:
EXA_API_KEY Your Exa API key (optional, for higher rate limits)
`);
process.exit(0);
}
try {
let result;
switch (command) {
case 'search':
if (!query) throw new Error('Query required');
result = await callMcp('web_search_exa', { query, numResults });
break;
case 'search-advanced':
if (!query) throw new Error('Query required');
const advancedArgs = { query, numResults };
if (args.domains) {
advancedArgs.includeDomains = args.domains.split(',');
}
if (args.after) {
advancedArgs.startPublishedDate = args.after;
}
if (args.before) {
advancedArgs.endPublishedDate = args.before;
}
result = await callMcp('web_search_advanced_exa', advancedArgs);
break;
case 'code':
if (!query) throw new Error('Query required');
const tokensNum = parseInt(args.tokens) || 5000;
result = await callMcp('get_code_context_exa', { query, tokensNum });
break;
case 'crawl':
if (!query) throw new Error('URL required');
result = await callMcp('crawling_exa', { url: query });
break;
case 'company':
if (!query) throw new Error('Company name required');
result = await callMcp('company_research_exa', { companyName: query });
break;
case 'people':
if (!query) throw new Error('Query required');
result = await callMcp('people_search_exa', { query, numResults });
break;
case 'research':
if (!query) throw new Error('Research topic required');
result = await callMcp('deep_researcher_start', { query });
break;
case 'research-check':
if (!query) throw new Error('Task ID required');
result = await callMcp('deep_researcher_check', { taskId: query });
break;
default:
console.error(`Unknown command: ${command}`);
process.exit(1);
}
console.log(formatContent(result?.content || result));
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
}
main();