Skip to main content

atif-make

Make ATIF v1.7 trajectories from agent logs — Claude Code, Codex, Copilot CLI, and HAR captures.

Zero runtime dependencies. Python 3.12+.

Install

uv tool install atif-make          # puts `atif-make` on your PATH

Or as a library in a project:

uv add atif-make

For a browser view of what you convert, see the companion atif-view, which depends on this package.

uv tool install builds an isolated environment, so nothing lands in your project or system Python. To follow local edits instead, use uv tool install --editable .; to remove it, uv tool uninstall atif-make.

Running from a checkout without installing works too: uv run atif-make ....

atif-make ~/.claude/projects/my-project/session.jsonl   # convert one log
atif-make index --stats                                 # what do I have, across agents?

Commands

atif-make <file>                 convert (shorthand for `atif-make convert`)
atif-make convert <file>         convert one log
atif-make convert <dir|archive>  convert every log inside
atif-make index [roots...]       scan for sessions across agents
atif-make formats                list supported input formats
Flag Command Meaning
-o, --output convert output path (default <input>.trajectory.json)
-f, --format convert force the input format instead of detecting it
--json convert write one self-contained document to stdout
--bundle OUT.zip convert zip the trajectory with its images and subagents
--split-subagents convert write subagents as sibling files, not embedded
--indent N convert JSON indent (default 2)
-q, --quiet convert suppress progress output
--stats index print a per-agent summary
--add index merge into the existing index instead of replacing it

Supported inputs

Most agents write two unrelated log shapes — what the CLI streams, and what it persists on disk — and they are not interchangeable. atif-make reads both.

Format Source
claude-code-transcript ~/.claude/projects/<project>/<session>.jsonl
claude-code-stream claude -p --output-format stream-json
codex-rollout ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl
codex-exec codex exec --json
copilot-cli Copilot CLI session logs
har Anthropic Messages, OpenAI Chat Completions, OpenAI Responses
atif An ATIF trajectory that is already converted

Directories, .zip and .tar.gz (also .tgz, .tar.bz2, .tar.xz) are read as containers: every log inside is found and converted. That closes the loop on --bundle — the zip atif-make hands you to send someone opens again in atif-make, images and all.

Archives are extracted to a temporary directory, once per run. Members naming absolute paths or climbing out with .. are refused rather than quietly sanitised, and an archive that expands past 8 GB or 20,000 entries is rejected outright.

Format is detected from content, never from the extension. atif exists so a trajectory someone sends you opens like anything else — it is loaded, not reparsed, and unknown fields from a newer ATIF minor version are dropped rather than rejected.

What it gets right

These are the things that are easy to get wrong, and that silently corrupt a trajectory when you do:

Split messages. Claude Code writes one API response as several JSONL lines that share a message.id — thinking, text, and each parallel tool_use arrive separately, with the same usage object repeated on every line. Treating those as separate turns inflates step counts and multiplies token totals. atif-make coalesces them and counts usage once.

Out-of-order tool results. Parallel calls come back interleaved, and a slow call can return several turns after it was issued. Pairing results to calls by position drops some and misattributes others. atif-make pairs by tool_use_id.

Byte-capped detection. A JSONL preamble (hook events, rate-limit notices) can push the identifying line kilobytes into a file. atif-make scans whole lines.

Subagent structure. Claude Code links a delegated agent through a .meta.json sidecar (toolUseId) and an agentId field on the result line — not through anything in the result text. atif-make links by call id, so refs actually resolve instead of leaving orphaned subagents.

Images. Codex embeds screenshots as base64 data URLs and Claude Code as base64 content blocks — 24 of the sessions on one test machine carried them, and a single Codex session held 65 images totalling 14 MB. Dropping them loses the thing the agent was actually looking at, and inlining them makes an unreadable document. atif-make writes them to images/ and references them by relative path, which is what the spec asks for.

Malformed timestamps. ATIF requires ISO 8601. A truncated or hand-edited log can carry something else, and passing it through would make the whole trajectory fail validation, so an unparseable timestamp is dropped rather than emitted.

HAR tool results. In a HAR capture a tool's output is not in the response that called it — it appears in the next request's message history. atif-make harvests results across entries and pairs them back by id, while emitting the shared conversation prefix only once.

Output — a trajectory is a directory, not a file

ATIF references images and split subagents by path relative to the trajectory file, so anything with attachments is inherently multi-file:

session.trajectory.json                 the document
session.trajectory.<agent-id>.json      subagents, with --split-subagents
images/<sha>.png                        images, referenced as "images/<sha>.png"

Images are de-duplicated by content hash, so the same screenshot pasted five times is stored once.

Three ways out, depending on where it's going:

atif-make session.jsonl -o out/t.json      # directory form: t.json + images/
atif-make session.jsonl --json             # one self-contained doc; images inlined as data: URIs
atif-make session.jsonl --bundle send.zip  # zip of the whole directory — for sending someone

--json is the exception that proves the rule: stdout has no directory to put siblings in, so images become data URIs to keep the document standalone.

Output carries timestamp, reasoning_content, per-step metrics, multimodal ContentPart message content, and subagents either embedded (subagent_trajectories) or split into sibling files with resolvable trajectory_path refs.

Viewing

Conversion is all this package does. To browse trajectories in a browser, install the companion atif-view, which depends on this package and reads the index below.

Where sessions come from

atif-make index scans your own machine — nothing is uploaded, and no network call is ever made. By default it looks in:

~/.claude/projects/     Claude Code sessions
~/.codex/sessions/      Codex sessions

The result is cached at ~/.atif-make/index.json. Indexing reads only each file's leading lines, so a 143 MB rollout costs the same as a 4 KB one; full conversion happens lazily, when you actually open a session.

Scan somewhere else, or add a single file someone sent you:

atif-make index ~/work/logs                    # replace the index with this scan
atif-make index --add received.trajectory.json # merge one file into the existing index
atif-make index --add received-bundle.zip      # or a whole bundle

--add merges; without it, index replaces. Both raw logs and already-converted ATIF trajectories can be added.

Tests

uv run pytest                      # unit tests, all synthetic fixtures
uv sync --extra spec               # pulls harbor (large)
uv run --extra spec pytest         # + validate against the reference ATIF models

The spec suite validates every fixture against harbor's own pydantic models — ground truth for whether the output is really ATIF, rather than what atif-make believes ATIF to be.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

atif_make-0.1.0.tar.gz (174.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

atif_make-0.1.0-py3-none-any.whl (36.2 kB view details)

Uploaded Python 3

File details

Details for the file atif_make-0.1.0.tar.gz.

File metadata

  • Download URL: atif_make-0.1.0.tar.gz
  • Upload date:
  • Size: 174.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for atif_make-0.1.0.tar.gz
Algorithm Hash digest
SHA256 354cda534730058c838cac220379d6e8a703e983a5c8f27a3d2610c0065d0d23
MD5 d72bd8091d2dc5ac03e048d5ca583ef2
BLAKE2b-256 ddd995649c6a3e916632807737606bf91f3b29c3cdccf26e037fb7080c5e955f

See more details on using hashes here.

File details

Details for the file atif_make-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: atif_make-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 36.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for atif_make-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8feb026abb53c5f9e68d9e88ea3ef6e73e731bc2a89d19e68b2f4f18c579e134
MD5 30f224206228a2ebfa846ba4bde58038
BLAKE2b-256 ac0a13f8e0211aa0e1a88397a51b55219349837f5728b034d232b5fa15a6032c

See more details on using hashes here.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page