scope

package
v0.5.7 Latest Latest
Warning

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

Go to latest
Published: Nov 3, 2026 License: Apache-2.0 Imports: 5 Imported by: 0

Documentation

Overview

Package scope answers one question — what is the work in this directory called — the same way for every caller, so that a hook, the CLI and the MCP server cannot disagree about it.

They did disagree, and it is the failure this package exists for. The name was the basename of the working directory, which is right for most repos and wrong for every repo whose folder is not what the work is called. This one is checked out as logos/ and is called logos; so activity was filed under "logos", checkpoints under "logos", and the SessionStart hook — which looks for a handoff under the folder name — found nothing and printed nothing, session after session, with no error anywhere. Two names for one project does not fail loudly. It just quietly stops being continuity.

The fix is a file the repository carries: .logos-project, one line, the name. A file rather than an environment variable because the name is a property of the repository, not of a shell — it has to be the same answer for a hook the host launched with no profile, for an agent in another editor, and for a person typing at a terminal, and anything living in a shell profile is none of those. It is committed for the same reason: the second agent on this repo should inherit the name rather than rediscover it.

Distinct from internal/project, which is about projects the rollup has *detected* in the vault and the dossier it assembles for each. This package never opens the database; it only reads a directory. Keeping them apart keeps the hooks — which must run in milliseconds with no index — from depending on anything that needs one.

Index

Constants

View Source
const DenyMarker = ".logos/deny"

DenyMarker is the file that tells Logos to stay out of a repository. Every repository the plugin sees is tracked unless it carries one (1g): opt-in setup meant a user could work for a day in a repo with the plugin installed and find nothing recorded, with nothing anywhere saying why.

It lives in the repository, not in Logos's config, because it is a property of the repository: it travels with a worktree copy, and a team can commit it to keep every member's agent out, or leave it ignored to keep it personal.

View Source
const MarkerFile = ".logos-project"

MarkerFile is the per-repository name override. One line of text; blank lines and # comments are skipped, so the file has room for a sentence saying why it is there without that sentence becoming the project name.

Variables

This section is empty.

Functions

func Basename

func Basename(dir string) string

Basename names the project after the directory the agent is standing in. The basename rather than the full path: a project is a name a human would recognise and type at the CLI, and "/Users/x/code/kestrel" and "/Users/x/kestrel" are the same work moved, not two projects.

Returns "" — meaning global, not "unknown" — for the places a host gets launched when the user has not opened a project at all. Naming a project after a home directory would scope every unrelated session into one bucket, which is worse than staying global.

func Denied added in v0.5.3

func Denied(dir string) bool

Denied reports whether dir is inside a directory that asked Logos to stay out. Every directory from dir upwards is asked, not only those up to its DenyRoot: a plain folder is its own root, so stopping there left every subfolder of a denied one tracked, and a marker written at a subfolder that was not a repository at the time still has to hold.

func Deny added in v0.5.3

func Deny(dir string) (string, error)

Deny writes the marker at dir's DenyRoot and returns where it went.

func DenyRoot added in v0.5.3

func DenyRoot(dir string) string

DenyRoot is the directory `logos deny` writes its marker in: the repository dir is inside, and dir itself outside one. The root rather than dir, so a deny typed in a subfolder covers the sessions opened at the top.

func FromMarker

func FromMarker(dir string) string

FromMarker reads the nearest MarkerFile at or above dir, and returns "" when there is none, when it is unreadable, or when it holds nothing but comments. An unreadable marker is not an error worth failing a session over: the basename is a workable answer, and a hook that aborts because a file was briefly locked is worse than one that files a note under the folder name.

func Name

func Name(dir string) string

Name is the project for dir: the nearest .logos-project at or above it, then the git repository dir is inside, and otherwise dir's own basename.

Callers that also honour an explicit argument or LOGOS_PROJECT check those first — this is deliberately only the part that reads the disk, so the precedence order stays written down in one place (internal/mcpserver/scope.go) rather than half here and half there.

func NormalizeArg added in v0.4.9

func NormalizeArg(arg string) string

NormalizeArg reduces a project argument that is really a filesystem path to the project name it names, and leaves everything else alone.

A model asked "which project" answers with the directory it is standing in, because that is what the host handed it. That argument was taken verbatim: checkpoint(project: "/Users/x/IdeaProjects/brain") filed at sessions/users/x/ideaprojects/brain/ — four levels deep, where session.Scopes reads exactly two, so the checkpoint existed and no count, list, resume or dead-end check could ever see it again. remember stored the same string as the memory's project, putting a machine-specific path carrying the user's account name into memories/fact.md, a file this product tells people to keep in git — and leaving the fact unreachable by the only name a human would type.

The rule is Name's, which already argues it: a project is a name a human would recognise and type, and "/Users/x/code/kestrel" and "/Users/x/kestrel" are the same work moved, not two projects.

A single separator is left alone, because that is the qualified scope resolveContinuity documents — "kestrel/feature-a", a linked worktree. Two or more, or a leading /, ~, ./ or ../, is a path and nothing else.

func OnDisk added in v0.5.6

func OnDisk(p string) string

OnDisk is p spelled the way the disk spells it, with each part that exists read back from its parent directory.

macOS and Windows disks ignore case by default, so ~/ideaprojects/brain and ~/IdeaProjects/brain are one folder, and a shell keeps whichever the user typed (Go's os.Getwd trusts $PWD). git answers in the disk's spelling. Every comparison of the two then split one checkout in two: a project named "BRAIN" beside "brain", a main checkout taken for a worktree named ".git", a ruling's file "outside" its own repository. Canonicalising where a path becomes a name or a root ends that for every caller at once, rather than fixing each comparison as it is found.

The part of p that does not exist is kept as given: there is no other spelling of it to find. Symlinks are not resolved — that is EvalSymlinks' job, and callers that want both call both.

func Undeny added in v0.5.3

func Undeny(dir string) ([]string, error)

Undeny removes every marker that makes dir denied, and returns the paths it removed — none when it was not denied. All of them, on the same walk Denied makes: an --undo that left one standing would report success on a directory that is still off.

Types

This section is empty.

Jump to

Keyboard shortcuts

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