gadak

package module
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 13, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

README

gadak gadak

Latest Release CI License

Follow the thread.

gadak mirrors Jira and Confluence into one local SQLite file — issues, comments, history, wiki pages — indexed together and searchable in milliseconds. Ask it yourself from a keyboard-driven web UI; let your coding agent ask in plain SQL. One binary, no server, no account.

The mirror is a cache you can throw away. If this project stops tomorrow, you delete a directory and have lost nothing: Jira stays the source of truth, and nothing you do here is stored anywhere else.

▶  Open the live demo  —  534 issues + 71 wiki pages, in your browser, right now.


Every clip in this README is generated from a script against the committed demo snapshot — this one from e2e/demo/web-demo.spec.ts. What you see is what CI checks.

brew install midagedev/tap/gadak

gadak init && gadak sync    # Jira (and Confluence) -> ~/.gadak/gadak.db
gadak serve                # http://gadak.localhost:7777
gadak sql "select key, summary from issues_full where reopen_count > 1"

That last query is the point. reopen_count is not a Jira field — gadak derives it from the changelog while it syncs, along with reopen_reason and the epic a sub-task ultimately rolls up to. Your site cannot answer "what keeps coming back?" at all; a local mirror answers it in a line. docs/RECIPES.md has thirteen more, each verified against the demo snapshot.

Status: working, pre-release. Sync (both sources), the read API, write-through, the web UI, the CLI, settings, the plugin boundary, and i18n are implemented and verified end to end against a live Atlassian site. docs/STATE_OF_PLAY.md is the honest inventory.

Why

Every developer suddenly has a coding agent, and agents burn context paging REST APIs and guessing at JQL. Worse, half of what an agent needs is not in the tracker at all — it is in the wiki next door. A local file that holds both answers "what do we know about X?" with one full-text query, joins across sources, and never spends a token on pagination.

Beyond the agent, three complaints about living in a tracker and a wiki, one root cause.

Search is slow, and it is two searches. Every filter change is a network round trip against a multi-tenant service — and the answer to "what do we know about idempotency?" is split between Jira search and Confluence search, which do not talk to each other. Once both are on local disk there is one FTS index: type a word, get the issues and the pages, instantly.

Agents cannot read your team's context well. A coding agent asked "what did we already fix in the billing flow, and what did we decide in the design doc?" has to page through two REST APIs, guess at JQL and CQL, and burn its context on JSON envelopes. Give it a SQLite file instead and it writes one query with a join and an FTS match. No tool schema, no pagination, no rate limit.

You cannot see your own team's shape. "Which issues came back after we closed them, and why?" is a join over the changelog. "Which epic is actually stuck?" is a rollup over the hierarchy. In Jira neither is a question you can ask; here they are where reopen_count > 0 and a group-by on epic_key.

All three fall out of the same move: mirror the data locally, then let the UI and the agent read the same store.

Two surfaces, one store

For Looks like
Web UI all-day triage — a browser tab (gadak serve) or its own macOS window (desktop app, no port at all) a list you triage without the mouse (j/k walk, x multi-select, s/a/c status·assignee·comment in place), epic grouping and rollups, saved views, a ⌘K palette that finds issues and wiki pages, / to narrow whichever screen you are on, a freshness chip that shows the mirror's age and pulls it on click, full issue detail (rich text, comments, history, attachments), and wiki documents as a first-class citizen: recency-first lists with label chips, a filter that marks its matches, deep-linkable pages (?doc=), and cross-references both ways — the documents an issue's text mentions on the issue, the issues a page mentions on the page
CLI + SQL agents, scripts, one-off questions gadak issue, gadak search (issues and pages), gadak sql, plus the file itself

Writes go through to Jira and then refresh the mirror, so the list is correct a moment later without a full sync. Comment, transition, and assign work from the web UI and the CLI; field edits and issue creation are web-only today (values always come from what Jira allows, never free text). The wiki mirror is read-only on purpose — Confluence stays the place where documents are written.

Hierarchy is first-class: epic_key is derived honestly (the nearest epic ancestor, so a sub-task groups under its epic, not its story), group-by-epic headers show the epic's actual title, an epic's detail rolls up its children (12 done / 14), and both breadcrumbs — issue and document — are clickable.

