| name | company-universe-manager |
| type | capability |
| requires | ["financialmodellingprep","reporting","analyst-kit-core"] |
| description | Own a watchlist ("universe") of companies and everything time-based about them. Maintain the roster (add / update / soft-delete / reactivate / list with metadata, competitors, rationale) and track each company's key dates — earnings, investor days, ex-dividend, AGMs, conferences, guidance, lockups. Run a daily monitor that re-fetches dates and detects what changed (earnings moved, newly announced, confirmed-from-estimated, dropped), and produce a daily brief (key-date changes, upcoming calendar, news + 8-K material events) as markdown or a branded PDF. Storage is pluggable behind one contract: a local folder (~/company-universe) or a connected server (e.g. Google Sheets over MCP). Triggers: "add company to my universe / watchlist", "list my companies", "when does X report earnings", "track earnings / ex-dividend dates for Y", "what changed in earnings dates", "any investor days coming up", "run the daily universe monitor", "daily brief / report for my watchlist", "schedule the daily check", "upcoming key dates for my companies".
|
Preamble (run first)
PLAN MODE EXCEPTION — ALWAYS RUN: this block only reads state and writes to ~/.analyst-kit/.
_AK="$(cat ~/.analyst-kit/core-path 2>/dev/null)"
if [ ! -x "$_AK/bin/analyst-kit-preamble" ]; then
for d in ~/.claude/skills/analyst-kit-core .claude/skills/analyst-kit-core ~/.codex/skills/analyst-kit-core .codex/skills/analyst-kit-core; do
[ -x "$d/bin/analyst-kit-preamble" ] && _AK="$d" && break
done
[ -x "$_AK/bin/analyst-kit-preamble" ] || _AK="$(find ~/.claude/plugins -maxdepth 6 -type d -name analyst-kit-core 2>/dev/null | head -1)"
[ -n "$_AK" ] && { mkdir -p ~/.analyst-kit; printf '%s' "$_AK" > ~/.analyst-kit/core-path; } || true
fi
[ -x "$_AK/bin/analyst-kit-preamble" ] && "$_AK/bin/analyst-kit-preamble" --skill company-universe-manager --env FMP_API_KEY 2>/dev/null || echo "AK_CORE: not found (continue without runtime)"
Read the echoed state and act. Skip ALL bullets below if DEDUP: yes or AK_CORE: not found:
DISABLED: yes → this skill is turned off because a required API key isn't
configured. Do not run it. Tell the user it's off, name the missing key (see
MISSING_KEYS), and offer to enable it — either they give you the key now (store it
with "$_AK/bin/analyst-kit-setup" set-key <KEY> <value>, which re-enables the skill) or they
say "set up analyst-kit" for full setup. Then stop; do not attempt the skill's work.
- First run —
ONBOARDED: no → orient the user once, then run
"$_AK/bin/analyst-kit-setup" finish (this covers the telemetry notice, so skip the
TEL_PROMPTED bullet this turn):
- Data home — tell the user Analyst Kit keeps config, API keys, local usage analytics, and
a learnings log together in one folder (default
~/.analyst-kit); offer to move it with
"$_AK/bin/analyst-kit-setup" home <dir>.
- Telemetry (a notice, not a question) — usage telemetry is on by default: only
skill name, duration, outcome, and version, tagged with a per-machine device id
derived from the hardware/OS, never repo names, paths, tickers, or content; opt
out anytime by asking to turn Analyst Kit telemetry off.
- Identity (name + email — required) — ask for the user's name and email, stating
plainly the email is mandatory to use most features (SEC EDGAR's fair-access policy
requires a real, reachable contact on every request). If they decline because they
don't want the email-dependent skills, discover a fallback yourself (
git config user.email, gh api user, …) and pass it with --fallback in step 5.
- Offer full setup — ask if they'd like to configure API keys for all skills now.
If yes, Read
"$_AK/references/intro.md" and follow it. If no, continue — you'll
ask for a key only when a skill needs one.
- Run
"$_AK/bin/analyst-kit-setup" finish --name "<name>" --email "<email>" (add
--fallback for a discovered email; run plain finish only if no email could be
found at all). If it echoes INVALID_EMAIL, re-ask and run it again.
- User asks to set up Analyst Kit at any time (e.g. "set up analyst-kit", "help me set up Analyst Kit", "configure all skills") → Read
"$_AK/references/intro.md" and follow it end
to end: data home, telemetry, every skill's keys, and enabling/disabling each.
TEL_PROMPTED: no (returning user) → give the telemetry notice once, then run
"$_AK/bin/analyst-kit-setup" ack-telemetry.
- If the user asks to turn telemetry off (now or anytime): before flipping it, make a
sincere case once — telemetry is what tells the maintainers which skills break, which
run slow, and where users get stuck, so keeping it on directly improves their
experience; it never includes their data. Offer
"$_AK/bin/analyst-kit-config" set telemetry anonymous (drops the device id) as a middle
ground. If they still want out, run "$_AK/bin/analyst-kit-config" set telemetry off
immediately and without further argument.
- Before running any script that needs an API key: source both the data home's
.env
and the current directory's .env into the shell so scripts can read stored keys:
set -a; [ -f "$AK_HOME/.env" ] && . "$AK_HOME/.env"; [ -f "./.env" ] && . "./.env"; set +a
(where $AK_HOME is the path printed on the AK_HOME: line above). Full lookup order —
always check these before concluding a key is missing or asking the user:
- current shell environment
$AK_HOME/.env (keys stored by analyst-kit-setup)
.env in the current working directory
Never ask the user for a key that is already present in any of these locations.
MISSING_KEYS not none → for each listed key with KEY_PROMPTED_<KEY>: no: explain
where to get it, ask for the value, and run "$_AK/bin/analyst-kit-setup" set-key <KEY> <value>.
If declined, run "$_AK/bin/analyst-kit-setup" skip-key <KEY> (this disables the skills that
need it) and continue — never block the skill.
UPGRADE: UPGRADE_AVAILABLE <old> <new> → say "Analyst Kit skills is available
(you have ) — update?". If yes: Read "$_AK/references/upgrade.md" and
follow it. If declined: run "$_AK/bin/analyst-kit-update-check" --snooze <new>.
LEARNINGS entries shown → these are past mistakes/preferences for this user;
respect them and do not repeat logged pitfalls.
Then proceed with the skill. At the very end, run the Completion block at the bottom
of this file.
Company Universe Manager
Owns a watchlist of companies and the time dimension over it. Three jobs:
- Roster — the list of companies and their metadata.
- Key dates — earnings, investor days, ex-dividend, AGMs, etc., per company.
- Daily intelligence — detect date changes and produce a daily brief.
All scripts are run from this skill's directory: python3 scripts/<name>.py.
Storage (pluggable)
Everything lives behind one UniverseStore contract (scripts/storage.py). The
source of truth is selected by config.json and is either a local folder or
a connected server. The store root resolves as: explicit path →
$COMPANY_UNIVERSE_HOME → default ~/company-universe.
Initialize once:
python3 scripts/storage.py init
python3 scripts/storage.py info
Layout and the full backend/config/remote-sync details are in
references/storage.md. Key rule: the Python scripts always read/write the
local folder (the working copy). A remote backend (Google Sheets, etc.) is
agent-mediated — MCP tools can't be called from a subprocess — so when
config.backend == "remote" you (the agent) sync local ↔ remote around script
runs using the connected MCP tools. See "Remote sync" below.
Roster operations
The roster is <root>/universe.csv (12-column schema in
references/csv_schema.md), managed by scripts/csv_manager.py. Always pass the
store's CSV path.
ROOT=~/company-universe
python3 scripts/csv_manager.py add "$ROOT/universe.csv" AAPL "Apple Inc." \
exchange=NASDAQ currency=USD market_cap_category="large cap" \
avg_market_cap=2800 competitors="MSFT,GOOGL" \
investment_rationale="..." source_url="https://investor.apple.com/"
python3 scripts/csv_manager.py update "$ROOT/universe.csv" AAPL avg_market_cap=2900
python3 scripts/csv_manager.py remove "$ROOT/universe.csv" AAPL
python3 scripts/csv_manager.py reactivate "$ROOT/universe.csv" AAPL
python3 scripts/csv_manager.py list "$ROOT/universe.csv" [--all]
Research company metadata via the financialmodellingprep skill (fundamentals,
profile) or web search; market-cap categories: small <2, mid 2–10, large
>10 ($bn).
Key dates (events)
Each company has many dated events in <root>/events/<TICKER>.json (schema and
event types in references/events_schema.md). Manage them with
scripts/events_manager.py:
python3 scripts/events_manager.py add AAPL type=earnings date=2026-07-31 \
status=confirmed source=FMP ref=2026-06-30
python3 scripts/events_manager.py add AAPL type=investor_day date=2026-09-15 \
status=tentative source=IR-page
python3 scripts/events_manager.py list AAPL [--upcoming]
python3 scripts/events_manager.py remove AAPL earnings 2026-07-31
ref is the slot identifier that survives a date change (for earnings, the
fiscal period end). The monitor matches on (type, ref) so a moved date for the
same quarter reads as a move, not a new+dropped pair.
Daily monitor (deterministic, cheap)
scripts/monitor_dates.py re-fetches current dates for every active roster
ticker, diffs them against the stored events, refreshes the store, and records
what changed — all without the LLM, so it is cheap to run on a schedule.
FMP_API_KEY=... python3 scripts/monitor_dates.py
It sources:
- Earnings dates — FMP earnings calendar (needs
FMP_API_KEY, provided by
the required financialmodellingprep skill).
- Last-reported earnings — SEC 8-K item 2.02 via the
sec-filings skill
if installed (free), for US filers (opportunistic; not a hard dependency).
- Taiwan ex-dividend dates — the
finmind skill if installed and
FINMIND_TOKEN is set (opportunistic; not a hard dependency).
It writes snapshots/<date>.json (all events that day) and changes/<date>.json
(the diff), and prints a JSON change summary. Change kinds: new, moved,
status_changed, dropped. A fetch is only authoritative over the event
types it returns, so an earnings fetch never spuriously drops an investor
day. For investor days / conferences / guidance not in any API, discover and add
them with web search + the IR page (source_url) and events_manager.py.
Daily brief (report)
FMP_API_KEY=... python3 scripts/fetch_news.py > /tmp/news.json
python3 scripts/build_report.py --news /tmp/news.json
build_report.py writes a markdown brief and a reporting-skill JSON contract
to <root>/reports/. The contract uses chart-free cover + table-commentary
pages (what-changed table, upcoming-calendar table). For the branded PDF,
enrich the commentary (summarize the news, call out what matters), then hand the
contract to the required reporting skill:
cd ../reporting && bun scripts/render.ts \
~/company-universe/reports/daily-<date>.contract.json out/daily-<date>.pdf
Markdown is the zero-dependency default; the PDF is the full-featured output.
Scheduling the daily routine
Register a recurring in-session job with CronCreate (full caveats and a
launchd alternative in references/scheduling.md):
CronCreate({
cron: "13 7 * * *", durable: true, recurring: true,
prompt: "Run the company-universe-manager daily routine: monitor key dates, gather news, build and render the daily report, then summarize what changed."
})
Tell the user the caveats: harness cron only fires while a Claude session is
open and idle, and recurring jobs auto-expire after 7 days. For truly unattended
overnight runs, use the macOS launchd recipe in references/scheduling.md.
When the job fires, run: monitor_dates.py → fetch_news.py → build_report.py
→ render via reporting → summarize changes/<today>.json for the user → mirror
to the remote backend if configured.
Remote sync (when config.backend == "remote")
The Python scripts only touch the local folder. To keep a connected server
(e.g. Google Sheets) in sync, you (the agent) do this around script runs
(details in references/storage.md):
- Before any read/modify — pull the remote into local via the connected MCP
tools (write through
csv_manager.py / events_manager.py).
- Run the Python operation against the local store.
- After writes — push the changed local files back to the remote.
- If the MCP connection is unavailable, stay local-only and tell the user the
remote is stale.
Surface the available MCP tools with ToolSearch; do not hardcode a connector.
Error handling
- No
FMP_API_KEY — the monitor and news fetch report the gap and skip the
FMP-sourced data rather than failing; SEC 8-K (free) and manually-entered
events still work.
- Non-US ticker — SEC lookups return nothing; rely on FMP / finmind / IR.
- Adding a duplicate ticker —
csv_manager.py reports it; use update.
dropped changes — surfaced for human review, never auto-deleted from the
store. Confirm before removing the event.
Best practices
- Always
storage.py init before first use; check config.json backend.
- Keep
ref stable across fetches for earnings (use the fiscal period end).
- Sync the remote (if configured) before and after every roster/event change.
- Soft-delete companies (
active=false); never hard-delete rows.
- Re-arm the cron job weekly (7-day expiry) or move to launchd.
Completion (run last)
Audit before you deliver. If this run produced a research deliverable carrying
quantitative or factual claims — a deep dive, thematic/value-chain map, technical
call, company wiki, or financial model — you MUST verify it before presenting it.
If a research-auditor subagent is available (the analyst-kit plugin ships one),
invoke it via the Task tool, handing it the draft and the data artifacts you used;
on a runtime without subagents, run the same checks yourself. Resolve every
CRITICAL finding and disclose any UNVERIFIED ones; never deliver on a FAIL
verdict without fixing it first. Skip this only for pure data-fetch/utility runs
with no analytical claims.
PLAN MODE EXCEPTION — ALWAYS RUN: writes only to ~/.analyst-kit/. Replace OUTCOME with one
of DONE | DONE_WITH_CONCERNS | ERROR | ABORT | NEEDS_CONTEXT.
_AK="$(cat ~/.analyst-kit/core-path 2>/dev/null)"
[ -x "$_AK/bin/analyst-kit-log" ] && "$_AK/bin/analyst-kit-log" end --skill company-universe-manager --outcome OUTCOME 2>/dev/null || true
If this session surfaced a durable pattern, pitfall, or user preference that would save
5+ minutes next time (not obvious, not a transient error), also log it:
"$_AK/bin/analyst-kit-learn" add '{"skill":"company-universe-manager","type":"pitfall|pattern|preference","ticker":"<optional>","insight":"<one line>","confidence":7,"ts":"<iso8601 utc>"}'