brief
One-call briefing at the start of a turn: spots known companies in `text`, recalls prior theses, claims, open alerts and ruled-out ideas about them, and returns the graph digest.
text
io.github.sekhar14Published by GitHub account sekhar14v0.4.1Updated Oct 30, 2026
Stock research memory for Claude: theses, sourced claims and SEC filing alerts in a local graph
Local stock-research memory that lets an assistant recall prior theses, record sourced claims and get SEC filing alerts in a graph.
Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.
Follow the launch instructions in the repository.
Stock research memory for Claude Code, Claude Desktop and Codex. Torus gives Claude a memory for your equity research. Before it answers, Claude sees what you already know about a company: your theses, the sourced facts behind them, its suppliers, customers and competitors, and the ideas you ruled out. New research builds on the old, and every new fact is saved with its source. When a new SEC filing or news story threatens a thesis, Torus warns you: on your desktop as it happens, and in a daily or weekly digest by email, Slack, Discord or ntfy.
Ask Claude "Is NVIDIA still supply-bound?" and it starts from what you already concluded, not from scratch. Torus keeps:
When a 10-K, 10-Q, 8-K or news item lands, Torus works out which of your theses it touches, through which supplier or customer link, and how much it matters. It also keeps the research honest (what's gone stale, what rests on one source, what a skeptic would say) and tracks how your calls did against the market.
Built for analysts, portfolio managers and serious individual investors.
~/.torus (override with
TORUS_HOME).Torus is a research memory, not financial advice. See CHANGELOG.md for what's in each release.
Torus works in three apps. All of them share one graph on your machine, so you can research in one and pick up in another.
Claude Desktop
The package is called torus-kg on PyPI; the command it installs is torus. torus setup asks
for the email the SEC requires, downloads the SEC ticker list, adds Torus to Claude Desktop's
settings (it keeps a .bak copy and leaves your other servers alone), and schedules the filing
watchers. Restart Claude Desktop, then try the Onboard companies prompt from the "+" menu
with a few tickers.
Codex
The same two commands. torus setup adds [mcp_servers.torus] to ~/.codex/config.toml (or
$CODEX_HOME) and installs the Torus skills (torus-research, torus-onboard, torus-review
and so on) under ~/.codex/skills. If you already wrote a [mcp_servers.torus] entry yourself,
setup leaves it as it is. Restart Codex, then ask it to onboard your tickers.
Claude Code
See Getting started below. torus setup also prints the two
/plugin commands.
torus setup configures the apps it finds; pass --claude-desktop or --codex to configure one
anyway, or --no-codex / --no-claude-desktop to skip one. torus doctor checks each app's
entry and that the server starts, and torus doctor --fix repairs what it can.
Just looking? torus demo builds a small sample graph (NVIDIA, TSMC, Micron and AMD theses,
an alert from a TSMC filing, the semis starter pack) in its own folder, TORUS_HOME/demo, and
opens it in the explorer. Your own graph is untouched, and every piece of sample research is
marked [demo].
Why this helps. Research done in chat is usually thrown away: the next session starts from scratch, and nothing connects new information to what you already concluded. Torus keeps the working parts of the research, not just the final report:
You need Python 3.11+ and Claude Code.
1. Install the torus command. The plugin's hooks and MCP server run it, so this step is
required:
2. Set it up:
3. Add the plugin in Claude Code:
You'll be asked for a capture mode:
4. Tell Torus what you cover (recommended). In Claude Code:
or from a portfolio or watchlist export: torus onboard --file portfolio.csv (any CSV with a
ticker or symbol column), then /torus:onboard to finish the mapping. Torus adds the
companies, watches their SEC filings, suggests matching starter packs, and a Haiku subagent maps
each company's suppliers, customers, competitors and customer concentration from its latest
annual report, citing the filing.
5. Keep the watchers running:
This uses Task Scheduler on Windows, launchd on macOS and cron on Linux.
6. Research as usual, e.g. "Is NVIDIA still supply-bound? Check TSMC's CoWoS plans." The
next time you mention NVIDIA, Claude sees the earlier thesis, claims and ruled-out ideas before it
starts. After each turn Torus tells you what it recorded ("Torus recorded 1 thesis, 3 claims
and 2 sources this turn"), and /torus:undo reverts anything you don't want.
You don't need to remember commands; ask in plain words and Claude uses the right Torus tools.
Behind them, the MCP server gives Claude 45 tools, from brief, recall and record_claim to
filing_diff, whatif, digest and scorecard. Claude Desktop and Codex see 33 of them by
default (the analyst toolset): the queue and low-level graph tools are hidden. Set
TORUS_TOOLSET=full in the server's env to show them all.
Neither app loads the Claude Code plugin, so the MCP server carries the workflow itself. When
TORUS_HARNESS is claude-desktop or codex (torus setup sets it):
brief with the user's question before
researching a company, to record sources and claims as it goes (capture mode live, since no
curator reads the chat later), and to end each turn with a "Torus recorded ..." receipt. The
wording is model-neutral, since Codex runs OpenAI models./torus: command is also an MCP prompt (Claude Desktop's "+" menu) and a
Codex skill, rendered from the same source in src/torus/workflows/. Steps a Claude Code
subagent would do (mapping companies from their 10-Ks, arguing the counter-case) run in the
chat instead.recent_records, undo_records).Claude in a web browser, ChatGPT and Copilot can't use Torus: they only reach MCP servers at a public address, and Torus runs on your machine.
Configuring by hand. torus setup writes these for you. In Claude Desktop (Settings >
Developer > Edit Config; %APPDATA%\Claude\claude_desktop_config.json on Windows,
~/Library/Application Support/Claude/claude_desktop_config.json on macOS):
In Codex (~/.codex/config.toml):
Use the full path (where torus on Windows, which torus on macOS and Linux): desktop apps don't
see your shell's PATH. If you set TORUS_HOME, add it under env so every app shares one
graph. Other env settings: TORUS_CAPTURE_MODE (live, balanced, curator) and
TORUS_TOOLSET (analyst, full). Other MCP clients (Cursor, VS Code and so on) may work
with the same entry, but they aren't tested.
Torus reads filings from EDGAR for Claude, so research cites primary sources instead of summaries of them:
business, risk_factors, mdna, legal, market_risk or financials (or an item number),
page by page. Any earlier filing of the same form is available too.filing) and a
verbatim quote, and count as primary sources in the audit and the challenge.Filings are downloaded once and cached as text in TORUS_HOME/sources/edgar. Reading them needs
sec_contact set: the SEC asks every client for a contact email.
This writes a single self-contained HTML file (default ~/.torus/explorer.html) and opens it in
your browser. It works offline and needs nothing installed, so you can share the file.
The screenshots use the semis starter pack plus illustrative [demo] research
(scripts/seed_demo.py). In the one above, the [demo] NVIDIA thesis is selected. A TSMC 8-K
raised an alert on it, and the panel shows why: the thesis has a trigger on TSMC, an assumption
claim that names TSMC, and TSMC supplies NVIDIA, the thesis's subject. It also shows how the
score was built: a strong graph connection, a low-materiality 8-K item (8.01, "other events"),
boosted because the filing's wording matches the thesis's trigger.
#select= and a name or ticker to the file's address, e.g.
explorer.html#select=NVDA, to open with that node selected, or #at=2026-09-20 to open the
graph as it stood that day. Combine them with &: #select=NVDA&at=2026-09-20.The same demo graph as it stood on 20 September: the starter pack wasn't added yet, the AMD thesis was still active (it broke in October), and the TSMC alert had just arrived.
The file is a snapshot. Run torus explore again after new research.
Starter packs give impact analysis a web of widely reported links from day one. Each link is marked as a starter link until research confirms it, and never overrides a link your research recorded.
Claude has the same tools through MCP (neighbors, find_paths, impact, seed_starter_pack).
When research re-records a starter link, it becomes yours.
torus watch run checks once:
Each new item becomes an event. Torus then walks the graph (suppliers, customers, competitors)
and opens alerts on theses it may affect. Review them with /torus:review. At the end of the
pass, strong new alerts show up as desktop notifications, and a
scheduled digest goes out if one is due.
The first check of a company or feed looks back 7 days. After that, only new items are processed.
A routine supplier 10-Q shouldn't interrupt you; a bankruptcy filing should. Each alert scores
graph score × materiality × (1 + trigger match):
Each alert shows how its score was built, with a label to read it by: strong (1.0 and up, as much as a fully material event about the thesis's own subject), moderate (0.5 and up) or weak. To tune them:
/torus:review suggests a mute when a thesis keeps getting the same kind of unrelated alert.
The watchers run every hour, but an alert is no use if you only see it the next time you open a chat. Torus tells you in two ways.
When torus watch run raises a strong alert, you get a notification:
Torus: strong alert on NVIDIA data-center growth is supply-bound TSMC 8-K: CoWoS capacity expansion delayed (and 1 more)
osascript), Linux uses notify-send.
Nothing extra to install. On Linux the cron job finds your desktop session on its own.torus doctor checks that the platform's notification tool is there.
torus digest writes what happened since a date, most urgent first:
In any app, ask "what happened this week?" and Claude calls the digest tool.
Nothing is sent until you set a destination. Then pick a schedule; the hourly watcher run sends the digest when it's due, so there's no second scheduled task.
A webhook (Slack, Discord, ntfy or anything that takes JSON):
The format comes from the address: Slack gets plain text, Discord and ntfy get Markdown, and
any other URL gets the digest as JSON. For a self-hosted ntfy server, set
digest_webhook_format ntfy. Long digests are cut at a whole section, with a pointer to the full
file. Webhook URLs contain a secret token, so Torus shows them masked
(https://hooks.slack.com/.../AbCd) and leaves them out of torus export.
Email through your own SMTP server, with Gmail as the example:
The password is never stored in the database. torus digest email-password saves it to
TORUS_HOME/secrets/smtp_password, a file only you can read, or you can set
TORUS_SMTP_PASSWORD instead. Torus only sends over an encrypted connection: STARTTLS on port 587
(the default) or TLS on port 465.
A digest with nothing new isn't sent (digest_send_empty on changes that). torus doctor shows
the schedule, where the digest goes, when it was last sent and the last error.
torus audit, /torus:audit): assumptions past their validity date, theses past
their horizon or unchanged for 90 days, assumptions resting on one non-filing source or on an
unsourced opinion, claims about the same company that may conflict, inferences built on
retracted claims, and starter links that alerts depend on but research never confirmed. Claude
walks through the findings with you and fixes the ones you agree to. In the terminal, long runs
of low findings of one kind (say, every starter link after seeding a pack) are summarised after
the first three; torus audit --all lists them all./torus:challenge): Torus lists a thesis's weak points (thin or expiring
assumptions, strong supplier or customer links it never mentions, triggers that can't fire,
opposing theses and verdicts) and a devil's-advocate subagent researches the counter-case. You
approve any risks or triggers it proposes; it never changes the thesis itself.torus whatif "TSMC fab outage halts CoWoS" TSM, /torus:whatif): runs the alert
logic on a hypothetical event and shows every thesis it would reach, through which links, and
whether it would raise an alert. Nothing is recorded./torus:undo): after each turn Torus says what it recorded. Undo
retracts claims (or restores the version a correction replaced), archives theses the session
created, retires links (restoring a starter link it replaced) and withdraws ruled-out ideas.
History keeps everything, so "what did we believe on date X" still works.Track a thesis against its stock and see how the call is doing:
Each tracked thesis shows its return against a benchmark (SPY unless set) in the direction of its stance, so a bearish thesis gains when the stock lags, and its worst drawdown. Theses get an outcome when you mark them confirmed or broken, or by price when they reach their horizon. Calibration compares your initial confidence with how often you were right: a Brier score (0 is perfect; always saying 50% scores 0.25) and a table by confidence band, e.g. "your 80%+ calls were right 6 times out of 10". It covers every decided thesis, whether or not it's tracked against a price, and says so when fewer than 10 are decided: below that it's an anecdote, not a measure.
Prices come from Yahoo Finance's public chart endpoint, which needs no key but is unofficial and
can change. If it fails, import closes from a CSV, or set price_source to none. The scorecard
is a record of your own calls, not advice.
torus memo THESIS_ID --open (or /torus:memo) writes a thesis up as Markdown and a standalone
HTML page in TORUS_HOME/memos: what it rests on, each claim with its sources and verbatim
quotes, the claims inferences derive from, triggers, the events since and how they were judged,
ruled-out ideas, the scorecard and the full history. In Claude Code, /torus:memo also writes a
short summary from what the memo says.
torus schedule install (see above). torus doctor warns when
companies are watched but nothing runs the watchers. Each run also sends the notifications
and the scheduled digest (Alerts that reach you).auto_distill at ask (the default), Claude offers to run
/torus:distill when sessions are waiting. Set it to on and a background curator runs
(claude -p on Haiku, Torus tools only) when a session ends; this uses your subscription. One
curator runs at a time, and its own session is never captured.torus config set KEY VALUE (omit the value to reset; torus config get KEY to read):
Torus keeps your research local. It reads from these:
sec.gov): the ticker list, watched companies' filing lists, and filings Claude
reads or the watcher diffs. Your sec_contact email is sent as the User-Agent, as the SEC
requires.price_source isn't none.And it sends research out only where you tell it to:
digest_webhook), if you set one: the digest's text, which includes thesis
titles, alert and event titles, audit findings and scorecard moves.smtp_host), if you set up email: the same digest, to digest_email_to.Desktop notifications stay on your machine.
Claude's own web research goes through the app you use, as usual.
torus doctor --fix rebuilds a broken search index, closes duplicate links, saves the SEC email
(it asks, or pass --contact EMAIL), downloads the SEC ticker list when it's missing or more than
30 days old, installs the watcher schedule when companies are watched but nothing runs the
watchers, and rewrites a broken Claude Desktop or Codex entry (keeping a backup, and leaving a
Codex entry you wrote yourself alone). It says what it did and what it skipped, then checks
again.
Upgrading migrates the database automatically the next time any torus command opens it. Hook
activity is logged to ~/.torus/logs/hooks.log, background curator output to
~/.torus/logs/curator.log. Hooks never block or fail your session.
Apache License 2.0. See LICENSE.
Torus is a research memory, not financial advice.
Source: README.md at commit c64bcbd
One-call briefing at the start of a turn: spots known companies in `text`, recalls prior theses, claims, open alerts and ruled-out ideas about them, and returns the graph digest.
textPrior theses, claims and rejected hypotheses about entities and/or a question. Call this before starting research so you build on earlier work.
as_oflimitqueryentitiesResolve an entity and list its current relations.
entityrequiredKeyword search over current claims, evidence and theses.
limitqueryrequiredtypesChronological claims and thesis changes for one entity.
sinceuntilentityrequiredEverything recorded or revised across the graph since a point in time.
sincerequireduntilCreate an entity (or merge into the one with the same canonical_key).
kindnamerequiredaliasestickerscanonical_keyFold a duplicate entity into the one to keep; its links move over.
keeprequiredreasonrequiredduplicaterequiredRecord a source. Re-recording the same URI returns the existing record.
urirequiredkindquotetitlepublisherpublished_atRecord a finding. Facts and metrics need evidence_ids.
textrequiredentitiesvalid_tomentionedclaim_typerequiredconfidencerequiredvalid_fromderived_fromevidence_idscontradicting_evidence_idsCorrect a claim: the old version stays in history, the new one is current.
textreasonrequiredclaim_idrequiredconfidenceevidence_idsWithdraw a claim that turned out to be wrong (kept in history).
reasonrequiredclaim_idrequiredLink two entities, e.g. TSMC SUPPLIES NVIDIA. Re-recording an existing link updates it (confidence, backing claim, dates) instead of duplicating it.
relrequiredsourcerequiredtargetrequiredclaim_idvalid_toconfidencerequiredvalid_fromRecord a conclusion and the claims it rests on.
titlerequiredstancerequiredhorizonsubjecttriggersstatementrequiredconfidencerequiredrisk_claim_idsassumption_claim_idssupporting_claim_idsRevise a thesis: status, confidence, the claims it rests on, and its triggers, in one atomic step. Everything is kept in its history. Also tunes its alerts: a threshold of its own, and mutes (e.g. no alerts for a supplier's routine 10-Qs).
reasonrequiredstatushorizonadd_mutesthesis_idrequiredadd_claimsconfidenceadd_triggersalert_thresholdremove_mute_idsremove_claim_idsremove_trigger_ids+1 moreRecord an idea that was checked and ruled out, so it isn't re-explored.
textrequiredreasonrequiredentitiesRecord an event and raise alerts on theses it may affect (through the companies involved and their suppliers, customers and competitors).
urikindtitlerequiredsummaryentitiesrequiredmaterialityoccurred_atrequiredDry run: which theses would an event on these entities affect, and why? Records nothing.
entitiesrequiredoccurred_atAlerts raised by events on theses, highest score first. Each shows how its score was built: graph score x event materiality x (1 + trigger match).
statusgroupedthesis_idHow useful alerts have been: verdicts, and the share judged related (precision) by source, form, hops and materiality.
sinceJudge an alert after reading the event and the thesis's claims; the thesis is updated accordingly and the change logged with the alert.
statusverdictrequiredalert_idrequiredrationalerequiredconfidenceThe entity's neighbourhood: companies within `hops` links (suppliers, customers, competitors...), the links among them, and theses about them.
hopsentityrequiredvalid_atrelationsHow two entities are connected, e.g. ASML -SUPPLIES-> TSMC -SUPPLIES-> AMD. Shortest and most confident paths first.
sourcerequiredtargetrequiredmax_hopsrelationsWatch a company: its new SEC filings become events (on the next `torus watch run`). Subjects of open theses are watched automatically.
noteentityrequiredRemove a company from the explicit watchlist.
entityrequiredWhat the watchers follow: the watchlist, open-thesis entities, feeds.
Takes no parameters.
Lease the oldest research session waiting to be distilled into the graph. Returns its transcript digest, what was already recorded (don't duplicate it), the sources the agent looked at, and existing theses on its entities. Until `complete_distillation`, new records are attributed to that session.
transcript_charsFinish a leased distillation job (or hand it back if it failed).
failedsummaryrequiredqueue_idrequiredStart covering companies: add them, watch their filings, suggest starter packs, and queue a mapping job for each (run by the company-mapper subagent, which reads their annual reports and records suppliers, customers and competitors).
watchtickersrequiredAdd a curated starter pack of companies and supplier/competitor links. Never overrides links research recorded.
packrequiredLease the next onboarded company to map from its annual report. Until `complete_mapping`, links you record are marked as curator links.
Takes no parameters.
Finish a leased mapping job (or hand it back if it failed).
failedsummaryrequiredqueue_idrequiredWhat the most recent research sessions recorded, newest first, and which records can still be undone.
sessionsRevert records the user doesn't want: claims are retracted (corrections go back to the earlier version), theses the session created are archived, links retired, ruled-out ideas withdrawn. History keeps everything.
reasonrecordsrequiredsession_idrequiredWhat if this happened? Which theses would it reach, through which links, and would it raise alerts. Records nothing.
entitiesrequireddescriptionrequiredmaterialityoccurred_atWrite a thesis up as a memo (Markdown and a standalone HTML page): what it rests on with sources and quotes, triggers, events and verdicts, ruled-out ideas and history. Saved under TORUS_HOME/memos.
thesis_idrequiredWrite the interactive graph explorer HTML file (under TORUS_HOME/explorer.html) and open it in the user's browser.
hopsfocusopen_browserWhere a thesis is weakest: thin or expiring assumptions, strong supplier or customer dependencies it never mentions, triggers that can't fire on events, and opposing views already in the graph. Input for /torus:challenge.
thesis_idrequiredPut a thesis on the scorecard: its return against a benchmark from an entry price and date, in the direction of its stance.
stoptickerbenchmarkthesis_idrequiredentry_dateentry_priceHow tracked theses have done against their benchmarks, their outcomes, and calibration: did the user's confidence match how often they were right?
refreshRecord whether a thesis turned out right, wrong or unclear (overrides the automatic outcome; used for calibration).
noteoutcomerequiredthesis_idrequiredHealth check of the research graph: expired or thinly sourced assumptions, theses past their horizon or gone stale, claims that may conflict, unconfirmed starter links, orphans. Each finding suggests a fix.
stale_daysWhat happened in the research over the last days: new alerts by thesis, thesis status and confidence changes, theses coming due, audit counts, scorecard moves and activity. ``markdown`` is ready to show.
since_daysWhat changed in a section of a company's latest filing against the previous one of the same form: new, dropped and reworded sentences.
formcompanyrequiredsectionmax_itemsRead a company's SEC filing as text, by section, from EDGAR (cached). Long sections come in pages: pass `next_offset` back as `offset`.
formindexoffsetcompanyrequiredsectionmax_chars90% of the applicable checks met
How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.
Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.
pip install torus-kgtorus setuppip install torus-kgtorus init --refresh-reference --contact [email protected] # SEC ticker list (the SEC asks for an email)torus config set sec_contact [email protected] # used when reading and watching filingstorus doctor # check that everything is in order/plugin marketplace add sekhar14/torus/plugin install torus@torus/torus:onboard NVDA AMD TSM AVGOtorus schedule install # checks filings, news and prices every hour{ "mcpServers": { "torus": { "command": "/full/path/to/torus", "args": ["serve"], "env": { "TORUS_HARNESS": "claude-desktop" } } }}[mcp_servers.torus]command = "/full/path/to/torus"args = ["serve"]
[mcp_servers.torus.env]TORUS_HARNESS = "codex"torus explore # the whole graphtorus explore NVDA --hops 2 # NVIDIA and everything within two linkstorus explore --no-open # write the file without opening a browsertorus graph packs # the packs abovetorus graph seed software # add one (safe to repeat)torus graph neighbors TSM --hops 2 # links around a companytorus graph paths ASML AMD # how two companies connect: ASML -SUPPLIES-> TSMC -SUPPLIES-> AMDtorus watch add NVDA # optional: onboarded companies and open theses' companies are watched automaticallytorus feed add https://example.com/semis.rss --name "Semis wire"torus watch list # what is watched, and whytorus watch run # one pass; exit code 1 if any source failedtorus schedule install # run it every hour; --every 6 for every 6 hourstorus schedule status # or: torus schedule removetorus alerts list # open alerts and how each scoredtorus alerts stats --days 90 # share judged related, by source, form, hops, materialitytorus alerts threshold 0.3 # global minimum score (default 0.2)torus alerts threshold 0.5 --thesis ID # per thesistorus alerts mute ID --entity TSM --form 10-Q # no alerts on this thesis for TSMC 10-Qstorus alerts mutes # list mutes; torus alerts unmute MUTE_IDtorus notify test # check that notifications show up on this machinetorus config set notify moderate # strong (default), moderate or offtorus watch run --no-notify # one pass without notificationstorus digest # the last day, as Markdown and HTML in TORUS_HOME/digeststorus digest --since 2w --open # 12h, 1d, 2w or a date (YYYY-MM-DD); open the HTMLtorus digest --json # the same data as JSONtorus config set digest daily # off (default), daily or weeklytorus config set digest_hour 8 # local time (default 8)torus config set digest_weekday mon # for weekly (default mon)torus config set digest_webhook https://hooks.slack.com/services/...torus config set digest_webhook https://ntfy.sh/your-private-topic # push to your phone with the ntfy apptorus config set digest_email_to [email protected]torus config set smtp_host smtp.gmail.comtorus config set smtp_user [email protected]torus digest email-password # asks for the password (for Gmail, an app password)torus digest test-send # a short test message to each destinationtorus digest --send # send now: everything since the last digest senttorus scorecard track THESIS_ID # default: the subject's ticker, entry = the close when recordedtorus scorecard --refresh # fetch closes, then show returns and calibrationtorus scorecard outcome THESIS_ID right # right, wrong, unclear or clear; your call beats the automatic onetorus scorecard import NVDA prices.csv # load closes yourself (date,close)torus doctor # health check (schema, integrity, search index, hooks, watchers, schedule, notifications, digest)torus doctor --fix # fix what it flags (see below)torus doctor --repair # only rebuild the search index and close duplicate linkstorus export # back up everything to TORUS_HOME/exports/*.jsonltorus import backup.jsonl # restore into a fresh TORUS_HOMEtorus status # row counts and queue statepython -m venv .venv.venv/Scripts/python -m pip install -e ".[dev]" # .venv/bin/python on macOS/Linux.venv/Scripts/python -m pytest # unit + integration (no network).venv/Scripts/python -m pytest -m perf tests/perf # performance checks on a 100k-claim graph