So is the seam between the sources. Jira and Confluence never tell each other what mentions what, but the text does: gadak extracts issue keys from page bodies and wiki links from issue text into an item_refs table while it syncs. That is why an issue can list the design docs that cite it and a page can list the tickets it references — a join neither product can make, and the receipt that both really live in one database.

Attachments are local too. The first view of an image caches its bytes next to the mirror and every later view is a disk read, so a screenshot-heavy issue opens at the speed of the rest of the app — and keeps rendering offline.

For agents

This is half the reason gadak exists, so it has its own reference: AGENTS.md — schema tour, query patterns, and the mistakes that silently return nothing. docs/AGENT_SETUP.md is one paste per agent (Claude Code, Cursor, Codex, MCP). Hooking one up is one line:

gadak mcp install claude    # pins this binary and profile into the registration

gadak search, gadak sql aggregation, and gadak issue in a terminal
A real agent session — one-line MCP registration, then a live cross-source answer. Generated from tools/tapes/agent.tape (VHS, unscripted model output).

The interface is the database, so anything that can run a shell command has full power:

# What keeps coming back? (reopen_count is derived here — Jira has no such field)
gadak sql "select key, summary, reopen_count from issues_full
          where reopen_count > 0 order by reopened_at desc limit 20"

# Full-text across issues AND wiki pages — one index, one query
gadak search "idempotency webhook"

# One issue whole, or a write straight through to Jira
gadak issue NMB-140 --json
gadak comment NMB-140 -m "Reproduced on staging."

Reads are safe by construction: gadak sql opens the database mode=ro, and MCP's gadak_query additionally rejects anything that is not a SELECT — so an agent can be given the mirror without being given arbitrary sqlite3. When the mirror does not model an endpoint at all, gadak api passes the request through to your site: read-only unless you add --write, never on MCP.

Everything can hold the file at once — WAL with one writer (the sync loop), readers everywhere else — so serve and an agent coexist by design.

One caveat we would rather you read here than discover later: an agent that reads your mirror sends what it reads to whatever model it talks to. gadak itself sends nothing anywhere (SECURITY.md), but the agent will — scope the mirror to what the agent should see (project and space allowlists, or a separate profile).

Install

Atlassian Cloud only, and you need an API token — one token covers Jira and Confluence on the same site.

You install one thing. There is a single binary and a single app, and the app has the binary inside it:

brew install midagedev/tap/gadak     # macOS + Linux — the CLI and the web UI

or download Gadak-<version>-arm64.dmg from the latest release for the macOS app — signed, notarized, sets itself up in its own window with no terminal at any point.

If you took the app and later want an agent on the same mirror, the CLI is already on your disk; macOS just does not put an app bundle on your PATH. One command does:

/Applications/Gadak.app/Contents/Resources/bin/gadak install-cli

Then first run:

gadak serve      # http://gadak.localhost:7777 — setup happens in the browser

Other routes (install script, release archive, source build, Docker), wiki mirroring, profiles for two sites, and the upgrade gotchas all live in docs/INSTALL.md.

Making it yours

Two axes, no forking required — see docs/EXTENDING.md.

Configuration covers most of it, from the settings dialog or ~/.gadak/config.json: map your custom fields (severity, environment, whatever your site calls them), classify issues into teams by label or component, choose which fields are inline-editable, set the staleness threshold and sync intervals, toggle features. Most keys apply without restart; sync intervals need a restart of gadak serve. Full key table: docs/CONFIGURATION.md.

The plugin boundary covers the rest. The core contains zero GitHub, CD, or test-management code on purpose. Anything else you want beside an issue — linked pull requests, deploy status, QA context — arrives by writing rows into the enrichments table and bumping a version counter, from any language on any schedule. The server merges them; the UI surfaces them. Working examples live in examples/plugins/, and the contract is docs/PLUGINS.md.

How it works

flowchart LR
  Jira["Jira Cloud REST"] -->|"incremental sync"| DB["SQLite + FTS5<br/>~/.gadak/gadak.db"]
  Wiki["Confluence REST"] -->|"incremental sync"| DB
  DB --> Serve["gadak serve"]
  Serve --> UI["Web UI<br/>(IndexedDB cache)"]
  DB --> Agent["Coding agent<br/>sqlite3 / gadak sql / MCP"]
  UI -->|"writes"| Serve
  Serve -->|"writes"| Jira

