---
name: cc-internals
description: How Claude Code works internally. Use for ANY question about CC behavior, architecture, or "how does CC do X". Covers session storage, /rename scope, project organization, file locations, and internal data structures.
---

# Claude Code Internals

Facts about how Claude Code organizes and stores data.

## Storage Layout

```
~/.claude/
├── settings.json              # User config (see cc-configuring-permissions)
├── skills/                    # User-level skills
├── session-env/               # Session environment cache
└── projects/                  # All session data, per-project
    └── <encoded-path>/        # One dir per project cwd
        ├── sessions-index.json    # Session metadata index
        ├── <uuid>.jsonl           # Session transcripts
        └── agent-<hash>.jsonl     # Subagent transcripts
```

## Project Path Encoding

Project directories are cwd paths with slashes replaced by dashes:

| cwd | Encoded directory name |
|-----|------------------------|
| `/Users/me/projects/foo` | `-Users-me-projects-foo` |
| `/Users/me/foo/.worktrees/bar` | `-Users-me-foo--worktrees-bar` |

**Decode:** `echo "-Users-me-foo" | sed 's/^-/\//; s/-/\//g'`

## Sessions Index (`sessions-index.json`)

Each project has one index tracking all sessions in that project.

```json
{
  "version": 1,
  "entries": [
    {
      "sessionId": "027b253b-7743-4cd7-8c7b-96740e0a3cf8",
      "fullPath": "/Users/.../<uuid>.jsonl",
      "firstPrompt": "user's first message...",
      "summary": "Auto-generated summary",
      "customTitle": "user-set via /rename",
      "messageCount": 28,
      "created": "2026-01-17T00:22:31.447Z",
      "modified": "2026-01-17T00:44:18.038Z",
      "gitBranch": "main",
      "projectPath": "/Users/me/projects/foo",
      "isSidechain": false,
      "fileMtime": 1769006749607
    }
  ]
}
```

### Session Entry Fields

| Field | Description |
|-------|-------------|
| `sessionId` | UUID, matches `.jsonl` filename |
| `summary` | Auto-generated by CC |
| `customTitle` | User-set via `/rename` (null if unset) |
| `firstPrompt` | First user message (truncated) |
| `gitBranch` | Branch at session creation |
| `projectPath` | Absolute cwd path |
| `messageCount` | Total messages in session |
| `isSidechain` | Whether session is a sidechain |
| `created` / `modified` | ISO timestamps |
| `fileMtime` | File modification time (epoch ms) |

## /rename Command

Sets `customTitle` field in `sessions-index.json`.

| Aspect | Behavior |
|--------|----------|
| **Scope** | Per-project (cwd-specific) |
| **Collision** | Same name can exist in different projects |
| **Storage** | Only in index, not in `.jsonl` transcript |
| **Persistence** | Survives session end, stored in index |

### Finding Named Sessions

```bash
# All named sessions across all projects
for dir in ~/.claude/projects/*/; do
  jq -r '.entries[] | select(.customTitle != null) | "\(.customTitle) | \(.projectPath)"' \
    "$dir/sessions-index.json" 2>/dev/null
done

# Named sessions in current project
jq -r '.entries[] | select(.customTitle != null) | "\(.customTitle): \(.sessionId)"' \
  ~/.claude/projects/-Users-me-projects-foo/sessions-index.json
```

## Session Transcripts (`.jsonl`)

Each line is a JSON object. See `cc-thread-search` for search patterns.

```json
{"type": "user", "timestamp": "<ISO>", "message": {...}, "sessionId": "<uuid>"}
{"type": "assistant", "timestamp": "<ISO>", "message": {...}, "sessionId": "<uuid>"}
{"type": "summary", ...}
```

## Related Skills

| Topic | Skill |
|-------|-------|
| Searching transcripts | `cc-thread-search` |
| Permission config | `cc-configuring-permissions` |
| Hook config | `cc-writing-hooks` |
| MCP config | `cc-configuring-mcp` |
