Skip to content

Commit 5ca6a94

Browse files
committed
v0.2 with multi workspace for cursor
1 parent cf71024 commit 5ca6a94

25 files changed

+3207
-15
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
Export AI conversation sessions into a Git repository using a readable, time-based structure.
44

5+
![](docs/assets/screenshot.png)
6+
57
## What it does
68

79
- Scans source session files (Codex JSONL, Claude projects, Cursor workspaceStorage).
@@ -13,6 +15,7 @@ Export AI conversation sessions into a Git repository using a readable, time-bas
1315
- `sync`: `history/<user>/<source-system>/` (flat — sessions directly inside)
1416
- `backup`: `history/<user>/<source-system>/<system-name>/<path-relative-to-home>/...`
1517
- Runs idempotently (only reprocesses changed or new sessions).
18+
- Cursor: supports both single-folder and multi-root (`.code-workspace`) windows — sessions are attributed to the matching repo folder.
1619

1720
## Install and run
1821

@@ -58,6 +61,7 @@ uv run convx backup \
5861
- default for Codex: `~/.codex/sessions`
5962
- default for Claude: `~/.claude/projects`
6063
- default for Cursor: `~/Library/Application Support/Cursor/User/workspaceStorage` (macOS)
64+
Supports both single-folder and multi-root (`.code-workspace`) Cursor windows.
6165
- `--user`: user namespace for history path (default: current OS user).
6266
- `--system-name`: system namespace for history path (default: hostname).
6367
- `--dry-run`: discover and plan without writing files.

docs/assets/screenshot.png

102 KB
Loading

history/pascal/cursor/.2026-02-19-1726-hashing-secrets-in-python-library.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,24 @@
8585
"role": "user",
8686
"text": "let's add docs to docs/secrets.md about how this works from user perspective and technicaily",
8787
"timestamp": null
88+
},
89+
{
90+
"kind": "assistant",
91+
"role": "assistant",
92+
"text": "Added `docs/secrets.md` with:\n\n- **User perspective:** Redaction on by default; `--no-redact` for sync/backup; what gets redacted vs left alone; that secrets appear as `[REDACTED:...]` in the written files.\n- **Technical:** plumbrc and its pattern set; redaction applied after render, before write, to markdown and JSON (not the index); where it’s implemented (redact module + engine) and how the CLI passes the flag. Also notes that if plumbrc isn’t available, redaction is skipped and content is written unchanged.",
93+
"timestamp": null
94+
},
95+
{
96+
"kind": "user",
97+
"role": "user",
98+
"text": "obviously we don't take any liability for people... they are responsible for making sure they don't leak secrets into the repo.",
99+
"timestamp": null
100+
},
101+
{
102+
"kind": "assistant",
103+
"role": "assistant",
104+
"text": "Added this sentence to the intro: “You are responsible for ensuring no secrets are committed; we provide no warranty or liability.”",
105+
"timestamp": null
88106
}
89107
],
90108
"session_id": "d19790a9-7a67-4331-9e2f-0edc6c13b99a",

