Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 1 addition & 7 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,7 @@
"supportUrl": "https://github.com/lperezmo/topic-filter-mod/issues",
"privacyPolicyUrl": "https://github.com/lperezmo/topic-filter-mod/blob/main/PRIVACY.md",
"license": "GPL-3.0-only",
"keywords": [
"mod",
"function-hooks",
"focus",
"placeholders",
"productivity"
],
"keywords": ["mod", "function-hooks", "focus", "placeholders", "productivity"],
"userConfig": {
"hideAnthropology": {
"type": "boolean",
Expand Down
18 changes: 15 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ on:
workflow_dispatch:
# Weekly, so a new Claude Code release that breaks the mod shows up on its own.
schedule:
- cron: "0 14 * * 1"
- cron: '0 14 * * 1'

permissions:
contents: read
Expand All @@ -21,12 +21,12 @@ jobs:
matrix:
os: [ubuntu-latest]
# 2.1.287 is the first release where mods load by default.
claude: ["2.1.287", "latest"]
claude: ['2.1.287', 'latest']
include:
- os: windows-latest
claude: latest
env:
DISABLE_AUTOUPDATER: "1"
DISABLE_AUTOUPDATER: '1'
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
Expand Down Expand Up @@ -56,3 +56,15 @@ jobs:
with:
src: tools/packs
- run: uv run pytest -q

format-and-lint:
name: bun fmt and lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
# bun fmt and bun lint are canary-only until they reach a stable release.
- uses: oven-sh/setup-bun@v2
with:
bun-version: canary
- run: bun fmt --check .
- run: bun lint --max-warnings 0 --format github
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Built by the Python pack builder in tools/packs, which has its own ruff checks.
packs/
tools/packs/
70 changes: 35 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ disappear. Your repos, notes and memory stay as they are.

![A Claude Code session summarizing a demo project: its reply uses codenames in place of the hidden terms, while the sidebar lists each hidden term, its codename, and where the hits came from](images/live_filter.png)

*Claude answers in codenames; the sidebar, shown to you only, maps each one back
_Claude answers in codenames; the sidebar, shown to you only, maps each one back
and counts where it was hidden. The dim `topic-filter: 50 hidden` sits in the
prompt footer.*
prompt footer._

## Install

Expand All @@ -46,15 +46,15 @@ plugin's rows. Enter or Space flips a switch or edits a field:

![The /config screen listing topic-filter's rows: one switch per built-in pack, other packs, the sidebar switches and the topics file](images/hide_config.png)

| Setting | Default | What it does |
| --- | --- | --- |
| Hide anthropology, biology, chemistry, genetics | off | One switch per [built-in pack](#topic-packs) |
| Other packs | empty | Names of [your own packs](#topic-packs), comma-separated |
| Start paused | off | Every session starts [paused](#pausing-it); `/topic-filter on` turns it on for that session |
| Sidebar | off | Opens [the sidebar](#the-sidebar) at every start |
| Sidebar: counts only | off | The sidebar shows counts by list, never a word or file name |
| Extra words to hide | empty | Comma-separated words or names; kept in secure storage, not in `settings.json` |
| Topics file (advanced) | empty | Where [the topics file](#the-topics-file) lives, if you want one |
| Setting | Default | What it does |
| ----------------------------------------------- | ------- | ------------------------------------------------------------------------------------------- |
| Hide anthropology, biology, chemistry, genetics | off | One switch per [built-in pack](#topic-packs) |
| Other packs | empty | Names of [your own packs](#topic-packs), comma-separated |
| Start paused | off | Every session starts [paused](#pausing-it); `/topic-filter on` turns it on for that session |
| Sidebar | off | Opens [the sidebar](#the-sidebar) at every start |
| Sidebar: counts only | off | The sidebar shows counts by list, never a word or file name |
| Extra words to hide | empty | Comma-separated words or names; kept in secure storage, not in `settings.json` |
| Topics file (advanced) | empty | Where [the topics file](#the-topics-file) lives, if you want one |

**Extra words to hide** is kept secret, so `/config` does not list it: set it
in `/plugin`: Installed, then `topic-filter`, then configure it. That screen
Expand Down Expand Up @@ -254,17 +254,17 @@ What Claude read before the pause keeps its placeholders.
}
```

| Field | Default | Meaning |
| --- | --- | --- |
| `placeholder` | `codename` | `codename` gives each term a capitalized word and a number (`Bubblegum7`, `Kazoo12`); the number keeps it from ever matching a word you really write. `tag` gives `[hidden-3fa2c1]`. |
| `informModel` | `true` | Tell the model that placeholders exist and how to treat them. Without it the model tends to treat `Bubblegum` as a real name and go looking for it. |
| `lists[].name` | `list N` | A label for you. It never reaches the model. |
| `lists[].terms` | `[]` | The terms to hide. |
| `lists[].mode` | `replace` | `replace` swaps each term for its placeholder. `drop-line` removes every line that mentions a term; in JSON (`gh ... --json`, MCP results) the whole array item goes. A typed prompt always gets placeholders, never lost lines. |
| `lists[].match` | `word` | `word` matches whole words only. `substring` matches inside words too (`maya` in `Mayapan`). |
| `lists[].restore` | `false` | When the model uses this list's placeholder in a tool call, write the real term back instead of refusing. For your own names (contacts, client names) that a tool must write out exactly. Not allowed on a list that uses a `pack`. |
| `lists[].pack` | none | A [topic pack](#topic-packs) whose terms join the list. |
| `lists[].exclude` | `[]` | Terms to leave out of the list, whatever brought them in (a pack or `terms`). Matched the same forgiving way as terms. |
| Field | Default | Meaning |
| ----------------- | ---------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `placeholder` | `codename` | `codename` gives each term a capitalized word and a number (`Bubblegum7`, `Kazoo12`); the number keeps it from ever matching a word you really write. `tag` gives `[hidden-3fa2c1]`. |
| `informModel` | `true` | Tell the model that placeholders exist and how to treat them. Without it the model tends to treat `Bubblegum` as a real name and go looking for it. |
| `lists[].name` | `list N` | A label for you. It never reaches the model. |
| `lists[].terms` | `[]` | The terms to hide. |
| `lists[].mode` | `replace` | `replace` swaps each term for its placeholder. `drop-line` removes every line that mentions a term; in JSON (`gh ... --json`, MCP results) the whole array item goes. A typed prompt always gets placeholders, never lost lines. |
| `lists[].match` | `word` | `word` matches whole words only. `substring` matches inside words too (`maya` in `Mayapan`). |
| `lists[].restore` | `false` | When the model uses this list's placeholder in a tool call, write the real term back instead of refusing. For your own names (contacts, client names) that a tool must write out exactly. Not allowed on a list that uses a `pack`. |
| `lists[].pack` | none | A [topic pack](#topic-packs) whose terms join the list. |
| `lists[].exclude` | `[]` | Terms to leave out of the list, whatever brought them in (a pack or `terms`). Matched the same forgiving way as terms. |

Matching ignores case and accents (`Teotihuacán` = `teotihuacan`), treats
spaces, hyphens and underscores as one separator (`secret repo` also finds
Expand Down Expand Up @@ -299,12 +299,12 @@ The session is told only that the settings need attention.

**Built-in packs** ship in this repository's [`packs/`](packs) folder:

| Pack | Terms | Covers |
| --- | --- | --- |
| Pack | Terms | Covers |
| -------------- | ----- | --------------------------------------------------------------------------------------------------------------------------- |
| `anthropology` | 2,167 | Ancient Mesoamerican, Andean and Egyptian sites, civilizations, rulers and deities; anthropology and archaeology vocabulary |
| `biology` | 270 | Molecular biology techniques, cellular processes, anatomical terms |
| `chemistry` | 940 | Chemical elements, named reactions, functional groups, laboratory equipment |
| `genetics` | 2,591 | Genetic disorders and syndromes; genetics and heredity vocabulary |
| `biology` | 270 | Molecular biology techniques, cellular processes, anatomical terms |
| `chemistry` | 940 | Chemical elements, named reactions, functional groups, laboratory equipment |
| `genetics` | 2,591 | Genetic disorders and syndromes; genetics and heredity vocabulary |

They are built by the script in [`tools/packs/`](tools/packs) from Wikipedia
and Wiktionary categories, word-frequency data and a dry run against ordinary
Expand Down Expand Up @@ -349,14 +349,14 @@ Text reaches the model through many doors, and there is no single outgoing
request to filter (`turn.step` carries a message count, not the messages). So
every door but the system prompt is hooked:

| What the model reads | Event |
| --- | --- |
| Tool results: Bash, Read, Grep, Glob, WebFetch, MCP tools, subagent answers, errors | `tool.call`, on the way up |
| Your typed prompt | `prompt.submit` |
| CLAUDE.md and the other first-message context blocks | `prompt.context` |
| Mentioned and edited files, nested CLAUDE.md, queued prompts, context added by classic hooks and plugins (Claude Code's own reminders pass unchanged) | `prompt.attachment` |
| Skill text, MCP tool descriptions (built-in tools keep theirs), slash command output | `skill.prompt`, `tool.describe`, `command.run` |
| Remote Control and peer deliveries | `session.receive` |
| What the model reads | Event |
| ----------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| Tool results: Bash, Read, Grep, Glob, WebFetch, MCP tools, subagent answers, errors | `tool.call`, on the way up |
| Your typed prompt | `prompt.submit` |
| CLAUDE.md and the other first-message context blocks | `prompt.context` |
| Mentioned and edited files, nested CLAUDE.md, queued prompts, context added by classic hooks and plugins (Claude Code's own reminders pass unchanged) | `prompt.attachment` |
| Skill text, MCP tool descriptions (built-in tools keep theirs), slash command output | `skill.prompt`, `tool.describe`, `command.run` |
| Remote Control and peer deliveries | `session.receive` |

Each of these hooks changes one thing: it replaces listed terms in the text
with their placeholders (or drops the line, for a `drop-line` list) and
Expand Down
10 changes: 10 additions & 0 deletions bunfig.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[fmt]
# Matches the style the code already had: no semicolons, single quotes, long lines.
printWidth = 120
semi = false
singleQuote = true

[lint]

[lint.rules]
"eqeqeq" = ["error", "smart"]
Loading
Loading