notes

package
v0.1.2 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 14 Imported by: 0

Documentation

Overview

Package notes implements a local engineering-note journal, persisted to SQLite (via GORM) under the user's application data directory.

A Note captures an engineering session or event against a project, environment, service, version, repo, branch, and set of users, with a real time range (start/stop) rather than just a duration. Notes can be searched and filtered by any of those dimensions or by free text, and the distinct values already in use for each dimension can be listed (for suggesting existing values rather than asking cold every time). A small key/value Config table lives alongside notes for remembered defaults (e.g. the project used most recently).

Notes can optionally be synced with 7pace Timetracker: pushing a note sends its date/start/stop/summary as a 7pace worklog, and pulling imports 7pace worklogs as notes. This sync is only registered when 7pace is configured (see internal/sevenpace).

The store is intentionally separate from server diagnostics: the server logs operational messages to stderr (stdout is reserved for the MCP protocol), while this package owns the user's persistent notes.

Index

Constants

View Source
const Name = "notes"

Name is the toolset name used for enable/disable filtering.

Variables

This section is empty.

Functions

func DBPath

func DBPath(dataDir string) string

DBPath returns the notes database path within a data directory.

func RegisterTools

func RegisterTools(s *server.Server, store *Store, sp *sevenpace.Client)

RegisterTools adds the notes toolset, backed by the SQLite store. It is a no-op when store is nil (database unavailable). sp is optional: when non-nil, the notes_push_sevenpace/notes_pull_sevenpace sync tools are also registered (see sevenpace_sync.go).

Types

type AddInput

type AddInput struct {
	Summary string `json:"summary" jsonschema:"short summary of the session or event"`
	Date    string `json:"date,omitempty" jsonschema:"date YYYY-MM-DD (defaults to today)"`
	Start   string `json:"start,omitempty" jsonschema:"session start time, RFC3339 (e.g. 2026-01-01T09:00:00Z) (optional)"`
	Stop    string `json:"stop,omitempty" jsonschema:"session stop time, RFC3339 (optional)"`
	Body    string `json:"body,omitempty" jsonschema:"full note body, e.g. Markdown (optional)"`
	Project string `json:"project,omitempty" jsonschema:"project (optional)"`
	Env     string `json:"env,omitempty" jsonschema:"environment, e.g. staging or production (optional)"`
	Service string `json:"service,omitempty" jsonschema:"service or component name (optional)"`
	Version string `json:"version,omitempty" jsonschema:"version or build identifier (optional)"`
	Repo    string `json:"repo,omitempty" jsonschema:"source repository (optional)"`
	Branch  string `json:"branch,omitempty" jsonschema:"branch name (optional)"`
	Users   string `json:"users,omitempty" jsonschema:"comma-separated list of users involved (optional)"`
}

AddInput records a new note.

type Bucket

type Bucket struct {
	Key     string `json:"key" jsonschema:"the date or project this total is for"`
	Notes   int    `json:"notes" jsonschema:"number of notes"`
	Minutes int    `json:"minutes" jsonschema:"total minutes (from notes with both start and stop set)"`
}

Bucket is an aggregated total for a date or project.

type Config

type Config struct {
	Key   string `gorm:"primaryKey" json:"key"`
	Value string `json:"value"`
}

Config is a generic key/value settings store living alongside notes, for remembered defaults (e.g. the project used most recently) so an interactive note-entry flow can suggest "same as last time?" instead of asking cold every time.

type ConfigKeyInput

type ConfigKeyInput struct {
	Key string `json:"key" jsonschema:"config key"`
}

ConfigKeyInput identifies a config key.

type ConfigValueOutput

type ConfigValueOutput struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

ConfigValueOutput wraps a single config value.

type IDInput

type IDInput struct {
	ID uint `json:"id" jsonschema:"note ID"`
}

IDInput identifies a note.

type ListFilter

type ListFilter struct {
	Date    string // exact date, YYYY-MM-DD
	From    string // inclusive lower bound, YYYY-MM-DD
	To      string // inclusive upper bound, YYYY-MM-DD
	Project string
	Env     string
	Service string
	Version string
	Repo    string
	Branch  string
	User    string // matches within the comma-separated Users list
	Search  string // substring match over summary or body
	Limit   int    // max rows (default 200)
}

ListFilter scopes a List query. Empty fields are ignored.

type ListInput

