Keep everything close at hand
pnpm add @aprovan/hardcopyOr for local development:
# From the hardcopy repo
pnpm install
pnpm -F @aprovan/hardcopy build# If installed as dependency
pnpm exec hardcopy init
# Local development
pnpm hardcopy initThis creates:
.hardcopy/— database and CRDT storagehardcopy.yaml— source and view configuration
Create a hardcopy.yaml in your project root:
sources:
- name: github
provider: github
orgs: [AprovanLabs]
# Or specify repos directly:
# repos: [AprovanLabs/zolvery, AprovanLabs/hardcopy]
- name: agents
provider: a2a
endpoint: http://localhost:8080
links:
- edge: a2a.TRACKS
to: github.Issue
match: "github:{{task.meta.github.repository}}#{{task.meta.github.issue_number}}"
- name: git
provider: git
repositories:
- path: ~/Projects/example
links:
- edge: git.TRACKS
to: a2a.Task
match: "a2a:{{branch.meta.a2a.task_id}}"
views:
- path: issues
description: "Open GitHub issues"
query: |
MATCH (i:github.Issue)
WHERE i.attrs->>'state' = 'open'
RETURN i
render:
- path: "{{attrs.number}}.github.issue.md"
type: github.IssueConflict resolution uses diff3 for clean merges, with automatic LLM fallback for conflicts. Configure via environment variables:
export OPENAI_BASE_URL=https://api.openai.com/v1 # or any OpenAI-compatible endpoint
export OPENAI_API_KEY=sk-...
export OPENAI_MODEL=gpt-4o # optional, defaults to gpt-4oRequires GITHUB_TOKEN environment variable for API access:
export GITHUB_TOKEN=ghp_xxxxxxxxxxxxConfiguration options:
orgs— fetch all repos from these organizationsrepos— specific repos (e.g.,owner/repo)token— override env token (not recommended)
Discovers branches and worktrees from local repositories. Reads A2A metadata from .a2a/session.json in worktrees for task linking.
Configuration options:
repositories[].path— paths to git repos (supports~)links— edge configuration for task linking
Connects to an A2A-compatible agent endpoint.
Configuration options:
endpoint— base URL of the A2A serverlinks— edge configuration for cross-provider linking
# Initialize hardcopy
pnpm hardcopy init
# Sync all sources (fetch remote data)
pnpm hardcopy sync
# Refresh a view (render to file tree)
pnpm hardcopy refresh <view>
# Show sync status
pnpm hardcopy status
# Push local changes to remotes
pnpm hardcopy push [file]After syncing and refreshing views:
project/
├── hardcopy.yaml
├── .hardcopy/
│ ├── db.sqlite # LibSQL database (nodes + edges)
│ ├── crdt/ # Per-node CRDT snapshots
│ └── errors/ # Sync error reports
└── issues/ # View directory
├── .index # Pagination state
├── 42.github.issue.md
└── 43.github.issue.md
import { Hardcopy } from "@aprovan/hardcopy";
const hc = new Hardcopy({ root: process.cwd() });
await hc.initialize();
// Sync all sources
const stats = await hc.sync();
console.log(`Synced ${stats.nodes} nodes`);
// Query the database
const db = hc.getDatabase();
const issues = await db.queryNodes("github.Issue");
await hc.close();