agentleFS
remoteossremoteoss/dexter/AGENTS.md

dexter

AGENTS.md421 starsChanged 7 months agoMITView on GitHub

For a detailed reference of the LSP code flows, use-chain resolution, variable scoping, and rename logic, see docs/architecture.md. Install golangci-lint if needed: - Our guiding principles for all code written are: We want maximum performance and speed, but without sacrificing correctness of results. It's okay to sacrifice some readability in the name of performance, but be mindful of the tradeoffs. - When fixing reported bugs, write the regression test first, verify the failure, and then write the fix. - When…

  • Installs packages
  • Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.

    Of the AGENTS.md files we read, the 31,700 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.

  • In this repositoryOne other file it gives its agents.

The file

We split this AGENTS.md into the rules it gives an agent

5 rules. About 0 are common, 2 are distinctive, and 3 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈2

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    When fixing reported bugs, write the regression test first, verify the failure, and then write the fix.

    line 23
  2. Very specific:

    When given real code causing bugs, use fake/generic module and function names in tests (e.g.

    line 24
  3. Distinctive:

    When writing code that affects the in-document runtime parsing that is similar to the index parsing (or vice versa), see if the code can be …

    line 26
  4. Very specific:

    Do not change Version or IndexVersion in a feature PR.

    line 39
  5. Very specific:

    Use parser.TokenWalker for new code that iterates over tokens.

    line 63

≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

Agent guidelines

For a detailed reference of the LSP code flows, use-chain resolution, variable scoping, and rename logic, see docs/architecture.md.

Building and testing

make build  # build dexter binary
make test   # run all tests
make lint   # run after completing a set of changes

Install golangci-lint if needed:

go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.11.4

Conventions

  • Our guiding principles for all code written are: We want maximum performance and speed, but without sacrificing correctness of results. It's okay to sacrifice some readability in the name of performance, but be mindful of the tradeoffs.
  • When fixing reported bugs, write the regression test first, verify the failure, and then write the fix.
  • When given real code causing bugs, use fake/generic module and function names in tests (e.g. MyApp.Accounts, SharedLib.Worker) — never use real names from the user's codebase, even for regression tests
  • When writing code that affects the in-document runtime parsing that is similar to the index parsing (or vice versa), see if the code can be unified to reduce deduplication. But we should never do this at the cost of performance. Indexing must be kept fast no matter what.
  • Keep the CLI commands (init, reindex, lookup, references, stop) working with no editor open. They are frontends to the workspace daemon, not clients of the LSP; init is the only one that takes exclusive workspace ownership. The daemon is the only process that opens the index for normal operation, and it belongs to the workspace rather than to any frontend: dexter lsp is also a frontend, a stdio proxy, and nothing may serve an editor in-process
  • Parser tests should cover real-world Elixir patterns from large codebases
  • Version strings (Version, IndexVersion, and daemon.ContractVersion) live in internal/version/version.go and internal/daemon/endpoint.go. Do not bump them in feature PRs; version bumps are made together in a separate release PR. Whenever IndexVersion is bumped, bump daemon.ContractVersion with it: a running daemon from the older build would otherwise keep serving the new frontend from the stale index.

When a release PR should bump IndexVersion

Do not change Version or IndexVersion in a feature PR. Note when a parser or schema change makes existing indexes produce wrong results so the separate release PR can bump IndexVersion alongside Version. The LSP server checks this on startup and, on a mismatch, triggers a forced rebuild of a populated index. An empty index is left to the background full build, which stamps the current version itself — so a bump is not a way to force anything to happen on a store that has no rows.

What to check when making changes

Area changedAlso check
parseUsingBody / use-chain logiclookupInUsingEntry, resolveModuleViaUseChainWithOpts, findModulesWhoseUsingImports
resolveBareFunctionModuleAll callers pass lineNum — used by LookupFunctionInFile to prefer the enclosing module
Rename edits (buildTextEdits)findFunctionTokenColumns (skips keyword keys), includeKeyword flag on import sites
Variable scoping (variables.go)Pin operator handling, with multi-clause, stab body rebinds
Tokenizer string/heredoc/sigil scanningTokenResult.Interp (the #{} token stream), flushInterpRefs, TokenizedFile.ExpressionAtCursor
ExtractAliasesLSP handlers use ExtractAliasesInScope(text, lineNum) — scope-aware. Only Completion and CodeAction use the unscoped ExtractAliases intentionally
Any new store queryAdd an index if the query will run on hot paths (definition, hover, references)
internal/beam ETF tag handlingThe ERTS external term format spec. One wrong field width desynchronises every later term in the chunk (EXPORT_EXT carries its arity as an integer term, NEW_FUN_EXT as a raw byte)
internal/workspace/runtime.go (mutation queue, readiness, subscribers)internal/lsp write coordination (IndexCoordinator), watch.go event filtering, failed-watch retries and one-shot coverage reconciliation, workspace/watch subscribers, and the Close ordering: watchers stop before the queue drains, and the queue drains before the store checkpoints
A new failure or degraded stateReport it through the workspace notify.Reporter (IndexCoordinator.Reporter()): a condition key with Set, and Clear with a message when it stops. A log line alone is not seen in an editor. Report only on state changes, never per file or per request. See "Telling the user" in docs/architecture.md
internal/daemon (protocol, registries, endpoint)ContractVersion, the reserved method and kind names in registry.go, client.go multiplexing (one reader, ids matched to callers, notifications interleaved), and docs/daemon.md. Socket paths must stay short: sockaddr_un is capped near 104 bytes

Token walking

Use parser.TokenWalker for new code that iterates over tokens. It provides:

  • Consistent depth tracking with automatic clamping (never goes negative)
  • Forward progress guarantees via EnsureProgress()
  • Module/function detection helpers
  • Statement boundary handling

See internal/parser/token_walker.go for the API and token_walk_test.go for examples.

The consistency tests in elixir_test.go (TestModuleScopeConsistency, TestDepthTrackingConsistency) verify that similar functions handle edge cases the same way. Add new edge cases there when fixing scope-related bugs.

Common gotchas

  • Nested modules: defmodule Inner do inside Outer creates Outer.Inner in the store but the raw line only says Inner. Use LookupModulesInFile / LookupEnclosingModule rather than regex-scanning lines for module names.
  • as: aliases: alias Foo.Bar, as: Baz — Baz ≠ last segment of Foo.Bar. Detected in PrepareRename by comparing the alias short name to moduleLastSegment(resolved).
  • Import-only sites in rename: Lines like import Module, only: [fn: 1] have the function name as a keyword key. They're flagged includeKeyword: true so buildTextEdits uses findAllTokenColumns instead of findFunctionTokenColumns.
  • Dynamic use-chain imports: import unquote(mod) goes into optBindings, not imports. Static findModulesWhoseUsingImports won't find these — the fast opt-binding path in the References handler handles them instead.
  • ExUnit.CaseTemplate: Uses using opts do instead of defmacro __using__. Only recognised when the module has use ExUnit.CaseTemplate. The body may delegate to a helper function (using_block(opts)) — parseHelperQuoteBlock follows this.
  • Performance: findModulesWhoseUsingImports is expensive (~30-65ms). The References handler skips it when the fast opt-binding check already found injectors.
  • A session is not a process: one daemon serves every editor and CLI client on a workspace, so nothing on a path shared with daemon-backed sessions may call os.Exit. Server.Exit closes the stream instead when manageWorkspace is false (every daemon session); Shutdown and disconnects go through CloseSession, which is sync.Once-guarded. The embedded manageWorkspace path exists for tests and library use only — the CLI never selects it, because one workspace has one owner
  • Session state stays per-session, index state stays shared: open documents, client capabilities, and position encoding belong to one Server; the store, IndexCoordinator, watchers, and caches belong to the workspace.Runtime. A new field on Server is a new per-editor copy unless it deliberately goes on the coordinator.
  • Daemon-backed sessions do not manage the workspace: with ManageWorkspace: false, Initialize starts no reindex and no .git/HEAD poll — the runtime does both. It still registers didChangeWatchedFiles, because the editor's glob covers deps/ and path dependencies that the native watcher deliberately skips; duplicate events from both sources coalesce by path. Editor notifications are routed through WorkspaceEvents into the one mutation queue rather than written to the index directly.
  • A workspace has to look like one: init, reindex, lookup, and references refuse a directory with no mix.exs, .git, or Dexter database unless -y/--yes is passed, so a mistyped command cannot build an index over the home directory. lsp warns and serves anyway (the editor decides), and stop never checks, so a stray daemon can always be cleaned up

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.