type ListInput struct {
	Date    string `json:"date,omitempty" jsonschema:"exact date YYYY-MM-DD"`
	From    string `json:"from,omitempty" jsonschema:"inclusive start date YYYY-MM-DD"`
	To      string `json:"to,omitempty" jsonschema:"inclusive end date YYYY-MM-DD"`
	Project string `json:"project,omitempty" jsonschema:"exact project match (optional)"`
	Env     string `json:"env,omitempty" jsonschema:"exact environment match (optional)"`
	Service string `json:"service,omitempty" jsonschema:"exact service match (optional)"`
	Version string `json:"version,omitempty" jsonschema:"exact version match (optional)"`
	Repo    string `json:"repo,omitempty" jsonschema:"exact repo match (optional)"`
	Branch  string `json:"branch,omitempty" jsonschema:"exact branch match (optional)"`
	User    string `json:"user,omitempty" jsonschema:"restrict to notes whose users list contains this identity (optional)"`
	Search  string `json:"search,omitempty" jsonschema:"free-text substring match over summary and body (optional)"`
	Limit   int    `json:"limit,omitempty" jsonschema:"maximum notes (default 200)"`
}

ListInput filters notes.

type ListValuesInput

type ListValuesInput struct {
	Field string `json:"field" jsonschema:"one of: project, env, service, version, repo, branch, users"`
}

ListValuesInput selects which field's distinct values to list.

type Note

type Note struct {
	ID      uint       `gorm:"primaryKey" json:"id"`
	Date    string     `gorm:"index" json:"date"` // YYYY-MM-DD (defaults to today)
	Start   *time.Time `gorm:"index" json:"start,omitempty"`
	Stop    *time.Time `json:"stop,omitempty"`
	Summary string     `json:"summary"` // short summary of the session
	Body    string     `json:"body,omitempty"`
	Project string     `gorm:"index" json:"project,omitempty"`
	Env     string     `gorm:"index" json:"env,omitempty"`
	Service string     `gorm:"index" json:"service,omitempty"`
	Version string     `json:"version,omitempty"`
	Repo    string     `gorm:"index" json:"repo,omitempty"`
	Branch  string     `json:"branch,omitempty"`
	// Users is a comma-separated list of user identities involved.
	Users     string    `json:"users,omitempty"`
	CreatedAt time.Time `json:"createdAt"`
	UpdatedAt time.Time `json:"updatedAt"`
}

Note is a single engineering note: a session or event recorded against a project, environment, service, version, repo, branch, and set of users, with a real time range rather than just a duration.

type PullSevenPaceInput

type PullSevenPaceInput struct {
	Filter string `json:"filter,omitempty" jsonschema:"optional OData $filter, e.g. \"Timestamp ge 2026-01-01T00:00:00Z\""`
	Top    int    `json:"top,omitempty" jsonschema:"maximum number of worklogs to pull (optional)"`
}

PullSevenPaceInput scopes a 7pace worklog pull.

type PullSevenPaceOutput

type PullSevenPaceOutput struct {
	Pulled  int      `json:"pulled" jsonschema:"number of worklogs fetched from 7pace"`
	Created []Note   `json:"created" jsonschema:"the notes created from those worklogs"`
	Skipped int      `json:"skipped" jsonschema:"worklogs that could not be mapped to a note, e.g. an unparseable timestamp"`
	Errors  []string `` /* 233-byte string literal not displayed */
}

PullSevenPaceOutput reports the notes created from a 7pace pull.

type PushSevenPaceInput

type PushSevenPaceInput struct {
	ID         uint `json:"id" jsonschema:"note ID to push"`
	WorkItemID int  `json:"workItemId,omitempty" jsonschema:"linked Azure DevOps work item ID (optional; some 7pace configurations require one)"`
}

PushSevenPaceInput identifies a note to push to 7pace.

type PushSevenPaceOutput

type PushSevenPaceOutput struct {
	NoteID  uint             `json:"noteId"`
	WorkLog sevenpace.Record `json:"workLog" jsonschema:"the created 7pace worklog record"`
}

PushSevenPaceOutput reports the result of pushing a note to 7pace.

type SetConfigInput

type SetConfigInput struct {
	Key   string `json:"key" jsonschema:"config key, e.g. default_project"`
	Value string `json:"value" jsonschema:"config value"`
}

SetConfigInput sets a config key/value pair.

