Skip to main content
Teleport hands a coding session in progress off to Aether. You are working locally in Claude Code or Codex, you need to close the laptop, and instead of writing a handoff prompt you run one skill: your working tree is pushed to a fresh branch, the conversation transcript is imported, and the task that opens on Aether resumes that exact conversation with full memory of what you were doing. Two things travel:
  • The repo’s working state. Every tracked and untracked change (respecting .gitignore) is committed to a fresh aether/teleport-<hex> branch and pushed to origin, along with any local commits origin does not already have. The commit is built through a temporary index, so your local index and working tree are left untouched. Linked worktrees (git worktree add) capture the same way as a primary checkout.
  • The session transcript. The local CLI’s session file is uploaded and seeded as the task’s resume checkpoint, so the agent continues the conversation rather than starting a new one.

Prerequisites

Installing the Skill

The skill is two files — a SKILL.md and the teleport.py script it runs. Install them into your agent’s skills directory.
This is a local skill for the CLI on your machine, not an Aether skill uploaded to your account. It has to live where the local agent can load it, because it reads that agent’s own session files.

Authenticating

Teleport calls the Aether API as you, using a platform API key. Create one under Settings → API Keys, or from the CLI:
Export it wherever your shell will have it when the agent runs:
A platform API key is unscoped and does not expire — it can do anything your account can. Treat it like a password: keep it out of the repo, store it in your shell profile or a secret manager, and revoke it with aether token revoke <id> if it leaks.

Teleporting a Session

From the session you want to move, invoke the skill — /aether, or just tell the agent to teleport this session to Aether.
Teleporting publishes more than your working tree. The capture is git add -A, so anything .gitignore does not exclude is committed — a tracked or merely un-ignored .env, private key, or .npmrc included. The push then puts that commit and every local commit origin does not already have on the remote. A secret in an unpushed commit therefore teleports from a perfectly clean working tree, and so does one a later commit deleted, because the blob stays reachable through history.As a backstop the script scans exactly that set — every file added or modified by the commits the push would newly publish — and aborts, listing them, if any basename looks like a credential (.env, .env.*, *.pem, *.key, id_rsa*, id_ed25519*, .netrc, .npmrc, *credentials*, service-account*.json, *.p12, *.pfx). Files origin already has are not reported: teleporting does not make them any more public. To decide that accurately the script runs git fetch --prune origin first, because a branch deleted or rewound on the server leaves a stale local tracking ref that would otherwise make an unpublished commit look published. If origin cannot be reached the script fails rather than scanning against stale refs.The scan matches on filename only, so a secret in a file it does not recognize still teleports. Once you have confirmed the flagged files are safe to publish, --allow-secrets proceeds.
If the scan flags a file, the fix depends on where it lives:
  • Only in your working tree. Add it to .gitignore, and git rm --cached <file> if it is already tracked.
  • Already in an unpushed commit. git rm --cached is not enough here — it records a later deletion while the earlier blob stays reachable, so the push would still publish it and the guard still refuses. Rewrite every commit that contains the file (git rebase -i, or a soft reset and recommit), then re-run.
  • Ever pushed anywhere. Assume the credential is burned and rotate it. Scrubbing it from history does not un-leak what was already published.
1

The agent prints a nonce

It emits a teleport-nonce: <token> line into the conversation. That line is how the script identifies which transcript file on disk belongs to this session, so it has to be written before the script runs.
2

The script captures and pushes your work

It commits the working state and pushes it to a new aether/teleport-<hex> branch on origin. The base branch is your current branch if it has an upstream, otherwise origin/HEAD.
3

The transcript is imported

The session file is compressed and posted to the teleport endpoint, which validates it and creates the task with the conversation pre-seeded.
4

You get a task URL

The script prints the Aether task URL. Open it and the agent picks up where the local session left off.
Once the script succeeds, stop working locally. Edits made after the capture never reach the teleported task.

Options

The agent passes --agent and --nonce itself. The rest you can ask it to add:

What a dry run actually checks

A dry run is a client-side rehearsal, not a preflight against the Aether API. It confirms that the nonce locates a session transcript and that the file is self-consistent, that the transcript is under the 32 MiB checkpoint cap, that your origin matches an Aether project, that an agent and model resolve, and that no file the push would newly publish trips the secret scan. It reaches the network twice — listing your projects, and running git fetch --prune origin for the secret scan. Nothing you own changes: your working tree, index, and branches are untouched, no branch is pushed, and no task is created. Two pieces of local Git bookkeeping do update, as they would on any fetch: origin/* remote-tracking refs are refreshed, and the capture commit’s objects are written to the local object store (unreferenced, so git gc reclaims them). It does not validate the transcript’s contents — malformed JSON on a later line passes the client — and it never contacts the teleport endpoint, so server-side transcript validation, model availability, and credential checks are untested. A clean dry run can still be followed by a real run that pushes a branch and is then rejected.

What Does Not Teleport

The conversation and the code come across. The rest of your local machine does not:
  • Gitignored files, and history origin already has. A .env that is gitignored and was never committed stays behind — and the workspace uses the project’s configured environment variables and secret files instead, so add anything the session depended on to the project first. A .env that is not ignored, or that sits in an unpushed commit, teleports; see the warning above.
  • Running processes. Dev servers, watchers, and background jobs you started locally are not carried over.
  • Locally configured MCP servers. The workspace has its own tool surface.
Your local CLI may be a newer version than the one pinned in the workspace. Transcripts are read tolerantly across versions, but a structurally invalid transcript is rejected at import, and a resume that would silently start a fresh session fails the task instead of continuing without memory. Teleport never quietly drops your history.

Troubleshooting

The script prints every failure to stderr prefixed with teleport: and exits non-zero. It never falls back to creating an ordinary task.

Recovering from a failed run

The branch is pushed before the task is created, so a failure is not automatically a clean slate:
  • A failure after the push leaves an aether/teleport-<hex> branch on origin.
  • If the response is lost in flight, the script reports failure even though the server created the task. Retrying then produces a second branch and a second task.
Before you retry, check your task list for a task on that branch. If one is there, the teleport worked — open it. If not, retry; each run generates a fresh branch name, so retries never collide. Either way, delete branches you have abandoned:

API

Teleport is a thin client over one endpoint — Teleport a session into a task. It takes the project, the base and head branches, the agent and model, and the gzipped session transcript, and returns the created task.