Skip to content

Commit 5b48a00

Browse files
feat: rename to cowork2code; carry MCP servers over as .mcp.json
- rebrand package/CLI/MCP/launcher to cowork2code (history preserved via git mv) - rehome now emits a ready-to-use .mcp.json from the session's remote MCP servers (never clobbers an existing one); surfaced in CLI + MCP output - README: headline trimmed; lead with 'what comes across' (conversation, files, MCP servers, plugins/skills); document .mcp.json - 55 tests passing, ruff clean, wheel builds
1 parent 10f6ebf commit 5b48a00

26 files changed

Lines changed: 273 additions & 229 deletions

README.md

Lines changed: 61 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,72 @@
1-
# claude-cowork-resume
1+
# cowork2code
22

3-
Resume a **Claude Cowork** conversation in **Claude Code** so you can change the model.
3+
**Resume a Claude Cowork conversation in Claude Code.**
44

55
[![License: MIT](https://img.shields.io/badge/License-MIT-informational.svg)](LICENSE)
66
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/downloads/)
77

8-
Claude Cowork ("local agent mode") fixes the model for the life of a session — once a
9-
session starts on, say, `claude-haiku-4-5`, there is no way to switch it to a stronger
10-
model. `claude-cowork-resume` lifts an existing Cowork conversation into Claude Code,
11-
where the model is just a runtime flag, so you can continue the exact same thread on
12-
whatever model you want.
8+
Claude Cowork ("local agent mode") locks the model for the life of a session. `cowork2code`
9+
moves an existing Cowork conversation into Claude Code — where the model is just a runtime
10+
flag — so you pick up the exact same thread on whatever model you want.
1311

14-
It is not a format converter. A Cowork session already stores its conversation as a
15-
standard Claude Code transcript inside a sandboxed home, so this tool simply re-homes
16-
that transcript into a real working directory, brings along the working files, and hands
17-
you the `claude --resume` command.
12+
## What comes across
1813

19-
---
14+
A Cowork session is a whole working environment, not just a chat log. `cowork2code` carries
15+
all of it into Claude Code:
2016

21-
## How it works
17+
- **The full conversation** — re-homed as a native Claude Code session you resume with
18+
`claude --resume` and continue on any model.
19+
- **Every working file** — the session's `outputs/` and `memory/` are copied into your
20+
target directory.
21+
- **The MCP servers** — written to a ready-to-use `.mcp.json` in the target directory, so
22+
Claude Code reconnects them automatically.
23+
- **Plugins, skills, and slash commands** — captured in a `COWORK_SESSION.md` sidecar (with
24+
the raw session config) so you can re-enable them in your Claude Code setup.
2225

23-
```
24-
Claude Cowork session (sandboxed) Claude Code (your home)
25-
--------------------------------- -----------------------
26-
local_<id>/ ~/.claude/projects/
27-
.claude/projects/<dir>/ <escaped-target-cwd>/
28-
<cliSessionId>.jsonl --- re-home (rewrite cwd) ---> <cliSessionId>.jsonl
29-
outputs/ --- copy -----------------> <target-cwd>/
30-
memory/ --- copy -----------------> <target-cwd>/memory/
31-
local_<id>.json (config) --- notes ----------------> <target-cwd>/COWORK_SESSION.md
32-
```
26+
The original Cowork session is never modified — the source is read-only.
27+
28+
## Why this works (and isn't a converter)
3329

34-
1. **Locate** the session's embedded transcript (found by globbing the sandbox, never by
35-
guessing its folder name).
36-
2. **Re-home** it to `~/.claude/projects/<escaped-target-cwd>/<id>.jsonl`, rewriting every
37-
record's working directory to the target so `claude --resume` finds and loads it.
38-
3. **Copy** the session's working files (`outputs/`, `memory/`) into the target directory.
39-
4. **Document** the original configuration (model, system prompt, MCP servers, slash
40-
commands) in a `COWORK_SESSION.md` sidecar for reference.
41-
5. **Print** the exact resume command (or launch it for you).
30+
A Cowork session already stores its conversation as a standard Claude Code transcript inside
31+
a sandboxed home. `cowork2code` does not translate any format — it re-homes that transcript
32+
into a real working directory, rewrites the recorded paths, brings the rest of the
33+
environment along, and hands you the `claude --resume` command.
4234

