Local-first Linear work-graph archive CLI.
lincrawl syncs Linear teams, projects, issues, comments, labels, and
workflow states into a private local SQLite archive, runs FTS5 search
offline, exports a canonical JSONL dump, and publishes encrypted
*.jsonl.zst.age snapshots to a tenant-controlled store that
subscribers can verify and re-import. Read-only against Linear by
construction.
This repository is the generic crawler core. Tenant credentials, real workspace identifiers, issue bodies, comments, snapshots, logs, reports, screenshots, and transcript-derived examples do not belong here.
From a checkout:
go run ./cmd/lincrawl version --json
./scripts/smokeKeep local state outside tracked paths:
export LINCRAWL_HOME=/tmp/lincrawl-home
go run ./cmd/lincrawl doctor --offline --jsonOffline path with synthetic fixtures (no LINEAR_API_KEY required):
go run ./cmd/lincrawl sync --fixture testdata/synthetic --json
go run ./cmd/lincrawl search "ingest" --json
go run ./cmd/lincrawl show LIN-1 --jsonLive path against Linear (reads LINEAR_API_KEY from .env.local or env):
go run ./cmd/lincrawl sync --entities --json
go run ./cmd/lincrawl sync --updated-since 24h --max-issues 200 --json
go run ./cmd/lincrawl sync --resume --max-issues 1000 --json
go run ./cmd/lincrawl sync --issue LIN-42 --jsonJSON by default everywhere. Errors are JSON envelopes on stderr.
| Command | Purpose |
|---|---|
doctor --offline |
Report resolved paths and redacted credential presence |
describe |
Machine-readable schema for every command (args, flags, exit codes, field masks, mutually-exclusive groups) |
status |
Local archive row counts |
sync |
Ingest from --fixture, --stdin, --entities, --updated-since, --resume, or --issue; supports --dry-run, --ndjson, --max-issues, --page-size |
search <query> |
FTS5 search; supports --fields, --limit, --raw, --ndjson |
show <id-or-identifier> |
Resolve one issue by UUID or TEAM-N; supports --fields |
query --graphql … --vars … |
Pass-through to the Linear GraphQL API for raw queries |
export --out <path> |
Canonical NDJSON dump of the local archive; --out is sandboxed to CWD |
archive --fixture … --recipient … --out … |
Encrypt a fixture directory as an *.jsonl.zst.age snapshot |
publish --recipient … --out … |
Encrypt the entire local archive as a publishable snapshot |
import --in … --identity … |
Decrypt + ingest a *.jsonl.zst.age snapshot into the local archive |
store verify <path> |
Verify a tenant store's manifest.json + canonical artifact layout |
subscribe <path> --identity … |
Verify a tenant store, then ingest every listed snapshot |
guard |
Scan the working tree for tenant leaks, plaintext archives, secrets |
version |
Build version, commit, date |
Round-trip via NDJSON:
lincrawl export --out ./snapshots/lincrawl.jsonl --json
LINCRAWL_HOME=$(mktemp -d) lincrawl sync --stdin --json \
< ./snapshots/lincrawl.jsonlLINEAR_API_KEY is the only required key, and only for live calls. Put it
in a git-ignored .env.local. LINCRAWL_HOME overrides the XDG data dir.
See .env.example for the full set.
- Agent guide — operator/agent contract
- Agent skill — structured per-workflow guidance
- Architecture — CLI, store, sync shape
- Roadmap — what's done and what's next
- Tenant data boundary — what stays out
- Bootstrap plan — original prompt
- Contributing — local setup and validation
- Security — private vulnerability reporting
./scripts/verify # tidy, vet, test, race, smoke, guard, release-check, whitespace
./scripts/local-live-smoke # opt-in bounded live Linear proof, env-gatedCI runs verify on every PR and push. Tagged releases (v0.0.x) come
from main via semantic-release + GoReleaser; see Distribution.
Built on openclaw/crawlkit for
the SQLite open / PRAGMA cocktail / schema-version / read-only / state
primitives. The CLI surface, agent-DX shape, structured error envelope,
and release pipeline mirror conventions from sibling crawlers in the
openclaw family.
MIT. See License.