https://github.com/uinaf/lincrawl
Local-first Linear work-graph archive CLI
https://github.com/uinaf/lincrawl
age-encryption archive cli crawler crawlkit linear sqlite
Last synced: about 1 month ago
JSON representation
Local-first Linear work-graph archive CLI
- Host: GitHub
- URL: https://github.com/uinaf/lincrawl
- Owner: uinaf
- License: mit
- Created: 2026-05-18T19:26:36.000Z (about 1 month ago)
- Default Branch: main
- Last Pushed: 2026-05-19T00:41:06.000Z (about 1 month ago)
- Last Synced: 2026-05-19T00:55:12.343Z (about 1 month ago)
- Topics: age-encryption, archive, cli, crawler, crawlkit, linear, sqlite
- Language: Go
- Homepage:
- Size: 157 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
- Roadmap: docs/roadmap.md
- Agents: AGENTS.md
Awesome Lists containing this project
README
# lincrawl
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.
## Install
From a checkout:
```bash
go run ./cmd/lincrawl version --json
./scripts/smoke
```
## Quick start
Keep local state outside tracked paths:
```bash
export LINCRAWL_HOME=/tmp/lincrawl-home
go run ./cmd/lincrawl doctor --offline --json
```
Offline path with synthetic fixtures (no `LINEAR_API_KEY` required):
```bash
go run ./cmd/lincrawl sync --fixture testdata/synthetic --json
go run ./cmd/lincrawl search "ingest" --json
go run ./cmd/lincrawl show LIN-1 --json
```
Live path against Linear (reads `LINEAR_API_KEY` from `.env.local` or env):
```bash
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 --json
```
## Commands
JSON 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 ` | FTS5 search; supports `--fields`, `--limit`, `--raw`, `--ndjson` |
| `show ` | 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 ` | 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 ` | Verify a tenant store's `manifest.json` + canonical artifact layout |
| `subscribe --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:
```bash
lincrawl export --out ./snapshots/lincrawl.jsonl --json
LINCRAWL_HOME=$(mktemp -d) lincrawl sync --stdin --json \
< ./snapshots/lincrawl.jsonl
```
## Configuration
`LINEAR_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`](.env.example) for the full set.
## Docs
- [Agent guide](AGENTS.md) — operator/agent contract
- [Agent skill](skills/lincrawl/SKILL.md) — structured per-workflow guidance
- [Architecture](docs/architecture.md) — CLI, store, sync shape
- [Roadmap](docs/roadmap.md) — what's done and what's next
- [Tenant data boundary](docs/tenant-data-boundary.md) — what stays out
- [Bootstrap plan](docs/plans/bootstrap-claude-prompt.md) — original prompt
- [Contributing](CONTRIBUTING.md) — local setup and validation
- [Security](SECURITY.md) — private vulnerability reporting
## Verification
```bash
./scripts/verify # tidy, vet, test, race, smoke, guard, release-check, whitespace
./scripts/local-live-smoke # opt-in bounded live Linear proof, env-gated
```
CI runs `verify` on every PR and push. Tagged releases (`v0.0.x`) come
from `main` via semantic-release + GoReleaser; see [Distribution](docs/distribution.md).
## Acknowledgements
Built on [`openclaw/crawlkit`](https://github.com/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`](https://github.com/openclaw) family.
## License
MIT. See [License](LICENSE).