# arcgent/linkedin-icp-sorter architecture

> Architecture diagram of the GitHub repository arcgent/linkedin-icp-sorter, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-30). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/arcgent/linkedin-icp-sorter
- Repository: https://github.com/arcgent/linkedin-icp-sorter

## Overview

LinkedIn ICP Sorter turns a LinkedIn connections CSV into privacy-separated records, enriches them optionally with post engagement, and asks Jev to classify each connection into configurable buckets. Users can run one decision or a batch, inspect confidence and costs, label answers for comparison, and export results through the local app or CLI. The graph follows documented flows and sampled code; the browser-to-server wiring and some app routes are not shown in the excerpts, so those links are marked unverified.

Main entry points: [`server.mjs`](https://github.com/arcgent/linkedin-icp-sorter/blob/main/server.mjs) (Local HTTP app), [`lib/store.mjs`](https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/store.mjs) (Local data files), [`lib/runner.mjs`](https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/runner.mjs) (Decision runner), [`cli.mjs`](https://github.com/arcgent/linkedin-icp-sorter/blob/main/cli.mjs) (Terminal commands)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_prep["Data preparation"]
  node_build["Build records<br/>[build-records.mjs]"]
  node_engagement_fetch["Fetch engagement"]
end

subgraph group_decision["Classification"]
  node_setup["ICP setup<br/>[setup.mjs]"]
  node_runner["Decision runner<br/>[runner.mjs]"]
  node_jev["Jev client<br/>[jev.mjs]"]
end

subgraph group_app["Local app and review"]
  node_cli["Terminal commands<br/>[cli.mjs]"]
  node_server["Local HTTP app<br/>[server.mjs]"]
  node_web_ui["Browser interface<br/>[index.html]"]
end

subgraph group_persistence["Local persistence"]
  node_records_store[("Local data files<br/>[store.mjs]")]
end

node_user(("User"))
node_connections_csv["Connections CSV"]
node_unipile["Unipile API"]
node_openrouter["OpenRouter"]
node_typesafe["TypeSafe API"]

node_user -->|"provides export"| node_connections_csv
node_connections_csv -->|"supplies input"| node_build
node_build -->|"writes records"| node_records_store
node_user -->|"requests enrichment"| node_engagement_fetch
node_engagement_fetch -->|"fetches activity"| node_unipile
node_engagement_fetch -->|"saves engagement"| node_records_store
node_records_store -->|"provides engagement"| node_build
node_user -->|"defines criteria"| node_setup
node_cli -->|"loads setup"| node_setup
node_cli -->|"loads records"| node_records_store
node_cli -->|"runs decisions"| node_runner
node_server -->|"loads and saves"| node_setup
node_server -->|"reads and writes"| node_records_store
node_server -->|"dispatches decisions"| node_runner
node_server -->|"reads configuration"| node_jev
node_runner -->|"builds requests"| node_setup
node_runner -->|"requests decisions"| node_jev
node_runner -->|"saves runs"| node_records_store
node_jev -->|"calls by default"| node_openrouter
node_jev -.->|"calls when configured"| node_typesafe
node_user -->|"runs terminal commands"| node_cli
node_user -->|"uses app tabs"| node_web_ui
node_web_ui -.->|"sends app requests"| node_server
node_server -->|"returns results"| node_user

click node_build "https://github.com/arcgent/linkedin-icp-sorter/blob/main/scripts/build-records.mjs"
click node_engagement_fetch "https://github.com/arcgent/linkedin-icp-sorter/blob/main/scripts/fetch-engagement.mjs"
click node_records_store "https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/store.mjs"
click node_setup "https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/setup.mjs"
click node_runner "https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/runner.mjs"
click node_jev "https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/jev.mjs"
click node_cli "https://github.com/arcgent/linkedin-icp-sorter/blob/main/cli.mjs"
click node_server "https://github.com/arcgent/linkedin-icp-sorter/blob/main/server.mjs"
click node_web_ui "https://github.com/arcgent/linkedin-icp-sorter/blob/main/public/index.html"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_build,node_engagement_fetch,node_user toneBlue
class node_setup,node_runner,node_jev toneAmber
class node_cli,node_server,node_web_ui,node_unipile,node_openrouter,node_typesafe toneMint
class node_records_store toneRose
class node_connections_csv toneIndigo
```

## Components

### Data preparation

- [Build records](https://github.com/arcgent/linkedin-icp-sorter/blob/main/scripts/build-records.mjs): `scripts/build-records.mjs`
- [Fetch engagement](https://github.com/arcgent/linkedin-icp-sorter/blob/main/scripts/fetch-engagement.mjs): `scripts/fetch-engagement.mjs`

### Classification

- [ICP setup](https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/setup.mjs): `lib/setup.mjs`
- [Decision runner](https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/runner.mjs): `lib/runner.mjs`
- [Jev client](https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/jev.mjs): `lib/jev.mjs`

### Local app and review

- [Terminal commands](https://github.com/arcgent/linkedin-icp-sorter/blob/main/cli.mjs): `cli.mjs`
- [Local HTTP app](https://github.com/arcgent/linkedin-icp-sorter/blob/main/server.mjs): `server.mjs`
- [Browser interface](https://github.com/arcgent/linkedin-icp-sorter/blob/main/public/index.html): `public/index.html`

### Local persistence

- [Local data files](https://github.com/arcgent/linkedin-icp-sorter/blob/main/lib/store.mjs): `lib/store.mjs`

### Other components

- **User**
- **Connections CSV**
- **Unipile API**
- **OpenRouter**
- **TypeSafe API**

## Connections

- User → Connections CSV: provides export (evidence: `README.md`)
- Connections CSV → Build records: supplies input (evidence: `scripts/build-records.mjs`)
- Build records → Local data files: writes records (evidence: `scripts/build-records.mjs`)
- User → Fetch engagement: requests enrichment (evidence: `README.md`)
- Fetch engagement → Unipile API: fetches activity (evidence: `scripts/fetch-engagement.mjs`)
- Fetch engagement → Local data files: saves engagement (evidence: `scripts/fetch-engagement.mjs`)
- Local data files → Build records: provides engagement (evidence: `scripts/build-records.mjs`)
- User → ICP setup: defines criteria (evidence: `README.md`)
- Terminal commands → ICP setup: loads setup (evidence: `cli.mjs`)
- Terminal commands → Local data files: loads records (evidence: `cli.mjs`)
- Terminal commands → Decision runner: runs decisions (evidence: `cli.mjs`)
- Local HTTP app → ICP setup: loads and saves (evidence: `server.mjs`)
- Local HTTP app → Local data files: reads and writes (evidence: `server.mjs`)
- Local HTTP app → Decision runner: dispatches decisions (evidence: `server.mjs`)
- Local HTTP app → Jev client: reads configuration (evidence: `server.mjs`)
- Decision runner → ICP setup: builds requests (evidence: `lib/runner.mjs`)
- Decision runner → Jev client: requests decisions (evidence: `lib/runner.mjs`)
- Decision runner → Local data files: saves runs (evidence: `lib/runner.mjs`)
- Jev client → OpenRouter: calls by default (evidence: `lib/jev.mjs`)
- Jev client → TypeSafe API: calls when configured (evidence: `lib/jev.mjs`)
- User → Terminal commands: runs terminal commands (evidence: `README.md`)
- User → Browser interface: uses app tabs (evidence: `README.md`)
- Browser interface → Local HTTP app: sends app requests
- Local HTTP app → User: returns results (evidence: `README.md`)
