project

package
v3.3.2 Latest Latest
Warning

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

Go to latest
Published: Oct 10, 2026 License: MIT Imports: 18 Imported by: 0

Documentation

Overview

Package project provides utilities for detecting and normalizing project names.

It replicates the detection logic from the Claude Code shell helpers and OpenCode TypeScript plugin in pure Go, so CLI and MCP server can share a single canonical implementation.

Index

Constants

View Source
const (
	SourceGitRemote        = "git_remote"        // current repository has an origin remote; Project may come from its binding
	SourceGitRoot          = "git_root"          // current repository has no origin remote; Project may come from its binding
	SourceGitChild         = "git_child"         // auto-promoted from single child git repo
	SourceDirBasename      = "dir_basename"      // fallback: directory basename
	SourceAmbiguous        = "ambiguous"         // cwd contains multiple git repos (Case 4)
	SourceExplicitOverride = "explicit_override" // JR2-2: caller explicitly supplied a project name
	SourceSessionProject   = "session"           // caller supplied a session_id with an existing project
	// SourceUserSelectedAfterAmbiguousProject means an MCP write initially hit
	// ErrAmbiguousProject and the caller provided an explicit user-selected
	// project from the ambiguity result's available_projects list.
	SourceUserSelectedAfterAmbiguousProject = "user_selected_after_ambiguous_project"
	SourceRequestBody                       = "request_body"     // REQ-414: project came from the request body (server-side, no filesystem path)
	SourceConfig                            = "config"           // derived from .engram/config.json project_name
	SourceAllProjects                       = "all_projects"     // caller asked for cross-project search (no single project resolved)
	SourceProcessOverride                   = "process_override" // resolved from the process-level project override
	// SourceStoredProject means the observation record itself carries the
	// project. The read-only mem_get_observation tool falls back to
	// it when cwd resolution fails only because the current directory is
	// ambiguous: the integer id fully identifies the record, so its stored
	// project is a safe anchor.
	SourceStoredProject = "stored_project"
)

Source constants describe how the project name was resolved.

View Source
const EnvProjectOverride = "ENGRAM_PROJECT"

EnvProjectOverride names the environment variable that carries the process-level project override.

View Source
const SourceUnboundGit = "unbound_git"

SourceUnboundGit is an inspected candidate, not persisted project authority.

Variables

View Source
var ErrAmbiguousProject = errors.New("ambiguous project: multiple git repos found in cwd")

ErrAmbiguousProject is returned when the working directory is a parent of multiple git repositories and we cannot auto-select one.

View Source
var ErrInvalidConfig = errors.New("invalid .engram/config.json")

ErrInvalidConfig is returned when .engram/config.json exists but cannot be used as a project write lock.

View Source
var ErrInvalidProjectName = errors.New("invalid project name")
View Source
var ErrProjectHistoryUnavailable = errors.New("project history unavailable")
View Source
var ErrRepositoryBinding = errors.New("repository identity binding unavailable")

ErrRepositoryBinding means automatic Git project detection cannot safely use its private repository binding. Callers must surface this rather than derive a potentially different name from mutable repository metadata.

Functions

func CanonicalizeProjectName

func CanonicalizeProjectName(name string) string

CanonicalizeProjectName returns the canonical storage form of a project name. Callers that accept external input must validate it before canonicalizing.

func DirectoryIdentity added in v3.1.0

func DirectoryIdentity(path string) string

DirectoryIdentity compares exact directories without promoting them to a repository root. Historical subdirectories and worktrees remain distinct.

func ProcessOverride

func ProcessOverride(explicit string) (string, bool)

ProcessOverride returns the single process-level project override that every entry point (CLI, MCP, HTTP server) applies before working-directory detection. It is the one precedence rule for process-level identity:

  1. explicit process argument — `engram mcp --project <name>`, which the MCP server carries as MCPConfig.DefaultProject;
  2. the ENGRAM_PROJECT environment variable;
  3. no override, so the caller falls back to cwd detection.

A request-scoped project (the CLI `engram save --project` flag or an MCP tool argument) is resolved by the caller before this rule and always wins over it. The returned name is trimmed but not normalized; callers normalize with store.NormalizeProject so the operator still sees the normalization warning.

func RuntimeWorktreeDirectory

func RuntimeWorktreeDirectory(dir string) string

RuntimeWorktreeDirectory returns the stable directory identity for runtime session binding. Unlike DetectProjectFull's Path, linked worktrees retain their individual checkout roots while their project identity may remain shared with the primary checkout.

