ctxttools 0.1.7__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. ctxttools-0.2.0/PKG-INFO +209 -0
  2. ctxttools-0.2.0/README.md +192 -0
  3. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/agents.py +796 -1
  4. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/cconnect.py +1 -1
  5. ctxttools-0.2.0/ctools/ccopy.py +317 -0
  6. ctxttools-0.1.7/ctools/ccopy.py → ctxttools-0.2.0/ctools/cextract.py +27 -21
  7. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/cgrep.py +83 -25
  8. ctxttools-0.2.0/ctools/cli.py +190 -0
  9. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/crm.py +1 -1
  10. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/ctools_mcp.py +1 -1
  11. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/lib.py +2 -0
  12. ctxttools-0.2.0/ctxttools.egg-info/PKG-INFO +209 -0
  13. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctxttools.egg-info/SOURCES.txt +1 -0
  14. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctxttools.egg-info/entry_points.txt +1 -0
  15. {ctxttools-0.1.7 → ctxttools-0.2.0}/pyproject.toml +2 -1
  16. ctxttools-0.1.7/PKG-INFO +0 -594
  17. ctxttools-0.1.7/README.md +0 -577
  18. ctxttools-0.1.7/ctools/cli.py +0 -83
  19. ctxttools-0.1.7/ctxttools.egg-info/PKG-INFO +0 -594
  20. {ctxttools-0.1.7 → ctxttools-0.2.0}/LICENSE.MIT +0 -0
  21. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/__init__.py +0 -0
  22. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/cdir.py +0 -0
  23. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/cdu.py +0 -0
  24. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/filterlib.py +0 -0
  25. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/log.py +0 -0
  26. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctools/strategy.py +0 -0
  27. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctxttools.egg-info/dependency_links.txt +0 -0
  28. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctxttools.egg-info/requires.txt +0 -0
  29. {ctxttools-0.1.7 → ctxttools-0.2.0}/ctxttools.egg-info/top_level.txt +0 -0
  30. {ctxttools-0.1.7 → ctxttools-0.2.0}/setup.cfg +0 -0
