diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..bcc67c6 --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", + "name": "carta-plugins", + "description": "Official Carta plugins for Claude Code", + "owner": { + "name": "Carta Engineering", + "email": "engineering@carta.com" + }, + "plugins": [ + { + "name": "carta-cap-table", + "source": "./plugins/carta-cap-table", + "description": "Carta Cap Table plugin — skills and hooks for querying cap tables, grants, SAFEs, 409A valuations, waterfall scenarios, and more", + "category": "productivity" + } + ] +} diff --git a/plugins/carta-cap-table/.claude-plugin/plugin.json b/plugins/carta-cap-table/.claude-plugin/plugin.json new file mode 100644 index 0000000..e550f3f --- /dev/null +++ b/plugins/carta-cap-table/.claude-plugin/plugin.json @@ -0,0 +1,25 @@ +{ + "name": "carta-cap-table", + "displayName": "Carta Cap Table", + "version": "0.6.1", + "description": "Carta Cap Table plugin — skills and hooks for querying cap tables, grants, SAFEs, 409A valuations, waterfall scenarios, and more", + "author": { + "name": "Carta Engineering", + "email": "engineering@carta.com" + }, + "homepage": "https://github.com/carta/plugins", + "repository": "https://github.com/carta/plugins", + "keywords": [ + "cap_table", + "mcp", + "carta", + "equity", + "grants" + ], + "mcpServers": { + "carta": { + "type": "http", + "url": "https://mcp.app.carta.com/mcp" + } + } +} diff --git a/plugins/carta-cap-table/README.md b/plugins/carta-cap-table/README.md new file mode 100644 index 0000000..b30319c --- /dev/null +++ b/plugins/carta-cap-table/README.md @@ -0,0 +1,53 @@ +# Carta Cap Table + +Claude Code plugin that gives Claude access to Carta cap table data. + +## How it works + +This plugin provides **skills** that teach Claude how to use the Carta MCP server effectively — querying cap tables, modeling rounds, detecting portfolio alerts, and more. + +The MCP server handles authentication via OAuth: +1. On first use, Claude opens a browser to the Carta login page +2. You log in with your Carta credentials +3. Carta issues an access token that Claude uses for all subsequent requests + +## Installation + +Install from the `carta/plugins` marketplace: + +``` +/plugin install carta-cap-table@carta-plugins +``` + +This registers the Carta MCP server (`https://mcp.app.carta.com/mcp`) and loads all skills automatically. + +After installing, restart Claude Code and run `/mcp` to complete OAuth authentication. + +## Try it out + +- "What Carta cap table data do I have access to?" +- "Show me the ownership breakdown for [company]" +- "Who are the stakeholders in [company]?" +- "What SAFEs are outstanding for [company]?" +- "When does the 409A expire for [company]?" +- "Model a Series B at $80M pre-money with a $20M raise" + +## Skills + +| Skill | Description | +|-------|-------------| +| `client-triggers` | Surface time-based BD triggers across the portfolio. Use when asked about client outreach, which clients closed a round recently, stale cap tables, pending grants, tombstones, weekly deals, or BD triggers. | +| `conversion-calculator` | Calculate SAFE and convertible note conversion into equity. Use when asked about SAFE conversion, note conversion, conversion shares, or how instruments convert in a round. | +| `discover-commands` | Find the right carta-cap-table command when no other skill matches. Use when unsure which command to call, exploring available data, or when the user's request doesn't match a specific skill. | +| `grant-vesting` | Fetch vesting schedule for a specific option grant. Use when asked about vesting details, cliff dates, vesting progress, or unvested shares for a particular grant. | +| `list-convertible-notes` | Fetch all convertible instruments (SAFEs and convertible debt) for a company. Use when asked about convertible notes, SAFEs, convertible debt, note terms, caps, discounts, or maturity dates. | +| `list-safes` | Fetch all SAFEs for a company. Use when asked about SAFEs, simple agreements for future equity, SAFE terms, valuation caps, or discounts. | +| `market-benchmarks` | Analyze cap structure patterns across the portfolio as market benchmarks. Use when asked about market benchmarks, typical option pool sizes, average SAFE terms, what's normal for a Series A, cap structure patterns, or portfolio-wide statistics. | +| `ownership` | Ownership structure by share class, voting rights, and liquidation seniority. Use when asked about ownership breakdown, preferred vs common holders, voting power, protective provisions, or consent requirements. | +| `portfolio-alerts` | Detect red flags and time-sensitive issues across portfolio companies. Use when asked to flag problems, find expiring items, or audit portfolio health. | +| `portfolio-query` | Query cap table data for one or more companies. Use when asked about cap tables, ownership breakdown, share classes, stakeholder holdings, portfolio-wide analysis, comparing companies, or finding patterns across multiple entities. | +| `pro-forma-model` | Model a pro-forma financing round to show dilution impact. Use when asked to model a Series A/B/C, new round, or show how a round would affect ownership. | +| `round-history` | Fetch financing round history for a company. Use when asked about funding rounds, capital raised, or financing history. | +| `stakeholders` | List stakeholders for a company. Use when asked who the stakeholders are, stakeholder list, shareholders, investors, or holders. | +| `valuation-history` | Fetch 409A valuation history for a company. Use when asked about 409A valuations, FMV, exercise prices, or valuation expiration dates. | +| `waterfall-scenarios` | Fetch saved waterfall / exit scenario models for a company. Use when asked about liquidation preferences, exit payouts, return multiples, or waterfall analysis. | diff --git a/plugins/carta-cap-table/hooks/hooks.json b/plugins/carta-cap-table/hooks/hooks.json new file mode 100644 index 0000000..6ac68ea --- /dev/null +++ b/plugins/carta-cap-table/hooks/hooks.json @@ -0,0 +1,27 @@ +{ + "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/hooks/inject-skill-context.js", + "timeout": 5 + } + ] + } + ], + "PostToolUse": [ + { + "matcher": "mcp__carta.*__fetch", + "hooks": [ + { + "type": "command", + "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/hooks/warn-empty-fetch.js", + "timeout": 5 + } + ] + } + ] + } +} diff --git a/plugins/carta-cap-table/scripts/hooks/inject-skill-context.js b/plugins/carta-cap-table/scripts/hooks/inject-skill-context.js new file mode 100644 index 0000000..85b3690 --- /dev/null +++ b/plugins/carta-cap-table/scripts/hooks/inject-skill-context.js @@ -0,0 +1,29 @@ +#!/usr/bin/env node +/** + * SessionStart hook: inject skill-first reminder into every session. + * + * Ensures Claude loads the relevant carta-cap-table skill before making + * any tool calls, even in subagents that don't inherit session context. + */ + +let inputData = ''; +process.stdin.on('data', chunk => (inputData += chunk)); + +process.stdin.on('end', () => { + let hookEventName = 'SessionStart'; + try { + const input = JSON.parse(inputData); + hookEventName = input.hook_event_name || hookEventName; + } catch {} + + const output = { + hookSpecificOutput: { + hookEventName, + additionalContext: + 'You have carta-cap-table tools available. Before ANY tool call, invoke the matching Skill(\'carta-cap-table:...\') first. The skill defines what to fetch, what inputs are required, and how to present results. If no skill matches, invoke Skill(\'carta-cap-table:discover-commands\') to find the right command via discover(). IMPORTANT: Skill is a deferred tool — if its schema is not yet loaded, you MUST call ToolSearch with query "select:Skill" first, then invoke the Skill tool.', + }, + }; + + process.stdout.write(JSON.stringify(output)); + process.exit(0); +}); diff --git a/plugins/carta-cap-table/scripts/hooks/warn-empty-fetch.js b/plugins/carta-cap-table/scripts/hooks/warn-empty-fetch.js new file mode 100755 index 0000000..6416d37 --- /dev/null +++ b/plugins/carta-cap-table/scripts/hooks/warn-empty-fetch.js @@ -0,0 +1,51 @@ +#!/usr/bin/env node +/** + * PostToolUse Hook: Warn on empty fetch() responses + * + * When carta-cap-table's fetch() returns a _warning (empty data), + * outputs a reminder to stderr (exit 2) which gets fed to Claude. + */ + +let inputData = ''; +process.stdin.on('data', chunk => (inputData += chunk)); + +process.stdin.on('end', () => { + try { + const input = JSON.parse(inputData); + const { tool_input, tool_response } = input; + + // Extract the result string from the MCP response + let resultStr = tool_response?.result || tool_response; + if (Array.isArray(resultStr) && resultStr[0]?.type === 'text') { + resultStr = resultStr[0].text; + } else if (resultStr?.content && Array.isArray(resultStr.content)) { + resultStr = resultStr.content[0]?.text || resultStr; + } + + // Parse JSON and check for _warning + let parsed; + try { + parsed = typeof resultStr === 'string' ? JSON.parse(resultStr) : resultStr; + } catch { + process.exit(0); + return; + } + + if (parsed && parsed._warning) { + const command = tool_input?.command || 'unknown command'; + // Exit 2 = stderr fed back to Claude + process.stderr.write( + `⚠️ EMPTY DATA: ${command} returned no results. ` + + `Per the golden rule, any values you derive from this gap are BEST EFFORT. ` + + `Tell the user what's missing and ask before computing substitutes.` + ); + process.exit(2); + return; + } + + process.exit(0); + } catch (err) { + // Never block on hook errors + process.exit(0); + } +}); diff --git a/plugins/carta-cap-table/skills/client-triggers/SKILL.md b/plugins/carta-cap-table/skills/client-triggers/SKILL.md new file mode 100644 index 0000000..16138ac --- /dev/null +++ b/plugins/carta-cap-table/skills/client-triggers/SKILL.md @@ -0,0 +1,106 @@ +--- +name: client-triggers +description: Surface time-based BD triggers across the portfolio. Use when asked about client outreach, which clients closed a round recently, stale cap tables, pending grants, tombstones, weekly deals, or BD triggers. +--- + +# Client Triggers + +Scan the portfolio for actionable outreach triggers: recent round closes, expiring 409As, and companies with pending grants but no current valuation. + +## Prerequisites + +No inputs required — this skill loops the full portfolio automatically. Cap at 20 companies. + +## Commands + +- `list_accounts` — get all portfolio companies +- `fetch("cap_table:get:financing_history", {"corporation_id": corporation_id})` — financing history per company +- `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` — 409A status (optional) + +## Key Fields + +From financing history: +- `issue_date`: date of issuance +- `round_name`: name of the round (e.g. "Series A") +- `is_grant`: true if this is a grant issuance (not a priced round) + +From 409A FMVs: +- `expiration_date`: when the valuation expires +- `price`: FMV per share + +## How to Present + +See Step 4 below. Group by trigger type. Omit sections with no results. + +## Step 1 — Get Portfolio + +Call `list_accounts`. Filter to accounts where `id` starts with `corporation_pk:`. Extract the numeric corporation IDs (up to 20). + +## Step 2 — Collect Data Per Company + +For each company, fetch in sequence: + +**Financing history:** +- `fetch("cap_table:get:financing_history", {"corporation_id": corporation_id})` +- Key fields: `issue_date`, `round_name`, `is_grant` +- Group by `round_name`, find max `issue_date` per round → "last round date" + +**409A status** (optional, only if checking valuation triggers): +- `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` +- Key fields: `expiration_date`, `price` (FMV per share) +- Find the most recent valuation (sort by `effective_date` desc) + +## Step 3 — Classify Triggers + +### Recent Closes +Companies whose last round closed within N days (default 90, or user-specified): +- Trigger: `last_round_date ≥ today - N days` +- Action: Congratulations / cap table review outreach + +### Stale 409A +Companies with an expired or near-expiry 409A (within 60 days): +- Trigger: `expiration_date ≤ today + 60 days` or no 409A on file +- Action: Renewal reminder outreach + +### Pending Grants (no current 409A) +Companies that issued grants recently (within 90 days) but have no valid 409A: +- Trigger: recent `is_grant=true` entry + no active 409A +- Action: Urgency outreach — grants issued without a current FMV create 409A exposure + +## Step 4 — Present Results + +Group output by trigger type. Example: + +--- + +**Recent Closes (last 90 days)** + +| Company | Round | Close Date | Days Ago | +|---------|-------|------------|----------| +| Acme Inc | Series B | 2026-01-15 | 63 days | + +**Stale or Expiring 409As** + +| Company | Last 409A | Expiration | Status | +|---------|-----------|------------|--------| +| Beta Corp | $2.50 | 2026-04-01 | Expiring soon | +| Gamma LLC | — | — | No 409A on file | + +**Grants Issued Without Current 409A** + +| Company | Last Grant Date | 409A Status | +|---------|----------------|-------------| +| Delta Co | 2026-02-20 | Expired | + +--- + +If no triggers found in a category, omit that section. + +## Parameters + +If the user specifies a time window (e.g., "last 60 days", "last 6 months"), use that instead of the default 90 days. + +## Best Effort + +- **Computed:** trigger classification (recent close / stale 409A / pending grants) and time-window detection are heuristic +- **Authoritative:** round close dates, 409A expiration dates, and grant issuance dates come directly from Carta diff --git a/plugins/carta-cap-table/skills/conversion-calculator/SKILL.md b/plugins/carta-cap-table/skills/conversion-calculator/SKILL.md new file mode 100644 index 0000000..8e71bc1 --- /dev/null +++ b/plugins/carta-cap-table/skills/conversion-calculator/SKILL.md @@ -0,0 +1,124 @@ +--- +name: conversion-calculator +description: Calculate SAFE and convertible note conversion into equity. Use when asked about SAFE conversion, note conversion, conversion shares, or how instruments convert in a round. +--- + +# Conversion Calculator + +Calculate how SAFEs and convertible notes convert into equity at a given round price or valuation. + +## When to Use + +- "How many shares would the SAFEs convert into at a $50M pre?" +- "Calculate SAFE conversions for the Series A" +- "What happens to the convertible notes if we raise at $10/share?" +- "Show me the conversion math for all outstanding instruments" + +## Prerequisites + +You need: +1. `corporation_id` — get from `list_accounts` +2. Round terms — user must provide at least a **pre-money valuation** or **price per share** + +If neither is provided, ask before proceeding: +> "What pre-money valuation or price per share should I use for the conversion calculation?" + +**Subagent prohibition:** Do NOT delegate this skill to a background agent if the round valuation or price per share is missing. A subagent cannot ask the user for input. If these are absent and you are considering dispatching an agent, stop — ask the user directly first, then proceed. + +## Data Retrieval + +- `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` — SAFEs + convertible notes +- `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` — current fully diluted count +- `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` — current FMV (for reference) + +## Key Fields + +From convertible instruments: +- `is_debt`: false = SAFE, true = convertible note +- `dollar_amount`: principal investment amount +- `price_cap`: valuation cap +- `discount_percent`: discount rate (e.g. `"20.00"` = 20%) +- `interest_rate`: annual interest rate (notes only) +- `total_with_interest`: principal + accrued interest (use this for note conversions) +- `has_most_favored_nation_clause`: MFN SAFE — converts at best subsequent terms +- `status_explanation`: filter to "Outstanding" only + +## Step 1: Gather Instrument Data + +1. `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` — SAFEs + notes (filter to `status_explanation: "Outstanding"`) +2. `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` — get current fully diluted share count from `totals.total_fully_diluted` +3. `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` — current FMV for context + +## Step 2: Conversion Math + +### SAFE Conversion + +For each SAFE, compute shares under both methods, use the one giving MORE shares: + +**Cap conversion:** +``` +cap_price = valuation_cap / pre_money_fully_diluted_shares +shares = investment_amount / cap_price +``` + +**Discount conversion:** +``` +discount_price = round_price_per_share * (1 - discount_rate) +shares = investment_amount / discount_price +``` + +**No cap, no discount (MFN):** +- Converts at the round price (or the best terms of a subsequent SAFE) +``` +shares = investment_amount / round_price_per_share +``` + +### Convertible Note Conversion + +Same as SAFE but: +- Use `total_with_interest` (principal + accrued interest) instead of investment amount +- If `interest_rate` and `maturity_date` are available and `total_with_interest` is not, calculate: + ``` + years = (conversion_date - issue_date) / 365 + total = principal * (1 + interest_rate * years) + ``` + +## Step 3: Present Results + +### Per-Instrument Table + +| Instrument | Investor | Amount | Accrued Interest | Total | Cap | Discount | Method Used | Price/Share | Shares | +|-----------|----------|--------|-----------------|-------|-----|----------|-------------|-------------|--------| +| SAFE-1 | Investor A | $500,000 | — | $500,000 | $6M | 20% | Cap | $0.60 | 833,333 | +| SAFE-2 | Investor B | $250,000 | — | $250,000 | $8M | — | Cap | $0.80 | 312,500 | +| CN-1 | Investor C | $500,000 | $240,164 | $740,164 | $8M | 20% | Discount | $0.88 | 840,914 | + +### Summary + +``` +Round price per share: $1.10 +Pre-money fully diluted: 10,000,000 shares + +SAFE conversions: 1,145,833 shares ($750,000 invested) + - Effective avg price: $0.65/share (41% discount to round price) + +Note conversions: 840,914 shares ($500,000 principal + $240,164 interest) + - Effective avg price: $0.88/share (20% discount to round price) + +Total conversion shares: 1,986,747 +Post-conversion fully diluted: 11,986,747 +``` + +## Important Notes + +- Always show which conversion method (cap vs discount) was more favorable and used +- If a SAFE has both cap and discount, compute both and pick the one yielding more shares +- For MFN SAFEs, note that they take the best terms of any subsequent SAFE +- Accrued interest on notes can significantly increase the conversion amount — always account for it +- State the assumed conversion date (today or the round close date) +- This is an estimate — actual conversion depends on legal documents. Recommend review by counsel. + +## Best Effort + +- **Computed:** SAFE/note conversion share counts, cap-vs-discount method selection, effective price per share — these are estimates derived from instrument terms and user-supplied round price +- **Authoritative:** instrument terms (cap, discount, principal, accrued interest) come directly from Carta diff --git a/plugins/carta-cap-table/skills/discover-commands/SKILL.md b/plugins/carta-cap-table/skills/discover-commands/SKILL.md new file mode 100644 index 0000000..d863013 --- /dev/null +++ b/plugins/carta-cap-table/skills/discover-commands/SKILL.md @@ -0,0 +1,38 @@ +--- +name: discover-commands +description: Find the right carta-cap-table command when no other skill matches. Use when unsure which command to call, exploring available data, or when the user's request doesn't match a specific skill. +--- + +# Discover Commands + +Use the `discover()` tool to find available commands when no specific skill covers the user's request. + +## When to Use + +- No other carta-cap-table skill matches the user's request +- User asks "what can you do?" or "what data is available?" +- You're unsure which command to call + +## Step 1 — Search for Relevant Commands + +``` +discover(search="") +``` + +Use a keyword that captures the user's intent (e.g. "valuation", "grant", "safe", "stakeholder"). + +## Step 2 — Pick the Best Match + +Review the returned commands. Each has: +- `command`: the name to pass to `fetch()` +- `description`: what it returns +- `required_params`: what you need to provide +- `help`: detailed field descriptions and caveats + +## Step 3 — Execute + +``` +fetch("", { ...params }) +``` + +You still need `corporation_id` for most commands — get it from `list_accounts` if you don't have it. diff --git a/plugins/carta-cap-table/skills/grant-vesting/SKILL.md b/plugins/carta-cap-table/skills/grant-vesting/SKILL.md new file mode 100644 index 0000000..c4de369 --- /dev/null +++ b/plugins/carta-cap-table/skills/grant-vesting/SKILL.md @@ -0,0 +1,36 @@ +--- +name: grant-vesting +description: Fetch vesting schedule for a specific option grant. Use when asked about vesting details, cliff dates, vesting progress, or unvested shares for a particular grant. +--- + +# Grant Vesting Data + +Fetch the full vesting schedule for an option grant and present it with useful context, not just the raw table. + +## Prerequisites + +You need: +1. `corporation_id` — get from `list_accounts` if you don't have it +2. `grant_id` — identify via the grants list using the holder's name or grant number + +## Workflow + +1. **Identify the grant**: Call `fetch("cap_table:list:grants", {"corporation_id": corporation_id, "search": ""})`. If multiple grants are returned, ask the user which one, or pick the most relevant based on context. +2. **Fetch vesting data**: Call `fetch("cap_table:get:grant_vesting", {"corporation_id": corporation_id, "grant_id": grant_id})`. +3. **Present with context** — see below. + +## How to Present + +The tool returns a formatted summary and vesting events table. Lead with a one-sentence plain-English summary before showing the table: + +- **Pre-cliff**: how long until the cliff, how many shares vest at cliff +- **Partially vested**: what % has vested, what the ongoing cadence is (monthly/quarterly), when fully vested +- **Fully vested**: confirm and note if any shares remain unexercised + +Flag anything time-sensitive: +- Cliff date within the next 90 days +- Grant expiring soon +- Large unvested block concentrated at a future date +- **Deep in-the-money grants**: if the current 409A FMV is available and the spread between exercise price and FMV exceeds 10×, flag it. Note that holders face significant ordinary income (NSO) or AMT (ISO) exposure at exercise, and recommend the company consider a tender offer, early exercise program, or liquidity event planning. + +Then show the formatted table from the tool. diff --git a/plugins/carta-cap-table/skills/list-convertible-notes/SKILL.md b/plugins/carta-cap-table/skills/list-convertible-notes/SKILL.md new file mode 100644 index 0000000..f50c6a9 --- /dev/null +++ b/plugins/carta-cap-table/skills/list-convertible-notes/SKILL.md @@ -0,0 +1,58 @@ +--- +name: list-convertible-notes +description: Fetch all convertible instruments (SAFEs and convertible debt) for a company. Use when asked about convertible notes, SAFEs, convertible debt, note terms, caps, discounts, or maturity dates. +--- + +# List Convertible Notes + +Fetch all convertible instruments for a company. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Data Fetching + +``` +fetch("cap_table:list:convertible_notes", {"corporation_id": corporation_id}) +``` + +Optional params: +- `page`, `pageSize`: pagination +- `search`: text search +- `statusExplanation`: filter by status (e.g. "Outstanding", "Converted", "Canceled") + +## Key Fields + +- `is_debt`: `false` = SAFE, `true` = convertible note +- `dollar_amount`: investment amount +- `price_cap`: valuation cap +- `discount_percent`: discount rate +- `interest_rate`: annual interest rate (convertible debt only) +- `maturity_date`: when the note matures +- `total_with_interest`: principal + accrued interest +- `status_explanation`: "Outstanding", "Converted", "Canceled" +- `note_block`: groups notes by round/tranche (e.g. "2013 SAFE", "Bridge 2014") +- `name`: investor name +- `label`: security label (e.g. "SAFE-1", "CN-1") + +## How to Present + +1. Separate into SAFEs (`is_debt: false`) and Convertible Notes (`is_debt: true`) +2. Filter out canceled/converted unless specifically asked +3. For outstanding instruments, check if `maturity_date` is approaching (within 90 days) or past + +**SAFEs:** + +| Investor | Amount | Val. Cap | Discount | MFN | Status | +|----------|--------|----------|----------|-----|--------| +| Investor A | $500,000 | $6,000,000 | 20% | No | Outstanding | + +**Convertible Notes:** + +| Investor | Amount | Val. Cap | Discount | Interest | Maturity | Total w/ Interest | Status | +|----------|--------|----------|----------|----------|----------|-------------------|--------| +| Investor B | $500,000 | $8,000,000 | 20% | 6% | 11/05/2019 | $740,164 | Converted | + +4. Group by `note_block` if there are multiple tranches +5. Show totals: total outstanding amount, total with interest diff --git a/plugins/carta-cap-table/skills/list-safes/SKILL.md b/plugins/carta-cap-table/skills/list-safes/SKILL.md new file mode 100644 index 0000000..c21dd12 --- /dev/null +++ b/plugins/carta-cap-table/skills/list-safes/SKILL.md @@ -0,0 +1,41 @@ +--- +name: list-safes +description: Fetch all SAFEs for a company. Use when asked about SAFEs, simple agreements for future equity, SAFE terms, valuation caps, or discounts. +--- + +# List SAFEs + +Fetch all SAFEs for a company. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## When to Use This Skill + +Use this skill for SAFEs only. If you need both SAFEs and convertible notes together (e.g. for the conversion-calculator or pro-forma-model skills), use `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` instead — that returns both types in one call and avoids a redundant API request. Do not call both `list-safes` and `list-convertible-notes` for the same query. + +## Data Fetching + +``` +fetch("cap_table:list:safes", {"corporation_id": corporation_id}) +``` + +## Key Fields + +- `investor_name` or `investor.name`: investor name +- `investment_amount` or `amount`: dollar amount invested +- `valuation_cap` or `cap`: valuation cap +- `discount_rate` or `discount`: discount percentage +- `mfn`: boolean, most favored nation clause +- `status`: "Outstanding", "Converted", etc. + +## How to Present + +Format as a table: + +| Investor | Amount | Val. Cap | Discount | MFN | Status | +|----------|--------|----------|----------|-----|--------| +| Investor A | $500,000 | $6,000,000 | 20% | No | Outstanding | + +Show totals: total outstanding amount, count by status. diff --git a/plugins/carta-cap-table/skills/market-benchmarks/SKILL.md b/plugins/carta-cap-table/skills/market-benchmarks/SKILL.md new file mode 100644 index 0000000..bde1cfe --- /dev/null +++ b/plugins/carta-cap-table/skills/market-benchmarks/SKILL.md @@ -0,0 +1,110 @@ +--- +name: market-benchmarks +description: Analyze cap structure patterns across the portfolio as market benchmarks. Use when asked about market benchmarks, typical option pool sizes, average SAFE terms, what's normal for a Series A, cap structure patterns, or portfolio-wide statistics. +--- + +# Market Benchmarks + +Compute portfolio-wide benchmarks from your own Carta data: option pool sizes, SAFE valuation caps, and round sizes. Useful for sanity-checking a new deal's terms against your existing portfolio. + +> **Note:** This reflects your firm's portfolio, not Carta-wide market data. Present results as "portfolio benchmarks" not "market data." + +## Prerequisites + +No inputs required — this skill loops the full portfolio automatically. Cap at 20 companies. + +## Commands + +- `list_accounts` — get all portfolio companies +- `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` — option pool data +- `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` — SAFE/note terms +- `fetch("cap_table:get:financing_history", {"corporation_id": corporation_id})` — round sizes + +## Key Fields + +From cap table (option pool): +- `option_plans[].authorized_shares`: shares authorized per plan +- `totals.total_fully_diluted`: total fully diluted share count + +From convertible notes: +- `price_cap`: SAFE/note valuation cap +- `discount_percent`: discount rate (e.g. `"20.00"` = 20%) +- `interest_rate`: annual interest rate +- `is_debt`: false = SAFE, true = convertible note + +From financing history: +- `round_name`: round name +- `cash_paid`: amount paid per security (sum by round_name for round total) +- `issue_date`: close date + +## How to Present + +See Step 4 below. Present as benchmark tables grouped by metric. + +## Step 1 — Get Portfolio + +Call `list_accounts`. Filter to `corporation_pk:` accounts. Extract up to 20 numeric corporation IDs. + +## Step 2 — Collect Data Per Company + +For each company, fetch in sequence: + +**Cap table by share class** (for option pool %): +- `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` +- From `option_plans[]`: sum `authorized_shares` across all plans +- From `totals.total_fully_diluted`: compute option pool % = option_pool_authorized / total_fully_diluted + +**SAFE / convertible note terms:** +- `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` +- Key fields per instrument: `price_cap`, `discount_percent`, `interest_rate`, `is_debt` (false=SAFE, true=note) +- Collect all valuation caps for SAFEs + +**Financing history** (for round sizes): +- `fetch("cap_table:get:financing_history", {"corporation_id": corporation_id})` +- Key fields: `round_name`, `amount_raised`, `issue_date` +- Identify the most recent priced round and its size + +## Step 3 — Compute Summary Statistics + +For each metric, compute across companies that have data: +- **Median**, **min**, **max** +- Skip companies with no data for a given metric (don't count as zero) + +Metrics: +- Option pool % (fully diluted) +- SAFE valuation cap +- Last priced round size + +## Step 4 — Present Results + +**Portfolio Benchmarks (N companies)** + +**Option Pool Size (% Fully Diluted)** +| Metric | Value | +|--------|-------| +| Median | 12.5% | +| Range | 8% – 20% | +| Companies with data | 14 | + +**SAFE Valuation Caps** +| Metric | Value | +|--------|-------| +| Median | $8,000,000 | +| Range | $3M – $25M | +| SAFEs analyzed | 28 | + +**Last Priced Round Size** +| Metric | Value | +|--------|-------| +| Median | $5,000,000 | +| Range | $500K – $30M | +| Companies with priced rounds | 10 | + +--- + +If the user asks about a specific company ("how does Acme's option pool compare?"), show that company's value alongside the portfolio median. + +## Best Effort + +- **Computed:** median, min, max summary statistics and company-vs-portfolio comparisons are derived from the portfolio sample +- **Authoritative:** per-company option pool sizes, SAFE terms, and round sizes come directly from Carta diff --git a/plugins/carta-cap-table/skills/ownership/SKILL.md b/plugins/carta-cap-table/skills/ownership/SKILL.md new file mode 100644 index 0000000..f4c6701 --- /dev/null +++ b/plugins/carta-cap-table/skills/ownership/SKILL.md @@ -0,0 +1,74 @@ +--- +name: ownership +description: Ownership structure by share class, voting rights, and liquidation seniority. Use when asked about ownership breakdown, preferred vs common holders, voting power, protective provisions, or consent requirements. +--- + +# Ownership Structure + +Surface which preferred stockholders hold voting power and would typically need to consent for major corporate actions (financing rounds, M&A, charter amendments). + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Data Retrieval + +1. `fetch("cap_table:get:rights_and_preferences", { corporation_id })` +2. `fetch("cap_table:get:cap_table_by_stakeholder", { corporation_id })` + +## Key Fields + +From share classes: +- `name`: class name (e.g. "Series A Preferred") +- `stock_type`: "PREFERRED" or "COMMON" +- `votes_per_share`: voting weight (0 = non-voting) +- `seniority`: liquidation seniority rank (higher = senior) + +From cap table by stakeholder: +- Per-stakeholder share counts and ownership % broken down by share class + +## Step 1 — Fetch Share Classes + +``` +fetch("cap_table:get:rights_and_preferences", { corporation_id }) +``` + +## Step 2 — Fetch Cap Table by Stakeholder + +``` +fetch("cap_table:get:cap_table_by_stakeholder", { corporation_id }) +``` + +This returns per-stakeholder ownership broken down by share class. + +## How to Present + +See Step 4 below for table format. + +## Step 3 — Identify Preferred Holders + +From the share class data, identify preferred classes (stock_type = "PREFERRED" or votes_per_share > 0). + +From the cap table, identify stakeholders holding preferred shares. Sort by: +1. Seniority (most senior first) +2. Fully diluted ownership % (descending) + +## Step 4 — Present Results + +**Preferred Stockholders with Voting Rights** + +| Holder | Share Class | Shares | FD % | Votes/Share | Seniority | +|--------|-------------|--------|------|-------------|-----------| +| ... | ... | ... | ... | ... | ... | + +**Common Stockholders** (if relevant — founders, employees) + +| Holder | Shares | FD % | +|--------|--------|------| +| ... | ... | ... | + +## Important Note + +> Carta surfaces share ownership and voting structure, but does **not** expose actual consent thresholds or protective provision terms — those live in the Stockholders' Agreement and Certificate of Incorporation. This data identifies *who* holds voting preferred shares; an attorney must interpret *what* approvals are required and at what thresholds. + +If the user asks about specific thresholds (e.g., "do we need 60% of Series A to approve?"), acknowledge the limitation and recommend reviewing the governing documents. diff --git a/plugins/carta-cap-table/skills/portfolio-alerts/SKILL.md b/plugins/carta-cap-table/skills/portfolio-alerts/SKILL.md new file mode 100644 index 0000000..0f507c8 --- /dev/null +++ b/plugins/carta-cap-table/skills/portfolio-alerts/SKILL.md @@ -0,0 +1,124 @@ +--- +name: portfolio-alerts +description: Detect red flags and time-sensitive issues across portfolio companies. Use when asked to flag problems, find expiring items, or audit portfolio health. +--- + +# Portfolio Alerts + +Scan multiple companies for red flags and time-sensitive issues. Builds on the `portfolio-query` pattern. + +## When to Use + +- "Flag any red flags across my portfolio" +- "Which companies need attention?" +- "Are any 409As expiring soon?" +- "Which companies have low option pools?" +- "Any SAFEs or notes approaching maturity?" + +## Prerequisites + +No inputs required — this skill loops the full portfolio. Call `list_accounts` to get all `corporation_pk` accounts automatically. + +## Commands + +Depending on the check: +- `list_accounts` — get all portfolio companies +- `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` — 409A expiry check +- `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` — option pool check +- `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` — note maturity check +- `fetch("cap_table:list:safes", {"corporation_id": corporation_id})` — SAFE exposure check + +## Key Fields + +From 409A: `expiration_date`, `price`, `effective_date` +From cap table option plans: `available_ownership`, `name` +From convertible notes: `maturity_date`, `status_explanation`, `is_debt`, `dollar_amount`, `total_with_interest` + +## How It Works + +1. Call `list_accounts` to get all `corporation_pk` accounts +2. For each company, run the relevant checks +3. Aggregate findings by severity: critical > warning > info +4. Present a summary dashboard + +## Alert Checks + +Run whichever checks are relevant to the user's question. If they say "all red flags", run all of them. + +### 1. Expiring 409A Valuations + +``` +fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id}) +``` +- **Critical**: no 409A on file at all — treat as missing/unknown FMV; flag for immediate follow-up if the company issues options +- **Critical**: expiration_date is in the past +- **Warning**: expiration_date is within 90 days of today +- **Info**: expiration_date is within 180 days + +Companies with no 409A data should never be silently skipped — always include them in the output as a distinct category. + +### 2. Low Option Pool + +``` +fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id}) +``` +- **Critical**: option plan available_ownership < 2% +- **Warning**: option plan available_ownership < 5% +- **Info**: option plan available_ownership < 10% + +### 3. SAFEs/Notes Approaching Maturity + +``` +fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id}) +``` +- Filter to `status_explanation: "Outstanding"` and `is_debt: true` (notes have maturity dates) +- **Critical**: maturity_date is in the past +- **Warning**: maturity_date is within 90 days +- **Info**: maturity_date is within 180 days + +### 4. Large Unconverted SAFE Exposure + +``` +fetch("cap_table:list:safes", {"corporation_id": corporation_id}) +``` +- Sum outstanding SAFE amounts +- **Warning**: total outstanding SAFEs > 20% of last known valuation cap +- Present total SAFE exposure per company + +## Presentation + +### Summary Dashboard + +``` +Portfolio Health Check — 12 companies scanned + +Critical (2): + - Beta Inc: 409A EXPIRED (expired 2025-01-14, 63 days ago) + - Gamma Corp: Option pool at 1.2% available + +Warning (3): + - Acme Corp: 409A expires in 37 days (04/24/2025) + - Delta LLC: Convertible note matures in 45 days + - Epsilon Inc: Option pool at 4.1% available + +Healthy (7): Alpha, Zeta, Eta, Theta, Iota, Kappa, Lambda +``` + +### Detail Table (for specific checks) + +| Company | Issue | Severity | Details | Action Needed | +|---------|-------|----------|---------|---------------| +| Beta Inc | 409A Expired | Critical | Expired 01/14/2025 | Order new 409A | +| Acme Corp | 409A Expiring | Warning | Expires 04/24/2025 (37 days) | Schedule valuation | + +## Important Notes + +- Be mindful of rate limits — if > 20 companies, ask the user to narrow scope +- Some companies may error (permissions, incomplete setup) — skip gracefully and note which failed +- Always show the scan date and count: "Scanned 12 companies on 2025-03-18" +- Sort by severity (critical first), then by urgency (nearest deadline first) + +## Best Effort + +- **Computed:** severity classification (critical/warning/info thresholds) and aggregated health summary are heuristic, not Carta-defined +- **Authoritative:** 409A expiration dates, option pool percentages, and note maturity dates come directly from Carta diff --git a/plugins/carta-cap-table/skills/portfolio-query/SKILL.md b/plugins/carta-cap-table/skills/portfolio-query/SKILL.md new file mode 100644 index 0000000..e947d42 --- /dev/null +++ b/plugins/carta-cap-table/skills/portfolio-query/SKILL.md @@ -0,0 +1,138 @@ +--- +name: portfolio-query +description: Query cap table data for one or more companies. Use when asked about cap tables, ownership breakdown, share classes, stakeholder holdings, portfolio-wide analysis, comparing companies, or finding patterns across multiple entities. +--- + +# Portfolio Query + +Fetch and present cap table data for a single company or across multiple companies. + +## When to Use + +- "Show me the cap table for Acme Corp" +- "What's the ownership breakdown?" +- "Who are the shareholders?" +- "Which companies have expiring 409As?" +- "Show me cap tables for all my portfolio companies" +- "Which companies have SAFEs outstanding?" +- "Compare option pool sizes across my portfolio" +- "Flag any red flags across my companies" + +## Single-Company Cap Table + +For a single company, fetch both views and present with a bar chart: + +``` +fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id}) +fetch("cap_table:get:cap_table_by_stakeholder", {"corporation_id": corporation_id}) +``` + +**Present in this exact order — do not skip any step:** + +1. **Summary line**: total outstanding shares, total fully diluted shares, total cash raised +2. **Share class table**: share class name, outstanding shares, outstanding %, FD shares, FD %, cash raised +3. **Option pool**: authorized vs issued/outstanding, available shares and % +4. **Top stakeholders table**: name, outstanding shares, outstanding %, FD %, cash raised +5. **ASCII bar chart** — REQUIRED, always render this, no exceptions + +**Bar chart — MANDATORY. You MUST render this after the tables. Do not skip it.** + +Use the stakeholder data to render an ASCII ownership bar chart. Steps: +1. Collect each stakeholder's fully diluted % (or outstanding % if FD not available) +2. Find the max value — that stakeholder gets 40 █ blocks +3. All others: `bar_width = round(pct / max_pct * 40)`, minimum 1 block +4. Left-align labels in a fixed-width column (pad to the longest label length + 2 spaces) +5. Immediately after the label padding, place the █ blocks — no spaces between label and blocks +6. After the blocks, one space then the percentage + +Example output (you MUST produce something like this): +``` +Ownership (Fully Diluted) + +Option Pool ████████████████████████████████████████ 71.8% +Lead Investor ████████ 7.8% +Founder A ████ 4.0% +Founder B ████ 3.9% +Other Investor ███ 2.6% +Others ████ 9.9% +``` + +## Prerequisites + +- Single company: resolve `corporation_id` from `list_accounts` +- Multi-company: loop all `corporation_pk` accounts from `list_accounts` + +## Commands + +- `list_accounts` — get all accessible entities +- Then per-company commands depending on the query (see reference below) + +### Command Reference + +| Data | Command | +|---|---| +| 409A valuations | `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` | +| SAFEs & convertible notes | `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` | +| Pro-forma models | `fetch("cap_table:get:pro_forma_models", {"corporation_id": corporation_id})` | +| Cap table by share class | `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` | +| Option grants | `fetch("cap_table:list:grants", {"corporation_id": corporation_id})` | + +## Key Fields + +From `list_accounts`: +- `id`: format `corporation_pk:` — extract the number for company-level commands +- `accountType`: `"company"` accounts work with all commands; `"portfolio"` and `"investment firm"` are organizations, not companies +- `name`: company display name + +## How It Works + +There is no single "portfolio query" endpoint. Instead: + +1. Call `list_accounts` to get all accessible entities +2. Filter to the relevant account type (`company`, `fund`, `investment firm`, `law firm`, `portfolio`) +3. Extract `corporation_id` from each account's `id` field (strip the `corporation_pk:` or `organization_pk:` prefix) +4. Loop through each corporation, calling the relevant command per company +5. Aggregate and present the results + +## Important + +- Only `corporation_pk` accounts work with company-level commands (cap table, grants, SAFEs, valuations). `organization_pk` accounts are portfolios/firms. +- Be mindful of rate — don't call 50+ companies at once. Ask the user to narrow down if the list is large. +- Some companies may return errors (permissions, setup state) — skip them gracefully and note which ones failed. + +## Example: Find Expiring 409As + +``` +1. list_accounts → get all corporation_pk accounts +2. For each corporation_id: + fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id}) +3. For each result, check if the most recent expiration_date is within 90 days of today +4. Present: +``` + +| Company | Current FMV | Effective | Expires | Days Left | +|---------|------------|-----------|---------|-----------| +| Acme Corp | $12.61 | 04/25/2024 | 04/24/2025 | 37 | +| Beta Inc | $5.00 | 01/15/2024 | 01/14/2025 | EXPIRED | + +## Example: Portfolio Option Pool Health + +``` +1. list_accounts → get all corporation_pk accounts +2. For each corporation_id: + fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id}) +3. Extract option plan available_ownership percentage +4. Flag companies with available pool < 5% +``` + +## Presentation Tips + +- Always show company name alongside the data +- Sort by urgency/severity (e.g. most expired first) +- Group results: "needs attention" vs "healthy" +- Include a count summary: "3 of 12 companies need attention" + +## Best Effort + +- **Computed:** cross-company comparisons, pattern detection, and urgency/severity classifications are derived by Claude +- **Authoritative:** per-company data (409A dates, cap tables, SAFEs, grants) comes directly from Carta diff --git a/plugins/carta-cap-table/skills/pro-forma-model/SKILL.md b/plugins/carta-cap-table/skills/pro-forma-model/SKILL.md new file mode 100644 index 0000000..b07b535 --- /dev/null +++ b/plugins/carta-cap-table/skills/pro-forma-model/SKILL.md @@ -0,0 +1,186 @@ +--- +name: pro-forma-model +description: Model a pro-forma financing round to show dilution impact. Use when asked to model a Series A/B/C, new round, or show how a round would affect ownership. +--- + +# Pro-Forma Round Modeling + +Model the dilution impact of a new financing round using cap table data, SAFE/note conversion terms, and round parameters. + +## When to Use + +- "Model a Series B at $80M pre-money" +- "What does dilution look like if we raise $20M at $100M pre?" +- "How would a 10% option pool increase affect ownership?" +- "Show me pro-forma ownership after converting SAFEs into a Series A" + +## Required Inputs — MUST BLOCK + +**STOP. Do not compute ANY pro-forma values, scenarios, or estimates without ALL required inputs. Do not model multiple scenarios as a substitute for asking. Do not proceed "in the meantime". Ask first, compute after.** + +| Input | Example | Required? | +|---|---|---| +| Pre-money valuation | "$80M pre-money" | Yes | +| New investment amount (raise size) | "raising $15M" | **Yes — ALWAYS ask if not given** | +| Option pool target | "10% post-money pool" | No — skip pool math if omitted | + +If the raise size is missing, you MUST call AskUserQuestion BEFORE any computation: +AskUserQuestion("How much are you raising in this round? I need the raise amount before I can compute any pro-forma numbers.") + +**Even in multi-part requests** (e.g. "get me cap table AND pro-forma"), complete the other parts but BLOCK on the pro-forma until raise size is confirmed. Do not guess. Do not model scenarios at common amounts. Ask. + +**Subagent prohibition:** Do NOT delegate this skill to a background agent if the raise size is missing. A subagent cannot ask the user for input. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Data Retrieval + +Fetch saved pro-forma round models: + +``` +fetch("cap_table:get:pro_forma_models", {"corporation_id": corporation_id}) +``` + +Returns saved pro-forma round models. If none exist, compute manually (see Option 2 below). + +## Key Fields + +From saved models: +- `name`: round name (e.g. "Series A") +- `inputs.pre_money_valuation`: pre-money valuation used +- `inputs.new_investment_amount`: round size +- `inputs.post_round_option_pool.value`: target option pool % +- `outputs.post_money_valuation`: computed post-money valuation +- `outputs.post_round_share_price`: new share price +- `outputs.post_round_option_pool_share_count_increase`: shares added to pool +- `investments[]`: per-investor breakdown +- `convertibles[]`: SAFEs/notes included in the model + +## Option 1: Use Saved Pro-Forma Models + +``` +fetch("cap_table:get:pro_forma_models", {"corporation_id": corporation_id}) +``` + +This returns any saved pro-forma round models. If the company has modeled rounds in Carta, use these directly. + +## Option 2: Compute from Cap Table Data (BEST EFFORT) + +When no saved models match the requested terms, **tell the user before computing**: +- State what the saved models contain (if any) and why they don't match +- Explicitly say: "I can compute a best-effort estimate from the cap table data. This is my math, not Carta's — it may not match actual round terms. Want me to proceed?" +- Only compute after the user confirms (or if they explicitly asked for a custom scenario) + +When computing manually, **prefix the results with a prominent disclaimer**: +> ⚠️ **Best-effort estimate** — computed from cap table data, not from a saved Carta model. Verify with counsel before relying on these numbers. + +### Step 1: Gather Current State + +1. `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})` — get current authorized/outstanding/fully-diluted shares +2. `fetch("cap_table:get:convertible_notes", {"corporation_id": corporation_id})` — get SAFEs and convertible notes (filter to `status_explanation: "Outstanding"`) +3. `fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id})` — get current FMV + +### Step 2: Apply Round Math + +Given user inputs: +- **Pre-money valuation** (e.g. $80M) +- **Round size** (e.g. $15M) +- **Option pool increase** (e.g. 10% post-money target) + +Before computing, check available pool shares. From `fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id})`, compare `option_plans[].authorized_shares` vs `option_plans[].fully_diluted_shares` — the difference is currently available (unissued) shares. Report both the authorized pool size and the available-for-future-grants amount separately. If the available pool is nearly exhausted (< 5% of authorized), flag it — even a large authorized pool means little if it's mostly issued/vested. + +Calculate using this EXACT formula. Do not deviate or simplify. + +**Option pool shuffle** — the pool increase is carved from pre-money, meaning it inflates the denominator for price-per-share. This is circular (pool depends on post-FD, post-FD depends on pool), so solve iteratively: + +``` +Inputs: + pre_money = user-provided (e.g. $80,000,000) + raise = user-provided (e.g. $20,000,000) + pool_pct = user-provided (e.g. 0.10 for 10%) + pre_fd = from cap table total_fully_diluted + existing_pool = from cap table option_plans fully_diluted_shares + conversion_shares = SAFE + note conversions (see below) + +Step A — Solve for new pool shares (iterative): + Start with: new_pool_shares = 0 + Repeat until stable (max 20 iterations): + adjusted_pre_fd = pre_fd + new_pool_shares + conversion_shares + price_per_share = pre_money / adjusted_pre_fd + new_round_shares = raise / price_per_share + post_fd = adjusted_pre_fd + new_round_shares + new_pool_shares = (post_fd * pool_pct) - existing_pool + if new_pool_shares < 0: new_pool_shares = 0 + +Step B — Final values: + post_money = pre_money + raise + price_per_share = pre_money / (pre_fd + new_pool_shares + conversion_shares) + new_round_shares = raise / price_per_share + post_fd = pre_fd + new_pool_shares + conversion_shares + new_round_shares + +Verify: new_round_shares / post_fd should equal raise / post_money (investor ownership = raise / post-money). If not, recheck the math. + +SAFE conversions (only if status = "Outstanding"): + Cap: shares = investment / (cap / pre_fd) + Discount: shares = investment / (price_per_share * (1 - discount)) + Use whichever gives MORE shares to the holder. + +Note conversions (only if status = "Outstanding"): + Same as SAFE but use total_with_interest instead of investment. +``` + + +### Step 3: Present Results + +Show a before/after ownership table: + +| Stakeholder Group | Pre-Round Shares | Pre-Round % | Post-Round Shares | Post-Round % | Dilution | +|-------------------|-----------------|-------------|-------------------|--------------|----------| +| Founders | 5,000,000 | 50.0% | 5,000,000 | 35.7% | -14.3% | +| Series A Investors | 2,000,000 | 20.0% | 2,000,000 | 14.3% | -5.7% | +| SAFE Conversions | — | — | 800,000 | 5.7% | +5.7% | +| Series B (new) | — | — | 1,875,000 | 13.4% | +13.4% | +| Option Pool | 3,000,000 | 30.0% | 4,325,000 | 30.9% | +0.9% | +| **Total** | **10,000,000** | **100%** | **14,000,000** | **100%** | | + +Also show: +- Price per share +- Pre-money and post-money valuation +- Total SAFE/note conversion shares +- New option pool shares added + +After the table, render an ASCII bar chart of post-round ownership by stakeholder group. +Sort descending by post-round ownership. Scale bars to max width 40 chars. + +**Bar chart format rules:** +- Left-align labels in a fixed-width column (pad with spaces to the longest label length + 2) +- Immediately after the label column, fill blocks: `bar_width = round(pct / max_pct * 40)` +- After the blocks, add a single space then the percentage +- Never insert spaces between the label column and the blocks — the blocks must start right after label padding +- Minimum 1 block for any non-zero value +- Always pin "Others", "Other", or any catch-all group to the bottom of the chart, regardless of its ownership size + +``` +Post-Round Ownership + +Option Pool (total) ████████████████████████████████████████ 64.4% +Series B (new) ████████████ 14.2% +Common Stock ████ 7.6% +Series A Preferred ███ 4.5% +Series B Preferred ██ 3.7% +Series C Preferred ██ 2.7% +Series Seed Pref. █ 1.7% +Series D Preferred █ 1.2% +``` + +Also render this same chart for cap-table-by-stakeholder views when showing current ownership +(pre-round). Use the same format rules. + +## Important Notes + +- Always state your assumptions clearly (e.g. "assuming all SAFEs convert at their caps") +- If SAFE terms are ambiguous, show both cap and discount scenarios +- Option pool shuffle: the pool increase typically comes from pre-money (dilutes existing holders, not new investors). Clarify this. +- This is an estimate — actual round math depends on legal terms. Recommend verifying with counsel. diff --git a/plugins/carta-cap-table/skills/round-history/SKILL.md b/plugins/carta-cap-table/skills/round-history/SKILL.md new file mode 100644 index 0000000..f7a4274 --- /dev/null +++ b/plugins/carta-cap-table/skills/round-history/SKILL.md @@ -0,0 +1,93 @@ +--- +name: round-history +description: Fetch financing round history for a company. Use when asked about funding rounds, capital raised, or financing history. +--- + +# Round History + +Fetch financing history and summarize by round, or use the cap table by share class for a quick overview. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Option 1: Detailed History (per-security) + +``` +fetch("cap_table:get:financing_history", {"corporation_id": corporation_id}) +``` + +This is the same data that powers the in-app "Financing History" tab. + +## Key Fields + +- `round_name`: name of the financing round (e.g. "Series A Preferred") +- `issue_date`: date the security was issued +- `cash_paid`: amount paid by the investor for this security +- `quantity`: number of shares issued +- `issue_price`: price per share +- `stakeholder_name`: investor name +- `label`: security label (e.g. "PB-9") +- `is_grant`: true if this is an option grant (not a priced round) +- `is_canceled`: true if the security was canceled — exclude from aggregates +- `is_converted`: true if the security converted (e.g. SAFE → preferred) + +### Response Format + +```json +{ + "count": 120, + "results": [ + { + "id": 666, + "pk_key": "certificate_pk", + "stakeholder_name": "Janet Sugiyama", + "currency": "USD", + "label": "PB-9", + "round_name": "Series B Preferred", + "quantity": 180000.0, + "issue_date": "2014-09-14", + "cash_paid": 219600.0, + "issue_price": "1.22", + "is_grant": false, + "is_canceled": false, + "is_converted": false + } + ] +} +``` + +### How to Present + +1. Group results by `round_name` +2. For each round, aggregate: total `cash_paid`, total `quantity`, count of investors, earliest `issue_date` +3. Use `issue_price` from any non-canceled entry as the price per share +4. Filter out entries where `is_canceled` is true +5. Present as: + +| Round | Close Date | Price/Share | Investors | Shares Issued | Cash Raised | +|-------|-----------|-------------|-----------|---------------|-------------| +| Series Seed Preferred | 2013-06-30 | $0.27 | 5 | 1,422,435 | $383,380 | +| Series A Preferred | 2013-11-15 | $0.44 | 8 | 3,697,191 | $1,645,250 | + +6. After the table, render an ASCII bar chart of cash raised per round (chronological order). + Scale bars to max width 40 chars. Exclude rounds with zero cash raised (e.g. option grants). + +``` +Cash Raised by Round + +Series Seed Preferred ████ $383K +Series A Preferred ████████████████ $1.6M +Series B Preferred ████████████████████████████████████████ $3.7M +``` + + Each bar width = (cash_raised / max_cash_raised) * 40, rounded to nearest integer. + Format large numbers as $XM or $XK for readability. + +## Option 2: Quick Summary (from cap table) + +``` +fetch("cap_table:get:cap_table_by_share_class", {"corporation_id": corporation_id}) +``` + +Each preferred share class represents a round. Faster but less detail: no individual investors, no issue dates, no price per share. diff --git a/plugins/carta-cap-table/skills/stakeholders/SKILL.md b/plugins/carta-cap-table/skills/stakeholders/SKILL.md new file mode 100644 index 0000000..9e6eeeb --- /dev/null +++ b/plugins/carta-cap-table/skills/stakeholders/SKILL.md @@ -0,0 +1,52 @@ +--- +name: stakeholders +description: List stakeholders for a company. Use when asked who the stakeholders are, stakeholder list, shareholders, investors, or holders. +--- + +# Stakeholders + +List stakeholders (shareholders, investors, employees) for a company. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Which Command to Use + +- **"Who are the stakeholders?"** → `cap_table:get:stakeholders` (names, emails, roles — no ownership data) +- **"What does everyone own?"** → `cap_table:get:cap_table_by_stakeholder` (holdings, ownership %, FD shares) + +Default to `cap_table:get:stakeholders` unless the user asks about ownership, shares, or percentages. + +## Data Retrieval + +``` +fetch("cap_table:get:stakeholders", { corporation_id }) +``` + +Supports `search` param to filter by name or email. + +## Key Fields + +- `full_name`: stakeholder display name +- `email`: contact email +- `event_relationship`: role (e.g. founder, employee, investor) +- `kind`: stakeholder type + +## Step 1 — Fetch Stakeholders + +``` +fetch("cap_table:get:stakeholders", { corporation_id }) +``` + +## Step 2 — Present Results + +| Name | Email | Role | Type | +|------|-------|------|------| +| ... | ... | ... | ... | + +If the user then asks about ownership, follow up with: + +``` +fetch("cap_table:get:cap_table_by_stakeholder", { corporation_id }) +``` diff --git a/plugins/carta-cap-table/skills/valuation-history/SKILL.md b/plugins/carta-cap-table/skills/valuation-history/SKILL.md new file mode 100644 index 0000000..fbef846 --- /dev/null +++ b/plugins/carta-cap-table/skills/valuation-history/SKILL.md @@ -0,0 +1,74 @@ +--- +name: valuation-history +description: Fetch 409A valuation history for a company. Use when asked about 409A valuations, FMV, exercise prices, or valuation expiration dates. +--- + +# 409A Valuation History + +Fetch 409A fair market value (FMV) history for a company. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Data Fetching + +``` +fetch("cap_table:get:409a_valuations", {"corporation_id": corporation_id}) +``` + +Optional params: +- `share_class_id`: filter to a specific share class +- `report_id`: filter to a specific valuation report + +## Key Fields + +- `effective_date`: date the 409A valuation became effective +- `expiration_date`: date the valuation expires (typically 1 year after effective) +- `stale_date`: date after which the valuation is considered stale (if applicable) +- `price`: FMV per share as a string (e.g. `"12.610000000000"`) +- `name`: share class name (e.g. "Common") +- `common`: true if this is the common stock FMV +- `report_id`: ID of the 409A report +- `share_class_id`: ID of the share class + +## Response Format + +JSON array of FMV records: +```json +[ + { + "id": 484, + "effective_date": "04/25/2024", + "expiration_date": "04/24/2025", + "stale_date": null, + "price": "12.610000000000", + "report_id": 472, + "share_class_id": 9, + "name": "Common", + "common": true, + "corporation_id": 7 + } +] +``` + +## How to Present + +1. Sort by `effective_date` descending (most recent first) +2. The **most recent** entry is the current 409A — highlight it +3. Format `price` as currency (e.g. "$12.61/share"), trimming trailing zeros +4. Check if `expiration_date` is within 90 days of today — **flag as a time-sensitive action item**, not just a data point: bold it, call out the exact days remaining, and recommend initiating renewal immediately (especially if a financing round is in progress, as closing will likely push past the expiry date) +5. Check if `expiration_date` is in the past — flag as "expired" +6. For history, show a table: + +| Effective | Expires | FMV/Share | Share Class | Status | +|-----------|---------|-----------|-------------|--------| +| 04/25/2024 | 04/24/2025 | $12.61 | Common | Current | +| 03/31/2023 | 04/24/2024 | $10.33 | Common | Expired | + +7. Do not render a bar chart for FMV history — values in mature companies cluster near the + maximum, making bars uninformative (all bars look the same width). The table is sufficient. + Instead, after the table, add a one-line trend summary: + > FMV has grown **Nx since YYYY**, with [acceleration/steady growth] since [year]. + +8. If multiple share classes exist, group by share class name in the table. diff --git a/plugins/carta-cap-table/skills/waterfall-scenarios/SKILL.md b/plugins/carta-cap-table/skills/waterfall-scenarios/SKILL.md new file mode 100644 index 0000000..70afbd4 --- /dev/null +++ b/plugins/carta-cap-table/skills/waterfall-scenarios/SKILL.md @@ -0,0 +1,53 @@ +--- +name: waterfall-scenarios +description: Fetch saved waterfall / exit scenario models for a company. Use when asked about liquidation preferences, exit payouts, return multiples, or waterfall analysis. +--- + +# Waterfall Scenarios + +Fetch saved exit scenario models and present them with meaningful context, not just the per-holder table. + +## Prerequisites + +You need the `corporation_id`. Get it from `list_accounts` if you don't have it. + +## Workflow + +``` +fetch("cap_table:get:waterfall_scenarios", {"corporation_id": corporation_id}) +``` + +The command returns each completed (status == "DONE", non-draft) scenario with a per-holder breakdown of cost basis, payout value, share count, and return multiple. + +## How to Present + +Don't just show the table — frame each scenario: + +- **Lead with the exit value and what it means**: who gets paid out, at what multiple, and whether any holders are underwater +- **Highlight the biggest winners and losers** by return multiple — a 1.0x return means a holder barely breaks even; anything below that means a loss +- **If there are multiple scenarios**, compare them: how do payouts shift as exit value changes? At what exit value does the common stack start to see meaningful returns? +- **Note liquidation preference effects**: if preferred holders take a large share at lower exit values, say so plainly + +After the per-holder table, render an ASCII bar chart of payout by holder (sorted descending by payout). +Scale bars to max width 40 chars: + +``` +Payout Distribution — $50M Exit + +Lead Investor ████████████████████████████████████████ $18.2M 3.7x +Founder ████████████████████ $9.1M 1.8x +Common Holders ██████████ $4.5M 0.9x +``` + +Each bar width = (value_of_holdings / max_value) * 40. Show return multiple after the dollar amount. + +Flag anything notable: +- Any holder with return multiple < 1.0x (loss scenario) +- Large gap between pref payout and common payout at a given exit value +- Scenarios that are very close in exit value but have very different common distributions + +## Custom Exit Values + +If the user asks to model a specific exit value not in the saved scenarios: + +> "There's no saved model at that exit value. To model a custom exit, create a new scenario in Carta's scenario modeling tool, then come back and I'll pull it up."