Types

type DetectionOptions added in v3.1.0

type DetectionOptions struct {
	HistoryLookup func(string) ([]string, error)
	InspectOnly   bool
}

DetectProjectFull resolves the project for dir using a 6-case algorithm:

  1. config — nearest .engram/config.json inside the enclosing repo/root
  2. git_remote — Git repo currently has origin: initialize an absent private binding from the remote name; otherwise reuse it
  3. git_root — Git repo currently has no origin: initialize an absent private binding from the root basename; otherwise reuse it
  4. git_child — cwd has exactly one git-repo child → resolve its canonical identity and auto-promote it
  5. ambiguous — cwd has multiple git-repo children → return ErrAmbiguousProject
  6. dir_basename — none of the above → use filepath.Base(dir)

DetectionOptions supplies local history only when establishing a Git binding. InspectOnly never initializes a binding; an unbound candidate is not authority.

type DetectionResult

type DetectionResult struct {
	// Project is the resolved project name. Empty when detection returns an error.
	Project string
	// Source describes the resolution path. For Git projects, it reflects current
	// origin-remote presence while Project may come from the stored binding.
	Source string
	// Path is the canonical directory associated with the project
	// (repo root for git cases, input dir for dir_basename).
	Path string
	// Warning is a non-empty advisory message when Source==SourceGitChild.
	Warning string
	// Error is non-nil when detection cannot safely resolve a project.
	Error error
	// AvailableProjects is populated only when Error==ErrAmbiguousProject.
	AvailableProjects []string
}

DetectionResult carries the full output of DetectProjectFull.

func DetectProjectFull

func DetectProjectFull(dir string) DetectionResult

func DetectProjectFullWithOptions added in v3.1.0

func DetectProjectFullWithOptions(dir string, options DetectionOptions) DetectionResult

func Resolve

func Resolve(options ResolutionOptions) (DetectionResult, error)

Resolve applies the canonical project-resolution contract. Explicit request values win over process overrides; process overrides win over cwd detection. ResolutionAll is intentionally global and ignores every project input.

type ProjectMatch

type ProjectMatch struct {
	Name      string // The existing project name
	MatchType string // "case-insensitive", "substring", or "levenshtein"
	Distance  int    // Levenshtein distance (0 for case-insensitive and substring matches)
}

ProjectMatch represents a project name that is similar to a query string.

func FindSimilar

func FindSimilar(name string, existing []string, maxDistance int) []ProjectMatch

FindSimilar finds projects similar to the given name from a list of existing project names. Similarity is determined by three criteria:

  1. Case-insensitive exact match (different case, same letters)
  2. Substring containment (query is a substring of candidate or vice-versa)
  3. Levenshtein distance ≤ maxDistance

Exact matches (identical strings) are always excluded.

Results are ordered: case-insensitive matches first, then substring matches, then levenshtein matches sorted by distance ascending.

type ProjectTransitionError added in v3.1.0

type ProjectTransitionError struct {
	Candidate          string
	HistoricalProjects []string
}

ProjectTransitionError signals uncertainty, not ownership of historical rows.

func (*ProjectTransitionError) Error added in v3.1.0

func (e *ProjectTransitionError) Error() string

type ResolutionMode

type ResolutionMode string

ResolutionMode makes the omission contract explicit at every entry point. Current resolves a single current project, Explicit requires a supplied project name, and All deliberately leaves the project filter unset.

const (
	ResolutionCurrent  ResolutionMode = "current"
	ResolutionExplicit ResolutionMode = "explicit"
	ResolutionAll      ResolutionMode = "all"
)

type ResolutionOptions

type ResolutionOptions struct {
	Mode            ResolutionMode
	Explicit        string
	ProcessOverride string
	Directory       string
	Detect          func(string) DetectionResult
	ProjectExists   func(string) (bool, error)

	RequireKnownExplicit bool
	RequireKnownProcess  bool
}

ResolutionOptions provides the small policy seam shared by CLI, HTTP, and MCP. Detection stays in this package; callers supply persistence existence checks only for contracts where an override must not create a new bucket.

type UnknownProjectError

type UnknownProjectError struct {
	Name string
}

UnknownProjectError reports a structurally valid name that is not known by the caller's persistence boundary.

func (*UnknownProjectError) Error

func (e *UnknownProjectError) Error() string

Jump to

Keyboard shortcuts

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