Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .claude/COMMON-COMMANDS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Commands

- `go run ./cmd/jay/main.go` - Run the CLI application
- `go build` - Build the binary
- `go test ./...` - Run all tests
- `go vet ./...` - Run static analysis
- `golangci-lint run` - Run linter
9 changes: 9 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"Flaphead",
"fortytw",
"Frandisco",
"fset",
"fsnotify",
"fsys",
"Fugazi",
Expand All @@ -87,6 +88,7 @@
"gomega",
"gomnd",
"gomod",
"GOMODCACHE",
"goreleaser",
"gosec",
"gosimple",
Expand All @@ -100,6 +102,7 @@
"hanno",
"hiber",
"icase",
"iface",
"incase",
"inconshreveable",
"ineffassign",
Expand Down Expand Up @@ -127,13 +130,15 @@
"logr",
"lorax",
"Lovin",
"macfg",
"mapstructure",
"Marillion",
"Marthas",
"mattn",
"MDNA",
"Megadeth",
"mitchellh",
"mname",
"Moonchild",
"Mötley",
"msys",
Expand Down Expand Up @@ -163,6 +168,7 @@
"Persistables",
"Phloam",
"pixa",
"pkgs",
"Plastikman",
"prealloc",
"Ptrs",
Expand Down Expand Up @@ -194,6 +200,8 @@
"Sprintf",
"Spys",
"staticcheck",
"stdlib",
"stringifiers",
"struct",
"structcheck",
"stylecheck",
Expand All @@ -210,6 +218,7 @@
"toplevel",
"Tourbook",
"tparallel",
"tparams",
"tpers",
"tpref",
"Trac",
Expand Down
179 changes: 179 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,179 @@
# CLAUDE.md - agenor

## Project Overview

`agenor` is a file system traversal library that navigates directory trees and notifies
callers of events at each node. It extends the standard `filepath.Walk` with: regex/glob
filtering, hibernation (deferred activation of callbacks until a condition is met),
resume from a previously interrupted session, concurrent navigation via pants worker pool,
and hook-able traversal behaviour.

- **Module**: `github.com/snivilised/agenor`
- **Package alias**: `age` (import as `age "github.com/snivilised/agenor"`)
- **Docs**: <https://pkg.go.dev/github.com/snivilised/agenor>

## Build & Test Commands

- **Test all**: `go test ./...`
- **Dependencies**: `go mod tidy`

## Package Architecture

The dependency rule is: packages may only depend on packages in layers below them.
This rule does not apply to unit tests.

```txt
🔆 user interface layer
age (root package) - public API; may use everything

🔆 feature layer
internal/feat/resume - depends on pref, opts, kernel
internal/feat/sampling - depends on filter
internal/feat/hiber - depends on filter, services
internal/feat/filter - no internal deps

🔆 central layer
internal/kernel - no internal deps
internal/enclave - depends on pref, override
internal/opts - depends on pref
internal/override - depends on tapable; must not use enclave

🔆 support layer
pref - depends on life, services, persist
internal/persist - no internal deps
internal/services - no internal deps

🔆 intermediary layer
life - no internal deps; must not use pref

🔆 platform layer
tapable - depends on core
core - no internal deps
enums - no deps
tfs - no internal deps
```

## Core API

### Traversal modes

There are two traversal modes and two extents, giving four possible scenarios:

| Mode | Extent | Description |
| --- | --- | --- |
| Walk | Prime | Sequential traversal from root |
| Walk | Resume | Sequential traversal resuming from a saved session |
| Run | Prime | Concurrent traversal from root |
| Run | Resume | Concurrent traversal resuming from a saved session |

The low-level API composes these explicitly:

```go
// Walk/Prime
age.Walk().Configure().Extent(age.Prime(facade, opts...)).Navigate(ctx)

// Run/Resume
age.Run(wg).Configure().Extent(age.Resume(facade, opts...)).Navigate(ctx)
```

### Scenario composites

To avoid conditional duplication at the call site, use the scenario composites:

