- Swift 98.7%
- Shell 1.3%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
A line-by-line audit (cross-checked against the real stores on this Mac) found path traversal, multi-gigabyte memory spikes, crashes on corrupt data, silent scan failures and several data-loss paths. All are fixed: Security - omp `blob:sha256:<hash>` must be a bare 64-hex digest; `../../.ssh/id_rsa` used to be read and embedded into the imported conversation. - Vibe and Copilot attachment paths (absolute, outside the session folder in real data) are only read when the file is a regular file <= 32 MB whose bytes are really an image (magic-byte check). - A project folder ending in `..` no longer makes Freebuff imports write outside ~/.config/manicode/projects: cwd is standardized for every target and the Freebuff project name is sanitized. - Session text renders inline markdown without clickable links. Memory - JSONL files are streamed in 1 MB chunks with an autorelease pool per chunk; Claude and pi/omp reads index parent links + line offsets and re-read only the active branch. Listing a real 1.3 GB Claude session went from 1.3 GB to 30 MB RSS, opening it from ~1.4 GB to 116 MB. - Goose/OpenCode scans stream rows instead of loading every user message; whole-document JSON reads are size-capped; thumbnails are downsampled off the main thread. Crashes and data integrity - Timestamps: unit classified by magnitude (s/ms/us/ns), NaN/inf/absurd values rejected; Double->Int64 conversions can no longer trap. - SQLite: text keeps embedded NULs, UTF-8 BLOBs read as text, scalar() reads the first selected column. - Writes are 0600 from the first byte and atomic; Aider transcripts and Freebuff chat folders are claimed exclusively (no clobbering on races). - Codex index appends use O_APPEND and repair a missing trailing newline; the installed version comes from the newest rollout's cli_version. - Messages without a timestamp keep their position in time-ordered stores (Hermes, OpenCode v1, Freebuff, Goose, Copilot). - OpenCode IDs are ascending like the CLI's own; existing v1 project files are no longer overwritten; git lookups time out after 5 s. - Goose session ids are allocated inside the write transaction. Format fidelity - Aider: consecutive `#### ` lines form one user message (as aider parses them), so multi-line prompts survive; escaped marker lines read back clean. - Codex: injected `<environment_context>` / AGENTS.md user messages are no longer imported as human turns; tool-output images are kept. - Copilot: compaction keeps the recent messages Copilot itself keeps; results stay in completion order; workspace.yaml strings are always quoted. - Vibe: provider read from the `config.models` list. - Freebuff: dates roll over past midnight and accept folders without ms. Reporting and app - A store that exists but cannot be read is reported (warning in the sidebar and list) instead of looking empty; one bad Copilot/Codex file no longer hides the others. - Rescans requested during a scan are queued; a deleted session's conversation is cleared; Terminal failures show an alert; the import preview is computed once per target. Packaging - The installer refuses sudo, checks bundle id + codesign before clearing the quarantine, and replaces the old app only after a verified staged copy; build_app.sh does the same. Docs updated; jimmy/ ignored. Verified: 72 tests, 0 failures (10 opt-in probes skipped); the 15 new regression tests fail on the previous code (one crashed the test process on Int64(inf)). RealStoreProbe: all 11 harnesses scan and load with no unreadable entries. Release app launched and opened the 1.3 GB session (407 MB RSS). Bump to 1.3.5 (build 9). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Model: anthropic/claude-opus-5-5 |
||
| scripts | ||
| Sources | ||
| Support | ||
| Tests/AgentChatCoreTests | ||
| .gitignore | ||
| Package.swift | ||
| project.yml | ||
| README.md | ||
Agent Chat Importer
A native macOS app that finds every chat session from your coding agents and copies any of them into another agent, so you can continue the same conversation there with its full context: every human turn, AI reply, reasoning block, tool call, tool result and image.
Claude Code, pi, Oh My Pi, Mistral Vibe, Freebuff, OpenCode (v1 and v2), Codex, Goose, Aider, GitHub Copilot CLI and Hermes are all supported as sources and as import targets.
Install (no coding needed)
You need: a Mac running macOS 14 (Sonoma) or newer, and at least one of the agents above already used on your Mac. Not sure of your macOS version? Click the menu → About This Mac.
Step 1: Download
- Open the Releases page and click the latest version (for example v1.3.5).
- Under Assets, click
Agent-Chat-Importer-1.3.5.dmgto download it. (If your browser or chat app doesn't like the.dmg, download...-dmg.zipinstead and double-click it to unzip.)
Step 2: Install
Easy way: double-click Installer Agent Chat Importer.command in the disk image.
Terminal opens, copies the app to Applications, clears the macOS quarantine flag and
launches it. Nothing else to do.
Manual way:
- Double-click the downloaded
.dmg. A small window opens. - Drag the "Agent Chat Importer" icon onto the "Applications" folder in that same window.
- Close the window. You can drag the
.dmgto the Trash, and eject the "Agent Chat Importer" disk from Finder's sidebar.
Step 3: Open it the first time
The app isn't signed with a paid Apple developer account, so macOS blocks the first launch with "Apple could not verify … is free of malware". That message means "not notarized", not "virus"; the sources are in this repository.
On macOS 15 and newer the dialog has only a Done button — the old right-click → Open trick no longer exists:
- Click Done.
- Open System Settings → Privacy & Security.
- Scroll to the bottom: "Agent Chat Importer was blocked…" → click Open Anyway.
- Authenticate, then Open.
Or clear the flag once from Terminal:
xattr -dr com.apple.quarantine "/Applications/Agent Chat Importer.app"
You only have to do this once. After that it opens normally (Launchpad, Spotlight, or the Dock).
Step 4: Move a chat
- In the left column, pick an agent (Claude Code, pi, Oh My Pi, Mistral Vibe, Freebuff, OpenCode, Codex, Goose, Aider, GitHub Copilot CLI or Hermes). Your chats appear in the middle.
- Click a chat to preview it on the right.
- Click Import into… at the top right and choose where it should go.
- Check the project folder (the default is where the chat originally ran, which is usually right) and click Import.
- The app shows a command like
cd ~/my-project && claude --resume …. Click Copy, open Terminal, paste it and press Return. You're now continuing the same conversation in the other agent.
Your original chat is never changed or deleted; the import always creates a new copy.
Continue a chat where it stopped
You don't need to import to pick an existing session back up. In the session list, every chat has a ▷ button (also in the right-click menu, and as Resume in the toolbar of the open conversation):
- Copy Resume Command puts the exact command on your clipboard, e.g.
cd ~/my-project && omp --resume ~/.omp/agent/sessions/.... - Resume in Terminal opens Terminal in the project folder and runs it for you (the command is also copied to the clipboard, so you can paste it by hand if needed).
The command uses the session's own ID and the folder it originally ran in, so the agent opens the conversation with its full history — no need to start the agent and hunt for the session.
Troubleshooting
| Problem | What to do |
|---|---|
| An agent shows a "–" or "not found" | That agent has no chats on this Mac yet (or isn't installed). Nothing is wrong. |
| A new chat isn't in the list | Press ⌘R (or the circular arrow button) to rescan. |
| "Agent Chat Importer is damaged" | Run the xattr command from Step 3 in Terminal, then open it again. |
| The imported chat isn't found when I start the agent | Start the agent from the same project folder you chose, or just use the copied command. |
To remove the app: drag it from Applications to the Trash. It stores nothing else on your Mac.
Supported agents
| Agent | Sessions read from / written to |
|---|---|
| Claude Code | ~/.claude/projects/<encoded-folder>/<uuid>.jsonl |
| pi | ~/.pi/agent/sessions/--<folder>--/<time>_<uuid>.jsonl |
| Oh My Pi (omp) | ~/.omp/agent/sessions/<folder>/<time>_<uuid>.jsonl (+ image blobs in ~/.omp/agent/blobs) |
| Mistral Vibe | ~/.vibe/logs/session/session_<time>_<id>/ (meta.json + messages.jsonl), or save_dir from ~/.vibe/config.toml |
| Freebuff | ~/.config/manicode/projects/<project>/chats/<timestamp>/ (chat-messages.json, chat-meta.json, run-state.json — the runner state with the replayed messageHistory) |
| OpenCode v2 | ~/.local/share/opencode/opencode.db (session_v2 + session_message) |
| OpenCode v1 | ~/.local/share/opencode/storage/session/<project>/<id>.json (+ message/, part/) |
| Codex | ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl (+ session_index.jsonl) |
| Goose | ~/.local/share/goose/sessions/sessions.db |
| Aider | per-project .aider.chat.history.md; imports write .aider.imported-<id>.md |
| GitHub Copilot CLI | ~/.copilot/session-state/<uuid>/events.jsonl (+ session-store.db) |
| Hermes | ~/.hermes/state.db |
Using it
- Open Agent Chat Importer from
/Applications. It scans every supported agent (about 1 s for ~1.6 GB of sessions). - Pick an agent, then a session. The preview shows exactly what will be imported. Every AI reply carries a model chip, and the session header summarizes the models used; click a chip for the provider, API and exact model ID.
- Click Import into…, choose the target agent and project folder (defaults to where the original session ran), then Import.
- Copy the shown command (or click Open Terminal Here) to continue the chat, e.g.
cd ~/Documents/Dev/FreeLLMapi && claude --resume <id>.
To continue the session in its own agent instead, click the ▷ button on its row and choose Copy Resume Command or Resume in Terminal.
The original session is never modified; every import creates a brand-new session.
What gets imported
The active context: what the source agent itself would send to its model.
- The current branch only (rewinds/abandoned branches and subagent sidechains are left out).
- For compacted sessions: the compaction summary plus the messages after it (for Copilot, the recent messages Copilot itself kept are included too).
- Harness-only records (hook attachments, status/reminder injections, omp developer notes, Codex's injected
<environment_context>/ AGENTS.md messages) are skipped. - Images referenced by path (Vibe and Copilot attachments, omp blobs) are read only when the file really is an image; a session file cannot make the app read anything else.
Sessions are read in a streaming way, so multi-gigabyte transcripts don't load into memory. If an agent's store exists but can't be read (a locked or corrupt database, an unreadable file), a ⚠️ appears next to the agent with the reason, instead of an empty list.
Each target gets what its model API accepts:
- Claude Code only accepts its own signed thinking, so reasoning from other agents becomes visible text. Tool-call IDs are rewritten to Anthropic's allowed alphabet.
- Mistral Vibe needs 9-character tool-call IDs (remapped), stores images as attachment files, and marks failed
tool results with
<tool_error>. Images inside tool results can't be stored by Vibe, so a text marker is left. - Tool calls without a recorded result (session ended mid-tool) get a placeholder error result. Results whose call was compacted away are kept as plain text, so every agent's API sees valid call/result pairs.
- Aider transcripts write every line of a user message with the
####prefix, as Aider itself does, so multi-line prompts keep their line breaks.
Build & install
Requirements: Xcode 15+ (license accepted) and XcodeGen (brew install xcodegen).
./scripts/build_app.sh # builds Release and installs /Applications/Agent Chat Importer.app
To work in Xcode instead: xcodegen generate && open AgentChatImporter.xcodeproj.
The icon is drawn in code; regenerate it with swift scripts/make_icon.swift ..
Tests
The conversion engine is a Foundation-only Swift package, tested with synthetic fixtures:
DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer swift test
Optional end-to-end checks use the real CLIs on this Mac (each writes only to a throwaway home):
ACI_INTEGRATION=1 swift test --filter IntegrationProbe
ACI_PROBE=1 swift test --filter RealStoreProbe
The integration tests are gated because they need the corresponding agent installed
and signed in; they never modify the real session stores. Besides listing/resuming,
IntegrationProbe also runs opencode session export on an imported session, which
fails if any stored row does not match the installed OpenCode's schema.
RobustnessTests.testEveryProviderRoundTripsTheSameConversation imports one
conversation into every target in an isolated home and reads it back, so each
provider is verified in both directions (reading real stores is RealStoreProbe).
Layout
Sources/AgentChatCore/ Conversion engine (no UI)
Model.swift Unified conversation model
ClaudeFormat.swift Claude Code reader/writer
PiFormat.swift pi + omp reader/writer
VibeFormat.swift Mistral Vibe reader/writer
FreebuffFormat.swift Freebuff reader/writer
OpenCodeFormat.swift OpenCode v1 (JSON) + v2 (SQLite) reader/writer
CodexFormat.swift Codex rollout reader/writer
GooseFormat.swift Goose SQLite reader/writer
AiderFormat.swift Aider markdown reader/writer
CopilotFormat.swift GitHub Copilot CLI event-log reader/writer
HermesFormat.swift Hermes SQLite reader/writer
SQLite.swift Small SQLite C-API wrapper
ExportPreparation.swift Per-target adaptations and tool-call pairing repair
SessionLibrary.swift Scan, load, import
AgentPaths.swift Storage locations and folder-name encodings
Sources/AgentChatImporter/ SwiftUI app
TerminalLauncher.swift Clipboard + "resume in Terminal" helpers
Tests/AgentChatCoreTests/ Unit tests
scripts/ build_app.sh, make_icon.swift
project.yml XcodeGen spec for the app