Sync is incremental with an overlap on the watermark, plus a reconcile pass so deletions do not linger. Confluence needs one extra trick the API forces: comment edits do not bump a page's version, so every incremental pass re-reads comments for changed pages separately. Derived fields the sources do not provide — reopen count and the reason it came back, last status change, resolution date, clone origin, the honest epic_key — are computed during sync and keyed on statusCategory and ids, never on a localized name.

The storage spine is source-neutral (items + per-kind projections + one FTS index), which is not a slogan: Confluence merged without reshaping the database, and the same spine is where the next source lands. See docs/decisions/0006-confluence-connector.md.

Why not a browser extension or a Forge app?

Jira Cloud deliberately sends no CORS headers on its REST API, so a static page cannot call it. Both alternatives that avoid a local process were considered and rejected because neither can hand a coding agent a queryable local database, which is half the point. See docs/decisions/0003-local-process.md.

Good fit / bad fit

Use gadak when… Use Jira/Confluence directly when…
You search and triage the same projects every day and the latency hurts. You need boards, sprints, reports, automation, permissions.
You want an agent to reason over your tracker's history and your wiki. You need administration, workflow editing, or document authoring.
You want offline reading of everything you have access to. A minute of staleness matters.
Your tracker holds tens of thousands of issues and Jira's UI struggles. Your team is small enough that Jira already feels instant.

In scope: issue fields, descriptions, comments, attachments, changelog, links, epic hierarchy, status transitions, assignee, wiki pages (bodies, comments, labels), full-text search across all of it, saved views, watches; field edits and issue creation on the web UI. Out of scope: boards and sprint mechanics, project administration, workflow configuration, permission schemes, writing to the wiki, and anything requiring Jira's own UI. Not a sync engine: Jira and Confluence are the systems of record. The mirror is disposable — delete it and re-sync.

How it compares

  • jira-cli talks to Jira's REST API per command, so every listing is a network round trip and JQL is the query language. gadak queries a local mirror: millisecond filters, SQL joins over the changelog, offline reads — plus a web UI over the same file. If all you want is "create an issue from the terminal", jira-cli is lighter.
  • Linear is a different tracker. If your team can move, move. gadak is for the (much larger) group whose org keeps Jira: it gives you Linear-ish speed and keyboard flow without asking anyone for permission — it is a mirror, not a migration.
  • Atlassian's Rovo MCP server gives agents official, hosted access to the same data — worth using if it fits. The architectural difference: a network MCP cannot join issues to wiki pages, aggregate, or work offline, every call costs tokens and rate budget, and it answers only the questions its tools anticipated. A local SQLite file has none of those limits, and derived history (reopen counts and reasons, honest epic ancestry) exists only in the mirror.
  • Jira's own UI stays the source of record and the place for boards, sprints, and admin. gadak does not replace it; it replaces waiting on it.

More sources later

Confluence was the proof: the second connector merged against the same spine, the same FTS index, and the same read contracts without reshaping the database (decision 0006). The pattern — mirror, project, index — is what the next source rides too. Candidates are ranked by user demand, not by roadmap romance; see docs/ROADMAP.md for what is actually next.

Documentation

Who makes this

One person, currently. Weigh that before pointing it at a company knowledge base — and weigh the other side too: the mirror is a disposable cache of your own Jira, the schema is a documented public contract, the license is Apache-2.0, and the file is plain SQLite. If this project stops tomorrow, you delete a directory and have lost nothing. The hard questions — site load, compliance, what an agent does with the data — are answered head-on in docs/FAQ.md.

Contributing and feedback

See CONTRIBUTING.md — and docs/GOOD_FIRST_ISSUES.md if you want a place to start. Bug reports should include your Jira deployment type (Cloud), the gadak commit, and the command you ran. Never paste real issue data, tokens, or site URLs into a public issue.

Using gadak with an agent and hitting friction? That is exactly the feedback we want — open an issue with the question you asked and what the agent did.

License

Apache-2.0. See LICENSE and NOTICE.

Documentation

Overview

Package gadak embeds the built web UI so a release is one self-contained binary. `npm run build` writes web assets to dist/app before `go build`; without that step the embed carries only the committed placeholder and WebUI reports ok=false, which `gadak serve` turns into a helpful error.