43-
---
35+
```
36+
Claude Cowork session (sandboxed) Claude Code (your home)
37+
--------------------------------- -----------------------
38+
local_<id>/
39+
.claude/projects/<dir>/<id>.jsonl --re-home--> ~/.claude/projects/<escaped-cwd>/<id>.jsonl
40+
outputs/ --copy-----> <target>/
41+
memory/ --copy-----> <target>/memory/
42+
local_<id>.json (config) --MCP------> <target>/.mcp.json
43+
--notes----> <target>/COWORK_SESSION.md
44+
```
4445

4546
## Installation
4647

4748
```console
48-
$ git clone https://github.com/looptech-ai/claude-cowork-resume.git
49-
$ cd claude-cowork-resume
49+
$ git clone https://github.com/looptech-ai/cowork2code.git
50+
$ cd cowork2code
5051
$ ./install.sh
5152
```
5253

53-
`install.sh` puts the `cowork-resume` command on your `PATH` (`~/.local/bin`) and registers
54-
a local MCP server with Claude Code so an agent can perform the migration for you.
54+
`install.sh` puts the `cowork2code` command on your `PATH` (`~/.local/bin`) and registers a
55+
local MCP server with Claude Code so an agent can perform the migration for you.
5556

5657
Requirements: `python3` >= 3.10 (the CLI core is standard-library only) and, for the MCP
5758
server, [`uv`](https://docs.astral.sh/uv/) — which resolves the single `mcp` dependency on
5859
demand, with no global install.
5960

6061
Prefer a packaged install? `uv tool install .` or `pipx install .` both expose the same
61-
`cowork-resume` command via the project's entry point.
62-
63-
---
62+
`cowork2code` command via the project's entry point.
6463

6564
## Usage
6665

6766
### List your Cowork sessions
6867

6968
```console
70-
$ cowork-resume list
69+
$ cowork2code list
7170
2026-06-12 09:41 claude-haiku-4-5-20251001 Research: emerging LLM productivity trends id=local_e51809de-...
7271
2026-06-11 16:02 claude-sonnet-4-6 Draft quarterly board update id=local_7a2c91b4-...
7372
2026-06-10 08:55 claude-opus-4-8[1m] Refactor ingestion pipeline id=local_3c4d77af-...
@@ -78,11 +77,12 @@ Add `--all` to include archived sessions, or `--json` for machine-readable outpu
7877
### Re-home a session and get the resume command
7978

8079
```console
81-
$ cowork-resume rehome --title "emerging" --into ~/dev/llm-trends-resume
80+
$ cowork2code rehome --title "emerging" --into ~/dev/llm-trends-resume
8281
Session: Research: emerging LLM productivity trends (claude-haiku-4-5-20251001 -> opus-4-8[1m])
8382
Target cwd: /Users/you/dev/llm-trends-resume
8483
Transcript: /Users/you/.claude/projects/-Users-you-dev-llm-trends-resume/a1803cbc-....jsonl
8584
Copied 3 file(s); notes: /Users/you/dev/llm-trends-resume/COWORK_SESSION.md
85+
MCP servers -> /Users/you/dev/llm-trends-resume/.mcp.json
8686

8787
cd /Users/you/dev/llm-trends-resume && claude --resume a1803cbc-... --model 'opus-4-8[1m]'
8888
```
@@ -98,28 +98,26 @@ $ cd ~/dev/llm-trends-resume && claude --resume a1803cbc-... --model 'opus-4-8[1
9898

9999
```console
100100
# Resume in the directory the conversation originated from (no --into):
101-
$ cowork-resume rehome --id a1803cbc-... --original
101+
$ cowork2code rehome --id a1803cbc-... --original
102102

103103
# Preview without writing anything:
104-
$ cowork-resume rehome --title "emerging" --into ~/tmp/x --dry-run
104+
$ cowork2code rehome --title "emerging" --into ~/tmp/x --dry-run
105105

106106
# Re-home and launch Claude Code immediately on a chosen model:
107-
$ cowork-resume rehome --title "emerging" --into ~/tmp/x --model opus-4-8 --launch
107+
$ cowork2code rehome --title "emerging" --into ~/tmp/x --model opus-4-8 --launch
108108

109109
# Point at a specific session folder instead of searching:
110-
$ cowork-resume rehome --from "~/Library/Application Support/Claude/local-agent-mode-sessions/<acct>/<wks>/local_<id>" --into ~/tmp/x
110+
$ cowork2code rehome --from "~/Library/Application Support/Claude/local-agent-mode-sessions/<acct>/<wks>/local_<id>" --into ~/tmp/x
111111
```
112112

113113
Selectors (`--title`, `--id`, `--from`) are mutually exclusive, and so are the targets
114114
(`--into`, `--original`); supply exactly one of each. The default launch model is
115115
`opus-4-8[1m]`. If a title is ambiguous, the tool lists the matches and exits so you can
116116
refine the selector or use `--id`.
117117

118-
---
119-
120118
## MCP usage
121119

122-
After `install.sh`, Claude Code exposes a `cowork-resume` MCP server with two tools:
120+
After `install.sh`, Claude Code exposes a `cowork2code` MCP server with two tools:
123121

124122
| Tool | Purpose |
125123
| --- | --- |
@@ -135,38 +133,33 @@ the agent calls these tools and reports back the resume command.
135133
> directory, so registering this server inside Cowork may not work even though registering
136134
> it in Claude Code does.
137135
138-
---
139-
140-
## What gets created
141-
142-
In the target directory:
136+
## What gets created in the target directory
143137

144-
- `COWORK_SESSION.md` — informational notes describing the original session (model, system
145-
prompt, MCP servers, slash commands, initial message). These are **not** applied to the
146-
resumed session.
138+
- The session's `outputs/` and `memory/` files.
139+
- `.mcp.json` — the session's remote MCP servers, ready for Claude Code to reconnect (never
140+
overwrites an existing `.mcp.json`).
141+
- `COWORK_SESSION.md` — notes describing the original session (model, system prompt, MCP
142+
servers, slash commands, initial message). Informational; not auto-applied.
147143
- `cowork-session-config.json` — the raw original session configuration.
148-
- Any `outputs/` and `memory/` files from the original session.
149144

150-
In `~/.claude/projects/<escaped-target-cwd>/` the re-homed transcript, named for its
145+
The re-homed transcript lands in `~/.claude/projects/<escaped-target-cwd>/`, named for its
151146
session id, so `claude --resume <id>` loads it from the target directory.
152147

153-
---
154-
155148
## Design notes and limits
156149

157150
- **Source is read-only.** Nothing under the Cowork sessions directory is modified, moved,
158-
or deleted. The tool only ever reads it.
151+
or deleted.
159152
- **Project-directory naming** follows Claude Code's exact scheme (each non-alphanumeric
160153
byte becomes `-`), verified against real `~/.claude/projects` directories.
161154
- **No clobbering.** If a transcript already exists at the target, the tool stops; use
162-
`--new-id` to mint a fresh session id or `--force` to overwrite.
155+
`--new-id` to mint a fresh session id or `--force` to overwrite. An existing `.mcp.json`
156+
is never overwritten.
157+
- **Symlinks** in the session's working files are skipped, not followed.
163158
- **Sensitive content.** The notes and config sidecars copy the original system prompt and
164159
MCP server list. Review them before sharing the target directory.
165160
- **Platform.** macOS in this release. The sessions root is configurable with
166161
`--sessions-root`, so other layouts can be added without code changes elsewhere.
167162

168-
---
169-
170163
## Development
171164

172165
```console
@@ -179,20 +172,18 @@ and never touches your real Claude data.
179172
Project layout:
180173

181174
```
182-
cowork_resume/
175+
cowork2code/
183176
paths.py project-directory escaping and path resolution
184177
transcript.py locate, rewrite, and write transcript JSONL
185178
discover.py scan the sessions root and resolve selectors
186179
notes.py render the configuration-notes sidecar
187180
rehome.py plan and execute a re-home
188-
cli.py the cowork-resume command
181+
cli.py the cowork2code command
189182
mcp_server.py the local MCP server
190-
cowork_mcp.py zero-install uv launcher for the MCP server
191-
install.sh install the CLI and register the MCP server
183+
cowork2code_mcp.py zero-install uv launcher for the MCP server
184+
install.sh install the CLI and register the MCP server
192185
```
193186

194-
---
195-
196187
## License
197188

198189
[MIT](LICENSE) (c) LoopTech.

bin/cowork-resume renamed to bin/cowork2code

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
# install.sh writes an equivalent copy to ~/.local/bin with the repo path baked in.
44
REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
55
export PYTHONPATH="${REPO}:${PYTHONPATH:-}"
6-
exec python3 -m cowork_resume.cli "$@"
6+
exec python3 -m cowork2code.cli "$@"

cowork2code/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""cowork2code: re-home a Claude Cowork session into a resumable Claude Code session."""
2+
3+
__version__ = "0.1.0"
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Command-line frontend for claude-cowork-resume."""
1+
"""Command-line frontend for cowork2code."""
22

33
import argparse
44
import datetime
@@ -19,7 +19,7 @@ def _fmt_ts(ms: int | None) -> str:
1919

2020
def _build_parser() -> argparse.ArgumentParser:
2121
parser = argparse.ArgumentParser(
22-
prog="cowork-resume",
22+
prog="cowork2code",
2323
description="Resume a Claude Cowork session in Claude Code so you can change the model.",
2424
)
2525
sub = parser.add_subparsers(dest="cmd", required=True)
@@ -103,6 +103,8 @@ def _cmd_rehome(args) -> int:
103103
print(f"{tag}Target cwd: {plan.target_cwd}")
104104
print(f"{tag}Transcript: {plan.transcript_dest}")
105105
print(f"{tag}Copied {len(plan.copied)} file(s); notes: {plan.notes_path}")
106+
if plan.mcp_config_path:
107+
print(f"{tag}MCP servers -> {plan.mcp_config_path}")
106108
for warning in plan.warnings:
107109
print(f"{tag}warn: {warning}")
108110
print()
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
"""Local MCP server exposing cowork-resume operations as tools.
1+
"""Local MCP server exposing cowork2code operations as tools.
22
33
The module-level handlers import only the standard library and the local core
44
package, so they are importable for tests even when ``mcp`` is not installed.
@@ -66,6 +66,7 @@ def rehome_session(
6666
"resume_command": plan.resume_command,
6767
"copied": plan.copied,
6868
"notes_path": str(plan.notes_path) if plan.notes_path else None,
69+
"mcp_config_path": str(plan.mcp_config_path) if plan.mcp_config_path else None,
6970
"warnings": plan.warnings,
7071
"dry_run": dry_run,
7172
}
@@ -75,7 +76,7 @@ def main() -> None:
7576
"""Run the stdio MCP server (requires the optional ``mcp`` dependency)."""
7677
from mcp.server.fastmcp import FastMCP
7778

78-
server = FastMCP("cowork-resume")
79+
server = FastMCP("cowork2code")
7980

8081
@server.tool()
8182
def cowork_session_list(
Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class RehomePlan:
2626
copied: list[str] = field(default_factory=list)
2727
notes_path: Path | None = None
2828
config_copy_path: Path | None = None
29+
mcp_config_path: Path | None = None
2930
resume_command: str = ""
3031
warnings: list[str] = field(default_factory=list)
3132

@@ -34,6 +35,20 @@ def _new_session_id() -> str:
3435
return str(uuid.uuid4())
3536

3637

38+
def _mcp_servers_from_config(cfg: dict) -> dict:
39+
"""Build a Claude Code ``.mcp.json`` server map from the Cowork session config.
40+
41+
Only the session's remote (URL-based) MCP servers can be carried over directly;
42+
they are emitted so Claude Code reconnects them from the target directory.
43+
"""
44+
servers: dict[str, dict] = {}
45+
for server in cfg.get("remoteMcpServersConfig") or []:
46+
name, url = server.get("name"), server.get("url")
47+
if name and url:
48+
servers[name] = {"type": "http", "url": url}
49+
return servers
50+
51+
3752
def build_plan(
3853
ref: SessionRef,
3954
*,
@@ -131,10 +146,24 @@ def execute_plan(
131146
if not dry_run:
132147
tx.write_transcript(records, plan.transcript_dest)
133148

149+
cfg = json.loads(ref.config_path.read_text(encoding="utf-8"))
134150
plan.notes_path = target / "COWORK_SESSION.md"
135151
plan.config_copy_path = target / "cowork-session-config.json"
152+
153+
# Carry the session's remote MCP servers over as a ready-to-use .mcp.json so
154+
# Claude Code reconnects them. Never clobber an existing project .mcp.json.
155+
mcp_servers = _mcp_servers_from_config(cfg)
156+
mcp_path = target / ".mcp.json"
157+
if mcp_servers and mcp_path.exists():
158+
plan.warnings.append("skip existing .mcp.json")
159+
elif mcp_servers:
160+
plan.mcp_config_path = mcp_path
161+
136162
if not dry_run:
137-
cfg = json.loads(ref.config_path.read_text(encoding="utf-8"))
138163
plan.notes_path.write_text(render_notes(cfg), encoding="utf-8")
139164
plan.config_copy_path.write_text(json.dumps(cfg, indent=2), encoding="utf-8")
165+
if plan.mcp_config_path:
166+
plan.mcp_config_path.write_text(
167+
json.dumps({"mcpServers": mcp_servers}, indent=2), encoding="utf-8"
168+
)
140169
return plan

0 commit comments

Comments
 (0)