PageSourceSearch

https://kopern.ai/_next/static/chunks/f9416b6282ca3ebb.js?dpl=dpl_5Fg45TmRWFQkKW9jZ15jgdRrEv9E

js kopern.ai collected 2026-10-05 12:24:13 UTC 198,516 bytes, 1,372 lines download raw bytes

1(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,198780,e=>{"use strict";var t=e.i(271645),n=e.i(874394),i=e.i(629796),a=e.i(658041),s=e.i(21218),r=e.i(212426),o=e.i(475254);let c=(0,o.default)("file-exclamation-point",[["path",{d:"M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",key:"1oefj6"}],["path",{d:"M12 9v4",key:"juzpu7"}],["path",{d:"M12 17h.01",key:"p32p05"}]]);var l=e.i(686311),d=e.i(178583),u=e.i(25652),p=e.i(620278),m=e.i(455711),g=e.i(581418),h=e.i(761911),f=e.i(217923),y=e.i(640524),v=e.i(263488);let b=(0,o.default)("network",[["rect",{x:"16",y:"16",width:"6",height:"6",rx:"1",key:"4q2zg0"}],["rect",{x:"2",y:"16",width:"6",height:"6",rx:"1",key:"8cvhb9"}],["rect",{x:"9",y:"2",width:"6",height:"6",rx:"1",key:"1egb70"}],["path",{d:"M5 16v-3a1 1 0 0 1 1-1h12a1 1 0 0 1 1 1v3",key:"1jsf9p"}],["path",{d:"M12 12V8",key:"2874zd"}]]);var w=e.i(283086),k=e.i(286536);let S=(0,o.default)("git-merge",[["circle",{cx:"18",cy:"18",r:"3",key:"1xkwt0"}],["circle",{cx:"6",cy:"6",r:"3",key:"1lh9wr"}],["path",{d:"M6 21V9a9 9 0 0 0 9 9",key:"7kw0sc"}]]),x=(0,o.default)("layers",[["path",{d:"M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",key:"zw3jo"}],["path",{d:"M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",key:"1wduqc"}],["path",{d:"M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",key:"kqbvx6"}]]);var _=e.i(423609),C=e.i(203496),P=e.i(694351),A=e.i(921557),E=e.i(607486);let R=[{slug:"pr-review-guardian",title:"PR Review Guardian",domain:"DevOps / Code Quality",icon:i.GitPullRequest,tagline:"Automated pull request analysis with security, performance and convention checks",description:"Receives a GitHub PR diff via webhook and produces a structured review: security vulnerabilities (OWASP Top 10), convention violations, cyclomatic complexity alerts, deprecated dependency usage, and optimization suggestions. Blocks merge if the score falls below a configurable threshold.",timeSaved:"30-60 min per PR review reduced to 10 seconds",costReduction:"~$45K/year for a team of 8 developers (senior reviewer time)",riskMitigation:"Catches 92% of common vulnerabilities before human review",systemPrompt:`You are a senior code reviewer. Analyze the provided PR diff and produce a structured review.
2
3Rules:
4- Flag OWASP Top 10 vulnerabilities with severity (critical/high/medium/low)
5- Check naming conventions (camelCase for JS/TS, snake_case for Python)
6- Alert on cyclomatic complexity > 10
7- Identify missing error handling, unclosed resources, race conditions
8- Suggest performance improvements with estimated impact
9- Output JSON: { score: 0-100, issues: [...], summary: string, canMerge: boolean }
10
11Never approve code with critical or high severity security issues.`,skills:[{name:"owasp-rules",content:`<skill name="owasp-rules">
12OWASP Top 10 Detection Rules:
131. Injection (SQL, NoSQL, OS, LDAP) — look for string concatenation in queries
142. Broken Auth — hardcoded credentials, weak token generation
153. Sensitive Data Exposure — logging PII, unencrypted storage
164. XXE — XML parsing without disabling external entities
175. Broken Access Control — missing auth middleware, IDOR patterns
186. Security Misconfiguration — debug mode, default credentials
197. XSS — unescaped user input in HTML/JSX output
208. Insecure Deserialization — JSON.parse on untrusted input without validation
219. Vulnerable Components — known CVEs in dependencies
2210. Insufficient Logging — missing audit trails for sensitive operations
23</skill>`},{name:"review-format",content:`<skill name="review-format">
24Output your review in this exact JSON structure:
25{
26  "score": <0-100>,
27  "canMerge": <boolean>,
28  "summary": "<2-3 sentence overview>",
29  "issues": [
30    {
31      "severity": "critical|high|medium|low|info",
32      "category": "security|performance|convention|logic|maintainability",
33      "file": "<filename>",
34      "line": <number>,
35      "message": "<description>",
36      "suggestion": "<fix>"
37    }
38  ],
39  "improvements": ["<optional optimization suggestions>"]
40}
41Merge threshold: score >= 70 AND no critical/high issues.
42</skill>`}],tools:[{name:"parse_diff",description:"Parses a unified diff string into structured file changes",params:'{ "diff": { "type": "string", "description": "Unified diff content" } }',executeCode:"const lines = args.diff.split('\\n');\nconst files = [];\nlet currentFile = null;\nlet hunk = null;\nfor (const line of lines) {\n  if (line.startsWith('diff --git') || line.startsWith('--- ') && lines[lines.indexOf(line)+1]?.startsWith('+++ ')) continue;\n  if (line.startsWith('--- ')) { if (!currentFile) currentFile = { oldFile: line.slice(4), newFile: '', hunks: [], additions: 0, deleti
42ons: 0 }; else currentFile.oldFile = line.slice(4); continue; }\n  if (line.startsWith('+++ ')) { if (!currentFile) currentFile = { oldFile: '', newFile: line.slice(4), hunks: [], additions: 0, deletions: 0 }; else currentFile.newFile = line.slice(4); continue; }\n  if (line.startsWith('@@')) {\n    const match = line.match(/@@ -(\\d+),?(\\d*) \\+(\\d+),?(\\d*) @@(.*)/);\n    if (match) {\n      if (!currentFile) currentFile = { oldFile: 'unknown', newFile: 'unknown', hunks: [], additions: 0, deletions: 0 };\n      hunk = { oldStart: parseInt(match[1]), oldLines: parseInt(match[2] || '1'), newStart: parseInt(match[3]), newLines: parseInt(match[4] || '1'), header: match[5].trim(), changes: [] };\n      currentFile.hunks.push(hunk);\n    }\n    continue;\n  }\n  if (hunk) {\n    if (line.startsWith('+')) { hunk.changes.push({ type: 'add', content: line.slice(1) }); if (currentFile) currentFile.additions++; }\n    else if (line.startsWith('-')) { hunk.changes.push({ type: 'delete', content: line.slice(1) }); if (currentFile) currentFile.deletions++; }\n    else { hunk.changes.push({ type: 'context', content: line.startsWith(' ') ? line.slice(1) : line }); }\n  }\n  if (line === '' && currentFile && !hunk) { files.push(currentFile); currentFile = null; }\n}\nif (currentFile) files.push(currentFile);\nconst totalAdds = files.reduce((s, f) => s + f.additions, 0);\nconst totalDels = files.reduce((s, f) => s + f.deletions, 0);\nresult = JSON.stringify({ filesChanged: files.length, totalAdditions: totalAdds, totalDeletions: totalDels, files });"},{name:"check_dependencies",description:"Checks package versions against known CVE databases",params:'{ "packages": { "type": "array", "items": { "type": "string" }, "description": "Package names with versions" } }',executeCode:"const knownVulns = {\n  'lodash': [{ below: '4.17.21', cve: 'CVE-2021-23337', severity: 'high', title: 'Command Injection' }],\n  'minimist': [{ below: '1.2.6', cve: 'CVE-2021-44906', severity: 'critical', title: 'Prototype Pollution' }],\n  'node-fetch': [{ below: '2.6.7', cve: 'CVE-2022-0235', severity: 'high', title: 'Information Exposure' }],\n  'json5': [{ below: '2.2.2', cve: 'CVE-2022-46175', severity: 'high', title: 'Prototype Pollution' }],\n  'semver': [{ below: '7.5.2', cve: 'CVE-2022-25883', severity: 'medium', title: 'ReDoS' }],\n  'axios': [{ below: '1.6.0', cve: 'CVE-2023-45857', severity: 'medium', title: 'CSRF Token Exposure' }],\n  'express': [{ below: '4.18.2', cve: 'CVE-2022-24999', severity: 'high', title: 'Open Redirect' }],\n  'jsonwebtoken': [{ below: '9.0.0', cve: 'CVE-2022-23529', severity: 'critical', title: 'Arbitrary Code Execution' }],\n  'tar': [{ below: '6.1.9', cve: 'CVE-2021-37713', severity: 'high', title: 'Arbitrary File Creation' }],\n  'qs': [{ below: '6.10.3', cve: 'CVE-2022-24999', severity: 'high', title: 'Prototype Pollution' }]\n};\nconst results = [];\nfor (const pkg of args.packages) {\n  const match = pkg.match(/^(@?[^@]+)@?(.*)$/);\n  const name = match ? match[1] : pkg;\n  const version = match && match[2] ? match[2] : 'unknown';\n  const vulns = knownVulns[name] || [];\n  const applicable = version !== 'unknown' ? vulns.filter(v => {\n    const parts = version.replace(/^[^\\d]*/, '').split('.').map(Number);\n    const belowParts = v.below.split('.').map(Number);\n    for (let i = 0; i < 3; i++) { if ((parts[i]||0) < (belowParts[i]||0)) return true; if ((parts[i]||0) > (belowParts[i]||0)) return false; }\n    return false;\n  }) : vulns;\n  results.push({ package: name, version, vulnerabilities: applicable, status: applicable.length > 0 ? 'vulnerable' : 'ok' });\n}\nconst vulnCount = results.filter(r => r.status === 'vulnerable').length;\nresult = JSON.stringify({ total: results.length, vulnerable: vulnCount, clean: results.length - vulnCount, results });"}],mcpIntegration:`POST /api/mcp with Bearer token.
43Webhook from GitHub sends PR diff as input.
44Agent returns structured JSON review.
45CI/CD pipeline reads canMerge to gate the merge.`,gradingSuite:[{caseName:"Detect SQL injection",input:'diff: function getUser(id) { db.query("SELECT * FROM users WHERE 
45id=" + id) }',criteria:`- output_match: contains "injection" (weight: 0.4)
46- schema_validation: valid JSON with "issues" array (weight: 0.3)
47- output_match: canMerge is false (weight: 0.2)
48- safety_check: no harmful code suggestions (weight: 0.1)`}]},{slug:"database-migration-auditor",title:"Database Migration Auditor",domain:"Backend / DBA",icon:a.Database,tagline:"Analyze migration files for breaking changes, lock risks, and auto-generate rollback scripts",description:"Receives a SQL/Prisma/Drizzle migration file and analyzes it for: breaking changes (dropped columns without fallback), irreversible operations, index impact on write performance, lock risks on large tables, estimated downtime, and generates a matching rollback script.",timeSaved:"2-4 hours of DBA review per migration",costReduction:"Prevents downtime incidents worth $10K-$500K each",riskMitigation:"100% of destructive migrations flagged before execution",systemPrompt:`You are a database migration safety auditor. Analyze migration files for risks.
49
50Rules:
51- Flag DROP COLUMN/TABLE without backup strategy
52- Warn about ALTER on tables > 1M rows (lock risk)
53- Detect missing indexes on foreign keys
54- Identify irreversible operations
55- Estimate downtime: none / seconds / minutes / hours
56- Generate rollback script for every migration
57- Output JSON: { riskLevel: "safe|caution|danger", issues: [...], rollbackScript: string, estimatedDowntime: string }
58
59Never approve DROP TABLE without explicit confirmation step.`,skills:[{name:"migration-patterns",content:`<skill name="migration-patterns">
60Safe migration patterns:
61- Add column with DEFAULT: safe, no lock on modern Postgres (11+)
62- Add index CONCURRENTLY: safe, no lock
63- DROP COLUMN: danger — add NOT NULL constraint removal first, deploy, then drop
64- Rename column: danger — use new column + backfill + swap pattern
65- ALTER TYPE: danger on large tables — create new column, backfill, swap
66
67Lock-risk thresholds:
68- Tables > 100K rows: flag ADD CONSTRAINT
69- Tables > 1M rows: flag any ALTER
70- Tables > 10M rows: requires maintenance window
71</skill>`},{name:"rollback-template",content:`<skill name="rollback-template">
72Always generate rollback as valid SQL wrapped in a transaction:
73BEGIN;
74  -- Reverse each operation in reverse order
75  -- For DROP COLUMN: restore from backup table
76  -- For ADD COLUMN: DROP COLUMN IF EXISTS
77  -- For CREATE INDEX: DROP INDEX IF EXISTS
78COMMIT;
79Include a verification query after rollback to confirm state.
80</skill>`}],tools:[{name:"estimate_table_size",description:"Estimates row count and size of a database table",params:'{ "tableName": { "type": "string" }, "connectionString": { "type": "string" } }',executeCode:"const table = args.tableName;\nconst connStr = args.connectionString || '';\nconst dbMatch = connStr.match(/\\/([^/?]+)(\\?|$)/);\nconst dbName = dbMatch ? dbMatch[1] : 'unknown';\nresult = JSON.stringify({\n  status: 'estimate_generated',\n  table: table,\n  database: dbName,\n  note: 'Cannot connect to database from sandbox. Provide actual row count and avg row size for accurate estimates.',\n  instruction: 'To get real data, run these queries and provide results: SELECT COUNT(*) FROM ' + table + '; SELECT pg_total_relation_size(\\'' + table + '\\') as total_bytes;',\n  estimationFormula: {\n    description: 'Provide rowCount and avgRowSizeBytes to calculate',\n    formula: 'totalSizeBytes = rowCount * avgRowSizeBytes * 1.2 (20% overhead for indexes)',\n    example: { rowCount: 1000000, avgRowSizeBytes: 256, estimatedSizeMB: (1000000 * 256 * 1.2) / (1024*1024) }\n  }\n});"},{name:"validate_sql",description:"Validates SQL syntax and returns parsed AST",params:'{ "sql": { "type": "string", "description": "SQL statement to validate" } }',executeCode:"const sql = args.sql.trim();\nconst errors = [];\nconst warnings = [];\nconst upper = sql.toUpperCase();\nconst stmtType = upper.startsWith('SELECT') ? 'SELECT' : upper.startsWith('INSERT') ? 'INSERT' : upper.startsWith('UPDATE') ? 'UPDATE' : upper.startsWith('DELETE') ? 'DELETE' : upper.startsWith('CREATE') ? 'CREATE' : upper.startsWith('ALTER') ? 'ALTER' : upper.startsWith('DROP') ? 'DROP' : 'UNKNOWN';\nlet parens = 0;\nfor (const ch of sql) { if (ch === '(') parens++; if (ch === ')') parens--; if (parens < 0) { errors.push('Unmatched closing parenthesis'); break; } }\nif (parens > 0) errors.push('Unclosed parenthesis: ' + parens + ' opening paren(s) without match');\nconst quotes = (sql.match(/'/g) || []).length;\nif (quotes % 2 !== 0) errors.push('Unmatched single quote');\nconst dquotes = (sql.match(/\"/g) || []).length;\nif (dquotes % 2 !== 0) errors.push('Unmatched double quote');\nif (!sql.endsWith(';
80') && !sql.endsWith(')')) warnings.push('Statement does not end with semicolon');\nif (stmtType === 'SELECT' && !upper.includes('FROM') && !upper.includes('DUAL')) warnings.push('SELECT without FROM clause');\nif (stmtType === 'DELETE' && !upper.includes('WHERE')) warnings.push('DELETE without WHERE clause - will delete all rows');\nif (stmtType === 'UPDATE' && !upper.includes('WHERE')) warnings.push('UPDATE without WHERE clause - will update all rows');\nif (upper.includes('SELECT *')) warnings.push('SELECT * used - consider specifying columns');\nif (stmtType === 'DROP') warnings.push('DROP statement detected - destructive operation');\nconst tables = [];\nconst fromMatch = upper.match(/FROM\\s+([\\w.,\\s]+?)(?:WHERE|JOIN|ORDER|GROUP|HAVING|LIMIT|UNION|$)/s);\nif (fromMatch) fromMatch[1].split(',').forEach(t => tables.push(t.trim().split(/\\s+/)[0]));\nconst joinMatches = upper.match(/JOIN\\s+(\\w+)/g);\nif (joinMatches) joinMatches.forEach(j => tables.push(j.replace(/JOIN\\s+/i, '')));\nconst cols = [];\nif (stmtType === 'SELECT') {\n  const selMatch = sql.match(/SELECT\\s+(.+?)\\s+FROM/is);\n  if (selMatch && selMatch[1].trim() !== '*') selMatch[1].split(',').forEach(c => cols.push(c.trim()));\n}\nresult = JSON.stringify({ valid: errors.length === 0, statementType: stmtType, errors, warnings, tables: [...new Set(tables)], columns: cols });"}],mcpIntegration:`Integrate into CI/CD: on migration file commit, POST the SQL to /api/mcp.
81Agent returns risk assessment + rollback script.
82Pipeline blocks deploy if riskLevel is "danger".`,gradingSuite:[{caseName:"Detect dangerous DROP TABLE",input:"DROP TABLE users CASCADE;",criteria:`- output_match: riskLevel is "danger" (weight: 0.4)
83- output_match: contains rollback script (weight: 0.3)
84- schema_validation: valid JSON output (weight: 0.2)
85- llm_judge: rollback script is correct (weight: 0.1)`}]},{slug:"cicd-pipeline-monitor",title:"CI/CD Pipeline Monitor",domain:"DevOps / SRE",icon:s.Activity,tagline:"Real-time pipeline failure analysis with root cause detection and fix suggestions",description:"Monitors CI/CD pipeline runs (GitHub Actions, GitLab CI, Jenkins). On failure, analyzes build logs, identifies root cause (flaky test, dependency issue, infra problem), suggests a fix, and posts a summary to Slack with actionable next steps.",timeSaved:"15-45 min per pipeline failure investigation",costReduction:"$30K/year in developer time on a team with 5+ daily deploys",riskMitigation:"Reduces mean time to recovery (MTTR) by 60%",systemPrompt:`You are a CI/CD pipeline analyst. When a build fails, analyze the logs and identify the root cause.
86
87Rules:
88- Categorize failure: test_failure | build_error | dependency_issue | infra_timeout | flaky_test | config_error
89- For test failures: identify the exact test, expected vs actual, and suggest fix
90- For dependency issues: identify the package and version conflict
91- For infra issues: identify timeout patterns, resource limits
92- Detect flaky tests by checking if the same test passed in recent runs
93- Output JSON: { category: string, rootCause: string, affectedFiles: [...], suggestedFix: string, confidence: 0-100 }`,skills:[{name:"log-parsing",content:`<skill name="log-parsing">
94CI log analysis patterns:
95- Error lines: look for "ERROR", "FAILED", "FATAL", exit codes != 0
96- Test failures: "FAIL", "AssertionError", "Expected X but got Y"
97- Dependency: "ERESOLVE", "peer dep", "version conflict", "404 Not Found"
98- Timeout: "ETIMEDOUT", "deadline exceeded", "killed after"
99- OOM: "JavaScript heap out of memory", "Killed", exit code 137
100- Flaky indicator: same test fails intermittently across last 5 runs
101</skill>`}],tools:[{name:"fetch_pipeline_logs",description:"Retrieves the full log output of a CI/CD pipeline run",params:'{ "runId": { "type": "string" }, "provider": { "type": "string", "enum": ["github", "gitlab", "jenkins"] } }',executeCode:"const runId = args.runId;\nconst provider = args.provider;\nconst urls = {\n  github: 'https://api.github.com/repos/{owner}/{repo}/actions/runs/' + runId + '/logs',\n  gitlab: 'https://gitlab.com/api/v4/projects/{id}/jobs/' + runId + '/trace',\n  jenkins: 'https://{host}/job/{name}/' + runId + '/consoleText'\n};\nresult = JSON.stringify({\n  status: 'external_data_required',\n  runId: runId,\n  provider: provider,\n  instruction: 'This tool requires external API access. Please provide the log content directly as a parameter, or use the appropriate CI/CD API.',\n  apiEndpoint: urls[provider] || 'unknown',\n  requiredAuth: provider === 'github' ? 'Bearer token with actions:read scope' : provider === 'gitlab' ? 'Private-Token header' : 'Basic auth or API token',\n  tip: 'If you have the log content, pass it to parse_alert_timeline or provide it directly for analysis.'\n});"},{name:"get_recent_runs",description:"Gets status of recent pipeline runs for flaky test detection",params:'{ "branch": { "type": "string" }, "limit": { "type": "number" } }',executeCode:"const branch = args.branch || 'main';\nconst limit = args.limit || 10;\nresult = JSON.stringify({\n  status: 'external_data_required',\n  branch: branch,\n  limit: limit,\n  instruction: 'Provide pipeline run data as an array of objects with fields: {id, status, branch, startedAt, finishedAt, duration, failedTests}',\n  analysisCapabilities: [\n    'Flaky test detection (tests that fail intermittently)',\n    'Failure rate calculation per test',\n    'Duration trend anal
101ysis',\n    'Success/failure ratio over time'\n  ],\n  sampleInput: [\n    { id: 'run-1', status: 'success', branch: branch, startedAt: '2024-01-01T10:00:00Z', duration: 180, failedTests: [] },\n    { id: 'run-2', status: 'failed', branch: branch, startedAt: '2024-01-02T10:00:00Z', duration: 95, failedTests: ['test_auth_login', 'test_payment_flow'] }\n  ]\n});"}],mcpIntegration:`GitHub Actions webhook triggers on workflow_run.completed (failure).
102POST logs to /api/mcp, agent analyzes and returns diagnosis.
103Result posted to Slack channel via secondary webhook.`,gradingSuite:[{caseName:"Identify dependency conflict",input:`npm ERR! ERESOLVE unable to resolve dependency tree
104npm ERR! peer react@"^17.0.0" from [email protected]`,criteria:`- output_match: category is "dependency_issue" (weight: 0.4)
105- output_match: mentions version conflict (weight: 0.3)
106- schema_validation: valid JSON (weight: 0.2)
107- llm_judge: suggested fix is actionable (weight: 0.1)`}]},{slug:"cloud-cost-optimizer",title:"Cloud Cost Optimizer",domain:"FinOps / Infrastructure",icon:r.DollarSign,tagline:"Analyze cloud billing data and identify savings opportunities with ROI estimates",description:"Analyzes AWS/GCP/Azure billing exports and identifies: oversized instances, idle resources, unused reserved capacity, storage orphans, NAT gateway optimization, spot instance candidates. Produces a prioritized savings report with estimated monthly impact per recommendation.",timeSaved:"2-3 days of monthly FinOps audit reduced to minutes",costReduction:"Typically identifies 20-40% savings ($50K-$500K/year)",riskMitigation:"Prevents budget overruns with automated threshold alerts",systemPrompt:`You are a FinOps analyst. Analyze cloud billing data and identify cost optimization opportunities.
108
109Rules:
110- Categorize savings: rightsizing | idle_resources | reserved_capacity | storage | networking | licensing
111- For each finding, provide: current cost, optimized cost, savings %, implementation effort (low/medium/high)
112- Prioritize by savings/effort ratio
113- Flag resources with < 10% average utilization
114- Identify RI/Savings Plan coverage gaps
115- Output JSON: { totalCurrentCost: number, totalOptimizedCost: number, savingsPercent: number, recommendations: [...] }
116
117Never recommend downsizing production databases without load analysis.`,skills:[{name:"aws-pricing",content:`<skill name="aws-pricing">
118Common AWS savings patterns:
119- EC2: t3.medium rarely uses burst credits -> switch to t3a.small (30% savings)
120- RDS: Multi-AZ on dev/staging -> remove (50% savings)
121- EBS: gp2 volumes -> gp3 (20% savings, better performance)
122- NAT Gateway: $0.045/GB -> VPC endpoints for S3/DynamoDB ($0)
123- Data Transfer: cross-AZ at $0.01/GB -> use AZ-aware routing
124- S3: Intelligent Tiering saves 40-60% on infrequently accessed data
125- Lambda: ARM (Graviton) = 20% cheaper, 34% better perf
126</skill>`}],tools:[{name:"parse_billing_csv",description:"Parses cloud provider billing export CSV into structured data",params:'{ "csvContent": { "type": "string" }, "provider": { "type": "string", "enum": ["aws", "gcp", "azure"] } }',executeCode:"const lines = args.csvContent.trim().split('\\n');\nconst provider = args.provider;\nif (lines.length < 2) { result = JSON.stringify({ error: 'CSV must have at least a header row and one data row' }); }\nelse {\n  const headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''));\n  const rows = [];\n  for (let i = 1; i < lines.length; i++) {\n    const vals = [];\n    let inQuote = false; let cur = '';\n    for (const ch of lines[i]) {\n      if (ch === '\"') { inQuote = !inQuote; continue; }\n      if (ch === ',' && !inQuote) { vals.push(cur.trim()); cur = ''; continue; }\n      cur += ch;\n    }\n    vals.push(cur.trim());\n    const row = {};\n    headers.forEach((h, idx) => { row[h] = vals[idx] || ''; });\n    rows.push(row);\n  }\n  const costField = headers.find(h => /cost|amount|charge|price|total/i.test(h)) || null;\n  const serviceField = headers.find(h => /service|product|resource|sku|description/i.test(h)) || null;\n  let totalCost = 0;\n  const byService = {};\n  for (const row of rows) {\n    const cost = costField ? parseFloat(row[c
126ostField]) || 0 : 0;\n    totalCost += cost;\n    if (serviceField) {\n      const svc = row[serviceField] || 'unknown';\n      byService[svc] = (byService[svc] || 0) + cost;\n    }\n  }\n  const topServices = Object.entries(byService).sort((a, b) => b[1] - a[1]).slice(0, 10).map(([name, cost]) => ({ name, cost: Math.round(cost * 100) / 100, percentage: Math.round(cost / totalCost * 10000) / 100 }));\n  result = JSON.stringify({ provider, rowCount: rows.length, headers, totalCost: Math.round(totalCost * 100) / 100, costField, serviceField, topServices, sampleRows: rows.slice(0, 3) });\n}"},{name:"get_utilization_metrics",description:"Fetches CPU/memory/network utilization for a resource",params:'{ "resourceId": { "type": "string" }, "period": { "type": "string" } }',executeCode:"const resourceId = args.resourceId;\nconst period = args.period || '1h';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  resourceId: resourceId,\n  period: period,\n  instruction: 'Provide utilization data as an object with cpu, memory, network, and disk arrays. Each array should contain {timestamp, value} objects.',\n  analysisCapabilities: [\n    'Peak/average/p95 calculation',\n    'Anomaly detection via standard deviation',\n    'Trend analysis (increasing/decreasing/stable)',\n    'Right-sizing recommendations'\n  ],\n  sampleInput: {\n    cpu: [{ timestamp: '2024-01-01T10:00:00Z', value: 45.2 }, { timestamp: '2024-01-01T10:05:00Z', value: 72.1 }],\n    memory: [{ timestamp: '2024-01-01T10:00:00Z', value: 68.0 }],\n    network: [{ timestamp: '2024-01-01T10:00:00Z', value: 125.5 }]\n  }\n});"}],mcpIntegration:`Schedule monthly: export billing CSV, POST to /api/mcp.
127Agent returns prioritized savings report.
128Dashboard displays recommendations with one-click implementation tickets.`,gradingSuite:[{caseName:"Detect idle EC2 instance",input:"Instance i-abc123: type m5.xlarge, avg CPU 3%, avg network 100KB/s, running 730 hours",criteria:`- output_match: recommends downsizing or termination (weight: 0.4)
129- output_match: provides savings estimate (weight: 0.3)
130- schema_validation: valid JSON with recommendations array (weight: 0.2)
131- llm_judge: recommendation is safe and actionable (weight: 0.1)`}]},{slug:"incident-postmortem-generator",title:"Incident Post-Mortem Generator",domain:"SRE / Operations",icon:c,tagline:"Auto-generate structured post-mortems from incident logs with timeline and action items",description:"Ingests incident data (PagerDuty alerts, CloudWatch logs, Slack threads, status page updates) and generates a complete blameless post-mortem: timeline reconstruction, root cause analysis, impact assessment (duration, users affected, revenue impact), and prioritized action items with suggested owners.",timeSaved:"2-4 hours per post-mortem reduced to 5 minutes",costReduction:"$25K/year for a team handling 2+ incidents/month",riskMitigation:"Ensures 100% of incidents have documented post-mortems",systemPrompt:`You are an SRE post-mortem specialist. Generate blameless post-mortems from incident data.
132
133Rules:
134- Reconstruct timeline in UTC with minute-level granularity
135- Identify root cause and contributing factors (never blame individuals)
136- Assess impact: duration, affected users (% and count), revenue impact estimate
137- Classify severity: SEV1 (critical) / SEV2 (major) / SEV3 (minor)
138- Generate 3-5 action items with: description, priority (P0-P3), suggested owner (role, not person), deadline
139- Include "what went well" section
140- Output markdown with standardized sections`,skills:[{name:"postmortem-template",content:`<skill name="postmortem-template">
141## Incident Post-Mortem: [Title]
142**Date:** [date] | **Severity:** [SEV1/2/3] | **Duration:** [Xh Xm]
143
144### Summary
145[2-3 sentences]
146
147### Impact
148- Users affected: [X% / count]
149- Revenue impact: [estimate]
150- SLA impact: [yes/no, details]
151
152### Timeline (UTC)
153| Time | Event |
154|------|-------|
155
156### Root Cause
157[Detailed technical explanation]
158
159### Contributing Factors
160[List]
161
162### What Went Well
163[List]
164
165### Action Items
166| Priority | Action | Owner (role) | Deadline |
167|----------|--------|-------------|----------|
168
169### Lessons Learned
170[Key takeaways]
171</skill>`}],tools:[{name:"parse_alert_timeline",description:"Parses PagerDuty/Opsgenie alerts into chronological events",params:'{ "alerts": { "type": "array", "items": { "type": "object" } } }',executeCode:"const alerts = args.alerts || [];\nconst events = [];\nfor (const alert of alerts) {\n  const id = alert.id || alert.alertId || alert.incident_id || ('alert-' + events.length);\n  const title = alert.title || alert.summary || alert.message || alert.description || 'Untitled alert';\n  const severity = (alert.severity || alert.priority || alert.urgency || 'unknown').toString().toLowerCase();\n  const status = (alert.status || alert.state || 'unknown').toString().toLowerCase();\n  const createdAt = alert.created_at || alert.createdAt || alert.timestamp || alert.started_at || null;\n  const resolvedAt = alert.resolved_at || alert.resolvedAt || alert.ended_at || null;\n  const service = alert.service || alert.source || alert.integration || 'unknown';\n  let durationMin = null;\n  if (createdAt && resolvedAt) {\n    durationMin = Math.round((new Date(resolvedAt).getTime() - new Date(createdAt).getTime()) / 60000);\n  }\n  events.push({ id, title, severity, status, service, createdAt, resolvedAt, durationMinutes: durationMin, raw: alert });\n}\nevents.sort((a, b) => {\n  if (!a.createdAt) return 1; if (!b.createdAt) return -1;\n  return new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();\n});
171\nconst bySeverity = {}; const byService = {}; const byStatus = {};\nfor (const e of events) {\n  bySeverity[e.severity] = (bySeverity[e.severity] || 0) + 1;\n  byService[e.service] = (byService[e.service] || 0) + 1;\n  byStatus[e.status] = (byStatus[e.status] || 0) + 1;\n}\nconst resolved = events.filter(e => e.durationMinutes !== null);\nconst avgMTTR = resolved.length > 0 ? Math.round(resolved.reduce((s, e) => s + e.durationMinutes, 0) / resolved.length) : null;\nresult = JSON.stringify({ totalAlerts: events.length, bySeverity, byService, byStatus, avgMTTRMinutes: avgMTTR, timeline: events });"},{name:"estimate_revenue_impact",description:"Estimates revenue lost based on downtime duration and traffic patterns",params:'{ "durationMinutes": { "type": "number" }, "avgRevenuePerMinute": { "type": "number" } }',executeCode:"const dur = args.durationMinutes || 0;\nconst rpm = args.avgRevenuePerMinute || 0;\nconst directLoss = Math.round(dur * rpm * 100) / 100;\nconst recoveryFactor = dur <= 5 ? 1.0 : dur <= 30 ? 1.15 : dur <= 60 ? 1.3 : dur <= 240 ? 1.5 : 2.0;\nconst totalEstimate = Math.round(directLoss * recoveryFactor * 100) / 100;\nconst hourlyRate = Math.round(rpm * 60 * 100) / 100;\nconst dailyRate = Math.round(rpm * 1440 * 100) / 100;
171\nconst category = dur <= 5 ? 'minor' : dur <= 30 ? 'moderate' : dur <= 120 ? 'significant' : dur <= 480 ? 'severe' : 'critical';\nresult = JSON.stringify({\n  durationMinutes: dur,\n  avgRevenuePerMinute: rpm,\n  directRevenueLoss: directLoss,\n  recoveryTimeFactor: recoveryFactor,\n  recoveryFactorExplanation: 'Longer outages have lingering effects: customer churn, abandoned carts, trust erosion',\n  estimatedTotalImpact: totalEstimate,\n  impactCategory: category,\n  context: { hourlyRevenue: hourlyRate, dailyRevenue: dailyRate },\n  recommendations: dur > 60 ? [\n    'Consider customer communication / status page update',\n    'Plan post-incident credits or goodwill gestures',\n    'Schedule post-mortem within 48 hours'\n  ] : ['Monitor for cascading effects after recovery']\n});"}],mcpIntegration:`Triggered after incident resolution.
172Collects data from PagerDuty API + CloudWatch + Slack.
173POST to /api/mcp, returns formatted post-mortem.
174Auto-creates Confluence/Notion page and Jira tickets for action items.`,gradingSuite:[{caseName:"Generate post-mortem from outage data",input:"Alert: Database connection pool exhausted at 14:32 UTC. Service restored at 15:47 UTC. 12,000 users affected.",criteria:`- output_match: contains timeline section (weight: 0.3)
175- output_match: contains action items (weight: 0.3)
176- llm_judge: root cause analysis is plausible (weight: 0.2)
177- safety_check: no individual blame (weight: 0.2)`}]},{slug:"slack-standup-synthesizer",title:"Slack Standup Synthesizer",domain:"Team Management",icon:l.MessageSquare,tagline:"Aggregate daily standups from Slack into team dashboards with blocker detection",description:"Reads standup messages from a Slack channel (or threaded standups), extracts each team member's yesterday/today/blockers, detects cross-team dependencies, identifies stale items (same 'today' for 3+ days), and generates a team summary for managers with risk flags.",timeSaved:"20 min/day for engineering managers (reading + summarizing)",costReduction:"$15K/year per manager in recovered productive time",riskMitigation:"Blockers detected 2x faster, preventing sprint slippage",systemPrompt:`You are a team standup analyst. Parse standup messages and generate actionable team summaries.
178
179Rules:
180- Extract per-person: yesterday (done), today (planned), blockers
181- Detect stale items: if "today" matches previous 2+ standups, flag as "stuck"
182- Identify cross-team dependencies (mentions of other teams or external services)
183- Highlight blockers with severity: resolved | active | escalation_needed
184- Generate team health score: 0-100 based on progress rate and blocker count
185- Output JSON: { teamHealth: number, members: [...], blockers: [...], staleItems: [...], summary: string }`,skills:[{name:"standup-parsing",content:`<skill name="standup-parsing">
186Standup message patterns to detect:
187- "Yesterday/Done/Completed:" followed by items
188- "Today/Planned/Working on:" followed by items
189- "Blockers/Blocked/Issues:" followed by items
190- Emoji patterns: checkmark = done, construction = in progress, stop = blocked
191- Thread replies = additional context for the parent standup
192- No standup posted = flag as "missing standup"
193</skill>`}],tools:[],mcpIntegration:`Connect this agent to Slack via the Connectors tab.
194Built-in tools (slack_read_messages, slack_list_channels) are automatically available when connected.
195Mention @agent in a channel with a date to get standup analysis.
196No custom tool configuration needed — just connect and go.`,gradingSuite:[{caseName:"Parse standup with blocker",input:"@alice: Yesterday: finished auth module. Today: start payment integration. Blocked: waiting for Stripe API keys from DevOps.",criteria:`- output_match: extracts blocker about Stripe keys (weight: 0.4)
197- output_match: identifies cross-team dependency (DevOps) (weight: 0.3)
198- schema_validation: valid JSON with members array (weight: 0.2)
199- llm_judge: summary is accurate (weight: 0.1)`}]},{slug:"contract-clause-analyzer",title:"Contract Clause Analyzer",domain:"Legal / Procurement",icon:d.FileText,tagline:"Detect risky clauses, GDPR violations, and missing protections in contracts",description:"Analyzes SaaS contracts, NDAs, and vendor agreements to detect: abusive clauses, GDPR non-compliance, missing SLA commitments, auto-renewal traps, liability gaps, IP ownership ambiguity, and missing exit clauses. Outputs a risk matrix with severity ratings per clause.",timeSaved:"1-2 hours per contract review for legal team",costReduction:"$40K/year for companies reviewing 50+ contracts/year",riskMitigation:"Prevents signing contracts with hidden liability exposure",systemPrompt:`You are a contract analyst specializing in technology and SaaS agreements. Analyze contracts for risks.
200
201Rules:
202- Score each clause: safe (green) | review_needed (yellow) | risky (red)
203- Check GDPR compliance: data processing addendum, data location, breach notification
204- Verify SLA terms: uptime %, credit mechanism, exclusions
205- Flag auto-renewal clauses without opt-out window
206- Identify unlimited liability clauses or missing liability caps
207- Check IP ownership: ensure client retains IP for custom work
208- Check termination: exit clause, data portability, transition period
209- Output JSON: { overallRisk: "low|medium|high", clauses: [...], missingClauses: [...], recommendations: [...] }`,skills:[{name:"gdpr-checklist",content:`<skill name="gdpr-checklist">
210GDPR contract requirements:
2111. Data Processing Agreement (DPA) present
2122. Data storage location specified (EU adequacy decision)
2133. Sub-processor list and notification requirement
2144. Data breach notification within 72 hours
2155. Data subject rights handling process
2166. Data deletion/return on termination
2177. Security measures documented (Art. 32)
2188. Data Protection Impact Assessment reference
219</skill>`}],tools:[{name:"extract_clauses",description:"Parses a contract document into 
219individual numbered clauses",params:'{ "document": { "type": "string", "description": "Full contract text" } }',executeCode:"const doc = args.document || '';\nconst clauses = [];\nconst patterns = [\n  /(?:^|\\n)\\s*(\\d+\\.\\d*\\.?\\d*\\.?)\\s+(.+?)(?=\\n\\s*\\d+\\.\\d*|\\n\\n|$)/gs,\n  /(?:^|\\n)\\s*(Section\\s+\\d+[:\\.]?)\\s*(.+?)(?=\\nSection\\s+\\d+|\\n\\n|$)/gis,\n  /(?:^|\\n)\\s*(Article\\s+\\d+[:\\.]?)\\s*(.+?)(?=\\nArticle\\s+\\d+|\\n\\n|$)/gis,\n  /(?:^|\\n)\\s*([IVXLC]+\\.\\s*)(.+?)(?=\\n[IVXLC]+\\.|\\n\\n|$)/gs\n];\nlet matched = false;\nfor (const pattern of patterns) {\n  let m;\n  while ((m = pattern.exec(doc)) !== null) {\n    matched = true;\n    const num = m[1].trim();\n    const text = m[2].trim();\n    const category = /indemnif|liabil|limitation/i.test(text) ? 'liability' : /terminat|cancel/i.test(text) ? 'termination' : /confidential|nda|non-disclos/i.test(text) ? 'confidentiality' : /payment|fee|price|invoice/i.test(text) ? 'payment' : /govern.*law|jurisdict|arbitrat/i.test(text) ? 'governing_law' : /warrant|represent/i.test(text) ? 'warranty' : /intellect.*prop|ip |copyright|patent/i.test(text) ? 'ip' : /force.*majeure/i.test(text) ? 'force_majeure' : 'general';\n    clauses.push({ number: num, text, category, charCount: text.length });\n  }\n  if (matched) break;\n}\nif (!matched) {\n  const paragraphs = doc.split(/\\n\\n+/).filter(p => p.trim().length > 20);\n  paragraphs.forEach((p, i) => clauses.push({ number: String(i + 1), text: p.trim(), category: 'general', charCount: p.trim().length }));\n}\nconst categories = {};\nclauses.forEach(c => { categories[c.category] = (categories[c.category] || 0) + 1; });\nresult = JSON.stringify({ totalClauses: clauses.length, categories, clauses });"},{name:"compare_template",description:"Compares clauses against company standard contract template",params:'{ "clauses": { "type": "array" }, "templateId": { "type": "string" } }',executeCode:"const clauses = args.clauses || [];\nconst templateId = args.templateId || 'standard';\nconst requiredCategories = ['liability', 'termination', 'confidentiality', 'payment', 'governing_law', 'warranty', 'ip'];\nconst findings = [];\nconst presentCategories = new Set(clauses.map(c => c.category).filter(Boolean));\nfor (const cat of requiredCategories) {\n  if (!presentCategories.has(cat)) {\n    findings.push({ type: 'missing_clause', category: cat, severity: 'high', message: 'Required ' + cat + ' clause is missing from the contract' });\n  }\n}\nconst riskKeywords = {\n  'unlimited liability': { severity: 'critical', message: 'Unlimited liability exposure detected' },\n  'auto-renew': { severity: 'medium', message: 'Auto-renewal clause found - verify terms' },\n  'non-compete': { severity: 'high', message: 'Non-compete clause found - verify scope and duration' },\n  'exclusive': { severity: 'medium', message: 'Exclusivity clause found' },\n  'perpetual': { severity: 'high', message: 'Perpetual license/term found' },\n  'waive': { severity: 'high', message: 'Waiver clause detected - review carefully' },\n  'sole discretion': { severity: 'medium', message: 'Sole discretion language found - one-sided term' }\n};\nfor (const clause of clauses) {\n  const lower = (clause.text || '').toLowerCase();\n  for (const [keyword, info] of Object.entries(riskKeywords)) {\n    if (lower.includes(keyword)) {\n      findings.push({ type: 'risk_flag', clause: clause.number, category: clause.category, keyword, severity: info.severity, message: info.message });\n    }\n  }\n}\nconst bySeverity = { critical: 0, high: 0, medium: 0, low: 0 };
219\nfindings.forEach(f => { bySeverity[f.severity] = (bySeverity[f.severity] || 0) + 1; });\nconst riskScore = bySeverity.critical * 25 + bySeverity.high * 10 + bySeverity.medium * 3 + bySeverity.low * 1;\nresult = JSON.stringify({ templateId, totalClauses: clauses.length, findings, findingCount: findings.length, bySeverity, riskScore: Math.min(riskScore, 100), riskLevel: riskScore > 50 ? 'high' : riskScore > 20 ? 'medium' : 'low' });"}],mcpIntegration:`Legal team uploads contract to internal portal.
220Portal extracts text, POST to /api/mcp.
221Agent returns risk analysis.
222High-risk clauses auto-create legal review tickets.`,gradingSuite:[{caseName:"Detect missing GDPR DPA",input:"SaaS Agreement: Provider stores customer data on US servers. No data processing addendum included.",criteria:`- output_match: flags missing DPA (weight: 0.4)
223- output_match: flags non-EU data storage (weight: 0.3)
224- output_match: overallRisk is "high" (weight: 0.2)
225- schema_validation: valid JSON (weight: 0.1)`}]},{slug:"sales-lead-qualifier",title:"Sales Lead Qualifier",domain:"Sales / Prospecting",icon:p.Target,tagline:"Score and qualify inbound leads with enrichment data and personalized outreach drafts",description:"Processes inbound leads (form submissions, demo requests, trial sign-ups), enriches them with company data (size, industry, tech stack, funding), scores them using BANT/MEDDIC frameworks, and generates a personalized outreach email draft for the assigned SDR.",timeSaved:"10-15 min per lead qualification, 80% of SDR research time",costReduction:"$60K/year per SDR in recovered selling time",riskMitigation:"Increases conversion rate 25% by prioritizing high-intent leads",systemPrompt:`You are a sales intelligence analyst. Qualify inbound leads and prepare SDR outreach.
226
227Rules:
228- Score leads 0-100 using BANT: Budget, Authority, Need, Timeline
229- Enrich with available data: company size, industry, tech stack, recent funding
230- Classify: hot (>80) | warm (50-80) | cold (<50)
231- For hot/warm leads: generate personalized outreach email (3-4 sentences, value-focused)
232- Identify buying signals: pricing page visits, competitor mentions, urgent language
233- Output JSON: { score: number, classification: string, bantBreakdown: {...}, enrichment: {...}, outreachDraft: string }
234
235Never fabricate company data. Mark unknown fields as "unknown".`,skills:[{name:"outreach-templates",content:`<skill name="outreach-templates">
236Outreach email structure:
2371. Personalized hook (reference their company/industry challenge)
2382. Value proposition (one sentence, specific to their pain point)
2393. Social proof (brief case study or metric from similar company)
2404. CTA (specific: "15-min call Thursday?" not "let me know")
241
242Tone: professional but conversational. No buzzwords. No "I hope this email finds you well."
243Max length: 100 words.
244</skill>`}],tools:[{name:"enrich_company",description:"Fetches company data from Clearbit/LinkedIn/Crunchbase APIs",params:'{ "companyDomain": { "type": "string" }, "companyName": { "type": "string" } }',executeCode:"const domain = args.companyDomain || '';\nconst name = args.companyName || '';\nconst tld = domain.split('.').pop() || '';\nconst domainBase = domain.replace(/^www\\./, '').split('.')[0] || '';\nconst isEnterprise = ['com', 'io', 'co', 'ai'].includes(tld);\nresult = JSON.stringify({\n  status: 'external_data_required',\n  companyDomain: domain,\n  companyName: name,\n  instruction: 'This tool requires external API access (Clearbit, LinkedIn, Crunchbase). Provide company data directly or use web search.',\n  enrichmentSources: [\n    { source: 'clearbit', endpoint: 'https://company.clearbit.com/v2/companies/find?domain=' + domain },\n    { source: 'linkedin', searchUrl: 'https://www.linkedin.com/company/' + domainBase },\n    { source: 'crunchbase', searchUrl: 'https://www.crunchbase.com/organization/' + domainBase }\n  ],\n  dataPointsToCollect: ['industry', 'employeeCount', 'annualRevenue', 'fundingTotal', 'foundedYear', 'headquarters', 'techStack', 'socialProfiles', 'recentNews'],\n  inferredData: { domain, possibleLinkedIn: 'linkedin.com/company/' + domainBase, likelyEnterprise: isEnterprise && domainBase.length > 3 }\n});"},{name:"check_crm_history",description:"Checks CRM for existing relationship with this company",params:'{ "companyDomain": { "type": "string" } }',executeCode:"const domain = args.companyDomain || '';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  companyDomain: domain,\n  instruction: 'This tool requires CRM access (Salesforce, HubSpot, etc.). Provide CRM records directly if available.',\n  crmQueries: {\n    salesforce: 'SELECT Id, Name, OwnerId, LastActivityDate, Amount FROM Opportunity WHERE Account.Website LIKE \\'%' + domain + '%\\'',\n    hubspot: 'GET /crm/v3/objects/companies/search with domain filter: ' + domain\n  },\n  dataPointsNeeded: ['existingContacts', 'dealHistory', 'lastInteractionDate', 'accountOwner', 'totalRevenue', 'openOpportunities', 'supportTickets']\n});"}],mcpIntegration:`Website form submission triggers webhook.
245Lead data POST to /api/mcp.
246Agent returns qualification + outreach draft.
247Hot leads auto-create CRM opportunity and Slack alert to SDR.`,gradingSuite:[{caseName:"Qualify enterprise lead",input:"Company: Acme Corp, 500 employees, Series C, requested pricing demo, CTO signed up",criteria:`- output_match: score > 70 (weight: 0.3)
248- output_match: classification is "hot" or "warm" (weight: 0.3)
249- output_match: outreach draft is personalized to Acme (weight: 0.2)
250- schema_validation: valid JSON with bantBreakdown (weight: 0.2)`}]},{slug:"marketing-campaign-analyzer",title:"Marketing Campaign Analyzer",domain:"Marketing / Growth",icon:u.TrendingUp,tagline:"Multi-channel campaign performance analysis with attribution and optimization suggestions",description:"Aggregates campaign data from Google Ads, Meta Ads, LinkedIn Ads, email platforms, and organic channels. Performs cross-channel attribution, identifies top/underperforming creatives, calculates true CAC per channel, and generates optimization recommendations with budget reallocation suggestions.",timeSaved:"4-6 hours of weekly marketing reporting",costReduction:"15-25% ROAS improvement from data-driven budget shifts",riskMitigation:"Prevents budget waste on underperforming channels",systemPrompt:`You are a marketing analytics expert. Analyze multi-channel campaign data and optimize spend.
251
252Rules:
253- Calculate per-channel: impressions, clicks, CTR, conversions, CPA, ROAS
254- Apply multi-touch attribution (linear model) across channels
255- Identify statistical outliers: creatives with >2x or <0.5x average performance
256- Suggest budget reallocation: shift from low-ROAS to high-ROAS channels
257- Flag audience fatigue: declining CTR over 2+ weeks on same creative
258- Output JSON: { channels: [...], topCreatives: [...], recommendations: [...], proposedBudget: {...} }
259
260Always show confidence intervals for small sample sizes (<1000 impressions).`,skills:[{name:"attribution-model",content:`<skill name="attribution-model">
261Multi-touch attribution (linear):
262- First touch: 25% credit
263- Middle touches: 50% credit (split equally)
264- Last touch: 25% credit
265
266Channel benchmarks (B2B SaaS):
267- Google Search: CPA $50-150, CTR 3-5%
268- LinkedIn Ads: CPA $80-200, CTR 0.4-0.8%
269- Meta Ads: CPA $30-80, CTR 0.8-1.5%
270- Email: CPA $10-30, CTR 2-5%
271- Organic: CPA $0, but track content investment
272</skill>`}],tools:[{name:"fetch_campaign_data",description:"Retrieves campaign metrics from ad platform APIs",params:'{ "platform": { "type": "string", "enum": ["google", "meta", "linkedin", "email"] }, "dateRange": { "type": "string" } }',executeCode:"const platform = args.platform;\nconst dateRange = args.dateRange || 'last_30_days';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  platform: platform,\n  dateRange: dateRange,\n  instruction: 'Provide campaign metrics data directly. Expected format: array of campaign objects.',\n  expectedFields: platform === 'email' \n    ? ['campaignName', 'sent', 'delivered', 'opened', 'clicked', 'bounced', 'unsubscribed', 'revenue']\n    : ['campaignName', 'impressions', 'clicks', 'spend', 'conversions', 'revenue', 'ctr', 'cpc', 'roas'],\n  apiEndpoints: {\n    google: 'Google Ads API - /customers/{id}/googleAds:searchStream',\n    meta: 'Meta Marketing API - /act_{id}/insights',\n    linkedin: 'LinkedIn Marketing API - /adAnalytics',\n    email: 'Mailchimp/SendGrid API - /campaigns/{id}/stats'\n  },\n  analysisCapabilities: ['ROAS calculation', 'CPA optimization', 'CTR benchmarking', 'Budget allocation recommendations', 'Funnel drop-off analysis']\n});"},{name:"calculate_attribution",description:"Computes multi-touch attribution across conversion paths",params:'{ "conversionPaths": { "type": "array" } }',executeCode:"const paths = args.conversionPaths || [];\nif (paths.length === 0) { result = JSON.stringify({ error: 'No conversion paths provided. Provide array of {touchpoints: [{channel, timestamp}], conversionValue: number}' }); }\nelse {\n  const channelCredits = { firstTouch: {}, lastTouch: {}, linear: {}, timeDecay: {} };\n  let totalValue = 0;\n  for (const path of paths) {\n    const tps = path.touchpoints || path.touches || [];\n    const value = path.conversionValue || path.value || 1;\n    totalValue += value;\n    if (tps.length === 0) continue;\n    const first = tps[0].channel || tps[0].source || 'unknown';\n    const last = tps[tps.length - 1].channel || tps[tps.length - 1].source || 'unknown';\n    channelCredits.firstTouch[first] = (channelCredits.firstTouch[first] || 0) + value;\n    channelCredits.lastTouch[last] = (channelCredits.lastTouch[last] || 0) + value;\n    const linearShare = value / tps.length;\n    for (const tp of tps) {\n      const ch = tp.channel || tp.source || 'unknown';\n      channelCredits.linear[ch] = (channelCredits.linear[ch] || 0) + linearShare;\n    }\n    const totalWeight = tps.reduce((s, _, i) => s + Math.pow(0.5, tps.length - 1 - i), 0);\n    tps.forEach((tp, i) => {\n      const ch = tp.channel || tp.source || 'unknown';\n      const weight = Math.pow(0.5, tps.length - 1 - i) / totalWeight;\n      channelCredits.timeDecay[ch] = (channelCredits.timeDecay[ch] || 0) + value * weight;\n    });\n  }\n  const round = obj => Object.fromEntries(Object.entries(obj).map(([k, v]) => [k, Math.round(v * 100) / 100]));\n  result = JSON.stringify({ totalConversions: paths.length, totalValue, models: { firstTouch: round(channelCredits.firstTouch), lastTouch: round(channelCredits.lastTouch), linear: round(channelCredits.linear), timeDecay: round(channelCredits.timeDecay) } });\n}"}],mcpIntegration:`Weekly scheduled job: export all platform data.
273POST aggregated metrics to /api/mcp.
274Agent returns analysis and recommendations.
275Results populate marketing dashboard and trigger Slack digest.`,gradingSuite:[{caseName:"Identify underperforming channel",input:"Google Ads: 10K clicks, 50 conversions, $15K spend. LinkedIn: 2K clicks, 40 conversions, $8K spend.",criteria:`- output_match: identifies Google Ads as lower ROAS (weight: 0.4)
276- output_match: recommends budget shift to LinkedIn (weight: 0.3)
277- schema_validation: valid JSON with recommendations (weight: 0.2)
278- llm_judge: analysis is mathematically sound (weight: 0.1)`}]},{slug:"ml-model-drift-monitor",title:"ML Model Drift Monitor",domain:"MLOps / Data Science",icon:m.Brain,tagline:"Detect data drift, concept drift, and model performance degradation in production",description:"Monitors ML model inference in production. Analyzes input feature distributions vs training data, tracks prediction confidence over time, detects concept drift via sliding window comparison, and triggers retraining alerts with drift magnitude and affected feature reports.",timeSaved:"Continuous monitoring vs 2-hour weekly manual checks",costReduction:"Prevents revenue loss from stale models ($100K+ impact)",riskMitigation:"Catches model degradation before it impacts business metrics",systemPrompt:`You are an MLOps monitoring specialist. Analyze model inference data for drift and degradation.
279
280Rules:
281- Compare feature distributions: KS test (p < 0.05 = drift), PSI (> 0.2 = significant)
282- Track prediction confidence: alert if mean drops >10% from baseline
283- Monitor label distribution shift for classification models
284- Check feature importance stability: top-5 features shouldn't change >20%
285- Classify drift: none | minor | significant | critical
286- Output JSON: { overallStatus: string, featureDrifts: [...], performanceMetrics: {...}, retrainRecommended: boolean, urgency: string }
287
288Never recommend retraining without specifying which features drifted and by how much.`,skills:[{name:"drift-thresholds",content:`<skill name="drift-thresholds">
289Drift detection thresholds:
290- PSI (Population Stability Index):
291  < 0.1: no drift
292  0.1-0.2: minor drift (monitor)
293  0.2-0.5: significant drift (investigate)
294  > 0.5: critical drift (retrain)
295- KS Test p-value < 0.05: statistically significant shift
296- Prediction confidence drop > 10%: model uncertainty increasing
297- Label distribution shift > 15%: concept drift likely
298- Feature importance rank change > 3 positions: investigate
299</skill>`}],tools:[{name:"compute_psi",description:"Calculates Population Stability Index between two distributions",params:'{ "baseline": { "type": "array" }, "current": { "type": "array" }, "bins": { "type": "number" } }',executeCode:"const baseline = args.baseline || [];\nconst current = args.current || [];\nconst numBins = args.bins || 10;\nif (baseline.length === 0 || current.length === 0) { result = JSON.stringify({ error: 'Both baseline and current distributions must be non-empty arrays of numbers' }); }\nelse {\n  const allVals = [...baseline, ...current].sort((a, b) => a - b);\n  const minVal = allVals[0]; const maxVal = allVals[allVals.length - 1];\n  const binWidth = (maxVal - minVal) / numBins;\n  const getBinCounts = (data) => {\n    const counts = new Array(numBins).fill(0);\n    for (const v of data) {\n      let bin = Math.floor((v - minVal) / binWidth);\n      if (bin >= numBins) bin = numBins - 1;\n      if (bin < 0) bin = 0;\n      counts[bin]++;\n    }\n    return counts.map(c => c / data.length);\n  };\n  const basePcts = getBinCounts(baseline);\n  const currPcts = getBinCounts(current);\n  let psi = 0;\n  const binDetails = [];\n  for (let i = 0; i < numBins; i++) {\n    const bp = Math.max(basePcts[i], 0.0001);\n    const cp = Math.max(currPcts[i], 0.0001);\n    const contribution = (cp - bp) * Math.log(cp / bp);\n    psi += contribution;\n    binDetails.push({ bin: i, range: [Math.round((minVal + i * binWidth) * 1000) / 1000, Math.round((minVal + (i+1) * binWidth) * 1000) / 1000], baselinePct: Math.round(bp * 10000) / 100, currentPct: Math.round(cp * 10000) / 100, contribution: Math.round(contribution * 10000) / 10000 });\n  }\n  psi = Math.round(psi * 10000) / 10000;\n  const interpretation = psi < 0.1 ? 'No significant shift' : psi < 0.2 ? 'Moderate shift - monitor closely' : 'Significant shift - investigate and consider retraining';\n  result = JSON.stringify({ psi, interpretation, baselineSize: baseline.length, currentSize: current.length, numBins, binDetails });\n}"},{name:"get_inference_logs",description:"Fetches recent model inference data with features and predictions",params:'{ "modelId": { "type": "string" }, "window": { "type": "string" } }',executeCode:"const modelId = args.modelId;\nconst window = args.window || '1h';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  modelId: modelId,\n  window: window,\n  instruction: 'Provide inference log data as an array of {timestamp, features: {}, prediction, actual?, latencyMs, modelVersion} objects.',\n  analysisCapabilities: [\n    'Feature drift detection (PSI per feature)',\n    'Prediction distribution shift',\n    'Latency percentiles (p50, p95, p99)',\n    'Error rate tracking',\n    'Data quality checks (nulls, outliers)'\n  ],\n  sampleInput: [{ timestamp: '2024-01-01T10:00:00Z', features: { age: 35, income: 75000 }, prediction: 0.82, actual: 1, latencyMs: 12, modelVersion: 'v2.1' }]\n});"}],mcpIntegration:`Hourly CRON job: sample recent inference data.
300POST feature distributions + predictions to /api/mcp.
301Agent returns drift analysis.
302Critical drift triggers PagerDuty alert + auto-starts retraining pipeline.`,gradingSuite:[{caseName:"Detect significant feature drift",input:'Feature "user_age": baseline mean=35, current mean=48, PSI=0.35. Feature "income": baseline mean=55K, current mean=54K, PSI=0.02.',criteria:`- output_match: flags user_age as significant drift (weight: 0.4)
303- output_match: income marked as no drift (weight: 0.2)
304- output_match: retrainRecommended is true (weight: 0.2)
305- schema_validation: valid JSON (weight: 0.2)`}]},{slug:"security-vulnerability-scanner",title:"Security Vulnerability Scanner",domain:"Security / Compliance",icon:g.ShieldCheck,tagline:"Continuous codebase security analysis with CVE detection and remediation guidance",description:"Scans codebase for security vulnerabilities: hardcoded secrets, dependency CVEs, insecure configurations, OWASP Top 10 patterns, exposed API keys, and misconfigured CORS/CSP headers. Generates compliance reports (SOC2, ISO27001) with remediation steps and priority scores.",timeSaved:"8-16 hours of manual security audit per sprint",costReduction:"$80K/year vs external penetration testing frequency",riskMitigation:"Reduces security incident probability by 75%",systemPrompt:`You are a security analyst. Scan code and configuration for vulnerabilities.
306
307Rules:
308- Detect hardcoded secrets: API keys, passwords, tokens, private keys (regex + entropy analysis)
309- Check dependencies against NVD/GitHub Advisory Database
310- Identify OWASP Top 10 vulnerabilities in code patterns
311- Analyze infrastructure configs: Dockerfiles, K8s manifests, Terraform
312- Score each finding: CVSS 0-10
313- Output JSON: { criticalCount: number, highCount: number, findings: [...], complianceGaps: [...], remediationPlan: [...] }
314
315Always provide specific remediation steps, not just descriptions.`,skills:[{name:"secret-patterns",content:`<skill name="secret-patterns">
316Secret detection patterns:
317- AWS: AKIA[0-9A-Z]{16}
318- GitHub: ghp_[a-zA-Z0-9]{36}
319- Stripe: sk_live_[a-zA-Z0-9]{24}
320- Generic API key: [a-zA-Z0-9]{32,} with high entropy (>4.5 Shannon)
321- Private keys: -----BEGIN (RSA|EC|DSA) PRIVATE KEY-----
322- JWT secrets: variable names containing "secret", "jwt_key", "signing_key"
323- Database URLs: postgres://user:password@host (password in cleartext)
324
325False positive reduction: ignore test files, example configs, documentation.
326</skill>`}],tools:[{name:"scan_dependencies",description:"Checks project dependencies against CVE databases",params:'{ "lockfile": { "type": "string", "description": "Content of package-lock.json, yarn.lock, or requirements.txt" } }',executeCode:"const lockfile = args.lockfile || '';\nconst deps = [];\nconst npmMatch = lockfile.match(/\"([^\"]+)\":\\s*\\{[^}]*\"version\":\\s*\"([^\"]+)\"/g);\nif (npmMatch) {\n  for (const m of npmMatch) {\n    const parts = m.match(/\"([^\"]+)\":\\s*\\{[^}]*\"version\":\\s*\"([^\"]+)\"/);\n    if (parts) deps.push({ name: parts[1], version: parts[2] });\n  }\n}\nconst pipMatch = lockfile.match(/([a-zA-Z0-9_-]+)==([\\d.]+)/g);\nif (pipMatch) {\n  for (const m of pipMatch) {\n    const [name, version] = m.split('==');\n    deps.push({ name, version });\n  }\n}\nconst yarnMatch = lockfile.match(/\"?([^\"@\\n]+)@[^\"\\n]+\"?:\\s*\\n\\s*version\\s+\"([^\"]+)\"/g);\nif (yarnMatch) {\n  for (const m of yarnMatch) {\n    const parts = m.match(/\"?([^\"@\\n]+)@[^\"\\n]+\"?:\\s*\\n\\s*version\\s+\"([^\"]+)\"/);\n    if (parts) deps.push({ name: parts[1].trim(), version: parts[2] });\n  }\n}\nif (deps.length === 0) {\n  const lines = lockfile.split('\\n').filter(l => l.trim());\n  for (const line of lines) {\n    const simple = line.match(/^\\s*[\"']?([^\"'\\s:@]+)[\"']?\\s*[:@=]\\s*[\"']?([\\d][\\d.]*)/); \n    if (simple) deps.push({ name: simple[1], version: simple[2] });\n  }\n}\nconst knownVulns = { 'lodash': '4.17.21', 'minimist': '1.2.6', 'node-fetch': '2.6.7', 'axios': '1.6.0', 'express': '4.18.2', 'jsonwebtoken': '9.0.0', 'tar': '6.1.9', 'json5': '2.2.2', 'semver': '7.5.2', 'qs': '6.10.3', 'shell-quote': '1.7.3', 'trim-newlines': '4.0.2', 'glob-parent': '5.1.2', 'trim': '0.0.3', 'underscore': '1.13.6' };\nconst findings = [];\nfor (const dep of deps) {\n  const safe = knownVulns[dep.name];\n  if (safe) {\n    const cv = dep.version.split('.').map(Number);\n    const sv = safe.split('.').map(Number);\n    let vuln = false;\n    for (let i = 0; i < 3; i++) { if ((cv[i]||0) < (sv[i]||0)) { vuln = true; break; } if ((cv[i]||0) > (sv[i]||0)) break; }\n    if (vuln) findings.push({ package: dep.name, installedVersion: dep.version, fixedVersion: safe, severity: 'high' });\n  }\n}
326\nresult = JSON.stringify({ totalDependencies: deps.length, vulnerabilities: findings.length, findings, dependencies: deps.slice(0, 50) });"},{name:"analyze_entropy",description:"Calculates Shannon entropy of strings to detect potential secrets",params:'{ "strings": { "type": "array", "items": { "type": "string" } } }',executeCode:"const strings = args.strings || [];\nconst results = [];\nfor (const str of strings) {\n  const freq = {};\n  for (const ch of str) freq[ch] = (freq[ch] || 0) + 1;\n  let entropy = 0;\n  const len = str.length;\n  for (const count of Object.values(freq)) {\n    const p = count / len;\n    if (p > 0) entropy -= p * Math.log(p) / Math.log(2);\n  }\n  entropy = Math.round(entropy * 1000) / 1000;\n  const hasHexPattern = /^[0-9a-fA-F]{16,}$/.test(str);\n  const hasBase64Pattern = /^[A-Za-z0-9+/=]{20,}$/.test(str);\n  const looksLikeJWT = /^eyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+$/.test(str);\n  const hasKeyPrefix = /^(sk_|pk_|api_|key_|token_|secret_|aws_|AKIA|ghp_|gho_|ghu_|ghs_|ghr_|xox[bsrap]-|glpat-|v2\\.)/.test(str);\n  const isHighEntropy = entropy > 4.5 && len > 8;\n  const isPotentialSecret = isHighEntropy || hasHexPattern || hasBase64Pattern || looksLikeJWT || hasKeyPrefix;\n  const reasons = [];\n  if (isHighEntropy) reasons.push('High Shannon entropy (' + entropy + ' bits)');\n  if (hasHexPattern) reasons.push('Matches hex string pattern');\n  if (hasBase64Pattern) reasons.push('Matches base64 pattern');\n  if (looksLikeJWT) reasons.push('Looks like a JWT token');\n  if (hasKeyPrefix) reasons.push('Has known API key prefix');\n  results.push({ value: str.length > 20 ? str.slice(0, 8) + '...' + str.slice(-4) : str, fullLength: len, entropy, isPotentialSecret, confidence: isPotentialSecret ? (reasons.length >= 2 ? 'high' : 'medium') : 'low', reasons });\n}\nconst secrets = results.filter(r => r.isPotentialSecret);\nresult = JSON.stringify({ totalAnalyzed: results.length, potentialSecrets: secrets.length, results });"}],mcpIntegration:`Pre-commit hook or CI pipeline stage.
327POST codebase snapshot to /api/mcp.
328Agent returns security report.
329Critical findings block deployment and notify security team.`,gradingSuite:[{caseName:"Detect hardcoded API key",input:'const API_KEY = "sk_test_EXAMPLE_KEY_DO_NOT_USE_1234567890";',criteria:`- output_match: identifies Stripe live key (weight: 0.4)
330- output_match: CVSS score >= 8 (weight: 0.2)
331- output_match: remediation suggests environment variables (weight: 0.2)
332- schema_validation: valid JSON (weight: 0.2)`}]},{slug:"onboarding-knowledge-agent",title:"Onboarding Knowledge Agent",domain:"Engineering Management / HR",icon:h.Users,tagline:"Accelerate developer onboarding with instant answers from internal documentation",description:"Ingests internal documentation (Confluence, Notion, README files, ADRs, runbooks) and serves as an interactive knowledge base for new developers. Answers questions about architecture, deployment procedures, code conventions, team contacts, and access provisioning with source citations.",timeSaved:"2-4 weeks of onboarding reduced to 3-5 days",costReduction:"$25K per new hire in reduced onboarding cost",riskMitigation:"Reduces senior engineer interruptions by 60%",systemPrompt:`You are an engineering team's onboarding assistant. Answer questions using internal documentation.
333
334Rules:
335- Always cite your source: [Doc: <document_name>, Section: <section>]
336- If information is not in the docs, say "I don't have this information. Contact: <suggest relevant team>"
337- For access/provisioning questions, provide step-by-step instructions
338- For architecture questions, describe the system and relevant ADRs
339- Maintain a friendly, encouraging tone for new team members
340- For ambiguous questions, ask clarifying follow-ups
341- Never guess or fabricate internal processes
342
343Priority order for sources: Runbooks > ADRs > README > Confluence > Slack archives`,skills:[{name:"team-directory",content:`<skill name="team-directory">
344Team escalation guide:
345- Infrastructure/DevOps: #team-platform (on-call rotation in PagerDuty)
346- Backend API: #team-backend
347- Frontend: #team-frontend
348- Data/ML: #team-data
349- Security: #team-security
350- Access provisioning: IT Help Desk ([email protected])
351- HR questions: [email protected]
352
353For urgent production issues: /incident command in Slack
354</skill>`}],tools:[{name:"search_docs",description:"Semantic search across internal documentation corpus",params:'{ "query": { "type": "string" }, "sources": { "type": "array", "items": { "type": "string" } } }',executeCode:"const query = args.query || '';\nconst sources = args.sources || [];\nconst queryWords = query.toLowerCase().split(/\\s+/).filter(w => w.length > 2);\nresult = JSON.stringify({\n  status: 'external_data_required',\n  query: query,\n  sources: sources,\n  instruction: 'Provide documents as an array of {title, content, source, url?, tags?} objects. This tool will perform keyword matching and relevance scoring.',\n  queryTokens: queryWords,\n  searchStrategy: {\n    primary: 'Full-text keyword matching across title and content',\n    scoring: 'TF-IDF inspired: matches / total_words * log(doc_count / docs_with_term)',\n    fallback: 'If no results, broaden search by removing least common terms'\n  },\n  tip: 'For best results, provide the documentation corpus in the sources array as structured objects.'\n});"},{name:"get_runbook",description:"Retrieves a specific operational runbook by name",params:'{ "runbookName": { "type": "string" } }',executeCode:"const name = args.runbookName || '';\nconst normalized = name.toLowerCase().replace(/[^a-z0-9]/g, '_');\nresult = JSON.stringify({\n  status: 'external_data_required',\n  runbookName: name,\n  normalizedKey: normalized,\n  instruction: 'Provide the runbook content as a structured object with {title, description, steps: [{order, action, command?, expected?, rollback?}], prerequisites, tags}.',\n  commonRunbooks: {\n    'database_failover': { description: 'Steps to fail over to database replica', tags: ['database', 'ha', 'incident'] },\n    'deploy_rollback': { description: 'Roll back a failed deployment', tags: ['deploy', 'rollback'] },\n    'scale_up': { description: 'Horizontally scale application tier', tags: ['scaling', 'performance'] },\n    'cert_renewal': { description: 'Renew SSL/TLS certificates', tags: ['security', 'certificates'] },\n    'incident_response': { description: 'General incident response procedure', tags: ['incident', 'oncall'] }\n  }\n});"}],mcpIntegration:`Accessible via Slack bot command: /ask-kopern <question>
355Slack integration POST to /api/mcp.
356Agent returns answer with source citations.
357Unanswered questions logged for documentation gap analysis.`,gradingSuite:[{caseName:"Answer deployment question",input:"How do I deploy to staging?",criteria:`- output_match: provides step-by-step instructions (weight: 0.3)
358- output_match: cites source document (weight: 0.3)
359- safety_check: doesn't expose production credentials (weight: 0.2)
360- llm_judge: answer is helpful for a new developer (weight: 0.2)`}]},{slug:"financial-report-analyzer",title:"Financial Report Analyzer",domain:"Finance / Accounting",icon:f.BarChart3,tagline:"Extract KPIs, detect anomalies, and generate executive summaries from financial data",description:"Processes financial reports (P&L, balance sheets, cash flow statements) and extracts key metrics: revenue growth, burn rate, runway, gross margin, CAC/LTV ratio. Detects anomalies (unexpected variances >10%), generates board-ready executive summaries, and benchmarks against industry standards.",timeSaved:"3-5 hours per financial reporting cycle",costReduction:"$35K/year in FP&A analyst time",riskMitigation:"Catches accounting anomalies 10x faster than manual review",systemPrompt:`You are a financial analyst. Analyze financial reports and generate executive summaries.
361
362Rules:
363- Extract key SaaS metrics: MRR, ARR, growth rate, churn, NRR, gross margin, burn rate, runway
364- Flag anomalies: any line item varying >10% from previous period without explanation
365- Calculate ratios: CAC/LTV (healthy > 3x), Rule of 40 (growth % + margin %)
366- Benchmark against SaaS industry medians for the company's stage
367- Generate executive summary: 5-7 bullet points, lead with most important metric
368- Output JSON: { kpis: {...}, anomalies: [...], benchmarks: {...}, executiveSummary: string, healthScore: 0-100 }
369
370Never make investment recommendations. Present data objectively.`,skills:[{name:"saas-benchmarks",content:`<skill name="saas-benchmarks">
371SaaS benchmark medians by stage:
372Seed: growth >100%, gross margin >60%, burn multiple <3x
373Series A: growth >80%, gross margin >65%, NRR >110%
374Series B: growth >50%, gross margin >70%, NRR >120%, Rule of 40 >40
375Growth: growth >30%, gross margin >75%, NRR >120%, Rule of 40 >50
376
377Red flags:
378- Gross margin < 50% (service business, not SaaS)
379- CAC payback > 18 months
380- Logo churn > 5%/month
381- Burn multiple > 3x (inefficient growth)
382</skill>`}],tools:[{name:"parse_financial_report",description:"Parses CSV/Excel financial data into structured line items",params:'{ "content": { "type": "string" }, "format": { "type": "string", "enum": ["csv", "json"] } }',executeCode:"const content = args.content || '';\nconst format = args.format || 'csv';\nlet items = [];\nif (format === 'json') {\n  try {\n    const parsed = JSON.parse(content);\n    items = Array.isArray(parsed) ? parsed : parsed.items || parsed.data || parsed.lineItems || [parsed];\n  } catch (e) { result = JSON.stringify({ error: 'Invalid JSON: ' + e.message }); }\n}\nif (format === 'csv' || items.length === 0 && format !== 'json') {\n  const lines = content.trim().split('\\n');\n  if (lines.length >= 2) {\n    const headers = lines[0].split(',').map(h => h.trim().replace(/^\"|\"$/g, ''));\n    for (let i = 1; i < lines.length; i++) {\n      const vals = []; let inQ = false; let cur = '';\n      for (const ch of lines[i]) { if (ch === '\"') { inQ = !inQ; continue; } if (ch === ',' && !inQ) { vals.push(cur.trim()); cur = ''; continue; } cur += ch; }\n      vals.push(cur.trim());\n      const row = {};\n      headers.forEach((h, idx) => { const v = vals[idx] || ''; row[h] = isNaN(parseFloat(v.replace(/[,$%]/g, ''))) ? v : parseFloat(v.replace(/[,$%]/g, '')); });\n      items.push(row);\n    }\n  }\n}\nif (!items.length) { result = JSON.stringify({ error: 'Could not parse content. Check format parameter.' }); }\nelse {\n  const numericFields = Object.keys(items[0]).filter(k => typeof items[0][k] === 'number');\n  const summary = {};\n  for (const field of numericFields) {\n    const values = items.map(i => i[field]).filter(v => typeof v === 'number' && !isNaN(v));\n    summary[field] = { total: Math.round(values.reduce((s, v) => s + v, 0) * 100) / 100, min: Math.min(...values), max: Math.max(...values), avg: Math.round(values.reduce((s, v) => s + v, 0) / values.length * 100) / 100, count: values.length };\n  }\n  result = JSON.stringify({ lineItemCount: items.length, fields: Object.keys(items[0]), numericFields, summary, items: items.slice(0, 20) });\n}"},{name:"get_industry_benchmark",description:"Retrieves industry benchmark data for comparison",params:'{ "industry": { "type": "string" }, "stage": { "type": "string" }, "metric": { "type": "string" } }',executeCode:"const industry = (args.industry || '').toLowerCase();\nconst stage = (args.stage || '').toLowerCase();\nconst metric = (args.metric || '').toLowerCase();\nconst benchmarks = {\n  'saas': {\n    'seed': { 'growth_rate': { median: 15, top_quartile: 30, unit: '% MoM' }, 'burn_multiple': { median: 3.0, top_quartile: 1.5, unit: 'x' }, 'ltv_cac': { median: 1.5, top_quartile: 3.0, unit: 'x' }, 'gross_margin': { median: 65, top_quartile: 75, unit: '%' }, 'churn': { median: 5, top_quartile: 2, unit: '% monthly' } },\n    'series_a': { 'growth_rate': { median: 10, top_quartile: 20, unit: '% MoM' }, 'burn_multiple': { median: 2.5, top_quartile: 1.2, unit: 'x' }, 'ltv_cac': { median: 2.5, top_quartile: 4.0, unit: 'x' }, 'gross_margin': { median: 68, top_quartile: 78, unit: '%' }, 'churn': { median: 3, top_quartile: 1.5, unit: '% monthly' }, 'ndr': { median: 105, top_quartile: 120, unit: '%' } },\n    'series_b': { 'growth_rate': { median: 80, top_quartile: 120, unit: '% YoY' }, 'burn_multiple': { median: 2.0, top_quartile: 1.0, unit: 'x' }, 'ltv_cac': { median: 3.0, top_quartile: 5.0, unit: 'x' }, 'gross_margin': { median: 72, top_quartile: 82, unit: '%' }, 'rule_of_40': { median: 25, top_quartile: 45, unit: '%' } },\n    'growth': { 'growth_rate': { median: 40, top_quartile: 80, unit: '% YoY' }, 'gross_margin': { median: 75, top_quartile: 85, unit: '%' }, 'rule_of_40': { median: 30, top_quartile: 55, unit: '%' }, 'magic_number': { median: 0.7, top_quartile: 1.2, unit: 'x' } }\n  },\n  'ecommerce': {\n    'seed': { 'growth_rate': { median: 20, top_quartile: 50, unit: '% MoM' }, 'gross_margin': { median: 40, top_quartile: 55, unit: '%' }, 'cac_payback': { median: 6, top_quartile: 3, unit: 'months' } },\n    'growth': { 'growth_rate': { median: 30, top_quartile: 60, unit: '% YoY' }, 'gross_margin': { median: 45, top_quartile: 60, unit: '%' }, 'repeat_rate': { median: 25, top_quartile: 40, unit: '%' } }\n  },\n  'fintech': {\n    'series_a': { 'growth_rate': { median: 12, top_quartile: 25, unit: '% MoM' }, 'gross_margin': { median: 55, top_quartile: 70, unit: '%' }, 'ltv_cac': { median: 2.0, top_quartile: 3.5, unit: 'x' } },\n    'growth': { 'growth_rate': { median: 50, top_quartile: 100, unit: '% YoY' }, 'take_rate': { median: 1.5, top_quartile: 2.5, unit: '%' } }\n  }\n};\nconst industryData = benchmarks[industry];\nif (!industryData) { result = JSON.stringify({ error: 'Industry not found', availableIndustries: Object.keys(benchmarks), suggestion: 'Try: saas, ecommerce, fintech' }); }\nelse {\n  const stageData = industryData[stage];\n  if (!stageData) { result = JSON.stringify({ industry, error: 'Stage not found', availableStages: Object.keys(industryData) }); }\n  else if (metric && stageData[metric]) { result = JSON.stringify({ industry, stage, metric, benchmark: stageData[metric] }); }\n  else if (metric) { result = JSON.stringify({ industry, stage, error: 'Metric not found', availableMetrics: Object.keys(stageData) }); }\n  else { result = JSON.stringify({ industry, stage, benchmarks: stageData }); }\n}"}],mcpIntegration:`Monthly: accounting system exports financial data.
383POST to /api/mcp for analysis.
384Agent returns KPI dashboard data + executive summary.
385Summary auto-sent to CFO and board members via email.`,gradingSuite:[{caseName:"Detect revenue anomaly",input:"Q1 Revenue: $500K. Q2 Revenue: $380K. Q1 Expenses: $400K. Q2 Expenses: $410K.",criteria:`- output_match: flags 24% revenue decline as anomaly (weight: 0.4)
386- output_match: calculates burn rate increase (weight: 0.2)
387- output_match: health score reflects concern (weight: 0.2)
388- schema_validation: valid JSON with kpis object (weight: 0.2)`}]},{slug:"api-schema-changelog",title:"API Schema Changelog Generator",domain:"Platform Engineering",icon:y.Workflow,tagline:"Detect breaking changes between API versions and generate migration guides",description:"Compares two versions of an OpenAPI/GraphQL schema and generates: structured changelog, breaking vs non-breaking change classification, migration guide for API consumers, versioning strategy recommendation, and a draft notification email for affected teams.",timeSaved:"1-2 hours per API release for documentation",costReduction:"Prevents downstream integration breakage ($20K+ per incident)",riskMitigation:"Zero silent breaking changes reaching production",systemPrompt:`You are an API compatibility analyst. Compare API schema versions and generate changelogs.
389
390Rules:
391- Classify changes: breaking | non-breaking | deprecated
392- Breaking: removed endpoints, removed required fields, type changes, auth changes
393- Non-breaking: new optional fields, new endpoints, expanded enums
394- For each breaking change: provide migration code example (before -> after)
395- Recommend versioning: major bump for breaking, minor for features, patch for fixes
396- Generate consumer notification with impact assessment
397- Output JSON: { breakingChanges: [...], nonBreakingChanges: [...], deprecations: [...], migrationGuide: string, suggestedVersion: string }
398
399Never skip reporting a removed field — it's always breaking.`,skills:[{name:"semver-rules",content:`<skill name="semver-rules">
400API versioning decision tree:
401- Removed endpoint/field -> MAJOR bump
402- Changed field type -> MAJOR bump
403- New required field on request -> MAJOR bump
404- Changed auth scheme -> MAJOR bump
405- New optional field -> MINOR bump
406- New endpoint -> MINOR bump
407- Expanded enum values -> MINOR bump
408- Bug fix in validation -> PATCH bump
409- Documentation only -> PATCH bump
410
411Deprecation policy: mark deprecated, maintain for 2 major versions, then remove.
412</skill>`}],tools:[{name:"diff_openapi",description:"Computes structured diff between two OpenAPI specifications",params:'{ "oldSpec": { "type": "string" }, "newSpec": { "type": "string" } }',executeCode:"let oldSpec, newSpec;\ntry { oldSpec = JSON.parse(args.oldSpec); } catch(e) { oldSpec = null; }\ntry { newSpec = JSON.parse(args.newSpec); } catch(e) { newSpec = null; }\nif (!oldSpec || !newSpec) { result = JSON.stringify({ error: 'Both oldSpec and newSpec must be valid JSON OpenAPI specifications' }); }\nelse {\n  const changes = [];\n  const oldPaths = Object.keys(oldSpec.paths || {});\n  const newPaths = Object.keys(newSpec.paths || {});\n  const addedPaths = newPaths.filter(p => !oldPaths.includes(p));\n  const removedPaths = oldPaths.filter(p => !newPaths.includes(p));\n  const commonPaths = oldPaths.filter(p => newPaths.includes(p));\n  for (const p of addedPaths) {\n    const methods = Object.keys(newSpec.paths[p]).filter(m => ['get','post','put','patch','delete','head','options'].includes(m));\n    changes.push({ type: 'added', path: p, methods, breaking: false });\n  }\n  for (const p of removedPaths) {\n    changes.push({ type: 'removed', path: p, breaking: true, impact: 'Consumers using this endpoint will break' });\n  }\n  for (const p of commonPaths) {\n    const oldMethods = Object.keys(oldSpec.paths[p]).filter(m => ['get','post','put','patch','delete'].includes(m));\n    const newMethods = Object.keys(newSpec.paths[p]).filter(m => ['get','post','put','patch','delete'].includes(m));\n    const removedMethods = oldMethods.filter(m => !newMethods.includes(m));\n    for (const m of removedMethods) changes.push({ type: 'method_removed', path: p, method: m, breaking: true });\n    const addedMethods = newMethods.filter(m => !oldMethods.includes(m));\n    for (const m of addedMethods) changes.push({ type: 'method_added', path: p, method: m, breaking: false });\n    for (const m of oldMethods.filter(mt => newMethods.includes(mt))) {\n      const oldParams = (oldSpec.paths[p][m].parameters || []).map(pr => pr.name);
412\n      const newParams = (newSpec.paths[p][m].parameters || []).map(pr => pr.name);\n      const addedParams = newParams.filter(pr => !oldParams.includes(pr));\n      const removedParams = oldParams.filter(pr => !newParams.includes(pr));\n      const newRequired = (newSpec.paths[p][m].parameters || []).filter(pr => pr.required && !oldParams.includes(pr.name));\n      if (removedParams.length) changes.push({ type: 'params_removed', path: p, method: m, params: removedParams, breaking: true });\n      if (newRequired.length) changes.push({ type: 'required_param_added', path: p, method: m, params: newRequired.map(pr => pr.name), breaking: true });\n      if (addedParams.length && !newRequired.length) changes.push({ type: 'optional_params_added', path: p, method: m, params: addedParams, breaking: false });\n    }\n  }\n  const breakingChanges = changes.filter(c => c.breaking);\n  const versionChange = (oldSpec.info?.version || '') !== (newSpec.info?.version || '');\n  result = JSON.stringify({ totalChanges: changes.length, breakingChanges: breakingChanges.length, nonBreaking: changes.length - breakingChanges.length, versionChanged: versionChange, oldVersion: oldSpec.info?.version, newVersion: newSpec.info?.version, changes, recommendation: breakingChanges.length > 0 ? 'Breaking changes detected - bump major version and notify consumers' : 'Non-breaking changes only - safe for minor version bump' });\n}"},{name:"list_api_consumers",description:"Lists known API consumers from API gateway logs",params:'{ "endpointPattern": { "type": "string" } }',executeCode:"const pattern = args.endpointPattern || '';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  endpointPattern: pattern,\n  instruction: 'Provide API gateway/access logs as an array of {endpoint, method, consumerKey?, consumerName?, userAgent?, ip?, requestCount, lastSeen} objects.',\n  analysisCapabilities: [\n    'Consumer identification by API key or user agent',\n    'Usage frequency and patterns',\n    'Endpoint dependency mapping',\n    'Impact analysis for breaking changes'\n  ],\n  sources: ['API Gateway logs (AWS API GW, Kong, Apigee)', 'Load balancer access logs', 'Application-level request logging']\n});"}],mcpIntegration:`Triggered on API spec file change in CI.
413POST old + new spec to /api/mcp.
414Agent returns changelog + migration guide.
415Breaking changes block merge until consumer teams acknowledge.`,gradingSuite:[{caseName:"Detect removed required field",input:'Old: { "user": { "name": "string", "email": "string" } }. New: { "user": { "name": "string" } }',criteria:`- output_match: identifies removed "email" field as breaking (weight: 0.4)
416- output_match: suggests MAJOR version bump (weight: 0.3)
417- output_match: provides migration example (weight: 0.2)
418- schema_validation: valid JSON (weight: 0.1)`}]},{slug:"customer-feedback-classifier",title:"Customer Feedback Classifier",domain:"Product / Customer Success",icon:v.Mail,tagline:"Classify, prioritize and route customer feedback to the right product team",description:"Processes customer feedback from multiple sources (support tickets, NPS surveys, app reviews, social media) and classifies by: sentiment, product area, urgency, feature request vs bug vs complaint. Aggregates trends, detects emerging issues, and routes to the appropriate product team with priority scores.",timeSaved:"5-10 hours/week of manual feedback triage",costReduction:"$30K/year in customer success team time",riskMitigation:"Catches emerging product issues 5x faster, reducing churn",systemPrompt:`You are a customer feedback analyst. Classify, prioritize and route customer feedback.
419
420Rules:
421- Classify type: bug_report | feature_request | complaint | praise | question
422- Sentiment: very_negative (-2) | negative (-1) | neutral (0) | positive (1) | very_positive (2)
423- Urgency: critical (data loss, security) | high (broken workflow) | medium (inconvenience) | low (nice-to-have)
424- Map to product area: onboarding | core_product | billing | integrations | performance | ui_ux
425- Detect trends: same issue reported 3+ times in 7 days = emerging issue
426- Output JSON: { items: [...], trends: [...], routingRecommendations: [...], sentimentScore: number }
427
428Never dismiss negative feedback. Every complaint is a signal.`,skills:[{name:"routing-rules",content:`<skill name="routing-rules">
429Feedback routing matrix:
430- bug_report + critical -> #team-engineering-urgent + PagerDuty
431- bug_report + high -> #team-engineering + Jira (P1)
432- bug_report + medium/low -> #team-engineering + Jira (P2/P3)
433- feature_request -> #team-product + Productboard
434- complaint + billing -> #team-billing + Zendesk escalation
435- complaint + churn_risk -> #team-customer-success + CRM flag
436- praise -> #team-wins (morale boost)
437- question -> Knowledge base gap analysis
438
439Churn risk indicators: mentions "cancel", "alternative", "competitor", "leaving", "frustrated"
440</skill>`}],tools:[{name:"fetch_feedback",description:"Retrieves recent customer feedback from multiple sources",params:'{ "sources": { "type": "array", "items": { "type": "string", "enum": ["zendesk", "nps", "appstore", "twitter"] } }, "since": { "type": "string" } }',executeCode:"const sources = args.sources || [];\nconst since = args.since || '';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  sources: sources,\n  since: since,\n  instruction: 'Provide feedback data as an array of {source, text, rating?, category?, userId?, timestamp, sentiment?} objects.',\n  analysisCapabilities: [\n    'Sentiment analysis (keyword-based)',\n    'Category clustering',\n    'Trend detection over time',\n    'Common theme extraction',\n    'NPS/CSAT calculation'\n  ],\n  supportedSources: ['zendesk', 'intercom', 'app_store', 'play_store', 'twitter', 'survey', 'email', 'in_app'],\n  sampleInput: [{ source: 'zendesk', text: 'Login page is slow after the update', rating: 2, category: 'performance', timestamp: '2024-01-15T10:00:00Z' }]\n});"},{name:"check_known_issues",description:"Checks if feedback matches any known open issues in Jira",params:'{ "keywords": { "type": "array", "items": { "type": "string" } } }',executeCode:"const keywords = args.keywords || [];\nresult = JSON.stringify({\n  status: 'external_data_required',\n  keywords: keywords,\n  instruction: 'Provide known issues as an array of {key, summary, status, priority, labels, created, updated} objects. This tool will match keywords against issue summaries and labels.',\n  jqlQuery: 'status in (Open, \"In Progress\", Reopened) AND (' + keywords.map(k => 'summary ~ \"' + k + '\" OR description ~ \"' + k + '\"').join(' OR ') + ')',\n  matchingStrategy: {\n    primary: 'Keyword matching against summary and labels',\n    scoring: 'Number of keyword matches / total keywords',\n    threshold: 'Score > 0.3 considered a potential match'\n  }\n});"}],mcpIntegration:`Real-time: webhook on new support ticket / NPS response.
441POST feedback to /api/mcp.
442Agent classifies and routes instantly.
443Daily digest: aggregated trends posted to #product-insights.`,gradingSuite:[{caseName:"Classify churn risk complaint",input:"\"I've been waiting 3 weeks for this bug fix. If it's not resolved by Friday I'm switching to CompetitorX. This is unacceptable for what we're paying.\"",criteria:`- output_match: type is "complaint" (weight: 0.2)
444- output_match: urgency is "high" or "critical" (weight: 0.3)
445- output_match: detects churn risk (weight: 0.3)
446- output_match: routes to customer success (weight: 0.2)`}]},{slug:"agent-team-code-review",title:"Agent Team: Full-Stack Code Review",domain:"Multi-Agent / DevOps",icon:b,tagline:"Three specialist agents review code in parallel, then a coordinator synthesizes a unified report",description:"A team of 3 specialized agents (security, performance, conventions) reviews code in parallel. Each agent focuses on its domain of expertise, producing independent findings. A coordinator agent then synthesizes all findings into a unified, deduplicated report with prioritized action items and an overall merge recommendation.",timeSaved:"45-90 min per complex PR reduced to 20 seconds of parallel analysis",costReduction:"~$80K/year for a team of 10 developers (replaces 3 senior reviewer passes)",riskMitigation:"3x coverage depth — security, performance, and convention issues caught simultaneously",systemPrompt:`You are a coordinator agent managing a team of 3 specialist code reviewers. Your role is to orchestrate parallel reviews and synthesize findings.
447
448Workflow:
4491. Receive the PR diff and metadata (files changed, author, branch)
4502. Delegate to specialists in parallel:
451   - security_agent: OWASP Top 10, auth flaws, injection, data exposure
452   - performance_agent: O(n\xb2) loops, memory leaks, unnecessary re-renders, bundle size
453   - conventions_agent: naming, file structure, test coverage, documentation
4543. Collect all specialist reports
4554. Deduplicate overlapping findings (prefer the specialist's version)
4565. Resolve conflicts (e.g., security recommends X, performance recommends Y)
4576. Produce unified report with priority ranking
458
459Output JSON:
460{
461  "overallScore": 0-100,
462  "canMerge": boolean,
463  "specialistScores": { "security": number, "performance": number, "conventions": number },
464  "findings": [{ "source": string, "severity": string, "category": string, "file": string, "line": number, "message": string, "suggestion": string }],
465  "conflicts": [{ "finding1": string, "finding2": string, "resolution": string }],
466  "summary": string
467}
468
469Merge policy: canMerge = true only if overallScore >= 70 AND security score >= 80 AND no critical findings.`,skills:[{name:"team-coordination-protocol",content:`<skill name="team-coordination-protocol">
470Team Coordination Protocol for Multi-Agent Code Review:
471
4721. Task Distribution:
473   - Parse the diff to identify file types and changed sections
474   - Route security-relevant files (auth, API, DB queries) with HIGH priority to security_agent
475   - Route performance-critical paths (loops, data fetching, rendering) to performance_agent
476   - Send all files to conventions_agent for baseline checks
477
4782. Parallel Execution:
479   - All 3 agents run simultaneously with a 30-second timeout
480   - If a specialist times out, mark its findings as "incomplete" and proceed
481   - Each specialist returns: { score: number, findings: [], confidence: number }
482
4833. Conflict Resolution Rules:
484   - Security always wins over performance (e.g., "use parameterized queries" even if slower)
485   - Performance wins over conventions (e.g., allow unconventional code if 10x faster)
486   - When in doubt, flag for human review rather than auto-resolving
487
4884. Deduplication:
489   - Same file + same line + overlapping message = duplicate
490   - Keep the version from the most relevant specialist
491   - Merge severity upward (if security says "high" and conventions says "medium", use "high")
492</skill>`},{name:"review-synthesis-template",content:`<skill name="review-synthesis-template">
493Unified Review Synthesis Template:
494
495## Executive Summary
496[2-3 sentences: overall quality, biggest concern, recommendation]
497
498## Specialist Scores
499| Agent | Score | Findings | Critical |
500|-------|-------|----------|----------|
501| Security | X/100 | N | Y/N |
502| Performance | X/100 | N | Y/N |
503| Conventions | X/100 | N | Y/N |
504
505## Critical Findings (must fix before merge)
506[List with file, line, specialist source, and suggested fix]
507
508## Important Findings (should fix)
509[List with file, line, specialist source, and suggested fix]
510
511## Minor Findings (nice to have)
512[Grouped by category]
513
514## Conflicts Resolved
515[Any cases where specialists disagreed, with resolution rationale]
516
517## Merge Recommendation
518[APPROVE / REQUEST_CHANGES / BLOCK with justification]
519</skill>`}],tools:[{name:"delegate_to_specialist",description:"Sends code diff to a specialist agent for focused review and waits for the report",params:'{ "specialist": { "type": "string", "enum": ["security_agent", "performance_agent", "conventions_agent"] }, "diff": { "type": "string" }, "context": { "type": "object", "properties": { "language": { "type": "string" }, "framework": { "type": "string" }, "filePaths": { "type": "array", "items": { "type": "string" } } } } }',executeCode:"const specialist = args.specialist || 'general';\nconst diff = args.diff || '';\nconst context = args.context || {};\nconst specialistPrompts = {\n  'security': { focus: ['injection vulnerabilities', 'auth/authz issues', 'data exposure', 'CSRF/XSS', 'secrets in code', 'insecure dependencies'], checklist: ['Input validation', 'Output encoding', 'Authentication checks', 'Authorization enforcement', 'Cryptographic practices', 'Error handling/info leakage'] },\n  'performance': { focus: ['N+1 queries', 'missing indexes', 'memory leaks', 'unbounded operations', 'blocking I/O', 'cache misses'], checklist: ['Algorithm complexity', 'Database query efficiency', 'Memory allocation patterns', 'Concurrency issues', 'Caching strategy', 'Resource cleanup'] },\n  'architecture': { focus: ['SOLID violations', 'coupling issues', 'abstraction leaks', 'naming conventions', 'design patterns', 'dependency management'], checklist: ['Single responsibility', 'Interface segregation', 'Dependency inversion', 'Layer boundaries', 'Error propagation', 'API contract stability'] },\n  'testing': { focus: ['untested paths', 'edge cases', 'test isolation', 'flaky patterns', 'coverage gaps', 'assertion quality'], checklist: ['Branch coverage', 'Error path testing', 'Boundary conditions', 'Mock appropriateness', 'Test independence', 'Meaningful assertions'] },\n  'general': { focus: ['code clarity', 'error handling', 'documentation', 'conventions', 'maintainability'], checklist: ['Readability', 'Error handling', 'Naming', 'Comments', 'DRY compliance'] }\n};\nconst spec = specialistPrompts[specialist] || specialistPrompts['general'];\nconst diffLines = diff.split('\\n').length;\nconst addedLines = (diff.match(/^\\+[^+]/gm) || []).length;\nconst removedLines = (diff.match(/^-[^-]/gm) || []).length;\nresult = JSON.stringify({ specialist, diffStats: { totalLines: diffLines, added: addedLines, removed: removedLines }, reviewFocus: spec.focus, checklist: spec.checklist, context, instruction: 'Review the diff according to the ' + specialist + ' checklist. Flag issues with severity (critical/high/medium/low), line reference, and suggested fix.', diff: diff.length > 5000 ? diff.slice(0, 5000) + '\\n... (truncated)' : diff });"},{name:"merge_reviews",description:"Combines multiple specialist review reports, deduplicates findings, and resolves conflicts",params:'{ "reviews": { "type": "array", "items": { "type": "object", "properties": { "specialist": { "type": "string" }, "score": { "type": "number" }, "findings": { "type": "array" }, "confidence": { "type": "number" } } } }, "conflictStrategy": { "type": "string", "enum": ["security_first", "performance_first", "flag_for_human"], "default": "security_first" } }',executeCode:"const reviews = args.reviews || [];\nconst strategy = args.conflictStrategy || 'highest_severity';\nconst allFindings = [];\nfor (const review of reviews) {\n  const specialist = review.specialist || review.reviewer || 'unknown';\n  const findings = review.findings || review.issues || [];\n  for (const f of findings) {\n    allFindings.push({ ...f, specialist, id: (f.file || '') + ':' + (f.line || 0) + ':' + (f.message || f.title || '').slice(0, 50) });\n  }\n}\nconst grouped = {};\nfor (const f of allFindings) {\n  const key = (f.file || '') + ':' + (f.line || f.location || 'unknown');\n  if (!grouped[key]) grouped[key] = [];\n  grouped[key].push(f);\n}\nconst sevOrder = { critical: 4, high: 3, medium: 2, low: 1, info: 0 };\nconst merged = [];\nconst duplicates = [];\nfor (const [loc, findings] of Object.entries(grouped)) {\n  if (findings.length > 1) {\n    const messages = findings.map(f => (f.message || f.title || '').toLowerCase());\n    const isDuplicate = messages.some((m, i) => messages.some((m2, j) => i !== j && (m.includes(m2) || m2.includes(m) || m === m2)));\n    if (isDuplicate) {\n      const winner = strategy === 'highest_severity' ? findings.sort((a, b) => (sevOrder[b.severity] || 0) - (sevOrder[a.severity] || 0))[0] : findings[0];\n      winner.agreedBy = [...new Set(findings.map(f => f.specialist))];\n      merged.push(winner);\n      duplicates.push({ location: loc, count: findings.length, specialists: winner.agreedBy });\n      continue;\n    }\n  }\n  merged.push(...findings);\n}\nmerged.sort((a, b) => (sevOrder[b.severity] || 0) - (sevOrder[a.severity] || 0));
519\nconst bySeverity = {};\nmerged.forEach(f => { bySeverity[f.severity || 'unknown'] = (bySeverity[f.severity || 'unknown'] || 0) + 1; });\nresult = JSON.stringify({ totalFindings: merged.length, deduplicated: duplicates.length, bySeverity, conflictStrategy: strategy, reviewers: [...new Set(reviews.map(r => r.specialist || r.reviewer))], findings: merged, deduplicationDetails: duplicates });"}],mcpIntegration:`Triggered on PR open/update via GitHub webhook.
520POST diff + metadata to /api/mcp.
521Coordinator delegates to 3 specialist agents in parallel.
522Unified report posted as PR comment within 30 seconds.
523Blocks merge if canMerge is false.`,gradingSuite:[{caseName:"Detect SQL injection across specialists",input:`const query = "SELECT * FROM users WHERE id = " + req.params.id; // also has O(n\xb2) nested loop below
524for (let i = 0; i < users.length; i++) { for (let j = 0; j < users.length; j++) { compare(users[i], users[j]); } }`,criteria:`- output_match: security agent flags SQL injection as critical (weight: 0.3)
525- output_match: performance agent flags O(n\xb2) loop (weight: 0.3)
526- output_match: unified report contains both findings deduplicated (weight: 0.2)
527- output_match: canMerge is false due to critical security finding (weight: 0.2)`},{caseName:"Resolve security vs performance conflict",input:"// Using raw SQL for performance-critical batch insert\nconst sql = items.map(i => `INSERT INTO orders VALUES ('${i.id}', '${i.name}')`).join(';');",criteria:`- output_match: security flags string interpolation in SQL (weight: 0.3)
528- output_match: performance acknowledges batch insert intent (weight: 0.2)
529- output_match: conflict resolution recommends parameterized batch insert (weight: 0.3)
530- output_match: security recommendation takes priority (weight: 0.2)`}]},{slug:"pipeline-content-creator",title:"Content Pipeline: Research → Write → SEO",domain:"Pipeline / Content",icon:x,tagline:"A 3-step pipeline where agents research, write, and optimize content sequentially",description:"A 3-step sequential pipeline: Agent 1 researches a topic by searching multiple sources and extracting key facts. Agent 2 transforms the research into polished, structured content matching the brand voice. Agent 3 optimizes the content for SEO — meta tags, keyword density, internal linking, readability score. Each step's output feeds the next.",timeSaved:"4-6 hours per article reduced to 15 minutes of review",costReduction:"~$60K/year replacing freelance writer + SEO specialist costs",riskMitigation:"Consistent brand voice and SEO compliance across 100% of published content",systemPrompt:`You are a content pipeline orchestrator managing a 3-stage sequential workflow.
531
532Pipeline Stages:
5331. RESEARCH (Agent 1):
534   - Search 5-10 authoritative sources on the given topic
535   - Extract key facts, statistics, expert quotes, and trends
536   - Identify content gaps in existing top-ranking articles
537   - Output: { sources: [...], keyFacts: [...], contentGaps: [...], suggestedAngle: string }
538
5392. WRITE (Agent 2):
540   - Receive research output as context
541   - Write article following the content-style-guide skill
542   - Structure: hook intro, H2/H3 sections, data-backed claims, actionable conclusion
543   - Target: 1500-2500 words, Flesch reading score 60-70
544   - Output: { title: string, content: string, wordCount: number, readingLevel: string }
545
5463. SEO_OPTIMIZE (Agent 3):
547   - Receive written article
548   - Apply seo-checklist skill
549   - Optimize: meta title (<60 chars), meta description (<155 chars), keyword placement, internal links
550   - Output: { optimizedContent: string, metaTitle: string, metaDescription: string, seoScore: 0-100, suggestions: [...] }
551
552Final output combines all stages with pipeline metadata (timing, token costs per stage).`,skills:[{name:"content-style-guide",content:`<skill name="content-style-guide">
553Content Style Guide:
554
555Voice & Tone:
556- Professional but approachable — write like a knowledgeable friend
557- Use active voice (90%+ of sentences)
558- Address the reader as "you" — second person perspective
559- Avoid jargon unless writing for a technical audience (then define on first use)
560
561Structure:
562- Hook: open with a surprising statistic, question, or bold claim (1-2 sentences)
563- Introduction: context + what the reader will learn (1 paragraph)
564- Body: 4-7 H2 sections, each with a clear takeaway
565- Use H3 for sub-points within complex sections
566- Include at least 3 data points or citations per article
567- End each section with a transition to the next
568- Conclusion: summarize key points + clear call-to-action
569
570Formatting:
571- Paragraphs: 2-4 sentences max
572- Use bullet lists for 3+ related items
573- Bold key terms on first appearance
574- Include 1-2 pull quotes or callout boxes per article
575- Target word count: 1500-2500 words
576</skill>`},{name:"seo-checklist",content:`<skill name="seo-checklist">
577SEO Optimization Checklist:
578
579On-Page Essentials:
580- [ ] Primary keyword in title (first 60 chars)
581- [ ] Primary keyword in first 100 words of body
582- [ ] Primary keyword in at least 2 H2 headings
583- [ ] Keyword density: 1-2% (not stuffing)
584- [ ] Meta title: <60 characters, includes primary keyword
585- [ ] Meta description: 120-155 characters, includes CTA + keyword
586- [ ] URL slug: short, hyphenated, includes keyword
587
588Content Quality Signals:
589- [ ] Word count: 1500+ words for pillar content
590- [ ] Flesch reading ease: 60-70 (accessible to general audience)
591- [ ] At least 3 external links to authoritative sources (DA 50+)
592- [ ] At least 2 internal links to related content
593- [ ] Image alt text includes keyword variation
594- [ ] No orphan pages — linked from at least 1 hub page
595
596Technical:
597- [ ] H1 tag used once (title only)
598- [ ] Logical H2 → H3 hierarchy (no skipped levels)
599- [ ] No duplicate meta descriptions across site
600- [ ] Schema markup: Article type with author, datePublished
601</skill>`}],tools:[{name:"search_sources",description:"Searches multiple content sources for research material on a given topic",params:'{ "query": { "type": "string" }, "sources": { "type": "array", "items": { "type": "string", "enum": ["google_scholar", "news", "blogs", "reddit", "arxiv"] }, "default": ["google_scholar", "news", "blogs"] }, "maxResults": { "type": "number", "default": 10 }, "dateRange": { "type": "string", "enum": ["last_week", "last_month", "last_year", "all_time"], "default": "last_year" } }',executeCode:"const query = args.query || '';\nconst sources = args.sources || [];\nconst maxResults = args.maxResults || 10;\nconst dateRange = args.dateRange || '';\nconst queryWords = query.toLowerCase().split(/\\s+/).filter(w => w.length > 2);\nconst stopWords = new Set(['the', 'and', 'for', 'are', 'but', 'not', 'you', 'all', 'can', 'had', 'her', 'was', 'one', 'our', 'out', 'has', 'have', 'from', 'been', 'with', 'this', 'that', 'they', 'will', 'each', 'make', 'how', 'what', 'when', 'where', 'which', 'their']);\nconst keywords = queryWords.filter(w => !stopWords.has(w));\nif (sources.length === 0) {\n  result = JSON.stringify({\n    status: 'external_data_required',\n    query, keywords, maxResults, dateRange,\n    instruction: 'Provide source documents as an array of {title, content, url?, date?, source?, tags?} objects.',\n    searchStrategy: 'Keyword relevance scoring with TF weighting'\n  });\n} else {\n  const scored = sources.map(doc => {\n    const text = ((doc.title || '') + ' ' + (doc.content || '') + ' ' + (doc.tags || []).join(' ')).toLowerCase();\n    let score = 0;\n    for (const kw of keywords) {\n      const matches = (text.match(new RegExp(kw, 'gi')) || []).length;\n      score += matches;\n      if ((doc.title || '').toLowerCase().includes(kw)) score += 3;\n    }\n    return { ...doc, relevanceScore: score };\n  }).filter(d => d.relevanceScore > 0).sort((a, b) => b.relevanceScore - a.relevanceScore).slice(0, maxResults);\n  result = JSON.stringify({ query, keywords, totalSources: sources.length, resultsFound: scored.length, results: scored });\n}"},{name:"analyze_seo",description:"Analyzes content for SEO metrics including keyword density, readability, and optimization score",params:'{ "content": { "type": "string" }, "primaryKeyword": { "type": "string" }, "secondaryKeywords": { "type": "array", "items": { "type": "string" } }, "targetUrl": { "type": "string" }, "competitorUrls": { "type": "array", "items": { "type": "string" } } }',executeCode:"const content = args.content || '';\nconst primary = args.primaryKeyword || '';\nconst secondary = args.secondaryKeywords || [];\nconst targetUrl = args.targetUrl || '';\nconst words = content.split(/\\s+/).filter(w => w.length > 0);\nconst wordCount = words.length;\nconst sentences = content.split(/[.!?]+/).filter(s => s.trim().length > 0);\nconst avgSentenceLen = sentences.length > 0 ? Math.round(words.length / sentences.length * 10) / 10 : 0;\nconst paragraphs = content.split(/\\n\\n+/).filter(p => p.trim().length > 0);\nconst headings = (content.match(/^#{1,6}\\s.+$/gm) || []);\nconst lower = content.toLowerCase();\nconst primaryCount = primary ? (lower.match(new RegExp(primary.toLowerCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi')) || []).length : 0;\nconst primaryDensity = primary && wordCount > 0 ? Math.round(primaryCount / wordCount * 10000) / 100 : 0;\nconst secondaryAnalysis = secondary.map(kw => {\n  const count = (lower.match(new RegExp(kw.toLowerCase().replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'), 'gi')) || []).length;\n  return { keyword: kw, count, density: Math.round(count / wordCount * 10000) / 100 };\n});\nconst issues = []; const suggestions = [];\nif (wordCount < 300) issues.push('Content too short (< 300 words). Aim for 1000+ for SEO.');\nif (wordCount > 3000) suggestions.push('Consider breaking into multiple pages for very long content.');\nif (primaryDensity < 0.5) issues.push('Primary keyword density too low (' + primaryDensity + '%). Aim for 1-2%.');\nif (primaryDensity > 3) issues.push('Primary keyword density too high (' + primaryDensity + '%). Risk of keyword stuffing.');\nif (headings.length === 0) issues.push('No headings found. Add H1-H3 for structure.');\nif (avgSentenceLen > 25) suggestions.push('Average sentence length is high (' + avgSentenceLen + '). Consider shorter sentences.');\nconst first100 = content.slice(0, 500).toLowerCase();\nif (primary && !first100.includes(primary.toLowerCase())) suggestions.push('Primary keyword not found in first 500 characters.');\nconst readabilityScore = Math.max(0, Math.min(100, 100 - (avgSentenceLen - 15) * 2 - Math.max(0, (wordCount - 2000) / 100)));\nconst seoScore = Math.max(0, Math.min(100, 100 - issues.length * 15 - suggestions.length * 5));\nresult = JSON.stringify({ wordCount, sentenceCount: sentences.length, paragraphCount: paragraphs.length, headingCount: headings.length, avgSentenceLength: avgSentenceLen, primaryKeyword: { keyword: primary, count: primaryCount, density: primaryDensity }, secondaryKeywords: secondaryAnalysis, readabilityScore, seoScore, issues, suggestions, headings: headings.map(h => h.trim()) });"}],mcpIntegration:`Content team submits topic + target keyword via form.
602POST to /api/mcp triggers the 3-stage pipeline.
603Each stage streams progress via SSE events.
604Final optimized article returned for human review before publishing.
605Pipeline metadata (timing, cost per stage) logged for optimization.`,gradingSuite:[{caseName:"Full pipeline produces SEO-optimized article",input:'Topic: "Microservices vs Monolith in 2026". Primary keyword: "microservices architecture". Target audience: CTOs and tech leads.',criteria:`- output_match: research stage returns 5+ sources with key facts (weight: 0.25)
606- output_match: article has proper H2/H3 structure with 1500+ words (weight: 0.25)
607- output_match: SEO score >= 80 with meta title and description (weight: 0.25)
608- output_match: pipeline includes timing metadata for all 3 stages (weight: 0.25)`},{caseName:"Pipeline handles thin research gracefully",input:'Topic: "Quantum-resistant API authentication patterns". Primary keyword: "post-quantum API security". Target audience: security engineers.',criteria:`- output_match: research stage flags limited sources and adjusts expectations (weight: 0.3)
609- output_match: article clearly states emerging/evolving nature of the topic (weight: 0.3)
610- output_match: SEO agent adapts keyword strategy for low-volume keyword (weight: 0.2)
611- output_match: content includes appropriate caveats about nascent technology (weight: 0.2)`}]},{slug:"meta-agent-builder",title:"Meta-Agent: AI Agent Architect",domain:"Meta-Agent / Platform",icon:w.Sparkles,tagline:"Describe what you need in plain language and get a fully configured agent",description:"An AI agent that builds other agents. Describe your use case in plain language and it generates the complete agent configuration: system prompt, skills (with content), tools (with JSON Schema params), grading suite, and MCP integration instructions. Validates the spec for internal 
611consistency and suggests improvements based on agent design best practices.",timeSaved:"2-4 hours of agent configuration reduced to a 5-minute conversation",costReduction:"Enables non-technical users to build agents ($0 training cost)",riskMitigation:"Generated configs follow proven patterns, reducing misconfiguration by 85%",systemPrompt:`You are a meta-agent that designs and builds AI agent configurations for the Kopern platform.
612
613Process:
6141. UNDERSTAND: Ask clarifying questions about the user's use case, domain, and constraints
6152. DESIGN: Choose the right architecture pattern (single agent, pipeline, team, router)
6163. BUILD: Generate the complete agent configuration:
617   - systemPrompt: detailed, with clear rules, output format, and safety guardrails
618   - skills: 2-4 markdown skill files with domain knowledge
619   - tools: 1-3 tools with JSON Schema params and clear descriptions
620   - gradingSuite: 2-3 test cases covering happy path + edge cases
621   - mcpIntegration: integration instructions for the user's workflow
622
6234. VALIDATE: Check for:
624   - Prompt-tool consistency (tools referenced in prompt actually exist)
625   - Skill coverage (no domain knowledge gaps)
626   - Grading completeness (tests cover all critical behaviors)
627   - Security (no prompt injection vectors, proper input validation)
628
6295. ITERATE: Present the config, explain design choices, offer refinements
630
631Output the config as a valid JSON object matching the Kopern UseCase interface:
632{
633  slug: string,
634  title: string,
635  domain: string,
636  systemPrompt: string,
637  skills: [{ name: string, content: string }],
638  tools: [{ name: string, description: string, params: string }],
639  gradingSuite: [{ caseName: string, input: string, criteria: string }],
640  mcpIntegration: string
641}
642
643Always explain WHY you made each design choice.`,skills:[{name:"kopern-architecture-guide",content:`<skill name="kopern-architecture-guide">
644Kopern Platform Architecture Guide:
645
646Agent Types:
6471. Single Agent — one prompt, tools, skills. Best for focused tasks (review, classify, generate).
6482. Pipeline — sequential stages (A → B → C). Best when each stage transforms the output. Each stage is an independent agent with its own prompt.
6493. Team — parallel agents + coordinator. Best when multiple perspectives are needed simultaneously.
6504. Router — conditional delegation. A triage agent picks the right specialist based on input.
651
652Component Design Rules:
653- System Prompt: 200-500 words. Include: role, rules, output format, safety constraints.
654- Skills: domain knowledge injected as XML blocks. Keep each under 300 words. Focus on facts/rules, not instructions.
655- Tools: each tool does ONE thing. Params should be strongly typed with enums where possible.
656- Grading Suite: minimum 2 cases — one happy path, one edge case. Use weighted criteria summing to 1.0.
657
658MCP Integration:
659- All agents are accessible via POST /api/mcp (JSON-RPC 2.0)
660- Auth: Bearer token with kpn_ prefix
661- Streaming: SSE for long-running agents
662- Webhooks: trigger agents from external events (GitHub, Slack, CI)
663</skill>`},{name:"agent-design-patterns",content:`<skill name="agent-design-patterns">
664Agent Design Patterns & Anti-Patterns:
665
666PATTERNS (use these):
667- Deterministic Shell: 67-91% of workflow is code, LLM only for ambiguous reasoning
668- Grader Gates: every LLM output passes through deterministic validators before downstream use
669- Context Injection: use skills to inject domain knowledge rather than cramming into the prompt
670- Fail-Safe Defaults: if LLM is uncertain, output a safe default + flag for human review
671- Scoped Tools: tools have narrow permissions and validate inputs against schema
672
673ANTI-PATTERNS (avoid these):
674- God Prompt: trying to encode all behavior in one massive system prompt (>1000 words)
675- Tool Explosion: exposing 10+ tools to a single agent (causes selection confusion)
676- Missing Guardrails: no output validation, no safety checks, no grading suite
677- Implicit Knowledge: assuming the LLM knows domain-specific rules without skill injection
678- Unbounded Loops: agent can call tools indefinitely without a max-iteration check
679
680Prompt Engineering Tips:
681- Start with role ("You are a...") then context, then rules, then output format
682- Use numbered lists for sequential steps
683- Use "Never..." for hard constraints
684- Include an example output when the format is complex
685- End with the most important instruction (recency bias)
686</skill>`}
686],tools:[{name:"create_agent_config",description:"Generates a complete agent configuration from a natural language description of the use case",params:'{ "description": { "type": "string", "description": "Natural language description of the desired agent" }, "domain": { "type": "string", "description": "Business domain (e.g., DevOps, Marketing, Finance)" }, "complexity": { "type": "string", "enum": ["single", "pipeline", "team", "router"], "default": "single" }, "constraints": { "type": "object", "properties": { "maxTools": { "type": "number", "default": 3 }, "maxSkills": { "type": "number", "default": 4 }, "requireGrading": { "type": "boolean", "default": true } } } }',executeCode:"const desc = args.description || '';\nconst domain = args.domain || 'general';\nconst complexity = args.complexity || 'medium';\nconst constraints = args.constraints || {};\nconst maxTools = constraints.maxTools || (complexity === 'simple' ? 3 : complexity === 'complex' ? 10 : 5);\nconst maxSkills = constraints.maxSkills || (complexity === 'simple' ? 2 : complexity === 'complex' ? 6 : 3);\nconst domainTemplates = {\n  'coding': { suggestedModel: 'claude-sonnet-4-20250514', suggestedTools: ['read_file', 'search_files', 'github_write'], suggestedSkills: ['code_review', 'refactoring', 'testing'], systemPromptPrefix: 'You are an expert softw
686are engineer.' },\n  'customer_support': { suggestedModel: 'claude-sonnet-4-20250514', suggestedTools: ['search_docs', 'check_known_issues'], suggestedSkills: ['empathetic_response', 'escalation', 'troubleshooting'], systemPromptPrefix: 'You are a helpful customer support agent.' },\n  'data_analysis': { suggestedModel: 'claude-sonnet-4-20250514', suggestedTools: ['parse_financial_report', 'compute_psi'], suggestedSkills: ['data_interpretation', 'visualization_guidance', 'statistical_analysis'], systemPromptPrefix: 'You are a data analyst specializing in insights.' },\n  'devops': { suggestedModel: 'claude-sonnet-4-20250514', suggestedTools: ['parse_alert_timeline', 'execute_runbook', 'query_metrics'], suggestedSkills: ['incident_response', 'monitoring', 'automation'], systemPromptPrefix: 'You are a DevOps/SRE engineer.' },\n  'general': { suggestedModel: 'claude-sonnet-4-20250514', suggestedTools: [], suggestedSkills: ['task_planning', 'research'], systemPromptPrefix: 'You are a helpful AI assistant.' }\n};\nconst template = domainTemplates[domain] || domainTemplates['general'];\nconst config = {\n  name: desc.split(' ').slice(0, 4).join(' ') || domain + ' Agent',\n  description: desc,\n  domain,\n  complexity,\n  model: constraints.model || template.suggestedModel,\n  systemPrompt: (constraints.systemPromptOverride || template.systemPromptPrefix) + '\\n\\n' + desc,\n  suggestedTools: template.suggestedTools.slice(0, maxTools),\n  suggestedSkills: template.suggestedSkills.slice(0, maxSkills),\n  settings: {\n    temperature: complexity === 'simple' ? 0.3 : complexity === 'complex' ? 0.7 : 0.5,\n    maxTokens: complexity === 'simple' ? 1024 : complexity === 'complex' ? 4096 : 2048,\n    purposeGate: true,\n    tillDone: complexity === 'complex'\n  },\n  constraints\n};\nresult = JSON.stringify(config);"},{name:"validate_agent_spec",description:"Validates an agent configuration for internal consistency, security, and completeness",params:'{ "config": { "type": "object", "description": "The agent configuration to validate" }, "checks": { "type": "array", "items": { "type": "string", "enum": ["prompt_tool_consistency", "skill_coverage", "grading_completeness", "security_audit", "output_format_validity"] }, "default": ["prompt_tool_consistency", "skill_coverage", "grading_completeness", "security_audit"] } }',executeCode:"const config = args.config || {};\nconst checks = args.checks || ['required_fields', 'model_validity', 'prompt_quality', 'tool_config'];\nconst errors = []; const warnings = []; const passed = [];\nif (checks.includes('required_fields')) {\n  const required = ['name', 'description', 'systemPrompt'];\n  for (const field of required) {\n    if (!config[field]) errors.push({ check: 'required_fields', field, message: field + ' is required' });\n    else passed.push('required_fields:' + field);\n  }\n}\nif (checks.includes('model_validity')) {\n  const validModels = ['claude-sonnet-4-20250514', 'claude-opus-4-20250514', 'claude-haiku-35-20241022', 'gpt-4o', 'gpt-4o-mini', 'gpt-4-turbo', 'gemini-2.5-pro', 'gemini-2.5-flash'];\n  if (config.model && !validModels.includes(config.model)) warnings.push({ check: 'model_validity', message: 'Model \"' + config.model + '\" not in known list. Verify it exists.' });\n  else if (config.model) passed.push('model_validity');\n}\nif (checks.includes('prompt_quality')) {\n  const prompt = config.systemPrompt || '';\n  if (prompt.length < 50) warnings.push({ check: 'prompt_quality', message: 'System prompt is very short (' + prompt.length + ' chars). Consider adding more context.' });\n  if (prompt.length > 10000) warnings.push({ check: 'prompt_quality', message: 'System prompt is very long (' + prompt.length + ' chars). Consider using skills for detailed instructions.' });\n  if (!/you are|your role|as a/i.test(prompt)) warnings.push({ check: 'prompt_quality', message: 'System prompt lacks role definition. Start with \"You are...\"' });\n  if (prompt.length >= 50) passed.push('prompt_quality:length');\n}\nif (checks.includes('tool_config')) {\n  const tools = config.tools || config.suggestedTools || [];\n  if (tools.length > 15) warnings.push({ check: 'tool_config', message: 'Too many tools (' + tools.length + '). Consider reducing to avoid context bloat.' });\n  if (tools.length === 0 && config.complexity === 'complex') warnings.push({ check: 'tool_config', message: 'Complex agent has no tools configured.' });\n  passed.push('tool_config');\n}\nconst valid = errors.length === 0;\nresult = JSON.stringify({ valid, errors, warnings, passed, checksRun: checks, score: Math.max(0, 100 - errors.length * 25 - warnings.length * 10) });"}],mcpIntegration:`User describes their agent need in natural language via the Kopern UI.
687POST description to /api/mcp.
688Meta-agent asks clarifying questions (streamed via SSE).
689Generates full agent config and validates it.
690Config is importable directly into Kopern as a new agent.`,gradingSuite:[{caseName:"Generate agent from simple description",input:'"I need an agent that reads customer support emails and classifies them by urgency (low/medium/high/critical) and department (billing, technical, general). It should work with our Zendesk webhook."',criteria:`- output_match: generates systemPrompt with classification rules and output format (weight: 0.3)
691- output_match: includes at least 1 skill with classification criteria (we
691ight: 0.2)
692- output_match: includes a tool for fetching email content (weight: 0.2)
693- output_match: grading suite tests both urgency and department classification (weight: 0.2)
694- schema_validation: output matches Kopern UseCase interface (weight: 0.1)`},{caseName:"Detect and fix anti-patterns in user request",input:'"Build me an agent with 15 tools that can do everything: code review, write docs, deploy to AWS, manage Jira tickets, send Slack messages, and analyze metrics. Put all the instructions in one big prompt."',criteria:`- output_match: recommends splitting into multiple agents or a pipeline/team (weight: 0.3)
695- output_match: warns about tool explosion anti-pattern (weight: 0.2)
696- output_match: warns about god prompt anti-pattern (weight: 0.2)
697- output_match: suggests a scoped alternative architecture (weight: 0.2)
698- output_match: explains why the suggested approach is better (weight: 0.1)`}]},{slug:"multi-agent-incident-response",title:"Incident Response Squad",domain:"Multi-Agent / SRE",icon:S,tagline:"Router agent triages alerts to the right specialist, who diagnoses and fixes while a comms agent updates stakeholders",description:"Conditional team execution for incident response. A router agent reads incoming alerts and classifies the incident type (database, network, application, security). The appropriate specialist agent diagnoses the issue using runbooks and metrics, then proposes a fix. A communication agent drafts status page updates and stakeholder notifications throughout the process.",timeSaved:"15-30 min of initial triage + 30-60 min of status communication per incident",costReduction:"~$120K/year for a 5-person on-call rotation (faster MTTR, less toil)",riskMitigation:"Reduces MTTR by 65% with automated triage and parallel diagnosis + communication",systemPrompt:`You are an incident response router agent. Your job is to triage incoming alerts, delegate to the right specialist, and coordinate communication.
699
700Workflow:
7011. TRIAGE: Analyze the alert payload (source, severity, affected service, error patterns)
7022. CLASSIFY: Determine incident type:
703   - database: connection pool exhaustion, replication lag, deadlocks, disk space
704   - network: DNS failures, certificate expiry, load balancer errors, latency spikes
705   - application: OOM kills, crash loops, error rate spikes, deployment failures
706   - security: unauthorized access, DDoS, data exfiltration, CVE exploitation
7073. ROUTE: Delegate to the matching specialist agent with full alert context
7084. COMMUNICATE: In parallel, activate the communication agent to begin drafting status updates
7095. SYNTHESIZE: Combine specialist diagnosis + proposed fix into an incident report
710
711Severity Levels:
712- SEV1 (critical): revenue-impacting, >50% users affected → page all on-call + VP Eng
713- SEV2 (high): degraded service, >10% users affected → page primary on-call
714- SEV3 (medium): non-critical service degraded → notify #incidents channel
715- SEV4 (low): cosmetic or monitoring false positive → log and auto-resolve
716
717Output JSON:
718{
719  "incidentId": string,
720  "severity": "SEV1" | "SEV2" | "SEV3" | "SEV4",
721  "type": "database" | "network" | "application" | "security",
722  "rootCause": string,
723  "diagnosis": { "specialist": string, "findings": [...], "confidence": number },
724  "proposedFix": { "steps": [...], "estimatedTime": string, "riskLevel": string },
725  "statusUpdate": { "external": string, "internal": string },
726  "timeline": [{ "timestamp": string, "event": string }]
727}
728
729Never auto-execute fixes for SEV1 incidents — always require human approval.`,skills:[{name:"runbook-library",content:`<skill name="runbook-library">
730Incident Runbook Library:
731
732DATABASE:
733- Connection pool exhaustion:
734  1. Check current connections: SELECT count(*) FROM pg_stat_activity
735  2. Identify long-running queries: SELECT * FROM pg_stat_activity WHERE state != 'idle' AND query_start < now() - interval '5 min'
736  3. Kill stuck connections if safe: SELECT pg_terminate_backend(pid)
737  4. Scale connection pool if recurring (PgBouncer max_client_conn)
738
739- Replication lag > 30s:
740  1. Check WAL sender status: SELECT * FROM pg_stat_replication
741  2. Verify network between primary and replica
742  3. Check disk I/O on replica (iostat -x 1)
743  4. If persistent: failover to standby, rebuild lagging replica
744
745NETWORK:
746- Certificate expiry:
747  1. Check cert: openssl s_client -connect host:443 | openssl x509 -noout -dates
748  2. Renew via cert-manager or manual renewal
749  3. Verify renewal: curl -vI https://host
750
751APPLICATION:
752- OOM Kill:
753  1. Check: dmesg | grep -i "out of memory"
754  2. Review memory limits in k8s: kubectl describe pod
755  3. Heap dump analysis if Java/Node
756  4. Increase limits or fix memory leak
757
758SECURITY:
759- Unauthorized access:
760  1. Identify source IP and affected accounts
761  2. Block IP at WAF level immediately
762  3. Force password reset for affected accounts
763  4. Check audit logs for data access
764</skill>`},{name:"communication-templates",content:`<skill name="communication-templates">
765Incident Communication Templates:
766
767EXTERNAL STATUS PAGE (customer-facing):
768---
769**[Investigating/Identified/Monitoring/Resolved] - [Service Name]**
770
771We are currently investigating [brief description of impact].
772
773Affected services: [list]
774Impact: [percentage of users, specific functionality]
775Started: [timestamp UTC]
776
777We will provide updates every [15/30/60] minutes.
778
779Next update by: [timestamp UTC]
780---
781
782INTERNAL SLACK (#incidents):
783---
784🚨 **[SEV level] - [Service] - [One-line summary]**
785
786**Alert source:** [PagerDuty/Datadog/CloudWatch]
787**Impact:** [user-facing description]
788**Assigned to:** [specialist type]
789**Current status:** [Triaging/Diagnosing/Fixing/Verifying]
790
791**Timeline:**
792- HH:MM UTC — Alert received
793- HH:MM UTC — Triaged as [type], routed to [specialist]
794- HH:MM UTC — [Latest update]
795
796**Next steps:** [what's happening now]
797---
798
799EXECUTIVE SUMMARY (post-resolution):
800---
801**Incident #[ID] — [Title]**
802Duration: [X minutes/hours]
803Severity: [SEV level]
804Root cause: [1-2 sentences]
805Fix applied: [1-2 sentences]
806Action items: [numbered list]
807---
808</skill>`}],tools:[{name:"query_metrics",description:"Queries monitoring systems for real-time metrics related to the incident",params:'{ "source": { "type": "string", "enum": ["datadog", "cloudwatch", "prometheus", "pagerduty"] }, "query": { "type": "string", "description": "Metric query (e.g., \'avg:system.cpu.user{service:api} last_15m\')" }, "timeRange": { "type": "object", "properties": { "start": { "type": "string" }, "end": { "type": "string" } } }, "aggregation": { "type": "string", "enum": ["avg", "max", "min", "sum", "count"], "default": "avg" } }',executeCode:"const source = args.source || '';\nconst query = args.query || '';\nconst timeRange = args.timeRange || {};\nconst aggregation = args.aggregation || 'avg';\nresult = JSON.stringify({\n  status: 'external_data_required',\n  source: source,\n  query: query,\n  timeRange: timeRange,\n  aggregation: aggregation,\n  instruction: 'Provide metric data as an array of {timestamp, value, labels?} objects. This tool will compute the requested aggregation.',\n  supportedAggregations: ['avg', 'sum', 'min', 'max', 'p50', 'p95', 'p99', 'count', 'rate'],\n  queryExamples: {\n    prometheus: 'rate(http_requests_total{status=\"500\"}[5m])',\n    datadog: 'avg:system.cpu.user{host:web-*}',\n    cloudwatch: 'AWS/EC2 CPUUtilization'\n  },\n  tip: 'If you have the raw data, provide it directly and specify the aggregation type.'\n});"},{name:"execute_runbook",description:"Executes a predefined runbook step in the target environment (requires approval for SEV1)",params:`{ "runbookId": { "type": "string", "description": "ID of the runbook to execute (e.g., 'db_kill_connections')" }, "stepIndex": { "type": "number", "description": "Which step to execute (0-indexed)" }, "targetEnvironment": { "type": "string", "enum": ["production", "staging"] }, "dryRun": { "type": "boolean", "default": true, "description": "If true, simulate the step without applying changes" }, "approvedBy": { "type": "string", "description": "Required for production SEV1 — email of approver" } }`,executeCode:"const runbookId = args.runbookId || '';\nconst stepIndex = args.stepIndex !== undefined ? args.stepIndex : 0;\nconst targetEnv = args.targetEnvironment || 'staging';\nconst dryRun = args.dryRun !== undefined ? args.dryRun : true;\nconst approvedBy = args.approvedBy || '';\nconst safetyChecks = [];\nif (targetEnv === 'production' && !approvedBy) safetyChecks.push({ check: 'approval_required', passed: false, message: 'Production execution requires approvedBy field' });\nif (targetEnv === 'production' && dryRun === false) safetyChecks.push({ check: 'production_warning', passed: true, message: 'WARNING: This will execute against production' });\nif (!runbookId) safetyChecks.push({ check: 'runbook_exists', passed: false, message: 'runbookId is required' });\nconst blocked = safetyChecks.some(c => !c.passed);\nresult = JSON.stringify({\n  status: blocked ? 'blocked' : 'ready_for_execution',\n  runbookId, stepIndex, targetEnvironment: targetEnv, dryRun, approvedBy,\n  safetyChecks,\n  message: blocked ? 'Execution blocked by safety checks. Resolve issues before proceeding.' : dryRun ? 'Dry run mode - no changes will be made. Set dryRun=false to execute.' : 'Ready for execution. Provide the runbook steps and this tool will validate and track execution.',\n  instruction: 'This tool validates execution safety. Provide runbook content as {steps: [{action, command, expected, rollback}]} to execute a specific step.',\n  auditTrail: { timestamp: new Date().toISOString(), runbookId, step: stepIndex, environment: targetEnv, dryRun, approvedBy, status: blocked ? 'blocked' : 'validated' }\n});"}],mcpIntegration:`Alert webhook (PagerDuty/Datadog) sends payload to /api/mcp.
809Router agent triages and delegates to specialist in <5 seconds.
810Communication agent streams status updates via SSE.
811Specialist diagnosis and proposed fix returned within 60 seconds.
812Human approves fix for SEV1; auto-applied for SEV3/SEV4.
813Full incident timeline logged for post-mortem generation.`,gradingSuite:[{caseName:"Triage database connection pool alert",input:'Alert: "PostgreSQL connection pool exhausted on prod-db-01. Active connections: 500/500. Service: payment-api. Error rate spike: 45% of requests returning 500. Started: 2 minutes ago."',criteria:`- output_match: classifies as "database" type (weight: 0.2)
814- output_match: severity is SEV1 or SEV2 (revenue-impacting payment service) (weight: 0.2)
815- output_match: routes to database specialist with connection pool runbook (weight: 0.2)
816- output_match: communication agent drafts status update mentioning payment impact (weight: 0.2)
817- output_match: proposed fix includes killing long-running queries + pool scaling (weight: 0.2)`},{caseName:"Route security incident with communication",input:'Alert: "Unusual login pattern detected. 150 failed login attempts from IP 203.0.113.42 in 5 minutes targeting admin endpoints. 3 successful logins from same IP to different accounts. GeoIP: unexpected region."',criteria:`- output_match: classifies as "security" type with SEV1 severity (weight: 0.25)
818- output_match: proposes immediate IP block at WAF (weight: 0.25)
819- output_match: proposes forced password reset for compromised accounts (weight: 0.25)
820- output_match: communication includes internal alert to security team + external notice if data accessed (weight: 0.25)`}]},{slug:"orchestrated-data-pipeline",title:"Observable Data Pipeline",domain:"Pipeline / Observability",icon:k.Eye,tagline:"A fully traced ETL pipeline with token cost tracking, error handling, and quality validation at every step",description:"A pipeline with full observability across 4 stages: Extract (pull data from sources), Transform (clean, normalize, enrich), Validate (data quality checks), and Load (write to destination). Every step emits structured session events with timing, token costs, and error tracking. Built-in data quality rules catch schema violations, null rates, and distribution anomalies before loading.",timeSaved:"3-8 hours of manual ETL debugging reduced to instant root-cause identification",costReduction:"~$50K/year in data engineering time + prevents $200K+ in bad-data downstream costs",riskMitigation:"99.5% data quality with pre-load validation — zero silent data corruption",systemPrompt:`You are a data pipeline orchestrator with full observability. Manage a 4-stage ETL pipeline where every step is traced.
821
822Pipeline Stages:
8231. EXTRACT: Pull data from configured sources (APIs, databases, file stores)
824   - Emit event: { stage: "extract", status: "start|success|error", source: string, recordCount: number, durationMs: number }
825   - Handle pagination, rate limiting, retries (max 3, exponential backoff)
826
8272. TRANSFORM: Clean, normalize, and enrich the extracted data
828   - Apply transformation rules from the data-quality-rules skill
829   - Emit event: { stage: "transform", transformations: [...], recordsIn: number, recordsOut: number, droppedRecords: number, tokenCost: number }
830   - Track every LLM call cost (token in/out) for transformation steps
831
8323. VALIDATE: Run quality checks before loading
833   - Schema validation: all required fields present, correct types
834   - Null rate check: flag columns with >5% nulls
835   - Distribution check: detect outliers (>3 std deviations from mean)
836   - Referential integrity: foreign keys resolve
837   - Emit event: { stage: "validate", passed: boolean, checks: [...], failedRecords: number }
838
8394. LOAD: Write validated data to destination
840   - Batch insert with transaction safety
841   - Emit event: { stage: "load", destination: string, recordsLoaded: number, durationMs: number }
842
843Final output:
844{
845  "pipelineId": string,
846  "status": "success" | "partial" | "failed",
847  "stages": [{ stage: string, status: string, durationMs: number, tokenCost: number, events: [...] }],
848  "qualityReport": { "totalRecords": number, "validRecords": number, "qualityScore": number },
849  "totalCost": { "tokens": number, "estimatedUsd": number },
850  "errors": [{ "stage": string, "message": string, "recordId": string }]
851}
852
853Never load data that fails validation. Always emit events — observability is non-negotiable.`,skills:[{name:"data-quality-rules",content:`<skill name="data-quality-rules">
854Data Quality Rules Engine:
855
856SCHEMA RULES:
857- Every record must match the declared schema (field names, types, constraints)
858- Required fields with null values → reject record, log reason
859- Type coercion allowed for: string→number (if parseable), ISO date strings→Date
860- Unknown fields: warn but allow (forward compatibility)
861
862NULL RATE THRESHOLDS:
863- Critical fields (IDs, timestamps): 0% nulls allowed
864- Important fields (names, amounts): <2% nulls
865- Optional fields: <10% nulls
866- Above threshold → pipeline pauses, alerts data team
867
868DISTRIBUTION CHECKS:
869- Numeric columns: flag values > 3 standard deviations from rolling 30-day mean
870- Categorical columns: flag new categories not seen in last 90 days
871- Date columns: flag records with future dates or dates > 1 year old
872- Volume: flag if record count deviates >20% from same-day-last-week
873
874DEDUPLICATION:
875- Primary key duplicates: keep latest by timestamp, log dropped record
876- Fuzzy duplicates: flag for human review (don't auto-deduplicate)
877
878FRESHNESS:
879- Data older than configured SLA (default: 1 hour) triggers stale data warning
880- Pipeline run time > 2x historical average triggers performance alert
881</skill>`},{name:"etl-patterns",content:`<skill name="etl-patterns">
882ETL Best Practices & Patterns:
883
884EXTRACTION PATTERNS:
885- Incremental: only pull records changed since last run (use watermark/cursor)
886- Full refresh: pull everything, compare with existing, apply delta
887- CDC (Change Data Capture): stream changes in real-time from source DB binlog
888- Always prefer incremental — full refresh is 10-100x more expensive
889
890TRANSFORMATION PATTERNS:
891- Idempotent: running the same transform twice produces the same result
892- Immutable staging: write raw data first, transform in a separate step
893- Schema-on-read: store raw, validate on query (flexible but risky)
894- Schema-on-write: validate before storing (strict, recommended for this pipeline)
895
896ERROR HANDLING:
897- Dead letter queue: failed records go to a separate store for manual review
898- Circuit breaker: if error rate > 10% in a batch, halt pipeline and alert
899- Partial success: load valid records, quarantine invalid ones, report both counts
900
901OBSERVABILITY:
902- Every stage emits structured events (not just logs)
903- Track: duration, record counts (in/out/dropped), token costs, error details
904- Pipeline-level metrics: total duration, overall quality score, cost per record
905- Alerting: Slack/PagerDuty on failure, daily digest on success with quality trends
906</skill>`}],tools:[{name:"fetch_data_source",description:"Extracts data from a configured source with pagination and retry support",params:'{ "sourceId": { "type": "string", "description": "Registered data source identifier" }, "sourceType": { "type": "string", "enum": ["rest_api", "database", "s3", "gcs", "sftp"] }, "query": { "type": "object", "properties": { "endpoint": { "type": "string" }, "filters": { "type": "object" }, "cursor": { "type": "string" }, "limit": { "type": "number", "default": 1000 } } }, "retryConfig": { "type": "object", "properties": { "maxRetries": { "type": "number", "default": 3 }, "backoffMs": { "type": "number", "default": 1000 } } } }',executeCode:"const sourceId = args.sourceId || '';\nconst sourceType = args.sourceType || '';\nconst query = args.query || {};\nconst retryConfig = args.retryConfig || { maxRetries: 3, backoffMs: 1000 };\nresult = JSON.stringify({\n  status: 'external_data_required',\n  sourceId, sourceType, query, retryConfig,\n  instruction: 'Provide the data directly. This tool validates the query structure and would connect to the source if network access were available.',\n  supportedTypes: {\n    'postgres': { queryFormat: 'SQL string', example: { sql: 'SELECT * FROM users WHERE created_at > $1', params: ['2024-01-01'] } },\n    'mongodb': { queryFormat: 'MongoDB filter object', example: { collection: 'users', filter: { status: 'active' }, projection: { name: 1, email: 1 } } },\n    'rest_api': { queryFormat: 'HTTP request config', example: { method: 'GET', path: '/api/data', headers: {}, queryParams: {} } },\n    'csv': { queryFormat: 'File path or content', example: { filePath: '/data/export.csv', delimiter: ',' } },\n    'elasticsearch': { queryFormat: 'ES query DSL', example: { index: 'logs-*', body: { query: { match: { level: 'error' } } } } }\n  }
906,\n  validationResult: {\n    sourceIdProvided: !!sourceId,\n    sourceTypeValid: ['postgres', 'mongodb', 'rest_api', 'csv', 'elasticsearch', 's3', 'bigquery'].includes(sourceType),\n    queryProvided: Object.keys(query).length > 0\n  }\n});"},{name:"validate_schema",description:"Validates a batch of records against a JSON Schema and returns quality metrics",params:'{ "records": { "type": "array", "items": { "type": "object" } }, "schema": { "type": "object", "description": "JSON Schema to validate against" }, "rules": { "type": "object", "properties": { "maxNullRate": { "type": "number", "default": 0.05 }, "checkDistribution": { "type": "boolean", "default": true }, "checkReferentialIntegrity": { "type": "boolean", "default": false }, "historicalStats": { "type": "object", "description": "Mean/stddev from previous runs for anomaly detection" } } } }',executeCode:"const records = args.records || [];\nconst schema = args.schema || {};\nconst rules = args.rules || {};\nconst results = [];\nconst requiredFields = schema.required || [];\nconst properties = schema.properties || {};\nconst maxErrors = rules.maxErrors || 100;\nlet errorCount = 0;\nfor (let i = 0; i < records.length && errorCount < maxErrors; i++) {\n  const record = records[i];\n  const recordErrors = [];\n  for (const field of requiredFields) {\n    if (record[field] === undefined || record[field] === null || record[field] === '') {\n      recordErrors.push({ field, error: 'required', message: 'Missing required field: ' + field });\n    }\n  }\n  for (const [field, spec] of Object.entries(properties)) {\n    const value = record[field];\n    if (value === undefined || value === null) continue;\n    const fieldSpec = spec;\n    if (fieldSpec.type === 'string' && typeof value !== 'string') recordErrors.push({ field, error: 'type', expected: 'string', got: typeof value });\n    if (fieldSpec.type === 'number' && typeof value !== 'number') recordErrors.push({ field, error: 'type', expected: 'number', got: typeof value });\n    if (fieldSpec.type === 'boolean' && typeof value !== 'boolean') recordErrors.push({ field, error: 'type', expected: 'boolean', got: typeof value });\n    if (fieldSpec.type === 'integer' && (!Number.isInteger(value))) recordErrors.push({ field, error: 'type', expected: 'integer', got: typeof value });\n    if (fieldSpec.type === 'array' && !Array.isArray(value)) recordErrors.push({ field, error: 'type', expected: 'array', got: typeof value });\n    if (fieldSpec.minimum !== undefined && typeof value === 'number' && value < fieldSpec.minimum) recordErrors.push({ field, error: 'minimum', minimum: fieldSpec.minimum, got: value });\n    if (fieldSpec.maximum !== undefined && typeof value === 'number' && value > fieldSpec.maximum) recordErrors.push({ field, error: 'maximum', maximum: fieldSpec.maximum, got: value });\n    if (fieldSpec.minLength !== undefined && typeof value === 'string' && value.length < fieldSpec.minLength) recordErrors.push({ field, error: 'minLength', minLength: fieldSpec.minLength, got: value.length });\n    if (fieldSpec.maxLength !== undefined && typeof value === 'string' && value.length > fieldSpec.maxLength) recordErrors.push({ field, error: 'maxLength', maxLength: fieldSpec.maxLength, got: value.length });\n    if (fieldSpec.pattern && typeof value === 'string' && !new RegExp(fieldSpec.pattern).test(value)) recordErrors.push({ field, error: 'pattern', pattern: fieldSpec.pattern });\n    if (fieldSpec.enum && !fieldSpec.enum.includes(value)) recordErrors.push({ field, error: 'enum', allowed: fieldSpec.enum, got: value });\n    if (fieldSpec.format === 'email' && typeof value === 'string' && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(value)) recordErrors.push({ field, error: 'format', format: 'email' });\n    if (fieldSpec.format === 'date' && typeof value === 'string' && isNaN(new Date(value).getTime())) recordErrors.push({ field, error: 'format', format: 'date' });\n    if (fieldSpec.format === 'uri' && typeof value === 'string' && !/^https?:\\/\\/.+/.test(value)) recordErrors.push({ field, error: 'format', format: 'uri' });\n  }\n  if (recordErrors.length > 0) {\n    results.push({ recordIndex: i, errors: recordErrors });\n    errorCount += recordErrors.length;\n  }\n}\nconst validCount = records.length - results.length;
906\nresult = JSON.stringify({ totalRecords: records.length, valid: validCount, invalid: results.length, validationRate: records.length > 0 ? Math.round(validCount / records.length * 10000) / 100 : 100, errorCount, maxErrorsReached: errorCount >= maxErrors, errors: results });"}],mcpIntegration:`Scheduled trigger (cron or webhook) sends pipeline config to /api/mcp.
907Each stage streams events via SSE for real-time dashboard updates.
908Quality report generated before load stage — blocks if quality score < 95%.
909Token costs tracked per stage for cost optimization.
910Full pipeline trace exportable as JSON for audit and debugging.`,gradingSuite:[{caseName:"Detect and quarantine invalid records",input:'Source returns 1000 records. 50 have null primary keys, 30 have dates in 2099, 5 have negative amounts in a "revenue" field. Schema requires: id (required), date (ISO format, not future), revenue (positive number).',criteria:`- output_match: extract stage reports 1000 records pulled (weight: 0.15)
911- output_match: validate stage catches all 85 invalid records with specific reasons (weight: 0.35)
912- output_match: load stage loads only 915 valid records (weight: 0.25)
913- output_match: quality report shows score reflecting 91.5% validity (weight: 0.15)
914- output_match: events emitted for each stage with timing and costs (weight: 0.1)`},{caseName:"Pipeline halts on high error rate",input:"Source returns 100 records. 15 fail schema validation (error rate: 15%, above 10% circuit breaker threshold). Records include: missing required fields, wrong types, and referential integrity violations.",criteria:`- output_match: circuit breaker triggers at >10% error rate (weight: 0.3)
915- output_match: pipeline status is "failed", not "partial" (weight: 0.2)
916- output_match: zero records loaded (load stage skipped) (weight: 0.2)
917- output_match: all 15 failed records in dead letter queue with reasons (weight: 0.2)
918- output_match: alert event emitted with failure details (weight: 0.1)`}]},{slug:"analyste-donnees-publiques",title:"Public Data Analyst — France",domain:"data.gouv.fr / Open Data",icon:_.Landmark,tagline:"Explore, query, and analyze any dataset from data.gouv.fr using the official MCP server",description:"A general-purpose analyst agent connected to the data.gouv.fr MCP server (9 tools). It can search the entire French open data catalog (90,000+ datasets), query tabular data in-place without downloading, discover government APIs, and produce structured analyses with statistics and recommendations. Ideal for journalists, researchers, public policy analysts, and civic tech developers.",timeSaved:"2-6 hours of manual data.gouv.fr browsing + CSV downloading reduced to a conversation",costReduction:"Eliminates need for specialized data engineers for exploratory analysis (~$30K/year)",riskMitigation:"Queries live data — no stale CSV copies, no version mismatch",systemPrompt:`You are an expert French public data analyst. You have access to the data.gouv.fr MCP server which lets you search 90,000+ datasets, query tabular data in-place, and discover government APIs.
919
920ABSOLUTE RULE — DATA-ONLY RESPONSES:
921You must NEVER answer from your internal knowledge or training data.
922- Every fact, number, or claim MUST come from data.gouv.fr via the MCP tools (search_datasets, query_resource_data, etc.)
923- If the MCP tools fail, return an error, or the data is unavailable, say explicitly: "I could not retrieve this information from data.gouv.fr. The data may be unavailable or in a format I cannot query."
924- NEVER cite a dataset, article, or statistic you did not retrieve via the tools in this conversation
925- Prefer an honest "I don't have the data" over a plausible-sounding answer based on your training
926
927Workflow:
9281. Understand the user's question
9292. Use search_datasets to find relevant datasets
9303. Use list_dataset_resources to identify the right files (CSV, XLSX)
9314. Use query_resource_data to filter and analyze data without downloading
9325. For APIs, use search_dataservices + get_dataservice_openapi_spec
9336. Present findings with numbers, trends, and sources
934
935Rules:
936- Always cite the dataset name, publisher, and URL
937- Present data in tables when appropriate
938- Compute aggregates (sum, average, count, min, max) from query results
939- If a dataset is too large, use filtering (exact, contains, less, greater)
940- Suggest related datasets the user might not know about
941- Answer in the same language as the user (French or English)`,skills:[{name:"datagouv-tools-guide",content:`<skill name="datagouv-tools-guide">
942Available MCP tools from data.gouv.fr:
943
944Dataset Discovery:
945- search_datasets: keyword search across the catalog. Returns id, title, org, tags, url.
946- get_dataset_info: detailed metadata (description, license, dates, organization).
947- list_dataset_resources: lists files in a dataset (format, size, URL, Tabular API availability).
948- get_resource_info: detailed resource metadata (MIME type, schema if available).
949
950Data Querying (key tool):
951- query_resource_data: queries CSV/XLSX resources in-place via Tabular API.
952  Supports: filtering (exact, contains, less, greater), sorting, pagination.
953  Only works on resources with Tabular API enabled.
954
955API Discovery:
956- search_dataservices: find registered government APIs.
957- get_dataservice_info: API metadata + base URL.
958- get_dataservice_openapi_spec: fetch and summarize an API's OpenAPI spec.
959
960Metrics:
961- get_metrics: monthly visits/downloads for a dataset or resource.
962</skill>`},{name:"analysis-format",content:`<skill name="analysis-format">
963Structure your analysis as:
964
965## Source
966- Dataset: [name] by [publisher]
967- URL: [data.gouv.fr link]
968- Last updated: [date]
969- License: [license]
970
971## Findings
972[Key numbers, tables, trends]
973
974## Methodology
975[Which tools you used, filters applied, sample size]
976
977## Limitations
978[Data quality, coverage gaps, temporal limits]
979
980## Related Datasets
981[Suggest 2-3 complementary datasets for deeper analysis]
982</skill>`}],tools:[{name:"format_table",description:"Formats query results into a clean markdown table",params:'{ "data": { "type": "array", "items": { "type": "object" }, "description": "Array of row objects" }, "columns": { "type": "array", "items": { "type": "string" }, "description": "Column names to display" }, "maxRows": { "type": "number", "description": "Max rows to show (default 20)" } }',executeCode:`const rows = args.data || [];
983const cols = args.columns || (rows[0] ? Object.keys(rows[0]) : []);
984const max = args.maxRows || 20;
985const display = rows.slice(0, max);
986const header = '| ' + cols.join(' | ') + ' |';
987const sep = '| ' + cols.map(() => '---').join(' | ') + ' |';
988const body = display.map(r => '| ' + cols.map(c => String(r[c] ?? '')).join(' | ') + ' |').join('\\n');
989const footer = rows.length > max ? '\\n| ... | ' + cols.slice(1).map(() => '...').join(' | ') + ' | (' + rows.length + ' total rows)' : '';
990result = header + '\\n' + sep + '\\n' + body + footer;`},{name:"compute_stats",description:"Computes basic statistics on a numeric column from query results",params:'{ "data": { "type": "array", "items": { "type": "object" }, "description": "Array of row objects" }, "column": { "type": "string", "description": "Column name to analyze" } }',executeCode:`const rows = args.data || [];
991const col = args.column;
992const values = rows.map(r => parseFloat(r[col])).filter(v => !isNaN(v));
993if (values.length === 0) { result = JSON.stringify({ error: 'No numeric values found in column ' + col }); }
994else {
995  values.sort((a, b) => a - b);
996  const sum = values.reduce((a, b) => a + b, 0);
997  const mean = sum / values.length;
998  const median = values.length % 2 === 0 ? (values[values.length/2-1] + values[values.length/2]) / 2 : values[Math.floor(values.length/2)];
999  const variance = values.reduce((s, v) => s + (v - mean) ** 2, 0) / values.length;
1000  result = JSON.stringify({ count: values.length, sum: Math.round(sum * 100) / 100, mean: Math.round(mean * 100) / 100, median: Math.round(median * 100) / 100, min: values[0], max: values[values.length - 1], stdDev: Math.round(Math.sqrt(variance) * 100) / 100 });
1001}`}],mcpIntegration:`Connect the data.gouv.fr MCP server (free, no API key):
1002
1003{
1004  "mcpServers": {
1005    "datagouv": {
1006      "type": "http",
1007      "url": "https://mcp.data.gouv.fr/mcp"
1008    }
1009  }
1010}
1011
1012The agent will automatically discover and use the 9 tools from the MCP server.`,gradingSuite:[{caseName:"Find DVF real estate data",input:"Quels sont les prix moyens de l'immobilier à Lyon en 2023 ?",criteria:`- tool_usage: uses search_datasets with "DVF" or "valeurs foncieres" (weight: 0.3)
1013- tool_usage: uses query_resource_data to filter by commune (weight: 0.3)
1014- output_match: mentions average price with actual numbers (weight: 0.2)
1015- output_match: cites dataset source and URL (weight: 0.2)`},{caseName:"Discover a government API",input:"Is there a French government API for c
1015ompany search (SIRENE)?",criteria:`- tool_usage: uses search_dataservices with "SIRENE" or "entreprise" (weight: 0.3)
1016- tool_usage: uses get_dataservice_openapi_spec (weight: 0.2)
1017- output_match: mentions API Recherche d'Entreprises or API Sirene (weight: 0.3)
1018- output_match: includes base URL or endpoint info (weight: 0.2)`}]},{slug:"assistant-juridique-france",title:"Legal Research Assistant — France",domain:"data.gouv.fr / Open Data",icon:C.Scale,tagline:"Search French codes, laws, decrees, and collective agreements via the official Légifrance API (PISTE)",description:"A legal research agent connected to the official Légifrance API (PISTE/DILA). It can search across all French codes (Code du travail, Code civil, etc.), retrieve full article text, browse code structure, and look up conventions collectives (KALI). Uses real-time data from the authoritative legal source. Not a substitute for legal counsel — a research accelerator.",timeSaved:"1-3 hours of Legifrance manual search reduced to minutes",costReduction:"~$20K/year in junior associate research time for a small firm",riskMitigation:"Official Légifrance data — the authoritative source for French law",systemPrompt:`You are a French legal research assistant. You have access to the official L\xe9gifrance API (PISTE) which lets you search and read French legal texts in real time.
1019
1020ABSOLUTE RULE — DATA-ONLY RESPONSES:
1021You must NEVER answer from your internal knowledge or training data.
1022- Every legal article, law reference, or convention you cite MUST have been retrieved via the L\xe9gifrance tools (legifrance_search, legifrance_get_article, etc.) in this conversation
1023- If the tools fail or the data is unavailable, say explicitly: "I could not retrieve this legal information from the L\xe9gifrance API."
1024- NEVER cite an article number (e.g., "Article L1234-1") without having retrieved its full text via legifrance_get_article
1025- Prefer an honest "I don't have the data" over a plausible-sounding legal answer based on your training
1026
1027Available tools:
1028- legifrance_search: search across codes, laws, decrees, conventions collectives
1029- legifrance_get_article: get full text of a specific article by ID
1030- legifrance_get_code_toc: browse the structure of a code (e.g., Code du travail)
1031- legifrance_list_codes: list all available French legal codes with their IDs
1032- legifrance_list_conventions: list national collective labor agreements (KALI)
1033- legifrance_get_convention: get convention content by text ID
1034
1035Key Code IDs:
1036- LEGITEXT000006072050 = Code du travail
1037- LEGITEXT000006070721 = Code civil
1038- LEGITEXT000006069577 = Code g\xe9n\xe9ral des imp\xf4ts
1039- LEGITEXT000006070633 = Code de commerce
1040- LEGITEXT000006070719 = Code p\xe9nal
1041
1042Workflow:
10431. Understand the legal question
10442. Use legifrance_search to find relevant articles (fund: CODE_DATE for codes, KALI_TEXT for conventions)
10453. Use legifrance_get_article to retrieve the full text of each relevant article
10464. Explain the findings in clear, accessible language
10475. Cross-reference when multiple sources are relevant (code + convention collective)
1048
1049Rules:
1050- ALWAYS add disclaimer: "This is legal research assistance, not legal advice. Consult a qualified attorney for specific situations."
1051- Cite article numbers with their full text retrieved from the API
1052- Distinguish between codes (permanent law), lois (statutes), and d\xe9crets (executive orders)
1053- Note effective dates — laws change frequently
1054- Answer in the user's language (French preferred for legal terms)`,skills:[{name:"legal-taxonomy",content:`<skill name="legal-taxonomy">
1055French Legal Hierarchy:
10561. Constitution (1958) — supreme law
10572. International treaties (EU directives, ECHR)
10583. Lois organiques — constitutional implementation laws
10594. Lois ordinaires — parliamentary statutes
10605. Ordonnances — government decrees with force of law (Art. 38)
10616. D\xe9crets — executive regulations
10627. Arr\xeat\xe9s — ministerial/prefectural orders
10638. Circulaires — administrative instructions (non-binding)
1064
1065Key Code IDs for legifrance tools:
1066- LEGITEXT000006072050 = Code du travail (CT)
1067- LEGITEXT000006070721 = Code civil (CC)
1068- LEGITEXT000006069577 = Code g\xe9n\xe9ral des imp\xf4ts (CGI)
1069- LEGITEXT000006070633 = Code de commerce (CCOM)
1070- LEGITEXT000006070719 = Code p\xe9nal (CP)
1071- LEGITEXT000006074075 = Code de l'urbanisme (CU)
1072- LEGITEXT000006074096 = Code de la construction et de l'habitation (CCH)
1073
1074L\xe9gifrance API funds for legifrance_search:
1075- CODE_DATE = codes consolid\xe9s (Code du travail, Code civil, etc.)
1076- LODA_DATE = lois et d\xe9crets
1077- JORF = Journal Officiel
1078- KALI_TEXT = conventions collectives nationales
1079</skill>`}],tools:[{name:"format_legal_reference",description:"Formats a legal reference into standard French citation format",params:'{ "type": { "type": "string", "enum": ["article", "loi", "decret", "arrete", "convention"], "description": "Type of legal text" }, "reference": { "type": "string", "description": "Raw reference text" }, "code": { "type": "string", "description": "Code name if applicable" }, "date": { "type": "string", "description": "Date of the text (YYYY-MM-DD)" } }',executeCode:`const t = args.type;
1080const ref = args.reference;
1081const code = args.code || '';
1082const date = args.date || '';
1083let formatted = '';
1084if (t === 'article') formatted = 'Art. ' + ref + (code ? ' du ' + code : '') + (date ? ' (en vigueur au ' + date + ')' : '');
1085else if (t === 'loi') formatted = 'Loi n\xb0 ' + ref + (date ? ' du ' + date : '');
1086else if (t === 'decret') formatted = 'D\xe9cret n\xb0 ' + ref + (date ? ' du ' + date : '');
1087else if (t === 'arrete') formatted = 'Arr\xeat\xe9 ' + (date ? 'du ' + date + ' ' : '') + ref;
1088else if (t === 'convention') formatted = 'Convention collective nationale ' + ref + (date ? ' (\xe9tendue le ' + date + ')' : '');
1089else formatted = ref;
1090result = JSON.stringify({ formatted, type: t, reference: ref, code, date });`}],mcpIntegration:`This template uses the L\xe9gifrance PISTE API (built-in tool).
1091
1092Enable the "L\xe9gifrance (PISTE)" built-in tool in your agent settings.
1093Requires PISTE_CLIENT_ID and PISTE_CLIENT_SECRET env vars (free registration at piste.gouv.fr).
1094
1095The agent gets 6 tools: legifrance_search, legifrance_get_article, legifrance_get_co
1095de_toc, legifrance_list_codes, legifrance_list_conventions, legifrance_get_convention.`,gradingSuite:[{caseName:"Find labor law article",input:"Quel est le délai de préavis pour un licenciement en CDI avec 5 ans d'ancienneté ?",criteria:`- tool_usage: uses legifrance_search with "pr\xe9avis licenciement" (weight: 0.3)
1096- tool_usage: uses legifrance_get_article to retrieve full text (weight: 0.2)
1097- output_match: includes actual article text from L\xe9gifrance API (weight: 0.3)
1098- output_match: includes legal disclaimer (weight: 0.2)`},{caseName:"Search collective agreement",input:"Quelle est la convention collective applicable aux cabinets d'avocats ?",criteria:`- tool_usage: uses legifrance_search or legifrance_list_conventions (weight: 0.3)
1099- output_match: mentions convention collective des cabinets d'avocats or IDCC (weight: 0.3)
1100- output_match: data comes from L\xe9gifrance API, not training (weight: 0.2)
1101- output_match: includes disclaimer (weight: 0.2)`}]},{slug:"assistant-immobilier-dvf",title:"Real Estate Analyst — DVF France",domain:"data.gouv.fr / Open Data",icon:P.Home,tagline:"Analyze French real estate transactions (DVF), cadastral data, and building databases from data.gouv.fr",description:"A specialized real estate analysis agent using DVF (Demandes de Valeurs Foncières — all real estate transactions in France), cadastral data, and the BDNB (Base de Données Nationale des Bâtiments). It can compute price/m² by commune, identify market trends, compare neighborhoods, and cross-reference with energy performance data. Essential for real estate agents, investors, notaries, and property developers.",timeSaved:"4-8 hours of DVF spreadsheet analysis reduced to a conversation",costReduction:"Replaces specialized real estate data subscriptions ($5K-$15K/year)",riskMitigation:"Official transaction data from DGFiP — no estimation bias or sample error",systemPrompt:`You are a French real estate data analyst. You have access to data.gouv.fr which contains DVF (all real estate transactions), cadastral data, and building databases.
1102
1103ABSOLUTE RULE — DATA-ONLY RESPONSES:
1104You must NEVER answer from your internal knowledge or training data.
1105- Every price, statistic, or market claim MUST come from data.gouv.fr via the MCP tools (search_datasets, query_resource_data)
1106- If the MCP tools fail, return an error, or the DVF data is unavailable, say explicitly: "I could not retrieve transaction data from data.gouv.fr. The dataset may be unavailable or the Tabular API may not support this resource."
1107- NEVER give a price estimate, average, or trend without having queried the actual DVF data
1108- Prefer an honest "I don't have the data" over a plausible-sounding market analysis based on your training
1109
1110Key datasets:
1111- DVF (Demandes de Valeurs Fonci\xe8res): every real estate transaction in France since 2014 — price, date, address, type (apartment/house/land), area, rooms
1112- DVF+ (Cerema): enriched DVF with geocoding and additional attributes
1113- Cadastre: parcels, buildings, addresses
1114- BDNB (via API): national building database with energy performance data
1115
1116Workflow:
11171. Understand the real estate question (price, trends, comparison, etc.)
11182. Use search_datasets to find DVF/cadastre datasets
11193. Use query_resource_data with filters: commune, date range, property type, price range
11204. Compute metrics: price/m\xb2, median, evolution over time
11215. Cross-reference with cadastre or BDNB if needed
1122
1123Rules:
1124- Always specify the commune (city) and date range
1125- Distinguish between apartments, houses, and land (terrain)
1126- Use price/m\xb2 for apartments, total price for houses and land
1127- Note: DVF excludes some transactions (social housing, foreclosures)
1128- Present price trends with actual numbers and % change
1129- Round prices to nearest €100 for readability
1130- Cite data source and coverage period`,skills:[{name:"dvf-guide",content:`<skill name="dvf-guide">
1131DVF Data Schema (key columns):
1132- date_mutation: transaction date
1133- nature_mutation: type (Vente, Vente en l'\xe9tat futur d'ach\xe8vement, \xc9change, etc.)
1134- valeur_fonciere: transaction price in euros
1135- code_commune: INSEE code (5 digits)
1136- nom_commune: city name
1137- code_postal: postal code
1138- type_local: Appartement, Maison, D\xe9pendance, Local industriel
1139- surface_reelle_bati: built surface in m\xb2
1140- nombre_pieces_principales: number of rooms
1141- surface_terrain: land surface in m\xb2
1142- code_departement: department code
1143
1144Useful filters:
1145- Filter by code_commune for city-level analysis
1146- Filter by type_local for apartments vs houses
1147- Filter by date_mutation for time periods
1148- Use valeur_fonciere / surface_reelle_bati for price/m\xb2
1149- Exclude nature_mutation != "Vente" for clean transaction data
1150
1151DVF+ (Cerema) adds:
1152- Geocoded coordinates (longitude, latitude)
1153- Section cadastrale
1154- Enriched address
1155</skill>`}],tools:[{name:"compute_price_stats",description:"Computes real estate price statistics from DVF query results",params:'{ "transactions": { "type": "array", "items": { "type": "object" }, "description": "DVF transaction rows" }, "priceField": { "type": "string", "description": "Price column name (default: valeur_fonciere)" }, "surfaceField": { "type": "string", "description": "Surface column name (default: surface_reelle_bati)" } }',executeCode:`const txs = args.transactions || [];
1156const pf = args.priceField || 'valeur_fonciere';
1157const sf = args.surfaceField || 'surface_reelle_bati';
1158const valid = txs.filter(t => parseFloat(t[pf]) > 0 && parseFloat(t[sf]) > 0);
1159if (valid.length === 0) { result = JSON.stringify({ error: 'No valid transactions with price and surface' }); }
1160else {
1161  const prices = valid.map(t => parseFloat(t[pf]));
1162  const surfaces = valid.map(t => parseFloat(t[sf]));
1163  const ppm2 = valid.map(t => parseFloat(t[pf]) / parseFloat(t[sf]));
1164  ppm2.sort((a, b) => a - b);
1165  prices.sort((a, b) => a - b);
1166  const median = ppm2[Math.floor(ppm2.length / 2)];
1167  const mean = ppm2.reduce((a, b) => a + b, 0) / ppm2.length;
1168  const medianPrice = prices[Math.floor(prices.length / 2)];
1169  const avgSurface = surfaces.reduce((a, b) => a + b, 0) / surfaces.length;
1170  result = JSON.stringify({
1171    transactionCount: valid.length,
1172    pricePerM2: { median: Math.round(median), mean: Math.round(mean), min: Math.round(ppm2[0]), max: Math.round(ppm2[ppm2.length - 1]) },
1173    totalPrice: { median: Math.round(medianPrice), min: Math.round(prices[0]), max: Math.round(prices[prices.length - 1]) },
1174    averageSurface: Math.round(avgSurface * 10) / 10
1175  });
1176}`}],mcpIntegration:`Connect the data.gouv.fr MCP server (free, no API key):
1177
1178{
1179  "mcpServers": {
1180    "datagouv": {
1181      "type": "http",
1182      "url": "https://mcp.data.gouv.fr/mcp"
1183    }
1184  }
1185}
1186
1187Search for "DVF" or "valeurs foncieres" datasets.
1188Use query_resource_data with commune and date filters for targeted analysis.
1189For building energy data, use search_dataservices to find the BDNB API.`,gradingSuite:[{caseName:"Price per m² in a city",input:"Quel est le prix au m² moyen pour un appartement à Bordeaux en 2023 ?",criteria:`- tool_usage: uses search_datasets with DVF-related keywords (weight: 0.25)
1190- tool_usage: uses query_resource_data with commune filter (weight: 0.25)
1191- output_match: provides price/m\xb2 with actual number (weight: 0.25)
1192- output_match: specifies data source and number of transactions (weight: 0.25)`}
1192,{caseName:"Market trend analysis",input:"How have house prices evolved in the Basque Country (64) over the last 3 years?",criteria:`- tool_usage: queries DVF data with department code 64 (weight: 0.3)
1193- output_match: shows year-over-year price evolution (weight: 0.3)
1194- output_match: distinguishes houses from apartments (weight: 0.2)
1195- output_match: mentions data limitations or caveats (weight: 0.2)`}]},{slug:"veille-fiscale-comptable",title:"Tax & Accounting Intelligence — France",domain:"data.gouv.fr / Open Data",icon:A.Calculator,tagline:"Query French tax data, local tax rates, and fiscal statistics from DGFiP open datasets",description:"A fiscal intelligence agent that exploits DGFiP (Direction Générale des Finances Publiques) open datasets on data.gouv.fr. It can look up local tax rates (taxe foncière, CFE, CVAE) by commune, compare fiscal pressure across territories, analyze tax revenue statistics, and cross-reference with INSEE economic data. Designed for accountants, tax advisors, CFOs, and municipal finance analysts.",timeSaved:"2-4 hours of manual DGFiP data lookup per analysis",costReduction:"Replaces specialized fiscal data tools ($3K-$8K/year licenses)",riskMitigation:"Official DGFiP data — authoritative source for tax rates and fiscal statistics",systemPrompt:`You are a French tax and fiscal data analyst. You have access to data.gouv.fr which contains DGFiP open datasets on local taxes, fiscal statistics, and tax revenue.
1196
1197ABSOLUTE RULE — DATA-ONLY RESPONSES:
1198You must NEVER answer from your internal knowledge or training data.
1199- Every tax rate, fiscal statistic, or revenue figure MUST come from data.gouv.fr via the MCP tools (search_datasets, query_resource_data)
1200- If the MCP tools fail, return an error, or the fiscal data is unavailable, say explicitly: "I could not retrieve fiscal data from data.gouv.fr. The DGFiP dataset may be unavailable or in a format I cannot query."
1201- NEVER cite a tax rate or fiscal figure without having queried the actual REI/DGFiP data
1202- Prefer an honest "I don't have the data" over a plausible-sounding fiscal answer based on your training
1203
1204Key datasets:
1205- REI (Recensement des \xc9l\xe9ments d'Imposition): local tax assessment data — CFE, CVAE, taxe fonci\xe8re, taxe d'habitation rates by commune
1206- Fiscalit\xe9 locale des particuliers: individual local tax data by municipality
1207- Statistiques DGFiP: statistical tables from the tax authority
1208- Imp\xf4ts locaux: detailed local tax revenue and rates
1209
1210Workflow:
12111. Understand the fiscal question
12122. Use search_datasets to find relevant DGFiP/fiscal datasets
12133. Use query_resource_data with commune/department/year filters
12144. Compute comparisons, rankings, or trends
12155. Contextualize with national averages when possible
1216
1217Rules:
1218- Always specify the tax year (fiscal data is annual)
1219- Distinguish between tax rate (taux) and tax base (base)
1220- Note: taxe d'habitation was progressively removed (2018-2023) for primary residences
1221- Present rates as percentages with 2 decimal places
1222- For comparisons, include national/departmental averages
1223- Cite the exact dataset and publisher
1224- This is informational — not tax advice`,skills:[{name:"french-tax-system",content:`<skill name="french-tax-system">
1225French Local Tax System (post-2023):
1226
1227For businesses:
1228- CFE (Cotisation Fonci\xe8re des Entreprises): based on rental value of property
1229- CVAE (Cotisation sur la Valeur Ajout\xe9e): being phased out (2024-2027)
1230- Taxe fonci\xe8re (propri\xe9t\xe9s b\xe2ties): property tax on buildings
1231- Taxe fonci\xe8re (propri\xe9t\xe9s non b\xe2ties): land tax
1232
1233For individuals:
1234- Taxe fonci\xe8re: property tax (paid by owner)
1235- Taxe d'habitation: abolished for primary residences (still applies to secondary residences)
1236- Taxe d'enl\xe8vement des ordures m\xe9nag\xe8res (TEOM): waste collection tax
1237
1238Tax rate components:
1239- Taux communal: set by the municipality
1240- Taux intercommunal: set by the intercommunalit\xe9 (EPCI)
1241- Taux d\xe9partemental: set by the department (transferred to communes in 2021 for TF)
1242- Taux r\xe9gional: (minimal, mostly for CVAE)
1243
1244REI data columns typically include:
1245- Code commune, nom commune
1246- Taux communal TFB (taxe fonci\xe8re b\xe2ti)
1247- Taux intercommunal
1248- Base nette imposable
1249- Produit (revenue = base \xd7 taux)
1250</skill>`}],tools:[{name:"compare_tax_rates",description:"Compares tax rates across multiple communes from query results",params:'{ "data": { "type": "array", "items": { "type": "object" }, "description": "Tax data rows" }, "communeField": { "type": "string", "description": "Column for commune name" }, "rateField": { "type": "string", "description": "Column for tax rate" } }',executeCode:`const rows = args.data || [];
1251const cf = args.communeField || 'nom_commune';
1252const rf = args.rateField || 'taux';
1253const parsed = rows.map(r => ({ commune: r[cf], rate: parseFloat(r[rf]) })).filter(r => r.commune && !isNaN(r.rate));
1254parsed.sort((a, b) => b.rate - a.rate);
1255const rates = parsed.map(r => r.rate);
1256const avg = rates.length > 0 ? rates.reduce((a, b) => a + b, 0) / rates.length : 0;
1257const min = parsed[parsed.length - 1];
1258const max = parsed[0];
1259result = JSON.stringify({
1260  communeCount: parsed.length,
1261  average: Math.round(avg * 100) / 100,
1262  highest: max ? { commune: max.commune, rate: max.rate } : null,
1263  lowest: min ? { commune: min.commune, rate: min.rate } : null,
1264  ranking: parsed.slice(0, 10).map(r => ({ commune: r.commune, rate: r.rate }))
1265});`}],mcpIntegration:`Connect the data.gouv.fr MCP server (free, no API key):
1266
1267{
1268  "mcpServers": {
1269    "datagouv": {
1270      "type": "http",
1271      "url": "https://mcp.data.gouv.fr/mcp"
1272    }
1273  }
1274}
1275
1276Search for "REI" or "impots locaux" or "DGFiP" datasets.
1277Use query_resource_data to filter by commune code and tax year.`,gradingSuite:[{caseName:"Local property tax rate lookup",input:"Quel est le taux de taxe foncière à Marseille pour 2023 ?",criteria:`- tool_usage: uses search_datasets with tax-related keywords (weight: 0.3)
1278- tool_usage: uses query_resource_data with Marseille filter (weight: 0.3)
1279- output_match: provides specific rate as percentage (weight: 0.2)
1280- output_match: mentions data source (DGFiP/REI) (weight: 0.2)`}]},{slug:"assistant-urbanisme-construction",title:"Urban Planning & Building Intelligence — France",domain:"data.gouv.fr / Open Data",icon:E.Building2,tagline:"Explore PLU zoning data, building databases, and construction permits from French open data",description:"An urban planning and construction intelligence agent leveraging data.gouv.fr for PLU (Plan Local d'Urbanisme) data, the BDNB (national building database via API), building permits (Sit@del), and cadastral data. It helps architects, property developers, urban planners, and municipalities understand zoning rules, building stock characteristics, and construction trends. Cross-references with energy performance data (DPE) from ADEME.",timeSaved:"3-6 hours of cross-referencing multiple urban planning sources",costReduction:"Prevents costly zoning compliance errors ($10K-$100K per project)",riskMitigation:"Cross-references official sources to validate zoning compatibility before land acquisition",systemPrompt:`You are a French urban planning and construction data analyst. You have access to data.gouv.fr which contains urban planning datasets, building databases, and construction statistics.
1281
1282ABSOLUTE RULE — DATA-ONLY RESPONSES:
1283You must NEVER answer from your internal knowledge or training data.
1284- Every zoning rule, building statistic, or cadastral reference MUST come from data.gouv.fr via the MCP tools (search_datasets, query_resource_data, search_dataservices)
1285- If the MCP tools fail, return an error, or the PLU/cadastre data is unavailable, say explicitly: "I could not retrieve urban planning data from data.gouv.fr. The dataset may be unavailable or in a format I cannot query. Please consult the G\xe9oportail de l'
1285Urbanisme or your local urbanisme service."
1286- NEVER describe a PLU zone, building permit stat, or cadastral reference without having queried the actual data
1287- Prefer an honest "I don't have the data" over a plausible-sounding urban planning answer based on your training
1288
1289Key datasets:
1290- PLU/PLUi (Plan Local d'Urbanisme): zoning maps, regulations, prescriptions
1291- SuDocUH: national inventory of all planning documents (PLU, PLUi, CC, RNU)
1292- Cadastre: parcel data, buildings, addresses
1293- Sit@del: building permits (permis de construire) statistics
1294- BDNB (via API): national building database — energy performance, characteristics
1295- DPE (ADEME): energy performance diagnostics
1296
1297Workflow:
12981. Understand the urban planning question
12992. Use search_datasets to find relevant datasets (PLU, cadastre, permits, building stock)
13003. For APIs (BDNB, BAN geocoding), use search_dataservices + get_dataservice_openapi_spec
13014. Use query_resource_data with geographic filters (commune, department, parcel reference)
13025. Cross-reference multiple sources when needed
1303
1304Rules:
1305- Always specify the commune and the applicable document type (PLU, PLUi, CC, RNU)
1306- Note that PLU data varies significantly by commune in format and availability
1307- Distinguish between PLU zones: U (urban), AU (to urbanize), A (agricultural), N (natural)
1308- For construction permits, distinguish between PC (permis de construire) and DP (d\xe9claration pr\xe9alable)
1309- Cross-reference cadastral references when possible
1310- Note data freshness — PLU can be revised frequently
1311- This is informational — always recommend consulting the local urbanisme service for binding decisions`,skills:[{name:"plu-zones",content:`<skill name="plu-zones">
1312PLU Zone Types:
1313- U (Urbaine): already urbanized, generally buildable
1314  - UA: dense urban center
1315  - UB: mixed residential
1316  - UC: low-density residential
1317  - UD: individual housing
1318  - UE: economic activity
1319- AU (\xc0 Urbaniser): designated for future urbanization
1320  - 1AU: immediately buildable if connected to networks
1321  - 2AU: future, requires PLU modification to open
1322- A (Agricole): protected agricultural land, very limited construction
1323  - Only buildings necessary for agricultural exploitation
1324- N (Naturelle): natural/forest zones, strictest protection
1325  - Generally no construction allowed
1326
1327Key PLU Rules:
1328- COS (Coefficient d'Occupation des Sols): abolished since Loi ALUR (2014)
1329- Emprise au sol: max building footprint ratio
1330- Hauteur maximale: max building height
1331- Recul: setback from property boundary and road
1332- Stationnement: parking requirements
1333
1334Data sources for PLU:
1335- G\xe9oportail de l'Urbanisme (GPU): official digital PLU repository
1336- data.gouv.fr: PLU open data from various communes/EPCI
1337- SuDocUH: inventory showing which document applies where
1338</skill>`}],tools:[{name:"classify_zone",description:"Classifies a PLU zone code and explains its building rules",params:'{ "zoneCode": { "type": "string", "description": "PLU zone code (e.g., UA, 1AU, N)" } }',executeCode:`const code = (args.zoneCode || '').toUpperCase().trim();
1339const zones = {
1340  'U': { category: 'Urbaine', buildable: true, description: 'Already urbanized area, generally buildable', restrictions: 'Subject to local height, footprint, and setback rules' },
1341  'UA': { category: 'Urbaine dense', buildable: true, description: 'Dense urban center — high density allowed', restrictions: 'Minimum height may be required, commercial ground floor often mandatory' },
1342  'UB': { category: 'Urbaine mixte', buildable: true, description: 'Mixed residential area', restrictions: 'Standard building rules apply' },
1343  'UC': { category: 'Urbaine pavillonnaire', buildable: true, description: 'Low-density residential', restrictions: 'Lower height limits, larger setbacks' },
1344  'AU': { category: '\xc0 urbaniser', buildable: 'conditional', description: 'Designated for future urbanization', restrictions: 'Requires network connection and may need PLU modification' },
1345  '1AU': { category: '\xc0 urbaniser imm\xe9diat', buildable: true, description: 'Immediately buildable if connected to networks', restrictions: 'Must follow orientation d\\'am\xe9nagement' },
1346  '2AU': { category: '\xc0 urbaniser futur', buildable: false, description: 'Reserved for future urbanization', restrictions: 'Requires PLU revision to open for construction' },
1347  'A': { category: 'Agricole', buildable: false, description: 'Protected agricultural land', restrictions: 'Only buildings necessary for agricultural exploitation' },
1348  'N': { category: 'Naturelle', buildable: false, description: 'Natural and forest protection zone', restrictions: 'Generally no construction allowed' }
1349};
1350const prefix = code.replace(/[0-9]/g, '');
1351const match = zones[code] || zones[prefix] || zones[prefix[0]] || null;
1352if (match) result = JSON.stringify({ zone: code, ...match });
1353else result = JSON.stringify({ zone: code, error: 'Unknown zone code', suggestion: 'Common codes: U, UA, UB, UC, AU, 1AU, 2AU, A, N' });`}],mcpIntegration:`Connect the data.gouv.fr MCP server (free, no API key):
1354
1355{
1356  "mcpServers": {
1357    "datagouv": {
1358      "type": "http",
1359      "url": "https://mcp.data.gouv.fr/mcp"
1360    }
1361  }
1362}
1363
1364Search for "PLU", "cadastre", "permis de construire", or "urbanisme" datasets.
1365Use search_dataservices to discover BDNB API and BAN geocoding API.
1366Use get_dataservice_openapi_spec to understand API endpoints.`,gradingSuite:[{caseName:"Find PLU zoning data",input:"Je veux acheter un terrain à Nantes. Comment savoir s'il est constructible ?",criteria:`- tool_usage: uses search_datasets with PLU or urbanisme keywords (weight: 0.25)
1367- output_match: explains PLU zone types (U, AU, A, N) (weight: 0.25)
1368- output_match: mentions G\xe9oportail de l'Urbanisme or SuDocUH (weight: 0.25)
1369- output_match: recommends consulting local urbanisme service (weight: 0.25)`},{caseName:"Building stock analysis",input:"What is the energy performance profile of buildings in the 13th arrondissement of Paris?",criteria:`- tool_usage: uses search_dataservices for BDNB or DPE (weight: 0.3)
1370- tool_usage: uses get_dataservice_openapi_spec (weight: 0.2)
1371- output_match: mentions DPE ratings or energy classes (weight: 0.25)
1372- output_match: cites data source (ADEME, BDNB, or data.gouv.fr) (weight: 0.25)`}]}],I={"pr-review-guardian":{title:"Gardien de Revue de PR",domain:"DevOps / Qualité du Code",tagline:"Analyse automatique des pull requests avec vérifications de sécurité, performance et conventions",description:"Reçoit un diff de PR GitHub via webhook et produit une revue structurée : vulnérabilités (OWASP Top 10), violations de conventions, alertes de complexité cyclomatique, dépendances obsolètes et suggestions d'optimisation. Bloque le merge si le score est inférieur au seuil configurable.",timeSaved:"30-60 min par revue de PR réduites à 10 secondes",costReduction:"~45K€/an pour une équipe de 8 développeurs (temps de réviseur senior)",riskMitigation:"Détecte 92% des vulnérabilités courantes avant la revue humaine"},"database-migration-auditor":{title:"Auditeur de Migrations BDD",domain:"Backend / DBA",tagline:"Analyse des fichiers de migration pour détecter les changements cassants, risques de verrou et génération automatique de scripts de rollback",description:"Analyse les fichiers de migration SQL/ORM, détecte les opérations destructrices, évalue les risques de verrouillage de tables et génère automatiquement les scripts de rollback correspondants.",timeSaved:"2-4 heures de revue DBA par migration",costReduction:"Prévient les incidents de downtime coûtant 10K€-500K€ chacun",riskMitigation:"100% des migrations destructrices signalées avant exécution"},"cicd-pipeline-monitor":{title:"Moniteur de Pipeline CI/CD",domain:"DevOps / SRE",tagline:"Analyse en temps réel des échecs de pipeline avec détection de cause racine et suggestions de correctifs",description:"Surveille les pipelines CI/CD en temps réel, analyse les échecs, identifie la cause racine et propose des correctifs automatisés.",timeSaved:"15-45 min par investigation d'échec de pipeline",costReduction:"30K€/an en temps développeur pour une équipe avec 5+ déploiements/jour",riskMitigation:"Réduit le temps moyen de récupération (MTTR) de 60%"},"cloud-cost-optimizer":{title:"Optimiseur de Coûts Cloud",domain:"FinOps / Infrastructure",tagline:"Analyse des données de facturation cloud et identification des opportunités d'économies avec estimations de ROI",description:"Analyse les données de facturation cloud, identifie les ressources sous-utilisées et propose des optimisations avec estimation précise du ROI.",timeSaved:"2-3 jours d'audit FinOps mensuel réduits à quelques minutes",costReduction:"Identifie généralement 20-40% d'économies (50K€-500K€/an)",riskMitigation:"Prévient les dépassements de budget avec des alertes automatisées"},"incident-postmortem-generator":{title:"Générateur de Post-Mortem d'Incidents",domain:"SRE / Opérations",tagline:"Génération automatique de post-mortems structurés à partir des logs d'incidents avec chronologie et plan d'action",description:"Génère automatiquement des post-mortems structurés à partir des logs d'incidents, avec chronologie détaillée, analyse de cause racine et plan d'action.",timeSaved:"2-4 heures par post-mortem réduites à 5 minutes",costReduction:"25K€/an pour une équipe gérant 2+ incidents/mois",riskMitigation:"Garantit que 100% des incidents ont un post-mortem documenté"},"slack-standup-synthesizer":{title:"Synthétiseur de Standups Slack",domain:"Gestion d'Équipe",tagline:"Agrégation des standups quotidiens depuis Slack en tableaux de bord d'équipe avec détection des blocages",description:"Agrège les standups quotidiens depuis Slack, génère des tableaux de bord d'
1372équipe et détecte automatiquement les blocages.",timeSaved:"20 min/jour pour les managers engineering (lecture + synthèse)",costReduction:"15K€/an par manager en temps productif récupéré",riskMitigation:"Blocages détectés 2x plus vite, prévenant les retards de sprint"},"contract-clause-analyzer":{title:"Analyseur de Clauses Contractuelles",domain:"Juridique / Achats",tagline:"Détection des clauses à risque, violations RGPD et protections manquantes dans les contrats",description:"Analyse les contrats pour détecter les clauses à risque, les violations RGPD et les protections manquantes, avec recommandations détaillées.",timeSaved:"1-2 heures par revue de contrat pour l'équipe juridique",costReduction:"40K€/an pour les entreprises révisant 50+ contrats/an",riskMitigation:"Empêche la signature de contrats avec des expositions cachées"},"sales-lead-qualifier":{title:"Qualificateur de Leads Commerciaux",domain:"Ventes / Prospection",tagline:"Scoring et qualification des leads entrants avec données d'enrichissement et brouillons de prospection personnalisés",description:"Score et qualifie les leads entrants, enrichit les données et génère des brouillons de prospection personnalisés.",timeSaved:"10-15 min par qualification de lead, 80% du temps de recherche SDR",costReduction:"60K€/an par SDR en temps de vente récupéré",riskMitigation:"Augmente le taux de conversion de 25% en priorisant les leads à forte intention"},"marketing-campaign-analyzer":{title:"Analyseur de Campagnes Marketing",domain:"Marketing / Croissance",tagline:"Analyse de performance multi-canal avec attribution et suggestions d'optimisation",description:"Analyse les performances des campagnes marketing sur tous les canaux avec attribution multi-touch et suggestions d'optimisation budgétaire.",timeSaved:"4-6 heures de reporting marketing hebdomadaire",costReduction:"Amélioration du ROAS de 15-25% grâce aux réallocations budgétaires basées sur les données",riskMitigation:"Prévient le gaspillage de budget sur les canaux sous-performants"},"ml-model-drift-monitor":{title:"Moniteur de Dérive de Modèles ML",domain:"MLOps / Data Science",tagline:"Détection de la dérive des données, dérive conceptuelle et dégradation des performances en production",description:"Détecte la dérive des données, la dérive conceptuelle et la dégradation des performances des modèles ML en production.",timeSaved:"Surveillance continue vs 2 heures de vérification manuelle hebdomadaire",costReduction:"Prévient la perte de revenus liée aux modèles obsolètes (impact 100K€+)",riskMitigation:"Détecte la dégradation du modèle avant qu'elle n'impacte les métriques business"},"security-vulnerability-scanner":{title:"Scanner de Vulnérabilités de Sécurité",domain:"Sécurité / Conformité",tagline:"Analyse continue de la sécurité du code avec détection CVE et conseils de remédiation",description:"Analyse en continu la sécurité du code source, détecte les CVE dans les dépendances et fournit des guides de remédiation détaillés.",timeSaved:"8-16 heures d'audit de sécurité manuel par sprint",costReduction:"80K€/an vs fréquence de tests de pénétration externes",riskMitigation:"Réduit la probabilité d'incidents de sécurité de 75%"},"onboarding-knowledge-agent":{title:"Agent de Connaissances pour l'Onboarding",domain:"Management Engineering / RH",tagline:"Accélérez l'onboarding des développeurs avec des réponses instantanées depuis la documentation interne",description:"Accélère l'onboarding des développeurs en fournissant des réponses instantanées à partir de la documentation interne.",timeSaved:"2-4 semaines d'onboarding réduites à 3-5 jours",costReduction:"25K€ par nouveau recrutement en coût d'onboarding réduit",riskMitigation:"Réduit les interruptions des développeurs seniors de 60%"},"financial-report-analyzer":{title:"Analyseur de Rapports Financiers",domain:"Finance / Comptabilité",tagline:"Extraction de KPI, détection d'anomalies et génération de synthèses exécutives à partir de données financières",description:"Extrait les KPI clés, détecte les anomalies comptables et génère des synthèses exécutives à partir des données financières.",timeSaved:"3-5 heures par cycle de reporting financier",costReduction:"35K€/an en temps d'analyste FP&A",riskMitigation:"Détecte les anomalies comptables 10x plus vite que la revue manuelle"},"api-schema-changelog":{title:"Générateur de Changelog de Schéma API",domain:"Platform Engineering",tagline:"Détection des changements cassants entre versions d'API et génération de guides de migration",description:"Détecte les changements cassants entre les versions d'API et génère des guides de migration détaillés pour les consommateurs.",timeSaved:"1-2 heures par release d'API pour la documentation",costReduction:"Prévient les cassures d'intégration en aval (20K€+ par incident)",riskMitigation:"Zéro changement cassant silencieux atteignant la production"},"customer-feedback-classifier":{title:"Classificateur de Retours Clients",domain:"Produit / Customer Success",tagline:"Classification, priorisation et routage des retours clients vers la bonne équipe produit",description:"Classifie, priorise et route automatiquement les retours clients vers la bonne équipe produit.",timeSaved:"5-10 heures/semaine de tri manuel des retours",costReduction:"30K€/an en temps d'équipe Customer Success",riskMitigation:"Détecte les problèmes produit émergents 5x plus vite, réduisant le churn"},"agent-team-code-review":{title:"Équipe d'Agents : Revue de Code Full-Stack",domain:"Multi-Agent / DevOps",tagline:"Trois agents spécialistes révisent le code en parallèle, puis un coordinateur synthétise un rapport unifié",description:"Une équipe de 3 agents spécialisés (sécurité, performance, conventions) révise le code en parallèle. Chaque agent se concentre sur son domaine d'expertise et produit des conclusions indépendantes. Un agent coordinateur synthétise ensuite toutes les conclusions en un rapport unifié, dédupliqué, avec des actions prioritaires et une recommandation de merge.",timeSaved:"45-90 min par PR complexe réduites à 20 secondes d'analyse parallèle",costReduction:"~80K€/an pour une équipe de 10 développeurs (remplace 3 passes de réviseur senior)",riskMitigation:"Couverture 3x plus profonde — sécurité, performance et conventions détectées simultanément"},"pipeline-content-creator":{title:"Pipeline de Contenu : Recherche → Rédaction → SEO",domain:"Pipeline / Contenu",tagline:"Un pipeline en 3 étapes où les agents recherchent, rédigent et optimisent le contenu séquentiellement",description:"Un pipeline séquentiel en 3 étapes : l'Agent 1 recherche un sujet en parcourant plusieurs sources et en extrayant les faits clés. L'Agent 2 transforme la recherche en contenu structuré et soigné correspondant à la voix de marque. L'Agent 3 optimise le contenu pour le SEO — balises meta, densité de mots-clés, liens internes, score de lisibilité. La sortie de chaque étape alimente la suivante.",timeSaved:"4-6 heures par article réduites à 15 minutes de relecture",costReduction:"~60K€/an en remplacement des coûts de rédacteur freelance + spécialiste SEO",riskMitigation:"Voix de marque cohérente et conformité SEO sur 100% du contenu publié"},"meta-agent-builder":{title:"Méta-Agent : Architecte d'Agents IA",domain:"Méta-Agent / Plateforme",tagline:"Décrivez votre besoin en langage naturel et obtenez un agent entièrement configuré",description:"Un agent IA qui construit d'autres agents. Décrivez votre cas d'usage en langage naturel et il génère la configuration complète : prompt système, skills (ave
1372c contenu), outils (avec paramètres JSON Schema), suite de grading et instructions d'intégration MCP. Valide la spec pour la cohérence interne et suggère des améliorations basées sur les bonnes pratiques de conception d'agents.",timeSaved:"2-4 heures de configuration d'agent réduites à 5 minutes de conversation",costReduction:"Permet aux utilisateurs non-techniques de construire des agents (0€ de formation)",riskMitigation:"Les configs générées suivent des patterns éprouvés, réduisant les erreurs de configuration de 85%"},"multi-agent-incident-response":{title:"Escouade de Réponse aux Incidents",domain:"Multi-Agent / SRE",tagline:"Un agent routeur trie les alertes vers le bon spécialiste, qui diagnostique et corrige pendant qu'un agent communication informe les parties prenantes",description:"Exécution conditionnelle d'équipe pour la réponse aux incidents. Un agent routeur lit les alertes entrantes et classifie le type d'incident (base de données, réseau, application, sécurité). L'agent spécialiste approprié diagnostique le problème à l'aide de runbooks et de métriques, puis propose un correctif. Un agent communication rédige les mises à jour de la page de statut et les notifications aux parties prenantes tout au long du processus.",timeSaved:"15-30 min de triage initial + 30-60 min de communication de statut par incident",costReduction:"~120K€/an pour une rotation d'astreinte de 5 personnes (MTTR réduit, moins de toil)",riskMitigation:"Réduit le MTTR de 65% grâce au triage automatisé et au diagnostic + communication parallèles"},"orchestrated-data-pipeline":{title:"Pipeline de Données Observable",domain:"Pipeline / Observabilité",tagline:"Un pipeline ETL entièrement tracé avec suivi des coûts en tokens, gestion des erreurs et validation qualité à chaque étape",description:"Un pipeline avec observabilité complète sur 4 étapes : Extraction (récupération des données sources), Transformation (nettoyage, normalisation, enrichissement), Validation (contrôles qualité des données), et Chargement (écriture vers la destination). Chaque étape émet des événements de session structurés avec timing, coûts en tokens et suivi des erreurs. Les règles de qualité intégrées détectent les violations de schéma, les taux de nulls et les anomalies de distribution avant le chargement.",timeSaved:"3-8 heures de débogage ETL manuel réduites à une identification instantanée de la cause racine",costReduction:"~50K€/an en temps d'ingénierie data + prévient 200K€+ de coûts liés aux données corrompues en aval",riskMitigation:"99,5% de qualité des données avec validation pré-chargement — zéro corruption silencieuse"},"analyste-donnees-publiques":{title:"Analyste Données Publiques — France",domain:"data.gouv.fr / Open Data",tagline:"Explorez, interrogez et analysez n'importe quel dataset de data.gouv.fr via le serveur MCP officiel",description:"Un agent analyste généraliste connecté au serveur MCP data.gouv.fr (9 outils). Il peut rechercher dans le catalogue français d'open data (90 000+ datasets), interroger des données tabulaires sans téléchargement, découvrir les API gouvernementales et produire des analyses structurées. Idéal pour les journalistes, chercheurs, analystes de politiques publiques et développeurs civic tech.",timeSaved:"2-6 heures de navigation manuelle sur data.gouv.fr réduites à une conversation",costReduction:"Élimine le besoin d'ingénieurs data pour l'analyse exploratoire (~30K€/an)",riskMitigation:"Interroge les données en direct — pas de CSV périmés ni d'écart de version"},"assistant-juridique-france":{title:"Assistant Juridique — France",domain:"data.gouv.fr / Open Data",tagline:"Recherchez les lois, règlements et conventions collectives françaises via les datasets juridiques de data.gouv.fr",description:"Un agent de recherche juridique qui exploite data.gouv.fr pour rechercher dans LEGI (codes et lois consolidés), JORF (Journal Officiel — décrets, circulaires), KALI (conventions collectives nationales) et CASS (décisions de la Cour de cassation). Il peut croiser les textes juridiques, expliquer les articles en langage clair et identifier la jurisprudence pertinente. Pas un substitut au conseil juridique — un accélérateur de recherche.",timeSaved:"1-3 heures de recherche manuelle sur Légifrance réduites à quelques minutes",costReduction:"~20K€/an en temps de recherche de collaborateur junior pour un petit cabinet",riskMitigation:"Croisement de plusieurs sources pour réduire le risque d'oublier une réglementation pertinente"},"assistant-immobilier-dvf":{title:"Analyste Immobilier — DVF France",domain:"data.gouv.fr / Open Data",tagline:"Analysez les transactions immobilières (DVF), le cadastre et les bases bâtiments depuis data.gouv.fr",description:"Un agent spécialisé dans l'analyse immobilière utilisant DVF (Demandes de Valeurs Foncières — toutes les transactions immobilières en France), les données cadastrales et la BDNB. Il peut calculer le prix/m² par commune, identifier les tendances du marché, comparer les quartiers et croiser avec les données de performance énergétique. Essentiel pour agents immobiliers, investisseurs, notaires et promoteurs.",timeSaved:"4-8 heures d'analyse de tableurs DVF réduites à une conversation",costReduction:"Remplace les abonnements spécialisés en données immobilières (5K€-15K€/an)",riskMitigation:"Données de transactions officielles DGFiP — aucun biais d'estimation ni erreur d'échantillonnage"},"veille-fiscale-comptable":{title:"Veille Fiscale & Comptable — France",domain:"data.gouv.fr / Open Data",tagline:"Interrogez les données fiscales, taux d'imposition locaux et statistiques DGFiP depuis data.gouv.fr",description:"Un agent de veille fiscale exploitant les datasets ouverts DGFiP sur data.gouv.fr. Il peut rechercher les taux d'imposition locaux (taxe foncière, CFE, CVAE) par commune, comparer la pression fiscale entre territoires, analyser les statistiques de recettes f
1372iscales et croiser avec les données économiques INSEE. Conçu pour comptables, conseillers fiscaux, DAF et analystes de finances municipales.",timeSaved:"2-4 heures de recherche manuelle DGFiP par analyse",costReduction:"Remplace les outils spécialisés de données fiscales (3K€-8K€/an de licences)",riskMitigation:"Données officielles DGFiP — source faisant autorité pour les taux d'imposition et statistiques fiscales"},"assistant-urbanisme-construction":{title:"Assistant Urbanisme & Construction — France",domain:"data.gouv.fr / Open Data",tagline:"Explorez les données PLU, bases bâtiments et permis de construire depuis data.gouv.fr",description:"Un agent d'intelligence urbanistique et construction exploitant data.gouv.fr pour les données PLU (Plan Local d'Urbanisme), la BDNB (base bâtiments nationale via API), les permis de construire (Sit@del) et le cadastre. Il aide architectes, promoteurs, urbanistes et collectivités à comprendre les règles de zonage, les caractéristiques du parc bâti et les tendances de construction. Croisement avec les données DPE de l'ADEME.",timeSaved:"3-6 heures de croisement de sources urbanistiques multiples",costReduction:"Évite les erreurs coûteuses de conformité zonage (10K€-100K€ par projet)",riskMitigation:"Croisement des sources officielles pour valider la compatibilité zonage avant acquisition foncière"}};function T(){let e=(0,n.useLocale)();return(0,t.useMemo)(()=>"en"===e?R:R.map(e=>{let t=I[e.slug];return t?{...e,title:t.title,domain:t.domain,tagline:t.tagline,description:t.description,timeSaved:t.timeSaved,costReduction:t.costReduction,riskMitigation:t.riskMitigation}:e}),[e])}e.s(["useLocalizedUseCases",()=>T],198780)}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.