type Store

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

Store is a GORM-backed SQLite store for notes and their config.

func Open

func Open(path string) (*Store, error)

Open opens (creating if needed) the SQLite database at path and migrates the schema. GORM's own logger is silenced so it never writes to stdout, which the MCP protocol owns.

func (*Store) Add

func (s *Store) Add(n *Note) (*Note, error)

Add inserts a new note and returns it (with its assigned ID).

func (*Store) Close

func (s *Store) Close() error

Close closes the underlying database connection.

func (*Store) Delete

func (s *Store) Delete(id uint) error

Delete removes a note by ID. It returns gorm.ErrRecordNotFound when no note has that ID, so callers don't report a successful delete for a row that never existed (GORM itself treats a zero-row delete as success).

func (*Store) Get

func (s *Store) Get(id uint) (*Note, error)

Get returns a single note by ID.

func (*Store) GetConfig

func (s *Store) GetConfig(key string) (string, error)

GetConfig returns a config value by key, or "" if unset.

func (*Store) List

func (s *Store) List(f ListFilter) ([]Note, error)

List returns notes matching the filter, newest date first.

func (*Store) ListConfig

func (s *Store) ListConfig() ([]Config, error)

ListConfig returns every config key/value pair.

func (*Store) ListValues

func (s *Store) ListValues(field string) ([]string, error)

ListValues returns the distinct, sorted values already used for the given field across all notes (empty values excluded). field must be one of "project", "env", "service", "version", "repo", "branch", "users". Since Users stores a comma-separated list, its values are split before deduping.

func (*Store) SetConfig

func (s *Store) SetConfig(key, value string) error

SetConfig sets (creating or overwriting) a config value. Uses an explicit upsert (not Save, which would only UPDATE since Key is a non-zero primary key and would silently affect zero rows for a key that doesn't exist yet).

func (*Store) Summarize

func (s *Store) Summarize(f ListFilter) (*Summary, error)

Summarize aggregates notes matching the filter by day and by project.

func (*Store) Update

func (s *Store) Update(id uint, changes map[string]any) (*Note, error)

Update applies non-empty fields from changes to the note with the given ID and returns the updated note.

type Summary

type Summary struct {
	Notes        int      `json:"notes" jsonschema:"total notes"`
	TotalMinutes int      `json:"totalMinutes" jsonschema:"total minutes across notes with both start and stop set"`
	TotalHours   float64  `json:"totalHours" jsonschema:"total hours (minutes / 60)"`
	ByDate       []Bucket `json:"byDate" jsonschema:"per-day totals, newest first"`
	ByProject    []Bucket `json:"byProject" jsonschema:"per-project totals, most minutes first"`
}

Summary aggregates notes over a date range.

type SummaryInput

type SummaryInput struct {
	Date string `json:"date,omitempty" jsonschema:"exact date YYYY-MM-DD"`
	From string `json:"from,omitempty" jsonschema:"inclusive start date YYYY-MM-DD"`
	To   string `json:"to,omitempty" jsonschema:"inclusive end date YYYY-MM-DD"`
}

SummaryInput scopes a notes summary.

type UpdateInput

type UpdateInput struct {
	ID      uint    `json:"id" jsonschema:"note ID"`
	Summary *string `json:"summary,omitempty" jsonschema:"new summary"`
	Date    *string `json:"date,omitempty" jsonschema:"new date YYYY-MM-DD"`
	Start   *string `json:"start,omitempty" jsonschema:"new start time, RFC3339"`
	Stop    *string `json:"stop,omitempty" jsonschema:"new stop time, RFC3339"`
	Body    *string `json:"body,omitempty" jsonschema:"new body"`
	Project *string `json:"project,omitempty" jsonschema:"new project"`
	Env     *string `json:"env,omitempty" jsonschema:"new environment"`
	Service *string `json:"service,omitempty" jsonschema:"new service"`
	Version *string `json:"version,omitempty" jsonschema:"new version"`
	Repo    *string `json:"repo,omitempty" jsonschema:"new repo"`
	Branch  *string `json:"branch,omitempty" jsonschema:"new branch"`
	Users   *string `json:"users,omitempty" jsonschema:"new comma-separated users list"`
}

UpdateInput changes fields of a note. Pointer fields are applied only when provided, so callers can update a single field without clearing others.

Jump to

Keyboard shortcuts

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