Distributed, code-native issue tracker - audit workflows via time-travel
368
stars
715
commits
TypeScript
primary language
Sep 9, 2026
updated
Issue tracking as code. Open source, distributed, local-first, and code-native.
Epiq provides issue tracking as a portable, integrated part of the development environment, with access to all the powerful tooling developers are used to.
Manage your projects in a visual kanban board — in your terminal or in your browser — while keeping all state local, Git-backed, and versioned.
With great attention to user ergonomics and developer experience, epiq strives to make project management painless and friction free.

Agents now run whole sprints unattended. Because state is a full event log, you can replay the board to find out what moved when, who moved it, and what changed along the way.
Epiq originated from the command line and offers a first-class terminal experience, but also features a browser interface powered by the same Git-backed event engine.
Epiq is a self hosted, vim-inspired issue tracker that brings developer experience to project management. It renders either as ASCII, or as a web GUI, and persists state as an immutable distributed event log, versioned and synchronized through Git.
Most issue trackers live outside your workflow. Instead of a centralized, managed service, Epiq keeps project state alongside your repository, where it travels with your code.
These design choices result in a system that is:
? to open a scrollable overview of all available commands and descriptionsBinary:
curl -fsSL https://raw.githubusercontent.com/ljtn/epiq/main/install.sh | sh
Installs to ~/.local/bin by default. Override with EPIQ_INSTALL_DIR (or XDG_BIN_HOME); pin a version with EPIQ_VERSION=v1.0.0.
npm install --global epiq
epiq --version
# If needed:
git init
# For collaboration, use a repo with a remote (e.g. clone from GitHub)
epiq
If it is your first run, this opens the interactive setup wizard that sets you up in about 30 seconds.
That’s it!
Once your project is set up, you can also launch the browser user interface with:
epiq gui
Setup wizard creates: User config persisted in
~/.epiq-global/config.json.
Initialization creates:
- Project definition in
./.epiq/project.json- Authoritative Git state at
~/.epiq-global/worktrees/<id>- Updates your
.gitignoreto ignore local-only.epiq/log/Epiq manages a dedicated Git state branch and worktree automatically as the source of truth for synchronization.- A local debug log at
.epiq/log/epiq.log— check it first if sync, boot, or a Git operation is misbehaving.
:help.? anytime to open the command palette with all available commands and descriptions.h j k l.enter, and navigate out of a context with q or esc: you are put in command line mode and can now insert commands.:close only exists for issues.:new issue|swimlane|board <Name of new node>.:comment <your-input>. Comments can be edited or deleted with the regular ':edit ...' or ':delete' commands.m. This sets you in a move state, after which you can navigate as normal, navigate to the target location, then press m again to confirm new location.filter command followed by a target, and a qualifier. So in order to filter all issues with a prio tag you can write :filter tag prio and hit enter. You can build a combination of filters by running several filter commands in succession.Clear all filters with :filter clear
:peek <offset>, where offset is <n>h, d, w, mo or y — so :peek 3d is the board three days ago. An absolute YYYY-MM-DD date works too. Step with :peek prev|next, and return with :peek now.:peek shows a frozen snapshot, :replay 1mo plays history forward from that point as an animation. An optional second argument sets the playback duration, e.g. :replay 1mo 30s.:close. This moves the issue to a special board named Closed which you can find if you navigate up (press q) a few times.Closed board, selecting an issue and typing command :reopen. This will restore the issue to its last previous location.Epiq provides a MCP (Model Context Protocol) server for agents to interact with, making it easy to plug into modern agent frameworks. The server is exposed by the epiq-mcp binary that ships with the package.
The reliable way to register the server is with the claude mcp add command — it writes to the correct config file for you, so you don't have to hand-edit JSON:
# Available everywhere (recommended)
claude mcp add --scope user epiq -- npx -y -p epiq epiq-mcp
# Or only in the current project
claude mcp add epiq -- npx -y -p epiq epiq-mcp
Use --scope user to make Epiq available in every directory; omit it to register Epiq only for the current project. Verify the connection with claude mcp list (it should report epiq … ✔ Connected). MCP servers are loaded at startup, so restart Claude Code after adding the server before its tools become available.
Find skill at .claude/skills/epiq/SKILL.md that documents a recommended workflow for working the Epiq board.
Every process — your TUI, your GUI, each agent's MCP server — writes as the user in ~/.epiq-global/config.json, so by default the board cannot tell one agent from another. Give an agent its own identity with EPIQ_USER_NAME:
{
"mcpServers": {
"epiq": {
"command": "npx",
"args": ["-y", "-p", "epiq", "epiq-mcp"],
"env": {"EPIQ_USER_NAME": "claude"}
}
}
}
That agent then shows up in the contributor list, assigns itself rather than you, and authors its own events. The id is derived from the name, so one name is one contributor on every machine — reuse names instead of inventing one per session, or the registry fills with single-run identities. EPIQ_USER_ID pins the id explicitly (26 characters of Crockford base32) if you would rather choose it. Naming yourself changes nothing.
For clients that are configured by hand, add the following to the client's MCP config file — note this is not the same as Claude Code's ~/.claude.json; Claude Desktop uses claude_desktop_config.json:
{
"mcpServers": {
"epiq": {
"command": "npx",
"args": ["-y", "-p", "epiq", "epiq-mcp"]
}
}
}
Once registered, agents can interact with your local Epiq instance through the MCP.
npx -y -p epiq epiq-mcp resolves the package against the npm registry every time it starts, even if it's already cached locally. In agent sandboxes with restricted network access, this can make the MCP server appear to hang — npx retries DNS resolution instead of failing fast, and there's no MCP-level error to explain why.
If you're running Epiq's MCP server in such an environment, install it globally once and point your MCP config at the resolved executable directly, bypassing npx (and the registry lookup) entirely on every subsequent start:
npm install --global epiq
which epiq-mcp # use this absolute path in your MCP config
{
"mcpServers": {
"epiq": {
"command": "/absolute/path/to/epiq-mcp"
}
}
}
npx remains the simpler option for normal, network-connected setups.
Epiq uses Git in the background - no manual Git commands are required. Running :sync synchronizes changes between your local state (persisted at ~/.epiq-global/worktrees/<id>/) and the remote state. By utilizing Git worktrees, synchronization stays isolated from your regular development workflow. Project tracking metadata is stored in .epiq/project.json.
Epiq is designed to provide robustness in a distributed, Git-backed environment where multiple users may update state concurrently. Instead of mutating shared files, Epiq uses an event-sourced model to prevent merge conflicts and make concurrent changes predictable.
All changes are stored as append-only events in user-scoped files, rather than modifying a shared state file. This avoids in-place edits to the same lines and significantly reduces the likelihood of Git conflicts.
State is reconstructed in-memory by replaying a merge of all user logs.
The current state is derived by replaying events in a deterministic order.
Events use a composite of time-sortable IDs (ULIDs) and a reference to the last known event ("edge"). On creation, events are appended relative to the last known event. If multiple events share the same reference point, their relative order is resolved using their time-based IDs.
This approach:
Epiq resolves concurrent changes at the event level:
Epiq follows a local-first model:
:sync) or automaticallyFrequent synchronization reduces divergence and keeps the system predictable
🫡 Never leave your editor!
Hacker News (1)
TypeScript
99.0%
Distributed, code-native issue tracker - audit workflows via time-travel
368
stars
715
commits
TypeScript
primary language
Sep 9, 2026
updated
Issue tracking as code. Open source, distributed, local-first, and code-native.
Epiq provides issue tracking as a portable, integrated part of the development environment, with access to all the powerful tooling developers are used to.
Manage your projects in a visual kanban board — in your terminal or in your browser — while keeping all state local, Git-backed, and versioned.
With great attention to user ergonomics and developer experience, epiq strives to make project management painless and friction free.

