Reshape long ChatGPT and Claude conversations: remove unwanted turns, isolate topics, and continue from clean context.
1
stars
47
commits
TypeScript
primary language
Aug 24, 2026
updated
Reshape your AI conversations.
Long AI chats get messy. You start on one thing, drift into two others, paste something you would rather not keep — and somewhere in the middle you lose patience and tell the model exactly what you think of it.
Chat Threads is a side panel for Chrome and Edge that untangles one long AI conversation into separate topic conversations. Clean up your act on the way out: remove the rant, keep the useful context.
Your original ChatGPT or Claude conversation is never changed. Chat Threads works on a copy.
Install from the Chrome Web Store — or build it from source.

What the demo shows. A long conversation holds several unrelated discussions. Chat Threads finds them. You review a topic and remove what you do not want. You copy out clean conversations and carry on. The conversation in the demo is invented for it — no real chat appears anywhere in this repository.
Scan a long conversation quickly by reading only what you asked, with the assistant's reply one click away when you want it.
Exclude whole turns, or edit the working copy of a turn to cut one paragraph and keep the rest. Removing turns from a ChatGPT or Claude conversation here never changes the conversation itself — only the copy you are about to carry forward.
Assign turns to topics by hand, or press Find Topics and have a model you choose propose the split. Review its choices, move anything it got wrong, and generate separate conversations that are ready to continue.
Manual cleaning and splitting need no account, no API key and no network requests. Find Topics is the one optional extra, and it only runs when you press the button.
Open conversation → Reshape → Review → Copy → Continue
One click, and it updates itself. This is the way to install it unless you have a reason to build from source. It works in Microsoft Edge too: Edge installs Chrome Web Store extensions once you allow it from the banner Edge shows.
Then:
You will click the icon again after reloading the page, or on a different tab. If you would rather not, the panel offers to let you grant ongoing access to chatgpt.com and claude.ai, which you can revoke at any time.
Requires Chrome or Edge 116 or later (the side panel API).
The extension also loads unpacked, which is what you want if you are changing it or would rather run something you compiled yourself. About a minute, and needs Node.js.
git clone https://github.com/onyourmark/chat-threads.git
cd chat-threads
npm install
npm run icons # generates the extension icons
npm run build # writes the unpacked extension to dist/
Or take the built chat-threads-<version>.zip from the
latest release
and unzip it — no Node.js, nothing to compile. The archive is reproducible:
running npm run package on the tagged commit produces a byte-identical file,
so you can check what you downloaded against the source it claims to come from.
The SHA-256 is published with the release.
Either way, load it:
chrome://extensions — or edge://extensions in Microsoft Edge.dist/ folder the build created.An unpacked extension does not update itself, and Chrome will remind you now and again that developer mode is on. The Web Store install has neither.
Chat Threads has no backend, no analytics and no telemetry. It has no permanent access to your browsing: it reads a conversation only after you explicitly invoke it on that tab, and that access ends when you navigate away. Ordinary cleaning and splitting happen entirely in your browser.
PRIVACY.md describes exactly what is read, stored and sent, and names the code that makes each claim true. SECURITY.md covers the threat model and how untrusted conversation text is handled.
ChatGPT and Claude both have branching, but branching answers a different question. Branching asks "what if I had said something else here?" Chat Threads answers "I want to keep going, but not with all of this."
That comes up constantly:
The usual workaround is to select the whole page, copy it and hand-edit the mess. That breaks on long conversations, because the page only renders part of them.
Think of Chat Threads as a ChatGPT and Claude conversation editor that never edits the original. You reshape a copy — clean the AI conversation context you want to keep, split AI conversations that drifted into separate subjects — and then continue a cleaned AI conversation in a fresh chat.
The three above are the reason to install it. The rest of what it does, in one table:
| Reads the whole conversation | From ChatGPT's and Claude's own conversation data, not from the visible page — collapsed and not-yet-rendered turns are included. Tested live on a 449-turn ChatGPT conversation |
| Asks before it reads anything | No standing site access; it can only read a tab you point it at with the toolbar icon |
| Readable file references | ChatGPT's private "attached file" markers become [Reference to attached file: notes.md] rather than raw syntax |
| Follows the branch you are on | Reconstructs the branch currently displayed, and says so when it cannot confirm which one that is |
| Finds where a chat was branched | On a ChatGPT chat started with "Branch in new chat", shows the turn it branched from and jumps straight to it |
| Never edits the original | Everything happens on a working copy the extension holds in memory |
| Prompts-only view | User turns in order, with the matching reply one click away |
| Exclude and restore | Leave a turn out of the result, put it back at any time |
| Edit working copies | Change a turn's text; the original is kept so you can compare and restore |
| Manual topic splitting | Any number of topics, editable names, per-turn assignment, plus Shared and Unassigned |
| A turn in more than one topic | Add a turn to a second topic and it appears in both, and in no others — without the all-or-nothing of Shared |
| One topic made for you | "Why is AI so stupid?" — for turns spent cursing at, arguing with or venting at the AI. Rename it, remove it, or ignore it |
| Review a topic and cut it | Open any topic on its own, tick the turns that really belong, and take them out of the cleaned conversation in one go |
| Optional AI topic suggestions | Bring your own API key; nothing is sent until you press the button |
| Honest about retrieval | Reports "complete", "unconfirmed" or "incomplete" and never passes off a partial transcript as a whole one |
| Preview, copy, download | Markdown, plain text and JSON; the preview is the exact text that gets copied |
| Export every topic at once | One file per topic, named after the topic, as Markdown or plain text — in a .zip or as separate downloads |
| Ask for a topic by name | Add a topic, press Find Topics, and it looks for the turns that belong to it alongside the ones it finds itself |
| Panel size control | Chrome's page zoom does not reach a side panel, so the panel has its own A− / A+ |
npm run build # build the unpacked extension into dist/
npm run test # run the test suite
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run check # all of the above, in order
After a rebuild, press the reload button on the Chat Threads card in
chrome://extensions, then click the Chat Threads icon on the provider tab
again. There is no need to reload the page itself — the reader script is
injected on demand rather than declared in the manifest.
Tests never touch a live account. Everything runs against hand-written fixtures
in tests/fixtures/. Please do not commit real conversation data — see
CONTRIBUTING.md.
Every conversation starts with one topic already made: Why is AI so stupid? — for turns spent cursing at, arguing with, or venting at the AI rather than getting anything done. Those are usually the first thing you want out of a transcript you are about to carry forward, and picking them out by hand is tedious.
It is a default, not a fixture. Rename it, remove it, or leave it empty and nothing else changes; it only appears in your output once something is in it. Reset Changes brings it back.
You create further topics and assign each turn to one of:
Excluding a turn overrides all of that: an excluded turn appears nowhere.
A turn belongs to exactly one topic. Shared is how a turn reaches more than
one, and it always carries the complete turn rather than a fragment of it. The
Output tab warns you when included turns are left unassigned, so nothing goes
missing without your noticing.
Generated conversations always keep the original chronological order, including shared turns, which slot back into the position they originally occupied.
A topic is good for two different things, and you do not have to choose:
The second is what Review is for. Every topic has one — the built-in topic, topics you made yourself, and topics Find Topics proposed, all through the same controls.
Review opens that topic on its own, showing only the turns assigned to it, with every turn ticked. Ticked means "remove this". That default matches the usual intention — take this whole thread out — and leaves you to untick the exceptions. There is a running count, Select all and Select none, and you can move a turn to a different topic from here if it was filed wrongly.
Remove selected turns then excludes exactly the ticked ones. "Excluded" here means the same thing it means in the Clean view: it is the same switch, so a turn removed through a topic shows as excluded in Clean, can be put back there, and is restored by Reset Changes. There is no separate idea of deletion, and nothing is ever removed from your actual ChatGPT or Claude conversation.
Turns marked Shared are deliberately not offered in Review. They belong to every topic, so removing one while looking at a single topic would quietly take it out of all the others.
Pressing Find Topics sends the turns you have kept — shortened to the first 1,500 characters each — to the model provider you configured, and asks it to return a strict JSON structure: a list of topics, and one assignment per turn, with an "uncertain" flag it is told to set whenever it is unsure.
Long conversations. A very long chat does not fit in one request — an 866-turn conversation is around 688,000 characters, well past what a model can read at once. Chat Threads works this out before it sends anything and divides the conversation into sections, then does three things: each section reports the topics it contains, one request reconciles those lists into a single set for the whole conversation, and each section is read again and sorted into that set. That middle step is why you get one coherent list rather than "Chrome extension publishing", "Web Store submission" and "Chrome Store setup" as three separate topics.
You do not have to do anything differently: press the button and wait. The panel says how many sections and roughly how many requests before you press it, shows which section it is on while it runs, and has a Stop button. A 876-turn conversation comes to 15 sections and 31 requests, with a stated ceiling of 36 if a reply has to be asked for twice. A conversation so large that even this would be unreasonable is refused before anything is sent, rather than turning into a hundred paid requests.
Each request carries the schema for its step, so the model is held to the exact shape the next step needs rather than merely to "some JSON". If a reply comes back in the wrong shape anyway, that one step is asked again — a few times per run at most, never a retry loop.
Which key you need, and when. Reading a conversation never needs one: Chat Threads uses the ChatGPT or Claude session you are already signed in with. An API key is only for Find Topics, and only for the provider you pick there. The two choices are independent of where the conversation came from — you can analyse a Claude conversation with an OpenAI key, and an Anthropic key is required only if you choose Anthropic as the Find Topics provider.
It is also told that Why is AI so stupid? already exists, so it keeps that topic rather than inventing its own version of it, and it is given rules for what goes in: swearing at the assistant, arguing with it about its own behaviour, venting at it — along with the assistant's side of that exchange, so you can lift the whole thing out in one go. The rules it is given are mostly about what does not belong there: ordinary discussion about AI, technical criticism, corrections, and plain disagreement all stay with the work, even when you were annoyed at the time.
That reply is validated before anything happens. A malformed reply, an invented turn number, a topic that was not proposed, or a name containing control characters is rejected or dropped, and you are told what happened. A rejected proposal changes nothing.
An accepted proposal writes into the same topic list and the same per-turn dropdowns you use manually. There is no separate AI state — changing a dropdown simply overrides what the model said, and turns you have overridden are marked so you can see which choices were yours.
Output has an Export everything button once there is more than one
conversation to export. It writes one file per conversation — the cleaned one
and each topic — named after the topic itself. The built-in topic comes out as
Why is AI so stupid.md, with only the question mark removed, because a
filesystem will not take it.
Markdown by default, so headings and code blocks survive; tick Plain text
for .txt instead. Download .zip gives you a single file; Download
separately saves them one at a time, which Chrome may ask you to allow.
Whatever is in Split at that moment is what you get, including any assignment you changed by hand after the suggestions arrived.
Find Topics is not only "tell me what is in here". Add a topic, give it a name, and press the button: the model is told that topic already exists and is asked to find the turns that belong to it, alongside the ones it identifies itself. Clear the topics first and add just one, and you have asked a narrower question — which parts of this are about that?
If nothing in the conversation fits, the topic stays empty. That is the honest answer, and it is the one the model is instructed to give rather than stretching unrelated turns to fill it.
Your own topics survive a re-run. Topics from a previous suggestion do not — pressing the button again is how you ask for a different answer.
Put it in both. A turn has one topic in the Goes to dropdown and can be added to others with Add to; the extra ones show as chips you can click to remove. It then appears once in each of those topics' conversations, and in no others. Find Topics does the same thing — a turn it thinks belongs to two topics is put in exactly those two.
When it is genuinely a judgement call rather than real dual membership, the model files the turn in one topic and marks it Unsure, which shows as a badge in Split so the turns worth a second look are the flagged ones. A turn you have moved yourself is marked Your choice and is never overwritten by a later suggestion.
Shared is a much stronger thing, and is yours alone to set: it means the turn belongs with every topic — an opening instruction, say — and puts a full copy in each of them. Find Topics is not allowed to use it. It used to be, and on a 876-turn conversation it reached for Shared several hundred times as a way of saying "not sure", which made every exported topic file about two thirds of the whole conversation. Ambiguity is now expressed by marking a turn Unsure, or by putting it in the two topics it actually belongs to.
The count beside a topic shows exactly what its conversation will contain: its own turns, and separately how many Shared turns will be added to every topic.
ChatGPT lets you take a message in the middle of a conversation and start a new chat from it. Months and a few hundred turns later, that boundary is very hard to find again: it sits somewhere in the middle of a long scroll, and Ctrl-F cannot reach it because the marker is not text you ever typed.
If a conversation was started that way, Chat Threads says so at the top of the panel:
Branch point: Turn 184 — Assistant: 1:1:1 by weight is a good place to start. — Go to branch point
Pressing the button jumps to that turn and flashes it, and the turn carries a Branch point badge wherever it appears. When ChatGPT recorded which conversation the branch came from, there is also a link back to it.
This reads ChatGPT's own record of the branch — the metadata it writes on the first message of the new chat — rather than searching the page for the "Branched from" wording, which can change or be translated, and which is not in the page at all until you scroll to it. It never guesses from the shape of the conversation: a regenerated answer and an edited prompt both fork ChatGPT's internal tree, and neither is reported as a branch.
It runs locally, on the conversation Chat Threads has already loaded. No API key, no request, nothing sent anywhere.
What it cannot do. ChatGPT records the branch only on the new chat, pointing back at the original — there is no field anywhere saying "this conversation has branches". So opening the original conversation will not show you where somebody branched out of it. Open the branched chat instead, which is where the record lives. Claude has no equivalent feature, and Chat Threads says so rather than reporting that a Claude chat has no branches.
Be aware of these before relying on it:
spec.pdf was attached, and an inline mention becomes
[Reference to attached file: spec.pdf]; it does not contain the file.Four layers, with the provider-specific code confined to the first:
src/adapters/ ChatGPT and Claude adapters — the only provider-aware code
src/model/ The common conversation representation + message validation
src/operations/ Working copy, exclusion, editing, topics, transcripts
src/ai/ Topic-analysis interface, schema, validation, providers
src/sidepanel/ The React side panel
src/background/ Service worker (opens the panel, reports the active tab)
src/content/ Content script (runs an adapter in the page)
Once a conversation has been normalized, nothing above src/adapters/ knows
or cares which provider it came from. Adding a provider means writing one
adapter and adding it to src/adapters/registry.ts.
The distinction the whole design rests on: a SourceConversation is frozen at
retrieval and never written to; a WorkingState holds editable copies. That is
what makes "reset", "restore original" and "your original chat is untouched"
true rather than aspirational.
More detail in docs/ARCHITECTURE.md.
Chat Threads is public source, but is not currently accepting pull requests or other external code contributions. That is about maintainer time, not about anyone's work — reviewing changes to an extension that reads private conversations is a commitment the owner cannot make right now, and it seems better to say so than to leave pull requests unanswered.
Reports, on the other hand, are very welcome:
Whichever you use, please do not include conversation content. Both templates are written so you never need to, and a screenshot of the side panel is a screenshot of your own conversation. CONTRIBUTING.md has the detail.
Forks are welcome under the licence. A fork is your software, not an official Chat Threads release, and it cannot alter anyone's installed copy. What counts as an official release is set out in docs/RELEASE-SECURITY.md.
Ideas, not commitments:
Deliberately out of scope: a Chat Threads account, cloud storage, a backend, analytics, or turning this into a general-purpose AI chat client.
Certain technology implemented in Chat Threads is the subject of a U.S. provisional patent application filed August 20, 2026.
MIT — see LICENSE.
47 commits
Hacker News (1)
TypeScript
95.3%
CSS
2.6%
JavaScript
2.1%
Reshape long ChatGPT and Claude conversations: remove unwanted turns, isolate topics, and continue from clean context.
1
stars
47
commits
TypeScript
primary language
Aug 24, 2026
updated
Reshape your AI conversations.
Long AI chats get messy. You start on one thing, drift into two others, paste something you would rather not keep — and somewhere in the middle you lose patience and tell the model exactly what you think of it.
Chat Threads is a side panel for Chrome and Edge that untangles one long AI conversation into separate topic conversations. Clean up your act on the way out: remove the rant, keep the useful context.
Your original ChatGPT or Claude conversation is never changed. Chat Threads works on a copy.
Install from the Chrome Web Store — or build it from source.

