pine

module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: MIT

README ΒΆ

🌲 Pine

A git-native, local-first workspace for AI-assisted development.

CI Release Go Report Card

Pine keeps your bugs, features, epics, project context, and persistent learnings as plain files inside your repository, so humans and AI agents (Claude Code, Codex, Gemini CLI, …) work from the same source of truth. No cloud, no accounts, no database β€” the repo is the database and git is the history.

A single binary gives you three surfaces over the same .pine/ folder:

  • a Beads-style CLI for tickets, dependencies, epics, and learnings from the terminal;
  • a beautiful local web UI (kanban board, markdown editor, attachments, search);
  • AI context/prompt generation so an agent understands the project instantly.

When an agent edits a ticket file on disk, the board updates live in your browser. When you drag a card, the file changes on disk. It is the same data, always.


Installation

Grab the binary for your platform from the Releases page. Each archive is a single self-contained pine binary with the web UI built in β€” no runtime dependencies.

macOS / Linux:

# pick the asset for your OS/arch, e.g. pine_0.1.0_darwin_arm64.tar.gz
tar -xzf pine_*_*.tar.gz
sudo mv pine /usr/local/bin/
pine --version

Windows: download the _windows_amd64.zip, extract pine.exe, and add it to your PATH.

With Go
go install github.com/underworld14/pine/cmd/pine@latest

This gives you the full CLI, HTTP API, and live sync. (The bundled web UI ships in the release binaries and make build only; a go install build serves a small placeholder page in place of the UI.)

Build from source

Requires Go 1.26+ and Node 20+.

git clone https://github.com/underworld14/pine
cd pine
make build            # builds the SvelteKit UI and embeds it into ./pine
./pine --version

Backend-only (no Node required) β€” serves a dev placeholder for the UI:

make build-dev

Quick start

cd your-repo
pine init                               # create .pine/
pine create --type bug --title "Login button dead" -p high -l login,ui
pine open                               # launch the web UI (localhost:3412)
The terminal workflow (Beads-style)

Everything works without leaving the shell; every read command takes --json for agents.

pine create --type epic --title "Auth system"
pine create --type feature --title "Login form" --parent EPIC-001 -p high
pine create --type bug --title "Button dead" --parent EPIC-001 --deps FEAT-001

pine list --blocked            # tickets waiting on dependencies (πŸ”’)
pine ready                     # actionable work: open and unblocked, most urgent first
pine dep tree BUG-001          # dependency tree
pine close FEAT-001            # β†’ BUG-001 becomes ready
pine show EPIC-001             # epic with child progress (1/2 done)
pine update BUG-001 --status doing
pine log BUG-001               # commits that mention or touch this ticket
pine doctor --fix              # health check; auto-repair the mechanical issues
pine import github             # pull open GitHub issues in as tickets (via gh)

Dependency cycles are refused at write time. A ticket is blocked while any of its deps is not done, and ready otherwise β€” computed from the files, never stored, so agents editing files can't desync it.

AI context
pine context | pbcopy          # a full project briefing for your agent
pine prompt BUG-001            # a fix-request prompt for one ticket
pine export --format md        # all tickets as markdown (or --format json)

pine context includes a Conventions block that teaches the agent how to write back to .pine/ (edit status to move a ticket, use deps/parent, run pine ready/pine close). It also surfaces Relevant Learnings from .pine/learnings/ when any exist β€” tip-resolving supersede chains so agents never see a stale rule, and excluding learnings whose --cites paths are missing on disk.

pine learn "Always use the query builder" --scope global --tags db
pine learn "Fixed in BUG-014" --scope ticket --ticket BUG-014 --supersedes LRN-001
pine learn "Store is the single write path" --scope component --component internal/store
pine learn "Race in retry" --cites internal/webhook/retry.go
pine learn list                    # hides superseded and citation-stale by default
pine learn search "migration"      # same default
pine learn show LRN-001            # supersedes / superseded by / cite βœ“βœ—
pine learn supersede LRN-001 "..." # capture a replacement (inherits scope/tags)
pine learn rm LRN-002              # delete a learning permanently
pine learn list --include-superseded
pine learn list --include-stale