Agents now run whole sprints unattended. Because state is a full event log, you can replay the board to find out what moved when, who moved it, and what changed along the way.
Epiq originated from the command line and offers a first-class terminal experience, but also features a browser interface powered by the same Git-backed event engine.
Epiq is a self hosted, vim-inspired issue tracker that brings developer experience to project management. It renders either as ASCII, or as a web GUI, and persists state as an immutable distributed event log, versioned and synchronized through Git.
Most issue trackers live outside your workflow. Instead of a centralized, managed service, Epiq keeps project state alongside your repository, where it travels with your code.
These design choices result in a system that is:
? to open a scrollable overview of all available commands and descriptionsBinary:
curl -fsSL https://raw.githubusercontent.com/ljtn/epiq/main/install.sh | sh
Installs to ~/.local/bin by default. Override with EPIQ_INSTALL_DIR (or XDG_BIN_HOME); pin a version with EPIQ_VERSION=v1.0.0.
npm install --global epiq
epiq --version
# If needed:
git init
# For collaboration, use a repo with a remote (e.g. clone from GitHub)
epiq
If it is your first run, this opens the interactive setup wizard that sets you up in about 30 seconds.
That’s it!
Once your project is set up, you can also launch the browser user interface with:
epiq gui
Setup wizard creates: User config persisted in
~/.epiq-global/config.json.
Initialization creates:
- Project definition in
./.epiq/project.json- Authoritative Git state at
~/.epiq-global/worktrees/<id>- Updates your
.gitignoreto ignore local-only.epiq/log/Epiq manages a dedicated Git state branch and worktree automatically as the source of truth for synchronization.- A local debug log at
.epiq/log/epiq.log— check it first if sync, boot, or a Git operation is misbehaving.
:help.? anytime to open the command palette with all available commands and descriptions.h j k l.enter, and navigate out of a context with q or esc: you are put in command line mode and can now insert commands.:close only exists for issues.:new issue|swimlane|board <Name of new node>.:comment <your-input>. Comments can be edited or deleted with the regular ':edit ...' or ':delete' commands.m. This sets you in a move state, after which you can navigate as normal, navigate to the target location, then press m again to confirm new location.filter command followed by a target, and a qualifier. So in order to filter all issues with a prio tag you can write :filter tag prio and hit enter. You can build a combination of filters by running several filter commands in succession.Clear all filters with :filter clear
:peek <offset>, where offset is <n>h, d, w, mo or y — so :peek 3d is the board three days ago. An absolute YYYY-MM-DD date works too. Step with :peek prev|next, and return with :peek now.:peek shows a frozen snapshot, :replay 1mo plays history forward from that point as an animation. An optional second argument sets the playback duration, e.g. :replay 1mo 30s.:close. This moves the issue to a special board named Closed which you can find if you navigate up (press q) a few times.Closed board, selecting an issue and typing command :reopen. This will restore the issue to its last previous location.Epiq provides a MCP (Model Context Protocol) server for agents to interact with, making it easy to plug into modern agent frameworks. The server is exposed by the epiq-mcp binary that ships with the package.
The reliable way to register the server is with the claude mcp add command — it writes to the correct config file for you, so you don't have to hand-edit JSON:
# Available everywhere (recommended)
claude mcp add --scope user epiq -- npx -y -p epiq epiq-mcp
# Or only in the current project
claude mcp add epiq -- npx -y -p epiq epiq-mcp
Use --scope user to make Epiq available in every directory; omit it to register Epiq only for the current project. Verify the connection with claude mcp list (it should report epiq … ✔ Connected). MCP servers are loaded at startup, so restart Claude Code after adding the server before its tools become available.
Find skill at .claude/skills/epiq/SKILL.md that documents a recommended workflow for working the Epiq board.
Every process — your TUI, your GUI, each agent's MCP server — writes as the user in ~/.epiq-global/config.json, so by default the board cannot tell one agent from another. Give an agent its own identity with EPIQ_USER_NAME:
{
"mcpServers": {
"epiq": {
"command": "npx",
"args": ["-y", "-p", "epiq", "epiq-mcp"],
"env": {"EPIQ_USER_NAME": "claude"}
}
}
}
That agent then shows up in the contributor list, assigns itself rather than you, and authors its own events. The id is derived from the name, so one name is one contributor on every machine — reuse names instead of inventing one per session, or the registry fills with single-run identities. EPIQ_USER_ID pins the id explicitly (26 characters of Crockford base32) if you would rather choose it. Naming yourself changes nothing.
For clients that are configured by hand, add the following to the client's MCP config file — note this is not the same as Claude Code's ~/.claude.json; Claude Desktop uses claude_desktop_config.json:
{
"mcpServers": {
"epiq": {
"command": "npx",
"args": ["-y", "-p", "epiq", "epiq-mcp"]
}
}
}
Once registered, agents can interact with your local Epiq instance through the MCP.
npx -y -p epiq epiq-mcp resolves the package against the npm registry every time it starts, even if it's already cached locally. In agent sandboxes with restricted network access, this can make the MCP server appear to hang — npx retries DNS resolution instead of failing fast, and there's no MCP-level error to explain why.
If you're running Epiq's MCP server in such an environment, install it globally once and point your MCP config at the resolved executable directly, bypassing npx (and the registry lookup) entirely on every subsequent start:
npm install --global epiq
which epiq-mcp # use this absolute path in your MCP config
{
"mcpServers": {
"epiq": {
"command": "/absolute/path/to/epiq-mcp"
}
}
}
npx remains the simpler option for normal, network-connected setups.
Epiq uses Git in the background - no manual Git commands are required. Running :sync synchronizes changes between your local state (persisted at ~/.epiq-global/worktrees/<id>/) and the remote state. By utilizing Git worktrees, synchronization stays isolated from your regular development workflow. Project tracking metadata is stored in .epiq/project.json.
Epiq is designed to provide robustness in a distributed, Git-backed environment where multiple users may update state concurrently. Instead of mutating shared files, Epiq uses an event-sourced model to prevent merge conflicts and make concurrent changes predictable.
All changes are stored as append-only events in user-scoped files, rather than modifying a shared state file. This avoids in-place edits to the same lines and significantly reduces the likelihood of Git conflicts.
State is reconstructed in-memory by replaying a merge of all user logs.
The current state is derived by replaying events in a deterministic order.
Events use a composite of time-sortable IDs (ULIDs) and a reference to the last known event ("edge"). On creation, events are appended relative to the last known event. If multiple events share the same reference point, their relative order is resolved using their time-based IDs.
This approach:
Epiq resolves concurrent changes at the event level:
Epiq follows a local-first model:
:sync) or automaticallyFrequent synchronization reduces divergence and keeps the system predictable
🫡 Never leave your editor!
Hacker News (1)
TypeScript
99.0%