Source selection
Every model call starts from an explicit, inspectable source manifest. The
selection subsystem (src/sources.ts, exported as collectSources) owns
discovery, exclusion, ordering, and byte budgets; kronika sources prints
its result without calling Brama.
Discovery
Without --source, discovery is automatic:
git ls-files --cached --others --exclude-standard lists tracked plus
untracked-but-not-ignored files; when the path is not a Git worktree, a
bounded recursive walk is used instead. The walk never follows symbolic
links and never enters excluded directories.
With --source (repeatable), only the named files and directories are
candidates. An explicit source must exist, must lie inside the repository,
and must not be a symbolic link — each violation is an error. Directories
are walked with the same exclusion rules.
Exclusions
Exclusions apply to every candidate, automatic or explicit, and each
excluded file appears in skipped with its reason:
- Directories, never entered:
.git,.next,.turbo,.vercel,.venv,build,coverage,dist,node_modules,recordings,target,vendor— reported as "generated, dependency, recording, or private deployment directory". - Credential and lock files:
.envand any.env.*,.netrc,.npmrc,auth.json,credentials.json,secrets.json,id_rsa,id_dsa,id_ed25519, and lock files (package-lock.json,yarn.lock,pnpm-lock.yaml,bun.lock,bun.lockb,Cargo.lock). - Key material: any
.key,.pem,.p12,.pfxfile.
Automatic discovery additionally keeps only recognizably textual files: a
fixed extension allowlist (.ts, .js, .py, .rs, .go, .md,
.json, .yaml, .toml, .sql, .proto, and the rest of the common
source and config extensions) plus Dockerfile, Makefile, Procfile,
README, and README.*. Explicit selections skip this filter but still
pass the exclusion, size, binary, and encoding checks below.
Ordering
Candidates are ranked so the budget is spent on the most relevant evidence first, ties broken alphabetically:
| Rank | Paths |
|---|---|
| 0 | the output document itself |
| 1 | README.md, anything under docs/ |
| 2 | package.json, pyproject.toml, cargo.toml, go.mod |
| 3 | paths containing config or schema |
| 4 | anything under src/ or app/ |
| 5 | everything else |
| 6 | paths containing test |
Budgets and content checks
Two byte budgets bound the payload, both required to be positive:
--max-file-bytes(default64000): a larger file is skipped with reasonlarger than <n> bytes.--max-input-bytes(default200000): once the running total would be exceeded, further files are skipped with reasontotal source limit <n> bytes reached— which is why ordering matters.
Each surviving file is then read and checked: content containing a NUL byte
is skipped as binary content; content that does not decode as strict UTF-8
is skipped as not valid UTF-8 text; files that are empty after trimming
are silently dropped. What remains is the manifest — path, content, and
byte count per file, plus the total — that write and check put in
front of the model.
Confinement
The selection and the output are confined to one repository. A candidate
outside the repository root is dropped; an explicit source outside it is an
error; an --output that resolves outside the repository is an error in
both the selector and the writer. The writer's atomic replacement
(cli) operates only on that confined path.