Learnings are cross-session, cross-agent insights (gotchas, conventions, workarounds). Capture them with pine learn so Claude Code, Codex, Cursor, and Gemini all see them next session. Scope them global, ticket (--ticket ID), or component (--component path). When a new insight replaces an older one, use pine learn supersede <LRN-id> "..." (or --supersedes <LRN-id>); use --include-superseded to audit, or pine learn rm <id> to delete outright. When an insight depends on specific files, pass --cites path/to/file β€” if that path is later deleted, pine doctor reports a dangling cite and list/search/context hide the entry by default (--include-stale to audit). Renames (git mv) look like deletions; that is intentional. pine doctor also flags dangling supersedes refs and supersede cycles.

Agent setup

pine init runs an interactive wizard to install instructions for your coding agent (Claude Code, Gemini CLI, Codex, Factory, …). Forgot to set it up?

pine setup agent        # interactive wizard
pine setup agent -y     # install AGENTS.md + CLAUDE.md + GEMINI.md
pine setup agents       # AGENTS.md only
pine setup claude       # CLAUDE.md only
pine setup gemini       # GEMINI.md only
pine setup --check      # verify sections are current
pine setup --remove     # strip pine sections

Use pine init --skip-agents to skip the wizard (e.g. in CI). Each file gets a marked <!-- pine:begin ... --> section with workflow rules, CLI reference, and instructions for pine learn (persistent cross-agent learnings). Re-run pine setup agent after upgrading Pine to refresh stale sections.

Beyond the markdown block, setup also installs a first-class skill (.claude/skills/pine/SKILL.md for Claude Code, .agents/skills/pine/SKILL.md for Codex/generic agents) and, for Claude Code, a Stop hook in .claude/settings.json that reminds the agent to capture learnings before ending a turn. Both are idempotent and removed by pine setup --remove.


How it stores data

Everything lives in .pine/ and is meant to be committed:

.pine/
  config.json           # project settings, ticket types, priorities, optimizer
  board.json            # kanban columns (statuses only β€” never ticket ids)
  tickets/
    BUG-001.md          # YAML frontmatter + markdown body
  learnings/
    LRN-001.md          # durable cross-agent insights
  attachments/
    BUG-001/login.webp  # optimized on ingest
  templates/            # bug.md, feature.md, epic.md
  prompts/fix.md        # the pine prompt template

A ticket file:

---
id: BUG-001
title: Login button not working
status: testing
priority: high
labels:
  - login
  - ui
deps:
  - FEAT-002
parent: EPIC-001
created: 2026-07-04T10:12:00Z
updated: 2026-07-04T11:00:00Z
---

# Description
...

The filename is the canonical id; frontmatter status decides which board column a ticket is in. Pine parses leniently β€” a malformed or agent-written file is surfaced as a read-only "degraded" ticket rather than lost, and pine doctor reports every problem (schema errors, dangling deps, dependency cycles, broken attachment references, orphaned directories, stray files, dangling supersedes refs, and supersede cycles).

A learning file:

---
id: LRN-001
scope: global
tags:
  - db
source_agent: manual
created: 2026-07-11T10:00:00Z
---

Always use the query builder β€” raw SQL caused schema drift (see BUG-014).

The filename is the canonical id. Optional frontmatter supersedes: LRN-xxx marks a replacement; superseded_by is derived at read time and never stored.


Pine & git branches

Because .pine/ is committed alongside your code, tickets are versioned with your branches β€” exactly like source files. Switching branches changes which tickets you see: a ticket created and committed on dev won't appear while you're on main (it's not lost β€” it returns on dev, or when the branches merge). Uncommitted new tickets stay visible across branches, since git leaves untracked files alone.

This is a deliberate trade-off of the "everything is files" model. If you prefer a single global backlog, keep .pine/ mastered on your mainline (create/close tickets there and let them flow to feature branches via merge), or run Pine against a git worktree pinned to one branch.

Merge-safe IDs. New tickets get random, collision-resistant IDs like BUG-7f3k2a by default ("idStyle": "hash" in config.json), so two branches β€” or two AI agents β€” never mint the same ID. Prefer the classic sequential BUG-001? Set "idStyle": "sequential"; just note that concurrent branches can then choose the same number and clash on merge (pine doctor flags duplicates).

Field-level merges. Run pine setup merge once per clone to register Pine's git merge driver for .pine/tickets/*.md. When two branches edit the same ticket, git then merges it field by field β€” one side's status change and the other's new label combine cleanly instead of producing a raw YAML conflict. Genuine divergences (both sides rewrite the body, or change the same scalar) are still surfaced for review. The .gitattributes rule is committed and shared; the git config is local, so each teammate runs pine setup merge after cloning (pine doctor reminds anyone who hasn't).

Commit history. pine log <ID> lists the commits that mention a ticket or touched its file β€” a quick audit trail linking tickets to the code that changed for them.

Importing. Already have a GitHub backlog? pine import github pulls open issues in as tickets via your existing gh CLI auth (idempotent β€” re-running skips anything already imported).

For contrast, Beads keeps issues global across branches by storing them in a Dolt database on a separate git ref rather than as files on your branches β€” a different point in the design space (global + cell-level merge, but not plain, hand-editable files).

Web UI

pine serve (or pine open) serves the UI on http://127.0.0.1:3412 (localhost only, with Host/Origin checks β€” no auth, no external access).

  • Dashboard β€” at-a-glance triage lists.
  • Board β€” drag & drop kanban; blocked cards show πŸ”’; cards glide + flash when an agent changes a file on disk.
  • Ticket β€” frontmatter controls, split markdown editor with a "changed on disk" conflict banner, dependency/epic chips, attachment grid + lightbox, and a one-click Copy AI prompt.
  • New issue in ≀10s: press c, type a title, paste a screenshot (⌘V), βŒ˜β†΅. Screenshots are downscaled and re-encoded to WebP on the way in.
  • Search (/) and a command palette (⌘K).

Persistent learnings (pine learn) are a CLI/AI-context surface only β€” they don't appear in the web UI, its board, or its search.

Attachments are optimized on upload: images are EXIF-oriented, downscaled to 2000px, and re-encoded to lossy WebP (kept only if smaller); videos pass through with an oversize warning. pine optimize back-fills files dropped in by hand.


VS Code extension

Prefer to stay in your editor? The Pine VS Code extension opens the same board in a VS Code tab β€” no terminal, no manual pine serve. Run Pine: Open Board and it starts Pine for you and embeds the UI; Pine: Create Bug / Create Feature add tickets from the command palette. It reuses the exact web UI, so live sync, attachments, and search all behave identically. See editors/vscode/README.md for details.


Development

make test        # Go unit + integration tests
make test-web    # frontend (vitest)
make e2e         # Playwright end-to-end (requires: cd web && npx playwright install)
make lint        # go vet

Tech

Go (cobra CLI, chi router, Bleve in-memory search, fsnotify watcher, SSE) with a SvelteKit 2 / Svelte 5 / Tailwind v4 UI embedded via go:embed. WebP encoding is pure-Go (no cgo), so the binary cross-compiles cleanly.

Contributing

Contributions of all kinds are welcome. See CONTRIBUTING.md for local setup and how to run the test suite, and please open an issue for bugs or feature ideas.

License

MIT Β© underworld14

Directories ΒΆ

Path Synopsis
cmd
pine command
Command pine is a git-native, local-first workspace for AI-assisted development: markdown tickets, a kanban web UI, attachments, search, and AI-context generation, all stored as files in a .pine/ directory.
Command pine is a git-native, local-first workspace for AI-assisted development: markdown tickets, a kanban web UI, attachments, search, and AI-context generation, all stored as files in a .pine/ directory.
internal
attach
Package attach ingests uploaded images and videos: it sniffs the real content type, optionally optimizes images (EXIF-orient, downscale, re-encode to lossy WebP, keep-smaller), and returns the bytes to persist plus metadata.
Package attach ingests uploaded images and videos: it sniffs the real content type, optionally optimizes images (EXIF-orient, downscale, re-encode to lossy WebP, keep-smaller), and returns the bytes to persist plus metadata.
cli
Package cli implements Pine's cobra command tree: init, serve, the Beads-style ticket commands (list/show/create/update/close/dep/ready/log), learn (capture/list/search/show/supersede/rm persistent learnings), and the AI helpers (context/prompt/export/doctor/optimize).
Package cli implements Pine's cobra command tree: init, serve, the Beads-style ticket commands (list/show/create/update/close/dep/ready/log), learn (capture/list/search/show/supersede/rm persistent learnings), and the AI helpers (context/prompt/export/doctor/optimize).
config
Package config loads, validates, and saves Pine's two JSON config files: .pine/config.json (project settings) and .pine/board.json (kanban columns).
Package config loads, validates, and saves Pine's two JSON config files: .pine/config.json (project settings) and .pine/board.json (kanban columns).
contextgen
Package contextgen builds the markdown that teaches an AI agent about the project: `pine context` (whole-project briefing) and `pine prompt <ID>` (a fix request for one ticket).
Package contextgen builds the markdown that teaches an AI agent about the project: `pine context` (whole-project briefing) and `pine prompt <ID>` (a fix request for one ticket).
crossbranch
Package crossbranch aggregates tickets that live on other git branches into a read-only overlay for the board.
Package crossbranch aggregates tickets that live on other git branches into a read-only overlay for the board.
doctor
Package doctor validates a .pine workspace: config/board schemas, ticket integrity, dependency/epic consistency, and attachment health.
Package doctor validates a .pine workspace: config/board schemas, ticket integrity, dependency/epic consistency, and attachment health.
frontmatter
Package frontmatter holds the YAML frontmatter parsing/serialization primitives shared by internal/ticket and internal/learning: splitting the "---" delimited block from the body, decoding string lists leniently, and formatting/parsing timestamps.
Package frontmatter holds the YAML frontmatter parsing/serialization primitives shared by internal/ticket and internal/learning: splitting the "---" delimited block from the body, decoding string lists leniently, and formatting/parsing timestamps.
gitx
Package gitx exposes read-only git awareness (branch, working-tree status, recent commits, tracked files) behind a small Client interface.
Package gitx exposes read-only git awareness (branch, working-tree status, recent commits, tracked files) behind a small Client interface.
learning
Package learning is the pure domain layer for Pine learnings: parsing and serializing markdown+frontmatter files under .pine/learnings/.
Package learning is the pure domain layer for Pine learnings: parsing and serializing markdown+frontmatter files under .pine/learnings/.
search
Package search maintains an in-memory Bleve index over tickets and learnings.
Package search maintains an in-memory Bleve index over tickets and learnings.
server
Package server exposes the store over an HTTP+JSON API and serves the embedded web UI.
Package server exposes the store over an HTTP+JSON API and serves the embedded web UI.
store
Package store is Pine's single write path over a .pine/ directory.
Package store is Pine's single write path over a .pine/ directory.
ticket
Package ticket is the pure domain layer for Pine tickets: parsing and serializing the markdown+frontmatter file format, reading body sections, and computing the dependency/epic graph.
Package ticket is the pure domain layer for Pine tickets: parsing and serializing the markdown+frontmatter file format, reading body sections, and computing the dependency/epic graph.
view
Package view builds the presentation DTO for a ticket, joining the parsed ticket with computed dependency state, epic children, attachments, and its content hash.
Package view builds the presentation DTO for a ticket, joining the parsed ticket with computed dependency state, epic children, attachments, and its content hash.
watch
Package watch turns raw filesystem notifications under .pine/ into debounced, classified change events.
Package watch turns raw filesystem notifications under .pine/ into debounced, classified change events.
Package web provides the embedded SvelteKit build.
Package web provides the embedded SvelteKit build.

Jump to

Keyboard shortcuts

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