history/pascal/cursor/.2026-02-19-1743-ai-session-extraction-from-cursor.json

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,72 @@
205205
"role": "assistant",
206206
"text": "The sync command now shows:\n\n1. **Step counter** – `[1/3]`, `[2/3]`, `[3/3]` for each source\n2. **Spinner** – a dots spinner while each source is processed\n3. **Per-source summary** – discovered, exported, updated, skipped, filtered for each source\n4. **Overall summary** – totals at the end\n\nThe backup command also shows a spinner while it runs.",
207207
"timestamp": null
208+
},
209+
{
210+
"kind": "user",
211+
"role": "user",
212+
"text": "great the output_repo, history and discovered lines... can we also make them better? check context7 for best practices with typer UI",
213+
"timestamp": null
214+
},
215+
{
216+
"kind": "user",
217+
"role": "user",
218+
"text": "can we also get mouse scrolling someehow? Feels cumbersome to get through the UI right now. Aren't there best practices for TUIs that let people feel autoamtically at home in the tool if they are used to CLI tools? \n\nalso I'm missing the user in the navigation in the TUI. and there's no point in having\n\nDate | tool | <date>-slug\n\nthe 2nd date should be stripped from the file slug.",
219+
"timestamp": null
220+
},
221+
{
222+
"kind": "assistant",
223+
"role": "assistant",
224+
"text": "Implementing the changes:",
225+
"timestamp": null
226+
},
227+
{
228+
"kind": "assistant",
229+
"role": "assistant",
230+
"text": "Implementing all changes:",
231+
"timestamp": null
232+
},
233+
{
234+
"kind": "assistant",
235+
"role": "assistant",
236+
"text": "Adding actions to forward j/k to the focused widget:",
237+
"timestamp": null
238+
},
239+
{
240+
"kind": "assistant",
241+
"role": "assistant",
242+
"text": "Checking how Textual dispatches actions:",
243+
"timestamp": null
244+
},
245+
{
246+
"kind": "assistant",
247+
"role": "assistant",
248+
"text": "Adding forwarding actions so j/k work when the list has focus:",
249+
"timestamp": null
250+
},
251+
{
252+
"kind": "assistant",
253+
"role": "assistant",
254+
"text": "Test failures are pre-existing (they expect the old `exported=4` format; output now uses a Rich table). Verifying the TUI runs:",
255+
"timestamp": null
256+
},
257+
{
258+
"kind": "assistant",
259+
"role": "assistant",
260+
"text": "Summary of what was implemented:\n\n## Changes\n\n### 1. **Mouse scrolling**\n- Wrapped the preview in a `VerticalScroll` container so the Markdown content scrolls with the mouse wheel\n- Set `scrollbar-size: 0 1` on the list and preview so scrollbars are visible\n\n### 2. **TUI bindings**\n- **j** / **k** – move up/down in the list (vim-style)\n- **g** – jump to first item\n- **G** – jump to last item\n\n### 3. **User in navigation**\n- Added `user` to session data by parsing it from the path (`history/<user>/<source>/...`)\n- Display format is now: `user | date | source | slug`\n\n### 4. **Date removed from slug**\n- Added `_slug_only()` to strip the `YYYY-MM-DD-HHMM-` prefix from the basename\n- Display shows only the slug (e.g. `hashing-secrets-in-python-library` instead of `2026-02-19-1726-hashing-secrets-in-python-library`)\n\n### Files touched\n- `src/conversation_exporter/tui.py` – bindings, layout, formatting\n- `src/conversation_exporter/search.py` – `_user_from_path()`, `user` in session data\n- `src/conversation_exporter/explore.css` – scrollbar styling\n\nThe list now shows rows like `pascal 2026-02-19 cursor hashing-sec` instead of repeating the date in the slug.",
261+
"timestamp": null
262+
},
263+
{
264+
"kind": "user",
265+
"role": "user",
266+
"text": "can you check the docs of the tool we use to build the TUI to see how we can make the UI more attractive/intuitive? what options do we have?",
267+
"timestamp": null
268+
},
269+
{
270+
"kind": "assistant",
271+
"role": "assistant",
272+
"text": "Here’s what Textual offers for making the TUI more attractive and intuitive:\n\n---\n\n## Textual UI Design Options\n\n### 1. **Header & Footer**\n- Built-in `Header` and `Footer` widgets\n- Header: app title, navigation\n- Footer: key bindings (already used by Textual)\n- Add them in `compose()`:\n ```python\n yield Header()\n yield Footer()\n ```\n\n### 2. **Themes**\n- Built-in themes: `textual-dark`, `textual-light`, `nord`, `gruvbox`, `tokyo-night`, `solarized-light`, `atom-one-dark`, `atom-one-light`\n- Switch at runtime: `self.theme = \"nord\"` or via Command Palette (Ctrl+P)\n- Custom themes via `Theme()` with `primary`, `secondary`, `accent`, `foreground`, `background`, `surface`, `panel`, etc.\n\n### 3. **Design Tokens (Theme Variables)**\nUse these in CSS for consistent styling:\n- **Base colors:** `$primary`, `$secondary`, `$accent`, `$foreground`, `$background`, `$surface`, `$panel`\n- **Text:** `$text-primary`, `$text-accent`, `$foreground-muted`, `$foreground-disabled`\n- **Muted backgrounds:** `$primary-muted`, `$accent-muted`, etc.\n- **Shades:** `$primary-darken-1`, `$primary-lighten-2`, etc.\n\n### 4. **CSS Styling**\n- **Borders:** `border: solid $accent`, `border: heavy white`, `border-bottom: solid blue`, `border: round orange 50%`\n- **Padding:** `padding: 1 2` or `padding: 2`\n- **Backgrounds:** `background: $panel`, `background: $primary-muted`\n- **Layout:** `align: center middle`, `content-align: center middle`\n- **Hover/focus:** `:hover`, `:focus` for interactive feedback\n\n### 5. **Layout**\n- **Dock:** `dock: top`, `dock: bottom` for fixed header/footer\n- **Grid:** `layout: grid`, `grid-size: 2 2`\n- **Spacing:** `margin`, `padding`, `grid-gutter`\n- **Sizing:** `width: 1fr`, `height: auto`, `min-width`, `min-height`\n\n### 6. **Widget-Specific Styling**\n- **ListView:** `padding: 1 2` on `Label`, `margin: 2 2` on `ListView`\n- **Input:** `input--cursor`, `input--placeholder`, `input--selection` for cursor/placeholder/selection\n- **ListItem:** Use CSS classes for selected vs unselected\n\n### 7. **Dynamic Styling**\n- Add/remove CSS classes for state changes (e.g. `.started`, `.selected`)\n- `check_action()` to show/hide or dim bindings based on state\n\n### 8. **Layout Design Tips**\n- Start with a sketch, then implement\n- Build from outside in: header/footer first, then main content\n- Use `Placeholder` to mock layout before real widgets\n\n---\n\n## Concrete Ideas for the Explore TUI\n\n| Option | What to do |\n|--------|------------|\n| **Header** | Add `Header()` with title like \"convx history\" |\n| **Footer** | Add `Footer()` so bindings (q, Esc, j/k, g/G) are visible |\n| **Theme** | Set `self.theme = \"nord\"` or `\"gruvbox\"` in `on_mount` |\n| **Panels** | Use `background: $panel` on list and preview for separation |\n| **Borders** | Add `border: tall $background` or `border-right: solid $border` between list and preview |\n| **List items** | Add `padding: 1 2` on list labels, `border-bottom: tall $background` for row separation |\n| **Search input** | Style with `border: solid $accent` on focus |\n| **Visual hierarchy** | Use `$foreground-muted` for dates/source, `$foreground` for slugs |\n\nI can help turn any of these into concrete code changes for your TUI.",
273+
"timestamp": null
208274
}
209275
],
210276
"session_id": "4f91a599-14d8-4739-a0ba-b6798f4a93b0",
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
{
2+
"child_sessions": null,
3+
"cwd": "/Users/pascal/Code/business/convx",
4+
"messages": [
5+
{
6+
"kind": "user",
7+
"role": "user",
8+
"text": "OK what are we missing before we can publish this to pypi? Any advice on missing pieces? \n\nMIT license for sure.",
9+
"timestamp": null
10+
},
11+
{
12+
"kind": "assistant",
13+
"role": "assistant",
14+
"text": "Checking a few more things:",
15+
"timestamp": null
16+
},
17+
{
18+
"kind": "assistant",
19+
"role": "assistant",
20+
"text": "Adding the missing pieces for PyPI publishing:",
21+
"timestamp": null
22+
},
23+
{
24+
"kind": "assistant",
25+
"role": "assistant",
26+
"text": "Here’s what was added and what’s left to do:\n\n## Changes made\n\n1. **LICENSE** – MIT license added. Update the copyright line (`Copyright (c) 2026`) with your name if you want.\n2. **pyproject.toml** – Added `license`, `keywords`, and `classifiers` for PyPI.\n3. **README** – Install section updated for PyPI users (`pip install conversation-exporter`) and a separate “From source” section.\n\n## Build check\n\n`uv build` succeeds and produces both sdist and wheel.\n\n## Optional next steps\n\n- **project.urls** – Add when you have a repo, e.g.:\n ```toml\n [project.urls]\n Homepage = \"https://github.com/you/convx\"\n Repository = \"https://github.com/you/convx\"\n ```\n- **PyPI account** – Create one at [pypi.org](https://pypi.org) if needed.\n- **TestPyPI first** – Try publishing to TestPyPI before the real index:\n ```bash\n uv publish --repository https://test.pypi.org/legacy/\n ```\n- **API token** – Use a PyPI API token instead of a password when publishing.\n\n## Publishing\n\n```bash\nuv build\nuv publish\n```\n\nYou’ll be prompted for your PyPI credentials or can use `UV_PUBLISH_TOKEN` for automation.",
27+
"timestamp": null
28+
},
29+
{
30+
"kind": "user",
31+
"role": "user",
32+
"text": "can you take inspiration from @medical-mcps/.github/ in terms of building the github CI? \n\npush to this remote \nhttps://github.com/pascalwhoop/convx",
33+
"timestamp": null
34+
},
35+
{
36+
"kind": "assistant",
37+
"role": "assistant",
38+
"text": "Here’s what was set up, based on [medical-mcps](https://github.com/pascalwhoop/medical-mcps):\n\n## GitHub CI\n\n**`.github/workflows/pr.yml`** – runs on pull requests:\n- **test**: `uv sync` → `uv run pytest`\n- **lint**: `uv run ruff check src tests`\n- Concurrency with cancel-in-progress\n\n**`.github/workflows/release.yml`** – runs on push to `main` (excluding docs/history):\n1. **test** – same as PR\n2. **version** – `uv version --bump patch`, updates `__init__.py`, commits, tags, pushes\n3. **pypi** – `uv build` → `uv publish` (uses `PYPI_API_TOKEN`)\n4. **github-release** – signs dists with sigstore, creates GitHub release with notes\n\n## Other changes\n\n- **ruff** added to dev deps for linting\n- **Unused `role`** removed in `claude.py`\n- **Test assertions** updated for Rich table output (`Exported 4` instead of `exported=4`)\n- **`project.urls`** added in `pyproject.toml` for the GitHub repo\n\n## Before first release\n\n1. **PyPI environment** – In the repo: Settings → Environments → create `pypi`\n2. **`PYPI_API_TOKEN`** – Add a PyPI API token as a secret in that environment\n3. **Optional**: Commit the `project.urls` change in `pyproject.toml`\n\nThe initial push to `https://github.com/pascalwhoop/convx` is done. Docker and Cloud Run steps from medical-mcps were left out since convx is a CLI tool.",
39+
"timestamp": null
40+
},
41+
{
42+
"kind": "user",
43+
"role": "user",
44+
"text": "don't like the token based auth can't we authenticate on pypi the repo ? \n\nI did the below:\n\n SuccessRegistered a new pending publisher to create the project 'convx-ai'.\nPyPI\nSearch PyPI\nType '/' to search projects\nSearch\nUser Image pascalwhoop \nYour account\n Your projects\n Your organizations\n Account settings\n Publishing\nTrusted Publisher Management\nOpenID Connect (OIDC) provides a flexible, credential-free mechanism for delegating publishing authority for a PyPI package to a trusted third party service, like GitHub Actions.\n\nPyPI users and projects can use trusted publishers to automate their release processes, without needing to use API tokens or passwords.\n\nYou can read more about trusted publishers and how to use them here.\n\nManage publishers\nProjects with active publishers",
45+
"timestamp": null
46+
},
47+
{
48+
"kind": "assistant",
49+
"role": "assistant",
50+
"text": "Updating the release workflow to use PyPI Trusted Publishers (OIDC) instead of API tokens:",
51+
"timestamp": null
52+
},
53+
{
54+
"kind": "assistant",
55+
"role": "assistant",
56+
"text": "The workflow now uses **Trusted Publishers (OIDC)** instead of API tokens. `uv publish` will use the GitHub Actions OIDC token automatically when the job runs in the `pypi` environment.\n\n**PyPI Trusted Publisher configuration** (must match exactly):\n\n| Field | Value |\n|-------|-------|\n| Owner | `pascalwhoop` |\n| Repository name | `convx` |\n| Workflow name | `release.yml` |\n| Environment name | `pypi` |\n\n**Package name:** You registered a publisher for the project `convx-ai`, but `pyproject.toml` uses `name = \"conversation-exporter\"`. They must match:\n\n- To publish as **convx-ai**: set `name = \"convx-ai\"` in `pyproject.toml`\n- To publish as **conversation-exporter**: add a trusted publisher for the existing `conversation-exporter` project on PyPI (or create it first)\n\nYou can remove the `PYPI_API_TOKEN` secret from the `pypi` environment; it’s no longer used.",
57+
"timestamp": null
58+
},
59+
{
60+
"kind": "user",
61+
"role": "user",
62+
"text": "ok pls update everything to convx-ai as needed.",
63+
"timestamp": null
64+
},
65+
{
66+
"kind": "assistant",
67+
"role": "assistant",
68+
"text": "Summary of changes:\n\n**Package rename**\n- `src/conversation_exporter/` → `src/convx_ai/`\n- PyPI package name: `convx-ai`\n- Python module: `convx_ai`\n\n**Updated files**\n- `pyproject.toml` – name, script entry point\n- `.github/workflows/release.yml` – paths, PyPI URL\n- All imports in `src/convx_ai/`\n- Tests – `-m convx_ai`\n- `README.md` – `pip install convx-ai`\n- `AGENTS.md`, `CLAUDE.md`, `docs/secrets.md`\n\n**Verification**\n- Tests pass\n- `uv build` succeeds\n- Ruff passes\n\nInstall with: `pip install convx-ai`",
69+
"timestamp": null
70+
},
71+
{
72+
"kind": "user",
73+
"role": "user",
74+
"text": "add your changes as a commit",
75+
"timestamp": null
76+
},
77+
{
78+
"kind": "assistant",
79+
"role": "assistant",
80+
"text": "Commit created as `e2caa09` with message:\n\n```\nrefactor: rename to convx-ai\n\n- Package: conversation-exporter -> convx-ai\n- Module: conversation_exporter -> convx_ai\n- Update PyPI workflow, README, docs\n```",
81+
"timestamp": null
82+
}
83+
],
84+
"session_id": "009604e4-6e2b-4cc8-ae7d-91417d433e07",
85+
"session_key": "cursor:009604e4-6e2b-4cc8-ae7d-91417d433e07",
86+
"source_path": "/Users/pascal/Library/Application Support/Cursor/User/globalStorage/state.vscdb",
87+
"source_system": "cursor",
88+
"started_at": "2026-02-19T18:35:25.186000Z",
89+
"summary": "PyPI publication requirements",
90+
"system_name": "Pascals-MBP.local",
91+
"user": "pascal"
92+
}

0 commit comments

Comments
 (0)