| Composite | Fixes | Selects by |
| --- | --- | --- |
| `Tortoise(isPrime)` | Walk | `isPrime bool` → Prime or Resume |
| `Hare(isPrime, wg)` | Run | `isPrime bool` → Prime or Resume |
| `Goldfish(isWalk, wg)` | Prime | `isWalk bool` → Walk or Run |
| `Hydra(isWalk, isPrime, wg)` | neither | both `isWalk` and `isPrime` |

Usage pattern - always pass `isPrime`/`isWalk` as named `const bool` values to avoid
lint warnings from bare literals:

```go
const isPrime = true
age.Tortoise(isPrime)(facade, opts...).Navigate(ctx)

var wg sync.WaitGroup
age.Hare(isPrime, &wg)(facade, opts...).Navigate(ctx)
wg.Wait()
```

### Facades

Construct facades as named variables, never inline:

```go
using := &pref.Using{...}
relic := &pref.Relic{...} // resume sessions only
```

- `pref.Using` - dependencies for a Prime session
- `pref.Relic` - saved state for a Resume session

### Enums

All enum values are in the `enums` package. Do not use `age.` prefixed aliases
for enum values - use `enums.` directly:

```go
enums.SubscribeFiles // not age.SubscribeFiles
enums.MetricNoFilesInvoked // not age.MetricNoFilesInvoked
enums.ResumeStrategyFastward
```

### Options (With* functions)

Options are passed as variadic `...pref.Option` to `Prime`/`Resume` or to a composite.
All `With*` option constructors are re-exported from the root `age` package:

```go
age.WithFilter(...)
age.WithDepth(5)
age.WithOnBegin(handler)
age.WithCPU // use all available CPUs for Run
age.WithNoW(n) // use n workers for Run
```

Use `age.IfOption` / `age.IfOptionF` / `age.IfElseOptionF` for conditional options.

## Key Types

| Type | Package | Purpose |
| --- | --- | --- |
| `age.Node` | `core` | A file system node passed to the client callback |
| `age.Servant` | `core` | Provides the client with traversal properties |
| `age.Client` | `core` | The callback signature: `func(node *age.Node) error` |
| `age.Navigator` | `core` | Returned by `Extent()`; call `.Navigate(ctx)` on it |
| `age.Options` | `pref` | Full options struct available inside `With*` constructors |
| `age.Using` | `pref` | Alias for `pref.Using` (Prime facade) |
| `age.Relic` | `pref` | Alias for `pref.Relic` (Resume facade) |
| `age.TraversalFS` | `tfs` | File system interface required for traversal |

## Internal Packages (do not import directly)

- `internal/kernel` - core traversal engine
- `internal/feat/*` - feature plugins (filter, hiber, resume, sampling, nanny)
- `internal/enclave` - supervisor and kernel result types
- `internal/opts` - options loading and binding
- `internal/persist` - session state marshalling for resume
- `internal/services` - cross-cutting concerns (message bus)
- `internal/filtering` - shared filter implementations used by multiple plugins
- `internal/laboratory` - internal test helpers (not for external use)

## Test Helpers

- **`test/hanno`** (`github.com/snivilised/agenor/test/hanno`) - utilities for building
virtual file system trees; see `GO-USER-CONFIG.md` for `Nuxx` usage
- **`test/data/musico-index.xml`** - standard XML fixture representing a sample music
directory tree, used by `Nuxx` to populate an in-memory file system
- **`internal/laboratory`** - internal-only test utilities; do not use from outside the module

## i18n

- Translation structs are defined in `github.com/snivilised/agenor/locale`
- Follow the i18n conventions in `GO-USER-CONFIG.md`

## File References

@~/.claude/GO-USER-CONFIG.md
9 changes: 9 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,15 @@ tasks:
cmds:
- govulncheck ./...

# === inspect GOMODCACHE ===================================

# run this to install inspec then invoke directly, eg:
# inspect github.com/spf13/cobra
# inspect --interfaces github.com/spf13/cobra
install-inspect:
cmds:
- go install ./tools/inspect

# === i18n =================================================

clear:
Expand Down
Loading
Loading