What the demo shows. A long conversation holds several unrelated discussions. Chat Threads finds them. You review a topic and remove what you do not want. You copy out clean conversations and carry on. The conversation in the demo is invented for it — no real chat appears anywhere in this repository.
Scan a long conversation quickly by reading only what you asked, with the assistant's reply one click away when you want it.
Exclude whole turns, or edit the working copy of a turn to cut one paragraph and keep the rest. Removing turns from a ChatGPT or Claude conversation here never changes the conversation itself — only the copy you are about to carry forward.
Assign turns to topics by hand, or press Find Topics and have a model you choose propose the split. Review its choices, move anything it got wrong, and generate separate conversations that are ready to continue.
Manual cleaning and splitting need no account, no API key and no network requests. Find Topics is the one optional extra, and it only runs when you press the button.
Open conversation → Reshape → Review → Copy → Continue
One click, and it updates itself. This is the way to install it unless you have a reason to build from source. It works in Microsoft Edge too: Edge installs Chrome Web Store extensions once you allow it from the banner Edge shows.
Then:
You will click the icon again after reloading the page, or on a different tab. If you would rather not, the panel offers to let you grant ongoing access to chatgpt.com and claude.ai, which you can revoke at any time.
Requires Chrome or Edge 116 or later (the side panel API).
The extension also loads unpacked, which is what you want if you are changing it or would rather run something you compiled yourself. About a minute, and needs Node.js.
git clone https://github.com/onyourmark/chat-threads.git
cd chat-threads
npm install
npm run icons # generates the extension icons
npm run build # writes the unpacked extension to dist/
Or take the built chat-threads-<version>.zip from the
latest release
and unzip it — no Node.js, nothing to compile. The archive is reproducible:
running npm run package on the tagged commit produces a byte-identical file,
so you can check what you downloaded against the source it claims to come from.
The SHA-256 is published with the release.
Either way, load it:
chrome://extensions — or edge://extensions in Microsoft Edge.dist/ folder the build created.An unpacked extension does not update itself, and Chrome will remind you now and again that developer mode is on. The Web Store install has neither.
Chat Threads has no backend, no analytics and no telemetry. It has no permanent access to your browsing: it reads a conversation only after you explicitly invoke it on that tab, and that access ends when you navigate away. Ordinary cleaning and splitting happen entirely in your browser.
PRIVACY.md describes exactly what is read, stored and sent, and names the code that makes each claim true. SECURITY.md covers the threat model and how untrusted conversation text is handled.
ChatGPT and Claude both have branching, but branching answers a different question. Branching asks "what if I had said something else here?" Chat Threads answers "I want to keep going, but not with all of this."
That comes up constantly:
The usual workaround is to select the whole page, copy it and hand-edit the mess. That breaks on long conversations, because the page only renders part of them.
Think of Chat Threads as a ChatGPT and Claude conversation editor that never edits the original. You reshape a copy — clean the AI conversation context you want to keep, split AI conversations that drifted into separate subjects — and then continue a cleaned AI conversation in a fresh chat.
The three above are the reason to install it. The rest of what it does, in one table:
| Reads the whole conversation | From ChatGPT's and Claude's own conversation data, not from the visible page — collapsed and not-yet-rendered turns are included. Tested live on a 449-turn ChatGPT conversation |
| Asks before it reads anything | No standing site access; it can only read a tab you point it at with the toolbar icon |
| Readable file references | ChatGPT's private "attached file" markers become [Reference to attached file: notes.md] rather than raw syntax |
| Follows the branch you are on | Reconstructs the branch currently displayed, and says so when it cannot confirm which one that is |
| Finds where a chat was branched | On a ChatGPT chat started with "Branch in new chat", shows the turn it branched from and jumps straight to it |
| Never edits the original | Everything happens on a working copy the extension holds in memory |
| Prompts-only view | User turns in order, with the matching reply one click away |
| Exclude and restore | Leave a turn out of the result, put it back at any time |
| Edit working copies | Change a turn's text; the original is kept so you can compare and restore |
| Manual topic splitting | Any number of topics, editable names, per-turn assignment, plus Shared and Unassigned |
| A turn in more than one topic | Add a turn to a second topic and it appears in both, and in no others — without the all-or-nothing of Shared |
| One topic made for you | "Why is AI so stupid?" — for turns spent cursing at, arguing with or venting at the AI. Rename it, remove it, or ignore it |
| Review a topic and cut it | Open any topic on its own, tick the turns that really belong, and take them out of the cleaned conversation in one go |
| Optional AI topic suggestions | Bring your own API key; nothing is sent until you press the button |
| Honest about retrieval | Reports "complete", "unconfirmed" or "incomplete" and never passes off a partial transcript as a whole one |
| Preview, copy, download | Markdown, plain text and JSON; the preview is the exact text that gets copied |
| Export every topic at once | One file per topic, named after the topic, as Markdown or plain text — in a .zip or as separate downloads |
| Ask for a topic by name | Add a topic, press Find Topics, and it looks for the turns that belong to it alongside the ones it finds itself |
| Panel size control | Chrome's page zoom does not reach a side panel, so the panel has its own A− / A+ |
npm run build # build the unpacked extension into dist/
npm run test # run the test suite
npm run lint # eslint
npm run typecheck # tsc --noEmit
npm run check # all of the above, in order
After a rebuild, press the reload button on the Chat Threads card in
chrome://extensions, then click the Chat Threads icon on the provider tab
again. There is no need to reload the page itself — the reader script is
injected on demand rather than declared in the manifest.
Tests never touch a live account. Everything runs against hand-written fixtures
in tests/fixtures/. Please do not commit real conversation data — see
CONTRIBUTING.md.
Every conversation starts with one topic already made: Why is AI so stupid? — for turns spent cursing at, arguing with, or venting at the AI rather than getting anything done. Those are usually the first thing you want out of a transcript you are about to carry forward, and picking them out by hand is tedious.
It is a default, not a fixture. Rename it, remove it, or leave it empty and nothing else changes; it only appears in your output once something is in it. Reset Changes brings it back.
You create further topics and assign each turn to one of:
Excluding a turn overrides all of that: an excluded turn appears nowhere.
A turn belongs to exactly one topic. Shared is how a turn reaches more than
one, and it always carries the complete turn rather than a fragment of it. The
Output tab warns you when included turns are left unassigned, so nothing goes
missing without your noticing.
Generated conversations always keep the original chronological order, including shared turns, which slot back into the position they originally occupied.
A topic is good for two different things, and you do not have to choose:
The second is what Review is for. Every topic has one — the built-in topic, topics you made yourself, and topics Find Topics proposed, all through the same controls.
Review opens that topic on its own, showing only the turns assigned to it, with every turn ticked. Ticked means "remove this". That default matches the usual intention — take this whole thread out — and leaves you to untick the exceptions. There is a running count, Select all and Select none, and you can move a turn to a different topic from here if it was filed wrongly.
Remove selected turns then excludes exactly the ticked ones. "Excluded" here means the same thing it means in the Clean view: it is the same switch, so a turn removed through a topic shows as excluded in Clean, can be put back there, and is restored by Reset Changes. There is no separate idea of deletion, and nothing is ever removed from your actual ChatGPT or Claude conversation.
Turns marked Shared are deliberately not offered in Review. They belong to every topic, so removing one while looking at a single topic would quietly take it out of all the others.
Pressing Find Topics sends the turns you have kept — shortened to the first 1,500 characters each — to the model provider you configured, and asks it to return a strict JSON structure: a list of topics, and one assignment per turn, with an "uncertain" flag it is told to set whenever it is unsure.
Long conversations. A very long chat does not fit in one request — an 866-turn conversation is around 688,000 characters, well past what a model can read at once. Chat Threads works this out before it sends anything and divides the conversation into sections, then does three things: each section reports the topics it contains, one request reconciles those lists into a single set for the whole conversation, and each section is read again and sorted into that set. That middle step is why you get one coherent list rather than "Chrome extension publishing", "Web Store submission" and "Chrome Store setup" as three separate topics.
You do not have to do anything differently: press the button and wait. The panel says how many sections and roughly how many requests before you press it, shows which section it is on while it runs, and has a Stop button. A 876-turn conversation comes to 15 sections and 31 requests, with a stated ceiling of 36 if a reply has to be asked for twice. A conversation so large that even this would be unreasonable is refused before anything is sent, rather than turning into a hundred paid requests.
Each request carries the schema for its step, so the model is held to the exact shape the next step needs rather than merely to "some JSON". If a reply comes back in the wrong shape anyway, that one step is asked again — a few times per run at most, never a retry loop.
Which key you need, and when. Reading a conversation never needs one: Chat Threads uses the ChatGPT or Claude session you are already signed in with. An API key is only for Find Topics, and only for the provider you pick there. The two choices are independent of where the conversation came from — you can analyse a Claude conversation with an OpenAI key, and an Anthropic key is required only if you choose Anthropic as the Find Topics provider.
It is also told that Why is AI so stupid? already exists, so it keeps that topic rather than inventing its own version of it, and it is given rules for what goes in: swearing at the assistant, arguing with it about its own behaviour, venting at it — along with the assistant's side of that exchange, so you can lift the whole thing out in one go. The rules it is given are mostly about what does not belong there: ordinary discussion about AI, technical criticism, corrections, and plain disagreement all stay with the work, even when you were annoyed at the time.
That reply is validated before anything happens. A malformed reply, an invented turn number, a topic that was not proposed, or a name containing control characters is rejected or dropped, and you are told what happened. A rejected proposal changes nothing.
An accepted proposal writes into the same topic list and the same per-turn dropdowns you use manually. There is no separate AI state — changing a dropdown simply overrides what the model said, and turns you have overridden are marked so you can see which choices were yours.
Output has an Export everything button once there is more than one
conversation to export. It writes one file per conversation — the cleaned one
and each topic — named after the topic itself. The built-in topic comes out as
Why is AI so stupid.md, with only the question mark removed, because a
filesystem will not take it.
Markdown by default, so headings and code blocks survive; tick Plain text
for .txt instead. Download .zip gives you a single file; Download
separately saves them one at a time, which Chrome may ask you to allow.
Whatever is in Split at that moment is what you get, including any assignment you changed by hand after the suggestions arrived.
Find Topics is not only "tell me what is in here". Add a topic, give it a name, and press the button: the model is told that topic already exists and is asked to find the turns that belong to it, alongside the ones it identifies itself. Clear the topics first and add just one, and you have asked a narrower question — which parts of this are about that?
If nothing in the conversation fits, the topic stays empty. That is the honest answer, and it is the one the model is instructed to give rather than stretching unrelated turns to fill it.
Your own topics survive a re-run. Topics from a previous suggestion do not — pressing the button again is how you ask for a different answer.
Put it in both. A turn has one topic in the Goes to dropdown and can be added to others with Add to; the extra ones show as chips you can click to remove. It then appears once in each of those topics' conversations, and in no others. Find Topics does the same thing — a turn it thinks belongs to two topics is put in exactly those two.
When it is genuinely a judgement call rather than real dual membership, the model files the turn in one topic and marks it Unsure, which shows as a badge in Split so the turns worth a second look are the flagged ones. A turn you have moved yourself is marked Your choice and is never overwritten by a later suggestion.
Shared is a much stronger thing, and is yours alone to set: it means the turn belongs with every topic — an opening instruction, say — and puts a full copy in each of them. Find Topics is not allowed to use it. It used to be, and on a 876-turn conversation it reached for Shared several hundred times as a way of saying "not sure", which made every exported topic file about two thirds of the whole conversation. Ambiguity is now expressed by marking a turn Unsure, or by putting it in the two topics it actually belongs to.
The count beside a topic shows exactly what its conversation will contain: its own turns, and separately how many Shared turns will be added to every topic.
ChatGPT lets you take a message in the middle of a conversation and start a new chat from it. Months and a few hundred turns later, that boundary is very hard to find again: it sits somewhere in the middle of a long scroll, and Ctrl-F cannot reach it because the marker is not text you ever typed.
If a conversation was started that way, Chat Threads says so at the top of the panel:
Branch point: Turn 184 — Assistant: 1:1:1 by weight is a good place to start. — Go to branch point
Pressing the button jumps to that turn and flashes it, and the turn carries a Branch point badge wherever it appears. When ChatGPT recorded which conversation the branch came from, there is also a link back to it.
This reads ChatGPT's own record of the branch — the metadata it writes on the first message of the new chat — rather than searching the page for the "Branched from" wording, which can change or be translated, and which is not in the page at all until you scroll to it. It never guesses from the shape of the conversation: a regenerated answer and an edited prompt both fork ChatGPT's internal tree, and neither is reported as a branch.
It runs locally, on the conversation Chat Threads has already loaded. No API key, no request, nothing sent anywhere.
What it cannot do. ChatGPT records the branch only on the new chat, pointing back at the original — there is no field anywhere saying "this conversation has branches". So opening the original conversation will not show you where somebody branched out of it. Open the branched chat instead, which is where the record lives. Claude has no equivalent feature, and Chat Threads says so rather than reporting that a Claude chat has no branches.
Be aware of these before relying on it:
spec.pdf was attached, and an inline mention becomes
[Reference to attached file: spec.pdf]; it does not contain the file.Four layers, with the provider-specific code confined to the first:
src/adapters/ ChatGPT and Claude adapters — the only provider-aware code
src/model/ The common conversation representation + message validation
src/operations/ Working copy, exclusion, editing, topics, transcripts
src/ai/ Topic-analysis interface, schema, validation, providers
src/sidepanel/ The React side panel
src/background/ Service worker (opens the panel, reports the active tab)
src/content/ Content script (runs an adapter in the page)
Once a conversation has been normalized, nothing above src/adapters/ knows
or cares which provider it came from. Adding a provider means writing one
adapter and adding it to src/adapters/registry.ts.
The distinction the whole design rests on: a SourceConversation is frozen at
retrieval and never written to; a WorkingState holds editable copies. That is
what makes "reset", "restore original" and "your original chat is untouched"
true rather than aspirational.
More detail in docs/ARCHITECTURE.md.
Chat Threads is public source, but is not currently accepting pull requests or other external code contributions. That is about maintainer time, not about anyone's work — reviewing changes to an extension that reads private conversations is a commitment the owner cannot make right now, and it seems better to say so than to leave pull requests unanswered.
Reports, on the other hand, are very welcome:
Whichever you use, please do not include conversation content. Both templates are written so you never need to, and a screenshot of the side panel is a screenshot of your own conversation. CONTRIBUTING.md has the detail.
Forks are welcome under the licence. A fork is your software, not an official Chat Threads release, and it cannot alter anyone's installed copy. What counts as an official release is set out in docs/RELEASE-SECURITY.md.
Ideas, not commitments:
Deliberately out of scope: a Chat Threads account, cloud storage, a backend, analytics, or turning this into a general-purpose AI chat client.
Certain technology implemented in Chat Threads is the subject of a U.S. provisional patent application filed August 20, 2026.
MIT — see LICENSE.
Hacker News (1)
47 commits
TypeScript
95.3%
CSS
2.6%
JavaScript
2.1%