cc-session-tools 0.7.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 (63) hide show
  1. cc_session_tools-0.7.0/LICENSE +21 -0
  2. cc_session_tools-0.7.0/PKG-INFO +348 -0
  3. cc_session_tools-0.7.0/README.md +292 -0
  4. cc_session_tools-0.7.0/pyproject.toml +57 -0
  5. cc_session_tools-0.7.0/setup.cfg +4 -0
  6. cc_session_tools-0.7.0/src/cc_session_tools/__init__.py +21 -0
  7. cc_session_tools-0.7.0/src/cc_session_tools/cli/__init__.py +0 -0
  8. cc_session_tools-0.7.0/src/cc_session_tools/cli/ccd.py +113 -0
  9. cc_session_tools-0.7.0/src/cc_session_tools/cli/ccr.py +151 -0
  10. cc_session_tools-0.7.0/src/cc_session_tools/cli/ccs.py +697 -0
  11. cc_session_tools-0.7.0/src/cc_session_tools/lib/__init__.py +0 -0
  12. cc_session_tools-0.7.0/src/cc_session_tools/lib/claude_flags.py +59 -0
  13. cc_session_tools-0.7.0/src/cc_session_tools/lib/debug.py +13 -0
  14. cc_session_tools-0.7.0/src/cc_session_tools/lib/levenshtein.py +20 -0
  15. cc_session_tools-0.7.0/src/cc_session_tools/lib/picker.py +26 -0
  16. cc_session_tools-0.7.0/src/cc_session_tools/lib/prompts.py +89 -0
  17. cc_session_tools-0.7.0/src/cc_session_tools/lib/roots.py +113 -0
  18. cc_session_tools-0.7.0/src/cc_session_tools/lib/rules.py +187 -0
  19. cc_session_tools-0.7.0/src/cc_session_tools/lib/sessions.py +242 -0
  20. cc_session_tools-0.7.0/src/cc_session_tools/lib/tasklist.py +17 -0
  21. cc_session_tools-0.7.0/src/cc_session_tools.egg-info/PKG-INFO +348 -0
  22. cc_session_tools-0.7.0/src/cc_session_tools.egg-info/SOURCES.txt +61 -0
  23. cc_session_tools-0.7.0/src/cc_session_tools.egg-info/dependency_links.txt +1 -0
  24. cc_session_tools-0.7.0/src/cc_session_tools.egg-info/entry_points.txt +5 -0
  25. cc_session_tools-0.7.0/src/cc_session_tools.egg-info/requires.txt +8 -0
  26. cc_session_tools-0.7.0/src/cc_session_tools.egg-info/top_level.txt +2 -0
  27. cc_session_tools-0.7.0/src/claude_code_usage/__init__.py +8 -0
  28. cc_session_tools-0.7.0/src/claude_code_usage/attribution.py +81 -0
  29. cc_session_tools-0.7.0/src/claude_code_usage/cache.py +202 -0
  30. cc_session_tools-0.7.0/src/claude_code_usage/ccusage_wrapper.py +139 -0
  31. cc_session_tools-0.7.0/src/claude_code_usage/cli.py +349 -0
  32. cc_session_tools-0.7.0/src/claude_code_usage/parent_inference.py +105 -0
  33. cc_session_tools-0.7.0/src/claude_code_usage/parser.py +199 -0
  34. cc_session_tools-0.7.0/src/claude_code_usage/pricing.py +97 -0
  35. cc_session_tools-0.7.0/src/claude_code_usage/query.py +295 -0
  36. cc_session_tools-0.7.0/src/claude_code_usage/report.py +136 -0
  37. cc_session_tools-0.7.0/src/claude_code_usage/schema.py +99 -0
  38. cc_session_tools-0.7.0/src/claude_code_usage/session_names.py +172 -0
  39. cc_session_tools-0.7.0/tests/test_attribution.py +81 -0
  40. cc_session_tools-0.7.0/tests/test_cache.py +212 -0
  41. cc_session_tools-0.7.0/tests/test_ccs_eta.py +39 -0
  42. cc_session_tools-0.7.0/tests/test_ccusage_wrapper.py +56 -0
  43. cc_session_tools-0.7.0/tests/test_claude_flags.py +60 -0
  44. cc_session_tools-0.7.0/tests/test_cli.py +386 -0
  45. cc_session_tools-0.7.0/tests/test_cli_ccd.py +206 -0
  46. cc_session_tools-0.7.0/tests/test_cli_ccr.py +215 -0
  47. cc_session_tools-0.7.0/tests/test_cli_ccs.py +695 -0
  48. cc_session_tools-0.7.0/tests/test_cli_version.py +16 -0
  49. cc_session_tools-0.7.0/tests/test_debug.py +31 -0
  50. cc_session_tools-0.7.0/tests/test_levenshtein.py +37 -0
  51. cc_session_tools-0.7.0/tests/test_parent_inference.py +179 -0
  52. cc_session_tools-0.7.0/tests/test_parser.py +308 -0
  53. cc_session_tools-0.7.0/tests/test_picker.py +72 -0
  54. cc_session_tools-0.7.0/tests/test_pricing.py +70 -0
  55. cc_session_tools-0.7.0/tests/test_prompts.py +118 -0
  56. cc_session_tools-0.7.0/tests/test_query.py +239 -0
  57. cc_session_tools-0.7.0/tests/test_report.py +62 -0
  58. cc_session_tools-0.7.0/tests/test_roots.py +189 -0
  59. cc_session_tools-0.7.0/tests/test_rules.py +159 -0
  60. cc_session_tools-0.7.0/tests/test_schema.py +106 -0
  61. cc_session_tools-0.7.0/tests/test_session_names.py +228 -0
  62. cc_session_tools-0.7.0/tests/test_sessions.py +179 -0
  63. cc_session_tools-0.7.0/tests/test_tasklist.py +37 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 raffishquartan
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.4
2
+ Name: cc-session-tools
3
+ Version: 0.7.0
4
+ Summary: Claude Code session management and usage analysis CLI tools (ccd, ccr, ccs, claude-code-usage) and shared library
5
+ Author: raffishquartan
6
+ License: MIT License
7
+
8
+ Copyright (c) 2026 raffishquartan
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/raffishquartan/claude-code-session-tools
29
+ Project-URL: Repository, https://github.com/raffishquartan/claude-code-session-tools
30
+ Project-URL: Issues, https://github.com/raffishquartan/claude-code-session-tools/issues
31
+ Keywords: claude-code,cli,session-management,usage-analytics
32
+ Classifier: Development Status :: 4 - Beta
33
+ Classifier: Environment :: Console
34
+ Classifier: License :: OSI Approved :: MIT License
35
+ Classifier: Operating System :: POSIX :: Linux
36
+ Classifier: Operating System :: MacOS
37
+ Classifier: Programming Language :: Python :: 3
38
+ Classifier: Programming Language :: Python :: 3 :: Only
39
+ Classifier: Programming Language :: Python :: 3.10
40
+ Classifier: Programming Language :: Python :: 3.11
41
+ Classifier: Programming Language :: Python :: 3.12
42
+ Classifier: Programming Language :: Python :: 3.13
43
+ Classifier: Topic :: Software Development
44
+ Classifier: Topic :: Utilities
45
+ Requires-Python: >=3.10
46
+ Description-Content-Type: text/markdown
47
+ License-File: LICENSE
48
+ Requires-Dist: pandas>=2.2
49
+ Requires-Dist: pyarrow>=15.0
50
+ Requires-Dist: jsonschema>=4.21
51
+ Requires-Dist: platformdirs>=4.0
52
+ Requires-Dist: httpx>=0.27
53
+ Provides-Extra: dev
54
+ Requires-Dist: pytest>=7; extra == "dev"
55
+ Dynamic: license-file
56
+
57
+ # claude-code-session-tools
58
+
59
+ Two concerns, one repo, for life on the [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI:
60
+
61
+ 1. **Session management** - start, resume, find, and relocate Claude Code sessions from the shell, with tagged dated session directories that don't pollute your repo root.
62
+ 2. **Usage analytics** - parse `~/.claude/projects/**/*.jsonl` into tokens-and-dollars breakdowns by project, session, model, MCP server, plugin, and tool.
63
+
64
+ The repo ships four CLIs and three bundled skills:
65
+
66
+ | | What it does |
67
+ |---|---|
68
+ | **`ccd <tag>`** | Start a new session with a pre-created `cc-sessions/<date>-<tag>/` directory and a tagged display name. |
69
+ | **`ccr <fragment>`** | Resume an existing session by typing any substring of its name. |
70
+ | **`ccs <query>`** | Search across your sessions by name (default), or by file contents (`--contents`), in the current project (default) or across every configured root (`--global`). |
71
+ | **`claude-code-usage`** | Multi-dimensional usage analytics CLI: query/group/filter by project, session, model, MCP server, plugin, tool, day/week/month/year. Reconciles dollar totals against `ccusage`. |
72
+ | Skill: **`find-claude-code-session`** | Wraps `ccs`. Lets a Claude Code session locate one of your prior sessions by name or content and offer a `ccr` command to resume it. |
73
+ | Skill: **`move-session`** | Move, rename, or move+rename a session while keeping its `~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` transcript resumable. |
74
+ | Skill: **`claude-usage`** | Wraps `claude-code-usage`. Lets a Claude Code session answer "how much have I spent on Opus this month?" without you typing the CLI yourself. |
75
+
76
+ If you've ever tried to remember which `1f4a8b3c-...` UUID is the session where you were debugging that flaky test last Tuesday, or wondered which project burned through last week's Opus budget, this is for you.
77
+
78
+ ## Why bother?
79
+
80
+ Claude Code stores each session as a `~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` transcript and exposes them through `claude --resume`. That works, but the picker shows untagged sessions in opaque order, the working files for a session sprawl into your repo root, and there's no built-in way to grep across past conversations or see where your tokens went.
81
+
82
+ These tools add:
83
+
84
+ 1. **A tagged, dated session directory** under `<project>/cc-sessions/<YYYYMMDD>-<tag>/` with `working/` and `out/` subdirs - the convention Claude Code's [session memory](https://docs.anthropic.com/en/docs/claude-code/memory) hooks expect when you want scratch space and deliverables that don't pollute your repo.
85
+ 2. **Resume-by-fragment** so you can type `ccr flaky` instead of scrolling through the picker.
86
+ 3. **Cross-session search** so `ccs --contents --global "GraphQL retry"` finds every conversation that mentioned it.
87
+ 4. **Usage analytics** so `claude-code-usage query --since 2026-04-01 --group-by project,model` answers where the spend went.
88
+ 5. **Skill wrappers** so the Claude Code agent can do all of the above on your behalf when you ask in natural language.
89
+
90
+ ## Installation
91
+
92
+ ### Prerequisites
93
+
94
+ - **Python 3.10+**
95
+ - **The `claude` CLI on your `$PATH`.** Install it first via [the official Claude Code instructions](https://docs.anthropic.com/en/docs/claude-code/setup) and verify with `claude --version`.
96
+ - **`ccusage` (optional)** - if on `$PATH`, `claude-code-usage reconcile` cross-checks dollar totals against it. Skipped gracefully if missing.
97
+ - **`ripgrep` (optional)** - `ccs --contents` prefers `rg`; falls back to threaded Python `grep` if missing.
98
+
99
+ ### Install the tools
100
+
101
+ The simplest path is [`pipx`](https://pipx.pypa.io/), which installs the commands into
102
+ an isolated venv and puts them on your `$PATH`:
103
+
104
+ ```sh
105
+ pipx install cc-session-tools
106
+ ```
107
+
108
+ If you use [`uv`](https://docs.astral.sh/uv/):
109
+
110
+ ```sh
111
+ uv tool install cc-session-tools
112
+ ```
113
+
114
+ Either way, `ccd`, `ccr`, `ccs`, and `claude-code-usage` will be available on your
115
+ `$PATH`. Verify:
116
+
117
+ ```sh
118
+ ccd --version
119
+ claude-code-usage --version
120
+ ```
121
+
122
+ > **Installing from source (pre-release or offline):**
123
+ > ```sh
124
+ > git clone https://github.com/raffishquartan/claude-code-session-tools.git
125
+ > cd claude-code-session-tools
126
+ > uv tool install .
127
+ > ```
128
+
129
+ ### Install the skills (optional)
130
+
131
+ Each skill is a self-contained directory under `skills/`. To make them visible to Claude Code, symlink them into `~/.claude/skills/`:
132
+
133
+ ```sh
134
+ ln -s "$PWD/skills/find-claude-code-session" ~/.claude/skills/find-claude-code-session
135
+ ln -s "$PWD/skills/move-session" ~/.claude/skills/move-session
136
+ ln -s "$PWD/skills/claude-usage" ~/.claude/skills/claude-usage
137
+ ```
138
+
139
+ The skills shell out to the installed CLIs - they don't import the Python library directly, so the only requirement is that `ccs` / `claude-code-usage` are on `$PATH`.
140
+
141
+ ## Configuration: where do your sessions live?
142
+
143
+ `ccd` refuses to start a session if your current working directory isn't a direct child of one of your configured **session roots**. This sounds annoying but turns out to be a feature: it stops you from accidentally starting a session in `/tmp` or in `~`, and it lets `ccr`/`ccs` find sessions across your projects without you telling them where to look.
144
+
145
+ Roots are configured via two environment variables - both optional, but you'll want at least one:
146
+
147
+ ### `CLAUDE_SESSION_TOOLS_REPO_ROOT` - the **loose** root
148
+
149
+ Point this at the directory whose direct children are your projects (the typical case is `~/repos`):
150
+
151
+ ```sh
152
+ export CLAUDE_SESSION_TOOLS_REPO_ROOT="$HOME/repos"
153
+ ```
154
+
155
+ A "session root" means: if `$REPO_ROOT/foo/` exists, then `cd ~/repos/foo && ccd my-tag` is allowed and creates `~/repos/foo/cc-sessions/20260509-my-tag/`. Sessions started two levels deep (`~/repos/foo/sub/`) are rejected unless you pass `--force`.
156
+
157
+ Under the loose root the only naming rule is **no spaces in the tag**. Tag suffixes can be anything else: `bugfix-7`, `redesign`, `try-out-thing`.
158
+
159
+ ### `CLAUDE_SESSION_TOOLS_PROJ_ROOT` - the **strict** (namespaced) root
160
+
161
+ Pointing at this is opt-in. It's useful if you keep a separate directory for "Claude Code project" workspaces (think one folder per long-running theme, like `~/cc-claude-code/migration/`, `~/cc-claude-code/oneshot/`):
162
+
163
+ ```sh
164
+ export CLAUDE_SESSION_TOOLS_PROJ_ROOT="$HOME/cc-claude-code"
165
+ ```
166
+
167
+ Under the strict root, two extra rules apply:
168
+
169
+ 1. **Project directory names** must match `[a-z0-9]+` - lowercase, no dashes, no underscores.
170
+ 2. **Tag suffixes** must start with `<project-name>-` followed by a descriptive label.
171
+
172
+ So in `~/cc-claude-code/oneshot/`, `ccd oneshot-config-cleanup` is fine but `ccd config-cleanup` is rejected with a friendly error. The strict root also enables `ccd`'s [Levenshtein typo prompt](#typo-protection-strict-root-only): if you type `oneshet-foo`, it offers to correct it to `oneshot-foo`.
173
+
174
+ You can configure either, both, or neither. With neither set, you'll need `ccd --force` to start any session, and `ccr`/`ccs` won't find anything.
175
+
176
+ ### Why two roots, and which should I use?
177
+
178
+ | | `REPO_ROOT` (loose) | `PROJ_ROOT` (strict) |
179
+ |---|---|---|
180
+ | Where you point it | A directory you already use for code, e.g. `~/repos` | A purpose-built directory for Claude Code workspaces, e.g. `~/cc-claude-code` |
181
+ | Naming conventions | None beyond no-spaces | Project name `[a-z0-9]+`, tag `<project>-<label>` |
182
+ | Typo protection | Off | On (Levenshtein-checked against project name) |
183
+ | Best for | Day-to-day work in existing repos | Long-running, themed Claude Code projects you want kept tidy |
184
+
185
+ Most users only need `REPO_ROOT`. Configure `PROJ_ROOT` later if you find yourself wanting tighter conventions for a specific subset of work.
186
+
187
+ ## Session management CLIs
188
+
189
+ ### `ccd` - start a session
190
+
191
+ ```sh
192
+ cd ~/repos/myproject
193
+ ccd bugfix-flaky-test
194
+ # Creates ~/repos/myproject/cc-sessions/20260509-bugfix-flaky-test/
195
+ # working/
196
+ # out/
197
+ # And launches `claude` with -n 20260509-bugfix-flaky-test (tagged display name).
198
+ ```
199
+
200
+ Useful flags:
201
+ - `--force` - skip the root check and any naming-convention checks (escape hatch for one-off invocations outside your roots).
202
+ - Anything after the tag is forwarded to `claude` verbatim, so `ccd my-tag --model opus` works.
203
+
204
+ ### `ccr` - resume by fragment
205
+
206
+ ```sh
207
+ ccr flaky # resumes whichever session has "flaky" in its name
208
+ ccr 20260509 # resumes whichever session was started on that date
209
+ ```
210
+
211
+ If multiple sessions match, `ccr` prints them and exits cleanly so you can rerun with a more specific fragment. If exactly one matches, it execs `claude --resume <basename>` with the right working directory.
212
+
213
+ ### `ccs` - search
214
+
215
+ ```sh
216
+ ccs flaky # name search in current project
217
+ ccs flaky --global # name search across all configured roots
218
+ ccs "GraphQL retry" --contents # full-text search of files in current project's sessions
219
+ ccs "GraphQL retry" --contents --global # ... across all projects
220
+ ```
221
+
222
+ Results are ordered newest-first by session start date. `--contents` shows one line of context around each match.
223
+
224
+ ## Usage analytics CLI
225
+
226
+ ### `claude-code-usage` - tokens and dollars by every dimension you care about
227
+
228
+ The CLI parses your `~/.claude/projects/**/*.jsonl` transcripts into a Pandas DataFrame (mtime-keyed Parquet cache means subsequent runs are fast), splits per-tool tokens evenly across `tool_use` blocks, and lets you slice the result.
229
+
230
+ Five subcommands:
231
+
232
+ | | What it does |
233
+ |---|---|
234
+ | `query` | Multi-dimensional filter + group-by, output as markdown / CSV / JSON. The workhorse. |
235
+ | `report` | Render a full multi-section markdown report (project / model / time-bucket breakdowns at once). |
236
+ | `children` | List child sessions (hook-security-review, subagent dispatches) of a given parent session. |
237
+ | `warm-cache` | Populate or refresh the Parquet cache without producing output. |
238
+ | `reconcile` | Compare our totals against [`ccusage`](https://github.com/ryoppippi/ccusage)'s authoritative figures, so we know the numbers are right. |
239
+
240
+ A few examples:
241
+
242
+ ```sh
243
+ # Total spend last month, grouped by project (top 10 by cost)
244
+ claude-code-usage query --since 2026-04-01 --until 2026-04-30 --group-by project --top 10
245
+
246
+ # Where Opus tokens went this week, by session
247
+ claude-code-usage query --since 2026-05-04 --model opus --group-by session
248
+
249
+ # How often each MCP server gets used, across the last quarter
250
+ claude-code-usage query --since 2026-02-01 --group-by mcp --sort token_total
251
+
252
+ # Daily spend trend for one project
253
+ claude-code-usage query --project myproject --group-by day --format csv
254
+
255
+ # Full report of last calendar month
256
+ claude-code-usage report --since 2026-04-01 --until 2026-04-30
257
+
258
+ # Cross-validate against ccusage
259
+ claude-code-usage reconcile --since 2026-04-01
260
+ ```
261
+
262
+ Run `claude-code-usage <subcommand> --help` for the full grammar. A few flags worth knowing:
263
+
264
+ - `--exclude-hooks` strips out the `bash-security-review.sh` hook sessions, which would otherwise distort per-session cost breakdowns by ~$1.60 each.
265
+ - `--include-children` (when grouping by session) folds child-session tokens and cost into the parent row.
266
+ - `--session-format {name,uuid,both}` controls how the session column renders - by display name (default), by UUID, or both.
267
+
268
+ ## Bundled skills
269
+
270
+ The repo ships three Claude Code skills, designed to be symlinked into `~/.claude/skills/`. They're thin wrappers around the CLIs so a Claude Code session can invoke them on your behalf in response to natural-language prompts.
271
+
272
+ ### `find-claude-code-session`
273
+
274
+ Wraps `ccs`. Triggers on prompts like "find my session about X", "did I work on foo before", "what session was I in when Y". Constructs the right `ccs` invocation, escalates from local to global search if the local hit list is empty, and presents results as `ccr <fragment>` commands you can paste.
275
+
276
+ ### `move-session`
277
+
278
+ Moves, renames, or move+renames a session directory while keeping the JSONL transcript resumable. Triggers on "move session to", "rename my session", "this session belongs in a different folder". Dry-run by default - you must pass `--execute` for any filesystem change. Validates source and destination against the same rules as `ccd`, copies the session directory tree, rewrites JSONL `cwd` fields to the destination path, and appends a tombstone record to the source JSONL so `claude --resume` on the old session explains where it went.
279
+
280
+ ### `claude-usage`
281
+
282
+ Wraps `claude-code-usage`. Triggers on usage questions: "how much have I spent on Claude Code", "tokens used this week", "which project costs the most", "Opus vs Sonnet", "any spike in usage recently". Picks the right subcommand and flags, runs it, and summarises the result in plain English.
283
+
284
+ See `docs/design.md` for the full design and CLI contract.
285
+
286
+ ## How it interacts with Claude Code's task lists
287
+
288
+ Claude Code lets multiple sessions share a single task list if they all set the same `CLAUDE_CODE_TASK_LIST_ID` environment variable. `ccd` and `ccr` derive this from the project layout:
289
+
290
+ - If your cwd is a direct child of a configured root (e.g. `~/repos/myproject`), the task list ID is set to the project directory name (`myproject`). All sessions started in `~/repos/myproject` share one task list.
291
+ - If your cwd is anywhere else (or both env vars are unset and you used `--force`), no task list ID is set and the session gets a private task list.
292
+
293
+ This means you can pick up a task created in yesterday's session from today's session in the same project, without any extra setup.
294
+
295
+ ## Typo protection (strict root only)
296
+
297
+ When you start a session under the **strict** (`PROJ_ROOT`) root, `ccd` checks whether your tag's first dash-separated term looks like a typo of the project directory name (Levenshtein distance ≤ 2):
298
+
299
+ ```sh
300
+ cd ~/cc-claude-code/oneshot
301
+ ccd oneshet-fix-bug
302
+ ccd: 'oneshet' looks like a typo of project folder 'oneshot' (Levenshtein 1).
303
+ ccd: Start session with tag 'oneshot-fix-bug' instead? [y/N]
304
+ ```
305
+
306
+ A second prompt fires if your first term is far from the current project name **and** far from every sibling project under `PROJ_ROOT` - in that case `ccd` offers to prepend the current project name. This behaviour is intentionally off under the loose root.
307
+
308
+ ## Sessions on disk
309
+
310
+ Each session directory looks like:
311
+
312
+ ```
313
+ cc-sessions/20260509-bugfix-flaky-test/
314
+ working/ # scratch files, notes, WORKLOG.md - whatever you want
315
+ out/ # deliverables you might keep or hand off
316
+ ```
317
+
318
+ Add `cc-sessions/` to your project's `.gitignore` if you don't want session artefacts tracked.
319
+
320
+ ## Development
321
+
322
+ ```sh
323
+ git clone https://github.com/raffishquartan/claude-code-session-tools.git
324
+ cd claude-code-session-tools
325
+ uv sync --extra dev
326
+ uv run pytest
327
+ ```
328
+
329
+ Tests run on Python 3.10, 3.11, 3.12, and 3.13 (see `.github/workflows/ci.yml`). CI also
330
+ includes an `install-check` job that runs `uv tool install .` and verifies all four CLIs
331
+ start up correctly - the direct guard against the editable-install/worktree failure mode.
332
+
333
+ > **When working in a git worktree:** test your changes with `uv run pytest` or
334
+ > `uv run python -m cc_session_tools.cli.ccd` - do not run `uv tool install` from inside
335
+ > a worktree. After merging, run `uv tool install ~/repos/claude-code-session-tools`
336
+ > (or `uv tool install cc-session-tools` if installed from PyPI) to update the global
337
+ > install.
338
+
339
+ ## Limitations and caveats
340
+
341
+ - Linux and macOS only. Windows is not tested; the tools assume POSIX paths and `os.execvpe`-style process replacement.
342
+ - The session-management CLIs shell out to `claude` via `os.execvpe`. If `claude` isn't on `$PATH`, `ccd` and `ccr` will fail with the standard "command not found" error.
343
+ - `claude-code-usage` reads from `~/.claude/projects/` and writes a Parquet cache under `~/.cache/claude-code-usage/parquet/` (overrideable via `--projects-dir` and `--cache-dir`). Pricing data is loaded from `data/pricing.json` shipped with the package, refreshed lazily from LiteLLM upstream with a 7-day TTL.
344
+ - The strict-root convention is opinionated. If you don't want it, just leave `CLAUDE_SESSION_TOOLS_PROJ_ROOT` unset.
345
+
346
+ ## Licence
347
+
348
+ MIT - see [LICENSE](LICENSE).
@@ -0,0 +1,292 @@
1
+ # claude-code-session-tools
2
+
3
+ Two concerns, one repo, for life on the [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI:
4
+
5
+ 1. **Session management** - start, resume, find, and relocate Claude Code sessions from the shell, with tagged dated session directories that don't pollute your repo root.
6
+ 2. **Usage analytics** - parse `~/.claude/projects/**/*.jsonl` into tokens-and-dollars breakdowns by project, session, model, MCP server, plugin, and tool.
7
+
8
+ The repo ships four CLIs and three bundled skills:
9
+
10
+ | | What it does |
11
+ |---|---|
12
+ | **`ccd <tag>`** | Start a new session with a pre-created `cc-sessions/<date>-<tag>/` directory and a tagged display name. |
13
+ | **`ccr <fragment>`** | Resume an existing session by typing any substring of its name. |
14
+ | **`ccs <query>`** | Search across your sessions by name (default), or by file contents (`--contents`), in the current project (default) or across every configured root (`--global`). |
15
+ | **`claude-code-usage`** | Multi-dimensional usage analytics CLI: query/group/filter by project, session, model, MCP server, plugin, tool, day/week/month/year. Reconciles dollar totals against `ccusage`. |
16
+ | Skill: **`find-claude-code-session`** | Wraps `ccs`. Lets a Claude Code session locate one of your prior sessions by name or content and offer a `ccr` command to resume it. |
17
+ | Skill: **`move-session`** | Move, rename, or move+rename a session while keeping its `~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` transcript resumable. |
18
+ | Skill: **`claude-usage`** | Wraps `claude-code-usage`. Lets a Claude Code session answer "how much have I spent on Opus this month?" without you typing the CLI yourself. |
19
+
20
+ If you've ever tried to remember which `1f4a8b3c-...` UUID is the session where you were debugging that flaky test last Tuesday, or wondered which project burned through last week's Opus budget, this is for you.
21
+
22
+ ## Why bother?
23
+
24
+ Claude Code stores each session as a `~/.claude/projects/<encoded-cwd>/<uuid>.jsonl` transcript and exposes them through `claude --resume`. That works, but the picker shows untagged sessions in opaque order, the working files for a session sprawl into your repo root, and there's no built-in way to grep across past conversations or see where your tokens went.
25
+
26
+ These tools add:
27
+
28
+ 1. **A tagged, dated session directory** under `<project>/cc-sessions/<YYYYMMDD>-<tag>/` with `working/` and `out/` subdirs - the convention Claude Code's [session memory](https://docs.anthropic.com/en/docs/claude-code/memory) hooks expect when you want scratch space and deliverables that don't pollute your repo.
29
+ 2. **Resume-by-fragment** so you can type `ccr flaky` instead of scrolling through the picker.
30
+ 3. **Cross-session search** so `ccs --contents --global "GraphQL retry"` finds every conversation that mentioned it.
31
+ 4. **Usage analytics** so `claude-code-usage query --since 2026-04-01 --group-by project,model` answers where the spend went.
32
+ 5. **Skill wrappers** so the Claude Code agent can do all of the above on your behalf when you ask in natural language.
33
+
34
+ ## Installation
35
+
36
+ ### Prerequisites
37
+
38
+ - **Python 3.10+**
39
+ - **The `claude` CLI on your `$PATH`.** Install it first via [the official Claude Code instructions](https://docs.anthropic.com/en/docs/claude-code/setup) and verify with `claude --version`.
40
+ - **`ccusage` (optional)** - if on `$PATH`, `claude-code-usage reconcile` cross-checks dollar totals against it. Skipped gracefully if missing.
41
+ - **`ripgrep` (optional)** - `ccs --contents` prefers `rg`; falls back to threaded Python `grep` if missing.
42
+
43
+ ### Install the tools
44
+
45
+ The simplest path is [`pipx`](https://pipx.pypa.io/), which installs the commands into
46
+ an isolated venv and puts them on your `$PATH`:
47
+
48
+ ```sh
49
+ pipx install cc-session-tools
50
+ ```
51
+
52
+ If you use [`uv`](https://docs.astral.sh/uv/):
53
+
54
+ ```sh
55
+ uv tool install cc-session-tools
56
+ ```
57
+
58
+ Either way, `ccd`, `ccr`, `ccs`, and `claude-code-usage` will be available on your
59
+ `$PATH`. Verify:
60
+
61
+ ```sh
62
+ ccd --version
63
+ claude-code-usage --version
64
+ ```
65
+
66
+ > **Installing from source (pre-release or offline):**
67
+ > ```sh
68
+ > git clone https://github.com/raffishquartan/claude-code-session-tools.git
69
+ > cd claude-code-session-tools
70
+ > uv tool install .
71
+ > ```
72
+
73
+ ### Install the skills (optional)
74
+
75
+ Each skill is a self-contained directory under `skills/`. To make them visible to Claude Code, symlink them into `~/.claude/skills/`:
76
+
77
+ ```sh
78
+ ln -s "$PWD/skills/find-claude-code-session" ~/.claude/skills/find-claude-code-session
79
+ ln -s "$PWD/skills/move-session" ~/.claude/skills/move-session
80
+ ln -s "$PWD/skills/claude-usage" ~/.claude/skills/claude-usage
81
+ ```
82
+
83
+ The skills shell out to the installed CLIs - they don't import the Python library directly, so the only requirement is that `ccs` / `claude-code-usage` are on `$PATH`.
84
+
85
+ ## Configuration: where do your sessions live?
86
+
87
+ `ccd` refuses to start a session if your current working directory isn't a direct child of one of your configured **session roots**. This sounds annoying but turns out to be a feature: it stops you from accidentally starting a session in `/tmp` or in `~`, and it lets `ccr`/`ccs` find sessions across your projects without you telling them where to look.
88
+
89
+ Roots are configured via two environment variables - both optional, but you'll want at least one:
90
+
91
+ ### `CLAUDE_SESSION_TOOLS_REPO_ROOT` - the **loose** root
92
+
93
+ Point this at the directory whose direct children are your projects (the typical case is `~/repos`):
94
+
95
+ ```sh
96
+ export CLAUDE_SESSION_TOOLS_REPO_ROOT="$HOME/repos"
97
+ ```
98
+
99
+ A "session root" means: if `$REPO_ROOT/foo/` exists, then `cd ~/repos/foo && ccd my-tag` is allowed and creates `~/repos/foo/cc-sessions/20260509-my-tag/`. Sessions started two levels deep (`~/repos/foo/sub/`) are rejected unless you pass `--force`.
100
+
101
+ Under the loose root the only naming rule is **no spaces in the tag**. Tag suffixes can be anything else: `bugfix-7`, `redesign`, `try-out-thing`.
102
+
103
+ ### `CLAUDE_SESSION_TOOLS_PROJ_ROOT` - the **strict** (namespaced) root
104
+
105
+ Pointing at this is opt-in. It's useful if you keep a separate directory for "Claude Code project" workspaces (think one folder per long-running theme, like `~/cc-claude-code/migration/`, `~/cc-claude-code/oneshot/`):
106
+
107
+ ```sh
108
+ export CLAUDE_SESSION_TOOLS_PROJ_ROOT="$HOME/cc-claude-code"
109
+ ```
110
+
111
+ Under the strict root, two extra rules apply:
112
+
113
+ 1. **Project directory names** must match `[a-z0-9]+` - lowercase, no dashes, no underscores.
114
+ 2. **Tag suffixes** must start with `<project-name>-` followed by a descriptive label.
115
+
116
+ So in `~/cc-claude-code/oneshot/`, `ccd oneshot-config-cleanup` is fine but `ccd config-cleanup` is rejected with a friendly error. The strict root also enables `ccd`'s [Levenshtein typo prompt](#typo-protection-strict-root-only): if you type `oneshet-foo`, it offers to correct it to `oneshot-foo`.
117
+
118
+ You can configure either, both, or neither. With neither set, you'll need `ccd --force` to start any session, and `ccr`/`ccs` won't find anything.
119
+
120
+ ### Why two roots, and which should I use?
121
+
122
+ | | `REPO_ROOT` (loose) | `PROJ_ROOT` (strict) |
123
+ |---|---|---|
124
+ | Where you point it | A directory you already use for code, e.g. `~/repos` | A purpose-built directory for Claude Code workspaces, e.g. `~/cc-claude-code` |
125
+ | Naming conventions | None beyond no-spaces | Project name `[a-z0-9]+`, tag `<project>-<label>` |
126
+ | Typo protection | Off | On (Levenshtein-checked against project name) |
127
+ | Best for | Day-to-day work in existing repos | Long-running, themed Claude Code projects you want kept tidy |
128
+
129
+ Most users only need `REPO_ROOT`. Configure `PROJ_ROOT` later if you find yourself wanting tighter conventions for a specific subset of work.
130
+
131
+ ## Session management CLIs
132
+
133
+ ### `ccd` - start a session
134
+
135
+ ```sh
136
+ cd ~/repos/myproject
137
+ ccd bugfix-flaky-test
138
+ # Creates ~/repos/myproject/cc-sessions/20260509-bugfix-flaky-test/
139
+ # working/
140
+ # out/
141
+ # And launches `claude` with -n 20260509-bugfix-flaky-test (tagged display name).
142
+ ```
143
+
144
+ Useful flags:
145
+ - `--force` - skip the root check and any naming-convention checks (escape hatch for one-off invocations outside your roots).
146
+ - Anything after the tag is forwarded to `claude` verbatim, so `ccd my-tag --model opus` works.
147
+
148
+ ### `ccr` - resume by fragment
149
+
150
+ ```sh
151
+ ccr flaky # resumes whichever session has "flaky" in its name
152
+ ccr 20260509 # resumes whichever session was started on that date
153
+ ```
154
+
155
+ If multiple sessions match, `ccr` prints them and exits cleanly so you can rerun with a more specific fragment. If exactly one matches, it execs `claude --resume <basename>` with the right working directory.
156
+
157
+ ### `ccs` - search
158
+
159
+ ```sh
160
+ ccs flaky # name search in current project
161
+ ccs flaky --global # name search across all configured roots
162
+ ccs "GraphQL retry" --contents # full-text search of files in current project's sessions
163
+ ccs "GraphQL retry" --contents --global # ... across all projects
164
+ ```
165
+
166
+ Results are ordered newest-first by session start date. `--contents` shows one line of context around each match.
167
+
168
+ ## Usage analytics CLI
169
+
170
+ ### `claude-code-usage` - tokens and dollars by every dimension you care about
171
+
172
+ The CLI parses your `~/.claude/projects/**/*.jsonl` transcripts into a Pandas DataFrame (mtime-keyed Parquet cache means subsequent runs are fast), splits per-tool tokens evenly across `tool_use` blocks, and lets you slice the result.
173
+
174
+ Five subcommands:
175
+
176
+ | | What it does |
177
+ |---|---|
178
+ | `query` | Multi-dimensional filter + group-by, output as markdown / CSV / JSON. The workhorse. |
179
+ | `report` | Render a full multi-section markdown report (project / model / time-bucket breakdowns at once). |
180
+ | `children` | List child sessions (hook-security-review, subagent dispatches) of a given parent session. |
181
+ | `warm-cache` | Populate or refresh the Parquet cache without producing output. |
182
+ | `reconcile` | Compare our totals against [`ccusage`](https://github.com/ryoppippi/ccusage)'s authoritative figures, so we know the numbers are right. |
183
+
184
+ A few examples:
185
+
186
+ ```sh
187
+ # Total spend last month, grouped by project (top 10 by cost)
188
+ claude-code-usage query --since 2026-04-01 --until 2026-04-30 --group-by project --top 10
189
+
190
+ # Where Opus tokens went this week, by session
191
+ claude-code-usage query --since 2026-05-04 --model opus --group-by session
192
+
193
+ # How often each MCP server gets used, across the last quarter
194
+ claude-code-usage query --since 2026-02-01 --group-by mcp --sort token_total
195
+
196
+ # Daily spend trend for one project
197
+ claude-code-usage query --project myproject --group-by day --format csv
198
+
199
+ # Full report of last calendar month
200
+ claude-code-usage report --since 2026-04-01 --until 2026-04-30
201
+
202
+ # Cross-validate against ccusage
203
+ claude-code-usage reconcile --since 2026-04-01
204
+ ```
205
+
206
+ Run `claude-code-usage <subcommand> --help` for the full grammar. A few flags worth knowing:
207
+
208
+ - `--exclude-hooks` strips out the `bash-security-review.sh` hook sessions, which would otherwise distort per-session cost breakdowns by ~$1.60 each.
209
+ - `--include-children` (when grouping by session) folds child-session tokens and cost into the parent row.
210
+ - `--session-format {name,uuid,both}` controls how the session column renders - by display name (default), by UUID, or both.
211
+
212
+ ## Bundled skills
213
+
214
+ The repo ships three Claude Code skills, designed to be symlinked into `~/.claude/skills/`. They're thin wrappers around the CLIs so a Claude Code session can invoke them on your behalf in response to natural-language prompts.
215
+
216
+ ### `find-claude-code-session`
217
+
218
+ Wraps `ccs`. Triggers on prompts like "find my session about X", "did I work on foo before", "what session was I in when Y". Constructs the right `ccs` invocation, escalates from local to global search if the local hit list is empty, and presents results as `ccr <fragment>` commands you can paste.
219
+
220
+ ### `move-session`
221
+
222
+ Moves, renames, or move+renames a session directory while keeping the JSONL transcript resumable. Triggers on "move session to", "rename my session", "this session belongs in a different folder". Dry-run by default - you must pass `--execute` for any filesystem change. Validates source and destination against the same rules as `ccd`, copies the session directory tree, rewrites JSONL `cwd` fields to the destination path, and appends a tombstone record to the source JSONL so `claude --resume` on the old session explains where it went.
223
+
224
+ ### `claude-usage`
225
+
226
+ Wraps `claude-code-usage`. Triggers on usage questions: "how much have I spent on Claude Code", "tokens used this week", "which project costs the most", "Opus vs Sonnet", "any spike in usage recently". Picks the right subcommand and flags, runs it, and summarises the result in plain English.
227
+
228
+ See `docs/design.md` for the full design and CLI contract.
229
+
230
+ ## How it interacts with Claude Code's task lists
231
+
232
+ Claude Code lets multiple sessions share a single task list if they all set the same `CLAUDE_CODE_TASK_LIST_ID` environment variable. `ccd` and `ccr` derive this from the project layout:
233
+
234
+ - If your cwd is a direct child of a configured root (e.g. `~/repos/myproject`), the task list ID is set to the project directory name (`myproject`). All sessions started in `~/repos/myproject` share one task list.
235
+ - If your cwd is anywhere else (or both env vars are unset and you used `--force`), no task list ID is set and the session gets a private task list.
236
+
237
+ This means you can pick up a task created in yesterday's session from today's session in the same project, without any extra setup.
238
+
239
+ ## Typo protection (strict root only)
240
+
241
+ When you start a session under the **strict** (`PROJ_ROOT`) root, `ccd` checks whether your tag's first dash-separated term looks like a typo of the project directory name (Levenshtein distance ≤ 2):
242
+
243
+ ```sh
244
+ cd ~/cc-claude-code/oneshot
245
+ ccd oneshet-fix-bug
246
+ ccd: 'oneshet' looks like a typo of project folder 'oneshot' (Levenshtein 1).
247
+ ccd: Start session with tag 'oneshot-fix-bug' instead? [y/N]
248
+ ```
249
+
250
+ A second prompt fires if your first term is far from the current project name **and** far from every sibling project under `PROJ_ROOT` - in that case `ccd` offers to prepend the current project name. This behaviour is intentionally off under the loose root.
251
+
252
+ ## Sessions on disk
253
+
254
+ Each session directory looks like:
255
+
256
+ ```
257
+ cc-sessions/20260509-bugfix-flaky-test/
258
+ working/ # scratch files, notes, WORKLOG.md - whatever you want
259
+ out/ # deliverables you might keep or hand off
260
+ ```
261
+
262
+ Add `cc-sessions/` to your project's `.gitignore` if you don't want session artefacts tracked.
263
+
264
+ ## Development
265
+
266
+ ```sh
267
+ git clone https://github.com/raffishquartan/claude-code-session-tools.git
268
+ cd claude-code-session-tools
269
+ uv sync --extra dev
270
+ uv run pytest
271
+ ```
272
+
273
+ Tests run on Python 3.10, 3.11, 3.12, and 3.13 (see `.github/workflows/ci.yml`). CI also
274
+ includes an `install-check` job that runs `uv tool install .` and verifies all four CLIs
275
+ start up correctly - the direct guard against the editable-install/worktree failure mode.
276
+
277
+ > **When working in a git worktree:** test your changes with `uv run pytest` or
278
+ > `uv run python -m cc_session_tools.cli.ccd` - do not run `uv tool install` from inside
279
+ > a worktree. After merging, run `uv tool install ~/repos/claude-code-session-tools`
280
+ > (or `uv tool install cc-session-tools` if installed from PyPI) to update the global
281
+ > install.
282
+
283
+ ## Limitations and caveats
284
+
285
+ - Linux and macOS only. Windows is not tested; the tools assume POSIX paths and `os.execvpe`-style process replacement.
286
+ - The session-management CLIs shell out to `claude` via `os.execvpe`. If `claude` isn't on `$PATH`, `ccd` and `ccr` will fail with the standard "command not found" error.
287
+ - `claude-code-usage` reads from `~/.claude/projects/` and writes a Parquet cache under `~/.cache/claude-code-usage/parquet/` (overrideable via `--projects-dir` and `--cache-dir`). Pricing data is loaded from `data/pricing.json` shipped with the package, refreshed lazily from LiteLLM upstream with a 7-day TTL.
288
+ - The strict-root convention is opinionated. If you don't want it, just leave `CLAUDE_SESSION_TOOLS_PROJ_ROOT` unset.
289
+
290
+ ## Licence
291
+
292
+ MIT - see [LICENSE](LICENSE).