It also embeds the Claude Code skill (skills/gadak/SKILL.md) so `gadak skill install` works for brew installs without a source checkout.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func SkillMarkdown

func SkillMarkdown() []byte

SkillMarkdown returns the embedded Claude Code skill body (skills/gadak/SKILL.md). Callers must not modify the returned slice.

func WebUI

func WebUI() (fs.FS, bool)

WebUI returns the embedded web assets rooted at the app directory. ok is false when the binary was built without a web build (placeholder only).

Types

This section is empty.

Directories

Path Synopsis
cmd
gadak command
export-static freezes the demo mirror into static JSON + attachment bytes for the zero-install hosted demo (GitHub Pages).
export-static freezes the demo mirror into static JSON + attachment bytes for the zero-install hosted demo (GitHub Pages).
internal
atlhttp
Package atlhttp is the shared HTTP transport for Atlassian Cloud clients (Jira, Confluence): retries, backoff, path safety, and optional usage meters.
Package atlhttp is the shared HTTP transport for Atlassian Cloud clients (Jira, Confluence): retries, backoff, path safety, and optional usage meters.
attachcache
Package attachcache stores attachment bytes on local disk.
Package attachcache stores attachment bytes on local disk.
clitool
Package clitool installs the gadak binary onto PATH via a symlink.
Package clitool installs the gadak binary onto PATH via a symlink.
config
Package config loads and saves ~/.gadak/config.json.
Package config loads and saves ~/.gadak/config.json.
confluence
Package confluence is a thin REST client for Confluence Cloud: enough of the API to fill the page mirror, and nothing else.
Package confluence is a thin REST client for Confluence Cloud: enough of the API to fill the page mirror, and nothing else.
fields
Package fields classifies and discovers Jira custom fields for the mirror.
Package fields classifies and discovers Jira custom fields for the mirror.
jira
Package jira is a thin REST client for Jira Cloud: enough of the API to fill the mirror, and nothing else.
Package jira is a thin REST client for Jira Cloud: enough of the API to fill the mirror, and nothing else.
mcp
Package mcp implements a thin stdio MCP server over the local gadak mirror.
Package mcp implements a thin stdio MCP server over the local gadak mirror.
secretscan
Package secretscan holds the credential-shaped string patterns that every outbound artifact is checked against before it is written.
Package secretscan holds the credential-shaped string patterns that every outbound artifact is checked against before it is written.
selfupdate
Package selfupdate answers one question — is a newer release published? — with one GitHub API call a day, cached on disk.
Package selfupdate answers one question — is a newer release published? — with one GitHub API call a day, cached on disk.
server
Package server serves the HTTP read API the web UI already speaks (specs/000-product/contracts/api.md).
Package server serves the HTTP read API the web UI already speaks (specs/000-product/contracts/api.md).
snapshot
Package snapshot builds shareable mirror copies for demos and benchmarks.
Package snapshot builds shareable mirror copies for demos and benchmarks.
store
Package store owns the SQLite mirror: schema, migrations, transactions, full-text index and the derived fields the source does not provide.
Package store owns the SQLite mirror: schema, migrations, transactions, full-text index and the derived fields the source does not provide.
sync
Package sync fills the mirror from Jira.
Package sync fills the mirror from Jira.
teamconfig
Package teamconfig builds and applies team-shareable gadak configuration files (views, field maps, group rules, …) without credentials or personal machine prefs.
Package teamconfig builds and applies team-shareable gadak configuration files (views, field maps, group rules, …) without credentials or personal machine prefs.
workspace
Package workspace mounts additional gadak profiles under /w/<name>/ and lists them at GET /api/v1/workspaces.
Package workspace mounts additional gadak profiles under /w/<name>/ and lists them at GET /api/v1/workspaces.
tools
bench-fixture command
Command bench-fixture builds a deterministic synthetic gadak.db for latency benchmarks (T6.7 / G5).
Command bench-fixture builds a deterministic synthetic gadak.db for latency benchmarks (T6.7 / G5).
seed-demo command
Command seed-demo populates a throwaway Jira Cloud site with a realistic demo backlog for gadak screenshots and examples/demo.db.
Command seed-demo populates a throwaway Jira Cloud site with a realistic demo backlog for gadak screenshots and examples/demo.db.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL