csv

package
v0.23.1 Latest Latest
Warning

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

Go to latest
Published: Jul 13, 2026 License: MIT Imports: 8 Imported by: 0

Documentation

Overview

Package csv writes comment rows to a CSV file that the launching Claude skill consumes. RFC-4180 quoting via encoding/csv handles multi-line bodies and embedded commas/quotes correctly without ad-hoc escaping.

Index

Constants

View Source
const (
	ColID        = "id"
	ColFile      = "file"
	ColFromLine  = "from_line"
	ColToLine    = "to_line"
	ColSide      = "side"
	ColBody      = "body"
	ColCreatedAt = "created_at"
	// `resolved` is "true" or "false". The skill should act only on
	// unresolved comments; resolved ones are kept as historical record.
	ColResolved = "resolved"
	// `anchor` is an opaque JSON blob (the content the comment was
	// anchored to at creation time) used internally to re-locate a
	// comment when the file changes. The skill must NOT parse it.
	ColAnchor = "anchor"
	// `anchor_status` is "ok" | "moved" | "outdated" (empty == "ok" for
	// legacy rows). A flat scalar the skill filters exactly like
	// `resolved`: treat `outdated` like `resolved=true` — the line
	// numbers no longer point at the intended content.
	ColAnchorStatus = "anchor_status"
	// `kind` classifies the comment shape: "line" (or empty for
	// pre-kind rows) for line-anchored comments where `from_line` and
	// `to_line` are meaningful; "file" for whole-file comments where
	// `from_line` / `to_line` / `side` / `anchor` / `anchor_status`
	// are all zero/empty; "area" for image-overlay annotations where
	// `area` (column 12) carries the rectangle.
	ColKind = "kind"
	// `area` is a JSON blob {"x":0.1,"y":0.2,"w":0.3,"h":0.15} where each
	// value is a 0..1 fraction. For `kind=area` it's a fraction of the
	// image's natural dimensions; for `kind=region` it's a fraction of the
	// live page's document scroll dimensions (so a re-pinned annotation
	// survives scroll). Empty for line / file rows. Fractions (not pixels)
	// so a re-encoded image / re-laid-out page still highlights the same
	// logical region.
	ColArea = "area"
	// `kind=region` annotations (the --external live-site mode) anchor to a
	// URL + rectangle instead of a file + line. `url` is the proxied page,
	// app-relative (pathname+query, no proxy origin — the proxy port is
	// random per run). Empty for every file-based kind.
	ColURL = "url"
	// `from_col` / `to_col` delimit the selected character range for
	// `kind=text` comments: a half-open [from_col, to_col) offset in raw
	// line coordinates (0-based rune index into the line). from_col binds
	// from_line, to_col binds to_line; interior lines are fully covered.
	// "0" for every other kind (line comments cover whole lines).
	ColFromCol = "from_col"
	ColToCol   = "to_col"
	// `hidden` is "true" or "false". A reviewer-only VIEW flag: an individually
	// re-hidden RESOLVED comment (issue #88) stays out of the diff/overview even
	// when "Show resolved" is on. The skill MUST ignore it — it never changes
	// which comments are actionable (resolved comments are already excluded from
	// the snapshot); it only declutters the human's view.
	ColHidden = "hidden"
	// `enqueued` is "true" or "false" (#119): whether the comment is queued for
	// the agent. A "draft" comment (enqueued=false) is the reviewer's not-yet-
	// submitted note, kept out of the actionable snapshot until they enqueue it.
	// ABSENT (short legacy rows) reads as "true": pre-#119 comments were always
	// active and must never silently become drafts. Persisted inverted from the
	// in-memory Draft flag (enqueued == !Draft) so the zero value is "enqueued".
	ColEnqueued = "enqueued"
)

Column names — load-bearing. The skill's reference docs and any LLM-side parser depend on these exact strings. Reordering breaks the contract.

Variables

Header is the row written before any data. Position-stable: only ever append new columns (readers tolerate short legacy rows by length).

Functions

This section is empty.

Types

type Row

type Row struct {
	ID        string
	File      string
	FromLine  int
	ToLine    int
	Side      string
	Body      string
	CreatedAt time.Time
	Resolved  bool
	// Anchor is an opaque JSON string (the main package owns its shape);
	// the csv package stays free of main-package types. AnchorStatus is
	// "ok" | "moved" | "outdated".
	Anchor       string
	AnchorStatus string
	// Kind is "line" (or "" for legacy/back-compat) for line-anchored
	// comments, "file" for whole-file comments, and "area" for image-
	// overlay annotations (geometry lives in Area).
	Kind string
	// Area is a JSON blob {"x":0.1,"y":0.2,"w":0.3,"h":0.15} (0..1
	// fractions) when Kind is "area" (of the image) or "region" (of the
	// live page's document); "" otherwise. Treated as opaque on the CSV
	// side — the main package owns the schema.
	Area string
	// URL is the proxied page (app-relative) for Kind=="region"; ""
	// otherwise.
	URL string
	// FromCol/ToCol delimit the selected character range for Kind=="text"
	// (half-open [FromCol, ToCol), 0-based rune offsets in raw line
	// coordinates); 0 for every other kind.
	FromCol int
	ToCol   int
	// Hidden is a reviewer-only VIEW flag: an individually re-hidden RESOLVED
	// comment (issue #88) stays out of the view even when "Show resolved" is on.
	// The main package owns the semantics; the skill ignores this column.
	Hidden bool
	// Draft is the not-yet-enqueued flag (#119). It persists INVERTED as the
	// `enqueued` column (enqueued == !Draft): the default (false) means enqueued/
	// active, so a comment saved without touching this field is queued for the
	// agent — "save auto-enqueues". A draft (true) is kept out of the actionable
	// snapshot until the reviewer enqueues it.
	Draft bool
}

Row is one comment serialized to the CSV. The csv package stays free of prereview's main-package types so the schema layer stays independent and reusable (and avoids an import cycle with the main package).

func Read

func Read(path string) ([]Row, error)

Read parses every Row from a CSV written by Writer. Returns an empty slice (and nil error) if the file doesn't exist — that's the "fresh session" case, indistinguishable from "session with zero comments". Skips the header row and any malformed rows (logging would be a caller concern; here we stay quiet to keep the CSV-as-truth contract simple).

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer serializes Rows to a CSV file atomically. Each Write replaces the entire file (write-tmp → fsync → rename → fsync parent dir), so the file on disk is either the pre-write state or the post-write state — never half-written, even if the process is killed mid-call.

All operations are serialized by an internal mutex; multiple WebSocket sessions for the same prereview process can call Write concurrently without corruption.

func NewWriter

func NewWriter(path string) *Writer

NewWriter returns a Writer targeting path. The file is not created until the first Write call.

func (*Writer) Path

func (w *Writer) Path() string

Path returns the destination path the writer was constructed with.

func (*Writer) Write

func (w *Writer) Write(rows []Row) error

Write replaces the CSV at w.path with header + every row. It returns only after the on-disk file has been fsynced and renamed; once Write returns nil, a reader on another process will see the full file.

Jump to

Keyboard shortcuts

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