@@ -0,0 +1,209 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctxttools
3
+ Version: 0.2.0
4
+ Summary: CLI tools for LLM conversations
5
+ Requires-Python: >=3.10
6
+ Description-Content-Type: text/markdown
7
+ License-File: LICENSE.MIT
8
+ Requires-Dist: typer>=0.9.0
9
+ Requires-Dist: rich>=13.0.0
10
+ Requires-Dist: tiktoken>=0.5.0
11
+ Requires-Dist: structlog>=24.0
12
+ Provides-Extra: mcp
13
+ Requires-Dist: mcp>=1.2.0; extra == "mcp"
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest>=7.0.0; extra == "dev"
16
+ Dynamic: license-file
17
+
18
+ <p align="center">
19
+ <img width="500" alt="ctools" src="https://github.com/user-attachments/assets/ee781bbe-6364-44ed-be2d-72114f1e6d8a" /><br/>
20
+ <a href=https://pypi.org/project/ctxttools><img src=https://badge.fury.io/py/ctxttools.svg/></a>
21
+ </p>
22
+
23
+ ------
24
+
25
+ Grep through your conversation history in Claude Desktop, Claude Code, Codex, Pi, Hermes, Goose, Kilo, Hermes, FreeBuff, Cline, and omp.
26
+
27
+ Copy your conversation from one to another. Start in one program, continue in another. using the normal resume syntax.
28
+
29
+
30
+ ### 1. `cgrep` — find it in your past conversations
31
+
32
+ You already know the answer is in a session you had months ago. You just don't remember which one. `cgrep` searches **every session you ever had, across every agent you use**, with a plain regex:
33
+
34
+ ```shell
35
+ $ cgrep -i "ssl"
36
+ claude-code/a351eedf:142:assistant: … the TLS handshake fails with SSL wrong version number …
37
+ opencode/ses_000d460f:16:user: ok graflex has an erorr in the check. i see this: … [[SSL: WRONG_VERSION_NUMBER] …
38
+ opencode/ses_000d460f:34:assistant: Found it. Root cause: `_check_host` (graflex/__init__.py:128) tries `http`, and …
39
+ ```
40
+
41
+ No agent argument? It searches **every installed agent at once** — Claude Code, Opencode, Kilo, Codex, Pi, Goose, Hermes, Cline, omp, and Freebuff, all in one pass. That last line is the root-cause analysis you wrote six months ago and would never have found again.
42
+
43
+ ### 2. `ccopy` — move your project from one tool to another
44
+
45
+ Switched from opencode to Claude Code mid-project? You don't have to start from zero. Copy the whole conversation — context, decisions, the lot — into a fresh session in the other tool:
46
+
47
+ ```shell
48
+ $ ccopy opencode/ses_abc123 claude-code
49
+ Copied 128 message(s) from opencode/ses_abc123 to a new claude-code session: 1a2b3c
50
+ Resume it with:
51
+ claude-code --resume 1a2b3c
52
+ ```
53
+
54
+ Your work travels with you. The source is never touched, so it's a true copy. And when the destination is on another machine, it's just `ccopy opencode/ses_abc123 ssh://chris@remote/codex` — it runs over your normal ssh.
55
+
56
+ ctools is a set of GNU-style tools for the history your coding agents leave behind. These two are the headline; the rest (`cdir` to list sessions, `cextract` to move concepts, `cconnect` to pipe them live, `cdu` to count tokens, `crm` to prune) are in service of them.
57
+
58
+ **Works with the agents you already use:** Claude Desktop, Claude Code, Opencode, Kilo, Codex, Pi, Goose, Hermes, Cline, omp (oh-my-pi), and Freebuff (Codebuff).
59
+
60
+ ## Tools
61
+
62
+ | Tool | What it does | Docs |
63
+ |------|--------------|------|
64
+ | `cgrep` | Search conversation content (grep across every agent) | [docs/cgrep.md](docs/cgrep.md) |
65
+ | `cdir` | List sessions (ls for your history) | [docs/cdir.md](docs/cdir.md) |
66
+ | `ccopy` | Copy a whole conversation from one agent to a new session in another | [docs/ccopy.md](docs/ccopy.md) |
67
+ | `cextract` | Extract concepts from a session into files, and inject them back | [docs/cextract.md](docs/cextract.md) |
68
+ | `cconnect` | Live concept pipelines between sessions | [docs/cconnect.md](docs/cconnect.md) |
69
+ | `cdu` | Token usage per session (du for context windows) | [docs/cdu.md](docs/cdu.md) |
70
+ | `crm` | Remove concepts from a session (mdel) | [docs/crm.md](docs/crm.md) |
71
+
72
+ Every tool takes `--version` and `--verbose` (structured logging). Each tool's full flag reference, output formats, and examples live in its `docs/` page — the table above is just the 30-second version.
73
+
74
+ ## How it works
75
+
76
+ ctools is a substrate for moving memory between context windows. Cross-platform, agent-agnostic, designed like a bus.
77
+
78
+ Two stages: **extraction** and **filtering**.
79
+
80
+ - A **strategy** extracts concepts from a conversation. It defines what counts as a concept and how to find it: regex patterns, LLM extraction, whatever. Different strategies produce different ontologies from the same conversation, because context is contestable.
81
+ - A **filter** selects which extracted concepts reach a destination. It's a binary classifier: pass through or filter out. One concept list can fan out to many destinations, each with its own filter. A coding agent gets coding preferences, a security agent gets security constraints, a PM agent gets goals, all from the same source.
82
+
83
+ ```mermaid
84
+ graph LR
85
+ SRC["source session"] --> STRAT["strategy<br/>(extraction)"]
86
+ STRAT --> CONCEPTS["concept list"]
87
+ CONCEPTS --> FA["filter A"]
88
+ CONCEPTS --> FB["filter B"]
89
+ CONCEPTS --> FC["filter C"]
90
+ FA --> DEST_A["destination A"]
91
+ FB --> DEST_B["destination B"]
92
+ FC --> DEST_C["destination C"]
93
+ ```
94
+
95
+ Context windows are endpoints: opencode, Claude Code, Codex, Pi. They all speak different protocols, but they all consume the same packets. See [cextract](docs/cextract.md) for the concept packet format, strategies, and the filter (JSON-RPC) protocol.
96
+
97
+ ## The problem
98
+
99
+ You talk to LLMs all day. Over weeks, you build up a set of constraints, preferences, and goals. These live in your conversations as system messages. They are valuable. They are also trapped.
100
+
101
+ Say you have been working with opencode for a month. You have refined your coding style through dozens of sessions. Now you start a new Claude Code project and you want those same preferences. You could copy them by hand. Or you could `cextract` them out and `cextract` them back in, or copy the whole conversation across with `ccopy`. Either way, your memory travels with you.
102
+
103
+ ## Supported endpoints
104
+
105
+ | Agent | Storage |
106
+ |-------|---------|
107
+ | claude | JSON |
108
+ | claude-code | JSONL |
109
+ | opencode | SQLite |
110
+ | kilo | SQLite (opencode fork, same schema) |
111
+ | codex | JSONL |
112
+ | pi | JSONL |
113
+ | goose | SQLite |
114
+ | hermes | SQLite (`~/.hermes/state.db`, `$HERMES_HOME`) |
115
+ | cline | JSON (`~/.cline/data/tasks/`) |
116
+ | omp | JSONL (`~/.omp/agent/sessions/`, tree journal) |
117
+ | freebuff | JSON (`~/.config/<codebuff>/projects/`) |
118
+
119
+ Run `cdir` to see which endpoints are found on your system and where they store data.
120
+
121
+ Every endpoint is one class in `ctools/agents.py`. The base `Agent` defines the whole interface the tools use — `sessions()`, `messages()`, `raw_messages()`, `lines()`, `inject_system()`, `inject_toolcall()`, `remove_messages()`, `create_session()` — and the storage-shaped subclasses (`JsonAgent`, `JsonlAgent`, `SqliteAgent`) implement most of it. Adding an endpoint means writing one subclass and adding it to `AGENT_CLASSES`; no command changes.
122
+
123
+ ```python
124
+ from ctools.agents import JsonlAgent, Session
125
+
126
+ class MyAgent(JsonlAgent):
127
+ name = 'myagent'
128
+ description = 'My Coding Agent'
129
+ session_pattern = 'sessions/**/*.jsonl'
130
+
131
+ @classmethod
132
+ def default_base_path(cls):
133
+ return Path.home() / '.myagent'
134
+
135
+ def sessions(self):
136
+ ...
137
+ ```
138
+
139
+ Anything the storage cannot do — pi's session trees cannot be rewritten in place — raises `UnsupportedOperation` instead of being silently skipped.
140
+
141
+ ## MCP server
142
+
143
+ There is an MCP server for use from Claude, opencode, Cursor, or anything else that speaks MCP.
144
+
145
+ Tools: `list_agents`, `list_sessions`, `search_sessions`, `export_session`, `extract_concepts`, `copy_concepts`, `get_session_concepts`.
146
+
147
+ Add to your MCP config:
148
+
149
+ ```json
150
+ {
151
+ "mcpServers": {
152
+ "ctools": {
153
+ "command": "python",
154
+ "args": ["/ABSOLUTE/PATH/TO/ctools/ctools_mcp.py"]
155
+ }
156
+ }
157
+ }
158
+ ```
159
+
160
+ ## Installation
161
+
162
+ ```sh
163
+ pip install ctxttools
164
+ ```
165
+
166
+ For MCP server support:
167
+
168
+ ```sh
169
+ pip install ctxttools[mcp]
170
+ ```
171
+
172
+ ## Library
173
+
174
+ Works as a Python library too.
175
+
176
+ ```python
177
+ from ctools.agents import REGISTRY, OpencodeAgent, SessionNotFound
178
+ from ctools.lib import get_formatter
179
+ from ctools.cgrep import grep_session
180
+ from ctools.cextract import extract_concepts_from_messages
181
+ from ctools.cdu import count_tokens, get_session_tokens
182
+ from ctools.filterlib import JSONRPCFilter, load_filter
183
+ from ctools.log import configure_logging, get_logger
184
+ ```
185
+
186
+ Everything goes through an agent:
187
+
188
+ ```python
189
+ from ctools.agents import REGISTRY
190
+
191
+ agent = REGISTRY['opencode']
192
+ for session in agent.sessions():
193
+ print(session.id, session.name, session.size)
194
+
195
+ for message in agent.messages('ses_abc123'):
196
+ print(message.role, message.content)
197
+ ```
198
+
199
+ Point an agent somewhere else by constructing it with a path:
200
+
201
+ ```python
202
+ from ctools.agents import OpencodeAgent
203
+
204
+ agent = OpencodeAgent('/backup/opencode')
205
+ ```
206
+
207
+ ## Credits
208
+
209
+ ctools was extracted from [Gab n' Go](https://github.com/day50-dev/gabngo). It's named after [GNU mtools](https://www.gnu.org/software/mtools/), which does the same thing for DOS floppies — because your context window is about the size of a DOS-floppy. The flag habits are GNU's too: `--version`, `-1`, `-S`, `-u`, and the rest behave the way you'd expect.
@@ -0,0 +1,192 @@
1
+ <p align="center">
2
+ <img width="500" alt="ctools" src="https://github.com/user-attachments/assets/ee781bbe-6364-44ed-be2d-72114f1e6d8a" /><br/>
3
+ <a href=https://pypi.org/project/ctxttools><img src=https://badge.fury.io/py/ctxttools.svg/></a>
4
+ </p>
5
+
6
+ ------
7
+
8
+ Grep through your conversation history in Claude Desktop, Claude Code, Codex, Pi, Hermes, Goose, Kilo, Hermes, FreeBuff, Cline, and omp.
9
+
10
+ Copy your conversation from one to another. Start in one program, continue in another. using the normal resume syntax.
11
+
12
+
13
+ ### 1. `cgrep` — find it in your past conversations
14
+
15
+ You already know the answer is in a session you had months ago. You just don't remember which one. `cgrep` searches **every session you ever had, across every agent you use**, with a plain regex:
16
+
17
+ ```shell
18
+ $ cgrep -i "ssl"
19
+ claude-code/a351eedf:142:assistant: … the TLS handshake fails with SSL wrong version number …
20
+ opencode/ses_000d460f:16:user: ok graflex has an erorr in the check. i see this: … [[SSL: WRONG_VERSION_NUMBER] …
21
+ opencode/ses_000d460f:34:assistant: Found it. Root cause: `_check_host` (graflex/__init__.py:128) tries `http`, and …
22
+ ```
23
+
24
+ No agent argument? It searches **every installed agent at once** — Claude Code, Opencode, Kilo, Codex, Pi, Goose, Hermes, Cline, omp, and Freebuff, all in one pass. That last line is the root-cause analysis you wrote six months ago and would never have found again.
25
+
26
+ ### 2. `ccopy` — move your project from one tool to another
27
+
28
+ Switched from opencode to Claude Code mid-project? You don't have to start from zero. Copy the whole conversation — context, decisions, the lot — into a fresh session in the other tool:
29
+
30
+ ```shell
31
+ $ ccopy opencode/ses_abc123 claude-code
32
+ Copied 128 message(s) from opencode/ses_abc123 to a new claude-code session: 1a2b3c
33
+ Resume it with:
34
+ claude-code --resume 1a2b3c
35
+ ```
36
+
37
+ Your work travels with you. The source is never touched, so it's a true copy. And when the destination is on another machine, it's just `ccopy opencode/ses_abc123 ssh://chris@remote/codex` — it runs over your normal ssh.
38
+
39
+ ctools is a set of GNU-style tools for the history your coding agents leave behind. These two are the headline; the rest (`cdir` to list sessions, `cextract` to move concepts, `cconnect` to pipe them live, `cdu` to count tokens, `crm` to prune) are in service of them.
40
+
41
+ **Works with the agents you already use:** Claude Desktop, Claude Code, Opencode, Kilo, Codex, Pi, Goose, Hermes, Cline, omp (oh-my-pi), and Freebuff (Codebuff).
42
+
43
+ ## Tools
44
+
45
+ | Tool | What it does | Docs |
46
+ |------|--------------|------|
47
+ | `cgrep` | Search conversation content (grep across every agent) | [docs/cgrep.md](docs/cgrep.md) |
48
+ | `cdir` | List sessions (ls for your history) | [docs/cdir.md](docs/cdir.md) |
49
+ | `ccopy` | Copy a whole conversation from one agent to a new session in another | [docs/ccopy.md](docs/ccopy.md) |
50
+ | `cextract` | Extract concepts from a session into files, and inject them back | [docs/cextract.md](docs/cextract.md) |
51
+ | `cconnect` | Live concept pipelines between sessions | [docs/cconnect.md](docs/cconnect.md) |
52
+ | `cdu` | Token usage per session (du for context windows) | [docs/cdu.md](docs/cdu.md) |
53
+ | `crm` | Remove concepts from a session (mdel) | [docs/crm.md](docs/crm.md) |
54
+
55
+ Every tool takes `--version` and `--verbose` (structured logging). Each tool's full flag reference, output formats, and examples live in its `docs/` page — the table above is just the 30-second version.
56
+
57
+ ## How it works
58
+
59
+ ctools is a substrate for moving memory between context windows. Cross-platform, agent-agnostic, designed like a bus.
60
+
61
+ Two stages: **extraction** and **filtering**.
62
+
63
+ - A **strategy** extracts concepts from a conversation. It defines what counts as a concept and how to find it: regex patterns, LLM extraction, whatever. Different strategies produce different ontologies from the same conversation, because context is contestable.
64
+ - A **filter** selects which extracted concepts reach a destination. It's a binary classifier: pass through or filter out. One concept list can fan out to many destinations, each with its own filter. A coding agent gets coding preferences, a security agent gets security constraints, a PM agent gets goals, all from the same source.
65
+
66
+ ```mermaid
67
+ graph LR
68
+ SRC["source session"] --> STRAT["strategy<br/>(extraction)"]
69
+ STRAT --> CONCEPTS["concept list"]
70
+ CONCEPTS --> FA["filter A"]
71
+ CONCEPTS --> FB["filter B"]
72
+ CONCEPTS --> FC["filter C"]
73
+ FA --> DEST_A["destination A"]
74
+ FB --> DEST_B["destination B"]
75
+ FC --> DEST_C["destination C"]
76
+ ```
77
+
78
+ Context windows are endpoints: opencode, Claude Code, Codex, Pi. They all speak different protocols, but they all consume the same packets. See [cextract](docs/cextract.md) for the concept packet format, strategies, and the filter (JSON-RPC) protocol.
79
+
80
+ ## The problem
81
+
82
+ You talk to LLMs all day. Over weeks, you build up a set of constraints, preferences, and goals. These live in your conversations as system messages. They are valuable. They are also trapped.
83
+
84
+ Say you have been working with opencode for a month. You have refined your coding style through dozens of sessions. Now you start a new Claude Code project and you want those same preferences. You could copy them by hand. Or you could `cextract` them out and `cextract` them back in, or copy the whole conversation across with `ccopy`. Either way, your memory travels with you.
85
+
86
+ ## Supported endpoints
87
+
88
+ | Agent | Storage |
89
+ |-------|---------|
90
+ | claude | JSON |
91
+ | claude-code | JSONL |
92
+ | opencode | SQLite |
93
+ | kilo | SQLite (opencode fork, same schema) |
94
+ | codex | JSONL |
95
+ | pi | JSONL |
96
+ | goose | SQLite |
97
+ | hermes | SQLite (`~/.hermes/state.db`, `$HERMES_HOME`) |
98
+ | cline | JSON (`~/.cline/data/tasks/`) |
99
+ | omp | JSONL (`~/.omp/agent/sessions/`, tree journal) |
100
+ | freebuff | JSON (`~/.config/<codebuff>/projects/`) |
101
+
102
+ Run `cdir` to see which endpoints are found on your system and where they store data.
103
+
104
+ Every endpoint is one class in `ctools/agents.py`. The base `Agent` defines the whole interface the tools use — `sessions()`, `messages()`, `raw_messages()`, `lines()`, `inject_system()`, `inject_toolcall()`, `remove_messages()`, `create_session()` — and the storage-shaped subclasses (`JsonAgent`, `JsonlAgent`, `SqliteAgent`) implement most of it. Adding an endpoint means writing one subclass and adding it to `AGENT_CLASSES`; no command changes.
105
+
106
+ ```python
107
+ from ctools.agents import JsonlAgent, Session
108
+
109
+ class MyAgent(JsonlAgent):
110
+ name = 'myagent'
111
+ description = 'My Coding Agent'
112
+ session_pattern = 'sessions/**/*.jsonl'
113
+
114
+ @classmethod
115
+ def default_base_path(cls):
116
+ return Path.home() / '.myagent'
117
+
118
+ def sessions(self):
119
+ ...
120
+ ```
121
+
122
+ Anything the storage cannot do — pi's session trees cannot be rewritten in place — raises `UnsupportedOperation` instead of being silently skipped.
123
+
124
+ ## MCP server
125
+
126
+ There is an MCP server for use from Claude, opencode, Cursor, or anything else that speaks MCP.
127
+
128
+ Tools: `list_agents`, `list_sessions`, `search_sessions`, `export_session`, `extract_concepts`, `copy_concepts`, `get_session_concepts`.
129
+
130
+ Add to your MCP config:
131
+
132
+ ```json
133
+ {
134
+ "mcpServers": {
135
+ "ctools": {
136
+ "command": "python",
137
+ "args": ["/ABSOLUTE/PATH/TO/ctools/ctools_mcp.py"]
138
+ }
139
+ }
140
+ }
141
+ ```
142
+
143
+ ## Installation
144
+
145
+ ```sh
146
+ pip install ctxttools
147
+ ```
148
+
149
+ For MCP server support:
150
+
151
+ ```sh
152
+ pip install ctxttools[mcp]
153
+ ```
154
+
155
+ ## Library
156
+
157
+ Works as a Python library too.
158
+
159
+ ```python
160
+ from ctools.agents import REGISTRY, OpencodeAgent, SessionNotFound
161
+ from ctools.lib import get_formatter
162
+ from ctools.cgrep import grep_session
163
+ from ctools.cextract import extract_concepts_from_messages
164
+ from ctools.cdu import count_tokens, get_session_tokens
165
+ from ctools.filterlib import JSONRPCFilter, load_filter
166
+ from ctools.log import configure_logging, get_logger
167
+ ```
168
+
169
+ Everything goes through an agent:
170
+
171
+ ```python
172
+ from ctools.agents import REGISTRY
173
+
174
+ agent = REGISTRY['opencode']
175
+ for session in agent.sessions():
176
+ print(session.id, session.name, session.size)
177
+
178
+ for message in agent.messages('ses_abc123'):
179
+ print(message.role, message.content)
180
+ ```
181
+
182
+ Point an agent somewhere else by constructing it with a path:
183
+
184
+ ```python
185
+ from ctools.agents import OpencodeAgent
186
+
187
+ agent = OpencodeAgent('/backup/opencode')
188
+ ```
189
+
190
+ ## Credits
191
+
192
+ ctools was extracted from [Gab n' Go](https://github.com/day50-dev/gabngo). It's named after [GNU mtools](https://www.gnu.org/software/mtools/), which does the same thing for DOS floppies — because your context window is about the size of a DOS-floppy. The flag habits are GNU's too: `--version`, `-1`, `-S`, `-u`, and the rest behave the way you'd expect.